[vtk-developers] Class design for spline visualizations

Lin M majcjc at gmail.com
Fri Jun 19 18:28:26 EDT 2015


I get it now. Previously I was thinking of bezier patches so we need
explicit definition about the degree to each dimension given a control
point array. We are now considering NURBS whose degree of interpolant has
been implicit defined together by the knot vectors and control points
(degree + number of control points = number of knot vector entries) but if
we assume all the control points for all patches are stored in one
vtkStructureGrid instance (the points are ultimately stored in the
vtkDataArray* vtkStructureGrid->GetPoints()->GetData()), we still need to
know where the i-th patch begins in the vtkDataArray.

What we want to implement now is to use vtkMappedDataArray to retrieve a
"fake" vtkDataArray for one patch from the original vtkDataArray.

On Fri, Jun 19, 2015 at 1:33 AM, David Thompson <david.thompson at kitware.com>
wrote:

> Hi Lin,
>
> The patch degree can be inferred from the difference between the length of
> the knot vector and the size of the control point grid. The patch dimension
> is the dimension of the control point grid. For example, if we run
>
>   vtkStructuredGrid* grid;
>   int dim[3];
>   grid->GetDimensions(dim);
>
> and dims is [4,1,1], then we have a 1-d curve spline with 4 control points
> (=4*1*1). If dims is [4,4,1] then we have a surface spline with 16 control
> points (=4*4*1). The order of the spline interpolant is the difference in
> length between the number of control points in that dimension and the
> number of knot vector entries in that dimension. See
> https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline#Knot_vector for
> examples.
>
> So, to answer your question, we do not need to cache the patch dimension
> and parameter range. The parameter interval is given by the knot vector
> entries corresponding to the current patch, so we should not need to store
> that, either.
>
>     David
>
>
> On Jun 18, 2015, at 23:44, Lin M <majcjc at gmail.com> wrote:
>
> Do we store the auxiliary variables like PatchDegree PatchDimension and
> PatchParameterRange for all patches in the adaptor class and keep the
> variables for certain patch in the vtkMappedDataArray subclass when we want
> to retrieve that patch?
>
> On Thu, Jun 18, 2015 at 9:17 PM, Lin M <majcjc at gmail.com> wrote:
>
>> Sorry...
>>
>> The vtkControlPointArray is the subclass of vtkMappedDataArray. I define
>> a variable to indicate that the begin idx of the patch which I assume will
>> be set when vtkBezierPatchAdaptor::Next() or vtkBezierPatchAdaptor::GoToPatch()
>> is called.
>>
>> On Thu, Jun 18, 2015 at 9:11 PM, David Thompson <
>> david.thompson at kitware.com> wrote:
>>
>>> Hi Lin,
>>>
>>> It looks like you forgot to add the header and implementation files to
>>> the commit.
>>>
>>>         David
>>>
>>> > On Jun 18, 2015, at 9:08 PM, Lin M <majcjc at gmail.com> wrote:
>>> >
>>> > Hi Dr. Thompson,
>>> >
>>> > I have added one commit which contains a vtkMappedDataArray subclass
>>> but I'm not sure I totally get the idea about the in-situ data structures.
>>> Can you take a look at it please? (
>>> https://gitlab.kitware.com/splines/vtk/merge_requests/4)
>>> >
>>> > Best,
>>> > Lin
>>> >
>>> > On Thu, Jun 18, 2015 at 10:47 AM, David Thompson <
>>> david.thompson at kitware.com> wrote:
>>> > Hi Lin,
>>> >
>>> > > From some examples I saw, usually we put some points in vtkPoints,
>>> store the connectivity in cellArray and add them to a dataset (vtkPolyData,
>>> vtkStructuredGrid or vtkUnstructureGrid). So is that we store the control
>>> point of multiple patches in the vtkPoints where we replace the
>>> vtkDataArray with vtkMappedDataArray now? And in a vtkMappedDataArray, we
>>> store all the points for all patches together but we overwrite how to
>>> iterate the array.
>>> >
>>> > That is nearly correct. We will accept
>>> >
>>> >     vtkDataObject* ControlPointData;
>>> >
>>> > from the user. For the case we will implement first,
>>> ControlPointData's actual type will be the vtkStructuredGrid subclass of
>>> vtkDataObject. The vtkStructuredGrid instance can hold many user-supplied
>>> vtkDataArray instances:
>>> >
>>> >     vtkStructuredGrid* structuredControlPoints =
>>> >       vtkStructuredGrid::SafeDownCast(ControlPointData);
>>> >
>>> >     // Control points to map from parameter-space to world coordinates:
>>> >     vtkDataArray* geometricControlPoints =
>>> >       structuredControlPoints->GetPoints()->GetData();
>>> >
>>> >     // Control points to map from paramter-space to scalar fields:
>>> >     vtkDataSetAttributes* scalarFields =
>>> structuredControlPoints->GetPointData();
>>> >     vtkIdType numScalarFields = scalarFields->GetNumberOfArrays();
>>> >     for (vtkIdType i = 0; i < numScalarFields; ++i)
>>> >       { // scalarFieldControlPoints will hold things like temperature,
>>> pressure, etc.:
>>> >       vtkDataArray* scalarFieldControlPoints =
>>> scalarFields->GetArray(i);
>>> >       }
>>> >
>>> > Because the user supplies these arrays, we cannot force them to be
>>> vtkMappedDataArray. Instead, we can make a vtkMappedDataArray subclass that
>>> owns a reference to one of the arrays above and only returns one patch's
>>> values from the user-supplied array (which holds the values for all patches
>>> in the spline).
>>> >
>>> > The BezierPatchAdaptor below would return instances of our
>>> vtkMappedDataArray when its GetPatchPoints method is called. Now that I've
>>> written some more out, it looks like the API should be changed a little bit
>>> so that users can ask for a particular array (geometricControlPoints or
>>> scalarFieldControlPoints):
>>> >
>>> > class vtkBezierPatchAdaptor : public vtkObject
>>> > {
>>> > public:
>>> >   virtual void SetControlPointData(vtkDataObject* controlPointData);
>>> >   vtkGetObjectMacro(vtkDataObject,ControlPointData);
>>> >
>>> >   // Methods to iterate over patches:
>>> >   // ... same as below ...
>>> >
>>> >   // Methods to access the current patch:
>>> >   // ...
>>> >   virtual vtkSmartPointer<vtkMappedArray> GetPatchPoints(int
>>> scalarField) const = 0;
>>> >   // ...
>>> > };
>>> >
>>> > This way, GetPatchPoints() could return the vtkMappedDataArray for
>>> geometricControlPoints when the "int scalarField" argument is negative
>>> value and a vtkMappedDataArray for one of the scalarFieldControlPoints
>>> arrays when scalarField is positive.
>>> >
>>> > Having GetPatchPoints() return a smart pointer to a vtkMappedDataArray
>>> subclass would also get around the issue that vtkPoints expects its
>>> vtkDataArray to have 3 components per tuple (where ours will have 4 for
>>> geometricControlPoints or an arbitrary number for scalarFieldControlPoints).
>>> >
>>> > The vtkMappedDataArray instances would own a weak reference back the
>>> vtkBezierPatchAdaptor which created them and a weak reference to the
>>> geometricControlPoints or scalarFieldControlPoints array it draws values
>>> from. When vtkBezierPatchAdaptor::Next() or
>>> vtkBezierPatchAdaptor::GoToPatch() is called, the adaptor would iterate
>>> over all of the vtkMappedDataArray instances it owns and change an internal
>>> variable so they would return values for the proper patch. That way the
>>> vtkMappedDataArray instances only have to be created once for each spline
>>> dataset, not once per patch.
>>> >
>>> > Is that clear enough, or should I sketch out a header file for our
>>> vtkMappedDataArray subclass?
>>> >
>>> >         David
>>> >
>>> > > On Wed, Jun 17, 2015 at 10:22 PM, David Thompson <
>>> david.thompson at kitware.com> wrote:
>>> > > Hi Lin,
>>> > >
>>> > >> Sorry for late reply. Since the midterm is coming, I'll definitely
>>> speed up the development from now on.
>>> > >
>>> > > We both need to do a little more work. I have been looking at how to
>>> read data from PetIGA so we can show some actual simulation data and have
>>> some 3-D volumetric examples.
>>> > >
>>> > > It would be nice to have a patch ready to merge into VTK's master
>>> branch at the midterm that provides spline interpolation and tests it for
>>> all 3 parametric dimensions (curves, surfaces, and volumes). The means
>>> producing triangles and tetrahedra, not just polylines.
>>> > >
>>> > >> I have a question about the class design in your last email. To my
>>> understanding, control points for one patch is stored in a vtkDataArray.
>>> > >
>>> > > Close. I think that we should use the vtkMappedDataArray class to
>>> hold control points for multiple patches but program it to appear as if
>>> points for each individual patch are stored in patch-order.
>>> > >
>>> > >> Do you mean store multiple vtkDataArray regarding to multiple
>>> patches in the vtkDataObject?
>>> > >
>>> > > There would be one data array to hold point coordinates, but other
>>> arrays would hold simulation variables like temperature, pressure,
>>> velocity, and so on.
>>> > >
>>> > >> If so, what class do I need to use for vtkDataObject?
>>> > >
>>> > > The class outline below is an abstract class. A concrete
>>> implementation for NURBs would require   the base class's ControlPointData
>>> to be an instance of vtkStructuredGrid. A T-spline implementation might
>>> expect ControlPointData to be a vtkUnstructuredGrid. I think all we should
>>> do this summer is the NURBs case.
>>> > >
>>> > >     David
>>> > >
>>> > >>
>>> > >> On Wed, Jun 10, 2015 at 2:39 PM, David Thompson <
>>> david.thompson at kitware.com> wrote:
>>> > >> Hi Lin,
>>> > >>
>>> > >> > I have made it support 3D ellipse with function ...
>>> > >>
>>> > >> Great!
>>> > >>
>>> > >> > As you can see in the python test, I call this function 4 times
>>> for each quadrant to generate the entire ellipse. Since we need to support
>>> multiple patches, I think it's better to define a new dataset class for
>>> bezier patches as you mentioned before about  creating a new
>>> vtkControlPoints class that inherits from vtkPoints and allows for an extra
>>> coordinate.
>>> > >>
>>> > >> Rather than create new dataset types, I would like to use existing
>>> datatypes to store the data and make an adaptor to iterate over them. That
>>> way existing pipelines could modify scalar values defined on control points
>>> (and perhaps even patches). For instance, B-splines could be represented as
>>> a vtkStructuredGrid whose FieldData contains a special knot array (special
>>> in the sense that the array must be of the proper size and with a fixed
>>> name like "_vtkKnotVector").
>>> > >>
>>> > >> The adaptor would take a vtkDataObject as input and provide
>>> iterator-style access to each patch defined by the dataset. Specific
>>> subclasses of the adaptor would be B-splines (which expect the
>>> vtkDataObject to be a vtkStructuredGrid) or T-splines (which might expect a
>>> vtkUniformGridAMR) or even point-based splines (PB-splines as defined in
>>> Sederberg's T-spline paper, which could expect any dataset derived
>>> vtkPointSet).
>>> > >>
>>> > >> class vtkBezierPatchAdaptor : public vtkObject
>>> > >> {
>>> > >> public:
>>> > >>   virtual void SetControlPointData(vtkDataObject* controlPointData);
>>> > >>   vtkGetObjectMacro(vtkDataObject,ControlPointData);
>>> > >>
>>> > >>   // Methods to iterate over patches:
>>> > >>   virtual void Begin() = 0;
>>> > >>   virtual bool IsAtEnd() = 0;
>>> > >>   virtual bool GoToPatch(vtkIdType patchId) = 0; // random access
>>> iteration
>>> > >>   virtual bool Next() = 0;
>>> > >>
>>> > >>   // Methods to access the current patch:
>>> > >>   virtual int GetPatchDimension() const = 0;
>>> > >>   virtual void GetPatchShape() const = 0;
>>> > >>     // returns VTK_TRIANGLE/VTK_QUAD when PatchDimension==2,
>>> > >>     // returns VTK_HEXAHEDRON/VTK_TETRA when PatchDimension==3
>>> > >>   virtual void GetPatchDegree(int* degreeOut) const = 0;
>>> > >>   virtual void GetPatchParameterRange(double* paramsOut) const = 0;
>>> > >>   virtual void GetPatchPoints(vtkPoints* pointsOut) const = 0;
>>> > >>
>>> > >>   // Some helper methods that would make Python wrappings usable:
>>> > >>   int GetPatchDegree(int dim) const
>>> > >>     {
>>> > >>     std::vector<int> degree(this->GetPatchDimension());
>>> > >>     this->GetPatchDegree(&degree[0]);
>>> > >>     return degree[dim];
>>> > >>     }
>>> > >>   void GetPatchParameterRange(int dim, double paramRange[2])
>>> > >>     {
>>> > >>     std::vector<double> range(2 * this->GetPatchDimension());
>>> > >>     this->GetPatchDegree(&range[0]);
>>> > >>     paramRange[0] = [2 * dim];
>>> > >>     paramRange[1] = [2 * dim + 1];
>>> > >>     }
>>> > >>
>>> > >> protected:
>>> > >>   vtkDataObject* ControlPointData;
>>> > >> };
>>> > >>
>>> > >> The B-spline subclass could then iterate over patches by keeping a
>>> current "cell ID," which would be incremented by calling Next(). When asked
>>> for the *patch* points (not the input points), the adaptor would create a
>>> vtkMappedDataArray (see http://www.vtk.org/Wiki/VTK/InSituDataStructure
>>> for more information) that did not copy control point coordinates from its
>>> ControlPointData->GetPoints() but instead used the implicit connectivity to
>>> provide access to underlying B-spline points.
>>> > >>
>>> > >> For example, consider the simple case of a rectangular B-spline
>>> with degree 1 (bi-linear). We might be given a 5x6 grid of control points
>>> as input and asked to iterate over all the patches. Each patch is simply a
>>> quadrilateral, and there are (5-1)*(6-1) = 20 of them.
>>> > >>
>>> > >>   adaptor->Begin(); // would initialize an internal "cell ID" to 0
>>> > >>   adaptor->IsAtEnd(); // would return false for "cell ID" values in
>>> [0,19] and true otherwise.
>>> > >>   adaptor->GetPatchDimension(); // would return 2
>>> > >>   adaptor->GetPatchShape(); // would return VTK_QUAD
>>> > >>   adaptor->GetPatchDegree(degreeOut); // would populate degreeOut
>>> with [1,1]
>>> > >>   adaptor->GetPatchPoints(pointsOut); // would populate pointsOut
>>> with a vtkMappedDataArray.
>>> > >>
>>> > >> The vtkMappedDataArray would report having 4 tuples (one for each
>>> corner of the quad... when the degree is higher, is would report the
>>> product of (degreeOut[i]+1) for all i in GetPatchDimension()). The actual
>>> points reported would depend on the "cell ID" in the adaptor.
>>> > >>
>>> > >> I have not figured out yet how the "w" homogeneous coordinate would
>>> work with vtkPoints. A different adaptor API might work better (keeping the
>>> "w" coordinate separate from the other points).
>>> > >>
>>> > >> > Another question is that currently the domain of parameter
>>> coordinate is [0, 1] for each patch. Do you think it's better to make the
>>> domain continuously, namely [0, 0.25] for the first quadrant, [0.25, 0.75]
>>> for the second quadrant, etc?
>>> > >>
>>> > >> If we focus on the B-spline adaptor above, we get that for free
>>> since the returned knot vector could be normalized to the range [0,1].
>>> > >>
>>> > >>         David
>>> > >>
>>> > >
>>> >
>>> >
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://public.kitware.com/pipermail/vtk-developers/attachments/20150619/32d3856c/attachment-0001.html>


More information about the vtk-developers mailing list