Exemplo n.º 1
0
    def render(self, _interactive=True):
        """ """
        mv = Plotter(
            N=2,
            axes=4 if brainrender.SHOW_AXES else 0,
            size="full" if brainrender.WHOLE_SCREEN else "auto",
            pos=brainrender.WINDOW_POS,
            bg=brainrender.BACKGROUND_COLOR,
            sharecam=True,
        )

        actors = []
        for scene in self.scenes:
            scene.apply_render_style()
            actors.append(scene.actors)
            mv.add(scene.actors)

        mv.show(
            actors[0],
            at=0,
            zoom=1.15,
            axes=4 if brainrender.SHOW_AXES else 0,
            roll=180,
            interactive=False,
        )
        mv.show(actors[1], at=1, interactive=False)

        if _interactive:
            interactive()
Exemplo n.º 2
0
    def export_for_web(self, filepath="brexport.html"):
        """
            This function is used to export a brainrender scene
            for hosting it online. It saves an html file that can
            be opened in a web browser to show an interactive brainrender scene
        """
        if not filepath.endswith(".html"):
            raise ValueError("Filepath should point to a .html file")

        # prepare settings
        vedosettings.notebookBackend = "k3d"

        # Create new plotter and save to file
        plt = Plotter()
        plt.add(self.actors)
        plt = plt.show(interactive=False)
        plt.camera[-2] = -1

        if self.verbose:
            print(
                "Ready for exporting. Exporting scenes with many actors might require a few minutes"
            )
        with open(filepath, "w") as fp:
            fp.write(plt.get_snapshot())

        if self.verbose:
            print(
                f"The brainrender scene has been exported for web. The results are saved at {filepath}"
            )

        # Reset settings
        vedosettings.notebookBackend = None
        self.jupyter = False
Exemplo n.º 3
0
    def render(self, _interactive=True, **kwargs):
        """

        :param _interactive:  (Default value = True)
        :param **kwargs:

        """
        camera = kwargs.pop("camera", None)

        for scene in self.scenes:
            scene.apply_render_style()

            if camera is None:
                if scene.atlas.default_camera is None:
                    scene_camera = brainrender.CAMERA
                else:
                    scene_camera = scene.atlas.default_camera
            else:
                if camera:
                    scene_camera = camera
                else:
                    scene_camera = None
            if scene_camera is not None:
                set_camera(scene, scene_camera)

        mv = Plotter(
            N=self.N,
            axes=4 if brainrender.SHOW_AXES else 0,
            size="full" if brainrender.WHOLE_SCREEN else "auto",
            sharecam=True,
            bg=brainrender.BACKGROUND_COLOR,
        )

        actors = []
        for i, scene in enumerate(self.scenes):
            scene.apply_render_style()
            actors.append(scene.actors)
            mv.add(scene.actors)

        for i, scene.actors in enumerate(actors):
            mv.show(scene.actors, at=i, interactive=False)

        print("Rendering complete")
        if _interactive:
            interactive()
Exemplo n.º 4
0
    def export(self, savepath):
        """
        Exports the scene to a .html
        file for online renderings.

        :param savepath: str, Path to a .html file to save the export
        """
        logger.debug(f"Exporting scene to {savepath}")
        _backend = self.backend

        if not self.is_rendered:
            self.render(interactive=False)

        path = Path(savepath)
        if path.suffix != ".html":
            raise ValueError("Savepath should point to a .html file")

        # prepare settings
        vsettings.notebookBackend = "k3d"

        # Create new plotter and save to file
        plt = Plotter()
        plt.add(self.renderables)
        plt = plt.show(interactive=False)
        plt.camera[-2] = -1

        with open(path, "w") as fp:
            fp.write(plt.get_snapshot())

        print(
            f"The brainrender scene has been exported for web. The results are saved at {path}"
        )

        # Reset settings
        vsettings.notebookBackend = None
        self.backend = _backend

        return str(path)
Exemplo n.º 5
0
# Create the initial positions and velocitites (0,0) of the bobs
bob_x = [0]
bob_y = [0]
x_dot = np.zeros(N + 1)  # velocities
y_dot = np.zeros(N + 1)

for k in range(1, N + 1):
    alpha = np.pi / 5 * k / 10
    bob_x.append(bob_x[k - 1] + np.cos(alpha) + np.random.normal(0, 0.1))
    bob_y.append(bob_y[k - 1] + np.sin(alpha) + np.random.normal(0, 0.1))

