NURBS via de Boor's Method
Loading the math board and drawing, please wait…
This example demonstrates the construction of a Non-Uniform Rational B-Spline (NURBS) curve using de Boor's algorithm, showing a smooth parametric curve defined by control points, weights, and a knot vector. Core mathematical concepts include B-spline basis functions, rational curves, parameterization, knot vectors, and the de Boor recursive evaluation scheme, involving matrix operations and similarity transformations. Construction details: using GeoGebra commands such as First, Join, Sequence, Min, Zip, PolyLine, Element, If, Segment, Translate, Text, and Polygon, the construction builds control points P0 to P9, weights w0 to w9, knot parameter t0, and intermediate points d1 to d10, with sliders n, k, m controlling curve order and subdivision, and objects like AzathothColour enabling dynamic visualization. Suitable for high school to university courses in mathematics or computer graphics, this example helps learners understand the geometric construction and algorithmic implementation of NURBS curves.
GeoGebra Commands & Parameters
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