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 _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 _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 plot_voxelgrid_with_pythreejs(voxel_centers, voxel_colors, width, height, **kwargs): if pythreejs is None: raise ImportError( "pythreejs is needed for plotting with pythreejs backend.") if display is None: raise ImportError( "IPython is needed for plotting with pythreejs backend.") centroid, camera_position = get_centroid_and_camera_position(voxel_centers) camera = pythreejs.PerspectiveCamera(fov=90, aspect=width / height, position=camera_position, up=[0, 0, 1]) mesh = get_voxelgrid_pythreejs(voxel_centers, voxel_colors) scene = pythreejs.Scene(children=[camera, mesh], background=None) controls = pythreejs.OrbitControls(controlling=camera, target=tuple(centroid)) camera.lookAt(tuple(centroid)) renderer = pythreejs.Renderer(scene=scene, camera=camera, controls=[controls], width=width, height=height) display(renderer)
def __init__(self, *args, **kwargs): self._backend_objects = dict(scene=pythreejs.Scene()) self._clip_scale = 1 self.z_offset = kwargs.get('translation', (0, 0, 0))[2] size = (40, 30) (width, height) = size dz = np.linalg.norm(size)*self._clip_scale self._backend_objects['camera'] = pythreejs.OrthographicCamera( -width/2, width/2, height/2, -height/2, 1, 1 + dz, position=(0, 0, 1)) self._backend_objects['controls'] = pythreejs.OrbitControls( self._backend_objects['camera'], target=(0, 0, 0)) self._backend_objects['scene'].add(self._backend_objects['camera']) self._backend_objects['directional_lights'] = [] (pixel_width, pixel_height) = (width*10, height*10) renderer_kwargs = dict( width=pixel_width, height=pixel_height, antialias=True, **self._backend_objects) renderer_kwargs['controls'] = [renderer_kwargs['controls']] self._backend_objects['renderer'] = pythreejs.Renderer(**renderer_kwargs) super(Scene, self).__init__(*args, **kwargs) self._update_canvas_size() # directly re-initialize rotation after camera has been set up self.rotation = kwargs.get('rotation', (1, 0, 0, 0)) self.translation = kwargs.get('translation', (0, 0, 0))
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 _display_sphere( self, fluxes, distances, theta, phi, cmap="magma", distance_transform=None, use_log=False, fig=None, background_color="white", show=True, **kwargs, ): if len(fluxes) == 0: log.warning("There are no detections to display") return if fig is None: fig = ipv.figure() ipv.pylab.style.box_off() ipv.pylab.style.axes_off() ipv.pylab.style.set_style_dark() ipv.pylab.style.background_color(background_color) if distance_transform is not None: distance = distance_transform(distances) else: distance = distances x, y, z = _create_sphere_variables(self._r_max, distance, theta, phi) _, colors = array_to_cmap(fluxes, cmap, use_log=True) ipv.scatter(x, y, z, color=colors, marker="sphere", **kwargs) r_value = fig if show: ipv.xyzlim(self._r_max) fig.camera.up = [1, 0, 0] control = pythreejs.OrbitControls(controlling=fig.camera) fig.controls = control control.autoRotate = True fig.render_continuous = True # control.autoRotate = True # toggle_rotate = widgets.ToggleButton(description="Rotate") # widgets.jslink((control, "autoRotate"), (toggle_rotate, "value")) # r_value = toggle_rotate return fig
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 convert_renderer(pv_renderer): """Convert a pyvista renderer to a pythreejs renderer.""" # verify plotter hasn't been closed width, height = pv_renderer.width, pv_renderer.height pv_camera = pv_renderer.camera children = meshes_from_actors(pv_renderer.actors.values(), pv_camera.focal_point) lights = extract_lights_from_renderer(pv_renderer) aspect = width / height camera = pvcamera_to_threejs_camera(pv_camera, lights, aspect) children.append(camera) if pv_renderer.axes_enabled: children.append(tjs.AxesHelper(0.1)) scene = tjs.Scene(children=children, background=color_to_hex(pv_renderer.background_color)) # replace inf with a real value here due to changes in # ipywidges==6.4.0 see # https://github.com/ipython/ipykernel/issues/771 inf = 1E20 orbit_controls = tjs.OrbitControls( controlling=camera, maxAzimuthAngle=inf, maxDistance=inf, maxZoom=inf, minAzimuthAngle=-inf, ) renderer = tjs.Renderer( camera=camera, scene=scene, alpha=True, clearOpacity=0, controls=[orbit_controls], width=width, height=height, antialias=pv_renderer.GetUseFXAA(), ) if pv_renderer.has_border: bdr_color = color_to_hex(pv_renderer.border_color) renderer.layout.border = f'solid {pv_renderer.border_width}px {bdr_color}' # for now, we can't dynamically size the render windows. If # unset, the renderer widget will attempt to resize and the # threejs renderer will not resize. # renderer.layout.width = f'{width}px' # renderer.layout.height = f'{height}px' return renderer
def render_scene(scene: Scene): renderer = three.Renderer( camera=scene.camera, background="white", background_opacity=1, scene=scene.scene, controls=[three.OrbitControls(controlling=scene.camera)], width=984, height=984, ) return display(renderer)
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 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 render(objects=None, camera=None, background="#DDDDDD", enableRotate=True, width=0, height=0): if isinstance(objects, list): children = objects + [camera] else: try: _ = iter(objects) children = list(objects) + [camera] except TypeError: children = [objects, camera] scene = p3.Scene(children=children, background=background) controller = p3.OrbitControls(controlling=camera, enableRotate=enableRotate) # Render the scene into a widget renderer = p3.Renderer(camera=camera, scene=scene, controls=[controller], width=width, height=height) 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])
import ipywidgets as widgets import numpy as np import pythreejs pn.sizing_mode = "stretch_width" FONTAWESOME_LINK = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.css" pn.config.css_files.append(FONTAWESOME_LINK) N = 1000 x, y, z = np.random.normal(0, 1, (3, N)) fig = ipv.figure(width=400, height=400) scatter = ipv.scatter(x, y, z, color="#4464ad") control = pythreejs.OrbitControls(controlling=fig.camera) fig.controls = control control.autoRotate = False fig.render_continuous = True # the controls does not update itself # if we toggle this setting, ipyvolume will update the controls toggle_rotate = widgets.ToggleButton(description="Rotate") widgets.jslink((control, "autoRotate"), (toggle_rotate, "value")) pn.Column( pn.pane.Markdown("## Panel meets IPyVolume"), pn.panel(toggle_rotate), pn.panel(fig, margin=50), width=800, ).servable()
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 __init__(self, cmap=None, norm=None, figsize=None, unit=None, log=None, nan_color=None, masks=None, pixel_size=None, tick_size=None, background=None, show_outline=True, extend=None, xlabel=None, ylabel=None, zlabel=None): if figsize is None: figsize = (config.plot.width, config.plot.height) # Figure toolbar self.toolbar = PlotToolbar(ndim=3) # Prepare colormaps self.cmap = cmap self.cmap.set_bad(color=nan_color) self.scalar_map = cm.ScalarMappable(norm=norm, cmap=self.cmap) self.masks_scalar_map = None if len(masks) > 0: self.masks_cmap = masks["cmap"] self.masks_cmap.set_bad(color=nan_color) self.masks_scalar_map = cm.ScalarMappable(norm=norm, cmap=self.masks_cmap) self.axlabels = {"x": xlabel, "y": ylabel, "z": zlabel} self.positions = None self.pixel_size = pixel_size self.tick_size = tick_size self.show_outline = show_outline self.unit = unit # Create the colorbar image self.cbar_image = ipw.Image() self.cbar_fig, self.cbar = self._create_colorbar(figsize, extend) # Create the point cloud material with pythreejs self.points_material = self._create_points_material() self.points_geometry = None self.point_cloud = None self.outline = None self.axticks = None self.camera_reset = {} # Define camera self.camera = p3.PerspectiveCamera(position=[0, 0, 0], aspect=config.plot.width / config.plot.height) # Add red/green/blue axes helper self.axes_3d = p3.AxesHelper() # Create the pythreejs scene self.scene = p3.Scene(children=[self.camera, self.axes_3d], background=background) # Add camera controller self.controls = p3.OrbitControls(controlling=self.camera) # Render the scene into a widget self.renderer = p3.Renderer(camera=self.camera, scene=self.scene, controls=[self.controls], width=figsize[0], height=figsize[1])
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 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 _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 plot(self, backend="pythreejs", width=800, height=500, background="black", mesh=False, use_as_color=["red", "green", "blue"], cmap="hsv", return_scene=False, output_name="pyntcloud_plot", polylines={}): """Visualize a PyntCloud using different backends. Parameters ---------- backend: {"pythreejs", "threejs"}, optional Default: "pythreejs" Used to select one of the available libraries for plotting. width: int, optional Default: 800 height: int, optional Default: 500 background: str, optional Default: "black" Used to select the default color of the background. In some backends, i.e "pythreejs" the background can be dynamically changed. use_as_color: str or ["red", "green", "blue"], optional Default: ["red", "green", "blue"] Indicates which scalar fields will be used to colorize the rendered point cloud. cmap: str, optional Default: "hsv" Color map that will be used to convert a single scalar field into rgb. Check matplotlib cmaps. return_scene: bool, optional Default: False. Used with "pythreejs" backend in order to return the pythreejs.Scene object polylines: dict, optional Default {}. Mapping hexadecimal colors to a list of list(len(3)) representing the points of the polyline. Example: polylines={ "0xFFFFFF": [[0, 0, 0], [0, 0, 1]], "0xFF00FF": [[1, 0, 0], [1, 0, 1], [1, 1, 1]] } Returns ------- pythreejs.Scene if return_scene else None """ try: colors = self.points[use_as_color].values except KeyError: colors = None if use_as_color != ["red", "green", "blue"] and colors is not None: import matplotlib.pyplot as plt s_m = plt.cm.ScalarMappable(cmap=cmap) colors = s_m.to_rgba(colors)[:, :-1] * 255 elif colors is None: # default color orange colors = np.repeat([[255, 125, 0]], self.xyz.shape[0], axis=0) colors = colors.astype(np.uint8) ptp = self.xyz.ptp() if backend == "pythreejs": import ipywidgets import pythreejs from IPython.display import display if mesh: raise NotImplementedError( "Plotting mesh geometry with pythreejs backend is not supported yet." ) if polylines: raise NotImplementedError( "Plotting polylines with pythreejs backend is not supported yet." ) points_geometry = pythreejs.BufferGeometry(attributes=dict( position=pythreejs.BufferAttribute(self.xyz, normalized=False), color=pythreejs.BufferAttribute(list(map(tuple, colors))))) points_material = pythreejs.PointsMaterial( vertexColors='VertexColors') points = pythreejs.Points(geometry=points_geometry, material=points_material, position=tuple(self.centroid)) camera = pythreejs.PerspectiveCamera( fov=90, aspect=width / height, position=tuple( self.centroid + [0, abs(self.xyz.max(0)[1]), abs(self.xyz.max(0)[2]) * 2]), up=[0, 0, 1]) orbit_control = pythreejs.OrbitControls(controlling=camera) orbit_control.target = tuple(self.centroid) camera.lookAt(tuple(self.centroid)) scene = pythreejs.Scene(children=[points, camera], background=background) renderer = pythreejs.Renderer(scene=scene, camera=camera, controls=[orbit_control], width=width, height=height) display(renderer) size = ipywidgets.FloatSlider(min=0., max=(ptp / 100), step=(ptp / 1000)) ipywidgets.jslink((size, 'value'), (points_material, 'size')) color = ipywidgets.ColorPicker() ipywidgets.jslink((color, 'value'), (scene, 'background')) display( ipywidgets.HBox(children=[ ipywidgets.Label('Background color:'), color, ipywidgets.Label('Point size:'), size ])) return scene if return_scene else None elif backend == "threejs": points = pd.DataFrame(self.xyz, columns=["x", "y", "z"]) for n, i in enumerate(["red", "green", "blue"]): points[i] = colors[:, n] new_PyntCloud = PyntCloud(points) if mesh and self.mesh is not None: new_PyntCloud.mesh = self.mesh[["v1", "v2", "v3"]] return plot_PyntCloud(new_PyntCloud, IFrame_shape=(width, height), point_size=ptp / 100, point_opacity=0.9, output_name=output_name, polylines=polylines) else: raise NotImplementedError( "{} backend is not supported".format(backend))
def get_orbit_controls(camera, centroid): import pythreejs orbit_controls = pythreejs.OrbitControls(controlling=camera) orbit_controls.target = tuple(centroid) return orbit_controls
def __initialize_controls(self): controls = three.OrbitControls(controlling=self.camera) controls.target = tuple( np.mean([drawable.center for drawable in self.drawables], axis=0)) return controls
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 create_jsrenderer(scene, height=400, width=400, background='gray', orthographic=False, camera_position=(0, 0, -10), view=(10, -10, -10, 10), fov=50): """ Properties ---------- orthographic : bool use orthographic camera (True) or perspective (False) camera_position : tuple position of camera in scene view : tuple view extents: (top, bottom, left, right) (orthographic only) fov : float camera field of view (perspective only) Returns ------- camera : pythreejs.Camera renderer : pythreejs.Renderer Examples -------- >>> import pythreejs as js >>> scene = js.Scene(children=[js.AmbientLight(color='#777777')]) >>> camera, renderer = create_jsrenderer(scene,200,200, 'gray', (1,-1,-1,1)) >>> type(renderer) <class 'pythreejs.pythreejs.Renderer'> >>> type(camera) <class 'pythreejs.pythreejs.OrthographicCamera'> """ if orthographic: top, bottom, left, right = view camera = js.OrthographicCamera(position=camera_position, up=[0, 0, 1], top=top, bottom=bottom, left=left, right=right, near=.1, far=2000) else: camera = js.PerspectiveCamera(position=camera_position, up=[0, 0, 1], far=2000, near=.1, fov=fov, aspect=width / float(height)) camera.children = [ js.DirectionalLight(color='white', position=[3, 5, 1], intensity=0.5) ] control = js.OrbitControls(controlling=camera) renderer = js.Renderer(camera=camera, background=background, background_opacity=.1, height=str(height), width=str(width), scene=scene, controls=[control]) return camera, renderer