-
Notifications
You must be signed in to change notification settings - Fork 35
/
TransformEventHandler2D.java
512 lines (428 loc) · 18.5 KB
/
TransformEventHandler2D.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
/*-
* #%L
* BigDataViewer core classes with minimal dependencies.
* %%
* Copyright (C) 2012 - 2022 BigDataViewer developers.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package bdv;
import net.imglib2.realtransform.AffineTransform3D;
import org.scijava.plugin.Plugin;
import org.scijava.ui.behaviour.Behaviour;
import org.scijava.ui.behaviour.ClickBehaviour;
import org.scijava.ui.behaviour.DragBehaviour;
import org.scijava.ui.behaviour.ScrollBehaviour;
import org.scijava.ui.behaviour.io.gui.CommandDescriptionProvider;
import org.scijava.ui.behaviour.io.gui.CommandDescriptions;
import org.scijava.ui.behaviour.util.Behaviours;
/**
* A {@link TransformEventHandler} that changes an {@link AffineTransform3D}
* through a set of {@link Behaviour}s.
*
* @author Tobias Pietzsch
*/
public class TransformEventHandler2D implements TransformEventHandler
{
// -- behaviour names --
public static final String DRAG_TRANSLATE = "2d drag translate";
public static final String DRAG_ROTATE = "2d drag rotate";
public static final String ZOOM_NORMAL = "2d scroll zoom";
public static final String SCROLL_TRANSLATE = "2d scroll translate";
public static final String SCROLL_ROTATE = "2d scroll rotate";
public static final String ROTATE_LEFT = "2d rotate left";
public static final String ROTATE_RIGHT = "2d rotate right";
public static final String KEY_ZOOM_IN = "2d zoom in";
public static final String KEY_ZOOM_OUT = "2d zoom out";
public static final String ZOOM_FAST = "2d scroll zoom fast";
public static final String SCROLL_TRANSLATE_FAST = "2d scroll translate fast";
public static final String SCROLL_ROTATE_FAST = "2d scroll rotate fast";
public static final String ROTATE_LEFT_FAST = "2d rotate left fast";
public static final String ROTATE_RIGHT_FAST = "2d rotate right fast";
public static final String KEY_ZOOM_IN_FAST = "2d zoom in fast";
public static final String KEY_ZOOM_OUT_FAST = "2d zoom out fast";
public static final String ZOOM_SLOW = "2d scroll zoom slow";
public static final String SCROLL_TRANSLATE_SLOW = "2d scroll translate slow";
public static final String SCROLL_ROTATE_SLOW = "2d scroll rotate slow";
public static final String ROTATE_LEFT_SLOW = "2d rotate left slow";
public static final String ROTATE_RIGHT_SLOW = "2d rotate right slow";
public static final String KEY_ZOOM_IN_SLOW = "2d zoom in slow";
public static final String KEY_ZOOM_OUT_SLOW = "2d zoom out slow";
// -- default shortcuts --
public static final String[] DRAG_TRANSLATE_KEYS = new String[] { "button2", "button3" };
public static final String[] DRAG_ROTATE_KEYS = new String[] { "button1" };
public static final String[] ZOOM_NORMAL_KEYS = new String[] { "scroll", "meta scroll", "ctrl shift scroll" };
public static final String[] SCROLL_TRANSLATE_KEYS = new String[] { "not mapped" };
public static final String[] SCROLL_ROTATE_KEYS = new String[] { "not mapped" };
public static final String[] ROTATE_LEFT_KEYS = new String[] { "LEFT" };
public static final String[] ROTATE_RIGHT_KEYS = new String[] { "RIGHT" };
public static final String[] KEY_ZOOM_IN_KEYS = new String[] { "UP" };
public static final String[] KEY_ZOOM_OUT_KEYS = new String[] { "DOWN" };
public static final String[] ZOOM_FAST_KEYS = new String[] { "shift scroll" };
public static final String[] SCROLL_TRANSLATE_FAST_KEYS = new String[] { "not mapped" };
public static final String[] SCROLL_ROTATE_FAST_KEYS = new String[] { "not mapped" };
public static final String[] ROTATE_LEFT_FAST_KEYS = new String[] { "shift LEFT" };
public static final String[] ROTATE_RIGHT_FAST_KEYS = new String[] { "shift RIGHT" };
public static final String[] KEY_ZOOM_IN_FAST_KEYS = new String[] { "shift UP" };
public static final String[] KEY_ZOOM_OUT_FAST_KEYS = new String[] { "shift DOWN" };
public static final String[] ZOOM_SLOW_KEYS = new String[] { "ctrl scroll" };
public static final String[] SCROLL_TRANSLATE_SLOW_KEYS = new String[] { "not mapped" };
public static final String[] SCROLL_ROTATE_SLOW_KEYS = new String[] { "not mapped" };
public static final String[] ROTATE_LEFT_SLOW_KEYS = new String[] { "ctrl LEFT" };
public static final String[] ROTATE_RIGHT_SLOW_KEYS = new String[] { "ctrl RIGHT" };
public static final String[] KEY_ZOOM_IN_SLOW_KEYS = new String[] { "ctrl UP" };
public static final String[] KEY_ZOOM_OUT_SLOW_KEYS = new String[] { "ctrl DOWN" };
/*
* Command descriptions for all provided commands
*/
@Plugin( type = CommandDescriptionProvider.class )
public static class Descriptions extends CommandDescriptionProvider
{
public Descriptions()
{
super( KeyConfigScopes.BIGDATAVIEWER, KeyConfigContexts.BIGDATAVIEWER );
}
@Override
public void getCommandDescriptions( final CommandDescriptions descriptions )
{
descriptions.add( DRAG_TRANSLATE, DRAG_TRANSLATE_KEYS, "Pan the view by mouse-dragging. Active in 2D mode." );
descriptions.add( DRAG_ROTATE, DRAG_ROTATE_KEYS, "Rotate the view by mouse-dragging. Active in 2D mode." );
descriptions.add( ZOOM_NORMAL, ZOOM_NORMAL_KEYS, "Zoom in by scrolling. Active in 2D mode." );
descriptions.add( ZOOM_FAST, ZOOM_FAST_KEYS, "Zoom in by scrolling (fast). Active in 2D mode." );
descriptions.add( ZOOM_SLOW, ZOOM_SLOW_KEYS, "Zoom in by scrolling (slow). Active in 2D mode." );
descriptions.add( SCROLL_TRANSLATE, SCROLL_TRANSLATE_KEYS, "Translate by scrolling. Active in 2D mode." );
descriptions.add( SCROLL_TRANSLATE_FAST, SCROLL_TRANSLATE_FAST_KEYS, "Translate by scrolling (fast). Active in 2D mode." );
descriptions.add( SCROLL_TRANSLATE_SLOW, SCROLL_TRANSLATE_SLOW_KEYS, "Translate by scrolling (slow). Active in 2D mode." );
descriptions.add( ROTATE_LEFT, ROTATE_LEFT_KEYS, "Rotate left (counter-clockwise) by 1 degree. Active in 2D mode." );
descriptions.add( ROTATE_RIGHT, ROTATE_RIGHT_KEYS, "Rotate right (clockwise) by 1 degree. Active in 2D mode." );
descriptions.add( KEY_ZOOM_IN, KEY_ZOOM_IN_KEYS, "Zoom in. Active in 2D mode." );
descriptions.add( KEY_ZOOM_OUT, KEY_ZOOM_OUT_KEYS, "Zoom out. Active in 2D mode." );
descriptions.add( ROTATE_LEFT_FAST, ROTATE_LEFT_FAST_KEYS, "Rotate left (counter-clockwise) by 10 degrees. Active in 2D mode." );
descriptions.add( ROTATE_RIGHT_FAST, ROTATE_RIGHT_FAST_KEYS, "Rotate right (clockwise) by 10 degrees. Active in 2D mode." );
descriptions.add( KEY_ZOOM_IN_FAST, KEY_ZOOM_IN_FAST_KEYS, "Zoom in (fast). Active in 2D mode." );
descriptions.add( KEY_ZOOM_OUT_FAST, KEY_ZOOM_OUT_FAST_KEYS, "Zoom out (fast). Active in 2D mode." );
descriptions.add( ROTATE_LEFT_SLOW, ROTATE_LEFT_SLOW_KEYS, "Rotate left (counter-clockwise) by 0.1 degree. Active in 2D mode." );
descriptions.add( ROTATE_RIGHT_SLOW, ROTATE_RIGHT_SLOW_KEYS, "Rotate right (clockwise) by 0.1 degree. Active in 2D mode." );
descriptions.add( KEY_ZOOM_IN_SLOW, KEY_ZOOM_IN_SLOW_KEYS, "Zoom in (slow). Active in 2D mode." );
descriptions.add( KEY_ZOOM_OUT_SLOW, KEY_ZOOM_OUT_SLOW_KEYS, "Zoom out (slow). Active in 2D mode." );
descriptions.add( SCROLL_ROTATE, SCROLL_ROTATE_KEYS, "Rotate by scrolling. Active in 2D mode." );
descriptions.add( SCROLL_ROTATE_FAST, SCROLL_ROTATE_FAST_KEYS, "Rotate by scrolling (fast). Active in 2D mode." );
descriptions.add( SCROLL_ROTATE_SLOW, SCROLL_ROTATE_SLOW_KEYS, "Rotate by scrolling (slow). Active in 2D mode." );
}
}
// -- behaviours --
private final DragTranslate dragTranslate;
private final DragRotate dragRotate;
private final Zoom zoom;
private final Zoom zoomFast;
private final Zoom zoomSlow;
private final ScrollTranslate scrollTranslate;
private final ScrollTranslate scrollTranslateFast;
private final ScrollTranslate scrollTranslateSlow;
private final ScrollRotate scrollRotate;
private final ScrollRotate scrollRotateFast;
private final ScrollRotate scrollRotateSlow;
private final KeyRotate keyRotateLeft;
private final KeyRotate keyRotateLeftFast;
private final KeyRotate keyRotateLeftSlow;
private final KeyRotate keyRotateRight;
private final KeyRotate keyRotateRightFast;
private final KeyRotate keyRotateRightSlow;
private final KeyZoom keyZoomIn;
private final KeyZoom keyZoomInFast;
private final KeyZoom keyZoomInSlow;
private final KeyZoom keyZoomOut;
private final KeyZoom keyZoomOutFast;
private final KeyZoom keyZoomOutSlow;
private static final double[] speed = { 1.0, 10.0, 0.1 };
/**
* Copy of transform when mouse dragging started.
*/
private final AffineTransform3D affineDragStart = new AffineTransform3D();
/**
* Current transform during mouse dragging.
*/
private final AffineTransform3D affineDragCurrent = new AffineTransform3D();
/**
* Coordinates where mouse dragging started.
*/
private double oX, oY;
/**
* The screen size of the canvas (the component displaying the image and
* generating mouse events).
*/
private int canvasW = 1, canvasH = 1;
/**
* Screen coordinates to keep centered while zooming or rotating with the
* keyboard. These are set to <em>(canvasW/2, canvasH/2)</em>
*/
private int centerX = 0, centerY = 0;
private final TransformState transform;
public TransformEventHandler2D( final TransformState transform )
{
this.transform = transform;
dragTranslate = new DragTranslate();
dragRotate = new DragRotate();
scrollTranslate = new ScrollTranslate( speed[ 0 ] );
scrollTranslateFast = new ScrollTranslate( speed[ 1 ] );
scrollTranslateSlow = new ScrollTranslate( speed[ 2 ] );
zoom = new Zoom( speed[ 0 ] );
zoomFast = new Zoom( speed[ 1 ] );
zoomSlow = new Zoom( speed[ 2 ] );
scrollRotate = new ScrollRotate( 2 * speed[ 0 ] );
scrollRotateFast = new ScrollRotate( 2 * speed[ 1 ] );
scrollRotateSlow = new ScrollRotate( 2 * speed[ 2 ] );
keyRotateLeft = new KeyRotate( speed[ 0 ] );
keyRotateLeftFast = new KeyRotate( speed[ 1 ] );
keyRotateLeftSlow = new KeyRotate( speed[ 2 ] );
keyRotateRight = new KeyRotate( -speed[ 0 ] );
keyRotateRightFast = new KeyRotate( -speed[ 1 ] );
keyRotateRightSlow = new KeyRotate( -speed[ 2 ] );
keyZoomIn = new KeyZoom( speed[ 0 ] );
keyZoomInFast = new KeyZoom( speed[ 1 ] );
keyZoomInSlow = new KeyZoom( speed[ 2 ] );
keyZoomOut = new KeyZoom( -speed[ 0 ] );
keyZoomOutFast = new KeyZoom( -speed[ 1 ] );
keyZoomOutSlow = new KeyZoom( -speed[ 2 ] );
}
@Override
public void install( final Behaviours behaviours )
{
behaviours.behaviour( dragTranslate, DRAG_TRANSLATE, DRAG_TRANSLATE_KEYS );
behaviours.behaviour( dragRotate, DRAG_ROTATE, DRAG_ROTATE_KEYS );
behaviours.behaviour( scrollTranslate, SCROLL_TRANSLATE, SCROLL_TRANSLATE_KEYS );
behaviours.behaviour( scrollTranslateFast, SCROLL_TRANSLATE_FAST, SCROLL_TRANSLATE_FAST_KEYS );
behaviours.behaviour( scrollTranslateSlow, SCROLL_TRANSLATE_SLOW, SCROLL_TRANSLATE_SLOW_KEYS );
behaviours.behaviour( zoom, ZOOM_NORMAL, ZOOM_NORMAL_KEYS );
behaviours.behaviour( zoomFast, ZOOM_FAST, ZOOM_FAST_KEYS );
behaviours.behaviour( zoomSlow, ZOOM_SLOW, ZOOM_SLOW_KEYS );
behaviours.behaviour( scrollRotate, SCROLL_ROTATE, SCROLL_ROTATE_KEYS );
behaviours.behaviour( scrollRotateFast, SCROLL_ROTATE_FAST, SCROLL_ROTATE_FAST_KEYS );
behaviours.behaviour( scrollRotateSlow, SCROLL_ROTATE_SLOW, SCROLL_ROTATE_SLOW_KEYS );
behaviours.behaviour( keyRotateLeft, ROTATE_LEFT, ROTATE_LEFT_KEYS );
behaviours.behaviour( keyRotateLeftFast, ROTATE_LEFT_FAST, ROTATE_LEFT_FAST_KEYS );
behaviours.behaviour( keyRotateLeftSlow, ROTATE_LEFT_SLOW, ROTATE_LEFT_SLOW_KEYS );
behaviours.behaviour( keyRotateRight, ROTATE_RIGHT, ROTATE_RIGHT_KEYS );
behaviours.behaviour( keyRotateRightFast, ROTATE_RIGHT_FAST, ROTATE_RIGHT_FAST_KEYS );
behaviours.behaviour( keyRotateRightSlow, ROTATE_RIGHT_SLOW, ROTATE_RIGHT_SLOW_KEYS );
behaviours.behaviour( keyZoomIn, KEY_ZOOM_IN, KEY_ZOOM_IN_KEYS );
behaviours.behaviour( keyZoomInFast, KEY_ZOOM_IN_FAST, KEY_ZOOM_IN_FAST_KEYS );
behaviours.behaviour( keyZoomInSlow, KEY_ZOOM_IN_SLOW, KEY_ZOOM_IN_SLOW_KEYS );
behaviours.behaviour( keyZoomOut, KEY_ZOOM_OUT, KEY_ZOOM_OUT_KEYS );
behaviours.behaviour( keyZoomOutFast, KEY_ZOOM_OUT_FAST, KEY_ZOOM_OUT_FAST_KEYS );
behaviours.behaviour( keyZoomOutSlow, KEY_ZOOM_OUT_SLOW, KEY_ZOOM_OUT_SLOW_KEYS );
}
@Override
public void setCanvasSize( final int width, final int height, final boolean updateTransform )
{
if ( width == 0 || height == 0 ) {
// NB: We are probably in some intermediate layout scenario.
// Attempting to trigger a transform update with 0 size will result
// in the exception "Matrix is singular" from imglib2-realtransform.
return;
}
if ( updateTransform )
{
final AffineTransform3D affine = transform.get();
affine.set( affine.get( 0, 3 ) - canvasW / 2, 0, 3 );
affine.set( affine.get( 1, 3 ) - canvasH / 2, 1, 3 );
affine.scale( ( double ) width / canvasW );
affine.set( affine.get( 0, 3 ) + width / 2, 0, 3 );
affine.set( affine.get( 1, 3 ) + height / 2, 1, 3 );
transform.set( affine );
}
canvasW = width;
canvasH = height;
centerX = width / 2;
centerY = height / 2;
}
/**
* One step of rotation (radian).
*/
final private static double step = Math.PI / 180;
private void scale( final double s, final double x, final double y )
{
final AffineTransform3D affine = transform.get();
// center shift
affine.set( affine.get( 0, 3 ) - x, 0, 3 );
affine.set( affine.get( 1, 3 ) - y, 1, 3 );
// scale
affine.scale( s );
// center un-shift
affine.set( affine.get( 0, 3 ) + x, 0, 3 );
affine.set( affine.get( 1, 3 ) + y, 1, 3 );
transform.set( affine );
}
/**
* Rotate by d radians around Z axis. Keep screen coordinates {@code (centerX, centerY)} fixed.
*/
private void rotate( final AffineTransform3D affine, final double d )
{
// center shift
affine.set( affine.get( 0, 3 ) - centerX, 0, 3 );
affine.set( affine.get( 1, 3 ) - centerY, 1, 3 );
// rotate
affine.rotate( 2, d );
// center un-shift
affine.set( affine.get( 0, 3 ) + centerX, 0, 3 );
affine.set( affine.get( 1, 3 ) + centerY, 1, 3 );
}
private class DragRotate implements DragBehaviour
{
@Override
public void init( final int x, final int y )
{
oX = x;
oY = y;
transform.get( affineDragStart );
}
@Override
public void drag( final int x, final int y )
{
final double dX = x - centerX;
final double dY = y - centerY;
final double odX = oX - centerX;
final double odY = oY - centerY;
final double theta = Math.atan2( dY, dX ) - Math.atan2( odY, odX );
affineDragCurrent.set( affineDragStart );
rotate( affineDragCurrent, theta );
transform.set( affineDragCurrent );
}
@Override
public void end( final int x, final int y )
{}
}
private class ScrollRotate implements ScrollBehaviour
{
private final double speed;
public ScrollRotate( final double speed )
{
this.speed = speed;
}
@Override
public void scroll( final double wheelRotation, final boolean isHorizontal, final int x, final int y )
{
final AffineTransform3D affine = transform.get();
final double theta = speed * wheelRotation * Math.PI / 180.0;
// center shift
affine.set( affine.get( 0, 3 ) - x, 0, 3 );
affine.set( affine.get( 1, 3 ) - y, 1, 3 );
affine.rotate( 2, theta );
// center un-shift
affine.set( affine.get( 0, 3 ) + x, 0, 3 );
affine.set( affine.get( 1, 3 ) + y, 1, 3 );
transform.set( affine );
}
}
private class DragTranslate implements DragBehaviour
{
@Override
public void init( final int x, final int y )
{
oX = x;
oY = y;
transform.get( affineDragStart );
}
@Override
public void drag( final int x, final int y )
{
final double dX = oX - x;
final double dY = oY - y;
affineDragCurrent.set( affineDragStart );
affineDragCurrent.set( affineDragCurrent.get( 0, 3 ) - dX, 0, 3 );
affineDragCurrent.set( affineDragCurrent.get( 1, 3 ) - dY, 1, 3 );
transform.set( affineDragCurrent );
}
@Override
public void end( final int x, final int y )
{}
}
private class ScrollTranslate implements ScrollBehaviour
{
private final double speed;
public ScrollTranslate( final double speed )
{
this.speed = speed;
}
@Override
public void scroll( final double wheelRotation, final boolean isHorizontal, final int x, final int y )
{
final AffineTransform3D affine = transform.get();
final double d = -wheelRotation * 10 * speed;
if ( isHorizontal )
affine.translate( d, 0, 0 );
else
affine.translate( 0, d, 0 );
transform.set( affine );
}
}
private class Zoom implements ScrollBehaviour
{
private final double speed;
public Zoom( final double speed )
{
this.speed = speed;
}
@Override
public void scroll( final double wheelRotation, final boolean isHorizontal, final int x, final int y )
{
final double s = speed * wheelRotation;
final double dScale = 1.0 + 0.05 * Math.abs( s );
if ( s > 0 )
scale( 1.0 / dScale, x, y );
else
scale( dScale, x, y );
}
}
private class KeyRotate implements ClickBehaviour
{
private final double speed;
public KeyRotate( final double speed )
{
this.speed = speed;
}
@Override
public void click( final int x, final int y )
{
final AffineTransform3D affine = transform.get();
rotate( affine, step * speed );
transform.set( affine );
}
}
private class KeyZoom implements ClickBehaviour
{
private final double dScale;
public KeyZoom( final double speed )
{
if ( speed > 0 )
dScale = 1.0 + 0.1 * speed;
else
dScale = 1.0 / ( 1.0 - 0.1 * speed );
}
@Override
public void click( final int x, final int y )
{
scale( dScale, centerX, centerY );
}
}
}