From 6ac2c864c5e7cd2a7b20bd488ea5ce101cdb2f81 Mon Sep 17 00:00:00 2001 From: spirkelmann Date: Sat, 14 Sep 2019 20:00:57 +0200 Subject: [PATCH] cleaned up code for segment output --- prototype/circles.py | 297 +++++------------------------------------ prototype/svg_utils.py | 222 ++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 267 deletions(-) create mode 100644 prototype/svg_utils.py diff --git a/prototype/circles.py b/prototype/circles.py index d191102..d8d9e55 100644 --- a/prototype/circles.py +++ b/prototype/circles.py @@ -3,176 +3,7 @@ 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 +from svg_utils import * # this function reads and processes data for optimal circle packaging obtained form packomania.com @@ -568,8 +399,8 @@ class PlateLayout: 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 + # text = svg_circle(0, 'plate', (0,0), self.target_plate_radius) + # f_lines = f_lines + text output_all = False if output_all: @@ -586,8 +417,8 @@ class PlateLayout: pass - def output_segment(self, f_lines, k): + def output_segment(self, f_lines, k): # k = which segment? k_next = (k + 1) % self.N @@ -602,102 +433,44 @@ class PlateLayout: 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 - + f_lines += svg_arc(p1, p2, self.target_center_hole_radius, 0, 1) # 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 + 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) # small circle - c = self.tube_2_coords[k] - text = svg_circle(k, 'small circle', c, self.target_radius_2) - f_lines = f_lines + text + f_lines += svg_circle(k, 'small circle', self.tube_2_coords[k], self.target_radius_2) # 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) + f_lines += svg_gear_marking(self.tube_1_tangents[k_next], self.tube_1_coords[k_next]) - 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 + # cutout rectangle for big circles + f_lines += svg_rectangle(k_next, 'cut', self.tube_1_cuts[k_next]) # 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) + f_lines += svg_gear_marking(self.tube_2_tangents[k], self.tube_2_coords[k]) - marking_length = 5.0 + # cutout rectangle for small circles + f_lines += svg_rectangle(k, 'cut', self.tube_2_cuts[k]) - p1 = c[0] - p2 = c[0] + v * marking_length + # 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) - text = svg_line(p1, p2) - f_lines = f_lines + text + # 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) - # 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 - - # 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 + # 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)]) @@ -726,6 +499,7 @@ class PlateLayout: coordinates.append(c_running) pass + # find nodes on gear path with minimal distance to segment cuts 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] @@ -736,24 +510,13 @@ class PlateLayout: coordinates = coordinates[min_dist_index_1:min_dist_index_2+1] else: coordinates = coordinates[min_dist_index_1:] + coordinates[0:min_dist_index_2] - print("TODO: check this") coordinates_data_raw_new = "".join(['{},{} '.format(c[0], c[1]) for c in coordinates]) + # keep only those nodes from the gear path that are between the segment cuts gear_data_new = gear_data[0:index_start] + "M " + coordinates_data_raw_new + gear_data[index_end+1:] f_lines[j] = gear_data_new - - - - # 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): diff --git a/prototype/svg_utils.py b/prototype/svg_utils.py new file mode 100644 index 0000000..0984c53 --- /dev/null +++ b/prototype/svg_utils.py @@ -0,0 +1,222 @@ +import numpy as np +import math + +# scale in inkscape +# 1 unit = 0.28222 mm +svg_scale = 1000.0 / 282.222 + + +def svg_circle(id, name, c, r): + # create circle object centered at point c with radius r + 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 + # the orientation flag determines if the upper or the lower half of the circle is drawn + + # 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): + center = c['center'] + width = c['length'] + height = c['width'] + angle = c['angle_deg'] + x = np.sqrt(center[0] ** 2 + center[1] ** 2) - width / 2 + y = - height + text = ['\n ' + '\n ' + '\n' + .format(angle, x, y, width, height)] + + return text + + +def svg_line(p1, p2, width=1.0): + text = [''.format(p1[0], + p1[1], + p2[0], + p2[1], + width)] + + return text + + +def svg_gear_marking(tangent_coord, circle_midpoint, marking_length=5.0): + c = tangent_coord + v = np.array(c[0]) - np.array(circle_midpoint) + v = v / np.linalg.norm(v) + + p1 = c[0] + p2 = c[0] + v * marking_length + + text = svg_line(p1, p2) + + return text + +def svg_segment_border_inner(angle, center_hole_radius, circle_pos, circle_radius): + a = angle + a = a / 360.0 * 2.0 * np.pi + r1 = np.linalg.norm(np.array(circle_pos)) - circle_radius + vunit = np.array([np.cos(a), np.sin(a)]) + p1 = vunit * center_hole_radius + p2 = vunit * r1 + text = svg_line_puzzle(p1, p2) + + return text + +def svg_segment_border_outer(angle, plate_pitch_radius, plate_gear_module, circle_pos, circle_radius): + a = angle + a = a / 360.0 * 2.0 * np.pi + vunit = np.array([np.cos(a), np.sin(a)]) + r2 = np.linalg.norm(np.array(circle_pos)) + circle_radius + p3 = vunit * r2 + r3 = plate_pitch_radius - plate_gear_module + p4 = vunit * r3 + text = svg_line_puzzle(p3, p4) + + return text \ No newline at end of file