@quartic/bokehjs
Version:
Interactive, novel data visualization
355 lines (354 loc) • 36.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var DashAtlas, extend = function (child, parent) { for (var key in parent) {
if (hasProp.call(parent, key))
child[key] = parent[key];
} function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty;
var gloo2 = require("gloo2");
var color_1 = require("core/util/color");
var base_1 = require("./base");
DashAtlas = (function () {
function DashAtlas(gl) {
this._atlas = {};
this._index = 0;
this._width = 256;
this._height = 256;
this.tex = new gloo2.Texture2D(gl);
this.tex.set_wrapping(gl.REPEAT, gl.REPEAT);
this.tex.set_interpolation(gl.NEAREST, gl.NEAREST);
this.tex.set_size([this._height, this._width], gl.RGBA);
this.tex.set_data([0, 0], [this._height, this._width], new Uint8Array(this._height * this._width * 4));
this.get_atlas_data([1]);
}
DashAtlas.prototype.get_atlas_data = function (pattern) {
var data, findex_period, key, period, ref, x;
key = pattern.join('-');
findex_period = this._atlas[key];
if (findex_period === void 0) {
ref = this.make_pattern(pattern), data = ref[0], period = ref[1];
this.tex.set_data([this._index, 0], [1, this._width], new Uint8Array((function () {
var l, len, results;
results = [];
for (l = 0, len = data.length; l < len; l++) {
x = data[l];
results.push(x + 10);
}
return results;
})()));
this._atlas[key] = [this._index / this._height, period];
this._index += 1;
}
return this._atlas[key];
};
DashAtlas.prototype.make_pattern = function (pattern) {
var C, Z, a, b, c, dash_end, dash_start, dash_type, i, index, j, l, len, n, p, period, q, r, ref, ref1, ref2, v, val, val_at_index, x;
if (pattern.length > 1 && pattern.length % 2) {
pattern = pattern.concat(pattern);
}
period = 0;
for (l = 0, len = pattern.length; l < len; l++) {
v = pattern[l];
period += v;
}
C = [];
c = 0;
for (i = p = 0, ref = pattern.length + 2; p < ref; i = p += 2) {
a = Math.max(0.0001, pattern[i % pattern.length]);
b = Math.max(0.0001, pattern[(i + 1) % pattern.length]);
C.push.apply(C, [c, c + a]);
c += a + b;
}
n = this._width;
Z = new Float32Array(n * 4);
for (i = q = 0, ref1 = n; 0 <= ref1 ? q < ref1 : q > ref1; i = 0 <= ref1 ? ++q : --q) {
x = period * i / (n - 1);
index = 0;
val_at_index = 1e16;
for (j = r = 0, ref2 = C.length; 0 <= ref2 ? r < ref2 : r > ref2; j = 0 <= ref2 ? ++r : --r) {
val = Math.abs(C[j] - x);
if (val < val_at_index) {
index = j;
val_at_index = val;
}
}
if (index % 2 === 0) {
dash_type = (x <= C[index]) ? +1 : 0;
dash_start = C[index];
dash_end = C[index + 1];
}
else {
dash_type = (x > C[index]) ? -1 : 0;
dash_start = C[index - 1];
dash_end = C[index];
}
Z[i * 4 + 0] = C[index];
Z[i * 4 + 1] = dash_type;
Z[i * 4 + 2] = dash_start;
Z[i * 4 + 3] = dash_end;
}
return [Z, period];
};
return DashAtlas;
})();
exports.LineGLGlyph = (function (superClass) {
extend(LineGLGlyph, superClass);
function LineGLGlyph() {
return LineGLGlyph.__super__.constructor.apply(this, arguments);
}
LineGLGlyph.prototype.GLYPH = 'line';
LineGLGlyph.prototype.JOINS = {
'miter': 0,
'round': 1,
'bevel': 2
};
LineGLGlyph.prototype.CAPS = {
'': 0,
'none': 0,
'.': 0,
'round': 1,
')': 1,
'(': 1,
'o': 1,
'triangle in': 2,
'<': 2,
'triangle out': 3,
'>': 3,
'square': 4,
'[': 4,
']': 4,
'=': 4,
'butt': 5,
'|': 5
};
LineGLGlyph.prototype.VERT = "precision mediump float;\n\nconst float PI = 3.14159265358979323846264;\nconst float THETA = 15.0 * 3.14159265358979323846264/180.0;\n\nuniform float u_pixel_ratio;\nuniform vec2 u_canvas_size, u_offset;\nuniform vec2 u_scale_aspect;\nuniform float u_scale_length;\n\nuniform vec4 u_color;\nuniform float u_antialias;\nuniform float u_length;\nuniform float u_linewidth;\nuniform float u_dash_index;\nuniform float u_closed;\n\nattribute vec2 a_position;\nattribute vec4 a_tangents;\nattribute vec2 a_segment;\nattribute vec2 a_angles;\nattribute vec2 a_texcoord;\n\nvarying vec4 v_color;\nvarying vec2 v_segment;\nvarying vec2 v_angles;\nvarying vec2 v_texcoord;\nvarying vec2 v_miter;\nvarying float v_length;\nvarying float v_linewidth;\n\nfloat cross(in vec2 v1, in vec2 v2)\n{\n return v1.x*v2.y - v1.y*v2.x;\n}\n\nfloat signed_distance(in vec2 v1, in vec2 v2, in vec2 v3)\n{\n return cross(v2-v1,v1-v3) / length(v2-v1);\n}\n\nvoid rotate( in vec2 v, in float alpha, out vec2 result )\n{\n float c = cos(alpha);\n float s = sin(alpha);\n result = vec2( c*v.x - s*v.y,\n s*v.x + c*v.y );\n}\n\nvoid main()\n{\n bool closed = (u_closed > 0.0);\n\n // Attributes and uniforms to varyings\n v_color = u_color;\n v_linewidth = u_linewidth;\n v_segment = a_segment * u_scale_length;\n v_length = u_length * u_scale_length;\n\n // Scale to map to pixel coordinates. The original algorithm from the paper\n // assumed isotropic scale. We obviously do not have this.\n vec2 abs_scale_aspect = abs(u_scale_aspect);\n vec2 abs_scale = u_scale_length * abs_scale_aspect;\n\n // Correct angles for aspect ratio\n vec2 av;\n av = vec2(1.0, tan(a_angles.x)) / abs_scale_aspect;\n v_angles.x = atan(av.y, av.x);\n av = vec2(1.0, tan(a_angles.y)) / abs_scale_aspect;\n v_angles.y = atan(av.y, av.x);\n\n // Thickness below 1 pixel are represented using a 1 pixel thickness\n // and a modified alpha\n v_color.a = min(v_linewidth, v_color.a);\n v_linewidth = max(v_linewidth, 1.0);\n\n // If color is fully transparent we just will discard the fragment anyway\n if( v_color.a <= 0.0 ) {\n gl_Position = vec4(0.0,0.0,0.0,1.0);\n return;\n }\n\n // This is the actual half width of the line\n float w = ceil(u_antialias+v_linewidth)/2.0;\n\n vec2 position = (a_position + u_offset) * abs_scale;\n\n vec2 t1 = normalize(a_tangents.xy * abs_scale_aspect); // note the scaling for aspect ratio here\n vec2 t2 = normalize(a_tangents.zw * abs_scale_aspect);\n float u = a_texcoord.x;\n float v = a_texcoord.y;\n vec2 o1 = vec2( +t1.y, -t1.x);\n vec2 o2 = vec2( +t2.y, -t2.x);\n\n // This is a join\n // ----------------------------------------------------------------\n if( t1 != t2 ) {\n float angle = atan (t1.x*t2.y-t1.y*t2.x, t1.x*t2.x+t1.y*t2.y); // Angle needs recalculation for some reason\n vec2 t = normalize(t1+t2);\n vec2 o = vec2( + t.y, - t.x);\n\n if ( u_dash_index > 0.0 )\n {\n // Broken angle\n // ----------------------------------------------------------------\n if( (abs(angle) > THETA) ) {\n position += v * w * o / cos(angle/2.0);\n float s = sign(angle);\n if( angle < 0.0 ) {\n if( u == +1.0 ) {\n u = v_segment.y + v * w * tan(angle/2.0);\n if( v == 1.0 ) {\n position -= 2.0 * w * t1 / sin(angle);\n u -= 2.0 * w / sin(angle);\n }\n } else {\n u = v_segment.x - v * w * tan(angle/2.0);\n if( v == 1.0 ) {\n position += 2.0 * w * t2 / sin(angle);\n u += 2.0*w / sin(angle);\n }\n }\n } else {\n if( u == +1.0 ) {\n u = v_segment.y + v * w * tan(angle/2.0);\n if( v == -1.0 ) {\n position += 2.0 * w * t1 / sin(angle);\n u += 2.0 * w / sin(angle);\n }\n } else {\n u = v_segment.x - v * w * tan(angle/2.0);\n if( v == -1.0 ) {\n position -= 2.0 * w * t2 / sin(angle);\n u -= 2.0*w / sin(angle);\n }\n }\n }\n // Continuous angle\n // ------------------------------------------------------------\n } else {\n position += v * w * o / cos(angle/2.0);\n if( u == +1.0 ) u = v_segment.y;\n else u = v_segment.x;\n }\n }\n\n // Solid line\n // --------------------------------------------------------------------\n else\n {\n position.xy += v * w * o / cos(angle/2.0);\n if( angle < 0.0 ) {\n if( u == +1.0 ) {\n u = v_segment.y + v * w * tan(angle/2.0);\n } else {\n u = v_segment.x - v * w * tan(angle/2.0);\n }\n } else {\n if( u == +1.0 ) {\n u = v_segment.y + v * w * tan(angle/2.0);\n } else {\n u = v_segment.x - v * w * tan(angle/2.0);\n }\n }\n }\n\n // This is a line start or end (t1 == t2)\n // ------------------------------------------------------------------------\n } else {\n position += v * w * o1;\n if( u == -1.0 ) {\n u = v_segment.x - w;\n position -= w * t1;\n } else {\n u = v_segment.y + w;\n position += w * t2;\n }\n }\n\n // Miter distance\n // ------------------------------------------------------------------------\n vec2 t;\n vec2 curr = a_position * abs_scale;\n if( a_texcoord.x < 0.0 ) {\n vec2 next = curr + t2*(v_segment.y-v_segment.x);\n\n rotate( t1, +v_angles.x/2.0, t);\n v_miter.x = signed_distance(curr, curr+t, position);\n\n rotate( t2, +v_angles.y/2.0, t);\n v_miter.y = signed_distance(next, next+t, position);\n } else {\n vec2 prev = curr - t1*(v_segment.y-v_segment.x);\n\n rotate( t1, -v_angles.x/2.0,t);\n v_miter.x = signed_distance(prev, prev+t, position);\n\n rotate( t2, -v_angles.y/2.0,t);\n v_miter.y = signed_distance(curr, curr+t, position);\n }\n\n if (!closed && v_segment.x <= 0.0) {\n v_miter.x = 1e10;\n }\n if (!closed && v_segment.y >= v_length)\n {\n v_miter.y = 1e10;\n }\n\n v_texcoord = vec2( u, v*w );\n\n // Calculate position in device coordinates. Note that we\n // already scaled with abs scale above.\n vec2 normpos = position * sign(u_scale_aspect);\n normpos += 0.5; // make up for Bokeh's offset\n normpos /= u_canvas_size / u_pixel_ratio; // in 0..1\n gl_Position = vec4(normpos*2.0-1.0, 0.0, 1.0);\n gl_Position.y *= -1.0;\n}\n";
LineGLGlyph.prototype.FRAG_ = "// Fragment shader that can be convenient during debugging to show the line skeleton.\nprecision mediump float;\nuniform vec4 u_color;\nvoid main () {\n gl_FragColor = u_color;\n}";
LineGLGlyph.prototype.FRAG = "precision mediump float;\n\nconst float PI = 3.14159265358979323846264;\nconst float THETA = 15.0 * 3.14159265358979323846264/180.0;\n\nuniform sampler2D u_dash_atlas;\n\nuniform vec2 u_linecaps;\nuniform float u_miter_limit;\nuniform float u_linejoin;\nuniform float u_antialias;\nuniform float u_dash_phase;\nuniform float u_dash_period;\nuniform float u_dash_index;\nuniform vec2 u_dash_caps;\nuniform float u_closed;\n\nvarying vec4 v_color;\nvarying vec2 v_segment;\nvarying vec2 v_angles;\nvarying vec2 v_texcoord;\nvarying vec2 v_miter;\nvarying float v_length;\nvarying float v_linewidth;\n\n// Compute distance to cap ----------------------------------------------------\nfloat cap( int type, float dx, float dy, float t, float linewidth )\n{\n float d = 0.0;\n dx = abs(dx);\n dy = abs(dy);\n if (type == 0) discard; // None\n else if (type == 1) d = sqrt(dx*dx+dy*dy); // Round\n else if (type == 3) d = (dx+abs(dy)); // Triangle in\n else if (type == 2) d = max(abs(dy),(t+dx-abs(dy))); // Triangle out\n else if (type == 4) d = max(dx,dy); // Square\n else if (type == 5) d = max(dx+t,dy); // Butt\n return d;\n}\n\n// Compute distance to join -------------------------------------------------\nfloat join( in int type, in float d, in vec2 segment, in vec2 texcoord, in vec2 miter,\n in float linewidth )\n{\n // texcoord.x is distance from start\n // texcoord.y is distance from centerline\n // segment.x and y indicate the limits (as for texcoord.x) for this segment\n\n float dx = texcoord.x;\n\n // Round join\n if( type == 1 ) {\n if (dx < segment.x) {\n d = max(d,length( texcoord - vec2(segment.x,0.0)));\n //d = length( texcoord - vec2(segment.x,0.0));\n } else if (dx > segment.y) {\n d = max(d,length( texcoord - vec2(segment.y,0.0)));\n //d = length( texcoord - vec2(segment.y,0.0));\n }\n }\n // Bevel join\n else if ( type == 2 ) {\n if (dx < segment.x) {\n vec2 x = texcoord - vec2(segment.x,0.0);\n d = max(d, max(abs(x.x), abs(x.y)));\n\n } else if (dx > segment.y) {\n vec2 x = texcoord - vec2(segment.y,0.0);\n d = max(d, max(abs(x.x), abs(x.y)));\n }\n /* Original code for bevel which does not work for us\n if( (dx < segment.x) || (dx > segment.y) )\n d = max(d, min(abs(x.x),abs(x.y)));\n */\n }\n\n return d;\n}\n\nvoid main()\n{\n // If color is fully transparent we just discard the fragment\n if( v_color.a <= 0.0 ) {\n discard;\n }\n\n // Test if dash pattern is the solid one (0)\n bool solid = (u_dash_index == 0.0);\n\n // Test if path is closed\n bool closed = (u_closed > 0.0);\n\n vec4 color = v_color;\n float dx = v_texcoord.x;\n float dy = v_texcoord.y;\n float t = v_linewidth/2.0-u_antialias;\n float width = 1.0; //v_linewidth; original code had dashes scale with line width, we do not\n float d = 0.0;\n\n vec2 linecaps = u_linecaps;\n vec2 dash_caps = u_dash_caps;\n float line_start = 0.0;\n float line_stop = v_length;\n\n // Apply miter limit; fragments too far into the miter are simply discarded\n if( (dx < v_segment.x) || (dx > v_segment.y) ) {\n float into_miter = max(v_segment.x - dx, dx - v_segment.y);\n if (into_miter > u_miter_limit*v_linewidth/2.0)\n discard;\n }\n\n // Solid line --------------------------------------------------------------\n if( solid ) {\n d = abs(dy);\n if( (!closed) && (dx < line_start) ) {\n d = cap( int(u_linecaps.x), abs(dx), abs(dy), t, v_linewidth );\n }\n else if( (!closed) && (dx > line_stop) ) {\n d = cap( int(u_linecaps.y), abs(dx)-line_stop, abs(dy), t, v_linewidth );\n }\n else {\n d = join( int(u_linejoin), abs(dy), v_segment, v_texcoord, v_miter, v_linewidth );\n }\n\n // Dash line --------------------------------------------------------------\n } else {\n float segment_start = v_segment.x;\n float segment_stop = v_segment.y;\n float segment_center= (segment_start+segment_stop)/2.0;\n float freq = u_dash_period*width;\n float u = mod( dx + u_dash_phase*width, freq);\n vec4 tex = texture2D(u_dash_atlas, vec2(u/freq, u_dash_index)) * 255.0 -10.0; // conversion to int-like\n float dash_center= tex.x * width;\n float dash_type = tex.y;\n float _start = tex.z * width;\n float _stop = tex.a * width;\n float dash_start = dx - u + _start;\n float dash_stop = dx - u + _stop;\n\n // Compute extents of the first dash (the one relative to v_segment.x)\n // Note: this could be computed in the vertex shader\n if( (dash_stop < segment_start) && (dash_caps.x != 5.0) ) {\n float u = mod(segment_start + u_dash_phase*width, freq);\n vec4 tex = texture2D(u_dash_atlas, vec2(u/freq, u_dash_index)) * 255.0 -10.0; // conversion to int-like\n dash_center= tex.x * width;\n //dash_type = tex.y;\n float _start = tex.z * width;\n float _stop = tex.a * width;\n dash_start = segment_start - u + _start;\n dash_stop = segment_start - u + _stop;\n }\n\n // Compute extents of the last dash (the one relatives to v_segment.y)\n // Note: This could be computed in the vertex shader\n else if( (dash_start > segment_stop) && (dash_caps.y != 5.0) ) {\n float u = mod(segment_stop + u_dash_phase*width, freq);\n vec4 tex = texture2D(u_dash_atlas, vec2(u/freq, u_dash_index)) * 255.0 -10.0; // conversion to int-like\n dash_center= tex.x * width;\n //dash_type = tex.y;\n float _start = tex.z * width;\n float _stop = tex.a * width;\n dash_start = segment_stop - u + _start;\n dash_stop = segment_stop - u + _stop;\n }\n\n // This test if the we are dealing with a discontinuous angle\n bool discontinuous = ((dx < segment_center) && abs(v_angles.x) > THETA) ||\n ((dx >= segment_center) && abs(v_angles.y) > THETA);\n //if( dx < line_start) discontinuous = false;\n //if( dx > line_stop) discontinuous = false;\n\n float d_join = join( int(u_linejoin), abs(dy),\n v_segment, v_texcoord, v_miter, v_linewidth );\n\n // When path is closed, we do not have room for linecaps, so we make room\n // by shortening the total length\n if (closed) {\n line_start += v_linewidth/2.0;\n line_stop -= v_linewidth/2.0;\n }\n\n // We also need to take antialias area into account\n //line_start += u_antialias;\n //line_stop -= u_antialias;\n\n // Check is dash stop is before line start\n if( dash_stop <= line_start ) {\n discard;\n }\n // Check is dash start is beyond line stop\n if( dash_start >= line_stop ) {\n discard;\n }\n\n // Check if current dash start is beyond segment stop\n if( discontinuous ) {\n // Dash start is beyond segment, we discard\n if( (dash_start > segment_stop) ) {\n discard;\n //gl_FragColor = vec4(1.0,0.0,0.0,.25); return;\n }\n\n // Dash stop is before segment, we discard\n if( (dash_stop < segment_start) ) {\n discard; //gl_FragColor = vec4(0.0,1.0,0.0,.25); return;\n }\n\n // Special case for round caps (nicer with this)\n if( dash_caps.x == 1.0 ) {\n if( (u > _stop) && (dash_stop > segment_stop ) && (abs(v_angles.y) < PI/2.0)) {\n discard;\n }\n }\n\n // Special case for round caps (nicer with this)\n if( dash_caps.y == 1.0 ) {\n if( (u < _start) && (dash_start < segment_start ) && (abs(v_angles.x) < PI/2.0)) {\n discard;\n }\n }\n\n // Special case for triangle caps (in & out) and square\n // We make sure the cap stop at crossing frontier\n if( (dash_caps.x != 1.0) && (dash_caps.x != 5.0) ) {\n if( (dash_start < segment_start ) && (abs(v_angles.x) < PI/2.0) ) {\n float a = v_angles.x/2.0;\n float x = (segment_start-dx)*cos(a) - dy*sin(a);\n float y = (segment_start-dx)*sin(a) + dy*cos(a);\n if( x > 0.0 ) discard;\n // We transform the cap into square to avoid holes\n dash_caps.x = 4.0;\n }\n }\n\n // Special case for triangle caps (in & out) and square\n // We make sure the cap stop at crossing frontier\n if( (dash_caps.y != 1.0) && (dash_caps.y != 5.0) ) {\n if( (dash_stop > segment_stop ) && (abs(v_angles.y) < PI/2.0) ) {\n float a = v_angles.y/2.0;\n float x = (dx-segment_stop)*cos(a) - dy*sin(a);\n float y = (dx-segment_stop)*sin(a) + dy*cos(a);\n if( x > 0.0 ) discard;\n // We transform the caps into square to avoid holes\n dash_caps.y = 4.0;\n }\n }\n }\n\n // Line cap at start\n if( (dx < line_start) && (dash_start < line_start) && (dash_stop > line_start) ) {\n d = cap( int(linecaps.x), dx-line_start, dy, t, v_linewidth);\n }\n // Line cap at stop\n else if( (dx > line_stop) && (dash_stop > line_stop) && (dash_start < line_stop) ) {\n d = cap( int(linecaps.y), dx-line_stop, dy, t, v_linewidth);\n }\n // Dash cap left - dash_type = -1, 0 or 1, but there may be roundoff errors\n else if( dash_type < -0.5 ) {\n d = cap( int(dash_caps.y), abs(u-dash_center), dy, t, v_linewidth);\n if( (dx > line_start) && (dx < line_stop) )\n d = max(d,d_join);\n }\n // Dash cap right\n else if( dash_type > 0.5 ) {\n d = cap( int(dash_caps.x), abs(dash_center-u), dy, t, v_linewidth);\n if( (dx > line_start) && (dx < line_stop) )\n d = max(d,d_join);\n }\n // Dash body (plain)\n else {// if( dash_type > -0.5 && dash_type < 0.5) {\n d = abs(dy);\n }\n\n // Line join\n if( (dx > line_start) && (dx < line_stop)) {\n if( (dx <= segment_start) && (dash_start <= segment_start)\n && (dash_stop >= segment_start) ) {\n d = d_join;\n // Antialias at outer border\n float angle = PI/2.+v_angles.x;\n float f = abs( (segment_start - dx)*cos(angle) - dy*sin(angle));\n d = max(f,d);\n }\n else if( (dx > segment_stop) && (dash_start <= segment_stop)\n && (dash_stop >= segment_stop) ) {\n d = d_join;\n // Antialias at outer border\n float angle = PI/2.+v_angles.y;\n float f = abs((dx - segment_stop)*cos(angle) - dy*sin(angle));\n d = max(f,d);\n }\n else if( dx < (segment_start - v_linewidth/2.)) {\n discard;\n }\n else if( dx > (segment_stop + v_linewidth/2.)) {\n discard;\n }\n }\n else if( dx < (segment_start - v_linewidth/2.)) {\n discard;\n }\n else if( dx > (segment_stop + v_linewidth/2.)) {\n discard;\n }\n }\n\n // Distance to border ------------------------------------------------------\n d = d - t;\n if( d < 0.0 ) {\n gl_FragColor = color;\n } else {\n d /= u_antialias;\n gl_FragColor = vec4(color.rgb, exp(-d*d)*color.a);\n }\n}";
LineGLGlyph.prototype.init = function () {
var gl;
gl = this.gl;
this._scale_aspect = 0;
this.prog = new gloo2.Program(gl);
this.prog.set_shaders(this.VERT, this.FRAG);
this.index_buffer = new gloo2.IndexBuffer(gl);
this.vbo_position = new gloo2.VertexBuffer(gl);
this.vbo_tangents = new gloo2.VertexBuffer(gl);
this.vbo_segment = new gloo2.VertexBuffer(gl);
this.vbo_angles = new gloo2.VertexBuffer(gl);
this.vbo_texcoord = new gloo2.VertexBuffer(gl);
return this.dash_atlas = new DashAtlas(gl);
};
LineGLGlyph.prototype.draw = function (indices, mainGlyph, trans) {
var baked_offset, chunk, chunks, chunksize, i, l, mainGlGlyph, nvertices, offset, p, q, ref, ref1, ref2, results, scale_length, sx, sy, these_indices, uint16_index;
mainGlGlyph = mainGlyph.glglyph;
if (mainGlGlyph.data_changed) {
if (!(isFinite(trans.dx) && isFinite(trans.dy))) {
return;
}
mainGlGlyph._baked_offset = [trans.dx, trans.dy];
mainGlGlyph._set_data();
mainGlGlyph.data_changed = false;
}
if (this.visuals_changed) {
this._set_visuals();
this.visuals_changed = false;
}
sx = trans.sx;
sy = trans.sy;
scale_length = Math.sqrt(sx * sx + sy * sy);
sx /= scale_length;
sy /= scale_length;
if (Math.abs(this._scale_aspect - (sy / sx)) > Math.abs(1e-3 * this._scale_aspect)) {
mainGlGlyph._update_scale(sx, sy);
this._scale_aspect = sy / sx;
}
this.prog.set_attribute('a_position', 'vec2', mainGlGlyph.vbo_position);
this.prog.set_attribute('a_tangents', 'vec4', mainGlGlyph.vbo_tangents);
this.prog.set_attribute('a_segment', 'vec2', mainGlGlyph.vbo_segment);
this.prog.set_attribute('a_angles', 'vec2', mainGlGlyph.vbo_angles);
this.prog.set_attribute('a_texcoord', 'vec2', mainGlGlyph.vbo_texcoord);
this.prog.set_uniform('u_length', 'float', [mainGlGlyph.cumsum]);
this.prog.set_texture('u_dash_atlas', this.dash_atlas.tex);
baked_offset = mainGlGlyph._baked_offset;
this.prog.set_uniform('u_pixel_ratio', 'float', [trans.pixel_ratio]);
this.prog.set_uniform('u_canvas_size', 'vec2', [trans.width, trans.height]);
this.prog.set_uniform('u_offset', 'vec2', [trans.dx - baked_offset[0], trans.dy - baked_offset[1]]);
this.prog.set_uniform('u_scale_aspect', 'vec2', [sx, sy]);
this.prog.set_uniform('u_scale_length', 'float', [scale_length]);
this.I_triangles = mainGlGlyph.I_triangles;
if (this.I_triangles.length < 65535) {
this.index_buffer.set_size(this.I_triangles.length * 2);
this.index_buffer.set_data(0, new Uint16Array(this.I_triangles));
return this.prog.draw(this.gl.TRIANGLES, this.index_buffer);
}
else {
indices = this.I_triangles;
nvertices = this.I_triangles.length;
chunksize = 64008;
chunks = [];
for (i = l = 0, ref = Math.ceil(nvertices / chunksize); 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) {
chunks.push([]);
}
for (i = p = 0, ref1 = indices.length; 0 <= ref1 ? p < ref1 : p > ref1; i = 0 <= ref1 ? ++p : --p) {
uint16_index = indices[i] % chunksize;
chunk = Math.floor(indices[i] / chunksize);
chunks[chunk].push(uint16_index);
}
results = [];
for (chunk = q = 0, ref2 = chunks.length; 0 <= ref2 ? q < ref2 : q > ref2; chunk = 0 <= ref2 ? ++q : --q) {
these_indices = new Uint16Array(chunks[chunk]);
offset = chunk * chunksize * 4;
if (these_indices.length === 0) {
continue;
}
this.prog.set_attribute('a_position', 'vec2', mainGlGlyph.vbo_position, 0, offset * 2);
this.prog.set_attribute('a_tangents', 'vec4', mainGlGlyph.vbo_tangents, 0, offset * 4);
this.prog.set_attribute('a_segment', 'vec2', mainGlGlyph.vbo_segment, 0, offset * 2);
this.prog.set_attribute('a_angles', 'vec2', mainGlGlyph.vbo_angles, 0, offset * 2);
this.prog.set_attribute('a_texcoord', 'vec2', mainGlGlyph.vbo_texcoord, 0, offset * 2);
this.index_buffer.set_size(these_indices.length * 2);
this.index_buffer.set_data(0, these_indices);
results.push(this.prog.draw(this.gl.TRIANGLES, this.index_buffer));
}
return results;
}
};
LineGLGlyph.prototype._set_data = function () {
this._bake();
this.vbo_position.set_size(this.V_position.length * 4);
this.vbo_position.set_data(0, this.V_position);
this.vbo_tangents.set_size(this.V_tangents.length * 4);
this.vbo_tangents.set_data(0, this.V_tangents);
this.vbo_angles.set_size(this.V_angles.length * 4);
this.vbo_angles.set_data(0, this.V_angles);
this.vbo_texcoord.set_size(this.V_texcoord.length * 4);
return this.vbo_texcoord.set_data(0, this.V_texcoord);
};
LineGLGlyph.prototype._set_visuals = function () {
var cap, color, dash_index, dash_pattern, dash_period, join, ref;
color = color_1.color2rgba(this.glyph.visuals.line.line_color.value(), this.glyph.visuals.line.line_alpha.value());
cap = this.CAPS[this.glyph.visuals.line.line_cap.value()];
join = this.JOINS[this.glyph.visuals.line.line_join.value()];
this.prog.set_uniform('u_color', 'vec4', color);
this.prog.set_uniform('u_linewidth', 'float', [this.glyph.visuals.line.line_width.value()]);
this.prog.set_uniform('u_antialias', 'float', [0.9]);
this.prog.set_uniform('u_linecaps', 'vec2', [cap, cap]);
this.prog.set_uniform('u_linejoin', 'float', [join]);
this.prog.set_uniform('u_miter_limit', 'float', [10.0]);
dash_pattern = this.glyph.visuals.line.line_dash.value();
dash_index = 0;
dash_period = 1;
if (dash_pattern.length) {
ref = this.dash_atlas.get_atlas_data(dash_pattern), dash_index = ref[0], dash_period = ref[1];
}
this.prog.set_uniform('u_dash_index', 'float', [dash_index]);
this.prog.set_uniform('u_dash_phase', 'float', [this.glyph.visuals.line.line_dash_offset.value()]);
this.prog.set_uniform('u_dash_period', 'float', [dash_period]);
this.prog.set_uniform('u_dash_caps', 'vec2', [cap, cap]);
return this.prog.set_uniform('u_closed', 'float', [0]);
};
LineGLGlyph.prototype._bake = function () {
var A, I, T, V_angles, V_angles2, V_position, V_position2, V_tangents, V_tangents2, V_texcoord, V_texcoord2, Vp, Vt, _x, _y, i, i1, j, k, l, m, n, ni, o, p, q, r, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, results, s, t, u, w, y, z;
n = this.nvertices;
_x = new Float64Array(this.glyph._x);
_y = new Float64Array(this.glyph._y);
V_position = Vp = new Float32Array(n * 2);
V_angles = new Float32Array(n * 2);
V_tangents = Vt = new Float32Array(n * 4);
V_texcoord = new Float32Array(n * 2);
for (i = l = 0, ref = n; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) {
V_position[i * 2 + 0] = _x[i] + this._baked_offset[0];
V_position[i * 2 + 1] = _y[i] + this._baked_offset[1];
}
this.tangents = T = new Float32Array(n * 2 - 2);
for (i = p = 0, ref1 = n - 1; 0 <= ref1 ? p < ref1 : p > ref1; i = 0 <= ref1 ? ++p : --p) {
T[i * 2 + 0] = Vp[(i + 1) * 2 + 0] - Vp[i * 2 + 0];
T[i * 2 + 1] = Vp[(i + 1) * 2 + 1] - Vp[i * 2 + 1];
}
for (i = q = 0, ref2 = n - 1; 0 <= ref2 ? q < ref2 : q > ref2; i = 0 <= ref2 ? ++q : --q) {
V_tangents[(i + 1) * 4 + 0] = T[i * 2 + 0];
V_tangents[(i + 1) * 4 + 1] = T[i * 2 + 1];
V_tangents[i * 4 + 2] = T[i * 2 + 0];
V_tangents[i * 4 + 3] = T[i * 2 + 1];
}
V_tangents[0 * 4 + 0] = T[0];
V_tangents[0 * 4 + 1] = T[1];
V_tangents[(n - 1) * 4 + 2] = T[(n - 2) * 2 + 0];
V_tangents[(n - 1) * 4 + 3] = T[(n - 2) * 2 + 1];
A = new Float32Array(n);
for (i = r = 0, ref3 = n; 0 <= ref3 ? r < ref3 : r > ref3; i = 0 <= ref3 ? ++r : --r) {
A[i] = Math.atan2(Vt[i * 4 + 0] * Vt[i * 4 + 3] - Vt[i * 4 + 1] * Vt[i * 4 + 2], Vt[i * 4 + 0] * Vt[i * 4 + 2] + Vt[i * 4 + 1] * Vt[i * 4 + 3]);
}
for (i = s = 0, ref4 = n - 1; 0 <= ref4 ? s < ref4 : s > ref4; i = 0 <= ref4 ? ++s : --s) {
V_angles[i * 2 + 0] = A[i];
V_angles[i * 2 + 1] = A[i + 1];
}
m = 4 * n - 4;
this.V_position = V_position2 = new Float32Array(m * 2);
this.V_angles = V_angles2 = new Float32Array(m * 2);
this.V_tangents = V_tangents2 = new Float32Array(m * 4);
this.V_texcoord = V_texcoord2 = new Float32Array(m * 2);
o = 2;
for (i = t = 0, ref5 = n; 0 <= ref5 ? t < ref5 : t > ref5; i = 0 <= ref5 ? ++t : --t) {
for (j = u = 0; u < 4; j = ++u) {
for (k = w = 0; w < 2; k = ++w) {
V_position2[(i * 4 + j - o) * 2 + k] = V_position[i * 2 + k];
V_angles2[(i * 4 + j) * 2 + k] = V_angles[i * 2 + k];
}
for (k = y = 0; y < 4; k = ++y) {
V_tangents2[(i * 4 + j - o) * 4 + k] = V_tangents[i * 4 + k];
}
}
}
for (i = z = 0, ref6 = n; 0 <= ref6 ? z <= ref6 : z >= ref6; i = 0 <= ref6 ? ++z : --z) {
V_texcoord2[(i * 4 + 0) * 2 + 0] = -1;
V_texcoord2[(i * 4 + 1) * 2 + 0] = -1;
V_texcoord2[(i * 4 + 2) * 2 + 0] = +1;
V_texcoord2[(i * 4 + 3) * 2 + 0] = +1;
V_texcoord2[(i * 4 + 0) * 2 + 1] = -1;
V_texcoord2[(i * 4 + 1) * 2 + 1] = +1;
V_texcoord2[(i * 4 + 2) * 2 + 1] = -1;
V_texcoord2[(i * 4 + 3) * 2 + 1] = +1;
}
ni = (n - 1) * 6;
this.I_triangles = I = new Uint32Array(ni);
results = [];
for (i = i1 = 0, ref7 = n; 0 <= ref7 ? i1 < ref7 : i1 > ref7; i = 0 <= ref7 ? ++i1 : --i1) {
I[i * 6 + 0] = 0 + 4 * i;
I[i * 6 + 1] = 1 + 4 * i;
I[i * 6 + 2] = 3 + 4 * i;
I[i * 6 + 3] = 2 + 4 * i;
I[i * 6 + 4] = 0 + 4 * i;
results.push(I[i * 6 + 5] = 3 + 4 * i);
}
return results;
};
LineGLGlyph.prototype._update_scale = function (sx, sy) {
var N, T, V_segment, V_segment2, cumsum, i, j, k, l, m, n, p, q, r, ref, ref1, ref2, s;
n = this.nvertices;
m = 4 * n - 4;
T = this.tangents;
N = new Float32Array(n - 1);
V_segment = new Float32Array(n * 2);
this.V_segment = V_segment2 = new Float32Array(m * 2);
for (i = l = 0, ref = n - 1; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) {
N[i] = Math.sqrt(Math.pow(T[i * 2 + 0] * sx, 2) + Math.pow(T[i * 2 + 1] * sy, 2));
}
cumsum = 0;
for (i = p = 0, ref1 = n - 1; 0 <= ref1 ? p < ref1 : p > ref1; i = 0 <= ref1 ? ++p : --p) {
cumsum += N[i];
V_segment[(i + 1) * 2 + 0] = cumsum;
V_segment[i * 2 + 1] = cumsum;
}
for (i = q = 0, ref2 = n; 0 <= ref2 ? q < ref2 : q > ref2; i = 0 <= ref2 ? ++q : --q) {
for (j = r = 0; r < 4; j = ++r) {
for (k = s = 0; s < 2; k = ++s) {
V_segment2[(i * 4 + j) * 2 + k] = V_segment[i * 2 + k];
}
}
}
this.cumsum = cumsum;
this.vbo_segment.set_size(this.V_segment.length * 4);
return this.vbo_segment.set_data(0, this.V_segment);
};
return LineGLGlyph;
})(base_1.BaseGLGlyph);