Merge remote-tracking branch 'origin/development' into development
[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
14 public class WireHandle extends Handle
15 {
16         private boolean selected = false;
17         private final static double WIDTH = 2.0;
18         private final static double WIDTH_SQUARED = WIDTH * WIDTH;
19         public final GUIWire parent;
20
21         public WireHandle(GUIWire parent)
22         {
23                 super(5);
24                 this.parent = parent;
25                 parent.addPathChangedListener(c -> updateBounds());
26                 updateBounds();
27         }
28
29         @Override
30         void destroy()
31         {
32                 super.destroy();
33                 parent.removePathChangedListener(c -> updateBounds());
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
95                 WireHandleClickInfo(WireHandle clicked, int segment, Point posOnWire, int stateMask)
96                 {
97                         super(clicked, stateMask);
98                         this.segment = segment;
99                         this.posOnWire = posOnWire;
100                 }
101
102         }
103
104         private static Optional<WireClickData> click(GUIWire w, double x, double y)
105         {
106                 Rectangle modifiedBounds = w.getBounds();
107                 modifiedBounds.x -= WIDTH;
108                 modifiedBounds.y -= WIDTH;
109                 modifiedBounds.width += WIDTH * 2;
110                 modifiedBounds.height += WIDTH * 2;
111                 if (modifiedBounds.contains(x, y))
112                 {
113                         double[] effectivePath = w.getEffectivePath();
114                         for (int i = 3; i < effectivePath.length; i += 2)
115                         {
116                                 double a1 = effectivePath[i - 3], a2 = effectivePath[i - 2], b1 = effectivePath[i - 1], b2 = effectivePath[i], r1 = b2 - a2,
117                                                 r2 = a1 - b1;
118
119                                 double f = ((x - a1) * r2 + (a2 - y) * r1) / (-r2 * r2 - r1 * r1);
120                                 if (f >= 0 && f <= 1)
121                                 {
122                                         double e1 = a1 + f * (b1 - a1), e2 = a2 + f * (b2 - a2);
123                                         r1 = e1 - x;
124                                         r2 = e2 - y;
125                                         if (r1 * r1 + r2 * r2 <= WIDTH_SQUARED)
126                                                 return Optional.of(new WireClickData(new Point(e1, e2), (i / 2) - 1));
127                                 }
128                         }
129                 }
130                 return Optional.empty();
131         }
132
133         private final static class WireClickData
134         {
135                 WireClickData(Point pos, int segment)
136                 {
137                         this.pos = pos;
138                         this.segment = segment;
139                 }
140
141                 /**
142                  * Position on the wire that is closest to the click
143                  */
144                 public final Point pos;
145                 /**
146                  * Segment of the wire that the {@link Point} pos is on
147                  */
148                 public final int segment;
149         }
150
151         @Override
152         public HandleType getType()
153         {
154                 return HandleType.WIRE;
155         }
156 }