Merge branch 'development' of
[Mograsim.git] / net.mograsim.logic.model.editor / src / net / mograsim / logic / model / editor / handles / WireHandle.java
1 package net.mograsim.logic.model.editor.handles;
2
3 import java.util.Optional;
4
5 import org.eclipse.swt.SWT;
6 import org.eclipse.swt.widgets.Display;
7
8 import net.haspamelodica.swt.helper.gcs.GeneralGC;
9 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
10 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
11 import net.mograsim.logic.model.editor.states.EditorState;
12 import net.mograsim.logic.model.model.wires.GUIWire;
13 import net.mograsim.logic.model.model.wires.GUIWire.PathChangedListener;
14
15 public class WireHandle extends Handle implements PathChangedListener
16 {
17         private boolean selected = false;
18         private final static double WIDTH = 2.0;
19         private final static double WIDTH_SQUARED = WIDTH * WIDTH;
20         public final GUIWire parent;
21
22         public WireHandle(GUIWire parent)
23         {
24                 this.parent = parent;
25                 parent.addPathChangedListener(this);
26                 updateBounds();
27         }
28         
29         @Override
30         void destroy()
31         {
32                 super.destroy();
33                 parent.removePathChangedListener(this);
34         }
35
36         public void updateBounds()
37         {
38                 Rectangle r = parent.getBounds();
39                 moveTo(r.x, r.y);
40                 setSize(r.width, r.height);
41         }
42         
43         @Override
44         public void render(GeneralGC gc)
45         {
46                 if(selected)
47                 {
48                         gc.setLineWidth(WIDTH);
49                         gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_YELLOW));
50                         gc.drawPolyline(parent.getEffectivePath());
51                 }
52         }
53                 
54         @Override
55         public void onSelect()
56         {
57                 selected = true;
58                 callRedrawListeners();
59         }
60
61         @Override
62         public void onDeselect()
63         {
64                 selected = false;
65                 callRedrawListeners();
66         }
67         
68         @Override
69         public void reqDelete()
70         {
71                 parent.destroy();
72         }
73         
74         @Override
75         public boolean contains(double x, double y)
76         {
77                 return click(parent, x, y).isPresent();
78         }
79         
80         @Override
81         public boolean click(double x, double y, int stateMask, EditorState state)
82         {
83                 Optional<WireClickData> op = click(parent, x, y);
84                 if(op.isEmpty())
85                         return false;
86                 WireClickData data = op.get();
87                 return state.clickedHandle(new WireHandleClickInfo(this, data.segment, data.pos, stateMask));
88         }
89         
90         public static class WireHandleClickInfo extends HandleClickInfo
91         {
92                 public final int segment;
93                 public final Point posOnWire;
94                 WireHandleClickInfo(WireHandle clicked, int segment, Point posOnWire, int stateMask)
95                 {
96                         super(clicked, stateMask);
97                         this.segment = segment;
98                         this.posOnWire = posOnWire;
99                 }
100                 
101         }
102         
103         private static Optional<WireClickData> click(GUIWire w, double x, double y)
104         {
105                 Rectangle modifiedBounds = w.getBounds();
106                 modifiedBounds.x -= WIDTH;
107                 modifiedBounds.y -= WIDTH;
108                 modifiedBounds.width += WIDTH * 2;
109                 modifiedBounds.height += WIDTH * 2;
110                 if (modifiedBounds.contains(x, y))
111                 {
112                         double[] effectivePath = w.getEffectivePath();
113                         for (int i = 3; i < effectivePath.length; i += 2)
114                         {
115                                 double a1 = effectivePath[i - 3], a2 = effectivePath[i - 2], b1 = effectivePath[i - 1],
116                                                 b2 = effectivePath[i], r1 = b2 - a2, r2 = a1 - b1;
117
118                                 double f = ((x - a1) * r2 + (a2 - y) * r1) / (-r2 * r2 - r1 * r1);
119                                 if (f >= 0 && f <= 1)
120                                 {
121                                         double e1 = a1 + f * (b1 - a1), e2 = a2 + f * (b2 - a2);
122                                         r1 = e1 - x;
123                                         r2 = e2 - y;
124                                         if (r1 * r1 + r2 * r2 <= WIDTH_SQUARED)
125                                                 return Optional.of(new WireClickData(new Point(e1, e2), (i / 2) - 1));
126                                 }
127                         }
128                 }
129                 return Optional.empty();
130         }
131
132         private final static class WireClickData
133         {
134                 WireClickData(Point pos, int segment)
135                 {
136                         this.pos = pos;
137                         this.segment = segment;
138                 }
139
140                 /**
141                  * Position on the wire that is closest to the click
142                  */
143                 public final Point pos;
144                 /**
145                  * Segment of the wire that the {@link Point} pos is on
146                  */
147                 public final int segment;
148         }
149         
150         @Override
151         public HandleType getType()
152         {
153                 return HandleType.WIRE;
154         }
155
156         @Override
157         public void pathChanged(GUIWire wire, int diff)
158         {
159                 updateBounds();
160         }
161 }