mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-25 17:42:46 +02:00
bsplines
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { enrich } from "../lib/enrich.js";
|
||||
import { Bezier } from "./types/bezier.js";
|
||||
import { BSpline } from "./types/bspline.js";
|
||||
import { Vector } from "./types/vector.js";
|
||||
import { Matrix } from "./types/matrix.js";
|
||||
import { Shape } from "./util/shape.js";
|
||||
@@ -791,4 +792,4 @@ class GraphicsAPI extends BaseAPI {
|
||||
}
|
||||
}
|
||||
|
||||
export { GraphicsAPI, Bezier, Vector, Matrix, Shape };
|
||||
export { GraphicsAPI, Bezier, BSpline, Vector, Matrix, Shape };
|
||||
|
85
docs/js/custom-element/api/types/bspline.js
Normal file
85
docs/js/custom-element/api/types/bspline.js
Normal file
@@ -0,0 +1,85 @@
|
||||
import interpolate from "../util/spline.js";
|
||||
|
||||
// cubic B-Spline
|
||||
const DEGREE = 3;
|
||||
|
||||
class BSpline {
|
||||
constructor(apiInstance, points) {
|
||||
this.api = apiInstance;
|
||||
this.ctx = apiInstance.ctx;
|
||||
|
||||
// the spline library needs points in array format [x,y] rather than object format {x:..., y:...}
|
||||
this.points = points.map((v) => {
|
||||
if (v instanceof Array) return v;
|
||||
return [v.x, v.y];
|
||||
});
|
||||
}
|
||||
|
||||
getLUT(count) {
|
||||
let c = count - 1;
|
||||
return [...new Array(count)].map((_, i) => {
|
||||
let p = interpolate(i / c, DEGREE, this.points, this.knots, this.weights);
|
||||
return { x: p[0], y: p[1] };
|
||||
});
|
||||
}
|
||||
|
||||
formKnots(open = false) {
|
||||
if (!open) return this.formUniformKnots();
|
||||
|
||||
let knots = [],
|
||||
l = this.points.length,
|
||||
m = l - DEGREE;
|
||||
|
||||
// form the open-uniform knot vector
|
||||
for (let i = 1; i < l - DEGREE; i++) {
|
||||
knots.push(i + DEGREE);
|
||||
}
|
||||
// add [degree] zeroes at the front
|
||||
for (let i = 0; i <= DEGREE; i++) {
|
||||
knots = [DEGREE].concat(knots);
|
||||
}
|
||||
// add [degree] max-values to the back
|
||||
for (let i = 0; i <= DEGREE; i++) {
|
||||
knots.push(m + DEGREE);
|
||||
}
|
||||
|
||||
return (this.knots = knots);
|
||||
}
|
||||
|
||||
formUniformKnots() {
|
||||
return (this.knots = [...new Array(this.points.length + DEGREE + 1)].map(
|
||||
(_, i) => i
|
||||
));
|
||||
}
|
||||
|
||||
formNodes() {
|
||||
const knots = this.knots;
|
||||
const domain = [DEGREE, knots.length - 1 - DEGREE],
|
||||
nodes = [];
|
||||
|
||||
for (let k = 0; k < this.points.length; k++) {
|
||||
let node = 0;
|
||||
for (let offset = 1; offset <= DEGREE; offset++) {
|
||||
node += knots[k + offset];
|
||||
}
|
||||
node /= DEGREE;
|
||||
if (node < knots[domain[0]]) continue;
|
||||
if (node > knots[domain[1]]) continue;
|
||||
nodes.push(node);
|
||||
}
|
||||
|
||||
return (this.nodes = nodes);
|
||||
}
|
||||
|
||||
formWeights() {
|
||||
return (this.weights = this.points.map((p) => 1));
|
||||
}
|
||||
|
||||
setDegree(d) {
|
||||
DEGREE += d;
|
||||
this.knots = this.formKnots();
|
||||
this.nodes = this.formNodes();
|
||||
}
|
||||
}
|
||||
|
||||
export { BSpline };
|
88
docs/js/custom-element/api/util/spline.js
Normal file
88
docs/js/custom-element/api/util/spline.js
Normal file
@@ -0,0 +1,88 @@
|
||||
// https://github.com/thibauts/b-spline
|
||||
export default function interpolate(
|
||||
t,
|
||||
degree,
|
||||
points,
|
||||
knots,
|
||||
weights,
|
||||
result,
|
||||
scaled
|
||||
) {
|
||||
var i, j, s, l; // function-scoped iteration variables
|
||||
var n = points.length; // points count
|
||||
var d = points[0].length; // point dimensionality
|
||||
|
||||
if (degree < 1) throw new Error("degree must be at least 1 (linear)");
|
||||
if (degree > n - 1)
|
||||
throw new Error("degree must be less than or equal to point count - 1");
|
||||
|
||||
if (!weights) {
|
||||
// build weight vector of length [n]
|
||||
weights = [];
|
||||
for (i = 0; i < n; i++) {
|
||||
weights[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!knots) {
|
||||
// build knot vector of length [n + degree + 1]
|
||||
var knots = [];
|
||||
for (i = 0; i < n + degree + 1; i++) {
|
||||
knots[i] = i;
|
||||
}
|
||||
} else {
|
||||
if (knots.length !== n + degree + 1)
|
||||
throw new Error("bad knot vector length");
|
||||
}
|
||||
|
||||
var domain = [degree, knots.length - 1 - degree];
|
||||
|
||||
var low = knots[domain[0]];
|
||||
var high = knots[domain[1]];
|
||||
|
||||
// remap t to the domain where the spline is defined
|
||||
if (!scaled) {
|
||||
t = t * (high - low) + low;
|
||||
}
|
||||
|
||||
if (t < low || t > high) throw new Error("out of bounds");
|
||||
|
||||
// find s (the spline segment) for the [t] value provided
|
||||
for (s = domain[0]; s < domain[1]; s++) {
|
||||
if (t >= knots[s] && t <= knots[s + 1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// convert points to homogeneous coordinates
|
||||
var v = [];
|
||||
for (i = 0; i < n; i++) {
|
||||
v[i] = [];
|
||||
for (j = 0; j < d; j++) {
|
||||
v[i][j] = points[i][j] * weights[i];
|
||||
}
|
||||
v[i][d] = weights[i];
|
||||
}
|
||||
|
||||
// l (level) goes from 1 to the curve degree + 1
|
||||
var alpha;
|
||||
for (l = 1; l <= degree + 1; l++) {
|
||||
// build level l of the pyramid
|
||||
for (i = s; i > s - degree - 1 + l; i--) {
|
||||
alpha = (t - knots[i]) / (knots[i + degree + 1 - l] - knots[i]);
|
||||
|
||||
// interpolate each component
|
||||
for (j = 0; j < d + 1; j++) {
|
||||
v[i][j] = (1 - alpha) * v[i - 1][j] + alpha * v[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert back to cartesian and return
|
||||
var result = result || [];
|
||||
for (i = 0; i < d; i++) {
|
||||
result[i] = v[s][i] / v[s][d];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@@ -207,7 +207,7 @@ class GraphicsElement extends CustomElement {
|
||||
* Program source: ${src}
|
||||
* Data attributes: ${JSON.stringify(this.dataset)}
|
||||
*/
|
||||
import { GraphicsAPI, Bezier, Vector, Matrix, Shape } from "${MODULE_PATH}/api/graphics-api.js";
|
||||
import { GraphicsAPI, Bezier, BSpline, Vector, Matrix, Shape } from "${MODULE_PATH}/api/graphics-api.js";
|
||||
|
||||
${globalCode}
|
||||
|
||||
|
Reference in New Issue
Block a user