Skip to content

Commit

Permalink
Merge pull request #14 from gl-vis/rotate3d-axis-lables
Browse files Browse the repository at this point in the history
Options for align axis labels and ticks in 3D scenes
  • Loading branch information
archmoj authored Oct 10, 2018
2 parents 8d8011d + 1633088 commit db308a3
Show file tree
Hide file tree
Showing 4 changed files with 1,829 additions and 26 deletions.
27 changes: 24 additions & 3 deletions axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ proto.draw = function(params) {
projection,
this.pixelRatio)

var alignOpt = [0,0,1]
// Note: the 3rd member is the integer option
// from {-1, 0, 1, 2, 3, ..., n}

for(var i=0; i<3; ++i) {

var minor = lineOffset[i].primalMinor
Expand All @@ -468,6 +472,12 @@ proto.draw = function(params) {
}
}

var axis = [0,0,0]
axis[i] = 1

var alignDir = [0,0,0]
alignDir[i] = 1

//Draw tick text
if(this.tickEnable[i]) {

Expand All @@ -480,9 +490,12 @@ proto.draw = function(params) {
this._text.drawTicks(
i,
this.tickSize[i],
this.tickAngle[i],
this.tickAngle[i] + 0.5 * Math.PI,
offset,
this.tickColor[i])
this.tickColor[i],
axis,
alignDir,
alignOpt)
}

//Draw labels
Expand All @@ -494,13 +507,21 @@ proto.draw = function(params) {
}
offset[i] += 0.5 * (bounds[0][i] + bounds[1][i])

var alignDir = [0,0,0]
if(this.labels[i].length > 4) { // for large label axis enable alignDir to axis
alignDir[i] = 1
}

//Draw axis
this._text.drawLabel(
i,
this.labelSize[i],
this.labelAngle[i],
offset,
this.labelColor[i])
this.labelColor[i],
[0,0,0],
alignDir,
alignOpt)
}
}

Expand Down
118 changes: 104 additions & 14 deletions lib/shaders/textVert.glsl
Original file line number Diff line number Diff line change
@@ -1,30 +1,120 @@
attribute vec3 position;

uniform mat4 model, view, projection;
uniform vec3 offset, axis;
uniform vec3 offset, axis, alignDir, alignOpt;
uniform float scale, angle, pixelScale;
uniform vec2 resolution;

