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