Skip to content

Instantly share code, notes, and snippets.

@laurent-dinh
Created August 4, 2020 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laurent-dinh/8578ed92b2de9de126cc70fb67c48aa2 to your computer and use it in GitHub Desktop.
Save laurent-dinh/8578ed92b2de9de126cc70fb67c48aa2 to your computer and use it in GitHub Desktop.
import numpy as np
# define letters
letter_d = [[0, 1, 0, 0], [0, 1, 2, 0]]
letter_i = [[1, 1], [2, 0]]
letter_n = [[0, 0, 1, 1, 1], [2, 0, 1, 2, 0]]
letter_h = [[0, 0, 0, 1, 1, 1], [0, 2, 1, 1, 2, 0]]
letter_names = ["d", "i", "n", "h"]
letter_paths = [letter_d, letter_i, letter_n, letter_h]
letter_shifts = [0, 0.4, 1.8, 3.2]
RATIO_YX = 1.3
MULXY = 15
START_X = 20 * MULXY / 100.
START_Y = 20 * MULXY / 100.
STROKE_WIDTH = 12 * MULXY / 100.
CURSOR_WIDTH = 26 * MULXY / 100.
def draw_path(xs_, ys_, id_, color_=(51, 51, 51), width=STROKE_WIDTH):
coords_ = (["M %f %f" % (xs_[0], ys_[0])]
+ ["L %f %f" % (x_, y_) for x_, y_ in zip(xs_[1:], ys_[1:])])
coords_ = " ".join(coords_)
res = ("""<path id="%s" d="%s" style="fill:none;""" % (id_, coords_)
+ """stroke:rgb(%d, %d, %d);stroke-linecap:round;""" % color_
+ """stroke-width:%f;stroke-linejoin:round;"/>""" % width)
return res
def draw_circle(x_, y_, id_, color_=(51, 51, 51), radius_=0):
res = ("""<circle id="%s" """ % id_
+ """r="%f" cx="%f" cy="%f" """ % (radius_, x_, y_)
+ """fill="rgb(%d, %d, %d)" />""" % color_)
return res
# create red strokes
red_strokes = []
reds_cursors = []
for letter_idx, (letter_name, letter_path) in enumerate(zip(letter_names, letter_paths)):
reds_cursors.append(draw_circle(
(letter_shifts[letter_idx] + letter_path[0][0]) * MULXY * RATIO_YX + START_X,
letter_path[1][0] * MULXY + START_Y,
"reds_%s_cursor" % letter_name,
(51, 51, 51)
))
n_lines = len(letter_path[0][:-1])
red_strokes.append(
draw_path([(letter_path[0][0] + letter_shifts[letter_idx]) * MULXY * RATIO_YX + START_Y
for in_ in letter_path[0]],
[letter_path[1][0] * MULXY + START_Y for in_ in letter_path[1]],
id_=("red_%s" % letter_name), color_=(140, 35, 35), width=0))
# create black strokes
black_strokes = []
blacks_cursors = []
for letter_idx, (letter_name, letter_path) in enumerate(zip(letter_names, letter_paths)):
n_lines = len(letter_path[0][:-1])
blacks_cursors.append(draw_circle(
letter_path[0][-1] * MULXY * RATIO_YX + START_X,
letter_path[1][-1] * MULXY + START_Y,
"blacks_%s_cursor" % letter_name,
(153, 51, 51)
))
black_strokes.append(
draw_path([in_ * MULXY * RATIO_YX + START_Y
for in_ in letter_path[0]],
[in_ * MULXY + START_Y for in_ in letter_path[1]],
id_=("black_%s" % letter_name), color_=(51, 51, 51), width=STROKE_WIDTH))
svg_prefix = """<svg id="logo_sign" version="2.0" height="{}pt" width="{}pt">""".format(
240 * MULXY / 100., (MULXY * letter_shifts[-1] + 140 * MULXY / 100.) * RATIO_YX)
svg_suffix = "</svg>"
background = ("""<rect height="{}" width="{}" id="back" """.format(
240 * MULXY / 100., (MULXY * letter_shifts[-1] + 140 * MULXY / 100.) * RATIO_YX)
+ """style="fill:rgb(255,255,255);stroke-width:0;stroke:rgb(0,0,0)"/>""")
clicker = ("""<rect height="{}" width="{}" id="clicker" """.format(
240 * MULXY / 100., (MULXY * letter_shifts[-1] + 140 * MULXY / 100.) * RATIO_YX)
+ """style="opacity:0%;fill:rgb(255,255,255);stroke-width:0;stroke:rgb(0,0,0)"/>""")
SPEED = 7
def get_letter_lens(letter_seq_):
letter_seq_x, letter_seq_y = letter_seq_
letter_lens = np.sqrt(
(np.array(letter_seq_x)[1:]- np.array(letter_seq_x)[:-1]) ** 2
+ (np.array(letter_seq_y)[1:] - np.array(letter_seq_y)[:-1]) ** 2)
# print(stroke_len)
return letter_lens
path_lens = [list(get_letter_lens(letter_path)) for letter_path in letter_paths]
longest_len = max(map(sum, path_lens))
path_lens = list(map(lambda in_: in_ / longest_len, path_lens))
path_cum_lens = [[0] + list(np.cumsum(path_len)) for path_len in path_lens]
path_rev_cum_lens = [[0] + list(
np.cumsum(path_len[::-1])) for path_len in path_lens]
# animations
DRAW_DUR = 1.2
CPRESS_DUR = 1
clicker_start = """<animate
xlink:href="#clicker"
attributeName="fill"
from="rgb(255,255,255)"
to="rgb(0,0,0)"
dur="{dur}s"
begin="mouseenter;mouseover;touchstart"
end="mouseleave;mouseout;touchend"
fill="freeze"
id="clicker-start" />""".format(dur=DRAW_DUR)
clicker_end = """<animate
xlink:href="#clicker"
attributeName="fill"
from="rgb(0,0,0)"
to="rgb(255,255,255)"
dur="{dur}s"
begin="mouseleave;mouseout;touchend"
end="mouseenter;mouseover;touchstart"
fill="freeze"
id="clicker-end" />""".format(dur=CPRESS_DUR)
def animate_svg(id_, attr_, values_, timestamps_,
ref_, offset_=0, keysplines_="", extra_id_="",
replace_=False):
new_timestamps = map(lambda in_: in_ / timestamps_[-1], timestamps_)
new_timestamps = ";".join(list(map(str, new_timestamps)))
res = ("""<animate
xlink:href="#{id_}"
attributeName="{attr_}"
from="{from_}"
to="{to_}"
dur="{dur_}s"
begin="{ref_}.begin + {offset_}s"
end="{ref_}.end"
values="{values_}"
keyTimes="{timestamps_}"
""" + keysplines_
+ """
fill="%s"
""" % ("replace" if replace_ else "freeze")
+ """id="{id_}-{attr_}-{extra_id_}" />""")
res = res.format(
id_=id_, attr_=attr_, offset_=offset_, ref_=ref_,
from_=values_.split(";")[0],
to_=values_.split(";")[-1],
dur_=timestamps_[-1], extra_id_=extra_id_,
values_=values_, timestamps_=new_timestamps)
return res
cursors_animations = []
strokes_animations = []
compress_animations = []
for letter_idx, (letter_name, letter_path) in enumerate(zip(letter_names, letter_paths)):
# animate cursors (x, y, radius)
cursors_animations.append(animate_svg(
extra_id_="draw",
id_=("reds_%s_cursor" % letter_name),
attr_="cx", ref_="clicker-start", values_=";".join(
[str(START_X + (
letter_shifts[letter_idx] + in_) * MULXY * RATIO_YX)
for in_ in letter_path[0]]),
timestamps_=np.array(path_cum_lens[letter_idx]) * DRAW_DUR))
cursors_animations.append(animate_svg(
extra_id_="draw",
id_=("reds_%s_cursor" % letter_name),
attr_="cy", ref_="clicker-start", values_=";".join(
[str(START_Y + (in_) * MULXY)
for in_ in letter_path[1]]),
timestamps_=np.array(path_cum_lens[letter_idx]) * DRAW_DUR))
cursors_animations.append(animate_svg(
extra_id_="draw",
id_=("reds_%s_cursor" % letter_name),
attr_="r", ref_="clicker-start", values_="0; %f; 0" % CURSOR_WIDTH,
keysplines_="keySplines=\"0 1 0 1; 1 0 1 0\"",
timestamps_=[0, .5 * path_cum_lens[letter_idx][-1] * DRAW_DUR,
path_cum_lens[letter_idx][-1] * DRAW_DUR]))
cursors_animations.append(animate_svg(
extra_id_="draw",
id_=("blacks_%s_cursor" % letter_name),
attr_="cx", ref_="clicker-start", values_=";".join(
[str(START_X + (in_) * MULXY * RATIO_YX)
for in_ in letter_path[0]]),
timestamps_=np.array(path_cum_lens[letter_idx]) * DRAW_DUR))
cursors_animations.append(animate_svg(
extra_id_="draw",
id_=("blacks_%s_cursor" % letter_name),
attr_="cy", ref_="clicker-start", values_=";".join(
[str(START_Y + (in_) * MULXY)
for in_ in letter_path[1]]),
timestamps_=np.array(path_cum_lens[letter_idx]) * DRAW_DUR))
cursors_animations.append(animate_svg(
extra_id_="draw",
id_=("blacks_%s_cursor" % letter_name),
attr_="r", ref_="clicker-start", values_="0; %f; 0" % CURSOR_WIDTH,
keysplines_="keySplines=\"0 1 0 1; 1 0 1 0\"",
timestamps_=[0, .5 * path_cum_lens[letter_idx][-1] * DRAW_DUR,
path_cum_lens[letter_idx][-1] * DRAW_DUR]))
# animate (draw)
strokes_animations.append(
animate_svg(
extra_id_="draw",
id_=("red_%s" % letter_name),
attr_="stroke-width",
values_="0; %f; %f" % (STROKE_WIDTH, STROKE_WIDTH),
timestamps_=[0, .001 * DRAW_DUR * path_cum_lens[letter_idx][-1],
1 * DRAW_DUR * path_cum_lens[letter_idx][-1]],
ref_="clicker-start",
offset_=0)
)
strokes_animations.append(
animate_svg(
extra_id_="draw",
id_=("red_%s" % letter_name),
attr_="stroke",
values_="rgb(51, 51, 51); rgb(140, 35, 35); rgb(140, 35, 35)",
timestamps_=[0, .001 * DRAW_DUR * path_cum_lens[letter_idx][-1],
1 * DRAW_DUR * path_cum_lens[letter_idx][-1]],
ref_="clicker-start",
offset_=0)
)
strokes_animations.append(
animate_svg(
extra_id_="draw",
id_=("black_%s" % letter_name),
attr_="stroke-width",
values_="%f; %f; 0" % (STROKE_WIDTH, STROKE_WIDTH),
timestamps_=[0, .999 * DRAW_DUR * path_cum_lens[letter_idx][-1],
1 * DRAW_DUR * path_cum_lens[letter_idx][-1]],
ref_="clicker-start",
offset_=0)
)
n_lines = len(letter_path[0][:-1])
drawing_values = []
for pt_idx in range(len(letter_path[0])):
cur_xs = letter_path[0][:(pt_idx + 1)] + [letter_path[0][pt_idx]] * (n_lines - pt_idx + 1)
cur_ys = letter_path[1][:(pt_idx + 1)] + [letter_path[1][pt_idx]] * (n_lines - pt_idx + 1)
cur_xs = map(
lambda in_: str(START_X + (
letter_shifts[letter_idx] + in_) * MULXY * RATIO_YX), cur_xs)
cur_ys = map(
lambda in_: str(START_Y + in_ * MULXY), cur_ys)
drawing_values.append(
"M" + " L".join(
list(map(lambda in_: " ".join(in_), zip(cur_xs, cur_ys)))))
drawing_values = ";".join(drawing_values)
strokes_animations.append(animate_svg(
extra_id_="draw",
id_=("red_%s" % letter_name),
attr_="d", ref_="clicker-start", values_=drawing_values,
timestamps_=np.array(path_cum_lens[letter_idx]) * DRAW_DUR))
erasing_values = []
for pt_idx in range(len(letter_path[0])):
cur_xs = [letter_path[0][pt_idx]] * (pt_idx + 1) + letter_path[0][(pt_idx + 1):]
cur_ys = [letter_path[1][pt_idx]] * (pt_idx + 1) + letter_path[1][(pt_idx + 1):]
cur_xs = map(
lambda in_: str(START_X + in_ * MULXY * RATIO_YX), cur_xs)
cur_ys = map(
lambda in_: str(START_Y + in_ * MULXY), cur_ys)
erasing_values.append(
"M" + " L".join(
list(map(lambda in_: " ".join(in_), zip(cur_xs, cur_ys)))))
erasing_values = ";".join(erasing_values)
strokes_animations.append(animate_svg(
extra_id_="draw",
id_=("black_%s" % letter_name),
attr_="d", ref_="clicker-start", values_=erasing_values,
timestamps_=np.array(path_rev_cum_lens[letter_idx]) * DRAW_DUR))
# compress animations (positions, color)
start_cpress_xs = list(map(
lambda in_: str(START_X + (
letter_shifts[letter_idx] + in_) * MULXY * RATIO_YX),
letter_path[0]))
end_cpress_xs = list(map(
lambda in_: str(START_X + in_ * MULXY * RATIO_YX),
letter_path[0]))
red_ys = list(map(
lambda in_: str(START_Y + in_ * MULXY), letter_path[1]))
start_coords = "M" + " L".join(
list(map(lambda in_: " ".join(in_), zip(start_cpress_xs, red_ys))))
end_coords = "M" + " L".join(
list(map(lambda in_: " ".join(in_), zip(end_cpress_xs, red_ys))))
if letter_idx != 0:
compress_animations.append(animate_svg(
extra_id_="compress",
id_=("red_%s" % letter_name),
attr_="d", ref_="clicker-end",
values_="%s;%s" % (start_coords, end_coords),
timestamps_=[0, (letter_shifts[letter_idx] * CPRESS_DUR
/ letter_shifts[-1])]))
compress_animations.append(animate_svg(
extra_id_="compress",
id_=("red_%s" % letter_name),
attr_="stroke", ref_="clicker-end",
values_="rgb(153, 51, 51);rgb(51, 51, 51)",
timestamps_=[0, CPRESS_DUR]))
compress_animations.append(animate_svg(
extra_id_="draw",
id_=("reds_%s_cursor" % letter_name),
attr_="r", ref_="clicker-end", values_="%f;0;0" % CURSOR_WIDTH,
timestamps_=[0, .001 * CPRESS_DUR * path_cum_lens[letter_idx][-1],
1 * CPRESS_DUR * path_cum_lens[letter_idx][-1]]))
print("\n".join(
[svg_prefix] + red_strokes
+ black_strokes + blacks_cursors + reds_cursors
+ [clicker_start, clicker_end] + cursors_animations
+ strokes_animations + compress_animations
+ [clicker, svg_suffix]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment