UnitGraph.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  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. drawSnappingHint(g);
  220. }
  221. private void drawSnappingHint(Graphics2D g)
  222. {
  223. //ColorHint
  224. g.setColor(Color.RED);
  225. //Threshhold Line
  226. final float dash1[] = {10.0f};
  227. final BasicStroke dashed =new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
  228. g.setStroke(dashed);
  229. int halfheight = border + heightWithBorder / 2;
  230. g.drawLine(0, halfheight , widthWithBorder + 2 * border, halfheight);
  231. //Threshhold Text
  232. g.drawString("Snapping Threshold", 10, halfheight - 2);
  233. }
  234. private void drawBoolGraphFromList(Graphics2D g, LinkedList<Position> list) {
  235. if(list.size() <= 1) return;
  236. ListIterator<Position> iter = list.listIterator();
  237. LinkedList<Position> cornerPoints = new LinkedList<Position>();
  238. Position actual = list.getFirst();
  239. Path2D.Double path = new Path2D.Double();
  240. path.moveTo(actual.x, actual.y);
  241. while (iter.hasNext())
  242. {
  243. Position target = iter.next();
  244. //BooleanConnection
  245. path.lineTo(target.x, actual.y); //line to corner
  246. cornerPoints.add(new Position(target.x, actual.y)); //save corner
  247. path.lineTo(target.x, target.y); //line to next Point
  248. actual = target;
  249. }
  250. g.draw(path);
  251. g.setColor(dotColor);
  252. for(Position p: cornerPoints)
  253. {
  254. drawDot(g, p);
  255. }
  256. }
  257. private void drawDoubleGraph(Graphics2D g) {
  258. if(actualGraphPoints.isEmpty()) throw new IndexOutOfBoundsException("A Graph Without Points is not supportet jet");
  259. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  260. Position actual = iter.next().displayedPosition;
  261. Path2D.Double path = this.initBezier(actual);
  262. while (iter.hasNext())
  263. {
  264. Position target = iter.next().displayedPosition;
  265. this.curveTo(path, actual, target);
  266. actual = target;
  267. }
  268. g.draw(path);
  269. }
  270. private void drawDoubleGraphWithEditPosition(Graphics2D g) {
  271. LinkedList<Position> before = new LinkedList<Position>();
  272. LinkedList<Position> after = new LinkedList<Position>();
  273. for(UnitGraphPoint p: actualGraphPoints)
  274. {
  275. if(p.displayedPosition.x < editPosition.x)
  276. before.add(p.displayedPosition);
  277. else
  278. after.add(p.displayedPosition);
  279. }
  280. drawUnitGraphFromList(g, before);
  281. drawUnitGraphFromList(g, after);
  282. //EditGraph
  283. LinkedList<Position> middle = new LinkedList<Position>();
  284. if(!before.isEmpty()) middle.add(before.getLast());
  285. middle.add(editPosition);
  286. if(!after.isEmpty()) middle.add(after.getFirst());
  287. g.setColor(editDotColor);
  288. drawUnitGraphFromList(g, middle);
  289. }
  290. private void drawUnitGraphFromList(Graphics2D g, LinkedList<Position> list) {
  291. if(list.size() <= 1) return;
  292. ListIterator<Position> iter = list.listIterator();
  293. Position actual = list.getFirst();
  294. Path2D.Double path = this.initBezier(actual);
  295. while (iter.hasNext())
  296. {
  297. Position target = iter.next();
  298. curveTo(path, actual, target);
  299. actual = target;
  300. }
  301. g.draw(path);
  302. }
  303. private void updateRepresentativePositions()
  304. {
  305. for(UnitGraphPoint p : actualGraphPoints) {
  306. p.calcDisplayedPosition(border, widthWithBorder, heightWithBorder);
  307. }
  308. }
  309. private void overrideUnitGraph(LinkedList<Point2D.Double> stateCurve) {
  310. actualGraphPoints.clear();
  311. for(Point2D.Double p: stateCurve){
  312. actualGraphPoints.add(new UnitGraphPoint(p));
  313. }
  314. updateRepresentativePositions();
  315. }
  316. private void calculateWidthHeight()
  317. {
  318. widthWithBorder = this.getWidth() - 2 * border;
  319. heightWithBorder = this.getHeight() - 2 * border;
  320. }
  321. public void initNewElement(GraphEditable element)
  322. {
  323. overrideUnitGraph(element.getStateGraph());
  324. actualGraphType = element.getGraphType();
  325. actualElement = element;
  326. repaint();
  327. }
  328. private void removePointNearPosition(Position mPosition) {
  329. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  330. while (iter.hasNext())
  331. {
  332. if(near(mPosition,iter.next().displayedPosition))
  333. {
  334. iter.remove();
  335. break;
  336. }
  337. }
  338. }
  339. private void detectStartEndPoint(Position mPosition)
  340. {
  341. UnitGraphPoint first = actualGraphPoints.getFirst();
  342. UnitGraphPoint last = actualGraphPoints.getLast();
  343. if(near(mPosition, first.displayedPosition)) editPoint = pointType.StartPoint;
  344. else if(near(mPosition, last.displayedPosition)) editPoint = pointType.EndPoint;
  345. else editPoint = pointType.Normal;
  346. }
  347. private boolean near(Position actual, Position target) {
  348. switch(actualGraphType)
  349. {
  350. case boolGraph: //Distance only with X
  351. int xDis = target.x - actual.x;
  352. return xDis * xDis < clickThreshholdSquared;
  353. case doubleGraph:
  354. return actual.squareDistance(target) < clickThreshholdSquared;
  355. default:
  356. return false;
  357. }
  358. }
  359. private void updateEditPointPosition(Position newPosition) {
  360. //make it in the bounds of the UnitGraph no Point out of the Border
  361. Position currentPosition = setInBounds(newPosition);
  362. if(editPoint != pointType.Normal) attachToBorder(currentPosition);
  363. if(actualGraphType == Graphtype.boolGraph) snapBoolean(currentPosition);
  364. this.editPosition = currentPosition;
  365. }
  366. private Position setInBounds(Position p) {
  367. p.clampX(border, border + widthWithBorder);
  368. p.clampY(border, border + heightWithBorder);
  369. return p;
  370. }
  371. private Position snapBoolean(Position p)
  372. {
  373. if (p.y < border + heightWithBorder / 2) {
  374. p.y = border;
  375. } else {
  376. p.y = border + heightWithBorder;
  377. }
  378. return p;
  379. }
  380. private Point.Double getBezierPoint(double t, Point.Double p0, Point.Double p1,Point.Double p2,Point.Double p3) {
  381. /*
  382. * Calculate Beziér:
  383. * B(t) = (1-t)^3 * P0 + 3*(1-t)^2 * t * P1 + 3*(1-t)*t^2 * P2 + t^3 * P3 , 0 < t < 1
  384. *
  385. * Source: //http://www.theappguruz.com/blog/bezier-curve-in-games
  386. */
  387. Point.Double bezier = new Point.Double();
  388. double OneSubT = 1-t;
  389. double OneSubT2 = Math.pow(OneSubT, 2);
  390. double OneSubT3 = Math.pow(OneSubT, 3);
  391. double t2 = Math.pow(t , 2);
  392. double t3 = Math.pow(t , 3);
  393. bezier.x = OneSubT3 * p0.x + 3 * OneSubT2 * t * p1.x + 3 * OneSubT * t2 * p2.x + t3 * p3.x;
  394. bezier.y = OneSubT3 * p0.y + 3 * OneSubT2 * t * p1.y + 3 * OneSubT * t2 * p2.y + t3 * p3.y;
  395. return bezier;
  396. }
  397. private double getYBetweenTwoPoints(double t, Point.Double start, Point.Double end) {
  398. double mitte = (start.x + end.x)* 0.5;
  399. Point.Double bezier = getBezierPoint(t, start, new Point.Double(mitte, start.y), new Point.Double(mitte, end.y), end);
  400. return bezier.y;
  401. }
  402. private float[] sampleGraph(int sampleLength)
  403. {
  404. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  405. Point.Double before = iter.next().getPoint();
  406. Point.Double after = iter.next().getPoint();
  407. float [] sampleCurve = new float[sampleLength];
  408. for(int i = 0; i<sampleLength ; i++)
  409. {
  410. double graphX = (double)i / (double) (sampleLength - 1); //from 0.0 to 1.0
  411. if(graphX > after.x)
  412. {
  413. before = after;
  414. after = iter.next().getPoint();
  415. }
  416. //inverseLerp(valueBetween, min, max) (valueBetween - min) / (max - min)
  417. // e.g. old.x = 0.4, actual.x = 0.8 and graphX = 0.6 then t is 0.5
  418. double t = (after.x -before.x > 0)? (graphX - before.x) / (after.x -before.x) : 0.0;
  419. sampleCurve[i] = (float) getYBetweenTwoPoints(t, before, after);
  420. }
  421. return sampleCurve;
  422. }
  423. private Position attachToBorder(Position p)
  424. {
  425. switch(editPoint)
  426. {
  427. case StartPoint:
  428. p.x = border;
  429. break;
  430. case EndPoint:
  431. p.x = border + widthWithBorder;
  432. break;
  433. default:
  434. break;
  435. }
  436. return p;
  437. }
  438. private void insertNewGraphPoint(Position pos)
  439. {
  440. System.out.println("insertNewGraphPoint");
  441. setInBounds(pos);
  442. ListIterator<UnitGraphPoint> iter2 = actualGraphPoints.listIterator();
  443. while (iter2.hasNext())
  444. {
  445. Position tempPosition = iter2.next().displayedPosition;
  446. if(pos.x <= tempPosition.x)
  447. {
  448. //previous to go back a position to make the new point before the the Position with greater X
  449. iter2.previous();
  450. iter2.add(generateUnitGraphPoint(pos));
  451. break;
  452. }
  453. }
  454. if(!iter2.hasNext()) //if behind last point
  455. {
  456. iter2.add(generateUnitGraphPoint(pos));
  457. }
  458. }
  459. private UnitGraphPoint generateUnitGraphPoint(Position pos) {
  460. UnitGraphPoint temp = new UnitGraphPoint((double) (pos.x - border) / (double) widthWithBorder,
  461. 1 - (double) (pos.y - border) / (double) heightWithBorder, true);
  462. temp.displayedPosition = pos;
  463. return temp;
  464. }
  465. @Override
  466. public void mouseDragged(MouseEvent e) {
  467. System.out.println("MouseDragged");
  468. updateEditPointPosition(new Position(e.getPoint()));
  469. repaint();
  470. }
  471. @Override
  472. public void mouseMoved(MouseEvent e) {
  473. }
  474. @Override
  475. public void mouseClicked(MouseEvent e) {
  476. }
  477. @Override
  478. public void mouseEntered(MouseEvent e) {
  479. }
  480. @Override
  481. public void mouseExited(MouseEvent e) {
  482. }
  483. @Override
  484. public void mousePressed(MouseEvent e) {
  485. System.out.println("mousePressed");
  486. Position mPosition = new Position(e.getPoint());
  487. if (e.getButton() == MouseEvent.BUTTON3) {
  488. // RightMouseButtonEvent
  489. detectStartEndPoint(mPosition);
  490. if (editPoint == pointType.Normal) {
  491. removePointNearPosition(mPosition);
  492. repaint();
  493. }
  494. editMode = false;
  495. } else if (e.getButton() == MouseEvent.BUTTON1) {
  496. // LeftMouseButtonEvent
  497. detectStartEndPoint(mPosition);
  498. removePointNearPosition(mPosition);
  499. updateEditPointPosition(mPosition);
  500. editMode = true;
  501. repaint();
  502. }
  503. }
  504. @Override
  505. public void mouseReleased(MouseEvent e) {
  506. System.out.println("mouseReleased");
  507. if(editMode)
  508. {
  509. this.insertNewGraphPoint(editPosition);
  510. editMode = false;
  511. repaint();
  512. }
  513. saveGraph();
  514. }
  515. /**
  516. * When the Component is Resized.
  517. *
  518. * @param e ComponentEvent
  519. */
  520. public void componentResized(ComponentEvent e) {
  521. System.out.println("componentResized");
  522. calculateWidthHeight();
  523. updateRepresentativePositions();
  524. repaint();
  525. }
  526. @Override
  527. public void componentHidden(ComponentEvent e) {
  528. }
  529. @Override
  530. public void componentMoved(ComponentEvent e) {
  531. }
  532. @Override
  533. public void componentShown(ComponentEvent e) {
  534. }
  535. public void reset() {
  536. System.out.println("reset");
  537. if(this.actualElement != null) {
  538. actualElement.reset();
  539. overrideUnitGraph(actualElement.getStateGraph());
  540. repaint();
  541. }
  542. }
  543. public void update(ArrayList<AbstractCpsObject> obj) {
  544. System.out.println("update");
  545. }
  546. public void setStretching(boolean selected) {
  547. System.out.println("setStretching");
  548. }
  549. public void setLocalPeriod(int localLength) {
  550. System.out.println("setLocalPeriod");
  551. }
  552. public void repaintGraph(AbstractCpsObject cps) {
  553. // TODO Auto-generated method stub
  554. System.out.println("repaintGraph");
  555. }
  556. }