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'' ) 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'') 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'' ) def export_to_svg(self): """描いたデータをsvgにして出力する。""" svg_elements = "\n".join(self.elements) return ( f'\n' + svg_elements + "\n" ) 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())