Exemplo n.º 1
0
 def test_constructor_copy(self):
     v1 = Vector2(angle=rad(30), m=1)
     v2 = Vector2(v1)
     self.assert_equals(v2.x, v1.x)
     self.assert_equals(v2.y, v1.y)
     self.assert_equals(v2.m, v1.m)
     self.assert_equals(v2.a, v1.a)
Exemplo n.º 2
0
    def precalc_node(self, node):
        """
        Precalculate node properties that are needed by the layout algorithms.
        """

        E = self._eval_func(node)

        node.fontsize = E('fontSize')
        self._precalc_text(node)

        padx = E('textPadX')
        pady = E('textPadY')

        node.width = node.textwidth + padx * 2
        node.height = node.textheight + pady * 2
        node.bboxwidth = node.width
        node.bboxheight = node.height

        node._textxoffs = padx
        node._textyoffs = pady

        node.text_has_background = True

        y = node.bboxheight / 2
        node._conn_point_left = Vector2(0, y)
        node._conn_point_right = Vector2(node.bboxwidth, y)
Exemplo n.º 3
0
 def _calcbbox(self):
     m = sys.maxint
     topleft = Vector2(m, m)
     bottomright = Vector2(-m, -m)
     self._calcbbox_recurse(self.root, topleft, bottomright)
     return Rectangle(topleft.x, topleft.y, bottomright.x - topleft.x,
                      bottomright.y - topleft.y)
Exemplo n.º 4
0
    def precalc_node(self, node):
        """
        Precalculate node properties that are needed by the layout and
        colorizer algorithms.
        """
        super(LineNodeDrawer, self).precalc_node(node)

        node.text_has_background = False

        y = node.height
        node._conn_point_left = Vector2(0, y)
        node._conn_point_right = Vector2(node.width, y)
Exemplo n.º 5
0
    def precalc_node(self, node):
        super(BoxNodeDrawer, self).precalc_node(node)

        E = self._eval_func(node)

        node._boxdepth = E('boxDepth')

        # Make the bounding box big enough so that the shadow can fit in
        # too -- the actual coordinate calculations will happen later
        node.bboxwidth += node._boxdepth
        node.bboxheight += node._boxdepth

        y = node.bboxheight / 2
        node._conn_point_left = Vector2(0, y)
        node._conn_point_right = Vector2(node.bboxwidth, y)
Exemplo n.º 6
0
 def test_normalize(self):
     v = Vector2(4, -4)
     self.assert_equals(5.65685424949238, v.m)
     self.assert_equals(45.0, deg(v.a))
     v.normalize()
     self.assert_equals(1.0, v.m)
     self.assert_equals(45.0, deg(v.a))
Exemplo n.º 7
0
    def _calc_arc_segment(cx, cy, x1, y1, x4, y4):
        ax = x1 - cx
        ay = y1 - cy
        bx = x4 - cx
        by = y4 - cy
        q1 = ax * ax + ay * ay
        q2 = q1 + ax * bx + ay * by

        d = ax * by - ay * bx
        if d == 0:
            d = 1e-15
        k2 = 1.3333333333 * (math.sqrt(2 * q1 * q2) - q2) / d

        x2 = cx + ax - k2 * ay
        y2 = cy + ay + k2 * ax
        x3 = cx + bx + k2 * by
        y3 = cy + by - k2 * bx

        return [Vector2(x1, y1), Vector2(x2, y2),
                Vector2(x3, y3), Vector2(x4, y4)]
Exemplo n.º 8
0
def calc_regular_polygon_points(cx, cy, r, numsides, rotation=0):
    """ Calculates the vertices of a regular n-sided polygon. """

    points = []
    a = 2 * math.pi / numsides
    sa = math.radians(rotation)

    for i in range(numsides + 1):
        x = cx + r * math.cos(sa + a * i)
        y = cy - r * math.sin(sa + a * i)
        points.append(Vector2(x, y))

    return points
