Exemple #1
0
    def __init__(self, pdf_panner2d, pdf_filepath, *args, **kwargs):
        TQGraphicsNodePath.__init__(self, *args, **kwargs)

        # logical variables
        self.pdf_filepath = pdf_filepath
        self.pptos = []
        self.y_pages_distance = PDFViewer.y_pages_distance_0

        # derived
        self.pdf_panner2d = pdf_panner2d  # something like Panner2d
        self.ppr = PopplerPDFRenderer(self.pdf_filepath)

        # direct objects
        base.accept('1', self.return_to_middle_view)
        base.accept('space', self.scroll_spacebar_down)
        base.accept('shift-space', self.scroll_spacebar_up)

        base.accept('control-0', self.return_to_middle_view)

        # quad visualizing large margins for visual orientation
        self.margins_quad = Quad(thickness=10.)
        self.margins_quad.reparentTo(self)

        # plot
        self.render_pages()

        self.return_to_middle_view()
Exemple #2
0
class DraggableFrame(TQGraphicsNodePath):
    """ """
    def __init__(self, camera_gear, height=None, width=None, **kwargs):
        """ """
        height_0 = 0.5
        width_0 = 16 / 9 * height_0

        TQGraphicsNodePath.__init__(self, **kwargs)

        self.camera_gear = camera_gear
        self.drag_point = Point3d()
        self.drag_point.reparentTo(engine.tq_graphics_basics.tq_render)
        self.drag_point.setColor(Vec4(1.0, 0., 0., 1.), 1)

        self.quad_border_thickness = None
        if "quad_border_thickness" in kwargs:
            self.quad_border_thickness = kwargs.get("quad_border_thickness")
        elif self.quad_border_thickness is None:
            self.quad_border_thickness = 1.5

        self.bg_quad = Quad(thickness=self.quad_border_thickness)

        if height is None:
            height = height_0

        if width is None:
            width = width_0

        self.bg_quad.set_height(height)
        self.bg_quad.set_width(width)
        self.bg_quad.reparentTo(self)
        self.bg_quad.setPos(Vec3(0., 0., 0.))
        self.bg_quad.setColor(Vec4(1., 1., 1., 0.5), 1)

        # -------------------------------------
        self.pom = PickableObjectManager()
        self.pom.tag(self.drag_point.get_p3d_nodepath())

        self.dadom = DragAndDropObjectsManager()

        self.pt_dragger = PickableObjectDragger(self.drag_point,
                                                self.camera_gear)
        self.pt_dragger.add_on_state_change_function(
            self.move_frame_when_dragged)

        self.dadom.add_dragger(self.pt_dragger)

        self.collisionPicker = CollisionPicker(
            camera_gear, engine.tq_graphics_basics.tq_render,
            base.mouseWatcherNode, self.dadom)

        # -- add a mouse task to check for picking
        self.p3d_draw_direct_object = DirectObject.DirectObject()
        self.p3d_draw_direct_object.accept('mouse1',
                                           self.collisionPicker.onMouseTask)

    def update_logical_position_from_drag_point(self):
        new_handle_pos = self.drag_point.getPos()
        self.v0 = new_handle_pos
    def update(self):
        if self.is_done_function() == False:
            if self.x_pos_left_border is None:
                self.x_pos_left_border = -1.0

            x_pos_waiting_symbol = self.x_pos_left_border + 0.1

            x_pos_text = x_pos_waiting_symbol + 0.2

            if self.y_pos_top_box is None:
                self.y_pos_top_box = 0.

            y_pos_waiting_symbol = self.y_pos_top_box - 0.05

            y_pos_text = self.y_pos_top_box - 0.125

            x_pos_elapsed_time = x_pos_text + 0.4
            y_pos_elapsed_time = y_pos_text

            quad_thickness = 2.0

            if self.waiting_symbol is None:
                self.waiting_symbol = WaitingSymbol(self.is_done_function, Vec3(x_pos_waiting_symbol, 0., y_pos_waiting_symbol), size=0.1)
                self.waiting_symbol.reparentTo(self)
            else:
                self.waiting_symbol.set_position(Vec3(x_pos_waiting_symbol, 0., y_pos_waiting_symbol))

            if self.text is None:
                self.text = Fixed2dLabel(text=self.description, font="fonts/arial.egg", x=x_pos_text, y=y_pos_text)
                self.text.reparentTo(self)
            else:
                self.text.setPos(x_pos_text, y_pos_text)

            if self.elapsed_time_text is None:
                self.elapsed_time_text = Fixed2dLabel(text="elapsed time", font="fonts/arial.egg", x=x_pos_elapsed_time, y=y_pos_elapsed_time)
                self.elapsed_time_text.reparentTo(self)
            else:
                self.elapsed_time_text.setPos(x_pos_elapsed_time, y_pos_elapsed_time)

            elapsed_time = (time.time_ns() - self.time_initial) / 1.0e9
            self.elapsed_time_text.setText("{0:.0f} s".format(elapsed_time))

            if self.quad is None:
                self.quad = Quad(thickness=quad_thickness, TQGraphicsNodePath_creation_parent_node=engine.tq_graphics_basics.tq_aspect2d)
                self.quad.reparentTo(self)

                self.quad.set_pos_vec3(Vec3(self.x_pos_left_border, 0., self.y_pos_top_box))

                self.quad.set_height(self.height)
                self.quad.set_width(self.width)
            else:
                self.quad.set_pos_vec3(Vec3(self.x_pos_left_border, 0., self.y_pos_top_box))

            return True  # call task.cont

        return False  # call task.done
Exemple #4
0
    def __init__(self, done_function, position, size=0.2, frequency=1):
        """
        Args:
            done_function: function which controls when the waiting symbol
                           animation should be stopped (when True)
            position: Vec3 of the upper left hand corner in p3d coordinates """

        # -- logical
        self.done_function = done_function

        self.position = position
        self.size = size
        self.frequency = frequency
        self.duration = 1.

        self.a = None

        TQGraphicsNodePath.__init__(self)

        # -- supporting graphics
        self.quad = Quad(thickness=3.0,
                         TQGraphicsNodePath_creation_parent_node=engine.
                         tq_graphics_basics.tq_aspect2d)
        self.quad.reparentTo(self)

        self.quad.set_pos_vec3(self.position)
        self.quad.set_height(self.size)
        self.quad.set_width(self.size)

        self.line = Line1dSolid(thickness=3.0,
                                TQGraphicsNodePath_creation_parent_node=engine.
                                tq_graphics_basics.tq_aspect2d)
        self.line.reparentTo(self)

        self.anim_seq = Sequence()

        self.anim_seq.set_sequence_params(
            duration=self.duration,
            extraArgs=[],
            update_function=self.update,
            on_finish_function=self.restart_animation)

        self.anim_seq.start()
Exemple #5
0
class WaitingSymbol(TQGraphicsNodePath):
    """ An animation that runs as long as done_function returns False. """
    def __init__(self, done_function, position, size=0.2, frequency=1):
        """
        Args:
            done_function: function which controls when the waiting symbol
                           animation should be stopped (when True)
            position: Vec3 of the upper left hand corner in p3d coordinates """

        # -- logical
        self.done_function = done_function

        self.position = position
        self.size = size
        self.frequency = frequency
        self.duration = 1.

        self.a = None

        TQGraphicsNodePath.__init__(self)

        # -- supporting graphics
        self.quad = Quad(thickness=3.0,
                         TQGraphicsNodePath_creation_parent_node=engine.
                         tq_graphics_basics.tq_aspect2d)
        self.quad.reparentTo(self)

        self.quad.set_pos_vec3(self.position)
        self.quad.set_height(self.size)
        self.quad.set_width(self.size)

        self.line = Line1dSolid(thickness=3.0,
                                TQGraphicsNodePath_creation_parent_node=engine.
                                tq_graphics_basics.tq_aspect2d)
        self.line.reparentTo(self)

        self.anim_seq = Sequence()

        self.anim_seq.set_sequence_params(
            duration=self.duration,
            extraArgs=[],
            update_function=self.update,
            on_finish_function=self.restart_animation)

        self.anim_seq.start()

    def restart_animation(self):
        self.anim_seq.set_t(0.)
        self.anim_seq.resume()

    def _get_center(self):
        return Vec3(self.position[0] + 0.5 * self.size, 0.,
                    self.position[2] - 0.5 * self.size)

    def set_position(self, vec3_pos):
        self.position = vec3_pos
        self.update(self.a)

    def update(self, a):
        """ a is in between 0 and 1 """

        self.a = a

        if self.done_function() == True:
            self.remove()
            return
        elif self.done_function() != False:
            raise ValueError("self.done_function() should return Boolean")

        t = a * self.duration
        omega = 2 * np.pi * self.frequency
        theta = omega * t

        r = self.size * 0.4
        p1 = self._get_center()
        p2 = p1 + Vec3(r * np.cos(theta), 0., r * np.sin(theta))

        self.line.setTailPoint(p1)
        self.line.setTipPoint(p2)

        self.quad.set_pos_vec3(self.position)

    def remove(self):
        """ release all resources """
        self.anim_seq.remove()
        self.line.remove()
        self.quad.remove()
Exemple #6
0
    def __init__(
            self,
            camera_gear=None,
            update_labels_orientation=False,
            with_ticks=True,
            attach_to_space="aspect2d",  # or "render",
            attach_directly=True):
        """ """
        TQGraphicsNodePath.__init__(self)

        self.attached_p = False

        if attach_to_space == "aspect2d":
            if update_labels_orientation == True:
                print(
                    "WARNING: do not set attach_to_space to aspect2d and update_labels_orientation=True simultaneously!"
                )
                update_labels_orientation = False

        if attach_to_space == "render":
            if camera_gear is None:
                print(
                    "ERR: if attach_to_space == render, supply a camera_gear as well that is not None!"
                )
                exit(1)

        self.attach_to_space = attach_to_space  # "render" or "aspect2d"

        self.quad = None

        # if camera_gear == None:
        #     self.update_labels_orientation == False

        # if update_labels_orientation == True and camera_gear == None:
        #     print("WARNING: if update_labels_orientation == True, camera_gear cannot be None")
        #      exit(1)

        self.camera_gear = camera_gear

        self.update_labels_orientation = update_labels_orientation

        size = 0.5
        self.height = size
        self.width = size * 1.618

        # self.lines = []  # lines

        # self.linesin2dframe = None
        self.linesin2dframe = LinesIn2dFrame()
        self.linesin2dframe.reparentTo(self)

        self.with_ticks = with_ticks

        self.d = 2  # dimension of frame

        self.xmin_hard = None
        self.xmax_hard = None

        self.ymin_hard = None
        self.ymax_hard = None

        self._xmin_soft = 0.
        self._xmax_soft = 1.

        self._ymin_soft = 0.
        self._ymax_soft = 1.

        self.axes_ticks_numbers = None
        self.regenerate_axes_ticks_numbers()

        self.clipping_planes_p3d_nodepaths = []

        self.quad = Quad(thickness=1.5)
        self.quad.reparentTo(self)

        # --- Ticks -----
        print("with_ticks: ", self.with_ticks)
        if self.with_ticks == True:
            self.axes_ticks = []

            for i, n_vec in zip(range(self.d), Frame2d.axis_direction_vectors):
                ticks = Ticks(
                    camera_gear,
                    update_labels_orientation=self.update_labels_orientation,
                    attach_to_space="aspect2d")
                self.axes_ticks.append(ticks)
                ticks.reparentTo(self)
                ticks.setMat_normal(
                    math_utils.getMat4by4_to_rotate_xhat_to_vector(n_vec))
                # h, p, r = ticks.getHpr()
                # ticks.setHpr(h, p , r - i * 90.)

            self.regenerate_ticks()

        self.update_alignment()

        if attach_directly == True and self.attached_p == False:
            # print("Attaching directly")
            if attach_to_space == "aspect2d":
                self.attach_to_aspect2d()
            elif attach_to_space == "render":
                self.attach_to_render()
            else:
                exit(1)
Exemple #7
0
class Frame2d(TQGraphicsNodePath):
    """ a 2d frame within which 2d data can be displayed, i.e. numpy x and y arrays """

    axis_direction_indices = [0, 1]  # 0 for x axis, 1 for y axis
    axis_direction_indices_chars = ["x", "y"]
    axis_direction_vectors = [Vec3(1., 0., 0.), Vec3(0., 0., 1.)]

    def __init__(
            self,
            camera_gear=None,
            update_labels_orientation=False,
            with_ticks=True,
            attach_to_space="aspect2d",  # or "render",
            attach_directly=True):
        """ """
        TQGraphicsNodePath.__init__(self)

        self.attached_p = False

        if attach_to_space == "aspect2d":
            if update_labels_orientation == True:
                print(
                    "WARNING: do not set attach_to_space to aspect2d and update_labels_orientation=True simultaneously!"
                )
                update_labels_orientation = False

        if attach_to_space == "render":
            if camera_gear is None:
                print(
                    "ERR: if attach_to_space == render, supply a camera_gear as well that is not None!"
                )
                exit(1)

        self.attach_to_space = attach_to_space  # "render" or "aspect2d"

        self.quad = None

        # if camera_gear == None:
        #     self.update_labels_orientation == False

        # if update_labels_orientation == True and camera_gear == None:
        #     print("WARNING: if update_labels_orientation == True, camera_gear cannot be None")
        #      exit(1)

        self.camera_gear = camera_gear

        self.update_labels_orientation = update_labels_orientation

        size = 0.5
        self.height = size
        self.width = size * 1.618

        # self.lines = []  # lines

        # self.linesin2dframe = None
        self.linesin2dframe = LinesIn2dFrame()
        self.linesin2dframe.reparentTo(self)

        self.with_ticks = with_ticks

        self.d = 2  # dimension of frame

        self.xmin_hard = None
        self.xmax_hard = None

        self.ymin_hard = None
        self.ymax_hard = None

        self._xmin_soft = 0.
        self._xmax_soft = 1.

        self._ymin_soft = 0.
        self._ymax_soft = 1.

        self.axes_ticks_numbers = None
        self.regenerate_axes_ticks_numbers()

        self.clipping_planes_p3d_nodepaths = []

        self.quad = Quad(thickness=1.5)
        self.quad.reparentTo(self)

        # --- Ticks -----
        print("with_ticks: ", self.with_ticks)
        if self.with_ticks == True:
            self.axes_ticks = []

            for i, n_vec in zip(range(self.d), Frame2d.axis_direction_vectors):
                ticks = Ticks(
                    camera_gear,
                    update_labels_orientation=self.update_labels_orientation,
                    attach_to_space="aspect2d")
                self.axes_ticks.append(ticks)
                ticks.reparentTo(self)
                ticks.setMat_normal(
                    math_utils.getMat4by4_to_rotate_xhat_to_vector(n_vec))
                # h, p, r = ticks.getHpr()
                # ticks.setHpr(h, p , r - i * 90.)

            self.regenerate_ticks()

        self.update_alignment()

        if attach_directly == True and self.attached_p == False:
            # print("Attaching directly")
            if attach_to_space == "aspect2d":
                self.attach_to_aspect2d()
            elif attach_to_space == "render":
                self.attach_to_render()
            else:
                exit(1)

    def toggle_clipping_planes(self):
        """ """
        if len(self.clipping_planes_p3d_nodepaths
               ) == 0:  # no clipping planes enabled
            self.turn_clipping_planes_on()
        else:
            self.turn_clipping_planes_off()

    def turn_clipping_planes_off(self):
        """ """
        for nodepath in self.clipping_planes_p3d_nodepaths:
            nodepath.removeNode()

        self.linesin2dframe.get_p3d_nodepath().setClipPlaneOff()

        self.clipping_planes_p3d_nodepaths = []

    def turn_clipping_planes_on(self):
        """ the lines may extend outside of the 'frame'
            setting clipping planes are one way to prevent them from being
            rendered outside

            if dimensions of the frame are updated, set_clipping_panel_geometry must be called before this
            with the appriopriate panel geometry """

        self.turn_clipping_planes_off()

        for i, normal_vector in zip(Frame2d.axis_direction_indices,
                                    Frame2d.axis_direction_vectors):
            d = self.get_p3d_axis_length_from_axis_direction_index(i)
            a, b, c = math_utils.p3d_to_np(normal_vector)

            plane1 = LPlanef(a, b, c, 0)
            plane1_node = PlaneNode('', plane1)
            plane1_node.setClipEffect(1)
            plane1_nodepath = NodePath(plane1_node)

            plane2 = LPlanef(-a, -b, -c, d)
            plane2_node = PlaneNode('', plane2)
            plane2_node.setClipEffect(1)
            plane2_nodepath = NodePath(plane2_node)

            clipped_thing_nodepath = self.linesin2dframe.get_p3d_nodepath()

            plane1_nodepath.reparentTo(clipped_thing_nodepath)
            clipped_thing_nodepath.setClipPlane(plane1_nodepath)
            self.clipping_planes_p3d_nodepaths.append(plane1_nodepath)

            plane2_nodepath.reparentTo(clipped_thing_nodepath)
            clipped_thing_nodepath.setClipPlane(plane2_nodepath)
            self.clipping_planes_p3d_nodepaths.append(plane2_nodepath)

    @staticmethod
    def get_axis_direction_index_from_char_index(char_index):
        if x_or_y_str == "x":
            return 0
        elif x_or_y_str == "y":
            return 1
        assert False

    def regenerate_ticks(self):
        """ make sure the density of ticks is between ticks_max_density and ticks_min_density,
        then adjust the ticks """

        self.regenerate_axes_ticks_numbers(
            possibly_update_lims_from_internal_data=True)

        for i, axis_p3d_length, axis_direction_index in zip(
                range(self.d), [self.width, self.height],
                Frame2d.axis_direction_indices):
            self.axes_ticks[i].regenerate_ticks_graphics(
                self.axes_ticks_numbers[i],
                axis_p3d_length,
                axis_direction_index=axis_direction_index)

    def set_figsize(self, width, height, update_graphics=True):
        """ set the size of the figure, in p3d units """

        self.height = height
        self.width = width

        self.quad.set_height(self.height)
        self.quad.set_width(self.width)

        if update_graphics == True:
            self.quad.setPos(Vec3(0., 0., self.height))
            self.turn_clipping_planes_on()
            self.update_graphics_alignment()

    def update_graphics_alignment(self):
        """ """
        if self.with_ticks == True:
            self.regenerate_ticks()

        self.update_alignment()

    def lims_hard_p(self):
        """ """
        return [
            self.xmin_hard is not None or self.xmax_hard is not None,
            self.ymin_hard is not None or self.ymax_hard is not None
        ]

    def lims_fully_hard(self):
        """ """
        return (self.xmin_hard is not None and self.xmax_hard is not None
                and self.ymin_hard is not None and self.ymax_hard is not None)

    def get_lims_from_internal_data(self):
        """ TODO: update this if to take into account not just lines,
            but also e.g. scatter data or color plot data """

        xmin = np.inf
        xmax = -np.inf

        ymin = np.inf
        ymax = -np.inf

        changed = False

        for line in self.linesin2dframe.lines:
            p3d_xyz_coords = line.getCoords_np()
            frame_x_coords = p3d_xyz_coords[:, 0]
            frame_y_coords = p3d_xyz_coords[:,
                                            2]  # in p3d, z is up, but in my frame2d, y is up

            cur_xmin = min(frame_x_coords)
            if cur_xmin < xmin:
                xmin = cur_xmin
                changed = True

            cur_xmax = max(frame_x_coords)
            if cur_xmax > xmax:
                xmax = cur_xmax
                changed = True

            cur_ymin = min(frame_y_coords)
            if cur_ymin < ymin:
                ymin = cur_ymin
                changed = True

            cur_ymax = max(frame_y_coords)
            if cur_ymax > ymax:
                ymax = cur_ymax
                changed = True

        _vars = [xmin, xmax, ymin, ymax]

        if np.inf in _vars or -np.inf in _vars:
            # print("ERR: infinity is not a valid axes limit!")
            return []
        else:
            # set an offset to the data
            # offset the margin of the plot by 0.1 times the span of the displayed data
            margin_factor = 0.05

            xspan = np.abs(xmax - xmin)
            offset = xspan * margin_factor
            xmin -= offset
            xmax += offset

            yspan = np.abs(ymax - ymin)
            offset = yspan * margin_factor
            ymin -= offset
            ymax += offset

            return [(xmin, xmax), (ymin, ymax)]

    def regenerate_axes_ticks_numbers(
            self, possibly_update_lims_from_internal_data=False):
        """ """
        if possibly_update_lims_from_internal_data == True:
            # set soft xlims if xlims are not hard
            auto_lims = self.get_lims_from_internal_data()
            if len(
                    auto_lims
            ) > 0:  # auto_lims can not be determined if there is no data
                if not self.lims_hard_p()[0]:  # x axis
                    self.set_xlim(*auto_lims[0],
                                  update_graphics=False,
                                  hard=False,
                                  regenerate=False)

                if not self.lims_hard_p()[1]:  # y axis
                    self.set_ylim(*auto_lims[1],
                                  update_graphics=False,
                                  hard=False,
                                  regenerate=False)

        self.axes_ticks_numbers = [
            np.linspace(*self.get_preceding_xlims(), num=5, endpoint=True),
            np.linspace(*self.get_preceding_ylims(), num=5, endpoint=True)
        ]

    def set_xlim(self,
                 xmin,
                 xmax,
                 update_graphics=True,
                 hard=True,
                 regenerate=True):
        """ if hard == False, figure out the preceding limits automatically
            from the line data """

        if math_utils.equal_up_to_epsilon(xmin, xmax):
            span_abs = np.abs(xmax - xmin)
            center = xmin + span_abs / 2.
            padding0 = 0.1
            xmin = center - padding0
            xmax = center + padding0

        if hard == True:
            self.xmin_hard = xmin
            self.xmax_hard = xmax
        elif hard == False:
            self.unset_hard_xylims(reset_x_lim=True, reset_y_lim=False)

            self._xmin_soft = xmin
            self._xmax_soft = xmax
        else:
            print("hard should be boolean")
            exit(1)

        if regenerate == True:
            self.regenerate_axes_ticks_numbers()

        if update_graphics == True:
            self.update_graphics_alignment()

    def unset_hard_xylims(self, reset_x_lim=True, reset_y_lim=True):
        """ """
        if reset_x_lim == True:
            self.xmax_hard = None
            self.xmin_hard = None
            assert self._xmax_soft is not None
            assert self._xmin_soft is not None

        if reset_y_lim == True:
            self.ymax_hard = None
            self.ymin_hard = None
            assert self._ymax_soft is not None
            assert self._ymin_soft is not None

    def get_preceding_xlims(self):
        """ """
        return ((self.xmin_hard if self.xmin_hard is not None else
                 self._xmin_soft), (self.xmax_hard if self.xmax_hard
                                    is not None else self._xmax_soft))

    def get_preceding_ylims(self):
        """ """
        return ((self.ymin_hard if self.ymin_hard is not None else
                 self._ymin_soft), (self.ymax_hard if self.ymax_hard
                                    is not None else self._ymax_soft))

    def set_ylim(self,
                 ymin,
                 ymax,
                 update_graphics=True,
                 hard=True,
                 regenerate=True):
        """ if hard == False, figure out the preceding limits automatically
            from the line data """

        if math_utils.equal_up_to_epsilon(ymin, ymax):
            span_abs = np.abs(ymax - ymin)
            center = ymin + span_abs / 2.
            padding0 = 0.1
            ymin = center - padding0
            ymax = center + padding0

        if hard == True:
            self.ymin_hard = ymin
            self.ymax_hard = ymax
        elif hard == False:
            self.unset_hard_xylims(reset_x_lim=False, reset_y_lim=True)

            self._ymin_soft = ymin
            self._ymax_soft = ymax
        else:
            print("hard should be boolean")
            exit(1)

        self.regenerate_axes_ticks_numbers()

        if update_graphics == True:
            self.update_graphics_alignment()

    def get_p3d_to_frame_unit_length_scaling_factor(self,
                                                    axis_direction_index):
        """ """
        axes_ticks_numbers_y_max = max(
            self.axes_ticks_numbers[axis_direction_index])
        axes_ticks_numbers_y_min = min(
            self.axes_ticks_numbers[axis_direction_index])
        max_y_p3d_pos = Ticks.get_tick_pos_along_axis(
            self.get_p3d_axis_length_from_axis_direction_index(
                axis_direction_index),
            self.axes_ticks_numbers[axis_direction_index],
            max(self.axes_ticks_numbers[axis_direction_index]))
        min_y_p3d_pos = Ticks.get_tick_pos_along_axis(
            self.get_p3d_axis_length_from_axis_direction_index(
                axis_direction_index),
            self.axes_ticks_numbers[axis_direction_index],
            min(self.axes_ticks_numbers[axis_direction_index]))

        return np.abs(max_y_p3d_pos -
                      min_y_p3d_pos) / np.abs(axes_ticks_numbers_y_max -
                                              axes_ticks_numbers_y_min)

    def update_alignment(self):
        """ """
        pos_for_x = math_utils.p3d_to_np(
            Frame2d.axis_direction_vectors[0]) * Ticks.get_tick_pos_along_axis(
                self.width, self.axes_ticks_numbers[0], 0.0)
        pos_for_y = math_utils.p3d_to_np(
            Frame2d.axis_direction_vectors[1]) * Ticks.get_tick_pos_along_axis(
                self.height, self.axes_ticks_numbers[1], 0.0)

        pos_tmp = math_utils.p3d_to_np(pos_for_y) + math_utils.p3d_to_np(
            pos_for_x)

        for line in self.linesin2dframe.lines:
            x_scale = self.get_p3d_to_frame_unit_length_scaling_factor(0)
            y_scale = self.get_p3d_to_frame_unit_length_scaling_factor(1)

            # print("x_scale, y_scale: ", x_scale, y_scale)
            cond = np.abs(x_scale) > 1e-8 and np.abs(y_scale) > 1e-8
            if cond:
                line.setScale(x_scale, 1., y_scale)
                line.setPos(math_utils.np_to_p3d_Vec3(pos_tmp))
            else:
                print(
                    "scaling by too low is not supported by p3d, do it differently!"
                )
                self.clear_plot()

    def get_p3d_axis_length_from_axis_direction_index(self,
                                                      axis_direction_index):
        """ """
        if axis_direction_index == 0:
            return self.width
        elif axis_direction_index == 1:
            return self.height
        else:
            print("axis_direction_index: ", axis_direction_index,
                  " does not correspond to any axis")
            exit(1)

    def plot(self, x, y, color="white", thickness=2):
        """ x, y: discrete points (each one a 1d numpy array, same size) """
        # TODO: check if it is a line along only x or only y and then set soft xlims with a flag

        if x is not None and y is not None:
            assert np.shape(x) == np.shape(y)

            sl = primitives.SegmentedLinePrimitive(color=get_color(color),
                                                   thickness=thickness)
            sl.extendCoords([Vec3(x[i], 0., y[i]) for i in range(len(x))])
            sl.reparentTo(self.linesin2dframe)
            self.linesin2dframe.lines.append(sl)

        if self.with_ticks == True and not self.lims_fully_hard():
            self.regenerate_ticks()

        self.update_alignment()

    def clear_plot(self, update_alignment=True):
        """ """
        for line in self.linesin2dframe.lines:
            line.removeNode()

        self.linesin2dframe.lines = []

        if update_alignment == True:
            if not self.lims_fully_hard():
                self.update_graphics_alignment()

    def get_plot_x_range(self):
        return self.xmax_hard - self.xmin_hard

    def get_plot_y_range(self):
        return self.ymax_hard - self.ymin_hard

    def get_frame_p3d_origin(self):
        pos = self.quad.getPos()
        return np.array([pos[0], pos[2]])

    def get_frame_p3d_width(self):
        return self.width

    def get_frame_p3d_height(self):
        return self.height

    def shift_curve_to_match_up_zeros():
        pos0 = self.get_frame_p3d_origin()

    def attach_to_aspect2d(self):
        if self.attach_to_space == "aspect2d":
            super().attach_to_aspect2d()
            self.attached_p = True
            self.set_figsize(self.width, self.height, update_graphics=True)
        else:
            print("ERR: Do not call attach_to_aspect2d() to a frame for",
                  "which self.attach_to_space is not set to aspect2d")
            exit(1)

    def attach_to_render(self):
        if self.attach_to_space == "render":
            super().attach_to_render()
            self.attached_p = True
            self.set_figsize(self.width, self.height, update_graphics=True)
        else:
            print("ERR: Do not call attach_to_render() to a frame for",
                  "which self.attach_to_space is not set to render")
            exit(1)
class ProcessingBox(TQGraphicsNodePath):
    """ an individual box with text, position, loading symbol, elapsed time since start e"""

    def __init__(self, is_done_function, description):
        self.description = description

        self.x_pos_left_border = None
        self.y_pos_top_box = None

        self.is_done_function = is_done_function

        self.time_initial = time.time_ns()

        self.task_obj_update = None

        self.waiting_symbol = None

        self.text = None

        self.elapsed_time_text = None

        self.quad = None

        self.task_obj_update = None

        self.task_obj_update = taskMgr.add(self.update_task, 'update_task')

        self.height = None

        self.width = None

        if self.height is None:
            self.height = 0.2

        if self.width is None:
            self.width = 0.9

        TQGraphicsNodePath.__init__(self)

        pass

    def set_xy_pos(self, x_pos_left_border, y_pos_top_box):
        """ """
        self.x_pos_left_border = x_pos_left_border
        self.y_pos_top_box = y_pos_top_box


    def update(self):
        if self.is_done_function() == False:
            if self.x_pos_left_border is None:
                self.x_pos_left_border = -1.0

            x_pos_waiting_symbol = self.x_pos_left_border + 0.1

            x_pos_text = x_pos_waiting_symbol + 0.2

            if self.y_pos_top_box is None:
                self.y_pos_top_box = 0.

            y_pos_waiting_symbol = self.y_pos_top_box - 0.05

            y_pos_text = self.y_pos_top_box - 0.125

            x_pos_elapsed_time = x_pos_text + 0.4
            y_pos_elapsed_time = y_pos_text

            quad_thickness = 2.0

            if self.waiting_symbol is None:
                self.waiting_symbol = WaitingSymbol(self.is_done_function, Vec3(x_pos_waiting_symbol, 0., y_pos_waiting_symbol), size=0.1)
                self.waiting_symbol.reparentTo(self)
            else:
                self.waiting_symbol.set_position(Vec3(x_pos_waiting_symbol, 0., y_pos_waiting_symbol))

            if self.text is None:
                self.text = Fixed2dLabel(text=self.description, font="fonts/arial.egg", x=x_pos_text, y=y_pos_text)
                self.text.reparentTo(self)
            else:
                self.text.setPos(x_pos_text, y_pos_text)

            if self.elapsed_time_text is None:
                self.elapsed_time_text = Fixed2dLabel(text="elapsed time", font="fonts/arial.egg", x=x_pos_elapsed_time, y=y_pos_elapsed_time)
                self.elapsed_time_text.reparentTo(self)
            else:
                self.elapsed_time_text.setPos(x_pos_elapsed_time, y_pos_elapsed_time)

            elapsed_time = (time.time_ns() - self.time_initial) / 1.0e9
            self.elapsed_time_text.setText("{0:.0f} s".format(elapsed_time))

            if self.quad is None:
                self.quad = Quad(thickness=quad_thickness, TQGraphicsNodePath_creation_parent_node=engine.tq_graphics_basics.tq_aspect2d)
                self.quad.reparentTo(self)

                self.quad.set_pos_vec3(Vec3(self.x_pos_left_border, 0., self.y_pos_top_box))

                self.quad.set_height(self.height)
                self.quad.set_width(self.width)
            else:
                self.quad.set_pos_vec3(Vec3(self.x_pos_left_border, 0., self.y_pos_top_box))

            return True  # call task.cont

        return False  # call task.done

    def update_task(self, task):
        """ This updates it's position, checks if the task is done, and if it is, removes the Processing Box """

        ret = self.update()
        if ret == True:
            return task.cont

        self.remove()
        return task.done

    def _is_done(self):
        """ check the is_done_function """
        return self.is_done_function()

    def get_height(self):
        return self.height

    def get_width(self):
        return self.width

    def remove(self):
        taskMgr.remove(self.task_obj_update)

        if self.waiting_symbol:
            self.waiting_symbol.remove()

        if self.text:
            self.text.remove()

        if self.elapsed_time_text:
            self.elapsed_time_text.remove()

        if self.quad:
            self.quad.remove()
