def gh_surface_decomposition(surface_guid, accuracy_value, point_guids, curve_guids): if point_guids == [None]: point_guids = [] if curve_guids == [None]: curve_guids = [] outer_boundary, inner_boundaries, polyline_features, point_features = surface_discrete_mapping(surface_guid, accuracy_value, crv_guids = curve_guids, pt_guids = point_guids) tri_mesh = boundary_triangulation(outer_boundary, inner_boundaries, polyline_features, point_features) decomposition = SkeletonDecomposition.from_mesh(tri_mesh) quad_mesh = decomposition.decomposition_mesh(point_features) RhinoSurface.from_guid(surface_guid).mesh_uv_to_xyz(quad_mesh) return quad_mesh
def surface_discrete_mapping(srf_guid, discretisation, minimum_discretisation = 5, crv_guids = [], pt_guids = []): """Map the boundaries of a Rhino NURBS surface to planar poylines dicretised within some discretisation using the surface UV parameterisation. Curve and point feautres on the surface can be included. Parameters ---------- srf_guid : guid A surface guid. crv_guids : list List of guids of curves on the surface. pt_guids : list List of guids of points on the surface. discretisation : float The discretisation of the surface boundaries. minimum_discretisation : int The minimum discretisation of the surface boundaries. Returns ------- tuple Tuple of the mapped objects: outer boundary, inner boundaries, polyline_features, point_features. """ srf = RhinoSurface.from_guid(srf_guid) # a boundary may be made of multiple boundary components and therefore checking for closeness and joining are necessary mapped_borders = [] for i in [1, 2]: mapped_border = [] for border_guid in srf.borders(type = i): points = [list(srf.point_xyz_to_uv(pt)) + [0.0] for pt in rs.DivideCurve(border_guid, max(int(rs.CurveLength(border_guid) / discretisation) + 1, minimum_discretisation))] if rs.IsCurveClosed(border_guid): points.append(points[0]) mapped_border.append(points) rs.DeleteObject(border_guid) mapped_borders.append(mapped_border) outer_boundaries, inner_boundaries = [network_polylines(Network.from_lines([(u, v) for border in mapped_borders[i] for u, v in pairwise(border)])) for i in [0, 1]] # mapping of the curve features on the surface mapped_curves = [] for crv_guid in crv_guids: curve = RhinoCurve.from_guid(crv_guid) points = [list(srf.point_xyz_to_uv(pt)) + [0.0] for pt in curve.divide(max(int(curve.length() / discretisation) + 1, minimum_discretisation))] if curve.is_closed(): points.append(points[0]) mapped_curves.append(points) polyline_features = network_polylines(Network.from_lines([(u, v) for curve in mapped_curves for u, v in pairwise(curve)])) # mapping of the point features onthe surface point_features = [list(srf.point_xyz_to_uv(rs.PointCoordinates(pt_guid))) + [0.0] for pt_guid in pt_guids] return outer_boundaries[0], inner_boundaries, polyline_features, point_features
def automated_smoothing_surface_constraints(mesh, surface): """Apply automatically surface-related constraints to the vertices of a mesh to smooth: kinks, boundaries and surface. Parameters ---------- mesh : Mesh The mesh to apply the constraints to for smoothing. surface : Rhino surface guid A Rhino surface guid on which to constrain mesh vertices. Returns ------- constraints : dict A dictionary of mesh constraints for smoothing as vertex keys pointing to point, curve or surface objects. """ surface = RhinoSurface.from_guid(surface) constraints = {} points = [rs.AddPoint(point) for point in surface.kinks()] curves = surface.borders(type = 0) constraints.update({vkey: surface.guid for vkey in mesh.vertices()}) for vkey in mesh.vertices_on_boundary(): xyz = mesh.vertex_coordinates(vkey) projections = {curve: distance_point_point(xyz, RhinoCurve.from_guid(curve).closest_point(xyz)) for curve in curves} constraints.update({vkey: min(projections, key = projections.get)}) key_to_index = {i: vkey for i, vkey in enumerate(mesh.vertices_on_boundary())} vertex_coordinates = tuple(mesh.vertex_coordinates(vkey) for vkey in mesh.vertices_on_boundary()) constraints.update({key_to_index[closest_point_in_cloud(rs.PointCoordinates(point), vertex_coordinates)[2]]: point for point in points}) return constraints
def automated_smoothing_constraints(mesh, points = None, curves = None, surface = None, mesh2 = None): """Apply automatically point, curve and surface constraints to the vertices of a mesh to smooth. Parameters ---------- mesh : Mesh The mesh to apply the constraints to for smoothing. points : list List of XYZ coordinates on which to constrain mesh vertices. Default is None. curves : list List of Rhino curve guids on which to constrain mesh vertices. Default is None. surface : Rhino surface guid A Rhino surface guid on which to constrain mesh vertices. Default is None. mesh2 : Rhino mesh guid A Rhino mesh guid on which to constrain mesh vertices. Default is None. Returns ------- constraints : dict A dictionary of mesh constraints for smoothing as vertex keys pointing to point, curve or surface objects. """ if surface: surface = RhinoSurface.from_guid(surface) if curves: curves = [RhinoCurve.from_guid(curve) for curve in curves] if mesh2: mesh2 = RhinoMesh.from_guid(mesh2) constraints = {} constrained_vertices = {} vertices = list(mesh.vertices()) vertex_coordinates = [mesh.vertex_coordinates(vkey) for vkey in mesh.vertices()] if points is not None and len(points) != 0: constrained_vertices.update({vertices[closest_point_in_cloud(rs.PointCoordinates(point), vertex_coordinates)[2]]: point for point in points}) if mesh2 is not None: constraints.update({vkey: mesh2.guid for vkey in mesh.vertices()}) if surface is not None: constraints.update({vkey: surface.guid for vkey in mesh.vertices()}) if curves is not None and len(curves) != 0: boundaries = [split_boundary for boundary in mesh.boundaries() for split_boundary in list_split(boundary, [boundary.index(vkey) for vkey in constrained_vertices.keys() if vkey in boundary])] boundary_midpoints = [Polyline([mesh.vertex_coordinates(vkey) for vkey in boundary]).point(t = .5) for boundary in boundaries] curve_midpoints = [rs.EvaluateCurve(curve, rs.CurveParameter(curve, .5)) for curve in curves] midpoint_map = {i: closest_point_in_cloud(boundary_midpoint, curve_midpoints)[2] for i, boundary_midpoint in enumerate(boundary_midpoints)} constraints.update({vkey: curves[midpoint_map[i]].guid for i, boundary in enumerate(boundaries) for vkey in boundary}) if points is not None: constraints.update(constrained_vertices) return constraints
def gh_surface_skeleton(surface_guid, accuracy_value, point_guids, curve_guids): if point_guids == [None]: point_guids = [] if curve_guids == [None]: curve_guids = [] outer_boundary, inner_boundaries, polyline_features, point_features = surface_discrete_mapping(surface_guid, accuracy_value, crv_guids = curve_guids, pt_guids = point_guids) tri_mesh = boundary_triangulation(outer_boundary, inner_boundaries, polyline_features, point_features) skeleton = Skeleton.from_mesh(tri_mesh) branches = skeleton.branches() polylines = [rs.AddPolyline(RhinoSurface.from_guid(surface_guid).polyline_uv_to_xyz([point[:2] for point in branch])) for branch in branches] return polylines
def customized_smoothing_constraints(mesh, constraints): """Add custom point, curve and surface constraints to the vertices of a mesh to smooth. Parameters ---------- mesh : Mesh The mesh to apply the constraints to for smoothing. constraints : dict A dictionary of mesh constraints for smoothing as vertex keys pointing to point, curve or surface objects. Returns ------- constraints : dict The updated dictionary of mesh constraints for smoothing as vertex keys pointing to point, curve or surface objects. """ while True: guids = display_smoothing_constraints(mesh, constraints) vkeys = mesh_select_vertices(mesh) if len(vkeys) == 2 and rs.GetString( 'get all polyedge?', strings=['True', 'False']) == 'True': u, v = vkeys vkeys = mesh.polyedge(u, v) if vkeys is None: break constraint = rs.GetString( 'edit smoothing constraints?', strings=['point', 'curve', 'surface', 'exit']) rs.DeleteObjects(guids) if constraint is None or constraint == 'exit': break elif constraint == 'point': point = RhinoPoint.from_selection() constraints.update({vkey: point.guid for vkey in vkeys}) elif constraint == 'curve': curve = RhinoCurve.from_selection() constraints.update({vkey: curve.guid for vkey in vkeys}) elif constraint == 'surface': surface = RhinoSurface.from_selection() constraints.update({vkey: surface.guid for vkey in vkeys}) return constraints
def callback(k, args): mesh, constraints = args for vkey, constraint in constraints.items(): if constraint is None: continue elif rs.ObjectType(constraint) == 1: x, y, z = RhinoPoint.from_guid(constraint).xyz elif rs.ObjectType(constraint) == 4: x, y, z = RhinoCurve.from_guid(constraint).closest_point( mesh.vertex_coordinates(vkey)) elif rs.ObjectType(constraint) == 8: x, y, z = RhinoSurface.from_guid(constraint).closest_point( mesh.vertex_coordinates(vkey)) elif rs.ObjectType(constraint) == 32: x, y, z = RhinoMesh.from_guid(constraint).closest_point( mesh.vertex_coordinates(vkey)) else: continue mesh.vertex[vkey]['x'] = x mesh.vertex[vkey]['y'] = y mesh.vertex[vkey]['z'] = z
def RunCommand(is_interactive): scene = get_scene() if not scene: return proxy = get_proxy() if not proxy: return delaunay = proxy.function('compas.geometry.delaunay_from_points_numpy') # Get input data. surf_guid = compas_rhino.select_surface("Select a surface to decompose.") if not surf_guid: return point_guids = compas_rhino.select_points( "Select points to include in the decomposition.") curve_guids = [] compas_rhino.rs.HideObjects([surf_guid] + point_guids + curve_guids) surface = RhinoSurface.from_guid(surf_guid) curves = [RhinoCurve.from_guid(guid) for guid in curve_guids] points = [RhinoPoint.from_guid(guid) for guid in point_guids] # Compute the feature discretisation length. box = compas_rhino.rs.BoundingBox([surf_guid]) diagonal = compas_rhino.rs.Distance(box[0], box[6]) D = 0.05 * diagonal # Get the target length for the final quad mesh. L = compas_rhino.rs.GetReal( "Define the target edge length of the pattern.", 1.0) # Generate the pattern pattern = Pattern.from_surface_and_features(D, L, surf_guid, curve_guids, point_guids, delaunay=delaunay) scene.clear() scene.add(pattern, name='pattern') scene.update() kmax = 10 # Constrain mesh components to the feature geometry. constraints = automated_smoothing_surface_constraints(pattern, surface) constraints.update( automated_smoothing_constraints(pattern, rhinopoints=points, rhinocurves=curves)) while True: option = compas_rhino.rs.GetString("Smoothen the pattern?", "No", ["Yes", "No"]) if not option: break if option != "Yes": break constrained_smoothing(pattern, kmax=kmax, damping=0.5, constraints=constraints, algorithm="area") scene.update() print('Pattern object successfully created. Input object has been hidden.')
def from_surface_and_features(cls, discretisation, target_edge_length, surf_guid, curve_guids=[], point_guids=[], delaunay=None): """Get a pattern object from a NURBS surface with optional point and curve features on the surface. The pattern is aligned to the surface boundaries and curve features. The pattern contains a pole singularity at the feature points. Pole singularities are a specific type of singularity. Parameters ---------- discretisation : float The surface boundary and curve feature discretisation length. Values between 1% and 5% of the length of the diagonal of the bounding box are recommended. target_edge_length : float The edge target length for densification. surf_guid : str A Rhino surface guid. curves : list of str, optional A list of Rhino curve guids. points : list of str, optional A list of Rhino point guids. Returns ------- Pattern A Pattern object. References ---------- Based on [1]_ and [2]_. .. [1] Oval et al. *Feature-based topology finding of patterns for shell structures*. Automation in Construction, 2019. Available at: https://www.researchgate.net/publication/331064073_Feature-based_Topology_Finding_of_Patterns_for_Shell_Structures. .. [2] Oval. *Topology finding of patterns for structural design*. PhD thesis, Unversite Paris-Est, 2019. Available at: https://www.researchgate.net/publication/340096530_Topology_Finding_of_Patterns_for_Structural_Design. """ from compas_singular.rhino import RhinoSurface AddInterpCrvOnSrfUV = compas_rhino.rs.AddInterpCrvOnSrfUV compas_rhino.rs.EnableRedraw(False) surface = RhinoSurface.from_guid(surf_guid) result = surface.discrete_mapping(discretisation, crv_guids=curve_guids, pt_guids=point_guids) outer_boundary, inner_boundaries, polyline_features, point_features = result trimesh = boundary_triangulation(*result, delaunay=delaunay) decomposition = SkeletonDecomposition.from_mesh(trimesh) coarsemesh = decomposition.decomposition_mesh(point_features) gkey_vertex = { geometric_key(coarsemesh.vertex_coordinates(vertex)): vertex for vertex in coarsemesh.vertices() } edge_curve = {} for polyline in decomposition.polylines: a = geometric_key(polyline[0]) b = geometric_key(polyline[-1]) u = gkey_vertex[a] v = gkey_vertex[b] points = [point[:2] for point in polyline] curve = AddInterpCrvOnSrfUV(surf_guid, points) edge_curve[u, v] = curve coarsemesh.collect_strips() coarsemesh.set_strips_density_target(target_edge_length) coarsemesh.densification(edges_to_curves=edge_curve) compas_rhino.delete_objects(edge_curve.values(), purge=True) densemesh = coarsemesh.get_quad_mesh() compas_rhino.rs.EnableRedraw(True) compas_rhino.rs.Redraw() return cls.from_vertices_and_faces(*densemesh.to_vertices_and_faces())