德布尔方法构造NURBS曲线
几何画板及绘图内容加载中,请稍等……
本范例演示了如何利用德布尔(de Boor)算法构造非均匀有理B样条(NURBS)曲线,展示了一条由多个控制点、权重和节点向量定义的平滑参数曲线。核心数学概念包括B样条基函数、有理曲线、参数化、节点向量以及德布尔递推算法,涉及矩阵运算和相似变换。作图要点:使用GeoGebra命令First、Join、Sequence、Min、Zip、PolyLine、Element、If、Segment、Translate、Text、Polygon等,构建了控制点P0至P9、权重w0至w9、节点参数t0及中间点d1至d10,通过滑块n、k、m控制曲线阶数与细分程度,并利用AzathothColour等对象实现动态可视化。适合高中至大学阶段的数学或计算机图形学课程,用于理解NURBS曲线的几何构造与算法实现。
GeoGebra 命令及参数
First({P_{0}, P_{1}, P_{2}, P_{3}, P_{4}, P_{5}, P_{6}, P_{7}, P_{8}, P_{9}}, n + 1) → PFirst({w_{0}, w_{1}, w_{2}, w_{3}, w_{4}, w_{5}, w_{6}, w_{7}, w_{8}, w_{9}}, n + 1) → weightsFirst({d_{1}, d_{2}, d_{3}, d_{4}, d_{5}, d_{6}, d_{7}, d_{8}, d_{9}, d_{10}, d_{11}, d_{12}, d_{13}, d_{14}, d_{15}}, m) → Δknots_{input}Join({Sequence[0, j, 1, k - 1], If[Sum[Take[Δknots_{input}, k, m + 1 - k]] > 0, Take[Δknots_{input}, k, m + 1 - k], Join[{If[m - k ≥ k, Take[Δknots_{input}, k, m - k], {}], {1}}]], Sequence[0, j, 1, k - 1]}) → Δknots_{tidyends}Sequence(Sum[First[Join[{{t_{0}}, Δknots_{tidyends}}], i]], i, 1, 1 + m) → knotsSequence(If[weights(1 + i) ≠ 0, Dilate[P(1 + i), weights(1 + i), Azathoth], P(1 + i)], i, 0, n) → P_wSequence(KDpos + KDioffset + (0, (i * KDiystep_i) + ((tri(k) - tri(k - i) - i) * KDiystep_j)), i, 0, k - 1) → KDibasesSequence(KDpos + KDuoffset + (0, KDublossomheight + (i * KDuystep_i) + ((tri(k) - tri(k - i) - i) * KDuystep_j)), i, 0, k - 1) → KDubasesMin(m - k, Length[KeepIf[x ≤ u, knots]] - 1) → Ki_uSequence(Sequence[(u - knots(1 + Ki_0 + i + j)) / (knots(1 + Ki_0 + j + k) - knots(1 + Ki_0 + i + j)), j, 0, k - 1 - i], i, 0, k - 1) → KublossomSequence(Sequence[KDibases(1 + i) + ((KDiscale * (Ki_0 + i + j)), (KDiystep_j * j)), j, 0, k - 1 - i], i, 0, k - 1) → KDi_aSequence(Sequence[KDibases(1 + i) + ((KDiscale * (Ki_0 + j + k)), (KDiystep_j * j)), j, 0, k - 1 - i], i, 0, k - 1) → KDi_bZip(Zip[Segment[aj, bj], aj, ai, bj, bi], ai, KDi_a, bi, KDi_b) → KDis_{ab}Sequence(Sequence[KDubases(1 + i) + ((KDiuscale * (knots(1 + Ki_0 + i + j) - knot_0)), (KDuystep_j * j)), j, 0, k - 1 - i], i, 0, k - 1) → KDu_aSequence(Sequence[KDubases(1 + i) + ((KDiuscale * (knots(1 + Ki_0 + j + k) - knot_0)), (KDuystep_j * j)), j, 0, k - 1 - i], i, 0, k - 1) → KDu_bPolyLine(KDpos + KDaoffset + ((-0.5 * KDiscale), 0) + {(0, 0), (0, KDaheight), (((m + 1) * KDiscale), KDaheight), (((m + 1) * KDiscale), 0), (0, 0)}) → KDaboxZip(Segment[a, a + (0, KDaheight)], a, Sequence[KDpos + KDaoffset + ((KDiscale * (i + 0.5)), 0), i, 0, m - 1]) → KDaboxdividersPolyLine(P) → cpPolyLine(P_w) → cp_{w,full}Zip(Zip[aj + ((bj - aj) * uj), aj, ai, bj, bi, uj, ui], ai, KDi_a, bi, KDi_b, ui, Kublossom) → KDi_mZip(Zip[aj + ((bj - aj) * uj), aj, ai, bj, bi, uj, ui], ai, KDu_a, bi, KDu_b, ui, Kublossom) → KDu_mZip(Zip[Segment[aj, bj], aj, ai, bj, bi], ai, KDu_a, bi, KDu_m) → KDus_{am}Zip(Zip[Segment[aj, bj], aj, ai, bj, bi], ai, KDu_b, bi, KDu_m) → KDus_{bm}First(KDu_m, k) → KDu_{m,sansfinal}Sequence({x(P_w(1 + Ki_0 + j)), y(P_w(1 + Ki_0 + j)), weights(1 + Ki_0 + j)}, j, 0, k) → Lerp_{h,0}Sequence(Element[Lerp_{h,0}, 1 + j] + ((Element[Lerp_{h,0}, 1 + j + 1] - Element[Lerp_{h,0}, 1 + j]) * Element[Element[Kublossom, 1], 1 + j]), j, 0, k - 1) → Lerp_{h,1}Sequence(Element[Lerp_{h,1}, 1 + j] + ((Element[Lerp_{h,1}, 1 + j + 1] - Element[Lerp_{h,1}, 1 + j]) * Element[Element[Kublossom, 2], 1 + j]), j, 0, k - 2) → Lerp_{h,2}Sequence(Element[Lerp_{h,2}, 1 + j] + ((Element[Lerp_{h,2}, 1 + j + 1] - Element[Lerp_{h,2}, 1 + j]) * Element[Element[Kublossom, 3], 1 + j]), j, 0, k - 3) → Lerp_{h,3}Sequence(Element[Lerp_{h,3}, 1 + j] + ((Element[Lerp_{h,3}, 1 + j + 1] - Element[Lerp_{h,3}, 1 + j]) * Element[Element[Kublossom, 4], 1 + j]), j, 0, k - 4) → Lerp_{h,4}Sequence(Element[Lerp_{h,4}, 1 + j] + ((Element[Lerp_{h,4}, 1 + j + 1] - Element[Lerp_{h,4}, 1 + j]) * Element[Element[Kublossom, 5], 1 + j]), j, 0, k - 5) → Lerp_{h,5}Sequence(Element[Lerp_{h,5}, 1 + j] + ((Element[Lerp_{h,5}, 1 + j + 1] - Element[Lerp_{h,5}, 1 + j]) * Element[Element[Kublossom, 6], 1 + j]), j, 0, k - 6) → Lerp_{h,6}Sequence(Element[Lerp_{h,6}, 1 + j] + ((Element[Lerp_{h,6}, 1 + j + 1] - Element[Lerp_{h,6}, 1 + j]) * Element[Element[Kublossom, 7], 1 + j]), j, 0, k - 7) → Lerp_{h,7}First({Lerp_{h,0}, Lerp_{h,1}, Lerp_{h,2}, Lerp_{h,3}, Lerp_{h,4}, Lerp_{h,5}, Lerp_{h,6}, Lerp_{h,7}}, k + 1) → Blossom_hSequence(PointList[Sequence[First[Element[Element[Blossom_h, 1 + i], 1 + j], 2], j, 0, k - i]], i, 0, k) → Lerp_wFirst(Lerp_w, k) → Lerp_{w,sansfinal}Element(Element[Blossom_h, 1 + k], 1) → C_hIf(Element[C_h, 3] ≠ 0, Dilate[C_w, 1 / Element[C_h, 3], Azathoth], C_w) → CSegment(Azathoth, If[C_h(3) ≤ 1, C, C_w]) → CwlineSequence(Sequence[Segment[Lerp_w(1 + i, 1 + j + 1), Lerp_w(1 + i + 1, 1 + j)], j, 0, k - 1 - i], i, 0, k - 1) → Lerpspan_{bm}Sequence(Sequence[Segment[Lerp_w(1 + i, 1 + j), Lerp_w(1 + i + 1, 1 + j)], j, 0, k - 1 - i], i, 0, k - 1) → Lerpspan_{am}Zip(Segment[P_w(1 + a), P_w(1 + a + 1)], a, KeepIf[x ≥ 0, Sequence[If[Sum[abs(Take[weights - 1, 1 + i, 1 + i + 1])] > 0, i, -1], i, 0, n - 1]]) → cp_{w,selective}Sequence(Segment[P(1 + i), P_w(1 + i)], i, 0, n) → wlinesTranslate(PolyLine[KDuoffset + ((KDiuscale * (u - knot_0)), KDublossomheight + KDuticklength), KDuoffset + ((KDiuscale * (u - knot_0)), (-KDtticklength)), KDaoffset + ((KDiscale * (Ki_u + Kublossom(Length[Kublossom], 1))), KDaheight + (KDiufantickheight * KDiufanheight)), KDaoffset + ((KDiscale * (Ki_u + Kublossom(Length[Kublossom], 1))), KDaheight + ((0.1 * KDiufantickheight) * KDiufanheight))], KDpos) → KDucrankSequence(Text[knots(1 + i), KDpos + KDaoffset + (-0.15, (0.35 * KDaheight)) + ((KDiscale * i), 0)], i, 0, m) → KDatextSequence(Translate[PolyLine[KDuoffset + ((KDiuscale * (knots(1 + i) - knot_0)), KDublossomheight + KDtticklength), KDuoffset + ((KDiuscale * (knots(1 + i) - knot_0)), (-KDtticklength)), KDaoffset + ((KDiscale * i), KDaheight + (KDiufantickheight * KDiufanheight)), KDaoffset + ((KDiscale * i), KDaheight)], KDpos], i, 0, m) → KDiufanSequence(Text[weights(1 + i), WIpos + (1 + (k - 1) / 2 - 0.07, 3.15) + ((KISspacing * i), 0)], i, 0, n) → WIatextSequence(Text["w_{" + (i) + "}", WIpos + (1 + (k - 1) / 2 - 0.1, -0.35) + ((KISspacing * i), 0)], i, 0, n) → WIalabelsSequence(Text["t_{" + (i) + "}", KIapos + (1 - 0.1, -0.35) + ((KISspacing * i), 0)], i, 0, m) → KIalabelsSegment(WIpos + (1 + (k - 1) / 2 - 0.2, 1), WIpos + (1 + (k - 1) / 2 + n + 0.2, 1)) → WIzerolineSegment(WIpos + (1 + (k - 1) / 2 - 0.2, 2), WIpos + (1 + (k - 1) / 2 + n + 0.2, 2)) → WIunitylineText("Weights", WIpos + (1 + (k - 1) / 2, -0.95)) → WeightsTextText("0", WIpos + (1 + (k - 1) / 2, 1) + (-0.45, -0.1)) → WILine0LabelText("1", WIpos + (1 + (k - 1) / 2, 2) + (-0.45, -0.1)) → WILine1LabelSegment(KIpos + (0.75, 1), KIpos + (1.25, 1)) → KIknot0zerolineText("0", KIpos + (0.75, 1) + (-0.3, -0.1)) → KIKnot0ZeroLabelText("u = " + u, KDupointer + (-0.13, 0.25)) → UValueTextText("Spans", DSIpos + (1, -1.95)) → SpansTextText("Degree", DSIpos + (1, -0.45)) → DegreeTextPolygon(Join[Zip[{(x0, y0), (x1, y0), (x1, y1), (x0, y1)}, y0, {y(KDirulerbase) - 0.75}, y1, {y(KDupointerbase) + 1.1}, x0, {x(KDpos)}, x1, {x(KDpos) + 2 + (KDiscale * m)}]]) → KDpanelPolygon(Join[Zip[{(x0, y0), (x1, y0), (x1, y1), (x0, y1)}, y0, {y(KIpos) - 3}, y1, {y(KIpos) + 4.5}, x0, {x(KIpos)}, x1, {x(KIpos) + 2 + (KDiscale * m)}]]) → KIpanelPolygon(Join[Zip[{(x0, y0), (x1, y0), (x1, y1), (x0, y1)}, y0, {y(WIpos) - 1.5}, y1, {y(WIpos) + 4.25}, x0, {x(WIpos)}, x1, {x(WIpos) + 2 + (KDiscale * m)}]]) → WIpanelPolygon(Join[Zip[{(x0, y0), (x1, y0), (x1, y1), (x0, y1)}, y0, {y(DSIpos) - 2.5}, y1, {y(DSIpos) + 1}, x0, {x(DSIpos)}, x1, {x(DSIpos) + 5}]]) → DSIpanelZip(Segment[a, a + (0, KDirulerticklength)], a, Sequence[KDirulerbase + ((KDiscale * j), 0), j, k - 1, m + 1 - k]) → KDirulerticksZip(Segment[a, a + (KDiscale, 0)], a, KeepIf[Defined[a], a, Sequence[If[knots(1 + j + 1) > knots(1 + j), KDirulerbase + ((KDiscale * j), 0)], j, k - 1, m - k]]) → KDirulerspansSequence(Text[knots(1 + i), KIapos + (1 - 0.13, 0.1) + ((KISspacing * i), 0)], i, 0, m) → KIatextText("Knot deltas", KIpos + (1, -0.45)) → ΔKnotsTextSequence(Text[Δknots_{input}(i), KIpos + (1 - 0.07, 2.15) + ((KISspacing * (i - 0.5)), 0)], i, k, m + 1 - k) → KIdtextSegment(KIpos + (1 + (0.5 * KISspacing), 0) + (-0.2, 0.4), KIpos + (1 + ((m - 0.5) * KISspacing), 0) + (0.2, 0.4)) → ΔKIunitylineText("1", KIpos + (1 + (0.5 * KISspacing), 0.4) + (-0.45, -0.1)) → ΔKILine1LabelText("Knots", KIapos + (1, -0.95)) → KnotsText