Example #1
0
def draw_dodecahedron(
    ctx: cairo.Context,
    rng: Generator,
    pos_x: float,
    pos_y: float,
    radius: float,
    rotation: float = 0,
):
    # Outer boundary
    outer_points = list(
        points_along_arc(pos_x, pos_y, radius, rotation, rotation + tau, 10))
    for prev_index, (bx, by) in enumerate(outer_points, -1):
        ax, ay = outer_points[prev_index]
        ctx.move_to(ax, ay)
        ctx.line_to(bx, by)
        ctx.stroke()

    # Frontside inner pentagon
    inner_fs_points = list(
        points_along_arc(pos_x, pos_y, 0.6 * radius, rotation, rotation + tau,
                         5))
    for prev_index, (bx, by) in enumerate(inner_fs_points, -1):
        ax, ay = inner_fs_points[prev_index]
        ctx.move_to(ax, ay)
        ctx.line_to(bx, by)
        ctx.stroke()

    # Outer to frontside inner
    for (ax, ay), (bx, by) in zip(inner_fs_points, outer_points[::2]):
        ctx.move_to(ax, ay)
        ctx.line_to(bx, by)
        ctx.stroke()

    # backside inner pentagon
    offset = pi / 5
    inner_bs_points = list(
        points_along_arc(
            pos_x,
            pos_y,
            0.6 * radius,
            rotation + offset,
            rotation + offset + tau,
            5,
        ))
    for prev_index, (bx, by) in enumerate(inner_bs_points, -1):
        ax, ay = inner_bs_points[prev_index]
        ctx.move_to(ax, ay)
        ctx.line_to(bx, by)
        ctx.stroke()
    # Outer to backside inner
    for (ax, ay), (bx, by) in zip(inner_bs_points, outer_points[1::2]):
        ctx.move_to(ax, ay)
        ctx.line_to(bx, by)
        ctx.stroke()
Example #2
0
def draw_circular_roman(
    ctx: cairo.Context,
    rng: Generator,
    pos_x: float,
    pos_y: float,
    radius_outer: float,
    radius_inner: float,
):
    chunks = rng.integers(6, 16)
    _calendar_base(ctx, pos_x, pos_y, radius_outer, radius_inner, chunks)

    ctx.select_font_face("Noto Sans Symbols")
    font_size = 0.8 * (radius_outer - radius_inner)
    ctx.set_font_size(font_size)

    angle_offset = pi / chunks
    for i, (x, y) in enumerate(
            points_along_arc(
                pos_x,
                pos_y,
                (radius_inner + radius_outer) / 2.0,
                angle_offset,
                angle_offset + tau,
                chunks,
            ),
            1,
    ):
        with translation(ctx, x, y), rotation(ctx, (i * tau / chunks) +
                                              (pi / 2) - angle_offset):
            roman = int_to_roman(i)
            extents = ctx.text_extents(roman)

            ctx.move_to(-1 * extents.width / 2.0, extents.height / 2.0)
            ctx.show_text(roman)
            ctx.new_path()
Example #3
0
def _calendar_base(
    ctx: cairo.Context,
    pos_x: float,
    pos_y: float,
    radius_outer: float,
    radius_inner: float,
    chunks: int,
):
    ctx.arc(pos_x, pos_y, radius_outer, 0, tau)
    ctx.stroke_preserve()

    ctx.arc(pos_x, pos_y, radius_inner, 0, tau)
    ctx.stroke()

    for (start_x, start_y), (end_x, end_y) in zip(
            points_along_arc(pos_x, pos_y, radius_inner, 0, tau, chunks),
            points_along_arc(pos_x, pos_y, radius_outer, 0, tau, chunks),
    ):
        ctx.move_to(start_x, start_y)
        ctx.line_to(end_x, end_y)
        ctx.stroke()
Example #4
0
def draw_star(ctx: cairo.Context, rng: Generator, pos_x: float, pos_y: float,
              size: float):
    spikes = rng.integers(5, 8)
    outer_points = list(
        jitter_points(points_along_arc(pos_x, pos_y, size, 0, tau, spikes),
                      rng, 0.1 * size), )
    inner_offset = pi / spikes
    inner_points = list(
        jitter_points(
            points_along_arc(pos_x, pos_y, size / 2.0, inner_offset,
                             inner_offset + tau, spikes),
            rng,
            0.1 * size,
        ))

    ctx.move_to(*inner_points[-1])
    for (ax, ay), (bx, by) in zip(outer_points, inner_points):
        ctx.line_to(ax, ay)
        ctx.line_to(bx, by)
    ctx.stroke_preserve()
    with source(ctx, Color(1.0, 1.0, 1.0).to_pattern()):
        ctx.fill()
def draw_moon_cycles(
    ctx: cairo.Context,
    rng: Generator,
    pos_x: float,
    pos_y: float,
    radius_outer: float,
    radius_inner: float,
):
    ctx.arc(pos_x, pos_y, radius_outer, 0, tau)
    ctx.arc(pos_x, pos_y, radius_inner, 0, tau)
    ctx.stroke()

    n_moons = rng.integers(6, 12)

    for i, (x, y) in enumerate(
        points_along_arc(
            pos_x, pos_y, (radius_outer + radius_inner) / 2.0, 0, tau, n_moons
        )
    ):
        pct_done = i / n_moons
        eclipse_pct = (pct_done * 2.0) - 1.0
        draw_moon(ctx, x, y, (radius_outer - radius_inner) / 2.3, eclipse_pct)
Example #6
0
def _calendar_mapped(
    ctx: cairo.Context,
    rng: Generator,
    pos_x: float,
    pos_y: float,
    radius_outer: float,
    radius_inner: float,
    mapping: Sequence[str],
    font_family: str = "Noto Sans Symbols",
):
    chunks = rng.integers(6, min(len(mapping), 24))
    _calendar_base(ctx, pos_x, pos_y, radius_outer, radius_inner, chunks)

    ctx.select_font_face(font_family)
    font_size = 0.8 * (radius_outer - radius_inner)
    ctx.set_font_size(font_size)

    symbols = rng.choice(mapping, size=chunks, replace=False)

    angle_offset = pi / chunks
    for i, (x, y) in enumerate(
            points_along_arc(
                pos_x,
                pos_y,
                (radius_inner + radius_outer) / 2.0,
                angle_offset,
                angle_offset + tau,
                chunks,
            ),
            1,
    ):
        with translation(ctx, x, y), rotation(ctx, (i * tau / chunks) +
                                              (pi / 2) - angle_offset):
            symbol = symbols[i - 1]
            extents = ctx.text_extents(symbol)

            ctx.move_to(-1 * extents.width / 2.0, extents.height / 2.0)
            ctx.show_text(symbol)
            ctx.new_path()
Example #7
0
def draw_star_band(
    ctx: cairo.Context,
    rng: Generator,
    pos_x: float,
    pos_y: float,
    radius_outer: float,
    radius_inner: float,
):
    band_center = (radius_outer + radius_inner) / 2
    star_size = 0.2 * (radius_outer - radius_inner)
    n_stars = rng.integers(20, 50)

    ctx.arc(pos_x, pos_y, radius_outer, 0, tau)
    ctx.arc_negative(pos_x, pos_y, radius_inner, 0, -tau)
    ctx.stroke_preserve()
    ctx.clip_preserve()

    grad_offset_x = rng.uniform(radius_inner / 3.0, radius_inner)
    grad_offset_y = rng.uniform(radius_inner / 3.0, radius_inner)
    grad = PointLinearGradient(
        [Color(0, 0, 0), Color(1, 1, 1, 0.0)], Pattern.PACKED)
    grad.fill(
        ctx,
        rng,
        pos_x - grad_offset_x,
        pos_y - grad_offset_y,
        pos_x + grad_offset_x,
        pos_y + grad_offset_y,
    )

    for x, y in jitter_points(
            points_along_arc(pos_x, pos_y, band_center, 0, tau, n_stars), rng,
            star_size):
        draw_star(ctx, rng, x, y, star_size)

    ctx.reset_clip()
Example #8
0
def draw_tangents(
    ctx: cairo.Context,
    rng: Generator,
    pos_x: float,
    pos_y: float,
    radius_outer: float,
    radius_inner: float,
):
    origin_points = rng.choice([12, 24, 36, 48, 60])

    for start_x, start_y in points_along_arc(
        pos_x, pos_y, radius_outer, 0, tau, origin_points
    ):
        angle_c_to_tangent = acos(radius_inner / radius_outer)
        angle_c_to_point = atan2(start_y - pos_y, start_x - pos_x)

        # 2 tangents:
        for op in (add, sub):
            angle_tangent = op(angle_c_to_point, angle_c_to_tangent)
            tangent_x = pos_x + radius_inner * cos(angle_tangent)
            tangent_y = pos_y + radius_inner * sin(angle_tangent)
            ctx.move_to(start_x, start_y)
            ctx.line_to(tangent_x, tangent_y)
            ctx.stroke()