Control.java 17 KB

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