avoid confusing the triangle library with too complex ring intersections
This commit is contained in:
parent
616af3fd18
commit
4594c44539
2 changed files with 71 additions and 33 deletions
|
@ -16,7 +16,7 @@ from shapely.ops import unary_union
|
||||||
from c3nav.mapdata.cache import MapHistory
|
from c3nav.mapdata.cache import MapHistory
|
||||||
from c3nav.mapdata.models import AltitudeArea, Level, MapUpdate
|
from c3nav.mapdata.models import AltitudeArea, Level, MapUpdate
|
||||||
from c3nav.mapdata.utils.geometry import assert_multipolygon, get_rings
|
from c3nav.mapdata.utils.geometry import assert_multipolygon, get_rings
|
||||||
from c3nav.mapdata.utils.mesh import triangulate_rings
|
from c3nav.mapdata.utils.mesh import triangulate_polygon, triangulate_rings
|
||||||
from c3nav.mapdata.utils.mpl import shapely_to_mpl
|
from c3nav.mapdata.utils.mpl import shapely_to_mpl
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,6 +54,28 @@ class HybridGeometry:
|
||||||
)
|
)
|
||||||
return HybridGeometry(geom, tuple(f for f in faces if f))
|
return HybridGeometry(geom, tuple(f for f in faces if f))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_full(cls, geom, vertices_offset, faces_offset):
|
||||||
|
if isinstance(geom, (LineString, MultiLineString)):
|
||||||
|
return HybridGeometry(geom, set()), np.empty((0, 2), dtype=np.int32), np.empty((0, 3), dtype=np.uint32)
|
||||||
|
|
||||||
|
vertices = deque()
|
||||||
|
faces = deque()
|
||||||
|
faces_i = deque()
|
||||||
|
for subgeom in assert_multipolygon(geom):
|
||||||
|
new_vertices, new_faces = triangulate_polygon(subgeom)
|
||||||
|
new_faces += vertices_offset
|
||||||
|
vertices.append(new_vertices)
|
||||||
|
faces.append(new_faces)
|
||||||
|
faces_i.append(set(range(faces_offset, faces_offset+new_faces.shape[0])))
|
||||||
|
vertices_offset += new_vertices.shape[0]
|
||||||
|
faces_offset += new_faces.shape[0]
|
||||||
|
|
||||||
|
vertices = np.vstack(vertices)
|
||||||
|
faces = np.vstack(faces)
|
||||||
|
|
||||||
|
return HybridGeometry(geom, tuple(faces_i)), vertices, faces
|
||||||
|
|
||||||
def union(self, other):
|
def union(self, other):
|
||||||
add_faces = self.add_faces
|
add_faces = self.add_faces
|
||||||
for crop_id, faces in other.add_faces.items():
|
for crop_id, faces in other.add_faces.items():
|
||||||
|
@ -123,17 +145,34 @@ class AltitudeAreaGeometries:
|
||||||
# noinspection PyCallByClass,PyTypeChecker
|
# noinspection PyCallByClass,PyTypeChecker
|
||||||
return AltitudeArea.get_altitudes(self, points/1000).astype(np.int32)
|
return AltitudeArea.get_altitudes(self, points/1000).astype(np.int32)
|
||||||
|
|
||||||
def get_geometries(self):
|
def create_hybrid_geometries(self, face_centers, vertices_offset, faces_offset):
|
||||||
return chain((self.geometry,),
|
|
||||||
chain(*(areas.values() for areas in self.colors.values())),
|
|
||||||
self.obstacles.values())
|
|
||||||
|
|
||||||
def create_hybrid_geometries(self, face_centers):
|
|
||||||
self.geometry = HybridGeometry.create(self.geometry, face_centers)
|
self.geometry = HybridGeometry.create(self.geometry, face_centers)
|
||||||
self.colors = {color: {key: HybridGeometry.create(geom, face_centers) for key, geom in areas.items()}
|
|
||||||
for color, areas in self.colors.items()}
|
vertices = deque()
|
||||||
self.obstacles = {key: HybridGeometry.create(geom, face_centers)
|
faces = deque()
|
||||||
for key, geom in self.obstacles.items()}
|
|
||||||
|
for color, areas in self.colors.items():
|
||||||
|
for key in tuple(areas.keys()):
|
||||||
|
geom = areas[key]
|
||||||
|
new_geom, new_vertices, new_faces = HybridGeometry.create_full(geom, vertices_offset, faces_offset)
|
||||||
|
areas[key] = new_geom
|
||||||
|
vertices_offset += new_vertices.shape[0]
|
||||||
|
faces_offset += new_faces.shape[0]
|
||||||
|
vertices.append(new_vertices)
|
||||||
|
faces.append(new_faces)
|
||||||
|
|
||||||
|
for key in tuple(self.obstacles.keys()):
|
||||||
|
geom = self.obstacles[key]
|
||||||
|
new_geom, new_vertices, new_faces = HybridGeometry.create_full(geom, vertices_offset, faces_offset)
|
||||||
|
self.obstacles[key] = new_geom
|
||||||
|
vertices_offset += new_vertices.shape[0]
|
||||||
|
faces_offset += new_faces.shape[0]
|
||||||
|
vertices.append(new_vertices)
|
||||||
|
faces.append(new_faces)
|
||||||
|
|
||||||
|
if not vertices:
|
||||||
|
return np.empty((0, 2), dtype=np.int32), np.empty((0, 3), dtype=np.uint32)
|
||||||
|
return np.vstack(vertices), np.vstack(faces)
|
||||||
|
|
||||||
def remove_faces(self, faces):
|
def remove_faces(self, faces):
|
||||||
self.geometry.remove_faces(faces)
|
self.geometry.remove_faces(faces)
|
||||||
|
@ -608,13 +647,25 @@ class LevelGeometries:
|
||||||
|
|
||||||
def get_geometries(self):
|
def get_geometries(self):
|
||||||
# omit heightareas as these are never drawn
|
# omit heightareas as these are never drawn
|
||||||
return chain(chain(*(area.get_geometries() for area in self.altitudeareas)), (self.walls, self.doors,),
|
return chain((area.geometry for area in self.altitudeareas), (self.walls, self.doors,),
|
||||||
self.restricted_spaces_indoors.values(), self.restricted_spaces_outdoors.values(), self.ramps,
|
self.restricted_spaces_indoors.values(), self.restricted_spaces_outdoors.values(), self.ramps,
|
||||||
(geom for altitude, geom in self.short_walls))
|
(geom for altitude, geom in self.short_walls))
|
||||||
|
|
||||||
def create_hybrid_geometries(self, face_centers):
|
def create_hybrid_geometries(self, face_centers):
|
||||||
|
vertices_offset = self.vertices.shape[0]
|
||||||
|
faces_offset = self.faces.shape[0]
|
||||||
|
new_vertices = deque()
|
||||||
|
new_faces = deque()
|
||||||
for area in self.altitudeareas:
|
for area in self.altitudeareas:
|
||||||
area.create_hybrid_geometries(face_centers)
|
area_vertices, area_faces = area.create_hybrid_geometries(face_centers, vertices_offset, faces_offset)
|
||||||
|
vertices_offset += area_vertices.shape[0]
|
||||||
|
faces_offset += area_faces.shape[0]
|
||||||
|
new_vertices.append(area_vertices)
|
||||||
|
new_faces.append(area_faces)
|
||||||
|
if new_vertices:
|
||||||
|
self.vertices = np.vstack((self.vertices, *new_vertices))
|
||||||
|
self.faces = np.vstack((self.faces, *new_faces))
|
||||||
|
|
||||||
self.heightareas = tuple((HybridGeometry.create(area, face_centers), height)
|
self.heightareas = tuple((HybridGeometry.create(area, face_centers), height)
|
||||||
for area, height in self.heightareas)
|
for area, height in self.heightareas)
|
||||||
self.walls = HybridGeometry.create(self.walls, face_centers)
|
self.walls = HybridGeometry.create(self.walls, face_centers)
|
||||||
|
|
|
@ -30,7 +30,10 @@ def triangulate_rings(rings, holes=None):
|
||||||
segments = set()
|
segments = set()
|
||||||
for ring in rings:
|
for ring in rings:
|
||||||
indices = tuple(vertices_lookup[vertex] for vertex in ring[:-1])
|
indices = tuple(vertices_lookup[vertex] for vertex in ring[:-1])
|
||||||
segments.update(zip(indices, indices[1:]+indices[:1]))
|
segments.update(tuple(sorted((a, b))) for a, b in zip(indices, indices[1:]+indices[:1]) if a != b)
|
||||||
|
|
||||||
|
if len(segments) < 3:
|
||||||
|
return np.empty((0, 2), dtype=np.int32), np.empty((0, 3), dtype=np.uint32)
|
||||||
|
|
||||||
# noinspection PyArgumentList
|
# noinspection PyArgumentList
|
||||||
info = triangle.MeshInfo()
|
info = triangle.MeshInfo()
|
||||||
|
@ -45,30 +48,14 @@ def triangulate_rings(rings, holes=None):
|
||||||
|
|
||||||
|
|
||||||
def _triangulate_polygon(polygon: Polygon, keep_holes=False):
|
def _triangulate_polygon(polygon: Polygon, keep_holes=False):
|
||||||
vertices = deque()
|
holes = None
|
||||||
segments = deque()
|
|
||||||
|
|
||||||
offset = 0
|
|
||||||
for ring in chain((polygon.exterior,), polygon.interiors):
|
|
||||||
new_vertices = np.array(ring.coords)[:-1]
|
|
||||||
vertices.append(new_vertices)
|
|
||||||
segments.append(get_face_indizes(offset, len(new_vertices)))
|
|
||||||
offset += len(new_vertices)
|
|
||||||
|
|
||||||
# noinspection PyArgumentList
|
|
||||||
info = triangle.MeshInfo()
|
|
||||||
info.set_points(np.vstack(vertices))
|
|
||||||
info.set_facets(np.vstack(segments).tolist())
|
|
||||||
|
|
||||||
if not keep_holes:
|
if not keep_holes:
|
||||||
holes = np.array(tuple(
|
holes = np.array(tuple(
|
||||||
Polygon(ring).representative_point().coords for ring in polygon.interiors
|
Polygon(ring).representative_point().coords for ring in polygon.interiors
|
||||||
))
|
))
|
||||||
if holes.size:
|
holes = holes.reshape((-1, 2)) if holes.size else None
|
||||||
info.set_holes(holes.reshape((holes.shape[0], -1)))
|
|
||||||
|
|
||||||
mesh = triangle.build(info, quality_meshing=False)
|
return triangulate_rings((polygon.exterior, *polygon.interiors), holes)
|
||||||
return np.array(mesh.points), np.array(mesh.elements)
|
|
||||||
|
|
||||||
|
|
||||||
def triangulate_polygon(geometry: Union[Polygon, MultiPolygon], keep_holes=False):
|
def triangulate_polygon(geometry: Union[Polygon, MultiPolygon], keep_holes=False):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue