1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.jcurl.demo.tactics.sg;
21
22 import java.awt.BasicStroke;
23 import java.awt.Color;
24 import java.awt.Cursor;
25 import java.awt.Paint;
26 import java.awt.RenderingHints;
27 import java.awt.Shape;
28 import java.awt.Stroke;
29 import java.awt.event.MouseEvent;
30 import java.awt.geom.AffineTransform;
31 import java.awt.geom.Arc2D;
32 import java.awt.geom.Line2D;
33 import java.awt.geom.Point2D;
34 import java.beans.PropertyChangeEvent;
35 import java.beans.PropertyChangeListener;
36
37 import javax.swing.BoundedRangeModel;
38 import javax.swing.event.ChangeEvent;
39 import javax.swing.event.ChangeListener;
40
41 import org.apache.commons.logging.Log;
42 import org.jcurl.core.api.IceSize;
43 import org.jcurl.core.api.RockProps;
44 import org.jcurl.core.api.RockSet;
45 import org.jcurl.core.log.JCLoggerFactory;
46 import org.jcurl.core.ui.BroomPromptModel;
47 import org.jcurl.core.ui.ChangeManager;
48 import org.jcurl.core.ui.IceShapes;
49 import org.jcurl.core.ui.BroomPromptModel.HandleMemento;
50 import org.jcurl.core.ui.BroomPromptModel.SplitMemento;
51 import org.jcurl.core.ui.BroomPromptModel.XYMemento;
52 import org.jcurl.demo.tactics.HasChanger;
53 import org.jcurl.math.MathVec;
54
55 import com.sun.scenario.scenegraph.SGGroup;
56 import com.sun.scenario.scenegraph.SGNode;
57 import com.sun.scenario.scenegraph.SGShape;
58 import com.sun.scenario.scenegraph.SGTransform;
59 import com.sun.scenario.scenegraph.SGAbstractShape.Mode;
60 import com.sun.scenario.scenegraph.SGTransform.Affine;
61 import com.sun.scenario.scenegraph.event.SGMouseAdapter;
62
63
64
65
66
67 public class BroomPromptScenario implements PropertyChangeListener,
68 ChangeListener, HasChanger {
69
70 private class MoveHandler extends SGMouseAdapter {
71 private XYMemento first = null;
72
73 private XYMemento create(final MouseEvent e, final SGNode node) {
74 final Point2D p = dc2wc.globalToLocal(e.getPoint(), tmp);
75 return new XYMemento(model, p);
76 }
77
78 @Override
79 public void mouseClicked(final MouseEvent e, final SGNode node) {
80 if (e.getClickCount() != 2)
81 return;
82 final HandleMemento pre = new HandleMemento(model, model
83 .getOutTurn());
84 final HandleMemento post = new HandleMemento(model, !model
85 .getOutTurn());
86 getChanger().undoable(pre, post);
87 }
88
89 @Override
90 public void mouseDragged(final MouseEvent e, final SGNode node) {
91 if (first == null)
92 first = new XYMemento(model, model.getBroom());
93 getChanger().temporary(create(e, node));
94 }
95
96 @Override
97 public void mouseReleased(final MouseEvent e, final SGNode node) {
98 if (first != null)
99 getChanger().undoable(first, create(e, node));
100 first = null;
101 }
102 }
103
104 private class SpeedHandler extends SGMouseAdapter {
105 private SplitMemento first = null;
106
107 private SplitMemento create(final MouseEvent e, final SGNode node) {
108 final Point2D p = handle.globalToLocal(e.getPoint(), tmp);
109 double f = 1 - p.getY() / stickLength;
110 if (f > 1)
111 f = 1;
112 if (f < 0)
113 f = 0;
114 final BoundedRangeModel brm = model.getSplitTimeMillis();
115 final int v = (int) (brm.getMinimum() + f
116 * (brm.getMaximum() - brm.getMinimum()));
117 return new SplitMemento(model, v);
118 }
119
120 @Override
121 public void mouseClicked(final MouseEvent e, final SGNode node) {
122 if (e.getClickCount() != 2)
123 return;
124 final HandleMemento pre = new HandleMemento(model, model
125 .getOutTurn());
126 final HandleMemento post = new HandleMemento(model, !model
127 .getOutTurn());
128 getChanger().undoable(pre, post);
129 }
130
131 @Override
132 public void mouseDragged(final MouseEvent e, final SGNode node) {
133 if (first == null)
134 first = new SplitMemento(model, model.getSplitTimeMillis()
135 .getValue());
136
137 getChanger().temporary(create(e, node));
138 }
139
140 @Override
141 public void mouseReleased(final MouseEvent e, final SGNode node) {
142 if (first != null)
143 getChanger().undoable(first, create(e, node));
144 first = null;
145 }
146 }
147
148 private static final Log log = JCLoggerFactory
149 .getLogger(BroomPromptScenario.class);
150
151 private static final Cursor moveC = Cursor
152 .getPredefinedCursor(Cursor.MOVE_CURSOR);
153
154 private static final double scale0 = 0;
155 private static final int scale50 = 50;
156
157 static SGShape node(final Shape sh, final Stroke st, final Paint sp,
158 final double minScale) {
159 final SGShape s = new SGShape();
160 s.setShape(sh);
161 if (sp != null)
162 s.setDrawPaint(sp);
163 if (st != null)
164 s.setDrawStroke(st);
165 s.setAntialiasingHint(RenderingHints.VALUE_ANTIALIAS_ON);
166 if (st != null && sp != null)
167 s.setMode(Mode.STROKE);
168 else if (sp != null)
169 s.setMode(Mode.FILL);
170 return s;
171 }
172
173
174 private static void syncBroomM2V(final Point2D b, final Affine scene) {
175 if (b == null)
176 return;
177 final AffineTransform t = scene.getAffine();
178 t.setToIdentity();
179 t.translate(b.getX(), b.getY());
180 MathVec.rotate(t, b.getX(), b.getY() - IceSize.FAR_HACK_2_TEE);
181 MathVec.rotate(t, 0, 1);
182 scene.setAffine(t);
183 }
184
185 private static void syncHandleM2V(final boolean outTurn, final Affine handle) {
186 final AffineTransform t = handle.getAffine();
187 t.setToIdentity();
188 if (outTurn)
189 t.scale(-1, 1);
190 handle.setAffine(t);
191 }
192
193 private static void syncIndexM2V(final int i16, final SGShape pie) {
194 if (pie != null)
195 pie.setFillPaint(RockSet.isDark(i16) ? Color.RED : Color.YELLOW);
196 }
197
198 private ChangeManager changer;
199 private Affine dc2wc;
200 private final Affine handle;
201 private BroomPromptModel model;
202 private final SGShape pie;
203 private final Affine scene;
204 private final SGShape sli;
205 private final Affine slider;
206 private final float stickLength;
207 private final Point2D tmp = new Point2D.Double();
208
209 public BroomPromptScenario() {
210
211 final boolean stickUp = false;
212 final boolean bothSides = true;
213 final int pieAngle = 150;
214 final Color sp = Color.BLACK;
215 final Color bgc = new Color(1, 1, 1, 0.5f);
216 final Stroke fine = new BasicStroke(0.01f, BasicStroke.CAP_BUTT,
217 BasicStroke.JOIN_MITER);
218 final Stroke bold = new BasicStroke(0.03f, BasicStroke.CAP_ROUND,
219 BasicStroke.JOIN_MITER);
220
221 final float halo = RockProps.DEFAULT.getRadius();
222 final float outer = 0.8f * RockProps.DEFAULT.getRadius();
223 stickLength = (stickUp ? 1 : -1) * 5 * outer;
224 final float inner = 0.5F * outer;
225
226 final SGGroup me = new SGGroup();
227
228 {
229 final SGShape bg = node(new Arc2D.Float(-halo, -halo, 2 * halo,
230 2 * halo, 0, 360, Arc2D.OPEN), null, null, scale0);
231 bg.setFillPaint(bgc);
232 bg.addMouseListener(new MoveHandler());
233 bg.setMouseBlocker(true);
234 bg.setCursor(moveC);
235 me.add(bg);
236 }
237
238 {
239 final int off = 90;
240 final int pieOff = 180;
241 final int arrowLengthDegrees = 7;
242
243 pie = node(new Arc2D.Float(-outer, -outer, 2 * outer, 2 * outer,
244 off - pieOff, pieAngle, Arc2D.PIE), null, null, scale0);
245 me.add(pie);
246
247 me.add(node(new Arc2D.Float(-inner, -inner, 2 * inner, 2 * inner,
248 off, pieOff + pieAngle, Arc2D.OPEN), fine, sp, scale50));
249
250 me.add(node(new Arc2D.Float(-outer, -outer, 2 * outer, 2 * outer,
251 off, pieOff + pieAngle - (14 + arrowLengthDegrees),
252 Arc2D.OPEN), fine, sp, scale50));
253
254
255
256 final double ar = Math.PI * (off + pieAngle) / 180.0;
257
258
259 me.add(node(new Line2D.Double(0, 0, -outer * Math.cos(ar), outer
260 * Math.sin(ar)), bold, sp, scale0));
261
262
263 final float f = outer / 10;
264 final SGShape s = node(IceShapes
265 .createArrowHead(f, 3 * f, 0.5f * f), null, null, scale50);
266 s.setFillPaint(sp);
267 final double a = Math.PI * (off + pieAngle - arrowLengthDegrees)
268 / 180.0;
269 final AffineTransform a_ = new AffineTransform();
270 a_.translate(-outer * Math.cos(a), outer * Math.sin(a));
271 a_.rotate(Math.PI
272 * (90 - (off + pieAngle) + 8 + arrowLengthDegrees) / 180.0);
273 final Affine s_ = SGTransform.createAffine(a_, s);
274 me.add(s_);
275 }
276 {
277 me.add(node(new Line2D.Float(0, -Math.signum(stickLength) * halo,
278 0, stickLength), fine, sp, scale0));
279
280 me.add(node(new Line2D.Float(-halo, 0, halo, 0), fine, sp, scale0));
281 }
282 {
283 sli = new SGShape();
284 sli.setShape(IceShapes.createSlider(0.4f * outer, bothSides));
285
286 sli.setDrawStroke(fine);
287 sli.setDrawPaint(sp);
288 sli.setMode(Mode.STROKE_FILL);
289 sli.setAntialiasingHint(RenderingHints.VALUE_ANTIALIAS_ON);
290 me.add(slider = SGTransform
291 .createAffine(new AffineTransform(), sli));
292 slider.setCursor(moveC);
293 slider.addMouseListener(new SpeedHandler());
294 slider.setMouseBlocker(true);
295 }
296 handle = SGTransform.createAffine(new AffineTransform(), me);
297 scene = SGTransform.createAffine(new AffineTransform(), handle);
298 scene.setVisible(false);
299 }
300
301 public ChangeManager getChanger() {
302 return ChangeManager.getTrivial(changer);
303 }
304
305 public Affine getDc2Wc() {
306 return dc2wc;
307 }
308
309 public BroomPromptModel getModel() {
310 return model;
311 }
312
313 public SGNode getScene() {
314 return scene;
315 }
316
317 public void propertyChange(final PropertyChangeEvent evt) {
318 if (evt.getSource() == model) {
319 if ("idx16".equals(evt.getPropertyName()))
320 syncIndexM2V((Integer) evt.getNewValue(), pie);
321 else if ("outTurn".equals(evt.getPropertyName()))
322 syncHandleM2V((Boolean) evt.getNewValue(), handle);
323 else if ("broom".equals(evt.getPropertyName()))
324 syncBroomM2V((Point2D) evt.getNewValue(), scene);
325 else if ("splitTimeMillis".equals(evt.getPropertyName())) {
326 final BoundedRangeModel os = (BoundedRangeModel) evt
327 .getOldValue();
328 if (os != null)
329 os.removeChangeListener(this);
330 final BoundedRangeModel ns = (BoundedRangeModel) evt
331 .getNewValue();
332 if (ns != null)
333 ns.addChangeListener(this);
334 syncSpeedM2V(ns);
335 } else
336 log.info(evt.getPropertyName() + " " + evt.getSource());
337 } else
338 log.warn("Unconsumed event from " + evt.getSource());
339 }
340
341 public void setChanger(final ChangeManager changer) {
342 final ChangeManager old = this.changer;
343 if (old == changer)
344 return;
345 this.changer = changer;
346
347 }
348
349 public void setDc2wc(final Affine dc2wc) {
350 this.dc2wc = dc2wc;
351 }
352
353 public void setModel(final BroomPromptModel model) {
354 if (this.model == model)
355 return;
356 if (this.model != null) {
357 this.model.removePropertyChangeListener(this);
358 if (this.model.getSplitTimeMillis() != null)
359 this.model.getSplitTimeMillis().removeChangeListener(this);
360 }
361 this.model = model;
362 scene
363 .setVisible(true || this.model != null
364 && model.getBroom() != null);
365 if (this.model != null) {
366
367
368
369
370 this.model.addPropertyChangeListener(this);
371 if (this.model.getSplitTimeMillis() != null)
372 this.model.getSplitTimeMillis().addChangeListener(this);
373 }
374 }
375
376 public void stateChanged(final ChangeEvent e) {
377 if (e.getSource() instanceof BoundedRangeModel)
378 syncSpeedM2V((BoundedRangeModel) e.getSource());
379 else
380 log.warn("Unconsumed event from " + e.getSource());
381 }
382
383 private void syncSpeedM2V(final BoundedRangeModel r) {
384
385 sli.setFillPaint(IceShapes.sliderColor(r));
386 final AffineTransform t = slider.getAffine();
387 t.setToTranslation(0, stickLength * IceShapes.value2ratio(r));
388 slider.setAffine(t);
389 }
390 }