Skip to content

Outlines, tests and moving paths

Outlines, tests and moving paths

Run it here, in your browser

Three thick lines are turned into shapes with a gradient. A grid of dots tests which points are inside a shape. A ring of petals is made by turning and shrinking one path.

How it works

  • path.expand_stroke() turns a thick line into a shape you can fill, so it can take a gradient. The options are width, cap, join and dash.
  • f.linear_gradient() fills a shape with colours that change along a line.
  • path.contains(x, y) says whether a point is inside the shape. The dots are green when it does.
  • path.bounds() gives the box around the shape as (x, y, width, height). The red rectangle is drawn from it.
  • path.rotate(), path.translate() and path.scale() give back a moved copy. The petals are one path changed twelve times.

Make it yours

  • Change the width= numbers in jobs, such as width=18 for the wave.
  • Change the dash list [22, 10]: the first number is the dash and the second is the gap.
  • Move the shape in blob().translate(330, 170) and watch the green dots follow it.
  • Change the step of the petals: i * 30 and the range(12) for more or fewer.
  • Change the gradient colours in ["tomato", "slateblue"].

The code

Copy it into a file and run it.

"""Outlines, tests and moving paths

Three thick lines are turned into shapes with a gradient. A grid of dots tests which points are
inside a shape. A ring of petals is made by turning and shrinking one path.

How it works:
- path.expand_stroke() turns a thick line into a shape you can fill, so it can take a gradient. The
  options are width, cap, join and dash.
- f.linear_gradient() fills a shape with colours that change along a line.
- path.contains(x, y) says whether a point is inside the shape. The dots are green when it does.
- path.bounds() gives the box around the shape as (x, y, width, height). The red rectangle is
  drawn from it.
- path.rotate(), path.translate() and path.scale() give back a moved copy. The petals are one
  path changed twelve times.

Make it yours:
- Change the width= numbers in jobs, such as width=18 for the wave.
- Change the dash list [22, 10]: the first number is the dash and the second is the gap.
- Move the shape in blob().translate(330, 170) and watch the green dots follow it.
- Change the step of the petals: i * 30 and the range(12) for more or fewer.
- Change the gradient colours in ["tomato", "slateblue"].
"""
import funground as f


def wave():
    return f.path().move_to(25, 90).curve_to(70, 20, 120, 170, 185, 80)


def zigzag():
    return f.path().move_to(25, 200).line_to(65, 150).line_to(105, 200).line_to(145, 150).line_to(185, 200)


def dashes():
    return f.path().move_to(25, 260).line_to(185, 260)


def blob():
    """A round shape with a hole in it, centred on (0, 0)."""
    return f.path().circle(0, 0, 100) - f.path().circle(10, 0, 44)


def setup():
    f.size(640, 400)


def draw():
    f.background("white")

    # stroke outlines, filled with a gradient; the thin original line sits on top
    jobs = [(wave(), dict(width=18)),
            (zigzag(), dict(width=16, cap="butt", join="miter")),
            (dashes(), dict(width=14, cap="butt", dash=[22, 10]))]
    f.no_stroke()
    f.fill(f.linear_gradient(25, 0, 185, 0, ["tomato", "slateblue"]))
    for line, options in jobs:
        f.draw_path(line.expand_stroke(**options))
    f.no_fill()
    f.stroke("white")
    f.stroke_width(1)
    for line, _ in jobs:
        f.draw_path(line)

    # a hit test: a dot is green when contains() says it is inside the shape
    shape = blob().translate(330, 170)
    f.no_stroke()
    f.fill(222)
    f.draw_path(shape)
    for gx in range(260, 401, 10):
        for gy in range(100, 241, 10):
            f.fill("seagreen" if shape.contains(gx, gy) else "lightgray")
            f.circle(gx, gy, 4)

    # bounds: the box around the shape
    x, y, w, h = shape.bounds()
    f.no_fill()
    f.stroke("crimson")
    f.stroke_width(1)
    f.rect(x, y, w, h)

    # transforms: one petal, rotated and scaled about a centre
    petal = f.path().ellipse(540, 120, 28, 80)
    f.no_stroke()
    for i in range(12):
        f.fill(f.color(60 + i * 16, 110, 230 - i * 12, 150))
        scale = 1.0 if i % 2 == 0 else 0.7
        f.draw_path(petal.rotate(i * 30, 540, 170).translate(-540, -170).scale(scale).translate(540, 170))

    f.fill(60)
    f.text_size(14)
    f.text_align("center", "center")
    f.text("expand_stroke", 105, 340)
    f.text("contains and bounds", 330, 340)
    f.text("rotate and scale", 540, 340)


f.run()

View on GitHub ยท Back to the gallery