Fully working app
This commit is contained in:
parent
6f1a3b91aa
commit
d4af46450c
|
@ -1,7 +1,9 @@
|
||||||
package configuration;
|
package configuration;
|
||||||
|
|
||||||
|
import metadata.BuildingMetadata;
|
||||||
import simulation.Building;
|
import simulation.Building;
|
||||||
import simulation.Route;
|
import simulation.Route;
|
||||||
|
import simulation.Warehouse;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@ -21,4 +23,10 @@ public class SimulationData {
|
||||||
public Collection<Route> getRoutes() {
|
public Collection<Route> getRoutes() {
|
||||||
return routes;
|
return routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Warehouse getWarehouse() {
|
||||||
|
return (Warehouse) buildings.stream()
|
||||||
|
.filter(b -> b.getType().equals(BuildingMetadata.TYPE_WAREHOUSE))
|
||||||
|
.findFirst().get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,9 +162,24 @@ public class XmlConfigurationParser implements ConfigurationParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subscribeFactories(buildings);
|
||||||
return new SimulationData(buildings, routes);
|
return new SimulationData(buildings, routes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void subscribeFactories(Collection<Building> 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<String, BuildingMetadata> buildingMetadata) {
|
private Building parseBuilding(Element buildingElement, Map<String, BuildingMetadata> buildingMetadata) {
|
||||||
String type = buildingElement.getAttribute(ATTRIBUTE_TYPE);
|
String type = buildingElement.getAttribute(ATTRIBUTE_TYPE);
|
||||||
int id = Integer.parseInt(buildingElement.getAttribute(ATTRIBUTE_ID));
|
int id = Integer.parseInt(buildingElement.getAttribute(ATTRIBUTE_ID));
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<usine type="usine-matiere" id="11" x="32" y="32"/>
|
<usine type="usine-matiere" id="11" x="32" y="32"/>
|
||||||
<usine type="usine-aile" id="21" x="320" y="32"/>
|
<usine type="usine-aile" id="21" x="320" y="32"/>
|
||||||
<usine type="usine-assemblage" id="41" x="160" y="192"/>
|
<usine type="usine-assemblage" id="41" x="160" y="192"/>
|
||||||
<usine type="entrepot" id="51" x="440" y="192"/> <!-- x=640 -->
|
<usine type="entrepot" id="51" x="640" y="192"/>
|
||||||
<usine type="usine-matiere" id="13" x="544" y="576"/>
|
<usine type="usine-matiere" id="13" x="544" y="576"/>
|
||||||
<usine type="usine-matiere" id="12" x="96" y="352"/>
|
<usine type="usine-matiere" id="12" x="96" y="352"/>
|
||||||
<usine type="usine-moteur" id="31" x="320" y="352"/>
|
<usine type="usine-moteur" id="31" x="320" y="352"/>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package simulation;
|
package simulation;
|
||||||
|
|
||||||
|
import metadata.BuildingMetadata;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public abstract class Building {
|
public abstract class Building {
|
||||||
|
@ -18,6 +20,7 @@ public abstract class Building {
|
||||||
|
|
||||||
public abstract void processInput(Component input);
|
public abstract void processInput(Component input);
|
||||||
public abstract Optional<Component> update();
|
public abstract Optional<Component> update();
|
||||||
|
public abstract BuildingMetadata getMetadata();
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|
|
@ -15,6 +15,10 @@ public enum ComponentType {
|
||||||
this.typeName = typeName;
|
this.typeName = typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getTypeName() {
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
|
||||||
public static ComponentType getForTypeName(String typeName) {
|
public static ComponentType getForTypeName(String typeName) {
|
||||||
return Arrays.stream(ComponentType.values())
|
return Arrays.stream(ComponentType.values())
|
||||||
.filter(t -> Objects.equals(t.typeName, typeName))
|
.filter(t -> Objects.equals(t.typeName, typeName))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package simulation;
|
package simulation;
|
||||||
|
|
||||||
|
import metadata.BuildingMetadata;
|
||||||
import metadata.FactoryInput;
|
import metadata.FactoryInput;
|
||||||
import metadata.FactoryMetadata;
|
import metadata.FactoryMetadata;
|
||||||
|
|
||||||
|
@ -7,11 +8,12 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class Factory extends Building {
|
public class Factory extends Building implements WarehouseObserver {
|
||||||
private final FactoryMetadata metadata;
|
private final FactoryMetadata metadata;
|
||||||
private final Map<ComponentType, Integer> inputsCount = new HashMap<>();
|
private final Map<ComponentType, Integer> inputsCount = new HashMap<>();
|
||||||
|
|
||||||
private boolean isProductionStarted;
|
private boolean isProductionStarted;
|
||||||
|
private boolean warehouseFull;
|
||||||
private long productionTicks = 0L;
|
private long productionTicks = 0L;
|
||||||
|
|
||||||
public Factory(int id, String type, int x, int y, FactoryMetadata metadata) {
|
public Factory(int id, String type, int x, int y, FactoryMetadata metadata) {
|
||||||
|
@ -27,20 +29,11 @@ public class Factory extends Building {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Component> update() {
|
public Optional<Component> update() {
|
||||||
if (!isProductionStarted) return Optional.empty();
|
if (!isProductionStarted || (productionTicks == 0 && warehouseFull)) return Optional.empty();
|
||||||
|
|
||||||
productionTicks++;
|
productionTicks++;
|
||||||
float productionInterval = metadata.getProductionInterval();
|
if (productionTicks < metadata.getProductionInterval()) {
|
||||||
if (productionTicks < productionInterval) {
|
updateState();
|
||||||
|
|
||||||
if (productionTicks / productionInterval >= (2 / 3f)) {
|
|
||||||
state = BuildingState.FULL;
|
|
||||||
} else if (productionTicks / productionInterval >= (1 / 3f)) {
|
|
||||||
state = BuildingState.TWO_THIRD;
|
|
||||||
} else {
|
|
||||||
state = BuildingState.ONE_THIRD;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.empty();
|
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() {
|
private boolean hasEnoughComponents() {
|
||||||
for (FactoryInput input : metadata.getInputs()) {
|
for (FactoryInput input : metadata.getInputs()) {
|
||||||
if (!inputsCount.containsKey(input.getType()) || inputsCount.get(input.getType()) < input.getQuantity()) {
|
if (!inputsCount.containsKey(input.getType()) || inputsCount.get(input.getType()) < input.getQuantity()) {
|
||||||
|
@ -94,16 +108,12 @@ public class Factory extends Building {
|
||||||
|
|
||||||
if (!inputsCount.isEmpty()) {
|
if (!inputsCount.isEmpty()) {
|
||||||
for (ComponentType type : inputsCount.keySet()) {
|
for (ComponentType type : inputsCount.keySet()) {
|
||||||
builder.append(type.toString())
|
builder.append(type.toString()).append(": ").append(inputsCount.get(type));
|
||||||
.append(": ")
|
|
||||||
.append(inputsCount.get(type));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isProductionStarted) {
|
if (isProductionStarted) {
|
||||||
builder.append(" (interval: ")
|
builder.append(" (interval: ").append(productionTicks).append(')');
|
||||||
.append(productionTicks)
|
|
||||||
.append(')');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(']');
|
builder.append(']');
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package simulation;
|
||||||
|
|
||||||
|
public interface SellStrategy {
|
||||||
|
boolean shouldSell();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
package simulation;
|
package simulation;
|
||||||
|
|
||||||
import configuration.SimulationConfiguration;
|
|
||||||
import configuration.SimulationConfigurationSingleton;
|
|
||||||
import metadata.BuildingMetadata;
|
import metadata.BuildingMetadata;
|
||||||
import metadata.WarehouseMetadata;
|
import metadata.WarehouseMetadata;
|
||||||
|
|
||||||
|
@ -11,26 +9,62 @@ import java.util.Optional;
|
||||||
|
|
||||||
public class Warehouse extends Building implements WarehouseSubject {
|
public class Warehouse extends Building implements WarehouseSubject {
|
||||||
private final WarehouseMetadata metadata;
|
private final WarehouseMetadata metadata;
|
||||||
|
private final int capacity;
|
||||||
private final Collection<WarehouseObserver> observers = new ArrayList<>();
|
private final Collection<WarehouseObserver> observers = new ArrayList<>();
|
||||||
|
|
||||||
|
private SellStrategy sellStrategy = new TimedSellStrategy();
|
||||||
private int planeCount = 0;
|
private int planeCount = 0;
|
||||||
|
|
||||||
public Warehouse(int id, String type, int x, int y, WarehouseMetadata metadata) {
|
public Warehouse(int id, String type, int x, int y, WarehouseMetadata metadata) {
|
||||||
super(id, type, x, y);
|
super(id, type, x, y);
|
||||||
|
|
||||||
this.metadata = metadata;
|
this.metadata = metadata;
|
||||||
|
this.capacity = metadata.getInput().getCapacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Component> update() {
|
public Optional<Component> update() {
|
||||||
|
if (planeCount > 0 && sellStrategy.shouldSell()) {
|
||||||
|
sellPlane();
|
||||||
|
}
|
||||||
|
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processInput(Component input) {
|
public void processInput(Component input) {
|
||||||
planeCount++;
|
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) {
|
public void attach(WarehouseObserver observer) {
|
||||||
|
@ -42,20 +76,13 @@ public class Warehouse extends Building implements WarehouseSubject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyObservers() {
|
public void notifyObservers() {
|
||||||
boolean isFull = planeCount >= getCapacity();
|
boolean isFull = planeCount >= capacity;
|
||||||
observers.forEach(o -> o.update(isFull));
|
observers.forEach(o -> o.updateWarehouseState(isFull));
|
||||||
}
|
|
||||||
|
|
||||||
private static int getCapacity() {
|
|
||||||
SimulationConfiguration configuration = SimulationConfigurationSingleton.getInstance().getConfiguration();
|
|
||||||
WarehouseMetadata metadata = (WarehouseMetadata) configuration.getMetadata().get(BuildingMetadata.TYPE_WAREHOUSE);
|
|
||||||
|
|
||||||
return metadata.getInput().getCapacity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
boolean isFull = planeCount >= getCapacity();
|
boolean isFull = planeCount >= capacity;
|
||||||
|
|
||||||
return String.format("[planes: %d, full: %s]", planeCount, isFull);
|
return String.format("[planes: %d, full: %s]", planeCount, isFull);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,5 @@ public interface WarehouseObserver {
|
||||||
*
|
*
|
||||||
* @param full Si l'entrepot est plein ou non
|
* @param full Si l'entrepot est plein ou non
|
||||||
*/
|
*/
|
||||||
void update(boolean full);
|
void updateWarehouseState(boolean full);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,59 +3,67 @@ package view;
|
||||||
import configuration.SimulationConfiguration;
|
import configuration.SimulationConfiguration;
|
||||||
import configuration.SimulationConfigurationSingleton;
|
import configuration.SimulationConfigurationSingleton;
|
||||||
import configuration.SimulationData;
|
import configuration.SimulationData;
|
||||||
import metadata.BuildingMetadata;
|
import simulation.Building;
|
||||||
import simulation.Component;
|
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 javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Serial;
|
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
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final Map<String, Map<BuildingState, Image>> buildingIcons = new HashMap<>();
|
|
||||||
private final Map<ComponentType, Image> componentsIcons = new HashMap<>();
|
|
||||||
private final Map<Integer, Building> buildingsById = new HashMap<>();
|
private final Map<Integer, Building> buildingsById = new HashMap<>();
|
||||||
private final Collection<ComponentRoute> componentRoutes = new ArrayList<>();
|
private final Collection<ComponentIcon> componentIcons = new ArrayList<>();
|
||||||
|
|
||||||
private SimulationConfiguration configuration;
|
private SimulationConfiguration configuration;
|
||||||
private SimulationData simulationData;
|
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() {
|
public void reloadSimulation() {
|
||||||
configuration = SimulationConfigurationSingleton.getInstance().getConfiguration();
|
configuration = SimulationConfigurationSingleton.getInstance().getConfiguration();
|
||||||
simulationData = configuration.getSimulationData();
|
simulationData = configuration.getSimulationData();
|
||||||
|
|
||||||
buildingsById.clear();
|
buildingsById.clear();
|
||||||
simulationData.getBuildings().forEach(b -> buildingsById.put(b.getId(), b));
|
|
||||||
|
|
||||||
buildingIcons.clear();
|
int routesOffset = 0;
|
||||||
configuration.getMetadata().values().forEach(m -> {
|
for (Building building : simulationData.getBuildings()) {
|
||||||
try {
|
BuildingIcon icon = this.initializeBuilding(building);
|
||||||
loadBuildingImages(m);
|
routesOffset = icon.getWidth() / 2;
|
||||||
} catch (IOException e) {
|
}
|
||||||
System.err.println("Could not load simulation icons");
|
|
||||||
}
|
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
|
@Override
|
||||||
public void paint(Graphics g) {
|
public void paint(Graphics g) {
|
||||||
|
@ -63,30 +71,29 @@ public class MainPanel extends JPanel {
|
||||||
|
|
||||||
if (configuration == null) return;
|
if (configuration == null) return;
|
||||||
|
|
||||||
simulationData.getRoutes().forEach(r -> paintRoute(r, g));
|
updateBuildings();
|
||||||
simulationData.getBuildings().forEach(b -> paintBuilding(b, g));
|
|
||||||
|
|
||||||
Collection<ComponentRoute> removedComponentRoutes = new ArrayList<>();
|
Collection<ComponentIcon> removedIcons = new ArrayList<>();
|
||||||
for (ComponentRoute route : componentRoutes) {
|
|
||||||
paintComponentRoute(route, g);
|
|
||||||
|
|
||||||
if (route.isTransitFinished()) {
|
for (ComponentIcon icon : componentIcons) {
|
||||||
removedComponentRoutes.add(route);
|
if (icon.isAtDestination()) {
|
||||||
route.sendToOutputBuilding();
|
removedIcons.add(icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent concurrency problems
|
removedIcons.forEach(i -> {
|
||||||
componentRoutes.removeAll(removedComponentRoutes);
|
componentIcons.remove(i);
|
||||||
|
remove(i);
|
||||||
|
|
||||||
|
Building outputBuilding = getBuildingById(i.getDestinationId());
|
||||||
|
outputBuilding.processInput(i.getComponent());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void paintBuilding(Building building, Graphics g) {
|
private void updateBuildings() {
|
||||||
building.update().ifPresent(component -> addComponentRoute(component, building));
|
simulationData.getBuildings().forEach(b -> {
|
||||||
|
b.update().ifPresent(c -> addComponentRoute(c, b));
|
||||||
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 addComponentRoute(Component component, Building fromBuilding) {
|
private void addComponentRoute(Component component, Building fromBuilding) {
|
||||||
|
@ -95,48 +102,17 @@ public class MainPanel extends JPanel {
|
||||||
.findFirst().get();
|
.findFirst().get();
|
||||||
|
|
||||||
Building toBuilding = buildingsById.get(route.getTo());
|
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) {
|
private Building getBuildingById(int id) {
|
||||||
Building fromBuilding = buildingsById.get(route.getFrom());
|
return buildingsById.get(id);
|
||||||
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<BuildingState, Image> 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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class MainWindow extends JFrame implements PropertyChangeListener {
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final String WINDOW_TITLE = "Laboratoire 1 : LOG121 - Simulation";
|
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() {
|
public MainWindow() {
|
||||||
MainPanel mainPanel = new MainPanel();
|
MainPanel mainPanel = new MainPanel();
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
package view;
|
package view;
|
||||||
|
|
||||||
|
import configuration.SimulationConfigurationSingleton;
|
||||||
|
import simulation.RandomSellStrategy;
|
||||||
|
import simulation.SellStrategy;
|
||||||
|
import simulation.TimedSellStrategy;
|
||||||
|
import simulation.Warehouse;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
@ -11,8 +17,8 @@ public class StrategyPanel extends JPanel {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public StrategyPanel() {
|
public StrategyPanel() {
|
||||||
JRadioButton strategy1 = new JRadioButton("Stratégie 1");
|
JRadioButton strategy1 = new JRadioButton("Interval");
|
||||||
JRadioButton strategy2 = new JRadioButton("Stratégie 2");
|
JRadioButton strategy2 = new JRadioButton("Aléatoire");
|
||||||
|
|
||||||
ButtonGroup buttonGroup = new ButtonGroup();
|
ButtonGroup buttonGroup = new ButtonGroup();
|
||||||
buttonGroup.add(strategy1);
|
buttonGroup.add(strategy1);
|
||||||
|
@ -20,8 +26,9 @@ public class StrategyPanel extends JPanel {
|
||||||
|
|
||||||
JButton confirmButton = new JButton("Confirmer");
|
JButton confirmButton = new JButton("Confirmer");
|
||||||
confirmButton.addActionListener((ActionEvent e) -> {
|
confirmButton.addActionListener((ActionEvent e) -> {
|
||||||
// TODO - Appeler la bonne stratégie
|
String selectedStrategyName = getSelectedButtonText(buttonGroup);
|
||||||
System.out.println(getSelectedButtonText(buttonGroup));
|
onStrategyChange(selectedStrategyName);
|
||||||
|
|
||||||
// Fermer la fenêtre du component
|
// Fermer la fenêtre du component
|
||||||
SwingUtilities.getWindowAncestor((Component) e.getSource()).dispose();
|
SwingUtilities.getWindowAncestor((Component) e.getSource()).dispose();
|
||||||
});
|
});
|
||||||
|
@ -54,4 +61,13 @@ public class StrategyPanel extends JPanel {
|
||||||
|
|
||||||
return null;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,6 @@ public class WindowMenu extends JMenuBar {
|
||||||
|
|
||||||
mainPanel.reloadSimulation();
|
mainPanel.reloadSimulation();
|
||||||
} catch (ConfigurationParsingException ex) {
|
} catch (ConfigurationParsingException ex) {
|
||||||
// TODO WN: Handle exception
|
|
||||||
throw new RuntimeException("Failed to parse config file", ex);
|
throw new RuntimeException("Failed to parse config file", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +81,6 @@ public class WindowMenu extends JMenuBar {
|
||||||
|
|
||||||
chooseMenu.addActionListener((ActionEvent e) -> {
|
chooseMenu.addActionListener((ActionEvent e) -> {
|
||||||
// Ouvrir la fenêtre de sélection
|
// Ouvrir la fenêtre de sélection
|
||||||
// TODO - Récupérer la bonne stratégie de vente
|
|
||||||
new StrategyWindow();
|
new StrategyWindow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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<String, Map<BuildingState, Image>> loadedImages = new HashMap<>();
|
||||||
|
|
||||||
|
private final Building building;
|
||||||
|
private final Map<BuildingState, Image> 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<BuildingState, Image> getImages(BuildingMetadata metadata) {
|
||||||
|
if (loadedImages.containsKey(metadata.getType())) {
|
||||||
|
return loadedImages.get(metadata.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadImages(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<BuildingState, Image> loadImages(BuildingMetadata metadata) {
|
||||||
|
Map<BuildingState, Image> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ComponentType, Image> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue