Skip to content

Commit c7f8316

Browse files
committed
bring camera.js to gl-plot3d
1 parent 33d5169 commit c7f8316

File tree

5 files changed

+287
-18
lines changed

5 files changed

+287
-18
lines changed

Diff for: README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ This module (and this whole subecosystem) skew more towards the easy-side of the
1616
[<img src="images/scatter3d.png" width="400px" alt="3D scatter plot">](http://requirebin.com/?gist=cf75d78184f6b8cac15a) [![view on requirebin](http://requirebin.com/badge.png)](http://requirebin.com/?gist=cf75d78184f6b8cac15a)
1717

1818
```javascript
19-
var createScene = require('gl-plot3d')
19+
var createScene = require('gl-plot3d').createScene
20+
var createCamera = require('gl-plot3d').createCamera
2021
var createScatter = require('gl-scatter3d')
2122
var bunny = require('bunny')
2223

Diff for: lib/camera.js

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
'use strict'
2+
3+
module.exports = createCamera
4+
5+
var now = require('right-now')
6+
var createView = require('3d-view')
7+
var mouseChange = require('mouse-change')
8+
var mouseWheel = require('mouse-wheel')
9+
var mouseOffset = require('mouse-event-offset')
10+
var hasPassive = require('has-passive-events')
11+
12+
function createCamera(element, options) {
13+
element = element || document.body
14+
options = options || {}
15+
16+
var limits = [ 0.01, Infinity ]
17+
if('distanceLimits' in options) {
18+
limits[0] = options.distanceLimits[0]
19+
limits[1] = options.distanceLimits[1]
20+
}
21+
if('zoomMin' in options) {
22+
limits[0] = options.zoomMin
23+
}
24+
if('zoomMax' in options) {
25+
limits[1] = options.zoomMax
26+
}
27+
28+
var view = createView({
29+
center: options.center || [0,0,0],
30+
up: options.up || [0,1,0],
31+
eye: options.eye || [0,0,10],
32+
ortho: options.ortho || false,
33+
mode: options.mode || 'orbit',
34+
distanceLimits: limits
35+
})
36+
37+
var pmatrix = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
38+
var distance = 0.0
39+
var width = element.clientWidth
40+
var height = element.clientHeight
41+
42+
var camera = {
43+
keyBindingMode: 'rotate',
44+
enableWheel: true,
45+
view: view,
46+
element: element,
47+
delay: options.delay || 16,
48+
rotateSpeed: options.rotateSpeed || 1,
49+
zoomSpeed: options.zoomSpeed || 1,
50+
translateSpeed: options.translateSpeed || 1,
51+
flipX: !!options.flipX,
52+
flipY: !!options.flipY,
53+
modes: view.modes,
54+
tick: function() {
55+
var t = now()
56+
var delay = this.delay
57+
var ctime = t - 2 * delay
58+
view.idle(t-delay)
59+
view.recalcMatrix(ctime)
60+
view.flush(t-(100+delay*2))
61+
var allEqual = true
62+
var matrix = view.computedMatrix
63+
for(var i=0; i<16; ++i) {
64+
allEqual = allEqual && (pmatrix[i] === matrix[i])
65+
pmatrix[i] = matrix[i]
66+
}
67+
var sizeChanged =
68+
element.clientWidth === width &&
69+
element.clientHeight === height
70+
width = element.clientWidth
71+
height = element.clientHeight
72+
if(allEqual) {
73+
return !sizeChanged
74+
}
75+
distance = Math.exp(view.computedRadius[0])
76+
return true
77+
},
78+
lookAt: function(eye, center, up) {
79+
view.lookAt(view.lastT(), eye, center, up)
80+
},
81+
rotate: function(pitch, yaw, roll) {
82+
view.rotate(view.lastT(), pitch, yaw, roll)
83+
},
84+
pan: function(dx, dy, dz) {
85+
view.pan(view.lastT(), dx, dy, dz)
86+
},
87+
translate: function(dx, dy, dz) {
88+
view.translate(view.lastT(), dx, dy, dz)
89+
}
90+
}
91+
92+
Object.defineProperties(camera, {
93+
matrix: {
94+
get: function() {
95+
return view.computedMatrix
96+
},
97+
set: function(mat) {
98+
view.setMatrix(view.lastT(), mat)
99+
return view.computedMatrix
100+
},
101+
enumerable: true
102+
},
103+
mode: {
104+
get: function() {
105+
return view.getMode()
106+
},
107+
set: function(mode) {
108+
var curUp = view.computedUp.slice()
109+
var curEye = view.computedEye.slice()
110+
var curCenter = view.computedCenter.slice()
111+
view.setMode(mode)
112+
if(mode === 'turntable') {
113+
// Hacky time warping stuff to generate smooth animation
114+
var t0 = now()
115+
view._active.lookAt(t0, curEye, curCenter, curUp)
116+
view._active.lookAt(t0 + 500, curEye, curCenter, [0, 0, 1])
117+
view._active.flush(t0)
118+
}
119+
return view.getMode()
120+
},
121+
enumerable: true
122+
},
123+
center: {
124+
get: function() {
125+
return view.computedCenter
126+
},
127+
set: function(ncenter) {
128+
view.lookAt(view.lastT(), null, ncenter)
129+
return view.computedCenter
130+
},
131+
enumerable: true
132+
},
133+
eye: {
134+
get: function() {
135+
return view.computedEye
136+
},
137+
set: function(neye) {
138+
view.lookAt(view.lastT(), neye)
139+
return view.computedEye
140+
},
141+
enumerable: true
142+
},
143+
up: {
144+
get: function() {
145+
return view.computedUp
146+
},
147+
set: function(nup) {
148+
view.lookAt(view.lastT(), null, null, nup)
149+
return view.computedUp
150+
},
151+
enumerable: true
152+
},
153+
distance: {
154+
get: function() {
155+
return distance
156+
},
157+
set: function(d) {
158+
view.setDistance(view.lastT(), d)
159+
return d
160+
},
161+
enumerable: true
162+
},
163+
distanceLimits: {
164+
get: function() {
165+
return view.getDistanceLimits(limits)
166+
},
167+
set: function(v) {
168+
view.setDistanceLimits(v)
169+
return v
170+
},
171+
enumerable: true
172+
}
173+
})
174+
175+
element.addEventListener('contextmenu', function(ev) {
176+
ev.preventDefault()
177+
return false
178+
})
179+
180+
var lastX = 0, lastY = 0, lastMods = {shift: false, control: false, alt: false, meta: false}
181+
camera.mouseListener = mouseChange(element, handleInteraction)
182+
183+
//enable simple touch interactions
184+
element.addEventListener('touchstart', function (ev) {
185+
var xy = mouseOffset(ev.changedTouches[0], element)
186+
handleInteraction(0, xy[0], xy[1], lastMods)
187+
handleInteraction(1, xy[0], xy[1], lastMods)
188+
189+
ev.preventDefault()
190+
}, hasPassive ? {passive: false} : false)
191+
192+
element.addEventListener('touchmove', function (ev) {
193+
var xy = mouseOffset(ev.changedTouches[0], element)
194+
handleInteraction(1, xy[0], xy[1], lastMods)
195+
196+
ev.preventDefault()
197+
}, hasPassive ? {passive: false} : false)
198+
199+
element.addEventListener('touchend', function (ev) {
200+
201+
handleInteraction(0, lastX, lastY, lastMods)
202+
203+
ev.preventDefault()
204+
}, hasPassive ? {passive: false} : false)
205+
206+
function handleInteraction (buttons, x, y, mods) {
207+
var keyBindingMode = camera.keyBindingMode
208+
209+
if(keyBindingMode === false) return
210+
211+
var rotate = keyBindingMode === 'rotate'
212+
var pan = keyBindingMode === 'pan'
213+
var zoom = keyBindingMode === 'zoom'
214+
215+
var ctrl = !!mods.control
216+
var alt = !!mods.alt
217+
var shift = !!mods.shift
218+
var left = !!(buttons & 1)
219+
var right = !!(buttons & 2)
220+
var middle = !!(buttons & 4)
221+
222+
var scale = 1.0 / element.clientHeight
223+
var dx = scale * (x - lastX)
224+
var dy = scale * (y - lastY)
225+
226+
var flipX = camera.flipX ? 1 : -1
227+
var flipY = camera.flipY ? 1 : -1
228+
229+
var drot = Math.PI * camera.rotateSpeed
230+
231+
var t = now()
232+
233+
if((rotate && left && !ctrl && !alt && !shift) || (left && !ctrl && !alt && shift)) {
234+
// Rotate
235+
view.rotate(t, flipX * drot * dx, -flipY * drot * dy, 0)
236+
}
237+
238+
if((pan && left && !ctrl && !alt && !shift) || right || (left && ctrl && !alt && !shift)) {
239+
// Pan
240+
view.pan(t, -camera.translateSpeed * dx * distance, camera.translateSpeed * dy * distance, 0)
241+
}
242+
243+
if((zoom && left && !ctrl && !alt && !shift) || middle || (left && !ctrl && alt && !shift)) {
244+
// Zoom
245+
var kzoom = -camera.zoomSpeed * dy / window.innerHeight * (t - view.lastT()) * 100
246+
view.pan(t, 0, 0, distance * (Math.exp(kzoom) - 1))
247+
}
248+
249+
lastX = x
250+
lastY = y
251+
lastMods = mods
252+
253+
return true
254+
}
255+
256+
camera.wheelListener = mouseWheel(element, function(dx, dy) {
257+
// TODO remove now that we can disable scroll via scrollZoom?
258+
if(camera.keyBindingMode === false) return
259+
if(!camera.enableWheel) return
260+
261+
var flipX = camera.flipX ? 1 : -1
262+
var flipY = camera.flipY ? 1 : -1
263+
var t = now()
264+
if(Math.abs(dx) > Math.abs(dy)) {
265+
view.rotate(t, 0, 0, -dx * flipX * Math.PI * camera.rotateSpeed / window.innerWidth)
266+
} else {
267+
var kzoom = -camera.zoomSpeed * flipY * dy / window.innerHeight * (t - view.lastT()) / 20.0
268+
view.pan(t, 0, 0, distance * (Math.exp(kzoom) - 1))
269+
}
270+
}, true)
271+
272+
return camera
273+
}

Diff for: package-lock.json

-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"example": "example"
88
},
99
"dependencies": {
10-
"3d-view-controls": "git://github.com/archmoj/3d-view-controls.git#fe3c77b807618c0d51f4c50a691e2af0486bfda9",
10+
"3d-view": "^2.0.0",
1111
"a-big-triangle": "^1.0.3",
1212
"gl-axes3d": "git://github.com/gl-vis/gl-axes3d.git#7a57149812d2a0bae4283ca927d0df3723af688e",
1313
"gl-fbo": "^2.0.5",
@@ -16,9 +16,13 @@
1616
"gl-shader": "^4.2.1",
1717
"gl-spikes3d": "^1.0.8",
1818
"glslify": "^7.0.0",
19+
"has-passive-events": "^1.0.0",
1920
"is-mobile": "^2.0.0",
2021
"mouse-change": "^1.4.0",
21-
"ndarray": "^1.0.18"
22+
"mouse-event-offset": "^3.0.2",
23+
"mouse-wheel": "^1.2.0",
24+
"ndarray": "^1.0.18",
25+
"right-now": "^1.0.0"
2226
},
2327
"devDependencies": {
2428
"bunny": "^1.0.1",

Diff for: scene.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
'use strict'
22

3-
module.exports = createScene
4-
5-
var createCamera = require('3d-view-controls')
3+
var createCamera = require('./lib/camera.js')
64
var createAxes = require('gl-axes3d')
75
var axesRanges = require('gl-axes3d/properties')
86
var createSpikes = require('gl-spikes3d')
@@ -15,6 +13,11 @@ var ortho = require('gl-mat4/ortho')
1513
var createShader = require('./lib/shader')
1614
var isMobile = require('is-mobile')({ tablet: true })
1715

16+
module.exports = {
17+
createScene: createScene,
18+
createCamera: createCamera
19+
}
20+
1821
function MouseSelect() {
1922
this.mouse = [-1,-1]
2023
this.screen = null

0 commit comments

Comments
 (0)