예제 #1
0
    def draw_freehand(self):

        """ Freehand sketching.
        """

        if _ctx._ns["mousedown"]:

            x, y = mouse()
            if self.show_grid:
                x, y = self.grid.snap(x, y)

            if self.freehand_move == True:
                cmd = MOVETO
                self.freehand_move = False
            else:
                cmd = LINETO

            # Add a new LINETO to the path,
            # except when starting to draw,
            # then a MOVETO is added to the path.
            pt = PathElement()
            if cmd != MOVETO:
                pt.freehand = True # Used when mixed with curve drawing.
            else:
                pt.freehand = False
            pt.cmd = cmd
            pt.x = x
            pt.y = y
            pt.ctrl1 = Point(x,y)
            pt.ctrl2 = Point(x,y)
            self._points.append(pt)

            # Draw the current location of the cursor.
            r = 4
            _ctx.nofill()
            _ctx.stroke(self.handle_color)
            _ctx.oval(pt.x-r, pt.y-r, r*2, r*2)
            _ctx.fontsize(9)
            _ctx.fill(self.handle_color)
            _ctx.text(" ("+str(int(pt.x))+", "+str(int(pt.y))+")", pt.x+r, pt.y)

            self._dirty = True

        else:

            # Export the updated drawing,
            # remember to do a MOVETO on the next interaction.
            self.freehand_move = True
            if self._dirty:
                self._points[-1].freehand = False
                self.export_svg()
                self._dirty = False
예제 #2
0
    def insert_point(self, x, y):

        """ Inserts a point on the path at the mouse location.

        We first need to check if the mouse location is on the path.
        Inserting point is time intensive and experimental.

        """

        from plotdevice.lib import pathmatics

        # Do a number of checks distributed along the path.
        # Keep the one closest to the actual mouse location.
        # Ten checks works fast but leads to imprecision in sharp corners
        # and curves closely located next to each other.
        # I prefer the slower but more stable approach.
        n = 100
        closest = None
        dx0 = float(INFINITY)
        dy0 = float(INFINITY)
        for i in range(n):
            t = float(i)/n
            pt = self.path.point(t)
            dx = abs(pt.x-x)
            dy = abs(pt.y-y)
            if dx+dy <= dx0+dy0:
                dx0 = dx
                dy0 = dy
                closest = t

        # Next, scan the area around the approximation.
        # If the closest point is located at 0.2 on the path,
        # we need to scan between 0.1 and 0.3 for a better
        # approximation. If 1.5 was the best guess, scan
        # 1.40, 1.41 ... 1.59 and so on.
        # Each decimal precision takes 20 iterations.
        decimals = [3,4]
        for d in decimals:
            d = 1.0/pow(10,d)

            for i in range(20):
                t = closest-d + float(i)*d*0.1
                if t < 0.0: t = 1.0+t
                if t > 1.0: t = t-1.0
                pt = self.path.point(t)
                dx = abs(pt.x-x)
                dy = abs(pt.y-y)
                if dx <= dx0 and dy <= dy0:
                    dx0 = dx
                    dy0 = dy
                    closest_precise = t

            closest = closest_precise

        # Update the points list with the inserted point.
        p = pathmatics.insert_point(self.path, closest_precise)
        i, t, pt = pathmatics._locate(self.path, closest_precise)
        i += 1
        pt = PathElement()
        pt.cmd = p[i].cmd
        pt.x = p[i].x
        pt.y = p[i].y
        pt.ctrl1 = Point(p[i].ctrl1.x, p[i].ctrl1.y)
        pt.ctrl2 = Point(p[i].ctrl2.x, p[i].ctrl2.y)
        pt.freehand = False
        self._points.insert(i, pt)
        self._points[i-1].ctrl1 = Point(p[i-1].ctrl1.x, p[i-1].ctrl1.y)
        self._points[i+1].ctrl1 = Point(p[i+1].ctrl1.x, p[i+1].ctrl1.y)
        self._points[i+1].ctrl2 = Point(p[i+1].ctrl2.x, p[i+1].ctrl2.y)