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, *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 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 _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 __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 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 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, eye_pos, obj_pos, obj, up=(0, 1, 0)): self.camera = three.PerspectiveCamera( position=tuple(eye_pos), lookAt=tuple(obj_pos), fov=30, ) self.camera.up = up self.light = three.PointLight( color="white", position=(-eye_pos[0], eye_pos[1], eye_pos[2]), ) self.obj = obj self.scene = three.Scene(children=[self.obj, self.light, self.camera])
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])
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 plot_with_pythreejs(cloud, **kwargs): if ipywidgets is None: raise ImportError( "ipywidgets is needed for plotting with pythreejs backend.") 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.") colors = get_colors(cloud, kwargs["use_as_color"], kwargs["cmap"]) ptp = cloud.xyz.ptp() children = [] widgets = [] if kwargs["mesh"]: raise NotImplementedError( "Plotting mesh geometry with pythreejs backend is not supported yet." ) if kwargs["polylines"]: lines = get_polylines_pythreejs(kwargs["polylines"]) children.extend(lines) points = get_pointcloud_pythreejs(cloud.xyz, colors) children.append(points) initial_point_size = kwargs["initial_point_size"] or ptp / 10 size = ipywidgets.FloatSlider(value=initial_point_size, min=0.0, max=initial_point_size * 10, step=initial_point_size / 100) ipywidgets.jslink((size, 'value'), (points.material, 'size')) widgets.append(ipywidgets.Label('Point size:')) widgets.append(size) if kwargs["scene"]: kwargs["scene"].children = [points] + list(kwargs["scene"].children) else: camera = get_camera_pythreejs(cloud.centroid, cloud.xyz, kwargs["width"], kwargs["height"]) children.append(camera) controls = [get_orbit_controls(camera, cloud.centroid)] scene = pythreejs.Scene(children=children) renderer = pythreejs.Renderer(scene=scene, camera=camera, controls=controls, width=kwargs["width"], height=kwargs["height"]) display(renderer) color = ipywidgets.ColorPicker() ipywidgets.jslink((color, 'value'), (scene, 'background')) widgets.append(ipywidgets.Label('Background color:')) widgets.append(color) display(ipywidgets.HBox(children=widgets)) return scene if kwargs["return_scene"] else None
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 _default_scene(self): # could be removed when https://github.com/jovyan/pythreejs/issues/176 is solved # the default for pythreejs is white, which leads the volume rendering pass to make everything white return pythreejs.Scene(background=None)
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 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 _default_scene(self): # return pythreejs.CombinedCamera(fov=46, position=(0, 0, 2), width=400, height=500) return pythreejs.Scene()
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 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 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))