1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-10-03 11:31:52 +02:00
Files
BezierInfo-2/components/sections/flattening/index.js
2016-01-12 18:55:20 -08:00

126 lines
4.3 KiB
JavaScript

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"
};
},
setupQuadratic: function(api) {
var curve = api.getDefaultQuadratic();
api.setCurve(curve);
api.steps = 3;
},
setupCubic: function(api) {
var curve = api.getDefaultCubic();
api.setCurve(curve);
api.steps = 5;
},
drawFlattened: function(api, curve) {
api.reset();
api.setColor("#DDD");
api.drawSkeleton(curve);
api.setColor("#DDD");
api.drawCurve(curve);
var step = 1 / api.steps;
var p0 = curve.points[0], pc;
for(var t=step; t<1.0+step; t+=step) {
pc = curve.get(Math.min(t,1));
api.setColor("red");
api.drawLine(p0,pc);
p0 = pc;
}
api.setFill("black");
api.text("Curve approximation using "+api.steps+" segments", {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;
}
}
},
render: function() {
return (
<section>
<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>
<p>We can do this is by saying "we want X segments", and then sampling the curve at intervals that are spaced such that we
end up with the number of segments we wanted. The advantage of this method is that it's fast: instead of evaluating 100 or
even 1000 curve coordinates, we can sample a much lower number and still end up with a curve that sort-of-kind-of looks good
enough. The disadvantage of course is that we lose the precision of working with "the real curve", so we usually can't use
the flattened for for doing true intersection detection, or curvature alignment.</p>
<Graphic preset="twopanel" title="Flattening a quadratic curve" setup={this.setupQuadratic} draw={this.drawFlattened} onKeyDown={this.onKeyDown}/>
<Graphic preset="twopanel" title="Flattening a cubic curve" setup={this.setupCubic} draw={this.drawFlattened} onKeyDown={this.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'll notice that for certain curvatures, a low number of segments works quite well, but for more complex
curvatures (try this for the cubic curve), a higher number is required to capture the curvature changes properly.</p>
<div className="howtocode">
<h3>How to implement curve flattening</h3>
<p>Let's just use the algorithm we just specified, and implement that:</p>
<pre>function flattenCurve(curve, segmentCount):
step = 1/segmentCount;
coordinates = [curve.getXValue(0), curve.getYValue(0)]
for(i=1; i <= segmentCount; i++):
t = i*step;
coordinates.push[curve.getXValue(t), curve.getYValue(t)]
return coordinates;</pre>
<p>And done, that's the algorithm implemented. That just leaves drawing the resulting "curve" as a sequence of lines:</p>
<pre>function drawFlattenedCurve(curve, segmentCount):
coordinates = flattenCurve(curve, segmentCount)
coord = coordinates[0], _coords;
for(i=1; i < coordinates.length; i++):
_coords = coordinates[i]
line(coords, _coords)
coords = _coords</pre>
<p>We start with the first coordinate as reference point, and then just draw lines between each point and its next point.</p>
</div>
</section>
);
}
});
module.exports = keyHandling(Flattening);