From d4af46450cc2e54bb80d76bbd213e617b0685c98 Mon Sep 17 00:00:00 2001 From: FyloZ Date: Wed, 15 Jun 2022 15:27:06 -0400 Subject: [PATCH] Fully working app --- src/configuration/SimulationData.java | 8 + src/configuration/XmlConfigurationParser.java | 15 ++ src/ressources/configuration.xml | 2 +- src/simulation/Building.java | 3 + src/simulation/ComponentType.java | 4 + src/simulation/Factory.java | 48 +++--- src/simulation/RandomSellStrategy.java | 15 ++ src/simulation/SellStrategy.java | 5 + src/simulation/TimedSellStrategy.java | 12 ++ src/simulation/Warehouse.java | 53 ++++-- src/simulation/WarehouseObserver.java | 2 +- src/view/MainPanel.java | 154 ++++++++---------- src/view/MainWindow.java | 2 +- src/view/StrategyPanel.java | 24 ++- src/view/WindowMenu.java | 2 - src/view/components/BuildingIcon.java | 81 +++++++++ src/view/components/ComponentIcon.java | 91 +++++++++++ src/view/components/RouteLine.java | 47 ++++++ 18 files changed, 438 insertions(+), 130 deletions(-) create mode 100644 src/simulation/RandomSellStrategy.java create mode 100644 src/simulation/SellStrategy.java create mode 100644 src/simulation/TimedSellStrategy.java create mode 100644 src/view/components/BuildingIcon.java create mode 100644 src/view/components/ComponentIcon.java create mode 100644 src/view/components/RouteLine.java diff --git a/src/configuration/SimulationData.java b/src/configuration/SimulationData.java index 0deb959..9f61fb0 100644 --- a/src/configuration/SimulationData.java +++ b/src/configuration/SimulationData.java @@ -1,7 +1,9 @@ package configuration; +import metadata.BuildingMetadata; import simulation.Building; import simulation.Route; +import simulation.Warehouse; import java.util.Collection; @@ -21,4 +23,10 @@ public class SimulationData { public Collection getRoutes() { return routes; } + + public Warehouse getWarehouse() { + return (Warehouse) buildings.stream() + .filter(b -> b.getType().equals(BuildingMetadata.TYPE_WAREHOUSE)) + .findFirst().get(); + } } diff --git a/src/configuration/XmlConfigurationParser.java b/src/configuration/XmlConfigurationParser.java index 4d3a066..ad46d3d 100644 --- a/src/configuration/XmlConfigurationParser.java +++ b/src/configuration/XmlConfigurationParser.java @@ -162,9 +162,24 @@ public class XmlConfigurationParser implements ConfigurationParser { } } + subscribeFactories(buildings); return new SimulationData(buildings, routes); } + private void subscribeFactories(Collection buildings) { + Warehouse warehouse = (Warehouse) buildings.stream() + .filter(b -> b.getType().equals(BuildingMetadata.TYPE_WAREHOUSE)) + .findFirst().get(); + + for (Building building : buildings) { + if (!(building instanceof Factory)) { + continue; + } + + warehouse.attach(((Factory) building)); + } + } + private Building parseBuilding(Element buildingElement, Map buildingMetadata) { String type = buildingElement.getAttribute(ATTRIBUTE_TYPE); int id = Integer.parseInt(buildingElement.getAttribute(ATTRIBUTE_ID)); diff --git a/src/ressources/configuration.xml b/src/ressources/configuration.xml index f7bf5ed..9dd3fb4 100644 --- a/src/ressources/configuration.xml +++ b/src/ressources/configuration.xml @@ -60,7 +60,7 @@ - + diff --git a/src/simulation/Building.java b/src/simulation/Building.java index ea48aa3..fb5767e 100644 --- a/src/simulation/Building.java +++ b/src/simulation/Building.java @@ -1,5 +1,7 @@ package simulation; +import metadata.BuildingMetadata; + import java.util.Optional; public abstract class Building { @@ -18,6 +20,7 @@ public abstract class Building { public abstract void processInput(Component input); public abstract Optional update(); + public abstract BuildingMetadata getMetadata(); public int getId() { return id; diff --git a/src/simulation/ComponentType.java b/src/simulation/ComponentType.java index 9afe438..19dc7dd 100644 --- a/src/simulation/ComponentType.java +++ b/src/simulation/ComponentType.java @@ -15,6 +15,10 @@ public enum ComponentType { this.typeName = typeName; } + public String getTypeName() { + return typeName; + } + public static ComponentType getForTypeName(String typeName) { return Arrays.stream(ComponentType.values()) .filter(t -> Objects.equals(t.typeName, typeName)) diff --git a/src/simulation/Factory.java b/src/simulation/Factory.java index 4e97d64..6b53487 100644 --- a/src/simulation/Factory.java +++ b/src/simulation/Factory.java @@ -1,5 +1,6 @@ package simulation; +import metadata.BuildingMetadata; import metadata.FactoryInput; import metadata.FactoryMetadata; @@ -7,11 +8,12 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -public class Factory extends Building { +public class Factory extends Building implements WarehouseObserver { private final FactoryMetadata metadata; private final Map inputsCount = new HashMap<>(); private boolean isProductionStarted; + private boolean warehouseFull; private long productionTicks = 0L; public Factory(int id, String type, int x, int y, FactoryMetadata metadata) { @@ -27,20 +29,11 @@ public class Factory extends Building { @Override public Optional update() { - if (!isProductionStarted) return Optional.empty(); + if (!isProductionStarted || (productionTicks == 0 && warehouseFull)) return Optional.empty(); productionTicks++; - float productionInterval = metadata.getProductionInterval(); - if (productionTicks < productionInterval) { - - if (productionTicks / productionInterval >= (2 / 3f)) { - state = BuildingState.FULL; - } else if (productionTicks / productionInterval >= (1 / 3f)) { - state = BuildingState.TWO_THIRD; - } else { - state = BuildingState.ONE_THIRD; - } - + if (productionTicks < metadata.getProductionInterval()) { + updateState(); return Optional.empty(); } @@ -68,6 +61,27 @@ public class Factory extends Building { } } + @Override + public BuildingMetadata getMetadata() { + return metadata; + } + + @Override + public void updateWarehouseState(boolean full) { + warehouseFull = full; + } + + private void updateState() { + float productionRatio = productionTicks / (float) metadata.getProductionInterval(); + if (productionRatio >= (2 / 3f)) { + state = BuildingState.FULL; + } else if (productionRatio >= (1 / 3f)) { + state = BuildingState.TWO_THIRD; + } else { + state = BuildingState.ONE_THIRD; + } + } + private boolean hasEnoughComponents() { for (FactoryInput input : metadata.getInputs()) { if (!inputsCount.containsKey(input.getType()) || inputsCount.get(input.getType()) < input.getQuantity()) { @@ -94,16 +108,12 @@ public class Factory extends Building { if (!inputsCount.isEmpty()) { for (ComponentType type : inputsCount.keySet()) { - builder.append(type.toString()) - .append(": ") - .append(inputsCount.get(type)); + builder.append(type.toString()).append(": ").append(inputsCount.get(type)); } } if (isProductionStarted) { - builder.append(" (interval: ") - .append(productionTicks) - .append(')'); + builder.append(" (interval: ").append(productionTicks).append(')'); } builder.append(']'); diff --git a/src/simulation/RandomSellStrategy.java b/src/simulation/RandomSellStrategy.java new file mode 100644 index 0000000..25a980a --- /dev/null +++ b/src/simulation/RandomSellStrategy.java @@ -0,0 +1,15 @@ +package simulation; + +import java.util.Random; + +public class RandomSellStrategy implements SellStrategy { + private static final int SELL_CHANCE_PERCENT = 1; + + private final Random random = new Random(); + + @Override + public boolean shouldSell() { + int gen = random.nextInt(100); + return gen < SELL_CHANCE_PERCENT; + } +} diff --git a/src/simulation/SellStrategy.java b/src/simulation/SellStrategy.java new file mode 100644 index 0000000..4432f1d --- /dev/null +++ b/src/simulation/SellStrategy.java @@ -0,0 +1,5 @@ +package simulation; + +public interface SellStrategy { + boolean shouldSell(); +} diff --git a/src/simulation/TimedSellStrategy.java b/src/simulation/TimedSellStrategy.java new file mode 100644 index 0000000..5ba25d9 --- /dev/null +++ b/src/simulation/TimedSellStrategy.java @@ -0,0 +1,12 @@ +package simulation; + +public class TimedSellStrategy implements SellStrategy { + private static final int SELL_INTERVAL = 300; + + private long tick = 0L; + + @Override + public boolean shouldSell() { + return ++tick % SELL_INTERVAL == 0; + } +} diff --git a/src/simulation/Warehouse.java b/src/simulation/Warehouse.java index 06926aa..3b0cd3b 100644 --- a/src/simulation/Warehouse.java +++ b/src/simulation/Warehouse.java @@ -1,7 +1,5 @@ package simulation; -import configuration.SimulationConfiguration; -import configuration.SimulationConfigurationSingleton; import metadata.BuildingMetadata; import metadata.WarehouseMetadata; @@ -11,26 +9,62 @@ import java.util.Optional; public class Warehouse extends Building implements WarehouseSubject { private final WarehouseMetadata metadata; + private final int capacity; private final Collection observers = new ArrayList<>(); + private SellStrategy sellStrategy = new TimedSellStrategy(); private int planeCount = 0; public Warehouse(int id, String type, int x, int y, WarehouseMetadata metadata) { super(id, type, x, y); + this.metadata = metadata; + this.capacity = metadata.getInput().getCapacity(); } @Override public Optional update() { + if (planeCount > 0 && sellStrategy.shouldSell()) { + sellPlane(); + } + return Optional.empty(); } @Override public void processInput(Component input) { planeCount++; + updateState(); + notifyObservers(); } - private void sellComponent() { + @Override + public BuildingMetadata getMetadata() { + return metadata; + } + + public void setSellStrategy(SellStrategy sellStrategy) { + this.sellStrategy = sellStrategy; + } + + private void updateState() { + float fullRatio = planeCount / (float) capacity; + if (fullRatio >= 1) { + state = BuildingState.FULL; + } else if (fullRatio >= (2 / 3f)) { + state = BuildingState.TWO_THIRD; + } else if (fullRatio >= (1 / 3f)) { + state = BuildingState.ONE_THIRD; + } else { + state = BuildingState.EMPTY; + } + } + + private void sellPlane() { + // Vend un avion + planeCount--; + updateState(); + notifyObservers(); } public void attach(WarehouseObserver observer) { @@ -42,20 +76,13 @@ public class Warehouse extends Building implements WarehouseSubject { } public void notifyObservers() { - boolean isFull = planeCount >= getCapacity(); - observers.forEach(o -> o.update(isFull)); - } - - private static int getCapacity() { - SimulationConfiguration configuration = SimulationConfigurationSingleton.getInstance().getConfiguration(); - WarehouseMetadata metadata = (WarehouseMetadata) configuration.getMetadata().get(BuildingMetadata.TYPE_WAREHOUSE); - - return metadata.getInput().getCapacity(); + boolean isFull = planeCount >= capacity; + observers.forEach(o -> o.updateWarehouseState(isFull)); } @Override public String toString() { - boolean isFull = planeCount >= getCapacity(); + boolean isFull = planeCount >= capacity; return String.format("[planes: %d, full: %s]", planeCount, isFull); } diff --git a/src/simulation/WarehouseObserver.java b/src/simulation/WarehouseObserver.java index dc3adc0..3fb55ef 100644 --- a/src/simulation/WarehouseObserver.java +++ b/src/simulation/WarehouseObserver.java @@ -6,5 +6,5 @@ public interface WarehouseObserver { * * @param full Si l'entrepot est plein ou non */ - void update(boolean full); + void updateWarehouseState(boolean full); } diff --git a/src/view/MainPanel.java b/src/view/MainPanel.java index ef92e57..f2f2439 100644 --- a/src/view/MainPanel.java +++ b/src/view/MainPanel.java @@ -3,59 +3,67 @@ package view; import configuration.SimulationConfiguration; import configuration.SimulationConfigurationSingleton; import configuration.SimulationData; -import metadata.BuildingMetadata; +import simulation.Building; import simulation.Component; -import simulation.*; +import simulation.Route; +import view.components.BuildingIcon; +import view.components.ComponentIcon; +import view.components.RouteLine; -import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; -import java.io.File; -import java.io.IOException; import java.io.Serial; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; -public class MainPanel extends JPanel { +public class MainPanel extends JLayeredPane { @Serial private static final long serialVersionUID = 1L; - private final Map> buildingIcons = new HashMap<>(); - private final Map componentsIcons = new HashMap<>(); private final Map buildingsById = new HashMap<>(); - private final Collection componentRoutes = new ArrayList<>(); + private final Collection componentIcons = new ArrayList<>(); private SimulationConfiguration configuration; private SimulationData simulationData; - public MainPanel() { - try { - componentsIcons.put(ComponentType.METAL, loadImage("src/ressources/metal.png")); - componentsIcons.put(ComponentType.MOTOR, loadImage("src/ressources/moteur.png")); - componentsIcons.put(ComponentType.PLANE, loadImage("src/ressources/avion.png")); - componentsIcons.put(ComponentType.WING, loadImage("src/ressources/aile.png")); - } catch (IOException e) { - throw new RuntimeException("Could not load components icons", e); - } - } - public void reloadSimulation() { configuration = SimulationConfigurationSingleton.getInstance().getConfiguration(); simulationData = configuration.getSimulationData(); buildingsById.clear(); - simulationData.getBuildings().forEach(b -> buildingsById.put(b.getId(), b)); - buildingIcons.clear(); - configuration.getMetadata().values().forEach(m -> { - try { - loadBuildingImages(m); - } catch (IOException e) { - System.err.println("Could not load simulation icons"); - } - }); + int routesOffset = 0; + for (Building building : simulationData.getBuildings()) { + BuildingIcon icon = this.initializeBuilding(building); + routesOffset = icon.getWidth() / 2; + } + + for (Route route : simulationData.getRoutes()) { + initializeRoute(route, routesOffset); + } } - int x = 0; + private BuildingIcon initializeBuilding(Building building) { + buildingsById.put(building.getId(), building); + + BuildingIcon icon = new BuildingIcon(building); + add(icon); + setLayer(icon, 2); + + return icon; + } + + private void initializeRoute(Route route, int offset) { + Building from = getBuildingById(route.getFrom()); + Building to = getBuildingById(route.getTo()); + + RouteLine line = new RouteLine(from, to, offset); + + add(line); + setLayer(line, 0); + } @Override public void paint(Graphics g) { @@ -63,30 +71,29 @@ public class MainPanel extends JPanel { if (configuration == null) return; - simulationData.getRoutes().forEach(r -> paintRoute(r, g)); - simulationData.getBuildings().forEach(b -> paintBuilding(b, g)); + updateBuildings(); - Collection removedComponentRoutes = new ArrayList<>(); - for (ComponentRoute route : componentRoutes) { - paintComponentRoute(route, g); + Collection removedIcons = new ArrayList<>(); - if (route.isTransitFinished()) { - removedComponentRoutes.add(route); - route.sendToOutputBuilding(); + for (ComponentIcon icon : componentIcons) { + if (icon.isAtDestination()) { + removedIcons.add(icon); } } - // Prevent concurrency problems - componentRoutes.removeAll(removedComponentRoutes); + removedIcons.forEach(i -> { + componentIcons.remove(i); + remove(i); + + Building outputBuilding = getBuildingById(i.getDestinationId()); + outputBuilding.processInput(i.getComponent()); + }); } - private void paintBuilding(Building building, Graphics g) { - building.update().ifPresent(component -> addComponentRoute(component, building)); - - Image icon = buildingIcons.get(building.getType()).get(building.getState()); - g.drawImage(icon, building.getX(), building.getY(), null); - - g.drawString(building.toString(), building.getX() - 20, building.getY() - 5); + private void updateBuildings() { + simulationData.getBuildings().forEach(b -> { + b.update().ifPresent(c -> addComponentRoute(c, b)); + }); } private void addComponentRoute(Component component, Building fromBuilding) { @@ -95,48 +102,17 @@ public class MainPanel extends JPanel { .findFirst().get(); Building toBuilding = buildingsById.get(route.getTo()); - componentRoutes.add(new ComponentRoute(component, toBuilding, fromBuilding.getX(), fromBuilding.getY())); + Point fromPosition = new Point(fromBuilding.getX(), fromBuilding.getY()); + Point toPosition = new Point(toBuilding.getX(), toBuilding.getY()); + + ComponentIcon icon = new ComponentIcon(component, route.getTo(), fromPosition, toPosition); + componentIcons.add(icon); + + add(icon); + setLayer(icon, 1); } - private void paintRoute(Route route, Graphics g) { - Building fromBuilding = buildingsById.get(route.getFrom()); - Image fromIcon = buildingIcons.get(fromBuilding.getType()).get(fromBuilding.getState()); - - Building toBuilding = buildingsById.get(route.getTo()); - Image toIcon = buildingIcons.get(toBuilding.getType()).get(fromBuilding.getState()); - - g.drawLine( - fromBuilding.getX() + fromIcon.getWidth(null) / 2, - fromBuilding.getY() + fromIcon.getHeight(null) / 2, - toBuilding.getX() + toIcon.getWidth(null) / 2, - toBuilding.getY() + toIcon.getHeight(null) / 2 - ); - } - - private void paintComponentRoute(ComponentRoute route, Graphics g) { - ComponentType componentType = route.getComponent().getType(); - Image componentIcon = componentsIcons.get(componentType); - - route.updatePosition(); - - g.drawImage(componentIcon, route.getX(), route.getY(), null); - } - - private void loadBuildingImages(BuildingMetadata metadata) throws IOException { - Map statesIcons = new HashMap<>(); - for (BuildingState state : BuildingState.values()) { - statesIcons.put(state, loadBuildingImage(metadata, state)); - } - - buildingIcons.put(metadata.getType(), statesIcons); - } - - private Image loadBuildingImage(BuildingMetadata metadata, BuildingState state) throws IOException { - String iconPath = metadata.getIconsPaths().get(state); - return loadImage(iconPath); - } - - private Image loadImage(String path) throws IOException { - return ImageIO.read(new File(path)); + private Building getBuildingById(int id) { + return buildingsById.get(id); } } diff --git a/src/view/MainWindow.java b/src/view/MainWindow.java index e71f275..8448d86 100644 --- a/src/view/MainWindow.java +++ b/src/view/MainWindow.java @@ -10,7 +10,7 @@ public class MainWindow extends JFrame implements PropertyChangeListener { @Serial private static final long serialVersionUID = 1L; private static final String WINDOW_TITLE = "Laboratoire 1 : LOG121 - Simulation"; - private static final Dimension DIMENSION = new Dimension(700, 700); + public static final Dimension DIMENSION = new Dimension(700, 700); public MainWindow() { MainPanel mainPanel = new MainPanel(); diff --git a/src/view/StrategyPanel.java b/src/view/StrategyPanel.java index 0bb2cc1..f035e01 100644 --- a/src/view/StrategyPanel.java +++ b/src/view/StrategyPanel.java @@ -1,5 +1,11 @@ package view; +import configuration.SimulationConfigurationSingleton; +import simulation.RandomSellStrategy; +import simulation.SellStrategy; +import simulation.TimedSellStrategy; +import simulation.Warehouse; + import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; @@ -11,8 +17,8 @@ public class StrategyPanel extends JPanel { private static final long serialVersionUID = 1L; public StrategyPanel() { - JRadioButton strategy1 = new JRadioButton("Stratégie 1"); - JRadioButton strategy2 = new JRadioButton("Stratégie 2"); + JRadioButton strategy1 = new JRadioButton("Interval"); + JRadioButton strategy2 = new JRadioButton("Aléatoire"); ButtonGroup buttonGroup = new ButtonGroup(); buttonGroup.add(strategy1); @@ -20,8 +26,9 @@ public class StrategyPanel extends JPanel { JButton confirmButton = new JButton("Confirmer"); confirmButton.addActionListener((ActionEvent e) -> { - // TODO - Appeler la bonne stratégie - System.out.println(getSelectedButtonText(buttonGroup)); + String selectedStrategyName = getSelectedButtonText(buttonGroup); + onStrategyChange(selectedStrategyName); + // Fermer la fenêtre du component SwingUtilities.getWindowAncestor((Component) e.getSource()).dispose(); }); @@ -54,4 +61,13 @@ public class StrategyPanel extends JPanel { return null; } + + private void onStrategyChange(String strategyName) { + SellStrategy strategy = strategyName.equalsIgnoreCase("Interval") ? + new TimedSellStrategy() : + new RandomSellStrategy(); + + Warehouse warehouse = SimulationConfigurationSingleton.getInstance().getConfiguration().getSimulationData().getWarehouse(); + warehouse.setSellStrategy(strategy); + } } diff --git a/src/view/WindowMenu.java b/src/view/WindowMenu.java index 0a84b3c..1475140 100644 --- a/src/view/WindowMenu.java +++ b/src/view/WindowMenu.java @@ -57,7 +57,6 @@ public class WindowMenu extends JMenuBar { mainPanel.reloadSimulation(); } catch (ConfigurationParsingException ex) { - // TODO WN: Handle exception throw new RuntimeException("Failed to parse config file", ex); } } @@ -82,7 +81,6 @@ public class WindowMenu extends JMenuBar { chooseMenu.addActionListener((ActionEvent e) -> { // Ouvrir la fenêtre de sélection - // TODO - Récupérer la bonne stratégie de vente new StrategyWindow(); }); diff --git a/src/view/components/BuildingIcon.java b/src/view/components/BuildingIcon.java new file mode 100644 index 0000000..46afb42 --- /dev/null +++ b/src/view/components/BuildingIcon.java @@ -0,0 +1,81 @@ +package view.components; + +import metadata.BuildingMetadata; +import simulation.Building; +import simulation.BuildingState; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class BuildingIcon extends JComponent { + private static final Map> loadedImages = new HashMap<>(); + + private final Building building; + private final Map images; + + public BuildingIcon(Building building) { + this.building = building; + this.images = getImages(building.getMetadata()); + + setBounds(); + } + + @Override + protected void paintComponent(Graphics g) { + Image image = getCurrentStateImage(); + + g.drawImage(image, 0, 0, null); + } + + public Image getCurrentStateImage() { + return images.get(building.getState()); + } + + private void setBounds() { + // Présume que toutes les icônes ont les mêmes dimensions + Image image = images.get(BuildingState.EMPTY); + + int x = building.getX(); + int y = building.getY(); + int width = image.getWidth(null); + int height = image.getHeight(null); + + setBounds(x, y, width, height); + } + + private static Map getImages(BuildingMetadata metadata) { + if (loadedImages.containsKey(metadata.getType())) { + return loadedImages.get(metadata.getType()); + } + + return loadImages(metadata); + } + + private static Map loadImages(BuildingMetadata metadata) { + Map images = new HashMap<>(); + + for (BuildingState state : BuildingState.values()) { + String path = metadata.getIconsPaths().get(state); + Image image = loadImage(path); + + images.put(state, image); + } + + loadedImages.put(metadata.getType(), images); + return images; + } + + private static Image loadImage(String path) { + try { + File file = new File(path); + return ImageIO.read(file); + } catch (IOException ex) { + throw new RuntimeException("Could not load building icon", ex); + } + } +} diff --git a/src/view/components/ComponentIcon.java b/src/view/components/ComponentIcon.java new file mode 100644 index 0000000..48e5a21 --- /dev/null +++ b/src/view/components/ComponentIcon.java @@ -0,0 +1,91 @@ +package view.components; + +import simulation.ComponentType; +import simulation.Component; +import view.MainWindow; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class ComponentIcon extends JComponent { + private static final Map loadedImages = new HashMap<>(); + private static final int SPEED = 2; + + private final Component component; + private final int destinationId; + private final Point position; + private final Point destination; + private final Point direction; + private final Image image; + + public ComponentIcon(Component component, int destinationId, Point source, Point destination) { + this.component = component; + this.destinationId = destinationId; + this.position = source; + this.destination = destination; + + this.direction = getDirection(); + this.image = getImage(component.getType()); + + setBounds(0, 0, MainWindow.DIMENSION.width, MainWindow.DIMENSION.height); + } + + @Override + public void paintComponent(Graphics g) { + position.translate(direction.x, direction.y); + + g.translate(position.x, position.y); + g.drawImage(image, 0, 0, null); + } + + public boolean isAtDestination() { + return position.equals(destination); + } + + public Component getComponent() { + return component; + } + + public int getDestinationId() { + return destinationId; + } + + private Point getDirection() { + return new Point( + normalizedDirection(position.x, destination.x), + normalizedDirection(position.y, destination.y) + ); + } + + private static int normalizedDirection(int from, int to) { + return Integer.compare(to, from) * SPEED; + } + + private static Image getImage(ComponentType type) { + if (loadedImages.containsKey(type)) { + return loadedImages.get(type); + } + + return loadImage(type); + } + + private static Image loadImage(ComponentType type) { + try { + String path = String.format("src/ressources/%s.png", type.getTypeName()); + File file = new File(path); + Image image = ImageIO.read(file); + + // Garde une référence à l'image chargée pour pouvoir la réutiliser la prochaine fois + loadedImages.put(type, image); + + return image; + } catch (IOException ex) { + throw new RuntimeException("Could not load component icon", ex); + } + } +} diff --git a/src/view/components/RouteLine.java b/src/view/components/RouteLine.java new file mode 100644 index 0000000..0ca54b4 --- /dev/null +++ b/src/view/components/RouteLine.java @@ -0,0 +1,47 @@ +package view.components; + +import simulation.Building; + +import javax.swing.*; +import java.awt.*; + +public class RouteLine extends JComponent { + private final Point from; + private final Point to; + + public RouteLine(Building fromBuilding, Building toBuilding, int offset) { + from = new Point(fromBuilding.getX(), fromBuilding.getY()); + to = new Point(toBuilding.getX(), toBuilding.getY()); + + setBounds(from, to, offset); + + int offsetFromOriginX = Math.min(from.x, to.x); + int offsetFromOriginY = Math.min(from.y, to.y); + + from.translate(-offsetFromOriginX, -offsetFromOriginY); + to.translate(-offsetFromOriginX, -offsetFromOriginY); + } + + @Override + protected void paintComponent(Graphics g) { + g.drawLine(from.x, from.y, to.x, to.y); + } + + private void setBounds(Point from, Point to, int offset) { + int x = Math.min(from.x, to.x); + int y = Math.min(from.y, to.y); + int width = Math.abs(to.x - from.x); + int height = Math.abs(to.y - from.y); + + // Si une des dimensions est zéro, la ligne n'apparaîtra pas. + if (width == 0) { + width = 1; + } + + if (height == 0) { + height = 1; + } + + setBounds(x + offset, y + offset, width, height); + } +}