Browse Source

Working rounded lines!

infinite
A.Olokhtonov 2 years ago
parent
commit
5593536485
  1. 323
      client/webgl.js

323
client/webgl.js

@ -102,60 +102,20 @@ const bgcolor = { 'r': 0, 'g': 0, 'b': 0 }; @@ -102,60 +102,20 @@ const bgcolor = { 'r': 0, 'g': 0, 'b': 0 };
const stroke_color = { 'r': 0.2, 'g': 0.2, 'b': 0.2 };
let debug_draw = true;
function push_cap_triangle(positions, cap_points, i1, i2, i3) {
positions.push(cap_points[i1].x, cap_points[i1].y);
positions.push(cap_points[i2].x, cap_points[i2].y);
positions.push(cap_points[i3].x, cap_points[i3].y);
}
function push_joint_triangle(positions, joint_points, i1, i2, i3) {
positions.push(joint_points[i1].x, joint_points[i1].y);
positions.push(joint_points[i2].x, joint_points[i2].y);
positions.push(joint_points[i3].x, joint_points[i3].y);
}
function push_cap(positions, pps, p, pnext, stroke_width) {
// Rounded cap!
const cap_points = [];
cap_points.push(p); /* not used in positions, added for convenience */
cap_points.push(pps.p1);
cap_points.push(pps.p2);
const dy = cap_points[1].y - cap_points[0].y;
const dx = cap_points[1].x - cap_points[0].x;
const down = (pnext.y >= p.y); // = accounts correctly for horizontal lines somehow
const phi_step = Math.PI / 8;
const r = stroke_width / 2;
const starting_phi = Math.atan(dy / dx);
const sign = down ? -1 : 1;
for (let i = 1; i <= 7; ++i) {
const phi = starting_phi + i * phi_step;
const ox = r * Math.cos(phi);
const oy = r * Math.sin(phi);
const x = cap_points[0].x + sign * ox;
const y = cap_points[0].y + sign * oy;
cap_points.push({'x': x, 'y': y});
}
push_cap_triangle(positions, cap_points, 2, 1, 6);
push_cap_triangle(positions, cap_points, 2, 6, 4);
push_cap_triangle(positions, cap_points, 6, 1, 8);
push_cap_triangle(positions, cap_points, 2, 4, 3);
push_cap_triangle(positions, cap_points, 4, 6, 5);
push_cap_triangle(positions, cap_points, 6, 8, 7);
push_cap_triangle(positions, cap_points, 8, 1, 9);
function push_circle_at(positions, c, o) {
positions.push(c.x + o[0].x, c.y + o[0].y, c.x + o[4].x, c.y + o[4].y, c.x + o[8].x, c.y + o[8].y);
positions.push(c.x + o[4].x, c.y + o[4].y, c.x + o[0].x, c.y + o[0].y, c.x + o[2].x, c.y + o[2].y);
positions.push(c.x + o[8].x, c.y + o[8].y, c.x + o[4].x, c.y + o[4].y, c.x + o[6].x, c.y + o[6].y);
positions.push(c.x + o[0].x, c.y + o[0].y, c.x + o[8].x, c.y + o[8].y, c.x + o[10].x, c.y + o[10].y);
positions.push(c.x + o[2].x, c.y + o[2].y, c.x + o[0].x, c.y + o[0].y, c.x + o[1].x, c.y + o[1].y);
positions.push(c.x + o[4].x, c.y + o[4].y, c.x + o[2].x, c.y + o[2].y, c.x + o[3].x, c.y + o[3].y);
positions.push(c.x + o[6].x, c.y + o[6].y, c.x + o[4].x, c.y + o[4].y, c.x + o[5].x, c.y + o[5].y);
positions.push(c.x + o[8].x, c.y + o[8].y, c.x + o[6].x, c.y + o[6].y, c.x + o[7].x, c.y + o[7].y);
positions.push(c.x + o[10].x, c.y + o[10].y, c.x + o[8].x, c.y + o[8].y, c.x + o[9].x, c.y + o[9].y);
positions.push(c.x + o[0].x, c.y + o[0].y, c.x + o[10].x, c.y + o[10].y, c.x + o[11].x, c.y + o[11].y);
}
function push_stroke_positions(stroke, stroke_width, positions) {
let last_x1;
let last_y1;
let last_x2;
let last_y2;
const points = stroke.points;
if (points.length < 2) {
@ -163,243 +123,55 @@ function push_stroke_positions(stroke, stroke_width, positions) { @@ -163,243 +123,55 @@ function push_stroke_positions(stroke, stroke_width, positions) {
return;
}
for (let i = 0; i < points.length; ++i) {
const px = points[i].x;
const py = points[i].y;
// These might be undefined
let nextpx;
let nextpy;
if (i < points.length - 1) {
nextpx = points[i + 1].x;
nextpy = points[i + 1].y;
}
if (i === 0) {
const pps = perpendicular(px, py, nextpx, nextpy, stroke_width);
last_x1 = pps.p1.x;
last_y1 = pps.p1.y;
last_x2 = pps.p2.x;
last_y2 = pps.p2.y;
push_cap(positions, pps, points[0], points[1], stroke_width);
continue;
}
// Place points at (stroke_width / 2) distance from the line
const prevpx = points[i - 1].x;
const prevpy = points[i - 1].y;
let x1;
let y1;
let x2;
let y2;
if (i < points.length - 1) {
let d1x = px - prevpx;
let d1y = py - prevpy;
let d2x = px - nextpx;
let d2y = py - nextpy;
// Simple 12 point circle (store offsets and reuse)
const POINTS = 12;
const phi_step = 2 * Math.PI / POINTS;
// Construct "inner" sides and find their intersection point
let perp1x = d1y;
let perp1y = -d1x;
const circle_offsets = [];
const perpnorm1 = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
perp1x /= perpnorm1;
perp1y /= perpnorm1;
// Starting point for first "inner" line
const inner1x = prevpx + perp1x * stroke_width / 2;
const inner1y = prevpy + perp1y * stroke_width / 2;
let perp2x = -d2y;
let perp2y = d2x;
const perpnorm2 = Math.sqrt(perp2x * perp2x + perp2y * perp2y);
perp2x /= perpnorm2;
perp2y /= perpnorm2;
// Starting point for second "inner" line
const inner2x = nextpx + perp2x * stroke_width / 2;
const inner2y = nextpy + perp2y * stroke_width / 2;
const innerintt2 = (d1x * (inner1y - inner2y) + d1y * (inner2x - inner1x)) / (d1x * d2y - d1y * d2x);
const innerint1x = inner2x + innerintt2 * d2x;
const innerint1y = inner2y + innerintt2 * d2y;
const sanityt1 = (inner2x + innerintt2 * d2x - inner1x) / d1x;
const sanity1x = inner1x + sanityt1 * d1x;
const sanity1y = inner1y + sanityt1 * d1y;
// Starting point for first "outer" line
const outer1x = prevpx - perp1x * stroke_width / 2;
const outer1y = prevpy - perp1y * stroke_width / 2;
// Starting point for second "outer" line
const outer2x = nextpx - perp2x * stroke_width / 2;
const outer2y = nextpy - perp2y * stroke_width / 2;
const outerintt2 = (d1x * (outer1y - outer2y) + d1y * (outer2x - outer1x)) / (d1x * d2y - d1y * d2x);
const outerint1x = outer2x + outerintt2 * d2x;
const outerint1y = outer2y + outerintt2 * d2y;
x1 = innerint1x;
y1 = innerint1y;
x2 = outerint1x;
y2 = outerint1y;
// If we are turning left, then we should place the circle on the right, and vice versa
const turn_left = point_right_of_line(points[i - 1], points[i + 1], points[i]);
// if (turn_left) {
const s1x = px - perp2x * stroke_width / 2;
const s1y = py - perp2y * stroke_width / 2;
const s2x = px - perp1x * stroke_width / 2;
const s2y = py - perp1y * stroke_width / 2;
let s12prod = perp1x * perp2x + perp1y * perp2y;
let angle = Math.acos(s12prod);
// Generate circular segment
const npoints = Math.ceil(angle / Math.PI * 7);
if (npoints > 1) {
const joint_points = [];
for (let i = 0; i < POINTS; ++i) {
const phi = phi_step * i;
const ox = stroke_width / 2 * Math.cos(phi);
const oy = stroke_width / 2 * Math.sin(phi);
circle_offsets.push({'x': ox, 'y': oy});
}
joint_points.push(points[i]);
joint_points.push({'x': s2x, 'y': s2y});
joint_points.push({'x': s1x, 'y': s1y});
for (let i = 0; i < points.length - 1; ++i) {
const px = points[i].x;
const py = points[i].y;
const pnext = points[i + 1];
const p = points[i];
const down = (pnext.y > p.y);
const phi_step = angle / (npoints + 1);
const r = stroke_width / 2;
const starting_phi = Math.atan(perp2y / perp2x);
const sign = down ? -1 : 1;
const nextpx = points[i + 1].x;
const nextpy = points[i + 1].y;
for (let i = 1; i <= npoints; ++i) {
const phi = starting_phi + i * phi_step;
const ox = r * Math.cos(phi);
const oy = r * Math.sin(phi);
const x = p.x + sign * ox;
const y = p.y + sign * oy;
joint_points.push({'x': x, 'y': y});
}
const d1x = nextpx - px;
const d1y = nextpy - py;
// Rectangular segment up to here
if (innerintt2 > 0) {
positions.push(last_x1, last_y1);
positions.push(innerint1x - perp1x * stroke_width, innerint1y - perp1y * stroke_width);
positions.push(last_x2, last_y2);
// Perpendicular to (d1x, d1y), points to the LEFT
let perp1x = -d1y;
let perp1y = d1x;
positions.push(last_x1, last_y1);
positions.push(innerint1x - perp1x * stroke_width, innerint1y - perp1y * stroke_width);
positions.push(innerint1x, innerint1y);
const perpnorm1 = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
// Four triangles to cover the non-circle part of the join
positions.push(innerint1x, innerint1y);
positions.push(px, py);
positions.push(innerint1x - perp1x * stroke_width, innerint1y - perp1y * stroke_width);
positions.push(innerint1x - perp1x * stroke_width, innerint1y - perp1y * stroke_width);
positions.push(px, py);
positions.push(s2x, s2y);
positions.push(innerint1x - perp2x * stroke_width, innerint1y - perp2y * stroke_width);
positions.push(px, py);
positions.push(innerint1x, innerint1y);
positions.push(innerint1x - perp2x * stroke_width, innerint1y - perp2y * stroke_width);
positions.push(px, py);
positions.push(s1x, s1y);
last_x1 = innerint1x;
last_y1 = innerint1y;
last_x2 = innerint1x - perp2x * stroke_width;
last_y2 = innerint1y - perp2y * stroke_width;
} else {
last_x1 = nextpx + perp2x * stroke_width / 2;
last_y1 = nextpy + perp2y * stroke_width / 2;
last_x2 = nextpx - perp2x * stroke_width / 2;
last_y2 = nextpy - perp2y * stroke_width / 2;
}
// push_joint_triangle(positions, joint_points, 0, 2, npoints + 2);
for (let i = 0; i < npoints; ++i) {
push_joint_triangle(positions, joint_points, 0, i + 3, i + 2);
}
push_joint_triangle(positions, joint_points, 0, 2 + npoints, 1);
} else {
if (innerintt2 > 0) {
// Rectangular segment up to interpolated perpendicular
let perp3x = (perp1x + perp2x) / 2.0;
let perp3y = (perp1y + perp2y) / 2.0;
const perp3norm = Math.sqrt(perp3x * perp3x + perp3y * perp3y);
perp3x /= perp3norm;
perp3y /= perp3norm;
positions.push(last_x1, last_y1);
positions.push(px - perp3x * stroke_width / 2, py - perp3y * stroke_width / 2);
positions.push(last_x2, last_y2);
positions.push(px - perp3x * stroke_width / 2, py - perp3y * stroke_width / 2);
positions.push(px + perp3x * stroke_width / 2, py + perp3y * stroke_width / 2);
positions.push(last_x1, last_y1);
last_x1 = px + perp3x * stroke_width / 2;
last_y1 = py + perp3y * stroke_width / 2;
last_x2 = px - perp3x * stroke_width / 2;
last_y2 = py - perp3y * stroke_width / 2;
} else {
last_x1 = nextpx + perp2x * stroke_width / 2;
last_y1 = nextpy + perp2y * stroke_width / 2;
last_x2 = nextpx - perp2x * stroke_width / 2;
last_y2 = nextpy - perp2y * stroke_width / 2;
// last_x1 = px + perp3x * stroke_width / 2;
// last_y1 = py + perp3y * stroke_width / 2;
// last_x2 = px - perp3x * stroke_width / 2;
// last_y2 = py - perp3y * stroke_width / 2;
}
}
// }
} else {
const pps = perpendicular(px, py, prevpx, prevpy, stroke_width);
perp1x /= perpnorm1;
perp1y /= perpnorm1;
x1 = pps.p2.x;
y1 = pps.p2.y;
x2 = pps.p1.x;
y2 = pps.p1.y;
const s1x = px + perp1x * stroke_width / 2;
const s1y = py + perp1y * stroke_width / 2;
const s2x = px - perp1x * stroke_width / 2;
const s2y = py - perp1y * stroke_width / 2;
push_cap(positions, pps, points[points.length - 1], points[points.length - 2], stroke_width);
const s3x = nextpx + perp1x * stroke_width / 2;
const s3y = nextpy + perp1y * stroke_width / 2;
const s4x = nextpx - perp1x * stroke_width / 2;
const s4y = nextpy - perp1y * stroke_width / 2;
positions.push(last_x1, last_y1);
positions.push(x2, y2);
positions.push(last_x2, last_y2);
positions.push(s1x, s1y, s2x, s2y, s4x, s4y);
positions.push(s1x, s1y, s4x, s4y, s3x, s3y);
positions.push(last_x1, last_y1);
positions.push(x1, y1);
positions.push(x2, y2);
}
push_circle_at(positions, points[i], circle_offsets);
}
push_circle_at(positions, points[points.length - 1], circle_offsets);
}
function draw(gl, program, locations, buffers, strokes) {
@ -527,6 +299,7 @@ function main() { @@ -527,6 +299,7 @@ function main() {
{'x': 100, 'y': 100},
{'x': 105, 'y': 500},
{'x': 108, 'y': 140},
{'x': 508, 'y': 240},
]
}
];

Loading…
Cancel
Save