Exemplo n.º 9
0
def intersect(p1, p2, p3, p4):
    c = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x)

    # The two lines are parallel
    if abs(c) < 1e-15:
        return None

    a = p1.x * p2.y - p1.y * p2.x
    b = p3.x * p4.y - p3.y * p4.x

    x = (a * (p3.x - p4.x) - (p1.x - p2.x) * b) / c
    y = (a * (p3.y - p4.y) - (p1.y - p2.y) * b) / c

    return Vector2(x, y)
Exemplo n.º 10
0
def slice_shape(points, y, h, ystep):
    """
    Calculate the intersections of a convex shape and a set of
    equidistant horizontal lines.

    `points`
       points defining the shape segments
    `y`
       topmost (lowest value) y coordinate of the original shape
    `h`
       height of the original shape
    `ystep`
        vertical distance between horizontal lines

    Only point-pairs are returned, single-point intersections are
    omitted (e.g. when a horizontal line goes exactly through a single
    vertex).

    The point-pairs are returned in a 3-dimensional array:

        points = [[l1p1, l2p2], [l2p1, l2p2], ... [lnp1, lnp2]]

    The points have the following properties:

        lnp1.y  = lnp2.y
        lnp1.x <= lnp2.x
        lnpm.y  < l(n + 1)pm.y
    """
    # Close the shape if the shape is already closed that doesn't
    # affect the algorithm
    points.append(points[0])

    numlines = (int) (float(h) / ystep)

    # Special case when ystep > h / 2
    if numlines == 1:
        numlines = 2

    # Center lines to the vertical center of the shape
    y = y + (h - (numlines - 1) * ystep) / 2.

    # Iterate through all horizontal lines (starting from the lowest y
    # coordinate) and calculate the two intersection points of each line
    # with the shape segments (some lines may result in zero or one
    # intersections, these will be omitted).

    intersections = []

    for l in range(numlines):
        pointpair = []

        for i in range(len(points) - 1):
            p1 = points[i]
            p2 = points[i + 1]

            if p1.y > p2.y:
                p1, p2 = p2, p1

            if y >= p1.y and y < p2.y:
                # Special case for p1.x = p2.x (vertical line)
                dx = p2.x - p1.x + 1e-5

                # Calculate the intersection of a horizontal line and a
                # single shape segment
                dy = p2.y - p1.y
                m =  dy / dx
                x =  p1.x + 1 / m * (y - p1.y)

                pointpair.append(Vector2(x, y))

                # There should be two (or zero) intersections for each
                # horizontal line
                if len(pointpair) == 2:
                    if pointpair[0].x > pointpair[1].x:
                        pointpair[0], pointpair[1] = pointpair[1], pointpair[0]

                    intersections.append(pointpair)
                    break
        y += ystep

    return intersections
