Commit 5ec2ec1c authored by Guillaume Seguin's avatar Guillaume Seguin

Merge /home/ixce/Printrun into experimental

parents 91ffeb24 1b8aaff8
......@@ -67,6 +67,7 @@ class printcore():
self.recvcb = None #impl (wholeline)
self.sendcb = None #impl (wholeline)
self.printsendcb = None #impl (wholeline)
self.layerchangecb = None #impl (wholeline)
self.errorcb = None #impl (wholeline)
self.startcb = None #impl ()
self.endcb = None #impl ()
......@@ -412,6 +413,10 @@ class printcore():
if self.printing and self.queueindex < len(self.mainqueue):
(layer, line) = self.mainqueue.idxs(self.queueindex)
gline = self.mainqueue.all_layers[layer].lines[line]
if self.layerchangecb and self.queueindex > 0:
(prev_layer, prev_line) = self.mainqueue.idxs(self.queueindex - 1)
if prev_layer != layer:
self.layerchangecb(layer)
tline = gline.raw
#check for host command
if tline.lstrip().startswith(";@"):
......
......@@ -148,7 +148,7 @@ class GCode(object):
(l.strip() for l in data)
if l2]
self._preprocess_lines()
self._preprocess_extrusion()
self.filament_length = self._preprocess_extrusion()
self._create_layers()
self._preprocess_layers()
......@@ -161,10 +161,12 @@ class GCode(object):
return
gline = Line(command)
self.lines.append(gline)
self._preprocess([gline])
self._preprocess_lines([gline])
self._preprocess_extrusion([gline])
self.append_layer.lines.append(gline)
self.layer_idxs.append(self.append_layer_id)
self.line_idxs.append(len(self.append_layer.lines))
return gline
def _preprocess_lines(self, lines = None):
"""Checks for G20, G21, G90 and G91, sets imperial and relative flags"""
......@@ -173,7 +175,7 @@ class GCode(object):
imperial = self.imperial
relative = self.relative
relative_e = self.relative_e
for line in self.lines:
for line in lines:
if line.is_move:
line.relative = relative
line.relative_e = relative_e
......@@ -197,12 +199,14 @@ class GCode(object):
self.relative = relative
self.relative_e = relative_e
def _preprocess_extrusion(self):
def _preprocess_extrusion(self, lines = None, cur_e = 0):
if not lines:
lines = self.lines
total_e = 0
max_e = 0
cur_e = 0
for line in self.lines:
for line in lines:
if line.e == None:
continue
if line.is_move:
......@@ -217,7 +221,7 @@ class GCode(object):
elif line.command == "G92":
cur_e = line.e
self.filament_length = max_e
return max_e
# FIXME : looks like this needs to be tested with list Z on move
def _create_layers(self):
......
......@@ -25,8 +25,6 @@ from pyglet.gl import *
from pyglet import gl
from pyglet.graphics.vertexbuffer import create_buffer, VertexBufferObject
from . import vector
from printrun.printrun_utils import install_locale
install_locale('pronterface')
......@@ -37,8 +35,8 @@ def compile_display_list(func, *options):
glEndList()
return display_list
def numpy2vbo(nparray, target = GL_ARRAY_BUFFER, usage = GL_STATIC_DRAW):
vbo = create_buffer(nparray.nbytes, target = target, usage = usage, vbo = True)
def numpy2vbo(nparray, target = GL_ARRAY_BUFFER, usage = GL_STATIC_DRAW, use_vbos = True):
vbo = create_buffer(nparray.nbytes, target = target, usage = usage, vbo = use_vbos)
vbo.bind()
vbo.set_data(nparray.ctypes.data)
return vbo
......@@ -203,15 +201,10 @@ class GcodeModel(Model):
"""
Model for displaying Gcode data.
"""
# vertices for arrow to display the direction of movement
arrow = numpy.require([
[0.0, 0.0, 0.0],
[0.4, -0.1, 0.0],
[0.4, 0.1, 0.0],
], 'f')
color_printed = (0.2, 0.75, 0, 0.6)
use_vbos = True
loaded = False
def load_data(self, model_data, callback=None):
......@@ -232,11 +225,6 @@ class GcodeModel(Model):
current_pos = (gline.current_x, gline.current_y, gline.current_z)
vertex_list.append(current_pos)
arrow = self.arrow
# position the arrow with respect to movement
arrow = vector.rotate(arrow, movement_angle(prev_pos, current_pos), 0.0, 0.0, 1.0)
arrow_list.extend(arrow)
vertex_color = self.movement_color(gline)
color_list.append(vertex_color)
......@@ -249,22 +237,11 @@ class GcodeModel(Model):
callback(layer_idx + 1, num_layers)
self.vertices = numpy.array(vertex_list, dtype = GLfloat)
self.colors = numpy.array(color_list, dtype = GLfloat)
self.arrows = numpy.array(arrow_list, dtype = GLfloat)
# by translating the arrow vertices outside of the loop, we achieve a
# significant performance gain thanks to numpy. it would be really nice
# if we could rotate in a similar fashion...
self.arrows = self.arrows + self.vertices[1::2].repeat(3, 0)
# for every pair of vertices of the model, there are 3 vertices for the arrow
assert len(self.arrows) == ((len(self.vertices) // 2) * 3), \
'The 2:3 ratio of model vertices to arrow vertices does not hold.'
self.colors = numpy.array(color_list, dtype = GLfloat).repeat(2, 0)
self.max_layers = len(self.layer_stops) - 1
self.num_layers_to_draw = self.max_layers
self.printed_until = -1
self.arrows_enabled = False
self.initialized = False
self.loaded = True
......@@ -275,7 +252,7 @@ class GcodeModel(Model):
def copy(self):
copy = GcodeModel()
for var in ["vertices", "arrows", "colors", "max_layers", "num_layers_to_draw", "printed_until", "arrows_enabled", "layer_stops"]:
for var in ["vertices", "colors", "max_layers", "num_layers_to_draw", "printed_until", "layer_stops"]:
setattr(copy, var, getattr(self, var))
copy.loaded = True
copy.initialized = False
......@@ -313,13 +290,8 @@ class GcodeModel(Model):
# ------------------------------------------------------------------------
def init(self):
self.vertex_buffer = numpy2vbo(self.vertices)
self.vertex_color_buffer = numpy2vbo(self.colors.repeat(2, 0)) # each pair of vertices shares the color
if self.arrows_enabled:
self.arrow_buffer = numpy2vbo(self.arrows)
self.arrow_color_buffer = numpy2vbo(self.colors.repeat(3, 0)) # each triplet of vertices shares the color
self.vertex_buffer = numpy2vbo(self.vertices, use_vbos = self.use_vbos)
self.vertex_color_buffer = numpy2vbo(self.colors, use_vbos = self.use_vbos) # each pair of vertices shares the color
self.initialized = True
def display(self, mode_2d=False):
......@@ -330,9 +302,6 @@ class GcodeModel(Model):
self._display_movements(mode_2d)
if self.arrows_enabled:
self._display_arrows()
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_VERTEX_ARRAY)
glPopMatrix()
......@@ -377,24 +346,3 @@ class GcodeModel(Model):
self.vertex_buffer.unbind()
self.vertex_color_buffer.unbind()
def _display_arrows(self):
self.arrow_buffer.bind()
has_vbo = isinstance(self.arrow_buffer, VertexBufferObject)
if has_vbo:
glVertexPointer(3, GL_FLOAT, 0, None)
else:
glVertexPointer(3, GL_FLOAT, 0, self.arrow_buffer.ptr)
self.arrow_color_buffer.bind()
if has_vbo:
glColorPointer(4, GL_FLOAT, 0, None)
else:
glColorPointer(4, GL_FLOAT, 0, self.arrow_color_buffer.ptr)
start = (self.layer_stops[self.num_layers_to_draw - 1] // 2) * 3
end = (self.layer_stops[self.num_layers_to_draw] // 2) * 3
glDrawArrays(GL_TRIANGLES, start, end - start)
self.arrow_buffer.unbind()
self.arrow_color_buffer.unbind()
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Denis Kobozev
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from __future__ import division
import numpy
import math
_identity_matrix = [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
]
_rotation_matrix_cache = {}
def identity_matrix():
return numpy.require(_identity_matrix[:], 'f')
def rotation_matrix(angle, x, y, z):
angle_r = math.radians(angle)
c = math.cos(angle_r)
s = math.sin(angle_r)
C = 1 - c
matrix = numpy.require([
[x ** 2 * C + c, x * y * C - z * s, x * z * C + y * s],
[y * x * C + z * s, y ** 2 * C + c, y * z * C - x * s],
[x * z * C - y * s, y * z * C + x * s, z ** 2 * C + c],
], 'f')
return matrix
def translate(vertices, x, y, z):
translated = vertices + numpy.array([x, y, z], 'f')
return translated
def rotate(vertices, angle, x, y, z):
key = (angle, x, y, z)
if key not in _rotation_matrix_cache:
_rotation_matrix_cache[key] = rotation_matrix(angle, x, y, z)
matrix = _rotation_matrix_cache[key]
rotated = numpy.dot(vertices, matrix)
return rotated
......@@ -56,3 +56,30 @@ def sharedfile(filename):
def configfile(filename):
return lookup_file(filename, [os.path.expanduser("~/.printrun/"),])
class RemainingTimeEstimator(object):
drift = None
gcode = None
def __init__(self, gcode):
self.drift = 1
self.previous_layers_estimate = 0
self.current_layer_estimate = 0
self.current_layer_lines = 0
self.remaining_layers_estimate = 0
self.gcode = gcode
def update_layer(self, layer, printtime):
self.previous_layers_estimate += self.current_layer_estimate
if self.previous_layers_estimate > 0 and printtime > 0:
self.drift = printtime / self.previous_layers_estimate
self.current_layer_estimate = self.gcode.all_layers[layer].duration
self.current_layer_lines = len(self.gcode.all_layers[layer].lines)
self.remaining_layers_estimate -= self.current_layer_estimate
def __call__(self, idx):
layer, line = self.gcode.idxs(idx)
layer_progress = (1 - ((line+1) / self.current_layer_lines))
remaining = layer_progress * self.current_layer_estimate + self.remaining_layers_estimate
return drift * remaining
......@@ -301,7 +301,6 @@ class pronsole(cmd.Cmd):
self.recvlisteners = []
self.in_macro = False
self.p.onlinecb = self.online
self.f = None
self.fgcode = None
self.listing = 0
self.sdfiles = []
......@@ -738,10 +737,9 @@ class pronsole(cmd.Cmd):
if not os.path.exists(filename):
self.log("File not found!")
return
self.f = [line.strip() for line in open(filename)]
self.fgcode = gcoder.GCode(self.f)
self.fgcode = gcoder.GCode(open(filename))
self.filename = filename
self.log("Loaded %s, %d lines." % (filename, len(self.f)))
self.log("Loaded %s, %d lines." % (filename, len(self.fgcode)))
def complete_load(self, text, line, begidx, endidx):
s = line.split()
......
......@@ -17,7 +17,7 @@
import os, Queue, re
from printrun.printrun_utils import install_locale
from printrun.printrun_utils import install_locale, RemainingTimeEstimator
install_locale('pronterface')
try:
......@@ -208,7 +208,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.capture_skip_newline = False
self.tempreport = ""
self.monitor = 0
self.f = None
self.fgcode = None
self.skeinp = None
self.monitor_interval = 3
self.current_pos = [0, 0, 0]
......@@ -279,8 +279,10 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.mini = False
self.p.sendcb = self.sentcb
self.p.printsendcb = self.printsentcb
self.p.layerchangecb = self.layer_change_cb
self.p.startcb = self.startcb
self.p.endcb = self.endcb
self.compute_eta = None
self.starttime = 0
self.extra_print_time = 0
self.curlayer = 0
......@@ -309,6 +311,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def startcb(self):
self.starttime = time.time()
self.compute_eta = RemainingTimeEstimator(self.p.mainqueue)
print _("Print Started at: %s") % format_time(self.starttime)
def endcb(self):
......@@ -343,6 +346,11 @@ class PronterWindow(MainWindow, pronsole.pronsole):
if self.filename:
wx.CallAfter(self.printbtn.Enable)
def layer_change_cb(self, newlayer):
if self.compute_eta:
secondselapsed = int(time.time() - self.starttime + self.extra_print_time)
self.compute_eta.update_layer(newlayer, secondselapsed)
def sentcb(self, line):
gline = gcoder.Line(line)
gline.parse_coordinates(imperial = False)
......@@ -565,14 +573,12 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.SetMenuBar(self.menustrip)
def doneediting(self, gcode):
f = open(self.filename, "w")
f.write("\n".join(gcode))
f.close()
open(self.filename, "w").write("\n".join(gcode))
wx.CallAfter(self.loadfile, None, self.filename)
def do_editgcode(self, e = None):
if self.filename is not None:
MacroEditor(self.filename, self.f, self.doneediting, 1)
MacroEditor(self.filename, "\n".join([line.raw for line in self.fgcode]), self.doneediting, 1)
def new_macro(self, e = None):
dialog = wx.Dialog(self, -1, _("Enter macro name"), size = (260, 85))
......@@ -675,7 +681,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
obj = e.GetEventObject()
popupmenu = wx.Menu()
item = popupmenu.Append(-1, _("SD Upload"))
if not self.f:
if not self.fgcode:
item.Enable(False)
self.Bind(wx.EVT_MENU, self.upload, id = item.GetId())
item = popupmenu.Append(-1, _("SD Print"))
......@@ -693,7 +699,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx.CallAfter(self.btemp.SetInsertionPoint, 0)
def showwin(self, event):
if self.f:
if self.fgcode:
self.gwindow.Show(True)
self.gwindow.SetToolTip(wx.ToolTip("Mousewheel zooms the display\nShift / Mousewheel scrolls layers"))
self.gwindow.Raise()
......@@ -713,21 +719,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
except:
pass
def toggleview(self, e):
if(self.mini):
self.mini = False
self.mainsizer.Fit(self)
#self.SetSize(winsize)
wx.CallAfter(self.minibtn.SetLabel, _("Mini mode"))
else:
self.mini = True
self.uppersizer.Fit(self)
#self.SetSize(winssize)
wx.CallAfter(self.minibtn.SetLabel, _("Full mode"))
def cbuttons_reload(self):
allcbs = []
ubs = self.uppersizer
......@@ -1177,17 +1168,24 @@ class PronterWindow(MainWindow, pronsole.pronsole):
if self.sdprinting:
fractioncomplete = float(self.percentdone / 100.0)
string += _(" SD printing:%04.2f %%") % (self.percentdone,)
if fractioncomplete > 0.0:
secondselapsed = int(time.time() - self.starttime + self.extra_print_time)
secondsestimate = secondselapsed / fractioncomplete
secondsremain = secondsestimate - secondselapsed
string += _(" Est: %s of %s remaining | ") % (format_duration(secondsremain),
format_duration(secondsestimate))
string += _(" Z: %.3f mm") % self.curlayer
if self.p.printing:
fractioncomplete = float(self.p.queueindex) / len(self.p.mainqueue)
string += _(" Printing: %04.2f%% |") % (100*float(self.p.queueindex)/len(self.p.mainqueue),)
string += _(" Line# %d of %d lines |" ) % (self.p.queueindex, len(self.p.mainqueue))
if fractioncomplete > 0.0:
if self.p.queueindex > 0:
secondselapsed = int(time.time() - self.starttime + self.extra_print_time)
secondsestimate = secondselapsed / fractioncomplete
secondsremain = secondsestimate - secondselapsed
secondsremain = self.compute_eta(self.p.queueindex)
secondsestimate = secondselapsed + secondsremain
string += _(" Est: %s of %s remaining | ") % (format_duration(secondsremain),
format_duration(secondsestimate))
string += _(" Z: %0.2f mm") % self.curlayer
string += _(" Z: %.3f mm") % self.curlayer
wx.CallAfter(self.status.SetStatusText, string)
wx.CallAfter(self.gviz.Refresh)
if(self.monitor and self.p.online):
......@@ -1329,13 +1327,12 @@ class PronterWindow(MainWindow, pronsole.pronsole):
fn = self.filename
try:
self.filename = self.filename.replace(".stl", "_export.gcode").replace(".STL", "_export.gcode").replace(".obj", "_export.gcode").replace(".OBJ", "_export.gcode")
self.f = [line.strip() for line in open(self.filename)]
self.fgcode = gcoder.GCode(self.f)
self.fgcode = gcoder.GCode(open(self.filename))
if self.p.online:
wx.CallAfter(self.printbtn.Enable)
wx.CallAfter(self.status.SetStatusText, _("Loaded %s, %d lines") % (self.filename, len(self.f),))
print _("Loaded %s, %d lines") % (self.filename, len(self.f),)
wx.CallAfter(self.status.SetStatusText, _("Loaded %s, %d lines") % (self.filename, len(self.fgcode),))
print _("Loaded %s, %d lines") % (self.filename, len(self.fgcode),)
wx.CallAfter(self.pausebtn.Disable)
wx.CallAfter(self.printbtn.SetLabel, _("Print"))
......@@ -1395,10 +1392,9 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.skein(name)
else:
self.filename = name
self.f = [line.strip() for line in open(self.filename)]
self.fgcode = gcoder.GCode(self.f)
self.status.SetStatusText(_("Loaded %s, %d lines") % (name, len(self.f)))
print _("Loaded %s, %d lines") % (name, len(self.f))
self.fgcode = gcoder.GCode(open(self.filename))
self.status.SetStatusText(_("Loaded %s, %d lines") % (name, len(self.fgcode)))
print _("Loaded %s, %d lines") % (name, len(self.fgcode))
wx.CallAfter(self.printbtn.SetLabel, _("Print"))
wx.CallAfter(self.pausebtn.SetLabel, _("Pause"))
wx.CallAfter(self.pausebtn.Disable)
......@@ -1435,7 +1431,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.p.send_now("M24")
return
if not self.f:
if not self.fgcode:
wx.CallAfter(self.status.SetStatusText, _("No file loaded. Please use load first."))
return
if not self.p.online:
......@@ -1466,7 +1462,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.recvlisteners.remove(self.uploadtrigger)
def upload(self, event):
if not self.f:
if not self.fgcode:
return
if not self.p.online:
return
......
Markdown is supported
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