def _filter_groups_from_poly_node(tree, groups, is_outer_perimeter=None): """ Recursively parses clippers <PyPolyTree> for island contours Fills #islands parameter with collections of paths that represent islands. Path element is <Point> if is_outer_perimeter is None -> we don't know what is it The first child that has non-empty .contour is considered as outer perimeter """ if is_outer_perimeter is None: is_outer_perimeter = True if tree.Contour else False if is_outer_perimeter: """ Structure of the tree: [Island root].contour = outer perimeter `-> [Island child].contour = inner perimeter `-> [Island root] ... `-> [Island child].contour = inner perimeter """ paths = [to_ndarray(tree.Contour)] groups.append(paths) for child in tree.Childs: paths.append(to_ndarray(child.Contour)) _filter_groups_from_poly_node(child, groups, False) else: for child in tree.Childs: _filter_groups_from_poly_node(child, groups, True)
def import_contents(self, progress): progress.set_size(self.vertex_nr) normal_kw = 'facet normal' vertex_kw = 'vertex' end_facet_kw = 'endfacet' for line_number, line in enumerate(self.contents.splitlines()): if normal_kw in line: normal = to_ndarray([ DEFAULT_COORDINATE_TYPE(x) for x in line.strip().split()[-3:] ]) if np.array_equal(normal, NULL_VECTOR): raise ValueError("missing normal in line " + str(line_number)) if vertex_kw in line: # parse vector coordinates vector = to_ndarray( [float(x) for x in line.strip().split()[-3:]]) self.merger.add(vector) progress.inc() if end_facet_kw in line: self.merger.set_last_face_normal(normal) self.merger.finalize() progress.done() return self.tm
def import_contents(self, progress): progress.set_size(self.vertex_nr) normal_kw = 'facet normal' vertex_kw = 'vertex' end_facet_kw = 'endfacet' for line_number, line in enumerate(self.contents.splitlines()): if normal_kw in line: normal = to_ndarray([DEFAULT_COORDINATE_TYPE(x) for x in line.strip().split()[-3:]]) if np.array_equal(normal, NULL_VECTOR): raise ValueError("missing normal in line "+str(line_number)) if vertex_kw in line: # parse vector coordinates vector = to_ndarray([float(x) for x in line.strip().split()[-3:]]) self.merger.add(vector) progress.inc() if end_facet_kw in line: self.merger.set_last_face_normal(normal) self.merger.finalize() progress.done() return self.tm
def __init__(self, cell_size, dim=3): super(MergingSpatialHash, self).__init__(cell_size) self.dim = dim self.cell_radius = cell_size / 2 # moves to all neighbor cells self._moves = to_ndarray(list(product([0, self.cell_radius, -self.cell_radius], repeat=dim)))
def import_contents(self, progress): progress.set_size(self.vertex_nr) byte_idx = 84 for face_idx in range(self._get_face_nr()): normal = self._parse_vector(byte_idx) if normal == [0.0, 0.0, 0.0]: raise ValueError("missing normal in face " + str(face_idx)) for vertex_idx in range(1, 4): vector = to_ndarray( self._parse_vector(byte_idx + 12 * vertex_idx)) self.merger.add(vector) progress.inc() self.merger.set_last_face_normal(normal) byte_idx += 50 self.merger.finalize() progress.done() return self.tm
def center(self, width, length): aabb = self.aabb # center X, Y x = width / 2.0 y = length / 2.0 z = aabb.center[2] - aabb.min[2] self.mx += to_ndarray([x, y, z]) - aabb.center
def execute(delta, nr, aabb, initial_delta=0): initial_delta += delta * nr contour = to_ndarray([(aabb.min - initial_delta)[:2], [aabb.min[0] - initial_delta, aabb.max[1] + initial_delta], (aabb.max + initial_delta)[:2], [aabb.max[0] + initial_delta, aabb.min[1] - initial_delta]]) offsets = offset.execute([contour], -abs(delta), nr) return [o[0] for o in offsets]
def __init__(self, cell_size, dim=3): super(MergingSpatialHash, self).__init__(cell_size) self.dim = dim self.cell_radius = cell_size / 2 # moves to all neighbor cells self._moves = to_ndarray( list(product([0, self.cell_radius, -self.cell_radius], repeat=dim)))
def slice(self, progress): self._init_slicing_positions() progress.set_size(len(self._slicing_positions)) self._init_edge_height_map() for i, h in enumerate(sorted(self._edge_map.keys())): contours = [] # edges that need to be visited on this height to_visit = self._edge_map[h] while len(to_visit) > 0: # 2D path - z coordinate is omitted as it is represented as height contour = [] prev_face = None edge = self.tm.edges[to_visit.pop()] while True: # intersect the edge intersection = _edge_2d_intersection(edge, h) contour.append(intersection) to_visit.discard(edge.mxid) # march if prev_face is None: # doesn't matter which face prev_face = edge.face_a else: # select the opposite from the one we came prev_face = edge.face_b if prev_face is edge.face_a else edge.face_a # select edge from edges of a face that has not yet been worked on edge = next( (e for e in prev_face.edges if e.mxid in to_visit), None) # contour is finished when there is no more edges to cut # WARNING: if the topology of the model is not manifold (holes in mesh) # it might happen that the contour will be closed too soon if edge is None: break # All contours are closed contours.append(to_ndarray(contour)) if contours: self.add_layer(contours, h, i) progress.inc() progress.done()
def execute(delta, nr, aabb, initial_delta=0): initial_delta += delta * nr contour = to_ndarray([ (aabb.min - initial_delta)[:2], [aabb.min[0] - initial_delta, aabb.max[1] + initial_delta], (aabb.max + initial_delta)[:2], [aabb.max[0] + initial_delta, aabb.min[1] - initial_delta] ]) offsets = offset.execute([contour], -abs(delta), nr) return [o[0] for o in offsets]
def slice(self, progress): self._init_slicing_positions() progress.set_size(len(self._slicing_positions)) self._init_edge_height_map() for i, h in enumerate(sorted(self._edge_map.keys())): contours = [] # edges that need to be visited on this height to_visit = self._edge_map[h] while len(to_visit) > 0: # 2D path - z coordinate is omitted as it is represented as height contour = [] prev_face = None edge = self.tm.edges[to_visit.pop()] while True: # intersect the edge intersection = _edge_2d_intersection(edge, h) contour.append(intersection) to_visit.discard(edge.mxid) # march if prev_face is None: # doesn't matter which face prev_face = edge.face_a else: # select the opposite from the one we came prev_face = edge.face_b if prev_face is edge.face_a else edge.face_a # select edge from edges of a face that has not yet been worked on edge = next((e for e in prev_face.edges if e.mxid in to_visit), None) # contour is finished when there is no more edges to cut # WARNING: if the topology of the model is not manifold (holes in mesh) # it might happen that the contour will be closed too soon if edge is None: break # All contours are closed contours.append(to_ndarray(contour)) if contours: self.add_layer(contours, h, i) progress.inc() progress.done()
def __init__(self, model, settings, gcode_file): self.model = model self.s = settings self.gcode_file = gcode_file self.coder = None self.home = to_ndarray([0, 0]) self.start = self.home self.change_layer = True self.height = 0 self.y = 0 self._model_center = model.get_center()
def move(self, vertex=None, x=None, y=None, z=None, e=None, ce=False, f=None): if vertex is not None: x = vertex[0] y = vertex[1] if ce: # calculate e e = euclidean_dist(self.start, vertex if vertex is not None else to_ndarray([x, y])) * self.e_per_mm if e: self.add_e(e) e = self.e_pos self.coder.move(x, y, z, e, f)
def __init__(self, model, settings, gcode_file): self.model = model self.s = settings self.coder = None self.gcode_file = gcode_file self.home = to_ndarray([0, 0]) self.start = self.home self.change_layer = True self.height = 0 self.e_per_mm = None self.e_pos = 0 self.e_len = 0
def get_aabb_lines(aabb, delta): # get max aabb for this area so that it is independent # of rotation d = euclidean_dist(aabb.center[:2], aabb.max[:2]) min_v, max_v = aabb.min - d, aabb.max + d x_l, x_r = min_v[0], max_v[0] y_positions = np_range(min_v[1], max_v[1], delta) lines = [] for y in y_positions: lines.append(to_ndarray([[x_l, y], [x_r, y]])) return lines
def import_contents(self, progress): progress.set_size(self.vertex_nr) vertex_kw = 'vertex' for line in self.contents.splitlines(): if vertex_kw in line: # parse vector coordinates vector = to_ndarray([float(x) for x in line.strip().split()[-3:]]) self.merger.add(vector) progress.inc() self.merger.finalize() progress.done() return self.tm
def import_contents(self, progress): progress.set_size(self.vertex_nr) byte_idx = 84 for face_idx in range(self._get_face_nr()): for vertex_idx in range(1, 4): vector = to_ndarray(self._parse_vector(byte_idx + 12 * vertex_idx)) self.merger.add(vector) progress.inc() byte_idx += 50 self.merger.finalize() progress.done() return self.tm
def _closest_path(segments, point, paths_dict, order_path): min_path, min_point, min_point_id, min_d, min_segment = None, None, None, None, None for segment in segments: path = paths_dict[segment[0]] point_id = None if _is_closed_segment(segment): # segment p, d, point_id = closest_point_on_edge(path[segment[1]], path[segment[2]], point) else: # point p = path[segment[1]] d = euclidean_dist_square(p, point) if min_d is None or d < min_d: min_path, min_point, min_d, min_segment, min_point_id = path, p, d, segment, point_id if order_path: if _is_closed_segment(min_segment): if min_point_id is 2: # order contour so that the projection point is the starting point min_path = np.concatenate( (to_ndarray([min_point]), min_path[min_segment[2]:], min_path[:min_segment[2]])) else: # closest point is part of the path, rotate the path # around that point if min_segment[1] is 0: # proper contour start, do nothing pass else: min_path = np.roll(min_path, min_segment[min_point_id], axis=0) elif min_segment[1] < 0: # reverse open path if it ends on the end point denoted as -1 min_path = min_path[::-1] return min_path, min_segment[0]
def move(self, vertex=None, x=None, y=None, z=None, e=None, ce=False, f=None): if vertex is not None: x = vertex[0] y = vertex[1] if ce: # calculate e e = euclidean_dist( self.start, vertex if vertex is not None else to_ndarray([x, y])) * self.e_per_mm if e: self.add_e(e) e = self.e_pos self.coder.move(x, y, z, e, f)
def import_contents(self, progress): progress.set_size(self.vertex_nr) byte_idx = 84 for face_idx in range(self._get_face_nr()): normal = self._parse_vector(byte_idx) if normal == [0.0, 0.0, 0.0]: raise ValueError("missing normal in face "+str(face_idx)) for vertex_idx in range(1, 4): vector = to_ndarray(self._parse_vector(byte_idx + 12 * vertex_idx)) self.merger.add(vector) progress.inc() self.merger.set_last_face_normal(normal) byte_idx += 50 self.merger.finalize() progress.done() return self.tm
def _closest_path(segments, point, paths_dict, order_path): min_path, min_point, min_point_id, min_d, min_segment = None, None, None, None, None for segment in segments: path = paths_dict[segment[0]] point_id = None if _is_closed_segment(segment): # segment p, d, point_id = closest_point_on_edge(path[segment[1]], path[segment[2]], point) else: # point p = path[segment[1]] d = euclidean_dist_square(p, point) if min_d is None or d < min_d: min_path, min_point, min_d, min_segment, min_point_id = path, p, d, segment, point_id if order_path: if _is_closed_segment(min_segment): if min_point_id is 2: # order contour so that the projection point is the starting point min_path = np.concatenate((to_ndarray([min_point]), min_path[min_segment[2]:], min_path[:min_segment[2]])) else: # closest point is part of the path, rotate the path # around that point if min_segment[1] is 0: # proper contour start, do nothing pass else: min_path = np.roll(min_path, min_segment[min_point_id], axis=0) elif min_segment[1] < 0: # reverse open path if it ends on the end point denoted as -1 min_path = min_path[::-1] return min_path, min_segment[0]
def convert(contours=None, lines=None, polylines=None, outputfile=None, display=False): lines = _get_list(lines) contours = _get_list(contours) polylines = _get_list(polylines) all_elements = [lines, contours, polylines] all_l = sum(len(l) for l in all_elements) colors = _get_colors(all_l) info = {} text_width = 120 default_width = text_width + 15 * all_l line_height = 15 extra_height = 3 * line_height aabb = get_aabb( np.concatenate([np.concatenate(x) for x in all_elements if len(x) > 0])) add_x = -aabb.min[0] add_y = -aabb.min[1] add_v = to_ndarray([add_x, add_y]) result = "\n<svg width=\"800\" height=\"600\" viewbox=\"{} {} {} {}\">".format( 0, 0, int(aabb.max[0] - aabb.min[0]) + 10, int(aabb.max[1] - aabb.min[1]) + 10) for contour in contours: contour = contour + add_v color = colors.pop() result += "\n\t<polygon points=\"{}\" style=\"fill:none;stroke:{};stroke-width:0.1\" />".format( _format_polygon(contour), color) info.setdefault('Contours', []).append(color) for line in lines: line = line + add_v color = colors.pop() result += "\n\t<line x1=\"{}\" y1=\"{}\" x2=\"{}\" y2=\"{}\" style=\"stroke:{};stroke-width:0.1\" />".format( line[0][0], line[0][1], line[-1][0], line[-1][1], color) info.setdefault('Lines', []).append(color) for polyline in polylines: polyline = polyline + add_v color = colors.pop() result += "\n\t<polyline points=\"{}\" style=\"fill:none;stroke:{};stroke-width:0.1\" />".format( _format_polygon(polyline), color) info.setdefault('Polylines', []).append(color) result += "\n</svg>" result += "\n<br /><svg width=\"{}\" height=\"{}\">".format( default_width, extra_height) i = 0 for k, v in info.items(): x, y = 0, extra_height - (i * line_height) result += "\n\t<text x=\"{}\" y=\"{}\" font-family=\"sans-serif\" font-size=\"{}\" fill=\"black\">{} ({})</text>".format( x, y, line_height, k, len(v)) for j, color in enumerate(v): result += "\n\t<rect x=\"{}\" y=\"{}\" width=\"10\" height=\"10\" style=\"fill:{};stroke-width:0;\"/>".format( text_width + (j * 15), y - 10, color) i += 1 result += "\n</svg>" return result
def _to_ndarrays(clipper_paths): paths = [] for clipper_path in clipper_paths: paths.append(to_ndarray(clipper_path)) return paths
import numpy as np from quadtree import Quadtree from grslicer.util.np import get_aabb, to_ndarray, euclidean_dist_square, closest_point_on_edge POINT_AABB = to_ndarray([[-10, -10], [10, 10]]) def follow(paths, get_start_func, closed=True): """ Iterate through paths as they follow by closeness. Yields the closest path that starts on the closest point, and the ending point of the path. """ if len(paths) is 0: # TODO: check this return tree, segment_mapping = _get_tree(paths, closed) todo_path_mapping = dict([(id(path), path) for path in paths]) while len(todo_path_mapping) > 0: path, path_id = _query(tree, segment_mapping, get_start_func(), todo_path_mapping) del todo_path_mapping[path_id] if closed: start = path[0] else: start = path[-1]
from struct import unpack import vertexmerger from grslicer.model import TopoModel, DEFAULT_COORDINATE_TYPE from grslicer.util.np import to_ndarray, np from grslicer.importers.base import ModelImporter from grslicer.util.progress import progress_log NULL_VECTOR = to_ndarray([0.0, 0.0, 0.0]) class StlAsciiImporter(ModelImporter, vertexmerger.VertexMerger): @staticmethod def can_import(contents): return all( [kw in contents for kw in ('solid', 'vertex', 'facet normal')]) def __init__(self, *args, **kwargs): super(StlAsciiImporter, self).__init__(*args, **kwargs) self.vertex_nr = self._get_face_nr() * 3 self.merger = vertexmerger.VertexMerger( TopoModel(shape=(self.vertex_nr * 3, 3)), DEFAULT_COORDINATE_TYPE(self.settings.roundOffError)) def _get_face_nr(self): return self.contents.count('facet normal') @property def tm(self):
def convert(contours=None, lines=None, polylines=None, outputfile=None, display=False): lines = _get_list(lines) contours = _get_list(contours) polylines = _get_list(polylines) all_elements = [lines, contours, polylines] all_l = sum(len(l) for l in all_elements) colors = _get_colors(all_l) info = {} text_width = 120 default_width = text_width + 15 * all_l line_height = 15 extra_height = 3 * line_height aabb = get_aabb(np.concatenate([np.concatenate(x) for x in all_elements if len(x) > 0])) add_x = -aabb.min[0] add_y = -aabb.min[1] add_v = to_ndarray([add_x, add_y]) result = "\n<svg width=\"800\" height=\"600\" viewbox=\"{} {} {} {}\">".format( 0, 0, int(aabb.max[0] - aabb.min[0]) + 10, int(aabb.max[1] - aabb.min[1]) + 10) for contour in contours: contour = contour + add_v color = colors.pop() result += "\n\t<polygon points=\"{}\" style=\"fill:none;stroke:{};stroke-width:0.1\" />".format( _format_polygon(contour), color) info.setdefault('Contours', []).append(color) for line in lines: line = line + add_v color = colors.pop() result += "\n\t<line x1=\"{}\" y1=\"{}\" x2=\"{}\" y2=\"{}\" style=\"stroke:{};stroke-width:0.1\" />".format( line[0][0], line[0][1], line[-1][0], line[-1][1], color) info.setdefault('Lines', []).append(color) for polyline in polylines: polyline = polyline + add_v color = colors.pop() result += "\n\t<polyline points=\"{}\" style=\"fill:none;stroke:{};stroke-width:0.1\" />".format( _format_polygon(polyline), color) info.setdefault('Polylines', []).append(color) result += "\n</svg>" result += "\n<br /><svg width=\"{}\" height=\"{}\">".format(default_width, extra_height) i = 0 for k, v in info.items(): x, y = 0, extra_height - (i * line_height) result += "\n\t<text x=\"{}\" y=\"{}\" font-family=\"sans-serif\" font-size=\"{}\" fill=\"black\">{} ({})</text>".format( x, y, line_height, k, len(v)) for j, color in enumerate(v): result += "\n\t<rect x=\"{}\" y=\"{}\" width=\"10\" height=\"10\" style=\"fill:{};stroke-width:0;\"/>".format( text_width + (j * 15), y - 10, color) i += 1 result += "\n</svg>" return result
def _intersect_face_line(face, scanline_point, scanline_direction): SMALL_NUM = 0.00000001 # anything that avoids division overflow # dot product (3D) which allows vector operations in arguments #define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z) # intersect3D_RayTriangle(): find the 3D intersection of a ray with a triangle # Input: a ray R, and a triangle T # Output: *I = intersection point (when it exists) # Return: -1 = triangle is degenerate (a segment or point) # 0 = disjoint (no intersect) # 1 = intersect in unique point I1 # 2 = are in the same plane # int intersect3D_RayTriangle( Ray R, Triangle T, Point* I ) # Vector u, v, n; // triangle vectors # Vector dir, w0, w; // ray vectors # float r, a, b; // params to calc ray-plane intersect # get triangle edge vectors and plane normal va = face.vertex_a.vector vb = face.vertex_b.vector vc = face.vertex_c.vector u = vb - va #u = T.V1 - T.V0; v = vc - va #v = T.V2 - T.V0; n2 = np.cross(u, v) #n = u * v; // cross product n = face.normal if n[0] == DEFAULT_COORDINATE_TYPE(0) and n[1] == DEFAULT_COORDINATE_TYPE(0) and n[2] == DEFAULT_COORDINATE_TYPE(0): # // triangle is degenerate return (-1, None) #// do not deal with this case dir = to_ndarray(scanline_direction) #dir = R.P1 - R.P0; // ray direction vector w0 = to_ndarray(scanline_point) - va # w0 = R.P0 - T.V0; a = - np.dot(n, w0) # a = -dot(n,w0); b = np.dot(n, dir) # b = dot(n,dir); if abs(b) < DEFAULT_COORDINATE_TYPE(SMALL_NUM): #if (fabs(b) < SMALL_NUM) { // ray is parallel to triangle plane if a == DEFAULT_COORDINATE_TYPE(0): # if (a == 0) // ray lies in triangle plane return (2, None) else: return (0, None) # // ray disjoint from plane # } # // get intersect point of ray with triangle plane r = a / b # r = a / b; if r < DEFAULT_COORDINATE_TYPE(0.0): # if (r < 0.0) // ray goes away from triangle return (0, None) # // => no intersect # // for a segment, also test if (r > 1.0) => no intersect I = to_ndarray([scanline_point[0]+r, scanline_point[1], scanline_point[2], np.dot(n, dir)/abs(np.dot(n, dir))]) #*I = R.P0 + r * dir; // intersect point of ray and plane # // is I inside T? # float uu, uv, vv, wu, wv, D; uu = np.dot(u, u) # uu = dot(u,u); uv = np.dot(u, v) # uv = dot(u,v); vv = np.dot(v, v) # vv = dot(v,v); w = I[0:3] - va # w = *I - T.V0; wu = np.dot(w, u) # wu = dot(w,u); wv = np.dot(w, v) # wv = dot(w,v); D = uv * uv - uu * vv # D = uv * uv - uu * vv; if D == DEFAULT_COORDINATE_TYPE(0.0): return (0, None) # // get and test parametric coords # float s, t; s = (uv * wv - vv * wu) / D # s = (uv * wv - vv * wu) / D; if s < DEFAULT_COORDINATE_TYPE(0.0) or s > DEFAULT_COORDINATE_TYPE(1.0): # if (s < 0.0 || s > 1.0) // I is outside T return (0, None) t = (uv * wu - uu * wv) / D # t = (uv * wu - uu * wv) / D; if t < DEFAULT_COORDINATE_TYPE(0.0) or (s+t) > DEFAULT_COORDINATE_TYPE(1.0): # if (t < 0.0 || (s + t) > 1.0) // I is outside T return (0, None) return (1, I) # // I is in T
from struct import unpack import vertexmerger from grslicer.model import TopoModel, DEFAULT_COORDINATE_TYPE from grslicer.util.np import to_ndarray, np from grslicer.importers.base import ModelImporter from grslicer.util.progress import progress_log NULL_VECTOR = to_ndarray([0.0, 0.0, 0.0]) class StlAsciiImporter(ModelImporter, vertexmerger.VertexMerger): @staticmethod def can_import(contents): return all([kw in contents for kw in ('solid', 'vertex', 'facet normal')]) def __init__(self, *args, **kwargs): super(StlAsciiImporter, self).__init__(*args, **kwargs) self.vertex_nr = self._get_face_nr() * 3 self.merger = vertexmerger.VertexMerger(TopoModel(shape=(self.vertex_nr * 3, 3)), DEFAULT_COORDINATE_TYPE(self.settings.roundOffError)) def _get_face_nr(self): return self.contents.count('facet normal') @property def tm(self): return self.merger.tm @progress_log('Importing ASCII STL file contents')
def _intersect_face_line(face, scanline_point, scanline_direction): SMALL_NUM = 0.00000001 # anything that avoids division overflow # dot product (3D) which allows vector operations in arguments #define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z) # intersect3D_RayTriangle(): find the 3D intersection of a ray with a triangle # Input: a ray R, and a triangle T # Output: *I = intersection point (when it exists) # Return: -1 = triangle is degenerate (a segment or point) # 0 = disjoint (no intersect) # 1 = intersect in unique point I1 # 2 = are in the same plane # int intersect3D_RayTriangle( Ray R, Triangle T, Point* I ) # Vector u, v, n; // triangle vectors # Vector dir, w0, w; // ray vectors # float r, a, b; // params to calc ray-plane intersect # get triangle edge vectors and plane normal va = face.vertex_a.vector vb = face.vertex_b.vector vc = face.vertex_c.vector u = vb - va #u = T.V1 - T.V0; v = vc - va #v = T.V2 - T.V0; n2 = np.cross(u, v) #n = u * v; // cross product n = face.normal if n[0] == DEFAULT_COORDINATE_TYPE(0) and n[1] == DEFAULT_COORDINATE_TYPE( 0) and n[2] == DEFAULT_COORDINATE_TYPE( 0): # // triangle is degenerate return (-1, None) #// do not deal with this case dir = to_ndarray( scanline_direction) #dir = R.P1 - R.P0; // ray direction vector w0 = to_ndarray(scanline_point) - va # w0 = R.P0 - T.V0; a = -np.dot(n, w0) # a = -dot(n,w0); b = np.dot(n, dir) # b = dot(n,dir); if abs(b) < DEFAULT_COORDINATE_TYPE( SMALL_NUM ): #if (fabs(b) < SMALL_NUM) { // ray is parallel to triangle plane if a == DEFAULT_COORDINATE_TYPE( 0 ): # if (a == 0) // ray lies in triangle plane return (2, None) else: return (0, None) # // ray disjoint from plane # } # // get intersect point of ray with triangle plane r = a / b # r = a / b; if r < DEFAULT_COORDINATE_TYPE( 0.0 ): # if (r < 0.0) // ray goes away from triangle return (0, None) # // => no intersect # // for a segment, also test if (r > 1.0) => no intersect I = to_ndarray([ scanline_point[0] + r, scanline_point[1], scanline_point[2], np.dot(n, dir) / abs(np.dot(n, dir)) ]) #*I = R.P0 + r * dir; // intersect point of ray and plane # // is I inside T? # float uu, uv, vv, wu, wv, D; uu = np.dot(u, u) # uu = dot(u,u); uv = np.dot(u, v) # uv = dot(u,v); vv = np.dot(v, v) # vv = dot(v,v); w = I[0:3] - va # w = *I - T.V0; wu = np.dot(w, u) # wu = dot(w,u); wv = np.dot(w, v) # wv = dot(w,v); D = uv * uv - uu * vv # D = uv * uv - uu * vv; if D == DEFAULT_COORDINATE_TYPE(0.0): return (0, None) # // get and test parametric coords # float s, t; s = (uv * wv - vv * wu) / D # s = (uv * wv - vv * wu) / D; if s < DEFAULT_COORDINATE_TYPE(0.0) or s > DEFAULT_COORDINATE_TYPE( 1.0): # if (s < 0.0 || s > 1.0) // I is outside T return (0, None) t = (uv * wu - uu * wv) / D # t = (uv * wu - uu * wv) / D; if t < DEFAULT_COORDINATE_TYPE(0.0) or (s + t) > DEFAULT_COORDINATE_TYPE( 1.0): # if (t < 0.0 || (s + t) > 1.0) // I is outside T return (0, None) return (1, I) # // I is in T
def _connect_lines(lines, delta, theta, center, max_connection_dist): # lines are np arrays of np arrays tha represent vertices connected_lines = [] # lines should be aligned left-right and be parallel to X-axis lines_by_y = {} d_lines = [] for line in lines: # align left to right if line[0][0] > line[-1][0]: line = line[::-1] # line should be a list of np arrays that represent vertices line = list(line) d_lines.append(line) lines_by_y.setdefault(line[0][1], []).append(line) y_positions = sorted(lines_by_y.keys()) dist_squared = max_connection_dist ** 2 while len(lines_by_y) > 0: prev_y = y_positions[0] # line has to be a list, not a np array # because points will be added to it line = lines_by_y[prev_y].pop() # used y-positions to be removed from line collections used_ys = [prev_y] for y in y_positions[1:]: if not prev_y < y < prev_y + (2 * delta): # > prev_y and np.isclose(y, prev_y + delta): break # find if any of lines at the next y is close enough to connect next_line_connection_side = 0 if ((len(line) % 4) is 0) else -1 current_line_connection_vertex = line[-1] for line_idx, l in enumerate(lines_by_y[y]): next_line_vertex = l[next_line_connection_side] # TODO: maybe do a clip in the square between points and if resulting number of polygons # is 1 than we cont need the euclidean dist -> just put that polygon into line if euclidean_dist_square(current_line_connection_vertex, next_line_vertex) < dist_squared: del lines_by_y[y][line_idx] used_ys.append(y) # properly orient the line if next_line_connection_side is -1: l = l[::-1] line.extend(list(l)) # go for next y position break prev_y = y for y in used_ys: if len(lines_by_y[y]) is 0: del lines_by_y[y] y_positions.remove(y) # convert back to np array connected_lines.append(rotate_xy(to_ndarray(line), theta, center)) return connected_lines