mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-31 20:11:59 +02:00
bsplines
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
module.exports = {
|
||||
degree: 3,
|
||||
activeDistance: 9,
|
||||
|
||||
setup() {
|
||||
this.size(600, 300);
|
||||
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);
|
||||
}
|
||||
};
|
57
docs/chapters/bsplines/basic.js
Normal file
57
docs/chapters/bsplines/basic.js
Normal file
@@ -0,0 +1,57 @@
|
||||
let points=[];
|
||||
|
||||
setup() {
|
||||
points = [
|
||||
{x:25, y:160},
|
||||
{x:90, y:75},
|
||||
{x:190,y:245},
|
||||
{x:290,y:25},
|
||||
{x:400,y:255},
|
||||
{x:480,y:70},
|
||||
{x:560,y:170}
|
||||
];
|
||||
setMovable(points);
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
|
||||
setStroke(`lightgrey`);
|
||||
drawGrid(20);
|
||||
|
||||
setStroke(`#CC00CC99`);
|
||||
for (let i=0, e=points.length-1, p, n; i<e; i++) {
|
||||
p = points[i];
|
||||
n = points[i+1];
|
||||
line(p.x, p.y, n.x, n.y);
|
||||
}
|
||||
|
||||
setColor(`black`);
|
||||
points.forEach(p => circle(p.x, p.y, 3));
|
||||
|
||||
this.drawSplineData();
|
||||
}
|
||||
|
||||
drawSplineData() {
|
||||
// we'll need at least 4 points
|
||||
if (points.length <= 3) return;
|
||||
|
||||
let spline = new BSpline(this, points);
|
||||
|
||||
noFill();
|
||||
setStroke(`black`);
|
||||
start();
|
||||
spline.getLUT((points.length - 3) * 20).forEach(p => vertex(p.x, p.y));
|
||||
end();
|
||||
}
|
||||
|
||||
onMouseDown() {
|
||||
if (!this.currentPoint) {
|
||||
points.push({
|
||||
x: this.cursor.x,
|
||||
y: this.cursor.y
|
||||
});
|
||||
resetMovable(points);
|
||||
redraw();
|
||||
}
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
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);
|
||||
}
|
||||
};
|
@@ -4,9 +4,9 @@ No discussion on Bézier curves is complete without also giving mention of that
|
||||
|
||||
First off: B-Splines are [piecewise polynomial interpolation curves](https://en.wikipedia.org/wiki/Piecewise), where the "single curve" is built by performing polynomial interpolation over a set of points, using a sliding window of a fixed number of points. For instance, a "cubic" B-Spline defined by twelve points will have its curve built by evaluating the polynomial interpolation of four points, and the curve can be treated as a lot of different sections, each controlled by four points at a time, such that the full curve consists of smoothly connected sections defined by points {1,2,3,4}, {2,3,4,5}, ..., {8,9,10,11}, and finally {9,10,11,12}, for eight sections.
|
||||
|
||||
What do they look like? They look like this! .. okay that's an empty graph, but simply click to place some point, with the stipulation that you need at least four point to see any curve. More than four points simply draws a longer B-Spline curve:
|
||||
What do they look like? They look like this! Tap on the graphic to add more points, and move points around to see how they map to the spline curve drawn.
|
||||
|
||||
<BSplineGraphic sketch={this.basicSketch} />
|
||||
<graphics-element title="A B-Spline example" width="600" height="300" src="./basic.js"></graphics-element>
|
||||
|
||||
The important part to notice here is that we are **not** doing the same thing with B-Splines that we do for poly-Béziers or Catmull-Rom curves: both of the latter simply define new sections as literally "new sections based on new points", so a 12 point cubic poly-Bézier curve is actually impossible, because we start with a four point curve, and then add three more points for each section that follows, so we can only have 4, 7, 10, 13, 16, etc point Poly-Béziers. Similarly, while Catmull-Rom curves can grow by adding single points, this addition of a single point introduces three implicit Bézier points. Cubic B-Splines, on the other hand, are smooth interpolations of *each possible curve involving four consecutive points*, such that at any point along the curve except for our start and end points, our on-curve coordinate is defined by four control points.
|
||||
|
||||
@@ -17,6 +17,7 @@ Consider the difference to be this:
|
||||
|
||||
In order to make this interpolation of curves work, the maths is necessarily more complex than the maths for Bézier curves, so let's have a look at how things work.
|
||||
|
||||
|
||||
## How to compute a B-Spline curve: some maths
|
||||
|
||||
Given a B-Spline of degree `d` and thus order `k=d+1` (so a quadratic B-Spline is degree 2 and order 3, a cubic B-Spline is degree 3 and order 4, etc) and `n` control points `P<sub>0</sub>` through `P<sub>n-1</sub>`, we can compute a point on the curve for some value `t` in the interval [0,1] (where 0 is the start of the curve, and 1 the end, just like for Bézier curves), by evaluating the following function:
|
||||
@@ -47,6 +48,7 @@ So this is where we see the interpolation: N(t) for an (i,k) pair (that is, for
|
||||
|
||||
And this function finally has a straight up evaluation: if a `t` value lies within a knot-specific interval once we reach a `k=1` value, it "counts", otherwise it doesn't. We did cheat a little, though, because for all these values we need to scale our `t` value first, so that it lies in the interval bounded by `knots[d]` and `knots[n]`, which are the start point and end point where curvature is controlled by exactly `order` control points. For instance, for degree 3 (=order 4) and 7 control points, with knot vector [1,2,3,4,5,6,7,8,9,10,11], we map `t` from [the interval 0,1] to the interval [4,8], and then use that value in the functions above, instead.
|
||||
|
||||
|
||||
## Can we simplify that?
|
||||
|
||||
We can, yes.
|
||||
@@ -98,10 +100,13 @@ One thing we need to keep in mind is that we're working with a spline that is co
|
||||
|
||||
If we run this computation "down", starting at d(3,3), then without special code in place we would be computing quite a few terms multiple times at each step. On the other hand, we can also start with that last "column", we can generate the terminating d() values first, then compute the a() constants, perform our multiplications, generate the previous step's d() values, compute their a() constants, do the multiplications, etc. until we end up all the way back at the top. If we run our computation this way, we don't need any explicit caching, we can just "recycle" the list of numbers we start with and simply update them as we move up the triangle. So, let's implement that!
|
||||
|
||||
|
||||
## Cool, cool... but I don't know what to do with that information
|
||||
|
||||
I know, this is pretty mathy, so let's have a look at what happens when we change parameters here. We can't change the maths for the interpolation functions, so that gives us only one way to control what happens here: the knot vector itself. As such, let's look at the graph that shows the interpolation functions for a cubic B-Spline with seven points with a uniform knot vector (so we see seven identical functions), representing how much each point (represented by one function each) influences the total curvature, given our knot values. And, because exploration is the key to discovery, let's make the knot vector a thing we can actually manipulate. Normally a proper knot vector has a constraint that any value is strictly equal to, or larger than the previous ones, but screw it this is programming, let's ignore that hard restriction and just mess with the knots however we like.
|
||||
|
||||
<!-- THIS GRAPH IS EXTREMELY NOT-USEFUL, BUT WE'RE PORTING IT FIRST, AND REWRITING IT LATER -->
|
||||
|
||||
<div class="two-column">
|
||||
<KnotController ref="interpolation-graph" />
|
||||
<BSplineGraphic sketch={this.interpolationGraph} controller={(owner, knots) => this.bindKnots(owner, knots, "interpolation-graph")}/>
|
||||
@@ -111,6 +116,7 @@ Changing the values in the knot vector changes how much each point influences th
|
||||
|
||||
After reading the rest of this section you may want to come back here to try some specific knot vectors, and see if the resulting interpolation landscape makes sense given what you will now think should happen!
|
||||
|
||||
|
||||
## Running the computation
|
||||
|
||||
Unlike the de Casteljau algorithm, where the `t` value stays the same at every iteration, for B-Splines that is not the case, and so we end having to (for each point we evaluate) run a fairly involving bit of recursive computation. The algorithm is discussed on [this Michigan Tech](http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/de-Boor.html) page, but an easier to read version is implemented by [b-spline.js](https://github.com/thibauts/b-spline/blob/master/index.js#L59-L71), so we'll look at its code.
|
||||
@@ -140,12 +146,14 @@ for(let L = 1; L <= order; L++) {
|
||||
|
||||
(A nice bit of behaviour in this code is that we work the interpolation "backwards", starting at `i=s` at each level of the interpolation, and we stop when `i = s - order + level`, so we always end up with a value for `i` such that those `v[i-1]` don't try to use an array index that doesn't exist)
|
||||
|
||||
|
||||
## Open vs. closed paths
|
||||
|
||||
Much like poly-Béziers, B-Splines can be either open, running from the first point to the last point, or closed, where the first and last point are *the same point*. 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 `d` B-Spline, we need to make the last `d` point the same as the first `d` points. And the easiest way to do this is to simply append `points.splice(0,d)` to `points`. Done!
|
||||
|
||||
Of course if we want to manipulate these kind of curves we need to make sure to mark them as "closed" so that we know the coordinate for `points[0]` and `points[n-k]` etc. are the same coordinate, and manipulating one will equally manipulate the other, but programming generally makes this really easy by storing references to coordinates (or other linked values such as coordinate weights, discussed in the NURBS section) rather than separate coordinate objects.
|
||||
|
||||
|
||||
## Manipulating the curve through the knot vector
|
||||
|
||||
The most important thing to understand when it comes to B-Splines is that they work *because* of the concept of a knot vector. As mentioned above, knots represent "where individual control points start/stop influencing the curve", but we never looked at the *values* that go in the knot vector. If you look back at the N() and a() functions, you see that interpolations are based on intervals in the knot vector, rather than the actual values in the knot vector, and we can exploit this to do some pretty interesting things with clever manipulation of the knot vector. Specifically there are four things we can do that are worth looking at:
|
||||
@@ -155,27 +163,28 @@ The most important thing to understand when it comes to B-Splines is that they w
|
||||
3. we can collapse sequential knots to the same value, locally lowering curve complexity using "null" intervals, and
|
||||
4. we can form a special case non-uniform vector, by combining (1) and (3) to for a vector with collapsed start and end knots, with a uniform vector in between.
|
||||
|
||||
|
||||
### Uniform B-Splines
|
||||
|
||||
The most straightforward type of B-Spline is the uniform spline. In a uniform spline, the knots are distributed uniformly over the entire curve interval. For instance, if we have a knot vector of length twelve, then a uniform knot vector would be [0,1,2,3,...,9,10,11]. Or [4,5,6,...,13,14,15], which defines *the same intervals*, or even [0,2,3,...,18,20,22], which also defines *the same intervals*, just scaled by a constant factor, which becomes normalised during interpolation and so does not contribute to the curvature.
|
||||
|
||||
<div class="two-column">
|
||||
<KnotController ref="uniform-spline" />
|
||||
<BSplineGraphic sketch={this.uniformBSpline} controller={(owner, knots) => this.bindKnots(owner, knots, "uniform-spline")}/>
|
||||
</div>
|
||||
<graphics-element title="A uniform B-Spline" width="400" height="400" src="./uniform.js">
|
||||
<!-- knot sliders go here, similar to the curve fitter section -->
|
||||
</graphics-element>
|
||||
|
||||
This is an important point: the intervals that the knot vector defines are *relative* intervals, so it doesn't matter if every interval is size 1, or size 100 - the relative differences between the intervals is what shapes any particular curve.
|
||||
|
||||
The problem with uniform knot vectors is that, as we need `order` control points before we have any curve with which we can perform interpolation, the curve does not "start" at the first point, nor "ends" at the last point. Instead there are "gaps". We can get rid of these, by being clever about how we apply the following uniformity-breaking approach instead...
|
||||
|
||||
|
||||
### Reducing local curve complexity by collapsing intervals
|
||||
|
||||
By collapsing knot intervals by making two or more consecutive knots have the same value, we can reduce the curve complexity in the sections that are affected by the knots involved. This can have drastic effects: for every interval collapse, the curve order goes down, and curve continuity goes down, to the point where collapsing `order` knots creates a situation where all continuity is lost and the curve "kinks".
|
||||
Collapsing knot intervals, by making two or more consecutive knots have the same value, allows us to reduce the curve complexity in the sections that are affected by the knots involved. This can have drastic effects: for every interval collapse, the curve order goes down, and curve continuity goes down, to the point where collapsing `order` knots creates a situation where all continuity is lost and the curve "kinks".
|
||||
|
||||
<graphics-element title="A reduced uniform B-Spline" width="400" height="400" src="./reduced.js">
|
||||
<!-- knot sliders go here, similar to the curve fitter section -->
|
||||
</graphics-element>
|
||||
|
||||
<div class="two-column">
|
||||
<KnotController ref="center-cut-bspline" />
|
||||
<BSplineGraphic sketch={this.centerCutBSpline} controller={(owner, knots) => this.bindKnots(owner, knots, "center-cut-bspline")}/>
|
||||
</div>
|
||||
|
||||
### Open-Uniform B-Splines
|
||||
|
||||
@@ -183,31 +192,24 @@ By combining knot interval collapsing at the start and end of the curve, with un
|
||||
|
||||
For any curve of degree `D` with control points `N`, we can define a knot vector of length `N+D+1` in which the values `0 ... D+1` are the same, the values `D+1 ... N+1` follow the "uniform" pattern, and the values `N+1 ... N+D+1` are the same again. For example, a cubic B-Spline with 7 control points can have a knot vector [0,0,0,0,1,2,3,4,4,4,4], or it might have the "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.
|
||||
|
||||
<div class="two-column">
|
||||
<KnotController ref="open-uniform-bspline" />
|
||||
<BSplineGraphic sketch={this.openUniformBSpline} controller={(owner, knots) => this.bindKnots(owner, knots, "open-uniform-bspline")}/>
|
||||
</div>
|
||||
<graphics-element title="An open, uniform B-Spline" width="400" height="400" src="./uniform.js" data-open="true">
|
||||
<!-- knot sliders go here, similar to the curve fitter section -->
|
||||
</graphics-element>
|
||||
|
||||
|
||||
### Non-uniform B-Splines
|
||||
|
||||
This is essentially the "free form" version of a B-Spline, and also the least interesting to look at, as without any specific reason to pick specific knot intervals, there is nothing particularly interesting going on. There is one constraint to the knot vector, and that is that any value `knots[k+1]` should be equal to, or greater than `knots[k]`.
|
||||
This is essentially the "free form" version of a B-Spline, and also the least interesting to look at, as without any specific reason to pick specific knot intervals, there is nothing particularly interesting going on. There is one constraint to the knot vector, other than that any value `knots[k+1]` should be greater than or equal to `knots[k]`.
|
||||
|
||||
## One last thing: Rational B-Splines
|
||||
|
||||
While it is true that this section on B-Splines is running quite long already, there is one more thing we need to talk about, and that's "Rational" splines, where the rationality applies to the "ratio", or relative weights, of the control points themselves. By introducing a ratio vector with weights to apply to each control point, we greatly increase our influence over the final curve shape: the more weight a control point carries, the close to that point the spline curve will lie, a bit like turning up the gravity of a control point.
|
||||
|
||||
<div class="two-column">
|
||||
{
|
||||
// <KnotController ref="rational-uniform-bspline" />
|
||||
}
|
||||
<WeightController ref="rational-uniform-bspline-weights" />
|
||||
<BSplineGraphic scrolling={true} sketch={this.rationalUniformBSpline} controller={(owner, knots, weights, closed) => {
|
||||
// this.bindKnots(owner, knots, "rational-uniform-bspline");
|
||||
this.bindWeights(owner, weights, closed, "rational-uniform-bspline-weights");
|
||||
}} />
|
||||
</div>
|
||||
<graphics-element title="An rational, uniform B-Spline" width="400" height="400" src="rational-uniform.js">
|
||||
<!-- knot sliders go here, similar to the curve fitter section -->
|
||||
</graphics-element>
|
||||
|
||||
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 (NURBS is not a plural, the capital S actually just stands for "spline", but a lot of people mistakenly treat it as if it is, so now you know better). NURBS are an important type of curve in computer-facilitated design, used a lot in 3D modelling (as NURBS surfaces) as well as in arbitrary-precision 2D design due to the level of control a NURBS curve offers designers.
|
||||
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](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline), or Non-Uniform Rational B-Spline (NURBS is not a plural, the capital S actually just stands for "spline", but a lot of people mistakenly treat it as if it is, so now you know better). NURBS is an important type of curve in computer-facilitated design, used a lot in 3D modelling (typically as NURBS surfaces) as well as in arbitrary-precision 2D design due to the level of control a NURBS curve offers designers.
|
||||
|
||||
While a true non-uniform rational B-Spline would be hard to work with, when we talk about NURBS we typically mean the Open-Uniform Rational B-Spline, or OURBS, but that doesn't roll off the tongue nearly 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.
|
||||
|
||||
|
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
basicSketch: require('./basic-sketch'),
|
||||
interpolationGraph: require('./interpolation-graph'),
|
||||
uniformBSpline: require('./uniform-bspline'),
|
||||
centerCutBSpline: require('./center-cut-bspline'),
|
||||
openUniformBSpline: require('./open-uniform-bspline'),
|
||||
rationalUniformBSpline: require('./rational-uniform-bspline'),
|
||||
|
||||
bindKnots: function(owner, knots, ref) {
|
||||
this.refs[ref].bindKnots(owner, knots);
|
||||
},
|
||||
|
||||
bindWeights: function(owner, weights, closed, ref) {
|
||||
this.refs[ref].bindWeights(owner, weights, closed);
|
||||
}
|
||||
};
|
@@ -1,45 +0,0 @@
|
||||
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);
|
||||
}
|
||||
};
|
@@ -1,50 +0,0 @@
|
||||
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
docs/chapters/bsplines/rational-uniform.js
Normal file
43
docs/chapters/bsplines/rational-uniform.js
Normal file
@@ -0,0 +1,43 @@
|
||||
let points=[];
|
||||
|
||||
setup() {
|
||||
var r = this.width/3;
|
||||
for (let i=0; i<6; i++) {
|
||||
points.push({
|
||||
x: this.width/2 + r * Math.cos(i/6 * TAU),
|
||||
y: this.height/2 + r * Math.sin(i/6 * TAU)
|
||||
});
|
||||
}
|
||||
points = points.concat(points.slice(0,3));
|
||||
setMovable(points);
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
|
||||
setStroke(`lightgrey`);
|
||||
drawGrid(20);
|
||||
|
||||
setStroke(`#CC00CC99`);
|
||||
for (let i=0, e=points.length-1, p, n; i<e; i++) {
|
||||
p = points[i];
|
||||
n = points[i+1];
|
||||
line(p.x, p.y, n.x, n.y);
|
||||
}
|
||||
|
||||
setColor(`black`);
|
||||
points.forEach(p => circle(p.x, p.y, 3));
|
||||
|
||||
this.drawSplineData();
|
||||
}
|
||||
|
||||
drawSplineData() {
|
||||
const spline = new BSpline(this, points, !!this.parameters.open);
|
||||
spline.formWeights();
|
||||
|
||||
noFill();
|
||||
setStroke(`black`);
|
||||
start();
|
||||
spline.getLUT((points.length - 3) * 20).forEach(p => vertex(p.x, p.y));
|
||||
end();
|
||||
}
|
49
docs/chapters/bsplines/reduced.js
Normal file
49
docs/chapters/bsplines/reduced.js
Normal file
@@ -0,0 +1,49 @@
|
||||
let points=[], knots;
|
||||
|
||||
setup() {
|
||||
for (let s=TAU/9, i=s/2; i<TAU; i+=s) {
|
||||
points.push({
|
||||
x: this.width/2 + 100 * Math.cos(i),
|
||||
y: this.height/2 + 100 * Math.sin(i)
|
||||
});
|
||||
}
|
||||
setMovable(points);
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
|
||||
setStroke(`lightgrey`);
|
||||
drawGrid(20);
|
||||
|
||||
setStroke(`#CC00CC99`);
|
||||
for (let i=0, e=points.length-1, p, n; i<e; i++) {
|
||||
p = points[i];
|
||||
n = points[i+1];
|
||||
line(p.x, p.y, n.x, n.y);
|
||||
}
|
||||
|
||||
setColor(`black`);
|
||||
points.forEach(p => circle(p.x, p.y, 3));
|
||||
|
||||
this.drawSplineData();
|
||||
}
|
||||
|
||||
drawSplineData() {
|
||||
const spline = new BSpline(this, points, !!this.parameters.open);
|
||||
|
||||
const knots = spline.formKnots();
|
||||
const m = round(points.length/2)|0;
|
||||
knots[m+0] = knots[m];
|
||||
knots[m+1] = knots[m];
|
||||
knots[m+2] = knots[m];
|
||||
for (let i=m+3; i<knots.length; i++) {
|
||||
knots[i] = knots[i-1] + 1;
|
||||
}
|
||||
|
||||
noFill();
|
||||
setStroke(`black`);
|
||||
start();
|
||||
spline.getLUT((points.length - 3) * 20).forEach(p => vertex(p.x, p.y));
|
||||
end();
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
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);
|
||||
}
|
||||
};
|
41
docs/chapters/bsplines/uniform.js
Normal file
41
docs/chapters/bsplines/uniform.js
Normal file
@@ -0,0 +1,41 @@
|
||||
let points=[];
|
||||
|
||||
setup() {
|
||||
for (let s=TAU/9, i=s/2; i<TAU; i+=s) {
|
||||
points.push({
|
||||
x: this.width/2 + 100 * Math.cos(i),
|
||||
y: this.height/2 + 100 * Math.sin(i)
|
||||
});
|
||||
}
|
||||
setMovable(points);
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
|
||||
setStroke(`lightgrey`);
|
||||
drawGrid(20);
|
||||
|
||||
setStroke(`#CC00CC99`);
|
||||
for (let i=0, e=points.length-1, p, n; i<e; i++) {
|
||||
p = points[i];
|
||||
n = points[i+1];
|
||||
line(p.x, p.y, n.x, n.y);
|
||||
}
|
||||
|
||||
setColor(`black`);
|
||||
points.forEach(p => circle(p.x, p.y, 3));
|
||||
|
||||
this.drawSplineData();
|
||||
}
|
||||
|
||||
drawSplineData() {
|
||||
const spline = new BSpline(this, points);
|
||||
spline.formKnots(!!this.parameters.open);
|
||||
|
||||
noFill();
|
||||
setStroke(`black`);
|
||||
start();
|
||||
spline.getLUT((points.length - 3) * 20).forEach(p => vertex(p.x, p.y));
|
||||
end();
|
||||
}
|
Reference in New Issue
Block a user