-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtext.js
293 lines (248 loc) · 8.76 KB
/
text.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
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
'use strict'
module.exports = createTextElements
var createBuffer = require('gl-buffer')
var createShader = require('gl-shader')
var getText = require('text-cache')
var bsearch = require('binary-search-bounds')
var shaders = require('./shaders')
function TextElements(plot, vbo, shader) {
this.plot = plot
this.vbo = vbo
this.shader = shader
this.tickOffset = [[],[]]
this.tickX = [[],[]]
this.labelOffset = [0,0]
this.labelCount = [0,0]
}
var proto = TextElements.prototype
proto.drawTicks = (function() {
var DATA_AXIS = [0,0]
var SCREEN_OFFSET = [0,0]
var ZERO_2 = [0,0]
return function(axis) {
var plot = this.plot
var shader = this.shader
var tickX = this.tickX[axis]
var tickOffset = this.tickOffset[axis]
var gl = plot.gl
var viewBox = plot.viewBox
var dataBox = plot.dataBox
var screenBox = plot.screenBox
var pixelRatio = plot.pixelRatio
var tickEnable = plot.tickEnable
var tickPad = plot.tickPad
var textColor = plot.tickColor
var textAngle = plot.tickAngle
// todo check if this should be used (now unused)
// var tickLength = plot.tickMarkLength
var labelEnable = plot.labelEnable
var labelPad = plot.labelPad
var labelColor = plot.labelColor
var labelAngle = plot.labelAngle
var labelOffset = this.labelOffset[axis]
var labelCount = this.labelCount[axis]
var start = bsearch.lt(tickX, dataBox[axis])
var end = bsearch.le(tickX, dataBox[axis+2])
DATA_AXIS[0] = DATA_AXIS[1] = 0
DATA_AXIS[axis] = 1
SCREEN_OFFSET[axis] = (viewBox[2+axis] + viewBox[axis]) / (screenBox[2+axis] - screenBox[axis]) - 1.0
var screenScale = 2.0 / screenBox[2+(axis^1)] - screenBox[axis^1]
SCREEN_OFFSET[axis^1] = screenScale * viewBox[axis^1] - 1.0
if(tickEnable[axis]) {
SCREEN_OFFSET[axis^1] -= screenScale * pixelRatio * tickPad[axis]
if(start < end && tickOffset[end] > tickOffset[start]) {
shader.uniforms.dataAxis = DATA_AXIS
shader.uniforms.screenOffset = SCREEN_OFFSET
shader.uniforms.color = textColor[axis]
shader.uniforms.angle = textAngle[axis]
gl.drawArrays(
gl.TRIANGLES,
tickOffset[start],
tickOffset[end] - tickOffset[start])
}
}
if(labelEnable[axis] && labelCount) {
SCREEN_OFFSET[axis^1] -= screenScale * pixelRatio * labelPad[axis]
shader.uniforms.dataAxis = ZERO_2
shader.uniforms.screenOffset = SCREEN_OFFSET
shader.uniforms.color = labelColor[axis]
shader.uniforms.angle = labelAngle[axis]
gl.drawArrays(
gl.TRIANGLES,
labelOffset,
labelCount)
}
SCREEN_OFFSET[axis^1] = screenScale * viewBox[2+(axis^1)] - 1.0
if(tickEnable[axis+2]) {
SCREEN_OFFSET[axis^1] += screenScale * pixelRatio * tickPad[axis+2]
if(start < end && tickOffset[end] > tickOffset[start]) {
shader.uniforms.dataAxis = DATA_AXIS
shader.uniforms.screenOffset = SCREEN_OFFSET
shader.uniforms.color = textColor[axis+2]
shader.uniforms.angle = textAngle[axis+2]
gl.drawArrays(
gl.TRIANGLES,
tickOffset[start],
tickOffset[end] - tickOffset[start])
}
}
if(labelEnable[axis+2] && labelCount) {
SCREEN_OFFSET[axis^1] += screenScale * pixelRatio * labelPad[axis+2]
shader.uniforms.dataAxis = ZERO_2
shader.uniforms.screenOffset = SCREEN_OFFSET
shader.uniforms.color = labelColor[axis+2]
shader.uniforms.angle = labelAngle[axis+2]
gl.drawArrays(
gl.TRIANGLES,
labelOffset,
labelCount)
}
}
})()
proto.drawTitle = (function() {
var DATA_AXIS = [0,0]
var SCREEN_OFFSET = [0,0]
return function() {
var plot = this.plot
var shader = this.shader
var gl = plot.gl
var screenBox = plot.screenBox
var titleCenter = plot.titleCenter
var titleAngle = plot.titleAngle
var titleColor = plot.titleColor
var pixelRatio = plot.pixelRatio
if(!this.titleCount) {
return
}
for(var i=0; i<2; ++i) {
SCREEN_OFFSET[i] = 2.0 * (titleCenter[i]*pixelRatio - screenBox[i]) /
(screenBox[2+i] - screenBox[i]) - 1
}
shader.bind()
shader.uniforms.dataAxis = DATA_AXIS
shader.uniforms.screenOffset = SCREEN_OFFSET
shader.uniforms.angle = titleAngle
shader.uniforms.color = titleColor
gl.drawArrays(gl.TRIANGLES, this.titleOffset, this.titleCount)
}
})()
proto.bind = (function() {
var DATA_SHIFT = [0,0]
var DATA_SCALE = [0,0]
var TEXT_SCALE = [0,0]
return function() {
var plot = this.plot
var shader = this.shader
var bounds = plot._tickBounds
var dataBox = plot.dataBox
var screenBox = plot.screenBox
var viewBox = plot.viewBox
shader.bind()
//Set up coordinate scaling uniforms
for(var i=0; i<2; ++i) {
var lo = bounds[i]
var hi = bounds[i+2]
var boundScale = hi - lo
var dataCenter = 0.5 * (dataBox[i+2] + dataBox[i])
var dataWidth = (dataBox[i+2] - dataBox[i])
var viewLo = viewBox[i]
var viewHi = viewBox[i+2]
var viewScale = viewHi - viewLo
var screenLo = screenBox[i]
var screenHi = screenBox[i+2]
var screenScale = screenHi - screenLo
DATA_SCALE[i] = 2.0 * boundScale / dataWidth * viewScale / screenScale
DATA_SHIFT[i] = 2.0 * (lo - dataCenter) / dataWidth * viewScale / screenScale
}
TEXT_SCALE[1] = 2.0 * plot.pixelRatio / (screenBox[3] - screenBox[1])
TEXT_SCALE[0] = TEXT_SCALE[1] * (screenBox[3] - screenBox[1]) / (screenBox[2] - screenBox[0])
shader.uniforms.dataScale = DATA_SCALE
shader.uniforms.dataShift = DATA_SHIFT
shader.uniforms.textScale = TEXT_SCALE
//Set attributes
this.vbo.bind()
shader.attributes.textCoordinate.pointer()
}
})()
proto.update = function(options) {
var vertices = []
var axesTicks = options.ticks
var bounds = options.bounds
var i, j, k, data, scale, dimension
for(dimension=0; dimension<2; ++dimension) {
var offsets = [Math.floor(vertices.length/3)], tickX = [-Infinity]
//Copy vertices over to buffer
var ticks = axesTicks[dimension]
for(i=0; i<ticks.length; ++i) {
var tick = ticks[i]
var x = tick.x
var text = tick.text
var font = tick.font || 'sans-serif'
var fontStyle = tick.fontStyle || 'normal'
var fontWeight = tick.fontWeight || 'normal'
var fontVariant = tick.fontVariant || 'normal'
scale = (tick.fontSize || 12)
var coordScale = 1.0 / (bounds[dimension+2] - bounds[dimension])
var coordShift = bounds[dimension]
var rows = text.split('\n')
for(var r = 0; r < rows.length; r++) {
data = getText(font, rows[r], {
fontStyle: fontStyle,
fontWeight: fontWeight,
fontVariant: fontVariant
}).data
for (j = 0; j < data.length; j += 2) {
vertices.push(
data[j] * scale,
-data[j + 1] * scale - r * scale * 1.2,
(x - coordShift) * coordScale)
}
}
offsets.push(Math.floor(vertices.length/3))
tickX.push(x)
}
this.tickOffset[dimension] = offsets
this.tickX[dimension] = tickX
}
//Add labels
for(dimension=0; dimension<2; ++dimension) {
this.labelOffset[dimension] = Math.floor(vertices.length/3)
data = getText(options.labelFont[dimension], options.labels[dimension], {
fontStyle: options.labelFontStyle[dimension],
fontWeight: options.labelFontWeight[dimension],
fontVariant: options.labelFontVariant[dimension],
textAlign: 'center'
}).data
scale = options.labelSize[dimension]
for(i=0; i<data.length; i+=2) {
vertices.push(data[i]*scale, -data[i+1]*scale, 0)
}
this.labelCount[dimension] =
Math.floor(vertices.length/3) - this.labelOffset[dimension]
}
//Add title
this.titleOffset = Math.floor(vertices.length/3)
data = getText(options.titleFont, options.title, {
fontStyle: options.titleFontStyle,
fontWeight: options.titleFontWeight,
fontVariant: options.titleFontVariant,
}).data
scale = options.titleSize
for(i=0; i<data.length; i+=2) {
vertices.push(data[i]*scale, -data[i+1]*scale, 0)
}
this.titleCount = Math.floor(vertices.length/3) - this.titleOffset
//Upload new vertices
this.vbo.update(vertices)
}
proto.dispose = function() {
this.vbo.dispose()
this.shader.dispose()
}
function createTextElements(plot) {
var gl = plot.gl
var vbo = createBuffer(gl)
var shader = createShader(gl, shaders.textVert, shaders.textFrag)
var text = new TextElements(plot, vbo, shader)
return text
}