def create_line_geom( coord_vals: SoCoordValsListType, indices: SoIndicesListType, line_color: SoVectorType, translation: SoVectorType = None, quaternion: SoQuaternionType = None ) -> ThreeJSSceneGraphObjectListType: """ Return a pythreejs Line object consisting of lines defined by the line_indices and the coord_vals. Additionally the attributes `Line.default_color` and `Line.edge_index` will be set before returning the `Line` array. Those attributes contain references to the default colors, that are set in this function to restore later changes, and the FreeCAD edge index. """ lines = [] for i, line_indices in enumerate(indices): vertices = generate_line_vertices(line_indices, coord_vals) vertices = np.array(vertices, dtype="float32") line_geom = BufferGeometry(attributes=dict( position=BufferAttribute(vertices, normalized=False))) # BUG: This is a bug in pythreejs and currently does not work #linesgeom.exec_three_obj_method('computeVertexNormals') col = so_col_to_hex(line_color) material = LineBasicMaterial(linewidth=LINE_WIDTH, color=col) material.default_color = col line = Line(geometry=line_geom, material=material) line.edge_index = i + 1 # using FreeCAD's 1-based indexing if translation: line.position = translation if quaternion: line.quaternion = quaternion lines.append(line) return lines
def pseudomaterial_render(atoms): c = cm.get_cmap("plasma") scale_axis_vertices = [[-1, -1, -1], [9, -1, -1]] scale_line_geom = Geometry(vertices=scale_axis_vertices, colors=['black'] * len(scale_axis_vertices)) scale_lines = Line( geometry=scale_line_geom, material=LineBasicMaterial(linewidth=50, vertexColors='VertexColors'), type='LinePieces', ) a = atoms.a[1] cube_vertices = [[0, 0, 0], [a, 0, 0], [a, 0, a], [a, 0, 0], [a, a, 0], [a, a, a], [a, a, 0], [0, a, 0], [0, a, a], [0, a, 0], [0, 0, 0], [0, 0, a], [a, 0, a], [a, a, a], [0, a, a], [0, 0, a]] linesgeom = Geometry(vertices=cube_vertices, colors=['black'] * len(cube_vertices)) lines = Line( geometry=linesgeom, material=LineBasicMaterial(linewidth=5, vertexColors='VertexColors'), type='LinePieces', ) balls = [] for p in atoms.itertuples(): positions = (p.x * p.a, p.y * p.a, p.z * p.a) new_ball = Mesh( geometry=SphereGeometry(radius=p.sigma, widthSegments=32, heightSegments=24), material=MeshLambertMaterial(color=rgb2hex(c(p.epsilon_norm))), position=positions) balls.append(new_ball) # [scale*2*a,scale*2*a,scale*2*a] camera = PerspectiveCamera(position=[25, a, a], up=[0, 1, 0], children=[ DirectionalLight(color='white', position=[3, 5, 1], intensity=0.5) ]) scene = Scene(children=[ scale_lines, lines, *balls, camera, AmbientLight(color='#777') ]) renderer = Renderer( camera=camera, scene=scene, controls=[OrbitControls(controlling=camera, target=[a, a, a])]) return renderer
def edges_to_mesh(name, np_edge_vertices, np_edge_indices, edge_color, linewidth=1): """ :param name: :param np_edge_vertices: :param np_edge_indices: :param edge_color: :param linewidth: :return: :rtype: pythreejs.objects.LineSegments_autogen.LineSegments """ edge_geometry = BufferGeometry( attributes={ "position": BufferAttribute(np_edge_vertices), "index": BufferAttribute(np_edge_indices), }) edge_material = LineBasicMaterial(color=format_color(*edge_color), linewidth=linewidth) edge_geom = LineSegments( geometry=edge_geometry, material=edge_material, type="LinePieces", name=name, ) return edge_geom
def AddCurveToScene(self, shp, edge_color, deflection): """ shp is either a TopoDS_Wire or a TopodS_Edge. """ if is_edge(shp): pnts = discretize_edge(shp, deflection) elif is_wire(shp): pnts = discretize_wire(shp, deflection) np_edge_vertices = np.array(pnts, dtype=np.float32) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) edge_geometry = BufferGeometry( attributes={ 'position': BufferAttribute(np_edge_vertices), 'index': BufferAttribute(np_edge_indices) }) edge_material = LineBasicMaterial(color=edge_color, linewidth=1) # and to the dict of shapes, to have a mapping between meshes and shapes edge_id = "%s" % uuid.uuid4().hex self._shapes[edge_id] = shp edge_line = Line(geometry=edge_geometry, material=edge_material, name=edge_id) # and to the dict of shapes, to have a mapping between meshes and shapes edge_id = "%s" % uuid.uuid4().hex self._shapes[edge_id] = shp return edge_line
def get_line_geometries(geom: ThreeJSSceneGraphObjectType) -> LineSegments: """ Return line segments that represent the edges of the given objects mesh. """ line_geom = EdgesGeometry(geom.geometry) lines = LineSegments(geometry=line_geom, material=LineBasicMaterial(linewidth=LINE_WIDTH, color='#000000')) return lines
def __init__(self, start, end, color='white', linewidth=1): geometry = BufferGeometry( attributes={ 'position': BufferAttribute(np.vstack((start, end)).astype(np.float32), normalized=False) }) material = LineBasicMaterial(color=color, linewidth=linewidth) pythreejs.Line.__init__(self, geometry=geometry, material=material)
def __init__(self, num_cells=5, color='#cccccc', linewidth=1, cellsize=0.5): Group.__init__(self) material = LineBasicMaterial(color=color, linewidth=linewidth) for i in range(num_cells + 1): edge = cellsize * num_cells / 2 position = edge - (i * cellsize) geometry_h = Geometry(vertices=[(-edge, position, 0), (edge, position, 0)]) geometry_v = Geometry(vertices=[(position, -edge, 0), (position, edge, 0)]) self.add(pythreejs.Line(geometry=geometry_h, material=material)) self.add(pythreejs.Line(geometry=geometry_v, material=material))
def AddCurveToScene(self, shp, color): """ shp is either a TopoDS_Wire or a TopodS_Edge. """ if is_edge(shp): pnts = discretize_edge(shp) elif is_wire(shp): pnts = discretize_wire(shp) np_edge_vertices = np.array(pnts, dtype=np.float32) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) edge_geometry = BufferGeometry(attributes={ 'position': BufferAttribute(np_edge_vertices), 'index' : BufferAttribute(np_edge_indices) }) edge_material = LineBasicMaterial(color=color, linewidth=1) edge_lines = Line(geometry=edge_geometry, material=edge_material) # Add geometries to pickable or non pickable objects self._displayed_pickable_objects.add(edge_lines)
def _get_surface_from_positions(positions, d_args, draw_edges=False): # get defaults obj_args = update_object_args(d_args, "Surfaces", ["color", "opacity"]) num_triangle = len(positions) / 3.0 assert num_triangle.is_integer() # make decision on transparency if obj_args["opacity"] > 0.99: transparent = False else: transparent = True num_triangle = int(num_triangle) index_list = [[itr * 3, itr * 3 + 1, itr * 3 + 2] for itr in range(num_triangle)] # Vertex ositions as a list of lists surf_vertices = BufferAttribute(array=positions, normalized=False) # Indices surf_indices = BufferAttribute(array=np.array(index_list, dtype=np.uint16).ravel(), normalized=False) geometry = BufferGeometry(attributes={ "position": surf_vertices, "index": surf_indices }) new_surface = Mesh( geometry=geometry, material=MeshLambertMaterial( color=obj_args["color"], side="DoubleSide", transparent=transparent, opacity=obj_args["opacity"], ), ) if draw_edges == True: edges = EdgesGeometry(geometry) edges_lines = LineSegments(edges, LineBasicMaterial(color=obj_args["color"])) return new_surface, edges_lines else: return new_surface, None
def DisplayMesh(self, part: "Part", edge_color=None, vertex_color=None, vertex_width=2): from OCC.Core.BRep import BRep_Builder from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeVertex from OCC.Core.gp import gp_Pnt from OCC.Core.TopoDS import TopoDS_Compound # edge_color = format_color(*part.colour) if edge_color is None else edge_color rgb = randint(0, 255), randint(0, 255), randint(0, 255) edge_color = format_color(*rgb) if edge_color is None else edge_color vertex_color = self._default_vertex_color if vertex_color is None else vertex_color pmesh_id = "%s" % uuid.uuid4().hex BB = BRep_Builder() compound = TopoDS_Compound() BB.MakeCompound(compound) vertices_list = [] def togp(n_): return gp_Pnt(float(n_[0]), float(n_[1]), float(n_[2])) for vertex in map(togp, part.fem.nodes): vertex_to_add = BRepBuilderAPI_MakeVertex(vertex).Shape() BB.Add(compound, vertex_to_add) vertices_list.append([vertex.X(), vertex.Y(), vertex.Z()]) attributes = { "position": BufferAttribute(vertices_list, normalized=False) } mat = PointsMaterial(color=vertex_color, sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points_geom = Points(geometry=geom, material=mat, name=pmesh_id) lmesh_id = "%s" % uuid.uuid4().hex edges_nodes = list( chain.from_iterable( filter( None, [get_vertices_from_elem(el) for el in part.fem.elements]))) np_edge_vertices = np.array(edges_nodes, dtype=np.float32) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) vertex_col = tuple([x / 255 for x in rgb]) edge_geometry = BufferGeometry( attributes={ "position": BufferAttribute(np_edge_vertices), "index": BufferAttribute(np_edge_indices), "color": BufferAttribute( [vertex_col for n in np_edge_vertices]), }) edge_material = LineBasicMaterial(vertexColors="VertexColors", linewidth=5) edge_geom = LineSegments( geometry=edge_geometry, material=edge_material, type="LinePieces", name=lmesh_id, ) output = [points_geom, edge_geom] for elem in output: self._shapes[elem.name] = compound self._refs[elem.name] = part self._displayed_pickable_objects.add(elem) self._fem_sets_opts.options = ["None"] + [ f"{part.fem.name}.{s.name}" for s in filter( lambda x: "internal" not in x.metadata.keys(), part.fem.sets) ] self._fem_refs[part.fem.name] = (part.fem, edge_geometry)
def AddShapeToScene(self, shp, # the TopoDS_Shape to be displayed shape_color=default_shape_color, # the default render_edges=False, edge_color=default_edge_color, compute_uv_coords=False, quality=1.0, transparency=False, opacity=1.): # first, compute the tesselation tess = Tesselator(shp) tess.Compute(uv_coords=compute_uv_coords, compute_edges=render_edges, mesh_quality=quality, parallel=self._parallel) # get vertices and normals vertices_position = tess.GetVerticesPositionAsTuple() number_of_triangles = tess.ObjGetTriangleCount() number_of_vertices = len(vertices_position) # number of vertices should be a multiple of 3 if number_of_vertices % 3 != 0: raise AssertionError("Wrong number of vertices") if number_of_triangles * 9 != number_of_vertices: raise AssertionError("Wrong number of triangles") # then we build the vertex and faces collections as numpy ndarrays np_vertices = np.array(vertices_position, dtype='float32').reshape(int(number_of_vertices / 3), 3) # Note: np_faces is just [0, 1, 2, 3, 4, 5, ...], thus arange is used np_faces = np.arange(np_vertices.shape[0], dtype='uint32') # set geometry properties buffer_geometry_properties = {'position': BufferAttribute(np_vertices), 'index' : BufferAttribute(np_faces)} if self._compute_normals_mode == NORMAL.SERVER_SIDE: # get the normal list, converts to a numpy ndarray. This should not raise # any issue, since normals have been computed by the server, and are available # as a list of floats np_normals = np.array(tess.GetNormalsAsTuple(), dtype='float32').reshape(-1, 3) # quick check if np_normals.shape != np_vertices.shape: raise AssertionError("Wrong number of normals/shapes") buffer_geometry_properties['normal'] = BufferAttribute(np_normals) # build a BufferGeometry instance shape_geometry = BufferGeometry(attributes=buffer_geometry_properties) # if the client has to render normals, add the related js instructions if self._compute_normals_mode == NORMAL.CLIENT_SIDE: shape_geometry.exec_three_obj_method('computeVertexNormals') # then a default material shp_material = self._material(shape_color, transparent=transparency, opacity=opacity) # create a mesh unique id mesh_id = uuid.uuid4().hex # finally create the mash shape_mesh = Mesh(geometry=shape_geometry, material=shp_material, name=mesh_id) # and to the dict of shapes, to have a mapping between meshes and shapes self._shapes[mesh_id] = shp # edge rendering, if set to True edge_lines = None if render_edges: edges = list(map(lambda i_edge: [tess.GetEdgeVertex(i_edge, i_vert) for i_vert in range(tess.ObjEdgeGetVertexCount(i_edge))], range(tess.ObjGetEdgeCount()))) edges = list(filter(lambda edge: len(edge) == 2, edges)) np_edge_vertices = np.array(edges, dtype=np.float32).reshape(-1, 3) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) edge_geometry = BufferGeometry(attributes={ 'position': BufferAttribute(np_edge_vertices), 'index' : BufferAttribute(np_edge_indices) }) edge_material = LineBasicMaterial(color=edge_color, linewidth=1) edge_lines = LineSegments(geometry=edge_geometry, material=edge_material) # Add geometries to pickable or non pickable objects self._displayed_pickable_objects.add(shape_mesh) if render_edges: self._displayed_non_pickable_objects.add(edge_lines)
def visualize(self): """Start the visualization and initialize widgets""" from pythreejs import PlainGeometry, Mesh, LambertMaterial, \ PhongMaterial, DirectionalLight, \ PerspectiveCamera, Scene, AmbientLight, \ Renderer, OrbitControls, Line, \ LineBasicMaterial, BoxGeometry, make_text from matplotlib import colors from matplotlib import cm from IPython.display import display import numpy as np import ipywidgets as widgets bbox = self._compute_bounding_box() diam = bbox[1, :] - bbox[0, :] center = .5 * _np.sum(bbox, axis=0) xmin, ymin, zmin = bbox[0, :] xmax, ymax, zmax = bbox[1, :] position = (center + 2.5 * diam).tolist() # Generate coordinate system base box = PlainGeometry( vertices=_np.array([[xmin, ymin, zmin], [xmax, ymin, zmin], [xmin, ymin, zmin], [xmin, ymax, zmin], [xmin, ymin, zmin], [xmin, ymin, zmax]]), colors=['red', 'red', 'green', 'green', 'blue', 'blue']) self._coord_box = Line(geometry=box, material=LineBasicMaterial( linewidth=10, vertexColors='VertexColors'), type='LinePieces') if self.data is not None: vis_data = self.data if not self.is_real: vis_data = _np.real(self.data) if not self.is_scalar: vis_data = _np.sum(_np.abs(self.data)**2, axis=2) self._vis_data = vis_data self._cmap = cm.jet self._vmin = vis_data.min() self._vmax = vis_data.max() cnorm = colors.Normalize(vmin=self._vmin, vmax=self._vmax) facecolors = self._convert_data_to_colors(self._cmap, cnorm, vis_data) else: facecolors = self.elements.shape[0] * [ 3 * [_np.array([1., 1., 1.])] ] self._geom = PlainGeometry(vertices=self.vertices, faces=self.elements, faceColors=facecolors) self._mesh = Mesh( geometry=self._geom, material=LambertMaterial(vertexColors='VertexColors')) self._wireframe = Mesh(geometry=self._geom, material=PhongMaterial(wireframe=True, color='black')) light = DirectionalLight(color='white', position=position, intensity=0.5) camera = PerspectiveCamera(position=position, fov=20) self._scene = Scene(children=[ self._coord_box, self._mesh, self._wireframe, AmbientLight(color='white') ]) self._renderer = Renderer(camera=camera, background='white', background_opacity=1, scene=self._scene, controls=[OrbitControls(controlling=camera)]) self._axes_info = widgets.Label(value="x: red; y: green; z: blue") coord_system_toggle = widgets.Checkbox( value=self._coord_box.visible, description='Show coordinate axes', disabled=False) coord_system_toggle.observe(self.on_toggle_coord_system, names='value') if self.data is not None: # Enable/Disable wireframe wireframe_toggle = widgets.Checkbox(value=self._wireframe.visible, description='Enable wireframe', disabled=False) wireframe_toggle.observe(self.on_toggle_wireframe, names='value') # Change vmin/vmax vmin_box = widgets.FloatText(value=self._vmin, description='vmin:', disabled=False) vmin_box.observe(self.on_change_vmin, names='value') vmax_box = widgets.FloatText(value=self._vmax, description='vmax:', disabled=False) vmax_box.observe(self.on_change_vmax, names='value') vmin_info = widgets.Label( value='Lower bound: {0}'.format(self._vmin)) vmax_info = widgets.Label( value='Upper bound: {0}'.format(self._vmax)) range_info_box = widgets.VBox([vmin_info, vmax_info]) range_change = widgets.HBox([vmin_box, vmax_box]) toggles = widgets.HBox([wireframe_toggle, coord_system_toggle]) vbox = widgets.VBox( [self._axes_info, range_info_box, range_change, toggles]) display(self._renderer, vbox) else: display(self._renderer, self._axes_info, coord_system_toggle)
def DisplayMesh(self, part, edge_color=None, vertex_color=None, vertex_width=2): """ :param part: :param edge_color: :param vertex_color: :param vertex_width: :type part: ada.Part """ from itertools import chain from random import randint from OCC.Core.BRep import BRep_Builder from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeVertex from OCC.Core.gp import gp_Pnt from OCC.Core.TopoDS import TopoDS_Compound # edge_color = format_color(*part.colour) if edge_color is None else edge_color edge_color = (format_color(randint(0, 255), randint( 0, 255), randint(0, 255)) if edge_color is None else edge_color) vertex_color = self._default_vertex_color if vertex_color is None else vertex_color pmesh_id = "%s" % uuid.uuid4().hex BB = BRep_Builder() compound = TopoDS_Compound() BB.MakeCompound(compound) vertices_list = [] def togp(n_): return gp_Pnt(float(n_[0]), float(n_[1]), float(n_[2])) for vertex in map(togp, part.fem.nodes): vertex_to_add = BRepBuilderAPI_MakeVertex(vertex).Shape() BB.Add(compound, vertex_to_add) vertices_list.append([vertex.X(), vertex.Y(), vertex.Z()]) attributes = { "position": BufferAttribute(vertices_list, normalized=False) } mat = PointsMaterial(color=vertex_color, sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points_geom = Points(geometry=geom, material=mat, name=pmesh_id) def grab_nodes(el): """ :param el: :type el: ada.fem.Elem :return: """ if el.edges_seq is None: return None return [ part.fem.nodes.from_id(i).p for i in [el.nodes[e].id for ed_seq in el.edges_seq for e in ed_seq] ] lmesh_id = "%s" % uuid.uuid4().hex edges_nodes = list( chain.from_iterable( filter(None, map(grab_nodes, part.fem.elements)))) np_edge_vertices = np.array(edges_nodes, dtype=np.float32) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) edge_geometry = BufferGeometry( attributes={ "position": BufferAttribute(np_edge_vertices), "index": BufferAttribute(np_edge_indices), }) edge_material = LineBasicMaterial(color=edge_color, linewidth=1) edge_geom = LineSegments( geometry=edge_geometry, material=edge_material, type="LinePieces", name=lmesh_id, ) output = [points_geom, edge_geom] for elem in output: self._shapes[elem.name] = compound self._refs[elem.name] = part self._displayed_pickable_objects.add(elem) self._fem_sets_opts.options = ["None"] + [ s.name for s in filter( lambda x: "internal" not in x.metadata.keys(), part.fem.sets) ]