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))