Septopus

Beware the Mighty Septopus
What a dandy guy!
He lives on top the submarine
And he's always eating pies.

That works great!

'''
# Septopus

> Beware the Mighty Septopus<br/>
> What a dandy guy!<br/>
> He lives on top the submarine<br/>
> And he's always eating pies.

That works great!
'''
import colorsys, math

from xml.etree.ElementTree import Element, fromstring
from markdown import markdown
from HTML import HTML, HTML4_STRICT_DOCTYPE
from highlighter import embed_code


height = 2.19064
width = 2.24698
Rc = 1 / (2 * math.sin(math.pi / 7))
# So that little right triangle in the corner of the bounding box is
a = (width - 1) / 2
b = math.sqrt(1 - a * a)
diagonal = height - b
square_edge = math.sqrt(diagonal * diagonal / 2)

square_r = Rc - diagonal / 2


def color_code(h, s, v):
    c = lambda n: round(255 * n)
    r, g, b = colorsys.hsv_to_rgb(h, s, v)
    return f'#{c(r):02x}{c(g):02x}{c(b):02x}'


def angle_to_coords(deg, radius, x=0, y=0):
    rads = math.radians(deg)
    return (
        round(x + radius * math.sin(rads), 3),
        round(y + radius * math.cos(rads), 3),
        )


doc = HTML()
head = doc.head
body = doc.body
svg = body.svg(
    width='300',
    height='300',
    viewBox="-150 -150 300 300",
    xmlns="http://www.w3.org/2000/svg",
    )


EDGE_LENGTH = 100
SQUARE_EDGE_LENGTH = square_edge * EDGE_LENGTH
SQUARE_CENTERS_DISTANCE = square_r * EDGE_LENGTH

seventh = 360 / 7
angle = 180
SEPTAGRAM_COORDS = []
for _ in range(7):
    x, y = angle_to_coords(angle, Rc * EDGE_LENGTH)
    angle += 3 * seventh
    SEPTAGRAM_COORDS.append((x, y))

septagram_path = (
    f'M{SEPTAGRAM_COORDS[0][0]},{SEPTAGRAM_COORDS[0][1]} L '
    + ' '.join(
        f'{a},{b}'
        for a, b in SEPTAGRAM_COORDS[1:]
        )
    + ' z'
    )


a = angle_to_coords(  0, SQUARE_CENTERS_DISTANCE)
b = angle_to_coords(120, SQUARE_CENTERS_DISTANCE)
c = angle_to_coords(240, SQUARE_CENTERS_DISTANCE)

##at = angle_to_coords( 60, SQUARE_CENTERS_DISTANCE)
##bt = angle_to_coords(180, SQUARE_CENTERS_DISTANCE)
##ct = angle_to_coords(300, SQUARE_CENTERS_DISTANCE)

g = svg.g
g.animateTransform(
    attributeName="transform",
    attributeType="XML",
    type_="rotate",
    from_=f"0",
    to=f"-360",
    dur=f"{7}s",
    repeatCount="indefinite",
    )

p = g.path(
    fill="none",
    stroke="#888",
    stroke_width="1",
    stroke_linejoin="round",
    d=septagram_path,
    )
p.animateTransform(
    attributeName="transform",
    attributeType="XML",
    type_="rotate",
    from_=f"0",
    to=f"360",
    dur=f"{7}s",
    repeatCount="indefinite",
    )


rg = g.g(
    transform=f"translate{a}",
    )
R0 = rg.rect(
    id_='square',
    x=f"-{SQUARE_EDGE_LENGTH/2}",
    y=f"-{SQUARE_EDGE_LENGTH/2}",
    width=f"{SQUARE_EDGE_LENGTH}",
    height=f"{SQUARE_EDGE_LENGTH}",
    stroke="red",
    fill="none",
    stroke_width="2",
    stroke_linejoin="round",
    )

R0.animateTransform(
    attributeName="transform",
    attributeType="XML",
    type_="rotate",
    from_=f"0",
    to=f"360",
    dur="4s",
    repeatCount="indefinite",
    )

g.use(
    href="#square",
    transform=f"translate{b}",
    )
g.use(
    href="#square",
    transform=f"translate{c}",
    )



# A frame to hold the triangles in orbit.
g = g.g

# A frame to translate the first triangle (which will be reused.)
cg = g.g(
    transform=f"translate({SQUARE_EDGE_LENGTH/2}, {SQUARE_EDGE_LENGTH/2})",
    )

# Our one triangle, pointed "down"
C = cg.polygon(
    id_='dot',
    points=f"{a[0]} {a[1]}, {b[0]} {b[1]}, {c[0]} {c[1]}, ",
    stroke="blue",
    fill="none",
    stroke_width="2",
    )
# And counter-rotating against orbit to remain oriented.
C.animateTransform(
    attributeName="transform",
    attributeType="XML",
    type_="rotate",
    from_=f"0",
    to=f"-360",
    dur="4s",
    repeatCount="indefinite",
    )

# Three copies at the other three corners.
g.use(
    href="#dot",
    transform=f"translate(-{SQUARE_EDGE_LENGTH/2}, {SQUARE_EDGE_LENGTH/2})",
    )
g.use(
    href="#dot",
    transform=f"translate({SQUARE_EDGE_LENGTH/2}, -{SQUARE_EDGE_LENGTH/2})",
    )
g.use(
    href="#dot",
    transform=f"translate(-{SQUARE_EDGE_LENGTH/2}, -{SQUARE_EDGE_LENGTH/2})",
    )

g.animateTransform(
    attributeName="transform",
    attributeType="XML",
    type_="rotate",
    from_=f"0",
    to=f"360",
    dur="4s",
    repeatCount="indefinite",
    )


##D = 100
##
##for n in range(9):
##    color = color_code(n/9, 1, 1)
##    x, y = divmod(n, 3)
##    svg.rect(
##        x=str(x * D),
##        y=str(y * D),
##        width=str(D),
##        height=str(D),
##        style="fill:" + color,
##        )
##
##    half_D = D / 2
##    color = color_code(n/9, 0.5, 1)
##    circ = svg.circle(
##        cx=str(x * D + half_D),
##        cy=str(y * D + half_D),
##        r=str(D / 2),
##        style="fill:" + color,
##        )
##    circ.animate(
##        attributeName="r",
##        from_="0",
##        to="0",
##        dur="5s",
##        values="0; 50; 0",
##        keytimes="0; 0.5; 1",
##        repeatCount="indefinite",
##        )
##
##for n in range(4):
##    r = D * math.sqrt(2) / 2
##    x, y = divmod(n, 2)
##    circ = svg.circle(
##        cx=str((x + 1) * D),
##        cy=str((y + 1) * D),
##        r='0',
##        style="fill:white; opacity:0.5",
##        )
##    circ.animate(
##        attributeName="r",
##        from_=f"{r}",
##        to=f"{r}",
##        dur="5s",
##        values=f"{r}; 20.7; {r}",  # (math.sqrt(2) - 1) * 50
##        keytimes="0; 0.5; 1",
##        repeatCount="indefinite",
##        )


body += fromstring(f"""<div>{markdown(__doc__)}</div>""")
# Markdown produces HTML without an enclosing element,
# fromstring() will refuse to parse this saying:
# xml.etree.ElementTree.ParseError: junk after document element
# which is totally unhelpful.


embed_code(head, body, __file__)


with open('out.html', 'w', encoding='UTF_8') as out:
    out.write(HTML4_STRICT_DOCTYPE)
    out.write(str(doc))