void main() {
//Compute plane offset
vec2 planeCoord = position.xy * pixelScale;
mat2 planeXform = scale * mat2(cos(angle), sin(angle),
-sin(angle), cos(angle));
vec2 viewOffset = 2.0 * planeXform * planeCoord / resolution;
vec3 project(vec3 p) {
vec4 pp = projection * view * model * vec4(p, 1.0);
return pp.xyz / max(pp.w, 0.0001);
}

const float PI = 3.141592;
const float TWO_PI = 2.0 * PI;
const float HALF_PI = 0.5 * PI;
const float ONE_AND_HALF_PI = 1.5 * PI;

float positive_angle(float a) {
if (a < 0.0) return a + TWO_PI;
return a;
}

float look_upwards(float a) {
float b = positive_angle(a);
if ((b > HALF_PI) && (b < ONE_AND_HALF_PI)) return b - PI;
return b;
}

float look_horizontal_or_vertical(float a, float ratio) {
// ratio controls the ratio between being horizontal to (vertical + horizontal)
// if ratio is set to 0.5 then it is 50%, 50%.
// when using a higher ratio e.g. 0.75 the result would
// likely be more horizontal than vertical.

float b = positive_angle(a);
if (b < ( ratio) * HALF_PI) return 0.0;
else if (b < (2.0 - ratio) * HALF_PI) return -HALF_PI;
else if (b < (2.0 + ratio) * HALF_PI) return 0.0;
else if (b < (4.0 - ratio) * HALF_PI) return HALF_PI;
return 0.0;
}

float roundTo(float a, float b) {
return float(b * floor((a + 0.5 * b) / b));
}

float look_round_n_directions(float a, int n) {
float b = positive_angle(a);
float div = TWO_PI / float(n);
float c = roundTo(b, div);
return look_upwards(c);
}

int option = int(floor(alignOpt.z + 0.001));

float applyAlignOption(float rawAngle) {

if (option == -1) {
// useful for backward compatibility, all texts remains horizontal
return 0.0;
} else if (option == 0) {
// use the raw angle as calculated by atan
return rawAngle;
} else if (option == 1) {
// option 1: use free angle, but flip when reversed
return look_upwards(rawAngle);
} else if (option == 2) {
// option 2: horizontal or vertical
return look_horizontal_or_vertical(rawAngle, 0.8); // 0.8 here means: increase the chance of getting horizontal labels
}

// option 3-n: round to n directions
return look_round_n_directions(rawAngle, option);
}

void main() {

//Compute world offset
float axisDistance = position.z;
vec3 dataPosition = axisDistance * axis + offset;
vec4 worldPosition = model * vec4(dataPosition, 1);


float clipAngle = 0.0;

if ((alignDir.x != 0.0) ||
(alignDir.y != 0.0) ||
(alignDir.z != 0.0)) {

vec3 REF = dataPosition;

vec3 startPoint = project(REF);
vec3 endPoint = project(REF + alignDir);

clipAngle = applyAlignOption(
angle + // i.e. user defined attributes for each tick
atan(
(endPoint.y - startPoint.y) * resolution.y,
(endPoint.x - startPoint.x) * resolution.x
)
);
}

//Compute plane offset
vec2 planeCoord = position.xy * pixelScale;

mat2 planeXform = scale * mat2(
cos(clipAngle), sin(clipAngle),
-sin(clipAngle), cos(clipAngle)
);

vec2 viewOffset = 2.0 * planeXform * planeCoord / resolution;

//Compute clip position
vec4 viewPosition = view * worldPosition;
vec4 clipPosition = projection * viewPosition;
clipPosition /= clipPosition.w;
vec3 clipPosition = project(dataPosition);

//Apply text offset in clip coordinates
clipPosition += vec4(viewOffset, 0, 0);
clipPosition += vec3(viewOffset, 0.0);

//Done
gl_Position = clipPosition;
gl_Position = vec4(clipPosition, 1.0);
}
18 changes: 9 additions & 9 deletions lib/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,34 +122,34 @@ proto.update = function(bounds, labels, labelFont, ticks, tickFont) {
}

//Draws the tick marks for an axis
var AXIS = [0,0,0]
proto.drawTicks = function(d, scale, angle, offset, color) {
proto.drawTicks = function(d, scale, angle, offset, color, axis, alignDir, alignOpt) {
if(!this.tickCount[d]) {
return
}

var v = AXIS
v[0] = v[1] = v[2] = 0
v[d] = 1
this.shader.uniforms.axis = v
this.shader.uniforms.axis = axis
this.shader.uniforms.color = color
this.shader.uniforms.angle = angle
this.shader.uniforms.scale = scale
this.shader.uniforms.offset = offset
this.shader.uniforms.alignDir = alignDir
this.shader.uniforms.alignOpt = alignOpt
this.vao.draw(this.gl.TRIANGLES, this.tickCount[d], this.tickOffset[d])
}

//Draws the text label for an axis
var ZERO = [0,0,0]
proto.drawLabel = function(d, scale, angle, offset, color) {
proto.drawLabel = function(d, scale, angle, offset, color, axis, alignDir, alignOpt) {
if(!this.labelCount[d]) {
return
}
this.shader.uniforms.axis = ZERO

this.shader.uniforms.axis = axis
this.shader.uniforms.color = color
this.shader.uniforms.angle = angle
this.shader.uniforms.scale = scale
this.shader.uniforms.offset = offset
this.shader.uniforms.alignDir = alignDir
this.shader.uniforms.alignOpt = alignOpt
this.vao.draw(this.gl.TRIANGLES, this.labelCount[d], this.labelOffset[d])
}

Expand Down
Loading

0 comments on commit db308a3

Please sign in to comment.