Public
Edited
Mar 2, 2023
1 fork
1 star
Insert cell
# Line Corner Test
Insert cell
points = [
[100,350], [400,250], [400+300*Math.cos((cornerAngle-20)/180*Math.PI), 250+300*Math.sin((cornerAngle-20)/180*Math.PI)]
]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
lengths = lines.map(l => len(sub(l[1], l[0])))
Insert cell
units = lines.map((l, i) => [(l[1][0]-l[0][0])/lengths[i], (l[1][1]-l[0][1])/lengths[i]])
Insert cell
normals = units.map(([x,y]) => [-y, x])
Insert cell
angles = lines.map(l => Math.atan2(l[1][1]-l[0][1], l[1][0]-l[0][0]))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
corner_geometry4 = {

let l1Pos1 = offsets[0]
let l1Pos2 = offsets[0] + widths[0];
let l2Pos1 = offsets[1]
let l2Pos2 = offsets[1] + widths[1];

let leftBend = cornerAngle <= 0;
let rightBend = !leftBend;

// check if l1 begins to the left of l2 - the special case where the lines are overlapping is dealt later on.
let l1PosOuter = (l1Pos1 < l2Pos1) ? l1Pos1 : l1Pos2;
let l1PosInner = (l1Pos1 < l2Pos1) ? l1Pos2 : l1Pos1;
let l2PosOuter = (l1Pos1 < l2Pos1) ? l2Pos2 : l2Pos1;
let l2PosInner = (l1Pos1 < l2Pos1) ? l2Pos1 : l2Pos2;

let bendTowards = (leftBend != (l1Pos1 < l2Pos1));

let linesFullyOverlap = ((l1Pos1 < l2Pos1 && l1Pos2 > l2Pos2) || (l2Pos1 < l1Pos1 && l2Pos2 > l1Pos2));

let minWidth = Math.min(widths[0], widths[1]);
let maxWidth = Math.max(widths[0], widths[1]);

let angleTowards2 = (l1Pos1 < l2Pos1) != (cornerAngle < 0)

let outerStartL1 = add(points[0], mult(normals[0],l1PosOuter));
let innerStartL1 = add(points[0], mult(normals[0],l1PosInner));
let outerEndL1 = add(points[1], mult(normals[0], l1PosOuter), mult(units[0], -margin2));
let innerEndL1 = add(points[1], mult(normals[0], l1PosInner), mult(units[0], -margin2));
let innerStartL2 = add(points[1], mult(normals[1], l2PosInner), mult(units[1], margin2));
let outerStartL2 = add(points[1], mult(normals[1], l2PosOuter), mult(units[1], margin2));
let innerEndL2 = add(points[2], mult(normals[1], l2PosInner));
let outerEndL2 = add(points[2], mult(normals[1], l2PosOuter));

let laneIntersectionPoints = [
intersect(outerEndL1, units[0], innerStartL2, units[1]),
intersect(innerEndL1, units[0], innerStartL2, units[1]),
intersect(innerEndL1, units[0], outerStartL2, units[1]),
intersect(outerEndL1, units[0], outerStartL2, units[1]),
];

let geometry = {
// modeled as polygons
extensionPoly1: null,
extensionPoly2: null,
bendPoly1: null, // if lane gets only narrower, use only this (?)
bendPoly2: null,
crossingPoly: null,
// modeled as lines
extensionLine1: null,
extensionLine2: null,
bendPoint1: null, // if lane gets only narrower, use only this (?)
bendPoint2: null,
crossingLine: null,
// legacy
polygon: null
};
// store 2 boundary lines separately
let poly = [
[
outerEndL1,
innerStartL2,
],
[
outerStartL2,
innerEndL1,
]
];

let outerIntersectionDistOnL1 = intersect_dist(outerStartL1, units[0], innerStartL2, units[1]);
let innerIntersectionDistOnL1 = intersect_dist(innerStartL1, units[0], outerStartL2, units[1]);
let outerIntersectionDistOnL2 = intersect_dist(outerStartL2, units[1], innerStartL1, units[0]);
let innerIntersectionDistOnL2 = intersect_dist(innerStartL2, units[1], innerStartL1, units[0]);

debugger;

let outerCornerL1 = (leftBend || outerIntersectionDistOnL1 < lengths[0]-margin2) ? outerEndL1 : laneIntersectionPoints[0];
let tangentsL2 = tangentPoints(outerStartL2, minWidth, outerCornerL1);
// choose tangent point closest to inner end point of lane
let tDistances = tangentsL2.map(t => dist(t, innerEndL2));
let tangentPointL2 = (tDistances[1] > tDistances[0]) ? tangentsL2[0] : tangentsL2[1];

let crossingUnit = unit(sub(tangentPointL2, outerCornerL1));
let crossingNormal = normal(crossingUnit);

let tangentsL1 = tangentPoints(outerCornerL1, minWidth, outerStartL2);
// choose tangent point closest to inner start point of lane
let startPoint = add(points[0], mult(normals[0], l1PosInner));
tDistances = tangentsL1.map(t => dist(t, innerStartL1));
let tangentPointL1 = (tDistances[1] > tDistances[0]) ? tangentsL1[0] : tangentsL1[1];
// intersect with outer lane to get point on edge of lane
let bendInnerL2 = intersectLinesByPoints(outerEndL1, tangentPointL2, innerStartL2, innerEndL2);
let bendOuterL2 = intersect(outerStartL2,units[1],bendInnerL2,normals[1]);
let bendOuterL2Crossing = intersect(outerStartL2, crossingUnit, bendInnerL2, crossingNormal);
if (!bendTowards || outerIntersectionDistOnL1 < lengths[0] - margin2) { //leftBend ||
poly[0][1] = bendInnerL2;
// bend points fully within L2 geom?
if (dist(outerEndL2, bendOuterL2) < lengths[1] - margin2) {
geometry.bendPoly2 = [bendInnerL2, bendOuterL2, outerStartL2, bendOuterL2Crossing];
}
else {
geometry.bendPoly2 = [bendInnerL2, innerStartL2, outerStartL2, bendOuterL2Crossing];
}
}

let bendInnerL1 = intersectLinesByPoints(outerStartL2, tangentPointL1, innerStartL1, innerEndL1);
let bendOuterL1 = intersect(outerEndL1, units[0], bendInnerL1, normals[0]);
let bendOuterL1Crossing = intersect(outerEndL1, crossingUnit, bendInnerL1, crossingNormal);
if (!bendTowards || outerIntersectionDistOnL1 < lengths[0]-margin2) { // && (!leftBend || innerIntersectionDistOnL2 > 0)
poly[1][1] = bendInnerL1;
// bend points fully within L1 geom
if (dist(outerStartL1, bendOuterL1) < lengths[0] - margin2) {
geometry.bendPoly1 = [bendInnerL1, bendOuterL1, outerEndL1, bendOuterL1Crossing];
}
else {
geometry.bendPoly1 = [bendInnerL1, innerEndL1, outerEndL1, bendOuterL1Crossing];
}
}
// check if intersection is after l1 and before l2 -> add intersection point
if (outerIntersectionDistOnL1 > lengths[0]-margin2 && outerIntersectionDistOnL2 < margin2) {
// add corner vertex to polygon
poly[0].splice(1, 0, add(outerStartL1, mult(units[0], outerIntersectionDistOnL1)));
}
// again for other side of lines
if (innerIntersectionDistOnL1 > lengths[0]-margin2 && outerIntersectionDistOnL2 < margin2) {
// add corner vertex to polygon
poly[1].splice(1, 0, add(innerStartL1, mult(units[0], innerIntersectionDistOnL1)));
}

// legacy
//geometry.bendPoly1 = poly;
geometry.polygon = poly;

return geometry;

}
Insert cell
Insert cell
Insert cell
corner_polygon3 = {

let l1Pos1 = offsets[0]
let l1Pos2 = offsets[0] + widths[0];
let l2Pos1 = offsets[1]
let l2Pos2 = offsets[1] + widths[1];

let leftBend = cornerAngle <= 0;

// check if l1 begins to the left of l2 - the special case where the lines are overlapping is dealt later on.
let l1PosOuter = (l1Pos1 < l2Pos1) ? l1Pos1 : l1Pos2;
let l1PosInner = (l1Pos1 < l2Pos1) ? l1Pos2 : l1Pos1;
let l2PosOuter = (l1Pos1 < l2Pos1) ? l2Pos2 : l2Pos1;
let l2PosInner = (l1Pos1 < l2Pos1) ? l2Pos1 : l2Pos2;

let linesFullyOverlap = ((l1Pos1 < l2Pos1 && l1Pos2 > l2Pos2) || (l2Pos1 < l1Pos1 && l2Pos2 > l1Pos2));

let minWidth = Math.min(widths[0], widths[1]);
let maxWidth = Math.max(widths[0], widths[1]);

let angleTowards2 = (l1Pos1 < l2Pos1) != (cornerAngle < 0)

let innerStartL1 = points[0];
let outerStartL1 = add(points[0], mult(normals[0],offsets[0]));
let outerEndL1 = add(points[1], mult(normals[0], l1PosOuter), mult(units[0], -margin2));
let innerEndL1 = add(points[1], mult(normals[0], l1PosInner), mult(units[0], -margin2));
let innerStartL2 = add(points[1], mult(normals[1], l2PosInner), mult(units[1], margin2));
let innerEndL2 = add(innerStartL2, mult(units[1], lengths[1]));
let outerStartL2 = add(points[1], mult(normals[1], l2PosOuter), mult(units[1], margin2));

let laneIntersectionPoints = [
intersect(outerEndL1, units[0], innerStartL2, units[1]),
intersect(innerEndL1, units[0], innerStartL2, units[1]),
intersect(innerEndL1, units[0], outerStartL2, units[1]),
intersect(outerEndL1, units[0], outerStartL2, units[1]),
];

// store 2 boundary lines separately
let poly = [
[
outerEndL1, // outer end l1
innerStartL2, // inner start l2
],
[
outerStartL2, // outer start l2
innerEndL1, // inner end l1
]
];

let l2EndPointInner = add(points[1], mult(normals[1], l2PosInner), mult(units[1], lengths[1]));

let outerIntersectionDistOnL1 = intersect_dist(outerStartL1, units[0], innerStartL2, units[1]);
let innerIntersectionDistOnL1 = intersect_dist(outerStartL1, units[0], outerStartL2, units[1]);
let outerIntersectionDistOnL2 = intersect_dist(innerStartL2, units[1], outerStartL1, units[0]);
let innerIntersectionDistOnL2 = intersect_dist(innerStartL2, units[1], innerStartL1, units[0]);

debugger;

let outerCornerL1 = (leftBend || outerIntersectionDistOnL1 < lengths[0]-margin2) ? outerEndL1 : laneIntersectionPoints[0];
let tangentsOuter = tangentPoints(outerStartL2, minWidth, outerCornerL1);
// choose tangent point closest to inner end point of lane
let tDistances = tangentsOuter.map(t => dist(t, innerEndL2));
let tangentPointOuter = (tDistances[1] > tDistances[0]) ? tangentsOuter[0] : tangentsOuter[1];
// intersect with outer lane to get point on edge of lane
let crossingIntersectionL2 = intersectLinesByPoints(outerEndL1, tangentPointOuter, innerStartL2, innerEndL2);

if (leftBend || outerIntersectionDistOnL1 < lengths[0] - margin2) {
poly[0][1] = crossingIntersectionL2;
}

let tangentsInner = tangentPoints(outerCornerL1, minWidth, outerStartL2);
// choose tangent point closest to inner start point of lane
let startPoint = add(points[0], mult(normals[0], l1PosInner));
tDistances = tangentsInner.map(t => dist(t, innerStartL1));
let tangentPointInner = (tDistances[1] > tDistances[0]) ? tangentsInner[0] : tangentsInner[1];
let crossingIntersectionL1 = intersectLinesByPoints(outerStartL2, tangentPointInner, innerStartL1, innerEndL1);
let crossingIntersectionDistanceL1 = intersect_dist(innerStartL1, units[0], outerStartL2, sub(tangentPointInner, outerStartL2));

// check for "overbend" to the left
if (crossingIntersectionDistanceL1 < lengths[0]-margin2 && (!leftBend || innerIntersectionDistOnL2 > 0)) {
poly[1][1] = crossingIntersectionL1;
}
// check if intersection is after l1 and before l2 -> add intersection point
//let innerStartL2 = add(points[1], mult(normals[1],offsets[1]));
if (outerIntersectionDistOnL1 > lengths[0]-margin2 && outerIntersectionDistOnL2 < margin2) {
// add corner vertex to polygon
poly[0].splice(1, 0, add(outerStartL1, mult(units[0], outerIntersectionDistOnL1)));
}
// again for other side of lines
let s1 = add(points[0], mult(normals[0], offsets[0]+widths[0]));
let s2 = add(points[1], mult(normals[1], offsets[1]+widths[1]));
let d1 = intersect_dist(s1, units[0], s2, units[1]);
let d2 = intersect_dist(s2, units[1], s1, units[0]);
if (d1 > lengths[0]-margin2 && d2 < margin2) {
// add corner vertex to polygon
poly[1].splice(1, 0, add(s1, mult(units[0], d1)));
}

return poly;

}
Insert cell
Insert cell
Insert cell
margin2 = Math.max(
Math.min(offsets[0], offsets[0]+width1, offsets[1], offsets[1]+width2) * Math.tan((angles[1]-angles[0])/2),
Math.max(offsets[0], offsets[0]+width1, offsets[1], offsets[1]+width2) * Math.tan((angles[1]-angles[0])/2)
)
Insert cell
corner_polygon2 = {
// store 2 boundary lines separately
let poly = [
[
add(points[1], mult(normals[0], offsets[0]), mult(units[0], -margin2)), // left end l1
add(points[1], mult(normals[1], offsets[1]), mult(units[1], margin2)), // left start l2
],
[
add(points[1], mult(normals[1], offsets[1]+widths[1]), mult(units[1], margin2)),// right end l1
add(points[1], mult(normals[0], offsets[0]+widths[0]), mult(units[0], -margin2)), /// right start l2
]
];

// check if intersection is after l1 and before l2 -> add intersection point
let s1 = add(points[0], mult(normals[0],offsets[0]));
let s2 = add(points[1], mult(normals[1],offsets[1]));
let d1 = intersect_dist(s1, units[0], s2, units[1]);
let d2 = intersect_dist(s2, units[1], s1, units[0]);
debugger;
if (d1 > lengths[0]-margin2 && d2 < margin2) {
// add corner vertex to polygon
poly[0].splice(1, 0, add(s1, mult(units[0], d1)));
}
// again for other side of lines
s1 = add(points[0], mult(normals[0], offsets[0]+widths[0]));
s2 = add(points[1], mult(normals[1], offsets[1]+widths[1]));
d1 = intersect_dist(s1, units[0], s2, units[1]);
d2 = intersect_dist(s2, units[1], s1, units[0]);
if (d1 > lengths[0]-margin2 && d2 < margin2) {
// add corner vertex to polygon
poly[1].splice(1, 0, add(s1, mult(units[0], d1)));
}

return poly;

}
Insert cell
Insert cell
Insert cell
intersections = [
intersect(add(points[0], mult(normals[0],offsets[0])), units[0], add(points[1], mult(normals[1],offsets[1])), units[1]),
intersect(add(points[0], mult(normals[0],offsets[0]+widths[0])), units[0], add(points[1], mult(normals[1],offsets[1])), units[1]),
intersect(add(points[0], mult(normals[0],offsets[0])), units[0], add(points[1], mult(normals[1],offsets[1]+widths[1])), units[1]),
intersect(add(points[0], mult(normals[0],offsets[0]+widths[0])), units[0], add(points[1], mult(normals[1],offsets[1]+widths[1])), units[1]),
]
Insert cell
margin = {
let f = Math.tan((angles[1]-angles[0])/2);
return [
Math.max(f * (offsets[0] + widths[0]), f * offsets[0]),
Math.max(f * (offsets[1] + widths[1]), f * offsets[1])
]
}
Insert cell
corner_polygon = [
add(points[1], mult(normals[0], offsets[0]+widths[0]), mult(units[0], -margin[0])), // right end l1
add(points[1], mult(normals[1], offsets[1]+widths[1]), mult(units[1], margin[1])), // right start l2
add(points[1], mult(normals[1], offsets[1]), mult(units[1], margin[1])), /// left start l2
add(points[1], mult(normals[0], offsets[0]), mult(units[0], -margin[0])),// left end l1
]
Insert cell
Insert cell
// Add vectors
function add(...args) {
return args.reduce((val, cur) => val.map((v,i) => v+cur[i]), [0,0])
}
Insert cell
function sub(a, b) {
return a.map((x, i) => x-b[i]);
}
Insert cell
// Multiply vector with scalar
function mult(vec, scalar) {
return vec.map(x => x*scalar);
}
Insert cell
function len(vec) {
return Math.sqrt(vec.reduce((cur, val) => cur + val*val, 0));
}
Insert cell
function unit(vec) {
return mult(vec, 1/len(vec));
}
Insert cell
function normal(vec) {
return [vec[1], -vec[0]];
}
Insert cell
function dist(p1, p2) {
return len(sub(p2,p1));
}
Insert cell
// Intersect two lines, given by starting point and direction vector
function intersect(a, da, b, db) {
let m = intersect_dist(a, da, b, db);
return [a[0]+m*da[0], a[1]+m*da[1]];
}
Insert cell
// intersect two lines, given by two points on each line
function intersectLinesByPoints(l1p1, l1p2, l2p1, l2p2) {
return intersect(l1p1, sub(l1p2, l1p1), l2p1, sub(l2p2, l2p1));
}
Insert cell
// returns the distance of intersection of two lines, measured from the first point
function intersect_dist(a, va, b, vb) {
// check for parallel lines
if (va[0]*vb[1] == va[1]*vb[0]) return Infinity;
// check for div 0 (vb[0]==0) -> swap x,y (gives same result)
if (vb[0] == 0) {
a=[a[1],a[0]];
b=[b[1],b[0]];
va=[va[1],va[0]];
vb=[vb[1],vb[0]];
}
let m = (b[1] + (a[0]-b[0])*vb[1]/vb[0]-a[1]) / (va[1] - va[0]*vb[1]/vb[0]);
return m;
}
Insert cell
function tangentPoints(p1, r1, p2) {
// tangent points on circle defined by p1, r1 from p2
let len = Math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2);
let th = Math.acos(r1 / len); // theta
let d = Math.atan2(p2[1] - p1[1], p2[0] - p1[0]); // angle of p2 from p1
let d1 = d + th; // direction angle of tangent points from p1
let d2 = d - th;

return [
[p1[0] + r1 * Math.cos(d1), p1[1] + r1 * Math.sin(d1)],
[p1[0] + r1 * Math.cos(d2), p1[1] + r1 * Math.sin(d2)]
];
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more