mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-29 11:10:38 +02:00
sections 5 and 6
This commit is contained in:
@@ -33,7 +33,6 @@ var Graphic = React.createClass({
|
||||
onMouseDown={this.mouseDown}
|
||||
onMouseMove={this.mouseMove}
|
||||
onMouseUp={this.mouseUp}
|
||||
onMouseOver={this.mouseOver}
|
||||
onClick={this.mouseClick}
|
||||
/>
|
||||
<figcaption>{this.props.title}</figcaption>
|
||||
@@ -217,6 +216,23 @@ var Graphic = React.createClass({
|
||||
this.drawCoordinates(curve, offset);
|
||||
},
|
||||
|
||||
drawHull: function(curve, t, offset) {
|
||||
var hull = curve.hull(t);
|
||||
if(hull.length === 6) {
|
||||
this.drawLine(hull[0], hull[1], offset);
|
||||
this.drawLine(hull[1], hull[2], offset);
|
||||
this.drawLine(hull[3], hull[4], offset);
|
||||
} else {
|
||||
this.drawLine(hull[0], hull[1], offset);
|
||||
this.drawLine(hull[1], hull[2], offset);
|
||||
this.drawLine(hull[2], hull[3], offset);
|
||||
this.drawLine(hull[4], hull[5], offset);
|
||||
this.drawLine(hull[5], hull[6], offset);
|
||||
this.drawLine(hull[7], hull[8], offset);
|
||||
}
|
||||
return hull;
|
||||
},
|
||||
|
||||
drawCoordinates: function(curve, offset) {
|
||||
offset = offset || { x:0, y:0 };
|
||||
var pts = curve.points;
|
||||
|
118
components/sections/decasteljau/index.js
Normal file
118
components/sections/decasteljau/index.js
Normal file
@@ -0,0 +1,118 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var LaTeX = require("../../LaTeX.jsx");
|
||||
|
||||
var deCasteljau = React.createClass({
|
||||
statics: {
|
||||
title: "de Casteljau's algorithm"
|
||||
},
|
||||
|
||||
setup: function(api) {
|
||||
var points = [
|
||||
{x: 90, y:110},
|
||||
{x: 25, y: 40},
|
||||
{x:230, y: 40},
|
||||
{x:150, y:240}
|
||||
];
|
||||
api.setCurve(new api.Bezier(points));
|
||||
},
|
||||
|
||||
draw: function(api, curve) {
|
||||
api.reset();
|
||||
api.drawSkeleton(curve);
|
||||
api.drawCurve(curve);
|
||||
|
||||
if (api.hover) {
|
||||
api.setColor("rgb(200,100,100)");
|
||||
var dim = api.getPanelWidth();
|
||||
var t = api.hover.x / dim;
|
||||
var hull = api.drawHull(curve, t);
|
||||
|
||||
for(var i=4; i<=8; i++) {
|
||||
api.drawCircle(hull[i],3);
|
||||
}
|
||||
|
||||
var p = curve.get(t);
|
||||
api.drawCircle(p, 5);
|
||||
api.setFill("black");
|
||||
api.drawCircle(p, 3);
|
||||
var perc = (t*100)|0;
|
||||
t = perc/100;
|
||||
api.text("Sequential interpolation for "+perc+"% (t="+t+")", {x: 10, y:15});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<section>
|
||||
<SectionHeader {...this.props}>{deCasteljau.title}</SectionHeader>
|
||||
<p>If we want to draw Bézier curves we can run through all values of <i>t</i> from 0 to 1 and then
|
||||
compute the weighted basis function, getting the <i>x</i>/<i>y</i> values we need to plot, but the
|
||||
more complex the curve gets, the more expensive this becomes. Instead, we can use "de Casteljau's
|
||||
algorithm" to draw curves, which is a geometric approach to drawing curves, and really easy to
|
||||
implement. So easy, in fact, you can do it by hand with a pencil and ruler.</p>
|
||||
|
||||
<p>Rather than using our calculus function to find <i>x</i>/<i>y</i> values for <i>t</i>, let's
|
||||
do this instead:</p>
|
||||
|
||||
<ul>
|
||||
<li>treat <i>t</i> as a ratio (which it is). t=0 is 0% along a line, t=1 is 100% along a line.</li>
|
||||
<li>Take all lines between the curve's defining points. For an order <i>n</i> curve, that's <i>n</i> lines.</li>
|
||||
<li>Place markers along each of these line, at distance <i>t</i>. So if <i>t</i> is 0.2, place the mark
|
||||
at 20% from the start, 80% from the end.</li>
|
||||
<li>Now form lines between <i>those</i> points. This gives <i>n-1</i> lines.</li>
|
||||
<li>Place markers along each of these line at distance <i>t</i>.</li>
|
||||
<li>Form lines between <i>those</i> points. This'll be <i>n-2</i> lines.</li>
|
||||
<li>place markers, form lines, place markers, etc.</li>
|
||||
<li>repeat this until you have only one line left. The point <i>t</i> on that line coincides with the
|
||||
original curve point at <i>t</i>.</li>
|
||||
</ul>
|
||||
|
||||
<div className="howtocode">
|
||||
<h3>How to implement de Casteljau's algorithm</h3>
|
||||
|
||||
<p>Let's just use the algorithm we just specified, and implement that:</p>
|
||||
|
||||
<pre>function drawCurve(points[], t):
|
||||
if(points.length==1):
|
||||
draw(points[0])
|
||||
else:
|
||||
newpoints=array(points.size-1)
|
||||
for(i=0; i<newpoints.length; i++):
|
||||
newpoints[i] = (1-t) * points[i] + t * points[i+1]
|
||||
drawCurve(newpoints, t)</pre>
|
||||
|
||||
<p>And done, that's the algorithm implemented. Except usually you don't get the luxury of
|
||||
overloading the "+" operator, so let's also give the code for when you need to work with
|
||||
<i>x</i> and <i>y</i> values:</p>
|
||||
|
||||
<pre>function drawCurve(points[], t):
|
||||
if(points.length==1):
|
||||
draw(points[0])
|
||||
else:
|
||||
newpoints=array(points.size-1)
|
||||
for(i=0; i<newpoints.length; i++):
|
||||
x = (1-t) * points[i].x + t * points[i+1].x
|
||||
y = (1-t) * points[i].y + t * points[i+1].y
|
||||
newpoints[i] = new point(x,y)
|
||||
drawCurve(newpoints, t)</pre>
|
||||
|
||||
<p>So what does this do? This draws a point, if the passed list of points is only 1 point
|
||||
long. Otherwise it will create a new list of points that sit at the <i>t</i> ratios (i.e.
|
||||
the "markers" outlined in the above algorithm), and then call the draw function for this
|
||||
new list.</p>
|
||||
</div>
|
||||
|
||||
<p>To see this in action, mouse-over the following sketch. Moving the mouse changes which curve point is
|
||||
explicitly evaluated using de Casteljau's algorithm, moving the cursor left-to-right (or, of course,
|
||||
right-to-left), shows you how a curve is generated using this approach.</p>
|
||||
|
||||
<Graphic preset="simple"title="Traversing a curve using de Casteljau's algorithm" setup={this.setup} draw={this.draw}/>
|
||||
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = deCasteljau;
|
@@ -7,49 +7,49 @@ module.exports = {
|
||||
introduction: require("./introduction"),
|
||||
whatis: require("./whatis"),
|
||||
explanation: require("./explanation"),
|
||||
control: require("./control")
|
||||
control: require("./control"),
|
||||
matrix: require("./matrix"),
|
||||
decasteljau: require("./decasteljau")
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
|
||||
matrix: require("./matrix.jsx"),
|
||||
decasteljau: require("./decasteljau.jsx"),
|
||||
flattening: require("./flattening.jsx"),
|
||||
splitting: require("./splitting.jsx"),
|
||||
matrixsplit: require("./matrixsplit.jsx"),
|
||||
reordering: require("./reordering.jsx"),
|
||||
flattening: require("./flattening"),
|
||||
splitting: require("./splitting"),
|
||||
matrixsplit: require("./matrixsplit"),
|
||||
reordering: require("./reordering"),
|
||||
|
||||
derivatives: require("./derivatives.jsx"),
|
||||
pointvectors: require("./pointvectors.jsx"),
|
||||
components: require("./components.jsx"),
|
||||
extremities: require("./extremities.jsx"),
|
||||
boundingbox: require("./boundingbox.jsx"),
|
||||
aligning: require("./aligning.jsx"),
|
||||
tightbounds: require("./tightbounds.jsx"),
|
||||
canonical: require("./canonical.jsx"),
|
||||
derivatives: require("./derivatives"),
|
||||
pointvectors: require("./pointvectors"),
|
||||
components: require("./components"),
|
||||
extremities: require("./extremities"),
|
||||
boundingbox: require("./boundingbox"),
|
||||
aligning: require("./aligning"),
|
||||
tightbounds: require("./tightbounds"),
|
||||
canonical: require("./canonical"),
|
||||
|
||||
arclength: require("./arclength.jsx"),
|
||||
arclengthapprox: require("./arclengthapprox.jsx"),
|
||||
tracing: require("./tracing.jsx"),
|
||||
arclength: require("./arclength"),
|
||||
arclengthapprox: require("./arclengthapprox"),
|
||||
tracing: require("./tracing"),
|
||||
|
||||
intersections: require("./intersections.jsx"),
|
||||
curveintersection: require("./curveintersection.jsx"),
|
||||
moulding: require("./moulding.jsx"),
|
||||
pointcurves: require("./pointcurves.jsx"),
|
||||
intersections: require("./intersections"),
|
||||
curveintersection: require("./curveintersection"),
|
||||
moulding: require("./moulding"),
|
||||
pointcurves: require("./pointcurves"),
|
||||
|
||||
catmullconv: require("./catmullconv.jsx"),
|
||||
catmullmoulding: require("./catmullmoulding.jsx"),
|
||||
catmullconv: require("./catmullconv"),
|
||||
catmullmoulding: require("./catmullmoulding"),
|
||||
|
||||
polybezier: require("./polybezier.jsx"),
|
||||
shapes: require("./shapes.jsx"),
|
||||
polybezier: require("./polybezier"),
|
||||
shapes: require("./shapes"),
|
||||
|
||||
projections: require("./projections.jsx"),
|
||||
projections: require("./projections"),
|
||||
|
||||
offsetting: require("./offsetting.jsx"),
|
||||
graduatedoffset: require("./graduatedoffset.jsx"),
|
||||
offsetting: require("./offsetting"),
|
||||
graduatedoffset: require("./graduatedoffset"),
|
||||
|
||||
circles: require("./circles.jsx"),
|
||||
circles_cubic: require("./circles_cubic.jsx"),
|
||||
arcapproximation: require("./arcapproximation.jsx")
|
||||
circles: require("./circles"),
|
||||
circles_cubic: require("./circles_cubic"),
|
||||
arcapproximation: require("./arcapproximation")
|
||||
*/
|
||||
|
154
components/sections/matrix/index.js
Normal file
154
components/sections/matrix/index.js
Normal file
@@ -0,0 +1,154 @@
|
||||
var React = require("react");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var LaTeX = require("../../LaTeX.jsx");
|
||||
|
||||
var Matrix = React.createClass({
|
||||
statics: {
|
||||
title: "Bézier curvatures as matrix operations"
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<section>
|
||||
<SectionHeader {...this.props}>{Matrix.title}</SectionHeader>
|
||||
|
||||
<p>We can also represent Bézier as matrix operations, by expressing the Bézier formula
|
||||
as a polynomial basis function, the weight matrix, and the actual coordinates as matrix.
|
||||
Let's look at what this means for the cubic curve :</p>
|
||||
|
||||
<LaTeX>\[
|
||||
B(t) = P_1 \cdot (1-t)^3 + P_2 \cdot 3 \cdot (1-t)^2 \cdot t + P_3 \cdot 3 \cdot (1-t) \cdot t^2 + P_4 \cdot t^3
|
||||
\]</LaTeX>
|
||||
|
||||
<p>Disregarding our actual coordinates for a moment, we have:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
B(t) = (1-t)^3 + 3 \cdot (1-t)^2 \cdot t + 3 \cdot (1-t) \cdot t^2 + t^3
|
||||
\]</LaTeX>
|
||||
|
||||
<p>We can write this as a sum of four expressions:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
\begin{matrix}
|
||||
... & = & (1-t)^3 \\
|
||||
& + & 3 \cdot (1-t)^2 \cdot t \\
|
||||
& + & 3 \cdot (1-t) \cdot t^2 \\
|
||||
& + & t^3 \\
|
||||
\end{matrix}
|
||||
\]</LaTeX>
|
||||
|
||||
<p>And we can expand these expressions:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
\begin{matrix}
|
||||
... & = & (1-t) \cdot (1-t) \cdot (1-t) & = & -t^3 + 3 \cdot t^2 - 3 \cdot t + 1 \\
|
||||
& + & 3 \cdot (1-t) \cdot (1-t) \cdot t & = & 3 \cdot t^3 - 6 \cdot t^2 + 3 \cdot t \\
|
||||
& + & 3 \cdot (1-t) \cdot t \cdot t & = & -3 \cdot t^3 + 3 \cdot t^2 \\
|
||||
& + & t \cdot t \cdot t & = & t^3 \\
|
||||
\end{matrix}
|
||||
\]</LaTeX>
|
||||
|
||||
<p>Furthermore, we can make all the 1 and 0 factors explicit:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
\begin{matrix}
|
||||
... & = & -1 \cdot t^3 + 3 \cdot t^2 - 3 \cdot t + 1 \\
|
||||
& + & +3 \cdot t^3 - 6 \cdot t^2 + 3 \cdot t + 0 \\
|
||||
& + & -3 \cdot t^3 + 3 \cdot t^2 + 0 \cdot t + 0 \\
|
||||
& + & +1 \cdot t^3 + 0 \cdot t^2 + 0 \cdot t + 0 \\
|
||||
\end{matrix}
|
||||
\]</LaTeX>
|
||||
|
||||
<p>And <em>that</em>, we can view as a series of four matrix operations:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
\begin{bmatrix}t^3 & t^2 & t & 1\end{bmatrix} \cdot \begin{bmatrix}-1 \\ 3 \\ -3 \\ 1\end{bmatrix}
|
||||
+ \begin{bmatrix}t^3 & t^2 & t & 1\end{bmatrix} \cdot \begin{bmatrix}3 \\ -6 \\ 3 \\ 0\end{bmatrix}
|
||||
+ \begin{bmatrix}t^3 & t^2 & t & 1\end{bmatrix} \cdot \begin{bmatrix}-3 \\ 3 \\ 0 \\ 0\end{bmatrix}
|
||||
+ \begin{bmatrix}t^3 & t^2 & t & 1\end{bmatrix} \cdot \begin{bmatrix}1 \\ 0 \\ 0 \\ 0\end{bmatrix}
|
||||
\]</LaTeX>
|
||||
|
||||
<p>If we compact this into a single matrix operation, we get:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
\begin{bmatrix}t^3 & t^2 & t & 1\end{bmatrix} \cdot \begin{bmatrix}
|
||||
-1 & 3 & -3 & 1 \\
|
||||
3 & -6 & 3 & 0 \\
|
||||
-3 & 3 & 0 & 0 \\
|
||||
1 & 0 & 0 & 0
|
||||
\end{bmatrix}
|
||||
\]</LaTeX>
|
||||
|
||||
<p>This kind of polynomial basis representation is generally written with the bases in
|
||||
increasing order, which means we need to flip our <em>t</em> matrix horizontally, and our
|
||||
big "mixing" matrix upside down:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
\begin{bmatrix}1 & t & t^2 & t^3\end{bmatrix} \cdot \begin{bmatrix}
|
||||
1 & 0 & 0 & 0 \\
|
||||
-3 & 3 & 0 & 0 \\
|
||||
3 & -6 & 3 & 0 \\
|
||||
-1 & 3 & -3 & 1
|
||||
\end{bmatrix}
|
||||
\]</LaTeX>
|
||||
|
||||
<p>And then finally, we can add in our original coordinates as a single third matrix:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
B(t) = \begin{bmatrix}
|
||||
1 & t & t^2 & t^3
|
||||
\end{bmatrix}
|
||||
\cdot
|
||||
\begin{bmatrix}
|
||||
1 & 0 & 0 & 0 \\
|
||||
-3 & 3 & 0 & 0 \\
|
||||
3 & -6 & 3 & 0 \\
|
||||
-1 & 3 & -3 & 1
|
||||
\end{bmatrix}
|
||||
\cdot
|
||||
\begin{bmatrix}
|
||||
P_1 \\ P_2 \\ P_3 \\ P_4
|
||||
\end{bmatrix}
|
||||
\]</LaTeX>
|
||||
|
||||
<p>We can perform the same trick for the quadratic curve, in which case we end up with:</p>
|
||||
|
||||
<LaTeX>\[
|
||||
B(t) = \begin{bmatrix}
|
||||
1 & t & t^2
|
||||
\end{bmatrix}
|
||||
\cdot
|
||||
\begin{bmatrix}
|
||||
1 & 0 & 0 \\
|
||||
-2 & 2 & 0 \\
|
||||
1 & -2 & 1
|
||||
\end{bmatrix}
|
||||
\cdot
|
||||
\begin{bmatrix}
|
||||
P_1 \\ P_2 \\ P_3
|
||||
\end{bmatrix}
|
||||
\]</LaTeX>
|
||||
|
||||
<p>If we plug in a <em>t</em> value, and then multiply the matrices, we will
|
||||
get exactly the same values as when we evaluate the original polynomial function,
|
||||
or as when we evaluate the curve using progessive linear interpolation.</p>
|
||||
|
||||
<p><strong>So: why would we bother with matrices?</strong> Matrix representations
|
||||
allow us to discover things about functions that would otherwise be hard to tell.
|
||||
It turns out that the curves form <a href="https://en.wikipedia.org/wiki/Triangular_matrix">triangular
|
||||
matrices</a>, and they have a determinant equal to the product of the actual
|
||||
coordinates we use for our curve. It's also invertible, which means there's
|
||||
<a href="https://en.wikipedia.org/wiki/Invertible_matrix#The_invertible_matrix_theorem">a
|
||||
ton of properties</a> that are all satisfied. Of course, the main question is:
|
||||
"Why is this useful to us, now?", and the answer to that is that it's not
|
||||
immediately useful, but you'll be seeing some instances where certain curve
|
||||
properties can be either computed via function manipulation, or via clever
|
||||
use of matrices, and sometimes the matrix approach can be (drastically) faster.</p>
|
||||
|
||||
<p>So for now, just remember that we can represent curves this way, and let's move on.</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Matrix;
|
@@ -60,7 +60,7 @@ var Whatis = React.createClass({
|
||||
<p>While it doesn't look like that's what we've just done, we actually just drew a quadratic curve, in steps,
|
||||
rather than in a single go. One of the fascinating parts about Bézier curves is that they can both be described
|
||||
in terms of polynomial functions, as well as in terms of very simple interpolations of interpolations of [...].
|
||||
That it turn means we can look at what these curves can do based on both "real maths" (by examining the functions,
|
||||
That, in turn, means we can look at what these curves can do based on both "real maths" (by examining the functions,
|
||||
their derivatives, and all that stuff), as well as by looking at the "mechanical" composition (which tells us
|
||||
that a curve will never extend beyond the points we used to construct it, for instance)</p>
|
||||
|
||||
|
Reference in New Issue
Block a user