def RunCommand(is_interactive): scene = get_scene() if not scene: return pattern = scene.get("pattern")[0] if not pattern: print("There is no Pattern in the scene.") return options = ["All", "Continuous", "Parallel", "Manual"] option = compas_rhino.rs.GetString("Selection Type", strings=options) if not option: return if option == "All": keys = keys = list(pattern.datastructure.edges()) elif option == "Continuous": temp = pattern.select_edges() keys = list(set(flatten([pattern.datastructure.edge_loop(key) for key in temp]))) elif option == "Parallel": temp = pattern.select_edges() keys = list(set(flatten([pattern.datastructure.edge_strip(key) for key in temp]))) elif option == "Manual": keys = pattern.select_edges() if keys: public = [name for name in pattern.datastructure.default_edge_attributes.keys() if not name.startswith('_')] if pattern.update_edges_attributes(keys, names=public): scene.update()
def __init__(self, matrix=None): if matrix: _, _, angles, _, _ = decompose_matrix(matrix) check = matrix_from_euler_angles(angles) if not allclose(flatten(matrix), flatten(check)): raise ValueError('This is not a proper rotation matrix.') super(Rotation, self).__init__(matrix=matrix)
def __init__(self, matrix=None): if matrix: _, _, _, _, perspective = decompose_matrix(matrix) check = matrix_from_perspective_entries(perspective) if not allclose(flatten(matrix), flatten(check)): raise ValueError('This is not a proper projection matrix.') super(Projection, self).__init__(matrix=matrix)
def __init__(self, matrix=None): if matrix: scale, _, _, _, _ = decompose_matrix(matrix) check = matrix_from_scale_factors(scale) if not allclose(flatten(matrix), flatten(check)): raise ValueError('This is not a proper scale matrix.') super(Scale, self).__init__(matrix=matrix)
def create_group_action(self, objs, transformations, times): # TODO: how to start multiple animations at once if len(transformations) != len(times): raise ValueError("Pass equal amount of transformations and times") x, y, z, w = objs[0].quaternion Tinit = Rotation.from_quaternion([w, x, y, z]) * Translation( objs[0].position) positions = [] quaternions = [] for M in transformations: Sc, Sh, R, T, P = (M * Tinit).decomposed() positions.append(list(T.translation)) quaternions.append(R.quaternion.xyzw) position_track = p3js.VectorKeyframeTrack(name='.position', times=times, values=list( flatten(positions))) rotation_track = p3js.QuaternionKeyframeTrack( name='.quaternion', times=times, values=list(flatten(quaternions))) animation_group = p3js.AnimationObjectGroup() animation_group.exec_three_obj_method('add', objs[0]) # this is not working obj_clip = p3js.AnimationClip(tracks=[position_track, rotation_track]) mixer = p3js.AnimationMixer(animation_group) obj_action = p3js.AnimationAction(mixer, obj_clip, animation_group) return obj_action
def __init__(self, matrix=None): if matrix: _, _, _, translation, _ = decompose_matrix(matrix) check = matrix_from_translation(translation) if not allclose(flatten(matrix), flatten(check)): raise ValueError('This is not a proper translation matrix.') super(Translation, self).__init__(matrix=matrix)
def RunCommand(is_interactive): scene = get_scene() if not scene: return pattern = scene.get("pattern")[0] if not pattern: print("There is no Pattern in the scene.") return options = ["ByContinuousEdges", "Manual"] option = compas_rhino.rs.GetString("Selection Type.", strings=options) if not option: return if option == "ByContinuousEdges": temp = pattern.select_edges() keys = list( set( flatten([ pattern.datastructure.vertices_on_edge_loop(key) for key in temp ]))) elif option == "Manual": keys = pattern.select_vertices() if keys: if pattern.move_vertices(keys): scene.update()
def RunCommand(is_interactive): scene = get_scene() if not scene: return force = scene.get("force")[0] if not force: print("There is no ForceDiagram in the scene.") return thrust = scene.get("thrust")[0] options = ["ByContinuousEdges", "Manual"] option = compas_rhino.rs.GetString("Selection Type.", strings=options) if not option: return if option == "ByContinuousEdges": temp = force.select_edges() keys = list(set(flatten([force.datastructure.vertices_on_edge_loop(key) for key in temp]))) elif option == "Manual": keys = force.select_vertices() if keys: if force.move_vertices(keys): if force.datastructure.primal: force.datastructure.update_angle_deviations() if thrust: thrust.settings['_is.valid'] = False scene.update()
def RunCommand(is_interactive): scene = get_scene() if not scene: return force = scene.get("force")[0] if not force: print("There is no ForceDiagram in the scene.") return thrust = scene.get("thrust")[0] options = ["All", "Continuous", "Parallel", "Manual"] option = compas_rhino.rs.GetString("Selection Type.", strings=options) if not option: return if option == "All": keys = list(force.datastructure.edges()) elif option == "Continuous": edges = force.select_edges() keys = list( set( flatten( [force.datastructure.edge_loop(edge) for edge in edges]))) elif option == "Parallel": temp = force.select_edges() keys = list( set(flatten([force.datastructure.edge_strip(key) for key in temp]))) elif option == "Manual": keys = force.select_edges() if keys: public = [ name for name in force.datastructure.default_edge_attributes.keys() if not name.startswith("_") ] if force.update_edges_attributes(keys, names=public): if thrust: thrust.settings['_is.valid'] = False scene.update()
def RunCommand(is_interactive): scene = get_scene() if not scene: return proxy = get_proxy() if not proxy: return form = scene.get("form")[0] if not form: print("There is no FormDiagram in the scene.") return thrust = scene.get("thrust")[0] anchors = list(form.datastructure.vertices_where({'is_anchor': True})) fixed = list(form.datastructure.vertices_where({'is_fixed': True})) fixed = anchors + fixed options = ['True', 'False'] option = compas_rhino.rs.GetString( "Press Enter to smooth or ESC to exit. Keep all boundaries fixed?", options[0], options) if option is None: print('Form smoothing aborted!') return if option == 'True': fixed += list(flatten(form.datastructure.vertices_on_boundaries())) fixed += list( flatten([ form.datastructure.face_vertices(face) for face in form.datastructure.faces_where({'_is_loaded': False}) ])) fixed = list(set(fixed)) form.datastructure.smooth_area(fixed=fixed) if thrust: thrust.settings['_is.valid'] = False scene.update()
def select_parallel_edges(diagram): rs.UnselectAllObjects() keys = DiagramHelper.select_edges(diagram) if not keys: return keys = [diagram.get_parallel_edges(key) for key in keys] keys = list(set(list(flatten(keys)))) DiagramHelper.highlight_edges(diagram, keys) return keys
def assembly_hull(assembly, keys=None, unify=True): """Construct the convex hull of an assembly. Parameters ---------- assembly : Assembly The assembly data structure. keys: list, optional The identifiers of the blocks to include in the hull calculation. Defaults to all blocks. unify : bool, optional Unify the face cycles of the hull. Default is ``True``. Returns ------- tuple The vertices and faces of the hull. Examples -------- .. code-block:: python import compas_assembly from compas.datastructures import Mesh from compas.viewers import MeshViewer from compas_assembly.datastructures import Assembly assembly = Assembly.from_json(compas_assembly.get('assembly.json')) vertices, faces = assembly_hull(assembly) hull = Mesh.from_vertices_and_faces(vertices, faces) viewer = MeshViewer() viewer.mesh = hull viewer.show() """ keys = keys or list(assembly.vertices()) points = [] for key in keys: block = assembly.blocks[key] points.extend(block.get_vertices_attributes('xyz')) faces = convex_hull(points) vertices = list(set(flatten(faces))) i_index = {i: index for index, i in enumerate(vertices)} vertices = [points[index] for index in vertices] faces = [[i_index[i] for i in face] for face in faces] if unify: faces = unify_cycles(vertices, faces) return vertices, faces
def select_continuous_edges(diagram): rs.UnselectAllObjects() keys = DiagramHelper.select_edges(diagram) if not keys: return keys = [diagram.get_continuous_edges(key) for key in keys] keys = list(set(list(flatten(keys)))) select_edges(diagram, keys) return keys
def __eq__(self, other): for a, b in zip(flatten(self.points), flatten(other.points)): if a != b: return False for a, b in zip(flatten(self.weights), flatten(other.weights)): if a != b: return False for a, b in zip(self.u_knots, self.v_knots): if a != b: return False for a, b in zip(self.u_mults, self.v_mults): if a != b: return False if self.u_degree != self.v_degree: return False if self.is_u_periodic != self.is_v_periodic: return False return True
def data(self, data): if isinstance(data, array.array): self._data = data else: self._data = array.array(self.dtype) if isinstance(data[0], (array.array, list, tuple)): data = list(flatten(data)) self._data.fromlist(list(data))
def float_array(data): try: len(data[0]) except TypeError: pass else: data = list(flatten(data)) array = (ctypes.c_double * len(data))() for index, value in enumerate(data): array[index] = float(value) return array
def RunCommand(is_interactive): scene = get_scene() if not scene: return force = scene.get("force")[0] if not force: print("There is no ForceDiagram in the scene.") return thrust = scene.get("thrust")[0] options = ["All", "ByContinuousEdges", "Manual"] option = compas_rhino.rs.GetString("Selection Type.", strings=options) if not option: return if option == "All": keys = list(force.datastructure.vertices()) elif option == "ByContinuousEdges": temp = force.select_edges() keys = list( set( flatten([ force.datastructure.vertices_on_edge_loop(key) for key in temp ]))) elif option == "Manual": keys = force.select_vertices() if keys: # current = scene.settings['RV2']['show.angles'] # scene.settings['RV2']['show.angles'] = False # scene.update() # ModifyAttributesForm.from_sceneNode(force, 'vertices', keys) # scene.settings['RV2']['show.angles'] = current # if thrust: # thrust.settings['_is.valid'] = False # scene.update() public = [ name for name in force.datastructure.default_vertex_attributes.keys() if not name.startswith('_') ] if force.update_vertices_attributes(keys, names=public): if thrust: thrust.settings['_is.valid'] = False scene.update()
def float_array(data): try: len(data[0]) except TypeError: pass else: data = list(flatten(data)) n = len(data) array = shapeopPython.doubleArray(n) for index, value in enumerate(data): array[index] = float(value) return array
def get_convex_hull_mesh(points): faces = convex_hull(points) vertices = list(set(flatten(faces))) i_index = {i: index for index, i in enumerate(vertices)} vertices = [points[index] for index in vertices] faces = [[i_index[i] for i in face] for face in faces] faces = unify_cycles(vertices, faces) mesh = Mesh.from_vertices_and_faces(vertices, faces) return mesh
def create_action(self, obj, transformations, times): if len(transformations) != len(times): raise ValueError("Pass equal amount of transformations and times") x, y, z, w = obj.quaternion Tinit = Rotation.from_quaternion([w, x, y, z]) * Translation( obj.position) positions = [] quaternions = [] for M in transformations: Sc, Sh, R, T, P = (M * Tinit).decomposed() positions.append(list(T.translation)) quaternions.append(R.quaternion.xyzw) position_track = p3js.VectorKeyframeTrack(name='.position', times=times, values=list( flatten(positions))) rotation_track = p3js.QuaternionKeyframeTrack( name='.quaternion', times=times, values=list(flatten(quaternions))) obj_clip = p3js.AnimationClip(tracks=[position_track, rotation_track]) obj_action = p3js.AnimationAction(p3js.AnimationMixer(obj), obj_clip, obj) return obj_action
def RunCommand(is_interactive): scene = get_scene() if not scene: return form = scene.get("form")[0] if not form: print("There is no FormDiagram in the scene.") return thrust = scene.get("thrust")[0] # show the form vertices form_vertices = "{}::vertices".format(form.settings['layer']) compas_rhino.rs.ShowGroup(form_vertices) if thrust: # hide the thrust vertices thrust_vertices_free = "{}::vertices_free".format(thrust.settings['layer']) thrust_vertices_anchor = "{}::vertices_anchor".format(thrust.settings['layer']) compas_rhino.rs.HideGroup(thrust_vertices_free) compas_rhino.rs.HideGroup(thrust_vertices_anchor) compas_rhino.rs.Redraw() # selection options options = ["ByContinuousEdges", "Manual"] option = compas_rhino.rs.GetString("Selection Type.", strings=options) if not option: scene.update() return if option == "ByContinuousEdges": temp = form.select_edges() keys = list(set(flatten([form.datastructure.vertices_on_edge_loop(key) for key in temp]))) elif option == "Manual": keys = form.select_vertices() if keys: if form.move_vertices(keys): if form.datastructure.dual: form.datastructure.dual.update_angle_deviations() if thrust: thrust.settings['_is.valid'] = False # the scene needs to be updated # even if the vertices where not modified # to reset group visibility to the configuration of settings scene.update()
def RunCommand(is_interactive): if 'EG' not in sc.sticky: compas_rhino.display_message("EscobedoGroup UI no available.") return mesh = sc.sticky["EG"].get('mesh') if not mesh: compas_rhino.display_message("No mesh is loaded.") return obj = MeshObject(mesh, layer="HiLo::Mesh") obj.clear_layer() obj.draw() compas_rhino.rs.EnableRedraw(True) compas_rhino.rs.Redraw() edges = obj.select_edges() if not edges: return loops = [] for edge in edges: loop = mesh.edge_loop(edge) loops.append(loop) obj.settings['color.edges'] = {} obj.settings['color.edges'].update({(u, v): (255, 0, 0) for u, v in flatten(loops)}) obj.settings['color.edges'].update({(v, u): (255, 0, 0) for u, v in flatten(loops)}) obj.clear() obj.draw() compas_rhino.rs.EnableRedraw(True) compas_rhino.rs.Redraw()
def RunCommand(is_interactive): scene = get_scene() if not scene: return pattern = scene.get("pattern")[0] if not pattern: print("There is no Pattern in the scene.") return options = ["AllBoundaryVertices", "Corners", "ByContinuousEdges", "Manual"] while True: option = compas_rhino.rs.GetString("Selection mode:", strings=options) if not option: return if option == "AllBoundaryVertices": keys = pattern.datastructure.vertices_on_boundary() elif option == "Corners": angle = compas_rhino.rs.GetInteger( 'Angle tolerance for non-quad face corners:', 170, 1, 180) keys = pattern.datastructure.corner_vertices(tol=angle) elif option == "ByContinuousEdges": temp = pattern.select_edges() keys = list( set( flatten([ pattern.datastructure.vertices_on_edge_loop(key) for key in temp ]))) elif option == "Manual": keys = pattern.select_vertices() if keys: public = [ name for name in pattern.datastructure.default_vertex_attributes.keys() if not name.startswith('_') ] if pattern.update_vertices_attributes(keys, names=public): scene.update()
def assembly_hull(assembly, keys=None, unify=True): """Construct the convex hull of an assembly. Parameters ---------- assembly : Assembly The assembly data structure. keys: list, optional The identifiers of the blocks to include in the hull calculation. Defaults to all blocks. unify : bool, optional Unify the face cycles of the hull. Default is ``True``. Returns ------- tuple The vertices and faces of the hull. Examples -------- >>> """ keys = keys or list(assembly.nodes()) points = [] for key in keys: block = assembly.blocks[key] points.extend(block.vertices_attributes('xyz')) faces = convex_hull(points) vertices = list(set(flatten(faces))) i_index = {i: index for index, i in enumerate(vertices)} vertices = [points[index] for index in vertices] faces = [[i_index[i] for i in face] for face in faces] if unify: faces = unify_cycles(vertices, faces) return vertices, faces
def RunCommand(is_interactive): scene = get_scene() if not scene: return proxy = get_proxy() if not proxy: return pattern = scene.get("pattern")[0] if not pattern: print("There is no Pattern in the scene.") return fixed = list(pattern.datastructure.vertices_where({'is_fixed': True})) if not fixed: print( "Pattern has no fixed vertices! Smoothing requires fixed vertices." ) return options = ['True', 'False'] option = compas_rhino.rs.GetString( "Press Enter to smooth or ESC to exit. Keep all boundaries fixed?", options[0], options) if option is None: print('Pattern smoothing aborted!') return if option == 'True': fixed = fixed + list( flatten(pattern.datastructure.vertices_on_boundaries())) pattern.datastructure.smooth_area(fixed=fixed) scene.update()
def RunCommand(is_interactive): scene = get_scene() if not scene: return form = scene.get("form")[0] if not form: print("There is no FormDiagram in the scene.") return thrust = scene.get("thrust")[0] # show the form vertices form_vertices = "{}::vertices".format(form.settings['layer']) compas_rhino.rs.ShowGroup(form_vertices) if thrust: # hide the thrust vertices thrust_vertices_free = "{}::vertices_free".format( thrust.settings['layer']) thrust_vertices_anchor = "{}::vertices_anchor".format( thrust.settings['layer']) compas_rhino.rs.HideGroup(thrust_vertices_free) compas_rhino.rs.HideGroup(thrust_vertices_anchor) compas_rhino.rs.Redraw() # selection options options = ["ByContinuousEdges", "Manual"] option = compas_rhino.rs.GetString("Selection Type.", strings=options) if not option: scene.update() return if option == "ByContinuousEdges": temp = form.select_edges() keys = list( set( flatten([ form.datastructure.vertices_on_edge_loop(key) for key in temp ]))) # elif option == "ByConstraints": # guids = form.datastructure.vertices_attribute('constraints') # guids = list(set(list(flatten(list(filter(None, guids)))))) # if not guids: # print('there are no constraints in this form') # return # current = form.settings['color.edges'] # form.settings['color.edges'] = [120, 120, 120] # scene.update() # compas_rhino.rs.ShowObjects(guids) # def custom_filter(rhino_object, geometry, component_index): # if str(rhino_object.Id) in guids: # return True # return False # constraints = compas_rhino.rs.GetObjects('select constraints', custom_filter=custom_filter) # if not constraints: # return # keys = [] # for guid in constraints: # for key, attr in form.datastructure.vertices(data=True): # if attr['constraints']: # if str(guid) in attr['constraints']: # keys.append(key) # keys = list(set(keys)) # compas_rhino.rs.HideObjects(guids) # form.settings['color.edges'] = current elif option == "Manual": keys = form.select_vertices() if keys: if form.move_vertices_horizontal(keys): if form.datastructure.dual: form.datastructure.dual.update_angle_deviations() if thrust: thrust.settings['_is.valid'] = False # the scene needs to be updated # even if the vertices where not modified # to reset group visibility to the configuration of settings scene.update()
def RunCommand(is_interactive): scene = get_scene() if not scene: return form = scene.get("form")[0] if not form: print("There is no FormDiagram in the scene.") return thrust = scene.get("thrust")[0] options = ["All", "Continuous", "Parallel", "Manual"] option = compas_rhino.rs.GetString("Selection Type.", strings=options) if not option: return if option == "All": keys = list(form.datastructure.edges()) elif option == "Continuous": temp = form.select_edges() keys = list(set(flatten([form.datastructure.edge_loop(key) for key in temp]))) elif option == "Parallel": temp = form.select_edges() keys = list(set(flatten([form.datastructure.edge_strip(key) for key in temp]))) # elif option == "ByConstraints": # guids = form.datastructure.vertices_attribute('constraints') # guids = list(set(list(flatten(list(filter(None, guids)))))) # if not guids: # print('there are no constraints in this form') # return # current = form.settings['color.edges'] # form.settings['color.edges'] = [120, 120, 120] # scene.update() # compas_rhino.rs.ShowObjects(guids) # def custom_filter(rhino_object, geometry, component_index): # if str(rhino_object.Id) in guids: # return True # return False # constraints = compas_rhino.rs.GetObjects('select constraints', custom_filter=custom_filter) # if not constraints: # return # def if_constraints(datastructure, key, guid): # constraints = datastructure.vertex_attribute(key, 'constraints') # if constraints: # if str(guid) in constraints: # return True # return False # keys = [] # for guid in constraints: # for (u, v) in form.datastructure.edges(): # if if_constraints(form.datastructure, u, guid) and if_constraints(form.datastructure, v, guid): # keys.append((u, v)) # compas_rhino.rs.HideObjects(guids) # form.settings['color.edges'] = current elif option == "Manual": keys = form.select_edges() if keys: # current = scene.settings['RV2']['show.angles'] # scene.settings['RV2']['show.angles'] = False # scene.update() # ModifyAttributesForm.from_sceneNode(form, 'edges', keys) # scene.settings['RV2']['show.angles'] = current # if thrust: # thrust.settings['_is.valid'] = False # scene.update() public = [name for name in form.datastructure.default_edge_attributes.keys() if not name.startswith('_')] if form.update_edges_attributes(keys, names=public): if thrust: thrust.settings['_is.valid'] = False scene.update()
# ============================================================================== # Continuous edges and parallel edges # ============================================================================== continuous = cablenet.get_continuous_edges(start) parallel = cablenet.get_parallel_edges(start, aligned=True) faces = cablenet.get_face_strip(start) cables = [] for edge in parallel: edges = cablenet.get_continuous_edges(edge) cables.append(edges) chained = cablenet.get_continuous_edges(start, aligned=True) chain = list(flatten(chained[::2] + chained[-1:])) # ============================================================================== # Visualize # ============================================================================== vertexcolor = { key: i_to_red(index / len(chain)) for index, key in enumerate(chain) } arrows = [{ 'start': cablenet.vertex_coordinates(start[0]), 'end': cablenet.vertex_coordinates(start[1]), 'color': (1.0, 0.0, 0.0) }]
P = np.array([mesh.vertex_coordinates(v) for v in mesh.vertices()]) max_x = np.amax(P[:, 0]) min_x = np.amin(P[:, 0]) max_y = np.amax(P[:, 1]) min_y = np.amin(P[:, 1]) P = np.zeros((num_x, num_y, 3)) x_space = np.linspace(min_x, max_x, num_x).reshape((-1, 1)) y_space = np.linspace(min_y, max_y, num_y).reshape((1, -1)) P[:, :, 0] = x_space P[:, :, 1] = y_space seeds = list(flatten(P.tolist())) # ========================================================================== # Other seeds # ========================================================================== seeds = [choice(str_mesh.face_centroids) for i in range(10)] seeds = str_mesh.boundary_polygon[:] # ========================================================================== # Trace streamlines # ========================================================================== for vf_name in VF_NAMES: print(f"Integrating vector field: {vf_name}")
def RunCommand(is_interactive): scene = get_scene() if not scene: return form = scene.get("form")[0] if not form: print("There is no FormDiagram in the scene.") return thrust = scene.get("thrust")[0] # show the form vertices form_vertices = "{}::vertices".format(form.settings['layer']) compas_rhino.rs.ShowGroup(form_vertices) if thrust: # hide the thrust vertices thrust_vertices_free = "{}::vertices_free".format( thrust.settings['layer']) thrust_vertices_anchor = "{}::vertices_anchor".format( thrust.settings['layer']) compas_rhino.rs.HideGroup(thrust_vertices_free) compas_rhino.rs.HideGroup(thrust_vertices_anchor) compas_rhino.rs.Redraw() # selection options options = ["All", "AllBoundaries", "ByContinuousEdges", "Manual"] option = compas_rhino.rs.GetString("Selection Type.", strings=options) if not option: scene.update() return if option == "All": keys = list(form.datastructure.vertices()) elif option == "AllBoundaries": keys = list( set( flatten([ form.datastructure.face_vertices(face) for face in form.datastructure.faces() if form.datastructure.is_face_on_boundary(face) ]))) elif option == "ByContinuousEdges": temp = form.select_edges() keys = list( set( flatten([ form.datastructure.vertices_on_edge_loop(key) for key in temp ]))) # elif option == "ByConstraints": # guids = form.datastructure.vertices_attribute('constraints') # guids = list(set(list(flatten(list(filter(None, guids)))))) # if not guids: # print('there are no constraints in this form') # return # current = form.settings['color.edges'] # form.settings['color.edges'] = [120, 120, 120] # scene.update() # compas_rhino.rs.ShowObjects(guids) # def custom_filter(rhino_object, geometry, component_index): # if str(rhino_object.Id) in guids: # return True # return False # constraints = compas_rhino.rs.GetObjects('select constraints', custom_filter=custom_filter) # if not constraints: # return # keys = [] # for guid in constraints: # for key, attr in form.datastructure.vertices(data=True): # if attr['constraints']: # if str(guid) in attr['constraints']: # keys.append(key) # keys = list(set(keys)) # compas_rhino.rs.HideObjects(guids) # form.settings['color.edges'] = current elif option == "Manual": keys = form.select_vertices() if keys: # current = scene.settings['RV2']['show.angles'] # scene.settings['RV2']['show.angles'] = False # scene.update() # ModifyAttributesForm.from_sceneNode(form, 'vertices', keys) # scene.settings['RV2']['show.angles'] = current # if thrust: # thrust.settings['_is.valid'] = False # scene.update() public = [ name for name in form.datastructure.default_vertex_attributes.keys() if not name.startswith('_') ] if form.update_vertices_attributes(keys, names=public): if thrust: thrust.settings['_is.valid'] = False scene.update()