First tests pass for triangles.

This commit is contained in:
Matthias Koefferlein 2023-08-13 16:37:01 +02:00
parent 255f2dd572
commit 745eb9b625
3 changed files with 288 additions and 288 deletions

View File

@ -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<db::Triangle *> *n
}
void
Triangles::add_more_triangles (const std::vector<db::Triangle *> &new_triangles,
Triangles::add_more_triangles (std::vector<db::Triangle *> &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<db::TriangleEdge *> (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<db::Triangle *> *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<db::Vertex *, TriangleEdge *> v2new_edges;
std::vector<TriangleEdge *> 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<db::Triangle *> 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<db::Triangle *> &tris, db::Vertex *vertex, db::TriangleEdge *split_edge, std::vector<db::Triangle *> *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<db::Triangle *> 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<db::TriangleEdge *> fixed_edges;
fixed_edges.push_back (s1);
fixed_edges.push_back (s2);
fix_triangles (new_triangles, fixed_edges, new_triangles_out);
}
std::vector<db::Vertex *>
@ -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)

View File

@ -39,18 +39,77 @@ class Layout;
class DB_PUBLIC Triangles
{
public:
typedef tl::shared_collection<db::Triangle> 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<db::Vertex *> 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<db::Triangle *> *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<db::Triangle *> *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<db::Triangle *> *new_triangles = 0);
// exposed for testing purposes:
std::pair<std::pair<db::Triangle *, db::Triangle *>, db::TriangleEdge *> flip (TriangleEdge *edge);
private:
tl::shared_collection<db::Triangle> mp_triangles;
tl::weak_collection<db::TriangleEdge> mp_edges;
@ -76,19 +162,17 @@ private:
// NOTE: these functions are SLOW and intended to test purposes only
std::vector<db::Vertex *> find_touching (const db::DBox &box) const;
std::vector<db::Vertex *> find_inside_circle (const db::DPoint &center, double radius) const;
db::Layout *to_layout () const;
void remove_outside_vertex (db::Vertex *vertex, std::vector<db::Triangle *> *new_triangles = 0);
void remove_inside_vertex (db::Vertex *vertex, std::vector<db::Triangle *> *new_triangles_out = 0);
std::vector<db::Triangle *> fill_concave_corners (const std::vector<TriangleEdge *> &edges);
int fix_triangles(const std::vector<db::Triangle *> &tris, const std::vector<db::TriangleEdge *> &fixed_edges, std::vector<db::Triangle *> *new_triangles);
std::pair<std::pair<db::Triangle *, db::Triangle *>, db::TriangleEdge *> flip (TriangleEdge *edge);
static bool is_illegal_edge (db::TriangleEdge *edge);
std::vector<db::Triangle *> 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<db::Triangle *> *new_triangles_out);
void split_triangles_on_edge (const std::vector<db::Triangle *> &tris, db::Vertex *vertex, db::TriangleEdge *split_edge, std::vector<db::Triangle *> *new_triangles_out);
void add_more_triangles (const std::vector<db::Triangle *> &new_triangles,
void add_more_triangles (std::vector<Triangle *> &new_triangles,
db::TriangleEdge *incoming_edge,
db::Vertex *from_vertex, db::Vertex *to_vertex,
db::TriangleEdge *conn_edge);

View File

@ -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