Control.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. package holeg.ui.controller;
  2. import static holeg.serialize.ModelDeserializer.gson;
  3. import holeg.model.AbstractCanvasObject;
  4. import holeg.model.Edge;
  5. import holeg.model.GroupNode;
  6. import holeg.model.HolonElement;
  7. import holeg.model.HolonObject;
  8. import holeg.model.HolonSwitch;
  9. import holeg.model.Model;
  10. import holeg.preferences.ImagePreference;
  11. import holeg.preferences.PreferenceKeys;
  12. import holeg.serialize.CategoryAdapter;
  13. import holeg.ui.model.GuiSettings;
  14. import holeg.ui.model.IdCounter;
  15. import holeg.ui.model.UndoHistory;
  16. import holeg.ui.view.category.Category;
  17. import holeg.utility.events.Action;
  18. import holeg.utility.events.Event;
  19. import holeg.utility.math.vector.Vec2i;
  20. import java.io.File;
  21. import java.io.FileReader;
  22. import java.io.FileWriter;
  23. import java.io.IOException;
  24. import java.util.ArrayList;
  25. import java.util.Arrays;
  26. import java.util.Collection;
  27. import java.util.HashMap;
  28. import java.util.HashSet;
  29. import java.util.List;
  30. import java.util.Optional;
  31. import java.util.Set;
  32. import java.util.logging.Logger;
  33. import java.util.prefs.Preferences;
  34. /**
  35. * The Class represents the controller.
  36. */
  37. public class Control {
  38. private static final Logger log = Logger.getLogger(Control.class.getName());
  39. private static final Preferences prefs = Preferences.userNodeForPackage(Control.class);
  40. private final CanvasController canvasController;
  41. private final SimulationManager simulationManager;
  42. public Event OnCategoryChanged = new Event();
  43. public Event OnSelectionChanged = new Event();
  44. public Event OnCanvasUpdate = new Event();
  45. public Action<Boolean> OnGuiSetEnabled = new Action<>();
  46. public Action<GroupNode> OnShowGroupNode = new Action<>();
  47. public Action<GroupNode> OnRemoveGroupNode = new Action<>();
  48. public Event OnModelChanged = new Event();
  49. private Model model;
  50. public Control(Model model) {
  51. this.model = model;
  52. this.canvasController = new CanvasController(this);
  53. this.simulationManager = new SimulationManager(this);
  54. OnCanvasUpdate.addListener(() -> UndoHistory.addSave(saveModelToJsonString()));
  55. UndoHistory.addSave(saveModelToJsonString());
  56. }
  57. /* Operations for Categories and Objects */
  58. /**
  59. * init default category and objects.
  60. */
  61. public void resetCategories() {
  62. GuiSettings.getCategories().clear();
  63. initCategories();
  64. saveCategories();
  65. }
  66. /**
  67. * Add new Holon Object to a Category.
  68. */
  69. public void addObject(Category cat, String obj, List<HolonElement> list, String img) {
  70. addNewHolonObject(cat, obj, list, img);
  71. OnCategoryChanged.broadcast();
  72. saveCategories();
  73. }
  74. public void deleteCategory(Category category) {
  75. GuiSettings.getCategories().remove(category);
  76. OnCategoryChanged.broadcast();
  77. saveCategories();
  78. }
  79. /**
  80. * removes a selectedObject from selection.
  81. */
  82. public void removeObjectsFromSelection(Collection<AbstractCanvasObject> objects) {
  83. if (GuiSettings.getSelectedObjects().removeAll(objects)) {
  84. OnSelectionChanged.broadcast();
  85. }
  86. }
  87. /**
  88. * removes a selectedObject from selection.
  89. *
  90. * @param obj
  91. */
  92. public void removeObjectFromSelection(AbstractCanvasObject obj) {
  93. if (GuiSettings.getSelectedObjects().remove(obj)) {
  94. OnSelectionChanged.broadcast();
  95. }
  96. }
  97. public void addObjectToSelection(AbstractCanvasObject obj) {
  98. if (GuiSettings.getSelectedObjects().add(obj)) {
  99. OnSelectionChanged.broadcast();
  100. }
  101. }
  102. public void addObjectsToSelection(Collection<AbstractCanvasObject> objects) {
  103. if (GuiSettings.getSelectedObjects().addAll(objects)) {
  104. OnSelectionChanged.broadcast();
  105. }
  106. }
  107. public void setSelection(Collection<AbstractCanvasObject> objects) {
  108. GuiSettings.getSelectedObjects().clear();
  109. addObjectsToSelection(objects);
  110. }
  111. public void clearSelection() {
  112. if (!GuiSettings.getSelectedObjects().isEmpty()) {
  113. GuiSettings.getSelectedObjects().clear();
  114. OnSelectionChanged.broadcast();
  115. }
  116. }
  117. public void addObjectOnCanvas(GroupNode node, AbstractCanvasObject object) {
  118. canvasController.addObject(node, object);
  119. updateStateForIteration(model.getCurrentIteration());
  120. }
  121. /**
  122. * Deletes an CpsObject on the Canvas and its connections.
  123. *
  124. * @param obj AbstractCpsObject
  125. */
  126. public void deleteObject(AbstractCanvasObject obj) {
  127. canvasController.deleteObject(obj);
  128. if (obj instanceof GroupNode groupnode) {
  129. canvasController.deleteAllObjectsInGroupNode(groupnode);
  130. }
  131. calculateStateForCurrentIteration();
  132. OnCanvasUpdate.broadcast();
  133. }
  134. public void deleteCanvasObjects(Collection<AbstractCanvasObject> objects) {
  135. canvasController.deleteObjects(objects);
  136. calculateStateForCurrentIteration();
  137. OnCanvasUpdate.broadcast();
  138. }
  139. /**
  140. * Replaces {@code toBeReplaced} by {@code by} on the canvas
  141. *
  142. * @param toBeReplaced the object that will be replaced
  143. * @param by the object that will replace it
  144. */
  145. public void replaceCanvasObject(AbstractCanvasObject toBeReplaced, AbstractCanvasObject by) {
  146. canvasController.replaceObject(toBeReplaced, by);
  147. OnCanvasUpdate.broadcast();
  148. }
  149. /**
  150. * Add an edge to the Canvas.
  151. *
  152. * @param edge the edge
  153. */
  154. public boolean addEdgeOnCanvas(Edge edge) {
  155. if (doesEdgeExist(edge)) {
  156. return false;
  157. }
  158. canvasController.addEdgeOnCanvas(edge);
  159. OnCanvasUpdate.broadcast();
  160. return true;
  161. }
  162. public boolean doesEdgeExist(Edge edge) {
  163. boolean connectsToSelf = edge.getA() == edge.getB();
  164. return !connectsToSelf && model.getEdgesOnCanvas().stream()
  165. .anyMatch(e -> (e.getA() == edge.getA() && e.getB() == edge.getB())
  166. || (e.getB() == edge.getA() && e.getA() == edge.getB()));
  167. }
  168. /**
  169. * Add an edge to the Canvas.
  170. *
  171. * @param edge the edge
  172. */
  173. public void addEdgeOnCanvasOrRemoveExisting(Edge edge) {
  174. boolean connectsToSelf = edge.getA() == edge.getB();
  175. if (connectsToSelf) {
  176. return;
  177. }
  178. Optional<Edge> edgeToRemove = model.getEdgesOnCanvas().stream()
  179. .filter(e -> (e.getA() == edge.getA() && e.getB() == edge.getB())
  180. || (e.getB() == edge.getA() && e.getA() == edge.getB())).findAny();
  181. edgeToRemove.ifPresentOrElse(canvasController::removeEdgesOnCanvas,
  182. () -> canvasController.addEdgeOnCanvas(edge));
  183. updateStateForCurrentIteration();
  184. }
  185. public void calculateStateForCurrentIteration() {
  186. simulationManager.calculateStateForIteration(model.getCurrentIteration());
  187. }
  188. public void updateStateForCurrentIteration() {
  189. simulationManager.calculateStateForIteration(model.getCurrentIteration());
  190. OnCanvasUpdate.broadcast();
  191. }
  192. /**
  193. * calculates the flow of the edges and the supply for objects.
  194. *
  195. * @param x current Iteration
  196. */
  197. public void updateStateForIteration(int x) {
  198. simulationManager.calculateStateForIteration(x);
  199. OnCanvasUpdate.broadcast();
  200. }
  201. /**
  202. * resets the whole State of the simulation including a reset of all Edges to the default "is
  203. * working" state
  204. */
  205. public void resetSimulation() {
  206. model.reset();
  207. }
  208. /**
  209. * Getter for Model.
  210. *
  211. * @return the Model
  212. */
  213. public Model getModel() {
  214. return model;
  215. }
  216. /**
  217. * Copy all Selected Objects.
  218. */
  219. public void copy() {
  220. GuiSettings.getClipboardObjects().clear();
  221. GuiSettings.getClipboardObjects().addAll(GuiSettings.getSelectedObjects());
  222. GuiSettings.getClipboardEdges().clear();
  223. GuiSettings.getClipboardEdges()
  224. .addAll(getEdgesBetweenObjects(GuiSettings.getSelectedObjects()));
  225. }
  226. private List<Edge> getEdgesBetweenObjects(Collection<AbstractCanvasObject> selectedObjects) {
  227. return model.getEdgesOnCanvas().stream()
  228. .filter(edge -> selectedObjects.containsAll(Arrays.asList(edge.getA(), edge.getB())))
  229. .toList();
  230. }
  231. public void paste(GroupNode groupNode, Vec2i offset) {
  232. if (GuiSettings.getClipboardObjects().isEmpty()) {
  233. return;
  234. }
  235. HashMap<AbstractCanvasObject, AbstractCanvasObject> oldToNew = new HashMap<>();
  236. Collection<AbstractCanvasObject> copies = GuiSettings.getClipboardObjects().stream()
  237. .map(obj -> {
  238. AbstractCanvasObject copy = obj.copy();
  239. oldToNew.put(obj, copy);
  240. return copy;
  241. }).toList();
  242. copies.forEach(obj -> {
  243. obj.getPosition().addAssign(offset);
  244. obj.getPosition().clampX(0, GuiSettings.canvasSize.getX());
  245. obj.getPosition().clampY(0, GuiSettings.canvasSize.getY());
  246. });
  247. groupNode.addAll(copies);
  248. Collection<Edge> copyEdges = GuiSettings.getClipboardEdges().stream().map(
  249. edge -> new Edge(oldToNew.get(edge.getA()), oldToNew.get(edge.getB()), edge.maxCapacity))
  250. .toList();
  251. model.getEdgesOnCanvas().addAll(copyEdges);
  252. updateStateForCurrentIteration();
  253. OnSelectionChanged.broadcast();
  254. }
  255. public void cut() {
  256. copy();
  257. deleteCanvasObjects(GuiSettings.getSelectedObjects());
  258. clearSelection();
  259. }
  260. public void guiSetEnabled(boolean state) {
  261. OnGuiSetEnabled.broadcast(state);
  262. }
  263. /**
  264. * init default category and objects.
  265. */
  266. private void initCategories() {
  267. Category energy = createCategoryWithName("Energy");
  268. Category building = createCategoryWithName("Building");
  269. Category component = createCategoryWithName("Component");
  270. HolonObject powerPlant = addNewHolonObject(energy, "Power Plant", new ArrayList<>(),
  271. ImagePreference.Canvas.DefaultObject.PowerPlant);
  272. HolonObject house = addNewHolonObject(building, "House", new ArrayList<>(),
  273. ImagePreference.Canvas.DefaultObject.House);
  274. HolonSwitch sw = new HolonSwitch("Switch");
  275. component.getObjects().add(sw);
  276. powerPlant.add(new HolonElement(null, "Power", 10000));
  277. energy.getObjects().add(powerPlant);
  278. house.add(new HolonElement(null, "TV", -250));
  279. house.add(new HolonElement(null, "TV", -250));
  280. house.add(new HolonElement(null, "Fridge", -500));
  281. house.add(new HolonElement(null, "Radio", -100));
  282. house.add(new HolonElement(null, "PC", -250));
  283. house.add(new HolonElement(null, "PC", -250));
  284. house.add(new HolonElement(null, "PC", -250));
  285. house.add(new HolonElement(null, "Light", -50));
  286. house.add(new HolonElement(null, "Light", -50));
  287. house.add(new HolonElement(null, "Light", -50));
  288. house.add(new HolonElement(null, "Light", -50));
  289. house.add(new HolonElement(null, "Light", -50));
  290. house.add(new HolonElement(null, "Solar Panel", 300));
  291. building.getObjects().add(house);
  292. OnCategoryChanged.broadcast();
  293. }
  294. public Category createCategoryWithName(String categoryName) {
  295. Optional<Category> category = findCategoryWithName(categoryName);
  296. if (category.isEmpty()) {
  297. Category cat = new Category(categoryName);
  298. GuiSettings.getCategories().add(cat);
  299. OnCategoryChanged.broadcast();
  300. return cat;
  301. } else {
  302. return category.get();
  303. }
  304. }
  305. /**
  306. * Add Object into a Category.
  307. *
  308. * @param category Category
  309. * @param object Object
  310. */
  311. public void addObject(Category category, AbstractCanvasObject object) {
  312. int i = 0;
  313. while (category.findObjectWithName(object.getName()).isPresent()) {
  314. if (object.getName().contains("_")) {
  315. object.setName(object.getName().substring(0, object.getName().indexOf('_')));
  316. }
  317. object.setName(object.getName() + "_" + i);
  318. i++;
  319. }
  320. category.getObjects().add(object);
  321. }
  322. /**
  323. * Add new Holon Object to a Category.
  324. *
  325. * @param category Category
  326. * @param object New Object Name
  327. * @param list Array of Elements
  328. * @param image the image Path
  329. */
  330. public HolonObject addNewHolonObject(Category category, String object, List<HolonElement> list,
  331. String image) {
  332. HolonObject obj = new HolonObject(object);
  333. obj.setImagePath(image);
  334. obj.clearElements();
  335. obj.add(list);
  336. addObject(category, obj);
  337. return obj;
  338. }
  339. public Optional<Category> findCategoryWithName(String categoryName) {
  340. return GuiSettings.getCategories().stream().filter(cat -> cat.getName().equals(categoryName))
  341. .findAny();
  342. }
  343. public void loadFile(File file) {
  344. log.info("load" + file);
  345. try {
  346. FileReader reader = new FileReader(file);
  347. Model model = gson.fromJson(reader, Model.class);
  348. reader.close();
  349. this.model = model;
  350. calculateStateForCurrentIteration();
  351. OnModelChanged.broadcast();
  352. } catch (IOException e) {
  353. log.warning(e.getLocalizedMessage());
  354. }
  355. clearSelection();
  356. GuiSettings.setActualSaveFile(file);
  357. }
  358. public void loadModelFromJsonString(String json) {
  359. this.model = gson.fromJson(json, Model.class);
  360. calculateStateForCurrentIteration();
  361. OnModelChanged.broadcast();
  362. }
  363. public String saveModelToJsonString() {
  364. return gson.toJson(model);
  365. }
  366. public void toggleSelectedObjects(Collection<AbstractCanvasObject> objects) {
  367. Set<AbstractCanvasObject> intersection = new HashSet<>(objects);
  368. intersection.retainAll(GuiSettings.getSelectedObjects());
  369. GuiSettings.getSelectedObjects().addAll(objects);
  370. GuiSettings.getSelectedObjects().removeAll(intersection);
  371. OnSelectionChanged.broadcast();
  372. }
  373. public void saveFile(File file) {
  374. log.info("save" + file);
  375. try {
  376. FileWriter writer = new FileWriter(file);
  377. gson.toJson(model, writer);
  378. writer.close();
  379. } catch (IOException e) {
  380. log.warning(e.getLocalizedMessage());
  381. }
  382. GuiSettings.setActualSaveFile(file);
  383. }
  384. public void group() {
  385. canvasController.group(GuiSettings.getSelectedObjects());
  386. clearSelection();
  387. OnCanvasUpdate.broadcast();
  388. }
  389. public void ungroup() {
  390. canvasController.ungroup(GuiSettings.getSelectedObjects());
  391. clearSelection();
  392. OnCanvasUpdate.broadcast();
  393. }
  394. public void undo() {
  395. UndoHistory.undo().ifPresent(this::loadModelFromJsonString);
  396. }
  397. public void redo() {
  398. UndoHistory.redo().ifPresent(this::loadModelFromJsonString);
  399. }
  400. public void clearModel() {
  401. model.clear();
  402. clearSelection();
  403. model.setCurrentIteration(0);
  404. IdCounter.reset();
  405. calculateStateForCurrentIteration();
  406. OnModelChanged.broadcast();
  407. GuiSettings.setActualSaveFile(null);
  408. }
  409. public void showGroupNode(GroupNode groupNode) {
  410. OnShowGroupNode.broadcast(groupNode);
  411. clearSelection();
  412. }
  413. public void loadCategory() {
  414. String json = prefs.get(PreferenceKeys.Category.DefaultCategory, "Init");
  415. if (json.equals("Init")) {
  416. initCategories();
  417. return;
  418. }
  419. GuiSettings.getCategories().clear();
  420. GuiSettings.getCategories()
  421. .addAll(CategoryAdapter.Gson.fromJson(json, CategoryAdapter.CategorySet));
  422. OnCategoryChanged.broadcast();
  423. }
  424. public void saveCategories() {
  425. String json = CategoryAdapter.Gson.toJson(GuiSettings.getCategories());
  426. prefs.put(PreferenceKeys.Category.DefaultCategory, json);
  427. }
  428. }