package application.simulator;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.view.mxGraph;
import models.dataConstraintModel.ResourcePath;
import simulator.Resource;
import simulator.ResourceIdentifier;
import simulator.Simulator;
import simulator.SystemState;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class SimulationLayout {
// Hold the division level for each resource
HashMap<ResourceIdentifier, Integer> divisionLevelMap = new HashMap<>();
// Hold the scaling factor for each resource
HashMap<ResourceIdentifier, Double> resourceScaleMap = new HashMap<>();
int maxDivisionLevel = 1;
public final double BASE_WIDTH = 720;
public final double BASE_HEIGHT = BASE_WIDTH / 2;
public final double MARGIN_SCALE = 0.92;
SystemState systemState;
public SimulationLayout(SystemState systemState) {
this.systemState = systemState;
// Calculate the division level for each resource
for (Resource res : systemState.getRootResources()) {
setDivisionLevel(divisionLevelMap, res.getResourceIdentifier(), 1);
}
// Calculate the scaling factor for each resource
for (ResourceIdentifier res : divisionLevelMap.keySet()) {
double scale = (double) maxDivisionLevel / divisionLevelMap.get(res);
resourceScaleMap.put(res, scale);
}
}
public double getScale(ResourceIdentifier resource) {
return resourceScaleMap.get(resource);
}
public double getDivision(ResourceIdentifier resource) {
return divisionLevelMap.get(resource);
}
public int getMaxDivisionLevel() {
return maxDivisionLevel;
}
public boolean isExistResource(ResourceIdentifier resourceIdentifier) {
return divisionLevelMap.containsKey(resourceIdentifier);
}
private void setDivisionLevel(HashMap<ResourceIdentifier, Integer> resourceSet, ResourceIdentifier resource, int childCount) {
int divisionLevel;
if (resourceSet.get(resource.getParent()) == null) {
divisionLevel = 1;
} else {
divisionLevel = resourceSet.get(resource.getParent()) * childCount;
}
if (divisionLevel > maxDivisionLevel) maxDivisionLevel = divisionLevel;
resourceSet.put(resource, divisionLevel);
Collection<Resource> identifiers = systemState.getResource(resource).getChildren();
if (identifiers != null) {
for (Resource child : identifiers) {
setDivisionLevel(resourceSet, child.getResourceIdentifier(), identifiers.size());
}
}
}
/**
*
* Draw an object corresponding to the target resource on the mxGraph.
*
* @param graph The mxGraph for drawing.
* @param parent The parent object of the target resource.
* @param resourceIdentifier The ID of the target resource.
* @param index The index.
* @param length The number of sibling resources of the target resource.
* @return The drawing object of the target resource.
*/
private Object setLayout(mxGraph graph, Object parent, ResourceIdentifier resourceIdentifier, double index, double length) {
double width, height, x, y;
double parentWidth = graph.getCellGeometry(parent).getWidth();
double parentHeight = graph.getCellGeometry(parent).getHeight();
width = parentWidth / length * MARGIN_SCALE;
height = width / 2;
x = parentWidth * (index - 1) / length + (parentWidth / length) * (1 - MARGIN_SCALE) / 2;
// Process to avoid hiding the parent's resource name
if ((int) length == 1) {
y = 20;
height -= 20;
} else {
y = parentHeight / 2 - height / 2;
}
Object result = graph.insertVertex(parent, null,
resourceIdentifier.toString(), x, y, width, height,
"shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;");
System.out.println(result);
return result;
}
public mxGraph constructSimulateGraph(mxGraph graph, Simulator simulator) {
((mxGraphModel) graph.getModel()).clear();
Object parent = graph.getDefaultParent();
graph.getModel().beginUpdate();
try {
Map<Resource, Object> resources = new HashMap<>();
int i = 0;
int childCount = simulator.getCurState().getRootResources().size();
// create resource vertices
for (Resource resNode : simulator.getCurState().getRootResources()) {
double scale = this.getScale(resNode.getResourceIdentifier());
double maxDiv = this.getMaxDivisionLevel();
double w = this.BASE_WIDTH * scale / maxDiv / childCount;
double h = this.BASE_HEIGHT * scale / maxDiv / childCount;
double x = w * i + 50;
double y = 20;
ResourcePath res = resNode.getResourceIdentifier();
Object resource = graph.insertVertex(parent, null,
res.toString(), x, y, w, h,
"shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex
resources.put(resNode, resource);
i++;
createNextChildSimulateResourceVerticies(graph, resource, resNode, resources);
}
} finally {
graph.getModel().endUpdate();
}
return graph;
}
private void createNextChildSimulateResourceVerticies(mxGraph graph, Object parent, Resource resNode, Map<Resource, Object> resources) { //sample
Collection<Resource> children = resNode.getChildren();
if (children != null) {
//List<Resource> children = resNode.getChildren();
double i = 1;
double childCount = children.size();
for (Resource childNode : children) {
Object childResource = setLayout(graph, parent, childNode.getResourceIdentifier(), i, childCount);
resources.put(childNode, childResource);
i++;
createNextChildSimulateResourceVerticies(graph, childResource, childNode, resources);
}
}
if (children == null || children.size() == 0) {
Object state = graph.insertVertex(parent, null,
resNode.getState().getValue().toString(), 0.5, 0.5, 0.25, 0.25, "opacity=0;verticalAlign=down;", true); // insert a state label as a vertex
}
}
}