示例#1
0
    def __init__(self, trimesh, uvs, width=512, height=512, duration=5, textureMap = None):
        self.viewer = TriMeshViewer(trimesh, width, height, textureMap)

        flatPosArray = None
        if (uvs.shape[1] == 2): flatPosArray = np.array(np.pad(uvs, [(0, 0), (0, 1)], 'constant'), dtype=np.float32)
        else:                   flatPosArray = np.array(uvs, dtype=np.float32)
        flatPos     = pythreejs.BufferAttribute(array=flatPosArray, normalized=False)
        flatNormals = pythreejs.BufferAttribute(array=np.repeat(np.array([[0, 0, 1]], dtype=np.float32), uvs.shape[0], axis=0), normalized=False)

        geom = self.viewer.currMesh.geometry
        mat  = self.viewer.currMesh.material
        geom.morphAttributes = {'position': [flatPos,], 'normal': [flatNormals,]}

        # Both of these material settings are needed or else our target positions/normals are ignored!
        mat.morphTargets, mat.morphNormals = True, True

        flatteningMesh = pythreejs.Mesh(geometry=geom, material=mat)

        amplitude = np.linspace(-1, 1, 20, dtype=np.float32)
        times = (np.arcsin(amplitude) / np.pi + 0.5) * duration
        blendWeights = 0.5 * (amplitude + 1)
        track = pythreejs.NumberKeyframeTrack('name=.morphTargetInfluences[0]', times = times, values = blendWeights, interpolation='InterpolateSmooth')

        self.action = pythreejs.AnimationAction(pythreejs.AnimationMixer(flatteningMesh),
                                                pythreejs.AnimationClip(tracks=[track]),
                                                flatteningMesh, loop='LoopPingPong')

        self.viewer.meshes.children = [flatteningMesh]

        self.layout = ipywidgets.VBox()
        self.layout.children = [self.viewer.renderer, self.action]
示例#2
0
    def update_keyframes(self):
        with self.output:
            fp = lambda x: "%f,%f,%f" % x
            fq = lambda x: "%f,%f,%f,%f" % x
            options = [(self.format_keyframe(t, p, q), i)
                       for i, (t, p, q) in enumerate(
                           zip(self.times, self.positions, self.quaternions))]
            self.select_keyframes.options = options
            self.position_track = pythreejs.VectorKeyframeTrack(
                name='.position',
                times=self.times,
                values=self.positions,
                interpolation=self.select_interpolation.value)
            self.rotation_track = pythreejs.QuaternionKeyframeTrack(
                name='.quaternion',
                times=self.times,
                values=self.quaternions,
                interpolation=self.select_interpolation.value)

            if len(self.positions):
                self.camera_clip = pythreejs.AnimationClip(
                    tracks=[self.position_track, self.rotation_track])
                self.mixer = pythreejs.AnimationMixer(self.camera)
                self.camera_action = pythreejs.AnimationAction(
                    self.mixer, self.camera_clip, self.camera)
                self.camera_action_box.children = [self.camera_action]
            else:
                self.camera_action_box.children = []
示例#3
0
    def create_group_action(self, objs, transformations, times):
        # TODO: how to start multiple animations at once
        if len(transformations) != len(times):
            raise ValueError("Pass equal amount of transformations and times")
        x, y, z, w = objs[0].quaternion
        Tinit = Rotation.from_quaternion([w, x, y, z]) * Translation(
            objs[0].position)
        positions = []
        quaternions = []
        for M in transformations:
            Sc, Sh, R, T, P = (M * Tinit).decomposed()
            positions.append(list(T.translation))
            quaternions.append(R.quaternion.xyzw)
        position_track = p3js.VectorKeyframeTrack(name='.position',
                                                  times=times,
                                                  values=list(
                                                      flatten(positions)))
        rotation_track = p3js.QuaternionKeyframeTrack(
            name='.quaternion', times=times, values=list(flatten(quaternions)))

        animation_group = p3js.AnimationObjectGroup()
        animation_group.exec_three_obj_method('add',
                                              objs[0])  # this is not working

        obj_clip = p3js.AnimationClip(tracks=[position_track, rotation_track])
        mixer = p3js.AnimationMixer(animation_group)
        obj_action = p3js.AnimationAction(mixer, obj_clip, animation_group)
        return obj_action
示例#4
0
文件: scene.py 项目: zizai/pydy
    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])
示例#5
0
 def create_action(self, obj, transformations, times):
     if len(transformations) != len(times):
         raise ValueError("Pass equal amount of transformations and times")
     x, y, z, w = obj.quaternion
     Tinit = Rotation.from_quaternion([w, x, y, z]) * Translation(obj.position)
     positions = []
     quaternions = []
     for M in transformations:
         Sc, Sh, R, T, P = (M * Tinit).decompose()
         positions.append(list(T.translation))
         quaternions.append(R.quaternion.xyzw)
     position_track = p3js.VectorKeyframeTrack(name='.position', times=times, values=list(flatten(positions)))
     rotation_track = p3js.QuaternionKeyframeTrack(name='.quaternion', times=times, values=list(flatten(quaternions)))
     obj_clip = p3js.AnimationClip(tracks=[position_track, rotation_track])
     obj_action = p3js.AnimationAction(p3js.AnimationMixer(obj), obj_clip, obj)
     return obj_action
示例#6
0
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)
示例#7
0
    def selectMode(self, modeNum, play = True):
        # Avoid flicker/partial redraws during updates
        self.renderer.pauseRendering()

        modeVector = None
        if (len(self.modeDoF.shape) == 1):
            if (modeNum != 0): raise Exception('modeNum should be zero; only a single mode was given.')
            modeVector = self.modeDoF
        else:
            modeVector = self.modeDoF[:, modeNum]

        # Rescale the modal shape so that the velocity it induces has
        # magnitude "self.amplitude" (relative to the structure's characteristic length scale)
        normalizedOffset = None
        if self.normalize and ("characteristicLength" in self.meshMethods) and ("approxLinfVelocity" in self.meshMethods):
            paramVelocity = self.mesh.approxLinfVelocity(modeVector)
            normalizedOffset = modeVector * (self.amplitude * self.mesh.characteristicLength() / paramVelocity)
        else:
            normalizedOffset = modeVector * self.amplitude

        morphTargetPositionsRaw = []
        morphTargetNormalsRaw = []
        modulations = np.linspace(-1, 1, self.numSteps, dtype=np.float32)

        # Animate the structure oscillating around its current degrees of freedom
        currVars = self.varGetter();

        for modulation in modulations:
            self.varSetter(currVars + modulation * normalizedOffset)
            pts, tris, normals = self.mesh.visualizationGeometry()
            morphTargetPositionsRaw.append(pts)
            morphTargetNormalsRaw  .append(normals)
        self.varSetter(currVars)

        if self.modeMesh is None:
            # We apparently need to create a new mesh to add our morph targets
            # (instead of reusing the viewer's mesh object, otherwise the mesh
            # does not display).
            geom = self.currMesh.geometry
            geom.morphAttributes = {'position': tuple(map(pythreejs.BufferAttribute, morphTargetPositionsRaw)),
                                    'normal':   tuple(map(pythreejs.BufferAttribute, morphTargetNormalsRaw))}
            self.modeMesh = pythreejs.Mesh(geometry=geom, material=self.morphMaterial)
            self.meshes.remove(self.currMesh)
            self.currMesh.close()
            self.currMesh = self.modeMesh
            self.meshes.add(self.currMesh)
        else:
            # Update the exisitng morph position/normal attribute arrays
            geom = self.currMesh.geometry
            assert(len(geom.morphAttributes['position']) == self.numSteps)
            assert(len(geom.morphAttributes['normal'  ]) == self.numSteps)
            for rawArray, attrArray in zip(morphTargetPositionsRaw, geom.morphAttributes['position']):
                attrArray.array = rawArray
            for rawArray, attrArray in zip(morphTargetNormalsRaw, geom.morphAttributes['normal']):
                attrArray.array = rawArray

        t = np.arcsin(modulations) / np.pi + 0.5
        I = np.identity(self.numSteps, dtype=np.float32)
        tracks = [pythreejs.NumberKeyframeTrack(f'name=.morphTargetInfluences[{i}]', times=t, values=I[:, i].ravel(), interpolation='InterpolateSmooth') for i in range(self.numSteps)]

        # Stop the old action (if it exists) so that the new animation is not superimposed atop it
        if (self.action is None):
            self.action = pythreejs.AnimationAction(pythreejs.AnimationMixer(self.modeMesh),
                                                    pythreejs.AnimationClip(tracks=tracks), self.modeMesh, loop='LoopPingPong')

        # Currently it doesn't seem possible to animate both the wireframe and solid mesh synchronously without
        # nontrivial changes to pythreejs or three.js.
        # There are some ideas discussed in "https://github.com/jupyter-widgets/pythreejs/issues/262" but
        # I can't seem t get them to work...
        # if ((self.wireframeAction is None) and (self.wireframeMesh is not None)):
        #     self.wireframeAction = pythreejs.AnimationAction(pythreejs.AnimationMixer(self.wireframeMesh),
        #                                                      pythreejs.AnimationClip(tracks=tracks), self.wireframeMesh, loop='LoopPingPong')
        #     # self.wireframeAction.syncWith(self.action)
        #     self.wireframeAction.play()

        controls = [self.action]
        if (self.mode_selector is not None): controls.append(self.mode_selector)
        self.controls_layout.children = controls
        self.layout.children = [self.renderer, self.controls_layout]

        # Start the animation if requested
        if (play): self.action.play()

        self.renderer.resumeRendering()
示例#8
0
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