We have animations now
This commit is contained in:
parent
93270c7b4e
commit
f3b554dc14
1 changed files with 65 additions and 7 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import colorsys
|
||||
import textwrap
|
||||
import math
|
||||
|
|
@ -194,23 +195,25 @@ blue = lambda s, l: hls_svg(240, l, s)
|
|||
@click.option('-r', '--radius', default='50/50')
|
||||
@click.option('-n', '--num-meshes', default='5/3')
|
||||
@click.option('-w', '--mesh-width', default='10/10')
|
||||
@click.option('-t', '--mesh-thickness', default='1.6/1.6')
|
||||
@click.option('-o', '--offset', type=float, default=20)
|
||||
@click.option('-p', '--phase', type=float, default=0)
|
||||
@click.option('--counterrotation/--corotation')
|
||||
@click.argument('out_svg_1', type=click.Path(dir_okay=False, path_type=Path))
|
||||
@click.argument('out_svg_2', type=click.Path(dir_okay=False, path_type=Path))
|
||||
def cli(out_svg_1, out_svg_2, radius, num_meshes, mesh_width, offset, phase, counterrotation):
|
||||
@click.argument('out_svg_3', type=click.Path(dir_okay=False, path_type=Path))
|
||||
def cli(out_svg_1, out_svg_2, out_svg_3, radius, num_meshes, mesh_width, mesh_thickness, offset, phase, counterrotation):
|
||||
tags = []
|
||||
dim_tags = []
|
||||
plots = []
|
||||
|
||||
def parse_split(s, parse=float):
|
||||
def parse_split(s, parse=float, extra_args=((), ())):
|
||||
if not isinstance(s, str):
|
||||
return s, s
|
||||
|
||||
l, _, r = s.partition('/')
|
||||
l = parse(l)
|
||||
r = parse(r) if r else l
|
||||
l = parse(l, *extra_args[0])
|
||||
r = parse(r, *extra_args[1]) if r else l
|
||||
return l, r
|
||||
|
||||
r1, r2 = parse_split(radius)
|
||||
|
|
@ -218,10 +221,25 @@ def cli(out_svg_1, out_svg_2, radius, num_meshes, mesh_width, offset, phase, cou
|
|||
assert offset > 0
|
||||
assert offset-r2 < r1 < offset+r2
|
||||
|
||||
th1, th2 = parse_split(mesh_thickness)
|
||||
assert th1 > 0 and th2 > 0
|
||||
|
||||
n1, n2 = parse_split(num_meshes, int)
|
||||
assert n1 > 0 and n2 > 0
|
||||
|
||||
mesh_w1, mesh_w2 = parse_split(mesh_width)
|
||||
def parse_mesh_width(s, r, th):
|
||||
num, unit = re.fullmatch(r'([0-9.]+)\s*(|deg|degree|mm|cm)', s).groups()
|
||||
num = float(num)
|
||||
|
||||
if unit in ('', 'deg', 'degree'):
|
||||
return num
|
||||
|
||||
r -= th/2
|
||||
out = math.degrees(math.atan(num/2 / r))
|
||||
print(f'Calculated mesh angle for mesh width {num:.1f}{unit} and thickness {th:.1f}mm at radius {r:.1f}mm: {out:.1f}°')
|
||||
return out
|
||||
|
||||
mesh_w1, mesh_w2 = parse_split(mesh_width, parse=parse_mesh_width, extra_args=((r1, th1), (r2, th2)))
|
||||
assert mesh_w1 > 0 and mesh_w2 > 0
|
||||
mesh_w1, mesh_w2 = math.radians(mesh_w1), math.radians(mesh_w2)
|
||||
|
||||
|
|
@ -325,7 +343,7 @@ def cli(out_svg_1, out_svg_2, radius, num_meshes, mesh_width, offset, phase, cou
|
|||
for i in range(num):
|
||||
yield (i*(space+w) + phase)%(2*math.pi), (i*(space+w) + w + phase)%(2*math.pi)
|
||||
|
||||
shift_angles = lambda angles, shift: [((a1+shift)%(2*math.pi), (a2+shift)%(2*math.pi)) for a1, a2 in angles]
|
||||
shift_angles = lambda angles, shift: [((a1+shift)%tau, (a2+shift)%tau) for a1, a2 in angles]
|
||||
|
||||
angles_1, angles_2 = list(make_angles(n1, mesh_w1, 0)), list(make_angles(n2, mesh_w2, math.radians(phase)))
|
||||
plots.append(list(plot_angles(angles_1, c1, 'url(#hatch_1)', title='Mesh 1')))
|
||||
|
|
@ -391,13 +409,19 @@ def cli(out_svg_1, out_svg_2, radius, num_meshes, mesh_width, offset, phase, cou
|
|||
|
||||
collided = list(collide_schedules(angles_1, angles_2))
|
||||
|
||||
best_solution = None
|
||||
def plot_centers(collided):
|
||||
nonlocal best_solution
|
||||
|
||||
widths = sorted(((end-start)%tau, start, end) for start, end in collided)
|
||||
|
||||
if collided:
|
||||
print('Best phases:')
|
||||
w, start, end = widths[0]
|
||||
best_solution = (start + (end-start)/2) % tau
|
||||
else:
|
||||
print('No solution.')
|
||||
|
||||
widths = sorted(((end-start)%tau, start, end) for start, end in collided)
|
||||
for w, start, end in widths:
|
||||
if math.isclose(start, end, abs_tol=1e-6):
|
||||
print(f'{math.degrees(start):>3.0f}° (exact)')
|
||||
|
|
@ -430,6 +454,40 @@ def cli(out_svg_1, out_svg_2, radius, num_meshes, mesh_width, offset, phase, cou
|
|||
|
||||
out_svg_2.write_text(str(Tag.setup_svg(tags + dim_tags, bounds=((0, 0), (100, (i+.8)*pitch)), margin=3)))
|
||||
|
||||
tags = []
|
||||
tags.append(Tag('defs', defs))
|
||||
dim_tags = []
|
||||
|
||||
for cx, cy, r, th, sched, stroke, fill in [
|
||||
( 0, 0, r1, th1, shift_angles(angles_1, a1), c1, 'url(#hatch_1)'),
|
||||
(offset, 0, r2, th2, shift_angles(angles_2, math.pi - a2 - best_solution), c2, 'url(#hatch_2)')]:
|
||||
|
||||
max_a = max((a2 - a1) % tau for a1, a2 in sched)
|
||||
max_w = math.tan(max_a) * (r-th/2)
|
||||
max_rd = math.hypot(r + th/2, max_w)
|
||||
tags.append(Tag('circle', cx=cx, cy=cy, fill='none', stroke=fill, stroke_opacity=0.4,
|
||||
r=((r-th/2) + max_rd)/2, stroke_width=max_rd - (r-th/2)))
|
||||
tags.append(Tag('circle', cx=cx, cy=cy, fill='none', stroke=stroke,
|
||||
r=(r-th/2), stroke_width=0.15))
|
||||
tags.append(Tag('circle', cx=cx, cy=cy, fill='none', stroke=stroke,
|
||||
r=max_rd, stroke_width=0.15))
|
||||
|
||||
group = []
|
||||
for a1, a2 in sched:
|
||||
da = (a2-a1)%tau
|
||||
aw = math.tan(da)*(r-th/2)
|
||||
rot = math.degrees(a2 - a1 + a1)
|
||||
group.append(Tag('rect', x=cx + r-th/2, y=cy-aw, width=th, height=2*aw,
|
||||
fill=stroke, transform=f'rotate({rot} {cx} {cy})'))
|
||||
group.append(Tag('animateTransform', attributeName='transform', attributeType='XML', type='rotate',
|
||||
_from=f'360 {cx} {cy}', to=f'0 {cx} {cy}', dur='10s', repeatCount='indefinite'))
|
||||
tags.append(Tag('g', group))
|
||||
print(math.degrees(best_solution))
|
||||
|
||||
out_svg_3.write_text(str(Tag.setup_svg(tags + dim_tags, bounds=(
|
||||
(min(-r1, -r2+offset), min(-r1, -r2)),
|
||||
(max(r1, r2+offset), max(r1, r2))), margin=3)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue