Exemple #1
0
class RectAlignmentControllerView(BaseToolController, GenModel):
    changed = QtCore.Signal()

    def __init__(self, parent, model):
        super(RectAlignmentControllerView, self).__init__()

        self._parent = parent
        self.model_overall = model
        self.model = model.ra

        self.__init_interaction()

        self.gls = None

        self.active = False

    idx_handle_sel = mdlacc(None)
    idx_handle_hover = mdlacc(None)

    #sel_mode = mdlacc(SEL_MODE_NONE)
    behave_mode = mdlacc(MODE_NONE)
    ghost_handle = mdlacc(None)

    def change(self):
        self.changed.emit()

    def __init_interaction(self):
        # Selection / drag handling
        self.idx_handle_sel = None
        #self.sel_mode = SEL_MODE_NONE

        self.behave_mode = MODE_NONE

        # ghost handle for showing placement
        self.ghost_handle = None

    def initialize(self):
        self.__init_interaction()
        self.active = True

    def finalize(self):
        self.active = False

    def initializeGL(self, gls):
        self.gls = gls
        # Basic solid-color program
        self.prog = self.gls.shader_cache.get("vert2", "frag1")
        self.mat_loc = GL.glGetUniformLocation(self.prog, "mat")
        self.col_loc = GL.glGetUniformLocation(self.prog, "color")

        # Build a VBO for rendering square "drag-handles"
        self.vbo_handles_ar = numpy.ndarray(4, dtype=[("vertex", numpy.float32, 2)])
        self.vbo_handles_ar["vertex"] = numpy.array(corners) * HANDLE_HALF_SIZE


        self.vbo_handles = VBO(self.vbo_handles_ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.vao_handles = VAO()
        with self.vbo_handles, self.vao_handles:
            vbobind(self.prog, self.vbo_handles_ar.dtype, "vertex").assign()

        # Build a VBO/VAO for the perimeter
        # We don't initialize it here because it is updated every render
        # 4 verticies for outside perimeter
        # 6 verticies for each dim
        self.vbo_per_dim_ar = numpy.zeros(16, dtype=[("vertex", numpy.float32, 2)])

        self.vbo_per_dim = VBO(self.vbo_per_dim_ar, GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.vao_per_dim = VAO()
        with self.vao_per_dim, self.vbo_per_dim:
            vbobind(self.prog, self.vbo_per_dim_ar.dtype, "vertex").assign()

    def im2V(self, pt):
        """Translate Image coordinates to viewport coordinates"""

        if self.model_overall.view_mode:
            ph = projectPoint(self.model.image_matrix, pt)
            return self.viewState.tfW2V(ph)
        else:
            return self.viewState.tfW2V(pt)

    def V2im(self, pt):
        """
        Translate viewport coordinates to image coordinates
        :param pt:
        :return:
        """
        world = self.viewState.tfV2W(pt)

        if self.model_overall.view_mode:
            inv = numpy.linalg.inv(self.model.image_matrix)
            return projectPoint(inv, world)
        else:
            return Vec2(world)
        #inv = numpy.linalg.inv(self.model.image_matrix)
        #return Vec2(inv.dot(pt)[:2])

    def gen_dim(self, idx, always_above = True):
        """
        Generate rendering data for the dimension-lines
        :param idx:
        :return:
        """
        a = self.im2V(self.model.dim_handles[0 + idx])
        b = self.im2V(self.model.dim_handles[1 + idx])

        d = b-a

        delta = (b-a).norm()

        normal = Point2(rotate(math.pi / 2)[:2,:2].dot(delta))

        if always_above:
            if numpy.cross(Vec2(1,0), normal) > 0:
                normal = -normal

        res = numpy.array([
            a + normal * 8,
            a + normal * 20,
            a + normal * 15,
            b + normal * 15,
            b + normal * 8,
            b + normal * 20,
            ])

        return res


    def render(self, vs):
        self.viewState = vs

        disabled = not self.active or self.model_overall.view_mode

        # Perimeter is defined by the first 4 handles
        self.vbo_per_dim_ar["vertex"][:4] = [self.im2V(pt) for pt in self.model.align_handles[:4]]

        # Generate the dimension lines. For ease of use, we always draw the dim-lines above when dims are manual
        # or below when dims are unlocked
        self.vbo_per_dim_ar["vertex"][4:10] = self.gen_dim(0, not self.model.dims_locked)
        self.vbo_per_dim_ar["vertex"][10:16] = self.gen_dim(2, not self.model.dims_locked)

        self.vbo_per_dim.set_array(self.vbo_per_dim_ar)

        # Ugh..... PyOpenGL isn't smart enough to bind the data when it needs to be copied
        with self.vbo_per_dim:
            self.vbo_per_dim.copy_data()


        GL.glDisable(GL.GL_BLEND)

        # ... and draw the perimeter
        with self.vao_per_dim, self.prog:
            GL.glUniformMatrix3fv(self.mat_loc, 1, True, self.viewState.glWMatrix.astype(numpy.float32))

            # Draw the outer perimeter
            if disabled:
                GL.glUniform4f(self.col_loc, 0.8, 0.8, 0.8, 1)
            else:
                GL.glUniform4f(self.col_loc, 0.8, 0.8, 0, 1)
            GL.glDrawArrays(GL.GL_LINE_LOOP, 0, 4)

            # Draw the dimensions
            GL.glUniform4f(self.col_loc, 0.8, 0.0, 0.0, 1)
            GL.glDrawArrays(GL.GL_LINES, 4, 6)

            GL.glUniform4f(self.col_loc, 0.0, 0.0, 0.8, 1)
            GL.glDrawArrays(GL.GL_LINES, 10, 6)

        if disabled:
            return

        # Now draw a handle at each corner
        with self.vao_handles, self.prog:
            for n, i in enumerate(self.model.align_handles):
                # skip nonexistent handles
                if i is None:
                    continue

                is_anchor = IDX_IS_ANCHOR(n)

                corner_pos = self.im2V(i)

                if disabled:
                    color = [0.8, 0.8, 0.8, 1]
                elif self.idx_handle_sel == n:
                    color = [1, 1, 1, 1]
                elif self.idx_handle_hover == n:
                    color = [1, 1, 0, 1]
                else:
                    color = [0.8, 0.8, 0, 0.5]

                self.render_handle(corner_pos, color, is_anchor, True)

                if self.idx_handle_sel == n:
                    self.render_handle(corner_pos, [0,0,0,1], is_anchor, False)

            if self.ghost_handle is not None:
                self.render_handle(self.ghost_handle,[0.8, 0.8, 0, 0.5], True)

            if not self.model.dims_locked:
                for n, i in enumerate(self.model.dim_handles):
                    handle_pos = self.im2V(i)
                    if n == self.idx_handle_sel:
                        color = [1, 1, 1, 1]
                    if n < 2:
                        color = [0.8, 0.0, 0.0, 1]
                    else:
                        color = [0.0, 0.0, 0.8, 1]
                    self.render_handle(handle_pos, color, False, True)
                    if self.idx_handle_sel == n:
                        self.render_handle(corner_pos, [0,0,0,1], is_anchor, False)

    def render_handle(self, position, color, diagonal=False, filled=False):
        if diagonal:
            r = rotate(math.pi/4)
        else:
            r = numpy.identity(3)

        m = self.viewState.glWMatrix.dot(translate(*position).dot(r))
        GL.glUniformMatrix3fv(self.mat_loc, 1, True, m.astype(numpy.float32))
        GL.glUniform4f(self.col_loc, *color)
        GL.glDrawArrays(GL.GL_TRIANGLE_FAN if filled else GL.GL_LINE_LOOP, 0, 4)

    def get_handle_for_mouse(self, pos):
        for n, handle in enumerate(self.model.all_handles()):
            if handle is None:
                continue
            # get the pix-wise BBOX of the handle
            p = self.im2V(handle)
            r = QtCore.QRect(p[0], p[1], 0, 0)
            r.adjust(-HANDLE_HALF_SIZE, -HANDLE_HALF_SIZE, HANDLE_HALF_SIZE, HANDLE_HALF_SIZE)

            # If event inside the bbox
            if r.contains(pos):
                return n
        return None

    def get_line_query_for_mouse(self, pos):
        for n, (p1, p2) in enumerate(self.model.line_iter()):
            p1_v = self.im2V(p1)
            p2_v = self.im2V(p2)

            p, d = project_point_line(pos, p1_v, p2_v)
            if p is not None and d < HANDLE_HALF_SIZE:
                return n, p

        return None, None


    def mousePressEvent(self, event):
        disabled = not self.active or self.model_overall.view_mode
        if disabled:
            return False

        handle = self.get_handle_for_mouse(event.pos())

        if event.button() == QtCore.Qt.LeftButton and event.modifiers() & ADD_MODIFIER:
            idx, p = self.get_line_query_for_mouse(event.pos())
            if idx is not None:
                anchors = self.model.get_anchors(idx)
                if len(anchors) < 2:
                    p = self.V2im(p)
                    idx = new_anchor_index(self.model, idx)

                    cmd = cmd_set_handle_position(self.model, idx, p)
                    self._parent.undoStack.push(cmd)

                    self.idx_handle_sel = idx
                    self.idx_handle_hover = None

        elif event.button() == QtCore.Qt.LeftButton and event.modifiers() & DEL_MODIFIER and (
                        handle is not None and handle >= 4):
            cmd = cmd_set_handle_position(self.model, handle, None)
            self._parent.undoStack.push(cmd)
            #self.model.set_handle(handle, None)

            self.idx_handle_sel = None
            self.idx_handle_hover = None

        elif event.button() == QtCore.Qt.LeftButton:
            self.idx_handle_sel = handle
            self.idx_handle_hover = None
            if handle is not None:
                self.behave_mode = MODE_DRAGGING

        else:
            return False

        return True

    def mouseReleaseEvent(self, event):
        disabled = not self.active or self.model_overall.view_mode
        if disabled:
            return False

        if event.button() == QtCore.Qt.LeftButton and self.behave_mode == MODE_DRAGGING:
            self.behave_mode = MODE_NONE
        else:
            return False

        return True

    def mouseMoveEvent(self, event):
        disabled = not self.active or self.model_overall.view_mode
        if disabled:
            return False

        needs_update = False
        idx = self.get_handle_for_mouse(event.pos())

        if self.ghost_handle is not None:
            self.ghost_handle = None

        if self.behave_mode == MODE_NONE:
            if idx is not None:
                self.idx_handle_hover = idx

            else:
                self.idx_handle_hover = None

            if event.modifiers() & ADD_MODIFIER:
                line_idx, pos = self.get_line_query_for_mouse(event.pos())

                if line_idx is not None:
                    self.ghost_handle = pos

        elif self.behave_mode == MODE_DRAGGING:
            w_pos = self.V2im(Vec2(event.pos()))

            cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, w_pos, merge=True)
            self._parent.undoStack.push(cmd)
            #self.model.set_handle(self.idx_handle_sel, w_pos)


        return False

    def focusOutEvent(self, evt):
        self.idx_handle_sel = None

    def keyPressEvent(self, evt):
        disabled = not self.active or self.model_overall.view_mode
        if disabled:
            return False

        if evt.key() == QtCore.Qt.Key_Escape:
            self.idx_handle_sel = None

        elif self.idx_handle_sel is not None:

            if evt.key() in (QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace) and IDX_IS_ANCHOR(self.idx_handle_sel):

                cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, None)
                self._parent.undoStack.push(cmd)
                #self.model.set_handle(self.idx_handle_sel, None)
                self.idx_handle_sel = None

            # Basic 1-px nudging
            elif evt.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down):
                nudge = {
                    QtCore.Qt.Key_Left:  (-1,  0),
                    QtCore.Qt.Key_Right: ( 1,  0),
                    QtCore.Qt.Key_Up:    ( 0, -1),
                    QtCore.Qt.Key_Down:  ( 0,  1),
                }[evt.key()]

                current = Vec2(self.im2V(self.model.align_handles[self.idx_handle_sel]))
                viewspace = self.V2im(current + nudge)
                cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, viewspace)
                self._parent.undoStack.push(cmd)
                #self.model.set_handle(self.idx_handle_sel, viewspace)

                self.ghost_handle = None

    def next_prev_child(self, next):
        all_handles = self.model.all_handles()

        step = -1
        if next:
            step = 1

        if self.behave_mode == MODE_DRAGGING:
            return True
        else:
            idx = self.idx_handle_sel

            if idx == None:
                return False

            while 1:
                idx = (idx + step) % len(all_handles)

                if all_handles[idx] is not None:
                    self.idx_handle_sel = idx
                    return True
Exemple #2
0
class RectAlignmentControllerView(BaseToolController):
    changed = QtCore.Signal()

    def __init__(self, parent,
                 model: 'pcbre.ui.dialogs.layeralignmentdialog.dialog.AlignmentViewModel'):
        super(RectAlignmentControllerView, self).__init__()

        self._parent = parent
        self.model_overall = model
        self.model = model.ra

        self.__idx_handle_sel: Optional[int] = None
        self.__idx_handle_hover: Optional[int] = None

        self.__behave_mode = DM.NONE
        self.__ghost_handle = None

        self.__init_interaction()

        self.gls: Optional[Any] = None

        self.active = False

    def change(self) -> None:
        self.changed.emit()

    @property
    def tool_actions(self) -> 'Sequence[ToolActionDescription]':
        return g_ACTIONS

    def __init_interaction(self) -> None:
        # Selection / drag handling
        self.__idx_handle_sel = None

        self.__behave_mode = DM.NONE

        # ghost handle for showing placement
        self.__ghost_handle = None

    def initialize(self) -> None:
        self.__init_interaction()
        self.active = True

    def finalize(self) -> None:
        self.active = False

    def initializeGL(self, gls: 'GLShared') -> None:
        self.gls = gls

        assert self.gls is not None

        # Basic solid-color program
        self.prog = self.gls.shader_cache.get("vert2", "frag1")
        self.mat_loc = GL.glGetUniformLocation(self.prog.program, "mat")
        self.col_loc = GL.glGetUniformLocation(self.prog.program, "color")

        # Build a VBO for rendering square "drag-handles"
        self.vbo_handles_ar = numpy.ndarray((4, ), dtype=[("vertex", numpy.float32, 2)])
        self.vbo_handles_ar["vertex"] = numpy.array(corners) * HANDLE_HALF_SIZE

        self.vbo_handles = VBO(self.vbo_handles_ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.vao_handles = VAO()
        with self.vbo_handles, self.vao_handles:
            VBOBind(self.prog.program, self.vbo_handles_ar.dtype, "vertex").assign()

        # Build a VBO/VAO for the perimeter
        # We don't initialize it here because it is updated every render
        # 4 verticies for outside perimeter
        # 6 verticies for each dim
        self.vbo_per_dim_ar = numpy.zeros(16, dtype=[("vertex", numpy.float32, 2)])

        self.vbo_per_dim = VBO(self.vbo_per_dim_ar, GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.vao_per_dim = VAO()
        with self.vao_per_dim, self.vbo_per_dim:
            VBOBind(self.prog.program, self.vbo_per_dim_ar.dtype, "vertex").assign()

    def im2V(self, pt: Vec2) -> Vec2:
        """Translate Image coordinates to viewport coordinates"""

        if self.model_overall.view_mode.is_aligned():
            ph = project_point(self.model.image_matrix, pt)
            return self.viewPort.tfW2V(ph)
        else:
            return self.viewPort.tfW2V(pt)

    def V2im(self, pt: Vec2) -> Vec2:
        """
        Translate viewport coordinates to image coordinates
        :param pt:
        :return:
        """
        world = self.viewPort.tfV2W(pt)

        if self.model_overall.view_mode.is_aligned():
            inv = numpy.linalg.inv(self.model.image_matrix)
            return project_point(inv, world)
        else:
            return world
        # inv = numpy.linalg.inv(self.model.image_matrix)
        # return Vec2(inv.dot(pt)[:2])

    def gen_dim(self, idx: int, always_above: bool = True) -> 'npt.NDArray[numpy.float64]':
        """
        Generate rendering data for the dimension-lines
        :param idx:
        :return:
        """
        a = self.im2V(self.model.dim_handles[0 + idx])
        b = self.im2V(self.model.dim_handles[1 + idx])

        d = b - a

        delta = (b - a).norm()

        normal = Vec2.from_mat(rotate(math.pi / 2)[:2, :2].dot(delta))

        if always_above:
            if numpy.cross(Vec2(1, 0), normal) > 0:
                normal = -normal

        res = numpy.array([
            a + normal * 8,
            a + normal * 20,
            a + normal * 15,
            b + normal * 15,
            b + normal * 8,
            b + normal * 20,
        ], dtype=numpy.float64)

        return res

    @property
    def disabled(self) -> bool:
        # When the we're showing the aligned view; or if we are using keypoint align
        # don't render any handles
        return not self.active or self.model_overall.view_mode.is_aligned()

    def render(self, viewPort: 'ViewPort') -> None:
        self.viewPort = viewPort

        # Perimeter is defined by the first 4 handles
        self.vbo_per_dim_ar["vertex"][:4] = [self.im2V(pt) for pt in self.model.align_handles[:4]]

        # Generate the dimension lines. For ease of use, we always draw the dim-lines above when dims are manual
        # or below when dims are unlocked
        self.vbo_per_dim_ar["vertex"][4:10] = self.gen_dim(0, not self.model.dims_locked)
        self.vbo_per_dim_ar["vertex"][10:16] = self.gen_dim(2, not self.model.dims_locked)

        self.vbo_per_dim.set_array(self.vbo_per_dim_ar)

        # Ugh..... PyOpenGL isn't smart enough to bind the data when it needs to be copied
        with self.vbo_per_dim:
            self.vbo_per_dim.copy_data()

        GL.glDisable(GL.GL_BLEND)

        # ... and draw the perimeter
        with self.vao_per_dim, self.prog.program:
            GL.glUniformMatrix3fv(self.mat_loc, 1, True, self.viewPort.glWMatrix.astype(numpy.float32))

            # Draw the outer perimeter
            if self.disabled:
                GL.glUniform4f(self.col_loc, 0.8, 0.8, 0.8, 1)
            else:
                GL.glUniform4f(self.col_loc, 0.8, 0.8, 0, 1)
            GL.glDrawArrays(GL.GL_LINE_LOOP, 0, 4)

            # Draw the dimensions
            GL.glUniform4f(self.col_loc, 0.8, 0.0, 0.0, 1)
            GL.glDrawArrays(GL.GL_LINES, 4, 6)

            GL.glUniform4f(self.col_loc, 0.0, 0.0, 0.8, 1)
            GL.glDrawArrays(GL.GL_LINES, 10, 6)

        if self.disabled:
            return

        # Now draw a handle at each corner
        with self.vao_handles, self.prog.program:
            for n, i in enumerate(self.model.align_handles):
                # skip nonexistent handles
                if i is None:
                    continue

                is_anchor = IDX_IS_LINE(n)

                corner_pos = self.im2V(i)

                if self.disabled:
                    color = [0.8, 0.8, 0.8, 1]
                elif self.__idx_handle_sel == n:
                    color = [1, 1, 1, 1]
                elif self.__idx_handle_hover == n:
                    color = [1, 1, 0, 1]
                else:
                    color = [0.8, 0.8, 0, 0.5]

                self.render_handle(corner_pos, color, is_anchor, True)

                if self.__idx_handle_sel == n:
                    self.render_handle(corner_pos, [0, 0, 0, 1], is_anchor, False)

            if self.__ghost_handle is not None:
                self.render_handle(self.__ghost_handle, [0.8, 0.8, 0, 0.5], True)

            if not self.model.dims_locked:
                for n, i in enumerate(self.model.dim_handles):
                    handle_pos = self.im2V(i)
                    if n == self.__idx_handle_sel:
                        color = [1, 1, 1, 1]
                    if n < 2:
                        color = [0.8, 0.0, 0.0, 1]
                    else:
                        color = [0.0, 0.0, 0.8, 1]
                    self.render_handle(handle_pos, color, False, True)
                    if self.__idx_handle_sel == n:
                        self.render_handle(corner_pos, [0, 0, 0, 1], is_anchor, False)

    def render_handle(self, position: Vec2, color: Sequence[float],
                      diagonal: bool = False, filled: bool = False) -> None:
        if diagonal:
            r = rotate(math.pi / 4)
        else:
            r = numpy.identity(3)

        m = self.viewPort.glWMatrix.dot(translate(*position).dot(r))
        GL.glUniformMatrix3fv(self.mat_loc, 1, True, m.astype(numpy.float32))
        GL.glUniform4f(self.col_loc, *color)
        GL.glDrawArrays(GL.GL_TRIANGLE_FAN if filled else GL.GL_LINE_LOOP, 0, 4)

    def get_handle_index_for_mouse(self, pos: Vec2) -> Optional[int]:
        """ Returns the index of a handle (or None if one isn't present) given
            a MoveEvent"""
        for n, handle in enumerate(self.model.all_handles()):
            if handle is None:
                continue

            # get the pix-wise BBOX of the handle
            p = self.im2V(handle)

            # Rect encompassing the handle
            r = Rect.from_center_size(p, HANDLE_HALF_SIZE * 2, HANDLE_HALF_SIZE * 2)

            # If event inside the bbox
            if r.point_test(pos) != 0:
                return n
        return None

    def get_line_query_for_mouse(self, pos: Vec2) -> Tuple[Optional[int], Optional[Vec2]]:
        for n, (p1, p2) in enumerate(self.model.lines()):
            p1_v = self.im2V(p1)
            p2_v = self.im2V(p2)

            p, d = project_point_line(pos, p1_v, p2_v)
            if d is not None and d < HANDLE_HALF_SIZE:
                return n, p

        return None, None

    def event_add_constraint(self, event: 'ToolActionEvent') -> None:
        idx, p = self.get_line_query_for_mouse(event.cursor_pos)
        if idx is not None:
            anchors = self.model.get_anchors(idx)
            if len(anchors) < 2:
                p = self.V2im(p)
                idx = new_anchor_index(self.model, idx)

                cmd = cmd_set_handle_position(self.model, idx, p)
                self._parent.undoStack.push(cmd)

                self.__idx_handle_sel = idx
                self.__idx_handle_hover = None

    def event_remove_constraint(self, event: 'ToolActionEvent') -> None:
        handle = self.get_handle_index_for_mouse(event.cursor_pos)

        if handle is not None and handle >= 4:
            cmd = cmd_set_handle_position(self.model, handle, None)
            self._parent.undoStack.push(cmd)

        self.__idx_handle_sel = None
        self.__idx_handle_hover = None

    def event_select(self, event: 'ToolActionEvent') -> None:
        handle = self.get_handle_index_for_mouse(event.cursor_pos)

        self.__idx_handle_sel = handle
        self.__idx_handle_hover = None

        if handle is not None:
            self.__behave_mode = DM.DRAGGING

    def event_release(self, event: 'ToolActionEvent') -> None:
        if self.__behave_mode == DM.DRAGGING:
            self.__behave_mode = DM.NONE

    def tool_event(self, event: 'ToolActionEvent') -> None:
        if self.disabled:
            return

        if event.code == EventCode.Select:
            self.event_select(event)
        elif event.code == EventCode.Release:
            self.event_release(event)
        elif event.code == EventCode.AddToLine:
            self.event_add_constraint(event)
        elif event.code == EventCode.RemoveFromLine:
            self.event_remove_constraint(event)

    def mouseMoveEvent(self, event: 'MoveEvent') -> None:
        if self.disabled:
            return

        needs_update = False
        idx = self.get_handle_index_for_mouse(event.cursor_pos)

        if self.__ghost_handle is not None:
            self.__ghost_handle = None

        if self.__behave_mode == DM.NONE:
            if idx is not None:
                self.__idx_handle_hover = idx

            else:
                self.__idx_handle_hover = None

            # if event.modifiers() & ADD_MODIFIER:
            #    line_idx, pos = self.get_line_query_for_mouse(event.cursor_pos)

            #    if line_idx is not None:
            #        self.__ghost_handle = pos

        elif self.__behave_mode == DM.DRAGGING:
            w_pos = self.V2im(event.cursor_pos)
            # print(w_pos, event.world_pos)

            cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, w_pos, merge=True)
            self._parent.undoStack.push(cmd)

    def focusOutEvent(self, evt: QtCore.QEvent) -> None:
        self.__idx_handle_sel = None

    def keyPressEvent(self, evt: QtGui.QKeyEvent) -> bool:
        if self.disabled:
            return False

        if evt.key() == QtCore.Qt.Key_Escape:
            self.__idx_handle_sel = None

        elif self.__idx_handle_sel is not None:

            if evt.key() in (QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace) and IDX_IS_LINE(self.__idx_handle_sel):

                cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, None)
                self._parent.undoStack.push(cmd)
                # self.model.set_handle(self.__idx_handle_sel, None)
                self.__idx_handle_sel = None

            # Basic 1-px nudging
            elif evt.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down):
                _nudgetab: Dict[int,Tuple[int, int]] = {
                    QtCore.Qt.Key_Left: (-1, 0),
                    QtCore.Qt.Key_Right: (1, 0),
                    QtCore.Qt.Key_Up: (0, -1),
                    QtCore.Qt.Key_Down: (0, 1),
                }

                nudge = _nudgetab[evt.key()]

                current = self.im2V(self.model.align_handles[self.__idx_handle_sel])
                viewspace = self.V2im(current + Vec2.from_mat(nudge))
                cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, viewspace)
                self._parent.undoStack.push(cmd)
                # self.model.set_handle(self.__idx_handle_sel, viewspace)

                self.__ghost_handle = None

    def next_prev_child(self, next: bool) -> bool:
        all_handles = self.model.all_handles()

        step = -1
        if next:
            step = 1

        if self.__behave_mode == DM.DRAGGING:
            return True
        else:
            idx = self.__idx_handle_sel

            if idx is None:
                return False

            while 1:
                idx = (idx + step) % len(all_handles)

                if all_handles[idx] is not None:
                    self.__idx_handle_sel = idx
                    return True
Exemple #3
0
    # disable cursor
    glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED)

    glfw.set_cursor_pos_callback(theWindow, window_cursor_callback)
    # initialize cursor position
    cursorPos = glfw.get_cursor_pos(theWindow)

    glfw.set_scroll_callback(theWindow, window_scroll_callback)

    vbo = VBO(vertices, 'GL_STATIC_DRAW')
    vbo.create_buffers()

    vao = glGenVertexArrays(1)
    glBindVertexArray(vao)
    vbo.bind()
    vbo.copy_data()
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    glBindVertexArray(0)

    # compile program
    renderProgram = GLProgram(rayTracingVertexShaderSource, rayTracingFragmentShaderSource)
    renderProgram.compile_and_link()
    uniformInfos = [
        ('backColor', 'vec3f'),
        ('ambientColor', 'vec3f'),
        ('o_c', 'vec3f'),
        ('o_p', 'vec3f'),
        ('x_c', 'vec3f'),
        ('y_c', 'vec3f'),
        ('x_p', 'vec3f'),
Exemple #4
0
    glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED)

    glfw.set_cursor_pos_callback(theWindow, window_cursor_callback)
    # initialize cursor position
    cursorPos = glfw.get_cursor_pos(theWindow)

    glfw.set_scroll_callback(theWindow, window_scroll_callback)

    sphereDataVBO = VBO(sphereData, usage='GL_STATIC_DRAW')
    sphereDataVBO.create_buffers()

    sphereVAO = glGenVertexArrays(1)

    glBindVertexArray(sphereVAO)
    sphereDataVBO.bind()
    sphereDataVBO.copy_data()
    glVertexAttribPointer(0, 3, GL_FLOAT,
                          GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
                          6 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float)))
    glEnableVertexAttribArray(1)
    sphereDataVBO.unbind()
    glBindVertexArray(0)

    cylinderDataVBO = VBO(cylinderData, usage='GL_STATIC_DRAW')
    cylinderDataVBO.create_buffers()

    cylinderVAO = glGenVertexArrays(1)
Exemple #5
0
    glfw.set_framebuffer_size_callback(theWindow, window_resize_callback)

    # create VBO to store vertices
    verticesVBO = VBO(triangleVertices, usage='GL_STATIC_DRAW')
    verticesVBO.create_buffers()

    # create VAO to describe array information
    triangleVAO = glGenVertexArrays(1)

    # bind VAO
    glBindVertexArray(triangleVAO)

    # bind VBO
    verticesVBO.bind()
    # buffer data into OpenGL
    verticesVBO.copy_data()

    # configure the fist 3-vector (pos)
    # arguments: index, size, type, normalized, stride, pointer
    # the stride is 6 * 4 because there are six floats per vertex, and the size of
    # each float is 4 bytes
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)

    # configure the second 3-vector (color)
    # the offset is 3 * 4 = 12 bytes
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * 4,
                          ctypes.c_void_p(3 * 4))
    glEnableVertexAttribArray(1)

    # unbind VBO
Exemple #6
0
    glfw.set_cursor_pos_callback(theWindow, window_cursor_callback)
    # initialize cursor position
    cursorPos = glfw.get_cursor_pos(theWindow)

    glfw.set_scroll_callback(theWindow, window_scroll_callback)

    vertexVBO = VBO(vertices, usage='GL_STATIC_DRAW')
    vertexVBO.create_buffers()
    textureVBO = VBO(textureVertices, usage='GL_STATIC_DRAW')
    textureVBO.create_buffers()

    frameVAO = glGenVertexArrays(1)
    glBindVertexArray(frameVAO)
    vertexVBO.bind()
    vertexVBO.copy_data()
    glVertexAttribPointer(0, 3, GL_FLOAT,
                          GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    glBindVertexArray(0)

    textureVAO = glGenVertexArrays(1)
    glBindVertexArray(textureVAO)
    textureVBO.bind()
    textureVBO.copy_data()
    glVertexAttribPointer(0, 3, GL_FLOAT,
                          GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
Exemple #7
0
    # create window
    theWindow = glfw.create_window(windowSize[0], windowSize[1],
                                   'Audio Oscilloscope', None, None)
    # make window the current context
    glfw.make_context_current(theWindow)

    # enable z-buffer
    glEnable(GL_DEPTH_TEST)

    dataVBO = VBO(dataBuffer, usage='GL_STATIC_DRAW')

    dataVAO = glGenVertexArrays(1)

    glBindVertexArray(dataVAO)
    dataVBO.bind()
    dataVBO.copy_data()
    glVertexAttribPointer(0, 2, GL_FLOAT,
                          GL_FALSE, 2 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    dataVBO.unbind()
    glBindVertexArray(0)

    renderProgram = GLProgram(waveVertexShaderSource, waveFragmentShaderSource)
    renderProgram.compile_and_link()

    waveColorUniform = GLUniform(renderProgram.get_program_id(), 'waveColor',
                                 'vec3f')

    # change drawing mode
    # glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
Exemple #8
0
    # create VBOs to store vertices, normals and elements
    cubeDataVBO = VBO(cubeData, usage='GL_STATIC_DRAW')
    cubeDataVBO.create_buffers()

    sphereDataVBO = VBO(sphereData, usage='GL_STATIC_DRAW')
    sphereDataVBO.create_buffers()

    # create VAO to describe array information
    triangleVAO, sphereVAO = glGenVertexArrays(2)

    # bind VAO
    glBindVertexArray(triangleVAO)
    # bind data VBO
    cubeDataVBO.bind()
    cubeDataVBO.copy_data()
    glVertexAttribPointer(0, 3, GL_FLOAT,
                          GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
                          6 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float)))
    glEnableVertexAttribArray(1)
    # unbind VBO
    cubeDataVBO.unbind()
    # unbind VAO
    glBindVertexArray(0)

    glBindVertexArray(sphereVAO)
    sphereDataVBO.bind()
def image_server(evtQueue, resultQueue):

    # resource-taking objects
    resObjs = []

    # initialize glfw
    glfw.init()

    # set glfw config
    glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
    glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
    glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
    glfw.window_hint(glfw.RESIZABLE, GL_FALSE)

    if pyPlatform.system().lower() == 'darwin':
        glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE)

    # create window
    theWindow = glfw.create_window(windowSize[0], windowSize[1], 'Spherical Projection', None, None)
    # make window the current context
    glfw.make_context_current(theWindow)


    # enable z-buffer
    glEnable(GL_DEPTH_TEST)

    # set resizing callback function
    # glfw.set_framebuffer_size_callback(theWindow, window_resize_callback)

    #glfw.set_key_callback(theWindow, window_keypress_callback)
    # disable cursor
    #glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED)

    #glfw.set_cursor_pos_callback(theWindow, window_cursor_callback)
    # initialize cursor position
    cursorPos = glfw.get_cursor_pos(theWindow)

    # glfw.set_scroll_callback(theWindow, window_scroll_callback)

    vbo = VBO(vertices, 'GL_STATIC_DRAW')
    vbo.create_buffers()
    resObjs.append(vbo)

    vao = glGenVertexArrays(1)
    glBindVertexArray(vao)
    vbo.bind()
    vbo.copy_data()
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    glBindVertexArray(0)

    # compile program
    renderProgram = GLProgram(rayTracingVertexShaderSource, rayTracingFragmentShaderSource)
    renderProgram.compile_and_link()
    uniformInfos = [
        ('backColor', 'vec3f'),
        ('ambientColor', 'vec3f'),
        ('o_c', 'vec3f'),
        ('o_p', 'vec3f'),
        ('x_c', 'vec3f'),
        ('y_c', 'vec3f'),
        ('x_p', 'vec3f'),
        ('y_p', 'vec3f'),
        ('c_c', 'vec3f'),
        ('c_p', 'vec3f'),
        ('winSize', 'vec2f')
    ]
    uniforms = create_uniform(renderProgram.get_program_id(), uniformInfos)


    # keep rendering until the window should be closed
    while not glfw.window_should_close(theWindow):

        # set background color
        glClearColor(*windowBackgroundColor)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        renderProgram.use()

        # update uniforms
        o_c, c_c, x_c, y_c = get_camera_vectors(camera)
        o_p, c_p, x_p, y_p = get_camera_vectors(projector)
        uniforms['o_c'].update(o_c)
        uniforms['x_c'].update(x_c)
        uniforms['y_c'].update(y_c)
        uniforms['c_c'].update(c_c)

        uniforms['o_p'].update(o_p)
        uniforms['x_p'].update(x_p)
        uniforms['y_p'].update(y_p)
        uniforms['c_p'].update(c_p)
        uniforms['backColor'].update(backColor)
        uniforms['ambientColor'].update(ambientColor)
        uniforms['winSize'].update(windowSize.astype(np.float32))

        try:
            newImage = evtQueue.get(timeout=0.05)
        except Exception as e:
            # tell glfw to poll and process window events
            glfw.poll_events()
            # swap frame buffer
            glfw.swap_buffers(theWindow)
            continue

        texture = create_texture(newImage)

        glBindVertexArray(vao)
        glActiveTexture(GL_TEXTURE0)
        texture.bind()
        glDrawArrays(GL_TRIANGLES, 0, 6)
        texture.unbind()
        glBindVertexArray(0)

        texture.delete()

        # respond key press
        keyboard_respond_func()
        # tell glfw to poll and process window events
        glfw.poll_events()
        # swap frame buffer
        glfw.swap_buffers(theWindow)

        result = get_screenshot(windowSize)
        resultQueue.put(result)

    for obj in resObjs:
        obj.delete()

    # terminate glfw
    glfw.terminate()
Exemple #10
0
    cursorPos = glfw.get_cursor_pos(theWindow)

    glfw.set_scroll_callback(theWindow, window_scroll_callback)

    gridVBO = VBO(gridArray, usage='GL_DYNAMIC_DRAW')
    gridVBO.create_buffers()
    gridEBO = VBO(elementArray,
                  usage='GL_STATIC_DRAW',
                  target='GL_ELEMENT_ARRAY_BUFFER')
    gridEBO.create_buffers()

    gridVAO = glGenVertexArrays(1)

    glBindVertexArray(gridVAO)
    gridVBO.bind()
    gridVBO.copy_data()
    gridEBO.bind()
    gridEBO.copy_data()
    glVertexAttribPointer(0, 3, GL_FLOAT,
                          GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
                          6 * ctypes.sizeof(ctypes.c_float),
                          ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float)))
    glEnableVertexAttribArray(1)
    glBindVertexArray(0)
    gridVBO.unbind()

    renderProgram = GLProgram(vertexShaderSource, fragmentShaderSource)
    renderProgram.compile_and_link()
class TextDrawer:
    def __init__(self):
        self.face = None
        self.textures = dict()

        # compile rendering program
        self.renderProgram = GLProgram(_textVertexShaderSource,
                                       _textFragmentShaderSource)
        self.renderProgram.compile_and_link()

        # make projection uniform
        self.projectionUniform = GLUniform(self.renderProgram.get_program_id(),
                                           'projection', 'mat4f')
        self.textColorUniform = GLUniform(self.renderProgram.get_program_id(),
                                          'textColor', 'vec3f')
        self.textureSizeUniform = GLUniform(
            self.renderProgram.get_program_id(), 'textureSize', 'vec2f')

        # create rendering buffer
        self.vbo = VBO(_get_rendering_buffer(0, 0, 0, 0))
        self.vbo.create_buffers()
        self.vboId = glGenBuffers(1)

        # initialize VAO
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)
        glBindBuffer(GL_ARRAY_BUFFER, self.vboId)
        self.vbo.bind()
        self.vbo.copy_data()
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
                              5 * ctypes.sizeof(ctypes.c_float),
                              ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(
            1, 2, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float),
            ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float)))
        glEnableVertexAttribArray(1)
        # self.vbo.unbind()
        glBindVertexArray(0)

        self.zNear = -1.0
        self.zFar = 1.0

    def delete(self):
        self.textures.clear()
        self.face = None
        self.renderProgram.delete()
        self.projectionUniform = None
        self.textColorUniform = None
        self.textureSizeUniform = None
        self.vbo.delete()
        glDeleteVertexArrays(1, [self.vao])

    def load_font(self, fontFilename, fontSize):
        assert os.path.exists(fontFilename)
        self.textures.clear()

        self.face = ft.Face(fontFilename)
        self.face.set_char_size(fontSize)

        # load all ASCII characters
        for i in range(128):
            self.load_character(chr(i))

    def load_character(self, character):
        assert self.face is not None
        assert len(character) == 1

        if character not in self.textures:
            # load glyph in freetype
            self.face.load_char(character)
            ftBitmap = self.face.glyph.bitmap
            height, width = ftBitmap.rows, ftBitmap.width
            bitmap = np.array(ftBitmap.buffer, dtype=np.uint8).reshape(
                (height, width))

            # create texture
            texture = _create_text_texture(bitmap)

            # add texture object to the dictionary
            characterSlot = CharacterSlot(texture, self.face.glyph)
            self.textures[character] = characterSlot

    def get_character(self, ch):
        if ch not in self.textures:
            self.load_character(ch)
        return self.textures[ch]

    def _draw_text(self, text, textPos, windowSize, scale, linespread,
                   foreColor):
        if len(text) == 0:
            return

        blendEnabled = glIsEnabled(GL_BLEND)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        self.renderProgram.use()
        glBindVertexArray(self.vao)
        glActiveTexture(GL_TEXTURE0)

        foreColor = np.asarray(foreColor, np.float32)
        self.textColorUniform.update(foreColor)

        projectionMat = orthographic_projection(0.0, windowSize[0], 0.0,
                                                windowSize[1], self.zNear,
                                                self.zFar)
        self.projectionUniform.update(projectionMat)

        lineY = textPos[1]
        nowX = textPos[0]

        # split text into lines
        lines = text.split('\n')

        for line in lines:

            if len(line) > 0:
                # analyze this line
                bearings = []
                for ch in line:
                    charSlot = self.get_character(ch)
                    bearings.append(charSlot.bearing[1] * scale[1])

                maxBearings = max(bearings)

                for ch in line:
                    charSlot = self.get_character(ch)

                    xpos = nowX + charSlot.bearing[0] * scale[0]
                    yLowerd = (maxBearings - charSlot.bearing[1] * scale[1])
                    ypos = lineY - yLowerd

                    w = charSlot.textureSize[0] * scale[0]
                    h = charSlot.textureSize[1] * scale[1]

                    self.textureSizeUniform.update(np.array((w, h),
                                                            np.float32))
                    charSlot.texture.bind()
                    self.vbo.bind()
                    self.vbo.set_array(
                        _get_rendering_buffer(xpos, ypos, w, h, 0.999))
                    self.vbo.copy_data()
                    self.vbo.unbind()

                    glDrawArrays(GL_TRIANGLES, 0, 6)
                    charSlot.texture.unbind()

                    # the advance is number of 1/64 pixels
                    nowX += (charSlot.advance / 64.0) * scale[0]

            nowX = textPos[0]

            yOffset = self.get_character(
                'X').textureSize[1] * scale[1] * linespread
            lineY -= yOffset

        glBindVertexArray(0)

        if not blendEnabled:
            glDisable(GL_BLEND)

    def draw_text(self,
                  text,
                  textPos,
                  windowSize,
                  color=(1.0, 1.0, 1.0),
                  scale=(1.0, 1.0),
                  linespread=1.5):
        return self._draw_text(text, textPos, windowSize, scale, linespread,
                               color)
class TextDrawer_Outlined:
    def __init__(self):
        self.face = None
        self.stroker = None
        self.foreTextures = dict()
        self.backTextures = dict()

        # compile rendering program
        self.renderProgram = GLProgram(_textVertexShaderSource,
                                       _textFragmentShaderSource)
        self.renderProgram.compile_and_link()

        # make projection uniform
        self.projectionUniform = GLUniform(self.renderProgram.get_program_id(),
                                           'projection', 'mat4f')
        self.textColorUniform = GLUniform(self.renderProgram.get_program_id(),
                                          'textColor', 'vec3f')
        self.textureSizeUniform = GLUniform(
            self.renderProgram.get_program_id(), 'textureSize', 'vec2f')

        # create rendering buffer
        self.vbo = VBO(_get_rendering_buffer(0, 0, 0, 0))
        self.vbo.create_buffers()
        self.vboId = glGenBuffers(1)

        # initialize VAO
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)
        glBindBuffer(GL_ARRAY_BUFFER, self.vboId)
        self.vbo.bind()
        self.vbo.copy_data()
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
                              5 * ctypes.sizeof(ctypes.c_float),
                              ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(
            1, 2, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float),
            ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float)))
        glEnableVertexAttribArray(1)
        # self.vbo.unbind()
        glBindVertexArray(0)

        self.zNear = -1.0
        self.zFar = 1.0

    def delete(self):
        self.foreTextures.clear()
        self.backTextures.clear()
        self.face = None
        self.stroker = None
        self.renderProgram.delete()
        self.projectionUniform = None
        self.textColorUniform = None
        self.textureSizeUniform = None
        self.vbo.delete()
        glDeleteVertexArrays(1, [self.vao])

    def load_font(self, fontFilename, fontSize, outlineSize=2 * 64):
        assert os.path.exists(fontFilename)
        self.foreTextures.clear()
        self.backTextures.clear()

        self.face = ft.Face(fontFilename)
        self.face.set_char_size(fontSize)

        self.outlineSize = outlineSize
        self.stroker = ft.Stroker()
        self.stroker.set(outlineSize,
                         ft.FT_STROKER_LINECAPS['FT_STROKER_LINECAP_ROUND'],
                         ft.FT_STROKER_LINEJOINS['FT_STROKER_LINEJOIN_ROUND'],
                         0)

        # load all ASCII characters
        for i in range(128):
            self.load_character(chr(i))

    def load_character(self, character):
        assert self.face is not None
        assert len(character) == 1

        if character not in self.foreTextures:
            # load background glyph
            # the render option will lead to an outline glyph (not rendered)
            self.face.load_char(character, ft.FT_LOAD_FLAGS['FT_LOAD_DEFAULT'])
            backGlyph = ft.FT_Glyph()
            ft.FT_Get_Glyph(self.face.glyph._FT_GlyphSlot, ft.byref(backGlyph))
            backGlyph = ft.Glyph(backGlyph)
            # add border to the glyph
            error = ft.FT_Glyph_StrokeBorder(ft.byref(backGlyph._FT_Glyph),
                                             self.stroker._FT_Stroker, False,
                                             False)
            if error:
                raise ft.FT_Exception(error)

            # the render option will lead to a rendered glyph
            backBitmapGlyph = backGlyph.to_bitmap(
                ft.FT_RENDER_MODES['FT_RENDER_MODE_NORMAL'], 0)

            backBitmap = backBitmapGlyph.bitmap
            backHeight, backWidth = backBitmap.rows, backBitmap.width
            backBitmap = np.array(backBitmap.buffer, dtype=np.uint8).reshape(
                (backHeight, backWidth))

            backTexture = _create_text_texture(backBitmap)

            backSlot = CharacterSlot(backTexture, backBitmapGlyph)
            self.backTextures[character] = backSlot

            # load foreground glyph
            self.face.load_char(character, ft.FT_LOAD_FLAGS['FT_LOAD_RENDER'])
            foreBitmap = self.face.glyph.bitmap
            foreHeight, foreWidth = foreBitmap.rows, foreBitmap.width
            foreBitmap = np.array(foreBitmap.buffer, dtype=np.uint8).reshape(
                (foreHeight, foreWidth))

            foreTexture = _create_text_texture(foreBitmap)

            foreSlot = CharacterSlot(foreTexture, self.face.glyph)
            self.foreTextures[character] = foreSlot

    def get_character(self, ch):
        if ch not in self.foreTextures:
            self.load_character(ch)
        return (self.foreTextures[ch], self.backTextures[ch])

    def _draw_text(self, text, textPos, windowSize, scale, linespread,
                   foreColor, backColor):
        if len(text) == 0:
            return

        blendEnabled = glIsEnabled(GL_BLEND)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        self.renderProgram.use()
        glBindVertexArray(self.vao)
        glActiveTexture(GL_TEXTURE0)

        foreColor = np.asarray(foreColor, np.float32)
        backColor = np.asarray(backColor, np.float32)

        projectionMat = orthographic_projection(0.0, windowSize[0], 0.0,
                                                windowSize[1], self.zNear,
                                                self.zFar)
        self.projectionUniform.update(projectionMat)

        lineY = textPos[1]
        nowX = textPos[0]

        # split text into lines
        lines = text.split('\n')

        for line in lines:

            if len(line) > 0:
                # analyze this line
                bearings = []
                for ch in line:
                    _, backSlot = self.get_character(ch)
                    bearings.append(backSlot.bearing)

                minBearings_X = min(zip(*bearings),
                                    key=operator.itemgetter(0))[0] * scale[0]
                maxBearings_Y = max(zip(*bearings),
                                    key=operator.itemgetter(1))[1] * scale[1]
                nowX = -minBearings_X

                for ch in line:
                    foreSlot, backSlot = self.get_character(ch)

                    # draw the background
                    xpos = nowX + backSlot.bearing[0] * scale[0]
                    yLowerd = (maxBearings_Y - backSlot.bearing[1] * scale[1])
                    ypos = lineY - yLowerd

                    w = backSlot.textureSize[0] * scale[0]
                    h = backSlot.textureSize[1] * scale[1]

                    self.textureSizeUniform.update(np.array((w, h),
                                                            np.float32))
                    backSlot.texture.bind()
                    self.textColorUniform.update(backColor)
                    self.vbo.bind()
                    self.vbo.set_array(
                        _get_rendering_buffer(xpos, ypos, w, h, 0.99))
                    self.vbo.copy_data()
                    self.vbo.unbind()
                    glDrawArrays(GL_TRIANGLES, 0, 6)
                    backSlot.texture.unbind()

                    # draw the foreground
                    xpos = nowX + foreSlot.bearing[0] * scale[0]
                    yLowerd = (maxBearings_Y - foreSlot.bearing[1] * scale[1])
                    ypos = lineY - yLowerd

                    w = foreSlot.textureSize[0] * scale[0]
                    h = foreSlot.textureSize[1] * scale[1]

                    foreSlot.texture.bind()
                    self.textColorUniform.update(foreColor)
                    self.vbo.bind()
                    # the foreground is set closer to the screen so that it
                    # is rendered above the background
                    self.vbo.set_array(
                        _get_rendering_buffer(xpos, ypos, w, h, 0.999))
                    self.vbo.copy_data()
                    self.vbo.unbind()
                    glDrawArrays(GL_TRIANGLES, 0, 6)
                    foreSlot.texture.unbind()

                    # the advance is number of 1/64 pixels
                    nowX += ((foreSlot.advance + 2.0 * self.outlineSize) /
                             64.0) * scale[0]

            yOffset = self.get_character(
                'X')[1].textureSize[1] * scale[1] * linespread
            lineY -= yOffset

        glBindVertexArray(0)

        if not blendEnabled:
            glDisable(GL_BLEND)

    def draw_text(self,
                  text,
                  textPos,
                  windowSize,
                  foreColor=(1.0, 1.0, 1.0),
                  backColor=(0.0, 0.0, 0.0),
                  scale=(1.0, 1.0),
                  linespread=1.5):
        return self._draw_text(text, textPos, windowSize, scale, linespread,
                               foreColor, backColor)
