Skip to content

Commit

Permalink
Added infill code
Browse files Browse the repository at this point in the history
Added code to infill the sliced contours and create these lines on the SVG files for viewing/confirmation.
Need to address infill spacing variable and menu options still.
  • Loading branch information
evanchodora committed Apr 19, 2018
1 parent c818bc5 commit 91e289b
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 30 deletions.
20 changes: 16 additions & 4 deletions Slicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
import path
from drawlines import draw_lines

'''
Program designed to open and view ASCII STL files
Evan Chodora, 2018
https://github.com/evanchodora/stl-slicer
[email protected]
'''


# Class to draw an STL object from an ASCII STL file
class DrawObject:
Expand Down Expand Up @@ -84,9 +92,13 @@ def slice_geometry(self):
geometry, normals = gtransform.rotation(self.model.geometry, self.model.normal, 1, 180)
# Compute the clipped point pairs at the current slice z coordinate
point_pairs = slice.compute_points_on_z(geometry, z, xdim.get(), ydim.get(), zdim.get())
# Output the slices to svg files for confirmation (optional)
path.svgcreate(point_pairs, z, xdim.get())
# Run the contour building algorithm to sort the point pairs into continous contour sets
# Create infill paths (X direction)
fillx = slice.infill(point_pairs, 0, 0.25*25.4)
# Create infill path (Y direction)
filly = slice.infill(point_pairs, 1, 0.25*25.4)
# Output the slices to svg files for confirmation/viewing
path.svgcreate(point_pairs, z, xdim.get(), fillx, filly)
# Run the contour building algorithm to sort the point pairs into continuous contour sets
contour = slice.build_contours(point_pairs)
# Create printer head path CSV file
path.headpath(contour, z)
Expand Down Expand Up @@ -263,7 +275,7 @@ def about_popup():
# Info box about the software from the Help menu
messagebox.showinfo('About STL Slicer',
'Created by Evan Chodora, 2018\n\n Designed to open and view ASCII STL files and perform'
' geometry slicing')
' geometry slicing and 3D printer path generation')


