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