mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-30 03:30:34 +02:00
intersections
This commit is contained in:
@@ -10,7 +10,7 @@ If we have two line segments with two coordinates each, segments A-B and C-D, we
|
||||
|
||||
The following graphic implements this intersection detection, showing a red point for an intersection on the lines our segments lie on (thus being a virtual intersection point), and a green point for an intersection that lies on both segments (being a real intersection point).
|
||||
|
||||
<Graphic title="Line/line intersections" setup={this.setupLines} draw={this.drawLineIntersection} />
|
||||
<graphics-element title="Line/line intersections" src="./line-line.js"></graphics-element>
|
||||
|
||||
<div class="howtocode">
|
||||
|
||||
@@ -44,7 +44,9 @@ lli = function(line1, line2):
|
||||
|
||||
Curve/line intersection is more work, but we've already seen the techniques we need to use in order to perform it: first we translate/rotate both the line and curve together, in such a way that the line coincides with the x-axis. This will position the curve in a way that makes it cross the line at points where its y-function is zero. By doing this, the problem of finding intersections between a curve and a line has now become the problem of performing root finding on our translated/rotated curve, as we already covered in the section on finding extremities.
|
||||
|
||||
<Graphic title="Quadratic curve/line intersections" setup={this.setupQuadratic} draw={this.draw}/>
|
||||
<Graphic title="Cubic curve/line intersections" setup={this.setupCubic} draw={this.draw}/>
|
||||
<div class="figure">
|
||||
<graphics-element title="Quadratic curve/line intersections" src="./curve-line.js" data-type="quadratic"></graphics-element>
|
||||
<graphics-element title="Cubic curve/line intersections" src="./curve-line.js" data-type="cubic"></graphics-element>
|
||||
</div>
|
||||
|
||||
Curve/curve intersection, however, is more complicated. Since we have no straight line to align to, we can't simply align one of the curves and be left with a simple procedure. Instead, we'll need to apply two techniques we've met before: de Casteljau's algorithm, and curve splitting.
|
||||
|
53
docs/chapters/intersections/curve-line.js
Normal file
53
docs/chapters/intersections/curve-line.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const utils = Bezier.getUtils();
|
||||
|
||||
let Point = Vector, curve, line;
|
||||
|
||||
setup() {
|
||||
const type = this.parameters.type ?? `quadratic`;
|
||||
if (type === `quadratic`) {
|
||||
curve = Bezier.defaultQuadratic(this);
|
||||
line = [new Point(15,250), new Point(220,20)];
|
||||
} else {
|
||||
curve = Bezier.defaultCubic(this);
|
||||
line = [new Point(25,260), new Point(240,55)];
|
||||
}
|
||||
setMovable(curve.points, line);
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
|
||||
curve.drawSkeleton();
|
||||
curve.drawCurve();
|
||||
curve.drawPoints();
|
||||
|
||||
this.drawLine(...line);
|
||||
|
||||
const [p1, p2] = line;
|
||||
const aligned = utils.align(curve.points, {p1,p2});
|
||||
const nB = new Bezier(this, aligned);
|
||||
const roots = utils.roots(nB.points);
|
||||
const coords = roots.map(t => curve.get(t));
|
||||
|
||||
if (roots.length) {
|
||||
roots.forEach((t,i) => {
|
||||
var p = coords[i];
|
||||
setColor(`magenta`);
|
||||
circle(p.x, p.y, 3);
|
||||
setColor(`black`);
|
||||
text(`t = ${t.toFixed(2)}`, p.x + 5, p.y + 10);
|
||||
});
|
||||
|
||||
setFill(`black`);
|
||||
const cval = coords.map(p => `(${p.x|0},${p.y|0})`).join(`, `);
|
||||
text(`Intersection${roots.length>=1?`s`:``} at ${cval}`, this.width/2, 15, CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
drawLine(p1, p2) {
|
||||
setStroke(`black`);
|
||||
line(p1.x, p1.y, p2.x, p2.y);
|
||||
setStroke( randomColor() );
|
||||
circle(p1.x, p1.y, 3);
|
||||
circle(p2.x, p2.y, 3);
|
||||
}
|
@@ -1,75 +0,0 @@
|
||||
var min = Math.min, max = Math.max;
|
||||
|
||||
module.exports = {
|
||||
setupLines: function(api) {
|
||||
var curve1 = new api.Bezier([50,50,150,110]);
|
||||
var curve2 = new api.Bezier([50,250,170,170]);
|
||||
api.setCurve(curve1, curve2);
|
||||
},
|
||||
|
||||
drawLineIntersection: function(api, curves) {
|
||||
api.reset();
|
||||
|
||||
var lli = api.utils.lli4;
|
||||
var p = lli(
|
||||
curves[0].points[0],
|
||||
curves[0].points[1],
|
||||
curves[1].points[0],
|
||||
curves[1].points[1]
|
||||
);
|
||||
|
||||
var mark = 0;
|
||||
curves.forEach(curve => {
|
||||
api.drawSkeleton(curve);
|
||||
api.setColor("black");
|
||||
if (p) {
|
||||
var pts = curve.points,
|
||||
mx = min(pts[0].x, pts[1].x),
|
||||
my = min(pts[0].y, pts[1].y),
|
||||
Mx = max(pts[0].x, pts[1].x),
|
||||
My = max(pts[0].y, pts[1].y);
|
||||
if (mx <= p.x && my <= p.y && Mx >= p.x && My >= p.y) {
|
||||
api.setColor("#00FF00");
|
||||
mark++;
|
||||
}
|
||||
}
|
||||
api.drawCurve(curve);
|
||||
});
|
||||
|
||||
if (p) {
|
||||
api.setColor(mark < 2 ? "red" : "#00FF00");
|
||||
api.drawCircle(p, 3);
|
||||
}
|
||||
},
|
||||
|
||||
setupQuadratic: function(api) {
|
||||
var curve1 = api.getDefaultQuadratic();
|
||||
var curve2 = new api.Bezier([15,250,220,20]);
|
||||
api.setCurve(curve1, curve2);
|
||||
},
|
||||
|
||||
setupCubic: function(api) {
|
||||
var curve1 = new api.Bezier([100,240, 30,60, 210,230, 160,30]);
|
||||
var curve2 = new api.Bezier([25,260, 230,20]);
|
||||
api.setCurve(curve1, curve2);
|
||||
},
|
||||
|
||||
draw: function(api, curves) {
|
||||
api.reset();
|
||||
curves.forEach(curve => {
|
||||
api.drawSkeleton(curve);
|
||||
api.drawCurve(curve);
|
||||
});
|
||||
|
||||
var utils = api.utils;
|
||||
var line = { p1: curves[1].points[0], p2: curves[1].points[1] };
|
||||
var acpts = utils.align(curves[0].points, line);
|
||||
var nB = new api.Bezier(acpts);
|
||||
var roots = utils.roots(nB.points);
|
||||
roots.forEach(t => {
|
||||
var p = curves[0].get(t);
|
||||
api.drawCircle(p, 3);
|
||||
api.text("t = " + t, {x: p.x + 5, y: p.y + 10});
|
||||
});
|
||||
}
|
||||
};
|
61
docs/chapters/intersections/line-line.js
Normal file
61
docs/chapters/intersections/line-line.js
Normal file
@@ -0,0 +1,61 @@
|
||||
let Point = Vector, points = [];
|
||||
|
||||
setup() {
|
||||
points.push(new Point(50,50));
|
||||
points.push(new Point(150,110));
|
||||
points.push(new Point(50,250));
|
||||
points.push(new Point(170,170));
|
||||
setMovable(points);
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
const [p1, p2, p3, p4] = points;
|
||||
|
||||
setStroke(`black`);
|
||||
this.drawLine(p1,p2);
|
||||
this.drawLine(p3,p4);
|
||||
|
||||
const p = this.lli(
|
||||
p1.x, p1.y,
|
||||
p2.x, p2.y,
|
||||
p3.x, p3.y,
|
||||
p4.x, p4.y
|
||||
);
|
||||
|
||||
if (p) {
|
||||
if (this.onLine(p, p1, p2) && this.onLine(p, p3, p4)) {
|
||||
setColor(`lime`);
|
||||
circle(p.x, p.y, 3);
|
||||
setFill(`black`);
|
||||
text(`Intersection at (${p.x|0}, ${p.y|0})`, this.width/2, 15, CENTER);
|
||||
} else {
|
||||
setColor(`red`);
|
||||
circle(p.x, p.y, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawLine(p1, p2) {
|
||||
setColor(`black`);
|
||||
line(p1.x, p1.y, p2.x, p2.y);
|
||||
setStroke( randomColor() );
|
||||
circle(p1.x, p1.y, 2);
|
||||
circle(p2.x, p2.y, 2);
|
||||
}
|
||||
|
||||
lli(x1, y1, x2, y2, x3, y3, x4, y4) {
|
||||
const nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
|
||||
ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
|
||||
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
if (d == 0) return;
|
||||
return { x: nx / d, y: ny / d };
|
||||
}
|
||||
|
||||
onLine(p, p1, p2) {
|
||||
const mx = min(p1.x, p2.x),
|
||||
my = min(p1.y, p2.y),
|
||||
Mx = max(p1.x, p2.x),
|
||||
My = max(p1.y, p2.y);
|
||||
return (mx <= p.x && my <= p.y && Mx >= p.x && My >= p.y);
|
||||
}
|
Reference in New Issue
Block a user