mirror of https://github.com/KLayout/klayout.git
Triangles: constrain
This commit is contained in:
parent
3cc35ce3ee
commit
6e9eb922a2
|
|
@ -1254,121 +1254,137 @@ Triangles::join_edges (std::vector<db::TriangleEdge *> &edges)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Triangles::constrain (const std::vector<std::vector<db::Vertex *> > &contours)
|
||||||
|
{
|
||||||
|
assert (! m_is_constrained);
|
||||||
|
|
||||||
|
std::vector<std::pair<db::DEdge, std::vector<db::TriangleEdge *> > > resolved_edges;
|
||||||
|
|
||||||
|
for (auto c = contours.begin (); c != contours.end (); ++c) {
|
||||||
|
for (auto v = c->begin (); v != c->end (); ++v) {
|
||||||
|
auto vv = v;
|
||||||
|
++vv;
|
||||||
|
if (vv == c->end ()) {
|
||||||
|
vv = c->begin ();
|
||||||
|
}
|
||||||
|
resolved_edges.push_back (std::make_pair (db::DEdge (**v, **vv), std::vector<db::TriangleEdge *> ()));
|
||||||
|
resolved_edges.back ().second = ensure_edge (*v, *vv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto tri = mp_triangles.begin (); tri != mp_triangles.end (); ++tri) {
|
||||||
|
tri->set_outside (false);
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
tri->edge (i)->set_is_segment (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<db::Triangle *, TriangleLessFunc> new_tri;
|
||||||
|
|
||||||
|
for (auto re = resolved_edges.begin (); re != resolved_edges.end (); ++re) {
|
||||||
|
auto edge = re->first;
|
||||||
|
auto edges = re->second;
|
||||||
|
for (auto e = edges.begin (); e != edges.end (); ++e) {
|
||||||
|
(*e)->set_is_segment (true);
|
||||||
|
db::Triangle *outer_tri = 0;
|
||||||
|
int d = db::sprod_sign (edge.d (), (*e)->d ());
|
||||||
|
if (d > 0) {
|
||||||
|
outer_tri = (*e)->left ();
|
||||||
|
}
|
||||||
|
if (d < 0) {
|
||||||
|
outer_tri = (*e)->right ();
|
||||||
|
}
|
||||||
|
if (outer_tri) {
|
||||||
|
new_tri.insert (outer_tri);
|
||||||
|
outer_tri->set_outside (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (! new_tri.empty ()) {
|
||||||
|
|
||||||
|
std::set<db::Triangle *, TriangleLessFunc> next_tris;
|
||||||
|
|
||||||
|
for (auto tri = new_tri.begin (); tri != new_tri.end (); ++tri) {
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
auto e = (*tri)->edge (i);
|
||||||
|
if (! e->is_segment ()) {
|
||||||
|
auto ot = e->other (*tri);
|
||||||
|
if (ot && ! ot->is_outside ()) {
|
||||||
|
next_tris.insert (ot);
|
||||||
|
ot->set_outside (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_tri.swap (next_tris);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// join edges where possible
|
||||||
|
for (auto re = resolved_edges.begin (); re != resolved_edges.end (); ++re) {
|
||||||
|
auto edges = re->second;
|
||||||
|
join_edges (edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_is_constrained = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
void
|
||||||
import math
|
Triangles::remove_outside_triangles ()
|
||||||
import sys
|
{
|
||||||
from .triangle import *
|
tl_assert (m_is_constrained);
|
||||||
|
|
||||||
class Triangles(object):
|
// NOTE: don't remove while iterating
|
||||||
|
std::vector<db::Triangle *> to_remove;
|
||||||
|
for (auto tri = begin (); tri != end (); ++tri) {
|
||||||
|
if (tri->is_outside ()) {
|
||||||
|
to_remove.push_back (const_cast<db::Triangle *> (tri.operator-> ()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def constrain(self, contours: [[Edge]]) -> object:
|
for (auto t = to_remove.begin (); t != to_remove.end (); ++t) {
|
||||||
|
remove (*t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
void
|
||||||
Given a set of contours with edges, mark outer triangles
|
Triangles::clear ()
|
||||||
|
{
|
||||||
|
mp_edges.clear ();
|
||||||
|
mp_triangles.clear ();
|
||||||
|
m_vertex_heap.clear ();
|
||||||
|
m_is_constrained = false;
|
||||||
|
m_level = 0;
|
||||||
|
m_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
The edges must be made from existing vertexes. Edge orientation is
|
void
|
||||||
clockwise.
|
Triangles::create_constrained_delaunay (const db::Region ®ion, double dbu)
|
||||||
"""
|
{
|
||||||
|
clear ();
|
||||||
|
|
||||||
assert(not self.is_constrained)
|
std::vector<std::vector<db::Vertex *> > edge_contours;
|
||||||
|
|
||||||
resolved_edges = []
|
for (auto p = region.begin_merged (); ! p.at_end (); ++p) {
|
||||||
|
|
||||||
for c in contours:
|
edge_contours.push_back (std::vector<db::Vertex *> ());
|
||||||
for edge in c:
|
for (auto pt = p->begin_hull (); pt != p->end_hull (); ++pt) {
|
||||||
edges = self.ensure_edge(edge)
|
edge_contours.back ().push_back (insert_point (db::DPoint (*pt) * dbu));
|
||||||
resolved_edges.append( (edge, edges) )
|
}
|
||||||
|
|
||||||
for tri in self.triangles:
|
for (unsigned int h = 0; h < p->holes (); ++h) {
|
||||||
tri.is_outside = False
|
edge_contours.push_back (std::vector<db::Vertex *> ());
|
||||||
for s in tri.edges():
|
for (auto pt = p->begin_hole (h); pt != p->end_hole (h); ++pt) {
|
||||||
s.is_segment = False
|
edge_contours.back ().push_back (insert_point (db::DPoint (*pt) * dbu));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
new_tri = set()
|
}
|
||||||
|
|
||||||
for re in resolved_edges:
|
constrain (edge_contours);
|
||||||
edge, edges = re
|
}
|
||||||
for s in edges:
|
|
||||||
s.is_segment = True
|
|
||||||
outer_tri = None
|
|
||||||
d = sprod_sign(edge.d(), s.d())
|
|
||||||
if d > 0:
|
|
||||||
outer_tri = s.left
|
|
||||||
if d < 0:
|
|
||||||
outer_tri = s.right
|
|
||||||
if outer_tri is not None:
|
|
||||||
assert (outer_tri in self.triangles)
|
|
||||||
new_tri.add(outer_tri)
|
|
||||||
outer_tri.is_outside = True
|
|
||||||
|
|
||||||
while len(new_tri) > 0:
|
}
|
||||||
|
|
||||||
next_tris = set()
|
|
||||||
|
|
||||||
for tri in new_tri:
|
|
||||||
for s in tri.edges():
|
|
||||||
if not s.is_segment:
|
|
||||||
ot = s.other(tri)
|
|
||||||
if ot is not None and not ot.is_outside:
|
|
||||||
next_tris.add(ot)
|
|
||||||
ot.is_outside = True
|
|
||||||
|
|
||||||
new_tri = next_tris
|
|
||||||
|
|
||||||
# join edges where possible
|
|
||||||
for re in resolved_edges:
|
|
||||||
_, edges = re
|
|
||||||
self._join_edges(edges)
|
|
||||||
|
|
||||||
self.is_constrained = True
|
|
||||||
|
|
||||||
def remove_outside_triangles(self):
|
|
||||||
|
|
||||||
assert self.is_constrained
|
|
||||||
|
|
||||||
for tri in self.triangles:
|
|
||||||
for s in tri.edges():
|
|
||||||
s.level = 0
|
|
||||||
|
|
||||||
to_remove = [tri for tri in self.triangles if tri.is_outside]
|
|
||||||
for tri in to_remove:
|
|
||||||
for s in tri.edges():
|
|
||||||
if not s.is_segment and s.level == 0:
|
|
||||||
s.level = 1
|
|
||||||
s.unlink()
|
|
||||||
tri.unlink()
|
|
||||||
self.triangles.remove(tri)
|
|
||||||
|
|
||||||
to_remove = [v for v in self.vertexes if len(v.edges) == 0]
|
|
||||||
for v in to_remove:
|
|
||||||
self.vertexes.remove(v)
|
|
||||||
|
|
||||||
def create_constrained_delaunay(self, contours: [[Edge]]):
|
|
||||||
|
|
||||||
edge_contours = []
|
|
||||||
|
|
||||||
for c in contours:
|
|
||||||
|
|
||||||
if len(c) < 3:
|
|
||||||
continue
|
|
||||||
|
|
||||||
edges = []
|
|
||||||
edge_contours.append(edges)
|
|
||||||
|
|
||||||
vl = None
|
|
||||||
vfirst = None
|
|
||||||
for pt in c:
|
|
||||||
v = self.insert(Vertex(pt.x, pt.y))
|
|
||||||
if vfirst is None:
|
|
||||||
vfirst = v
|
|
||||||
else:
|
|
||||||
edges.append(Edge(vl, v))
|
|
||||||
vl = v
|
|
||||||
edges.append(Edge(vl, vfirst))
|
|
||||||
|
|
||||||
self.constrain(edge_contours)
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include "dbCommon.h"
|
#include "dbCommon.h"
|
||||||
#include "dbTriangle.h"
|
#include "dbTriangle.h"
|
||||||
#include "dbBox.h"
|
#include "dbBox.h"
|
||||||
|
#include "dbRegion.h"
|
||||||
|
|
||||||
#include "tlObjectCollection.h"
|
#include "tlObjectCollection.h"
|
||||||
|
|
||||||
|
|
@ -76,6 +77,18 @@ public:
|
||||||
*/
|
*/
|
||||||
size_t num_triangles () const { return mp_triangles.size (); }
|
size_t num_triangles () const { return mp_triangles.size (); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears the triangle set
|
||||||
|
*/
|
||||||
|
void clear ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a constrained Delaunay triangulation from the given Region
|
||||||
|
*/
|
||||||
|
void create_constrained_delaunay (const db::Region ®ion, double dbu = 1.0);
|
||||||
|
|
||||||
|
// exposed for testing purposes:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Checks the triangle graph for consistency
|
* @brief Checks the triangle graph for consistency
|
||||||
* This method is for testing purposes mainly.
|
* This method is for testing purposes mainly.
|
||||||
|
|
@ -123,9 +136,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void remove (db::Vertex *vertex, std::vector<db::Triangle *> *new_triangles = 0);
|
void remove (db::Vertex *vertex, std::vector<db::Triangle *> *new_triangles = 0);
|
||||||
|
|
||||||
|
|
||||||
// exposed for testing purposes:
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Flips the given edge
|
* @brief Flips the given edge
|
||||||
*/
|
*/
|
||||||
|
|
@ -155,6 +165,21 @@ public:
|
||||||
*/
|
*/
|
||||||
std::vector<db::TriangleEdge *> ensure_edge (db::Vertex *from, db::Vertex *to);
|
std::vector<db::TriangleEdge *> ensure_edge (db::Vertex *from, db::Vertex *to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Given a set of contours with edges, mark outer triangles
|
||||||
|
*
|
||||||
|
* The edges must be made from existing vertexes. Edge orientation is
|
||||||
|
* clockwise.
|
||||||
|
*
|
||||||
|
* This will also mark triangles as outside ones.
|
||||||
|
*/
|
||||||
|
void constrain (const std::vector<std::vector<Vertex *> > &contours);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes the outside triangles.
|
||||||
|
*/
|
||||||
|
void remove_outside_triangles ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tl::shared_collection<db::Triangle> mp_triangles;
|
tl::shared_collection<db::Triangle> mp_triangles;
|
||||||
tl::weak_collection<db::TriangleEdge> mp_edges;
|
tl::weak_collection<db::TriangleEdge> mp_edges;
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,7 @@ TEST(Triangle_test_ensure_edge)
|
||||||
double x = round (flt_rand () * res) * (1.0 / res);
|
double x = round (flt_rand () * res) * (1.0 / res);
|
||||||
double y = round (flt_rand () * res) * (1.0 / res);
|
double y = round (flt_rand () * res) * (1.0 / res);
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
for (int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) {
|
for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) {
|
||||||
if (ee[j].side_of (db::DPoint (x, y)) == 0) {
|
if (ee[j].side_of (db::DPoint (x, y)) == 0) {
|
||||||
--i;
|
--i;
|
||||||
ok = false;
|
ok = false;
|
||||||
|
|
@ -302,13 +302,13 @@ TEST(Triangle_test_ensure_edge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) {
|
for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) {
|
||||||
tris.insert_point (ee[i].p1 ());
|
tris.insert_point (ee[i].p1 ());
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ (tris.check (), true);
|
EXPECT_EQ (tris.check (), true);
|
||||||
|
|
||||||
for (int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) {
|
for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) {
|
||||||
tris.ensure_edge (tris.find_vertex_for_point (ee[i].p1 ()), tris.find_vertex_for_point (ee[i].p2 ()));
|
tris.ensure_edge (tris.find_vertex_for_point (ee[i].p1 ()), tris.find_vertex_for_point (ee[i].p2 ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,7 +316,7 @@ TEST(Triangle_test_ensure_edge)
|
||||||
|
|
||||||
double area_in = 0.0;
|
double area_in = 0.0;
|
||||||
db::DBox clip_box;
|
db::DBox clip_box;
|
||||||
for (int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) {
|
for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) {
|
||||||
clip_box += ee[i].p1 ();
|
clip_box += ee[i].p1 ();
|
||||||
}
|
}
|
||||||
for (auto t = tris.begin (); t != tris.end (); ++t) {
|
for (auto t = tris.begin (); t != tris.end (); ++t) {
|
||||||
|
|
@ -329,6 +329,65 @@ TEST(Triangle_test_ensure_edge)
|
||||||
EXPECT_EQ (tl::to_string (area_in), "0.25");
|
EXPECT_EQ (tl::to_string (area_in), "0.25");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Triangle_test_constrain)
|
||||||
|
{
|
||||||
|
srand (0);
|
||||||
|
|
||||||
|
db::Triangles tris;
|
||||||
|
double res = 128.0;
|
||||||
|
|
||||||
|
db::DEdge ee[] = {
|
||||||
|
db::DEdge (0.25, 0.25, 0.25, 0.75),
|
||||||
|
db::DEdge (0.25, 0.75, 0.75, 0.75),
|
||||||
|
db::DEdge (0.75, 0.75, 0.75, 0.25),
|
||||||
|
db::DEdge (0.75, 0.25, 0.25, 0.25)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < 200; ++i) {
|
||||||
|
double x = round (flt_rand () * res) * (1.0 / res);
|
||||||
|
double y = round (flt_rand () * res) * (1.0 / res);
|
||||||
|
bool ok = true;
|
||||||
|
for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) {
|
||||||
|
if (ee[j].side_of (db::DPoint (x, y)) == 0) {
|
||||||
|
--i;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
tris.insert_point (x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<db::Vertex *> contour;
|
||||||
|
for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) {
|
||||||
|
contour.push_back (tris.insert_point (ee[i].p1 ()));
|
||||||
|
}
|
||||||
|
std::vector<std::vector<db::Vertex *> > contours;
|
||||||
|
contours.push_back (contour);
|
||||||
|
|
||||||
|
EXPECT_EQ (tris.check (), true);
|
||||||
|
|
||||||
|
tris.constrain (contours);
|
||||||
|
EXPECT_EQ (tris.check (false), true);
|
||||||
|
|
||||||
|
tris.remove_outside_triangles ();
|
||||||
|
|
||||||
|
EXPECT_EQ (tris.check (), true);
|
||||||
|
|
||||||
|
double area_in = 0.0;
|
||||||
|
db::DBox clip_box;
|
||||||
|
for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) {
|
||||||
|
clip_box += ee[i].p1 ();
|
||||||
|
}
|
||||||
|
for (auto t = tris.begin (); t != tris.end (); ++t) {
|
||||||
|
EXPECT_EQ (clip_box.overlaps (t->bbox ()), true);
|
||||||
|
EXPECT_EQ (t->bbox ().inside (clip_box), true);
|
||||||
|
area_in += t->area ();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ (tl::to_string (area_in), "0.25");
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
def test_heavy_constrain(self):
|
def test_heavy_constrain(self):
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue