def _renderer_default(self): center = tuple(self.ds.domain_center.in_units("code_length").d) right = tuple((self.ds.domain_right_edge + (self.ds.domain_right_edge - self.ds.domain_center) * 2.0).in_units("code_length").d) camera = pythreejs.PerspectiveCamera( position=right, fov=20, children=[pythreejs.AmbientLight()]) scene = pythreejs.Scene( children=[camera, pythreejs.AmbientLight(color="#dddddd")] + self.grid_views) orbit_control = pythreejs.OrbitControls(controlling=camera) renderer = pythreejs.Renderer( scene=scene, camera=camera, controls=[orbit_control], width=400, height=400, background="black", background_opacity=1, antialias=True, ) camera.lookAt(center) orbit_control.target = center renderer.layout.border = "1px solid darkgrey" return renderer
def _ipython_display_(self): # This needs to actually display, which is not the same as returning a display. cam = pythreejs.PerspectiveCamera( position=[25, 35, 100], fov=20, children=[pythreejs.AmbientLight()], ) children = [cam, pythreejs.AmbientLight(color="#dddddd")] material = pythreejs.MeshBasicMaterial(color="#ff0000", vertexColors="VertexColors", side="DoubleSide") for model in self.components: mesh = pythreejs.Mesh(geometry=model.geometry, material=material, position=[0, 0, 0]) children.append(mesh) scene = pythreejs.Scene(children=children) rendererCube = pythreejs.Renderer( camera=cam, background="white", background_opacity=1, scene=scene, controls=[pythreejs.OrbitControls(controlling=cam)], width=800, height=800, ) return rendererCube
def show(self, camera_position=[2.0, 5.0, 2.0], action=None, geometry=None): self.camera.position = camera_position children = [p3js.AmbientLight(color='#dddddd'), self.camera] children += list(self.geometry) if geometry: children += geometry scene = p3js.Scene(children=children, background="#aaaaaa") #gridHelper = p3js.GridHelper(10, 10) #scene.add(gridHelper) #axesHelper = p3js.AxesHelper(1) #axesHelper.exec_three_obj_method('geometry.rotateX', -3.14159 / 2.0) #scene.add(axesHelper) controls = p3js.OrbitControls(controlling=self.camera) controls.target = (2.0, 1.0, -2.0) renderer = p3js.Renderer(scene=scene, camera=self.camera, controls=[controls], width=self.width, height=self.height) if not action: display(renderer) else: display(renderer, action)
def view_3js(sheet, coords=['x', 'y', 'z'], **draw_specs): """ Creates a javascript renderer of the edge lines to be displayed in Jupyter Notebooks Returns ------- renderer: a :class:`pythreejs.pythreejs.Renderer` instance lines: a :class:`pythreejs.pythreejs.Line` object Example ------- >>> from IPython import display >>> renderer, lines = view_3js(eptm) >>> display(renderer) """ lines = edge_lines(sheet, coords, **draw_specs) scene = py3js.Scene(children=[ lines, py3js.DirectionalLight(color='#ccaabb', position=[0, 5, 0]), py3js.AmbientLight(color='#cccccc') ]) c = py3js.PerspectiveCamera(position=[0, 5, 5]) renderer = py3js.Renderer(camera=c, scene=scene, controls=[py3js.OrbitControls(controlling=c)]) return renderer, lines
def __init__(self, obj, width=512, height=512, textureMap=None, scalarField=None, vectorField=None): # Note: subclass's constructor should define # self.MeshConstructor and self.isLineMesh, which will # determine how the geometry is interpreted. if (self.isLineMesh is None): self.isLineMesh = False if (self.MeshConstructor is None): self.MeshConstructor = pythreejs.Mesh light = pythreejs.PointLight(color='white', position=[0, 0, 5]) light.intensity = 0.6 self.cam = pythreejs.PerspectiveCamera(position = [0, 0, 5], up = [0, 1, 0], aspect=width / height, children=[light]) self.avoidRedrawFlicker = False self.objects = pythreejs.Group() self.meshes = pythreejs.Group() self.ghostMeshes = pythreejs.Group() # Translucent meshes kept around by preserveExisting self.materialLibrary = MaterialLibrary(self.isLineMesh) # Sometimes we do not use a particular attribute buffer, e.g. the index buffer when displaying # per-face scalar fields. But to avoid reallocating these buffers when # switching away from these cases, we need to preserve the buffers # that may have previously been allocated. This is done with the bufferAttributeStash. # A buffer attribute, if it exists, must always be attached to the # current BufferGeometry or in this stash (but not both!). self.bufferAttributeStash = {} self.currMesh = None # The main mesh being viewed self.wireframeMesh = None # Wireframe for the main visualization mesh self.pointsMesh = None # Points for the main visualization mesh self.vectorFieldMesh = None self.cachedWireframeMaterial = None self.cachedPointsMaterial = None self.objects.add([self.meshes, self.ghostMeshes]) self.shouldShowWireframe = False self.scalarField = None self.vectorField = None self.arrowMaterial = None # Will hold this viewer's instance of the special vector field shader self._arrowSize = 60 # Camera needs to be part of the scene because the scene light is its child # (so that it follows the camera). self.scene = pythreejs.Scene(children=[self.objects, self.cam, pythreejs.AmbientLight(intensity=0.5)]) # Sane trackball controls. self.controls = pythreejs.TrackballControls(controlling=self.cam) self.controls.staticMoving = True self.controls.rotateSpeed = 2.0 self.controls.zoomSpeed = 2.0 self.controls.panSpeed = 1.0 self.renderer = pythreejs.Renderer(camera=self.cam, scene=self.scene, controls=[self.controls], width=width, height=height) self.update(True, obj, updateModelMatrix=True, textureMap=textureMap, scalarField=scalarField, vectorField=vectorField)
def Plot3D(S,size=(800,200),center=(0,0,0), rot=[(pi/3., pi/6., 0 )],scale=1): """Function to create 3D interactive visualization widgets in a jupiter notebook **ARGUMENTS:** ====== ================================================================= S System, component or surface to plot size Field of view in mm window shown in the notebook center Coordinate of the center of the visualization window rot List of tuples. Each tuple describe an (Rx, Ry, Rz) rotation and are applied in order to generate the first view of the window. scale Scale factor applied to the rendered window ====== ================================================================= **RETURN VALUE** pyjs renderer needed to show the image in the jupiter notebook. """ width,height=size light = py3js.DirectionalLight(color='#ffffff', intensity=.7, position=[0, 1000,0]) alight = py3js.AmbientLight(color='#777777',) # Set up a scene and render it: #cam = py3js.PerspectiveCamera(position=[0, 0, 500], fov=70, children=[light], aspect=width / height) pos = array((0, 0, 500)) for r in rot: pos=dot(rot_z(r[2]),pos) pos=dot(rot_y(r[1]),pos) pos=dot(rot_x(r[0]),pos) cam = py3js.OrthographicCamera(-width/2*scale,width/2*scale, height/2*scale, -height/2*scale,children=[light], position=list(pos), zoom=scale) if isinstance(S,System): c=sys2mesh(S) elif isinstance(S,Component): c=comp2mesh(S,(0,0,0),(0,0,0)) else: c=surf2mesh(S,(0,0,0),(0,0,0)) scene = py3js.Scene(children=[c, alight,cam],background="#000000") oc=py3js.OrbitControls(controlling=cam) oc.target=center renderer = py3js.Renderer(camera=cam, background='black', background_opacity=1, scene=scene, controls=[oc],width=width*scale, height=height*scale) return(renderer)
def __initialize_scene(self): threejs_items = [] for drawable in self.drawables: threejs_items += drawable.threejs_items scene = three.Scene(children=[ three.AmbientLight(color='white', intensity=1), self.camera, *threejs_items ]) return scene
def display_jupyter(self, window_size=(800, 600), axes_arrow_length=None): """Returns a PyThreeJS Renderer and AnimationAction for displaying and animating the scene inside a Jupyter notebook. Parameters ========== window_size : 2-tuple of integers 2-tuple containing the width and height of the renderer window in pixels. axes_arrow_length : float If a positive value is supplied a red (x), green (y), and blue (z) arrows of the supplied length will be displayed as arrows for the global axes. Returns ======= vbox : widgets.VBox A vertical box containing the action (pythreejs.AnimationAction) and renderer (pythreejs.Renderer). """ if p3js is None: raise ImportError('pythreejs needs to be installed.') self._generate_meshes_tracks() view_width = window_size[0] view_height = window_size[1] camera = p3js.PerspectiveCamera(position=[1, 1, 1], aspect=view_width / view_height) key_light = p3js.DirectionalLight() ambient_light = p3js.AmbientLight() children = self._meshes + [camera, key_light, ambient_light] if axes_arrow_length is not None: children += [p3js.AxesHelper(size=abs(axes_arrow_length))] scene = p3js.Scene(children=children) controller = p3js.OrbitControls(controlling=camera) renderer = p3js.Renderer(camera=camera, scene=scene, controls=[controller], width=view_width, height=view_height) clip = p3js.AnimationClip(tracks=self._tracks, duration=self.times[-1]) action = p3js.AnimationAction(p3js.AnimationMixer(scene), clip, scene) return widgets.VBox([action, renderer])
def __init__(self, settings): self.__update_settings(settings) self._light = p3s.DirectionalLight(color='white', position=[0, 0, 1], intensity=0.6) self._light2 = p3s.AmbientLight(intensity=0.5) self._cam = p3s.PerspectiveCamera(position=[0, 0, 1], lookAt=[0, 0, 0], fov=self.__s["fov"], aspect=self.__s["width"]/self.__s["height"], children=[self._light]) self._orbit = p3s.OrbitControls(controlling=self._cam) self._scene = p3s.Scene(children=[self._cam, self._light2], background=self.__s["background"])#"#4c4c80" self._renderer = p3s.Renderer(camera=self._cam, scene = self._scene, controls=[self._orbit], width=self.__s["width"], height=self.__s["height"], antialias=self.__s["antialias"]) self.__objects = {} self.__cnt = 0
def Plot3D(S, size=(800, 200), center=(0, 0, 0), rot=[(pi / 3., pi / 6., 0)], scale=1): width, height = size light = py3js.DirectionalLight(color='#ffffff', intensity=.7, position=[0, 1000, 0]) alight = py3js.AmbientLight(color='#777777', ) # Set up a scene and render it: #cam = py3js.PerspectiveCamera(position=[0, 0, 500], fov=70, children=[light], aspect=width / height) pos = array((0, 0, 500)) for r in rot: pos = dot(rot_z(r[2]), pos) pos = dot(rot_y(r[1]), pos) pos = dot(rot_x(r[0]), pos) cam = py3js.OrthographicCamera(-width / 2 * scale, width / 2 * scale, height / 2 * scale, -height / 2 * scale, children=[light], position=list(pos), zoom=scale) if isinstance(S, System): c = sys2mesh(S) elif isinstance(S, Component): c = comp2mesh(S, (0, 0, 0), (0, 0, 0)) else: c = surf2mesh(S, (0, 0, 0), (0, 0, 0)) scene = py3js.Scene(children=[c, alight, cam], background="#000000") oc = py3js.OrbitControls(controlling=cam) oc.target = center renderer = py3js.Renderer(camera=cam, background='black', background_opacity=1, scene=scene, controls=[oc], width=width * scale, height=height * scale) return (renderer)
def __init__(self): # TODO: arguments for width/height self._width = 600 self._height = 400 self._ball = _three.Mesh( geometry=_three.SphereGeometry( radius=1, widthSegments=30, heightSegments=20, ), material=_three.MeshLambertMaterial(color='lightgray'), ) self._axes = _three.AxesHelper(size=1.2) self._ambient_light = _three.AmbientLight( intensity=0.5, ) self._directional_light1 = _three.DirectionalLight( position=[0, 0, 1], intensity=0.6, ) self._directional_light2 = _three.DirectionalLight( position=[0, 0, -1], intensity=0.6, ) self._scene = _three.Scene( children=[ self._ball, self._axes, self._ambient_light, self._directional_light1, self._directional_light2, ], ) self._camera = _three.PerspectiveCamera( position=[0, 0, 2.4], up=[0, 0, 1], aspect=self._width/self._height, ) self._controls = _three.OrbitControls(controlling=self._camera) self._renderer = _three.Renderer( camera=self._camera, scene=self._scene, controls=[self._controls], width=self._width, height=self._height, #alpha=True, #clearOpacity=0.5, )
def enable(self, name, auto_value=None, **parameters): super(Scene, self).enable(name, auto_value, **parameters) if name == 'ambient_light': light = pythreejs.AmbientLight('#ffffff', self.get_feature_config(name)['value']) self.disable(name, strict=False) self._backend_objects['scene'].add(light) elif name == 'directional_light': # Remove existing lights self._remove_lights() # Create new lights dz = np.linalg.norm(self.size) * self._clip_scale for light_vector in np.atleast_2d( self.get_feature_config(name).get('value', DEFAULT_DIRECTIONAL_LIGHTS)): position = (-light_vector * dz).tolist() light = pythreejs.DirectionalLight( color='#ffffff', position=position, intensity=np.linalg.norm(light_vector)) self._backend_objects['directional_lights'].append(light) self._backend_objects['camera'].add(light)
def viewer_cloth(cloth): view_width = 800 view_height = 600 camera = THREE.PerspectiveCamera(position=[20, 5, 30], aspect=view_width / view_height) key_light = THREE.DirectionalLight(position=[10, 10, 10]) ambient_light = THREE.AmbientLight() axes_helper = THREE.AxesHelper(0.5) scene = THREE.Scene() controller = THREE.OrbitControls(controlling=camera) renderer = THREE.Renderer(camera=camera, scene=scene, controls=[controller], width=view_width, height=view_height) scene.children = [cloth, axes_helper, camera, key_light, ambient_light] return renderer
def getView(self, regenerateView=False, viewWidth=600, viewHeight=400): if regenerateView or self.view is None: center = self.model.get_center() camera = three.PerspectiveCamera( position=(center + np.array([0, 200, 0])).tolist(), aspect=viewWidth / viewHeight) key_light = three.DirectionalLight(position=[0, 10, 10]) ambient_light = three.AmbientLight() scene = three.Scene( children=[self.selectable, camera, key_light, ambient_light]) three.Picker(controlling=scene, event='mousemove') controller = three.OrbitControls(controlling=camera, screenSpacePanning=True, target=center.tolist()) camera.lookAt(center.tolist()) self.coord_label = HTML('Select beads by double-clicking') selIncrButton = Button(description='Increase selection') selIncrButton.on_click(self.increaseSelection) selDecrButton = Button(description='Decrease selection') selDecrButton.on_click(self.decreaseSelection) click_picker = three.Picker(controlling=self.selectable, event='dblclick') click_picker.observe(self.selectBead, names=['point']) renderer = three.Renderer(camera=camera, scene=scene, controls=[controller, click_picker], width=viewWidth, height=viewHeight, antialias=True) self.view = VBox([ self.coord_label, renderer, HBox([selDecrButton, selIncrButton]) ]) return self.view
def _setup_scene(self, bounding_box, render_size): self.min_width = render_size[0] self.min_height = render_size[1] fov_angle = 60 if len(bounding_box[0]) == 2: lower = np.array([bounding_box[0][0], bounding_box[0][1], 0]) upper = np.array([bounding_box[1][0], bounding_box[1][1], 0]) bounding_box = (lower, upper) combined_bounds = np.hstack(bounding_box) absx = np.abs(combined_bounds[0] - combined_bounds[3]) not_mathematical_distance_scaling = 1.2 self.camera_distance = np.sin( (90 - fov_angle / 2) * np.pi / 180) * 0.5 * absx / np.sin( fov_angle / 2 * np.pi / 180) self.camera_distance *= not_mathematical_distance_scaling xhalf = (combined_bounds[0] + combined_bounds[3]) / 2 yhalf = (combined_bounds[1] + combined_bounds[4]) / 2 zhalf = (combined_bounds[2] + combined_bounds[5]) / 2 self.mesh_center = (xhalf, yhalf, zhalf) self.cam = p3js.PerspectiveCamera( aspect=render_size[0] / render_size[1], position=[0, 0, 0 + self.camera_distance]) self.light = p3js.AmbientLight(color='white', intensity=1.0) self.scene = p3js.Scene(children=[self.cam, self.light], background='white') self.controller = p3js.OrbitControls( controlling=self.cam, position=[0, 0, 0 + self.camera_distance], target=[0, 0, 0]) self.freeze_camera(True) self.renderer = p3js.Renderer(camera=self.cam, scene=self.scene, controls=[self.controller], webgl_version=1, width=render_size[0], height=render_size[1])
def Plot3D(S, size=(800, 200), center=(0, 0, 0), rot=[(pi / 3.0, pi / 6.0, 0)], scale=1): """Function to create 3D interactive visualization widgets in a jupyter notebook Args: S: (:class:`~pyoptools.raytrace.system.System`, :class:`~pyoptools.raytrace.component.Component` or :class:`~pyoptools.raytrace.component.Component`) Object to plot size: (Tuple(float,float)) Field of view in X and Y for the window shown in the notebook. center: (Tuple(float,float,float) Coordinate of the center of the visualization window given in the coordinate system of the object to plot. rot: List of tuples. Each tuple describe an (Rx, Ry, Rz) rotation and are applied in order to generate the first view of the window. scale: (float) Scale factor applied to the rendered window Returns: pyjs renderer needed to show the image in the jupiter notebook. """ width, height = size light = py3js.DirectionalLight(color="#ffffff", intensity=0.7, position=[0, 1000, 0]) alight = py3js.AmbientLight(color="#777777", ) # Set up a scene and render it: # cam = py3js.PerspectiveCamera(position=[0, 0, 500], fov=70, children=[light], aspect=width / height) pos = array((0, 0, 500)) for r in rot: pos = dot(rot_z(r[2]), pos) pos = dot(rot_y(r[1]), pos) pos = dot(rot_x(r[0]), pos) cam = py3js.OrthographicCamera( -width / 2 * scale, width / 2 * scale, height / 2 * scale, -height / 2 * scale, children=[light], position=list(pos), zoom=scale, ) if isinstance(S, System): c = sys2mesh(S) elif isinstance(S, Component): c = comp2mesh(S, (0, 0, 0), (0, 0, 0)) else: c = surf2mesh(S, (0, 0, 0), (0, 0, 0)) scene = py3js.Scene(children=[c, alight, cam], background="#000000") oc = py3js.OrbitControls(controlling=cam) oc.target = center renderer = py3js.Renderer( camera=cam, background="black", background_opacity=1, scene=scene, controls=[oc], width=width * scale, height=height * scale, ) return renderer
def create_js_scene_view(gcollect, add_objects=True, add_labels=False, gobject_jsmap=None, jslink=False): """create PyThreeJS Scene for GeometricCollection and one-way link all GeometricObject attributes and creation/deletion Properties ---------- gcollect : GeometricCollection add_objects: bool add objects to scene add_labels : bool add object labels to scene gobject_jsmap : None or dict if None use default gobject->jsobject mapping jslink : bool if True, where possible, create client side links http://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html#The-difference-between-linking-in-the-kernel-and-linking-in-the-client Returns ------- scene : pythreejs.Scene scene.children = [gobjcontainer,light] Examples -------- >>> from pandas3js.models import GeometricCollection, Sphere >>> collection = GeometricCollection() >>> scene = create_js_scene_view(collection,add_objects=True,add_labels=True) >>> container = scene.children[0] >>> [type(child) for child in container.children] [] >>> sphere = Sphere(id=1) >>> collection.add_object(sphere) >>> [type(child) for child in container.children] [<class 'traitlets.traitlets.Sprite'>, <class 'traitlets.traitlets.Mesh'>] >>> mesh = container.children[1] >>> mesh.position [0.0, 0.0, 0.0] >>> sphere.position = (1,0,0) >>> mesh.position [1.0, 0.0, 0.0] >>> sphere = collection.pop(1) >>> [type(child) for child in container.children] [] """ assert isinstance( gcollect, GeometricCollection), 'gcollect must be a GeometricCollection' meshes = [] for gobject in gcollect.idobjects: if add_labels: lmesh = create_jslabelmesh_view(gobject, gobject_jsmap, jslink=jslink) meshes.append(lmesh) if add_objects: gmesh = create_jsmesh_view(gobject, gobject_jsmap, jslink=jslink) meshes.append(gmesh) # create dummy parent mesh to house all meshes, so we can use single mouse picker # NB: it would be better to use groups https://threejs.org/docs/#api/objects/Group # but this is not implemented in pythreejs gcontainer = js.Mesh(geometry=js.Geometry(), material=js.BasicMaterial(), position=[0, 0, 0], children=meshes) scenelight = js.AmbientLight(color='#777777') scene = js.Scene(children=[gcontainer, scenelight]) def gobjects_changed(change): old = set(change.old) new = set(change.new) removed_objects = old.difference(new) added_objects = new.difference(old) if removed_objects: removed_ids = [o.id for o in removed_objects] original_children = [] for child in gcontainer.children: if child.gobject_id not in removed_ids: original_children.append(child) else: original_children = gcontainer.children new_meshes = [] for gobject in added_objects: if add_labels: new_meshes.append( create_jslabelmesh_view(gobject, gobject_jsmap, jslink=jslink)) if add_objects: new_meshes.append( create_jsmesh_view(gobject, gobject_jsmap, jslink=jslink)) gcontainer.children = new_meshes + original_children gcollect.observe(gobjects_changed, names='idobjects') return scene
def cqdisplay(result, color='#708090', scale=1.0): 'display CQ object in a ThreeJS Webgl context' # Open stream output = StringIO.StringIO() # cadquery will stream a ThreeJS JSON (using old v3 schema, which is deprecated) exporters.exportShape(result.shape.findSolid().scale(scale), 'TJS', output) # store stream to a variable contents = output.getvalue() # Close stream output.close() # Overwrite the JSON color portion with user defined color. Disallows NAMED colors col = list(matplotlib.colors.hex2color(color)) old_col_str = '"colorDiffuse" : [0.6400000190734865, 0.10179081114814892, 0.126246120426746]' new_col_str = '"colorDiffuse" : ' + str(col) new_contents = contents.replace(old_col_str, new_col_str) # Take the string and create a proper json object contents = json.loads(contents) # Vertices and Faces are both flat lists, but the pythreejs module requires list of lists old_v = contents['vertices'] old_f = contents['faces'] # Splits the list up in 3s, to produce a list of lists representing the vertices vertices = [old_v[i:i+3] for i in range(0, len(old_v), 3)] # JSON Schema has first position in the face's list reserved to indicate type. # Cadquery returns Triangle mesh, so we know that we must split list into lists of length 4 # 1st entry to indicate triangle, next 3 to specify vertices three_faces = [old_f[i:i+4] for i in range(0, len(old_f), 4)] faces = [] # Drop the first entry in the face list for entry in three_faces: entry.pop(0) faces.append(entry) # Cadquery does not supply face normals in the JSON, # and we cannot use THREE.JS built in 'computefaceNormals' # (at least, not easily) # Instead, we just calculate the face normals ourselves. # It is just the cross product of 2 vectors in the triangle. # TODO: see if there is a better way to achieve this result face_normals = [] for entry in faces: v_a = np.asarray(vertices[entry[0]]) v_b = np.asarray(vertices[entry[1]]) v_c = np.asarray(vertices[entry[2]]) vec_a = v_b - v_a vec_b = v_c - v_a cross = np.cross(vec_a, vec_b) face_normals.append([cross[0], cross[1], cross[2]]) # set up geometry geom = pythreejs.PlainGeometry(vertices=vertices, faces=faces, faceNormals=face_normals) mtl = pythreejs.LambertMaterial(color=color, shading='FlatShading') obj = pythreejs.Mesh(geometry=geom, material=mtl) # set up scene and camera cam_dist = 50 fov = 35 cam = pythreejs.PerspectiveCamera( position=[cam_dist, cam_dist, cam_dist], fov=fov, children=[pythreejs.DirectionalLight(color='#ffffff', position=[-3, 5, 1], intensity=0.45)]) scn_chld = [ obj, pythreejs.AmbientLight(color='#dddddd') ] scn = pythreejs.Scene(children=scn_chld) render = pythreejs.Renderer( width='830'.decode('utf-8'), height='553'.decode('utf-8'), camera=cam, scene=scn, controls=[pythreejs.OrbitControls(controlling=cam)] ) return render
def _render_obj(self, rendered_obj, **kw): obj_geometry = pjs.BufferGeometry(attributes=dict( position=pjs.BufferAttribute(rendered_obj.plot_verts), color=pjs.BufferAttribute(rendered_obj.base_cols), normal=pjs.BufferAttribute( rendered_obj.face_normals.astype('float32')))) vertices = rendered_obj.vertices # Create a mesh. Note that the material need to be told to use the vertex colors. my_object_mesh = pjs.Mesh( geometry=obj_geometry, material=pjs.MeshLambertMaterial(vertexColors='VertexColors'), position=[0, 0, 0], ) line_material = pjs.LineBasicMaterial(color='#ffffff', transparent=True, opacity=0.3, linewidth=1.0) my_object_wireframe_mesh = pjs.LineSegments( geometry=obj_geometry, material=line_material, position=[0, 0, 0], ) n_vert = vertices.shape[0] center = vertices.mean(axis=0) extents = self._get_extents(vertices) max_delta = np.max(extents[:, 1] - extents[:, 0]) camPos = [center[i] + 4 * max_delta for i in range(3)] light_pos = [center[i] + (i + 3) * max_delta for i in range(3)] # Set up a scene and render it: camera = pjs.PerspectiveCamera(position=camPos, fov=20, children=[ pjs.DirectionalLight( color='#ffffff', position=light_pos, intensity=0.5) ]) camera.up = (0, 0, 1) v = [0.0, 0.0, 0.0] if n_vert > 0: v = vertices[0].tolist() select_point_geom = pjs.SphereGeometry(radius=1.0) select_point_mesh = pjs.Mesh( select_point_geom, material=pjs.MeshBasicMaterial(color=SELECTED_VERTEX_COLOR), position=v, scale=(0.0, 0.0, 0.0)) #select_edge_mesh = pjs.ArrowHelper(dir=pjs.Vector3(1.0, 0.0, 0.0), origin=pjs.Vector3(0.0, 0.0, 0.0), length=1.0, # hex=SELECTED_EDGE_COLOR_INT, headLength=0.1, headWidth=0.05) arrow_cyl_mesh = pjs.Mesh(geometry=pjs.SphereGeometry(radius=0.01), material=pjs.MeshLambertMaterial()) arrow_head_mesh = pjs.Mesh(geometry=pjs.SphereGeometry(radius=0.001), material=pjs.MeshLambertMaterial()) scene_things = [ my_object_mesh, my_object_wireframe_mesh, select_point_mesh, arrow_cyl_mesh, arrow_head_mesh, camera, pjs.AmbientLight(color='#888888') ] if self.draw_grids: grids, space = self._get_grids(vertices) scene_things.append(grids) scene = pjs.Scene(children=scene_things, background=BACKGROUND_COLOR) click_picker = pjs.Picker(controlling=my_object_mesh, event='dblclick') out = Output() top_msg = HTML() def on_dblclick(change): if change['name'] == 'point': try: point = np.array(change['new']) face = click_picker.faceIndex face_points = rendered_obj.face_verts[face] face_vecs = face_points - np.roll(face_points, 1, axis=0) edge_lens = np.sqrt((face_vecs**2).sum(axis=1)) point_vecs = face_points - point[np.newaxis, :] point_dists = (point_vecs**2).sum(axis=1) min_point = np.argmin(point_dists) v1s = point_vecs.copy() v2s = np.roll(v1s, -1, axis=0) edge_mids = 0.5 * (v2s + v1s) edge_mid_dists = (edge_mids**2).sum(axis=1) min_edge_point = np.argmin(edge_mid_dists) edge_start = min_edge_point edge = face * 3 + edge_start close_vert = rendered_obj.face_verts[face, min_point] edge_start_vert = rendered_obj.face_verts[face, edge_start] edge_end_vert = rendered_obj.face_verts[face, (edge_start + 1) % 3] vertex = face * 3 + min_point radius = min( [edge_lens.max() * 0.02, 0.1 * edge_lens.min()]) edge_head_length = radius * 4 edge_head_width = radius * 2 select_point_mesh.scale = (radius, radius, radius) top_msg.value = '<font color="{}">selected face: {}</font>, <font color="{}">edge: {}</font>, <font color="{}"> vertex: {}</font>'.format( SELECTED_FACE_COLOR, face, SELECTED_EDGE_COLOR, edge, SELECTED_VERTEX_COLOR, vertex) newcols = rendered_obj.base_cols.copy() newcols[face * 3:(face + 1) * 3] = np.array( SELECTED_FACE_RGB, dtype='float32') select_point_mesh.position = close_vert.tolist() obj_geometry.attributes['color'].array = newcols with out: make_arrow(arrow_cyl_mesh, arrow_head_mesh, edge_start_vert, edge_end_vert, radius / 2, radius, radius * 3, SELECTED_EDGE_COLOR) except: with out: print(traceback.format_exc()) click_picker.observe(on_dblclick, names=['point']) renderer_obj = pjs.Renderer( camera=camera, background='#cccc88', background_opacity=0, scene=scene, controls=[pjs.OrbitControls(controlling=camera), click_picker], width=self.width, height=self.height) display_things = [top_msg, renderer_obj, out] if self.draw_grids: s = """ <svg width="{}" height="30"> <rect width="20" height="20" x="{}" y="0" style="fill:none;stroke-width:1;stroke:rgb(0,255,0)" /> <text x="{}" y="15">={:.1f}</text> Sorry, your browser does not support inline SVG. </svg>""".format(self.width, self.width // 2, self.width // 2 + 25, space) display_things.append(HTML(s)) display(VBox(display_things))
def _render_stl(self, stl_file): vertices, faces = self._conv_stl(stl_file) # Map the vertex colors into the 'color' slot of the faces faces = [f + [None, [OBJ_COLOR for i in f], None] for f in faces] # Create the geometry: obj_geometry = pjs.Geometry(vertices=vertices, faces=faces, colors=[OBJ_COLOR] * len(vertices)) # Calculate normals per face, for nice crisp edges: obj_geometry.exec_three_obj_method('computeFaceNormals') # Create a mesh. Note that the material need to be told to use the vertex colors. my_object_mesh = pjs.Mesh( geometry=obj_geometry, material=pjs.MeshLambertMaterial(vertexColors='VertexColors'), position=[0, 0, 0], # Center the cube ) n_vert = len(vertices) center = [ sum([vertex[i] for vertex in vertices]) / float(n_vert) for i in range(3) ] extents = self._get_extents(vertices) max_delta = max([extent[1] - extent[0] for extent in extents]) camPos = [center[i] + 4 * max_delta for i in range(3)] light_pos = [center[i] + (i + 3) * max_delta for i in range(3)] # Set up a scene and render it: camera = pjs.PerspectiveCamera(position=camPos, fov=20, children=[ pjs.DirectionalLight( color='#ffffff', position=light_pos, intensity=0.5) ]) camera.up = (0, 0, 1) scene_things = [ my_object_mesh, camera, pjs.AmbientLight(color='#888888') ] if self.draw_grids: grids, space = self._get_grids(vertices) scene_things.append(grids) scene = pjs.Scene(children=scene_things, background=BACKGROUND_COLOR) renderer_obj = pjs.Renderer( camera=camera, background='#cccc88', background_opacity=0, scene=scene, controls=[pjs.OrbitControls(controlling=camera)], width=self.width, height=self.height) display_things = [renderer_obj] if self.draw_grids: s = """ <svg width="{}" height="30"> <rect width="20" height="20" x="{}" y="0" style="fill:none;stroke-width:1;stroke:rgb(0,255,0)" /> <text x="{}" y="15">={:.1f}</text> Sorry, your browser does not support inline SVG. </svg>""".format(self.width, self.width // 2, self.width // 2 + 25, space) display_things.append(HTML(s)) display(*display_things)
def visualise(mesh, geometric_field, number_of_dimensions, xi_interpolation, dependent_field=None, variable=None, mechanics_animation=False, colour_map_dependent_component_number=None, cmap='gist_rainbow', resolution=1, node_labels=False): if number_of_dimensions != 3: print( 'Warning: Only visualisation of 3D meshes is currently supported.') return if xi_interpolation != [1, 1, 1]: print( 'Warning: Only visualisation of 3D elements with linear Lagrange \ interpolation along all coordinate directions is currently \ supported.') return view_width = 600 view_height = 600 debug = False if debug: vertices = [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]] faces = [[0, 1, 3], [0, 3, 2], [0, 2, 4], [2, 6, 4], [0, 4, 1], [1, 4, 5], [2, 3, 6], [3, 7, 6], [1, 5, 3], [3, 5, 7], [4, 6, 5], [5, 6, 7]] vertexcolors = [ '#000000', '#0000ff', '#00ff00', '#ff0000', '#00ffff', '#ff00ff', '#ffff00', '#ffffff' ] else: # Get mesh topology information. num_nodes = mesh_tools.num_nodes_get(mesh, mesh_component=1) node_nums = list(range(1, num_nodes + 1)) num_elements, element_nums = mesh_tools.num_element_get( mesh, mesh_component=1) # Convert geometric field to a morphic mesh and export to json mesh = mesh_tools.OpenCMISS_to_morphic(mesh, geometric_field, element_nums, node_nums, dimension=3, interpolation='linear') vertices, faces, _, xi_element_nums, xis = get_faces( mesh, res=resolution, exterior_only=True, include_xi=True) vertices = vertices.tolist() faces = faces.tolist() centroid = np.mean(vertices, axis=0) max_positions = np.max(vertices, axis=0) min_positions = np.min(vertices, axis=0) range_positions = max_positions - min_positions if (dependent_field is not None) and (colour_map_dependent_component_number is not None): solution = np.zeros(xis.shape[0]) for idx, (xi, xi_element_num) in enumerate(zip(xis, xi_element_nums)): solution[idx] = mesh_tools.interpolate_opencmiss_field_xi( dependent_field, xi, element_ids=[xi_element_num], dimension=3, deriv=1)[colour_map_dependent_component_number - 1] minima = min(solution) maxima = max(solution) import matplotlib norm = matplotlib.colors.Normalize(vmin=minima, vmax=maxima, clip=True) mapper = cm.ScalarMappable(norm=norm, cmap=cm.get_cmap(name=cmap)) vertex_colors = np.zeros((len(vertices), 3), dtype='float32') for idx, v in enumerate(solution): vertex_colors[idx, :] = mapper.to_rgba(v, alpha=None)[:3] # else: # raise ValueError('Visualisation not supported.') else: vertex_colors = np.tile(np.array([0.5, 0.5, 0.5], dtype='float32'), (len(vertices), 1)) geometry = pjs.BufferGeometry(attributes=dict( position=pjs.BufferAttribute(vertices, normalized=False), index=pjs.BufferAttribute( np.array(faces).astype(dtype='uint16').ravel(), normalized=False), color=pjs.BufferAttribute(vertex_colors), )) if mechanics_animation: deformed_vertices = np.zeros((xis.shape[0], 3), dtype='float32') for idx, (xi, xi_element_num) in enumerate(zip(xis, xi_element_nums)): deformed_vertices[idx, :] = \ mesh_tools.interpolate_opencmiss_field_xi( dependent_field, xi, element_ids=[xi_element_num], dimension=3, deriv=1)[0][:3] geometry.morphAttributes = { 'position': [ pjs.BufferAttribute(deformed_vertices), ] } geometry.exec_three_obj_method('computeFaceNormals') geometry.exec_three_obj_method('computeVertexNormals') surf1 = pjs.Mesh(geometry, pjs.MeshPhongMaterial(color='#ff3333', shininess=150, morphTargets=True, side='FrontSide'), name='A') surf2 = pjs.Mesh(geometry, pjs.MeshPhongMaterial(color='#ff3333', shininess=150, morphTargets=True, side='BackSide'), name='B') surf = pjs.Group(children=[surf1, surf2]) # camera = pjs.PerspectiveCamera( # fov=20, position=[range_positions[0] * 10, # range_positions[1] * 10, # range_positions[2] * 10], # width=view_width, # height=view_height, near=1, # far=max(range_positions) * 10) camera = pjs.PerspectiveCamera(position=[ range_positions[0] * 3, range_positions[1] * 3, range_positions[2] * 3 ], aspect=view_width / view_height) camera.up = [0, 0, 1] camera.lookAt(centroid.tolist()) scene3 = pjs.Scene(children=[ surf1, surf2, camera, pjs.DirectionalLight(position=[3, 5, 1], intensity=0.6), pjs.AmbientLight(intensity=0.5) ]) axes = pjs.AxesHelper(size=range_positions[0] * 2) scene3.add(axes) A_track = pjs.NumberKeyframeTrack( name='scene/A.morphTargetInfluences[0]', times=[0, 3], values=[0, 1]) B_track = pjs.NumberKeyframeTrack( name='scene/B.morphTargetInfluences[0]', times=[0, 3], values=[0, 1]) pill_clip = pjs.AnimationClip(tracks=[A_track, B_track]) pill_action = pjs.AnimationAction(pjs.AnimationMixer(scene3), pill_clip, scene3) renderer3 = pjs.Renderer( camera=camera, scene=scene3, controls=[pjs.OrbitControls(controlling=camera)], width=view_width, height=view_height) display(renderer3, pill_action) else: geometry.exec_three_obj_method('computeFaceNormals') geometry.exec_three_obj_method('computeVertexNormals') surf1 = pjs.Mesh(geometry=geometry, material=pjs.MeshLambertMaterial( vertexColors='VertexColors', side='FrontSide')) # Center the cube. surf2 = pjs.Mesh(geometry=geometry, material=pjs.MeshLambertMaterial( vertexColors='VertexColors', side='BackSide')) # Center the cube. surf = pjs.Group(children=[surf1, surf2]) camera = pjs.PerspectiveCamera(position=[ range_positions[0] * 3, range_positions[1] * 3, range_positions[2] * 3 ], aspect=view_width / view_height) camera.up = [0, 0, 1] camera.lookAt(centroid.tolist()) # if perspective: # camera.mode = 'perspective' # else: # camera.mode = 'orthographic' lights = [ pjs.DirectionalLight(position=[ range_positions[0] * 16, range_positions[1] * 12, range_positions[2] * 17 ], intensity=0.5), pjs.AmbientLight(intensity=0.8), ] orbit = pjs.OrbitControls(controlling=camera, screenSpacePanning=True, target=centroid.tolist()) scene = pjs.Scene() axes = pjs.AxesHelper(size=max(range_positions) * 2) scene.add(axes) scene.add(surf1) scene.add(surf2) scene.add(lights) if node_labels: # Add text labels for each mesh node. v, ids = mesh.get_node_ids(group='_default') for idx, v in enumerate(v): text = make_text(str(ids[idx]), position=(v[0], v[1], v[2])) scene.add(text) # Add text for axes labels. x_axis_label = make_text('x', position=(max(range_positions) * 2, 0, 0)) y_axis_label = make_text('y', position=(0, max(range_positions) * 2, 0)) z_axis_label = make_text('z', position=(0, 0, max(range_positions) * 2)) scene.add(x_axis_label) scene.add(y_axis_label) scene.add(z_axis_label) renderer = pjs.Renderer(scene=scene, camera=camera, controls=[orbit], width=view_width, height=view_height) camera.zoom = 1 display(renderer) return vertices, faces
def mesh_animation(times, xt, faces): """ Animate a mesh from a sequence of mesh vertex positions Args: times - a list of time values t_i at which the configuration x is specified xt - i.e., x(t). A list of arrays representing mesh vertex positions at times t_i. Dimensions of each array should be the same as that of mesh.geometry.array TODO nt - n(t) vertex normals faces - array of faces, with vertex loop for each face Side effects: displays rendering of mesh, with animation action Returns: None TODO renderer - THREE.Render to show the default scene TODO position_action - THREE.AnimationAction IPython widget """ position_morph_attrs = [] for pos in xt[ 1:]: # xt[0] uses as the Mesh's default/initial vertex position position_morph_attrs.append( THREE.BufferAttribute(pos, normalized=False)) # Testing mesh.geometry.morphAttributes = {'position': position_morph_attrs} geom = THREE.BufferGeometry( attributes={ 'position': THREE.BufferAttribute(xt[0], normalized=False), 'index': THREE.BufferAttribute(faces.ravel()) }, morphAttributes={'position': position_morph_attrs}) matl = THREE.MeshStandardMaterial(side='DoubleSide', color='red', wireframe=True, morphTargets=True) mesh = THREE.Mesh(geom, matl) # create key frames position_track = THREE.NumberKeyframeTrack( name='.morphTargetInfluences[0]', times=times, values=list(range(0, len(times)))) # create animation clip from the morph targets position_clip = THREE.AnimationClip(tracks=[position_track]) # create animation action position_action = THREE.AnimationAction(THREE.AnimationMixer(mesh), position_clip, mesh) # TESTING camera = THREE.PerspectiveCamera(position=[2, 1, 2], aspect=600 / 400) scene = THREE.Scene(children=[ mesh, camera, THREE.AxesHelper(0.2), THREE.DirectionalLight(position=[3, 5, 1], intensity=0.6), THREE.AmbientLight(intensity=0.5) ]) renderer = THREE.Renderer( camera=camera, scene=scene, controls=[THREE.OrbitControls(controlling=camera)], width=600, height=400) display(renderer, position_action)
def generate_3js_render( element_groups, canvas_size, zoom, camera_fov=30, background_color="white", background_opacity=1.0, reuse_objects=False, use_atom_arrays=False, use_label_arrays=False, ): """Create a pythreejs scene of the elements. Regarding initialisation performance, see: https://github.com/jupyter-widgets/pythreejs/issues/154 """ import pythreejs as pjs key_elements = {} group_elements = pjs.Group() key_elements["group_elements"] = group_elements unique_atom_sets = {} for el in element_groups["atoms"]: element_hash = ( ("radius", el.sradius), ("color", el.color), ("fill_opacity", el.fill_opacity), ("stroke_color", el.get("stroke_color", "black")), ("ghost", el.ghost), ) unique_atom_sets.setdefault(element_hash, []).append(el) group_atoms = pjs.Group() group_ghosts = pjs.Group() atom_geometries = {} atom_materials = {} outline_materials = {} for el_hash, els in unique_atom_sets.items(): el = els[0] data = dict(el_hash) if reuse_objects: atom_geometry = atom_geometries.setdefault( el.sradius, pjs.SphereBufferGeometry(radius=el.sradius, widthSegments=30, heightSegments=30), ) else: atom_geometry = pjs.SphereBufferGeometry(radius=el.sradius, widthSegments=30, heightSegments=30) if reuse_objects: atom_material = atom_materials.setdefault( (el.color, el.fill_opacity), pjs.MeshLambertMaterial(color=el.color, transparent=True, opacity=el.fill_opacity), ) else: atom_material = pjs.MeshLambertMaterial(color=el.color, transparent=True, opacity=el.fill_opacity) if use_atom_arrays: atom_mesh = pjs.Mesh(geometry=atom_geometry, material=atom_material) atom_array = pjs.CloneArray( original=atom_mesh, positions=[e.position.tolist() for e in els], merge=False, ) else: atom_array = [ pjs.Mesh( geometry=atom_geometry, material=atom_material, position=e.position.tolist(), name=e.info_string, ) for e in els ] data["geometry"] = atom_geometry data["material_body"] = atom_material if el.ghost: key_elements["group_ghosts"] = group_ghosts group_ghosts.add(atom_array) else: key_elements["group_atoms"] = group_atoms group_atoms.add(atom_array) if el.get("stroke_width", 1) > 0: if reuse_objects: outline_material = outline_materials.setdefault( el.get("stroke_color", "black"), pjs.MeshBasicMaterial( color=el.get("stroke_color", "black"), side="BackSide", transparent=True, opacity=el.get("stroke_opacity", 1.0), ), ) else: outline_material = pjs.MeshBasicMaterial( color=el.get("stroke_color", "black"), side="BackSide", transparent=True, opacity=el.get("stroke_opacity", 1.0), ) # TODO use stroke width to dictate scale if use_atom_arrays: outline_mesh = pjs.Mesh( geometry=atom_geometry, material=outline_material, scale=(1.05, 1.05, 1.05), ) outline_array = pjs.CloneArray( original=outline_mesh, positions=[e.position.tolist() for e in els], merge=False, ) else: outline_array = [ pjs.Mesh( geometry=atom_geometry, material=outline_material, position=e.position.tolist(), scale=(1.05, 1.05, 1.05), ) for e in els ] data["material_outline"] = outline_material if el.ghost: group_ghosts.add(outline_array) else: group_atoms.add(outline_array) key_elements.setdefault("atom_arrays", []).append(data) group_elements.add(group_atoms) group_elements.add(group_ghosts) group_labels = add_labels(element_groups, key_elements, use_label_arrays) group_elements.add(group_labels) if len(element_groups["cell_lines"]) > 0: cell_line_mat = pjs.LineMaterial( linewidth=1, color=element_groups["cell_lines"].group_properties["color"]) cell_line_geo = pjs.LineSegmentsGeometry(positions=[ el.position.tolist() for el in element_groups["cell_lines"] ]) cell_lines = pjs.LineSegments2(geometry=cell_line_geo, material=cell_line_mat) key_elements["cell_lines"] = cell_lines group_elements.add(cell_lines) if len(element_groups["bond_lines"]) > 0: bond_line_mat = pjs.LineMaterial( linewidth=element_groups["bond_lines"]. group_properties["stroke_width"], vertexColors="VertexColors", ) bond_line_geo = pjs.LineSegmentsGeometry( positions=[ el.position.tolist() for el in element_groups["bond_lines"] ], colors=[[Color(c).rgb for c in el.color] for el in element_groups["bond_lines"]], ) bond_lines = pjs.LineSegments2(geometry=bond_line_geo, material=bond_line_mat) key_elements["bond_lines"] = bond_lines group_elements.add(bond_lines) group_millers = pjs.Group() if len(element_groups["miller_lines"]) or len( element_groups["miller_planes"]): key_elements["group_millers"] = group_millers if len(element_groups["miller_lines"]) > 0: miller_line_mat = pjs.LineMaterial( linewidth=3, vertexColors="VertexColors" # TODO use stroke_width ) miller_line_geo = pjs.LineSegmentsGeometry( positions=[ el.position.tolist() for el in element_groups["miller_lines"] ], colors=[[Color(el.stroke_color).rgb] * 2 for el in element_groups["miller_lines"]], ) miller_lines = pjs.LineSegments2(geometry=miller_line_geo, material=miller_line_mat) group_millers.add(miller_lines) for el in element_groups["miller_planes"]: vertices = el.position.tolist() faces = [( 0, 1, 2, triangle_normal(vertices[0], vertices[1], vertices[2]), "black", 0, )] if len(vertices) == 4: faces.append(( 2, 3, 0, triangle_normal(vertices[2], vertices[3], vertices[0]), "black", 0, )) elif len(vertices) != 3: raise NotImplementedError("polygons with more than 4 points") plane_geom = pjs.Geometry(vertices=vertices, faces=faces) plane_mat = pjs.MeshBasicMaterial( color=el.fill_color, transparent=True, opacity=el.fill_opacity, side="DoubleSide", ) plane_mesh = pjs.Mesh(geometry=plane_geom, material=plane_mat) group_millers.add(plane_mesh) group_elements.add(group_millers) scene = pjs.Scene(background=None) scene.add([group_elements]) view_width, view_height = canvas_size minp, maxp = element_groups.get_position_range() # compute a minimum camera distance, that is guaranteed to encapsulate all elements camera_dist = maxp[2] + sqrt(maxp[0]**2 + maxp[1]**2) / tan( radians(camera_fov / 2)) camera = pjs.PerspectiveCamera( fov=camera_fov, position=[0, 0, camera_dist], aspect=view_width / view_height, zoom=zoom, ) scene.add([camera]) ambient_light = pjs.AmbientLight(color="lightgray") key_elements["ambient_light"] = ambient_light direct_light = pjs.DirectionalLight(position=(maxp * 2).tolist()) key_elements["direct_light"] = direct_light scene.add([camera, ambient_light, direct_light]) camera_control = pjs.OrbitControls(controlling=camera, screenSpacePanning=True) atom_picker = pjs.Picker(controlling=group_atoms, event="dblclick") key_elements["atom_picker"] = atom_picker material = pjs.SpriteMaterial( map=create_arrow_texture(right=False), transparent=True, depthWrite=False, depthTest=False, ) atom_pointer = pjs.Sprite(material=material, scale=(4, 3, 1), visible=False) scene.add(atom_pointer) key_elements["atom_pointer"] = atom_pointer renderer = pjs.Renderer( camera=camera, scene=scene, controls=[camera_control, atom_picker], width=view_width, height=view_height, alpha=True, clearOpacity=background_opacity, clearColor=background_color, ) return renderer, key_elements