-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpointcloud2d.js
218 lines (173 loc) · 5.82 KB
/
pointcloud2d.js
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
'use strict'
var createShader = require('gl-shader')
var createBuffer = require('gl-buffer')
var pool = require('typedarray-pool')
var SHADERS = require('./lib/shader')
module.exports = createPointcloud2D
function Pointcloud2D(plot, offsetBuffer, pickBuffer, shader, pickShader) {
this.plot = plot
this.offsetBuffer = offsetBuffer
this.pickBuffer = pickBuffer
this.shader = shader
this.pickShader = pickShader
this.sizeMin = 0.5
this.sizeMinCap = 2
this.sizeMax = 20
this.areaRatio = 1.0
this.pointCount = 0
this.color = [1, 0, 0, 1]
this.borderColor = [0, 0, 0, 1]
this.blend = false
this.pickOffset = 0
this.points = null
}
var proto = Pointcloud2D.prototype
proto.dispose = function() {
this.shader.dispose()
this.pickShader.dispose()
this.offsetBuffer.dispose()
this.pickBuffer.dispose()
this.plot.removeObject(this)
}
proto.update = function(options) {
var i
options = options || {}
function dflt(opt, value) {
if(opt in options) {
return options[opt]
}
return value
}
this.sizeMin = dflt('sizeMin', 0.5)
// this.sizeMinCap = dflt('sizeMinCap', 2)
this.sizeMax = dflt('sizeMax', 20)
this.color = dflt('color', [1, 0, 0, 1]).slice()
this.areaRatio = dflt('areaRatio', 1)
this.borderColor = dflt('borderColor', [0, 0, 0, 1]).slice()
this.blend = dflt('blend', false)
//Update point data
// Attempt straight-through processing (STP) to avoid allocation and copy
// TODO eventually abstract out STP logic, maybe into `pool` or a layer above
var pointCount = options.positions.length >>> 1
var dataStraightThrough = options.positions instanceof Float32Array
var idStraightThrough = options.idToIndex instanceof Int32Array && options.idToIndex.length >= pointCount // permit larger to help reuse
var data = options.positions
var packed = dataStraightThrough ? data : pool.mallocFloat32(data.length)
var packedId = idStraightThrough ? options.idToIndex : pool.mallocInt32(pointCount)
if(!dataStraightThrough) {
packed.set(data)
}
if(!idStraightThrough) {
packed.set(data)
for(i = 0; i < pointCount; i++) {
packedId[i] = i
}
}
this.points = data
this.offsetBuffer.update(packed)
this.pickBuffer.update(packedId)
if(!dataStraightThrough) {
pool.free(packed)
}
if(!idStraightThrough) {
pool.free(packedId)
}
this.pointCount = pointCount
this.pickOffset = 0
}
function count(points, dataBox) {
var visiblePointCountEstimate = 0
var length = points.length >>> 1
var i
for(i = 0; i < length; i++) {
var x = points[i * 2]
var y = points[i * 2 + 1]
if(x >= dataBox[0] && x <= dataBox[2] && y >= dataBox[1] && y <= dataBox[3])
visiblePointCountEstimate++
}
return visiblePointCountEstimate
}
proto.unifiedDraw = (function() {
var MATRIX = [1, 0, 0,
0, 1, 0,
0, 0, 1]
var PICK_VEC4 = [0, 0, 0, 0]
return function(pickOffset) {
var pick = pickOffset !== void(0)
var shader = pick ? this.pickShader : this.shader
var gl = this.plot.gl
var dataBox = this.plot.dataBox
if(this.pointCount === 0) {
return pickOffset
}
var dataX = dataBox[2] - dataBox[0]
var dataY = dataBox[3] - dataBox[1]
var visiblePointCountEstimate = count(this.points, dataBox)
var basicPointSize = this.plot.pickPixelRatio * Math.max(Math.min(this.sizeMinCap, this.sizeMin), Math.min(this.sizeMax, this.sizeMax / Math.pow(visiblePointCountEstimate, 0.33333)))
MATRIX[0] = 2.0 / dataX
MATRIX[4] = 2.0 / dataY
MATRIX[6] = -2.0 * dataBox[0] / dataX - 1.0
MATRIX[7] = -2.0 * dataBox[1] / dataY - 1.0
this.offsetBuffer.bind()
shader.bind()
shader.attributes.position.pointer()
shader.uniforms.matrix = MATRIX
shader.uniforms.color = this.color
shader.uniforms.borderColor = this.borderColor
shader.uniforms.pointCloud = basicPointSize < 5
shader.uniforms.pointSize = basicPointSize
shader.uniforms.centerFraction = Math.min(1, Math.max(0, Math.sqrt(1 - this.areaRatio)))
if(pick) {
PICK_VEC4[0] = ( pickOffset & 0xff)
PICK_VEC4[1] = ((pickOffset >> 8) & 0xff)
PICK_VEC4[2] = ((pickOffset >> 16) & 0xff)
PICK_VEC4[3] = ((pickOffset >> 24) & 0xff)
this.pickBuffer.bind()
shader.attributes.pickId.pointer(gl.UNSIGNED_BYTE)
shader.uniforms.pickOffset = PICK_VEC4
this.pickOffset = pickOffset
}
// Worth switching these off, but we can't make assumptions about other
// renderers, so let's restore it after each draw
var blend = gl.getParameter(gl.BLEND)
var dither = gl.getParameter(gl.DITHER)
if(blend && !this.blend)
gl.disable(gl.BLEND)
if(dither)
gl.disable(gl.DITHER)
gl.drawArrays(gl.POINTS, 0, this.pointCount)
if(blend && !this.blend)
gl.enable(gl.BLEND)
if(dither)
gl.enable(gl.DITHER)
return pickOffset + this.pointCount
}
})()
proto.draw = proto.unifiedDraw
proto.drawPick = proto.unifiedDraw
proto.pick = function(x, y, value) {
var pickOffset = this.pickOffset
var pointCount = this.pointCount
if(value < pickOffset || value >= pickOffset + pointCount) {
return null
}
var pointId = value - pickOffset
var points = this.points
return {
object: this,
pointId: pointId,
dataCoord: [points[2 * pointId], points[2 * pointId + 1] ]
}
}
function createPointcloud2D(plot, options) {
var gl = plot.gl
var buffer = createBuffer(gl)
var pickBuffer = createBuffer(gl)
var shader = createShader(gl, SHADERS.pointVertex, SHADERS.pointFragment)
var pickShader = createShader(gl, SHADERS.pickVertex, SHADERS.pickFragment)
var result = new Pointcloud2D(plot, buffer, pickBuffer, shader, pickShader)
result.update(options)
//Register with plot
plot.addObject(result)
return result
}