1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-13 12:14:23 +02:00

section 15, 16

This commit is contained in:
Pomax
2016-01-02 11:27:23 -08:00
parent 28373e5321
commit 982d432da8
24 changed files with 1583 additions and 163 deletions

View File

@@ -3,7 +3,7 @@ var ReactDOM = require("react-dom");
var SectionHeader = React.createClass({
render: function() {
return <h2 data-num={this.props.number}><a href={'#' + this.props.name}>{this.props.children}</a></h2>;
return <h2 data-num={this.props.number}><a href={'#' + this.props.name}>{this.props.title}</a></h2>;
}
});

View File

@@ -0,0 +1,174 @@
var React = require("react");
var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Aligning = React.createClass({
getDefaultProps: function() {
return {
title: "Aligning curves"
};
},
setupQuadratic: function(api) {
var curve = api.getDefaultQuadratic();
api.setCurve(curve);
},
setupCubic: function(api) {
var curve = api.getDefaultCubic();
api.setCurve(curve);
},
align: function(points, line) {
var tx = line.p1.x,
ty = line.p1.y,
a = -Math.atan2(line.p2.y-ty, line.p2.x-tx),
cos = Math.cos,
sin = Math.sin,
d = function(v) {
return {
x: (v.x-tx)*cos(a) - (v.y-ty)*sin(a),
y: (v.x-tx)*sin(a) + (v.y-ty)*cos(a)
};
};
return points.map(d);
},
draw: function(api, curve) {
api.setPanelCount(2);
api.reset();
api.drawSkeleton(curve);
api.drawCurve(curve);
var pts = curve.points;
var line = {p1: pts[0], p2: pts[pts.length-1]};
var apts = this.align(pts, line);
var aligned = new api.Bezier(apts);
var w = api.getPanelWidth();
var h = api.getPanelHeight();
var offset = {x:w, y:0};
api.setColor("black");
api.drawLine({x:0,y:0}, {x:0,y:h}, offset);
offset.x += w/4;
offset.y += h/2;
api.setColor("grey");
api.drawLine({x:0,y:-h/2}, {x:0,y:h/2}, offset);
api.drawLine({x:-w/4,y:0}, {x:w,y:0}, offset);
api.setFill("grey");
api.setColor("black");
api.drawSkeleton(aligned, offset);
api.drawCurve(aligned, offset);
},
render: function() {
return (
<section>
<SectionHeader {...this.props} />
<p>While there are an incredible number of curves we can define by varying the x- and y-coordinates for
the control points, not all curves are actually distinct. For instance, if we define a curve, and then
rotate it 90 degrees, it's still the same curve, and we'll find its extremities in the same spots, just
at different draw coordinates. As such, one way to make sure we're working with a "unique" curve is to
"axis-align" it.</p>
<p>Aligning also simplifies a curve's functions. We can translate (move) the curve so that the first
point lies on (0,0), which turns our <i>n</i> term polynomial functions into <i>n-1</i> term functions.
The order stays the same, but we have less terms. Then, we can rotate the curves so that the last point
always lies on the x-axis, too, making its coordinate (...,0). This further simplifies the function for
the y-component to an <i>n-2</i> term function. For instance, if we have a cubic curve such as this:</p>
<p>\[
\left \{ \begin{matrix}
x = BLUE[120] \cdot (1-t)^3 BLUE[+ 35] \cdot 3 \cdot (1-t)^2 \cdot t BLUE[+ 220] \cdot 3 \cdot (1-t) \cdot t^2 BLUE[+ 220] \cdot t^3 \\
y = BLUE[160] \cdot (1-t)^3 BLUE[+ 200] \cdot 3 \cdot (1-t)^2 \cdot t BLUE[+ 260] \cdot 3 \cdot (1-t) \cdot t^2 BLUE[+ 40] \cdot t^3
\end{matrix} \right. \]</p>
<p>Then translating it so that the first coordinate lies on (0,0), moving all <i>x</i> coordinates
by -120, and all <i>y</i> coordinates by -160, gives us:</p>
<p>\[
\left \{ \begin{matrix}
x = BLUE[0] \cdot (1-t)^3 BLUE[- 85] \cdot 3 \cdot (1-t)^2 \cdot t BLUE[+ 100] \cdot 3 \cdot (1-t) \cdot t^2 BLUE[+ 100] \cdot t^3 \\
y = BLUE[0] \cdot (1-t)^3 BLUE[+ 40] \cdot 3 \cdot (1-t)^2 \cdot t BLUE[+ 100] \cdot 3 \cdot (1-t) \cdot t^2 BLUE[- 120] \cdot t^3
\end{matrix} \right. \]</p>
<p>If we then rotate the curve so that its end point lies on the x-axis, the coordinates (integer-rounded
for illustrative purposes here) become:</p>
<p>\[
\left \{ \begin{matrix}
x = BLUE[0] \cdot (1-t)^3 BLUE[+ 85] \cdot 3 \cdot (1-t)^2 \cdot t BLUE[+ 12] \cdot 3 \cdot (1-t) \cdot t^2 BLUE[- 156] \cdot t^3 \\
y = BLUE[0] \cdot (1-t)^3 BLUE[+ 40] \cdot 3 \cdot (1-t)^2 \cdot t BLUE[- 140] \cdot 3 \cdot (1-t) \cdot t^2 BLUE[+ 0] \cdot t^3
\end{matrix} \right. \]</p>
<p>If we drop all the zero-terms, this gives us:</p>
<p>\[
\left \{ \begin{array}{l}
x = BLUE[85] \cdot 3 \cdot (1-t)^2 \cdot t BLUE[+ 13] \cdot 3 \cdot (1-t) \cdot t^2 BLUE[- 156] \cdot t^3 \\
y = BLUE[40] \cdot 3 \cdot (1-t)^2 \cdot t BLUE[- 141] \cdot 3 \cdot (1-t) \cdot t^2
\end{array} \right. \]</p>
<p>We can see that our original curve definition has been simplified considerably. The following graphics
illustrate the result of aligning our example curves to the x-axis, with the cubic case using
the coordinates that were just used in the example formulae:</p>
<Graphic preset="twopanel" title="Aligning a quadratic curve" setup={this.setupQuadratic} draw={this.draw} />
<Graphic preset="twopanel" title="Aligning a cubic curve" setup={this.setupCubic} draw={this.draw} />
</section>
);
}
});
module.exports = Aligning;
/*
void setupCurve() {
setupDefaultQuadratic();
}
void drawCurve(BezierCurve curve) {
additionals();
curve.draw();
nextPanel();
stroke(0);
line(0,0,0,dim);
stroke(0,50);
translate(3*dim/4,dim/2);
line(-3*dim/4,0,dim/4,0);
line(0,-dim/2,0,dim/2);
curve.align().draw(color(150));
}</textarea>
void setupCurve() {
setupDefaultCubic();
}
void drawCurve(BezierCurve curve) {
additionals();
curve.draw();
nextPanel();
stroke(0);
line(0,0,0,dim);
stroke(0,50);
translate(3*dim/4,dim/2);
line(-3*dim/4,0,dim/4,0);
line(0,-dim/2,0,dim/2);
curve.align().draw(color(150));
}</textarea>
*/

View File

@@ -0,0 +1,64 @@
var React = require("react");
var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var BoundingBox = React.createClass({
getDefaultProps: function() {
return {
title: "Bounding boxes"
};
},
setupQuadratic: function(api) {
var curve = api.getDefaultQuadratic();
api.setCurve(curve);
},
setupCubic: function(api) {
var curve = api.getDefaultCubic();
api.setCurve(curve);
},
draw: function(api, curve) {
api.reset();
api.drawSkeleton(curve);
api.drawCurve(curve);
api.setColor("#00FF00");
api.drawbbox(curve.bbox());
},
render: function() {
return (
<section>
<SectionHeader {...this.props} />
<p>If we have the extremities, and the start/end points, a simple for loop that tests for min/max values for
x and y means we have the four values we need to box in our curve:</p>
<p id="bounds_p"><i>Computing the bounding box for a Bézier curve</i></p>
<ol>
<li>Find all <i>t</i> value(s) for the curve derivative's x- and y-roots.</li>
<li>Discard any <i>t</i> value that's lower than 0 or higher than 1, because Bézier curves only use the interval [0,1].</li>
<li>Determine the lowest and highest value when plugging the values <i>t=0</i>, <i>t=1</i> and each of the found
roots into the original functions: the lowest value is the lower bound, and the highest value is the upper
bound for the bounding box we want to construct.</li>
</ol>
<p>Applying this approach to our previous root finding, we get the following bounding boxes (with curve
extremities coloured the same as in the root finding graphics):</p>
<Graphic preset="simple" title="Quadratic Bézier bounding box" setup={this.setupQuadratic} draw={this.draw} />
<Graphic preset="simple" title="Cubic Bézier bounding box" setup={this.setupCubic} draw={this.draw} />
<p>We can construct even nicer boxes by aligning them along our curve, rather than along the x- and y-axis,
but in order to do so we first need to look at how aligning works.</p>
</section>
);
}
});
module.exports = BoundingBox;

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Components = React.createClass({
statics: {
title: "Component functions"
getDefaultProps: function() {
return {
title: "Component functions"
};
},
setupQuadratic: function(api) {
@@ -48,7 +50,7 @@ var Components = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ Components.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>One of the first things people run into when they start using Bézier curves in their own programs is
"I know how to draw the curve, but how do I determine the bounding box?". It's actually reasonably straight

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Control = React.createClass({
statics: {
title: "Controlling Bézier curvatures"
getDefaultProps: function() {
return {
title: "Controlling Bézier curvatures"
};
},
drawCubic: function(api) {
@@ -168,7 +170,7 @@ var Control = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ Control.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>Bézier curves are (like all "splines") interpolation functions, meaning they take a set of
points, and generate values somewhere "between" those points. (One of the consequences of this

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var deCasteljau = React.createClass({
statics: {
title: "de Casteljau's algorithm"
getDefaultProps: function() {
return {
title: "de Casteljau's algorithm"
};
},
setup: function(api) {
@@ -45,7 +47,7 @@ var deCasteljau = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{deCasteljau.title}</SectionHeader>
<SectionHeader {...this.props} />
<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

View File

@@ -3,14 +3,16 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Derivatives = React.createClass({
statics: {
title: "Derivatives"
getDefaultProps: function() {
return {
title: "Derivatives"
};
},
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ Derivatives.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>There's a number of useful things that you can do with Bézier curves based on their derivative,
and one of the more amusing observations about Bézier curves is that their derivatives are, in fact,

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Explanation = React.createClass({
statics: {
title: "The basics of Bézier curves"
getDefaultProps: function() {
return {
title: "The basics of Bézier curves"
};
},
circle: require("./circle"),
@@ -17,7 +19,7 @@ var Explanation = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ Explanation.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>Bézier curves are a form of "parametric" function. Mathematically speaking, parametric
functions are cheats: a "function" is actually a well defined term representing a mapping

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Extremities = React.createClass({
statics: {
title: "Component functions"
getDefaultProps: function() {
return {
title: "Component functions"
};
},
setupQuadratic: function(api) {
@@ -63,7 +65,7 @@ var Extremities = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ Extremities.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>Now that we understand (well, superficially anyway) the component functions, we can find the extremities of our
Bézier curve by finding maxima and minima on the component functions, by solving the equations B'(t) = 0 and B''(t) = 0.

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Flattening = React.createClass({
statics: {
title: "Simplified drawing"
getDefaultProps: function() {
return {
title: "Simplified drawing"
};
},
setupQuadratic: function(api) {
@@ -58,7 +60,7 @@ var Flattening = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ Flattening.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>We can also simplify the drawing process by "sampling" the curve at certain points, and then joining those points up with straight lines, a process known as "flattening", as we are reducing a curve to a simple sequence of straight, "flat" lines.</p>

View File

@@ -19,13 +19,13 @@ module.exports = {
derivatives: require("./derivatives"),
pointvectors: require("./pointvectors"),
components: require("./components"),
extremities: require("./extremities")
extremities: require("./extremities"),
boundingbox: require("./boundingbox"),
aligning: require("./aligning")
};
/*
boundingbox: require("./boundingbox"),
aligning: require("./aligning"),
tightbounds: require("./tightbounds"),
canonical: require("./canonical"),

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Introduction = React.createClass({
statics: {
title: "A lightning introduction"
getDefaultProps: function() {
return {
title: "A lightning introduction"
};
},
drawQuadratic: function(api) {
@@ -26,7 +28,7 @@ var Introduction = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{Introduction.title}</SectionHeader>
<SectionHeader {...this.props} />
<p>Let's start with the good stuff: when we're talking about Bézier curves, we're talking about the
things that you can see in the following graphics. They run from some start point to some end point,

View File

@@ -2,14 +2,16 @@ var React = require("react");
var SectionHeader = require("../../SectionHeader.jsx");
var Matrix = React.createClass({
statics: {
title: "Bézier curvatures as matrix operations"
getDefaultProps: function() {
return {
title: "Bézier curvatures as matrix operations"
};
},
render: function() {
return (
<section>
<SectionHeader {...this.props}>{Matrix.title}</SectionHeader>
<SectionHeader {...this.props} />
<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.

View File

@@ -3,14 +3,16 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var MatrixSplit = React.createClass({
statics: {
title: "Splitting curves using matrices"
getDefaultProps: function() {
return {
title: "Splitting curves using matrices"
};
},
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ MatrixSplit.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>Another way to split curves is to exploit the matrix representation of
a Bézier curve. In <a href="#matrix">the section on matrices</a> we saw that

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var PointVectors = React.createClass({
statics: {
title: "Tangents and normals"
getDefaultProps: function() {
return {
title: "Tangents and normals"
};
},
setupQuadratic: function(api) {
@@ -41,7 +43,7 @@ var PointVectors = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ PointVectors.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>If you want to move objects along a curve, or "away from" a curve, the two vectors you're most interested
in are the tangent vector and normal vector for curve points. These are actually really easy to find. For

View File

@@ -1,14 +1,16 @@
var React = require("react");
var Preface = React.createClass({
statics: {
title: "Preface"
getDefaultProps: function() {
return {
title: "Preface"
};
},
render: function() {
return (
<section>
<h2>{Preface.title}</h2>
<h2>{this.props.title}</h2>
<p>In order to draw things in 2D, we usually rely on lines, which typically get classified
into two categories: straight lines, and curves. The first of these are as easy to draw as they

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Reordering = React.createClass({
statics: {
title: "Lowering and elevating curve order"
getDefaultProps: function() {
return {
title: "Lowering and elevating curve order"
};
},
getInitialState: function() {
@@ -110,7 +112,7 @@ var Reordering = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ Reordering.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>One interesting property of Bézier curves is that an <i>n<sup>th</sup></i> order curve can
always be perfectly represented by an <i>(n+1)<sup>th</sup></i> order curve, by giving the

View File

@@ -3,8 +3,10 @@ var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx");
var Splitting = React.createClass({
statics: {
title: "Splitting curves"
getDefaultProps: function() {
return {
title: "Splitting curves"
};
},
setupCubic: function(api) {
@@ -100,7 +102,7 @@ var Splitting = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{ Splitting.title }</SectionHeader>
<SectionHeader {...this.props} />
<p>With de Casteljau's algorithm we also find all the points we need to split up a Bézier curve into two, smaller
curves, which taken together form the original curve. When we construct de Casteljau's skeleton for some value

View File

@@ -4,8 +4,10 @@ var SectionHeader = require("../../SectionHeader.jsx");
var Whatis = React.createClass({
statics: {
title: "So what makes a Bézier Curve?"
getDefaultProps: function() {
return {
title: "So what makes a Bézier Curve?"
};
},
interpolation: require("./interpolation"),
@@ -18,7 +20,7 @@ var Whatis = React.createClass({
render: function() {
return (
<section>
<SectionHeader {...this.props}>{Whatis.title}</SectionHeader>
<SectionHeader {...this.props} />
<p>Playing with the points for curves may have given you a feel for how Bézier curves behaves, but
what <em>are</em> Bézier curves, really? There are two ways to explain what a Bézier curve is, and