예제 #1
0
class HairlineRenderer:
    def __init__(self, view: 'BoardViewWidget') -> None:
        self.__view = view

    def initializeGL(self) -> None:
        self.__dtype = numpy.dtype([('vertex', numpy.float32, 2)])
        self.__shader = self.__view.gls.shader_cache.get(
            "basic_fill_vert", "basic_fill_frag")

        self._va_vao = VAO()
        self._va_batch_vbo = VBO(numpy.array([], dtype=self.__dtype),
                                 GL.GL_STREAM_DRAW)
        GL.glObjectLabel(GL.GL_BUFFER, int(self._va_batch_vbo), -1,
                         "Hairline VA batch VBO")

        with self._va_vao, self._va_batch_vbo:
            VBOBind(self.__shader.program, self.__dtype, "vertex").assign()

    def render_va(self, mat: 'npt.NDArray[numpy.float64]', va: 'VA_xy',
                  col: int) -> None:
        self._va_batch_vbo.set_array(va.buffer()[:])

        with self.__shader.program, self._va_vao, self._va_batch_vbo:
            GL.glUniformMatrix3fv(self.__shader.uniforms.mat, 1, True,
                                  mat.astype(numpy.float32))
            GL.glUniform4ui(self.__shader.uniforms.layer_info, 255, col, 0, 0)
            GL.glDrawArrays(GL.GL_LINES, 0, va.count())
예제 #2
0
class RenderBatch(object):
    def __init__(self, draw_type=GL_QUADS):
        self.count = 0
        self.color_data = []
        self.position_data = []
        self.color_buffer = VBO(np.array([]))
        self.position_buffer = VBO(np.array([]))

        self.draw_type = draw_type


    def draw2d(self, points, color=(0, 0, 0, 1), rotation=0, center=(0, 0)):
        n = len(points)
        self.count += n

        if not isinstance(color[0], (tuple, list)):
            color = [color]*n

        if rotation:
            transform = psi.calc.rotation_matrix(rotation)

            temp = np.array(points) - center
            temp = transform.dot(temp.T).T + center
            points = temp.tolist()

        self.color_data.extend(color)
        self.position_data.extend(points)

    def clear(self):
        self.position_data = []
        self.color_data = []
        self.count = 0

    def render(self):
        self.color_buffer.set_array(np.array(self.color_data, dtype='float32'))
        self.position_buffer.set_array(np.array(self.position_data, dtype='float32'))

        self.color_buffer.bind()
        glColorPointer(4, GL_FLOAT, 0, self.color_buffer)

        self.position_buffer.bind()
        glVertexPointer(2, GL_FLOAT, 0, self.position_buffer)

        glEnableClientState(GL_VERTEX_ARRAY)
        glEnableClientState(GL_COLOR_ARRAY)
        glDrawArrays(self.draw_type, 0, self.count)
        glDisableClientState(GL_COLOR_ARRAY)
        glDisableClientState(GL_VERTEX_ARRAY)
예제 #3
0
class RenderBatch(object):
    def __init__(self, draw_type=GL_QUADS):
        self.count = 0
        self.color_data = []
        self.position_data = []
        self.color_buffer = VBO(np.array([]))
        self.position_buffer = VBO(np.array([]))

        self.draw_type = draw_type

    def draw2d(self, points, color=(0, 0, 0, 1), rotation=0, center=(0, 0)):
        n = len(points)
        self.count += n

        if not isinstance(color[0], (tuple, list)):
            color = [color] * n

        if rotation:
            transform = psi.calc.rotation_matrix(rotation)

            temp = np.array(points) - center
            temp = transform.dot(temp.T).T + center
            points = temp.tolist()

        self.color_data.extend(color)
        self.position_data.extend(points)

    def clear(self):
        self.position_data = []
        self.color_data = []
        self.count = 0

    def render(self):
        self.color_buffer.set_array(np.array(self.color_data, dtype='float32'))
        self.position_buffer.set_array(
            np.array(self.position_data, dtype='float32'))

        self.color_buffer.bind()
        glColorPointer(4, GL_FLOAT, 0, self.color_buffer)

        self.position_buffer.bind()
        glVertexPointer(2, GL_FLOAT, 0, self.position_buffer)

        glEnableClientState(GL_VERTEX_ARRAY)
        glEnableClientState(GL_COLOR_ARRAY)
        glDrawArrays(self.draw_type, 0, self.count)
        glDisableClientState(GL_COLOR_ARRAY)
        glDisableClientState(GL_VERTEX_ARRAY)
