def rotate_z(self, verts, angle): if abs(angle) < 1e-6: return verts projection = [self.to2d(v) for v in verts] x0, y0 = center(projection) c = self.from2d(x0, y0) rot = Matrix.Rotation(angle, 4, self.normal_axis) result = [(rot @ (v - c)) + c for v in verts] return result
def ray_intersection(self, p, line): p = Vector(center(line.sites)) intersection = self.circle.intersect_with_line(line) #info("RI: {line} X {self.circle} => {intersection}") if intersection is None: return None else: v1, v2 = intersection r1 = (p - v1).length r2 = (p - v2).length if r1 < r2: return v1 else: return v2
def init_from_sites(self, sites): self.x_max = -BIG_FLOAT self.x_min = BIG_FLOAT self.y_min = BIG_FLOAT self.y_max = -BIG_FLOAT x0, y0, z0 = center(sites) self.center = (x0, y0) # creates points in format for voronoi library, throwing away z for x, y, z in sites: r = sqrt((x-x0)**2 + (y-y0)**2) self.r_max = max(r, self.r_max) self.x_max = max(x, self.x_max) self.x_min = min(x, self.x_min) self.y_max = max(y, self.y_max) self.y_min = min(y, self.y_min)
def scale_cells(self, verts, sites, insets, precision): if all(i == 0.0 for i in insets): return verts verts_out = [] for vs, site, inset in zip(verts, sites, insets): if inset >= 1.0: continue if self.scale_center == 'SITE': c = site else: c = center(vs) vs1 = scale_relative(vs, c, 1.0 - inset) if diameter(vs1, axis=None) <= precision: continue verts_out.append(vs1) return verts_out
def ray_intersection(self, p, line): p = Vector(center(line.sites)) min_r = BIG_FLOAT nearest = None for v_i, v_j in self.edges: bound = LineEquation2D.from_two_points(v_i, v_j) intersection = bound.intersect_with_line(line) if intersection is not None: r = (p - intersection).length #info("INT: [%s - %s] X [%s] => %s (%s)", v_i, v_j, line, intersection, r) if r < min_r: nearest = intersection min_r = r return nearest
def process(self): if not self.inputs['Vertices'].is_linked: return if not self.outputs['Vertices'].is_linked: return points_in = self.inputs['Vertices'].sv_get() pts_out = [] # polys_out = [] edges_out = [] for obj in points_in: bounds = Bounds.new(self.bound_mode) source_sites = [] bounds.x_max = -BIG_FLOAT bounds.x_min = BIG_FLOAT bounds.y_min = BIG_FLOAT bounds.y_max = -BIG_FLOAT x0, y0, z0 = center(obj) bounds.center = (x0, y0) # creates points in format for voronoi library, throwing away z for x, y, z in obj: r = sqrt((x - x0)**2 + (y - y0)**2) bounds.r_max = max(r, bounds.r_max) bounds.x_max = max(x, bounds.x_max) bounds.x_min = min(x, bounds.x_min) bounds.y_max = max(y, bounds.y_max) bounds.y_min = min(y, bounds.y_min) source_sites.append(Site(x, y)) delta = self.clip bounds.x_max = bounds.x_max + delta bounds.y_max = bounds.y_max + delta bounds.x_min = bounds.x_min - delta bounds.y_min = bounds.y_min - delta bounds.r_max = bounds.r_max + delta voronoi_data = computeVoronoiDiagram(source_sites) verts = voronoi_data.vertices lines = voronoi_data.lines all_edges = voronoi_data.edges finite_edges = [(edge[1], edge[2]) for edge in all_edges if -1 not in edge] bm = Mesh2D.from_pydata(verts, finite_edges) # clipping box to bounding box. verts_to_remove = set() edges_to_remove = set() bounding_verts = [] # For each diagram vertex that is outside of the bounds, # cut each edge connected with that vertex by bounding line. # Remove such vertices, remove such edges, and instead add # vertices lying on the bounding line and corresponding edges. for vert_idx, vert in enumerate(bm.verts[:]): x, y = tuple(vert) if not bounds.contains((x, y)): verts_to_remove.add(vert_idx) for other_vert_idx in list(bm.linked_verts[vert_idx]): edges_to_remove.add((vert_idx, other_vert_idx)) if self.draw_hangs or self.draw_bounds: other_vert = bm.verts[other_vert_idx] if other_vert is not None: x2, y2 = tuple(other_vert) intersection = bounds.segment_intersection( (x, y), (x2, y2)) if intersection is not None: intersection = tuple(intersection) new_vert_idx = bm.new_vert(intersection) bounding_verts.append(new_vert_idx) #info("CLIP: Added point: %s => %s", (x_i, y_i), new_vert_idx) bm.new_edge(other_vert_idx, new_vert_idx) # Diagram lines that go infinitely from one side of diagram to another infinite_lines = [] # Lines that start at the one vertex of the diagram and go to infinity rays = defaultdict(list) if self.draw_hangs or self.draw_bounds: sites_by_line = defaultdict(list) for site_idx in voronoi_data.polygons.keys(): for line_index, i1, i2 in voronoi_data.polygons[site_idx]: if i1 == -1 or i2 == -1: site = source_sites[site_idx] sites_by_line[line_index].append((site.x, site.y)) for line_index, i1, i2 in all_edges: if i1 == -1 or i2 == -1: line = lines[line_index] a, b, c = line eqn = LineEquation2D(a, b, -c) if i1 == -1 and i2 != -1: eqn.sites = sites_by_line[line_index] rays[i2].append(eqn) elif i2 == -1 and i1 != -1: eqn.sites = sites_by_line[line_index] rays[i1].append(eqn) elif i1 == -1 and i2 == -1: infinite_lines.append(eqn) # For each (half-infinite) ray, calculate it's intersection # with the bounding line and draw an edge from ray's beginning to # the bounding line. # NB: The data returned from voronoi.py for such lines # is a vertex and a line equation. The line obviously intersects # the bounding line in two points; which one should we choose? # Let's choose that one which is closer to site points which the # line is dividing. for vert_index in rays.keys(): x, y = bm.verts[vert_index] vert = Vector((x, y)) if vert_index not in verts_to_remove: for line in rays[vert_index]: intersection = bounds.ray_intersection(vert, line) intersection = tuple(intersection) new_vert_idx = bm.new_vert(intersection) bounding_verts.append(new_vert_idx) #info("INF: Added point: %s: %s => %s", (x,y), (x_i, y_i), new_vert_idx) bm.new_edge(vert_index, new_vert_idx) # For each infinite (in two directions) line, # calculate two it's intersections with the bounding # line and connect them by an edge. for eqn in infinite_lines: intersections = bounds.line_intersection(eqn) if len(intersections) == 2: v1, v2 = intersections new_vert_1_idx = bm.new_vert(tuple(v1)) new_vert_2_idx = bm.new_vert(tuple(v2)) bounding_verts.append(new_vert_1_idx) bounding_verts.append(new_vert_2_idx) bm.new_edge(new_vert_1_idx, new_vert_2_idx) else: self.error( "unexpected number of intersections of infinite line %s with area bounds: %s", eqn, intersections) # TODO: there could be (finite) edges, which have both ends # outside of the bounding line. We could detect such edges and # process similarly to infinite lines - calculate two intersections # with the bounding line and connect them by an edge. # Currently I consider such cases as rare, so this is a low priority issue. # Btw, such edges do not fall under definition of either "bounding edge" # or "hanging edge"; so should we add a separate checkbox for such edges?... if self.draw_bounds and bounding_verts: bounding_verts.sort( key=lambda idx: atan2(bm.verts[idx][1], bm.verts[idx][0])) for i, j in zip(bounding_verts, bounding_verts[1:]): bm.new_edge(i, j) bm.new_edge(bounding_verts[-1], bounding_verts[0]) for i, j in edges_to_remove: bm.remove_edge(i, j) for vert_idx in verts_to_remove: bm.verts[vert_idx] = None verts, edges = bm.to_pydata() verts3d = [(vert[0], vert[1], 0) for vert in verts] pts_out.append(verts3d) edges_out.append(edges) #edges_out.append(finite_edges) # outputs self.outputs['Vertices'].sv_set(pts_out) self.outputs['Edges'].sv_set(edges_out)
def process(self): if not any(socket.is_linked for socket in self.outputs): return solid_in = self.inputs['Solid'].sv_get() sites_in = self.inputs['Sites'].sv_get() inset_in = self.inputs['Inset'].sv_get() solid_in = ensure_nesting_level(solid_in, 2, data_types=(Part.Shape, )) input_level = get_data_nesting_level(sites_in) sites_in = ensure_nesting_level(sites_in, 4) inset_in = ensure_nesting_level(inset_in, 2) nested_output = input_level > 3 need_inner = self.outputs['InnerSolid'].is_linked need_outer = self.outputs['OuterSolid'].is_linked precision = 10**(-self.accuracy) inner_fragments_out = [] outer_fragments_out = [] for params in zip_long_repeat(solid_in, sites_in, inset_in): new_inner_fragments = [] new_outer_fragments = [] for solid, sites, inset in zip_long_repeat(*params): verts, edges, faces = voronoi_on_solid(solid, sites, do_clip=True, clipping=None) if inset != 0.0: scale = 1.0 - inset if self.scale_center == 'SITE': verts = [ scale_relative(vs, site, scale) for vs, site in zip(verts, sites) ] else: verts = [ scale_relative(vs, center(vs), scale) for vs, site in zip(verts, sites) ] fragments = [ svmesh_to_solid(vs, fs, precision, method=BMESH, remove_splitter=False) for vs, fs in zip(verts, faces) ] if self.mode == 'SURFACE': if solid.Shells: shell = solid.Shells[0] else: shell = Part.Shell(solid.Faces) src = shell else: # VOLUME src = solid if need_inner: inner = [src.common(fragment) for fragment in fragments] if self.flat_output: new_inner_fragments.extend(inner) else: new_inner_fragments.append(inner) if need_outer: outer = [src.cut(fragments)] if self.flat_output: new_outer_fragments.extend(outer) else: new_outer_fragments.append(outer) if nested_output: inner_fragments_out.append(new_inner_fragments) outer_fragments_out.append(new_outer_fragments) else: inner_fragments_out.extend(new_inner_fragments) outer_fragments_out.extend(new_outer_fragments) self.outputs['InnerSolid'].sv_set(inner_fragments_out) self.outputs['OuterSolid'].sv_set(outer_fragments_out)
def make_surface(self, face, degree_u, degree_v, vertices, planes, vert_weights, tangent_weights, face_weight, edge_weights_dict, edge_planes_dict): """ V0 ------ [E01] --- [E0C] --- [E02] --- V1 | | | | | | | | | | | | | | | [E11] --- [F1] ---- [E0F] --- [F2] --- [E21] | | | | | | | | | | | | | | | [E1C] --- [E1F] --- [CC] --- [E2F] --- [E2C] | | | | | | | | | | | | | | | [E12] --- [F3] ---- [E3F] --- [F4] --- [E22] | | | | | | | | | | | | | | | V3 ------ [E31] --- [E3C] --- [E32] --- V2 """ tangent_weights = [w / 3.0 for w in tangent_weights] vertices = [Vector(v) for v in vertices] def mk_edge_point(i, j): return (vertices[j] - vertices[i]) * tangent_weights[i] + vertices[i] def mk_face_corner_point(i, j, k): dv1 = (vertices[j] - vertices[i]) * tangent_weights[i] dv2 = (vertices[k] - vertices[i]) * tangent_weights[i] #m = face_weight return planes[i].projection_of_point(vertices[i] + dv1 + dv2) # edge planes e0p = edge_planes_dict[(face[0], face[1])] e1p = edge_planes_dict[(face[0], face[3])] e2p = edge_planes_dict[(face[1], face[2])] e3p = edge_planes_dict[(face[2], face[3])] def mk_edge_center_point(ep1, ep2): return (ep1 + ep2) / 2.0 def mk_face_edge_point(edge_plane, edge_point, edge_vec, vec1, vec2): length = (vec1.length + vec2.length) / 2.0 vec = edge_plane.normal.cross(edge_vec) #print("EV: %s, N: %s, L: %s, Res: %s" % (edge_vec, edge_plane.normal, length, vec)) vec = length * vec.normalized() return edge_point + vec e01 = planes[0].projection_of_point(mk_edge_point(0, 1)) e02 = planes[1].projection_of_point(mk_edge_point(1, 0)) e11 = planes[0].projection_of_point(mk_edge_point(0, 3)) e21 = planes[1].projection_of_point(mk_edge_point(1, 2)) f1 = mk_face_corner_point(0, 1, 3) f2 = mk_face_corner_point(1, 0, 2) e12 = planes[3].projection_of_point(mk_edge_point(3, 0)) e31 = planes[3].projection_of_point(mk_edge_point(3, 2)) e32 = planes[2].projection_of_point(mk_edge_point(2, 3)) e22 = planes[2].projection_of_point(mk_edge_point(2, 1)) f3 = mk_face_corner_point(3, 0, 2) f4 = mk_face_corner_point(2, 3, 1) e0c = mk_edge_center_point(e01, e02) e1c = mk_edge_center_point(e11, e12) e2c = mk_edge_center_point(e21, e22) e3c = mk_edge_center_point(e31, e32) e0f = mk_face_edge_point(e0p, e0c, (vertices[1] - vertices[0]), (f1 - e01), (f2 - e02)) e1f = mk_face_edge_point(e1p, e1c, (vertices[0] - vertices[3]), (f3 - e12), (f1 - e11)) e2f = mk_face_edge_point(e2p, e2c, (vertices[2] - vertices[1]), (f2 - e21), (f4 - e22)) e3f = mk_face_edge_point(e3p, e3c, (vertices[3] - vertices[2]), (f3 - e31), (f4 - e32)) cc = center([f1, e0f, f2, e2f, f4, e3f, f3, e1f]) control_points = [ vertices[0], e01, e0c, e02, vertices[1], e11, f1, e0f, f2, e21, e1c, e1f, cc, e2f, e2c, e12, f3, e3f, f4, e22, vertices[3], e31, e3c, e32, vertices[2] ] # edge point weights e0w = edge_weights_dict[(face[0], face[1])] e1w = edge_weights_dict[(face[0], face[3])] e2w = edge_weights_dict[(face[1], face[2])] e3w = edge_weights_dict[(face[2], face[3])] weights = [ vert_weights[0], e0w, e0w, e0w, vert_weights[1], e1w, face_weight, face_weight, face_weight, e2w, e1w, face_weight, face_weight, face_weight, e2w, e1w, face_weight, face_weight, face_weight, e2w, vert_weights[3], e3w, e3w, e3w, vert_weights[2] ] surface = NURBS.Surface() surface.degree_u = degree_u surface.degree_v = degree_v surface.ctrlpts_size_u = 5 surface.ctrlpts_size_v = 5 surface.ctrlpts = control_points surface.weights = weights surface.knotvector_u = knotvector.generate(surface.degree_u, 5) surface.knotvector_v = knotvector.generate(surface.degree_v, 5) new_surf = SvExGeomdlSurface(surface) return new_surf, control_points, weights