Module sverchok.utils.solid
Expand source code
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
import math
from collections import defaultdict
import numpy as np
from mathutils.kdtree import KDTree
from sverchok.data_structure import match_long_repeat as mlr
from sverchok.utils.curve.core import SvCurve
from sverchok.utils.surface.core import SvSurface
from sverchok.dependencies import FreeCAD
from sverchok.utils.solid_conversion import to_solid, to_solid_recursive
if FreeCAD is not None:
import Part
import Mesh
import MeshPart
from FreeCAD import Base
from sverchok.nodes.solid.mesh_to_solid import ensure_triangles
from sverchok.utils.curve.freecad import curve_to_freecad
from sverchok.utils.surface.freecad import surface_to_freecad, is_solid_face_surface
class SvSolidTopology(object):
class Item(object):
def __init__(self, item):
self.item = item
def __hash__(self):
return self.item.hashCode()
def __eq__(self, other):
return self.item.isSame(other.item)
def __repr__(self):
return f"<Item: {type(self.item).__name__} #{self.item.hashCode()}>"
def __init__(self, solid):
self.solid = solid
self._init()
def __repr__(self):
v = len(self.solid.Vertexes)
e = len(self.solid.Edges)
f = len(self.solid.Faces)
return f"<Solid topology: {v} vertices, {e} edges, {f} faces>"
def _init(self):
self._faces_by_vertex = defaultdict(set)
self._faces_by_edge = defaultdict(set)
self._edges_by_vertex = defaultdict(set)
for face in self.solid.Faces:
for vertex in face.Vertexes:
self._faces_by_vertex[SvSolidTopology.Item(vertex)].add(SvSolidTopology.Item(face))
for edge in face.Edges:
self._faces_by_edge[SvSolidTopology.Item(edge)].add(SvSolidTopology.Item(face))
for edge in self.solid.Edges:
for vertex in edge.Vertexes:
self._edges_by_vertex[SvSolidTopology.Item(vertex)].add(SvSolidTopology.Item(edge))
self._tree = KDTree(len(self.solid.Vertexes))
for i, vertex in enumerate(self.solid.Vertexes):
co = (vertex.X, vertex.Y, vertex.Z)
self._tree.insert(co, i)
self._tree.balance()
def tessellate(self, precision):
self._points_by_edge = defaultdict(list)
self._points_by_face = defaultdict(list)
for edge in self.solid.Edges:
points = edge.discretize(Deflection=precision)
i_edge = SvSolidTopology.Item(edge)
for pt in points:
self._points_by_edge[i_edge].append((pt.x, pt.y, pt.z))
for face in self.solid.Faces:
data = face.tessellate(precision)
i_face = SvSolidTopology.Item(face)
for pt in data[0]:
self._points_by_face[i_face].append((pt.x, pt.y, pt.z))
def calc_normals(self):
self._normals_by_face = dict()
for face in self.solid.Faces:
#face.tessellate(precision)
#u_min, u_max, v_min, v_max = face.ParameterRange
sum_normal = Base.Vector(0,0,0)
for u, v in face.getUVNodes():
normal = face.normalAt(u,v)
sum_normal = sum_normal + normal
sum_normal = np.array([sum_normal.x, sum_normal.y, sum_normal.z])
sum_normal = sum_normal / np.linalg.norm(sum_normal)
self._normals_by_face[SvSolidTopology.Item(face)] = sum_normal
def calc_face_centers(self):
self._centers_by_face = dict()
for face in self.solid.Faces:
sum_points = np.array([0.0,0.0,0.0])
for u,v in face.getUVNodes():
p = face.valueAt(u,v)
sum_points += np.array([p.x, p.y, p.z])
mean = sum_points / len(sum_points)
self._centers_by_face[SvSolidTopology.Item(face)] = mean
def get_normal_by_face(self, face):
return self._normals_by_face[SvSolidTopology.Item(face)]
def get_center_by_face(self, face):
return self._centers_by_face[SvSolidTopology.Item(face)]
def get_vertices_by_location(self, condition):
to_tuple = lambda v : (v.X, v.Y, v.Z)
return [to_tuple(v) for v in self.solid.Vertexes if condition(to_tuple(v))]
def get_vertices_by_location_mask(self, condition):
to_tuple = lambda v : (v.X, v.Y, v.Z)
return [condition(to_tuple(v)) for v in self.solid.Vertexes]
def get_points_by_edge(self, edge):
return self._points_by_edge[SvSolidTopology.Item(edge)]
def get_points_by_face(self, face):
return self._points_by_face[SvSolidTopology.Item(face)]
def get_edges_by_location_mask(self, condition, include_partial):
# condition is vectorized
check = any if include_partial else all
mask = []
for edge in self.solid.Edges:
test = condition(np.array(self._points_by_edge[SvSolidTopology.Item(edge)]))
mask.append(check(test))
return mask
def get_faces_by_location_mask(self, condition, include_partial):
# condition is vectorized
check = any if include_partial else all
mask = []
for face in self.solid.Faces:
test = condition(np.array(self._points_by_face[SvSolidTopology.Item(face)]))
mask.append(check(test))
return mask
def get_faces_by_vertex(self, vertex):
return [i.item for i in self._faces_by_vertex[SvSolidTopology.Item(vertex)]]
def get_faces_by_vertices_mask(self, vertices, include_partial=True):
if include_partial:
good = set()
for vertex in vertices:
new = self._faces_by_vertex[SvSolidTopology.Item(vertex)]
good.update(new)
return [SvSolidTopology.Item(face) in good for face in self.solid.Faces]
else:
vertices = set([SvSolidTopology.Item(v) for v in vertices])
mask = []
for face in self.solid.Faces:
ok = all(SvSolidTopology.Item(v) in vertices for v in face.Vertexes)
mask.append(ok)
return mask
def get_faces_by_edge(self, edge):
return [i.item for i in self._faces_by_edge[SvSolidTopology.Item(edge)]]
def get_faces_by_edges_mask(self, edges, include_partial=True):
if include_partial:
good = set()
for edge in edges:
new = self._faces_by_edge[SvSolidTopology.Item(edge)]
good.update(new)
return [SvSolidTopology.Item(face) in good for face in self.solid.Faces]
else:
edges = set([SvSolidTopology.Item(e) for e in edges])
mask = []
for face in self.solid.Faces:
ok = all(SvSolidTopology.Item(e) in edges for e in face.Edges)
mask.append(ok)
return mask
def get_edges_by_vertex(self, vertex):
return [i.item for i in self._edges_by_vertex[SvSolidTopology.Item(vertex)]]
def get_edges_by_vertices_mask(self, vertices, include_partial=True):
if include_partial:
good = set()
for vertex in vertices:
new = self._edges_by_vertex[SvSolidTopology.Item(vertex)]
good.update(new)
return [SvSolidTopology.Item(edge) in good for edge in self.solid.Edges]
else:
vertices = set([SvSolidTopology.Item(v) for v in vertices])
mask = []
for edge in self.solid.Edges:
ok = all(SvSolidTopology.Item(v) in vertices for v in edge.Vertexes)
mask.append(ok)
return mask
def get_edges_by_faces_mask(self, faces):
good = set()
for face in faces:
new = set([SvSolidTopology.Item(e) for e in face.Edges])
good.update(new)
return [SvSolidTopology.Item(edge) in good for edge in self.solid.Edges]
def get_vertices_by_faces_mask(self, faces):
good = set()
for face in faces:
new = set([SvSolidTopology.Item(v) for v in face.Vertexes])
good.update(new)
return [SvSolidTopology.Item(vertex) in good for vertex in self.solid.Vertexes]
def get_vertices_by_edges_mask(self, edges):
good = set()
for edge in edges:
new = set([SvSolidTopology.Item(v) for v in edge.Vertexes])
good.update(new)
return [SvSolidTopology.Item(vertex) in good for vertex in self.solid.Vertexes]
def get_vertices_within_range(self, origin, distance):
found = self._tree.find_range(tuple(origin), distance)
idxs = [item[1] for item in found]
vertices = [self.solid.Vertexes[i] for i in idxs]
return vertices
def get_vertices_within_range_mask(self, origin, distance):
found = self._tree.find_range(tuple(origin), distance)
idxs = set([item[1] for item in found])
return [i in idxs for i in range(len(self.solid.Vertexes))]
class SvGeneralFuse(object):
def __init__(self, solids):
self.solids = solids
self.result, self.map = solids[0].generalFuse(solids[1:])
self._per_source = defaultdict(set)
self._per_source_idx = defaultdict(set)
self._sources_by_part = defaultdict(set)
self._source_idxs_by_part = defaultdict(set)
for src_idx, (source, parts) in enumerate(zip(solids, self.map)):
items = set(SvSolidTopology.Item(i) for i in parts)
src_key = SvSolidTopology.Item(source)
self._per_source[src_key] = items
self._per_source_idx[src_idx] = items
for item in items:
self._sources_by_part[item].add(src_key)
#print(f"{src_idx}: P[{item}] := {src_key}")
self._source_idxs_by_part[item].add(src_idx)
self._edge_indirect_source_idxs = defaultdict(set)
self._edge_indirect_sources = defaultdict(set)
self._face_indirect_source_idxs = defaultdict(set)
self._face_indirect_sources = defaultdict(set)
for part in self.result.Solids:
item = SvSolidTopology.Item(part)
sources = self._sources_by_part[item]
src_idxs = self._source_idxs_by_part[item]
#print(f"P? {item} => {src_idxs}")
for edge in part.Edges:
edge_item = SvSolidTopology.Item(edge)
self._edge_indirect_sources[edge_item].update(sources)
self._edge_indirect_source_idxs[edge_item].update(src_idxs)
for face in part.Faces:
face_item = SvSolidTopology.Item(face)
self._face_indirect_source_idxs[face_item].update(src_idxs)
self._face_indirect_sources[face_item].update(sources)
# self._edge_direct_source_idxs = defaultdict(set)
# self._edge_direct_sources = defaultdict(set)
# for part in self.result.Solids:
# item = SvSolidTopology.Item(part)
# for edge in part.Edges:
# edge_item = SvSolidTopology.Item(edge)
# indirect_sources = self._edge_indirect_sources[edge_item]
# indirect_source_idxs = self._edge_indirect_source_idxs[edge_item]
# for src_idx, indirect_source in zip(indirect_source_idxs, indirect_sources):
# src_edges = set(SvSolidTopology.Item(e) for e in indirect_source.item.Edges)
# if edge_item in src_edges:
# self._edge_direct_sources[edge_item].add(indirect_source)
# self._edge_direct_source_idxs[edge_item].add(src_idx)
def get_all_parts(self):
return self.result.Solids
def get_union_all(self, refine=False):
solids = self.result.Solids
solid = solids[0].fuse(solids[1:])
if refine:
solid = solid.removeSplitter()
return solid
def get_intersect_all(self, refine=False):
result = None
for source, parts in self._per_source.items():
if result is None:
result = parts
else:
result.intersection_update(parts)
if not result:
return None
elif len(result) == 1:
return list(result)[0].item
else:
solids = [p.item for p in result]
solid = solids[0].fuse(solids[1:])
if refine:
solid = solid.removeSplitter()
return solid
def get_edge_sources(self, edge):
return self._edge_indirect_sources[SvSolidTopology.Item(edge)]
def get_edge_source_idxs(self, edge):
return self._edge_indirect_source_idxs[SvSolidTopology.Item(edge)]
def get_face_sources(self, face):
return self._face_indirect_sources[SvSolidTopology.Item(face)]
def get_face_source_idxs(self, face):
return self._face_indirect_source_idxs[SvSolidTopology.Item(face)]
def get_part_sources(self, part):
return self._sources_by_part[SvSolidTopology.Item(part)]
def get_part_source_idxs(self, part):
return self._source_idxs_by_part[SvSolidTopology.Item(part)]
def get_by_source(self, solid):
return self._per_source[SvSolidTopology.Item(solid)]
def get_by_source_idx(self, idx):
return self._per_source_idx[idx]
def get_intersection(self, solid_a, solid_b):
result_a = self._per_source[SvSolidTopology.Item(solid_a)]
result_b = self._per_source[SvSolidTopology.Item(solid_b)]
return result_a.intersection(result_b)
def get_intersection_by_idx(self, idx_a, idx_b):
result_a = self._per_source_idx[idx_a]
result_b = self._per_source_idx[idx_b]
return result_a.intersection(result_b)
def get_difference(self, solid_a, solid_b):
result_a = self._per_source[SvSolidTopology.Item(solid_a)]
result_b = self._per_source[SvSolidTopology.Item(solid_b)]
return result_a.difference(result_b)
def get_clean_part(self, solid):
item = SvSolidTopology.Item(solid)
result = self._per_source[item].copy()
for source, results in self._per_source.items():
if source != item:
result.difference_update(results)
return result
def get_clean_part_by_idx(self, idx, refine=False):
result = self._per_source_idx[idx].copy()
for source_idx, results in self._per_source_idx.items():
if source_idx != idx:
result.difference_update(results)
parts = [part.item for part in result]
if not parts:
solid = None
elif len(parts) == 1:
solid = parts[0]
else:
solid = parts[0].fuse(parts[1:])
if do_refine:
solid = solid.removeSplitter()
return solid
class SvBoolResult(object):
def __init__(self, solid, edge_mask=None, edge_map=None, face_mask=None, face_map=None, solid_map=None):
self.solid = solid
if edge_mask is None:
edge_mask = []
self.edge_mask = edge_mask
if edge_map is None:
edge_map = []
self.edge_map = edge_map
if face_mask is None:
face_mask = []
self.face_mask = face_mask
if face_map is None:
face_map = []
self.face_map = face_map
if solid_map is None:
solid_map = []
self.solid_map = solid_map
def transform_solid(matrix, solid):
"""
Utility function to apply mathutils.Matrix to a Solid object.
"""
mat = Base.Matrix(*[i for v in matrix for i in v])
return solid.transformGeometry(mat)
def basic_mesher(solids, precisions):
verts = []
faces = []
for solid, precision in zip(*mlr([solids, precisions])):
rawdata = solid.tessellate(precision)
b_verts = []
b_faces = []
for v in rawdata[0]:
b_verts.append((v.x, v.y, v.z))
for f in rawdata[1]:
b_faces.append(f)
verts.append(b_verts)
faces.append(b_faces)
return verts, faces
def standard_mesher(solids, surface_deviation, angle_deviation, relative_surface_deviation):
verts = []
faces = []
for solid, s_dev, ang_dev in zip(*mlr([solids, surface_deviation, angle_deviation])):
mesh = MeshPart.meshFromShape(
Shape=solid,
LinearDeflection=s_dev,
AngularDeflection=math.radians(ang_dev),
Relative=relative_surface_deviation)
verts.append([v[:] for v in mesh.Topology[0]])
faces.append(mesh.Topology[1])
return verts, faces
def mefisto_mesher(solids, max_edge_length):
verts = []
faces = []
for solid, max_edge in zip(*mlr([solids, max_edge_length])):
mesh = MeshPart.meshFromShape(
Shape=solid,
MaxLength=max_edge
)
verts.append([v[:] for v in mesh.Topology[0]])
faces.append(mesh.Topology[1])
return verts, faces
FCMESH = 'FCMESH'
BMESH = 'BMESH'
def svmesh_to_solid(verts, faces, precision=1e-6, remove_splitter=True, method=FCMESH):
"""
input:
verts: list of 3element iterables, [vector, vector...]
faces: list of lists of face indices
precision: a conversion factor defined in makeShapeFromMesh (FreeCAD)
remove_splitter: default True, removes duplicate geometry (edges)
output:
a FreeCAD solid
"""
if method == FCMESH:
tri_faces = ensure_triangles(verts, faces, True)
faces_t = [[verts[c] for c in f] for f in tri_faces]
mesh = Mesh.Mesh(faces_t)
shape = Part.Shape()
shape.makeShapeFromMesh(mesh.Topology, precision)
if remove_splitter:
# may slow it down, or be totally necessary
shape = shape.removeSplitter()
return Part.makeSolid(shape)
elif method == BMESH:
fc_faces = []
for face in faces:
face_i = list(face) + [face[0]]
face_verts = [Base.Vector(verts[i]) for i in face_i]
wire = Part.makePolygon(face_verts)
wire.fixTolerance(precision)
try:
fc_face = Part.Face(wire)
#fc_face = Part.makeFilledFace(wire.Edges)
except Exception as e:
print(f"Face idxs: {face_i}, verts: {face_verts}")
raise Exception("Maybe face is not planar?") from e
fc_faces.append(fc_face)
shell = Part.makeShell(fc_faces)
solid = Part.makeSolid(shell)
if remove_splitter:
solid = solid.removeSplitter()
return solid
else:
raise Exception("Unsupported method")
def mesh_from_solid_faces(solid):
verts = [(v.X, v.Y, v.Z) for v in solid.Vertexes]
all_fc_verts = {SvSolidTopology.Item(v) : i for i, v in enumerate(solid.Vertexes)}
def find_vertex(v):
#for i, fc_vert in enumerate(solid.Vertexes):
# if v.isSame(fc_vert):
# return i
#return None
return all_fc_verts[SvSolidTopology.Item(v)]
edges = []
for fc_edge in solid.Edges:
edge = [find_vertex(v) for v in fc_edge.Vertexes]
if len(edge) == 2:
edges.append(edge)
faces = []
for fc_face in solid.Faces:
incident_verts = defaultdict(set)
for fc_edge in fc_face.Edges:
edge = [find_vertex(v) for v in fc_edge.Vertexes]
if len(edge) == 2:
i, j = edge
incident_verts[i].add(j)
incident_verts[j].add(i)
face = [find_vertex(v) for v in fc_face.Vertexes]
vert_idx = face[0]
correct_face = [vert_idx]
for i in range(len(face)):
incident = list(incident_verts[vert_idx])
other_verts = [i for i in incident if i not in correct_face]
if not other_verts:
break
other_vert_idx = other_verts[0]
correct_face.append(other_vert_idx)
vert_idx = other_vert_idx
if len(correct_face) > 2:
faces.append(correct_face)
return verts, edges, faces
def hascurves(shape):
for e in shape.Edges:
if not isinstance(e.Curve, (Part.Line, Part.LineSegment)): return True
return False
def drop_existing_faces(faces):
"""
this avoids the following bmesh exception:
faces.new(verts): face already exists
"""
faces_set = set()
new_faces = []
good_face = new_faces.append
for face in faces:
proposed_face = tuple(sorted(face))
if proposed_face in faces_set:
continue
else:
faces_set.add(proposed_face)
good_face(face)
return new_faces
def mesh_from_solid_faces_MOD(shape, quality=1.0, tessellate=False):
"""
modified from yorik van havre's FreeCAD importer for Blender.
"""
vdict = {}
faces = []
add_face = faces.append # alias increase speed
# write FreeCAD faces as polygons when possible
for face in shape.Faces:
if (len(face.Wires) > 1) or (not isinstance(face.Surface, Part.Plane)) or hascurves(face) or tessellate:
# face has holes or is curved, so we need to triangulate it
rawdata = face.tessellate(quality)
for v in rawdata[0]:
if not (v1 := (v.x, v.y, v.z)) in vdict:
vdict[v1] = len(vdict)
for f in rawdata[1]:
raw = rawdata[0]
nf = [vdict[(nv.x, nv.y, nv.z)] for nv in [raw[vi] for vi in f]]
add_face(nf)
else:
f = []
ov = face.OuterWire.OrderedVertexes
for v in ov:
if not (vec := (v.X, v.Y, v.Z)) in vdict:
vdict[vec] = len(vdict)
f.append(len(vdict) - 1)
else:
f.append(vdict[(v.X, v.Y, v.Z)])
# FreeCAD doesn't care about verts order. Make sure our loop goes clockwise
c = face.CenterOfMass
v1 = ov[0].Point.sub(c)
v2 = ov[1].Point.sub(c)
n = face.normalAt(0,0)
if (v1.cross(v2)).getAngle(n) > 1.57:
f.reverse() # inverting verts order if the direction is couterclockwise
add_face(f)
faces = drop_existing_faces(faces)
verts = list(vdict.keys())
return verts, faces
Functions
def basic_mesher(solids, precisions)
-
Expand source code
def basic_mesher(solids, precisions): verts = [] faces = [] for solid, precision in zip(*mlr([solids, precisions])): rawdata = solid.tessellate(precision) b_verts = [] b_faces = [] for v in rawdata[0]: b_verts.append((v.x, v.y, v.z)) for f in rawdata[1]: b_faces.append(f) verts.append(b_verts) faces.append(b_faces) return verts, faces
def drop_existing_faces(faces)
-
this avoids the following bmesh exception:
faces.new(verts): face already exists
Expand source code
def drop_existing_faces(faces): """ this avoids the following bmesh exception: faces.new(verts): face already exists """ faces_set = set() new_faces = [] good_face = new_faces.append for face in faces: proposed_face = tuple(sorted(face)) if proposed_face in faces_set: continue else: faces_set.add(proposed_face) good_face(face) return new_faces
def hascurves(shape)
-
Expand source code
def hascurves(shape): for e in shape.Edges: if not isinstance(e.Curve, (Part.Line, Part.LineSegment)): return True return False
def mefisto_mesher(solids, max_edge_length)
-
Expand source code
def mefisto_mesher(solids, max_edge_length): verts = [] faces = [] for solid, max_edge in zip(*mlr([solids, max_edge_length])): mesh = MeshPart.meshFromShape( Shape=solid, MaxLength=max_edge ) verts.append([v[:] for v in mesh.Topology[0]]) faces.append(mesh.Topology[1]) return verts, faces
def mesh_from_solid_faces(solid)
-
Expand source code
def mesh_from_solid_faces(solid): verts = [(v.X, v.Y, v.Z) for v in solid.Vertexes] all_fc_verts = {SvSolidTopology.Item(v) : i for i, v in enumerate(solid.Vertexes)} def find_vertex(v): #for i, fc_vert in enumerate(solid.Vertexes): # if v.isSame(fc_vert): # return i #return None return all_fc_verts[SvSolidTopology.Item(v)] edges = [] for fc_edge in solid.Edges: edge = [find_vertex(v) for v in fc_edge.Vertexes] if len(edge) == 2: edges.append(edge) faces = [] for fc_face in solid.Faces: incident_verts = defaultdict(set) for fc_edge in fc_face.Edges: edge = [find_vertex(v) for v in fc_edge.Vertexes] if len(edge) == 2: i, j = edge incident_verts[i].add(j) incident_verts[j].add(i) face = [find_vertex(v) for v in fc_face.Vertexes] vert_idx = face[0] correct_face = [vert_idx] for i in range(len(face)): incident = list(incident_verts[vert_idx]) other_verts = [i for i in incident if i not in correct_face] if not other_verts: break other_vert_idx = other_verts[0] correct_face.append(other_vert_idx) vert_idx = other_vert_idx if len(correct_face) > 2: faces.append(correct_face) return verts, edges, faces
def mesh_from_solid_faces_MOD(shape, quality=1.0, tessellate=False)
-
modified from yorik van havre's FreeCAD importer for Blender.
Expand source code
def mesh_from_solid_faces_MOD(shape, quality=1.0, tessellate=False): """ modified from yorik van havre's FreeCAD importer for Blender. """ vdict = {} faces = [] add_face = faces.append # alias increase speed # write FreeCAD faces as polygons when possible for face in shape.Faces: if (len(face.Wires) > 1) or (not isinstance(face.Surface, Part.Plane)) or hascurves(face) or tessellate: # face has holes or is curved, so we need to triangulate it rawdata = face.tessellate(quality) for v in rawdata[0]: if not (v1 := (v.x, v.y, v.z)) in vdict: vdict[v1] = len(vdict) for f in rawdata[1]: raw = rawdata[0] nf = [vdict[(nv.x, nv.y, nv.z)] for nv in [raw[vi] for vi in f]] add_face(nf) else: f = [] ov = face.OuterWire.OrderedVertexes for v in ov: if not (vec := (v.X, v.Y, v.Z)) in vdict: vdict[vec] = len(vdict) f.append(len(vdict) - 1) else: f.append(vdict[(v.X, v.Y, v.Z)]) # FreeCAD doesn't care about verts order. Make sure our loop goes clockwise c = face.CenterOfMass v1 = ov[0].Point.sub(c) v2 = ov[1].Point.sub(c) n = face.normalAt(0,0) if (v1.cross(v2)).getAngle(n) > 1.57: f.reverse() # inverting verts order if the direction is couterclockwise add_face(f) faces = drop_existing_faces(faces) verts = list(vdict.keys()) return verts, faces
def standard_mesher(solids, surface_deviation, angle_deviation, relative_surface_deviation)
-
Expand source code
def standard_mesher(solids, surface_deviation, angle_deviation, relative_surface_deviation): verts = [] faces = [] for solid, s_dev, ang_dev in zip(*mlr([solids, surface_deviation, angle_deviation])): mesh = MeshPart.meshFromShape( Shape=solid, LinearDeflection=s_dev, AngularDeflection=math.radians(ang_dev), Relative=relative_surface_deviation) verts.append([v[:] for v in mesh.Topology[0]]) faces.append(mesh.Topology[1]) return verts, faces
def svmesh_to_solid(verts, faces, precision=1e-06, remove_splitter=True, method='FCMESH')
-
input: verts: list of 3element iterables, [vector, vector…] faces: list of lists of face indices precision: a conversion factor defined in makeShapeFromMesh (FreeCAD) remove_splitter: default True, removes duplicate geometry (edges) output: a FreeCAD solid
Expand source code
def svmesh_to_solid(verts, faces, precision=1e-6, remove_splitter=True, method=FCMESH): """ input: verts: list of 3element iterables, [vector, vector...] faces: list of lists of face indices precision: a conversion factor defined in makeShapeFromMesh (FreeCAD) remove_splitter: default True, removes duplicate geometry (edges) output: a FreeCAD solid """ if method == FCMESH: tri_faces = ensure_triangles(verts, faces, True) faces_t = [[verts[c] for c in f] for f in tri_faces] mesh = Mesh.Mesh(faces_t) shape = Part.Shape() shape.makeShapeFromMesh(mesh.Topology, precision) if remove_splitter: # may slow it down, or be totally necessary shape = shape.removeSplitter() return Part.makeSolid(shape) elif method == BMESH: fc_faces = [] for face in faces: face_i = list(face) + [face[0]] face_verts = [Base.Vector(verts[i]) for i in face_i] wire = Part.makePolygon(face_verts) wire.fixTolerance(precision) try: fc_face = Part.Face(wire) #fc_face = Part.makeFilledFace(wire.Edges) except Exception as e: print(f"Face idxs: {face_i}, verts: {face_verts}") raise Exception("Maybe face is not planar?") from e fc_faces.append(fc_face) shell = Part.makeShell(fc_faces) solid = Part.makeSolid(shell) if remove_splitter: solid = solid.removeSplitter() return solid else: raise Exception("Unsupported method")
def transform_solid(matrix, solid)
-
Utility function to apply mathutils.Matrix to a Solid object.
Expand source code
def transform_solid(matrix, solid): """ Utility function to apply mathutils.Matrix to a Solid object. """ mat = Base.Matrix(*[i for v in matrix for i in v]) return solid.transformGeometry(mat)
Classes
class SvBoolResult (solid, edge_mask=None, edge_map=None, face_mask=None, face_map=None, solid_map=None)
-
Expand source code
class SvBoolResult(object): def __init__(self, solid, edge_mask=None, edge_map=None, face_mask=None, face_map=None, solid_map=None): self.solid = solid if edge_mask is None: edge_mask = [] self.edge_mask = edge_mask if edge_map is None: edge_map = [] self.edge_map = edge_map if face_mask is None: face_mask = [] self.face_mask = face_mask if face_map is None: face_map = [] self.face_map = face_map if solid_map is None: solid_map = [] self.solid_map = solid_map
class SvGeneralFuse (solids)
-
Expand source code
class SvGeneralFuse(object): def __init__(self, solids): self.solids = solids self.result, self.map = solids[0].generalFuse(solids[1:]) self._per_source = defaultdict(set) self._per_source_idx = defaultdict(set) self._sources_by_part = defaultdict(set) self._source_idxs_by_part = defaultdict(set) for src_idx, (source, parts) in enumerate(zip(solids, self.map)): items = set(SvSolidTopology.Item(i) for i in parts) src_key = SvSolidTopology.Item(source) self._per_source[src_key] = items self._per_source_idx[src_idx] = items for item in items: self._sources_by_part[item].add(src_key) #print(f"{src_idx}: P[{item}] := {src_key}") self._source_idxs_by_part[item].add(src_idx) self._edge_indirect_source_idxs = defaultdict(set) self._edge_indirect_sources = defaultdict(set) self._face_indirect_source_idxs = defaultdict(set) self._face_indirect_sources = defaultdict(set) for part in self.result.Solids: item = SvSolidTopology.Item(part) sources = self._sources_by_part[item] src_idxs = self._source_idxs_by_part[item] #print(f"P? {item} => {src_idxs}") for edge in part.Edges: edge_item = SvSolidTopology.Item(edge) self._edge_indirect_sources[edge_item].update(sources) self._edge_indirect_source_idxs[edge_item].update(src_idxs) for face in part.Faces: face_item = SvSolidTopology.Item(face) self._face_indirect_source_idxs[face_item].update(src_idxs) self._face_indirect_sources[face_item].update(sources) # self._edge_direct_source_idxs = defaultdict(set) # self._edge_direct_sources = defaultdict(set) # for part in self.result.Solids: # item = SvSolidTopology.Item(part) # for edge in part.Edges: # edge_item = SvSolidTopology.Item(edge) # indirect_sources = self._edge_indirect_sources[edge_item] # indirect_source_idxs = self._edge_indirect_source_idxs[edge_item] # for src_idx, indirect_source in zip(indirect_source_idxs, indirect_sources): # src_edges = set(SvSolidTopology.Item(e) for e in indirect_source.item.Edges) # if edge_item in src_edges: # self._edge_direct_sources[edge_item].add(indirect_source) # self._edge_direct_source_idxs[edge_item].add(src_idx) def get_all_parts(self): return self.result.Solids def get_union_all(self, refine=False): solids = self.result.Solids solid = solids[0].fuse(solids[1:]) if refine: solid = solid.removeSplitter() return solid def get_intersect_all(self, refine=False): result = None for source, parts in self._per_source.items(): if result is None: result = parts else: result.intersection_update(parts) if not result: return None elif len(result) == 1: return list(result)[0].item else: solids = [p.item for p in result] solid = solids[0].fuse(solids[1:]) if refine: solid = solid.removeSplitter() return solid def get_edge_sources(self, edge): return self._edge_indirect_sources[SvSolidTopology.Item(edge)] def get_edge_source_idxs(self, edge): return self._edge_indirect_source_idxs[SvSolidTopology.Item(edge)] def get_face_sources(self, face): return self._face_indirect_sources[SvSolidTopology.Item(face)] def get_face_source_idxs(self, face): return self._face_indirect_source_idxs[SvSolidTopology.Item(face)] def get_part_sources(self, part): return self._sources_by_part[SvSolidTopology.Item(part)] def get_part_source_idxs(self, part): return self._source_idxs_by_part[SvSolidTopology.Item(part)] def get_by_source(self, solid): return self._per_source[SvSolidTopology.Item(solid)] def get_by_source_idx(self, idx): return self._per_source_idx[idx] def get_intersection(self, solid_a, solid_b): result_a = self._per_source[SvSolidTopology.Item(solid_a)] result_b = self._per_source[SvSolidTopology.Item(solid_b)] return result_a.intersection(result_b) def get_intersection_by_idx(self, idx_a, idx_b): result_a = self._per_source_idx[idx_a] result_b = self._per_source_idx[idx_b] return result_a.intersection(result_b) def get_difference(self, solid_a, solid_b): result_a = self._per_source[SvSolidTopology.Item(solid_a)] result_b = self._per_source[SvSolidTopology.Item(solid_b)] return result_a.difference(result_b) def get_clean_part(self, solid): item = SvSolidTopology.Item(solid) result = self._per_source[item].copy() for source, results in self._per_source.items(): if source != item: result.difference_update(results) return result def get_clean_part_by_idx(self, idx, refine=False): result = self._per_source_idx[idx].copy() for source_idx, results in self._per_source_idx.items(): if source_idx != idx: result.difference_update(results) parts = [part.item for part in result] if not parts: solid = None elif len(parts) == 1: solid = parts[0] else: solid = parts[0].fuse(parts[1:]) if do_refine: solid = solid.removeSplitter() return solid
Methods
def get_all_parts(self)
-
Expand source code
def get_all_parts(self): return self.result.Solids
def get_by_source(self, solid)
-
Expand source code
def get_by_source(self, solid): return self._per_source[SvSolidTopology.Item(solid)]
def get_by_source_idx(self, idx)
-
Expand source code
def get_by_source_idx(self, idx): return self._per_source_idx[idx]
def get_clean_part(self, solid)
-
Expand source code
def get_clean_part(self, solid): item = SvSolidTopology.Item(solid) result = self._per_source[item].copy() for source, results in self._per_source.items(): if source != item: result.difference_update(results) return result
def get_clean_part_by_idx(self, idx, refine=False)
-
Expand source code
def get_clean_part_by_idx(self, idx, refine=False): result = self._per_source_idx[idx].copy() for source_idx, results in self._per_source_idx.items(): if source_idx != idx: result.difference_update(results) parts = [part.item for part in result] if not parts: solid = None elif len(parts) == 1: solid = parts[0] else: solid = parts[0].fuse(parts[1:]) if do_refine: solid = solid.removeSplitter() return solid
def get_difference(self, solid_a, solid_b)
-
Expand source code
def get_difference(self, solid_a, solid_b): result_a = self._per_source[SvSolidTopology.Item(solid_a)] result_b = self._per_source[SvSolidTopology.Item(solid_b)] return result_a.difference(result_b)
def get_edge_source_idxs(self, edge)
-
Expand source code
def get_edge_source_idxs(self, edge): return self._edge_indirect_source_idxs[SvSolidTopology.Item(edge)]
def get_edge_sources(self, edge)
-
Expand source code
def get_edge_sources(self, edge): return self._edge_indirect_sources[SvSolidTopology.Item(edge)]
def get_face_source_idxs(self, face)
-
Expand source code
def get_face_source_idxs(self, face): return self._face_indirect_source_idxs[SvSolidTopology.Item(face)]
def get_face_sources(self, face)
-
Expand source code
def get_face_sources(self, face): return self._face_indirect_sources[SvSolidTopology.Item(face)]
def get_intersect_all(self, refine=False)
-
Expand source code
def get_intersect_all(self, refine=False): result = None for source, parts in self._per_source.items(): if result is None: result = parts else: result.intersection_update(parts) if not result: return None elif len(result) == 1: return list(result)[0].item else: solids = [p.item for p in result] solid = solids[0].fuse(solids[1:]) if refine: solid = solid.removeSplitter() return solid
def get_intersection(self, solid_a, solid_b)
-
Expand source code
def get_intersection(self, solid_a, solid_b): result_a = self._per_source[SvSolidTopology.Item(solid_a)] result_b = self._per_source[SvSolidTopology.Item(solid_b)] return result_a.intersection(result_b)
def get_intersection_by_idx(self, idx_a, idx_b)
-
Expand source code
def get_intersection_by_idx(self, idx_a, idx_b): result_a = self._per_source_idx[idx_a] result_b = self._per_source_idx[idx_b] return result_a.intersection(result_b)
def get_part_source_idxs(self, part)
-
Expand source code
def get_part_source_idxs(self, part): return self._source_idxs_by_part[SvSolidTopology.Item(part)]
def get_part_sources(self, part)
-
Expand source code
def get_part_sources(self, part): return self._sources_by_part[SvSolidTopology.Item(part)]
def get_union_all(self, refine=False)
-
Expand source code
def get_union_all(self, refine=False): solids = self.result.Solids solid = solids[0].fuse(solids[1:]) if refine: solid = solid.removeSplitter() return solid
class SvSolidTopology (solid)
-
Expand source code
class SvSolidTopology(object): class Item(object): def __init__(self, item): self.item = item def __hash__(self): return self.item.hashCode() def __eq__(self, other): return self.item.isSame(other.item) def __repr__(self): return f"<Item: {type(self.item).__name__} #{self.item.hashCode()}>" def __init__(self, solid): self.solid = solid self._init() def __repr__(self): v = len(self.solid.Vertexes) e = len(self.solid.Edges) f = len(self.solid.Faces) return f"<Solid topology: {v} vertices, {e} edges, {f} faces>" def _init(self): self._faces_by_vertex = defaultdict(set) self._faces_by_edge = defaultdict(set) self._edges_by_vertex = defaultdict(set) for face in self.solid.Faces: for vertex in face.Vertexes: self._faces_by_vertex[SvSolidTopology.Item(vertex)].add(SvSolidTopology.Item(face)) for edge in face.Edges: self._faces_by_edge[SvSolidTopology.Item(edge)].add(SvSolidTopology.Item(face)) for edge in self.solid.Edges: for vertex in edge.Vertexes: self._edges_by_vertex[SvSolidTopology.Item(vertex)].add(SvSolidTopology.Item(edge)) self._tree = KDTree(len(self.solid.Vertexes)) for i, vertex in enumerate(self.solid.Vertexes): co = (vertex.X, vertex.Y, vertex.Z) self._tree.insert(co, i) self._tree.balance() def tessellate(self, precision): self._points_by_edge = defaultdict(list) self._points_by_face = defaultdict(list) for edge in self.solid.Edges: points = edge.discretize(Deflection=precision) i_edge = SvSolidTopology.Item(edge) for pt in points: self._points_by_edge[i_edge].append((pt.x, pt.y, pt.z)) for face in self.solid.Faces: data = face.tessellate(precision) i_face = SvSolidTopology.Item(face) for pt in data[0]: self._points_by_face[i_face].append((pt.x, pt.y, pt.z)) def calc_normals(self): self._normals_by_face = dict() for face in self.solid.Faces: #face.tessellate(precision) #u_min, u_max, v_min, v_max = face.ParameterRange sum_normal = Base.Vector(0,0,0) for u, v in face.getUVNodes(): normal = face.normalAt(u,v) sum_normal = sum_normal + normal sum_normal = np.array([sum_normal.x, sum_normal.y, sum_normal.z]) sum_normal = sum_normal / np.linalg.norm(sum_normal) self._normals_by_face[SvSolidTopology.Item(face)] = sum_normal def calc_face_centers(self): self._centers_by_face = dict() for face in self.solid.Faces: sum_points = np.array([0.0,0.0,0.0]) for u,v in face.getUVNodes(): p = face.valueAt(u,v) sum_points += np.array([p.x, p.y, p.z]) mean = sum_points / len(sum_points) self._centers_by_face[SvSolidTopology.Item(face)] = mean def get_normal_by_face(self, face): return self._normals_by_face[SvSolidTopology.Item(face)] def get_center_by_face(self, face): return self._centers_by_face[SvSolidTopology.Item(face)] def get_vertices_by_location(self, condition): to_tuple = lambda v : (v.X, v.Y, v.Z) return [to_tuple(v) for v in self.solid.Vertexes if condition(to_tuple(v))] def get_vertices_by_location_mask(self, condition): to_tuple = lambda v : (v.X, v.Y, v.Z) return [condition(to_tuple(v)) for v in self.solid.Vertexes] def get_points_by_edge(self, edge): return self._points_by_edge[SvSolidTopology.Item(edge)] def get_points_by_face(self, face): return self._points_by_face[SvSolidTopology.Item(face)] def get_edges_by_location_mask(self, condition, include_partial): # condition is vectorized check = any if include_partial else all mask = [] for edge in self.solid.Edges: test = condition(np.array(self._points_by_edge[SvSolidTopology.Item(edge)])) mask.append(check(test)) return mask def get_faces_by_location_mask(self, condition, include_partial): # condition is vectorized check = any if include_partial else all mask = [] for face in self.solid.Faces: test = condition(np.array(self._points_by_face[SvSolidTopology.Item(face)])) mask.append(check(test)) return mask def get_faces_by_vertex(self, vertex): return [i.item for i in self._faces_by_vertex[SvSolidTopology.Item(vertex)]] def get_faces_by_vertices_mask(self, vertices, include_partial=True): if include_partial: good = set() for vertex in vertices: new = self._faces_by_vertex[SvSolidTopology.Item(vertex)] good.update(new) return [SvSolidTopology.Item(face) in good for face in self.solid.Faces] else: vertices = set([SvSolidTopology.Item(v) for v in vertices]) mask = [] for face in self.solid.Faces: ok = all(SvSolidTopology.Item(v) in vertices for v in face.Vertexes) mask.append(ok) return mask def get_faces_by_edge(self, edge): return [i.item for i in self._faces_by_edge[SvSolidTopology.Item(edge)]] def get_faces_by_edges_mask(self, edges, include_partial=True): if include_partial: good = set() for edge in edges: new = self._faces_by_edge[SvSolidTopology.Item(edge)] good.update(new) return [SvSolidTopology.Item(face) in good for face in self.solid.Faces] else: edges = set([SvSolidTopology.Item(e) for e in edges]) mask = [] for face in self.solid.Faces: ok = all(SvSolidTopology.Item(e) in edges for e in face.Edges) mask.append(ok) return mask def get_edges_by_vertex(self, vertex): return [i.item for i in self._edges_by_vertex[SvSolidTopology.Item(vertex)]] def get_edges_by_vertices_mask(self, vertices, include_partial=True): if include_partial: good = set() for vertex in vertices: new = self._edges_by_vertex[SvSolidTopology.Item(vertex)] good.update(new) return [SvSolidTopology.Item(edge) in good for edge in self.solid.Edges] else: vertices = set([SvSolidTopology.Item(v) for v in vertices]) mask = [] for edge in self.solid.Edges: ok = all(SvSolidTopology.Item(v) in vertices for v in edge.Vertexes) mask.append(ok) return mask def get_edges_by_faces_mask(self, faces): good = set() for face in faces: new = set([SvSolidTopology.Item(e) for e in face.Edges]) good.update(new) return [SvSolidTopology.Item(edge) in good for edge in self.solid.Edges] def get_vertices_by_faces_mask(self, faces): good = set() for face in faces: new = set([SvSolidTopology.Item(v) for v in face.Vertexes]) good.update(new) return [SvSolidTopology.Item(vertex) in good for vertex in self.solid.Vertexes] def get_vertices_by_edges_mask(self, edges): good = set() for edge in edges: new = set([SvSolidTopology.Item(v) for v in edge.Vertexes]) good.update(new) return [SvSolidTopology.Item(vertex) in good for vertex in self.solid.Vertexes] def get_vertices_within_range(self, origin, distance): found = self._tree.find_range(tuple(origin), distance) idxs = [item[1] for item in found] vertices = [self.solid.Vertexes[i] for i in idxs] return vertices def get_vertices_within_range_mask(self, origin, distance): found = self._tree.find_range(tuple(origin), distance) idxs = set([item[1] for item in found]) return [i in idxs for i in range(len(self.solid.Vertexes))]
Class variables
var Item
Methods
def calc_face_centers(self)
-
Expand source code
def calc_face_centers(self): self._centers_by_face = dict() for face in self.solid.Faces: sum_points = np.array([0.0,0.0,0.0]) for u,v in face.getUVNodes(): p = face.valueAt(u,v) sum_points += np.array([p.x, p.y, p.z]) mean = sum_points / len(sum_points) self._centers_by_face[SvSolidTopology.Item(face)] = mean
def calc_normals(self)
-
Expand source code
def calc_normals(self): self._normals_by_face = dict() for face in self.solid.Faces: #face.tessellate(precision) #u_min, u_max, v_min, v_max = face.ParameterRange sum_normal = Base.Vector(0,0,0) for u, v in face.getUVNodes(): normal = face.normalAt(u,v) sum_normal = sum_normal + normal sum_normal = np.array([sum_normal.x, sum_normal.y, sum_normal.z]) sum_normal = sum_normal / np.linalg.norm(sum_normal) self._normals_by_face[SvSolidTopology.Item(face)] = sum_normal
def get_center_by_face(self, face)
-
Expand source code
def get_center_by_face(self, face): return self._centers_by_face[SvSolidTopology.Item(face)]
def get_edges_by_faces_mask(self, faces)
-
Expand source code
def get_edges_by_faces_mask(self, faces): good = set() for face in faces: new = set([SvSolidTopology.Item(e) for e in face.Edges]) good.update(new) return [SvSolidTopology.Item(edge) in good for edge in self.solid.Edges]
def get_edges_by_location_mask(self, condition, include_partial)
-
Expand source code
def get_edges_by_location_mask(self, condition, include_partial): # condition is vectorized check = any if include_partial else all mask = [] for edge in self.solid.Edges: test = condition(np.array(self._points_by_edge[SvSolidTopology.Item(edge)])) mask.append(check(test)) return mask
def get_edges_by_vertex(self, vertex)
-
Expand source code
def get_edges_by_vertex(self, vertex): return [i.item for i in self._edges_by_vertex[SvSolidTopology.Item(vertex)]]
def get_edges_by_vertices_mask(self, vertices, include_partial=True)
-
Expand source code
def get_edges_by_vertices_mask(self, vertices, include_partial=True): if include_partial: good = set() for vertex in vertices: new = self._edges_by_vertex[SvSolidTopology.Item(vertex)] good.update(new) return [SvSolidTopology.Item(edge) in good for edge in self.solid.Edges] else: vertices = set([SvSolidTopology.Item(v) for v in vertices]) mask = [] for edge in self.solid.Edges: ok = all(SvSolidTopology.Item(v) in vertices for v in edge.Vertexes) mask.append(ok) return mask
def get_faces_by_edge(self, edge)
-
Expand source code
def get_faces_by_edge(self, edge): return [i.item for i in self._faces_by_edge[SvSolidTopology.Item(edge)]]
def get_faces_by_edges_mask(self, edges, include_partial=True)
-
Expand source code
def get_faces_by_edges_mask(self, edges, include_partial=True): if include_partial: good = set() for edge in edges: new = self._faces_by_edge[SvSolidTopology.Item(edge)] good.update(new) return [SvSolidTopology.Item(face) in good for face in self.solid.Faces] else: edges = set([SvSolidTopology.Item(e) for e in edges]) mask = [] for face in self.solid.Faces: ok = all(SvSolidTopology.Item(e) in edges for e in face.Edges) mask.append(ok) return mask
def get_faces_by_location_mask(self, condition, include_partial)
-
Expand source code
def get_faces_by_location_mask(self, condition, include_partial): # condition is vectorized check = any if include_partial else all mask = [] for face in self.solid.Faces: test = condition(np.array(self._points_by_face[SvSolidTopology.Item(face)])) mask.append(check(test)) return mask
def get_faces_by_vertex(self, vertex)
-
Expand source code
def get_faces_by_vertex(self, vertex): return [i.item for i in self._faces_by_vertex[SvSolidTopology.Item(vertex)]]
def get_faces_by_vertices_mask(self, vertices, include_partial=True)
-
Expand source code
def get_faces_by_vertices_mask(self, vertices, include_partial=True): if include_partial: good = set() for vertex in vertices: new = self._faces_by_vertex[SvSolidTopology.Item(vertex)] good.update(new) return [SvSolidTopology.Item(face) in good for face in self.solid.Faces] else: vertices = set([SvSolidTopology.Item(v) for v in vertices]) mask = [] for face in self.solid.Faces: ok = all(SvSolidTopology.Item(v) in vertices for v in face.Vertexes) mask.append(ok) return mask
def get_normal_by_face(self, face)
-
Expand source code
def get_normal_by_face(self, face): return self._normals_by_face[SvSolidTopology.Item(face)]
def get_points_by_edge(self, edge)
-
Expand source code
def get_points_by_edge(self, edge): return self._points_by_edge[SvSolidTopology.Item(edge)]
def get_points_by_face(self, face)
-
Expand source code
def get_points_by_face(self, face): return self._points_by_face[SvSolidTopology.Item(face)]
def get_vertices_by_edges_mask(self, edges)
-
Expand source code
def get_vertices_by_edges_mask(self, edges): good = set() for edge in edges: new = set([SvSolidTopology.Item(v) for v in edge.Vertexes]) good.update(new) return [SvSolidTopology.Item(vertex) in good for vertex in self.solid.Vertexes]
def get_vertices_by_faces_mask(self, faces)
-
Expand source code
def get_vertices_by_faces_mask(self, faces): good = set() for face in faces: new = set([SvSolidTopology.Item(v) for v in face.Vertexes]) good.update(new) return [SvSolidTopology.Item(vertex) in good for vertex in self.solid.Vertexes]
def get_vertices_by_location(self, condition)
-
Expand source code
def get_vertices_by_location(self, condition): to_tuple = lambda v : (v.X, v.Y, v.Z) return [to_tuple(v) for v in self.solid.Vertexes if condition(to_tuple(v))]
def get_vertices_by_location_mask(self, condition)
-
Expand source code
def get_vertices_by_location_mask(self, condition): to_tuple = lambda v : (v.X, v.Y, v.Z) return [condition(to_tuple(v)) for v in self.solid.Vertexes]
def get_vertices_within_range(self, origin, distance)
-
Expand source code
def get_vertices_within_range(self, origin, distance): found = self._tree.find_range(tuple(origin), distance) idxs = [item[1] for item in found] vertices = [self.solid.Vertexes[i] for i in idxs] return vertices
def get_vertices_within_range_mask(self, origin, distance)
-
Expand source code
def get_vertices_within_range_mask(self, origin, distance): found = self._tree.find_range(tuple(origin), distance) idxs = set([item[1] for item in found]) return [i in idxs for i in range(len(self.solid.Vertexes))]
def tessellate(self, precision)
-
Expand source code
def tessellate(self, precision): self._points_by_edge = defaultdict(list) self._points_by_face = defaultdict(list) for edge in self.solid.Edges: points = edge.discretize(Deflection=precision) i_edge = SvSolidTopology.Item(edge) for pt in points: self._points_by_edge[i_edge].append((pt.x, pt.y, pt.z)) for face in self.solid.Faces: data = face.tessellate(precision) i_face = SvSolidTopology.Item(face) for pt in data[0]: self._points_by_face[i_face].append((pt.x, pt.y, pt.z))