2019-08-26 21:49:10 +00:00
|
|
|
from casadi import *
|
|
|
|
import matplotlib.pyplot as plt
|
2019-08-27 09:38:04 +00:00
|
|
|
import math
|
|
|
|
import operator
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-09-14 18:00:57 +00:00
|
|
|
from svg_utils import *
|
2019-09-06 09:38:35 +00:00
|
|
|
|
|
|
|
|
2019-08-27 09:38:04 +00:00
|
|
|
# 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])
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-08-27 09:38:04 +00:00
|
|
|
return radii[N], coords
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-09-06 09:38:35 +00:00
|
|
|
|
2019-08-27 09:38:04 +00:00
|
|
|
# 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))
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-08-27 09:38:04 +00:00
|
|
|
coords_sort = []
|
|
|
|
for a in a_sort:
|
|
|
|
coords_sort.append(coords[a[0]])
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-08-27 09:38:04 +00:00
|
|
|
return coords_sort
|
|
|
|
|
2019-09-06 09:38:35 +00:00
|
|
|
|
2019-08-27 20:33:53 +00:00
|
|
|
# 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)
|
|
|
|
|
2019-08-27 09:38:04 +00:00
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
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
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
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))
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
# compute coordinates and various measurements for fixed radii of plate and tubes
|
2019-09-06 09:38:35 +00:00
|
|
|
self.target_plate_radius = 160.0
|
2019-09-12 06:23:18 +00:00
|
|
|
self.target_center_hole_radius = 7.5
|
2019-09-12 08:54:10 +00:00
|
|
|
self.target_radius_1 = 50.5
|
2019-09-03 08:38:23 +00:00
|
|
|
self.target_radius_2 = 20.0
|
2019-09-03 20:34:20 +00:00
|
|
|
teeth = 200
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
D = 2 * self.target_plate_radius
|
2019-09-06 09:38:35 +00:00
|
|
|
self.plate_module = D/teeth
|
2019-08-26 21:49:10 +00:00
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
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))
|
2019-09-06 09:38:35 +00:00
|
|
|
print("module: M = {} mm".format(self.plate_module))
|
2019-09-03 08:38:23 +00:00
|
|
|
|
2019-09-03 20:34:20 +00:00
|
|
|
|
|
|
|
# parameters for dispenser gears
|
2019-09-06 09:38:35 +00:00
|
|
|
self.dispenser_module = 1.0
|
|
|
|
dispenser_1_target_pitch_diameter_big = 50.0
|
|
|
|
dispenser_1_target_pitch_diameter_small = 15.0
|
2019-09-03 20:34:20 +00:00
|
|
|
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
|
|
|
|
|
2019-09-06 09:38:35 +00:00
|
|
|
dispenser_2_target_pitch_diameter_big = 20.0
|
|
|
|
dispenser_2_target_pitch_diameter_small = 15.0
|
2019-09-03 20:34:20 +00:00
|
|
|
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
|
2019-09-06 09:38:35 +00:00
|
|
|
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))
|
2019-09-03 20:34:20 +00:00
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
# 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 = {}
|
2019-09-06 09:38:35 +00:00
|
|
|
self.tube_1_angles = {}
|
|
|
|
self.tube_2_angles = {}
|
2019-09-03 08:38:23 +00:00
|
|
|
self.tube_1_tangents = {}
|
|
|
|
self.tube_1_tangent_angles = {}
|
2019-09-06 09:38:35 +00:00
|
|
|
self.tube_1_cuts = {}
|
2019-09-03 08:38:23 +00:00
|
|
|
self.tube_2_tangents = {}
|
|
|
|
self.tube_2_tangent_angles = {}
|
2019-09-06 09:38:35 +00:00
|
|
|
self.tube_2_cuts = {}
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
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)
|
2019-09-06 09:38:35 +00:00
|
|
|
self.tube_1_angles[k] = angle
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2019-09-03 20:34:20 +00:00
|
|
|
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))
|
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
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)
|
|
|
|
|
2019-09-06 09:38:35 +00:00
|
|
|
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)
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
self.tube_1_tangents[k] = (t1, t2)
|
2019-09-06 09:38:35 +00:00
|
|
|
self.tube_1_tangent_angles[k] = (angle1_deg, angle2_deg)
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
print(
|
|
|
|
" k = {}, t1 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg\n t2 = ({:8.3f}, {:8.3f}), angle "
|
2019-09-06 09:38:35 +00:00
|
|
|
"= {:8.3f} deg".format(k, t1[0], t1[1], angle1_deg, t2[0], t2[1], angle2_deg))
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
plt.plot(t1[0], t1[1], 'o')
|
|
|
|
plt.plot(t2[0], t2[1], 'o')
|
|
|
|
|
2019-09-06 09:38:35 +00:00
|
|
|
## 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
|
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
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)
|
2019-09-06 09:38:35 +00:00
|
|
|
self.tube_2_angles[k] = angle
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2019-09-03 20:34:20 +00:00
|
|
|
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))
|
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
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)
|
2019-09-06 09:38:35 +00:00
|
|
|
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)
|
2019-09-03 08:38:23 +00:00
|
|
|
self.tube_2_tangents[k] = (t1, t2)
|
2019-09-06 09:38:35 +00:00
|
|
|
self.tube_2_tangent_angles[k] = (angle1_deg, angle2_deg)
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
print(" k = {}, t1 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg\n t2 = ({:8.3f}, {:8.3f}), angle "
|
2019-09-06 09:38:35 +00:00
|
|
|
"= {:8.3f} deg".format(k, t1[0], t1[1], angle1_deg, t2[0], t2[1], angle2_deg))
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
plt.plot(t1[0], t1[1], 'o')
|
|
|
|
plt.plot(t2[0], t2[1], 'o')
|
|
|
|
|
2019-09-06 09:38:35 +00:00
|
|
|
## 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
|
2019-09-03 20:34:20 +00:00
|
|
|
pass
|
|
|
|
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
input: file = svg with plate gear centered at (0,0)
|
|
|
|
"""
|
|
|
|
def generate_svg(self, file):
|
|
|
|
|
|
|
|
f = open(file)
|
|
|
|
f_lines = f.readlines()
|
|
|
|
f.close()
|
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
circle_def_start = None
|
|
|
|
circle_def_end = None
|
2019-09-12 06:23:18 +00:00
|
|
|
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:]
|
|
|
|
|
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# find begin and end of center circle definition
|
2019-09-12 06:23:18 +00:00
|
|
|
if '<circle' in current_line:
|
2019-09-15 18:26:41 +00:00
|
|
|
circle_def_start = k
|
|
|
|
if circle_def_start is not None and circle_def_end is None and '/>' in current_line:
|
|
|
|
circle_def_end = k
|
2019-09-12 06:23:18 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# remove center circle
|
|
|
|
del f_lines[circle_def_start:circle_def_end + 1]
|
2019-09-12 06:23:18 +00:00
|
|
|
|
|
|
|
# delete last line with </svg> command
|
2019-09-03 08:38:23 +00:00
|
|
|
f_lines.remove(f_lines[-1])
|
|
|
|
|
|
|
|
# output plate as svg
|
2019-09-14 18:00:57 +00:00
|
|
|
# text = svg_circle(0, 'plate', (0,0), self.target_plate_radius)
|
|
|
|
# f_lines = f_lines + text
|
2019-09-03 08:38:23 +00:00
|
|
|
|
2019-09-12 08:54:10 +00:00
|
|
|
output_all = False
|
|
|
|
if output_all:
|
2019-09-12 19:27:54 +00:00
|
|
|
f_lines = self.output_whole(f_lines)
|
2019-09-12 08:54:10 +00:00
|
|
|
else:
|
2019-09-15 18:26:41 +00:00
|
|
|
f_lines = self.output_segment(f_lines, 2, split_at_big_circles=False)
|
2019-09-12 19:27:54 +00:00
|
|
|
|
|
|
|
f_lines.append('</svg>\n')
|
|
|
|
|
|
|
|
# write new svg image
|
|
|
|
fw = open('output.svg', 'w')
|
|
|
|
fw.writelines(f_lines)
|
|
|
|
fw.close()
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
def output_segment(self, f_lines, k, split_at_big_circles=True):
|
2019-09-12 19:27:54 +00:00
|
|
|
# k = which segment?
|
2019-09-13 16:08:48 +00:00
|
|
|
k_next = (k + 1) % self.N
|
2019-09-15 18:26:41 +00:00
|
|
|
k_next_next = (k + 2) % self.N
|
|
|
|
|
|
|
|
if split_at_big_circles:
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# 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
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
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
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
f_lines += svg_arc(p1, p2, self.target_center_hole_radius, 0, 1)
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# big circles arcs
|
|
|
|
f_lines += svg_half_circle(k, 'big circle', self.tube_1_coords[k], self.target_radius_1, self.tube_1_angles[k])
|
|
|
|
f_lines += svg_half_circle(k_next, 'big circle', self.tube_1_coords[k_next], self.target_radius_1,
|
|
|
|
self.tube_1_angles[k_next], orientation_flag=0)
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# small circle
|
|
|
|
f_lines += svg_circle(k, 'small circle', self.tube_2_coords[k], self.target_radius_2)
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# gear pos for big circle
|
|
|
|
f_lines += svg_gear_marking(self.tube_1_tangents[k_next], self.tube_1_coords[k_next])
|
2019-09-12 08:54:10 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# cutout rectangle for big circles
|
|
|
|
f_lines += svg_rectangle(k_next, 'cut', self.tube_1_cuts[k_next])
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# gear pos for small circle
|
|
|
|
f_lines += svg_gear_marking(self.tube_2_tangents[k], self.tube_2_coords[k])
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# cutout rectangle for small circles
|
|
|
|
f_lines += svg_rectangle(k, 'cut', self.tube_2_cuts[k])
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# first segment border
|
|
|
|
f_lines += svg_segment_border_inner(self.tube_1_angles[k], self.target_center_hole_radius,
|
|
|
|
self.tube_1_coords[k], self.target_radius_1)
|
|
|
|
f_lines += svg_segment_border_outer(self.tube_1_angles[k], self.target_plate_radius, self.plate_module,
|
|
|
|
self.tube_1_coords[k], self.target_radius_1)
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# second segment border
|
|
|
|
f_lines += svg_segment_border_inner(self.tube_1_angles[k_next], self.target_center_hole_radius,
|
|
|
|
self.tube_1_coords[k_next], self.target_radius_1)
|
|
|
|
f_lines += svg_segment_border_outer(self.tube_1_angles[k_next], self.target_plate_radius, self.plate_module,
|
|
|
|
self.tube_1_coords[k_next], self.target_radius_1)
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
# find outmost points for segment cut lines
|
|
|
|
# in addition we rotate the points by 0.9 degrees because we also rotated the gear path
|
|
|
|
# by this amount above
|
|
|
|
r_pitch_minus_module = self.target_plate_radius - self.plate_module
|
|
|
|
a1 = (self.tube_1_angles[k] - 0.9) / 360.0 * 2.0 * np.pi
|
|
|
|
vunit1 = np.array([np.cos(a1), np.sin(a1)])
|
|
|
|
outer_point_1 = vunit1 * r_pitch_minus_module
|
2019-09-12 19:27:54 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
a2 = (self.tube_1_angles[k_next] - 0.9) / 360.0 * 2.0 * np.pi
|
|
|
|
vunit2 = np.array([np.cos(a2), np.sin(a2)])
|
|
|
|
outer_point_2 = vunit2 * r_pitch_minus_module
|
2019-09-13 16:08:48 +00:00
|
|
|
|
2019-09-15 18:26:41 +00:00
|
|
|
else:
|
|
|
|
# center hole
|
|
|
|
a = self.tube_2_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_2_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
|
|
|
|
|
|
|
|
f_lines += svg_arc(p1, p2, self.target_center_hole_radius, 0, 1)
|
|
|
|
|
|
|
|
# small circles arcs
|
|
|
|
f_lines += svg_half_circle(k, 'small circle', self.tube_2_coords[k], self.target_radius_2,
|
|
|
|
self.tube_2_angles[k])
|
|
|
|
f_lines += svg_half_circle(k_next, 'small circle', self.tube_2_coords[k_next], self.target_radius_2,
|
|
|
|
self.tube_2_angles[k_next], orientation_flag=0)
|
|
|
|
|
|
|
|
# big circle
|
|
|
|
f_lines += svg_circle(k, 'big circle', self.tube_1_coords[k_next], self.target_radius_1)
|
|
|
|
|
|
|
|
# gear pos for big circle
|
|
|
|
f_lines += svg_gear_marking(self.tube_1_tangents[k_next], self.tube_1_coords[k_next])
|
|
|
|
|
|
|
|
# cutout rectangle for big circles
|
|
|
|
f_lines += svg_rectangle(k, 'cut', self.tube_1_cuts[k_next])
|
|
|
|
f_lines += svg_rectangle(k, 'cut', self.tube_1_cuts[k_next_next])
|
|
|
|
|
|
|
|
# gear pos for small circle
|
|
|
|
f_lines += svg_gear_marking(self.tube_2_tangents[k_next], self.tube_2_coords[k_next])
|
|
|
|
|
|
|
|
# cutout rectangle for small circles
|
|
|
|
f_lines += svg_rectangle(k_next, 'cut', self.tube_2_cuts[k_next])
|
|
|
|
|
|
|
|
# first segment border
|
|
|
|
f_lines += svg_segment_border_inner(self.tube_2_angles[k], self.target_center_hole_radius,
|
|
|
|
self.tube_2_coords[k], self.target_radius_2, puzzle_scale=0.5, placement=0.25)
|
|
|
|
f_lines += svg_segment_border_outer(self.tube_2_angles[k], self.target_plate_radius, self.plate_module,
|
|
|
|
self.tube_2_coords[k], self.target_radius_2)
|
|
|
|
|
|
|
|
# second segment border
|
|
|
|
f_lines += svg_segment_border_inner(self.tube_2_angles[k_next], self.target_center_hole_radius,
|
|
|
|
self.tube_2_coords[k_next], self.target_radius_2, puzzle_scale=0.5, placement=0.25)
|
|
|
|
f_lines += svg_segment_border_outer(self.tube_2_angles[k_next], self.target_plate_radius, self.plate_module,
|
|
|
|
self.tube_2_coords[k_next], self.target_radius_2)
|
|
|
|
|
|
|
|
|
|
|
|
# find outmost points for segment cut lines
|
|
|
|
# in addition we rotate the points by 0.9 degrees because we also rotated the gear path
|
|
|
|
# by this amount above
|
|
|
|
r_pitch_minus_module = self.target_plate_radius - self.plate_module
|
|
|
|
a1 = (self.tube_2_angles[k] - 0.9) / 360.0 * 2.0 * np.pi
|
|
|
|
vunit1 = np.array([np.cos(a1), np.sin(a1)])
|
|
|
|
outer_point_1 = vunit1 * r_pitch_minus_module
|
|
|
|
|
|
|
|
a2 = (self.tube_2_angles[k_next] - 0.9) / 360.0 * 2.0 * np.pi
|
|
|
|
vunit2 = np.array([np.cos(a2), np.sin(a2)])
|
|
|
|
outer_point_2 = vunit2 * r_pitch_minus_module
|
2019-09-12 19:54:53 +00:00
|
|
|
|
|
|
|
# truncate gear path
|
2019-09-13 06:44:31 +00:00
|
|
|
for j in range(len(f_lines)):
|
|
|
|
current_line = f_lines[j]
|
2019-09-12 19:54:53 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2019-09-14 18:00:57 +00:00
|
|
|
# find nodes on gear path with minimal distance to segment cuts
|
2019-09-13 15:17:05 +00:00
|
|
|
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]
|
2019-09-12 19:54:53 +00:00
|
|
|
|
2019-09-13 16:08:48 +00:00
|
|
|
min_dist_index_1 = np.argmin(dist_1)
|
|
|
|
min_dist_index_2 = np.argmin(dist_2)
|
|
|
|
|
|
|
|
if min_dist_index_2 > min_dist_index_1:
|
|
|
|
coordinates = coordinates[min_dist_index_1:min_dist_index_2+1]
|
|
|
|
else:
|
|
|
|
coordinates = coordinates[min_dist_index_1:] + coordinates[0:min_dist_index_2]
|
|
|
|
|
|
|
|
coordinates_data_raw_new = "".join(['{},{} '.format(c[0], c[1]) for c in coordinates])
|
|
|
|
|
2019-09-14 18:00:57 +00:00
|
|
|
# keep only those nodes from the gear path that are between the segment cuts
|
2019-09-13 16:08:48 +00:00
|
|
|
gear_data_new = gear_data[0:index_start] + "M " + coordinates_data_raw_new + gear_data[index_end+1:]
|
|
|
|
f_lines[j] = gear_data_new
|
|
|
|
|
2019-09-12 19:27:54 +00:00
|
|
|
return f_lines
|
|
|
|
|
|
|
|
def output_whole(self, f_lines):
|
2019-09-03 08:38:23 +00:00
|
|
|
# output big circles as svg
|
2019-09-12 19:27:54 +00:00
|
|
|
for k, c in self.tube_1_coords.items():
|
|
|
|
text = svg_circle(k, 'big circle', c, self.target_radius_1)
|
2019-09-03 08:38:23 +00:00
|
|
|
f_lines = f_lines + text
|
|
|
|
pass
|
|
|
|
|
|
|
|
# output small circles as svg
|
2019-09-12 19:27:54 +00:00
|
|
|
for k, c in self.tube_2_coords.items():
|
2019-09-03 08:38:23 +00:00
|
|
|
text = svg_circle(k, 'small circle', c, self.target_radius_2)
|
|
|
|
f_lines = f_lines + text
|
|
|
|
pass
|
|
|
|
|
2019-09-06 09:38:35 +00:00
|
|
|
# gear markings for big cirlces and small circles
|
2019-09-12 19:27:54 +00:00
|
|
|
for k, c in self.tube_1_tangents.items():
|
2019-09-06 09:38:35 +00:00
|
|
|
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
|
|
|
|
|
2019-09-12 19:27:54 +00:00
|
|
|
for k, c in self.tube_2_tangents.items():
|
2019-09-06 09:38:35 +00:00
|
|
|
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
|
2019-09-12 19:27:54 +00:00
|
|
|
for k, c in self.tube_1_cuts.items():
|
2019-09-06 09:38:35 +00:00
|
|
|
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
|
2019-09-12 19:27:54 +00:00
|
|
|
for k, c in self.tube_2_cuts.items():
|
2019-09-06 09:38:35 +00:00
|
|
|
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
|
2019-09-12 19:27:54 +00:00
|
|
|
for k, a in self.tube_1_angles.items():
|
2019-09-06 09:38:35 +00:00
|
|
|
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)])
|
2019-09-12 06:23:18 +00:00
|
|
|
p1 = vunit * self.target_center_hole_radius
|
|
|
|
p2 = vunit * r1
|
2019-09-06 09:38:35 +00:00
|
|
|
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
|
|
|
|
|
2019-09-12 19:27:54 +00:00
|
|
|
return f_lines
|
2019-09-03 08:38:23 +00:00
|
|
|
|
|
|
|
|