Control.java 16 KB

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