Skip to content

Commit

Permalink
feat: toBezierCurves, toQuadraticCurves 5275aa4
Browse files Browse the repository at this point in the history
  • Loading branch information
plantain-00 committed Oct 24, 2023
1 parent 344506c commit 3682871
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 12 deletions.
17 changes: 15 additions & 2 deletions dev/cad-editor/plugins/nurbs.plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,25 @@ export function getModel(ctx: PluginContext): model.Model<NurbsContent>[] {
return geometriesCache.get(content, () => {
let points: core.Position[]
const nurbsSegmentCount = content.segmentCount ?? ctx.defaultSegmentCount
let lines: core.GeometryLine[]
if (content.points.length > 2) {
points = ctx.getNurbsPoints(content.degree, content.points, content.knots, content.weights, nurbsSegmentCount)
if (!content.weights && !content.knots && (content.degree === 2 || content.points.length === 3)) {
lines = ctx.getQuadraticSplineCurves(content.points).map(c => ({ type: 'quadratic curve' as const, curve: c }))
points = ctx.getGeometryLinesPoints(lines, nurbsSegmentCount)
} else if (!content.weights && !content.knots && content.degree === 3) {
lines = ctx.getBezierSplineCurves(content.points, false).map(c => ({ type: 'bezier curve' as const, curve: c }))
points = ctx.getGeometryLinesPoints(lines, nurbsSegmentCount)
} else if (!content.weights && !content.knots && content.degree === 1) {
points = content.points
lines = Array.from(ctx.iteratePolylineLines(points))
} else {
points = ctx.getNurbsPoints(content.degree, content.points, content.knots, content.weights, nurbsSegmentCount)
lines = Array.from(ctx.iteratePolylineLines(points))
}
} else {
points = content.points
lines = Array.from(ctx.iteratePolylineLines(points))
}
const lines = Array.from(ctx.iteratePolylineLines(points))
return {
lines,
points,
Expand Down
4 changes: 3 additions & 1 deletion dev/cad-editor/plugins/spline.plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ export function getModel(ctx: PluginContext): model.Model<SplineContent | Spline
if (content.points.length > 2) {
if (content.fitting) {
lines = ctx.getBezierSplineCurves(content.points).map(c => ({ type: 'bezier curve' as const, curve: c }))
} else {
} else if (content.points.length === 3) {
lines = ctx.getQuadraticSplineCurves(content.points).map(c => ({ type: 'quadratic curve' as const, curve: c }))
} else {
lines = ctx.getBezierSplineCurves(content.points, false).map(c => ({ type: 'bezier curve' as const, curve: c }))
}
points = ctx.getGeometryLinesPoints(lines, splineSegmentCount)
} else {
Expand Down
21 changes: 18 additions & 3 deletions dev/cad-editor/plugins/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5398,12 +5398,25 @@ function getModel(ctx) {
var _a;
let points;
const nurbsSegmentCount = (_a = content.segmentCount) != null ? _a : ctx.defaultSegmentCount;
let lines;
if (content.points.length > 2) {
points = ctx.getNurbsPoints(content.degree, content.points, content.knots, content.weights, nurbsSegmentCount);
if (!content.weights && !content.knots && (content.degree === 2 || content.points.length === 3)) {
lines = ctx.getQuadraticSplineCurves(content.points).map((c) => ({ type: "quadratic curve", curve: c }));
points = ctx.getGeometryLinesPoints(lines, nurbsSegmentCount);
} else if (!content.weights && !content.knots && content.degree === 3) {
lines = ctx.getBezierSplineCurves(content.points, false).map((c) => ({ type: "bezier curve", curve: c }));
points = ctx.getGeometryLinesPoints(lines, nurbsSegmentCount);
} else if (!content.weights && !content.knots && content.degree === 1) {
points = content.points;
lines = Array.from(ctx.iteratePolylineLines(points));
} else {
points = ctx.getNurbsPoints(content.degree, content.points, content.knots, content.weights, nurbsSegmentCount);
lines = Array.from(ctx.iteratePolylineLines(points));
}
} else {
points = content.points;
lines = Array.from(ctx.iteratePolylineLines(points));
}
const lines = Array.from(ctx.iteratePolylineLines(points));
return {
lines,
points,
Expand Down Expand Up @@ -8393,8 +8406,10 @@ function getModel(ctx) {
if (content.points.length > 2) {
if (content.fitting) {
lines = ctx.getBezierSplineCurves(content.points).map((c) => ({ type: "bezier curve", curve: c }));
} else {
} else if (content.points.length === 3) {
lines = ctx.getQuadraticSplineCurves(content.points).map((c) => ({ type: "quadratic curve", curve: c }));
} else {
lines = ctx.getBezierSplineCurves(content.points, false).map((c) => ({ type: "bezier curve", curve: c }));
}
points = ctx.getGeometryLinesPoints(lines, splineSegmentCount);
} else {
Expand Down
2 changes: 1 addition & 1 deletion main.bundle.js

Large diffs are not rendered by default.

71 changes: 70 additions & 1 deletion spec/nurbs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'ava'
import { interpolateNurbs2 } from '../src'
import { toQuadraticCurves, toBezierCurves, equals, interpolate3, interpolate4, interpolateNurbs, interpolateNurbs2 } from '../src'

test('interpolateNurbs2', (t) => {
const tvalues = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
Expand Down Expand Up @@ -36,4 +36,73 @@ test('interpolateNurbs2', (t) => {
t.snapshot(tvalues.map(t => interpolateNurbs2(t, 2, points2, knots, [1, w, 1, w, 1, w, 1, w, 1])))
const boosted = 4 * w
t.snapshot(tvalues.map(t => interpolateNurbs2(t, 2, points2, knots, [1, boosted, 1, boosted, 1, boosted, 1, boosted, 1])))
})

test('toBezierCurves', (t) => {
const interpolate = (t: number, x: number[]) => {
const a = t * (x.length - 3)
const b = Math.floor(a)
const { from, cp1, cp2, to } = toBezierCurves(x, b + 1)
return interpolate4(
from,
cp1,
cp2,
to,
a - b,
)
}
const count = 32
let p = [1, 2, 3, 4]
for (let i = 0; i < count; i++) {
t.true(equals(interpolate(i / count, p), interpolateNurbs(i / count, 3, p)))
}

p = [1, 2, 3, 4, 5]
for (let i = 0; i < count; i++) {
t.true(equals(interpolate(i / count, p), interpolateNurbs(i / count, 3, p)))
}

p = [1, 2, 3, 4, 5, 6]
for (let i = 0; i < count; i++) {
t.true(equals(interpolate(i / count, p), interpolateNurbs(i / count, 3, p)))
}

p = [1, 2, 3, 4, 5, 7]
for (let i = 0; i < count; i++) {
t.true(equals(interpolate(i / count, p), interpolateNurbs(i / count, 3, p)))
}
})

test('toQuadraticCurves', (t) => {
const interpolate = (t: number, x: number[]) => {
const a = t * (x.length - 2)
const b = Math.floor(a)
const { from, cp, to } = toQuadraticCurves(x, b + 1)
return interpolate3(
from,
cp,
to,
a - b,
)
}
const count = 32
let p = [1, 2, 3]
for (let i = 0; i < count; i++) {
t.true(equals(interpolate(i / count, p), interpolateNurbs(i / count, 2, p)))
}

p = [1, 2, 3, 4]
for (let i = 0; i < count; i++) {
t.true(equals(interpolate(i / count, p), interpolateNurbs(i / count, 2, p)))
}

p = [1, 2, 3, 4, 5]
for (let i = 0; i < count; i++) {
t.true(equals(interpolate(i / count, p), interpolateNurbs(i / count, 2, p)))
}

p = [1, 2, 3, 4, 5, 6]
for (let i = 0; i < count; i++) {
t.true(equals(interpolate(i / count, p), interpolateNurbs(i / count, 2, p)))
}
})
24 changes: 20 additions & 4 deletions src/utils/bezier.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { calculateEquation2, calculateEquation3 } from "./equation-calculater"
import { Position, getTwoPointCenter, isZero } from "./geometry"
import { BezierCurve, QuadraticCurve } from "./intersection"
import { toBezierCurves } from "./nurbs"
import { Vec3 } from "./types"

function interpolate2(n1: number, n2: number, percent: number) {
export function interpolate2(n1: number, n2: number, percent: number) {
return n1 + (n2 - n1) * percent
}

function interpolate3(n1: number, n2: number, n3: number, percent: number) {
export function interpolate3(n1: number, n2: number, n3: number, percent: number) {
return interpolate2(
interpolate2(n1, n2, percent),
interpolate2(n2, n3, percent),
percent,
)
}

function interpolate4(n1: number, n2: number, n3: number, n4: number, percent: number) {
export function interpolate4(n1: number, n2: number, n3: number, n4: number, percent: number) {
return interpolate3(
interpolate2(n1, n2, percent),
interpolate2(n2, n3, percent),
Expand Down Expand Up @@ -103,8 +104,23 @@ export function getBezierCurvePoints3D(p1: Vec3, p2: Vec3, p3: Vec3, p4: Vec3, s
return points
}

export function getBezierSplineCurves(points: Position[]) {
export function getBezierSplineCurves(points: Position[], fitting = true) {
const result: BezierCurve[] = []
if (!fitting) {
const x = points.map(p => p.x)
const y = points.map(p => p.y)
for (let i = 1; i < points.length - 2; i++) {
const sx = toBezierCurves(x, i)
const sy = toBezierCurves(y, i)
result.push({
from: { x: sx.from, y: sy.from },
cp1: { x: sx.cp1, y: sy.cp1 },
cp2: { x: sx.cp2, y: sy.cp2 },
to: { x: sx.to, y: sy.to },
})
}
return result
}
const cps = getBezierSplineControlPointsOfPoints(points)
cps.forEach((p, i) => {
result.push({
Expand Down
81 changes: 81 additions & 0 deletions src/utils/nurbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,87 @@ export function interpolateNurbs(
return result / weight
}

export function toBezierCurves(x: number[], i: number) {
if (i === 1) {
if (x.length === 4) {
return {
from: x[0],
cp1: x[1],
cp2: x[2],
to: x[3],
}
}
if (x.length === 5) {
return {
from: x[0],
cp1: x[1],
cp2: x[1] / 2 + x[2] / 2,
to: x[1] / 4 + x[2] / 2 + x[3] / 4,
}
}
return {
from: x[0],
cp1: x[1],
cp2: 0.5 * x[2] + 0.5 * x[1],
to: x[3] / 6 + 7 / 12 * x[2] + x[1] / 4,
}
}
if (i === 2) {
if (x.length === 5) {
return {
from: 0.25 * x[1] + 0.25 * x[3] + 0.5 * x[2],
cp1: 0.5 * x[2] + 0.5 * x[3],
cp2: x[3],
to: x[4],
}
}
if (x.length === 6) {
return {
from: 7 / 12 * x[2] + x[3] / 6 + 0.25 * x[1],
cp1: 2 / 3 * x[2] + x[3] / 3,
cp2: x[2] / 3 + 2 / 3 * x[3],
to: x[2] / 6 + 7 / 12 * x[3] + x[4] / 4,
}
}
return {
from: x[1] / 4 + x[3] / 6 + 7 / 12 * x[2],
cp1: x[3] / 3 + 2 * x[2] / 3,
cp2: 2 / 3 * x[3] + x[2] / 3,
to: x[4] / 6 + 2 / 3 * x[3] + x[2] / 6,
}
}
if (i === x.length - 4) {
return {
from: x[x.length - 5] / 6 + 2 / 3 * x[x.length - 4] + x[x.length - 3] / 6,
cp1: x[x.length - 3] / 3 + 2 / 3 * x[x.length - 4],
cp2: 2 / 3 * x[x.length - 3] + x[x.length - 4] / 3,
to: x[x.length - 2] / 4 + 7 / 12 * x[x.length - 3] + x[x.length - 4] / 6,
}
}
if (i === x.length - 3) {
return {
from: x[x.length - 4] / 6 + 7 / 12 * x[x.length - 3] + x[x.length - 2] / 4,
cp1: x[x.length - 2] / 2 + x[x.length - 3] / 2,
cp2: x[x.length - 2],
to: x[x.length - 1],
}
}
return {
from: x[i + 1] / 6 + 2 / 3 * x[i] + x[i - 1] / 6,
cp1: x[i + 1] / 3 + 2 * x[i] / 3,
cp2: 2 * x[i + 1] / 3 + x[i] / 3,
to: x[i + 2] / 6 + 2 / 3 * x[i + 1] + x[i] / 6,
}
}

export function toQuadraticCurves(x: number[], i: number) {
return {
from: i === 1 ? x[0] : (x[i - 1] + x[i]) / 2,
cp: x[i],
to: i === x.length - 2 ? x[x.length - 1] : (x[i] + x[i + 1]) / 2,
}
}

export function interpolateNurbs2(t: number, degree: number, points: number[][], knots = getDefaultNurbsKnots(points.length, degree), weights?: number[]) {
return points[0].map((_, i) => interpolateNurbs(t, degree, points.map(p => p[i]), knots, weights))
}
Expand Down

0 comments on commit 3682871

Please sign in to comment.