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
0762728f
Commit
0762728f
authored
May 21, 2013
by
Guillaume Seguin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Import actors.py and vector.py from libtatlin
parent
a81811c9
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
575 additions
and
0 deletions
+575
-0
actors.py
printrun/libtatlin/actors.py
+514
-0
vector.py
printrun/libtatlin/vector.py
+61
-0
No files found.
printrun/libtatlin/actors.py
0 → 100644
View file @
0762728f
# -*- 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
math
import
numpy
import
logging
import
time
from
OpenGL.GL
import
*
from
OpenGL.GLE
import
*
from
OpenGL.arrays.vbo
import
VBO
import
vector
from
gcodeparser
import
Movement
def
compile_display_list
(
func
,
*
options
):
display_list
=
glGenLists
(
1
)
glNewList
(
display_list
,
GL_COMPILE
)
func
(
*
options
)
glEndList
()
return
display_list
class
BoundingBox
(
object
):
"""
A rectangular box (cuboid) enclosing a 3D model, defined by lower and upper corners.
"""
def
__init__
(
self
,
upper_corner
,
lower_corner
):
self
.
upper_corner
=
upper_corner
self
.
lower_corner
=
lower_corner
@
property
def
width
(
self
):
width
=
abs
(
self
.
upper_corner
[
0
]
-
self
.
lower_corner
[
0
])
return
round
(
width
,
2
)
@
property
def
depth
(
self
):
depth
=
abs
(
self
.
upper_corner
[
1
]
-
self
.
lower_corner
[
1
])
return
round
(
depth
,
2
)
@
property
def
height
(
self
):
height
=
abs
(
self
.
upper_corner
[
2
]
-
self
.
lower_corner
[
2
])
return
round
(
height
,
2
)
class
Platform
(
object
):
"""
Platform on which models are placed.
"""
graduations_major
=
10
def
__init__
(
self
,
width
,
depth
):
self
.
width
=
width
self
.
depth
=
depth
self
.
color_grads_minor
=
(
0xaf
/
255
,
0xdf
/
255
,
0x5f
/
255
,
0.1
)
self
.
color_grads_interm
=
(
0xaf
/
255
,
0xdf
/
255
,
0x5f
/
255
,
0.2
)
self
.
color_grads_major
=
(
0xaf
/
255
,
0xdf
/
255
,
0x5f
/
255
,
0.33
)
self
.
color_fill
=
(
0xaf
/
255
,
0xdf
/
255
,
0x5f
/
255
,
0.05
)
self
.
initialized
=
False
def
init
(
self
):
self
.
display_list
=
compile_display_list
(
self
.
draw
)
self
.
initialized
=
True
def
draw
(
self
):
glPushMatrix
()
glTranslate
(
-
self
.
width
/
2
,
-
self
.
depth
/
2
,
0
)
def
color
(
i
):
if
i
%
self
.
graduations_major
==
0
:
glColor
(
*
self
.
color_grads_major
)
elif
i
%
(
self
.
graduations_major
/
2
)
==
0
:
glColor
(
*
self
.
color_grads_interm
)
else
:
glColor
(
*
self
.
color_grads_minor
)
# draw the grid
glBegin
(
GL_LINES
)
for
i
in
range
(
0
,
self
.
width
+
1
):
color
(
i
)
glVertex3f
(
float
(
i
),
0.0
,
0.0
)
glVertex3f
(
float
(
i
),
self
.
depth
,
0.0
)
for
i
in
range
(
0
,
self
.
depth
+
1
):
color
(
i
)
glVertex3f
(
0
,
float
(
i
),
0.0
)
glVertex3f
(
self
.
width
,
float
(
i
),
0.0
)
glEnd
()
# draw fill
glColor
(
*
self
.
color_fill
)
glRectf
(
0.0
,
0.0
,
float
(
self
.
width
),
float
(
self
.
depth
))
glPopMatrix
()
def
display
(
self
,
mode_2d
=
False
):
glCallList
(
self
.
display_list
)
class
Model
(
object
):
"""
Parent class for models that provides common functionality.
"""
AXIS_X
=
(
1
,
0
,
0
)
AXIS_Y
=
(
0
,
1
,
0
)
AXIS_Z
=
(
0
,
0
,
1
)
letter_axis_map
=
{
'x'
:
AXIS_X
,
'y'
:
AXIS_Y
,
'z'
:
AXIS_Z
,
}
axis_letter_map
=
dict
([(
v
,
k
)
for
k
,
v
in
letter_axis_map
.
items
()])
def
__init__
(
self
,
offset_x
=
0
,
offset_y
=
0
):
self
.
offset_x
=
offset_x
self
.
offset_y
=
offset_y
self
.
init_model_attributes
()
def
init_model_attributes
(
self
):
"""
Set/reset saved properties.
"""
self
.
invalidate_bounding_box
()
self
.
modified
=
False
def
invalidate_bounding_box
(
self
):
self
.
_bounding_box
=
None
@
property
def
bounding_box
(
self
):
"""
Get a bounding box for the model.
"""
if
self
.
_bounding_box
is
None
:
self
.
_bounding_box
=
self
.
_calculate_bounding_box
()
return
self
.
_bounding_box
def
_calculate_bounding_box
(
self
):
"""
Calculate an axis-aligned box enclosing the model.
"""
# swap rows and columns in our vertex arrays so that we can do max and
# min on axis 1
xyz_rows
=
self
.
vertices
.
reshape
(
-
1
,
order
=
'F'
)
.
reshape
(
3
,
-
1
)
lower_corner
=
xyz_rows
.
min
(
1
)
upper_corner
=
xyz_rows
.
max
(
1
)
box
=
BoundingBox
(
upper_corner
,
lower_corner
)
return
box
@
property
def
width
(
self
):
return
self
.
bounding_box
.
width
@
property
def
depth
(
self
):
return
self
.
bounding_box
.
depth
@
property
def
height
(
self
):
return
self
.
bounding_box
.
height
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'
)
def
load_data
(
self
,
model_data
,
callback
=
None
):
t_start
=
time
.
time
()
vertex_list
=
[]
color_list
=
[]
self
.
layer_stops
=
[
0
]
arrow_list
=
[]
num_layers
=
len
(
model_data
)
for
layer_idx
,
layer
in
enumerate
(
model_data
):
for
movement
in
layer
:
vertex_list
.
append
(
movement
.
src
)
vertex_list
.
append
(
movement
.
dst
)
arrow
=
self
.
arrow
# position the arrow with respect to movement
arrow
=
vector
.
rotate
(
arrow
,
movement
.
angle
(),
0.0
,
0.0
,
1.0
)
arrow_list
.
extend
(
arrow
)
vertex_color
=
self
.
movement_color
(
movement
)
color_list
.
append
(
vertex_color
)
self
.
layer_stops
.
append
(
len
(
vertex_list
))
if
callback
:
callback
(
layer_idx
+
1
,
num_layers
)
self
.
vertices
=
numpy
.
array
(
vertex_list
,
'f'
)
self
.
colors
=
numpy
.
array
(
color_list
,
'f'
)
self
.
arrows
=
numpy
.
array
(
arrow_list
,
'f'
)
# 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
.
max_layers
=
len
(
self
.
layer_stops
)
-
1
self
.
num_layers_to_draw
=
self
.
max_layers
self
.
arrows_enabled
=
True
self
.
initialized
=
False
t_end
=
time
.
time
()
logging
.
info
(
'Initialized Gcode model in
%.2
f seconds'
%
(
t_end
-
t_start
))
logging
.
info
(
'Vertex count:
%
d'
%
len
(
self
.
vertices
))
def
movement_color
(
self
,
move
):
"""
Return the color to use for particular type of movement.
"""
# default movement color is gray
color
=
[
0.6
,
0.6
,
0.6
,
0.6
]
extruder_on
=
(
move
.
flags
&
Movement
.
FLAG_EXTRUDER_ON
or
move
.
delta_e
>
0
)
outer_perimeter
=
(
move
.
flags
&
Movement
.
FLAG_PERIMETER
and
move
.
flags
&
Movement
.
FLAG_PERIMETER_OUTER
)
if
extruder_on
and
outer_perimeter
:
color
=
[
0.0
,
0.875
,
0.875
,
0.6
]
# cyan
elif
extruder_on
and
move
.
flags
&
Movement
.
FLAG_PERIMETER
:
color
=
[
0.0
,
1.0
,
0.0
,
0.6
]
# green
elif
extruder_on
and
move
.
flags
&
Movement
.
FLAG_LOOP
:
color
=
[
1.0
,
0.875
,
0.0
,
0.6
]
# yellow
elif
extruder_on
:
color
=
[
1.0
,
0.0
,
0.0
,
0.6
]
# red
return
color
# ------------------------------------------------------------------------
# DRAWING
# ------------------------------------------------------------------------
def
init
(
self
):
self
.
vertex_buffer
=
VBO
(
self
.
vertices
,
'GL_STATIC_DRAW'
)
self
.
vertex_color_buffer
=
VBO
(
self
.
colors
.
repeat
(
2
,
0
),
'GL_STATIC_DRAW'
)
# each pair of vertices shares the color
if
self
.
arrows_enabled
:
self
.
arrow_buffer
=
VBO
(
self
.
arrows
,
'GL_STATIC_DRAW'
)
self
.
arrow_color_buffer
=
VBO
(
self
.
colors
.
repeat
(
3
,
0
),
'GL_STATIC_DRAW'
)
# each triplet of vertices shares the color
self
.
initialized
=
True
def
display
(
self
,
mode_2d
=
False
):
glPushMatrix
()
glTranslate
(
self
.
offset_x
,
self
.
offset_y
,
0
)
glEnableClientState
(
GL_VERTEX_ARRAY
)
glEnableClientState
(
GL_COLOR_ARRAY
)
self
.
_display_movements
(
mode_2d
)
if
self
.
arrows_enabled
:
self
.
_display_arrows
()
glDisableClientState
(
GL_COLOR_ARRAY
)
glDisableClientState
(
GL_VERTEX_ARRAY
)
glPopMatrix
()
def
_display_movements
(
self
,
mode_2d
=
False
):
self
.
vertex_buffer
.
bind
()
glVertexPointer
(
3
,
GL_FLOAT
,
0
,
None
)
self
.
vertex_color_buffer
.
bind
()
glColorPointer
(
4
,
GL_FLOAT
,
0
,
None
)
if
mode_2d
:
glScale
(
1.0
,
1.0
,
0.0
)
# discard z coordinates
start
=
self
.
layer_stops
[
self
.
num_layers_to_draw
-
1
]
end
=
self
.
layer_stops
[
self
.
num_layers_to_draw
]
-
start
else
:
# 3d
start
=
0
end
=
self
.
layer_stops
[
self
.
num_layers_to_draw
]
glDrawArrays
(
GL_LINES
,
start
,
end
)
self
.
vertex_buffer
.
unbind
()
self
.
vertex_color_buffer
.
unbind
()
def
_display_arrows
(
self
):
self
.
arrow_buffer
.
bind
()
glVertexPointer
(
3
,
GL_FLOAT
,
0
,
None
)
self
.
arrow_color_buffer
.
bind
()
glColorPointer
(
4
,
GL_FLOAT
,
0
,
None
)
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
()
class
StlModel
(
Model
):
"""
Model for displaying and manipulating STL data.
"""
def
load_data
(
self
,
model_data
,
callback
=
None
):
t_start
=
time
.
time
()
vertices
,
normals
=
model_data
# convert python lists to numpy arrays for constructing vbos
self
.
vertices
=
numpy
.
require
(
vertices
,
'f'
)
self
.
normals
=
numpy
.
require
(
normals
,
'f'
)
self
.
scaling_factor
=
1.0
self
.
rotation_angle
=
{
self
.
AXIS_X
:
0.0
,
self
.
AXIS_Y
:
0.0
,
self
.
AXIS_Z
:
0.0
,
}
self
.
mat_specular
=
(
1.0
,
1.0
,
1.0
,
1.0
)
self
.
mat_shininess
=
50.0
self
.
light_position
=
(
20.0
,
20.0
,
20.0
)
self
.
initialized
=
False
t_end
=
time
.
time
()
logging
.
info
(
'Initialized STL model in
%.2
f seconds'
%
(
t_end
-
t_start
))
logging
.
info
(
'Vertex count:
%
d'
%
len
(
self
.
vertices
))
def
normal_data_empty
(
self
):
"""
Return true if the model has no normal data.
"""
empty
=
(
self
.
normals
.
max
()
==
0
and
self
.
normals
.
min
()
==
0
)
return
empty
def
calculate_normals
(
self
):
"""
Calculate surface normals for model vertices.
"""
a
=
self
.
vertices
[
0
::
3
]
-
self
.
vertices
[
1
::
3
]
b
=
self
.
vertices
[
1
::
3
]
-
self
.
vertices
[
2
::
3
]
cross
=
numpy
.
cross
(
a
,
b
)
# normalize the cross product
magnitudes
=
numpy
.
apply_along_axis
(
numpy
.
linalg
.
norm
,
1
,
cross
)
.
reshape
(
-
1
,
1
)
normals
=
cross
/
magnitudes
# each of 3 facet vertices shares the same normal
normals
=
normals
.
repeat
(
3
,
0
)
return
normals
# ------------------------------------------------------------------------
# DRAWING
# ------------------------------------------------------------------------
def
init
(
self
):
"""
Create vertex buffer objects (VBOs).
"""
self
.
vertex_buffer
=
VBO
(
self
.
vertices
,
'GL_STATIC_DRAW'
)
if
self
.
normal_data_empty
():
logging
.
info
(
'STL model has no normal data'
)
self
.
normals
=
self
.
calculate_normals
()
self
.
normal_buffer
=
VBO
(
self
.
normals
,
'GL_STATIC_DRAW'
)
self
.
initialized
=
True
def
draw_facets
(
self
):
glPushMatrix
()
glEnable
(
GL_LIGHT0
)
glEnable
(
GL_LIGHT1
)
glShadeModel
(
GL_SMOOTH
)
# material properties (white plastic)
glMaterial
(
GL_FRONT
,
GL_AMBIENT
,
(
0.0
,
0.0
,
0.0
,
1.0
))
glMaterial
(
GL_FRONT
,
GL_DIFFUSE
,
(
0.55
,
0.55
,
0.55
,
1.0
))
glMaterial
(
GL_FRONT
,
GL_SPECULAR
,
(
0.7
,
0.7
,
0.7
,
1.0
))
glMaterial
(
GL_FRONT
,
GL_SHININESS
,
32.0
)
# lights properties
glLight
(
GL_LIGHT0
,
GL_AMBIENT
,
(
0.3
,
0.3
,
0.3
,
1.0
))
glLight
(
GL_LIGHT0
,
GL_DIFFUSE
,
(
0.3
,
0.3
,
0.3
,
1.0
))
glLight
(
GL_LIGHT1
,
GL_DIFFUSE
,
(
0.3
,
0.3
,
0.3
,
1.0
))
# lights position
glLightfv
(
GL_LIGHT0
,
GL_POSITION
,
self
.
light_position
)
glLightfv
(
GL_LIGHT1
,
GL_POSITION
,
(
-
20.0
,
-
20.0
,
20.0
))
glColor
(
1.0
,
1.0
,
1.0
)
### VBO stuff
self
.
vertex_buffer
.
bind
()
glVertexPointer
(
3
,
GL_FLOAT
,
0
,
None
)
self
.
normal_buffer
.
bind
()
glNormalPointer
(
GL_FLOAT
,
0
,
None
)
glEnableClientState
(
GL_VERTEX_ARRAY
)
glEnableClientState
(
GL_NORMAL_ARRAY
)
glDrawArrays
(
GL_TRIANGLES
,
0
,
len
(
self
.
vertices
))
glDisableClientState
(
GL_NORMAL_ARRAY
)
glDisableClientState
(
GL_VERTEX_ARRAY
)
self
.
normal_buffer
.
unbind
()
self
.
vertex_buffer
.
unbind
()
### end VBO stuff
glDisable
(
GL_LIGHT1
)
glDisable
(
GL_LIGHT0
)
glPopMatrix
()
def
display
(
self
,
mode_2d
=
False
):
glEnable
(
GL_LIGHTING
)
self
.
draw_facets
()
glDisable
(
GL_LIGHTING
)
# ------------------------------------------------------------------------
# TRANSFORMATIONS
# ------------------------------------------------------------------------
def
scale
(
self
,
factor
):
if
factor
!=
self
.
scaling_factor
:
logging
.
info
(
'actually scaling vertices'
)
self
.
vertices
*=
(
factor
/
self
.
scaling_factor
)
self
.
scaling_factor
=
factor
self
.
invalidate_bounding_box
()
self
.
modified
=
True
def
translate
(
self
,
x
,
y
,
z
):
self
.
vertices
=
vector
.
translate
(
self
.
vertices
,
x
,
y
,
z
)
self
.
invalidate_bounding_box
()
self
.
modified
=
True
def
rotate_rel
(
self
,
angle
,
axis
):
logging
.
info
(
'rotating vertices by a relative angle of '
'
%.2
f degrees along the
%
s axis'
%
(
angle
,
self
.
axis_letter_map
[
axis
]))
angle
=
angle
%
360
self
.
vertices
=
vector
.
rotate
(
self
.
vertices
,
angle
,
*
axis
)
self
.
rotation_angle
[
axis
]
+=
angle
self
.
invalidate_bounding_box
()
self
.
modified
=
True
def
rotate_abs
(
self
,
angle
,
axis
):
angle
=
angle
%
360
if
self
.
rotation_angle
[
axis
]
==
angle
:
return
logging
.
info
(
'rotating vertices by an absolute angle of '
'
%.2
f degrees along the
%
s axis'
%
(
angle
,
self
.
axis_letter_map
[
axis
]))
final_matrix
=
vector
.
identity_matrix
()
# modify matrix to rotate to initial position
for
v
in
[
self
.
AXIS_Z
,
self
.
AXIS_Y
,
self
.
AXIS_X
]:
matrix
=
vector
.
rotation_matrix
(
-
self
.
rotation_angle
[
v
],
*
v
)
final_matrix
=
final_matrix
.
dot
(
matrix
)
# change the angle
self
.
rotation_angle
[
axis
]
=
angle
# modify matrix to rotate to new position
for
v
in
[
self
.
AXIS_X
,
self
.
AXIS_Y
,
self
.
AXIS_Z
]:
matrix
=
vector
.
rotation_matrix
(
self
.
rotation_angle
[
v
],
*
v
)
final_matrix
=
final_matrix
.
dot
(
matrix
)
self
.
vertices
=
self
.
vertices
.
dot
(
final_matrix
)
self
.
invalidate_bounding_box
()
self
.
modified
=
True
printrun/libtatlin/vector.py
0 → 100644
View file @
0762728f
# -*- 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
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