-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
1 parent
c818bc5
commit 91e289b
Showing
3 changed files
with
93 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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: | ||
|
@@ -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) | ||
|
@@ -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 ****** | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
||
|
||
|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | ||
''' | ||
|
||
|
@@ -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 | ||
|
@@ -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) | ||
|
@@ -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)) | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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 |