Parse
File Parse tangents.js
This tree is parsed live from the source file.
Classes
-
{{ item.name }}
- {{ key }}
Not Classes
{{ getTree() }}
Comments
{{ getTreeComments() }}
Source
/*
https://www.youtube.com/watch?v=m1zmWiboxzU
*/
function calculateEdgeToEdgeLine(pointA, pointB) {
// Destructure points
const { x: x1, y: y1, radius: r1 } = pointA;
const { x: x2, y: y2, radius: r2 } = pointB;
// Calculate the distance between the centers
const dx = x2 - x1;
const dy = y2 - y1;
const dist = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction
const nx = dx / dist;
const ny = dy / dist;
// Calculate the edge points by moving along the direction vector scaled by the radius
const pointAEdge = {
x: x1 + nx * r1,
y: y1 + ny * r1
};
const pointBEdge = {
x: x2 - nx * r2,
y: y2 - ny * r2
};
return { pointAEdge, pointBEdge };
}
function rawCalculateAdjustedRotatedTangentLines(pointA, pointB) {
const { x: x1, y: y1, radius: r1 } = pointA;
const { x: x2, y: y2, radius: r2 } = pointB;
// Calculate distance between centers
const dx = x2 - x1;
const dy = y2 - y1;
const dist = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
const nx = dx / dist;
const ny = dy / dist;
// Calculate the angle between the points
const angle = Math.atan2(dy, dx);
const extra = 0// -Math.PI
// Calculate the angle offset to adjust for size differences
const angleOffset = (-Math.asin((r1 - r2) / dist) ) + extra || 0;
// Rotate the direction vectors by +90 and -90 degrees (perpendicular) based on the relative angle
const perpNx1 = Math.cos(angle + Math.PI / 2 + angleOffset);
const perpNy1 = Math.sin(angle + Math.PI / 2 + angleOffset);
const perpNx2 = Math.cos(angle - Math.PI / 2 - angleOffset);
const perpNy2 = Math.sin(angle - Math.PI / 2 - angleOffset);
// Calculate the tangent points for both sides of pointA (adjusted with angle)
const lineA1 = {
x: x1 + perpNx1 * r1,
y: y1 + perpNy1 * r1
};
const lineA2 = {
x: x1 + perpNx2 * r1,
y: y1 + perpNy2 * r1
};
// Calculate the tangent points for both sides of pointB (adjusted with angle)
const lineB1 = {
x: x2 + perpNx1 * r2,
y: y2 + perpNy1 * r2
};
const lineB2 = {
x: x2 + perpNx2 * r2,
y: y2 + perpNy2 * r2
};
// Return the two lines (top and bottom)
return {
a: [{ x: lineA1.x, y: lineA1.y }, { x: lineB1.x, y: lineB1.y }],
b: [{ x: lineA2.x, y: lineA2.y }, { x: lineB2.x, y: lineB2.y }]
};
}
const halfPi = Math.PI * .5;
function calculateAdjustedRotatedTangentLines(pointA, pointB) {
const { x: x1, y: y1, radius: r1 } = pointA;
const { x: x2, y: y2, radius: r2 } = pointB;
// Calculate distance between centers
const dx = x2 - x1;
const dy = y2 - y1;
const dist = Math.sqrt(dx * dx + dy * dy);
// let d2 =pointA.distance(pointB)
// Normalize the direction vector
// //
// const nx = dx / dist;
// const ny = dy / dist;
// Calculate the angle between the points
const angle = Math.atan2(dy, dx);
const extra = 0
// Calculate the angle offset to adjust for size differences
const angleOffset = -Math.asin((r1 - r2) / dist) || 0;
let a = angle + halfPi + angleOffset + extra
// Rotate the direction vectors by +90 and -90 degrees (perpendicular) based on the relative angle
const perpNx1 = Math.cos(a);
const perpNy1 = Math.sin(a);
let am = angle - halfPi - angleOffset - extra
const perpNx2 = Math.cos(am);
const perpNy2 = Math.sin(am);
// Calculate the tangent points for both sides of pointA (adjusted with angle)
const lineA1 = {
x: x1 + perpNx1 * r1,
y: y1 + perpNy1 * r1
};
const lineA2 = {
x: x1 + perpNx2 * r1,
y: y1 + perpNy2 * r1
};
// Calculate the tangent points for both sides of pointB (adjusted with angle)
const lineB1 = {
x: x2 + perpNx1 * r2,
y: y2 + perpNy1 * r2
};
const lineB2 = {
x: x2 + perpNx2 * r2,
y: y2 + perpNy2 * r2
};
// Return the two lines (top and bottom)
return {
a: [{ x: lineA1.x, y: lineA1.y }, { x: lineB1.x, y: lineB1.y }],
b: [{ x: lineA2.x, y: lineA2.y }, { x: lineB2.x, y: lineB2.y }]
};
}
let arcTangents = function(a, b, rLength, add=true){
// https://www.youtube.com/watch?v=fVbpIwh_S6o
let r = rLength
let r1 = a.radius
let r2 = b.radius
// Then build the new protractor points.
let c = a.copy().update({
radius: add? r + r1: r - r1
})
let d = b.copy().update({
radius: add? r + r2: r - r2
})
/* The circle interections of the _outer_ protractor lines.
These intersections mark the position to score the arc.*/
let cdIntersect = getCircleCircleIntersections(c, d)
if(cdIntersect === false || cdIntersect.length == 0) {
return
}
// The two origin points.
let intersect0 = new Point(cdIntersect[0])
let intersect1 = new Point(cdIntersect[1])
let comp = function(protractorPoint, targetPoint) {
const angle = twoPointsAngle(protractorPoint, targetPoint)
let xStart = protractorPoint.x + r * Math.cos(angle)
let yStart = protractorPoint.y + r * Math.sin(angle)
let tangentPoint = new Point(xStart, yStart, 5, radiansToDegrees(angle))
return tangentPoint
}
let node00 = comp(intersect0, a)
let node01 = comp(intersect0, b)
let node10 = comp(intersect1, a)
let node11 = comp(intersect1, b)
return {
n: [
node00
, node01
, node10
, node11
]
, a, b, c , d
, origin0: intersect0
, origin1: intersect1
, sharedRadius: r
}
}
const twoPointsAngleRaw = function(origin, target) {
/*given two points, return the relative angle (in radians), of target from the origin.
For example if the target was left of the origin, this would be a -Math.PI */
const { x: x1, y: y1, radius: r1 } = origin;
const { x: x2, y: y2, radius: r2 } = target;
// Calculate distance between centers
const dx = x2 - x1;
const dy = y2 - y1;
const dist = Math.sqrt(dx * dx + dy * dy);
// Calculate the angle between the points
const angle = Math.atan2(dy, dx);
return angle
}
const twoPointsAngle = function(origin, target) {
let dist = new Point(target.distance2D(origin))
return dist.atan2()
}
class PointTangents {
constructor(point) {
this.parent = point
}
calculateTangentLines(pointA, pointB) {
const { x: x1, y: y1, radius: r1 } = pointA;
const { x: x2, y: y2, radius: r2 } = pointB;
// Calculate distance between centers
const dx = x2 - x1;
const dy = y2 - y1;
const dist = Math.sqrt(dx * dx + dy * dy);
// Calculate the angle between the points
const angle = Math.atan2(dy, dx);
const extra = 0
// Calculate the angle offset to adjust for size differences
const angleOffset = -Math.asin((r1 - r2) / dist) || 0;
let a = angle + halfPi + angleOffset + extra
// Rotate the direction vectors by +90 and -90 degrees (perpendicular) based on the relative angle
const perpNx1 = Math.cos(a);
const perpNy1 = Math.sin(a);
let am = angle - halfPi - angleOffset - extra
const perpNx2 = Math.cos(am);
const perpNy2 = Math.sin(am);
// Calculate the tangent points for both sides of pointA (adjusted with angle)
const lineA1 = {
x: x1 + perpNx1 * r1,
y: y1 + perpNy1 * r1
};
const lineA2 = {
x: x1 + perpNx2 * r1,
y: y1 + perpNy2 * r1
};
// Calculate the tangent points for both sides of pointB (adjusted with angle)
const lineB1 = {
x: x2 + perpNx1 * r2,
y: y2 + perpNy1 * r2
};
const lineB2 = {
x: x2 + perpNx2 * r2,
y: y2 + perpNy2 * r2
};
// Return the two lines (top and bottom)
return {
a: [{ x: lineA2.x, y: lineA2.y }, { x: lineB2.x, y: lineB2.y }],
b: [{ x: lineA1.x, y: lineA1.y }, { x: lineB1.x, y: lineB1.y }]
};
}
calculateCrossTangentLines(pointA, pointB) {
const { x: x1, y: y1, radius: r1 } = pointA;
const { x: x2, y: y2, radius: r2 } = pointB;
// Distance between centers
const d = Math.hypot(x2 - x1, y2 - y1);
// Internal tangents exist only if circles are separate enough:
if (d < r1 + r2) {
// Could return an empty result, throw, or handle as desired
return null;
}
// Base angle from A to B
const baseAngle = Math.atan2(y2 - y1, x2 - x1);
// For the internal tangents, we have sin(delta) = (r1 + r2)/d.
// (delta exists only when d >= r1+r2)
const delta = Math.asin((r1 + r2) / d);
// The radius (tangent) direction makes an angle offset from perpendicular:
const offset = Math.PI / 2 - delta;
let cpx = Math.cos(baseAngle + offset)
let cpy = Math.sin(baseAngle + offset)
let cmx = Math.cos(baseAngle - offset)
let cmy = Math.sin(baseAngle - offset)
// First internal tangent ('ab'):
const tangentA1 = {
x: x1 + cpx * r1,
y: y1 + cpy * r1,
};
const tangentB1 = {
x: x2 - Math.cos(baseAngle + offset) * r2,
y: y2 - cpy * r2,
};
// Second internal tangent ('ba'):
const tangentA2 = {
x: x1 + cmx * r1,
y: y1 + cmy * r1,
};
const tangentB2 = {
x: x2 - cmx * r2,
y: y2 - cmy * r2,
};
return {
// 'ab' goes from A’s tangent point (using +offset) to B’s tangent point (using +offset)
// and 'ba' is the other internal tangent.
ab: [tangentA1, tangentB1],
ba: [tangentA2, tangentB2],
};
}
calculateConcaveTangentArcs(pointA, pointB, length) {
return arcTangents(a, b, length, 0)
}
calculateConvexTangentArcs(pointA, pointB, length) {
return arcTangents(a, b, length, 1)
}
points(other) {
/* return two points on _this_ point for the _start_ of two tangent lines
[a(top), b(top), a(bottom), b(bottom)]
*/
let v = this.calculateTangentLines(this.parent, other)
return v.a.concat(v.b)
}
outerLines(other){
/* return lines _a_ and _b_, each being a direct tagent */
let v = this.calculateTangentLines(this.parent, other)
return v
}
crossLines(){
/* return the inner tagents, point A (top), to point B (bottom), and
the antethisis. similar to [ab(), ba()]*/
}
lineA(other) {
/* return a line (two points) for the _top_ direct tagent. */
let v = this.calculateTangentLines(this.parent, other)
return v.a
}
lineB(other) {
/* return a line (two points) for the _bottom_ direct tagent. */
let v = this.calculateTangentLines(this.parent, other)
return v.b
}
a(other) {
/* return the top line tangent _start_ point */
let v = this.calculateTangentLines(this.parent, other)
return v && v.a[0]
}
b(other){
/* return the bottom line _start_ point */
let v = this.calculateTangentLines(this.parent, other)
return v && v.b[0]
}
aa(other){
/* return the _line_ of pointA (top), to pointB (top)
Similar to `this.points(other)[0,2]` */
let v = this.calculateTangentLines(this.parent, other)
return v && v.a
}
bb(other){
/* return the antipose parallel tangent to aa(), similar to
`this.points(other)[1,4]`
*/
let v = this.calculateTangentLines(this.parent, other)
return v && v.b
}
ab(other) {
/* return the tangent line _through_ the center, similar to points [0,3]
However the tangent points are adjusted - rotated around the radius. */
let v = this.calculateCrossTangentLines(this.parent, other)
return v && v.ab
}
ba(other) {
/* return the tangent line point A (bottom), point B (top) */
let v = this.calculateCrossTangentLines(this.parent, other)
return v && v.ba
}
}
Polypoint.head.lazyProp('Point', {
tangent() {
let r = this._tangents
if(r == undefined) {
r = new PointTangents(this)
this._tangents = r
}
return r
}
})
copy