예제 #4
0
class BackgroundGridRender:
    def __init__(self):
        self.__width = 0
        self.__height = 0
        self.__vbo_dtype = numpy.dtype([("vertex", numpy.float32, 2)])

    def initializeGL(self, gls, width, height):
        # Get shader

        self.__shader = gls.shader_cache.get("basic_fill_vert", "basic_fill_frag")

        self.__vbo = VBO(numpy.ndarray(0, dtype=self.__vbo_dtype), GL.GL_STATIC_DRAW)
        self.__vao = VAO()

        # Create array of lines sufficient to fill screen
        self.resize(width, height)

    def __get_vbo_data(self):
        va = VA_xy(1024)

        # Unit steps from -n/2..n/2 along X axis. Y lines from -1 to +1
        for i in range(0, self.n_steps * 2, 2):
            i_ = i - int(self.n_steps / 2)
            va.add_line(i_, -1, i_, 1)

        return va

    def resize(self, width, height):
        self.n_steps = max(self.__width, self.__height) // MIN_PIXEL_SPACE
        self.__vbo.set_array(self.__get_vbo_data())

    def set_grid(self, step):
        pass

    def draw(self, transform_matrix):
        pass
예제 #5
0
class RenderBatchOpt(object):
    def __init__(self, draw_type=GL_QUADS):
        self.count = 0
        self.color_buffer = VBO(np.array([]))
        self.vertex_buffer = VBO(np.array([]))
        self.draw_type = draw_type


    def draw2d(self, points, color=(0, 0, 0, 1), rotation=0, center=(0, 0)):
        n = points.shape[0]
        self.count += n

        if rotation:
            transform = psi.calc.rotation_matrix(rotation)

            temp = points - center
            temp = transform.dot(temp.T).T + center
            points = temp.tolist()

        self.color_buffer.set_array(color)
        self.vertex_buffer.set_array(points)

    def clear(self):
        self.color_buffer.set_array(np.array([]))
        self.vertex_buffer.set_array(np.array([]))
        self.count = 0

    def render(self):
        self.color_buffer.bind()
        glColorPointer(4, GL_FLOAT, 0, self.color_buffer)

        self.vertex_buffer.bind()
        glVertexPointer(2, GL_FLOAT, 0, self.vertex_buffer)

        glEnableClientState(GL_VERTEX_ARRAY)
        glEnableClientState(GL_COLOR_ARRAY)
        glDrawArrays(self.draw_type, 0, self.count)
        glDisableClientState(GL_COLOR_ARRAY)
        glDisableClientState(GL_VERTEX_ARRAY)
예제 #6
0
class RenderBatchOpt(object):
    def __init__(self, draw_type=GL_QUADS):
        self.count = 0
        self.color_buffer = VBO(np.array([]))
        self.vertex_buffer = VBO(np.array([]))
        self.draw_type = draw_type

    def draw2d(self, points, color=(0, 0, 0, 1), rotation=0, center=(0, 0)):
        n = points.shape[0]
        self.count += n

        if rotation:
            transform = psi.calc.rotation_matrix(rotation)

            temp = points - center
            temp = transform.dot(temp.T).T + center
            points = temp.tolist()

        self.color_buffer.set_array(color)
        self.vertex_buffer.set_array(points)

    def clear(self):
        self.color_buffer.set_array(np.array([]))
        self.vertex_buffer.set_array(np.array([]))
        self.count = 0

    def render(self):
        self.color_buffer.bind()
        glColorPointer(4, GL_FLOAT, 0, self.color_buffer)

        self.vertex_buffer.bind()
        glVertexPointer(2, GL_FLOAT, 0, self.vertex_buffer)

        glEnableClientState(GL_VERTEX_ARRAY)
        glEnableClientState(GL_COLOR_ARRAY)
        glDrawArrays(self.draw_type, 0, self.count)
        glDisableClientState(GL_COLOR_ARRAY)
        glDisableClientState(GL_VERTEX_ARRAY)
