UnitGraph.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. package ui.view;
  2. import classes.*;
  3. import classes.comparator.UnitGraphPointComperator;
  4. import interfaces.GraphEditable;
  5. import interfaces.GraphEditable.Graphtype;
  6. import interfaces.IGraphedElement;
  7. import sun.reflect.generics.reflectiveObjects.NotImplementedException;
  8. import ui.controller.Control;
  9. import ui.controller.SingletonControl;
  10. import ui.model.Model;
  11. import javax.swing.*;
  12. import java.awt.*;
  13. import java.awt.event.*;
  14. import java.awt.geom.Path2D;
  15. import java.awt.geom.Point2D;
  16. import java.util.ArrayList;
  17. import java.util.LinkedList;
  18. import java.util.ListIterator;
  19. /**
  20. * This Class represents a Graph where the User can model the behavior of
  21. * elements and switches over time.
  22. *
  23. * @author Tom Troppmann
  24. */
  25. public class UnitGraph extends JPanel implements MouseListener, MouseMotionListener, ComponentListener {
  26. private static final long serialVersionUID = 1L;
  27. // Normal Settings
  28. private int border = 4;
  29. private int clickThreshholdSquared = 25;
  30. // Display Settings
  31. /**
  32. * The size of a dot in the graph.
  33. * It should be at least 1.
  34. * */
  35. int dotSize = 8;
  36. /** The Color of a dot in the graph. */
  37. Color dotColor = Color.blue;
  38. Color editDotColor = new Color(255, 119, 0);
  39. //Intern Variables
  40. //TODO: JavaDoc
  41. private LinkedList<UnitGraphPoint> actualGraphPoints = new LinkedList<UnitGraphPoint>();
  42. private Graphtype actualGraphType;
  43. private GraphEditable actualElement;
  44. Position editPosition;
  45. boolean editMode = false;
  46. private enum pointType {Normal, StartPoint, EndPoint};
  47. pointType editPoint = pointType.Normal;
  48. //Maybe Needed
  49. private Model model;
  50. private Control controller;
  51. private int widthWithBorder, heightWithBorder;
  52. /**
  53. * Constructor.
  54. *
  55. * @param model the Model
  56. * @param control the Controller
  57. */
  58. public UnitGraph(final Model model, Control control) {
  59. setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
  60. this.controller = control;
  61. this.model = model;
  62. this.setBackground(Color.WHITE);
  63. this.addMouseListener(this);
  64. this.addMouseMotionListener(this);
  65. this.addComponentListener(this);
  66. }
  67. /**
  68. * Paints all Components on the Canvas.
  69. *
  70. * @param g Graphics
  71. */
  72. public void paintComponent(Graphics g) {
  73. super.paintComponent(g);
  74. Graphics2D g2D = (Graphics2D) g;
  75. drawGrid(g2D);
  76. g2D.setColor(Color.BLACK);
  77. g2D.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
  78. g2D.setStroke(new BasicStroke(2));
  79. drawUnitGraph(g2D);
  80. g2D.setColor(dotColor);
  81. if(editMode)
  82. {
  83. drawUnitGraphPointsReleased(g2D);
  84. }else
  85. {
  86. drawUnitGraphPoints(g2D);
  87. }
  88. g2D.setColor(dotColor);
  89. g2D.setStroke(new BasicStroke(1));
  90. drawCurrentIterartionLine(g2D);
  91. }
  92. private void drawGrid(Graphics2D g2D) {
  93. g2D.setStroke(new BasicStroke(1));
  94. g2D.setColor(Color.lightGray);
  95. int amountOfLines = 10;
  96. int width = widthWithBorder + 2 * border;
  97. int height = heightWithBorder;
  98. for(int i = 0; i<=amountOfLines; i++)
  99. {
  100. int linehieght = (int) (((double)i/ (double) amountOfLines) * (double) height) + border;
  101. g2D.drawLine(0, linehieght, width, linehieght);
  102. }
  103. }
  104. private void drawCurrentIterartionLine(Graphics2D g)
  105. {
  106. int cur = model.getCurIteration();
  107. int max = model.getIterations();
  108. double where = ((double) cur)/((double) max - 1);
  109. Position oben = new Position(border + (int)(where * widthWithBorder), 0);
  110. Position unten = new Position(border + (int)(where * widthWithBorder), 2 * border + heightWithBorder);
  111. drawLine(g,oben,unten);
  112. }
  113. private void drawLine(Graphics2D g, Position start, Position end)
  114. {
  115. Path2D.Double path = new Path2D.Double();
  116. path.moveTo(start.x, start.y);
  117. path.lineTo(end.x, end.y);
  118. g.draw(path);
  119. }
  120. private Path2D.Double initBezier(Position start) {
  121. //Good Source for basic understanding for Bezier Curves
  122. //http://www.theappguruz.com/blog/bezier-curve-in-games
  123. Path2D.Double path = new Path2D.Double();
  124. path.moveTo(start.x, start.y);
  125. return path;
  126. }
  127. private void curveTo(Path2D.Double path, Position actual, Position target) {
  128. double mitte = (actual.x + target.x)* 0.5;
  129. path.curveTo(mitte, actual.y, mitte, target.y, target.x, target.y);
  130. }
  131. private void drawDot(Graphics2D g, Position p)
  132. {
  133. g.fillOval(p.x -dotSize/2, p.y-dotSize/2, dotSize, dotSize);
  134. }
  135. private void drawUnitGraph(Graphics2D g) {
  136. switch(actualGraphType) {
  137. case boolGraph:
  138. if(editMode)
  139. drawBoolGraphWithEditPosition(g);
  140. else
  141. drawBoolGraph(g);
  142. break;
  143. case doubleGraph:
  144. if(editMode)
  145. drawDoubleGraphWithEditPosition(g);
  146. else
  147. drawDoubleGraph(g);
  148. break;
  149. default:
  150. throw new UnsupportedOperationException();
  151. }
  152. }
  153. private void drawUnitGraphPoints(Graphics2D g) {
  154. g.setColor(dotColor);
  155. for(UnitGraphPoint p : actualGraphPoints){
  156. drawDot(g, p.displayedPosition);
  157. }
  158. }
  159. private void saveGraph() {
  160. LinkedList<Point2D.Double> actual = actualElement.getStateGraph();
  161. actual.clear();
  162. for(UnitGraphPoint p: actualGraphPoints)
  163. {
  164. actual.add(p.getPoint());
  165. }
  166. actualElement.sampleGraph();
  167. }
  168. private void drawUnitGraphPointsReleased(Graphics2D g) {
  169. drawUnitGraphPoints(g);
  170. g.setColor(editDotColor);
  171. drawDot(g, editPosition);
  172. }
  173. private void drawBoolGraph(Graphics2D g) {
  174. System.out.println("DoImplement");
  175. if(actualGraphPoints.size() <= 1) return;
  176. LinkedList<Position> cornerPoints = new LinkedList<Position>();
  177. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  178. Position actual = actualGraphPoints.getFirst().displayedPosition;
  179. Path2D.Double path = new Path2D.Double();
  180. path.moveTo(actual.x, actual.y);
  181. while (iter.hasNext())
  182. {
  183. Position target = iter.next().displayedPosition;
  184. //BooleanConnection
  185. path.lineTo(target.x, actual.y); //line to corner
  186. cornerPoints.add(new Position(target.x, actual.y)); //save corner
  187. path.lineTo(target.x, target.y); //line to next Point
  188. actual = target;
  189. }
  190. g.draw(path);
  191. //Draw the Points on the Corner that dont exist in Data but should be visual
  192. g.setColor(dotColor);
  193. for(Position p: cornerPoints)
  194. {
  195. drawDot(g, p);
  196. }
  197. }
  198. private void drawBoolGraphWithEditPosition(Graphics2D g) {
  199. LinkedList<Position> before = new LinkedList<Position>();
  200. LinkedList<Position> after = new LinkedList<Position>();
  201. for(UnitGraphPoint p: actualGraphPoints)
  202. {
  203. if(p.displayedPosition.x < editPosition.x)
  204. before.add(p.displayedPosition);
  205. else
  206. after.add(p.displayedPosition);
  207. }
  208. g.setColor(Color.BLACK);
  209. drawBoolGraphFromList(g, before);
  210. g.setColor(Color.BLACK);
  211. drawBoolGraphFromList(g, after);
  212. //EditGraph
  213. LinkedList<Position> middle = new LinkedList<Position>();
  214. if(!before.isEmpty()) middle.add(before.getLast());
  215. middle.add(editPosition);
  216. if(!after.isEmpty()) middle.add(after.getFirst());
  217. g.setColor(editDotColor);
  218. drawBoolGraphFromList(g, middle);
  219. }
  220. private void drawBoolGraphFromList(Graphics2D g, LinkedList<Position> list) {
  221. if(list.size() <= 1) return;
  222. ListIterator<Position> iter = list.listIterator();
  223. LinkedList<Position> cornerPoints = new LinkedList<Position>();
  224. Position actual = list.getFirst();
  225. Path2D.Double path = new Path2D.Double();
  226. path.moveTo(actual.x, actual.y);
  227. while (iter.hasNext())
  228. {
  229. Position target = iter.next();
  230. //BooleanConnection
  231. path.lineTo(target.x, actual.y); //line to corner
  232. cornerPoints.add(new Position(target.x, actual.y)); //save corner
  233. path.lineTo(target.x, target.y); //line to next Point
  234. actual = target;
  235. }
  236. g.draw(path);
  237. g.setColor(dotColor);
  238. for(Position p: cornerPoints)
  239. {
  240. drawDot(g, p);
  241. }
  242. }
  243. private void drawDoubleGraph(Graphics2D g) {
  244. if(actualGraphPoints.isEmpty()) throw new IndexOutOfBoundsException("A Graph Without Points is not supportet jet");
  245. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  246. Position actual = iter.next().displayedPosition;
  247. Path2D.Double path = this.initBezier(actual);
  248. while (iter.hasNext())
  249. {
  250. Position target = iter.next().displayedPosition;
  251. this.curveTo(path, actual, target);
  252. actual = target;
  253. }
  254. g.draw(path);
  255. }
  256. private void drawDoubleGraphWithEditPosition(Graphics2D g) {
  257. LinkedList<Position> before = new LinkedList<Position>();
  258. LinkedList<Position> after = new LinkedList<Position>();
  259. for(UnitGraphPoint p: actualGraphPoints)
  260. {
  261. if(p.displayedPosition.x < editPosition.x)
  262. before.add(p.displayedPosition);
  263. else
  264. after.add(p.displayedPosition);
  265. }
  266. drawUnitGraphFromList(g, before);
  267. drawUnitGraphFromList(g, after);
  268. //EditGraph
  269. LinkedList<Position> middle = new LinkedList<Position>();
  270. if(!before.isEmpty()) middle.add(before.getLast());
  271. middle.add(editPosition);
  272. if(!after.isEmpty()) middle.add(after.getFirst());
  273. g.setColor(editDotColor);
  274. drawUnitGraphFromList(g, middle);
  275. }
  276. private void drawUnitGraphFromList(Graphics2D g, LinkedList<Position> list) {
  277. if(list.size() <= 1) return;
  278. ListIterator<Position> iter = list.listIterator();
  279. Position actual = list.getFirst();
  280. Path2D.Double path = this.initBezier(actual);
  281. while (iter.hasNext())
  282. {
  283. Position target = iter.next();
  284. curveTo(path, actual, target);
  285. actual = target;
  286. }
  287. g.draw(path);
  288. }
  289. private void updateRepresentativePositions()
  290. {
  291. for(UnitGraphPoint p : actualGraphPoints) {
  292. p.calcDisplayedPosition(border, widthWithBorder, heightWithBorder);
  293. }
  294. }
  295. private void overrideUnitGraph(LinkedList<Point2D.Double> stateCurve) {
  296. actualGraphPoints.clear();
  297. for(Point2D.Double p: stateCurve){
  298. actualGraphPoints.add(new UnitGraphPoint(p));
  299. }
  300. updateRepresentativePositions();
  301. }
  302. private void calculateWidthHeight()
  303. {
  304. widthWithBorder = this.getWidth() - 2 * border;
  305. heightWithBorder = this.getHeight() - 2 * border;
  306. }
  307. public void initNewElement(GraphEditable element)
  308. {
  309. overrideUnitGraph(element.getStateGraph());
  310. actualGraphType = element.getGraphType();
  311. actualElement = element;
  312. repaint();
  313. }
  314. private void removePointNearPosition(Position mPosition) {
  315. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  316. while (iter.hasNext())
  317. {
  318. if(near(mPosition,iter.next().displayedPosition))
  319. {
  320. iter.remove();
  321. break;
  322. }
  323. }
  324. }
  325. private void detectStartEndPoint(Position mPosition)
  326. {
  327. UnitGraphPoint first = actualGraphPoints.getFirst();
  328. UnitGraphPoint last = actualGraphPoints.getLast();
  329. if(near(mPosition, first.displayedPosition)) editPoint = pointType.StartPoint;
  330. else if(near(mPosition, last.displayedPosition)) editPoint = pointType.EndPoint;
  331. else editPoint = pointType.Normal;
  332. }
  333. private boolean near(Position actual, Position target) {
  334. switch(actualGraphType)
  335. {
  336. case boolGraph: //Distance only with X
  337. int xDis = target.x - actual.x;
  338. return xDis * xDis < clickThreshholdSquared;
  339. case doubleGraph:
  340. return actual.squareDistance(target) < clickThreshholdSquared;
  341. default:
  342. return false;
  343. }
  344. }
  345. private void updateEditPointPosition(Position newPosition) {
  346. //make it in the bounds of the UnitGraph no Point out of the Border
  347. Position currentPosition = setInBounds(newPosition);
  348. if(editPoint != pointType.Normal) attachToBorder(currentPosition);
  349. if(actualGraphType == Graphtype.boolGraph) snapBoolean(currentPosition);
  350. this.editPosition = currentPosition;
  351. }
  352. private Position setInBounds(Position p) {
  353. p.clampX(border, border + widthWithBorder);
  354. p.clampY(border, border + heightWithBorder);
  355. return p;
  356. }
  357. private Position snapBoolean(Position p)
  358. {
  359. if (p.y < border + heightWithBorder / 2) {
  360. p.y = border;
  361. } else {
  362. p.y = border + heightWithBorder;
  363. }
  364. return p;
  365. }
  366. private Point.Double getBezierPoint(double t, Point.Double p0, Point.Double p1,Point.Double p2,Point.Double p3) {
  367. /*
  368. * Calculate Beziér:
  369. * B(t) = (1-t)^3 * P0 + 3*(1-t)^2 * t * P1 + 3*(1-t)*t^2 * P2 + t^3 * P3 , 0 < t < 1
  370. *
  371. * Source: //http://www.theappguruz.com/blog/bezier-curve-in-games
  372. */
  373. Point.Double bezier = new Point.Double();
  374. double OneSubT = 1-t;
  375. double OneSubT2 = Math.pow(OneSubT, 2);
  376. double OneSubT3 = Math.pow(OneSubT, 3);
  377. double t2 = Math.pow(t , 2);
  378. double t3 = Math.pow(t , 3);
  379. bezier.x = OneSubT3 * p0.x + 3 * OneSubT2 * t * p1.x + 3 * OneSubT * t2 * p2.x + t3 * p3.x;
  380. bezier.y = OneSubT3 * p0.y + 3 * OneSubT2 * t * p1.y + 3 * OneSubT * t2 * p2.y + t3 * p3.y;
  381. return bezier;
  382. }
  383. private double getYBetweenTwoPoints(double t, Point.Double start, Point.Double end) {
  384. double mitte = (start.x + end.x)* 0.5;
  385. Point.Double bezier = getBezierPoint(t, start, new Point.Double(mitte, start.y), new Point.Double(mitte, end.y), end);
  386. return bezier.y;
  387. }
  388. private float[] sampleGraph(int sampleLength)
  389. {
  390. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  391. Point.Double before = iter.next().getPoint();
  392. Point.Double after = iter.next().getPoint();
  393. float [] sampleCurve = new float[sampleLength];
  394. for(int i = 0; i<sampleLength ; i++)
  395. {
  396. double graphX = (double)i / (double) (sampleLength - 1); //from 0.0 to 1.0
  397. if(graphX > after.x)
  398. {
  399. before = after;
  400. after = iter.next().getPoint();
  401. }
  402. //inverseLerp(valueBetween, min, max) (valueBetween - min) / (max - min)
  403. // e.g. old.x = 0.4, actual.x = 0.8 and graphX = 0.6 then t is 0.5
  404. double t = (after.x -before.x > 0)? (graphX - before.x) / (after.x -before.x) : 0.0;
  405. sampleCurve[i] = (float) getYBetweenTwoPoints(t, before, after);
  406. }
  407. return sampleCurve;
  408. }
  409. private Position attachToBorder(Position p)
  410. {
  411. switch(editPoint)
  412. {
  413. case StartPoint:
  414. p.x = border;
  415. break;
  416. case EndPoint:
  417. p.x = border + widthWithBorder;
  418. break;
  419. default:
  420. break;
  421. }
  422. return p;
  423. }
  424. private void insertNewGraphPoint(Position pos)
  425. {
  426. System.out.println("insertNewGraphPoint");
  427. setInBounds(pos);
  428. ListIterator<UnitGraphPoint> iter2 = actualGraphPoints.listIterator();
  429. while (iter2.hasNext())
  430. {
  431. Position tempPosition = iter2.next().displayedPosition;
  432. if(pos.x <= tempPosition.x)
  433. {
  434. //previous to go back a position to make the new point before the the Position with greater X
  435. iter2.previous();
  436. iter2.add(generateUnitGraphPoint(pos));
  437. break;
  438. }
  439. }
  440. if(!iter2.hasNext()) //if behind last point
  441. {
  442. iter2.add(generateUnitGraphPoint(pos));
  443. }
  444. }
  445. private UnitGraphPoint generateUnitGraphPoint(Position pos) {
  446. UnitGraphPoint temp = new UnitGraphPoint((double) (pos.x - border) / (double) widthWithBorder,
  447. 1 - (double) (pos.y - border) / (double) heightWithBorder, true);
  448. temp.displayedPosition = pos;
  449. return temp;
  450. }
  451. @Override
  452. public void mouseDragged(MouseEvent e) {
  453. System.out.println("MouseDragged");
  454. updateEditPointPosition(new Position(e.getPoint()));
  455. repaint();
  456. }
  457. @Override
  458. public void mouseMoved(MouseEvent e) {
  459. }
  460. @Override
  461. public void mouseClicked(MouseEvent e) {
  462. }
  463. @Override
  464. public void mouseEntered(MouseEvent e) {
  465. }
  466. @Override
  467. public void mouseExited(MouseEvent e) {
  468. }
  469. @Override
  470. public void mousePressed(MouseEvent e) {
  471. System.out.println("mousePressed");
  472. Position mPosition = new Position(e.getPoint());
  473. if (e.getButton() == MouseEvent.BUTTON3) {
  474. // RightMouseButtonEvent
  475. detectStartEndPoint(mPosition);
  476. if (editPoint == pointType.Normal) {
  477. removePointNearPosition(mPosition);
  478. repaint();
  479. }
  480. editMode = false;
  481. } else if (e.getButton() == MouseEvent.BUTTON1) {
  482. // LeftMouseButtonEvent
  483. detectStartEndPoint(mPosition);
  484. removePointNearPosition(mPosition);
  485. updateEditPointPosition(mPosition);
  486. editMode = true;
  487. repaint();
  488. }
  489. }
  490. @Override
  491. public void mouseReleased(MouseEvent e) {
  492. System.out.println("mouseReleased");
  493. if(editMode)
  494. {
  495. this.insertNewGraphPoint(editPosition);
  496. editMode = false;
  497. repaint();
  498. }
  499. saveGraph();
  500. }
  501. /**
  502. * When the Component is Resized.
  503. *
  504. * @param e ComponentEvent
  505. */
  506. public void componentResized(ComponentEvent e) {
  507. System.out.println("componentResized");
  508. calculateWidthHeight();
  509. updateRepresentativePositions();
  510. repaint();
  511. }
  512. @Override
  513. public void componentHidden(ComponentEvent e) {
  514. }
  515. @Override
  516. public void componentMoved(ComponentEvent e) {
  517. }
  518. @Override
  519. public void componentShown(ComponentEvent e) {
  520. }
  521. public void reset() {
  522. System.out.println("reset");
  523. }
  524. public void update(ArrayList<AbstractCpsObject> obj) {
  525. System.out.println("update");
  526. }
  527. public void setStretching(boolean selected) {
  528. System.out.println("setStretching");
  529. }
  530. public void setLocalPeriod(int localLength) {
  531. System.out.println("setLocalPeriod");
  532. }
  533. public void repaintGraph(AbstractCpsObject cps) {
  534. // TODO Auto-generated method stub
  535. System.out.println("repaintGraph");
  536. }
  537. }