Exemple #13
0
class RectAlignmentControllerView(BaseToolController, GenModel):
    changed = QtCore.Signal()

    def __init__(self, parent, model):
        super(RectAlignmentControllerView, self).__init__()

        self._parent = parent
        self.model_overall = model
        self.model = model.ra

        self.__init_interaction()

        self.gls = None

        self.active = False

    idx_handle_sel = mdlacc(None)
    idx_handle_hover = mdlacc(None)

    #sel_mode = mdlacc(SEL_MODE_NONE)
    behave_mode = mdlacc(MODE_NONE)
    ghost_handle = mdlacc(None)

    def change(self):
        self.changed.emit()

    def __init_interaction(self):
        # Selection / drag handling
        self.idx_handle_sel = None
        #self.sel_mode = SEL_MODE_NONE

        self.behave_mode = MODE_NONE

        # ghost handle for showing placement
        self.ghost_handle = None

    def initialize(self):
        self.__init_interaction()
        self.active = True

    def finalize(self):
        self.active = False

    def initializeGL(self, gls):
        self.gls = gls
        # Basic solid-color program
        self.prog = self.gls.shader_cache.get("vert2", "frag1")
        self.mat_loc = GL.glGetUniformLocation(self.prog, "mat")
        self.col_loc = GL.glGetUniformLocation(self.prog, "color")

        # Build a VBO for rendering square "drag-handles"
        self.vbo_handles_ar = numpy.ndarray(4, dtype=[("vertex", numpy.float32, 2)])
        self.vbo_handles_ar["vertex"] = numpy.array(corners) * HANDLE_HALF_SIZE


        self.vbo_handles = VBO(self.vbo_handles_ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.vao_handles = VAO()
        with self.vbo_handles, self.vao_handles:
            vbobind(self.prog, self.vbo_handles_ar.dtype, "vertex").assign()

        # Build a VBO/VAO for the perimeter
        # We don't initialize it here because it is updated every render
        # 4 verticies for outside perimeter
        # 6 verticies for each dim
        self.vbo_per_dim_ar = numpy.zeros(16, dtype=[("vertex", numpy.float32, 2)])

        self.vbo_per_dim = VBO(self.vbo_per_dim_ar, GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER)

        self.vao_per_dim = VAO()
        with self.vao_per_dim, self.vbo_per_dim:
            vbobind(self.prog, self.vbo_per_dim_ar.dtype, "vertex").assign()

    def im2V(self, pt):
        """Translate Image coordinates to viewport coordinates"""

        if self.model_overall.view_mode:
            ph = projectPoint(self.model.image_matrix, pt)
            return self.viewState.tfW2V(ph)
        else:
            return self.viewState.tfW2V(pt)

    def V2im(self, pt):
        """
        Translate viewport coordinates to image coordinates
        :param pt:
        :return:
        """
        world = self.viewState.tfV2W(pt)

        if self.model_overall.view_mode:
            inv = numpy.linalg.inv(self.model.image_matrix)
            return projectPoint(inv, world)
        else:
            return Vec2(world)
        #inv = numpy.linalg.inv(self.model.image_matrix)
        #return Vec2(inv.dot(pt)[:2])

    def gen_dim(self, idx, always_above = True):
        """
        Generate rendering data for the dimension-lines
        :param idx:
        :return:
        """
        a = self.im2V(self.model.dim_handles[0 + idx])
        b = self.im2V(self.model.dim_handles[1 + idx])

        d = b-a

        delta = (b-a).norm()

        normal = Point2(rotate(math.pi / 2)[:2,:2].dot(delta))

        if always_above:
            if numpy.cross(Vec2(1,0), normal) > 0:
                normal = -normal

        res = numpy.array([
            a + normal * 8,
            a + normal * 20,
            a + normal * 15,
            b + normal * 15,
            b + normal * 8,
            b + normal * 20,
            ])

        return res


    def render(self, vs):
        self.viewState = vs

        disabled = not self.active or self.model_overall.view_mode

        # Perimeter is defined by the first 4 handles
        self.vbo_per_dim_ar["vertex"][:4] = [self.im2V(pt) for pt in self.model.align_handles[:4]]

        # Generate the dimension lines. For ease of use, we always draw the dim-lines above when dims are manual
        # or below when dims are unlocked
        self.vbo_per_dim_ar["vertex"][4:10] = self.gen_dim(0, not self.model.dims_locked)
        self.vbo_per_dim_ar["vertex"][10:16] = self.gen_dim(2, not self.model.dims_locked)

        self.vbo_per_dim.set_array(self.vbo_per_dim_ar)

        # Ugh..... PyOpenGL isn't smart enough to bind the data when it needs to be copied
        with self.vbo_per_dim:
            self.vbo_per_dim.copy_data()


        GL.glDisable(GL.GL_BLEND)

        # ... and draw the perimeter
        with self.vao_per_dim, self.prog:
            GL.glUniformMatrix3fv(self.mat_loc, 1, True, self.viewState.glWMatrix.astype(numpy.float32))

            # Draw the outer perimeter
            if disabled:
                GL.glUniform4f(self.col_loc, 0.8, 0.8, 0.8, 1)
            else:
                GL.glUniform4f(self.col_loc, 0.8, 0.8, 0, 1)
            GL.glDrawArrays(GL.GL_LINE_LOOP, 0, 4)

            # Draw the dimensions
            GL.glUniform4f(self.col_loc, 0.8, 0.0, 0.0, 1)
            GL.glDrawArrays(GL.GL_LINES, 4, 6)

            GL.glUniform4f(self.col_loc, 0.0, 0.0, 0.8, 1)
            GL.glDrawArrays(GL.GL_LINES, 10, 6)

        if disabled:
            return

        # Now draw a handle at each corner
        with self.vao_handles, self.prog:
            for n, i in enumerate(self.model.align_handles):
                # skip nonexistent handles
                if i is None:
                    continue

                is_anchor = IDX_IS_ANCHOR(n)

                corner_pos = self.im2V(i)

                if disabled:
                    color = [0.8, 0.8, 0.8, 1]
                elif self.idx_handle_sel == n:
                    color = [1, 1, 1, 1]
                elif self.idx_handle_hover == n:
                    color = [1, 1, 0, 1]
                else:
                    color = [0.8, 0.8, 0, 0.5]

                self.render_handle(corner_pos, color, is_anchor, True)

                if self.idx_handle_sel == n:
                    self.render_handle(corner_pos, [0,0,0,1], is_anchor, False)

            if self.ghost_handle is not None:
                self.render_handle(self.ghost_handle,[0.8, 0.8, 0, 0.5], True)

            if not self.model.dims_locked:
                for n, i in enumerate(self.model.dim_handles):
                    handle_pos = self.im2V(i)
                    if n == self.idx_handle_sel:
                        color = [1, 1, 1, 1]
                    if n < 2:
                        color = [0.8, 0.0, 0.0, 1]
                    else:
                        color = [0.0, 0.0, 0.8, 1]
                    self.render_handle(handle_pos, color, False, True)
                    if self.idx_handle_sel == n:
                        self.render_handle(corner_pos, [0,0,0,1], is_anchor, False)

    def render_handle(self, position, color, diagonal=False, filled=False):
        if diagonal:
            r = rotate(math.pi/4)
        else:
            r = numpy.identity(3)

        m = self.viewState.glWMatrix.dot(translate(*position).dot(r))
        GL.glUniformMatrix3fv(self.mat_loc, 1, True, m.astype(numpy.float32))
        GL.glUniform4f(self.col_loc, *color)
        GL.glDrawArrays(GL.GL_TRIANGLE_FAN if filled else GL.GL_LINE_LOOP, 0, 4)

    def get_handle_for_mouse(self, pos):
        for n, handle in enumerate(self.model.all_handles()):
            if handle is None:
                continue
            # get the pix-wise BBOX of the handle
            p = self.im2V(handle)
            r = QtCore.QRect(p[0], p[1], 0, 0)
            r.adjust(-HANDLE_HALF_SIZE, -HANDLE_HALF_SIZE, HANDLE_HALF_SIZE, HANDLE_HALF_SIZE)

            # If event inside the bbox
            if r.contains(pos):
                return n
        return None

    def get_line_query_for_mouse(self, pos):
        for n, (p1, p2) in enumerate(self.model.line_iter()):
            p1_v = self.im2V(p1)
            p2_v = self.im2V(p2)

            p, d = project_point_line(pos, p1_v, p2_v)
            if p is not None and d < HANDLE_HALF_SIZE:
                return n, p

        return None, None


    def mousePressEvent(self, event):
        disabled = not self.active or self.model_overall.view_mode
        if disabled:
            return False

        handle = self.get_handle_for_mouse(event.pos())

        if event.button() == QtCore.Qt.LeftButton and event.modifiers() & ADD_MODIFIER:
            idx, p = self.get_line_query_for_mouse(event.pos())
            if idx is not None:
                anchors = self.model.get_anchors(idx)
                if len(anchors) < 2:
                    p = self.V2im(p)
                    idx = new_anchor_index(self.model, idx)

                    cmd = cmd_set_handle_position(self.model, idx, p)
                    self._parent.undoStack.push(cmd)

                    self.idx_handle_sel = idx
                    self.idx_handle_hover = None

        elif event.button() == QtCore.Qt.LeftButton and event.modifiers() & DEL_MODIFIER and (
                        handle is not None and handle >= 4):
            cmd = cmd_set_handle_position(self.model, handle, None)
            self._parent.undoStack.push(cmd)
            #self.model.set_handle(handle, None)

            self.idx_handle_sel = None
            self.idx_handle_hover = None

        elif event.button() == QtCore.Qt.LeftButton:
            self.idx_handle_sel = handle
            self.idx_handle_hover = None
            if handle is not None:
                self.behave_mode = MODE_DRAGGING

        else:
            return False

        return True

    def mouseReleaseEvent(self, event):
        disabled = not self.active or self.model_overall.view_mode
        if disabled:
            return False

        if event.button() == QtCore.Qt.LeftButton and self.behave_mode == MODE_DRAGGING:
            self.behave_mode = MODE_NONE
        else:
            return False

        return True

    def mouseMoveEvent(self, event):
        disabled = not self.active or self.model_overall.view_mode
        if disabled:
            return False

        needs_update = False
        idx = self.get_handle_for_mouse(event.pos())

        if self.ghost_handle is not None:
            self.ghost_handle = None

        if self.behave_mode == MODE_NONE:
            if idx is not None:
                self.idx_handle_hover = idx

            else:
                self.idx_handle_hover = None

            if event.modifiers() & ADD_MODIFIER:
                line_idx, pos = self.get_line_query_for_mouse(event.pos())

                if line_idx is not None:
                    self.ghost_handle = pos

        elif self.behave_mode == MODE_DRAGGING:
            w_pos = self.V2im(Vec2(event.pos()))

            cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, w_pos, merge=True)
            self._parent.undoStack.push(cmd)
            #self.model.set_handle(self.idx_handle_sel, w_pos)


        return False

    def focusOutEvent(self, evt):
        self.idx_handle_sel = None

    def keyPressEvent(self, evt):
        disabled = not self.active or self.model_overall.view_mode
        if disabled:
            return False

        if evt.key() == QtCore.Qt.Key_Escape:
            self.idx_handle_sel = None

        elif self.idx_handle_sel is not None:

            if evt.key() in (QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace) and IDX_IS_ANCHOR(self.idx_handle_sel):

                cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, None)
                self._parent.undoStack.push(cmd)
                #self.model.set_handle(self.idx_handle_sel, None)
                self.idx_handle_sel = None

            # Basic 1-px nudging
            elif evt.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down):
                nudge = {
                    QtCore.Qt.Key_Left:  (-1,  0),
                    QtCore.Qt.Key_Right: ( 1,  0),
                    QtCore.Qt.Key_Up:    ( 0, -1),
                    QtCore.Qt.Key_Down:  ( 0,  1),
                }[evt.key()]

                current = Vec2(self.im2V(self.model.align_handles[self.idx_handle_sel]))
                viewspace = self.V2im(current + nudge)
                cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, viewspace)
                self._parent.undoStack.push(cmd)
                #self.model.set_handle(self.idx_handle_sel, viewspace)

                self.ghost_handle = None

    def next_prev_child(self, next):
        all_handles = self.model.all_handles()

        step = -1
        if next:
            step = 1

        if self.behave_mode == MODE_DRAGGING:
            return True
        else:
            idx = self.idx_handle_sel

            if idx == None:
                return False

            while 1:
                idx = (idx + step) % len(all_handles)

                if all_handles[idx] is not None:
                    self.idx_handle_sel = idx
                    return True