1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-15 05:04:25 +02:00

section 22

This commit is contained in:
Pomax
2016-01-05 18:33:00 -08:00
parent 1e14b594b4
commit b708aa06a0
5 changed files with 771 additions and 89 deletions

View File

@@ -0,0 +1,271 @@
var React = require("react");
var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var min = Math.min, max = Math.max;
var Intersections = React.createClass({
getDefaultProps: function() {
return {
title: "Intersections"
};
},
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 = curves[0].getUtils().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 = curves[0].getUtils();
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});
});
},
render: function() {
return (
<section>
<SectionHeader {...this.props} />
<p>Let's look at some more things we will want to do with Bézier curves. Almost immediately after figuring out how to
get bounding boxes to work, people tend to run into the problem that even though the minimal bounding box (based on
rotation) is tight, it's not sufficient to perform true collision detection. It's a good first step to make sure
there <em>might</em> be a collision (if there is no bounding box overlap, there can't be one), but in order to do
real collision detection we need to know whether or not there's an intersection on the actual curve.</p>
<p>We'll do this in steps, because it's a bit of a journey to get to curve/curve intersection checking. First, let's
start simple, by implementing a line-line intersection checker. While we can solve this the traditional calculus way
(determine the functions for both lines, then compute the intersection by equating them and solving for two unknowns),
linear algebra actually offers a nicer solution.</p>
<h3>Line-line intersections</h3>
<p id="intersection_ll">if we have two line segments with two coordinates each, segments A-B and C-D, we can find the
intersection of the lines these segments are an intervals on by linear algebra, using the procedure outlined in
this <a href="http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2#line_line_intersection">top coder</a> article.
Of course, we need to make sure that the intersection isn't just on the lines our line segments lie on, but also on our
line segments themselves, so after we find the intersection we need to verify it lies without the bounds of our original
line segments.</p>
<p>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).</p>
<Graphic preset="simple" title="Line/line intersections" setup={this.setupLines} draw={this.drawLineIntersection} />
<div className="howtocode">
<h3>Implementing line-line intersections</h3>
<p>Let's have a look at how to implement a line-line intersection checking function. The basics are covered in the
article mentioned above, but sometimes you need more function signatures, because you might not want to call your
function with eight distinct parameters. Maybe you're using point structs or the line. Let's get coding:</p>
<pre>lli8 = function(x1,y1,x2,y2,x3,y3,x4,y4):
var 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 false
return point(nx/d, ny/d)
lli4 = function(p1,p2,p3,p4):
var x1 = p1.x, y1 = p1.y,
x2 = p2.x, y2 = p2.y,
x3 = p3.x, y3 = p3.y,
x4 = p4.x, y4 = p4.y;
return lli8(x1,y1,x2,y2,x3,y3,x4,y4)
lli = function(line1, line2):
return lli4(line1.p1, line1.p2, line2.p1, line2.p2)</pre>
</div>
<h3>What about curve-line intersections?</h3>
<p>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.
</p>
<Graphic preset="simple" title="Quadratic curve/line intersections" setup={this.setupQuadratic} draw={this.draw}/>
<Graphic preset="simple" title="Cubic curve/line intersections" setup={this.setupCubic} draw={this.draw}/>
<p>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 not covered yet: de Casteljau's algorithm, and curve splitting.</p>
</section>
);
}
});
module.exports = Intersections;
/*
Point p1, p2, p3, p4;
void setupCurve() {
p1 = new Point(50,50);
p2 = new Point(150,110);
curves.add(new BezierCurve(new Point[]{p1,p2}, false));
p3 = new Point(50,250);
p4 = new Point(170,170);
curves.add(new BezierCurve(new Point[]{p3,p4}, false));
}
void drawCurve(BezierCurve curve) {
// draw the lines through p1/p2 and p3/p4
stroke(0,50);
float dx = 10*(p2.x-p1.x), dy = 10*(p2.y-p1.y);
line(p1.x-dx,p1.y-dy,p2.x+dx,p2.y+dy);
dx = 10*(p4.x-p3.x); dy = 10*(p4.y-p3.y);
line(p3.x-dx,p3.y-dy,p4.x+dx,p4.y+dy);
// show the line segments
curves.get(0).draw();
curves.get(1).draw();
// show the intersection point
Point ntr = comp.getProjection(p1,p2,p3,p4);
// red if virtual intersection, green if real
boolean oncurves = true;
if(min(p1.x,p2.x) > ntr.x || ntr.x > max(p1.x,p2.x) ||
min(p1.y,p2.y) > ntr.y || ntr.y > max(p1.y,p2.y)) oncurves = false;
if(oncurves) {
if(min(p3.x,p4.x) > ntr.x || ntr.x > max(p3.x,p4.x) ||
min(p3.y,p4.y) > ntr.y || ntr.y > max(p3.y,p4.y)) oncurves = false; }
stroke(oncurves?0:255, oncurves?255:0, 0);
ellipse(ntr.x,ntr.y,5,5);
}</textarea>
Point p1, p2;
void setupCurve() {
p1 = new Point(40,60);
p2 = new Point(260,200);
curves.add(new BezierCurve(new Point[]{
p1, p2
}, false));
curves.add(new BezierCurve(new Point[]{
new Point(25,150),
new Point(180,30),
new Point(230,250)
}));
}
void drawCurve(BezierCurve curve) {
curves.get(0).draw();
curves.get(1).draw();
BezierCurve aligned = curves.get(1).align(p1,p2);
float[] roots = comp.findAllRoots(0, aligned.y_values);
fill(150,0,150);
float x, y;
for(float t: roots) {
if(t<0 || t>1) continue;
x = curves.get(1).getXValue(t);
y = curves.get(1).getYValue(t);
ellipse(x,y,5,5);
text(""+round(1000*t)/1000,x+10,y);
}
}</textarea>
<textarea class="sketch-code" data-sketch-preset="simple" data-sketch-title="Cubic curve/line intersections">
Point p1, p2;
void setupCurve() {
p1 = new Point(100,20);
p2 = new Point(195,255);
curves.add(new BezierCurve(new Point[]{
p1, p2
}, false));
curves.add(new BezierCurve(new Point[]{
new Point(150,125),
new Point(40,30),
new Point(270,115),
new Point(145,200)
}));
}
void drawCurve(BezierCurve curve) {
curves.get(0).draw();
curves.get(1).draw();
BezierCurve aligned = curves.get(1).align(p1,p2);
float[] roots = comp.findAllRoots(0, aligned.y_values);
fill(150,0,150);
float x, y;
for(float t: roots) {
if(t<0 || t>1) continue;
x = curves.get(1).getXValue(t);
y = curves.get(1).getYValue(t);
ellipse(x,y,5,5);
text(""+round(1000*t)/1000,x+10,y);
}
}</textarea>
*/