1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-10 02:36:54 +02:00

let's deploy this.

This commit is contained in:
Pomax
2016-01-12 18:55:20 -08:00
parent 8db94a3f50
commit 419fe35edf
20 changed files with 251 additions and 275 deletions

View File

@@ -34,7 +34,8 @@ var Article = React.createClass({
generateNavItem: function(section, entry) { generateNavItem: function(section, entry) {
var name = section.props.name; var name = section.props.name;
return <li key={name} data-number={entry}><a href={'#' + name}>{ section.props.title }</a></li>; var title = section.props.title;
return <li key={name} data-number={entry}><a href={'#' + name}>{ title }</a></li>;
}, },
render: function() { render: function() {

View File

@@ -1,6 +1,8 @@
var React = require("react"); var React = require("react");
var chroma = require("chroma-js"); var chroma = require("chroma-js");
var Bezier = require("bezier-js");
// event coordinate fix
var fix = function(e) { var fix = function(e) {
e = e || window.event; e = e || window.event;
var target = e.target || e.srcElement, var target = e.target || e.srcElement,
@@ -9,11 +11,7 @@ var fix = function(e) {
e.offsetY = e.clientY - rect.top; e.offsetY = e.clientY - rect.top;
}; };
var Bezier = require("bezier-js");
var Graphic = React.createClass({ var Graphic = React.createClass({
Paper: false, Paper: false,
defaultWidth: 275, defaultWidth: 275,

View File

@@ -9,7 +9,7 @@ var Ribbon = React.createClass({
render: function() { render: function() {
return (<div className="ribbon"> return (<div className="ribbon">
<img src="images/ribbon.png" alt="This page on GitHub" border={0} useMap={"#githubmap"} /> <img src="images/ribbon.png" alt="This page on GitHub" border={0} useMap={"#githubmap"} width="200px" height="149px"/>
<map name="githubmap"> <map name="githubmap">
<area shape="poly" coords="30,0, 200,0, 200,114" href={this.state.href} alt="This page on GitHub"/> <area shape="poly" coords="30,0, 200,0, 200,114" href={this.state.href} alt="This page on GitHub"/>
</map> </map>

View File

@@ -0,0 +1,33 @@
var React = require("react");
var noop = require("../../lib/noop");
module.exports = function(Component) {
var options = Component.keyHandlingOptions,
propName = options.propName || "",
values = options.values || {},
controller = options.controller || noop,
getDefaultProps = Component.getDefaultProps;
return React.createClass({
values: values,
getDefaultProps: getDefaultProps,
onKeyDown: function(event, api) {
var v = this.values[event.keyCode];
if(v) {
event.preventDefault();
if (typeof v === "function") {
v(api);
} else {
api[propName] += v;
controller(api);
}
}
},
render: function() {
return <Component {...this.props} onKeyDown={this.onKeyDown} />;
}
});
};

View File

@@ -1,10 +1,25 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var atan2 = Math.atan2, PI = Math.PI, TAU = 2*PI, cos = Math.cos, sin = Math.sin; var atan2 = Math.atan2, PI = Math.PI, TAU = 2*PI, cos = Math.cos, sin = Math.sin;
var Introduction = React.createClass({ var Introduction = React.createClass({
statics: {
keyHandlingOptions: {
propName: "error",
values: {
"38": 0.1, // up arrow
"40": -0.1 // down arrow
},
controller: function(api) {
if (api.error < 0.1) {
api.error = 0.1;
}
}
}
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
title: "Approximating Bézier curves with circular arcs" title: "Approximating Bézier curves with circular arcs"
@@ -27,22 +42,6 @@ var Introduction = React.createClass({
api.error = 0.5; api.error = 0.5;
}, },
values: {
"38": 0.1, // up arrow
"40": -0.1 // down arrow
},
onKeyDown: function(e, api) {
var v = this.values[e.keyCode];
if(v) {
e.preventDefault();
api.error += v;
if (api.error < 0.1) {
api.error = 0.1;
}
}
},
getCCenter: function(api, p1, p2, p3) { getCCenter: function(api, p1, p2, p3) {
// deltas // deltas
var dx1 = (p2.x - p1.x), var dx1 = (p2.x - p1.x),
@@ -237,7 +236,7 @@ var Introduction = React.createClass({
interactive, and you can use your up and down cursor keys keys to increase or decrease the error threshold, interactive, and you can use your up and down cursor keys keys to increase or decrease the error threshold,
to see what the effect of a smaller or larger error threshold is.</p> to see what the effect of a smaller or larger error threshold is.</p>
<Graphic preset="simple" title="Arc approximation of a Bézier curve" setup={this.setupCubic} draw={this.drawSingleArc} onKeyDown={this.onKeyDown} /> <Graphic preset="simple" title="Arc approximation of a Bézier curve" setup={this.setupCubic} draw={this.drawSingleArc} onKeyDown={this.props.onKeyDown} />
<p>With that in place, all that's left now is to "restart" the procedure by treating the found arc's <p>With that in place, all that's left now is to "restart" the procedure by treating the found arc's
end point as the new to-be-determined arc's starting point, and using points further down the curve. We end point as the new to-be-determined arc's starting point, and using points further down the curve. We
@@ -246,7 +245,7 @@ var Introduction = React.createClass({
so you can see how picking a different threshold changes the number of arcs that are necessary to so you can see how picking a different threshold changes the number of arcs that are necessary to
reasonably approximate a curve:</p> reasonably approximate a curve:</p>
<Graphic preset="simple" title="Arc approximation of a Bézier curve" setup={this.setupCubic} draw={this.drawArcs} onKeyDown={this.onKeyDown} /> <Graphic preset="simple" title="Arc approximation of a Bézier curve" setup={this.setupCubic} draw={this.drawArcs} onKeyDown={this.props.onKeyDown} />
<p>So... what is this good for? Obviously, If you're working with technologies that can't do curves, <p>So... what is this good for? Obviously, If you're working with technologies that can't do curves,
but can do lines and circles, then the answer is pretty straight-forward, but what else? There are but can do lines and circles, then the answer is pretty straight-forward, but what else? There are
@@ -265,4 +264,4 @@ var Introduction = React.createClass({
} }
}); });
module.exports = Introduction; module.exports = keyHandling(Introduction);

View File

@@ -1,8 +1,24 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var ArclengthApprox = React.createClass({ var ArclengthApprox = React.createClass({
statics: {
keyHandlingOptions: {
propName: "steps",
values: {
"38": 1, // up arrow
"40": -1 // down arrow
},
controller: function(api) {
if (api.steps < 1) {
api.steps = 1;
}
}
}
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
title: "Approximated arc length" title: "Approximated arc length"
@@ -51,23 +67,6 @@ var ArclengthApprox = React.createClass({
api.text("Approximate length, "+api.steps+" steps: "+alen+" (true: "+len+")", {x:10, y: 15}); api.text("Approximate length, "+api.steps+" steps: "+alen+" (true: "+len+")", {x:10, y: 15});
}, },
values: {
"38": 1, // up arrow
"40": -1 // down arrow
},
onKeyDown: function(e, api) {
var v = this.values[e.keyCode];
if(v) {
e.preventDefault();
api.steps += v;
if (api.steps < 1) {
api.steps = 1;
}
console.log(api.steps);
}
},
render: function() { render: function() {
return ( return (
<section> <section>
@@ -81,8 +80,8 @@ var ArclengthApprox = React.createClass({
<p>If we combine the work done in the previous sections on curve flattening and arc length computation, we can <p>If we combine the work done in the previous sections on curve flattening and arc length computation, we can
implement these with minimal effort:</p> implement these with minimal effort:</p>
<Graphic preset="twopanel" title="Approximate quadratic curve arc length" setup={this.setupQuadratic} draw={this.draw} onKeyDown={this.onKeyDown} /> <Graphic preset="twopanel" title="Approximate quadratic curve arc length" setup={this.setupQuadratic} draw={this.draw} onKeyDown={this.props.onKeyDown} />
<Graphic preset="twopanel" title="Approximate cubic curve arc length" setup={this.setupCubic} draw={this.draw} onKeyDown={this.onKeyDown} /> <Graphic preset="twopanel" title="Approximate cubic curve arc length" setup={this.setupCubic} draw={this.draw} onKeyDown={this.props.onKeyDown} />
<p>Try clicking on the sketch and using your up and down arrow keys to lower the number of segments for both <p>Try clicking on the sketch and using your up and down arrow keys to lower the number of segments for both
the quadratic and cubic curve. You may notice that the error in length is actually pretty significant, even if the quadratic and cubic curve. You may notice that the error in length is actually pretty significant, even if
@@ -94,87 +93,4 @@ var ArclengthApprox = React.createClass({
} }
}); });
module.exports = ArclengthApprox; module.exports = keyHandling(ArclengthApprox);
/*
void setupCurve() {
setupDefaultQuadratic();
offsetting();
offset = 16;
}
void drawCurve(BezierCurve curve) {
additionals();
curve.draw();
nextPanel();
stroke(0);
float x = curve.getXValue(0),
y = curve.getYValue(0),
x2, y2, step = 1/offset, t,
length=0;
for(int i=1; i<=offset; i++) {
t = i*step;
x2 = curve.getXValue(t);
y2 = curve.getYValue(t);
line(x,y,x2,y2);
length += dist(x,y,x2,y2);
x = x2;
y = y2;
}
float arclength = curve.getCurveLength();
float error = 100 * (arclength - length) / arclength;
length = nfc(length, 3, 3);
arclength = nfc(arclength, 3, 3);
error = nfc(error, 3, 3);
if(error.indexOf(".")===0) { error = "0" + error; }
fill(0);
text("Approximate arc length based on "+offset+" segments: " + length, -dim/4, dim-20);
text("True length: " + arclength + ", error: " + error + "%", -dim/4, dim-5);
}</textarea>
void setupCurve() {
setupDefaultCubic();
offsetting();
offset = 24;
}
void drawCurve(BezierCurve curve) {
additionals();
curve.draw();
nextPanel();
stroke(0);
float x = curve.getXValue(0),
y = curve.getYValue(0),
x2, y2, step = 1/offset, t,
length=0;
for(int i=1; i<=offset; i++) {
t = i*step;
x2 = curve.getXValue(t);
y2 = curve.getYValue(t);
line(x,y,x2,y2);
length += dist(x,y,x2,y2);
x = x2;
y = y2;
}
float arclength = curve.getCurveLength();
float error = 100 * (arclength - length) / arclength;
length = nfc(length, 3, 3);
arclength = nfc(arclength, 3, 3);
error = nfc(error, 3, 3);
if(error.indexOf(".")===0) { error = "0" + error; }
fill(0);
text("Approximate arc length based on "+offset+" segments: " + length, -dim/4, dim-20);
text("True length: " + arclength + ", error: " + error + "%", -dim/4, dim-5);
}</textarea>
*/

View File

@@ -1,8 +1,19 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var CatmullRomMoulding = React.createClass({ var CatmullRomMoulding = React.createClass({
statics: {
keyHandlingOptions: {
propName: "distance",
values: {
"38": 1, // up arrow
"40": -1 // down arrow
}
}
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
title: "Creating a Catmull-Rom curve from three points" title: "Creating a Catmull-Rom curve from three points"
@@ -19,16 +30,6 @@ var CatmullRomMoulding = React.createClass({
api.distance = 0; api.distance = 0;
}, },
onKeyDown: function(evt, api) {
if (evt.key === "ArrowUp") {
evt.preventDefault();
api.distance += 1;
} else if (evt.key === "ArrowDown") {
evt.preventDefault();
api.distance -= 1;
}
},
convert: function(p1, p2, p3, p4) { convert: function(p1, p2, p3, p4) {
var t = 0.5; var t = 0.5;
return [ return [
@@ -167,7 +168,7 @@ var CatmullRomMoulding = React.createClass({
we want to do this? Because the line p0--p2 acts as departure tangent at p1, and the line p2--p4 acts as we want to do this? Because the line p0--p2 acts as departure tangent at p1, and the line p2--p4 acts as
arrival tangent at p3. Play around with the graphic a bit to get an idea of what all of that meant:</p> arrival tangent at p3. Play around with the graphic a bit to get an idea of what all of that meant:</p>
<Graphic preset="threepanel" title="Catmull-Rom curve fitting" setup={this.setup} draw={this.draw} onKeyDown={this.onKeyDown}/> <Graphic preset="threepanel" title="Catmull-Rom curve fitting" setup={this.setup} draw={this.draw} onKeyDown={this.props.onKeyDown}/>
<p>As should be obvious by now, Catmull-Rom curves are great for "fitting a curvature to some points", but if we want <p>As should be obvious by now, Catmull-Rom curves are great for "fitting a curvature to some points", but if we want
to convert that curve to Bézier form we're going to end up with a lot of separate (but visually joined) Bézier curves. to convert that curve to Bézier form we're going to end up with a lot of separate (but visually joined) Bézier curves.
@@ -178,4 +179,4 @@ var CatmullRomMoulding = React.createClass({
} }
}); });
module.exports = CatmullRomMoulding; module.exports = keyHandling(CatmullRomMoulding);

View File

@@ -1,28 +1,28 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var Explanation = React.createClass({ var Explanation = React.createClass({
getDefaultProps: function() { statics: {
return { keyHandlingOptions: {
title: "The mathematics of Bézier curves" propName: "step",
};
},
values: { values: {
"38": 0.1, // up arrow "38": 0.1, // up arrow
"40": -0.1 // down arrow "40": -0.1 // down arrow
}, },
controller: function(api) {
onKeyDown: function(e, api) {
var v = this.values[e.keyCode];
if(v) {
e.preventDefault();
api.step += v;
if (api.step < 0.1) { if (api.step < 0.1) {
api.step = 0.1; api.step = 0.1;
} }
} }
}
},
getDefaultProps: function() {
return {
title: "The mathematics of Bézier curves"
};
}, },
setup: function(api) { setup: function(api) {
@@ -126,7 +126,7 @@ var Explanation = React.createClass({
radius 1 around the origin (0,0). If we plot it for <i>t</i> from 0 to 5, we get this (use radius 1 around the origin (0,0). If we plot it for <i>t</i> from 0 to 5, we get this (use
your up and down cursor keys to change the plot end value):</p> your up and down cursor keys to change the plot end value):</p>
<Graphic preset="empty" title="A (partial) circle: x=sin(t), y=cos(t)" static={true} setup={this.setup} draw={this.draw} onKeyDown={this.onKeyDown}/> <Graphic preset="empty" title="A (partial) circle: x=sin(t), y=cos(t)" static={true} setup={this.setup} draw={this.draw} onKeyDown={this.props.onKeyDown}/>
<p>Bézier curves are (one in many classes of) parametric functions, and are characterised <p>Bézier curves are (one in many classes of) parametric functions, and are characterised
by using the same base function for all its dimensions. Unlike the above example, by using the same base function for all its dimensions. Unlike the above example,
@@ -277,4 +277,4 @@ function Bezier(3,t):
} }
}); });
module.exports = Explanation; module.exports = keyHandling(Explanation);

View File

@@ -1,8 +1,24 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var Flattening = React.createClass({ var Flattening = React.createClass({
statics: {
keyHandlingOptions: {
propName: "steps",
values: {
"38": 1, // up arrow
"40": -1 // down arrow
},
controller: function(api) {
if (api.steps < 1) {
api.steps = 1;
}
}
}
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
title: "Simplified drawing" title: "Simplified drawing"
@@ -106,4 +122,4 @@ var Flattening = React.createClass({
} }
}); });
module.exports = Flattening; module.exports = keyHandling(Flattening);

View File

@@ -1,8 +1,19 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var GraduatedOffsetting = React.createClass({ var GraduatedOffsetting = React.createClass({
statics: {
keyHandlingOptions: {
propName: "distance",
values: {
"38": 1, // up arrow
"40": -1 // down arrow
}
}
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
title: "Graduated curve offsetting" title: "Graduated curve offsetting"
@@ -35,19 +46,6 @@ var GraduatedOffsetting = React.createClass({
outline.curves.forEach(c => api.drawCurve(c)); outline.curves.forEach(c => api.drawCurve(c));
}, },
values: {
"38": 1, // up arrow
"40": -1 // down arrow
},
onKeyDown: function(e, api) {
var v = this.values[e.keyCode];
if(v) {
e.preventDefault();
api.distance += v;
}
},
render: function() { render: function() {
return ( return (
<section> <section>
@@ -82,12 +80,11 @@ var GraduatedOffsetting = React.createClass({
will give us the following result (these have with a starting width of 0, and an end width will give us the following result (these have with a starting width of 0, and an end width
of 40 pixels, but can be controlled with your up and down cursor keys):</p> of 40 pixels, but can be controlled with your up and down cursor keys):</p>
<Graphic preset="simple" title="Offsetting a quadratic Bézier curve" setup={this.setupQuadratic} draw={this.draw} onKeyDown={this.onKeyDown}/> <Graphic preset="simple" title="Offsetting a quadratic Bézier curve" setup={this.setupQuadratic} draw={this.draw} onKeyDown={this.props.onKeyDown}/>
<Graphic preset="simple" title="Offsetting a cubic Bézier curve" setup={this.setupCubic} draw={this.draw} onKeyDown={this.onKeyDown}/> <Graphic preset="simple" title="Offsetting a cubic Bézier curve" setup={this.setupCubic} draw={this.draw} onKeyDown={this.props.onKeyDown}/>
</section> </section>
); );
} }
}); });
module.exports = GraduatedOffsetting; module.exports = keyHandling(GraduatedOffsetting);

View File

@@ -1,8 +1,19 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var Offsetting = React.createClass({ var Offsetting = React.createClass({
statics: {
keyHandlingOptions: {
propName: "distance",
values: {
"38": 1, // up arrow
"40": -1 // down arrow
}
}
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
title: "Curve offsetting" title: "Curve offsetting"
@@ -56,19 +67,6 @@ var Offsetting = React.createClass({
api.drawCircle(last.points[3] || last.points[2],1); api.drawCircle(last.points[3] || last.points[2],1);
}, },
values: {
"38": 1, // up arrow
"40": -1 // down arrow
},
onKeyDown: function(e, api) {
var v = this.values[e.keyCode];
if(v) {
e.preventDefault();
api.distance += v;
}
},
render: function() { render: function() {
return ( return (
<section> <section>
@@ -183,8 +181,8 @@ var Offsetting = React.createClass({
curves, no reduction is necessary, but the more twisty the curve gets, the more the curve needs to be reduced curves, no reduction is necessary, but the more twisty the curve gets, the more the curve needs to be reduced
in order to get segments that can safely be scaled.</p> in order to get segments that can safely be scaled.</p>
<Graphic preset="simple" title="Offsetting a quadratic Bézier curve" setup={this.setupQuadratic} draw={this.draw} onKeyDown={this.onKeyDown} /> <Graphic preset="simple" title="Offsetting a quadratic Bézier curve" setup={this.setupQuadratic} draw={this.draw} onKeyDown={this.props.onKeyDown} />
<Graphic preset="simple" title="Offsetting a cubic Bézier curve" setup={this.setupCubic} draw={this.draw} onKeyDown={this.onKeyDown} /> <Graphic preset="simple" title="Offsetting a cubic Bézier curve" setup={this.setupCubic} draw={this.draw} onKeyDown={this.props.onKeyDown} />
<p>You may notice that this may still lead to small 'jumps' in the sub-curves when moving the <p>You may notice that this may still lead to small 'jumps' in the sub-curves when moving the
curve around. This is caused by the fact that we're still performing a naive form of offsetting, curve around. This is caused by the fact that we're still performing a naive form of offsetting,
@@ -196,4 +194,4 @@ var Offsetting = React.createClass({
} }
}); });
module.exports = Offsetting; module.exports = keyHandling(Offsetting);

View File

@@ -47,15 +47,19 @@ var Preface = React.createClass({
<p>So, what if you need to program them yourself? What are the pitfalls? How do you draw them? <p>So, what if you need to program them yourself? What are the pitfalls? How do you draw them?
What are the bounding boxes, how do you determine intersections, how can you extrude a curve, What are the bounding boxes, how do you determine intersections, how can you extrude a curve,
in short: how do you do everything that you might want when you do with these curves? That's in short: how do you do everything that you might want when you do with these curves? That's
what this page is for. Prepare to be mathed.</p> what this page is for. Prepare to be mathed!</p>
<div> <p>—Pomax (or in the tweetworld, <a href="https://twitter.com/TheRealPomax">@TheRealPomax</a>)</p>
<h2>All Bézier graphics are interactive.</h2>
<p>This page uses interactive examples, as well as "real" maths (in LaTeX form) which <div className="note">
is typeset using the most excellent <a href="http://MathJax.org">MathJax</a> library. <h2>Note: All Bézier graphics are interactive.</h2>
This page is still in rewriting, but once done, all examples will also have a "view source"
option, which lets you see how things were implemented using the Bezier.js library.</p> <p>This page uses interactive examples, relying heavily on <a href="http://pomax.github.io/bezierjs/">Bezier.js</a>,
as well as "real" maths (in LaTeX form) which is typeset using the most excellent <a href="http://MathJax.org">MathJax</a> library.
The page is generated offline as a React application, using Webpack, which has made adding
"view source" options considerably more challenging. I'm still trying to figure out how to
add them back in, but it didn't feel like it should hold up deploying this update compared
to the previous years' version.</p>
<h2>How complicated is the maths going to be?</h2> <h2>How complicated is the maths going to be?</h2>
@@ -74,8 +78,6 @@ var Preface = React.createClass({
but you can use the issue tracker for that as well. Once the rewrite is done, I'll add a general but you can use the issue tracker for that as well. Once the rewrite is done, I'll add a general
comment section back in, and maybe a more topical "select this section of text and hit the comment section back in, and maybe a more topical "select this section of text and hit the
'question' button to ask a question about it" system. We'll see. 'question' button to ask a question about it" system. We'll see.
<p>Pomax (or in the tweetworld, <a href="https://twitter.com/TheRealPomax">@TheRealPomax</a>)</p>
</div> </div>
</section> </section>
); );

View File

@@ -1,8 +1,39 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var Reordering = React.createClass({ var Reordering = React.createClass({
statics: {
// Improve this based on http://www.sirver.net/blog/2011/08/23/degree-reduction-of-bezier-curves/
lower: function(curve) {
var pts = curve.points, q=[], n = pts.length;
pts.forEach((p,k) => {
if (!k) { return (q[k] = p); }
var f1 = k/n, f2 = 1 - f1;
q[k] = {
x: f1 * p.x + f2 * pts[k-1].x,
y: f1 * p.y + f2 * pts[k-1].y
};
});
q.splice(n-1,1);
q[n-2] = pts[n-1];
curve.points = q;
return curve;
},
keyHandlingOptions: {
values: {
"38": function(api) {
api.setCurve(api.curve.raise());
},
"40": function(api) {
api.setCurve(Reordering.lower(api.curve));
}
}
}
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
title: "Lowering and elevating curve order" title: "Lowering and elevating curve order"
@@ -69,32 +100,6 @@ var Reordering = React.createClass({
}); });
}, },
// Improve this based on http://www.sirver.net/blog/2011/08/23/degree-reduction-of-bezier-curves/
lower: function(curve) {
var pts = curve.points, q=[], n = pts.length;
pts.forEach((p,k) => {
if (!k) { return (q[k] = p); }
var f1 = k/n, f2 = 1 - f1;
q[k] = {
x: f1 * p.x + f2 * pts[k-1].x,
y: f1 * p.y + f2 * pts[k-1].y
};
});
q.splice(n-1,1);
q[n-2] = pts[n-1];
curve.points = q;
return curve;
},
onKeyDown: function(evt, api) {
evt.preventDefault();
if(evt.key === "ArrowUp") {
api.setCurve(api.curve.raise());
} else if(evt.key === "ArrowDown") {
api.setCurve(this.lower(api.curve));
}
},
getOrder: function() { getOrder: function() {
var order = this.state.order; var order = this.state.order;
if (order%10 === 1 && order !== 11) { if (order%10 === 1 && order !== 11) {
@@ -146,7 +151,7 @@ var Reordering = React.createClass({
<p>We can apply this to a (semi) random curve, as is done in the following graphic. Select the sketch <p>We can apply this to a (semi) random curve, as is done in the following graphic. Select the sketch
and press your up and down cursor keys to elevate or lower the curve order.</p> and press your up and down cursor keys to elevate or lower the curve order.</p>
<Graphic preset="simple" title={"A " + this.getOrder() + " order Bézier curve"} setup={this.setup} draw={this.draw} onKeyDown={this.onKeyDown} /> <Graphic preset="simple" title={"A " + this.getOrder() + " order Bézier curve"} setup={this.setup} draw={this.draw} onKeyDown={this.props.onKeyDown} />
<p>There is a good, if mathematical, explanation on the matrices necessary for optimal reduction <p>There is a good, if mathematical, explanation on the matrices necessary for optimal reduction
over on <a href="http://www.sirver.net/blog/2011/08/23/degree-reduction-of-bezier-curves/">Sirver's Castle</a>, over on <a href="http://www.sirver.net/blog/2011/08/23/degree-reduction-of-bezier-curves/">Sirver's Castle</a>,
@@ -157,4 +162,4 @@ var Reordering = React.createClass({
} }
}); });
module.exports = Reordering; module.exports = keyHandling(Reordering);

View File

@@ -1,8 +1,24 @@
var React = require("react"); var React = require("react");
var Graphic = require("../../Graphic.jsx"); var Graphic = require("../../Graphic.jsx");
var SectionHeader = require("../../SectionHeader.jsx"); var SectionHeader = require("../../SectionHeader.jsx");
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
var Tracing = React.createClass({ var Tracing = React.createClass({
statics: {
keyHandlingOptions: {
propName: "steps",
values: {
"38": 1, // up arrow
"40": -1 // down arrow
},
controller: function(api) {
if (api.steps < 1) {
api.steps = 1;
}
}
}
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
title: "Tracing a curve at fixed distance intervals" title: "Tracing a curve at fixed distance intervals"
@@ -60,23 +76,6 @@ var Tracing = React.createClass({
} }
}, },
values: {
"38": 1, // up arrow
"40": -1 // down arrow
},
onKeyDown: function(e, api) {
var v = this.values[e.keyCode];
if(v) {
e.preventDefault();
api.steps += v;
if (api.steps < 1) {
api.steps = 1;
}
console.log(api.steps);
}
},
drawColoured: function(api, curve) { drawColoured: function(api, curve) {
api.setPanelCount(3); api.setPanelCount(3);
var w = api.getPanelWidth(); var w = api.getPanelWidth();
@@ -155,11 +154,11 @@ var Tracing = React.createClass({
apart. In fact, let's look at the relation between "distance long a curve" and apart. In fact, let's look at the relation between "distance long a curve" and
"<i>t</i> value", by plotting them against one another.</p> "<i>t</i> value", by plotting them against one another.</p>
<p>The following graphic shows a particularly illustrative curve, and it's length-to-<i>t</i> plot. <p>The following graphic shows a particularly illustrative curve, and it's length-to-t plot.
For linear traversal, this line needs to be straight, running from (0,0) to (length,1). This is, For linear traversal, this line needs to be straight, running from (0,0) to (length,1). This is,
it's safe to say, not what we'll see, we'll see something wobbly instead. To make matters even it's safe to say, not what we'll see, we'll see something wobbly instead. To make matters even
worse, the length-to-<i>t</i> function is also of a much higher order than our curve is: while worse, the length-to-<i>t</i> function is also of a much higher order than our curve is: while
the curve we're using for this exercise is a cubic curve, able to switch concave/convex form twice the curve we're using for this exercise is a cubic curve, which can switch concave/convex form once
at best, the plot shows that the distance function along the curve is able to switch forms three at best, the plot shows that the distance function along the curve is able to switch forms three
times (to see this, try creating an S curve with the start/end close together, but the control times (to see this, try creating an S curve with the start/end close together, but the control
points far apart).</p> points far apart).</p>
@@ -183,7 +182,7 @@ var Tracing = React.createClass({
along the horizontal axis. It also shows the curve in an alternating colouring based on the along the horizontal axis. It also shows the curve in an alternating colouring based on the
t-for-distance values we find our LUT:</p> t-for-distance values we find our LUT:</p>
<Graphic preset="threepanel" title="Fixed-interval coloring a curve" setup={this.setup} draw={this.drawColoured} onKeyDown={this.onKeyDown}/> <Graphic preset="threepanel" title="Fixed-interval coloring a curve" setup={this.setup} draw={this.drawColoured} onKeyDown={this.props.onKeyDown}/>
<p>Use your up and down arrow keys to increase or decrease the number of equidistant segments <p>Use your up and down arrow keys to increase or decrease the number of equidistant segments
used to colour the curve.</p> used to colour the curve.</p>
@@ -199,4 +198,4 @@ var Tracing = React.createClass({
} }
}); });
module.exports = Tracing; module.exports = keyHandling(Tracing);

View File

@@ -16,14 +16,14 @@
<meta property="og:section" content="Bézier Curves"> <meta property="og:section" content="Bézier Curves">
<meta property="og:tag" content="Bézier Curves"> <meta property="og:tag" content="Bézier Curves">
<!-- these rules will get overwritten by the style in the article.js React bundle -->
<style> <style>
html, body, article { height: 100%; margin: 0; } html, body, article { height: 100%; margin: 0; }
body { width: 800px; margin: auto; } body { width: 800px; margin: auto; font-size: 4.25mm!important; }
header h1, header h2 { text-align: center; } </style>
header h1 { font-size: 300%; margin: 0.2em; } </head>
p.jsnote { margin: 2em; text-align: justify; } <body>
p.jsnote:first-child { width: 13em; margin: auto; } <div class="dev">
<style>
div.dev { div.dev {
background: rgb(43, 49, 95); background: rgb(43, 49, 95);
color: rgb(81, 181, 255); color: rgb(81, 181, 255);
@@ -39,11 +39,18 @@
box-shadow: 0px 5px 7px 2px rgba(0, 0, 0, 0.3); box-shadow: 0px 5px 7px 2px rgba(0, 0, 0, 0.3);
} }
</style> </style>
</head> DEV PREVIEW ONLY
<body> </div>
<div style="" class="dev">DEV PREVIEW ONLY</div>
<article> <article>
<!-- these rules will get overwritten by the style in the article.js React bundle -->
<style>
header h1, header h2 { text-align: center; }
header h1 { font-size: 300%; margin: 0.2em; }
p.jsnote { margin: 2em; text-align: justify; }
p.jsnote:first-child { width: 13em; margin: auto; }
</style>
<header> <header>
<h1>A Primer on Bézier Curves</h1> <h1>A Primer on Bézier Curves</h1>
<h2>A free, online book for when you really need to know how to do Bézier things.</h2> <h2>A free, online book for when you really need to know how to do Bézier things.</h2>

1
lib/noop.js Normal file
View File

@@ -0,0 +1 @@
module.exports = function(){};

View File

@@ -2,7 +2,6 @@
"name": "bezierinfo", "name": "bezierinfo",
"version": "1.0.0", "version": "1.0.0",
"description": "pomax.github.io/bezierinfo", "description": "pomax.github.io/bezierinfo",
"main": "build.js",
"scripts": { "scripts": {
"build": "webpack --prod", "build": "webpack --prod",
"latex": "node tools/mathjax", "latex": "node tools/mathjax",
@@ -43,6 +42,7 @@
"file-loader": "^0.8.5", "file-loader": "^0.8.5",
"image-size": "^0.4.0", "image-size": "^0.4.0",
"jsmin": "^1.0.1", "jsmin": "^1.0.1",
"json-loader": "^0.5.4",
"json-stringify-safe": "^5.0.1", "json-stringify-safe": "^5.0.1",
"less": "^2.5.3", "less": "^2.5.3",
"less-loader": "^2.2.2", "less-loader": "^2.2.2",

View File

@@ -3,6 +3,9 @@
top: 0; top: 0;
right: 0; right: 0;
height: 0;
overflow: visible;
img { img {
position: relative; position: relative;
z-index: 999; z-index: 999;

View File

@@ -7,7 +7,6 @@ html, body {
body { body {
background: url('../images/paper.png'); background: url('../images/paper.png');
font-size: 16px;
} }
header, section, footer { header, section, footer {

View File

@@ -38,6 +38,7 @@ module.exports = {
loaders: [ loaders: [
{ test: /\.(png|gif)$/, loader: "file?name=images/packed/[hash].[ext]" }, { test: /\.(png|gif)$/, loader: "file?name=images/packed/[hash].[ext]" },
{ test: /\.less$/, loader: "style!css!less" }, { test: /\.less$/, loader: "style!css!less" },
{ test: /\.json$/, loader: "json" },
{ {
test: /.jsx?$/, test: /.jsx?$/,
include: /components/, include: /components/,