Exemplo n.º 11
0
    def _draw(self, node, direction=None):
        """
        Draw a curved connection between a node and its child nodes.
        """

        E = self._eval_func(node)

        children = node.getchildren(direction)
        if not children:
            return

        linewidth = E('lineWidth')

        _ctx.autoclosepath(True)
        _ctx.stroke(node.connectioncolor)
        _ctx.fill(node.connectioncolor)
        _ctx.strokewidth(linewidth)

        firstchild = children[0]
        lastchild = children[-1]

        direction = firstchild.direction()
        opp_direction = opposite_dir(direction)
        x1, y1 = node.connection_point(direction)
        xfirst, yfirst = firstchild.connection_point(opp_direction)

        # Special case: draw straight line if there's only one child
        if len(children) == 1:
            _ctx.line(x1, y1, xfirst, yfirst)
            return

        # Calculate junction point position
        jx = x1 + (xfirst - x1) * E('junctionXFactor')
        jy = y1

        # Draw line from parent node to junction point
        _ctx.line(x1, y1, jx, jy)

        # Limit first & last corner radius to the available area
        ylast = lastchild.connection_point(opp_direction)[1]
        ysecond = children[1].connection_point(opp_direction)[1]
        ypenultimate = children[-2].connection_point(opp_direction)[1]

        # Starting corner radius
        cornerPad = E('cornerPad')
        r = min(E('cornerRadius'), abs(jx - xfirst) - cornerPad)
        r = max(r, 0)

        # Adjusted first (top) corner radius
        r1 = min(r, abs(yfirst - jy) - cornerPad)
        r1 = max(r1, 0)
        if ysecond < jy:
            r1 = min(r, abs(yfirst - ysecond) - cornerPad)
            r1 = max(r1, 0)

        # Adjusted last (bottom) corner radius
        r2 = min(r, abs(ylast - jy) - cornerPad)
        r2 = max(r2, 0)
        if ypenultimate > jy:
            r2 = min(r, abs(ylast - ypenultimate) - cornerPad)
            r2 = max(r2, 0)

        # Draw main branch as a single path to ensure line continuity
        p1 = Vector2(jx, yfirst + r1)
        p2 = Vector2(jx, ylast - r2)
        segments = [[p1, p2]]

        corner_style = E('cornerStyle')

        for i, child in enumerate(children):
            direction = child.direction()
            opp_direction = opposite_dir(direction)

            x2, y2 = child.connection_point(opp_direction)
            if direction == Direction.Left:
                x2 -= linewidth / 2
            elif direction == Direction.Right:
                x2 += linewidth / 2

            # Draw corners
            if direction == Direction.Left:
                a1 = 90
                da = -90
                dx1 = r1 * 2
                dx2 = r2 * 2
            else:
                a1 = da = 90
                dx1 = dx2 = 0

            x1 = jx
            if child is firstchild:
                x1 += -r1 if direction == Direction.Left else r1

                if (corner_style == 'square' or abs(y2 - jy) < .001):
                    p1 = Vector2(jx, y2)
                    p2 = Vector2(jx, y2 + r1)
                    segments.insert(0, [p1, p2])

                    p1 = Vector2(x1, y2)
                    p2 = Vector2(jx, y2)
                    segments.insert(0, [p1, p2])

                elif corner_style == 'beveled':
                    p1 = Vector2(x1, y2)
                    p2 = Vector2(jx, y2 + r1)
                    segments.insert(0, [p1, p2])

                elif corner_style == 'rounded':
                    arc = arcpath(jx - dx1, y2, r1 * 2, r1 * 2, a1, da)
                    segments = arc + segments

                p1 = Vector2(x2, y2)
                p2 = Vector2(x1, y2)
                segments.insert(0, [p1, p2])

            elif child is lastchild:
                x1 += -r2 if direction == Direction.Left else r2

                if (corner_style == 'square' or abs(y2 - jy) < .001):
                    p1 = Vector2(jx, y2 - r2)
                    p2 = Vector2(jx, y2)
                    segments.append([p1, p2])

                    p1 = Vector2(jx, y2)
                    p2 = Vector2(x1, y2)
                    segments.append([p1, p2])

                elif corner_style == 'beveled':
                    p1 = Vector2(jx, y2 - r2)
                    p2 = Vector2(x1, y2)
                    segments.append([p1, p2])

                elif corner_style == 'rounded':
                    arc = arcpath(jx - dx2, y2 - r2 * 2, r2 * 2, r2 * 2,
                                  a1 + da, da)
                    segments = segments + arc

                p1 = Vector2(x1, y2)
                p2 = Vector2(x2, y2)
                segments.append([p1, p2])

            else:
                _ctx.line(x1, y2, x2, y2)

        # Draw main branch path
        _ctx.nofill()

        path = createpath(_ctx, segments, close=False)
        _ctx.drawpath(path)

        # Draw junction point
        style = E('junctionStyle')
        if style == 'none':
            return

        r = E('junctionRadius')
        r2 = r / 2.

        _ctx.fill(E('junctionFillColor'))
        _ctx.stroke(E('junctionStrokeColor'))
        _ctx.strokewidth(E('junctionStrokeWidth'))

        if style == 'square':
            _ctx.rect(jx - r2, jy - r2, r, r)

        elif style == 'disc':
            _ctx.oval(jx - r2, jy - r2, r, r)

        elif style == 'diamond':
            _ctx.beginpath(jx, jy - r2)
            _ctx.lineto(jx + r2, jy)
            _ctx.lineto(jx, jy + r2)
            _ctx.lineto(jx - r2, jy)
            _ctx.lineto(jx, jy - r2)
            _ctx.endpath()

        # Draw junction sign
        sign = E('junctionSign')
        if sign == 'none':
            return

        _ctx.stroke(E('junctionSignColor'))

        d = E('junctionSignSize') / 2.
        _ctx.strokewidth(E('junctionSignStrokeWidth'))

        if sign in ('minus', 'plus'):
            _ctx.line(jx - d, jy, jx + d, jy)

        if sign == 'plus':
            _ctx.line(jx, jy - d, jx, jy + d)
