from casadi import * import matplotlib.pyplot as plt import math import operator # scale in inkscape # 1 unit = 0.283 mm svg_scale = 1000.0/282.222 def svg_circle(id, name, c, r): # create circle object in svg notation text = [' \n'.format(c[0])] return text def svg_puzzle(p, size, angle): # convert angle to radians angle = angle / 360.0 * 2.0 * np.pi # compute points """ v1 and v2 are orthogonal vectors construction of points (starting at p): p3 <------ -2 v1 ------ p2 ^ | v2 | | p4 <-- -v1 -- p -- v1 --> p1 then between points p2 and p3 with draw an arc """ v1 = np.array([np.cos(angle), np.sin(angle)]) v2 = np.array([v1[1], -v1[0]]) p1 = p + size * v1 p2 = p1 + size * v2 p3 = p2 - 2.0 * size * v1 p4 = p - size * v1 # convert to svg units p1 *= svg_scale p2 *= svg_scale p3 *= svg_scale p4 *= svg_scale radius_scaled = 1.25 * size * svg_scale text = [' \n'.format(p1[0], p1[1], p2[0], p2[1], radius_scaled, radius_scaled, p3[0], p3[1], p4[0], p4[1])] return text def svg_line_puzzle(start, end, puzzle_scale=1.0, linewidth=0.50): # draws a line from start to end with a simple jigsaw puzzle style cutout in the middle # the size of the cutout can be controlled with the puzzle_scale parameter # compute points """ v1 and v2 are orthogonal vectors construction of points (starting at p (middle between start and end)): p2 ------- 2 v1 -----> p3 ^ | v2 | | start --- p1 <-- -v1 -- p -- v1 --> p4 --- end then between points p2 and p3 with draw an arc """ v = end - start dist = np.linalg.norm(v) size = dist / 10.0 * puzzle_scale # size of the cutout v = v / dist angle = math.atan2(v[1], v[0]) # angle of v # midpoint between start and end p = np.mean([start, end], axis=0) v1 = np.array([np.cos(angle), np.sin(angle)]) v2 = np.array([v1[1], -v1[0]]) p1 = p - size * v1 p2 = p1 + size * v2 p3 = p2 + 2.0 * size * v1 p4 = p + size * v1 # convert to svg units p1 *= svg_scale p2 *= svg_scale p3 *= svg_scale p4 *= svg_scale start *= svg_scale end *= svg_scale radius_scaled = 1.25 * size * svg_scale text = [' \n'.format(linewidth, start[0], start[1], p1[0], p1[1], p2[0], p2[1], radius_scaled, radius_scaled, p3[0], p3[1], p4[0], p4[1], end[0], end[1])] return text def svg_half_circle(id, name, c, r, angle, orientation_flag=1): # draws half a circle centered at c with radius r # angle specifies how the half circle should be rotated # for the default angle of zero, it draws the top half of the circle # convert angle to radians angle = angle/360.0 * 2.0 * np.pi # compute starting point v = np.array([np.cos(angle), np.sin(angle)]) begin = c + r * v # in millimeters begin *= svg_scale # in svg units # compute end point end = c - r * v # in millimeters end *= svg_scale # in svg units radius_scaled = r * svg_scale # radius in svg units text = [' \n'.format(begin[0], begin[1], radius_scaled, radius_scaled, orientation_flag, orientation_flag, end[0], end[1])] return text def svg_arc(p1, p2, r, large_arc, sweep): begin = p1 * svg_scale end = p2 * svg_scale radius_scaled = r * svg_scale text = [' \n'.format(begin[0], begin[1], radius_scaled, radius_scaled, large_arc, sweep, end[0], end[1])] return text def svg_rectangle(id, name, c, width, heigth, angle): x = np.sqrt(c[0]**2 + c[1]**2) - width/2 y = - heigth text = ['\n ' '\n ' '\n' .format(angle, x, y ,width, heigth)] return text def svg_line(p1, p2, width=1.0): text = [''.format(p1[0], p1[1], p2[0], p2[1], width)] return text # this function reads and processes data for optimal circle packaging obtained form packomania.com def read_circle_data(N): coords_raw = open('cci/cci{}.txt'.format(N)) radii_raw = open('cci/radii.txt'.format(N)) coords_raw = coords_raw.readlines() coords_raw = [c.split() for c in coords_raw if c[0] != '#'] coords = {} for c in coords_raw: coords[int(c[0])] = (float(c[1]), float(c[2])) coords = sort_ccw(coords, (0,0)) radii_raw = radii_raw.readlines() radii_raw = [r.split() for r in radii_raw if r[0] != '#'] radii = {} for r in radii_raw: radii[int(r[0])] = float(r[1]) return radii[N], coords # this function sorts enclosed circle coordinates counter-clockwise w.r.t. the center point # TODO: there is a problem when circles are present that are not touching the boundary of the enclosing circle (e.g. N = 7) def sort_ccw(coords, center): a = {} for c in coords: a[c] = math.atan2(coords[c][1] - center[1], coords[c][0] - center[0]) a_sort = sorted(a.items(), key=operator.itemgetter(1)) coords_sort = [] for a in a_sort: coords_sort.append(coords[a[0]]) return coords_sort # compute the two tangential points at the circle with center c and radius r intersecting the point p def compute_tangent_points(p, c, r): b = sqrt((p[0] - c[0]) ** 2 + (p[1] - c[1]) ** 2) th = acos(r / b) # angle theta d = atan2(p[1] - c[1], p[0] - c[0]) # direction angle of point p from c d1 = d + th # direction angle of point T1 from c d2 = d - th # direction angle of point T2 from c T1x = c[0] + r * cos(d1) T1y = c[1] + r * sin(d1) T2x = c[0] + r * cos(d2) T2y = c[1] + r * sin(d2) return (T1x, T1y), (T2x, T2y) class PlateLayout: def __init__(self, N, plate_radius): self.N = N # number of enclosed circles self.plate_radius = plate_radius def compute_layout(self): # read radius and center coordinates for enclosed circles rtilde, coords = read_circle_data(self.N) c = (0.0, 0.0) # center of big circle R = 1.0 # radius of big circle plt.xlim((-1, 1)) plt.ylim((-1, 1)) plt.gca().set_aspect('equal', 'box') plt.ion() plt.show() for p in coords: plt.plot(p[0], p[1], 'o') circle = plt.Circle(p, rtilde, fill=False) plt.gca().add_artist(circle) circle = plt.Circle(c, R, fill=False) plt.gca().add_artist(circle) plt.plot(c[0], c[1], 'o') coords_2 = [] for k in range(0, self.N): p1 = coords[k] p2 = coords[(k+1) % self.N] # midpoint between center of two circles m = np.mean([p1, p2], axis=0) # vector in direction of midpoint v = m - np.array(c) v = v/np.linalg.norm(v) #plt.plot(m[0], m[1], 'o') # optimization problem for computing position and radius for a maximal circle fitting in space between two big circles # and being fully contained in enclosing circle opti = casadi.Opti() r = opti.variable(1) # radius of new circle p = opti.variable(2) # center of new circle lamb = opti.variable(1) # distance of center of new circle to center of enclosing circle opti.minimize(-r) opti.subject_to(p == c + v * lamb) opti.subject_to((p[0] - p1[0])**2 + (p[1] - p1[1])**2 >= (rtilde + r)**2) opti.subject_to(R == lamb + r) opti.subject_to(r >= 0) opti.subject_to(r <= R) opti.solver('ipopt') init_r = 0.1 init_lamb = R - init_r init_p = c + v * init_lamb opti.set_initial(r, init_r) opti.set_initial(p, init_p) opti.set_initial(lamb, init_lamb) sol = opti.solve() p = sol.value(p) r = sol.value(r) lamb = sol.value(lamb) print("p = {}".format(p)) print("r = {}".format(r)) print("lambda = {}".format(lamb)) print("v = {}".format(v)) coords_2.append(p) plt.plot(p[0], p[1], 'o') circle = plt.Circle(p, r, fill=False) plt.gca().add_artist(circle) tube1 = {} tube2 = {} # postprocessing solution: # - output radii for circles # - output center coordinates, angle w.r.t. origin and distance from origin outer_radius = self.plate_radius # desired plate radius in meters tube1_radius = outer_radius * rtilde tube2_radius = outer_radius * r print("\n------------------") print("optimal values") print("plate radius = {:6.3} m = {:6.2f} mm".format(outer_radius / 1000, outer_radius)) print("big circles:") print(" radius = {:6.3} m = {:6.2f} mm".format(tube1_radius / 1000, tube1_radius)) print(" diameter = {:6.3} m = {:6.2f} mm".format(2 * tube1_radius / 1000, 2 * tube1_radius)) print("small circles:") print(" radius = {:6.3} m = {:6.2f} mm".format(tube2_radius, tube2_radius * 1000)) print(" diameter = {:6.3} m = {:6.2f} mm".format(2*tube2_radius, 2*tube2_radius * 1000)) # compute coordinates and various measurements for fixed radii of plate and tubes self.target_plate_radius = 160.0 self.target_center_hole_radius = 7.5 self.target_radius_1 = 50.5 self.target_radius_2 = 20.0 teeth = 200 D = 2 * self.target_plate_radius self.plate_module = D/teeth print("plate radius = {:6.2f} mm".format(self.target_plate_radius)) print("big circle radius = {:6.2f} mm".format(self.target_radius_1)) print("small circle radius = {:6.2f} mm".format(self.target_radius_2)) print("number of teeth: N = {}".format(teeth)) print("pitch diameter: D = {} mm".format(D)) print("module: M = {} mm".format(self.plate_module)) # parameters for dispenser gears self.dispenser_module = 1.0 dispenser_1_target_pitch_diameter_big = 50.0 dispenser_1_target_pitch_diameter_small = 15.0 dispenser_1_teeth_big = int(dispenser_1_target_pitch_diameter_big/self.dispenser_module) dispenser_1_teeth_small = int(dispenser_1_target_pitch_diameter_small/ self.dispenser_module) dispenser_1_pitch_diameter_big = self.dispenser_module * dispenser_1_teeth_big dispenser_1_outer_diameter_big = dispenser_1_pitch_diameter_big + 2 * self.dispenser_module dispenser_1_pitch_diameter_small = self.dispenser_module * dispenser_1_teeth_small dispenser_1_outer_diameter_small = dispenser_1_pitch_diameter_small + 2 * self.dispenser_module dispenser_2_target_pitch_diameter_big = 20.0 dispenser_2_target_pitch_diameter_small = 15.0 dispenser_2_teeth_big = int(dispenser_2_target_pitch_diameter_big / self.dispenser_module) dispenser_2_teeth_small = int(dispenser_2_target_pitch_diameter_small / self.dispenser_module) dispenser_2_pitch_diameter_big = self.dispenser_module * dispenser_2_teeth_big dispenser_2_outer_diameter_big = dispenser_2_pitch_diameter_big + 2 * self.dispenser_module dispenser_2_pitch_diameter_small = self.dispenser_module * dispenser_2_teeth_small dispenser_2_outer_diameter_small = dispenser_2_pitch_diameter_small + 2 * self.dispenser_module print("parameters for dispenser gears:") print(" big container:") print(" module = {}".format(self.dispenser_module)) print(" pitch diameter big gear = {}".format(dispenser_1_pitch_diameter_big)) print(" number of teeth big gear = {}".format(dispenser_1_teeth_big)) print(" pitch diameter small gear = {}".format(dispenser_1_pitch_diameter_small)) print(" number of teeth small gear = {}".format(dispenser_1_teeth_small)) print(" small container:") print(" module = {}".format(self.dispenser_module)) print(" pitch diameter big gear = {}".format(dispenser_2_pitch_diameter_big)) print(" number of teeth big gear = {}".format(dispenser_2_teeth_big)) print(" pitch diameter small gear = {}".format(dispenser_2_pitch_diameter_small)) print(" number of teeth small gear = {}".format(dispenser_2_teeth_small)) # plot plate plt.figure(2) plt.plot(0.0, 0.0, 'o') circle = plt.Circle((0.0, 0.0), self.target_plate_radius, fill=False) plt.gca().add_artist(circle) plt.xlim((-self.target_plate_radius*1.1, self.target_plate_radius*1.1)) plt.ylim((-self.target_plate_radius*1.1, self.target_plate_radius*1.1)) plt.gca().set_aspect('equal', 'box') # plate coordinates print("plate coordinates: (x,y) = ({:8.3f}, {:8.3f})".format(0.0, 0.0)) self.tube_1_coords = {} self.tube_2_coords = {} self.tube_1_angles = {} self.tube_2_angles = {} self.tube_1_tangents = {} self.tube_1_tangent_angles = {} self.tube_1_cuts = {} self.tube_2_tangents = {} self.tube_2_tangent_angles = {} self.tube_2_cuts = {} print(" big circle coordinates:") for k in range(0,self.N): x = coords[k][0] * outer_radius y = coords[k][1] * outer_radius angle = arctan2(y, x) * 360.0 / (2.0 * math.pi) self.tube_1_coords[k] = (x,y) self.tube_1_angles[k] = angle print(" k = {}, (x,y) = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg".format(k, x, y, angle)) circle = plt.Circle((x,y), self.target_radius_1, fill=False) plt.gca().add_artist(circle) p1 = dispenser_1_pitch_diameter_big p2 = dispenser_1_pitch_diameter_small a1 = self.dispenser_module a2 = self.dispenser_module offset_1 = sqrt((p1 / 2 + p2 / 2) ** 2 - (p1 / 2 + a1) ** 2) - p2 / 2 - a2 print("dispenser 1 offset = {}".format(offset_1)) print(" big circle tangent points: ") for k in range(0, self.N): x = coords[k][0] * outer_radius y = coords[k][1] * outer_radius t1, t2 = compute_tangent_points((0, 0), (x, y), self.target_radius_1) angle1_rad = arctan2(t1[1], t1[0]) angle2_rad = arctan2(t2[1], t2[0]) angle1_deg = angle1_rad * 360.0 / (2.0 * math.pi) angle2_deg = angle2_rad * 360.0 / (2.0 * math.pi) self.tube_1_tangents[k] = (t1, t2) self.tube_1_tangent_angles[k] = (angle1_deg, angle2_deg) print( " k = {}, t1 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg\n t2 = ({:8.3f}, {:8.3f}), angle " "= {:8.3f} deg".format(k, t1[0], t1[1], angle1_deg, t2[0], t2[1], angle2_deg)) plt.plot(t1[0], t1[1], 'o') plt.plot(t2[0], t2[1], 'o') ## compute position of cut for dispenser gear # vector pointing from center in direction of tangent point v = np.array([math.cos(angle1_rad), math.sin(angle1_rad)]) cut_center = np.array(t1) - v * (offset_1 + dispenser_1_outer_diameter_small/2.0) self.tube_1_cuts[k] = {} self.tube_1_cuts[k]['center'] = cut_center self.tube_1_cuts[k]['tangent_point'] = t1 self.tube_1_cuts[k]['angle_deg'] = angle1_deg self.tube_1_cuts[k]['length'] = dispenser_1_outer_diameter_small self.tube_1_cuts[k]['width'] = 5.0 plt.plot(cut_center[0], cut_center[1], 'o') pass print(" small circle coordinates:") for k in range(0,self.N): x = coords_2[k][0] * outer_radius y = coords_2[k][1] * outer_radius angle = arctan2(y, x) * 360.0 / (2.0 * math.pi) self.tube_2_coords[k] = (x, y) self.tube_2_angles[k] = angle print(" k = {}, (x,y) = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg".format(k, x, y, angle)) circle = plt.Circle((x, y), self.target_radius_2, fill=False) plt.gca().add_artist(circle) p1 = dispenser_2_pitch_diameter_big p2 = dispenser_2_pitch_diameter_small a1 = self.dispenser_module a2 = self.dispenser_module offset_2 = sqrt((p1 / 2 + p2 / 2) ** 2 - (p1 / 2 + a1) ** 2) - p2 / 2 - a2 print("dispenser 2 offset = {}".format(offset_2)) print(" small circle tangent points: ") for k in range(0, self.N): x = coords_2[k][0] * outer_radius y = coords_2[k][1] * outer_radius t1, t2 = compute_tangent_points((0, 0), (x, y), self.target_radius_2) angle1_rad = arctan2(t1[1], t1[0]) angle2_rad = arctan2(t2[1], t2[0]) angle1_deg = angle1_rad * 360.0 / (2.0 * math.pi) angle2_deg = angle2_rad * 360.0 / (2.0 * math.pi) self.tube_2_tangents[k] = (t1, t2) self.tube_2_tangent_angles[k] = (angle1_deg, angle2_deg) print(" k = {}, t1 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg\n t2 = ({:8.3f}, {:8.3f}), angle " "= {:8.3f} deg".format(k, t1[0], t1[1], angle1_deg, t2[0], t2[1], angle2_deg)) plt.plot(t1[0], t1[1], 'o') plt.plot(t2[0], t2[1], 'o') ## compute position of cut for dispenser gear # vector pointing from center in direction of tangent point v = np.array([math.cos(angle1_rad), math.sin(angle1_rad)]) cut_center = np.array(t1) + v * (offset_2 + dispenser_2_outer_diameter_small/2.0) self.tube_2_cuts[k] = {} self.tube_2_cuts[k]['center'] = cut_center self.tube_2_cuts[k]['tangent_point'] = t1 self.tube_2_cuts[k]['angle_deg'] = angle1_deg self.tube_2_cuts[k]['length'] = dispenser_2_outer_diameter_small self.tube_2_cuts[k]['width'] = 5.0 plt.plot(cut_center[0], cut_center[1], 'o') pass pass """ input: file = svg with plate gear centered at (0,0) """ def generate_svg(self, file): f = open(file) f_lines = f.readlines() f.close() circle_found = False for k in range(len(f_lines)): current_line = f_lines[k] if 'transform' in current_line: # rotate gear such that teeth match the cut lines index = current_line.find('"\n') f_lines[k] = current_line[0:index] + " rotate(0.9)" + current_line[-2:] if 'd="m ' in current_line: # remove center whole drawn by gear-dev gear_data = current_line index = gear_data.find('z') # end of path containing gear outline coordinates f_lines[k] = gear_data[0:index+1] + gear_data[-2:] if ' command f_lines.remove(f_lines[-1]) # output plate as svg text = svg_circle(0, 'plate', (0,0), self.target_plate_radius) f_lines = f_lines + text output_all = False if output_all: f_lines = self.output_whole(f_lines) else: f_lines = self.output_segment(f_lines, 2) f_lines.append('\n') # write new svg image fw = open('output.svg', 'w') fw.writelines(f_lines) fw.close() pass def output_segment(self, f_lines, k): # k = which segment? k_next = (k + 1) % 5 # center hole a = self.tube_1_angles[k] a = a / 360.0 * 2.0 * np.pi vunit = np.array([np.cos(a), np.sin(a)]) p1 = vunit * self.target_center_hole_radius a2 = self.tube_1_angles[k_next] a2 = a2 / 360.0 * 2.0 * np.pi vunit2 = np.array([np.cos(a2), np.sin(a2)]) p2 = vunit2 * self.target_center_hole_radius text = svg_arc(p1, p2, self.target_center_hole_radius, 0, 1) f_lines = f_lines + text # big circles arcs c = self.tube_1_coords[k] angle = self.tube_1_angles[k] text = svg_half_circle(k, 'big circle', c, self.target_radius_1, angle) f_lines = f_lines + text c = self.tube_1_coords[k_next] angle = self.tube_1_angles[k_next] text = svg_half_circle(k_next, 'big circle', c, self.target_radius_1, angle, orientation_flag=0) f_lines = f_lines + text # small circle c = self.tube_2_coords[k] text = svg_circle(k, 'small circle', c, self.target_radius_2) f_lines = f_lines + text # gear pos for big circle c = self.tube_1_tangents[k_next] circle_midpoint = self.tube_1_coords[k_next] v = np.array(c[0]) - np.array(circle_midpoint) v = v / np.linalg.norm(v) marking_length = 5.0 p1 = c[0] p2 = c[0] + v * marking_length text = svg_line(p1, p2) f_lines = f_lines + text # rectangle for big circles c = self.tube_1_cuts[k_next] text = svg_rectangle(k_next, 'cut', c['center'], c['length'], c['width'], c['angle_deg']) f_lines = f_lines + text # gear pos for small circle c = self.tube_2_tangents[k] circle_midpoint = self.tube_2_coords[k] v = np.array(c[0]) - np.array(circle_midpoint) v = v/np.linalg.norm(v) marking_length = 5.0 p1 = c[0] p2 = c[0] + v * marking_length text = svg_line(p1, p2) f_lines = f_lines + text # rectangle for small circles c = self.tube_2_cuts[k] text = svg_rectangle(k, 'cut', c['center'], c['length'], c['width'], c['angle_deg']) f_lines = f_lines + text # segment border (right) a = self.tube_1_angles[k] a = a / 360.0 * 2.0 * np.pi r1 = np.linalg.norm(np.array(self.tube_1_coords[k])) - self.target_radius_1 vunit = np.array([np.cos(a), np.sin(a)]) p1 = vunit * self.target_center_hole_radius p2 = vunit * r1 #text = svg_line(p1, p2, 0.1) text = svg_line_puzzle(p1, p2) f_lines = f_lines + text r2 = np.linalg.norm(np.array(self.tube_1_coords[k])) + self.target_radius_1 p3 = vunit * r2 r3 = self.target_plate_radius - self.plate_module p4 = vunit * r3 text = svg_line_puzzle(p3, p4) #text = svg_line(p3, p4, 0.1) f_lines = f_lines + text outer_point_1 = p4 # segment border (left) a = self.tube_1_angles[k_next] a = a / 360.0 * 2.0 * np.pi r1 = np.linalg.norm(np.array(self.tube_1_coords[k_next])) - self.target_radius_1 vunit = np.array([np.cos(a), np.sin(a)]) p1 = vunit * self.target_center_hole_radius p2 = vunit * r1 #text = svg_line(p1, p2, 0.1) text = svg_line_puzzle(p1, p2) f_lines = f_lines + text r2 = np.linalg.norm(np.array(self.tube_1_coords[k_next])) + self.target_radius_1 p3 = vunit * r2 r3 = self.target_plate_radius - self.plate_module p4 = vunit * r3 #text = svg_line(p3, p4, 0.1) text = svg_line_puzzle(p3, p4) f_lines = f_lines + text outer_point_2 = p4 # truncate gear path for j in range(len(f_lines)): current_line = f_lines[j] if 'd="m ' in current_line: # remove center whole drawn by gear-dev gear_data = current_line index_start = gear_data.find('m') # end of path containing gear outline coordinates index_end = gear_data.find('z') coordinates_data_raw = gear_data[index_start+1:index_end].split() c_running = np.array([float(v) for v in coordinates_data_raw[0].split(',')]) coordinates = [c_running] for c in coordinates_data_raw[1:]: dv = np.array([float(v) for v in c.split(',')]) c_running = c_running + dv coordinates.append(c_running) pass dist_1 = [np.linalg.norm(c - outer_point_1 * svg_scale) for c in coordinates] dist_2 = [np.linalg.norm(c - outer_point_2 * svg_scale) for c in coordinates] # find minimum distance and keep only points between the two distances # problem: does not consider manual rotation of the plate # -> rotate points outer_point_1 and outer_point_2 before computing the distance # ... pass #f_lines[k] = gear_data[0:index+1] + gear_data[-2:] return f_lines def output_whole(self, f_lines): # output big circles as svg for k, c in self.tube_1_coords.items(): text = svg_circle(k, 'big circle', c, self.target_radius_1) f_lines = f_lines + text pass # output small circles as svg for k, c in self.tube_2_coords.items(): text = svg_circle(k, 'small circle', c, self.target_radius_2) f_lines = f_lines + text pass # gear markings for big cirlces and small circles for k, c in self.tube_1_tangents.items(): circle_midpoint = self.tube_1_coords[k] v = np.array(c[0]) - np.array(circle_midpoint) v = v/np.linalg.norm(v) marking_length = 5.0 p1 = c[0] p2 = c[0] + v * marking_length text = svg_line(p1, p2) f_lines = f_lines + text for k, c in self.tube_2_tangents.items(): circle_midpoint = self.tube_2_coords[k] v = np.array(c[0]) - np.array(circle_midpoint) v = v/np.linalg.norm(v) marking_length = 5.0 p1 = c[0] p2 = c[0] + v * marking_length text = svg_line(p1, p2) f_lines = f_lines + text pass # output cuts for big circles for k, c in self.tube_1_cuts.items(): text = svg_rectangle(k, 'cut', c['center'], c['length'], c['width'], c['angle_deg']) f_lines = f_lines + text pass # output cuts for small circles for k, c in self.tube_2_cuts.items(): text = svg_rectangle(k, 'cut', c['center'], c['length'], c['width'], c['angle_deg']) f_lines = f_lines + text pass # lines for manufacturing out of multiple pieces for k, a in self.tube_1_angles.items(): a = a/360.0 * 2.0 * np.pi r1 = np.linalg.norm(np.array(self.tube_1_coords[k])) - self.target_radius_1 vunit = np.array([np.cos(a), np.sin(a)]) p1 = vunit * self.target_center_hole_radius p2 = vunit * r1 text = svg_line(p1, p2, 0.1) f_lines = f_lines + text r2 = np.linalg.norm(np.array(self.tube_1_coords[k])) + self.target_radius_1 p3 = vunit * r2 r3 = self.target_plate_radius - self.plate_module p4 = vunit * r3 text = svg_line(p3, p4, 0.1) f_lines = f_lines + text pass return f_lines