UnitGraph.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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. //System.out.println("paint");
  75. Graphics2D g2D = (Graphics2D) g;
  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.setStroke(new BasicStroke(1));
  89. drawCurrentIterartionLine(g2D);
  90. }
  91. //TODO -> New Section
  92. private void drawCurrentIterartionLine(Graphics2D g)
  93. {
  94. int cur = model.getCurIteration();
  95. int max = model.getIterations();
  96. double where = ((double) cur)/((double) max);
  97. Position oben = new Position(border + (int)(where * widthWithBorder), 0);
  98. Position unten = new Position(border + (int)(where * widthWithBorder), 2 * border + heightWithBorder);
  99. drawLine(g,oben,unten);
  100. }
  101. private void drawLine(Graphics2D g, Position start, Position end)
  102. {
  103. Path2D.Double path = new Path2D.Double();
  104. path.moveTo(start.x, start.y);
  105. path.lineTo(end.x, end.y);
  106. g.draw(path);
  107. }
  108. private Path2D.Double initBezier(Position start) {
  109. //Good Source for basic understanding for Bezier Curves
  110. //http://www.theappguruz.com/blog/bezier-curve-in-games
  111. Path2D.Double path = new Path2D.Double();
  112. path.moveTo(start.x, start.y);
  113. return path;
  114. }
  115. private void curveTo(Path2D.Double path, Position actual, Position target) {
  116. double mitte = (actual.x + target.x)* 0.5;
  117. path.curveTo(mitte, actual.y, mitte, target.y, target.x, target.y);
  118. }
  119. private void drawDot(Graphics2D g, Position p)
  120. {
  121. g.fillOval(p.x -dotSize/2, p.y-dotSize/2, dotSize, dotSize);
  122. }
  123. private void drawUnitGraph(Graphics2D g) {
  124. switch(actualGraphType) {
  125. case boolGraph:
  126. drawBoolGraph(g);
  127. break;
  128. case doubleGraph:
  129. if(editMode)
  130. drawDoubleGraphWithEditPosition(g);
  131. else
  132. drawDoubleGraph(g);
  133. break;
  134. default:
  135. throw new UnsupportedOperationException();
  136. }
  137. }
  138. private void drawUnitGraphPoints(Graphics2D g) {
  139. g.setColor(dotColor);
  140. for(UnitGraphPoint p : actualGraphPoints){
  141. drawDot(g, p.displayedPosition);
  142. }
  143. }
  144. private void saveGraph() {
  145. LinkedList<Point2D.Double> actual = actualElement.getStateGraph();
  146. actual.clear();
  147. for(UnitGraphPoint p: actualGraphPoints)
  148. {
  149. actual.add(p.getPoint());
  150. }
  151. actualElement.sampleGraph();
  152. }
  153. private void drawUnitGraphPointsReleased(Graphics2D g) {
  154. drawUnitGraphPoints(g);
  155. g.setColor(editDotColor);
  156. drawDot(g, editPosition);
  157. }
  158. private void drawBoolGraph(Graphics2D g) {
  159. throw new NotImplementedException();
  160. }
  161. private void drawDoubleGraph(Graphics2D g) {
  162. if(actualGraphPoints.isEmpty()) throw new IndexOutOfBoundsException("A Graph Without Points is not supportet jet");
  163. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  164. Position actual = iter.next().displayedPosition;
  165. Path2D.Double path = this.initBezier(actual);
  166. while (iter.hasNext())
  167. {
  168. Position target = iter.next().displayedPosition;
  169. this.curveTo(path, actual, target);
  170. actual = target;
  171. }
  172. g.draw(path);
  173. }
  174. private void drawDoubleGraphWithEditPosition(Graphics2D g) {
  175. LinkedList<Position> before = new LinkedList<Position>();
  176. LinkedList<Position> after = new LinkedList<Position>();
  177. for(UnitGraphPoint p: actualGraphPoints)
  178. {
  179. if(p.displayedPosition.x < editPosition.x)
  180. before.add(p.displayedPosition);
  181. else
  182. after.add(p.displayedPosition);
  183. }
  184. drawUnitGraphFromList(g, before);
  185. drawUnitGraphFromList(g, after);
  186. //EditGraph
  187. LinkedList<Position> middle = new LinkedList<Position>();
  188. if(!before.isEmpty()) middle.add(before.getLast());
  189. middle.add(editPosition);
  190. if(!after.isEmpty()) middle.add(after.getFirst());
  191. g.setColor(editDotColor);
  192. drawUnitGraphFromList(g, middle);
  193. }
  194. private void drawUnitGraphFromList(Graphics2D g, LinkedList<Position> list) {
  195. if(list.size() <= 1) return;
  196. ListIterator<Position> iter = list.listIterator();
  197. Position actual = list.getFirst();
  198. Path2D.Double path = this.initBezier(actual);
  199. while (iter.hasNext())
  200. {
  201. Position target = iter.next();
  202. curveTo(path, actual, target);
  203. actual = target;
  204. }
  205. g.draw(path);
  206. }
  207. private void updateRepresentativePositions()
  208. {
  209. for(UnitGraphPoint p : actualGraphPoints) {
  210. p.calcDisplayedPosition(border, widthWithBorder, heightWithBorder);
  211. }
  212. }
  213. private void overrideUnitGraph(LinkedList<Point2D.Double> stateCurve) {
  214. actualGraphPoints.clear();
  215. for(Point2D.Double p: stateCurve){
  216. actualGraphPoints.add(new UnitGraphPoint(p));
  217. }
  218. updateRepresentativePositions();
  219. }
  220. private void calculateWidthHeight()
  221. {
  222. widthWithBorder = this.getWidth() - 2 * border;
  223. heightWithBorder = this.getHeight() - 2 * border;
  224. }
  225. public void initNewElement(GraphEditable element)
  226. {
  227. overrideUnitGraph(element.getStateGraph());
  228. actualGraphType = element.getGraphType();
  229. actualElement = element;
  230. repaint();
  231. }
  232. private void removePointNearPosition(Position mPosition) {
  233. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  234. while (iter.hasNext())
  235. {
  236. if(mPosition.squareDistance(iter.next().displayedPosition) < clickThreshholdSquared)
  237. {
  238. iter.remove();
  239. break;
  240. }
  241. }
  242. }
  243. private void detectStartEndPoint(Position mPosition)
  244. {
  245. UnitGraphPoint first = actualGraphPoints.getFirst();
  246. UnitGraphPoint last = actualGraphPoints.getLast();
  247. if((mPosition.squareDistance(first.displayedPosition) < clickThreshholdSquared)) editPoint = pointType.StartPoint;
  248. else if(mPosition.squareDistance(last.displayedPosition) < clickThreshholdSquared) editPoint = pointType.EndPoint;
  249. else editPoint = pointType.Normal;
  250. }
  251. private void updateEditPointPosition(Position newPosition) {
  252. //make it in the bounds of the UnitGraph no Point out of the Border
  253. Position currentPosition = setInBounds(newPosition);
  254. if(editPoint != pointType.Normal) attachToBorder(currentPosition);
  255. this.editPosition = currentPosition;
  256. }
  257. private Position setInBounds(Position p) {
  258. p.clampX(border, border + widthWithBorder);
  259. p.clampY(border, border + heightWithBorder);
  260. return p;
  261. }
  262. private Point.Double getBezierPoint(double t, Point.Double p0, Point.Double p1,Point.Double p2,Point.Double p3) {
  263. /*
  264. * Calculate Beziér:
  265. * B(t) = (1-t)^3 * P0 + 3*(1-t)^2 * t * P1 + 3*(1-t)*t^2 * P2 + t^3 * P3 , 0 < t < 1
  266. *
  267. * Source: //http://www.theappguruz.com/blog/bezier-curve-in-games
  268. */
  269. Point.Double bezier = new Point.Double();
  270. double OneSubT = 1-t;
  271. double OneSubT2 = Math.pow(OneSubT, 2);
  272. double OneSubT3 = Math.pow(OneSubT, 3);
  273. double t2 = Math.pow(t , 2);
  274. double t3 = Math.pow(t , 3);
  275. bezier.x = OneSubT3 * p0.x + 3 * OneSubT2 * t * p1.x + 3 * OneSubT * t2 * p2.x + t3 * p3.x;
  276. bezier.y = OneSubT3 * p0.y + 3 * OneSubT2 * t * p1.y + 3 * OneSubT * t2 * p2.y + t3 * p3.y;
  277. return bezier;
  278. }
  279. private double getYBetweenTwoPoints(double t, Point.Double start, Point.Double end) {
  280. double mitte = (start.x + end.x)* 0.5;
  281. Point.Double bezier = getBezierPoint(t, start, new Point.Double(mitte, start.y), new Point.Double(mitte, end.y), end);
  282. return bezier.y;
  283. }
  284. private float[] sampleGraph(int sampleLength)
  285. {
  286. ListIterator<UnitGraphPoint> iter = actualGraphPoints.listIterator();
  287. Point.Double before = iter.next().getPoint();
  288. Point.Double after = iter.next().getPoint();
  289. float [] sampleCurve = new float[sampleLength];
  290. for(int i = 0; i<sampleLength ; i++)
  291. {
  292. double graphX = (double)i / (double) (sampleLength - 1); //from 0.0 to 1.0
  293. if(graphX > after.x)
  294. {
  295. before = after;
  296. after = iter.next().getPoint();
  297. }
  298. //inverseLerp(valueBetween, min, max) (valueBetween - min) / (max - min)
  299. // e.g. old.x = 0.4, actual.x = 0.8 and graphX = 0.6 then t is 0.5
  300. double t = (after.x -before.x > 0)? (graphX - before.x) / (after.x -before.x) : 0.0;
  301. sampleCurve[i] = (float) getYBetweenTwoPoints(t, before, after);
  302. }
  303. return sampleCurve;
  304. }
  305. private Position attachToBorder(Position p)
  306. {
  307. switch(editPoint)
  308. {
  309. case StartPoint:
  310. p.x = border;
  311. break;
  312. case EndPoint:
  313. p.x = border + widthWithBorder;
  314. break;
  315. default:
  316. break;
  317. }
  318. return p;
  319. }
  320. private void insertNewGraphPoint(Position pos)
  321. {
  322. System.out.println("insertNewGraphPoint");
  323. setInBounds(pos);
  324. ListIterator<UnitGraphPoint> iter2 = actualGraphPoints.listIterator();
  325. while (iter2.hasNext())
  326. {
  327. Position tempPosition = iter2.next().displayedPosition;
  328. if(pos.x <= tempPosition.x)
  329. {
  330. //previous to go back a position to make the new point before the the Position with greater X
  331. iter2.previous();
  332. iter2.add(generateUnitGraphPoint(pos));
  333. break;
  334. }
  335. }
  336. if(!iter2.hasNext()) //if behind last point
  337. {
  338. iter2.add(generateUnitGraphPoint(pos));
  339. }
  340. }
  341. private UnitGraphPoint generateUnitGraphPoint(Position pos) {
  342. UnitGraphPoint temp = new UnitGraphPoint((double) (pos.x - border) / (double) widthWithBorder,
  343. 1 - (double) (pos.y - border) / (double) heightWithBorder, true);
  344. temp.displayedPosition = pos;
  345. return temp;
  346. }
  347. @Override
  348. public void mouseDragged(MouseEvent e) {
  349. System.out.println("MouseDragged");
  350. updateEditPointPosition(new Position(e.getPoint()));
  351. repaint();
  352. }
  353. @Override
  354. public void mouseMoved(MouseEvent e) {
  355. }
  356. @Override
  357. public void mouseClicked(MouseEvent e) {
  358. }
  359. @Override
  360. public void mouseEntered(MouseEvent e) {
  361. }
  362. @Override
  363. public void mouseExited(MouseEvent e) {
  364. }
  365. @Override
  366. public void mousePressed(MouseEvent e) {
  367. System.out.println("mousePressed");
  368. Position mPosition = new Position(e.getPoint());
  369. if (e.getButton() == MouseEvent.BUTTON3) {
  370. // RightMouseButtonEvent
  371. detectStartEndPoint(mPosition);
  372. if (editPoint == pointType.Normal) {
  373. removePointNearPosition(mPosition);
  374. repaint();
  375. }
  376. editMode = false;
  377. } else if (e.getButton() == MouseEvent.BUTTON1) {
  378. // LeftMouseButtonEvent
  379. detectStartEndPoint(mPosition);
  380. removePointNearPosition(mPosition);
  381. updateEditPointPosition(mPosition);
  382. editMode = true;
  383. repaint();
  384. }
  385. }
  386. @Override
  387. public void mouseReleased(MouseEvent e) {
  388. System.out.println("mouseReleased");
  389. if(editMode)
  390. {
  391. this.insertNewGraphPoint(editPosition);
  392. editMode = false;
  393. repaint();
  394. }
  395. saveGraph();
  396. }
  397. /**
  398. * When the Component is Resized.
  399. *
  400. * @param e ComponentEvent
  401. */
  402. public void componentResized(ComponentEvent e) {
  403. System.out.println("componentResized");
  404. calculateWidthHeight();
  405. updateRepresentativePositions();
  406. repaint();
  407. }
  408. @Override
  409. public void componentHidden(ComponentEvent e) {
  410. }
  411. @Override
  412. public void componentMoved(ComponentEvent e) {
  413. }
  414. @Override
  415. public void componentShown(ComponentEvent e) {
  416. }
  417. public void reset() {
  418. System.out.println("reset");
  419. }
  420. public void update(ArrayList<AbstractCpsObject> obj) {
  421. System.out.println("update");
  422. }
  423. public void setStretching(boolean selected) {
  424. System.out.println("setStretching");
  425. }
  426. public void setLocalPeriod(int localLength) {
  427. System.out.println("setLocalPeriod");
  428. }
  429. public void repaintGraph(AbstractCpsObject cps) {
  430. // TODO Auto-generated method stub
  431. System.out.println("repaintGraph");
  432. }
  433. }