-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Expand file tree
/
Copy pathinteraction.js
More file actions
888 lines (835 loc) · 26.8 KB
/
interaction.js
File metadata and controls
888 lines (835 loc) · 26.8 KB
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
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
/**
* @module 3D
* @submodule Interaction
* @for p5
* @requires core
*/
import p5 from '../core/main';
import * as constants from '../core/constants';
/**
* Allows the user to orbit around a 3D sketch using a mouse, trackpad, or
* touchscreen.
*
* 3D sketches are viewed through an imaginary camera. Calling
* `orbitControl()` within the <a href="#/p5/draw">draw()</a> function allows
* the user to change the camera’s position:
*
* ```js
* function draw() {
* background(200);
*
* // Enable orbiting with the mouse.
* orbitControl();
*
* // Rest of sketch.
* }
* ```
*
* Left-clicking and dragging or swipe motion will rotate the camera position
* about the center of the sketch. Right-clicking and dragging or multi-swipe
* will pan the camera position without rotation. Using the mouse wheel
* (scrolling) or pinch in/out will move the camera further or closer from the
* center of the sketch.
*
* The first three parameters, `sensitivityX`, `sensitivityY`, and
* `sensitivityZ`, are optional. They’re numbers that set the sketch’s
* sensitivity to movement along each axis. For example, calling
* `orbitControl(1, 2, -1)` keeps movement along the x-axis at its default
* value, makes the sketch twice as sensitive to movement along the y-axis,
* and reverses motion along the z-axis. By default, all sensitivity values
* are 1.
*
* The fourth parameter, `options`, is also optional. It’s an object that
* changes the behavior of orbiting. For example, calling
* `orbitControl(1, 1, 1, options)` keeps the default sensitivity values while
* changing the behaviors set with `options`. The object can have the
* following properties:
*
* ```js
* let options = {
* // Setting this to false makes mobile interactions smoother by
* // preventing accidental interactions with the page while orbiting.
* // By default, it's true.
* disableTouchActions: true,
*
* // Setting this to true makes the camera always rotate in the
* // direction the mouse/touch is moving.
* // By default, it's false.
* freeRotation: false
* };
*
* orbitControl(1, 1, 1, options);
* ```
*
* @method orbitControl
* @for p5
* @param {Number} [sensitivityX] sensitivity to movement along the x-axis. Defaults to 1.
* @param {Number} [sensitivityY] sensitivity to movement along the y-axis. Defaults to 1.
* @param {Number} [sensitivityZ] sensitivity to movement along the z-axis. Defaults to 1.
* @param {Object} [options] object with two optional properties, `disableTouchActions`
* and `freeRotation`. Both are `Boolean`s. `disableTouchActions`
* defaults to `true` and `freeRotation` defaults to `false`.
* @chainable
*
* @example
* <div>
* <code>
* // Click and drag the mouse to view the scene from different angles.
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* describe('A multicolor box on a gray background. The camera angle changes when the user interacts using a mouse, trackpad, or touchscreen.');
* }
*
* function draw() {
* background(200);
*
* // Enable orbiting with the mouse.
* orbitControl();
*
* // Style the box.
* normalMaterial();
*
* // Draw the box.
* box(30, 50);
* }
* </code>
* </div>
*
* <div>
* <code>
* // Click and drag the mouse to view the scene from different angles.
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* describe('A multicolor box on a gray background. The camera angle changes when the user interacts using a mouse, trackpad, or touchscreen.');
* }
*
* function draw() {
* background(200);
*
* // Enable orbiting with the mouse.
* // Make the interactions 3X sensitive.
* orbitControl(3, 3, 3);
*
* // Style the box.
* normalMaterial();
*
* // Draw the box.
* box(30, 50);
* }
* </code>
* </div>
*
* <div>
* <code>
* // Click and drag the mouse to view the scene from different angles.
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* describe('A multicolor box on a gray background. The camera angle changes when the user interacts using a mouse, trackpad, or touchscreen.');
* }
*
* function draw() {
* background(200);
*
* // Create an options object.
* let options = {
* disableTouchActions: false,
* freeRotation: true
* };
*
* // Enable orbiting with the mouse.
* // Prevent accidental touch actions on touchscreen devices
* // and enable free rotation.
* orbitControl(1, 1, 1, options);
*
* // Style the box.
* normalMaterial();
*
* // Draw the box.
* box(30, 50);
* }
* </code>
* </div>
*/
// implementation based on three.js 'orbitControls':
// https://github.com/mrdoob/three.js/blob/6afb8595c0bf8b2e72818e42b64e6fe22707d896/examples/jsm/controls/OrbitControls.js#L22
p5.prototype.orbitControl = function(
sensitivityX,
sensitivityY,
sensitivityZ,
options
) {
this._assert3d('orbitControl');
p5._validateParameters('orbitControl', arguments);
const cam = this._renderer._curCamera;
if (typeof sensitivityX === 'undefined') {
sensitivityX = 1;
}
if (typeof sensitivityY === 'undefined') {
sensitivityY = sensitivityX;
}
if (typeof sensitivityZ === 'undefined') {
sensitivityZ = 1;
}
if (typeof options !== 'object') {
options = {};
}
// default right-mouse and mouse-wheel behaviors (context menu and scrolling,
// respectively) are disabled here to allow use of those events for panning and
// zooming. However, whether or not to disable touch actions is an option.
// disable context menu for canvas element and add 'contextMenuDisabled'
// flag to p5 instance
if (this.contextMenuDisabled !== true) {
this.canvas.oncontextmenu = () => false;
this._setProperty('contextMenuDisabled', true);
}
// disable default scrolling behavior on the canvas element and add
// 'wheelDefaultDisabled' flag to p5 instance
if (this.wheelDefaultDisabled !== true) {
this.canvas.onwheel = () => false;
this._setProperty('wheelDefaultDisabled', true);
}
// disable default touch behavior on the canvas element and add
// 'touchActionsDisabled' flag to p5 instance
const { disableTouchActions = true } = options;
if (this.touchActionsDisabled !== true && disableTouchActions) {
this.canvas.style['touch-action'] = 'none';
this._setProperty('touchActionsDisabled', true);
}
// If option.freeRotation is true, the camera always rotates freely in the direction
// the pointer moves. default value is false (normal behavior)
const { freeRotation = false } = options;
// get moved touches.
const movedTouches = [];
this.touches.forEach(curTouch => {
this._renderer.prevTouches.forEach(prevTouch => {
if (curTouch.id === prevTouch.id) {
const movedTouch = {
x: curTouch.x,
y: curTouch.y,
px: prevTouch.x,
py: prevTouch.y
};
movedTouches.push(movedTouch);
}
});
});
this._renderer.prevTouches = this.touches;
// The idea of using damping is based on the following website. thank you.
// https://github.com/freshfork/p5.EasyCam/blob/9782964680f6a5c4c9bee825c475d9f2021d5134/p5.easycam.js#L1124
// variables for interaction
let deltaRadius = 0;
let deltaTheta = 0;
let deltaPhi = 0;
let moveDeltaX = 0;
let moveDeltaY = 0;
// constants for dampingProcess
const damping = 0.85;
const rotateAccelerationFactor = 0.6;
const moveAccelerationFactor = 0.15;
// For touches, the appropriate scale is different
// because the distance difference is multiplied.
const mouseZoomScaleFactor = 0.01;
const touchZoomScaleFactor = 0.0004;
const scaleFactor = this.height < this.width ? this.height : this.width;
// Flag whether the mouse or touch pointer is inside the canvas
let pointersInCanvas = false;
// calculate and determine flags and variables.
if (movedTouches.length > 0) {
/* for touch */
// if length === 1, rotate
// if length > 1, zoom and move
// for touch, it is calculated based on one moved touch pointer position.
pointersInCanvas =
movedTouches[0].x > 0 && movedTouches[0].x < this.width &&
movedTouches[0].y > 0 && movedTouches[0].y < this.height;
if (movedTouches.length === 1) {
const t = movedTouches[0];
deltaTheta = -sensitivityX * (t.x - t.px) / scaleFactor;
deltaPhi = sensitivityY * (t.y - t.py) / scaleFactor;
} else {
const t0 = movedTouches[0];
const t1 = movedTouches[1];
const distWithTouches = Math.hypot(t0.x - t1.x, t0.y - t1.y);
const prevDistWithTouches = Math.hypot(t0.px - t1.px, t0.py - t1.py);
const changeDist = distWithTouches - prevDistWithTouches;
// move the camera farther when the distance between the two touch points
// decreases, move the camera closer when it increases.
deltaRadius = -changeDist * sensitivityZ * touchZoomScaleFactor;
// Move the center of the camera along with the movement of
// the center of gravity of the two touch points.
moveDeltaX = 0.5 * (t0.x + t1.x) - 0.5 * (t0.px + t1.px);
moveDeltaY = 0.5 * (t0.y + t1.y) - 0.5 * (t0.py + t1.py);
}
if (this.touches.length > 0) {
if (pointersInCanvas) {
// Initiate an interaction if touched in the canvas
this._renderer.executeRotateAndMove = true;
this._renderer.executeZoom = true;
}
} else {
// End an interaction when the touch is released
this._renderer.executeRotateAndMove = false;
this._renderer.executeZoom = false;
}
} else {
/* for mouse */
// if wheelDeltaY !== 0, zoom
// if mouseLeftButton is down, rotate
// if mouseRightButton is down, move
// For mouse, it is calculated based on the mouse position.
pointersInCanvas =
(this.mouseX > 0 && this.mouseX < this.width) &&
(this.mouseY > 0 && this.mouseY < this.height);
if (this._mouseWheelDeltaY !== 0) {
// zoom the camera depending on the value of _mouseWheelDeltaY.
// move away if positive, move closer if negative
deltaRadius = Math.sign(this._mouseWheelDeltaY) * sensitivityZ;
deltaRadius *= mouseZoomScaleFactor;
this._mouseWheelDeltaY = 0;
// start zoom when the mouse is wheeled within the canvas.
if (pointersInCanvas) this._renderer.executeZoom = true;
} else {
// quit zoom when you stop wheeling.
this._renderer.executeZoom = false;
}
if (this.mouseIsPressed) {
if (this.mouseButton === this.LEFT) {
deltaTheta = -sensitivityX * this.movedX / scaleFactor;
deltaPhi = sensitivityY * this.movedY / scaleFactor;
} else if (this.mouseButton === this.RIGHT) {
moveDeltaX = this.movedX;
moveDeltaY = this.movedY * cam.yScale;
}
// start rotate and move when mouse is pressed within the canvas.
if (pointersInCanvas) this._renderer.executeRotateAndMove = true;
} else {
// quit rotate and move if mouse is released.
this._renderer.executeRotateAndMove = false;
}
}
// interactions
// zoom process
if (deltaRadius !== 0 && this._renderer.executeZoom) {
// accelerate zoom velocity
this._renderer.zoomVelocity += deltaRadius;
}
if (Math.abs(this._renderer.zoomVelocity) > 0.001) {
// if freeRotation is true, we use _orbitFree() instead of _orbit()
if (freeRotation) {
cam._orbitFree(
0, 0, this._renderer.zoomVelocity
);
} else {
cam._orbit(
0, 0, this._renderer.zoomVelocity
);
}
// In orthogonal projection, the scale does not change even if
// the distance to the gaze point is changed, so the projection matrix
// needs to be modified.
if (cam.projMatrix.mat4[15] !== 0) {
cam.projMatrix.mat4[0] *= Math.pow(
10, -this._renderer.zoomVelocity
);
cam.projMatrix.mat4[5] *= Math.pow(
10, -this._renderer.zoomVelocity
);
// modify uPMatrix
this._renderer.uPMatrix.mat4[0] = cam.projMatrix.mat4[0];
this._renderer.uPMatrix.mat4[5] = cam.projMatrix.mat4[5];
}
// damping
this._renderer.zoomVelocity *= damping;
} else {
this._renderer.zoomVelocity = 0;
}
// rotate process
if ((deltaTheta !== 0 || deltaPhi !== 0) &&
this._renderer.executeRotateAndMove) {
// accelerate rotate velocity
this._renderer.rotateVelocity.add(
deltaTheta * rotateAccelerationFactor,
deltaPhi * rotateAccelerationFactor
);
}
if (this._renderer.rotateVelocity.magSq() > 0.000001) {
// if freeRotation is true, the camera always rotates freely in the direction the pointer moves
if (freeRotation) {
cam._orbitFree(
-this._renderer.rotateVelocity.x,
this._renderer.rotateVelocity.y,
0
);
} else {
cam._orbit(
this._renderer.rotateVelocity.x,
this._renderer.rotateVelocity.y,
0
);
}
// damping
this._renderer.rotateVelocity.mult(damping);
} else {
this._renderer.rotateVelocity.set(0, 0);
}
// move process
if ((moveDeltaX !== 0 || moveDeltaY !== 0) &&
this._renderer.executeRotateAndMove) {
// Normalize movement distance
const ndcX = moveDeltaX * 2/this.width;
const ndcY = -moveDeltaY * 2/this.height;
// accelerate move velocity
this._renderer.moveVelocity.add(
ndcX * moveAccelerationFactor,
ndcY * moveAccelerationFactor
);
}
if (this._renderer.moveVelocity.magSq() > 0.000001) {
// Translate the camera so that the entire object moves
// perpendicular to the line of sight when the mouse is moved
// or when the centers of gravity of the two touch pointers move.
const local = cam._getLocalAxes();
// Calculate the z coordinate in the view coordinates of
// the center, that is, the distance to the view point
const diffX = cam.eyeX - cam.centerX;
const diffY = cam.eyeY - cam.centerY;
const diffZ = cam.eyeZ - cam.centerZ;
const viewZ = Math.sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ);
// position vector of the center.
let cv = new p5.Vector(cam.centerX, cam.centerY, cam.centerZ);
// Calculate the normalized device coordinates of the center.
cv = cam.cameraMatrix.multiplyPoint(cv);
cv = this._renderer.uPMatrix.multiplyAndNormalizePoint(cv);
// Move the center by this distance
// in the normalized device coordinate system.
cv.x -= this._renderer.moveVelocity.x;
cv.y -= this._renderer.moveVelocity.y;
// Calculate the translation vector
// in the direction perpendicular to the line of sight of center.
let dx, dy;
const uP = this._renderer.uPMatrix.mat4;
if (uP[15] === 0) {
dx = ((uP[8] + cv.x)/uP[0]) * viewZ;
dy = ((uP[9] + cv.y)/uP[5]) * viewZ;
} else {
dx = (cv.x - uP[12])/uP[0];
dy = (cv.y - uP[13])/uP[5];
}
// translate the camera.
cam.setPosition(
cam.eyeX + dx * local.x[0] + dy * local.y[0],
cam.eyeY + dx * local.x[1] + dy * local.y[1],
cam.eyeZ + dx * local.x[2] + dy * local.y[2]
);
// damping
this._renderer.moveVelocity.mult(damping);
} else {
this._renderer.moveVelocity.set(0, 0);
}
return this;
};
/**
* Adds a grid and an axes icon to clarify orientation in 3D sketches.
*
* `debugMode()` adds a grid that shows where the “ground” is in a sketch. By
* default, the grid will run through the origin `(0, 0, 0)` of the sketch
* along the XZ plane. `debugMode()` also adds an axes icon that points along
* the positive x-, y-, and z-axes. Calling `debugMode()` displays the grid
* and axes icon with their default size and position.
*
* There are four ways to call `debugMode()` with optional parameters to
* customize the debugging environment.
*
* The first way to call `debugMode()` has one parameter, `mode`. If the
* system constant `GRID` is passed, as in `debugMode(GRID)`, then the grid
* will be displayed and the axes icon will be hidden. If the constant `AXES`
* is passed, as in `debugMode(AXES)`, then the axes icon will be displayed
* and the grid will be hidden.
*
* The second way to call `debugMode()` has six parameters. The first
* parameter, `mode`, selects either `GRID` or `AXES` to be displayed. The
* next five parameters, `gridSize`, `gridDivisions`, `xOff`, `yOff`, and
* `zOff` are optional. They’re numbers that set the appearance of the grid
* (`gridSize` and `gridDivisions`) and the placement of the axes icon
* (`xOff`, `yOff`, and `zOff`). For example, calling
* `debugMode(20, 5, 10, 10, 10)` sets the `gridSize` to 20 pixels, the number
* of `gridDivisions` to 5, and offsets the axes icon by 10 pixels along the
* x-, y-, and z-axes.
*
* The third way to call `debugMode()` has five parameters. The first
* parameter, `mode`, selects either `GRID` or `AXES` to be displayed. The
* next four parameters, `axesSize`, `xOff`, `yOff`, and `zOff` are optional.
* They’re numbers that set the appearance of the size of the axes icon
* (`axesSize`) and its placement (`xOff`, `yOff`, and `zOff`).
*
* The fourth way to call `debugMode()` has nine optional parameters. The
* first five parameters, `gridSize`, `gridDivisions`, `gridXOff`, `gridYOff`,
* and `gridZOff` are numbers that set the appearance of the grid. For
* example, calling `debugMode(100, 5, 0, 0, 0)` sets the `gridSize` to 100,
* the number of `gridDivisions` to 5, and sets all the offsets to 0 so that
* the grid is centered at the origin. The next four parameters, `axesSize`,
* `xOff`, `yOff`, and `zOff` are numbers that set the appearance of the size
* of the axes icon (`axesSize`) and its placement (`axesXOff`, `axesYOff`,
* and `axesZOff`). For example, calling
* `debugMode(100, 5, 0, 0, 0, 50, 10, 10, 10)` sets the `gridSize` to 100,
* the number of `gridDivisions` to 5, and sets all the offsets to 0 so that
* the grid is centered at the origin. It then sets the `axesSize` to 50 and
* offsets the icon 10 pixels along each axis.
*
* @method debugMode
*
* @example
* <div>
* <code>
* // Click and drag the mouse to view the scene from different angles.
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* // Enable debug mode.
* debugMode();
*
* describe('A multicolor box on a gray background. A grid and axes icon are displayed near the box.');
* }
*
* function draw() {
* background(200);
*
* // Enable orbiting with the mouse.
* orbitControl();
*
* // Style the box.
* normalMaterial();
*
* // Draw the box.
* box(20, 40);
* }
* </code>
* </div>
*
* <div>
* <code>
* // Click and drag the mouse to view the scene from different angles.
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* // Enable debug mode.
* // Only display the axes icon.
* debugMode(AXES);
*
* describe('A multicolor box on a gray background. A grid and axes icon are displayed near the box.');
* }
*
* function draw() {
* background(200);
*
* // Enable orbiting with the mouse.
* orbitControl();
*
* // Style the box.
* normalMaterial();
*
* // Draw the box.
* box(20, 40);
* }
* </code>
* </div>
*
* <div>
* <code>
* // Click and drag the mouse to view the scene from different angles.
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* // Enable debug mode.
* // Only display the grid and customize it:
* // - size: 50
* // - divisions: 10
* // - offsets: 0, 20, 0
* debugMode(GRID, 50, 10, 0, 20, 0);
*
* describe('A multicolor box on a gray background. A grid is displayed below the box.');
* }
*
* function draw() {
* background(200);
*
* // Enable orbiting with the mouse.
* orbitControl();
*
* // Style the box.
* normalMaterial();
*
* // Draw the box.
* box(20, 40);
* }
* </code>
* </div>
*
* <div>
* <code>
* // Click and drag the mouse to view the scene from different angles.
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* // Enable debug mode.
* // Display the grid and axes icon and customize them:
* // Grid
* // ----
* // - size: 50
* // - divisions: 10
* // - offsets: 0, 20, 0
* // Axes
* // ----
* // - size: 50
* // - offsets: 0, 0, 0
* debugMode(50, 10, 0, 20, 0, 50, 0, 0, 0);
*
* describe('A multicolor box on a gray background. A grid is displayed below the box. An axes icon is displayed at the center of the box.');
* }
*
* function draw() {
* background(200);
*
* // Enable orbiting with the mouse.
* orbitControl();
*
* // Style the box.
* normalMaterial();
*
* // Draw the box.
* box(20, 40);
* }
* </code>
* </div>
*/
/**
* @method debugMode
* @param {Constant} mode either GRID or AXES
*/
/**
* @method debugMode
* @param {Constant} mode
* @param {Number} [gridSize] side length of the grid.
* @param {Number} [gridDivisions] number of divisions in the grid.
* @param {Number} [xOff] offset from origin along the x-axis.
* @param {Number} [yOff] offset from origin along the y-axis.
* @param {Number} [zOff] offset from origin along the z-axis.
*/
/**
* @method debugMode
* @param {Constant} mode
* @param {Number} [axesSize] length of axes icon markers.
* @param {Number} [xOff]
* @param {Number} [yOff]
* @param {Number} [zOff]
*/
/**
* @method debugMode
* @param {Number} [gridSize]
* @param {Number} [gridDivisions]
* @param {Number} [gridXOff] grid offset from the origin along the x-axis.
* @param {Number} [gridYOff] grid offset from the origin along the y-axis.
* @param {Number} [gridZOff] grid offset from the origin along the z-axis.
* @param {Number} [axesSize]
* @param {Number} [axesXOff] axes icon offset from the origin along the x-axis.
* @param {Number} [axesYOff] axes icon offset from the origin along the y-axis.
* @param {Number} [axesZOff] axes icon offset from the origin along the z-axis.
*/
p5.prototype.debugMode = function(...args) {
this._assert3d('debugMode');
p5._validateParameters('debugMode', args);
// start by removing existing 'post' registered debug methods
for (let i = this._registeredMethods.post.length - 1; i >= 0; i--) {
// test for equality...
if (
this._registeredMethods.post[i].toString() === this._grid().toString() ||
this._registeredMethods.post[i].toString() === this._axesIcon().toString()
) {
this._registeredMethods.post.splice(i, 1);
}
}
// then add new debugMode functions according to the argument list
if (args[0] === constants.GRID) {
this.registerMethod(
'post',
this._grid(args[1], args[2], args[3], args[4], args[5])
);
} else if (args[0] === constants.AXES) {
this.registerMethod(
'post',
this._axesIcon(args[1], args[2], args[3], args[4])
);
} else {
this.registerMethod(
'post',
this._grid(args[0], args[1], args[2], args[3], args[4])
);
this.registerMethod(
'post',
this._axesIcon(args[5], args[6], args[7], args[8])
);
}
};
/**
* Turns off <a href="#/p5/debugMode">debugMode()</a> in a 3D sketch.
*
* @method noDebugMode
*
* @example
* <div>
* <code>
* // Click and drag the mouse to view the scene from different angles.
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* // Enable debug mode.
* debugMode();
*
* describe('A multicolor box on a gray background. A grid and axes icon are displayed near the box. They disappear when the user double-clicks.');
* }
*
* function draw() {
* background(200);
*
* // Enable orbiting with the mouse.
* orbitControl();
*
* // Style the box.
* normalMaterial();
*
* // Draw the box. box(20, 40);
* }
*
* // Disable debug mode when the user double-clicks.
* function doubleClicked() {
* noDebugMode();
* }
* </code>
* </div>
*/
p5.prototype.noDebugMode = function() {
this._assert3d('noDebugMode');
// start by removing existing 'post' registered debug methods
for (let i = this._registeredMethods.post.length - 1; i >= 0; i--) {
// test for equality...
if (
this._registeredMethods.post[i].toString() === this._grid().toString() ||
this._registeredMethods.post[i].toString() === this._axesIcon().toString()
) {
this._registeredMethods.post.splice(i, 1);
}
}
};
/**
* For use with debugMode
* @private
* @method _grid
* @param {Number} [size] size of grid sides
* @param {Number} [div] number of grid divisions
* @param {Number} [xOff] offset of grid center from origin in X axis
* @param {Number} [yOff] offset of grid center from origin in Y axis
* @param {Number} [zOff] offset of grid center from origin in Z axis
*/
p5.prototype._grid = function(size, numDivs, xOff, yOff, zOff) {
if (typeof size === 'undefined') {
size = this.width / 2;
}
if (typeof numDivs === 'undefined') {
// ensure at least 2 divisions
numDivs = Math.round(size / 30) < 4 ? 4 : Math.round(size / 30);
}
if (typeof xOff === 'undefined') {
xOff = 0;
}
if (typeof yOff === 'undefined') {
yOff = 0;
}
if (typeof zOff === 'undefined') {
zOff = 0;
}
const spacing = size / numDivs;
const halfSize = size / 2;
return function() {
this.push();
this.stroke(
this._renderer.curStrokeColor[0] * 255,
this._renderer.curStrokeColor[1] * 255,
this._renderer.curStrokeColor[2] * 255
);
this._renderer.uModelMatrix.reset();
// Lines along X axis
for (let q = 0; q <= numDivs; q++) {
this.beginShape(this.LINES);
this.vertex(-halfSize + xOff, yOff, q * spacing - halfSize + zOff);
this.vertex(+halfSize + xOff, yOff, q * spacing - halfSize + zOff);
this.endShape();
}
// Lines along Z axis
for (let i = 0; i <= numDivs; i++) {
this.beginShape(this.LINES);
this.vertex(i * spacing - halfSize + xOff, yOff, -halfSize + zOff);
this.vertex(i * spacing - halfSize + xOff, yOff, +halfSize + zOff);
this.endShape();
}
this.pop();
};
};
/**
* For use with debugMode
* @private
* @method _axesIcon
* @param {Number} [size] size of axes icon lines
* @param {Number} [xOff] offset of icon from origin in X axis
* @param {Number} [yOff] offset of icon from origin in Y axis
* @param {Number} [zOff] offset of icon from origin in Z axis
*/
p5.prototype._axesIcon = function(size, xOff, yOff, zOff) {
if (typeof size === 'undefined') {
size = this.width / 20 > 40 ? this.width / 20 : 40;
}
if (typeof xOff === 'undefined') {
xOff = -this.width / 4;
}
if (typeof yOff === 'undefined') {
yOff = xOff;
}
if (typeof zOff === 'undefined') {
zOff = xOff;
}
return function() {
this.push();
this._renderer.uModelMatrix.reset();
// X axis
this.strokeWeight(2);
this.stroke(255, 0, 0);
this.beginShape(this.LINES);
this.vertex(xOff, yOff, zOff);
this.vertex(xOff + size, yOff, zOff);
this.endShape();
// Y axis
this.stroke(0, 255, 0);
this.beginShape(this.LINES);
this.vertex(xOff, yOff, zOff);
this.vertex(xOff, yOff + size, zOff);
this.endShape();
// Z axis
this.stroke(0, 0, 255);
this.beginShape(this.LINES);
this.vertex(xOff, yOff, zOff);
this.vertex(xOff, yOff, zOff + size);
this.endShape();
this.pop();
};
};
export default p5;