Commit 7ddf5ec8 authored by Nicholas Gar Hei Chan's avatar Nicholas Gar Hei Chan
Browse files

Commenting changes

parent 97badd38
......@@ -9,6 +9,7 @@ The parse_gcode() function takes a GCODE file and turns it into a Model object.
import re
import numpy as np
from scipy.spatial import ConvexHull
try:
import matplotlib.pyplot as plt
except:
......@@ -21,19 +22,19 @@ class Model():
"""
Parameters
----------
layers: [Layer]
layers : [Layer]
List of Layer objects
max_x: float
max_x : float
Maximum x value of model
max_y: float
max_y : float
Maximum y value of model
max_z: float
max_z : float
Maximum z value of model
min_x: float
min_x : float
Minimum x value of model
min_y: float
min_y : float
Minimum y value of model
min_z: float
min_z : float
Minimum z value of model
"""
......@@ -45,12 +46,13 @@ class Model():
self.min_x = min_x
self.min_z = min_z
self.layer_heights = [layer.z_height for layer in layers]
def to_svgs(self, dir_name):
"""Saves layers as SVG images in directory
"""Saves all layers as SVG images in directory. Used for testing only
Parameters
----------
dir_name: str
dir_name : str
Directory name to save images into
"""
......@@ -66,20 +68,39 @@ class Line():
self.line_type = "regular"
def append_coords(self, x, y):
"""Append a coordinate to an existing line
Parameters
----------
x : float
x coordinate of point
y : float
y coordinate of point
"""
# If this is first point in the line, append the coordinates to the line
if len(self.x) == 0:
self.x.append(x)
self.y.append(y)
else:
prev_x = self.x[-1]
prev_y = self.y[-1]
# Compute the length between the new point and the last point
segment_length = np.sqrt((x-prev_x)**2+(y-prev_y)**2)
# Calculate number of points required to interpolate to a resolution of 0.1mm
num_points = int(np.floor(segment_length/0.1))
# Filter by segment length. Generally, infills are long lines defined by 2 points, whereas
# shells are defined by regularly spaced points.
if segment_length > 2.5:
if len(self.x) > 5:
self.line_type = "regular"
else:
self.line_type = "infill"
# Interpolation
if num_points < 2:
# If the number of points to interpolate is less than 2, just append the points
self.x.append(x)
self.y.append(y)
else:
......@@ -94,10 +115,12 @@ class Line():
self.x.extend(xs[1:])
self.y.extend(ys[1:])
def len(self):
def len(self):
"""Returns number of points in line - 1"""
return len(self.x) - 1
def is_empty(self):
"""Checks if line is empty"""
if self.len() > 0:
return False
else:
......@@ -110,8 +133,8 @@ class Layer():
def __init__(self, z_height):
"""
Parameters
/----------
z_height: float
----------
z_height : float
The height of the layer
"""
......@@ -130,13 +153,13 @@ class Layer():
Parameters
----------
x: float
x : float
X-coordinate of command
y: float
y : float
Y-coordinate of command
e: float
e : float
Extursion value
g: int
g : int
G-CODE command type (not used)
"""
......@@ -152,7 +175,6 @@ class Layer():
def new_line(self):
"""Makes a new line"""
self.lines.append(Line())
def to_svg(self, max_height, max_width, fn):
......@@ -161,11 +183,11 @@ class Layer():
Parameters
----------
max_height: float
max_height : float
Maximum height of model in mm
max_width: float
max_width : float
Maximum width of model in mm
fn: str
fn : str
Filename to save image to
"""
......@@ -184,26 +206,54 @@ class Layer():
f.write('</svg>')
def to_svg_inline(self, max_height, viewbox_width, viewbox_height):
"""
Returns a layer as an SVG image
Parameters
----------
max_height : float
Maximum height of model in mm
viewbox_width : float
Width of the SVG viewbox
viewbox_height : float
Height of the SVG viewbox
"""
# Append the SVG header first
out = ('<svg xmlns="http://www.w3.org/2000/svg"'
' xmlns:xlink="http://www.w3.org/1999/xlink"'
' viewBox="0 0 {} {}" height="{}" width="100%">\n').format(viewbox_width, viewbox_height, max_height)
# Iterate through each line
for line in self.lines:
points = '\t<polyline points="'
coords = [f"{x},{y} " for x,y in zip(line.x, line.y)]
if line.line_type == "regular":
# Append the points with black color if regular extrusion
points = points + "".join(coords) + ('"\n\tstyle="fill:none;stroke:black;stroke-width:0.4;'
'stroke-linejoin:round;stroke-linecap:round" />\n')
if line.line_type == "infill":
# Append the points with blue color if infill
points = points + "".join(coords) + ('"\n\tstyle="fill:none;stroke:blue;stroke-width:0.4;'
'stroke-linejoin:round;stroke-linecap:round" />\n')
out += points
# Add circles for each sample point
for point in zip(self.sample_points['x'], self.sample_points['y']):
out += '\t<circle cx="{}" cy="{}" r="1" stroke="red" stroke-width="0" fill="red"></circle>\n'.format(point[0], point[1])
out += '</svg>'
return out
def plot_layer(self, col_reg, col_infill):
""" Plot a layer in Matplotlib. Used for testing
Parameters
----------
col_reg : str
Colorspec for regular extrusion
col_infill : str
Colorspec for infill
"""
for line in self.lines:
if line.line_type == "regular":
plt.plot(line.x, line.y, col_reg) #self.z_height,
......@@ -211,14 +261,29 @@ class Layer():
plt.plot(line.x, line.y, col_infill)
def to_csv(self, fn):
""" Saves points as as csv
Parameters
----------
fn : str
Filename to save to
"""
with open(fn, "w") as f:
for line in self.lines:
[f.write("{},{}\n".format(x, y)) for (x,y) in zip(line.x, line.y)]
def len(self):
""" Returns the number of lines in the layer """
return len([line for line in self.lines if line.len() > 0])
def get_points(self, ignore_infill = False):
""" Returns a dictionary of all points in the layer
Keyword Arguments
-----------------
ignore_infill : bool (Optional)
Set to True to ignore infills. Defaults as false
"""
x_pts = []
y_pts = []
......@@ -228,50 +293,92 @@ class Layer():
return dict([('x', x_pts), ('y', y_pts), ('num_pts', len(x_pts))])
def gen_sample_points(self, method, num_samples):
""" Generate sample points for this layer
Sampling point algorithms live here. To add a new method, add a new elif section with an appropriate
name. Can also add keyword arguments as needed.
Parameters
----------
method : string
Name of method to use
num_samples : int
Number of samples to generate
"""
# Get the points in this layer
points = self.get_points()
# Random sampling method: Pick X number of points from all available points
if method == "Random sampling":
sample_pts = list(np.random.randint(0, points['num_pts'], num_samples))
x_pts = [points['x'][i] for i in sample_pts]
y_pts = [points['y'][i] for i in sample_pts]
self.sample_points = dict([('x', x_pts), ('y', y_pts)])
# Min-max method: Pick points along the maximum/minimum of the X-Y axes, and a 45 degree tilted axes
elif method == "Min-max":
# Put the points into a numpy array to make life a bit easier
point_array = np.array([points['x'], points['y']])
# Find the minimum and maximum values along the regular axes
max_vals = np.argmax(point_array, axis=1)
min_vals = np.argmin(point_array, axis=1)
rot45 = np.array([[0.7071, -0.7071],[0.7071, 0.7071]])
point_array_rot = np.matmul(rot45, point_array)
max_vals_rot = np.argmax(point_array_rot, axis=1)
rot45 = np.array([[0.7071, -0.7071],[0.7071, 0.7071]]) # Transformation matrix for a rotation by 45 degrees
point_array_rot = np.matmul(rot45, point_array) # Rotate all points by 45 degrees
max_vals_rot = np.argmax(point_array_rot, axis=1) # Repeat process of finding min/max points
min_vals_rot = np.argmin(point_array_rot, axis=1)
# Remove duplicate values if any
sample_pts = np.unique(np.concatenate((max_vals, min_vals, max_vals_rot, min_vals_rot))).tolist()
x_pts = [points['x'][i] for i in sample_pts]
y_pts = [points['y'][i] for i in sample_pts]
self.sample_points = dict([('x', x_pts), ('y', y_pts)])
# Inside outside method: Pick 10 points from outside the convex hull, pick 10 points from inside the convex hull
elif method == "Inside-outside":
# Put the points into a numpy array to make life a bit easier
point_list = []
[point_list.append([x,y]) for x,y in zip(points['x'], points['y'])]
features = np.array(point_list)
# If there are less than 4 points, do nothing
if features.shape[0] < 4:
self.sample_points = dict([('x', []), ('y', [])])
else:
hull = ConvexHull(features)
outside_indx = np.random.choice(hull.vertices, 10)
hull = ConvexHull(features) # Compute the convex hull
outside_indx = np.random.choice(hull.vertices, 10) # Get the indices for 10 random points along the vertices of the hull
# Find all points that are inside the convex hull
all_inside_indx = np.arange(1, len(point_list))
all_inside_indx = all_inside_indx[np.isin(all_inside_indx, hull.vertices, invert=True)]
# Pick 10 points from inside the hull
inside_indx = np.random.choice(all_inside_indx, 10)
all_points = np.concatenate((inside_indx, outside_indx));
x_pts = [feature[0] for feature in features[all_points]]
y_pts = [feature[1] for feature in features[all_points]]
self.sample_points = dict([('x', x_pts), ('y', y_pts)])
if method == "none":
self.sample_points = dict([('x', []), ('y', [])])
# Return nothing if no sampling method
else:
self.sample_points = dict([('x', []), ('y', [])])
def parse_gcode(filename, sample_spacing, samples_per_layer, method):
""" Function for parsing GCODE file
Parameters
----------
filename : str
File to process
sample_spacing : int
Number of layers to space between sample layers
samples_per_layer : int
Number of samples per layer
method : str
Sampling method. Choose from methods in gen_sample_points method
"""
layers = []
num_layers = 0
layer_heights = []
......@@ -345,10 +452,10 @@ def parse_gcode(filename, sample_spacing, samples_per_layer, method):
for i in range(len(lines)): # Iterate through each line
layers[index[zs[i]]].append_coords(xs[i],ys[i],es[i],Gs[i]) # Append each command into the model list
layers = [layer for layer in layers if layer.len() > 0]
layers = [layer for layer in layers if layer.len() > 0] # Find non-empty layers
for i in range(len(layers)):
if (i % sample_spacing) == 0 and (i != 0):
layers[i].gen_sample_points(method, samples_per_layer)
layers[i].gen_sample_points(method, samples_per_layer) # Generate sample points for desired layers
else:
layers[i].gen_sample_points("none", 0)
model = Model(layers, max(xs), max(ys), max(zs), min(xs), min(ys), min(zs))
......
......@@ -22,46 +22,46 @@ layer = model.layers[10]
layer.to_svg(model.max_y, model.max_x, 'test.svg')
#layer.plot_layer()
#plt.show()
layer.plot_layer('k', 'b')
plt.show()
"""
Uncomment this block to save each layer to an SVG image
into a folder called "layers"
"""
#for layer_index, layer in enumerate(model.layers):
# layer.to_svg(model.max_y, model.max_x, "layers/{}.svg".format(layer_index))
AlphaShape test here
"""
# Put all points in layer into a numpy array
points = layer.get_points()
point_list = []
[point_list.append([x,y]) for x,y in zip(points['x'], points['y'])]
features = np.array(point_list)
# Import Delaunay and ConvexHull functions from scipy
from scipy.spatial import Delaunay, ConvexHull
# Create the Delaunay triangulation
tri = Delaunay(features)
valid_simps = []
bound_simps = []
valid_simplices = []
bound_simplices = []
# Alpha value of 0.8mm
alpha = 0.8
edges = set()
# Iterate through simplices in the triangulation
for i, element in enumerate(tri.simplices):
# Check the length of each edge of the simplex
l1 = np.sqrt(np.sum((features[element[0], :] - features[element[1], :])**2))
l2 = np.sqrt(np.sum((features[element[0], :] - features[element[2], :])**2))
l3 = np.sqrt(np.sum((features[element[1], :] - features[element[2], :])**2))
num_friendos = np.sum(tri.neighbors[i] != -1)
num_neighbors = np.sum(tri.neighbors[i] != -1)
# If all the edges are smaller than alpha, then the simplex is part of the body
if l1 < alpha and l2 < alpha and l3 < alpha:
valid_simps.append(i)
valid_simplices.append(i)
for i, neighbors in enumerate(tri.neighbors[valid_simps]):
if np.sum(np.isin(valid_simps, neighbors)) < 3:
bound_simps.append(valid_simps[i])
# Iterate through each valid simplex, checking for the number of neighbors it has
for i, neighbors in enumerate(tri.neighbors[valid_simplices]):
# If a simplex has less than 3 layers, it is on a boundary
if np.sum(np.isin(valid_simplices, neighbors)) < 3:
bound_simplices.append(valid_simplices[i])
plt.triplot(features[:,0], features[:,1], tri.simplices.copy()[bound_simps,:])
plt.triplot(features[:,0], features[:,1], tri.simplices.copy()[bound_simplices,:])
plt.show()
"""
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment