HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GEO_SplitPoints.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  * Definitions of functions for splitting points based on vertices or primitives
27  */
28 
29 #include "GEO_SplitPoints.h"
30 
31 #include <GEO/GEO_Detail.h>
32 #include <GA/GA_ATINumeric.h>
33 #include <GA/GA_Attribute.h>
34 #include <GA/GA_AttributeFilter.h>
35 #include <GA/GA_AIFCompare.h>
36 #include <GA/GA_ElementGroup.h>
37 #include <GA/GA_ElementWrangler.h>
38 #include <GA/GA_Handle.h>
39 #include <GA/GA_Range.h>
40 #include <GA/GA_Types.h>
41 #include <UT/UT_Interrupt.h>
42 #include <UT/UT_SmallArray.h>
43 #include <UT/UT_UniquePtr.h>
44 #include <SYS/SYS_Types.h>
45 #include <SYS/SYS_Math.h>
46 
47 namespace HDK_Sample {
48 
49 GA_Size
51  GEO_Detail *detail,
52  const GA_ElementGroup *group)
53 {
54  if (group && group->isEmpty())
55  return 0;
56 
57  UT_AutoInterrupt boss("Making Unique Points");
58  if (boss.wasInterrupted())
59  return 0;
60 
61  // Behaviour is as if it's a point group if no group is present.
62  GA_GroupType grouptype = GA_GROUP_POINT;
63  const GA_PointGroup *point_group = nullptr;
64  GA_PointGroupUPtr point_group_deleter;
65  if (group)
66  {
67  grouptype = group->classType();
68  if (grouptype == GA_GROUP_POINT)
69  point_group = static_cast<const GA_PointGroup *>(group);
70  else if (grouptype == GA_GROUP_VERTEX || grouptype == GA_GROUP_PRIMITIVE)
71  {
72  GA_PointGroup *ptgroup = new GA_PointGroup(*detail);
73  point_group_deleter.reset(ptgroup);
74  ptgroup->combine(group);
75  point_group = ptgroup;
76  }
77  }
78 
79  GA_Size numpointsadded = 0;
80 
81  char bcnt = 0;
82 
83  UT_UniquePtr<GA_PointWrangler> ptwrangler(nullptr);
84 
86  GA_Offset points_end = detail->getPointMap().lastOffset() + 1;
88  GA_Offset end;
89  for (GA_Iterator it(detail->getPointRange(point_group)); it.blockAdvance(start, end); )
90  {
91  // Prevent going beyond the original points; new points don't need to be split.
92  if (start >= points_end)
93  break;
94  if (end > points_end)
95  end = points_end;
96 
97  for (GA_Offset ptoff = start; ptoff < end; ++ptoff)
98  {
99  vtxoffs.setSize(0);
100 
101  // Make vertex order consistent, regardless of linked list order,
102  // by sorting in vertex offset order
103  bool has_other = false;
104  if (grouptype == GA_GROUP_POINT)
105  {
106  for (GA_Offset vtxoff = detail->pointVertex(ptoff); GAisValid(vtxoff); vtxoff = detail->vertexToNextVertex(vtxoff))
107  vtxoffs.append(vtxoff);
108  }
109  else
110  {
111  for (GA_Offset vtxoff = detail->pointVertex(ptoff); GAisValid(vtxoff); vtxoff = detail->vertexToNextVertex(vtxoff))
112  {
113  GA_Offset group_offset = vtxoff;
114  if (grouptype == GA_GROUP_PRIMITIVE)
115  group_offset = detail->vertexPrimitive(vtxoff);
116  if (group->contains(group_offset))
117  vtxoffs.append(vtxoff);
118  else
119  has_other = true;
120  }
121  }
122 
123  // If there's only one vertex in the group and none outside, nothing to split.
124  // If there are no vertices in the group, also nothing to split.
125  // Unique all but first vertex if all vertices of the point are in vtxoffs.
126  GA_Size nvtx = vtxoffs.size();
127  GA_Size points_to_add = nvtx - GA_Size(!has_other);
128  if (points_to_add <= 0)
129  continue;
130 
131  numpointsadded += points_to_add;
132 
133  if (!ptwrangler)
134  ptwrangler.reset(new GA_PointWrangler(*detail, GA_AttributeFilter::selectPublic()));
135 
136  // TODO: This is not perfect; we should be sorting by primitive order
137  // and then vertex order within a primitive, so that results
138  // are the same whether the input was loaded from a file or
139  // computed.
140  vtxoffs.stdsort(std::less<GA_Offset>());
141 
142  bool skip_first = !has_other;
143  GA_Offset newptoff = detail->appendPointBlock(points_to_add);
144  for (exint j = exint(skip_first); j < nvtx; ++j, ++newptoff)
145  {
146  if (!bcnt++ && boss.wasInterrupted())
147  return numpointsadded;
148 
149  ptwrangler->copyAttributeValues(newptoff, ptoff);
150  detail->getTopology().wireVertexPoint(vtxoffs(j), newptoff);
151  }
152  }
153  }
154  if (numpointsadded > 0)
155  {
156  // If we'd only added/removed points, we could use
157  // detail->bumpDataIdsForAddOrRemove(true, false, false),
158  // but we also rewired vertices, so we need to bump the
159  // linked-list topology attributes.
161  GA_ATITopology *topo = detail->getTopology().getPointRef();
162  if (topo)
163  topo->bumpDataId();
164  topo = detail->getTopology().getVertexNextRef();
165  if (topo)
166  topo->bumpDataId();
167  topo = detail->getTopology().getVertexPrevRef();
168  if (topo)
169  topo->bumpDataId();
170  // Edge groups might also be affected, if any edges
171  // were on points that were split, so we bump their
172  // data IDs, just in case.
173  detail->edgeGroups().bumpAllDataIds();
174  }
175  return numpointsadded;
176 }
177 
178 namespace {
179 template<typename T>
180 bool
181 compareVertices(
182  GA_Offset v1,
183  GA_Offset v2,
184  const GA_ROHandleT<T> &attrib,
185  int tuplesize,
186  fpreal tol)
187 {
188  for (int i = 0; i < tuplesize; i++)
189  {
190  if (SYSabs(attrib.get(v1, i) - attrib.get(v2, i)) > tol)
191  return false;
192  }
193  return true;
194 }
195 
196 GA_Size
197 geoSplitPointsByAttrib(
198  GEO_Detail *detail,
199  const GA_Range &points,
200  const GA_ElementGroup *group,
201  const GA_Attribute *attrib,
202  fpreal tolerance)
203 {
204  // Point groups should be weeded out by the wrappers below.
205  UT_ASSERT(!group || group->classType() == GA_GROUP_PRIMITIVE || group->classType() == GA_GROUP_VERTEX);
206  bool is_primgroup = group && group->classType() == GA_GROUP_PRIMITIVE;
207 
208  GA_Size numpointsadded = 0;
209 
210  UT_ASSERT(attrib);
211  if (!attrib)
212  return numpointsadded;
213 
214  GA_AttributeOwner owner = attrib->getOwner();
215  if (owner == GA_ATTRIB_POINT || owner == GA_ATTRIB_DETAIL)
216  return numpointsadded;
217 
218  const GA_AIFCompare *compare = NULL;
219  GA_ROHandleT<int64> attribi;
220  GA_ROHandleR attribf;
221  int tuplesize;
222  const GA_ATINumeric *numeric = dynamic_cast<const GA_ATINumeric *>(attrib);
223  if (numeric)
224  {
225  GA_StorageClass storeclass = attrib->getStorageClass();
226  if (storeclass == GA_STORECLASS_INT)
227  {
228  attribi = attrib;
229  UT_ASSERT(attribi.isValid());
230  tuplesize = attribi.getTupleSize();
231  }
232  else if (storeclass == GA_STORECLASS_FLOAT)
233  {
234  attribf = attrib;
235  UT_ASSERT(attribf.isValid());
236  tuplesize = attribf.getTupleSize();
237  }
238  else
239  {
240  UT_ASSERT_MSG(0, "Why does a GA_ATINumeric have a storage class other than int or float?");
241  compare = attrib->getAIFCompare();
242  }
243  }
244  else
245  {
246  compare = attrib->getAIFCompare();
247  if (compare == NULL)
248  {
249  UT_ASSERT_MSG(0, "Missing an implementation of GA_AIFCompare!");
250  return numpointsadded;
251  }
252  }
253 
254  UT_UniquePtr<GA_PointWrangler> ptwrangler(nullptr);
255 
256  UT_SmallArray<GA_Offset> vtxlist;
257  GA_Size initnumpts = detail->getNumPoints();
258  for (GA_Iterator it(points); !it.atEnd(); ++it)
259  {
260  GA_Offset ptoff = *it;
261  GA_Index ptidx = detail->pointIndex(ptoff);
262  if (ptidx >= initnumpts)
263  break; // New points are already split
264 
265  bool splitfound;
266  do
267  {
268  GA_Offset vtxoff = detail->pointVertex(ptoff);
269  if (!GAisValid(vtxoff))
270  break; // Point doesn't belong to anyone
271 
272  // To avoid the results being dependent on the order of the
273  // linked-list topology attribute, find the highest vertex offset
274  // in the primitive of highest offset to be the base vertex.
275  // Lowest would make more sense, but highest is more compatible with
276  // the previous code.
277  GA_Offset primoff = detail->vertexPrimitive(vtxoff);
278  GA_Offset basevtxoff = GA_INVALID_OFFSET;
279  GA_Offset baseprimoff = GA_INVALID_OFFSET;
280  if (!group || group->contains(is_primgroup ? primoff : vtxoff))
281  {
282  basevtxoff = vtxoff;
283  baseprimoff = primoff;
284  }
285  vtxoff = detail->vertexToNextVertex(vtxoff);
286  while (GAisValid(vtxoff))
287  {
288  primoff = detail->vertexPrimitive(vtxoff);
289 
290  if (!group || group->contains(is_primgroup ? primoff : vtxoff))
291  {
292  if (primoff > baseprimoff || (primoff == baseprimoff && vtxoff > basevtxoff))
293  {
294  basevtxoff = vtxoff;
295  baseprimoff = primoff;
296  }
297  }
298  vtxoff = detail->vertexToNextVertex(vtxoff);
299  }
300 
301  if (!GAisValid(basevtxoff))
302  break; // No vertices in group on current point
303 
304  // We have a vertex on the point that's in the group.
305  // If any vertices mismatch, we make a new point.
306 
307  GA_Offset baseoffset = (owner == GA_ATTRIB_VERTEX) ? basevtxoff : baseprimoff;
308  vtxlist.setSize(1);
309  vtxlist(0) = basevtxoff;
310  splitfound = false;
311  for (vtxoff = detail->pointVertex(ptoff); GAisValid(vtxoff); vtxoff = detail->vertexToNextVertex(vtxoff))
312  {
313  if (vtxoff == basevtxoff)
314  continue;
315  GA_Offset offset = (owner == GA_ATTRIB_VERTEX) ? vtxoff : detail->vertexPrimitive(vtxoff);
316  bool match;
317  if (attribi.isValid())
318  match = compareVertices<int64>(baseoffset, offset, attribi, tuplesize, tolerance);
319  else if (attribf.isValid())
320  match = compareVertices<fpreal>(baseoffset, offset, attribf, tuplesize, tolerance);
321  else
322  {
323  bool success = compare->isEqual(match, *attrib, baseoffset, *attrib, offset);
324  if (!success)
325  match = true;
326  }
327  if (match)
328  {
329  // Only append vertices in the group
330  if (!group || group->contains(is_primgroup ? detail->vertexPrimitive(vtxoff) : vtxoff))
331  {
332  vtxlist.append(vtxoff);
333  }
334  }
335  else
336  {
337  // Split regardless of whether the unmatched vertex is in the group.
338  // This may or may not be the behaviour people expect,
339  // but it's ambiguous as to what people would expect.
340  // At least, if all vertices in the group have one value
341  // and all vertices out of the group have a different value,
342  // they probably want a new point, and that's accomplished
343  // with this condition.
344  splitfound = true;
345  }
346  }
347 
348  if (splitfound)
349  {
350  // A split has been found! Create a new point
351  // using all the entries of vtxlist.
352  GA_Offset newptoff = detail->appendPointOffset();
353  ++numpointsadded;
354  if (!ptwrangler)
355  ptwrangler.reset(new GA_PointWrangler(*detail, GA_AttributeFilter::selectPublic()));
356  ptwrangler->copyAttributeValues(newptoff, ptoff);
357  for (exint i = 0; i < vtxlist.size(); i++)
358  detail->getTopology().wireVertexPoint(vtxlist(i), newptoff);
359  }
360  } while (splitfound);
361  }
362 
363  if (numpointsadded > 0)
364  {
365  // If we'd only added/removed points, we could use
366  // detail->bumpDataIdsForAddOrRemove(true, false, false),
367  // but we also rewired vertices, so we need to bump the
368  // linked-list topology attributes.
370  GA_ATITopology *topo = detail->getTopology().getPointRef();
371  if (topo)
372  topo->bumpDataId();
373  topo = detail->getTopology().getVertexNextRef();
374  if (topo)
375  topo->bumpDataId();
376  topo = detail->getTopology().getVertexPrevRef();
377  if (topo)
378  topo->bumpDataId();
379  // Edge groups might also be affected, if any edges
380  // were on points that were split, so we bump their
381  // data IDs, just in case.
382  detail->edgeGroups().bumpAllDataIds();
383  }
384  return numpointsadded;
385 }
386 }
387 
388 GA_Size
390  GEO_Detail *detail,
391  const GA_ElementGroup *group,
392  const GA_Attribute *attrib,
393  fpreal tolerance)
394 {
395  bool isptgroup = (group == nullptr || group->getOwner() == GA_ATTRIB_POINT);
396  return geoSplitPointsByAttrib(
397  detail,
398  detail->getPointRange(isptgroup ? UTverify_cast<const GA_PointGroup *>(group) : nullptr),
399  isptgroup ? nullptr : group,
400  attrib,
401  tolerance);
402 }
403 
404 GA_Size
406  GEO_Detail *detail,
407  const GA_Range &points,
408  const GA_Attribute *attrib,
409  fpreal tolerance)
410 {
411  return geoSplitPointsByAttrib(
412  detail,
413  points,
414  nullptr,
415  attrib,
416  tolerance);
417 }
418 
419 } // End of HDK_Sample namespace
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:303
Definition of a geometry attribute.
Definition: GA_Attribute.h:196
Iteration over a range of elements.
Definition: GA_Iterator.h:28
GA_StorageClass
Definition: GA_Types.h:71
static GA_AttributeFilter selectPublic(bool include_noninternal_groups=true)
Select public scope attributes and non-internal groups.
bool blockAdvance(GA_Offset &start, GA_Offset &end)
bool GAisValid(GA_Size v)
Definition: GA_Types.h:647
int64 exint
Definition: SYS_Types.h:125
#define SYSabs(a)
Definition: SYS_Math.h:1523
void stdsort(ComparatorBool is_less)
Sort using std::sort. The ComparatorBool uses the less-than semantics.
Definition: UT_Array.h:301
GA_Size GEOsplitPointsByAttrib(GEO_Detail *detail, const GA_ElementGroup *group, const GA_Attribute *attrib, fpreal tolerance)
GA_EdgeGroupTable & edgeGroups()
Definition: GA_Detail.h:1131
exint size() const
Definition: UT_Array.h:458
void setSize(exint newsize)
Definition: UT_Array.h:478
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:233
#define GA_INVALID_OFFSET
Definition: GA_Types.h:676
A range of elements in an index-map.
Definition: GA_Range.h:42
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:33
virtual const GA_AIFCompare * getAIFCompare() const
Return the attribute's comparison interface or NULL.
bool combine(const GA_Group *src) overridefinal
#define UT_ASSERT_MSG(ZZ,...)
Definition: UT_Assert.h:138
GA_Size GA_Offset
Definition: GA_Types.h:639
bool atEnd() const
Definition: GA_Iterator.h:100
CompareResults OIIO_API compare(const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, ROI roi={}, int nthreads=0)
GLfloat GLfloat GLfloat v2
Definition: glew.h:1856
void wireVertexPoint(GA_Offset vtx, GA_Offset pt)
GA_Range getPointRange(const GA_PointGroup *group=0) const
Get a range of all points in the detail.
Definition: GA_Detail.h:1666
const GA_IndexMap & getPointMap() const
Definition: GA_Detail.h:684
SYS_FORCE_INLINE GA_Offset appendPointBlock(GA_Size npoints)
Append new points, returning the first offset of the contiguous block.
Definition: GA_Detail.h:281
GA_StorageClass getStorageClass() const
Returns the approximate type of the attribute.
SYS_FORCE_INLINE const GA_ATITopology * getVertexNextRef() const
Definition: GA_Topology.h:229
GLuint GLuint end
Definition: glew.h:1253
GA_AttributeSet & getAttributes()
Definition: GA_Detail.h:739
SYS_FORCE_INLINE GA_Offset pointVertex(GA_Offset point) const
Definition: GA_Detail.h:502
Attribute Interface class to perform comparisons on attributes.
Definition: GA_AIFCompare.h:27
SYS_FORCE_INLINE T get(GA_Offset off, int comp=0) const
Definition: GA_Handle.h:193
SYS_FORCE_INLINE GA_Offset lastOffset() const
Definition: GA_IndexMap.h:179
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:633
GLuint start
Definition: glew.h:1253
exint append()
Definition: UT_Array.h:95
SYS_FORCE_INLINE GA_Offset vertexToNextVertex(GA_Offset vtx) const
Definition: GA_Detail.h:514
GA_GroupType classType() const
Definition: GA_Group.h:54
GA_Topology & getTopology()
Definition: GA_Detail.h:741
SYS_FORCE_INLINE bool isValid() const
Definition: GA_Handle.h:184
SYS_FORCE_INLINE GA_Index pointIndex(GA_Offset offset) const
Given a point's data offset, return its index.
Definition: GA_Detail.h:300
GA_AttributeOwner getOwner() const
SYS_FORCE_INLINE GA_Offset vertexPrimitive(GA_Offset vertex) const
Definition: GA_Detail.h:488
SYS_FORCE_INLINE const GA_ATITopology * getVertexPrevRef() const
Definition: GA_Topology.h:227
SYS_FORCE_INLINE GA_Offset appendPointOffset()
Definition: GEO_Detail.h:1128
GA_AttributeOwner
Definition: GA_Types.h:33
virtual bool isEqual(bool &result, const GA_Attribute &a, GA_Offset ai, const GA_Attribute &b, GA_Offset bi) const =0
fpreal64 fpreal
Definition: SYS_Types.h:277
GA_GroupType
An ordinal enum for the different types of groups in GA.
Definition: GA_Types.h:159
GLuint GLdouble GLdouble GLint GLint const GLdouble * points
Definition: glew.h:3446
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:135
void bumpAllDataIds(GA_AttributeOwner owner)
Bumps all data IDs of attributes of the specified owner.
GA_Size GEOsplitPoints(GEO_Detail *detail, const GA_ElementGroup *group)
UT_UniquePtr< GA_PointGroup > GA_PointGroupUPtr
SYS_FORCE_INLINE bool isEmpty() const
Query whether the group is empty of primary elements.
SYS_FORCE_INLINE const GA_ATITopology * getPointRef() const
Definition: GA_Topology.h:221
GLfloat GLfloat v1
Definition: glew.h:1852
GLboolean GLuint group
Definition: glew.h:2745
SYS_FORCE_INLINE bool contains(GA_Offset offset) const
SYS_FORCE_INLINE GA_Size getNumPoints() const
Return the number of points.
Definition: GA_Detail.h:285
GLintptr offset
Definition: glew.h:1682