Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
P
Printrun
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
machinery
Printrun
Commits
067f0925
Commit
067f0925
authored
Apr 04, 2014
by
Guillaume Seguin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Merge GCode and LightGCode classes
Hopefully things will still work as before now ^^
parent
22c25826
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
198 additions
and
540 deletions
+198
-540
gcoder.py
printrun/gcoder.py
+198
-540
No files found.
printrun/gcoder.py
View file @
067f0925
...
@@ -106,6 +106,8 @@ class Layer(list):
...
@@ -106,6 +106,8 @@ class Layer(list):
class
GCode
(
object
):
class
GCode
(
object
):
line_class
=
Line
lines
=
None
lines
=
None
layers
=
None
layers
=
None
all_layers
=
None
all_layers
=
None
...
@@ -208,13 +210,12 @@ class GCode(object):
...
@@ -208,13 +210,12 @@ class GCode(object):
def
prepare
(
self
,
data
=
None
,
home_pos
=
None
,
layer_callback
=
None
):
def
prepare
(
self
,
data
=
None
,
home_pos
=
None
,
layer_callback
=
None
):
self
.
home_pos
=
home_pos
self
.
home_pos
=
home_pos
if
data
:
if
data
:
self
.
lines
=
[
Line
(
l2
)
for
l2
in
line_class
=
self
.
line_class
self
.
lines
=
[
line_class
(
l2
)
for
l2
in
(
l
.
strip
()
for
l
in
data
)
(
l
.
strip
()
for
l
in
data
)
if
l2
]
if
l2
]
self
.
_preprocess
(
build_layers
=
True
,
self
.
_preprocess
(
build_layers
=
True
,
layer_callback
=
layer_callback
)
layer_callback
=
layer_callback
)
self
.
filament_length
=
self
.
max_e
self
.
_compute_bounding_box
()
else
:
else
:
self
.
lines
=
[]
self
.
lines
=
[]
self
.
append_layer_id
=
0
self
.
append_layer_id
=
0
...
@@ -318,13 +319,43 @@ class GCode(object):
...
@@ -318,13 +319,43 @@ class GCode(object):
offset_y
=
self
.
offset_y
offset_y
=
self
.
offset_y
offset_z
=
self
.
offset_z
offset_z
=
self
.
offset_z
# Extrusion computation
current_e
=
self
.
current_e
current_e
=
self
.
current_e
offset_e
=
self
.
offset_e
offset_e
=
self
.
offset_e
total_e
=
self
.
total_e
total_e
=
self
.
total_e
max_e
=
self
.
max_e
max_e
=
self
.
max_e
# Initialize layers
# Initialize layers
and other global computations
if
build_layers
:
if
build_layers
:
# Bounding box computation
xmin
=
float
(
"inf"
)
ymin
=
float
(
"inf"
)
zmin
=
0
xmax
=
float
(
"-inf"
)
ymax
=
float
(
"-inf"
)
zmax
=
float
(
"-inf"
)
# Also compute extrusion-only values
xmin_e
=
float
(
"inf"
)
ymin_e
=
float
(
"inf"
)
xmax_e
=
float
(
"-inf"
)
ymax_e
=
float
(
"-inf"
)
# Duration estimation
# TODO:
# get device caps from firmware: max speed, acceleration/axis
# (including extruder)
# calculate the maximum move duration accounting for above ;)
lastx
=
lasty
=
lastz
=
laste
=
lastf
=
0.0
lastdx
=
0
lastdy
=
0
x
=
y
=
e
=
f
=
0.0
currenttravel
=
0.0
moveduration
=
0.0
totalduration
=
0.0
acceleration
=
2000.0
# mm/s^2
layerbeginduration
=
0.0
# Initialize layers
all_layers
=
self
.
all_layers
=
[]
all_layers
=
self
.
all_layers
=
[]
all_zs
=
self
.
all_zs
=
set
()
all_zs
=
self
.
all_zs
=
set
()
layer_idxs
=
self
.
layer_idxs
=
[]
layer_idxs
=
self
.
layer_idxs
=
[]
...
@@ -340,8 +371,13 @@ class GCode(object):
...
@@ -340,8 +371,13 @@ class GCode(object):
cur_lines
=
[]
cur_lines
=
[]
cur_layer_has_extrusion
=
False
cur_layer_has_extrusion
=
False
for
line
in
lines
:
for
true_
line
in
lines
:
# # Parse line
# # Parse line
# Use a heavy copy of the light line to preprocess
if
self
.
line_class
!=
Line
:
line
=
Line
(
true_line
.
raw
)
else
:
line
=
true_line
split_raw
=
split
(
line
)
split_raw
=
split
(
line
)
if
line
.
command
:
if
line
.
command
:
# Update properties
# Update properties
...
@@ -428,532 +464,143 @@ class GCode(object):
...
@@ -428,532 +464,143 @@ class GCode(object):
elif
line
.
command
==
"G92"
:
elif
line
.
command
==
"G92"
:
offset_e
=
current_e
-
line
.
e
offset_e
=
current_e
-
line
.
e
# # Create layers
# # Create layers and perform global computations
if
not
build_layers
:
if
build_layers
:
continue
# Update bounding box
# FIXME : looks like this needs to be tested with "lift Z on move"
if
line
.
is_move
:
if
line
.
command
==
"G92"
and
line
.
z
is
not
None
:
if
line
.
extruding
:
cur_z
=
line
.
z
if
line
.
current_x
is
not
None
:
elif
line
.
is_move
:
xmin_e
=
min
(
xmin_e
,
line
.
current_x
)
if
line
.
z
is
not
None
:
xmax_e
=
max
(
xmax_e
,
line
.
current_x
)
if
line
.
relative
and
cur_z
is
not
None
:
if
line
.
current_y
is
not
None
:
cur_z
+=
line
.
z
ymin_e
=
min
(
ymin_e
,
line
.
current_y
)
else
:
ymax_e
=
max
(
ymax_e
,
line
.
current_y
)
cur_z
=
line
.
z
if
line
.
current_x
is
not
None
:
xmin
=
min
(
xmin
,
line
.
current_x
)
if
line
.
e
is
not
None
and
line
.
is_move
:
xmax
=
max
(
xmax
,
line
.
current_x
)
cur_layer_has_extrusion
|=
line
.
extruding
if
line
.
current_y
is
not
None
:
ymin
=
min
(
ymin
,
line
.
current_y
)
# FIXME: the logic behind this code seems to work, but it might be
ymax
=
max
(
ymax
,
line
.
current_y
)
# broken
if
cur_z
!=
prev_z
:
# Compute duration
if
prev_z
is
not
None
and
last_layer_z
is
not
None
:
if
line
.
command
in
[
"G1"
,
"G0"
,
"G4"
]:
offset
=
self
.
est_layer_height
if
self
.
est_layer_height
else
0.01
if
line
.
command
==
"G4"
:
if
abs
(
prev_z
-
last_layer_z
)
<
offset
:
moveduration
=
P
(
line
)
if
self
.
est_layer_height
is
None
:
if
moveduration
:
zs
=
sorted
([
l
.
z
for
l
in
all_layers
if
l
.
z
is
not
None
])
moveduration
/=
1000.0
heights
=
[
round
(
zs
[
i
+
1
]
-
zs
[
i
],
3
)
for
i
in
range
(
len
(
zs
)
-
1
)]
totalduration
+=
moveduration
heights
=
[
height
for
height
in
heights
if
height
]
if
len
(
heights
)
>=
2
:
self
.
est_layer_height
=
heights
[
1
]
elif
heights
:
self
.
est_layer_height
=
heights
[
0
]
else
:
self
.
est_layer_height
=
0.1
base_z
=
round
(
prev_z
-
(
prev_z
%
self
.
est_layer_height
),
2
)
else
:
else
:
base_z
=
round
(
prev_z
,
2
)
x
=
line
.
x
if
line
.
x
is
not
None
else
lastx
else
:
y
=
line
.
y
if
line
.
y
is
not
None
else
lasty
base_z
=
prev_z
z
=
line
.
z
if
line
.
z
is
not
None
else
lastz
e
=
line
.
e
if
line
.
e
is
not
None
else
laste
if
base_z
!=
prev_base_z
:
# mm/s vs mm/m => divide by 60
all_layers
.
append
(
Layer
(
cur_lines
,
base_z
))
f
=
line
.
f
/
60.0
if
line
.
f
is
not
None
else
lastf
if
cur_layer_has_extrusion
and
prev_z
not
in
all_zs
:
all_zs
.
add
(
prev_z
)
# given last feedrate and current feedrate calculate the
cur_lines
=
[]
# distance needed to achieve current feedrate.
cur_layer_has_extrusion
=
False
# if travel is longer than req'd distance, then subtract
layer_id
+=
1
# distance to achieve full speed, and add the time it took
layer_line
=
0
# to get there.
last_layer_z
=
base_z
# then calculate the time taken to complete the remaining
if
layer_callback
is
not
None
:
# distance
layer_callback
(
self
,
len
(
all_layers
)
-
1
)
# FIXME: this code has been proven to be super wrong when 2
prev_base_z
=
base_z
# subsquent moves are in opposite directions, as requested
# speed is constant but printer has to fully decellerate
if
build_layers
:
# and reaccelerate
cur_lines
.
append
(
line
)
# The following code tries to fix it by forcing a full
layer_idxs
.
append
(
layer_id
)
# reacceleration if this move is in the opposite direction
line_idxs
.
append
(
layer_line
)
# of the previous one
layer_line
+=
1
dx
=
x
-
lastx
prev_z
=
cur_z
dy
=
y
-
lasty
# ## Loop done
if
dx
*
lastdx
+
dy
*
lastdy
<=
0
:
lastf
=
0
# Store current status
self
.
imperial
=
imperial
currenttravel
=
math
.
hypot
(
dx
,
dy
)
self
.
relative
=
relative
if
currenttravel
==
0
:
self
.
relative_e
=
relative_e
if
line
.
z
is
not
None
:
self
.
current_tool
=
current_tool
currenttravel
=
abs
(
line
.
z
)
if
line
.
relative
else
abs
(
line
.
z
-
lastz
)
self
.
current_x
=
current_x
elif
line
.
e
is
not
None
:
self
.
current_y
=
current_y
currenttravel
=
abs
(
line
.
e
)
if
line
.
relative_e
else
abs
(
line
.
e
-
laste
)
self
.
current_z
=
current_z
# Feedrate hasn't changed, no acceleration/decceleration planned
self
.
offset_x
=
offset_x
if
f
==
lastf
:
self
.
offset_y
=
offset_y
moveduration
=
currenttravel
/
f
if
f
!=
0
else
0.
self
.
offset_z
=
offset_z
else
:
# FIXME: review this better
self
.
current_e
=
current_e
# this looks wrong : there's little chance that the feedrate we'll decelerate to is the previous feedrate
self
.
offset_e
=
offset_e
# shouldn't we instead look at three consecutive moves ?
self
.
max_e
=
max_e
distance
=
2
*
abs
(((
lastf
+
f
)
*
(
f
-
lastf
)
*
0.5
)
/
acceleration
)
# multiply by 2 because we have to accelerate and decelerate
self
.
total_e
=
total_e
if
distance
<=
currenttravel
and
lastf
+
f
!=
0
and
f
!=
0
:
moveduration
=
2
*
distance
/
(
lastf
+
f
)
# This is distance / mean(lastf, f)
# Finalize layers
moveduration
+=
(
currenttravel
-
distance
)
/
f
if
build_layers
:
else
:
if
cur_lines
:
moveduration
=
2
*
currenttravel
/
(
lastf
+
f
)
# This is currenttravel / mean(lastf, f)
all_layers
.
append
(
Layer
(
cur_lines
,
prev_z
))
# FIXME: probably a little bit optimistic, but probably a much better estimate than the previous one:
if
cur_layer_has_extrusion
and
prev_z
not
in
all_zs
:
# moveduration = math.sqrt(2 * distance / acceleration) # probably buggy : not taking actual travel into account
all_zs
.
add
(
prev_z
)
lastdx
=
dx
self
.
append_layer_id
=
len
(
all_layers
)
lastdy
=
dy
self
.
append_layer
=
Layer
([])
all_layers
.
append
(
self
.
append_layer
)
self
.
layer_idxs
=
array
(
'I'
,
layer_idxs
)
self
.
line_idxs
=
array
(
'I'
,
line_idxs
)
def
idxs
(
self
,
i
):
totalduration
+=
moveduration
return
self
.
layer_idxs
[
i
],
self
.
line_idxs
[
i
]
def
_compute_bounding_box
(
self
):
lastx
=
x
xmin
=
float
(
"inf"
)
lasty
=
y
ymin
=
float
(
"inf"
)
lastz
=
z
zmin
=
0
laste
=
e
xmax
=
float
(
"-inf"
)
lastf
=
f
ymax
=
float
(
"-inf"
)
zmax
=
float
(
"-inf"
)
# Count moves without extrusion if filament length is lower than 0
count_noe
=
self
.
filament_length
<=
0
for
line
in
self
.
lines
:
if
line
.
is_move
and
(
line
.
extruding
or
count_noe
):
if
line
.
current_x
is
not
None
:
xmin
=
min
(
xmin
,
line
.
current_x
)
xmax
=
max
(
xmax
,
line
.
current_x
)
if
line
.
current_y
is
not
None
:
ymin
=
min
(
ymin
,
line
.
current_y
)
ymax
=
max
(
ymax
,
line
.
current_y
)
all_zs
=
self
.
all_zs
.
union
(
set
([
zmin
]))
.
difference
(
set
([
None
]))
zmin
=
min
(
all_zs
)
zmax
=
max
(
all_zs
)
self
.
xmin
=
xmin
if
not
math
.
isinf
(
xmin
)
else
0
self
.
xmax
=
xmax
if
not
math
.
isinf
(
xmax
)
else
0
self
.
ymin
=
ymin
if
not
math
.
isinf
(
ymin
)
else
0
self
.
ymax
=
ymax
if
not
math
.
isinf
(
ymax
)
else
0
self
.
zmin
=
zmin
if
not
math
.
isinf
(
zmin
)
else
0
self
.
zmax
=
zmax
if
not
math
.
isinf
(
zmax
)
else
0
self
.
width
=
self
.
xmax
-
self
.
xmin
self
.
depth
=
self
.
ymax
-
self
.
ymin
self
.
height
=
self
.
zmax
-
self
.
zmin
def
estimate_duration
(
self
):
# FIXME : looks like this needs to be tested with "lift Z on move"
if
self
.
duration
is
not
None
:
if
line
.
command
==
"G92"
and
line
.
z
is
not
None
:
return
self
.
layers_count
,
self
.
duration
cur_z
=
line
.
z
lastx
=
lasty
=
lastz
=
laste
=
lastf
=
0.0
elif
line
.
is_move
:
lastdx
=
0
lastdy
=
0
x
=
y
=
e
=
f
=
0.0
currenttravel
=
0.0
moveduration
=
0.0
totalduration
=
0.0
acceleration
=
2000.0
# mm/s^2
layerbeginduration
=
0.0
# TODO:
# get device caps from firmware: max speed, acceleration/axis
# (including extruder)
# calculate the maximum move duration accounting for above ;)
for
layer
in
self
.
all_layers
:
for
line
in
layer
:
if
line
.
command
not
in
[
"G1"
,
"G0"
,
"G4"
]:
continue
if
line
.
command
==
"G4"
:
moveduration
=
P
(
line
)
if
not
moveduration
:
continue
else
:
moveduration
/=
1000.0
totalduration
+=
moveduration
else
:
x
=
line
.
x
if
line
.
x
is
not
None
else
lastx
y
=
line
.
y
if
line
.
y
is
not
None
else
lasty
z
=
line
.
z
if
line
.
z
is
not
None
else
lastz
e
=
line
.
e
if
line
.
e
is
not
None
else
laste
# mm/s vs mm/m => divide by 60
f
=
line
.
f
/
60.0
if
line
.
f
is
not
None
else
lastf
# given last feedrate and current feedrate calculate the
# distance needed to achieve current feedrate.
# if travel is longer than req'd distance, then subtract
# distance to achieve full speed, and add the time it took
# to get there.
# then calculate the time taken to complete the remaining
# distance
# FIXME: this code has been proven to be super wrong when 2
# subsquent moves are in opposite directions, as requested
# speed is constant but printer has to fully decellerate
# and reaccelerate
# The following code tries to fix it by forcing a full
# reacceleration if this move is in the opposite direction
# of the previous one
dx
=
x
-
lastx
dy
=
y
-
lasty
if
dx
*
lastdx
+
dy
*
lastdy
<=
0
:
lastf
=
0
currenttravel
=
math
.
hypot
(
dx
,
dy
)
if
currenttravel
==
0
:
if
line
.
z
is
not
None
:
if
line
.
z
is
not
None
:
currenttravel
=
abs
(
line
.
z
)
if
line
.
relative
else
abs
(
line
.
z
-
lastz
)
if
line
.
relative
and
cur_z
is
not
None
:
elif
line
.
e
is
not
None
:
cur_z
+=
line
.
z
currenttravel
=
abs
(
line
.
e
)
if
line
.
relative_e
else
abs
(
line
.
e
-
laste
)
# Feedrate hasn't changed, no acceleration/decceleration planned
if
f
==
lastf
:
moveduration
=
currenttravel
/
f
if
f
!=
0
else
0.
else
:
# FIXME: review this better
# this looks wrong : there's little chance that the feedrate we'll decelerate to is the previous feedrate
# shouldn't we instead look at three consecutive moves ?
distance
=
2
*
abs
(((
lastf
+
f
)
*
(
f
-
lastf
)
*
0.5
)
/
acceleration
)
# multiply by 2 because we have to accelerate and decelerate
if
distance
<=
currenttravel
and
lastf
+
f
!=
0
and
f
!=
0
:
moveduration
=
2
*
distance
/
(
lastf
+
f
)
# This is distance / mean(lastf, f)
moveduration
+=
(
currenttravel
-
distance
)
/
f
else
:
moveduration
=
2
*
currenttravel
/
(
lastf
+
f
)
# This is currenttravel / mean(lastf, f)
# FIXME: probably a little bit optimistic, but probably a much better estimate than the previous one:
# moveduration = math.sqrt(2 * distance / acceleration) # probably buggy : not taking actual travel into account
lastdx
=
dx
lastdy
=
dy
totalduration
+=
moveduration
lastx
=
x
lasty
=
y
lastz
=
z
laste
=
e
lastf
=
f
layer
.
duration
=
totalduration
-
layerbeginduration
layerbeginduration
=
totalduration
totaltime
=
datetime
.
timedelta
(
seconds
=
int
(
totalduration
))
self
.
duration
=
totaltime
return
self
.
layers_count
,
str
(
totaltime
)
# FIXME: heavy code duplication :(
# Once this works well, we should be able to remove duplication by
# 1) specifying the Line class to use as a class property
# 2) reuse the LightGCode all-in-1 function preparation code
# 3) do the line copy at the beginning of the prepare loop if needed
# 4) compute extents with and without extrusion
class
LightGCode
(
GCode
):
def
prepare
(
self
,
data
=
None
,
home_pos
=
None
,
layer_callback
=
None
):
self
.
home_pos
=
home_pos
if
data
:
self
.
lines
=
[
LightLine
(
l2
)
for
l2
in
(
l
.
strip
()
for
l
in
data
)
if
l2
]
self
.
_preprocess
(
build_layers
=
True
,
layer_callback
=
layer_callback
)
self
.
filament_length
=
self
.
max_e
self
.
_compute_bounding_box
()
else
:
self
.
lines
=
[]
self
.
append_layer_id
=
0
self
.
append_layer
=
Layer
([])
self
.
all_layers
=
[
self
.
append_layer
]
self
.
all_zs
=
set
()
self
.
layers
=
{}
self
.
layer_idxs
=
array
(
'I'
,
[])
self
.
line_idxs
=
array
(
'I'
,
[])
def
_preprocess
(
self
,
lines
=
None
,
build_layers
=
False
,
layer_callback
=
None
):
"""Checks for imperial/relativeness settings and tool changes"""
if
not
lines
:
lines
=
self
.
lines
imperial
=
self
.
imperial
relative
=
self
.
relative
relative_e
=
self
.
relative_e
current_tool
=
self
.
current_tool
current_x
=
self
.
current_x
current_y
=
self
.
current_y
current_z
=
self
.
current_z
offset_x
=
self
.
offset_x
offset_y
=
self
.
offset_y
offset_z
=
self
.
offset_z
# Extrusion computation
current_e
=
self
.
current_e
offset_e
=
self
.
offset_e
total_e
=
self
.
total_e
max_e
=
self
.
max_e
# Bounding box computation
xmin
=
float
(
"inf"
)
ymin
=
float
(
"inf"
)
zmin
=
0
xmax
=
float
(
"-inf"
)
ymax
=
float
(
"-inf"
)
zmax
=
float
(
"-inf"
)
# Duration estimation
# TODO:
# get device caps from firmware: max speed, acceleration/axis
# (including extruder)
# calculate the maximum move duration accounting for above ;)
lastx
=
lasty
=
lastz
=
laste
=
lastf
=
0.0
lastdx
=
0
lastdy
=
0
x
=
y
=
e
=
f
=
0.0
currenttravel
=
0.0
moveduration
=
0.0
totalduration
=
0.0
acceleration
=
2000.0
# mm/s^2
layerbeginduration
=
0.0
# Initialize layers
if
build_layers
:
all_layers
=
self
.
all_layers
=
[]
all_zs
=
self
.
all_zs
=
set
()
layer_idxs
=
self
.
layer_idxs
=
[]
line_idxs
=
self
.
line_idxs
=
[]
layer_id
=
0
layer_line
=
0
last_layer_z
=
None
prev_z
=
None
prev_base_z
=
(
None
,
None
)
cur_z
=
None
cur_lines
=
[]
cur_layer_has_extrusion
=
False
for
light_line
in
lines
:
# # Parse line
# Use a heavy copy of the light line to preprocess
line
=
Line
(
light_line
.
raw
)
split_raw
=
split
(
line
)
if
line
.
command
:
# Update properties
if
line
.
command
==
"G20"
:
imperial
=
True
elif
line
.
command
==
"G21"
:
imperial
=
False
elif
line
.
command
==
"G90"
:
relative
=
False
relative_e
=
False
elif
line
.
command
==
"G91"
:
relative
=
True
relative_e
=
True
elif
line
.
command
==
"M82"
:
relative_e
=
False
elif
line
.
command
==
"M83"
:
relative_e
=
True
elif
line
.
command
[
0
]
==
"T"
:
current_tool
=
int
(
line
.
command
[
1
:])
if
line
.
command
[
0
]
==
"G"
:
parse_coordinates
(
line
,
split_raw
,
imperial
)
# Compute current position
if
line
.
is_move
:
x
=
line
.
x
y
=
line
.
y
z
=
line
.
z
if
line
.
f
is
not
None
:
self
.
current_f
=
line
.
f
if
line
.
relative
:
x
=
current_x
+
(
x
or
0
)
y
=
current_y
+
(
y
or
0
)
z
=
current_z
+
(
z
or
0
)
else
:
if
x
is
not
None
:
x
=
x
+
offset_x
if
y
is
not
None
:
y
=
y
+
offset_y
if
z
is
not
None
:
z
=
z
+
offset_z
if
x
is
not
None
:
current_x
=
x
if
y
is
not
None
:
current_y
=
y
if
z
is
not
None
:
current_z
=
z
elif
line
.
command
==
"G28"
:
home_all
=
not
any
([
line
.
x
,
line
.
y
,
line
.
z
])
if
home_all
or
line
.
x
is
not
None
:
offset_x
=
0
current_x
=
self
.
home_x
if
home_all
or
line
.
y
is
not
None
:
offset_y
=
0
current_y
=
self
.
home_y
if
home_all
or
line
.
z
is
not
None
:
offset_z
=
0
current_z
=
self
.
home_z
elif
line
.
command
==
"G92"
:
if
line
.
x
is
not
None
:
offset_x
=
current_x
-
line
.
x
if
line
.
y
is
not
None
:
offset_y
=
current_y
-
line
.
y
if
line
.
z
is
not
None
:
offset_z
=
current_z
-
line
.
z
line
.
current_x
=
current_x
line
.
current_y
=
current_y
line
.
current_z
=
current_z
# # Process extrusion
if
line
.
e
is
not
None
:
if
line
.
is_move
:
if
line
.
relative_e
:
line
.
extruding
=
line
.
e
>
0
total_e
+=
line
.
e
current_e
+=
line
.
e
else
:
new_e
=
line
.
e
+
offset_e
line
.
extruding
=
new_e
>
current_e
total_e
+=
new_e
-
current_e
current_e
=
new_e
max_e
=
max
(
max_e
,
total_e
)
elif
line
.
command
==
"G92"
:
offset_e
=
current_e
-
line
.
e
# Update bounding box
if
line
.
is_move
and
line
.
extruding
:
if
line
.
current_x
is
not
None
:
xmin
=
min
(
xmin
,
line
.
current_x
)
xmax
=
max
(
xmax
,
line
.
current_x
)
if
line
.
current_y
is
not
None
:
ymin
=
min
(
ymin
,
line
.
current_y
)
ymax
=
max
(
ymax
,
line
.
current_y
)
# Compute duration
if
line
.
command
in
[
"G1"
,
"G0"
,
"G4"
]:
if
line
.
command
==
"G4"
:
moveduration
=
P
(
line
)
if
moveduration
:
moveduration
/=
1000.0
totalduration
+=
moveduration
else
:
x
=
line
.
x
if
line
.
x
is
not
None
else
lastx
y
=
line
.
y
if
line
.
y
is
not
None
else
lasty
z
=
line
.
z
if
line
.
z
is
not
None
else
lastz
e
=
line
.
e
if
line
.
e
is
not
None
else
laste
# mm/s vs mm/m => divide by 60
f
=
line
.
f
/
60.0
if
line
.
f
is
not
None
else
lastf
# given last feedrate and current feedrate calculate the
# distance needed to achieve current feedrate.
# if travel is longer than req'd distance, then subtract
# distance to achieve full speed, and add the time it took
# to get there.
# then calculate the time taken to complete the remaining
# distance
# FIXME: this code has been proven to be super wrong when 2
# subsquent moves are in opposite directions, as requested
# speed is constant but printer has to fully decellerate
# and reaccelerate
# The following code tries to fix it by forcing a full
# reacceleration if this move is in the opposite direction
# of the previous one
dx
=
x
-
lastx
dy
=
y
-
lasty
if
dx
*
lastdx
+
dy
*
lastdy
<=
0
:
lastf
=
0
currenttravel
=
math
.
hypot
(
dx
,
dy
)
if
currenttravel
==
0
:
if
line
.
z
is
not
None
:
currenttravel
=
abs
(
line
.
z
)
if
line
.
relative
else
abs
(
line
.
z
-
lastz
)
elif
line
.
e
is
not
None
:
currenttravel
=
abs
(
line
.
e
)
if
line
.
relative_e
else
abs
(
line
.
e
-
laste
)
# Feedrate hasn't changed, no acceleration/decceleration planned
if
f
==
lastf
:
moveduration
=
currenttravel
/
f
if
f
!=
0
else
0.
else
:
# FIXME: review this better
# this looks wrong : there's little chance that the feedrate we'll decelerate to is the previous feedrate
# shouldn't we instead look at three consecutive moves ?
distance
=
2
*
abs
(((
lastf
+
f
)
*
(
f
-
lastf
)
*
0.5
)
/
acceleration
)
# multiply by 2 because we have to accelerate and decelerate
if
distance
<=
currenttravel
and
lastf
+
f
!=
0
and
f
!=
0
:
moveduration
=
2
*
distance
/
(
lastf
+
f
)
# This is distance / mean(lastf, f)
moveduration
+=
(
currenttravel
-
distance
)
/
f
else
:
else
:
moveduration
=
2
*
currenttravel
/
(
lastf
+
f
)
# This is currenttravel / mean(lastf, f)
cur_z
=
line
.
z
# FIXME: probably a little bit optimistic, but probably a much better estimate than the previous one:
# moveduration = math.sqrt(2 * distance / acceleration) # probably buggy : not taking actual travel into account
if
line
.
e
is
not
None
and
line
.
is_move
:
cur_layer_has_extrusion
|=
line
.
extruding
lastdx
=
dx
lastdy
=
dy
# FIXME: the logic behind this code seems to work, but it might be
# broken
totalduration
+=
moveduration
if
cur_z
!=
prev_z
:
if
prev_z
is
not
None
and
last_layer_z
is
not
None
:
lastx
=
x
offset
=
self
.
est_layer_height
if
self
.
est_layer_height
else
0.01
lasty
=
y
if
abs
(
prev_z
-
last_layer_z
)
<
offset
:
lastz
=
z
if
self
.
est_layer_height
is
None
:
laste
=
e
zs
=
sorted
([
l
.
z
for
l
in
all_layers
if
l
.
z
is
not
None
])
lastf
=
f
heights
=
[
round
(
zs
[
i
+
1
]
-
zs
[
i
],
3
)
for
i
in
range
(
len
(
zs
)
-
1
)]
heights
=
[
height
for
height
in
heights
if
height
]
# # Create layers
if
len
(
heights
)
>=
2
:
self
.
est_layer_height
=
heights
[
1
]
if
not
build_layers
:
elif
heights
:
self
.
est_layer_height
=
heights
[
0
]
continue
else
:
self
.
est_layer_height
=
0.1
# FIXME : looks like this needs to be tested with "lift Z on move"
base_z
=
round
(
prev_z
-
(
prev_z
%
self
.
est_layer_height
),
2
)
if
line
.
command
==
"G92"
and
line
.
z
is
not
None
:
else
:
cur_z
=
line
.
z
base_z
=
round
(
prev_z
,
2
)
elif
line
.
is_move
:
if
line
.
z
is
not
None
:
if
line
.
relative
and
cur_z
is
not
None
:
cur_z
+=
line
.
z
else
:
cur_z
=
line
.
z
if
line
.
e
is
not
None
and
line
.
is_move
:
cur_layer_has_extrusion
|=
line
.
extruding
# FIXME: the logic behind this code seems to work, but it might be
# broken
if
cur_z
!=
prev_z
:
if
prev_z
is
not
None
and
last_layer_z
is
not
None
:
offset
=
self
.
est_layer_height
if
self
.
est_layer_height
else
0.01
if
abs
(
prev_z
-
last_layer_z
)
<
offset
:
if
self
.
est_layer_height
is
None
:
zs
=
sorted
([
l
.
z
for
l
in
all_layers
if
l
.
z
is
not
None
])
heights
=
[
round
(
zs
[
i
+
1
]
-
zs
[
i
],
3
)
for
i
in
range
(
len
(
zs
)
-
1
)]
heights
=
[
height
for
height
in
heights
if
height
]
if
len
(
heights
)
>=
2
:
self
.
est_layer_height
=
heights
[
1
]
elif
heights
:
self
.
est_layer_height
=
heights
[
0
]
else
:
self
.
est_layer_height
=
0.1
base_z
=
round
(
prev_z
-
(
prev_z
%
self
.
est_layer_height
),
2
)
else
:
else
:
base_z
=
round
(
prev_z
,
2
)
base_z
=
prev_z
else
:
base_z
=
prev_z
if
base_z
!=
prev_base_z
:
new_layer
=
Layer
(
cur_lines
,
base_z
)
if
base_z
!=
prev_base_z
:
new_layer
.
duration
=
totalduration
-
layerbeginduration
new_layer
=
Layer
(
cur_lines
,
base_z
)
layerbeginduration
=
totalduration
new_layer
.
duration
=
totalduration
-
layerbeginduration
all_layers
.
append
(
new_layer
)
layerbeginduration
=
totalduration
if
cur_layer_has_extrusion
and
prev_z
not
in
all_zs
:
all_layers
.
append
(
new_layer
)
all_zs
.
add
(
prev_z
)
if
cur_layer_has_extrusion
and
prev_z
not
in
all_zs
:
cur_lines
=
[]
all_zs
.
add
(
prev_z
)
cur_layer_has_extrusion
=
False
cur_lines
=
[]
layer_id
+=
1
cur_layer_has_extrusion
=
False
layer_line
=
0
layer_id
+=
1
last_layer_z
=
base_z
layer_line
=
0
if
layer_callback
is
not
None
:
last_layer_z
=
base_z
layer_callback
(
self
,
len
(
all_layers
)
-
1
)
if
layer_callback
is
not
None
:
layer_callback
(
self
,
len
(
all_layers
)
-
1
)
prev_base_z
=
base_z
prev_base_z
=
base_z
if
build_layers
:
if
build_layers
:
cur_lines
.
append
(
light
_line
)
cur_lines
.
append
(
true
_line
)
layer_idxs
.
append
(
layer_id
)
layer_idxs
.
append
(
layer_id
)
line_idxs
.
append
(
layer_line
)
line_idxs
.
append
(
layer_line
)
layer_line
+=
1
layer_line
+=
1
...
@@ -993,24 +640,32 @@ class LightGCode(GCode):
...
@@ -993,24 +640,32 @@ class LightGCode(GCode):
self
.
layer_idxs
=
array
(
'I'
,
layer_idxs
)
self
.
layer_idxs
=
array
(
'I'
,
layer_idxs
)
self
.
line_idxs
=
array
(
'I'
,
line_idxs
)
self
.
line_idxs
=
array
(
'I'
,
line_idxs
)
# Compute bounding box
# Compute bounding box
all_zs
=
self
.
all_zs
.
union
(
set
([
zmin
]))
.
difference
(
set
([
None
]))
all_zs
=
self
.
all_zs
.
union
(
set
([
zmin
]))
.
difference
(
set
([
None
]))
zmin
=
min
(
all_zs
)
zmin
=
min
(
all_zs
)
zmax
=
max
(
all_zs
)
zmax
=
max
(
all_zs
)
self
.
xmin
=
xmin
if
not
math
.
isinf
(
xmin
)
else
0
self
.
filament_length
=
self
.
max_e
self
.
xmax
=
xmax
if
not
math
.
isinf
(
xmax
)
else
0
self
.
ymin
=
ymin
if
not
math
.
isinf
(
ymin
)
else
0
if
self
.
filament_length
>
0
:
self
.
ymax
=
ymax
if
not
math
.
isinf
(
ymax
)
else
0
self
.
xmin
=
xmin_e
if
not
math
.
isinf
(
xmin_e
)
else
0
self
.
zmin
=
zmin
if
not
math
.
isinf
(
zmin
)
else
0
self
.
xmax
=
xmax_e
if
not
math
.
isinf
(
xmax_e
)
else
0
self
.
zmax
=
zmax
if
not
math
.
isinf
(
zmax
)
else
0
self
.
ymin
=
ymin_e
if
not
math
.
isinf
(
ymin_e
)
else
0
self
.
width
=
self
.
xmax
-
self
.
xmin
self
.
ymax
=
ymax_e
if
not
math
.
isinf
(
ymax_e
)
else
0
self
.
depth
=
self
.
ymax
-
self
.
ymin
else
:
self
.
height
=
self
.
zmax
-
self
.
zmin
self
.
xmin
=
xmin
if
not
math
.
isinf
(
xmin
)
else
0
self
.
xmax
=
xmax
if
not
math
.
isinf
(
xmax
)
else
0
# Finalize duration
self
.
ymin
=
ymin
if
not
math
.
isinf
(
ymin
)
else
0
totaltime
=
datetime
.
timedelta
(
seconds
=
int
(
totalduration
))
self
.
ymax
=
ymax
if
not
math
.
isinf
(
ymax
)
else
0
self
.
duration
=
totaltime
self
.
zmin
=
zmin
if
not
math
.
isinf
(
zmin
)
else
0
self
.
zmax
=
zmax
if
not
math
.
isinf
(
zmax
)
else
0
self
.
width
=
self
.
xmax
-
self
.
xmin
self
.
depth
=
self
.
ymax
-
self
.
ymin
self
.
height
=
self
.
zmax
-
self
.
zmin
# Finalize duration
totaltime
=
datetime
.
timedelta
(
seconds
=
int
(
totalduration
))
self
.
duration
=
totaltime
def
idxs
(
self
,
i
):
def
idxs
(
self
,
i
):
return
self
.
layer_idxs
[
i
],
self
.
line_idxs
[
i
]
return
self
.
layer_idxs
[
i
],
self
.
line_idxs
[
i
]
...
@@ -1018,6 +673,9 @@ class LightGCode(GCode):
...
@@ -1018,6 +673,9 @@ class LightGCode(GCode):
def
estimate_duration
(
self
):
def
estimate_duration
(
self
):
return
self
.
layers_count
,
self
.
duration
return
self
.
layers_count
,
self
.
duration
class
LightGCode
(
GCode
):
line_class
=
LightLine
def
main
():
def
main
():
if
len
(
sys
.
argv
)
<
2
:
if
len
(
sys
.
argv
)
<
2
:
print
"usage:
%
s filename.gcode"
%
sys
.
argv
[
0
]
print
"usage:
%
s filename.gcode"
%
sys
.
argv
[
0
]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment