mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-12 19:54:31 +02:00
bsplines
This commit is contained in:
36
article.js
36
article.js
File diff suppressed because one or more lines are too long
@@ -18,6 +18,7 @@ var BSplineGraphic = React.createClass({
|
||||
this.activeDistance = 9;
|
||||
this.points = [];
|
||||
this.knots = [];
|
||||
this.weights = [];
|
||||
this.nodes = [];
|
||||
this.cp = undefined;
|
||||
this.dx = undefined;
|
||||
@@ -37,33 +38,55 @@ var BSplineGraphic = React.createClass({
|
||||
return <canvas className="bspline-graphic" ref="sketch" />;
|
||||
},
|
||||
|
||||
keydownlisten(e) { this.setKeyboardValues(e); this.keyDown(); },
|
||||
keyuplisten(e) { this.setKeyboardValues(e); this.keyUp(); },
|
||||
keypresslisten(e) { this.setKeyboardValues(e); this.keyPressed(); },
|
||||
mousedownlisten(e) { this.setMouseValues(e); this.mouseDown(); },
|
||||
mouseuplisten(e) { this.setMouseValues(e); this.mouseUp(); },
|
||||
mousemovelisten(e) { this.setMouseValues(e); this.mouseMove(); if(this.isMouseDown && this.mouseDrag) { this.mouseDrag(); }},
|
||||
wheellissten(e) { e.preventDefault(); this.scrolled(e.deltaY < 0 ? 1 : -1); },
|
||||
|
||||
componentDidMount() {
|
||||
var cvs = this.cvs = this.refs.sketch;
|
||||
// Keyboard event handling
|
||||
cvs.addEventListener("keydown", (e) => { this.setKeyboardValues(e); if (typeof this.keyDown !== "undefined") { this.keyDown(); }});
|
||||
cvs.addEventListener("keyup", (e) => { this.setKeyboardValues(e); if (typeof this.keyUp !== "undefined") { this.keyUp(); }});
|
||||
cvs.addEventListener("keypress", (e) => { this.setKeyboardValues(e); if (typeof this.keyPressed !== "undefined") { this.keyPressed(); }});
|
||||
cvs.addEventListener("keydown", this.keydownlisten);
|
||||
cvs.addEventListener("keyup", this.keyuplisten);
|
||||
cvs.addEventListener("keypress", this.keypresslisten);
|
||||
// Mouse event handling
|
||||
cvs.addEventListener("mousedown", (e) => { this.setMouseValues(e); if (typeof this.mouseDown !== "undefined") { this.mouseDown(); }});
|
||||
cvs.addEventListener("mouseup", (e) => { this.setMouseValues(e); if (typeof this.mouseUp !== "undefined") { this.mouseUp(); }});
|
||||
cvs.addEventListener("mousemove", (e) => { this.setMouseValues(e); if (typeof this.mouseMove !== "undefined") { this.mouseMove(); } if(this.isMouseDown && this.mouseDrag) { this.mouseDrag(); }});
|
||||
cvs.addEventListener("mousedown", this.mousedownlisten);
|
||||
cvs.addEventListener("mouseup", this.mouseuplisten);
|
||||
cvs.addEventListener("mousemove", this.mousemovelisten);
|
||||
// Scroll event handling
|
||||
if (this.props.scrolling) { cvs.addEventListener("wheel", this.wheellissten); }
|
||||
// Boom let's go
|
||||
this.setup();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
var cvs = this.cvs = this.refs.sketch;
|
||||
cvs.removeEventListener("keydown", this.keydownlisten);
|
||||
cvs.removeEventListener("keyup", this.keyuplisten);
|
||||
cvs.removeEventListener("keypress", this.keypresslisten);
|
||||
cvs.removeEventListener("mousedown", this.mousedownlisten);
|
||||
cvs.removeEventListener("mouseup", this.mouseuplisten);
|
||||
cvs.removeEventListener("mousemove", this.mousemovelisten);
|
||||
if (this.props.scrolling) { cvs.removeEventListener("wheel", this.wheellissten); }
|
||||
},
|
||||
|
||||
// base API
|
||||
|
||||
drawCurve(points) {
|
||||
points = points || this.points;
|
||||
var ctx = this.ctx;
|
||||
var weights = this.weights.length>0 ? this.weights : false;
|
||||
ctx.beginPath();
|
||||
var p = interpolate(0, this.degree, points, this.knots);
|
||||
var p = interpolate(0, this.degree, points, this.knots, weights);
|
||||
ctx.moveTo(p[0], p[1]);
|
||||
for(let t=0.01; t<1; t+=0.01) {
|
||||
p = interpolate(t, this.degree, points, this.knots);
|
||||
p = interpolate(t, this.degree, points, this.knots, weights);
|
||||
ctx.lineTo(p[0], p[1]);
|
||||
}
|
||||
p = interpolate(1, this.degree, points, this.knots);
|
||||
p = interpolate(1, this.degree, points, this.knots, weights);
|
||||
ctx.lineTo(p[0], p[1]);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
@@ -71,11 +94,12 @@ var BSplineGraphic = React.createClass({
|
||||
|
||||
drawKnots(points) {
|
||||
var knots = this.knots;
|
||||
var weights = this.weights.length>0 ? this.weights : false;
|
||||
knots.forEach((knot,i) => {
|
||||
if (i < this.degree) return;
|
||||
if (i > knots.length - 1 - this.degree) return;
|
||||
var p = interpolate(knot, this.degree, points, knots, false, false, true);
|
||||
this.ellipse(p[0], p[1], 3);
|
||||
var p = interpolate(knot, this.degree, points, knots, weights, false, true);
|
||||
this.circle(p[0], p[1], 3);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -102,11 +126,11 @@ var BSplineGraphic = React.createClass({
|
||||
i;
|
||||
|
||||
// form the open-uniform knot vector
|
||||
for (i=1; i < l - this.degree; i++) { knots.push(i); }
|
||||
for (i=1; i < l - this.degree; i++) { knots.push(i + this.degree); }
|
||||
// add [degree] zeroes at the front
|
||||
for (i=0; i <= this.degree; i++) { knots = [0].concat(knots); }
|
||||
for (i=0; i <= this.degree; i++) { knots = [this.degree].concat(knots); }
|
||||
// add [degree] max-values to the back
|
||||
for (i=0; i <= this.degree; i++) { knots.push(m); }
|
||||
for (i=0; i <= this.degree; i++) { knots.push(m + this.degree); }
|
||||
|
||||
return knots;
|
||||
},
|
||||
@@ -138,6 +162,12 @@ var BSplineGraphic = React.createClass({
|
||||
return nodes;
|
||||
},
|
||||
|
||||
formWeights(points) {
|
||||
var weights = [];
|
||||
points.forEach(p => weights.push(1));
|
||||
return weights;
|
||||
},
|
||||
|
||||
setDegree(d) {
|
||||
this.degree += d;
|
||||
this.knots = this.formKnots(this.points);
|
||||
@@ -211,6 +241,28 @@ var BSplineGraphic = React.createClass({
|
||||
// ... do nothing?
|
||||
},
|
||||
|
||||
scrolled(direction) {
|
||||
this.cp = this.getCurrentPoint(this.mouseX, this.mouseY);
|
||||
if (!this.cp) return;
|
||||
// base case
|
||||
var pos = this.points.indexOf(this.cp);
|
||||
if (this.weights.length>pos) {
|
||||
this.weights[pos] += direction * 0.1;
|
||||
if (this.weights[pos] < 0) {
|
||||
this.weights[pos] = 0;
|
||||
}
|
||||
}
|
||||
// possible multiplicity due to "closed" curves
|
||||
pos = this.points.indexOf(this.cp, pos+1);
|
||||
if (pos!==-1 && this.weights.length>pos) {
|
||||
this.weights[pos] += direction * 0.1;
|
||||
if (this.weights[pos] < 0) {
|
||||
this.weights[pos] = 0;
|
||||
}
|
||||
}
|
||||
this.redraw();
|
||||
},
|
||||
|
||||
// keyboard events
|
||||
setKeyboardValues(e) {
|
||||
if (!e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
@@ -250,7 +302,7 @@ var BSplineGraphic = React.createClass({
|
||||
for(let y=spacing; y<this.height-1; y+=spacing) { this.line(0,y,this.width,y); }
|
||||
},
|
||||
|
||||
ellipse(x,y,r) {
|
||||
circle(x,y,r) {
|
||||
let hr = r/2;
|
||||
var ctx = this.ctx;
|
||||
ctx.beginPath();
|
||||
@@ -270,6 +322,9 @@ var BSplineGraphic = React.createClass({
|
||||
},
|
||||
|
||||
stroke(r,g,b,a) {
|
||||
if (typeof r === "string") {
|
||||
return (this.ctx.strokeStyle = r);
|
||||
}
|
||||
if (g===undefined) { g=r; b=r; }
|
||||
if (a===undefined) { a = 1; }
|
||||
this.ctx.strokeStyle = this.rgba(r,g,b,a);
|
||||
@@ -278,6 +333,9 @@ var BSplineGraphic = React.createClass({
|
||||
noStroke() { this.ctx.strokeStyle = "none"; },
|
||||
|
||||
fill(r,g,b,a) {
|
||||
if (typeof r === "string") {
|
||||
return (this.ctx.fillStyle = r);
|
||||
}
|
||||
if (g===undefined) { g=r; b=r; }
|
||||
if (a===undefined) { a = 1; }
|
||||
this.ctx.fillStyle = this.rgba(r,g,b,a);
|
||||
|
39
components/KnotController.jsx
Normal file
39
components/KnotController.jsx
Normal file
@@ -0,0 +1,39 @@
|
||||
var React = require('react');
|
||||
|
||||
var KnotController = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
owner: false,
|
||||
knots: []
|
||||
};
|
||||
},
|
||||
bindKnots(owner, knots) {
|
||||
this.setState({owner, knots});
|
||||
},
|
||||
render() {
|
||||
var type = 'range';
|
||||
var min = 0;
|
||||
var max = this.state.knots.length;
|
||||
var step = 1;
|
||||
return (
|
||||
<section className='knot-controls'><h2>knot values</h2>{
|
||||
this.state.knots.map((value,position) => {
|
||||
var props = {
|
||||
type, min, max, step,
|
||||
value,
|
||||
onChange: e => {
|
||||
var k = this.state.knots;
|
||||
k[position] = e.target.value;
|
||||
this.setState({ knots: k }, () => {
|
||||
this.state.owner.redraw();
|
||||
});
|
||||
}
|
||||
};
|
||||
return <div key={'knot'+position}>{min}<input {...props}/>{max} (= {value})</div>;
|
||||
})
|
||||
}</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = KnotController;
|
53
components/WeightController.jsx
Normal file
53
components/WeightController.jsx
Normal file
@@ -0,0 +1,53 @@
|
||||
var React = require('react');
|
||||
|
||||
var WeightController = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
owner: false,
|
||||
weights: [],
|
||||
closed: false
|
||||
};
|
||||
},
|
||||
bindWeights(owner, weights, closed) {
|
||||
this.setState({owner, weights, closed});
|
||||
},
|
||||
render() {
|
||||
var type = 'range';
|
||||
var min = 0;
|
||||
var max = this.state.weights.length;
|
||||
var step = 1;
|
||||
|
||||
var overlap = this.state.closed;
|
||||
var baselength = this.state.weights.length;
|
||||
if (overlap !== false) {
|
||||
baselength -= overlap;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className='knot-controls'><h2>weight values</h2>{
|
||||
this.state.weights.map((value,position) => {
|
||||
if (overlap && position >= baselength) {
|
||||
return null;
|
||||
}
|
||||
var props = {
|
||||
type, min, max, step,
|
||||
value,
|
||||
onChange: e => {
|
||||
var k = this.state.weights;
|
||||
k[position] = e.target.value;
|
||||
if (overlap && position < overlap) {
|
||||
k[position+baselength] = e.target.value;
|
||||
}
|
||||
this.setState({ weights: k }, () => {
|
||||
this.state.owner.redraw();
|
||||
});
|
||||
}
|
||||
};
|
||||
return <div key={'knot'+position}>{min}<input {...props}/>{max} (= {value})</div>;
|
||||
})
|
||||
}</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = WeightController;
|
@@ -16,7 +16,7 @@ module.exports = {
|
||||
this.line(n.x, n.y, p.x, p.y);
|
||||
p = n;
|
||||
this.stroke(0);
|
||||
this.ellipse(p.x, p.y, 4);
|
||||
this.circle(p.x, p.y, 4);
|
||||
});
|
||||
this.drawSplineData();
|
||||
},
|
||||
|
52
components/sections/bsplines/center-cut-bspline.js
Normal file
52
components/sections/bsplines/center-cut-bspline.js
Normal file
@@ -0,0 +1,52 @@
|
||||
module.exports = {
|
||||
degree: 3,
|
||||
activeDistance: 9,
|
||||
|
||||
setup() {
|
||||
this.size(400, 400);
|
||||
|
||||
var TAU = Math.PI*2;
|
||||
for (let i=0; i<TAU; i+=TAU/9) {
|
||||
this.points.push({
|
||||
x: this.width/2 + 100 * Math.cos(i),
|
||||
y: this.height/2 + 100 * Math.sin(i)
|
||||
});
|
||||
}
|
||||
|
||||
this.knots = this.formKnots(this.points);
|
||||
var m = Math.round(this.points.length/2)|0;
|
||||
this.knots[m+0] = this.knots[m];
|
||||
this.knots[m+1] = this.knots[m];
|
||||
this.knots[m+2] = this.knots[m];
|
||||
for (let i=m+3; i<this.knots.length; i++) {
|
||||
this.knots[i] = this.knots[i-1] + 1;
|
||||
}
|
||||
|
||||
if(this.props.controller) {
|
||||
this.props.controller(this, this.knots);
|
||||
}
|
||||
|
||||
this.draw();
|
||||
},
|
||||
|
||||
draw() {
|
||||
this.clear();
|
||||
this.grid(25);
|
||||
var p = this.points[0];
|
||||
this.points.forEach(n => {
|
||||
this.stroke(200);
|
||||
this.line(n.x, n.y, p.x, p.y);
|
||||
p = n;
|
||||
this.stroke(0);
|
||||
this.circle(p.x, p.y, 4);
|
||||
});
|
||||
this.drawSplineData();
|
||||
},
|
||||
|
||||
drawSplineData() {
|
||||
if (this.points.length <= this.degree) return;
|
||||
var mapped = this.points.map(p => [p.x, p.y]);
|
||||
this.drawCurve(mapped);
|
||||
this.drawKnots(mapped);
|
||||
}
|
||||
};
|
@@ -1,6 +1,8 @@
|
||||
var React = require("react");
|
||||
var BSplineGraphic = require("../../BSplineGraphic.jsx");
|
||||
var SectionHeader = require("../../SectionHeader.jsx");
|
||||
var KnotController = require("../../KnotController.jsx");
|
||||
var WeightController = require("../../WeightController.jsx");
|
||||
|
||||
var BoundingBox = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
@@ -9,6 +11,14 @@ var BoundingBox = React.createClass({
|
||||
};
|
||||
},
|
||||
|
||||
bindKnots: function(owner, knots, ref) {
|
||||
this.refs[ref].bindKnots(owner, knots);
|
||||
},
|
||||
|
||||
bindWeights: function(owner, weights, closed, ref) {
|
||||
this.refs[ref].bindWeights(owner, weights, closed);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<section>
|
||||
@@ -254,6 +264,18 @@ var BoundingBox = React.createClass({
|
||||
value for <code>i</code> such that those <code>v[i-1]</code> don't have an array index that doesn't exist)
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
Open vs. closed paths
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
Much like poly-Beziers, B-Splines can be either open, running from the first point to the last point, or closed,
|
||||
where the first and last point are <em>the same point</em>. However, because B-Splines are an interpolation of
|
||||
curves, not just point, we can't simply make the first and last point the same, we need to link a few point point:
|
||||
for an order <code>d</code> B-Spline, we need to make the last <code>d</code> point the same as the first <code>d</code> points.
|
||||
And the easiest way to do this is to simply append <code>points.splice(0,d)</code> to <code>points</code>. Done!
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
Manipulating the curve through the knot vector
|
||||
</h2>
|
||||
@@ -284,7 +306,10 @@ var BoundingBox = React.createClass({
|
||||
which becomes normalised during interpolation and so does not contribute to the curvature.
|
||||
</p>
|
||||
|
||||
<p>SHOW A UNIFORM SPLINE HERE</p>
|
||||
<div className="two-column">
|
||||
<KnotController ref="uniform-spline" />
|
||||
<BSplineGraphic sketch={require('./uniform-bspline')} controller={(owner, knots) => this.bindKnots(owner, knots, "uniform-spline")}/>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
This is an important point: the intervals that the knot vector defines are <em>relative</em> intervals, so it
|
||||
@@ -308,7 +333,10 @@ var BoundingBox = React.createClass({
|
||||
collapsing <code>order</code> knots creates a situation where all continuity is lost and the curve "kinks".
|
||||
</p>
|
||||
|
||||
<p>SHOW A CENTER-CUT SPLINE HERE</p>
|
||||
<div className="two-column">
|
||||
<KnotController ref="center-cut-bspline" />
|
||||
<BSplineGraphic sketch={require('./center-cut-bspline')} controller={(owner, knots) => this.bindKnots(owner, knots, "center-cut-bspline")}/>
|
||||
</div>
|
||||
|
||||
<h3>Open-Uniform B-splines</h3>
|
||||
|
||||
@@ -325,7 +353,10 @@ var BoundingBox = React.createClass({
|
||||
"identical" knot vector [0,0,0,0,2,4,6,8,8,8,8], etc. Again, it is the relative differences that determine the curve shape.
|
||||
</p>
|
||||
|
||||
<p>SHOW AN OPEN-UNIFORM SPLINE HERE</p>
|
||||
<div className="two-column">
|
||||
<KnotController ref="open-uniform-bspline" />
|
||||
<BSplineGraphic sketch={require('./open-uniform-bspline')} controller={(owner, knots) => this.bindKnots(owner, knots, "open-uniform-bspline")}/>
|
||||
</div>
|
||||
|
||||
<h3>Non-uniform B-splines</h3>
|
||||
|
||||
@@ -343,7 +374,16 @@ var BoundingBox = React.createClass({
|
||||
point carries, the close to that point the spline curve will lie, a bit like turning up the gravity
|
||||
of a control point.</p>
|
||||
|
||||
<p>SHOW A WEIGHT-MANIPULABLE RATIONAL B-SPLINE HERE</p>
|
||||
<div className="two-column">
|
||||
{
|
||||
// <KnotController ref="rational-uniform-bspline" />
|
||||
}
|
||||
<WeightController ref="rational-uniform-bspline-weights" />
|
||||
<BSplineGraphic scrolling={true} sketch={require('./rational-uniform-bspline')} controller={(owner, knots, weights, closed) => {
|
||||
// this.bindKnots(owner, knots, "rational-uniform-bspline");
|
||||
this.bindWeights(owner, weights, closed, "rational-uniform-bspline-weights");
|
||||
}} />
|
||||
</div>
|
||||
|
||||
<p>Of course this brings us to the final topic that any text on B-splines must touch on before calling it
|
||||
a day: the NURBS, or Non-Uniform Rational B-Spline (NUBRS is not a plural, the capital S actually just stands
|
||||
@@ -357,8 +397,6 @@ var BoundingBox = React.createClass({
|
||||
as nicely, and so remember that when people talk about NURBS, they typically mean open-uniform, which
|
||||
has the useful property of starting the curve at the first control point, and ending it at the last.</p>
|
||||
|
||||
<p>SHOW A NURBS HERE</p>
|
||||
|
||||
<h2>Extending our implementation to cover rational splines</h2>
|
||||
|
||||
<p>
|
||||
|
@@ -24,8 +24,9 @@ module.exports = {
|
||||
var nt = n+k+1;
|
||||
var w2 = this.width/2;
|
||||
var h1 = this.height;
|
||||
var step = 0.1;
|
||||
var ti = [0,1,2,3,4,5,6];
|
||||
var step = 0.1, t = ti[0];
|
||||
var t = ti[0];
|
||||
var N = [[],[],[],[],[],[],[],[]];
|
||||
|
||||
var i1 = 0;
|
||||
@@ -52,10 +53,20 @@ module.exports = {
|
||||
t += step;
|
||||
}
|
||||
|
||||
var stw = this.width/6;
|
||||
var colors = [
|
||||
'#C00',
|
||||
'#CC0',
|
||||
'#0C0',
|
||||
'#0CC',
|
||||
'#00C',
|
||||
'#C0C'
|
||||
];
|
||||
|
||||
var stw = this.width/8;
|
||||
for (let j = 0; j < n1; j++) {
|
||||
t = ti[0];
|
||||
let to = t;
|
||||
this.stroke(colors[j]);
|
||||
for (let l = 1; l < w2; l++) {
|
||||
t += step;
|
||||
let t1 = t;
|
||||
@@ -68,5 +79,11 @@ module.exports = {
|
||||
to = t1;
|
||||
}
|
||||
}
|
||||
|
||||
this.stroke(0);
|
||||
this.fill(0);
|
||||
for(let j=0; j<n+k+1; j++) {
|
||||
this.circle(pad + j*stw, h1 - pad, 3);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
45
components/sections/bsplines/open-uniform-bspline.js
Normal file
45
components/sections/bsplines/open-uniform-bspline.js
Normal file
@@ -0,0 +1,45 @@
|
||||
module.exports = {
|
||||
degree: 3,
|
||||
activeDistance: 9,
|
||||
|
||||
setup() {
|
||||
this.size(400, 400);
|
||||
|
||||
var TAU = Math.PI*2;
|
||||
for (let i=0; i<TAU; i+=TAU/10) {
|
||||
this.points.push({
|
||||
x: this.width/2 + 100 * Math.cos(i),
|
||||
y: this.height/2 + 100 * Math.sin(i)
|
||||
});
|
||||
}
|
||||
|
||||
this.knots = this.formKnots(this.points, true);
|
||||
|
||||
if(this.props.controller) {
|
||||
this.props.controller(this, this.knots);
|
||||
}
|
||||
|
||||
this.draw();
|
||||
},
|
||||
|
||||
draw() {
|
||||
this.clear();
|
||||
this.grid(25);
|
||||
var p = this.points[0];
|
||||
this.points.forEach(n => {
|
||||
this.stroke(200);
|
||||
this.line(n.x, n.y, p.x, p.y);
|
||||
p = n;
|
||||
this.stroke(0);
|
||||
this.circle(p.x, p.y, 4);
|
||||
});
|
||||
this.drawSplineData();
|
||||
},
|
||||
|
||||
drawSplineData() {
|
||||
if (this.points.length <= this.degree) return;
|
||||
var mapped = this.points.map(p => [p.x, p.y]);
|
||||
this.drawCurve(mapped);
|
||||
this.drawKnots(mapped);
|
||||
}
|
||||
};
|
42
components/sections/bsplines/rational-non-uniform-bspline.js
Normal file
42
components/sections/bsplines/rational-non-uniform-bspline.js
Normal file
@@ -0,0 +1,42 @@
|
||||
module.exports = {
|
||||
degree: 3,
|
||||
activeDistance: 9,
|
||||
weights: [],
|
||||
|
||||
setup() {
|
||||
this.size(400, 400);
|
||||
|
||||
var TAU = Math.PI*2;
|
||||
for (let i=0; i<TAU; i+=TAU/10) {
|
||||
this.points.push({
|
||||
x: this.width/2 + 100 * Math.cos(i),
|
||||
y: this.height/2 + 100 * Math.sin(i)
|
||||
});
|
||||
}
|
||||
|
||||
this.knots = this.formKnots(this.points, true);
|
||||
this.weights = this.formWeights(this.points);
|
||||
this.draw();
|
||||
},
|
||||
|
||||
draw() {
|
||||
this.clear();
|
||||
this.grid(25);
|
||||
var p = this.points[0];
|
||||
this.points.forEach(n => {
|
||||
this.stroke(200);
|
||||
this.line(n.x, n.y, p.x, p.y);
|
||||
p = n;
|
||||
this.stroke(0);
|
||||
this.circle(p.x, p.y, 4);
|
||||
});
|
||||
this.drawSplineData();
|
||||
},
|
||||
|
||||
drawSplineData() {
|
||||
if (this.points.length <= this.degree) return;
|
||||
var mapped = this.points.map(p => [p.x, p.y]);
|
||||
this.drawCurve(mapped);
|
||||
this.drawKnots(mapped);
|
||||
}
|
||||
};
|
50
components/sections/bsplines/rational-uniform-bspline.js
Normal file
50
components/sections/bsplines/rational-uniform-bspline.js
Normal file
@@ -0,0 +1,50 @@
|
||||
module.exports = {
|
||||
degree: 3,
|
||||
activeDistance: 9,
|
||||
weights: [],
|
||||
|
||||
setup() {
|
||||
this.size(400, 400);
|
||||
|
||||
var TAU = Math.PI*2;
|
||||
var r = this.width/3;
|
||||
for (let i=0; i<6; i++) {
|
||||
this.points.push({
|
||||
x: this.width/2 + r * Math.cos(i/6 * TAU),
|
||||
y: this.height/2 + r * Math.sin(i/6 * TAU)
|
||||
});
|
||||
}
|
||||
this.points = this.points.concat(this.points.slice(0,3));
|
||||
this.closed = this.degree;
|
||||
|
||||
this.knots = this.formKnots(this.points);
|
||||
this.weights = this.formWeights(this.points);
|
||||
|
||||
if(this.props.controller) {
|
||||
this.props.controller(this, this.knots, this.weights, this.closed);
|
||||
}
|
||||
|
||||
this.draw();
|
||||
},
|
||||
|
||||
draw() {
|
||||
this.clear();
|
||||
this.grid(25);
|
||||
var p = this.points[0];
|
||||
this.points.forEach(n => {
|
||||
this.stroke(200);
|
||||
this.line(n.x, n.y, p.x, p.y);
|
||||
p = n;
|
||||
this.stroke(0);
|
||||
this.circle(p.x, p.y, 4);
|
||||
});
|
||||
this.drawSplineData();
|
||||
},
|
||||
|
||||
drawSplineData() {
|
||||
if (this.points.length <= this.degree) return;
|
||||
var mapped = this.points.map(p => [p.x, p.y]);
|
||||
this.drawCurve(mapped);
|
||||
this.drawKnots(mapped);
|
||||
}
|
||||
};
|
43
components/sections/bsplines/uniform-bspline.js
Normal file
43
components/sections/bsplines/uniform-bspline.js
Normal file
@@ -0,0 +1,43 @@
|
||||
module.exports = {
|
||||
degree: 3,
|
||||
activeDistance: 9,
|
||||
|
||||
setup() {
|
||||
this.size(400, 400);
|
||||
|
||||
var TAU = Math.PI*2;
|
||||
for (let i=0; i<TAU; i+=TAU/10) {
|
||||
this.points.push({
|
||||
x: this.width/2 + 100 * Math.cos(i),
|
||||
y: this.height/2 + 100 * Math.sin(i)
|
||||
});
|
||||
}
|
||||
|
||||
this.knots = this.formKnots(this.points);
|
||||
if(this.props.controller) {
|
||||
this.props.controller(this, this.knots);
|
||||
}
|
||||
this.draw();
|
||||
},
|
||||
|
||||
draw() {
|
||||
this.clear();
|
||||
this.grid(25);
|
||||
var p = this.points[0];
|
||||
this.points.forEach(n => {
|
||||
this.stroke(200);
|
||||
this.line(n.x, n.y, p.x, p.y);
|
||||
p = n;
|
||||
this.stroke(0);
|
||||
this.circle(p.x, p.y, 4);
|
||||
});
|
||||
this.drawSplineData();
|
||||
},
|
||||
|
||||
drawSplineData() {
|
||||
if (this.points.length <= this.degree) return;
|
||||
var mapped = this.points.map(p => [p.x, p.y]);
|
||||
this.drawCurve(mapped);
|
||||
this.drawKnots(mapped);
|
||||
}
|
||||
};
|
@@ -7,7 +7,7 @@
|
||||
"build:singles": "npm run less && webpack --prod --singles",
|
||||
"dev": "npm run less && webpack-dev-server --progress --colors --hot --inline",
|
||||
"latex": "node tools/mathjax",
|
||||
"less": "node ./node_modules/less/bin/lessc stylesheets/style.less > stylesheets/style.css",
|
||||
"less": "lessc stylesheets/style.less > stylesheets/style.css",
|
||||
"singles": "npm run dev -- --singles",
|
||||
"start": "npm run dev",
|
||||
"style": "webpack --jscs",
|
||||
|
@@ -1,3 +1,18 @@
|
||||
.bspline-graphic {
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.two-column {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
canvas {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-top: 0;
|
||||
padding-left: 2em;
|
||||
}
|
||||
}
|
@@ -320,6 +320,18 @@ footer {
|
||||
}
|
||||
.bspline-graphic {
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
}
|
||||
.two-column {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.two-column canvas {
|
||||
flex: none;
|
||||
}
|
||||
.two-column section {
|
||||
margin-top: 0;
|
||||
padding-left: 2em;
|
||||
}
|
||||
code {
|
||||
background: #DDE;
|
||||
|
Reference in New Issue
Block a user