예제 #7
0
파일: rectalign.py 프로젝트: pcbre/pcbre
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
예제 #8
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
예제 #9
0
파일: textrender.py 프로젝트: pcbre/pcbre
class TextBatch:
    __tag = namedtuple("__tag", ['mat', 'inf'])

    def __init__(self, tr: 'TextRender') -> None:
        self.__text_render = tr

        self.__elem_count = 0
        self.__color = (255, COL_TEXT, 0, 0)

    def initializeGL(self) -> None:
        self.vbo = VBO(
            numpy.ndarray([], dtype=self.__text_render.buffer_dtype),
            GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)
        self.vao = VAO()

        self._va = VA_tex(1024)

        with self.vao, self.vbo:
            self.__text_render.b1.assign()
            self.__text_render.b2.assign()

    def restart(self) -> None:
        self.__strs: List[TextBatch.__tag] = []

    # TEMP
    def get_string(self, text: str) -> '_StringMetrics':
        """

        :param text:
        :return:
        :returns: _StringMetrics
        """
        return self.__text_render.getStringMetrics(text)

    def add(self, mat: 'npt.NDArray[numpy.float64]',
            str_info: '_StringMetrics') -> None:
        """
        Queues a text string to be rendered in the batch
        :param mat: location matrix
        :param str_info:
        :type str_info: _StringMetrics
        :return:
        """
        self.__strs.append(TextBatch.__tag(mat, str_info))

    def prepare(self) -> None:
        self.__text_render.updateTexture()

        self._va.clear()

        for mat, str_info in self.__strs:
            self._va.extend_project(mat, str_info.arr)

        self.vbo.set_array(self._va.buffer()[:])
        self.vbo.bind()

        self.__elem_count = self._va.count()

    def render(self, mat: 'npt.NDArray[numpy.float64]') -> None:
        with self.__text_render.sdf_shader.program, self.__text_render.tex.on(
                GL.GL_TEXTURE_2D), self.vao:
            GL.glUniform1i(self.__text_render.sdf_shader.uniforms.tex1, 0)
            GL.glUniformMatrix3fv(self.__text_render.sdf_shader.uniforms.mat,
                                  1, True, mat.astype(numpy.float32))
            GL.glUniform4ui(self.__text_render.sdf_shader.uniforms.layer_info,
                            *self.__color)

            GL.glDrawArrays(GL.GL_TRIANGLES, 0, self.__elem_count)
예제 #10
0
파일: viaview.py 프로젝트: pcbre/pcbre
class THRenderer:
    def __init__(self, parent_view: 'BoardViewWidget') -> None:
        self.parent = parent_view

    def initializeGL(self, glshared: 'GLShared') -> None:

        self._filled_shader = glshared.shader_cache.get(
            "via_filled_vertex_shader", "via_filled_fragment_shader")

        self._outline_shader = glshared.shader_cache.get(
            "via_outline_vertex_shader", "basic_fill_frag")

        # Build geometry for filled rendering using the frag shader for circle borders
        filled_points = [
            ((-1, -1), ),
            ((1, -1), ),
            ((-1, 1), ),
            ((1, 1), ),
        ]
        ar = numpy.array(filled_points, dtype=[("vertex", numpy.float32, 2)])

        self._sq_vbo = VBO(ar, GL.GL_STATIC_DRAW)
        GL.glObjectLabel(GL.GL_BUFFER, int(self._sq_vbo), -1, "Via SQ VBO")

        # Build geometry for outline rendering
        outline_points = []
        for i in numpy.linspace(0, math.pi * 2, N_OUTLINE_SEGMENTS, False):
            outline_points.append(((math.cos(i), math.sin(i)), ))

        outline_points_array = numpy.array(outline_points,
                                           dtype=[("vertex", numpy.float32, 2)
                                                  ])

        self._outline_vbo = VBO(outline_points_array, GL.GL_STATIC_DRAW)
        GL.glObjectLabel(GL.GL_BUFFER, int(self._sq_vbo), -1,
                         "Via Outline VBO")

        self.__dtype = numpy.dtype([
            ("pos", numpy.float32, 2),
            ("r", numpy.float32),
            ("r_inside_frac_sq", numpy.float32),
        ])

        self.__filled_vao = VAO()
        self.__outline_vao = VAO()

        with self.__filled_vao, self._sq_vbo:
            VBOBind(self._filled_shader.program, self._sq_vbo.data.dtype,
                    "vertex").assign()

        # Use a fake array to get a zero-length VBO for initial binding
        filled_instance_array = numpy.ndarray(0, dtype=self.__dtype)
        self.filled_instance_vbo = VBO(filled_instance_array)
        GL.glObjectLabel(GL.GL_BUFFER, int(self._sq_vbo), -1,
                         "Via Filled Instance VBO")

        with self.__filled_vao, self.filled_instance_vbo:
            VBOBind(self._filled_shader.program, self.__dtype, "pos",
                    div=1).assign()
            VBOBind(self._filled_shader.program, self.__dtype, "r",
                    div=1).assign()
            VBOBind(self._filled_shader.program,
                    self.__dtype,
                    "r_inside_frac_sq",
                    div=1).assign()

        with self.__outline_vao, self._outline_vbo:
            VBOBind(self._outline_shader.program, self._outline_vbo.data.dtype,
                    "vertex").assign()

        # Build instance for outline rendering
        # We don't have an inner 'r' for this because we just do two instances per vertex

        # Use a fake array to get a zero-length VBO for initial binding
        outline_instance_array = numpy.ndarray(0, dtype=self.__dtype)
        self.outline_instance_vbo = VBO(outline_instance_array)
        GL.glObjectLabel(GL.GL_BUFFER, int(self.outline_instance_vbo), -1,
                         "Via Outline Instance VBO")

        with self.__outline_vao, self.outline_instance_vbo:
            VBOBind(self._outline_shader.program, self.__dtype, "pos",
                    div=1).assign()
            VBOBind(self._outline_shader.program, self.__dtype, "r",
                    div=1).assign()

    def render_filled(self,
                      mat: 'npt.NDArray[numpy.float64]',
                      va: 'VA_xy',
                      color: Tuple[float, float, float] = COL_VIA) -> None:
        self.filled_instance_vbo.set_array(va.buffer()[:])

        try:
            with self._filled_shader.program:
                with self.__filled_vao:
                    with self.filled_instance_vbo:
                        with self._sq_vbo:
                            GL.glUniformMatrix3fv(
                                self._filled_shader.uniforms.mat, 1, True,
                                mat.astype(numpy.float32))
                            GL.glUniform1ui(self._filled_shader.uniforms.color,
                                            color)
                            GL.glDrawArraysInstancedBaseInstance(
                                GL.GL_TRIANGLE_STRIP, 0, 4, va.count(), 0)
        except OpenGL.error.GLError as e:
            print("Threw OGL error:", e)

    def render_outlines(self, mat: 'npt.NDArray[numpy.float64]',
                        va: 'VA_xy') -> None:
        if not va.count():
            return

        self.outline_instance_vbo.set_array(va.buffer()[:])
        self.outline_instance_vbo.bind()

        with self._outline_shader.program, self.__outline_vao, self.outline_instance_vbo, self._sq_vbo:
            GL.glUniformMatrix3fv(self._outline_shader.uniforms.mat, 1, True,
                                  mat.astype(numpy.float32))
            GL.glUniform4ui(self._outline_shader.uniforms.layer_info, 255,
                            COL_SEL, 0, 0)
            GL.glDrawArraysInstanced(GL.GL_LINE_LOOP, 0, N_OUTLINE_SEGMENTS,
                                     va.count())
예제 #11
0
파일: traceview.py 프로젝트: pcbre/pcbre
class TraceRender:
    def __init__(self, parent_view: 'BoardViewWidget') -> None:
        self.parent = parent_view
        self.restart()

    def initializeGL(self, gls: 'GLShared') -> None:
        # Build trace vertex VBO and associated vertex data
        dtype = [("vertex", numpy.float32, 2), ("ptid", numpy.uint32)]
        self.working_array = numpy.zeros(NUM_ENDCAP_SEGMENTS * 2 + 2,
                                         dtype=dtype)
        self.trace_vbo = VBO(self.working_array, GL.GL_DYNAMIC_DRAW)
        GL.glObjectLabel(GL.GL_BUFFER, int(self.trace_vbo), -1,
                         "Thickline Trace VBO")

        # Generate geometry for trace and endcaps
        # ptid is a variable with value 0 or 1 that indicates which endpoint the geometry is associated with
        self.__build_trace()

        self.__attribute_shader_vao = VAO(
            debug_name="Thickline attribute shader VAO")
        shader = gls.shader_cache.get("line_vertex_shader",
                                      "basic_fill_frag",
                                      defines={"INPUT_TYPE": "in"})
        assert shader is not None
        self.__attribute_shader: 'EnhShaderProgram' = shader

        # Now we build an index buffer that allows us to render filled geometry from the same
        # VBO.
        arr = []
        for i in range(NUM_ENDCAP_SEGMENTS - 1):
            arr.append(0)
            arr.append(i + 2)
            arr.append(i + 3)

        for i in range(NUM_ENDCAP_SEGMENTS - 1):
            arr.append(1)
            arr.append(i + NUM_ENDCAP_SEGMENTS + 2)
            arr.append(i + NUM_ENDCAP_SEGMENTS + 3)

        arr.append(2)
        arr.append(2 + NUM_ENDCAP_SEGMENTS - 1)
        arr.append(2 + NUM_ENDCAP_SEGMENTS)
        arr.append(2 + NUM_ENDCAP_SEGMENTS)
        arr.append(2 + NUM_ENDCAP_SEGMENTS * 2 - 1)
        arr.append(2)

        arr2 = numpy.array(arr, dtype=numpy.uint32)
        self.index_vbo = VBO(arr2, target=GL.GL_ELEMENT_ARRAY_BUFFER)
        GL.glObjectLabel(GL.GL_BUFFER, int(self.index_vbo), -1,
                         "Thickline Index VBO")

        self.instance_dtype = numpy.dtype([
            ("pos_a", numpy.float32, 2),
            ("pos_b", numpy.float32, 2),
            ("thickness", numpy.float32),
            # ("color", numpy.float32, 4)
        ])

        # Use a fake array to get a zero-length VBO for initial binding
        instance_array: 'npt.NDArray[Any]' = numpy.ndarray(
            0, dtype=self.instance_dtype)
        self.instance_vbo = VBO(instance_array)
        GL.glObjectLabel(GL.GL_BUFFER, int(self.instance_vbo), -1,
                         "Thickline Instance VBO")

        with self.__attribute_shader_vao, self.trace_vbo:
            VBOBind(self.__attribute_shader.program, self.trace_vbo.dtype,
                    "vertex").assign()
            VBOBind(self.__attribute_shader.program, self.trace_vbo.dtype,
                    "ptid").assign()

        with self.__attribute_shader_vao, self.instance_vbo:
            self.__bind_pos_a = VBOBind(self.__attribute_shader.program,
                                        self.instance_dtype,
                                        "pos_a",
                                        div=1)
            self.__bind_pos_b = VBOBind(self.__attribute_shader.program,
                                        self.instance_dtype,
                                        "pos_b",
                                        div=1)
            self.__bind_thickness = VBOBind(self.__attribute_shader.program,
                                            self.instance_dtype,
                                            "thickness",
                                            div=1)
            # vbobind(self.__attribute_shader, self.instance_dtype, "color", div=1).assign()
            self.__base_rebind(0)

            self.index_vbo.bind()

    def __base_rebind(self, base: int) -> None:
        self.__bind_pos_a.assign(base)
        self.__bind_pos_b.assign(base)
        self.__bind_thickness.assign(base)

    def restart(self) -> None:
        pass

    def __build_trace(self) -> None:
        # Update trace VBO
        self.working_array["vertex"][0] = (0, 0)
        self.working_array["ptid"][0] = 0
        self.working_array["vertex"][1] = (0, 0)
        self.working_array["ptid"][1] = 1

        end = Vec2(1, 0)
        for i in range(0, NUM_ENDCAP_SEGMENTS):
            theta = math.pi * i / (NUM_ENDCAP_SEGMENTS - 1) + math.pi / 2
            m = rotate(theta).dot(end.homol())
            self.working_array["vertex"][2 + i] = m[:2]
            self.working_array["ptid"][2 + i] = 0
            self.working_array["vertex"][2 + i + NUM_ENDCAP_SEGMENTS] = -m[:2]
            self.working_array["ptid"][2 + i + NUM_ENDCAP_SEGMENTS] = 1

        # Force data copy
        self.trace_vbo.bind()
        self.trace_vbo.set_array(self.working_array)

    def __render_va_inner(self, col: int, is_outline: bool, first: int,
                          count: int) -> None:
        GL.glUniform4ui(self.__attribute_shader.uniforms.layer_info, 255, col,
                        0, 0)

        if has_base_instance:
            # Many instances backport glDrawElementsInstancedBaseInstance
            # This is faster than continually rebinding, so support if possible
            if not is_outline:
                # filled traces come first in the array
                GL.glDrawElementsInstancedBaseInstance(GL.GL_TRIANGLES,
                                                       TRIANGLES_SIZE,
                                                       GL.GL_UNSIGNED_INT,
                                                       ctypes.c_void_p(0),
                                                       count, first)
            else:
                # Then outline traces. We reuse the vertex data for the outside
                GL.glDrawArraysInstancedBaseInstance(GL.GL_LINE_LOOP, 2,
                                                     NUM_ENDCAP_SEGMENTS * 2,
                                                     count, first)
        else:
            with self.instance_vbo:
                if not is_outline:
                    # filled traces come first in the array
                    self.__base_rebind(first)
                    GL.glDrawElementsInstanced(GL.GL_TRIANGLES, TRIANGLES_SIZE,
                                               GL.GL_UNSIGNED_INT,
                                               ctypes.c_void_p(0), count)
                else:
                    self.__base_rebind(first)
                    # Then outline traces. We reuse the vertex data for the outside
                    GL.glDrawArraysInstanced(GL.GL_LINE_LOOP, 2,
                                             NUM_ENDCAP_SEGMENTS * 2, count)

    def render_va(self,
                  va: 'VA_thickline',
                  mat: 'npt.NDArray[numpy.float64]',
                  col: int,
                  is_outline: bool = False,
                  first: int = 0,
                  count: 'Optional[int]' = None) -> None:
        GL.glPushDebugGroup(GL.GL_DEBUG_SOURCE_APPLICATION, 0, -1,
                            "Thickline Draw")
        assert self.instance_dtype.itemsize == va.stride

        self.instance_vbo.set_array(va.buffer()[:])

        if count is None:
            count = va.count() - first

        with self.__attribute_shader.program, self.__attribute_shader_vao, self.instance_vbo:
            GL.glUniformMatrix3fv(self.__attribute_shader.uniforms.mat, 1,
                                  True, mat.astype(numpy.float32))

            self.__render_va_inner(col, is_outline, first, count)
        GL.glPopDebugGroup()
예제 #12
0
        uniforms['projection'].update(
            camera.get_projection_matrix(aspect, zNear, zFar))
        uniforms['view'].update(camera.get_view_matrix())
        uniforms['objectColor'].update(clothColor)

        rightShift = grid.numCols * grid.initLength / 2.0
        downShift = grid.numRows * grid.initLength / 2.0
        uniforms['model'].update(translate(-rightShift, downShift, 0.0))

        # compute new parameters
        grid.get_new_state_and_update(deltaTime)
        # compute normal
        gridArray = np.concatenate([grid.pos.squeeze(), grid.normal], axis=2)
        gridArray = gridArray.flatten().astype(np.float32)
        # update buffer
        gridVBO.set_array(gridArray)
        gridVBO.bind()
        gridVBO.copy_data()
        gridVBO.unbind()

        glBindVertexArray(gridVAO)
        glDrawElements(GL_TRIANGLES, elementArray.size, GL_UNSIGNED_INT,
                       ctypes.c_void_p(0))
        glBindVertexArray(0)

        # respond key press
        keyboard_respond_func()
        # tell glfw to poll and process window events
        glfw.poll_events()
        # swap frame buffer
        glfw.swap_buffers(theWindow)
예제 #13
0
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)
예제 #14
0
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)
예제 #15
0
파일: debugrender.py 프로젝트: pcbre/pcbre
class DebugRender:
    def __init__(self, boardview: 'BoardViewWidget') -> None:
        self.debug_draw = False
        self.debug_draw_bbox = True
        self.parent = boardview

    def initializeGL(self, gls: 'GLShared') -> None:
        self.shader = gls.shader_cache.get("vert2", "frag1")

        self.vao = VAO()

        self.vbo = VBO(bytes(), usage=GL.GL_STREAM_DRAW)

        # bind the shader
        with self.vao, self.vbo:
            loc = GL.glGetAttribLocation(self.shader.program, "vertex")
            assert loc != -1
            GL.glEnableVertexAttribArray(loc)

            # TODO - HACK. AttribPointer is dependent on ABI data layout.
            # Should be common on all sane archs, but this should be fetched on demand
            GL.glVertexAttribPointer(loc, 2, GL.GL_FLOAT, False, 8,
                                     ctypes.c_void_p(0))
            GL.glVertexAttribDivisor(loc, 0)

    def render(self) -> None:

        if not self.debug_draw:
            return

        # Create two X-Y vertex buffers; one for selected features, and one non-selected
        buf = VA_xy(1024)
        buf_sel = VA_xy(1024)

        t_debug_bbox = Timer()
        t_debug_bbox_add = Timer()
        with t_debug_bbox:
            if self.debug_draw_bbox:

                # Build a list of all bboxes we're going to draw
                bboxes = []
                for i in self.parent.getVisible():
                    bboxes.append((i.bbox, i in self.parent.selectionList))

                    if isinstance(i, Component):
                        for j in i.get_pads():
                            bboxes.append((j.bbox, j
                                           in self.parent.selectionList))

                with t_debug_bbox_add:
                    # Add bboxes to buffers
                    for bbox, selected in bboxes:
                        cx = (bbox.left + bbox.right) / 2
                        cy = (bbox.bottom + bbox.top) / 2
                        h = bbox.top - bbox.bottom
                        w = bbox.right - bbox.left

                        dest = buf
                        if selected:
                            dest = buf_sel

                        dest.add_aligned_box(cx, cy, w, h)

        t_debug_draw = Timer()
        with t_debug_draw:
            # TODO: HACK
            # PyOpenGL doesn't know how to deal with a cffi buffer
            buf_r = buf.buffer()[:] + buf_sel.buffer()[:]
            self.vbo.set_array(buf_r, None)

            # Now render the two buffers
            with self.shader.program, self.vao, self.vbo:
                GL.glUniformMatrix3fv(
                    self.shader.uniforms.mat, 1, True,
                    self.parent.viewState.glMatrix.astype(numpy.float32))

                GL.glUniform4f(self.shader.uniforms.color, 255, 0, 255, 255)
                GL.glDrawArrays(GL.GL_LINES, 0, buf.count())

                GL.glUniform4f(self.shader.uniforms.color, 255, 255, 255, 255)
                GL.glDrawArrays(GL.GL_LINES, buf.count(), buf_sel.count())

        print("debug draw time: %f(%f) %f" %
              (t_debug_bbox.interval, t_debug_bbox_add.interval,
               t_debug_draw.interval))
예제 #16
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