mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-01 06:20:52 +02:00
let's deploy this.
This commit is contained in:
@@ -34,7 +34,8 @@ var Article = React.createClass({
|
||||
|
||||
generateNavItem: function(section, entry) {
|
||||
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() {
|
||||
|
@@ -1,6 +1,8 @@
|
||||
var React = require("react");
|
||||
var chroma = require("chroma-js");
|
||||
var Bezier = require("bezier-js");
|
||||
|
||||
// event coordinate fix
|
||||
var fix = function(e) {
|
||||
e = e || window.event;
|
||||
var target = e.target || e.srcElement,
|
||||
@@ -9,11 +11,7 @@ var fix = function(e) {
|
||||
e.offsetY = e.clientY - rect.top;
|
||||
};
|
||||
|
||||
|
||||
var Bezier = require("bezier-js");
|
||||
|
||||
var Graphic = React.createClass({
|
||||
|
||||
Paper: false,
|
||||
|
||||
defaultWidth: 275,
|
||||
@@ -62,7 +60,7 @@ var Graphic = React.createClass({
|
||||
onKeyUp={this.onKeyUp}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyPress={this.onKeyPress}
|
||||
/>
|
||||
/>
|
||||
<figcaption>{this.props.title} {this.props.children}</figcaption>
|
||||
</figure>
|
||||
);
|
||||
|
@@ -9,7 +9,7 @@ var Ribbon = React.createClass({
|
||||
|
||||
render: function() {
|
||||
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">
|
||||
<area shape="poly" coords="30,0, 200,0, 200,114" href={this.state.href} alt="This page on GitHub"/>
|
||||
</map>
|
||||
|
33
components/decorators/keyhandling-decorator.jsx
Normal file
33
components/decorators/keyhandling-decorator.jsx
Normal 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} />;
|
||||
}
|
||||
});
|
||||
};
|
@@ -1,10 +1,25 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.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 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() {
|
||||
return {
|
||||
title: "Approximating Bézier curves with circular arcs"
|
||||
@@ -27,22 +42,6 @@ var Introduction = React.createClass({
|
||||
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) {
|
||||
// deltas
|
||||
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,
|
||||
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
|
||||
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
|
||||
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,
|
||||
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);
|
||||
|
@@ -1,8 +1,24 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
|
||||
|
||||
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() {
|
||||
return {
|
||||
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});
|
||||
},
|
||||
|
||||
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() {
|
||||
return (
|
||||
<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
|
||||
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 cubic curve arc length" setup={this.setupCubic} 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.props.onKeyDown} />
|
||||
|
||||
<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
|
||||
@@ -94,87 +93,4 @@ var ArclengthApprox = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = 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>
|
||||
|
||||
*/
|
||||
module.exports = keyHandling(ArclengthApprox);
|
@@ -1,8 +1,19 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
|
||||
|
||||
var CatmullRomMoulding = React.createClass({
|
||||
statics: {
|
||||
keyHandlingOptions: {
|
||||
propName: "distance",
|
||||
values: {
|
||||
"38": 1, // up arrow
|
||||
"40": -1 // down arrow
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "Creating a Catmull-Rom curve from three points"
|
||||
@@ -19,16 +30,6 @@ var CatmullRomMoulding = React.createClass({
|
||||
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) {
|
||||
var t = 0.5;
|
||||
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
|
||||
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
|
||||
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);
|
||||
|
@@ -1,30 +1,30 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
|
||||
|
||||
var Explanation = React.createClass({
|
||||
statics: {
|
||||
keyHandlingOptions: {
|
||||
propName: "step",
|
||||
values: {
|
||||
"38": 0.1, // up arrow
|
||||
"40": -0.1 // down arrow
|
||||
},
|
||||
controller: function(api) {
|
||||
if (api.step < 0.1) {
|
||||
api.step = 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "The mathematics of Bézier curves"
|
||||
};
|
||||
},
|
||||
|
||||
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.step += v;
|
||||
if (api.step < 0.1) {
|
||||
api.step = 0.1;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setup: function(api) {
|
||||
api.step = 5;
|
||||
},
|
||||
@@ -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
|
||||
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
|
||||
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);
|
||||
|
@@ -1,8 +1,24 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
|
||||
|
||||
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() {
|
||||
return {
|
||||
title: "Simplified drawing"
|
||||
@@ -106,4 +122,4 @@ var Flattening = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Flattening;
|
||||
module.exports = keyHandling(Flattening);
|
||||
|
@@ -1,8 +1,19 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
|
||||
|
||||
var GraduatedOffsetting = React.createClass({
|
||||
statics: {
|
||||
keyHandlingOptions: {
|
||||
propName: "distance",
|
||||
values: {
|
||||
"38": 1, // up arrow
|
||||
"40": -1 // down arrow
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "Graduated curve offsetting"
|
||||
@@ -35,19 +46,6 @@ var GraduatedOffsetting = React.createClass({
|
||||
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() {
|
||||
return (
|
||||
<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
|
||||
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 cubic Bézier curve" setup={this.setupCubic} 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.props.onKeyDown}/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = GraduatedOffsetting;
|
||||
module.exports = keyHandling(GraduatedOffsetting);
|
||||
|
@@ -1,8 +1,19 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
|
||||
|
||||
var Offsetting = React.createClass({
|
||||
statics: {
|
||||
keyHandlingOptions: {
|
||||
propName: "distance",
|
||||
values: {
|
||||
"38": 1, // up arrow
|
||||
"40": -1 // down arrow
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "Curve offsetting"
|
||||
@@ -56,19 +67,6 @@ var Offsetting = React.createClass({
|
||||
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() {
|
||||
return (
|
||||
<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
|
||||
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 cubic Bézier curve" setup={this.setupCubic} 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.props.onKeyDown} />
|
||||
|
||||
<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,
|
||||
@@ -196,4 +194,4 @@ var Offsetting = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Offsetting;
|
||||
module.exports = keyHandling(Offsetting);
|
||||
|
@@ -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?
|
||||
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
|
||||
what this page is for. Prepare to be mathed.</p>
|
||||
what this page is for. Prepare to be mathed!</p>
|
||||
|
||||
<div>
|
||||
<h2>All Bézier graphics are interactive.</h2>
|
||||
<p>—Pomax (or in the tweetworld, <a href="https://twitter.com/TheRealPomax">@TheRealPomax</a>)</p>
|
||||
|
||||
<p>This page uses interactive examples, as well as "real" maths (in LaTeX form) which
|
||||
is typeset using the most excellent <a href="http://MathJax.org">MathJax</a> library.
|
||||
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>
|
||||
<div className="note">
|
||||
<h2>Note: All Bézier graphics are interactive.</h2>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -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
|
||||
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.
|
||||
|
||||
<p>—Pomax (or in the tweetworld, <a href="https://twitter.com/TheRealPomax">@TheRealPomax</a>)</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
@@ -1,8 +1,39 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
|
||||
|
||||
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() {
|
||||
return {
|
||||
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() {
|
||||
var order = this.state.order;
|
||||
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
|
||||
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
|
||||
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);
|
||||
|
@@ -1,8 +1,24 @@
|
||||
var React = require("react");
|
||||
var Graphic = require("../../Graphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var keyHandling = require("../../decorators/keyhandling-decorator.jsx");
|
||||
|
||||
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() {
|
||||
return {
|
||||
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) {
|
||||
api.setPanelCount(3);
|
||||
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
|
||||
"<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,
|
||||
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
|
||||
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
|
||||
times (to see this, try creating an S curve with the start/end close together, but the control
|
||||
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
|
||||
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
|
||||
used to colour the curve.</p>
|
||||
@@ -199,4 +198,4 @@ var Tracing = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Tracing;
|
||||
module.exports = keyHandling(Tracing);
|
||||
|
49
index.html
49
index.html
@@ -16,34 +16,41 @@
|
||||
<meta property="og:section" 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>
|
||||
html, body, article { height: 100%; margin: 0; }
|
||||
body { width: 800px; margin: auto; }
|
||||
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; }
|
||||
div.dev {
|
||||
background: rgb(43, 49, 95);
|
||||
color: rgb(81, 181, 255);
|
||||
position: fixed;
|
||||
transform: rotate(-45deg);
|
||||
width: 22em;
|
||||
text-align: center;
|
||||
top: 4em;
|
||||
left: -6em;
|
||||
font-variant: small-caps;
|
||||
font-weight: bolder;
|
||||
font-family: Helvetica;
|
||||
box-shadow: 0px 5px 7px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
body { width: 800px; margin: auto; font-size: 4.25mm!important; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="" class="dev">DEV PREVIEW ONLY</div>
|
||||
<div class="dev">
|
||||
<style>
|
||||
div.dev {
|
||||
background: rgb(43, 49, 95);
|
||||
color: rgb(81, 181, 255);
|
||||
position: fixed;
|
||||
transform: rotate(-45deg);
|
||||
width: 22em;
|
||||
text-align: center;
|
||||
top: 4em;
|
||||
left: -6em;
|
||||
font-variant: small-caps;
|
||||
font-weight: bolder;
|
||||
font-family: Helvetica;
|
||||
box-shadow: 0px 5px 7px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
</style>
|
||||
DEV PREVIEW ONLY
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
1
lib/noop.js
Normal file
1
lib/noop.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = function(){};
|
@@ -2,7 +2,6 @@
|
||||
"name": "bezierinfo",
|
||||
"version": "1.0.0",
|
||||
"description": "pomax.github.io/bezierinfo",
|
||||
"main": "build.js",
|
||||
"scripts": {
|
||||
"build": "webpack --prod",
|
||||
"latex": "node tools/mathjax",
|
||||
@@ -43,6 +42,7 @@
|
||||
"file-loader": "^0.8.5",
|
||||
"image-size": "^0.4.0",
|
||||
"jsmin": "^1.0.1",
|
||||
"json-loader": "^0.5.4",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"less": "^2.5.3",
|
||||
"less-loader": "^2.2.2",
|
||||
|
@@ -3,6 +3,9 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
|
@@ -7,7 +7,6 @@ html, body {
|
||||
|
||||
body {
|
||||
background: url('../images/paper.png');
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
header, section, footer {
|
||||
|
@@ -38,6 +38,7 @@ module.exports = {
|
||||
loaders: [
|
||||
{ test: /\.(png|gif)$/, loader: "file?name=images/packed/[hash].[ext]" },
|
||||
{ test: /\.less$/, loader: "style!css!less" },
|
||||
{ test: /\.json$/, loader: "json" },
|
||||
{
|
||||
test: /.jsx?$/,
|
||||
include: /components/,
|
||||
|
Reference in New Issue
Block a user