# ****** Initialize Main Window ******
Expand Down
31 changes: 22 additions & 9 deletions path.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,30 @@
should be turned on when moving to that location
Evan Chodora, 2018
https://github.com/evanchodora/viewer
https://github.com/evanchodora/stl-slicer
[email protected]
'''


def svgcreate(pairs, z, ymax):
def svgcreate(pairs, z, ymax, fillx, filly):
# Create a new SVG file with the file name as the z-coordinate of the slice
dwg = svgwrite.Drawing('outputs/' + str(z) + '.svg', profile='tiny')

# Create lines for the geometry segments sliced on the given z-plane
for pair in pairs:
# Offset the y position by ymax-y in order to account for the difference in SVG coordinate system (+Y is down)
dwg.add(dwg.line((pair[0], ymax-pair[1]), (pair[2], ymax-pair[3]), stroke=svgwrite.rgb(0, 0, 0, "%")))
# Create lines for
for fill_line in fillx:
# Loop over points for each fill line in x (always even number)
for pts in range(int(len(fill_line[1])/2)):
dwg.add(dwg.line((fill_line[0], ymax - fill_line[1][2*pts]), (fill_line[0], ymax - fill_line[1][2*pts+1]),
stroke=svgwrite.rgb(0, 0, 0, "%")))
for fill_line in filly:
# Loop over points for each fill line in y (always even number)
for pts in range(int(len(fill_line[1])/2)):
dwg.add(dwg.line((fill_line[1][2*pts], ymax - fill_line[0]), (fill_line[1][2*pts+1], ymax - fill_line[0]),
stroke=svgwrite.rgb(0, 0, 0, "%")))
dwg.save() # Save the SVG file to the output folder


Expand All @@ -30,23 +43,23 @@ def headpath(contour, z):
begin = []
# Open the path.csv file to append new lines, create it if it does not exist
with open('outputs/path.csv', 'a', newline='') as csvfile:
pathwriter = csv.writer(csvfile, delimiter=' ', quotechar='|', quoting=csv.QUOTE_MINIMAL)
path_writer = csv.writer(csvfile, delimiter=' ', quotechar='|', quoting=csv.QUOTE_MINIMAL)
# Loop over the contour segments
for segment in contour:
if segment[4] == contour_num:
if start == 1:
begin = [segment[1], segment[2]]
begin = [segment[0], segment[1]]
row = [segment[0], segment[1], z, 0]
pathwriter.writerow(row)
path_writer.writerow(row)
start = 0
else:
row = [segment[0], segment[1], z, 1]
pathwriter.writerow(row)
path_writer.writerow(row)
else:
# Add the beginning coordinate to the end of a contour to complete the contour
contour_num = segment[4]
row1 = [begin[1], begin[2], z, 1]
row1 = [begin[0], begin[1], z, 1]
row2 = [segment[0], segment[1], z, 0]
pathwriter.writerow(row1)
pathwriter.writerow(row2)
path_writer.writerow(row1)
path_writer.writerow(row2)
return
72 changes: 55 additions & 17 deletions slice.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import numpy as np
import gtransform
from math import isinf

'''
Codes to slice geometry at a given value Z (height above the print bed)
Evan Chodora, 2018
https://github.com/evanchodora/viewer
https://github.com/evanchodora/stl-slicer
[email protected]
'''

Expand All @@ -26,7 +27,7 @@ def geom_to_bed_coords(geometry, xdim, ydim, zdim):


def interpolation(p1, p2, slice_z):

# Interpolate between points based on slice z value
vector = (p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]) # Compute the vector between point 1 and 2 on the line in 3D
rel_z = slice_z - p1[2] # Relative z = height between slice z and the z of the lower point
a = rel_z/vector[2] # Parametric length along the vector
Expand All @@ -36,7 +37,7 @@ def interpolation(p1, p2, slice_z):


def compute_points_on_z(geometry, z, xdim, ydim, zdim):

# Compute the points on the z slice plane
geometry = geom_to_bed_coords(geometry, xdim, ydim, zdim) # Position and size geometry correctly
geometry = np.around(geometry, 5)
num_faces = int((geometry.shape[0]) / 3) # Every 3 points represents a single face (length/3)
Expand All @@ -48,21 +49,21 @@ def compute_points_on_z(geometry, z, xdim, ydim, zdim):
xy = [geometry[3*(f+1)-3].tolist(), geometry[3*(f+1)-2].tolist(), geometry[3*(f+1)-1].tolist()]
# Store the 3 lines that make up face "f" using lists of points
# Remember: Screen Plotting = (X, Y, Z) and Printing Geometry = (Z, X, Y)
line = [[xy[0][2], xy[0][0], xy[0][1], xy[1][2], xy[1][0], xy[1][1]],
[xy[1][2], xy[1][0], xy[1][1], xy[2][2], xy[2][0], xy[2][1]],
[xy[2][2], xy[2][0], xy[2][1], xy[0][2], xy[0][0], xy[0][1]]]
line = [[xy[0][2], xy[0][0], xy[0][1]],
[xy[1][2], xy[1][0], xy[1][1]],
[xy[2][2], xy[2][0], xy[2][1]]]

if (line[0][2] > z and line[1][2] < z) or (line[0][2] < z and line[1][2] > z):
if (line[1][2] < z < line[0][2]) or (line[0][2] < z < line[1][2]):
p1 = [line[0][0], line[0][1], line[0][2]]
p2 = [line[1][0], line[1][1], line[1][2]]
pairs.append(interpolation(p1, p2, z))

if (line[0][2] > z and line[2][2] < z) or (line[0][2] < z and line[2][2] > z):
if (line[2][2] < z < line[0][2]) or (line[0][2] < z < line[2][2]):
p1 = [line[0][0], line[0][1], line[0][2]]
p2 = [line[2][0], line[2][1], line[2][2]]
pairs.append(interpolation(p1, p2, z))

if (line[1][2] > z and line[2][2] < z) or (line[1][2] < z and line[2][2] > z):
if (line[2][2] < z < line[1][2]) or (line[1][2] < z < line[2][2]):
p1 = [line[1][0], line[1][1], line[1][2]]
p2 = [line[2][0], line[2][1], line[2][2]]
pairs.append(interpolation(p1, p2, z))
Expand All @@ -79,21 +80,23 @@ def compute_points_on_z(geometry, z, xdim, ydim, zdim):
else:
points.append(pairs)
points = [item for sublist in points for item in sublist] # Flatten list sets into an array
edge_points = np.asarray(points).reshape((-1, 4)) # Reshape and convert to X1Y1, X2Y2 numpy array
edge_points = np.asarray(points).reshape((-1, 4)) # Reshape and convert to X1,Y1,X2,Y2 numpy array
edge_points = np.around(edge_points, 5)

return edge_points


def build_contours(edge_points):
# Function to build continuous contours for point sets on the slice z
contours = [] # Initialize contour point loop array
contour_num = 1
tol = 0.005
contour_num = 1 # Initialize the count for the number of contours
tol = 0.005 # Tolerance criteria for matching the next point in the contour
points_left = edge_points # Initialize points that are remaining = original points
tail = []
loop_cnt = 1
loop_cnt = 1 # Count number of times searhced over the remaining points for geometry error handling
while len(points_left) != 0: # Loop over the point pairs and the index in the data array
j = 1
if not contours: # First point pair needs to be added to the contour data
if not contours: # First point pair needs to be added to the contour data if variable is empty
pair = points_left[0, :]
contours.append([pair[0], pair[1], pair[2], pair[3], contour_num])
tail = [pair[2], pair[3]] # Tail is the second point of the line pair
Expand All @@ -103,13 +106,13 @@ def build_contours(edge_points):
if abs(pair[0]-tail[0]) < tol and abs(pair[1]-tail[1]) < tol:
contours.append([pair[0], pair[1], pair[2], pair[3], contour_num])
points_left = np.delete(points_left, j-1, axis=0)
tail = [pair[2], pair[3]]
tail = [pair[2], pair[3]] # Tail is the second point of the line pair
loop_cnt = 1
break
if abs(pair[2] - tail[0]) < tol and abs(pair[3] - tail[1]) < tol:
contours.append([pair[2], pair[3], pair[0], pair[1], contour_num])
points_left = np.delete(points_left, j-1, axis=0)
tail = [pair[0], pair[1]]
tail = [pair[0], pair[1]] # Tail is the second point of the line pair
loop_cnt = 1
break
j = j + 1
Expand All @@ -121,5 +124,40 @@ def build_contours(edge_points):
contours.append([pair[0], pair[1], pair[2], pair[3], contour_num])
tail = [pair[2], pair[3]] # Tail is the second point of the line pair
points_left = np.delete(points_left, 0, axis=0) # Remove the point pair from the array of points
contours = np.asarray(contours).reshape((-1, 5))
contours = np.asarray(contours).reshape((-1, 5)) # Reshape and convert to X1,Y1,X2,Y2,contour_num numpy array

return contours


def infill(pairs, dir, spacing):
# Function to compute the line infill spacing for the 3D printing
# direction: X=0, Y=1
fill = []

if len(pairs) != 0:
# Calculate max and min dimensions of the sliced points
min_pos = min(np.min(pairs[:, dir]), np.min(pairs[:, dir+2]))
max_pos = max(np.max(pairs[:, dir]), np.max(pairs[:, dir+2]))

num_passes = int((max_pos - min_pos)/spacing) # Number of infill lines to cover the object

for fill_pass in range(num_passes):
loc = min_pos + fill_pass*spacing # Increment fill pass position by the infill spacing variable
pts = []
for segment in pairs:
if segment[dir] < loc < segment[dir+2] or segment[dir+2] < loc < segment[dir]:
m = (segment[3]-segment[1])/(segment[2]-segment[0])
# Fill lines at x-locations
if dir == 0:
pts.append(m * (loc - segment[dir]) + segment[dir + 1]) # y = m(x_loc-x1)+y1
# Fill lines at y-locations
else:
# Check if the slope is infinite (#/0)
if isinf(m):
pts.append(segment[dir+1]) # Intercept at either x-location of the segment
else:
pts.append((loc-segment[dir])/m + segment[dir-1]) # x = (y_loc-y1)/m + x1
pts.sort() # Sort points in order to construct infill path lines
fill.append((loc, pts)) # Append to fill path list

return fill

0 comments on commit 91e289b

Please sign in to comment.