Exemple #9
0
class PDFViewer(TQGraphicsNodePath):
    """ includes the p3d_camera and rendering functionality for viewing a pdf """

    y_pages_distance_0 = 0.1
    x_left_offset = 0.1
    y_top_offset = 0.1

    spacebar_scroll_overlap = 0.1

    upper_scroll_margin = 1.

    margins_quad_pos_0 = Vec3(0., 1., 0.)

    filmsize_x_0 = 1.
    filmsize_y_0 = 1.

    drawing_mode_1_width = 15.  # in this drawing mode, you have huge margins in wich to draw and put derivations
    # TODO: make a drawing mode 2, where you exit those margins, this makes navigation a bit more messy, but you have
    # infinite margins

    drawing_mode_1_top_margin = 2.
    drawing_mode_1_bottom_margin = 2.

    def __init__(self, pdf_panner2d, pdf_filepath, *args, **kwargs):
        TQGraphicsNodePath.__init__(self, *args, **kwargs)

        # logical variables
        self.pdf_filepath = pdf_filepath
        self.pptos = []
        self.y_pages_distance = PDFViewer.y_pages_distance_0

        # derived
        self.pdf_panner2d = pdf_panner2d  # something like Panner2d
        self.ppr = PopplerPDFRenderer(self.pdf_filepath)

        # direct objects
        base.accept('1', self.return_to_middle_view)
        base.accept('space', self.scroll_spacebar_down)
        base.accept('shift-space', self.scroll_spacebar_up)

        base.accept('control-0', self.return_to_middle_view)

        # quad visualizing large margins for visual orientation
        self.margins_quad = Quad(thickness=10.)
        self.margins_quad.reparentTo(self)

        # plot
        self.render_pages()

        self.return_to_middle_view()

    def scroll_spacebar_down(self):
        self.scroll_spacebar(-1.)

    def scroll_spacebar_up(self):
        self.scroll_spacebar(+1.)

    def get_total_view_height(self):
        _sum = len(self.pptos) * self.y_pages_distance
        for ppto in self.pptos:
            if ppto is self.pptos[-1]:
                x_sizef, y_sizef = self.pptos[-1].get_size()
                _sum += y_sizef
                continue

            x_size, y_size = ppto.get_size()
            _sum += y_size

        return _sum

    def scroll_spacebar(self, direction_sign):
        """
        args:
            direction_sign: -1: scroll down, +1: scroll up """
        filmsize_x, filmsize_y = self.pdf_panner2d.get_filmsize_with_window_active(
        )
        self.pdf_panner2d.x[1]

        old_pos = self.pdf_panner2d.x[1]
        pos_step_try = direction_sign * (filmsize_y -
                                         PDFViewer.spacebar_scroll_overlap)
        new_pos = np.clip(
            old_pos + pos_step_try, -self.get_total_view_height(),
            -PDFViewer.upper_scroll_margin)  # clip(val, min, max)

        actual_pos_step = new_pos - old_pos

        # clip to not run off
        self.pdf_panner2d.x[1] += actual_pos_step
        self.pdf_panner2d.p_previous_offset[1] -= actual_pos_step

        self.pdf_panner2d.update_camera_state()
        self.pdf_panner2d.set_lookat_after_updated_camera_pos()
        self.pdf_panner2d.update_film_size_from_view_distance()

    def return_to_middle_view(self):
        """ scroll so that the pdf is centered again """
        assert len(self.pptos) > 0

        first_page_width = self.pptos[0].get_size()[0]

        filmsize_x, filmsize_y = self.pdf_panner2d.get_filmsize_with_window_active(
        )

        x_val = first_page_width / 2.
        self.pdf_panner2d.x[0] = x_val
        self.pdf_panner2d.p_previous_offset[0] = -x_val

        self.pdf_panner2d.update_camera_state()
        self.pdf_panner2d.set_lookat_after_updated_camera_pos()
        self.pdf_panner2d.update_film_size_from_view_distance()

    def get_page_width(self, page_num):
        x_size, y_size = self.pptos[page_num].get_size()
        return x_size

    def render_pages(self):
        for i in range(self.ppr.get_number_of_pages()):
            ppto = PDFPageTextureObject(i, self.ppr)
            ppto.reparentTo(self)

            pos_3d = ppto.getPos()
            x0 = pos_3d[0]
            y0 = pos_3d[
                2]  # since z is up in p3d and y is up in the 2d pdf convention here

            x_size, y_size = ppto.get_size()

            y = y0
            x = x0

            if i > 0:
                y -= self.y_pages_distance
            y -= (i + 1.) * y_size

            ppto.setPos(Vec3(x, 0., y))
            self.pptos.append(ppto)

        self.render_background_graphics()

    def render_background_graphics(self):
        width = PDFViewer.drawing_mode_1_width
        height = self.get_total_view_height(
        ) + PDFViewer.drawing_mode_1_top_margin + PDFViewer.drawing_mode_1_bottom_margin

        self.margins_quad.set_height(height)
        self.margins_quad.set_width(width)

        # self.margins_quad.setPos(PDFViewer.margins_quad_pos_0)
        self.margins_quad.setColor(Vec4(1., 0., 0., 0.3), 1)
        self.margins_quad.b2d.setColor(Vec4(1., 1., 1., 0.9), 1)

        first_page_width = self.pptos[0].get_size()[0]

        self.margins_quad.setPos(-width / 2. + first_page_width / 2.,
                                 PDFViewer.margins_quad_pos_0[1],
                                 PDFViewer.drawing_mode_1_top_margin)

    def calculate_mean_width(self):
        x_sizes = []
        for i, ppto in enumerate(self.pptos):
            x_size, y_size = ppto.get_size()
            x_sizes.append(x_size)

        return np.sum(x_sizes) / len(x_sizes)