Exemplo n.º 12
0
def rounded_rect(x, y, w, h, r):
    points = [Vector2(x, y), Vector2(x + w, y),
              Vector2(x + w, y + h), Vector2(x, y + h)]

    return round_poly(points, r)
Exemplo n.º 13
0
 def test_constructor_cartesian2(self):
     v = Vector2(4, -4)
     self.assert_equals(5.6568542494923806, v.m)
     self.assert_equals(45.0, deg(v.a))
Exemplo n.º 14
0
 def test_rotate_positive(self):
     v = Vector2(4, -4)
     v.rotate(rad(-15))
     self.assert_equals(30.0, deg(v.a))
Exemplo n.º 15
0
def round_corner(p1, p2, p3, r):
    """ Calculate the Bezier-path segments defining a circular rounded
    corner of radius ``r`` of the two straight line segments `(p1,p2)`
    and `(p2,p3)`.

    Fit a circle of radius ``r`` into the triangle defined by points
    ``p1``, ``p2`` and ``p3`` so that segments `(p1,p2)` and `(p3,p2)`
    are tangents to the circle, then return the Bezier-path of the arc
    segment of radius ``r`` between points ``p1`` and ``p3``, facing
    ``p1``.
    """

    # Optimization for the 90 degree case when the two segments are
    # parallel to the axes (~2.3x speedup). This is used frequently for
    # drawing rounded rectangles.
    d = 1e-5
    s1_horiz = abs(p1.y - p2.y) < d
    s2_horiz = abs(p2.y - p3.y) < d
    s1_vert = abs(p1.x - p2.x) < d
    s2_vert = abs(p2.x - p3.x) < d

    # Handle 0, 180 degree and single point cases
    if (s1_horiz and s2_horiz) or (s1_vert and s2_vert):
        return []

    if (s1_vert and s2_horiz) or (s1_horiz or s2_vert):
        if s1_horiz:
            dx = p2.x > p1.x
            dy = p3.y > p2.y
        else:
            dx = p3.x > p2.x
            dy = p2.y > p1.y

        # the conditions calculating the arc's angles can be derived
        # from the following table:
        #
        #  dx   dy   sa   da s1_horiz quadrant
        # --- ---- ---- ---- -------- --------
        #   1    1   90  -90   1         1
        #   0    1    0  -90   0         4
        #   0    0  270  -90   1         3
        #   1    0  180  -90   0         2
        #   0    0    0   90   0         1
        #   1    0  270   90   1         4
        #   1    1  180   90   0         3
        #   0    1   90   90   1         2
        if (   (dx == dy) and s1_horiz
            or (dx != dy) and not s1_horiz):
            da = -90
        else:
            da = 90

        if s1_horiz:
            sa = 90 if dy else 270
        else:
            sa = 180 if dx else 0

        # Determine which quadrant should the arc be drawn in
        q = sa
        if da < 0:
            q -= 90
            if q < 0:
                q += 360

        r *= 2
        x = p2.x
        y = p2.y
        if q == 0:
            x -= r
        elif q == 180:
            y -= r
        elif q == 270:
            x -= r
            y -= r

        return arcpath(x, y, r, r, sa, da)

    # General case (we've already handles the 0 and 180 degree cases)
    a1 = (p1 - p2).a
    a2 = (p3 - p2).a
    ang = a2 - a1

    if ang > math.pi:
        ang -= 2 * math.pi
    elif ang < -math.pi:
        ang += 2 * math.pi

    # Length of the segment between p2 and the center of the circle
    aa = r / math.sin(ang / 2)

    # Calculate the center point of the circle
    c = Vector2(m=abs(aa), angle=a1 + ang / 2) + p2

    # Distance from p2 to the tangent points
    dd = r / math.tan(ang / 2)

    # Tangent point of segments p1, p2
    p = Vector2(m=abs(dd), angle=a1) + p2

    # Tangent point of segments p3, p2
    q = Vector2(m=abs(dd), angle=a2) + p2

    # Calculate a third point on the circle halfway between the two
    # tangent points. This will be on the arc segment that should be
    # drawn, which is needed to be able to determine the correct
    # direction of the arc.
    m = Vector2(m=abs(aa) - r, angle=a1 + ang / 2) + p2

    # Calculate the positions of points p, q and m on the circle
    # expressed as angles from the positive X axis.
    start_a = (p - c).a
    mid_a = (m - c).a
    end_a = (q - c).a

    # Start angle of the arc segment
    sa = start_a

    # Adjust arc length so the arc will always be drawn correctly in the
    # corner of the triangle.
    if start_a < mid_a < end_a or start_a > mid_a > end_a:
        da = end_a - start_a
    else:
        if start_a < end_a:
            da = -(start_a + 2 * math.pi - end_a)
        else:
            da = end_a + 2 * math.pi - start_a

    # Calculate Bezier points of the arc
    return arcpath(c.x - r, c.y - r, 2 * r, 2 * r,
                   math.degrees(sa), math.degrees(da))
Exemplo n.º 16
0
 def test_rotate_negative(self):
     v = Vector2(4, -4)
     v.rotate(rad(30))
     self.assert_equals(75.0, deg(v.a))
Exemplo n.º 17
0
def slice_ellipse(x, y, w, h, ystep, rfactor=1.0):
    """
    Calculate the intersections of an ellipse (or two elliptical arcs
    horizontally symmetrical to the center point) and a set of
    equidistant horizontal lines.

    ``x, y, w, h``
        Bounding box of the ellipse.
    ``ystep``
        Vertical distance between horizontal lines.
    ``rfactor``
        If ``rfactor`` equals 1.0, a normal ellipse is used. If
        ``rfactor`` is greater than 1.0, two elliptical arcs
        horizontally symmetrical to the center point will be calculated
        (rfactor sets the 'flatness' of the arcs).

    The function returns the intersection point-pairs in the same format
    as ``slice_shape``.
    """

    if ystep >= h:
        return []

    points = []

    # Store original x center for the final mirroring step
    cxo = x + w / 2.

    # Calculate the "ovalness" of the ellipse and adjust center point
    xscale = float(w) / h
    x += h * xscale / 2.

    # If rfactor > 1, not a full half-arc will be used but only a
    # smaller symmetrical segment of it.  This is accomplished by
    # scaling the original radius up then adjusting the location of the
    # center point.
    r = h / 2.
    cx = x + r * (rfactor - 1)
    cy = y + r
    r *= rfactor

    # Special case when ystep > circle radius
    numlines = (int) (float(h) / ystep)
    if numlines == 1:
        numlines = 2

    # Adjust the x coords of the points so that the top and bottommost
    # points of the arc are always in the same position, regardless of
    # the rfactor
    xcorr = x - (cx - r * xscale) - w / 2.

    # Center points vertically
    py = y + (h - (numlines - 1) * ystep) / 2.

    while numlines:
        px = cx - math.sqrt(r * r - pow(py - cy, 2)) * xscale + xcorr
        points.append(Vector2(px, py))
        py += ystep
        numlines -= 1

    # Mirror points on the y axis and return point-pair arrays for each
    # line
    return [[p, Vector2(2 * cxo - p.x, p.y)] for p in points]
Exemplo n.º 18
0
    def draw(self, node):
        """
        Draw the node at its (x,y) anchor point.

        Relies on internal properties precalculated by precalc_node.
        """

        E = self._eval_func(node)
        strokewidth = E('strokeWidth')
        drawstroke = strokewidth > 0

        d = node._boxdepth

        _ctx.push()
        _ctx.translate(node.x, node.y)

        if drawstroke:
            # Set up clip path
            cx1 = cx6 = 0
            cy2 = 0
            cx3 = cx4 = cx1 + node.width + d
            cy5 = cy2 + node.height + d

            if self._vert_dir == self._horiz_dir:
                cy1 = d
                cx2 = d
                cy3 = 0
                cy4 = cy3 + node.height
                cx5 = node.width
                cy6 = cy4 + d

            elif self._vert_dir != self._horiz_dir:
                cy1 = 0
                cx2 = node.width
                cy3 = d
                cy4 = cy3 + node.height
                cx5 = d
                cy6 = cy4 - d

            outline = [
                Vector2(cx1, cy1),
                Vector2(cx2, cy2),
                Vector2(cx3, cy3),
                Vector2(cx4, cy4),
                Vector2(cx5, cy5),
                Vector2(cx6, cy6)
            ]

            offs = geom.offset_poly(outline, strokewidth * .5)
            clippath = createpath(_ctx, offs)

            _ctx.beginclip(clippath)

        # Box drawing stuff
        if drawstroke:
            _ctx.stroke(E('strokeColor'))
            _ctx.strokewidth(strokewidth)
        else:
            _ctx.nostroke()

        if self._vert_dir == 1:
            y1 = d
            y2 = y1 - d
            dv = -d
            oy = y1
        else:
            y1 = node.height
            y2 = y1 + d
            dv = d
            oy = 0

        if self._horiz_dir == 1:
            x1 = node.width
            x2 = x1 + d
            dh = d
            ox = 0
        else:
            x1 = d
            x2 = x1 - d
            dh = -d
            ox = x1

        # Draw box
        path = _ctx.rect(ox, oy, node.width, node.height, draw=False)
        self._draw_gradient_shape(node, path, node.fillcolor)

        # Draw horizontal 3D side
        _ctx.beginpath(ox, y1)
        _ctx.lineto(ox + node.width, y1)
        _ctx.lineto(ox + node.width + dh, y2)
        _ctx.lineto(ox + dh, y2)
        _ctx.lineto(ox, y1)

        path = _ctx.endpath(draw=False)
        col = E('horizSideColor')
        self._draw_gradient_shape(node, path, col)

        # Draw vertical 3D side
        _ctx.beginpath(x1, oy)
        _ctx.lineto(x1, oy + node.height)
        _ctx.lineto(x2, oy + node.height + dv)
        _ctx.lineto(x2, oy + dv)
        _ctx.lineto(x1, oy)

        path = _ctx.endpath(draw=False)
        col = E('vertSideColor')
        self._draw_gradient_shape(node, path, col)

        tx = node._textxoffs + ox
        ty = node._textyoffs + oy

        _ctx.fill(node.fontcolor)
        self._drawtext(node, tx, ty)

        if drawstroke:
            _ctx.endclip()

        _ctx.pop()
Exemplo n.º 19
0
 def test_constructor_polar(self):
     v = Vector2(angle=rad(30), m=1)
     self.assert_equals(30.0, deg(v.a))
     self.assert_equals(1.0, v.m)
     self.assert_equals(0.86602540378443, v.x)
     self.assert_equals(-0.5, v.y)
Exemplo n.º 20
0
 def test_scalar_multiply_left(self):
     v = Vector2(3, 2)
     m, a = v.m, v.a
     v = 2 * v
     self.assert_equals(a, v.a)
     self.assert_equals(m * 2, v.m)
Exemplo n.º 21
0
 def test_constructor_cartesian1(self):
     v = Vector2(3, -4)
     self.assert_equals(5, v.m)
     self.assert_equals(53.13010235415598, deg(v.a))
Exemplo n.º 22
0
 def test_scalar_divide_right(self):
     v = Vector2(3, 2)
     m, a = v.m, v.a
     v = v / 2
     self.assert_equals(a, v.a)
     self.assert_equals(m / 2, v.m)
Exemplo n.º 23
0
 def test_scalar_divide_and_assign(self):
     v = Vector2(3, 2)
     m, a = v.m, v.a
     v /= 2
     self.assert_equals(a, v.a)
     self.assert_equals(m / 2, v.m)
Exemplo n.º 24
0
 def test_scalar_multiply_and_assign(self):
     v = Vector2(3, 2)
     m, a = v.m, v.a
     v *= 2
     self.assert_equals(a, v.a)
     self.assert_equals(m * 2, v.m)