hub/venv/lib/python3.7/site-packages/trimesh/path/exchange/misc.py

216 lines
6.0 KiB
Python

import numpy as np
from ... import util
from ... import graph
from ... import grouping
from ..entities import Line, Arc
from collections import deque
def dict_to_path(as_dict):
"""
Turn a pure dict into a dict containing entity objects that
can be sent directly to a Path constructor.
Parameters
------------
as_dict : dict
Has keys: 'vertices', 'entities'
Returns
------------
kwargs : dict
Has keys: 'vertices', 'entities'
"""
# start kwargs with initial value
result = as_dict.copy()
# map of constructors
loaders = {'Arc': Arc, 'Line': Line}
# pre- allocate entity array
entities = [None] * len(as_dict['entities'])
# run constructor for dict kwargs
for entity_index, entity in enumerate(as_dict['entities']):
entities[entity_index] = loaders[entity['type']](
points=entity['points'], closed=entity['closed'])
result['entities'] = entities
return result
def lines_to_path(lines):
"""
Turn line segments into a Path2D or Path3D object.
Parameters
------------
lines : (n, 2, dimension) or (n, dimension) float
Line segments or connected polyline curve in 2D or 3D
Returns
-----------
kwargs : dict
kwargs for Path constructor
"""
lines = np.asanyarray(lines, dtype=np.float64)
if util.is_shape(lines, (-1, (2, 3))):
# the case where we have a list of points
# we are going to assume they are connected
result = {'entities': np.array([Line(np.arange(len(lines)))]),
'vertices': lines}
return result
elif util.is_shape(lines, (-1, 2, (2, 3))):
# case where we have line segments in 2D or 3D
dimension = lines.shape[-1]
# convert lines to even number of (n, dimension) points
lines = lines.reshape((-1, dimension))
# merge duplicate vertices
unique, inverse = grouping.unique_rows(lines)
# use scipy edges_to_path to skip creating
# a bajillion individual line entities which
# will be super slow vs. fewer polyline entities
return edges_to_path(edges=inverse.reshape((-1, 2)),
vertices=lines[unique])
else:
raise ValueError('Lines must be (n,(2|3)) or (n,2,(2|3))')
return result
def polygon_to_path(polygon):
"""
Load shapely Polygon objects into a trimesh.path.Path2D object
Parameters
-------------
polygon : shapely.geometry.Polygon
Input geometry
Returns
-----------
kwargs : dict
Keyword arguments for Path2D constructor
"""
# start with a single polyline for the exterior
entities = deque([Line(points=np.arange(
len(polygon.exterior.coords)))])
# start vertices
vertices = np.array(polygon.exterior.coords).tolist()
# append interiors as single Line objects
for boundary in polygon.interiors:
entities.append(Line(np.arange(len(boundary.coords)) +
len(vertices)))
# append the new vertex array
vertices.extend(boundary.coords)
# make sure result arrays are numpy
kwargs = {'entities': np.array(entities),
'vertices': np.array(vertices)}
return kwargs
def linestrings_to_path(multi):
"""
Load shapely LineString objects into a trimesh.path.Path2D object
Parameters
-------------
multi : shapely.geometry.LineString or MultiLineString
Input 2D geometry
Returns
-------------
kwargs : dict
Keyword arguments for Path2D constructor
"""
# append to result as we go
entities = []
vertices = []
if not util.is_sequence(multi):
multi = [multi]
for line in multi:
# only append geometry with points
if hasattr(line, 'coords'):
coords = np.array(line.coords)
if len(coords) < 2:
continue
entities.append(Line(np.arange(len(coords)) +
len(vertices)))
vertices.extend(coords)
kwargs = {'entities': np.array(entities),
'vertices': np.array(vertices)}
return kwargs
def faces_to_path(mesh, face_ids=None, **kwargs):
"""
Given a mesh and face indices find the outline edges and
turn them into a Path3D.
Parameters
------------
mesh : trimesh.Trimesh
Triangulated surface in 3D
face_ids : (n,) int
Indexes referencing mesh.faces
Returns
---------
kwargs : dict
Kwargs for Path3D constructor
"""
if face_ids is None:
edges = mesh.edges_sorted
else:
# take advantage of edge ordering to index as single row
edges = mesh.edges_sorted.reshape(
(-1, 6))[face_ids].reshape((-1, 2))
# an edge which occurs onely once is on the boundary
unique_edges = grouping.group_rows(
edges, require_count=1)
# add edges and vertices to kwargs
kwargs.update(edges_to_path(edges=edges[unique_edges],
vertices=mesh.vertices))
return kwargs
def edges_to_path(edges,
vertices,
**kwargs):
"""
Given an edge list of indices and associated vertices
representing lines, generate kwargs for a Path object.
Parameters
-----------
edges : (n, 2) int
Vertex indices of line segments
vertices : (m, dimension) float
Vertex positions where dimension is 2 or 3
Returns
----------
kwargs : dict
Kwargs for Path constructor
"""
# sequence of ordered traversals
dfs = graph.traversals(edges, mode='dfs')
# make sure every consecutive index in DFS
# traversal is an edge in the source edge list
dfs_connected = graph.fill_traversals(dfs, edges=edges)
# kwargs for Path constructor
# turn traversals into Line objects
lines = [Line(d) for d in dfs_connected]
kwargs.update({'entities': lines,
'vertices': vertices,
'process': False})
return kwargs