Skip to content

Instantly share code, notes, and snippets.

@GrantTrebbin
Created January 17, 2017 13:49
Show Gist options
  • Save GrantTrebbin/7cb290b91f2192a2551339e8b053c2f7 to your computer and use it in GitHub Desktop.
Save GrantTrebbin/7cb290b91f2192a2551339e8b053c2f7 to your computer and use it in GitHub Desktop.
Create and Arrange Rectangles with relationships.
# rectangleBuilder
# Grant Trebbin - 2017_01_17
import svgwrite
class Rectangle:
def __init__(self, rectangle_width, rectangle_height):
# Calculate coordinates for corners and middle of sides for rectangle
# These are offset from the bottom left
# B = bottom
# T = top
# L = left
# R = right
# M = middle
self.coordinates = {'BL': [0, 0],
'ML': [0, rectangle_height / 2],
'TL': [0, rectangle_height],
'TM': [rectangle_width / 2, rectangle_height],
'TR': [rectangle_width, rectangle_height],
'MR': [rectangle_width, rectangle_height / 2],
'BR': [rectangle_width, 0],
'BM': [rectangle_width / 2, 0]}
# Position of the bottom left corner
self.offset = [0, 0]
# information about the rectangle
self.dimensions = [rectangle_width,
rectangle_height,
rectangle_width * rectangle_height]
def __call__(self, marker):
# When called, the Rectangle object will return the coordinates of the
# specified marker. For Example
#
# test_rectangle = Rectangle(100, 50)
# test_rectangle('TM')
#
# will return a list containing the xy coordinates for the top middle
# [50, 50]
marker_relative = self.coordinates[marker]
marker_absolute = [marker_relative[0] + self.offset[0],
marker_relative[1] + self.offset[1]]
return marker_absolute
def __str__(self):
# When printed, the coordinates of all four corners are displayed
return str([self("BL"), self("TL"), self("TR"), self("BR")])
def position(self, marker, location, move_relative):
# Place the rectangle at certain xy position in the world
# Marker is the xy coordinate on the rectangle to use as a reference
# point. The rectangle will be shifted so that this point is located
# at the location variable. Move_relative allows the rectangle to be
# shifted from the final position. For Example
#
# A = rectangle(10, 50)
# B = rectangle(20, 100)
# A.position('BM', [0,0], [0,0])
# B.position('BM', A('TM'), [1 , 1])
#
# Rectangle A is positioned so that its bottom middle point is at the
# origin. Rectangle B is positioned so that its bottom middle is
# shifted 1 up and 1 to the right of the top middle of rectangle A
marker_coordinates = self.coordinates[marker]
self.offset = [move_relative[0] + location[0] - marker_coordinates[0],
move_relative[1] + location[1] - marker_coordinates[1]]
def make_svg_coord_transform(total_width, total_height, minimum_x, minimum_y):
# The test box was designed using the standard xy coordinate system for
# mathematics. SVG is different the y axis is flipped.
# This changes coordinates so that the SVG output is right.
return lambda v: [v[0] - minimum_x, total_height - v[1] + minimum_y]
# Define box parameters
#d = 230 + 20
#w = 166 + 20
#h = 54 + 14 + 14 + 10 + 10
w = 125.4
d = 77.5
h = 16.9
t = 4.2
alpha = 0.55
b = alpha * t
gt = 1
gi = 1
gs = 1
# Create rectangles
rA = Rectangle(w + 4*t, (d + 2*b - gt) / 2)
rB = Rectangle(w + 2*b, h + t + 2*b)
rC = Rectangle(w + 2*t + 2*b, d + 2*b)
rD = Rectangle(w + 2*b, h + t + 2*b)
rE = Rectangle(w + 4 * t, (d + 2 * b - gt) / 2)
rF = Rectangle(h + 2*b, d + 2*t)
rG = Rectangle((w + 2*t + 2*b - gi) / 2, d)
rH = Rectangle(h + 2*b, d + 2*t)
rI = Rectangle((w + 2*t + 2*b - gi) / 2, d)
rJ = Rectangle((d + 2*b - gs) / 2, h)
rK = Rectangle((d + 2*b - gs) / 2, h)
rL = Rectangle((d + 2*b - gs) / 2, h)
rM = Rectangle((d + 2*b - gs) / 2, h)
# Position the rectangles by defining relationships
rA.position("BM", [0, 0], [0, 0])
rB.position("BM", rA("TM"), [0, 0])
rC.position("BM", rB("TM"), [0, 0])
rD.position("BM", rC("TM"), [0, 0])
rE.position("BM", rD("TM"), [0, 0])
rF.position("MR", rC("ML"), [0, 0])
rG.position("MR", rF("ML"), [0, 0])
rH.position("ML", rC("MR"), [0, 0])
rI.position("ML", rH("MR"), [0, 0])
rJ.position("TR", rD("TL"), [0, -t - b])
rK.position("TL", rD("TR"), [0, -t - b])
rL.position("BR", rB("BL"), [0, t + b])
rM.position("BL", rB("BR"), [0, t + b])
# Create a list of the outline coordinates
outline = [rA('BL'), rA('TL'), rB('BL'), rL('BR'), rL('BL'),
rL('TL'), rL('TR'), rB('TL'), rC('BL'), rF('BR'),
rF('BL'), rG('BR'), rG('BL'), rG('TL'), rG('TR'),
rF('TL'), rF('TR'), rC('TL'), rD('BL'), rJ('BR'),
rJ('BL'), rJ('TL'), rJ('TR'), rD('TL'), rE('BL'),
rE('TL'), rE('TR'), rE('BR'), rD('TR'), rK('TL'),
rK('TR'), rK('BR'), rK('BL'), rD('BR'), rC('TR'),
rH('TL'), rH('TR'), rI('TL'), rI('TR'), rI('BR'),
rI('BL'), rH('BR'), rH('BL'), rC('BR'), rB('TR'),
rM('TL'), rM('TR'), rM('BR'), rM('BL'), rB('BR'),
rA('TR'), rA('BR'), rA('BL')]
# Create a list of the start and end coordinates of each bend line
bends = [[rB('BL'), rB('BR')],
[rB('TL'), rB('TR')],
[rD('BL'), rD('BR')],
[rD('TL'), rD('TR')],
[rC('TL'), rC('BL')],
[rC('TR'), rC('BR')],
[rG('TR'), rG('BR')],
[rI('TL'), rI('BL')],
[rJ('TR'), rJ('BR')],
[rK('TL'), rK('BL')],
[rL('TR'), rL('BR')],
[rM('TL'), rM('BL')]]
# Calculate the extents of the rectangles
rectangles = [rA, rB, rC, rD, rE, rF, rG, rH, rI, rJ, rK, rL, rM]
min_x = 0
max_x = 0
min_y = 0
max_y = 0
for rectangle in rectangles:
corners = ['TL', 'TR', 'BL', 'BR']
for corner in corners:
test_coord = rectangle(corner)
if test_coord[0] > max_x:
max_x = test_coord[0]
if test_coord[0] < min_x:
min_x = test_coord[0]
if test_coord[1] > max_y:
max_y = test_coord[1]
if test_coord[1] < min_y:
min_y = test_coord[1]
# Generate the transform to make coordinates appear correct in an SVG file
height = max_y - min_y
width = max_x - min_x
SVG_coord_transform = make_svg_coord_transform(width, height, min_x, min_y)
# Transform the outline and bend lines for the SVG
SVG_points = []
for point in outline:
SVG_points.append(SVG_coord_transform(point))
SVG_lines = []
for line in bends:
SVG_lines.append([SVG_coord_transform(line[0]),
SVG_coord_transform(line[1])])
# Draw the SVG
dwg = svgwrite.Drawing('test.svg',
size=(str(width)+'mm', str(height)+'mm'),
viewBox=('0 0 ' + str(width) + ' ' + str(height)))
for line in SVG_lines:
dwg.add(dwg.line(line[0], line[1],
stroke='red',
fill='none',
stroke_width=1))
dwg.add(dwg.polyline(SVG_points, stroke='black', fill='none', stroke_width=1))
dwg.save()
# Find all the horizontal edges. Add a list of the x coord and the start and
# end y coordinates to a list
horizontal_lines = []
vertical_lines = []
for rect in rectangles:
horizontal_lines.append([rect('TL')[1], rect('TL')[0], rect('TR')[0]])
horizontal_lines.append([rect('BL')[1], rect('BL')[0], rect('BR')[0]])
vertical_lines.append([rect('TL')[0], rect('BL')[1], rect('TL')[1]])
vertical_lines.append([rect('TR')[0], rect('BR')[1], rect('TR')[1]])
# When drawing the box an offset from the bottom or left edge may be needed
x_plot_offset = 30
y_plot_offset = 30
# Generate the plot instructions rounded to the nearest 0.5
offset_horizontal_lines = []
for line in horizontal_lines:
offset_horizontal_lines.append([0.5 * round((line[0] - min_y + y_plot_offset)/0.5),
0.5 * round((line[1] - min_x + x_plot_offset)/0.5),
0.5 * round((line[2] - min_x + x_plot_offset)/0.5)])
offset_vertical_lines = []
for line in vertical_lines:
offset_vertical_lines.append([0.5 * round((line[0] - min_x + x_plot_offset)/0.5),
0.5 * round((line[1] - min_y + y_plot_offset)/0.5),
0.5 * round((line[2] - min_y + y_plot_offset)/0.5)])
# Sort the horizontal and vertical line sections to make plotting easier
offset_horizontal_lines.sort(key=lambda v: (v[0], v[1]))
offset_vertical_lines.sort(key=lambda v: (v[0], v[1]))
# Calculate the total area
total_area = rA.dimensions[2] + rB.dimensions[2] + rC.dimensions[2] +\
rD.dimensions[2] + rE.dimensions[2] + rF.dimensions[2] +\
rG.dimensions[2] + rH.dimensions[2] + rI.dimensions[2] +\
rJ.dimensions[2] + rK.dimensions[2] + rL.dimensions[2] +\
rM.dimensions[2]
# Print out all the stats and the cut drawing instructions.
print('Item width =', w, '\nItem depth =', d, '\nItem height =', h)
print('Template width =', width, '\nTemplate height =', height)
print('Total area =', round(total_area))
print("Cut and bend list")
print("Horizontal Lines")
for line in offset_horizontal_lines:
print(line[0], ', (', line[1], '-', line[2], ') length =', line[2] - line[1])
print("\nVertical Lines")
for line in offset_vertical_lines:
print(line[0], ', (', line[1], '-', line[2], ') length =', line[2] - line[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment