NURBS via de Boor's Method

NURBS via de Boor's Method
Loading the math board and drawing, please wait…
2020-01-07 20:02
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

  1. First({P_{0}, P_{1}, P_{2}, P_{3}, P_{4}, P_{5}, P_{6}, P_{7}, P_{8}, P_{9}}, n + 1) → P
  2. First({w_{0}, w_{1}, w_{2}, w_{3}, w_{4}, w_{5}, w_{6}, w_{7}, w_{8}, w_{9}}, n + 1) → weights
  3. First({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}
  4. 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}
  5. Sequence(Sum[First[Join[{{t_{0}}, Δknots_{tidyends}}], i]], i, 1, 1 + m) → knots
  6. Sequence(If[weights(1 + i) ≠ 0, Dilate[P(1 + i), weights(1 + i), Azathoth], P(1 + i)], i, 0, n) → P_w
  7. Sequence(KDpos + KDioffset + (0, (i * KDiystep_i) + ((tri(k) - tri(k - i) - i) * KDiystep_j)), i, 0, k - 1) → KDibases
  8. Sequence(KDpos + KDuoffset + (0, KDublossomheight + (i * KDuystep_i) + ((tri(k) - tri(k - i) - i) * KDuystep_j)), i, 0, k - 1) → KDubases
  9. Min(m - k, Length[KeepIf[x ≤ u, knots]] - 1) → Ki_u
  10. Sequence(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) → Kublossom
  11. Sequence(Sequence[KDibases(1 + i) + ((KDiscale * (Ki_0 + i + j)), (KDiystep_j * j)), j, 0, k - 1 - i], i, 0, k - 1) → KDi_a
  12. Sequence(Sequence[KDibases(1 + i) + ((KDiscale * (Ki_0 + j + k)), (KDiystep_j * j)), j, 0, k - 1 - i], i, 0, k - 1) → KDi_b
  13. Zip(Zip[Segment[aj, bj], aj, ai, bj, bi], ai, KDi_a, bi, KDi_b) → KDis_{ab}
  14. 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_a
  15. Sequence(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_b
  16. PolyLine(KDpos + KDaoffset + ((-0.5 * KDiscale), 0) + {(0, 0), (0, KDaheight), (((m + 1) * KDiscale), KDaheight), (((m + 1) * KDiscale), 0), (0, 0)}) → KDabox
  17. Zip(Segment[a, a + (0, KDaheight)], a, Sequence[KDpos + KDaoffset + ((KDiscale * (i + 0.5)), 0), i, 0, m - 1]) → KDaboxdividers
  18. PolyLine(P) → cp
  19. PolyLine(P_w) → cp_{w,full}
  20. Zip(Zip[aj + ((bj - aj) * uj), aj, ai, bj, bi, uj, ui], ai, KDi_a, bi, KDi_b, ui, Kublossom) → KDi_m
  21. Zip(Zip[aj + ((bj - aj) * uj), aj, ai, bj, bi, uj, ui], ai, KDu_a, bi, KDu_b, ui, Kublossom) → KDu_m
  22. Zip(Zip[Segment[aj, bj], aj, ai, bj, bi], ai, KDu_a, bi, KDu_m) → KDus_{am}
  23. Zip(Zip[Segment[aj, bj], aj, ai, bj, bi], ai, KDu_b, bi, KDu_m) → KDus_{bm}
  24. First(KDu_m, k) → KDu_{m,sansfinal}
  25. 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}
  26. 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}
  27. 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}
  28. 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}
  29. 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}
  30. 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}
  31. 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}
  32. 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}
  33. 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_h
  34. Sequence(PointList[Sequence[First[Element[Element[Blossom_h, 1 + i], 1 + j], 2], j, 0, k - i]], i, 0, k) → Lerp_w
  35. First(Lerp_w, k) → Lerp_{w,sansfinal}
  36. Element(Element[Blossom_h, 1 + k], 1) → C_h
  37. If(Element[C_h, 3] ≠ 0, Dilate[C_w, 1 / Element[C_h, 3], Azathoth], C_w) → C
  38. Segment(Azathoth, If[C_h(3) ≤ 1, C, C_w]) → Cwline
  39. Sequence(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}
  40. 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}
  41. 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}
  42. Sequence(Segment[P(1 + i), P_w(1 + i)], i, 0, n) → wlines
  43. Translate(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) → KDucrank
  44. Sequence(Text[knots(1 + i), KDpos + KDaoffset + (-0.15, (0.35 * KDaheight)) + ((KDiscale * i), 0)], i, 0, m) → KDatext
  45. Sequence(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) → KDiufan
  46. Sequence(Text[weights(1 + i), WIpos + (1 + (k - 1) / 2 - 0.07, 3.15) + ((KISspacing * i), 0)], i, 0, n) → WIatext
  47. Sequence(Text["w_{" + (i) + "}", WIpos + (1 + (k - 1) / 2 - 0.1, -0.35) + ((KISspacing * i), 0)], i, 0, n) → WIalabels
  48. Sequence(Text["t_{" + (i) + "}", KIapos + (1 - 0.1, -0.35) + ((KISspacing * i), 0)], i, 0, m) → KIalabels
  49. Segment(WIpos + (1 + (k - 1) / 2 - 0.2, 1), WIpos + (1 + (k - 1) / 2 + n + 0.2, 1)) → WIzeroline
  50. Segment(WIpos + (1 + (k - 1) / 2 - 0.2, 2), WIpos + (1 + (k - 1) / 2 + n + 0.2, 2)) → WIunityline
  51. Text("Weights", WIpos + (1 + (k - 1) / 2, -0.95)) → WeightsText
  52. Text("0", WIpos + (1 + (k - 1) / 2, 1) + (-0.45, -0.1)) → WILine0Label
  53. Text("1", WIpos + (1 + (k - 1) / 2, 2) + (-0.45, -0.1)) → WILine1Label
  54. Segment(KIpos + (0.75, 1), KIpos + (1.25, 1)) → KIknot0zeroline
  55. Text("0", KIpos + (0.75, 1) + (-0.3, -0.1)) → KIKnot0ZeroLabel
  56. Text("u = " + u, KDupointer + (-0.13, 0.25)) → UValueText
  57. Text("Spans", DSIpos + (1, -1.95)) → SpansText
  58. Text("Degree", DSIpos + (1, -0.45)) → DegreeText
  59. Polygon(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)}]]) → KDpanel
  60. Polygon(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)}]]) → KIpanel
  61. Polygon(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)}]]) → WIpanel
  62. Polygon(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}]]) → DSIpanel
  63. Zip(Segment[a, a + (0, KDirulerticklength)], a, Sequence[KDirulerbase + ((KDiscale * j), 0), j, k - 1, m + 1 - k]) → KDirulerticks
  64. Zip(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]]) → KDirulerspans
  65. Sequence(Text[knots(1 + i), KIapos + (1 - 0.13, 0.1) + ((KISspacing * i), 0)], i, 0, m) → KIatext
  66. Text("Knot deltas", KIpos + (1, -0.45)) → ΔKnotsText
  67. Sequence(Text[Δknots_{input}(i), KIpos + (1 - 0.07, 2.15) + ((KISspacing * (i - 0.5)), 0)], i, k, m + 1 - k) → KIdtext
  68. Segment(KIpos + (1 + (0.5 * KISspacing), 0) + (-0.2, 0.4), KIpos + (1 + ((m - 0.5) * KISspacing), 0) + (0.2, 0.4)) → ΔKIunityline
  69. Text("1", KIpos + (1 + (0.5 * KISspacing), 0.4) + (-0.45, -0.1)) → ΔKILine1Label
  70. Text("Knots", KIapos + (1, -0.95)) → KnotsText