Add mozc dial version.

Co-authored-by: Takashi Toyoshima <toyoshim@google.com>
Co-authored-by: Shun Ikejima <ikejima@google.com>
This commit is contained in:
Shun Ikejima
2025-10-01 10:01:00 +09:00
parent 9b4017fb9b
commit 6783bfea76
88 changed files with 233948 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
import math
class SVGGenerator:
"""SVGを作るためのクラス"""
def __init__(self, width_mm=100, height_mm=100):
self.width_mm = width_mm
self.height_mm = height_mm
self.elements = []
def circle(self, radius_mm):
"""半径radius_mmのなるべく細い円を書く"""
self.elements.append(
f'<circle cx="{self.width_mm / 2}" cy="{self.height_mm / 2}" r="{radius_mm}" stroke="black" stroke-width="0.1" fill="none" />'
)
def sector_ring(self, outer_radius_mm, inner_radius_mm, start_degree, end_degree):
"""outer_radius_mmの円から、inner_radius_mmの円を切り取り、start_degreeからend_degreeまでを取り出した扇形環を描く"""
def polar_to_cartesian(radius, angle_in_degrees):
angle_in_radians = (angle_in_degrees - 90) * math.pi / 180.0
return (
(self.width_mm / 2) + (radius * math.cos(angle_in_radians)),
(self.height_mm / 2) + (radius * math.sin(angle_in_radians))
)
start_outer = polar_to_cartesian(outer_radius_mm, end_degree)
end_outer = polar_to_cartesian(outer_radius_mm, start_degree)
start_inner = polar_to_cartesian(inner_radius_mm, end_degree)
end_inner = polar_to_cartesian(inner_radius_mm, start_degree)
large_arc_flag = "1" if end_degree - start_degree > 180 else "0"
path_data = [
f"M {start_outer[0]} {start_outer[1]}",
f"A {outer_radius_mm} {outer_radius_mm} 0 {large_arc_flag} 0 {end_outer[0]} {end_outer[1]}",
f"L {end_inner[0]} {end_inner[1]}",
f"A {inner_radius_mm} {inner_radius_mm} 0 {large_arc_flag} 1 {start_inner[0]} {start_inner[1]}",
"Z"
]
self.elements.append(f'<path d="{" ".join(path_data)}" stroke="none" stroke-width="0.1" fill="black"/>')
def indicator(self, inner_radius_mm, pos_digree):
"""inner_radius_mm、0度の位置に大きさ2mm程度の三角を描く"""
size_mm = 2
def polar_to_cartesian(radius, angle_in_degrees):
angle_in_radians = (angle_in_degrees - 90) * math.pi / 180.0
return (
(self.width_mm / 2) + (radius * math.cos(angle_in_radians)),
(self.height_mm / 2) + (radius * math.sin(angle_in_radians))
)
# Tip of the triangle, pointing towards the center
p1 = polar_to_cartesian(inner_radius_mm, 0 + pos_digree)
# Base of the triangle is further out
base_radius = inner_radius_mm + size_mm
half_base_width = size_mm / 2
half_base_angle_rad = half_base_width / base_radius
half_base_angle_deg = half_base_angle_rad * 180 / math.pi
p2 = polar_to_cartesian(base_radius, -half_base_angle_deg + pos_digree)
p3 = polar_to_cartesian(base_radius, half_base_angle_deg + pos_digree)
self.elements.append(
f'<polygon points="{p1[0]},{p1[1]} {p2[0]},{p2[1]} {p3[0]},{p3[1]}" fill="black" />'
)
def export_to_svg(self):
"""描いたデータをsvgにして出力する。"""
svg_elements = "\n".join(self.elements)
return (
f'<svg width="{self.width_mm}mm" height="{self.height_mm}mm" viewBox="0 0 {self.width_mm} {self.height_mm}" xmlns="http://www.w3.org/2000/svg">\n'
+ svg_elements
+ "\n</svg>"
)
encoders = [
{
"name": "one_dial",
"bits": 6,
"degrees": [
0, # 最初の遊び
]+[
(i * 23 / 3 + 20) for i in range(33+2)
] + [320],
"indicator": 23/3*.5+20 + 23/3+5,
}
]
for encoder in encoders:
gen = SVGGenerator()
bits = encoder["bits"]
templ = encoder["degrees"]
gen.circle(8)
gen.circle(8+5*bits)
gen.indicator(8+5*bits, encoder["indicator"])
for i in range(len(templ)-1):
num = i+1
gray = num ^ (num>>1)
s = f'{gray:0{bits}b}'
# print(s)
for j in range(bits):
if (s[bits-j-1] == "1"):
gen.sector_ring(8+5+5*j, 8+5*j, templ[i], templ[i+1])
with open(f"{encoder["name"]}.svg", "w") as f:
f.write(gen.export_to_svg())