Skip to content

Commit 6218356

Browse files
authored
Earclip (#564)
* earclip compiles * some tests passing * 46 tests passing * refactor * cleanup * new cost * cleanup * fixed TouchingHole * more fixes * only 5 tests failing * fixed short sides * fixed Sponge2 * better cost * improved keyholing * Moved Rect to public * added top/bottom check * fixed bBox * Polygon tests pass * fixed Sponge3a * another fix, another test * keyhole refactor * Added ClipDegenerates * Refactor FindStarts * fixed bugs * fixed Sponge4 * refactor clipped * another bug, another test * another bug, another test * Sponge4 passing * all tests passing * removed old triangulator * fix isfinite * debug checks * remove 2-manifold check * added comments * fixed missing point bug * another test, another fix * two tests added and fixed * fix build * fixed CoplanarProp * fixed seg fault * reenable 2-manifold check * refactor for polygon area * factored out loop * tests passing * another test, another fix * another test, another fix * fixed dedupe segfault * ignore duplicated verts * re-enabled turn180 * re-enabled duplicate tests * another test, another fix * refactor keyholing * more precise area computation * disabled a test * added test * another test, another test * fixed bridge bound * fewer short edges * replaced intersection sorting with InsideEdge * another test, another fix * allow a few degenerates in Sponge4
1 parent bec8164 commit 6218356

File tree

14 files changed

+2416
-1116
lines changed

14 files changed

+2416
-1116
lines changed

.vscode/settings.json

+1
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,5 @@
144144
"editor.defaultFormatter": "ms-python.black-formatter"
145145
},
146146
"python.formatting.provider": "none",
147+
"clang-format.executable": "clang-format",
147148
}

bindings/c/include/manifoldc.h

-2
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,6 @@ ManifoldRect *manifold_rect_mul(void *mem, ManifoldRect *r, float x, float y);
301301
int manifold_rect_does_overlap_rect(ManifoldRect *a, ManifoldRect *r);
302302
int manifold_rect_is_empty(ManifoldRect *r);
303303
int manifold_rect_is_finite(ManifoldRect *r);
304-
ManifoldCrossSection *manifold_rect_as_cross_section(void *mem,
305-
ManifoldRect *r);
306304

307305
// Bounding Box
308306