# Create the bobs
vp = Plotter(title="Multiple Pendulum", axes=0, interactive=0)
vp += Box(pos=(0, -5, 0), length=12, width=12, height=0.7, c="k").wireframe(1)
bob = [vp.add(Sphere(pos=(bob_x[0], bob_y[0], 0), r=R / 2, c="gray"))]
for k in range(1, N + 1):
    bob.append(
        vp.add(Cylinder(pos=(bob_x[k], bob_y[k], 0), r=R, height=0.3, c=k)))

# Create the springs out of N links
link = [None] * N
for k in range(N):
    p0 = bob[k].pos()
    p1 = bob[k + 1].pos()
    link[k] = vp.add(Spring(p0, p1, thickness=0.015, r=R / 3, c="gray"))

# Create some auxiliary variables
x_dot_m = np.zeros(N + 1)
y_dot_m = np.zeros(N + 1)
dij = np.zeros(N + 1)  # array with distances to previous bob
Exemplo n.º 6
0
class Render:
    is_rendered = False
    plotter = None

    axes_names = ("AP", "DV", "LR")
    axes_lookup = {"x": "AP", "y": "DV", "z": "LR"}
    axes_indices = {"AP": 0, "DV": 1, "LR": 2}

    def __init__(self):
        """
        Backend for Scene, handles all rendering and exporting
        related tasks.
        """
        self._get_plotter()

    def _get_plotter(self):
        """
        Make a vedo plotter with
        fancy axes and all
        """
        self.plotter = Plotter(
            axes=self._make_axes() if settings.SHOW_AXES else None,
            pos=(0, 0),
            title="brainrender",
            bg=settings.BACKGROUND_COLOR,
            offscreen=settings.OFFSCREEN,
            size="full" if settings.WHOLE_SCREEN else "auto",
        )

        self.plotter.keyPressFunction = self.keypress

    def _make_axes(self):
        """
        Returns a dictionary with axes
        parameters for the vedo plotter
        """
        ax_idx = self.atlas.space.axes_order.index("frontal")

        # make acustom axes dict
        atlas_shape = np.array(self.atlas.metadata["shape"]) * np.array(
            self.atlas.metadata["resolution"]
        )
        z_range = np.array([-atlas_shape[2], 0])
        z_ticks = [
            (-v, str(np.abs(v).astype(np.int32)))
            for v in np.linspace(
                0,
                atlas_shape[ax_idx],
                10,
            )
        ]

        if self.atlas.atlas_name == "allen_human_500um":
            z_range = None
            z_ticks = None
            logger.debug(
                "RENDER: manually forcing axes size for human atlas, atlas needs fixing"
            )

        # make custom axes dict
        axes = dict(
            axesLineWidth=3,
            tipSize=0,
            xtitle="AP (μm)",
            ytitle="DV (μm)",
            ztitle="LR (μm)",
            textScale=0.8,
            xTitleRotation=180,
            zrange=z_range,
            zValuesAndLabels=z_ticks,
            xyGrid=False,
            yzGrid=False,
            zxGrid=False,
            xUseBounds=True,
            yUseBounds=True,
            zUseBounds=True,
            xLabelRotation=180,
            yLabelRotation=180,
            zLabelRotation=90,
        )

        return axes

    def _prepare_actor(self, actor):
        """
        When an actor is first rendered, a transform matrix
        is applied to its points to correct axes orientation
        mismatches: https://github.com/brainglobe/bg-atlasapi/issues/73

        Once an actor is 'corrected' it spawns labels and silhouettes as needed
        """

        # Flip every actor's orientation
        if not actor._is_transformed:
            try:
                actor._mesh = actor.mesh.clone()
                actor._mesh.applyTransform(mtx)
            except AttributeError:  # some types of actors dont trasform
                actor._is_transformed = True
            else:
                try:
                    actor.mesh.reverse()
                except AttributeError:  # Volumes don't have reverse
                    pass
                actor._is_transformed = True

        # Add silhouette and labels
        if actor._needs_silhouette and not self.backend:
            self.plotter.add(actor.make_silhouette().mesh)

        if actor._needs_label and not self.backend:
            self.labels.extend(actor.make_label(self.atlas))

    def _apply_style(self):
        """
        Sets the rendering style for each mesh
        """
        for actor in self.clean_actors:
            if settings.SHADER_STYLE != "cartoon":
                style = settings.SHADER_STYLE
            else:
                if self.backend:  # notebook backend
                    print(
                        'Shader style "cartoon" cannot be used in a notebook'
                    )
                style = "off"

            try:
                actor.mesh.reverse()  # flip normals
                actor.mesh.lighting(style=style)

                actor._mesh.reverse()
                actor._mesh.lighting(style=style)
            except AttributeError:
                pass

    def render(
        self,
        interactive=None,
        camera=None,
        zoom=None,
        update_camera=True,
        **kwargs,
    ):
        """
        Renders the scene.

        :param interactive: bool. If note settings.INTERACTIVE is used.
            If true the program's execution is stopped and users
            can interact with scene.
        :param camera: str, dict. If none the default camera is used.
            Pass a valid camera input to specify the camera position when
            the scene is rendered.
        :param zoom: float, if None atlas default is used
        :param update_camera: bool, if False the camera is not changed
        :param kwargs: additional arguments to pass to self.plotter.show
        """
        logger.debug(
            f"Rendering scene. Interactive: {interactive}, camera: {camera}, zoom: {zoom}"
        )
        # get zoom
        zoom = zoom or self.atlas.zoom

        # get vedo plotter
        if self.plotter is None:
            self._get_plotter()

        # Get camera
        camera = camera or settings.DEFAULT_CAMERA
        if isinstance(camera, str):
            camera = get_camera(camera)
        else:
            camera = check_camera_param(camera)

        if camera["focalPoint"] is None:
            camera["focalPoint"] = self.root._mesh.centerOfMass()

        if not self.backend and camera is not None:
            camera = set_camera(self, camera)

        # Apply axes correction
        for actor in self.clean_actors:
            if not actor._is_transformed:
                self._prepare_actor(actor)
                self.plotter.add(actor.mesh)

            if actor._needs_silhouette or actor._needs_label:
                self._prepare_actor(actor)

        # add labels to the scene
        for label in self.labels:
            if label._is_added:
                continue
            else:
                label._mesh = label.mesh.clone()
                self._prepare_actor(label)
                self.plotter.add(label._mesh)
                label._is_added = True

        # Apply style
        self._apply_style()

        if self.inset and not self.is_rendered:
            self._get_inset()

        # render
        self.is_rendered = True
        if not self.backend:  # not running in a python script
            if interactive is None:
                interactive = settings.INTERACTIVE

            for txt in self.labels:
                txt.followCamera(self.plotter.camera)

            self.plotter.show(
                interactive=interactive,
                zoom=zoom,
                bg=settings.BACKGROUND_COLOR,
                offscreen=settings.OFFSCREEN,
                camera=camera.copy() if update_camera else None,
                interactorStyle=0,
                rate=40,
            )
        elif self.backend == "k3d":  # pragma: no cover
            # Remove silhouettes
            self.remove(*self.get_actors(br_class="silhouette"))
            print(
                f"[{teal}]Your scene is ready for rendering, use:\n",
                Syntax("from vedo import show", lexer_name="python"),
                Syntax("vedo.show(*scene.renderables)", lexer_name="python"),
                sep="\n",
            )
        else:  # pragma: no cover
            print(
                f"[{teal}]Your scene is ready for rendering, use:\n",
                Syntax("from itkwidgets import view", lexer_name="python"),
                Syntax(
                    "view(scene.plotter.show(*scene.renderables))",
                    lexer_name="python",
                ),
                sep="\n",
            )

    def close(self):
        closePlotter()

    def export(self, savepath):
        """
        Exports the scene to a .html
        file for online renderings.

        :param savepath: str, Path to a .html file to save the export
        """
        logger.debug(f"Exporting scene to {savepath}")
        _backend = self.backend

        if not self.is_rendered:
            self.render(interactive=False)

        path = Path(savepath)
        if path.suffix != ".html":
            raise ValueError("Savepath should point to a .html file")

        # prepare settings
        vsettings.notebookBackend = "k3d"

        # Create new plotter and save to file
        plt = Plotter()
        plt.add(self.renderables)
        plt = plt.show(interactive=False)
        plt.camera[-2] = -1

        with open(path, "w") as fp:
            fp.write(plt.get_snapshot())

        print(
            f"The brainrender scene has been exported for web. The results are saved at {path}"
        )

        # Reset settings
        vsettings.notebookBackend = None
        self.backend = _backend

        return str(path)

    def screenshot(self, name=None, scale=None):
        """
        Takes a screenshot of the current view
        and save it to file.
        Screenshots are saved in `screenshots_folder`
        (see Scene)

        :param name: str, name of png file
        :param scale: float, >1 for higher resolution
        """

        if not self.is_rendered:
            self.render(interactive=False)

        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        name = name or f"brainrender_screenshot_{timestamp}"
        if ".png" not in name:
            name += ".png"

        scale = scale or settings.SCREENSHOT_SCALE

        print(f"\nSaving new screenshot at {name}\n")

        savepath = str(self.screenshots_folder / name)
        logger.debug(f"Saving scene at {savepath}")
        self.plotter.screenshot(filename=savepath, scale=scale)
        return savepath

    def _print_camera(self):
        pms = get_camera_params(scene=self)

        focal = pms.pop("focalPoint", None)
        dst = pms.pop("distance", None)

        names = [
            f"[green bold]     '{k}'[/green bold]: [{amber}]{v},"
            for k, v in pms.items()
        ]
        print(
            f"[{deep_purple_light}]Camera parameters:",
            f"[{orange}]    {{",
            *names,
            f"[{orange}]   }}",
            f"[{deep_purple_light}]Additional, (optional) parameters:",
            f"[green bold]     'focalPoint'[/green bold]: [{amber}]{focal},",
            f"[green bold]     'distance'[/green bold]: [{amber}]{dst},",
            sep="\n",
        )

    def keypress(self, key):  # pragma: no cover
        """
        Hanles key presses for interactive view
        -s: take's a screenshot
        -q: closes the window
        -c: prints the current camera parameters
        """
        if key == "s":
            self.screenshot()

        elif key == "q" or key == "Esc":
            self.close()

        elif key == "c":
            self._print_camera()
Exemplo n.º 7
0
# Create the initial positions and velocitites (0,0) of the bobs
bob_x = [0]
bob_y = [0]
x_dot = np.zeros(N+1)  # velocities
y_dot = np.zeros(N+1)

for k in range(1, N + 1):
    alpha = np.pi / 5 * k / 10
    bob_x.append(bob_x[k - 1] + np.cos(alpha) + np.random.normal(0, 0.1))
    bob_y.append(bob_y[k - 1] + np.sin(alpha) + np.random.normal(0, 0.1))

# Create the bobs
vp = Plotter(title="Multiple Pendulum", axes=0, interactive=0, bg2='ly')
vp += Box(pos=(0, -5, 0), length=12, width=12, height=0.7, c="k").wireframe(1)
bob = [vp.add(Sphere(pos=(bob_x[0], bob_y[0], 0), r=R / 2, c="gray"))]
for k in range(1, N + 1):
    c = Cylinder(pos=(bob_x[k], bob_y[k], 0), r=R, height=0.3, c=k)
    vp += c
    bob.append(c)

# Create the springs out of N links
link = [None] * N
for k in range(N):
    p0 = bob[k].pos()
    p1 = bob[k + 1].pos()
    link[k] = Spring(p0, p1, thickness=0.015, r=R / 3, c="gray")
    vp += link[k]

# Create some auxiliary variables
x_dot_m = np.zeros(N+1)
Exemplo n.º 8
0
class Morpher:
    def __init__(self, mesh1, mesh2, n):  ############################### init

        self.n = n  # desired nr. of intermediate shapes
        self.mode = '2d'
        self.mesh1 = mesh1
        self.mesh2 = mesh2
        self.merged_meshes = merge(mesh1, mesh2)
        self.mesh1.lw(4).c('grey2').pickable(False)
        self.mesh2.lw(4).c('grey1').pickable(False)

        self.arrow_starts = []
        self.arrow_stops = []
        self.dottedln = None
        self.toggle = False
        self.instructions = (
            "Click to add arrows interactively on the left panel\n"
            "right-click to remove last arrow. Then press:\n"
            "- m to morph the plane\n"
            "- c to clear\n"
            "- g to generate interpolation")
        self.msg1 = Text2D(self.instructions,
                           pos='top-left',
                           font="VictorMono",
                           bg='g2',
                           alpha=0.6)
        self.msg2 = Text2D('[output will show here]',
                           pos='top-left',
                           font="VictorMono")

        sz = self.merged_meshes.diagonalSize()
        self.plane1 = Grid(sx=sz, sy=sz, resx=50,
                           resy=50).pos(self.merged_meshes.centerOfMass())
        self.plane1.wireframe(False).alpha(1).lineWidth(0.1).c('white').lc(
            'grey5')
        self.plane2 = self.plane1.clone().pickable(False)

        self.plotter = Plotter(N=2, bg='light blue', size=(2000, 1000))
        self.plotter.addCallback('left click', self.onleftclick)
        self.plotter.addCallback('right click', self.onrightclick)
        self.plotter.addCallback('key press', self.onkeypress)

    def start(
            self):  ################################################ show stuff
        paxes = Axes(self.plane1, xyGrid=0, textScale=0.6)
        self.plotter.show(self.plane1,
                          paxes,
                          self.msg1,
                          self.mesh1,
                          self.mesh2,
                          at=0)
        self.plotter.show(self.plane2, self.msg2, at=1, mode='image')
        if len(self.arrow_starts):
            self.draw(True)
            self.draw(False)
            self.msg1.text(self.instructions)
        self.plotter.show(interactive=True, zoom=1.3).close()

    def draw(self,
             toggle=None):  #################################### update scene
        if toggle is None:
            toggle = self.toggle
        if toggle:
            self.msg1.text(
                "Choose start point or press:\nm to morph the shapes\ng to interpolate"
            )
            self.plotter.remove("displacementArrows", at=0)
            if not len(self.arrow_starts): return
            arrows = Arrows2D(self.arrow_starts, self.arrow_stops, c='red4')
            arrows.name = "displacementArrows"
            self.plotter.add(arrows, at=0)
        else:
            self.msg1.text("Click to choose an end point")
            self.plotter.remove("displacementPoints", at=0)
            points = Points(self.arrow_starts, r=15, c='green3', alpha=0.5)
            points.name = "displacementPoints"
            self.plotter.add(points, at=0)

    def onleftclick(
            self,
            evt):  ############################################ add points
        msh = evt.actor
        if not msh or msh.name != "Grid": return
        pt = self.merged_meshes.closestPoint(
            evt.picked3d)  # get the closest pt on the line
        self.arrow_stops.append(
            pt) if self.toggle else self.arrow_starts.append(pt)
        self.draw()
        self.toggle = not self.toggle

    def onrightclick(
            self, evt):  ######################################## remove points
        if not self.arrow_starts: return
        self.arrow_starts.pop()
        if not self.toggle:
            self.arrow_stops.pop()
        self.plotter.clear(at=0).addRendererFrame()
        self.plotter.add([self.plane1, self.msg1, self.mesh1, self.mesh2],
                         at=0,
                         render=False)
        self.draw(False)
        self.draw(True)

    def onkeypress(
            self,
            evt):  ###################################### MORPH & GENERATE
        if evt.keyPressed == 'm':  ##--------- morph mesh1 based on the existing arrows
            if len(self.arrow_starts) != len(self.arrow_stops):
                printc("You must select your end point first!", c='y')
                return

            output = [self.mesh1.clone().c('grey4'), self.mesh2, self.msg2]
            warped_plane = self.plane1.clone().pickable(False)
            warped_plane.warp(self.arrow_starts,
                              self.arrow_stops,
                              mode=self.mode)
            output.append(warped_plane +
                          Axes(warped_plane, xyGrid=0, textScale=0.6))

            mw = self.mesh1.clone().applyTransform(
                warped_plane.transform).c('red4')
            output.append(mw)

            T_inv = warped_plane.transform.GetInverse()
            a = Points(self.arrow_starts,
                       r=10).applyTransform(warped_plane.transform)
            b = Points(self.arrow_stops,
                       r=10).applyTransform(warped_plane.transform)
            self.dottedln = Lines(
                a, b, res=self.n).applyTransform(T_inv).pointSize(5)
            output.append(self.dottedln)

            self.msg1.text(self.instructions)
            self.msg2.text("Morphed output:")
            self.plotter.clear(at=1).addRendererFrame().add(output, at=1)

        elif evt.keyPressed == 'g':  ##------- generate intermediate shapes
            if not self.dottedln:
                return
            intermediates = []
            allpts = self.dottedln.points()
            allpts = allpts.reshape(len(self.arrow_starts), self.n + 1, 3)
            for i in range(self.n + 1):
                pi = allpts[:, i, :]
                m_nterp = self.mesh1.clone().warp(self.arrow_starts,
                                                  pi,
                                                  mode=self.mode).c('b3').lw(1)
                intermediates.append(m_nterp)
            self.msg2.text("Morphed output + Interpolation:")
            self.plotter.add(intermediates, at=1)
            self.dottedln = None

        elif evt.keyPressed == 'c':  ##------- clear all
            self.arrow_starts = []
            self.arrow_stops = []
            self.toggle = False
            self.dottedln = None
            self.msg1.text(self.instructions)
            self.msg2.text("[output will show here]")
            self.plotter.clear(at=0).clear(at=1).addRendererFrame()
            self.plotter.add([self.plane1, self.msg1, self.mesh1, self.mesh2],
                             at=0)
            self.plotter.add([self.plane2, self.msg2], at=1)