Broke the UI somehow, but hooking up some buttons

This commit is contained in:
2019-01-09 22:50:44 -08:00
parent a8d9efb6bb
commit e165aa673f
2 changed files with 217 additions and 97 deletions

View File

@@ -0,0 +1,16 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="3">
<item index="0" class="java.lang.String" itemvalue="decorator" />
<item index="1" class="java.lang.String" itemvalue="six" />
<item index="2" class="java.lang.String" itemvalue="pkg-resources" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>

356
main.py
View File

@@ -1,4 +1,6 @@
from tkinter import Tk, Label, filedialog, Button #from tkinter import Tk, Label, filedialog, Button, LEFT, RIGHT,
from tkinter import *
from tkinter import filedialog
from PIL import Image, ImageTk from PIL import Image, ImageTk
from svgpathtools import svg2paths, Line, QuadraticBezier, CubicBezier from svgpathtools import svg2paths, Line, QuadraticBezier, CubicBezier
import cairo, subprocess, bezier, os, math, time import cairo, subprocess, bezier, os, math, time
@@ -6,12 +8,82 @@ import numpy as np
class GCoder(Tk): class GCoder(Tk):
def clear_screen(self, ):
self.png_context.rectangle(0, 0, self.bed_actual_x, self.bed_actual_y)
self.png_context.set_source_rgba(1, 1, 1, 1.0)
self.png_context.fill()
self.png_context.set_source_rgba(0, 0, 0, 1.0)
self.png_context.stroke()
self.svg_context.rectangle(0, 0, self.bed_actual_x, self.bed_actual_y)
self.svg_context.set_source_rgba(1, 1, 1, 1.0)
self.svg_context.fill()
self.svg_context.set_source_rgba(0, 0, 0, 1.0)
self.svg_context.stroke()
def flip_markers(self):
self.lift_markers = not self.lift_markers
def update_highpass_value(self, value):
self.highpass_filter = value
def re_render(self):
self.clear_screen()
# self.render_gcode()
#
# if self.label is not None:
# self.label.pack_forget()
#
# # Apply the rendered gcode image to the UI
# self.image_ref = ImageTk.PhotoImage(
# Image.frombuffer("RGBA", (self.bed_actual_x, self.bed_actual_y), self.png_surface.get_data().tobytes(), "raw", "BGRA", 0, 1))
# self.label = Label(self, image=self.image_ref)
# self.label.pack(expand=True, fill="both")
def init_surfaces(self):
self.png_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.bed_actual_x, self.bed_actual_y)
self.svg_surface = cairo.SVGSurface("tmp/rendered-output-t.svg", self.bed_actual_x, self.bed_actual_y)
self.png_context = cairo.Context(self.png_surface)
self.png_context.scale(1, 1)
self.png_context.set_line_width(0.4)
self.svg_context = cairo.Context(self.svg_surface)
self.svg_context.scale(1, 1)
self.svg_context.set_line_width(0.4)
def save_surfaces(self):
self.png_surface.write_to_png('tmp/rendered-output.png')
# Save the SVG so we can view it, then immediately reopen it so it's ready for a re-render
self.svg_surface.finish()
os.rename("tmp/rendered-output-t.svg", "tmp/rendered-output.svg")
self.svg_surface = cairo.SVGSurface("tmp/rendered-output-t.svg", self.bed_actual_x, self.bed_actual_y)
self.svg_context = cairo.Context(self.svg_surface)
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.png_surface = None
self.svg_surface = None
self.png_context = None
self.svg_context = None
# Setup the file structure # Setup the file structure
if not os.path.exists("output"): if not os.path.exists("output"):
os.makedirs("output") os.makedirs("output")
if not os.path.exists("tmp"):
os.makedirs("tmp")
self.highpass_filter = 15
self.label = None
self.image_ref = None
# Height at which the pen touches and draws on the surface # Height at which the pen touches and draws on the surface
self.touch_height = 20 self.touch_height = 20
@@ -22,7 +94,7 @@ class GCoder(Tk):
# XY movement speed # XY movement speed
self.speed = 500 self.speed = 500
# Weather we render lift markers # Weather we render lift markers
self.lift_markers = True self.lift_markers = False
# X and Y offsets to place the image on A11 paper # X and Y offsets to place the image on A11 paper
self.offset_x = 75 + self.head_x_offset self.offset_x = 75 + self.head_x_offset
@@ -33,6 +105,10 @@ class GCoder(Tk):
self.bed_min_x = self.offset_x self.bed_min_x = self.offset_x
self.bed_max_y = 280 self.bed_max_y = 280
self.bed_min_y = 20 self.bed_min_y = 20
self.bed_actual_x = 300
self.bed_actual_y = 300
# First cycle base case flag
self.started = False self.started = False
self.gcode_preamble = ''' self.gcode_preamble = '''
@@ -54,42 +130,51 @@ class GCoder(Tk):
M84 ; Turn off the motors M84 ; Turn off the motors
'''.format(75) '''.format(75)
w, h = 300, 300 # UI counter for times the pen was lifted
self.lift_counter = 0
self.geometry("{}x{}".format(w, h)) # Initialize TK
self.geometry("{}x{}".format(self.bed_actual_x, self.bed_actual_y))
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 300, 300) # UI ELEMENTS
self.init_surfaces()
self.context = cairo.Context(self.surface) self.rightframe = Frame(self)
self.context.scale(1, 1) self.rightframe.pack(side=RIGHT)
self.context.set_line_width(0.4)
self.button = Button(self.rightframe, text="Select Image", command=self.file_select_callback)
self.button = Button(self, text="Select Image", command=self.file_select_callback)
self.button.pack() self.button.pack()
self.button = Button(self.rightframe, text="Re-Render", command=self.re_render)
self.button.pack()
self.lift_markers_checkbox = Checkbutton(self.rightframe, text="Lift Markers", command=self.flip_markers)
self.lift_markers_checkbox.pack()
self.highpass_slider = Scale(self.rightframe, command=self.update_highpass_value, resolution=0.1, to=15)
self.highpass_slider.set(self.highpass_filter)
self.highpass_slider.pack()
# Start TK
self.mainloop() self.mainloop()
def file_select_callback(self): def file_select_callback(self):
filepath = filedialog.askopenfilename(initialdir=".", title="Select file", filepath = filedialog.askopenfilename(initialdir=".", title="Select file",
filetypes=(("jpeg files", "*.jpg"), ("all files", "*.*"))) filetypes=(("jpeg files", "*.jpg"), ("all files", "*.*")))
# User didn't select a file
if len(filepath) is 0: if len(filepath) is 0:
return return
self.context.rectangle(0, 0, 300, 300)
self.context.set_source_rgba(1, 1, 1, 1.0)
self.context.fill()
self.context.set_source_rgba(0, 0, 0, 1.0)
filename = os.path.basename(filepath) filename = os.path.basename(filepath)
self.convert_image(filename) self.convert_image(filename)
self.convert_gcode() self.convert_gcode()
self.clear_screen()
self.render_gcode() self.render_gcode()
self._image_ref = ImageTk.PhotoImage( self.image_ref = ImageTk.PhotoImage(Image.frombuffer("RGBA", (self.bed_actual_x, self.bed_actual_y), self.png_surface.get_data().tobytes(), "raw", "BGRA", 0, 1))
Image.frombuffer("RGBA", (300, 300), self.surface.get_data().tobytes(), "raw", "BGRA", 0, 1)) self.label = Label(self, image=self.image_ref)
self.label = Label(self, image=self._image_ref)
self.label.pack(expand=True, fill="both") self.label.pack(expand=True, fill="both")
def convert_image(self, file_name): def convert_image(self, file_name):
@@ -106,7 +191,7 @@ class GCoder(Tk):
print("Running mkbitmap...") print("Running mkbitmap...")
start = time.time() start = time.time()
subprocess.call(["mkbitmap", "input-images/{}.bmp".format(base_name), "-x", subprocess.call(["mkbitmap", "input-images/{}.bmp".format(base_name), "-x",
"-f", "15", "-f", "{}".format(self.highpass_filter),
# "-b", "0", # "-b", "0",
"-o", "input-images/{}-n.bmp".format(base_name) "-o", "input-images/{}-n.bmp".format(base_name)
]) ])
@@ -124,6 +209,109 @@ class GCoder(Tk):
]) ])
print("Run took [{:.2f}] seconds\n".format(time.time() - start)) print("Run took [{:.2f}] seconds\n".format(time.time() - start))
def convert_gcode(self):
# read in the svg
paths, attributes = svg2paths("tmp/conversion-output.svg")
bounding_x_max = None
bounding_x_min = None
bounding_y_max = None
bounding_y_min = None
for path in paths:
bbox = path.bbox()
if bounding_x_max is None or bbox[0] > bounding_x_max:
bounding_x_max = bbox[0]
if bounding_x_min is None or bbox[1] < bounding_x_min:
bounding_x_min = bbox[1]
if bounding_y_max is None or bbox[2] > bounding_y_max:
bounding_y_max = bbox[2]
if bounding_y_min is None or bbox[3] > bounding_y_min:
bounding_y_min = bbox[3]
print("Maximum X : {:.2f}".format(bounding_x_max))
print("Minimum Y : {:.2f}".format(bounding_x_min))
print("Maximum X : {:.2f}".format(bounding_y_max))
print("Minimum Y : {:.2f}".format(bounding_y_min))
max_dim = max(bounding_x_max, bounding_x_min, bounding_y_max, bounding_y_min)
scale = (300 - self.offset_x) / max_dim
print("Scaling to : {:.5f}\n".format(scale))
# Start the gcode
gcode = ""
gcode += self.gcode_preamble
# Walk through the paths and create the GCODE
for path in paths:
previous_x = None
previous_y = None
for part in path:
start = part.start
end = part.end
start_x = start.real * scale + self.offset_x
start_y = start.imag * scale + self.offset_y
end_x = end.real * scale + self.offset_x
end_y = end.imag * scale + self.offset_y
# Check to see if the endpoint of the last cycle continues and wether we need to lift the pen or not
lift = True
if previous_x is not None and previous_y is not None:
if abs(start.real - previous_x) < 30 and abs(start.imag - previous_y) < 30:
lift = False
# if the pen needs to lift,
# if lift:
previous_x = end.real
previous_y = end.imag
if lift:
gcode += "G1 Z{:.3f}\n".format(self.raise_height + self.touch_height)
else:
gcode += "# NOT LIFTING [{}]\n".format(self.lift_counter)
if isinstance(part, CubicBezier):
nodes = np.asfortranarray([
[start.real, part.control1.real, part.control2.real, end.real],
[start.imag, part.control1.imag, part.control2.imag, end.imag],
])
curve = bezier.Curve.from_nodes(nodes)
evals = []
pos = np.linspace(0.1, 1, 10)
for i in pos:
evals.append(curve.evaluate(i))
gcode += "G1 X{:.3f} Y{:.3f}\n".format(start_x, start_y)
gcode += "G1 Z{:.3f} \n".format(self.touch_height)
for i in evals:
x = i[0][0]
y = i[1][0]
gcode += "G1 X{:.3f} Y{:.3f}\n".format(x * scale + self.offset_x, y * scale + self.offset_y)
if isinstance(part, Line):
gcode += "G1 X{:.3f} Y{:.3f}\n".format(start_x, start_y)
gcode += "G1 Z{:.3f} \n".format(self.touch_height)
gcode += "G1 X{:.3f} Y{:.3f}\n".format(end_x, end_y)
gcode += self.gcode_end
output_gcode = open("output/gcode-output.gcode", "w")
output_gcode.write(gcode)
output_gcode.close()
def render_gcode(self): def render_gcode(self):
file = open("output/gcode-output.gcode", "r") file = open("output/gcode-output.gcode", "r")
@@ -158,8 +346,20 @@ class GCoder(Tk):
# signify a lift # signify a lift
if prev_x is not None and prev_y is not None and self.lift_markers: if prev_x is not None and prev_y is not None and self.lift_markers:
self.context.arc(prev_x - self.head_x_offset, prev_y, 0.5, 0, 2*math.pi) self.png_context.arc(prev_x - self.head_x_offset, prev_y, 0.5, 0, 2 * math.pi)
self.context.stroke() self.png_context.stroke()
self.svg_context.arc(prev_x - self.head_x_offset, prev_y, 0.5, 0, 2 * math.pi)
self.svg_context.stroke()
self.svg_context.set_source_rgba(1, 1, 1, 1.0)
self.svg_context.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
self.svg_context.set_font_size(3)
self.svg_context.move_to(prev_x - self.head_x_offset, prev_y)
self.svg_context.show_text(str(self.lift_counter))
self.lift_counter += 1
self.svg_context.stroke()
self.svg_context.set_source_rgba(0, 0, 0, 1.0)
prev_x = None prev_x = None
prev_y = None prev_y = None
@@ -167,10 +367,13 @@ class GCoder(Tk):
y = None y = None
if (prev_x != x and prev_x is not None) or (prev_y != y and prev_y is not None): if (prev_x != x and prev_x is not None) or (prev_y != y and prev_y is not None):
self.context.line_to(prev_x - self.head_x_offset, prev_y) self.png_context.line_to(prev_x - self.head_x_offset, prev_y)
self.context.line_to(x - self.head_x_offset, y) self.png_context.line_to(x - self.head_x_offset, y)
self.context.stroke() self.png_context.stroke()
self.svg_context.line_to(prev_x - self.head_x_offset, prev_y)
self.svg_context.line_to(x - self.head_x_offset, y)
self.svg_context.stroke()
print("Largest X : " + str(largest_x)) print("Largest X : " + str(largest_x))
print("Smallest X : " + str(smallest_x)) print("Smallest X : " + str(smallest_x))
@@ -188,108 +391,9 @@ class GCoder(Tk):
if smallest_y < self.bed_min_y: if smallest_y < self.bed_min_y:
print("Y_UNDERFLOW") print("Y_UNDERFLOW")
def convert_gcode(self): self.save_surfaces()
#self.init_surfaces()
# read in the svg
paths, attributes = svg2paths("tmp/conversion-output.svg")
bounding_x_max = None
bounding_x_min = None
bounding_y_max = None
bounding_y_min = None
for path in paths:
bbox = path.bbox()
if bounding_x_max is None or bbox[0] > bounding_x_max:
bounding_x_max = bbox[0]
if bounding_x_min is None or bbox[1] < bounding_x_min:
bounding_x_min = bbox[1]
if bounding_y_max is None or bbox[2] > bounding_y_max:
bounding_y_max = bbox[2]
if bounding_y_min is None or bbox[3] > bounding_y_min:
bounding_y_min = bbox[3]
print("Maximum X : {}".format(bounding_x_max))
print("Minimum Y : {}".format(bounding_x_min))
print("Maximum X : {}".format(bounding_y_max))
print("Minimum Y : {}".format(bounding_y_min))
max_dim = max(bounding_x_max, bounding_x_min, bounding_y_max, bounding_y_min)
scale = (300 - self.offset_x) / max_dim
print("Scaling to : {}\n".format(scale))
# Start the gcode
gcode = ""
gcode += self.gcode_preamble
# Walk through the paths and create the GCODE
for path in paths:
previous_x = None
previous_y = None
for part in path:
start = part.start
end = part.end
start_x = start.real * scale + self.offset_x
start_y = start.imag * scale + self.offset_y
end_x = end.real * scale + self.offset_x
end_y = end.imag * scale + self.offset_y
# Check to see if the endpoint of the last cycle continues and wether we need to lift the pen or not
lift = True
if previous_x is not None and previous_y is not None:
if abs(start.real - previous_x) < 30 and abs(start.imag - previous_y) < 30:
lift = False
# if the pen needs to lift,
# if lift:
previous_x = end.real
previous_y = end.imag
if lift:
gcode += "G1 Z{}\n".format(self.raise_height + self.touch_height)
else:
gcode += "# NOT LIFTING\n"
if isinstance(part, CubicBezier):
nodes = np.asfortranarray([
[start.real, part.control1.real, part.control2.real, end.real],
[start.imag, part.control1.imag, part.control2.imag, end.imag],
])
curve = bezier.Curve.from_nodes(nodes)
evals = []
pos = np.linspace(0.1, 1, 10)
for i in pos:
evals.append(curve.evaluate(i))
gcode += "G1 X{} Y{}\n".format(start_x, start_y)
gcode += "G1 Z{} \n".format(self.touch_height)
for i in evals:
x = i[0][0]
y = i[1][0]
gcode += "G1 X{} Y{}\n".format(x * scale + self.offset_x, y * scale + self.offset_y)
if isinstance(part, Line):
gcode += "G1 X{} Y{}\n".format(start_x, start_y)
gcode += "G1 Z{} \n".format(self.touch_height)
gcode += "G1 X{} Y{}\n".format(end_x, end_y)
gcode += self.gcode_end
output_gcode = open("output/gcode-output.gcode", "w")
output_gcode.write(gcode)
output_gcode.close()
if __name__ == "__main__": if __name__ == "__main__":
GCoder() GCoder()