bindings/c/rect.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,3 @@ int manifold_rect_does_overlap_rect(ManifoldRect *a, ManifoldRect *r) {
9898
int manifold_rect_is_empty(ManifoldRect *r) { return from_c(r)->IsEmpty(); }
9999

100100
int manifold_rect_is_finite(ManifoldRect *r) { return from_c(r)->IsFinite(); }
101-
102-
ManifoldCrossSection *manifold_rect_as_cross_section(void *mem,
103-
ManifoldRect *r) {
104-
auto cs = from_c(r)->AsCrossSection();
105-
return to_c(new (mem) CrossSection(cs));
106-
}

src/cross_section/include/cross_section.h

-62
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424

2525
namespace manifold {
2626

27-
class Rect;
28-
2927
/** @addtogroup Core
3028
* @{
3129
*/
@@ -172,64 +170,4 @@ class CrossSection {
172170
std::shared_ptr<const PathImpl> GetPaths() const;
173171
};
174172
/** @} */
175-
176-
/** @addtogroup Connections
177-
* @{
178-
*/
179-
180-
/**
181-
* Axis-aligned rectangular bounds.
182-
*/
183-
class Rect {
184-
public:
185-
glm::vec2 min = glm::vec2(0);
186-
glm::vec2 max = glm::vec2(0);
187-
188-
/** @name Creation
189-
* Constructors
190-
*/
191-
///@{
192-
Rect();
193-
~Rect();
194-
Rect(const Rect& other);
195-
Rect& operator=(const Rect& other);
196-
Rect(Rect&&) noexcept;
197-
Rect& operator=(Rect&&) noexcept;
198-
Rect(const glm::vec2 a, const glm::vec2 b);
199-
///@}
200-
201-
/** @name Information
202-
* Details of the rectangle
203-
*/
204-
///@{
205-
glm::vec2 Size() const;
206-
float Area() const;
207-
float Scale() const;
208-
glm::vec2 Center() const;
209-
bool Contains(const glm::vec2& pt) const;
210-
bool Contains(const Rect& other) const;
211-
bool DoesOverlap(const Rect& other) const;
212-
bool IsEmpty() const;
213-
bool IsFinite() const;
214-
///@}
215-
216-
/** @name Modification
217-
*/
218-
///@{
219-
void Union(const glm::vec2 p);
220-
Rect Union(const Rect& other) const;
221-
Rect operator+(const glm::vec2 shift) const;
222-
Rect& operator+=(const glm::vec2 shift);
223-
Rect operator*(const glm::vec2 scale) const;
224-
Rect& operator*=(const glm::vec2 scale);
225-
Rect Transform(const glm::mat3x2& m) const;
226-
///@}
227-
228-
/** @name Conversion
229-
*/
230-
///@{
231-
CrossSection AsCrossSection() const;
232-
///@}
233-
};
234-
/** @} */
235173
} // namespace manifold

src/cross_section/src/cross_section.cpp

-163
Original file line numberDiff line numberDiff line change
@@ -742,167 +742,4 @@ Polygons CrossSection::ToPolygons() const {
742742
}
743743
return polys;
744744
}
745-
746-
// Rect
747-
748-
/**
749-
* Default constructor is an empty rectangle..
750-
*/
751-
Rect::Rect() {}
752-
753-
Rect::~Rect() = default;
754-
Rect::Rect(Rect&&) noexcept = default;
755-
Rect& Rect::operator=(Rect&&) noexcept = default;
756-
Rect::Rect(const Rect& other) {
757-
min = glm::vec2(other.min);
758-
max = glm::vec2(other.max);
759-
}
760-
761-
/**
762-
* Create a rectangle that contains the two given points.
763-
*/
764-
Rect::Rect(const glm::vec2 a, const glm::vec2 b) {
765-
min = glm::min(a, b);
766-
max = glm::max(a, b);
767-
}
768-
769-
/**
770-
* Return the dimensions of the rectangle.
771-
*/
772-
glm::vec2 Rect::Size() const { return max - min; }
773-
774-
/**
775-
* Return the area of the rectangle.
776-
*/
777-
float Rect::Area() const {
778-
auto sz = Size();
779-
return sz.x * sz.y;
780-
}
781-
782-
/**
783-
* Returns the absolute-largest coordinate value of any contained
784-
* point.
785-
*/
786-
float Rect::Scale() const {
787-
glm::vec2 absMax = glm::max(glm::abs(min), glm::abs(max));
788-
return glm::max(absMax.x, absMax.y);
789-
}
790-
791-
/**
792-
* Returns the center point of the rectangle.
793-
*/
794-
glm::vec2 Rect::Center() const { return 0.5f * (max + min); }
795-
796-
/**
797-
* Does this rectangle contain (includes on border) the given point?
798-
*/
799-
bool Rect::Contains(const glm::vec2& p) const {
800-
return glm::all(glm::greaterThanEqual(p, min)) &&
801-
glm::all(glm::greaterThanEqual(max, p));
802-
}
803-
804-
/**
805-
* Does this rectangle contain (includes equal) the given rectangle?
806-
*/
807-
bool Rect::Contains(const Rect& rect) const {
808-
return glm::all(glm::greaterThanEqual(rect.min, min)) &&
809-
glm::all(glm::greaterThanEqual(max, rect.max));
810-
}
811-
812-
/**
813-
* Does this rectangle overlap the one given (including equality)?
814-
*/
815-
bool Rect::DoesOverlap(const Rect& rect) const {
816-
return min.x <= rect.max.x && min.y <= rect.max.y && max.x >= rect.min.x &&
817-
max.y >= rect.min.y;
818-
}
819-
820-
/**
821-
* Is the rectangle empty (containing no space)?
822-
*/
823-
bool Rect::IsEmpty() const { return max.y <= min.y || max.x <= min.x; };
824-
825-
/**
826-
* Does this recangle have finite bounds?
827-
*/
828-
bool Rect::IsFinite() const {
829-
return glm::all(glm::isfinite(min)) && glm::all(glm::isfinite(max));
830-
}
831-
832-
/**
833-
* Expand this rectangle (in place) to include the given point.
834-
*/
835-
void Rect::Union(const glm::vec2 p) {
836-
min = glm::min(min, p);
837-
max = glm::max(max, p);
838-
}
839-
840-
/**
841-
* Expand this rectangle to include the given Rect.
842-
*/
843-
Rect Rect::Union(const Rect& rect) const {
844-
Rect out;
845-
out.min = glm::min(min, rect.min);
846-
out.max = glm::max(max, rect.max);
847-
return out;
848-
}
849-
850-
/**
851-
* Shift this rectangle by the given vector.
852-
*/
853-
Rect Rect::operator+(const glm::vec2 shift) const {
854-
Rect out;
855-
out.min = min + shift;
856-
out.max = max + shift;
857-
return out;
858-
}
859-
860-
/**
861-
* Shift this rectangle in-place by the given vector.
862-
*/
863-
Rect& Rect::operator+=(const glm::vec2 shift) {
864-
min += shift;
865-
max += shift;
866-
return *this;
867-
}
868-
869-
/**
870-
* Scale this rectangle by the given vector.
871-
*/
872-
Rect Rect::operator*(const glm::vec2 scale) const {
873-
Rect out;
874-
out.min = min * scale;
875-
out.max = max * scale;
876-
return out;
877-
}
878-
879-
/**
880-
* Scale this rectangle in-place by the given vector.
881-
*/
882-
Rect& Rect::operator*=(const glm::vec2 scale) {
883-
min *= scale;
884-
max *= scale;
885-
return *this;
886-
}
887-
888-
/**
889-
* Transform the rectangle by the given axis-aligned affine transform.
890-
*
891-
* Ensure the transform passed in is axis-aligned (rotations are all
892-
* multiples of 90 degrees), or else the resulting rectangle will no longer
893-
* bound properly.
894-
*/
895-
Rect Rect::Transform(const glm::mat3x2& m) const {
896-
Rect rect;
897-
rect.min = m * glm::vec3(min, 1);
898-
rect.max = m * glm::vec3(max, 1);
899-
return rect;
900-
}
901-
902-
/**
903-
* Return a CrossSection with an outline defined by this axis-aligned
904-
* rectangle.
905-
*/
906-
CrossSection Rect::AsCrossSection() const { return CrossSection(*this); }
907-
908745
} // namespace manifold

src/manifold/src/edge_op.cpp

+62-11
Original file line numberDiff line numberDiff line change
@@ -145,24 +145,29 @@ namespace manifold {
145145
void Manifold::Impl::SimplifyTopology() {
146146
if (!halfedge_.size()) return;
147147

148-
int nbEdges = halfedge_.size();
148+
const int nbEdges = halfedge_.size();
149+
auto policy = autoPolicy(nbEdges);
149150
int numFlagged = 0;
150151
Vec<uint8_t> bflags(nbEdges);
151152

152-
Vec<SortEntry> entries(nbEdges);
153-
auto policy = autoPolicy(halfedge_.size());
154-
for_each_n(policy, countAt(0), nbEdges, [&](int i) {
155-
entries[i].start = halfedge_[i].startVert;
156-
entries[i].end = halfedge_[i].endVert;
157-
entries[i].index = i;
158-
});
153+
// In the case of a very bad triangulation, it is possible to create pinched
154+
// verts. They must be removed before edge collapse.
155+
SplitPinchedVerts();
156+
157+
Vec<SortEntry> entries;
158+
entries.reserve(nbEdges / 2);
159+
for (int i = 0; i < nbEdges; ++i) {
160+
if (halfedge_[i].IsForward()) {
161+
entries.push_back({halfedge_[i].startVert, halfedge_[i].endVert, i});
162+
}
163+
}
159164

160165
stable_sort(policy, entries.begin(), entries.end());
161-
for (int i = 0; i < nbEdges - 1; ++i) {
166+
for (int i = 0; i < entries.size() - 1; ++i) {
162167
if (entries[i].start == entries[i + 1].start &&
163168
entries[i].end == entries[i + 1].end) {
164-
DedupeEdge(std::min(entries[i].index, entries[i + 1].index));
165-
entries[i + 1].index = std::max(entries[i].index, entries[i + 1].index);
169+
DedupeEdge(entries[i].index);
170+
numFlagged++;
166171
}
167172
}
168173

@@ -243,6 +248,8 @@ void Manifold::Impl::SimplifyTopology() {
243248
#endif
244249
}
245250

251+
// Deduplicate the given 4-manifold edge by duplicating endVert, thus making the
252+
// edges distinct. Also duplicates startVert if it becomes pinched.
246253
void Manifold::Impl::DedupeEdge(const int edge) {
247254
// Orbit endVert
248255
const int startVert = halfedge_[edge].startVert;
@@ -251,6 +258,7 @@ void Manifold::Impl::DedupeEdge(const int edge) {
251258
while (current != edge) {
252259
const int vert = halfedge_[current].startVert;
253260
if (vert == startVert) {
261+
// Single topological unit needs 2 faces added to be split
254262
const int newVert = vertPos_.size();
255263
vertPos_.push_back(vertPos_[endVert]);
256264
if (vertNormal_.size() > 0) vertNormal_.push_back(vertNormal_[endVert]);
@@ -297,6 +305,45 @@ void Manifold::Impl::DedupeEdge(const int edge) {
297305

298306
current = halfedge_[NextHalfedge(current)].pairedHalfedge;
299307
}
308+
309+
if (current == edge) {
310+
// Separate topological unit needs no new faces to be split
311+
const int newVert = vertPos_.size();
312+
vertPos_.push_back(vertPos_[endVert]);
313+
if (vertNormal_.size() > 0) vertNormal_.push_back(vertNormal_[endVert]);
314+
315+
do {
316+
halfedge_[current].endVert = newVert;
317+
current = NextHalfedge(current);
318+
halfedge_[current].startVert = newVert;
319+
current = halfedge_[current].pairedHalfedge;
320+
} while (current != edge);
321+
}
322+
323+
// Orbit startVert
324+
const int pair = halfedge_[edge].pairedHalfedge;
325+
current = halfedge_[NextHalfedge(pair)].pairedHalfedge;
326+
while (current != pair) {
327+
const int vert = halfedge_[current].startVert;
328+
if (vert == endVert) {
329+
break; // Connected: not a pinched vert
330+
}
331+
current = halfedge_[NextHalfedge(current)].pairedHalfedge;
332+
}
333+
334+
if (current == pair) {
335+
// Split the pinched vert the previous split created.
336+
const int newVert = vertPos_.size();
337+
vertPos_.push_back(vertPos_[endVert]);
338+
if (vertNormal_.size() > 0) vertNormal_.push_back(vertNormal_[endVert]);
339+
340+
do {
341+
halfedge_[current].endVert = newVert;
342+
current = NextHalfedge(current);
343+
halfedge_[current].startVert = newVert;
344+
current = halfedge_[current].pairedHalfedge;
345+
} while (current != pair);
346+
}
300347
}
301348

302349
void Manifold::Impl::PairUp(int edge0, int edge1) {
@@ -380,6 +427,10 @@ void Manifold::Impl::RemoveIfFolded(int edge) {
380427
}
381428
}
382429

430+
// Collapses the given edge by removing startVert. May split the mesh
431+
// topologically if the collapse would have resulted in a 4-manifold edge. Do
432+
// not collapse an edge if startVert is pinched - the vert will be marked NaN,
433+
// but other edges may still be pointing to it.
383434
void Manifold::Impl::CollapseEdge(const int edge, std::vector<int>& edges) {
384435
Vec<TriRef>& triRef = meshRelation_.triRef;
385436
Vec<glm::ivec3>& triProp = meshRelation_.triProperties;

0 commit comments

Comments
 (0)