From 745eb9b6258c9969ff0ed6f54aadf26a494827f8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 13 Aug 2023 16:37:01 +0200 Subject: [PATCH] First tests pass for triangles. --- src/db/db/dbTriangles.cc | 368 ++++++-------------------- src/db/db/dbTriangles.h | 90 ++++++- src/db/unit_tests/dbTrianglesTests.cc | 118 ++++++++- 3 files changed, 288 insertions(+), 288 deletions(-) diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index 4332e6fec..5ac17e981 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -89,7 +89,7 @@ Triangles::remove (db::Triangle *tri) // clean up edges we do no longer need for (int i = 0; i < 3; ++i) { - if (edges [i]->left () == 0 && edges [i]->right () == 0) { + if (edges [i] && edges [i]->left () == 0 && edges [i]->right () == 0) { delete edges [i]; } } @@ -494,97 +494,118 @@ Triangles::insert_new_vertex (db::Vertex *vertex, std::vector *n } void -Triangles::add_more_triangles (const std::vector &new_triangles, +Triangles::add_more_triangles (std::vector &new_triangles, db::TriangleEdge *incoming_edge, db::Vertex *from_vertex, db::Vertex *to_vertex, db::TriangleEdge *conn_edge) { -#if 0 - while True: + while (true) { - next_edge = None - for s in from_vertex.edges: - if not s.has_vertex(to_vertex) and s.is_outside(): - # TODO: remove and break - assert(next_edge is None) - next_edge = s + db::TriangleEdge *next_edge = 0; - assert (next_edge is not None) - next_vertex = next_edge.other_vertex(from_vertex) + for (auto e = from_vertex->begin_edges (); e != from_vertex->end_edges (); ++e) { + if (! e->has_vertex (to_vertex) && e->is_outside ()) { + // TODO: remove and break + tl_assert (next_edge == 0); + next_edge = const_cast (e.operator-> ()); + } + } - d_from_to = sub(to_vertex, from_vertex) - incoming_vertex = incoming_edge.other_vertex(from_vertex) - if vprod_sign(sub(from_vertex, incoming_vertex), d_from_to) * vprod_sign(sub(from_vertex, next_vertex), d_from_to) >= 0: - return + tl_assert (next_edge != 0); + db::Vertex *next_vertex = next_edge->other (from_vertex); - next_conn_edge = TriangleEdge(next_vertex, to_vertex) - t = Triangle(next_conn_edge, next_edge, conn_edge) - self.triangles.append(t) - new_triangles.append(t) + db::DVector d_from_to = *to_vertex - *from_vertex; + db::Vertex *incoming_vertex = incoming_edge->other (from_vertex); + if (db::vprod_sign(*from_vertex - *incoming_vertex, d_from_to) * db::vprod_sign(*from_vertex - *next_vertex, d_from_to) >= 0) { + return; + } - incoming_edge = next_edge - conn_edge = next_conn_edge - from_vertex = next_vertex -#endif + db::TriangleEdge *next_conn_edge = create_edge (next_vertex, to_vertex); + db::Triangle *t = create_triangle (next_conn_edge, next_edge, conn_edge); + new_triangles.push_back (t); + + incoming_edge = next_edge; + conn_edge = next_conn_edge; + from_vertex = next_vertex; + + } } void Triangles::split_triangle (db::Triangle *t, db::Vertex *vertex, std::vector *new_triangles_out) { -#if 0 - # TODO: this is not quite efficient - self.triangles.remove(t) - t.unlink() + t->unlink (); - new_edges = {} - for v in t.vertexes: - new_edges[v] = TriangleEdge(v, vertex) + std::map v2new_edges; + std::vector new_edges; + for (int i = 0; i < 3; ++i) { + db::Vertex *v = t->vertex (i); + db::TriangleEdge *e = create_edge (v, vertex); + v2new_edges[v] = e; + new_edges.push_back (e); + } - new_triangles = [] - for s in t.edges(): - new_triangle = Triangle(s, new_edges[s.p1], new_edges[s.p2]) - if new_triangles_out is not None: - new_triangles_out.append(new_triangle) - new_triangle.is_outside = t.is_outside - new_triangles.append(new_triangle) - self.triangles.append(new_triangle) + std::vector new_triangles; + for (int i = 0; i < 3; ++i) { + db::TriangleEdge *e = t->edge (i); + db::Triangle *new_triangle = create_triangle (e, v2new_edges[e->v1 ()], v2new_edges[e->v2 ()]); + if (new_triangles_out) { + new_triangles_out->push_back (new_triangle); + } + new_triangle->set_outside (t->is_outside ()); + new_triangles.push_back (new_triangle); + } - return self._fix_triangles(new_triangles, new_edges.values(), new_triangles_out) -#endif + remove (t); + + fix_triangles (new_triangles, new_edges, new_triangles_out); } void Triangles::split_triangles_on_edge (const std::vector &tris, db::Vertex *vertex, db::TriangleEdge *split_edge, std::vector *new_triangles_out) { -#if 0 - split_edge.unlink() + TriangleEdge *s1 = create_edge (split_edge->v1 (), vertex); + TriangleEdge *s2 = create_edge (split_edge->v2 (), vertex); + s1->set_is_segment (split_edge->is_segment ()); + s2->set_is_segment (split_edge->is_segment ()); - s1 = TriangleEdge(split_edge.p1, vertex) - s2 = TriangleEdge(split_edge.p2, vertex) - s1.is_segment = split_edge.is_segment - s2.is_segment = split_edge.is_segment + std::vector new_triangles; - new_triangles = [] + for (auto t = tris.begin (); t != tris.end (); ++t) { - for t in tris: + (*t)->unlink (); - self.triangles.remove(t) + db::Vertex *ext_vertex = (*t)->opposite (split_edge); + TriangleEdge *new_edge = create_edge (ext_vertex, vertex); - ext_vertex = t.ext_vertex(split_edge) - new_edge = TriangleEdge(ext_vertex, vertex) + for (int i = 0; i < 3; ++i) { - for s in t.edges(): - if s.has_vertex(ext_vertex): - partial = s1 if s.has_vertex(split_edge.p1) else s2 - new_triangle = Triangle(new_edge, partial, s) - if new_triangles_out is not None: - new_triangles_out.append(new_triangle) - new_triangle.is_outside = t.is_outside - new_triangles.append(new_triangle) - self.triangles.append(new_triangle) + db::TriangleEdge *e = (*t)->edge (i); + if (e->has_vertex (ext_vertex)) { - return self._fix_triangles(new_triangles, [s1, s2], new_triangles_out) -#endif + TriangleEdge *partial = e->has_vertex (split_edge->v1 ()) ? s1 : s2; + db::Triangle *new_triangle = create_triangle (new_edge, partial, e); + + if (new_triangles_out) { + new_triangles_out->push_back (new_triangle); + } + new_triangle->set_outside ((*t)->is_outside ()); + new_triangles.push_back (new_triangle); + + } + + } + + } + + for (auto t = tris.begin (); t != tris.end (); ++t) { + remove (*t); + } + + std::vector fixed_edges; + fixed_edges.push_back (s1); + fixed_edges.push_back (s2); + fix_triangles (new_triangles, fixed_edges, new_triangles_out); } std::vector @@ -1031,29 +1052,6 @@ from .triangle import * class Triangles(object): - def insert(self, vertex, new_triangles: [Vertex] = None): - - - def find_triangle_for_vertex(self, p: Point) -> [Triangle]: - - edge = self.find_closest_edge(p) - if edge is not None: - return [ t for t in [ edge.left, edge.right ] if t is not None and t.contains(p) >= 0 ] - else: - return [] - - def find_vertex_for_point(self, p: Point) -> TriangleEdge: - - edge = self.find_closest_edge(p) - if edge is None: - return None - v = None - if equals(edge.p1, p): - v = edge.p1 - elif equals(edge.p2, p): - v = edge.p2 - return v - def find_edge_for_points(self, p1: Point, p2: Point) -> TriangleEdge: v = self.find_vertex_for_point(p1) @@ -1064,64 +1062,6 @@ class Triangles(object): return s return None - def find_closest_edge(self, p: Point, nstart: int = None, vstart: Vertex = None, inside_only = False) -> TriangleEdge: - - if nstart is None and vstart is None: - if len(self.vertexes) > 0: - vstart = self.vertexes[0] - else: - return None - elif nstart is not None: - if len(self.vertexes) > nstart: - vstart = self.vertexes[nstart] - else: - return None - elif vstart is None: - return None - - line = Edge(vstart, p) - - d = None - edge = None - - v = vstart - - while v is not None: - - vnext = None - - for s in v.edges: - if inside_only: - # NOTE: in inside mode we stay on the line of sight as we don't - # want to walk around outside pockets. - if not s.is_segment and s.is_for_outside_triangles(): - continue - if not s.crosses_including(line): - continue - ds = s.distance(p) - if d is None or ds < d: - d = ds - edge = s - vnext = edge.other_vertex(v) - elif abs(ds - d) < max(1, abs(ds) + abs(d)) * 1e-10: - # this differentiation selects the edge which bends further towards - # the target point if both edges share a common point and that - # is the one the determines the distance. - cv = edge.common_vertex(s) - if cv is not None: - edge_d = sub(edge.other_vertex(cv), cv) - s_d = sub(s.other_vertex(cv), cv) - r = sub(p, cv) - edge_sp = sprod(r, edge_d) / math.sqrt(square(edge_d)) - s_sp = sprod(r, s_d) / math.sqrt(square(s_d)) - if s_sp > edge_sp: - edge = s - vnext = edge.other_vertex(v) - - v = vnext - - return edge - def search_edges_crossing(self, edge: TriangleEdge) -> set: """ Finds all edges that cross the given one for a convex triangulation @@ -1175,146 +1115,6 @@ class Triangles(object): assert (next_edge is not None) - def _insert_new_vertex(self, vertex, new_triangles_out: [Vertex] = None): - - if len(self.vertexes) <= 3: - if len(self.vertexes) == 3: - # form the first triangle - s1 = TriangleEdge(self.vertexes[0], self.vertexes[1]) - s2 = TriangleEdge(self.vertexes[1], self.vertexes[2]) - s3 = TriangleEdge(self.vertexes[2], self.vertexes[0]) - # avoid degenerate Triangles to happen here @@@ - if vprod_sign(s1.d(), s2.d()) == 0: - self.vertexes = [] # reject some points? - else: - t = Triangle(s1, s2, s3) - if new_triangles_out is not None: - new_triangles_out.append(t) - self.triangles.append(t) - return 0 - - new_triangles = [] - - # Find closest edge - closest_edge = self.find_closest_edge(vertex) - assert(closest_edge is not None) - - s1 = TriangleEdge(vertex, closest_edge.p1) - s2 = TriangleEdge(vertex, closest_edge.p2) - - t = Triangle(s1, closest_edge, s2) - self.triangles.append(t) - new_triangles.append(t) - - self._add_more_triangles(new_triangles, closest_edge, closest_edge.p1, vertex, s1) - self._add_more_triangles(new_triangles, closest_edge, closest_edge.p2, vertex, s2) - - if new_triangles_out is not None: - new_triangles_out += new_triangles - - return self._fix_triangles(new_triangles, [], new_triangles_out) - - def _add_more_triangles(self, new_triangles, incoming_edge: TriangleEdge, from_vertex: Vertex, to_vertex: Vertex, conn_edge: TriangleEdge): - - while True: - - next_edge = None - for s in from_vertex.edges: - if not s.has_vertex(to_vertex) and s.is_outside(): - # TODO: remove and break - assert(next_edge is None) - next_edge = s - - assert (next_edge is not None) - next_vertex = next_edge.other_vertex(from_vertex) - - d_from_to = sub(to_vertex, from_vertex) - incoming_vertex = incoming_edge.other_vertex(from_vertex) - if vprod_sign(sub(from_vertex, incoming_vertex), d_from_to) * vprod_sign(sub(from_vertex, next_vertex), d_from_to) >= 0: - return - - next_conn_edge = TriangleEdge(next_vertex, to_vertex) - t = Triangle(next_conn_edge, next_edge, conn_edge) - self.triangles.append(t) - new_triangles.append(t) - - incoming_edge = next_edge - conn_edge = next_conn_edge - from_vertex = next_vertex - - - def _split_triangle(self, t, vertex, new_triangles_out: [Vertex] = None): - - # TODO: this is not quite efficient - self.triangles.remove(t) - t.unlink() - - new_edges = {} - for v in t.vertexes: - new_edges[v] = TriangleEdge(v, vertex) - - new_triangles = [] - for s in t.edges(): - new_triangle = Triangle(s, new_edges[s.p1], new_edges[s.p2]) - if new_triangles_out is not None: - new_triangles_out.append(new_triangle) - new_triangle.is_outside = t.is_outside - new_triangles.append(new_triangle) - self.triangles.append(new_triangle) - - return self._fix_triangles(new_triangles, new_edges.values(), new_triangles_out) - - def _split_triangles_on_edge(self, tris, vertex: Vertex, split_edge: TriangleEdge, new_triangles_out: [Vertex] = None): - - split_edge.unlink() - - s1 = TriangleEdge(split_edge.p1, vertex) - s2 = TriangleEdge(split_edge.p2, vertex) - s1.is_segment = split_edge.is_segment - s2.is_segment = split_edge.is_segment - - new_triangles = [] - - for t in tris: - - self.triangles.remove(t) - - ext_vertex = t.ext_vertex(split_edge) - new_edge = TriangleEdge(ext_vertex, vertex) - - for s in t.edges(): - if s.has_vertex(ext_vertex): - partial = s1 if s.has_vertex(split_edge.p1) else s2 - new_triangle = Triangle(new_edge, partial, s) - if new_triangles_out is not None: - new_triangles_out.append(new_triangle) - new_triangle.is_outside = t.is_outside - new_triangles.append(new_triangle) - self.triangles.append(new_triangle) - - return self._fix_triangles(new_triangles, [s1, s2], new_triangles_out) - - def _is_illegal_edge(self, edge): - - left = edge.left - right = edge.right - if left is None or right is None: - return False - - center, radius = left.circumcircle() - if right.ext_vertex(edge).in_circle(center, radius) > 0: - return True - - center, radius = right.circumcircle() - if left.ext_vertex(edge).in_circle(center, radius) > 0: - return True - - return False - - def flipped_edge(self, s: TriangleEdge) -> Edge: - - return Edge(s.left.ext_vertex(s), s.right.ext_vertex(s)) - def _ensure_edge_inner(self, edge: Edge) -> [TriangleEdge]: crossed_edges = self.search_edges_crossing(edge) diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h index f59ab9e96..d1ed0c8e4 100644 --- a/src/db/db/dbTriangles.h +++ b/src/db/db/dbTriangles.h @@ -39,18 +39,77 @@ class Layout; class DB_PUBLIC Triangles { public: + typedef tl::shared_collection triangles_type; + typedef triangles_type::const_iterator triangle_iterator; + Triangles (); ~Triangles (); + /** + * @brief Initializes the triangle collection with a box + * Two triangles will be created. + */ void init_box (const db::DBox &box); + + /** + * @brief Returns a string representation of the triangle graph. + */ std::string to_string (); + /** + * @brief Returns the bounding box of the triangle graph. + */ db::DBox bbox () const; + /** + * @brief Iterates the triangles in the graph (begin iterator) + */ + triangle_iterator begin () const { return mp_triangles.begin (); } + + /** + * @brief Iterates the triangles in the graph (end iterator) + */ + triangle_iterator end () const { return mp_triangles.end (); } + + /** + * @brief Returns the number of triangles in the graph + */ + size_t num_triangles () const { return mp_triangles.size (); } + + /** + * @brief Checks the triangle graph for consistency + * This method is for testing purposes mainly. + */ bool check (bool check_delaunay = true) const; + + /** + * @brief Dumps the triangle graph to a GDS file at the given path + * This method is for testing purposes mainly. + */ void dump (const std::string &path) const; + /** + * @brief Creates a new layout object representing the triangle graph + * This method is for testing purposes mainly. + */ + db::Layout *to_layout () const; + + /** + * @brief Creates a new vertex object + * + * In order to insert the new vertex into the graph, use "insert". + * Normally, "insert_point" should be used which creates a vertex and + * inserts it. + */ db::Vertex *create_vertex (double x, double y); + + /** + * @brief Creates a new vertex object + * + * In order to insert the new vertex into the graph, use "insert". + * Normally, "insert_point" should be used which creates a vertex and + * inserts it. + */ db::Vertex *create_vertex (const db::DPoint &pt); /** @@ -58,10 +117,37 @@ public: */ std::vector find_points_around (Vertex *vertex, double radius); + /** + * @brief Inserts a new vertex as the given point + * + * If "new_triangles" is not null, it will receive the list of new triangles created during + * the remove step. + */ db::Vertex *insert_point (const db::DPoint &point, std::vector *new_triangles = 0); + + /** + * @brief Inserts the given vertex + * + * If "new_triangles" is not null, it will receive the list of new triangles created during + * the remove step. + * The return value is the actual vertex created. It is no necessarily the one passed. When + * a vertex already exists at the given location, the input vertex is ignored and the present + * vertex is returned. + */ db::Vertex *insert (db::Vertex *vertex, std::vector *new_triangles = 0); + + /** + * @brief Removes the given vertex + * + * If "new_triangles" is not null, it will receive the list of new triangles created during + * the remove step. + */ void remove (db::Vertex *vertex, std::vector *new_triangles = 0); + + // exposed for testing purposes: + std::pair, db::TriangleEdge *> flip (TriangleEdge *edge); + private: tl::shared_collection mp_triangles; tl::weak_collection mp_edges; @@ -76,19 +162,17 @@ private: // NOTE: these functions are SLOW and intended to test purposes only std::vector find_touching (const db::DBox &box) const; std::vector find_inside_circle (const db::DPoint ¢er, double radius) const; - db::Layout *to_layout () const; void remove_outside_vertex (db::Vertex *vertex, std::vector *new_triangles = 0); void remove_inside_vertex (db::Vertex *vertex, std::vector *new_triangles_out = 0); std::vector fill_concave_corners (const std::vector &edges); int fix_triangles(const std::vector &tris, const std::vector &fixed_edges, std::vector *new_triangles); - std::pair, db::TriangleEdge *> flip (TriangleEdge *edge); static bool is_illegal_edge (db::TriangleEdge *edge); std::vector find_triangle_for_point (const db::DPoint &point); db::TriangleEdge *find_closest_edge (const db::DPoint &p, db::Vertex *vstart = 0, bool inside_only = false); void split_triangle (db::Triangle *t, db::Vertex *vertex, std::vector *new_triangles_out); void split_triangles_on_edge (const std::vector &tris, db::Vertex *vertex, db::TriangleEdge *split_edge, std::vector *new_triangles_out); - void add_more_triangles (const std::vector &new_triangles, + void add_more_triangles (std::vector &new_triangles, db::TriangleEdge *incoming_edge, db::Vertex *from_vertex, db::Vertex *to_vertex, db::TriangleEdge *conn_edge); diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index e30e4e6e0..a293bdc82 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -24,12 +24,128 @@ #include "dbTriangles.h" #include "tlUnitTest.h" -TEST(1) +TEST(Triangle_basic) { db::Triangles tris; tris.init_box (db::DBox (1, 0, 5, 4)); EXPECT_EQ (tris.bbox ().to_string (), "(1,0;5,4)"); + EXPECT_EQ (tris.to_string (), "((1, 0), (1, 4), (5, 0)), ((1, 4), (5, 4), (5, 0))"); EXPECT_EQ (tris.check (), true); } + +TEST(Triangle_flip) +{ + db::Triangles tris; + tris.init_box (db::DBox (0, 0, 1, 1)); + EXPECT_EQ (tris.to_string (), "((0, 0), (0, 1), (1, 0)), ((0, 1), (1, 1), (1, 0))"); + + EXPECT_EQ (tris.num_triangles (), size_t (2)); + + const db::Triangle &t1 = *tris.begin (); + db::TriangleEdge *diag_segment; + for (int i = 0; i < 3; ++i) { + diag_segment = t1.edge (i); + if (diag_segment->side_of (db::DPoint (0.5, 0.5)) == 0) { + break; + } + } + tris.flip (diag_segment); + EXPECT_EQ (tris.to_string (), "((1, 1), (0, 0), (0, 1)), ((1, 1), (1, 0), (0, 0))"); + EXPECT_EQ (tris.check (), true); +} + +TEST(Triangle_insert) +{ + db::Triangles tris; + tris.init_box (db::DBox (0, 0, 1, 1)); + + tris.insert (tris.create_vertex (0.2, 0.2)); + EXPECT_EQ (tris.to_string (), "((0, 0), (0, 1), (0.2, 0.2)), ((1, 0), (0, 0), (0.2, 0.2)), ((1, 1), (0.2, 0.2), (0, 1)), ((1, 1), (1, 0), (0.2, 0.2))"); + EXPECT_EQ (tris.check (), true); +} + +TEST(Triangle_split_segment) +{ + db::Triangles tris; + tris.init_box (db::DBox (0, 0, 1, 1)); + + tris.insert (tris.create_vertex (0.5, 0.5)); + EXPECT_EQ (tris.to_string (), "((1, 1), (1, 0), (0.5, 0.5)), ((1, 1), (0.5, 0.5), (0, 1)), ((0, 0), (0, 1), (0.5, 0.5)), ((0, 0), (0.5, 0.5), (1, 0))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(Triangle_insert_vertex_twice) +{ + db::Triangles tris; + tris.init_box (db::DBox (0, 0, 1, 1)); + + tris.insert (tris.create_vertex (0.5, 0.5)); + // inserted a vertex twice does not change anything + tris.insert (tris.create_vertex (0.5, 0.5)); + EXPECT_EQ (tris.to_string (), "((1, 1), (1, 0), (0.5, 0.5)), ((1, 1), (0.5, 0.5), (0, 1)), ((0, 0), (0, 1), (0.5, 0.5)), ((0, 0), (0.5, 0.5), (1, 0))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(Triangle_insert_vertex_convex) +{ + db::Triangles tris; + tris.insert (tris.create_vertex (0.2, 0.2)); + tris.insert (tris.create_vertex (0.2, 0.8)); + tris.insert (tris.create_vertex (0.6, 0.5)); + tris.insert (tris.create_vertex (0.7, 0.5)); + tris.insert (tris.create_vertex (0.6, 0.4)); + EXPECT_EQ (tris.to_string (), "((0.2, 0.2), (0.2, 0.8), (0.6, 0.5)), ((0.7, 0.5), (0.6, 0.5), (0.2, 0.8)), ((0.6, 0.5), (0.6, 0.4), (0.2, 0.2)), ((0.6, 0.5), (0.7, 0.5), (0.6, 0.4))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(Triangle_insert_vertex_convex2) +{ + db::Triangles tris; + tris.insert (tris.create_vertex (0.25, 0.1)); + tris.insert (tris.create_vertex (0.1, 0.4)); + tris.insert (tris.create_vertex (0.4, 0.15)); + tris.insert (tris.create_vertex (1, 0.7)); + EXPECT_EQ (tris.to_string (), "((0.25, 0.1), (0.1, 0.4), (0.4, 0.15)), ((1, 0.7), (0.4, 0.15), (0.1, 0.4))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(Triangle_insert_vertex_convex3) +{ + db::Triangles tris; + tris.insert (tris.create_vertex (0.25, 0.5)); + tris.insert (tris.create_vertex (0.25, 0.55)); + tris.insert (tris.create_vertex (0.15, 0.8)); + tris.insert (tris.create_vertex (1, 0.4)); + EXPECT_EQ (tris.to_string (), "((0.25, 0.5), (0.15, 0.8), (0.25, 0.55)), ((1, 0.4), (0.25, 0.5), (0.25, 0.55)), ((0.15, 0.8), (1, 0.4), (0.25, 0.55))"); + EXPECT_EQ (tris.check(), true); +} + +#if 0 +TEST(Triangle_find_crossing_segments) +{ + db::Triangles tris; + v1 = tris.create_vertex (0.2, 0.2); + v2 = tris.create_vertex (0.2, 0.8); + v3 = tris.create_vertex (0.6, 0.5); + v4 = tris.create_vertex (0.7, 0.5); + v5 = tris.create_vertex (0.6, 0.4); + v6 = tris.create_vertex (0.7, 0.2); + tris.insert (v1); + tris.insert (v2); + tris.insert (v3); + tris.insert (v4); + tris.insert (v5); + tris.insert (v6); + EXPECT_EQ (tris.check(), true); + + auto xedges = tris.search_edges_crossing (db::DEdge (*v2, *v6)); + + EXPECT_EQ (xedges.size (), size_t (2)); + auto s1 = tris.find_edge_for_points (v1, v3); + auto s2 = tris.find_edge_for_points (v1, v5); + EXPECT_EQ (s1 in xedges, true); + EXPECT_EQ (s2 in xedges, true); +} +#endif