7
$\begingroup$

I want to be able to create a function so that instead of BSplineSurface[pts], it would be TSplineSurface[pts,error]. I think it would be useful for many people.

I am learning this little by little, so some of the explanation below might be false. Especially since I am not an expert.

The results are impressive because you can significantly reduce the number of control points. Here is a photo of an example that I took from this paper:

This is a photo that has two identical heads, but one was done with NURBS and the other was done with T-Splines. The T-Splines requires less than 25% of the control points as NURBS.

A T-spline is a generalization of a a B-spline, so instead of just allowing rectangular grid of knots, it allows for T-junctions.

Here is an example of a random T-junction:

This is an example of a T-junction.

made with the code:

RandomTJunction[]:=
Module[{vertices,edges,tIntersectionGraph},
vertices = {1 -> {-RandomReal[{1,5}], 0}, 2 -> {0, 0}, 3 -> {RandomReal[{1,5}], 0}, 4 -> {0, -RandomReal[{1,5}]}};
edges = {1 <-> 2, 2 <-> 3, 2 <-> 4};
tIntersectionGraph = Graph[edges, VertexCoordinates -> vertices, VertexLabels -> "Name", 
  Axes -> False, GraphLayout -> "PlanarEmbedding", PlotRange -> All];
  
Show[tIntersectionGraph]
];

but it also uses 'knots', 'knot vectors', 'knot intervals', and 'extraordinary points'. A knot is just a positive real number, a knot vector is a non-decreasing list of knots. The total of each knot vector (knot intervals too) must be the same in a T-Mesh, and each knot vector in a T-mesh has the same start and end..

The best explanation for extraordinary points I found is this:

Extraordinary points (EPs) are either interior vertices with μ ≠ 4 that are not T-junctions or boundary vertices with μ > 3.

The formula for calculating the T-Spline from a T-mesh is complicated and uses blending functions which I don't really understand any of that except for the weights part comes from the first reference and is this:

$$T = \frac{\sum_{i=1}^{n} P_i \cdot \omega_i \cdot N_i (u, v)}{\sum_{i=1}^{n} \omega_i \cdot N_i (u, v)}$$

Here is an example T-Mesh:

This is a random T-mesh. The vertical lines are red, the horizontal lines are green.

that I wrote with the code:

RandomKnotVector[totalLength_Real, numKnots_Integer] :=
Module[
  {randomPoints, knotVector},
  
  (* Generate numKnots-1 random points, then scale to the total length *)
  randomPoints = Sort[RandomReal[{0, totalLength}, numKnots - 1]];
  
  (* Create the knot vector by including 0 at the start and totalLength at the end *)
  knotVector = Join[{0}, randomPoints, {totalLength}];
  
  (* Return the knot vector *)
  knotVector
]

RandomConsecutiveIndices[knotVector_List] :=
Module[
  {possibleAs, pairs = {}, knotVectorLength, consecutivePairQuantity, a},
  
  knotVectorLength = Length[knotVector];
  possibleAs = Range[1, knotVectorLength - 1];

  (* Randomly decide the number of consecutive pairs *)
  consecutivePairQuantity = RandomInteger[{0, knotVectorLength-2}];
  |
  While[Length[pairs] < consecutivePairQuantity,
    a = RandomChoice[possibleAs];
    possibleAs = DeleteCases[possibleAs, a]; (* Remove 'a' from possible choices to avoid direct overlap *)
    AppendTo[pairs, {a, a + 1}];
  ];
  
  pairs
];

(* Example usage *)
knotVectorsOutput = createTMesh[];
transformedOutput = TransformKnotVectors[knotVectorsOutput];

VisualizeTMesh[transformedOutput_List] := 
Module[
  {points, horizontalLines, verticalLines, i, j, currentRow, nextRow, currentPoint, nextPoint},
  
  (* Flatten to get all points for plotting *)
  points = Point /@ Flatten[transformedOutput, 1];
  
  (* Generate horizontal lines within each row *)
  horizontalLines = Line /@ (Partition[#, 2, 1] & /@ transformedOutput);
  
  (* Generate vertical lines between rows with matching y-coordinates *)
  verticalLines = Table[
    currentRow = transformedOutput[[i]];
    nextRow = transformedOutput[[i + 1]];
    Table[
      currentPoint = currentRow[[j]];
      (* Find matching y-coordinate in the next row *)
      nextPoint = Select[nextRow, #[[2]] == currentPoint[[2]] &, 1];
      If[nextPoint =!= {} && Length[nextPoint] > 0,
        Line[{currentPoint, First[nextPoint]}],
        Nothing (* If there's no match, return Nothing *)
      ],
      {j, Length[currentRow]}
    ],
    {i, Length[transformedOutput] - 1}
  ];
  
  (* Draw the visualization with points and line segments *)
  Graphics[{
    PointSize[Medium], Blue, points,
    Thick, Red, horizontalLines,
    Thick, Green, Flatten[verticalLines]
  }, Frame -> False, AspectRatio -> 1/GoldenRatio]
];

(* Example usage with transformedOutput from previous steps *)
VisualizeTMesh[transformedOutput]
$\endgroup$
1

1 Answer 1

1
$\begingroup$

I believe I finally understand the papers and the details about T-splines. I originally had many misunderstandings. There is no code in this answer simply because the amount of subjectivity means would involve quite a bit of code to be robust enough for people to use in all situations. With the OP above and explanations below, I think it can be coded for a specific user's purpose much easier now than just looking at the literature. But I would certainly hope Mathematica adds it to their commands along with user defined rules for making the control points and T-meshes.

The commonality among almost all errors in my original thoughts was that splines are objective because of how much math it requires. But creating an appropriate T-spline is subjective as meshing/splining typically is. My autistic self had a difficult time wrapping my mind around this as I was biased in that I only wanted to use it for one thing. (Which I discovered T-splines is not a best choice.)

Here are a list of my misunderstandings for programming a random T-Spline:

  • Control Points: User input or definitions for the control points is required for how the T-spline is made. A control point does not need to be a point on the rectangular grid for Nurbs, but for T-splines with zero error, a control point is a point on the mesh after the mesh has been approprately mapped to the data we are interested in splining. Furthermore, suppose we are interested in using Blending functions of a certain degree. We can only have control points on the mesh with enough knots before and after it in the knot vector.

  • T-Mesh: It is subjective on how the mesh is made for a given set of points. And while mine is rectangular, there are papers where a T-spline is done on a subdivision or Catmill-Clark mesh which includes trapezoids and such.

  • Error: First of all, since I am generating a random T-Spline, it does not makes sense to introduce a T-spline error level, but it would obviously be useful in practical situations. The paper T-spline Simplification and Local Refinement T-spline Simplication and Local Refinement tells how to calculate the error level in Section 5 (which relies on section 4.2). Given a T-mesh, there are many T-splines from it and there is a linear transformation between each T-spline to the other inside of this resulting space. And we can use this linear transformation and our mesh to deal with it.

  • Euclidean Space: Another mistake is that while my T-mesh has points which can be denoted by a tuple of points, it is not Euclidean space in literature. It is more like a 'paramer space' that obviously can be mapped to various shapes or data in Euclidean space. This really blew my mind.

  • Knot vectors: They are typically nondecreasing in literature for B-Splines and NURBS. But the way I made the algorithm, they are strictly increasing.

  • Extraordinary Points: Not really needed to be understood for what I am doing. They are useful for explaining how T-Splines are useful in practice though.

  • Points: We treat them like matrices what we can perform linear algebra on in the formula given in the OP.

  • B-spline basis function knot vector: We just go along the knot vectors for both directions. If there are not enough points for one of the ends of the knot vectors given the basis degree we wish to use, we cannot use that point as a control point. This is demonstrated in the first paper referenced in the OP by acknowledging that $P_1$ is not used in Section 3.

  • Cox-De Boor recursion formula: To calculate this, we just use the Cox-De Boor recursion formula (which we can plug and chug to get our desired iterative formula) as described in a lot of the papers for the associated vertical and horizontal knot vectors associated with a control point and then multiply them. But I was confusing myself with Cox de boor's algorithm.

In other words, this entire topic was completely new information for me.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.