Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
freecad_x3d
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Elphel
freecad_x3d
Commits
7ceaa925
Commit
7ceaa925
authored
Dec 13, 2015
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added more parameetrs to save/restore
parent
37b45e3c
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
380 additions
and
78 deletions
+380
-78
x3d_step_assy.py
x3d_step_assy.py
+380
-78
No files found.
x3d_step_assy.py
View file @
7ceaa925
...
@@ -50,14 +50,15 @@ from xml.dom import minidom
...
@@ -50,14 +50,15 @@ from xml.dom import minidom
from
FreeCAD
import
Base
from
FreeCAD
import
Base
from
PySide
import
QtCore
,
QtGui
from
PySide
import
QtCore
,
QtGui
from
ConfigParser
import
SafeConfigParser
import
sys
import
sys
import
traceback
import
traceback
CONFIG_PATH
=
"~/.FreeCAD/x3d_step_assy.ini"
ROOT_DIR
=
'~/parts/0393/export
1
'
ROOT_DIR
=
'~/parts/0393/export'
STEP_PARTS
=
'~/parts/0393/export
1
/step_parts'
STEP_PARTS
=
'~/parts/0393/export/step_parts'
#DIR_LIST = ["parts","subassy_flat"]
#DIR_LIST = ["parts","subassy_flat"]
ASSEMBLY_PATH
=
None
ASSEMBLY_PATH
=
""
INFO_DIR
=
"info"
INFO_DIR
=
"info"
X3D_DIR
=
"x3d"
X3D_DIR
=
"x3d"
X3D_EXT
=
".x3d"
X3D_EXT
=
".x3d"
...
@@ -66,11 +67,14 @@ PRECISION = 0.0001
...
@@ -66,11 +67,14 @@ PRECISION = 0.0001
PRECISION_AREA
=
0.001
PRECISION_AREA
=
0.001
PRECISION_VOLUME
=
0.001
PRECISION_VOLUME
=
0.001
PRECISION_GYRATION
=
0.001
PRECISION_GYRATION
=
0.001
PRECISION_INSIDE
=
0.03
PRECISION_INSIDE
=
0.03
COLOR_PER_VERTEX
=
True
COLOR_PER_VERTEX
=
True
COMPONENTS
=
None
# to hold data structure that is long to build so it will survive if the macro crashes
COMPONENTS
=
None
# to hold data structure that is long to build so it will survive if the macro crashes
if
CONFIG_PATH
[
0
]
==
"~"
:
CONFIG_PATH
=
os
.
path
.
join
(
os
.
path
.
expanduser
(
'~'
),
CONFIG_PATH
[
2
:])
if
ROOT_DIR
[
0
]
==
"~"
:
if
ROOT_DIR
[
0
]
==
"~"
:
ROOT_DIR
=
os
.
path
.
join
(
os
.
path
.
expanduser
(
'~'
),
ROOT_DIR
[
2
:])
ROOT_DIR
=
os
.
path
.
join
(
os
.
path
.
expanduser
(
'~'
),
ROOT_DIR
[
2
:])
if
STEP_PARTS
[
0
]
==
"~"
:
if
STEP_PARTS
[
0
]
==
"~"
:
...
@@ -88,22 +92,17 @@ def get_step_list(dir_listdirs):
...
@@ -88,22 +92,17 @@ def get_step_list(dir_listdirs):
for
root
,
_
,
files
in
os
.
walk
(
dir_path
,
topdown
=
True
,
onerror
=
None
,
followlinks
=
True
)
for
root
,
_
,
files
in
os
.
walk
(
dir_path
,
topdown
=
True
,
onerror
=
None
,
followlinks
=
True
)
for
f
in
files
if
f
.
endswith
((
".step"
,
".stp"
,
".STP"
,
".STEP"
))]
for
f
in
files
if
f
.
endswith
((
".step"
,
".stp"
,
".STP"
,
".STEP"
))]
"""
def get_step_list(dir_list):
step_files = []
for rpath in dir_list:
apath = os.path.join(ROOT_DIR,rpath)
try:
step_files += [os.path.join(rpath, f) for f in os.listdir(apath) if os.path.isfile(os.path.join(apath, f)) and f.endswith((".step",".stp"))]
except:
print ("Failed to add files from
%
s"
%
apath)
return step_files
"""
def
vector_to_tuple
(
v
):
def
vector_to_tuple
(
v
):
return
((
v
.
x
,
v
.
y
,
v
.
z
))
return
((
v
.
x
,
v
.
y
,
v
.
z
))
def
repair_solids_from_shells
(
shape
):
def
repair_solids_from_shells
(
shape
):
"""
Some imported object from STEP files turned out to be open shells.
Convert them to solids with FreeCAD
@param shape - FreeCAD Shape
@return a list of FreeCAD solids
"""
solids
=
shape
.
Solids
solids
=
shape
.
Solids
new_solids
=
[]
new_solids
=
[]
for
sh
in
shape
.
Shells
:
for
sh
in
shape
.
Shells
:
...
@@ -116,8 +115,14 @@ def repair_solids_from_shells(shape):
...
@@ -116,8 +115,14 @@ def repair_solids_from_shells(shape):
new_solids
.
append
(
Part
.
Solid
(
sh
))
new_solids
.
append
(
Part
.
Solid
(
sh
))
return
new_solids
return
new_solids
#Find Vertex indices with maximal/minima X,Y,Z to check orientation(Still does not check for holes - Add them somehow?
#Find Vertex indices with maximal/minima
l
X,Y,Z to check orientation(Still does not check for holes - Add them somehow?
def
verticesToCheck
(
solid
):
def
verticesToCheck
(
solid
):
"""
Create a list of 18 vertices having maximal/minimal X, Y, Z (and their +/- pairs).
These vertices will be later tested to be inside (with certain precision) the assembly part
@param solid - A Solid object
@return list of 18 3-tuples (x,y,z)
"""
l
=
[[],[],[],[],[],[],[],[],[]]
l
=
[[],[],[],[],[],[],[],[],[]]
for
v
in
solid
.
Vertexes
:
for
v
in
solid
.
Vertexes
:
l
[
0
]
.
append
(
v
.
X
)
l
[
0
]
.
append
(
v
.
X
)
...
@@ -140,6 +145,17 @@ def verticesToCheck(solid):
...
@@ -140,6 +145,17 @@ def verticesToCheck(solid):
return
lv
return
lv
def
create_file_info_nogui
(
shape
,
fname
=
""
):
def
create_file_info_nogui
(
shape
,
fname
=
""
):
"""
A no-Gui version of the create_file_info, rather useless now as the color
is critical for the program. Using FreeCAD GGUI significantly slows down
the program and prevents it from running in a true batch mode. It seems
possible to hack Face.Tolerance property (unused so far) and import STEP
files saving colors in this property.
@param shape - FreeCAD Shape, containing one or more solids
@param fname - source file path
@return a pair of a list of info for each solid (as a dictionary) and a
list of solids in the shape
"""
objects
=
[]
objects
=
[]
#repairing open shells
#repairing open shells
solids
=
shape
.
Solids
solids
=
shape
.
Solids
...
@@ -171,6 +187,15 @@ def create_file_info_nogui(shape, fname=""):
...
@@ -171,6 +187,15 @@ def create_file_info_nogui(shape, fname=""):
def
create_file_info
(
freecadObjects
,
fname
=
""
):
def
create_file_info
(
freecadObjects
,
fname
=
""
):
"""
Collect information about each part/solid to be used for comparison between
assembly objects and parts
@param shape - FreeCAD Shape, containing one or more solids
@param fname - source file path
@return a pair of a list of info for each solid (as a dictionary) and a
list of solids in the shape
"""
if
not
"Gui"
in
dir
(
FreeCAD
):
if
not
"Gui"
in
dir
(
FreeCAD
):
return
create_file_info_nogui
(
freecadObjects
,
fname
)
return
create_file_info_nogui
(
freecadObjects
,
fname
)
# Count all shells in all objects
# Count all shells in all objects
...
@@ -213,7 +238,6 @@ def create_file_info(freecadObjects, fname=""):
...
@@ -213,7 +238,6 @@ def create_file_info(freecadObjects, fname=""):
if
(
len
(
dc
)
==
1
)
and
(
len
(
o
.
Shape
.
Faces
)
>
1
):
if
(
len
(
dc
)
==
1
)
and
(
len
(
o
.
Shape
.
Faces
)
>
1
):
dc
=
dc
*
len
(
o
.
Shape
.
Faces
)
dc
=
dc
*
len
(
o
.
Shape
.
Faces
)
colorCenters
=
[[
0.0
,
0.0
,
0.0
,
0.0
]
for
c
in
col_list
]
# SX,SY,SZ,S0
colorCenters
=
[[
0.0
,
0.0
,
0.0
,
0.0
]
for
c
in
col_list
]
# SX,SY,SZ,S0
# for clr,face in zip(o.ViewObject.DiffuseColor, o.Shape.Faces):
for
clr
,
face
in
zip
(
dc
,
o
.
Shape
.
Faces
):
for
clr
,
face
in
zip
(
dc
,
o
.
Shape
.
Faces
):
clr_index
=
col_dict
[
clr
]
clr_index
=
col_dict
[
clr
]
m
=
face
.
Area
m
=
face
.
Area
...
@@ -231,7 +255,6 @@ def create_file_info(freecadObjects, fname=""):
...
@@ -231,7 +255,6 @@ def create_file_info(freecadObjects, fname=""):
colorCenters
[
clr_index
][
2
]
/
colorCenters
[
clr_index
][
3
]),
colorCenters
[
clr_index
][
2
]
/
colorCenters
[
clr_index
][
3
]),
"area"
:
colorCenters
[
clr_index
][
3
]}
"area"
:
colorCenters
[
clr_index
][
3
]}
# print ("color_center_area[%s] = %s"%(str(clr), str(color_center_area[clr])))
# print ("color_center_area[%s] = %s"%(str(clr), str(color_center_area[clr])))
for
i
,
s
in
enumerate
(
solids
):
for
i
,
s
in
enumerate
(
solids
):
pp
=
s
.
PrincipalProperties
pp
=
s
.
PrincipalProperties
...
@@ -260,6 +283,17 @@ def create_file_info(freecadObjects, fname=""):
...
@@ -260,6 +283,17 @@ def create_file_info(freecadObjects, fname=""):
return
(
objects
,
allSolids
)
return
(
objects
,
allSolids
)
def
get_info_files_nogui
(
dir_list
=
None
):
def
get_info_files_nogui
(
dir_list
=
None
):
"""
a no-gui version of get_info_files()
@param dir_list - list of directories (usually a single-element) to scan for
STEP part models (including subdirectories). Non-existing
directories in the list are OK, they will be silently skipped.
@return a dictionary with part names as keys and info parameters lists of
dictionaries created by create_file_info() as values
Each part usually has just one solid, but may have more than one, in that
case only the largest (by volume) is used for identification in the
assembly, and it is returned at index 0 in the result
"""
if
dir_list
is
None
:
if
dir_list
is
None
:
dir_list
=
[
STEP_PARTS
]
dir_list
=
[
STEP_PARTS
]
start_time
=
time
.
time
()
start_time
=
time
.
time
()
...
@@ -304,6 +338,19 @@ def get_info_files_nogui(dir_list = None):
...
@@ -304,6 +338,19 @@ def get_info_files_nogui(dir_list = None):
return
info_dict
return
info_dict
def
get_info_files
(
dir_list
=
None
):
def
get_info_files
(
dir_list
=
None
):
"""
Get information about each part collected with create_file_info() function
Generate this information for each part that does not have it or have
obsolete (older than STEP file) one.
@param dir_list - list of directories (usually a single-element) to scan for
STEP part models (including subdirectories). Non-existing
directories in the list are OK, they will be silently skipped.
@return a dictionary with part names as keys and info parameters lists of
dictionaries created by create_file_info() as values
Each part usually has just one solid, but may have more than one, in that
case only the largest (by volume) is used for identification in the
assembly, and it is returned at index 0 in the result
"""
if
dir_list
is
None
:
if
dir_list
is
None
:
dir_list
=
[
STEP_PARTS
]
dir_list
=
[
STEP_PARTS
]
if
not
"Gui"
in
dir
(
FreeCAD
):
if
not
"Gui"
in
dir
(
FreeCAD
):
...
@@ -367,16 +414,31 @@ def get_info_files(dir_list = None):
...
@@ -367,16 +414,31 @@ def get_info_files(dir_list = None):
def
findPartsTransformations
(
solids
,
objects
,
candidates
,
info_dict
,
insidePrecision
=
PRECISION_INSIDE
,
precision
=
PRECISION
):
def
findPartsTransformations
(
solids
,
objects
,
candidates
,
info_dict
,
insidePrecision
=
PRECISION_INSIDE
,
precision
=
PRECISION
):
"""
"""
@param candidates - list (per assembly element) of dictionaries indexed by part name (usually just one) one,
Find transformation (translation+rotation) matrices for each assembly part and each candidate part
containing list of colors (tuples) for which there is an area match between assembly element and a part.
@param solids - list of solids, they are used to check that the test part vertices are almost inside
Build element part frame from colors first, then (if not enough) use inertial directions
Number of elements in solids, objects, candidates should match
@param objects - list of the solid properties (as dictionaries made by create_file_info()) of the assembly
elements.
@param candidates - list (per assembly element) of dictionaries indexed by part name (usually just one),
containing list of colors (tuples) for which there is an area match between assembly
element and a part. Build element part frame from colors first, then (if not enough)
use inertial directions.
@param info_dict - dictionary (part name as a key) of lists (first element is the largest by volume) of
part solid properties used for matching
@param inside_precision - precision for determining if the test points (available for each part) get inside
the assembly element. Currently as a fraction of the object bounding box diagonal,
but maybe it is better to use fraction of the translation distance plus diagonal?
@param precision - relative precision for matrix/vector calculations (determining co-linear/co-planar objects
@return a list (per assembly solid) of dictionaries part_name -> 4x4 transformation matrix (not yet tested
with multiple fits
"""
"""
progress_bar
=
Base
.
ProgressIndicator
()
progress_bar
=
Base
.
ProgressIndicator
()
progress_bar
.
start
(
"Finding transformations for library parts to match assembly elements ..."
,
len
(
objects
))
progress_bar
.
start
(
"Finding transformations for library parts to match assembly elements ..."
,
len
(
objects
))
transformations
=
[]
transformations
=
[]
for
i
,
s
in
enumerate
(
solids
):
for
i
,
s
in
enumerate
(
solids
):
tolerance
=
insidePrecision
*
s
.
BoundBox
.
DiagonalLength
# Or should it be fraction of the translation distance?
tolerance
=
insidePrecision
*
s
.
BoundBox
.
DiagonalLength
# Or should it be fraction of the translation distance?
trans
=
[]
trans
=
{}
print
(
"
%
d findPartsTransformations:"
%
(
i
))
print
(
"
%
d findPartsTransformations:"
%
(
i
))
for
cand_name
in
candidates
[
i
]:
for
cand_name
in
candidates
[
i
]:
co
=
info_dict
[
cand_name
][
0
]
# First solid in the candidate part file
co
=
info_dict
[
cand_name
][
0
]
# First solid in the candidate part file
...
@@ -403,7 +465,8 @@ def findPartsTransformations(solids, objects, candidates, info_dict, insidePreci
...
@@ -403,7 +465,8 @@ def findPartsTransformations(solids, objects, candidates, info_dict, insidePreci
break
break
else
:
else
:
print
(
"
%
d:
%
s - got transformation with orientation
%
d"
%
(
i
,
cand_name
,
orient
))
print
(
"
%
d:
%
s - got transformation with orientation
%
d"
%
(
i
,
cand_name
,
orient
))
trans
.
append
(
matrix_part_assy
)
# trans.append(matrix_part_assy)
trans
[
cand_name
]
=
matrix_part_assy
break
break
else
:
else
:
print
(
"Could not find match for part
%
s, trying manually around that vertex"
%
(
cand_name
))
print
(
"Could not find match for part
%
s, trying manually around that vertex"
%
(
cand_name
))
...
@@ -431,19 +494,28 @@ def findPartsTransformations(solids, objects, candidates, info_dict, insidePreci
...
@@ -431,19 +494,28 @@ def findPartsTransformations(solids, objects, candidates, info_dict, insidePreci
break
# no luck
break
# no luck
else
:
else
:
print
(
"
%
d:
%
s - finally got transformation with orientation
%
d"
%
(
i
,
cand_name
,
orient
))
print
(
"
%
d:
%
s - finally got transformation with orientation
%
d"
%
(
i
,
cand_name
,
orient
))
trans
.
append
(
matrix_part_assy
)
# trans.append(matrix_part_assy)
trans
[
cand_name
]
=
matrix_part_assy
break
break
else
:
else
:
trans
.
append
(
None
)
# so Transformations have same structure as candidates
# trans.append(None) # so Transformations have same structure as candidates, and it is now dictionary
print
(
"*** Could not find match for part
%
s"
%
(
cand_name
))
print
(
"*** Could not find match for part
%
s"
%
(
cand_name
))
transformations
.
append
(
trans
)
transformations
.
append
(
trans
)
progress_bar
.
next
()
# True)
# True - enable ESC to abort
progress_bar
.
next
()
# True)
progress_bar
.
stop
()
progress_bar
.
stop
()
return
transformations
return
transformations
def
colorMatchCandidate
(
assy_object
,
candidates
,
info_dict
,
precision
=
PRECISION_AREA
):
def
colorMatchCandidate
(
assy_object
,
candidates
,
info_dict
,
precision
=
PRECISION_AREA
):
"""
"""
Select colored features among the parts candidates by comparing total area per color
with the candidates so if some feature on the assembly object and the part have
different colors, the others can still be used for orientation identification.
Parts w/o matching color information can only be oriented by axes of gyration
@param assy_object - a dictionary of the parameters of the assembly object
@param candidates - a list of part name that fit this assembly object without color
properties
@param info_dict - dictionary of parameters for all parts (indexed by part names)
@return dictionary partName -> list of colors (as 3-tuples)
@return dictionary partName -> list of colors (as 3-tuples)
"""
"""
colored_candidates
=
{}
colored_candidates
=
{}
...
@@ -470,12 +542,7 @@ def colorMatchCandidate(assy_object, candidates, info_dict, precision = PRECISIO
...
@@ -470,12 +542,7 @@ def colorMatchCandidate(assy_object, candidates, info_dict, precision = PRECISIO
if
len
(
colors
)
==
max_match
:
if
len
(
colors
)
==
max_match
:
colored_candidates
[
candidate
]
=
colors
colored_candidates
[
candidate
]
=
colors
return
colored_candidates
return
colored_candidates
"""
PRECISION_AREA = 0.02
PRECISION_VOLUME = 0.03
PRECISION_INSIDE = 0.03
"""
#components=scan_step_parts.findComponents("/home/andrey/parts/0393/export/nc393_07_flat_noassy.stp")
def
findComponents
(
assembly
,
def
findComponents
(
assembly
,
precision_area
=
PRECISION_AREA
,
precision_area
=
PRECISION_AREA
,
precision_volume
=
PRECISION_VOLUME
,
precision_volume
=
PRECISION_VOLUME
,
...
@@ -484,13 +551,14 @@ def findComponents(assembly,
...
@@ -484,13 +551,14 @@ def findComponents(assembly,
precision
=
PRECISION
,
precision
=
PRECISION
,
show_best
=
True
):
show_best
=
True
):
"""
"""
@param assembly - may be file path (different treatment for Gui/no-Gui, Shape or doc.Objects or None - will use ActiveDocument().Objects
Match each assembly element with a part, provide the transformation matrix
@param assembly - may be file path (different treatment for Gui/no-Gui, Shape or doc.Objects or "" - will use ActiveDocument().Objects
@param precision_area = PRECISION_AREA - relative precision in surface area calculations
@param precision_area = PRECISION_AREA - relative precision in surface area calculations
@param precision_volume = PRECISION_VOLUME - relative precision in volume calculations
@param precision_volume = PRECISION_VOLUME - relative precision in volume calculations
@param precision_gyration = PRECISION_GYRATION - relative precision in radius of gyration calculations
@param precision_gyration = PRECISION_GYRATION - relative precision in radius of gyration calculations
@param precision_inside = PRECISION_INSIDE - relative precision in calculations of point inside/outside of a solid
@param precision_inside = PRECISION_INSIDE - relative precision in calculations of point inside/outside of a solid
@param precision = PRECISION - precision in vector calculation
@param precision = PRECISION - precision in vector calculation
@param show_best - calculate and show the best relative match for each parameter
@param show_best - calculate and show the best relative match for each parameter
- can be used to fine-tune PRECISION* parameters
"""
"""
FreeCAD
.
Console
.
PrintMessage
(
"findComponents(): Getting parts database"
);
FreeCAD
.
Console
.
PrintMessage
(
"findComponents(): Getting parts database"
);
global
COMPONENTS
global
COMPONENTS
...
@@ -499,7 +567,7 @@ def findComponents(assembly,
...
@@ -499,7 +567,7 @@ def findComponents(assembly,
info_dict
=
get_info_files
()
info_dict
=
get_info_files
()
FreeCAD
.
Console
.
PrintMessage
(
"findComponents(): Got parts database"
);
FreeCAD
.
Console
.
PrintMessage
(
"findComponents(): Got parts database"
);
aname
=
""
aname
=
""
if
not
assembly
:
if
not
assembly
:
# including "" string
assembly
=
FreeCAD
.
activeDocument
()
.
Objects
assembly
=
FreeCAD
.
activeDocument
()
.
Objects
FreeCAD
.
Console
.
PrintMessage
(
"Using
%
d solids in the active document @
%
f"
%
(
len
(
assembly
),
time
.
time
()
-
start_time
));
FreeCAD
.
Console
.
PrintMessage
(
"Using
%
d solids in the active document @
%
f"
%
(
len
(
assembly
),
time
.
time
()
-
start_time
));
if
isinstance
(
assembly
,
(
str
,
unicode
)):
if
isinstance
(
assembly
,
(
str
,
unicode
)):
...
@@ -965,15 +1033,12 @@ def generateAssemblyX3d(assembly_path,
...
@@ -965,15 +1033,12 @@ def generateAssemblyX3d(assembly_path,
defined_parts
=
{}
# for each defined part holds index (for ID generation)
defined_parts
=
{}
# for each defined part holds index (for ID generation)
for
i
,
component
in
enumerate
(
components
[
'objects'
]):
for
i
,
component
in
enumerate
(
components
[
'objects'
]):
parts
=
components
[
'candidates'
][
i
]
transformations
=
components
[
'transformations'
][
i
]
# same structure as candidates, missing - {}'None'
transformations
=
components
[
'transformations'
][
i
]
# same structure as candidates, missing - 'None'
if
not
transformations
:
for
transformation
,
part
in
zip
(
transformations
,
parts
):
print
(
"Component
%
d does not have any matches, ignoring. Candidates:
%
s"
%
(
i
,
str
(
components
[
'candidates'
][
i
])))
if
transformation
:
break
else
:
print
(
"Component
%
d does not have any matches, ignoring. Candidates:
%
s"
%
(
i
,
str
(
parts
)))
continue
continue
# bbox=components['shape'].Shells[i].BoundBox
part
=
transformations
.
keys
()[
0
]
transformation
=
transformations
[
part
]
bbox
=
components
[
'solids'
][
i
]
.
BoundBox
bbox
=
components
[
'solids'
][
i
]
.
BoundBox
bboxCenter
=
((
bbox
.
XMax
+
bbox
.
XMin
)
/
2
,(
bbox
.
YMax
+
bbox
.
YMin
)
/
2
,(
bbox
.
ZMax
+
bbox
.
ZMin
)
/
2
)
bboxCenter
=
((
bbox
.
XMax
+
bbox
.
XMin
)
/
2
,(
bbox
.
YMax
+
bbox
.
YMin
)
/
2
,(
bbox
.
ZMax
+
bbox
.
ZMin
)
/
2
)
bboxSize
=
(
bbox
.
XMax
-
bbox
.
XMin
,
bbox
.
YMax
-
bbox
.
YMin
,
bbox
.
ZMax
-
bbox
.
ZMin
)
bboxSize
=
(
bbox
.
XMax
-
bbox
.
XMin
,
bbox
.
YMax
-
bbox
.
YMin
,
bbox
.
ZMax
-
bbox
.
ZMin
)
...
@@ -1044,10 +1109,18 @@ def run():
...
@@ -1044,10 +1109,18 @@ def run():
########################################################################
########################################################################
class
X3dStepAssyDialog
(
QtGui
.
QWidget
):
class
X3dStepAssyDialog
(
QtGui
.
QWidget
):
""""""
""""""
assembly_path
=
None
assembly_path
=
""
x3d_root_path
=
None
x3d_root_path
=
""
step_parts_path
=
None
step_parts_path
=
""
log_file
=
None
log_file
=
""
precision
=
0.0001
precision_area
=
0.001
precision_volume
=
0.001
precision_gyration
=
0.001
precision_inside
=
0.03
#----------------------------------------------------------------------
#----------------------------------------------------------------------
def
get_path_text
(
self
,
path
,
mode
=
None
):
def
get_path_text
(
self
,
path
,
mode
=
None
):
if
path
:
if
path
:
...
@@ -1059,11 +1132,70 @@ class X3dStepAssyDialog(QtGui.QWidget):
...
@@ -1059,11 +1132,70 @@ class X3dStepAssyDialog(QtGui.QWidget):
else
:
else
:
return
"not set"
return
"not set"
def
saveSettings
(
self
):
def
__init__
(
self
,
assembly_path
=
None
,
x3d_root_path
=
None
,
step_parts_path
=
None
):
config
=
SafeConfigParser
()
self
.
assembly_path
=
assembly_path
config
.
read
(
CONFIG_PATH
)
# OK not to have any file
self
.
x3d_root_path
=
x3d_root_path
try
:
self
.
step_parts_path
=
step_parts_path
config
.
add_section
(
'paths'
)
except
:
pass
# OK if the section already exists
config
.
set
(
'paths'
,
'assembly_path'
,
self
.
assembly_path
)
config
.
set
(
'paths'
,
'x3d_root_path'
,
self
.
x3d_root_path
)
config
.
set
(
'paths'
,
'step_parts_path'
,
self
.
step_parts_path
)
config
.
set
(
'paths'
,
'log_file'
,
self
.
log_file
)
try
:
config
.
add_section
(
'precisions'
)
except
:
pass
# OK if the section already exists
config
.
set
(
'precisions'
,
'precision'
,
'
%
f'
%
(
self
.
precision
))
config
.
set
(
'precisions'
,
'precision_area'
,
'
%
f'
%
(
self
.
precision_area
))
config
.
set
(
'precisions'
,
'precision_volume'
,
'
%
f'
%
(
self
.
precision_volume
))
config
.
set
(
'precisions'
,
'precision_gyration'
,
'
%
f'
%
(
self
.
precision_gyration
))
config
.
set
(
'precisions'
,
'precision_inside'
,
'
%
f'
%
(
self
.
precision_inside
))
with
open
(
CONFIG_PATH
,
'w'
)
as
f
:
config
.
write
(
f
)
def
restoreSettings
(
self
):
config
=
SafeConfigParser
()
config
.
read
(
CONFIG_PATH
)
# OK not to have any file
try
:
self
.
assembly_path
=
config
.
get
(
'paths'
,
'assembly_path'
)
except
:
self
.
assembly_path
=
ASSEMBLY_PATH
try
:
self
.
x3d_root_path
=
config
.
get
(
'paths'
,
'x3d_root_path'
)
except
:
self
.
x3d_root_path
=
ROOT_DIR
try
:
self
.
step_parts_path
=
config
.
get
(
'paths'
,
'step_parts_path'
)
except
:
self
.
step_parts_path
=
STEP_PARTS
try
:
self
.
log_file
=
config
.
get
(
'paths'
,
'log_file'
)
except
:
self
.
log_file
=
""
try
:
self
.
precision
=
float
(
config
.
get
(
'precisions'
,
'precision'
))
except
:
self
.
precision
=
PRECISION
try
:
self
.
precision_area
=
float
(
config
.
get
(
'precisions'
,
'precision_area'
))
except
:
self
.
precision_area
=
PRECISION_AREA
try
:
self
.
precision_volume
=
float
(
config
.
get
(
'precisions'
,
'precision_volume'
))
except
:
self
.
precision_volume
=
PRECISION_VOLUME
try
:
self
.
precision_gyration
=
float
(
config
.
get
(
'precisions'
,
'precision_gyration'
))
except
:
self
.
precision_gyration
=
PRECISION_GYRATION
try
:
self
.
precision_inside
=
float
(
config
.
get
(
'precisions'
,
'precision_inside'
))
except
:
self
.
precision_inside
=
PRECISION_INSIDE
def
__init__
(
self
):
self
.
restoreSettings
()
"""Constructor"""
"""Constructor"""
QtGui
.
QWidget
.
__init__
(
self
)
# ,parent=parent)
QtGui
.
QWidget
.
__init__
(
self
)
# ,parent=parent)
...
@@ -1086,36 +1218,83 @@ class X3dStepAssyDialog(QtGui.QWidget):
...
@@ -1086,36 +1218,83 @@ class X3dStepAssyDialog(QtGui.QWidget):
self
.
step_parts_btn
.
setToolTip
(
"Select directory containing all the parts STEP models. Will scan sub-directories"
)
self
.
step_parts_btn
.
setToolTip
(
"Select directory containing all the parts STEP models. Will scan sub-directories"
)
self
.
help_btn
=
QtGui
.
QPushButton
(
"?"
)
self
.
help_btn
=
QtGui
.
QPushButton
(
"?"
)
self
.
execute_btn
=
QtGui
.
QPushButton
(
"Execute macro (may take hours!)"
)
self
.
execute_btn
=
QtGui
.
QPushButton
(
"Execute macro (may take hours!)"
)
label_precision
=
QtGui
.
QLabel
(
"precision"
)
label_precision_area
=
QtGui
.
QLabel
(
"precision_area"
)
label_precision_volume
=
QtGui
.
QLabel
(
"precision_volume"
)
label_precision_gyration
=
QtGui
.
QLabel
(
"precision_gyration"
)
label_precision_inside
=
QtGui
.
QLabel
(
"precision_inside"
)
self
.
lineedit_precision
=
QtGui
.
QLineEdit
()
self
.
lineedit_precision_area
=
QtGui
.
QLineEdit
()
self
.
lineedit_precision_volume
=
QtGui
.
QLineEdit
()
self
.
lineedit_precision_gyration
=
QtGui
.
QLineEdit
()
self
.
lineedit_precision_inside
=
QtGui
.
QLineEdit
()
self
.
lineedit_precision
.
setToolTip
(
"Relative precision in matrix/vector calculations"
)
self
.
lineedit_precision_area
.
setToolTip
(
"Relative precision when comparing objects surface area"
)
self
.
lineedit_precision_volume
.
setToolTip
(
"Relative precision when comparing objects volume"
)
self
.
lineedit_precision_gyration
.
setToolTip
(
"Relative precision when comparing objects radii of gyration"
)
self
.
lineedit_precision_inside
.
setToolTip
(
"Relative precision when determining if part vertices fit into assembly object"
)
self
.
lineedit_precision
.
setText
(
'
%
f'
%
(
self
.
precision
))
self
.
lineedit_precision_area
.
setText
(
'
%
f'
%
(
self
.
precision_area
))
self
.
lineedit_precision_volume
.
setText
(
'
%
f'
%
(
self
.
precision_volume
))
self
.
lineedit_precision_gyration
.
setText
(
'
%
f'
%
(
self
.
precision_gyration
))
self
.
lineedit_precision_inside
.
setText
(
'
%
f'
%
(
self
.
precision_inside
))
self
.
log_file_btn
.
clicked
.
connect
(
self
.
selectLogFile
)
self
.
log_file_btn
.
clicked
.
connect
(
self
.
selectLogFile
)
self
.
assembly_btn
.
clicked
.
connect
(
self
.
selectAssembly
)
self
.
assembly_btn
.
clicked
.
connect
(
self
.
selectAssembly
)
self
.
x3d_root_btn
.
clicked
.
connect
(
self
.
selectX3dRoot
)
self
.
x3d_root_btn
.
clicked
.
connect
(
self
.
selectX3dRoot
)
self
.
step_parts_btn
.
clicked
.
connect
(
self
.
selectStepParts
)
self
.
step_parts_btn
.
clicked
.
connect
(
self
.
selectStepParts
)
self
.
lineedit_precision
.
editingFinished
.
connect
(
self
.
editedPrecision
)
self
.
lineedit_precision_area
.
editingFinished
.
connect
(
self
.
editedPrecisionArea
)
self
.
lineedit_precision_volume
.
editingFinished
.
connect
(
self
.
editedPrecisionVolume
)
self
.
lineedit_precision_gyration
.
editingFinished
.
connect
(
self
.
editedPrecisionGyration
)
self
.
lineedit_precision_inside
.
editingFinished
.
connect
(
self
.
editedPrecisionInside
)
self
.
help_btn
.
clicked
.
connect
(
self
.
showHelp
)
self
.
help_btn
.
clicked
.
connect
(
self
.
showHelp
)
self
.
execute_btn
.
clicked
.
connect
(
self
.
executeMacro
)
self
.
execute_btn
.
clicked
.
connect
(
self
.
executeMacro
)
# layout widgets
# layout widgets
layout
=
QtGui
.
QGridLayout
()
# parent=parent)
layout
=
QtGui
.
QGridLayout
()
# parent=parent)
layout
.
setColumnStretch
(
1
,
1
)
layout
.
setColumnStretch
(
1
,
1
)
layout
.
addWidget
(
label_assembly
,
0
,
0
)
layout
.
addWidget
(
label_assembly
,
0
,
0
)
layout
.
addWidget
(
self
.
assembly_btn
,
0
,
1
)
layout
.
addWidget
(
self
.
assembly_btn
,
0
,
1
)
layout
.
addWidget
(
label_x3d_root_btn
,
1
,
0
)
layout
.
addWidget
(
self
.
x3d_root_btn
,
1
,
1
)
layout
.
addWidget
(
label_
step_parts
,
2
,
0
)
layout
.
addWidget
(
label_
x3d_root_btn
,
1
,
0
)
layout
.
addWidget
(
self
.
step_parts_btn
,
2
,
1
)
layout
.
addWidget
(
self
.
x3d_root_btn
,
1
,
1
)
layout
.
addWidget
(
label_
log_file
,
3
,
0
)
layout
.
addWidget
(
label_
step_parts
,
2
,
0
)
layout
.
addWidget
(
self
.
log_file_btn
,
3
,
1
)
layout
.
addWidget
(
self
.
step_parts_btn
,
2
,
1
)
layout
.
addWidget
(
self
.
help_btn
,
4
,
0
)
layout
.
addWidget
(
label_log_file
,
3
,
0
)
layout
.
addWidget
(
self
.
execute_btn
,
4
,
1
)
layout
.
addWidget
(
self
.
log_file_btn
,
3
,
1
)
layout
.
addWidget
(
label_precision
,
4
,
0
)
layout
.
addWidget
(
self
.
lineedit_precision
,
4
,
1
)
layout
.
addWidget
(
label_precision_area
,
5
,
0
)
layout
.
addWidget
(
self
.
lineedit_precision_area
,
5
,
1
)
layout
.
addWidget
(
label_precision_volume
,
6
,
0
)
layout
.
addWidget
(
self
.
lineedit_precision_volume
,
6
,
1
)
layout
.
addWidget
(
label_precision_gyration
,
7
,
0
)
layout
.
addWidget
(
self
.
lineedit_precision_gyration
,
7
,
1
)
layout
.
addWidget
(
label_precision_inside
,
8
,
0
)
layout
.
addWidget
(
self
.
lineedit_precision_inside
,
8
,
1
)
layout
.
addWidget
(
self
.
help_btn
,
9
,
0
)
layout
.
addWidget
(
self
.
execute_btn
,
9
,
1
)
self
.
setLayout
(
layout
)
self
.
setLayout
(
layout
)
# set the position and size of the window
# set the position and size of the window
self
.
setGeometry
(
100
,
100
,
300
,
1
00
)
self
.
setGeometry
(
100
,
100
,
300
,
2
00
)
self
.
setWindowTitle
(
"STEP assembly to X3D converter"
)
self
.
setWindowTitle
(
"STEP assembly to X3D converter"
)
...
@@ -1128,7 +1307,7 @@ class X3dStepAssyDialog(QtGui.QWidget):
...
@@ -1128,7 +1307,7 @@ class X3dStepAssyDialog(QtGui.QWidget):
"Select log file (or cancel to use stdout)"
,
"Select log file (or cancel to use stdout)"
,
prompt_file
)
prompt_file
)
self
.
log_file_btn
.
setText
(
self
.
get_path_text
(
self
.
log_file
,
"log"
))
self
.
log_file_btn
.
setText
(
self
.
get_path_text
(
self
.
log_file
,
"log"
))
self
.
saveSettings
()
def
selectAssembly
(
self
):
def
selectAssembly
(
self
):
...
@@ -1137,18 +1316,75 @@ class X3dStepAssyDialog(QtGui.QWidget):
...
@@ -1137,18 +1316,75 @@ class X3dStepAssyDialog(QtGui.QWidget):
self
.
assembly_path
,
self
.
assembly_path
,
"STEP files (*.step *.stp *.STEP *.STP)"
)
"STEP files (*.step *.stp *.STEP *.STP)"
)
self
.
assembly_btn
.
setText
(
self
.
get_path_text
(
self
.
assembly_path
,
True
))
self
.
assembly_btn
.
setText
(
self
.
get_path_text
(
self
.
assembly_path
,
True
))
self
.
saveSettings
()
def
selectX3dRoot
(
self
):
def
selectX3dRoot
(
self
):
self
.
x3d_root_path
=
QtGui
.
QFileDialog
.
getExistingDirectory
(
self
,
self
.
x3d_root_path
=
QtGui
.
QFileDialog
.
getExistingDirectory
(
self
,
"Select working directory for STEP->x3d conversion"
,
"Select working directory for STEP->x3d conversion"
,
self
.
x3d_root_path
)
self
.
x3d_root_path
)
self
.
x3d_root_btn
.
setText
(
self
.
get_path_text
(
self
.
x3d_root_path
))
self
.
x3d_root_btn
.
setText
(
self
.
get_path_text
(
self
.
x3d_root_path
))
self
.
saveSettings
()
def
selectStepParts
(
self
):
def
selectStepParts
(
self
):
self
.
step_parts_path
=
QtGui
.
QFileDialog
.
getExistingDirectory
(
self
,
self
.
step_parts_path
=
QtGui
.
QFileDialog
.
getExistingDirectory
(
self
,
"Select working directory for STEP->x3d conversion"
,
"Select working directory for STEP->x3d conversion"
,
self
.
step_parts_path
)
self
.
step_parts_path
)
self
.
step_parts_btn
.
setText
(
self
.
get_path_text
(
self
.
step_parts_path
))
self
.
step_parts_btn
.
setText
(
self
.
get_path_text
(
self
.
step_parts_path
))
self
.
saveSettings
()
def
editedPrecision
(
self
):
txt
=
self
.
lineedit_precision
.
text
()
try
:
number
=
float
(
txt
)
self
.
precision
=
number
except
Exception
:
pass
self
.
lineedit_precision
.
setText
(
'
%
f'
%
(
self
.
precision
))
self
.
saveSettings
()
def
editedPrecisionArea
(
self
):
txt
=
self
.
lineedit_precision_area
.
text
()
try
:
number
=
float
(
txt
)
self
.
precision_area
=
number
except
Exception
:
pass
self
.
lineedit_precision_area
.
setText
(
'
%
f'
%
(
self
.
precision_area
))
self
.
saveSettings
()
def
editedPrecisionVolume
(
self
):
txt
=
self
.
lineedit_precision_volume
.
text
()
try
:
number
=
float
(
txt
)
self
.
precision_volume
=
number
except
Exception
:
pass
self
.
lineedit_precision_volume
.
setText
(
'
%
f'
%
(
self
.
precision_volume
))
self
.
saveSettings
()
def
editedPrecisionGyration
(
self
):
txt
=
self
.
lineedit_precision_gyration
.
text
()
try
:
number
=
float
(
txt
)
self
.
precision_gyration
=
number
except
Exception
:
pass
self
.
lineedit_precision_gyration
.
setText
(
'
%
f'
%
(
self
.
precision_gyration
))
self
.
saveSettings
()
def
editedPrecisionInside
(
self
):
txt
=
self
.
lineedit_precision_inside
.
text
()
try
:
number
=
float
(
txt
)
self
.
precision_inside
=
number
except
Exception
:
pass
self
.
lineedit_precision_inside
.
setText
(
'
%
f'
%
(
self
.
precision_inside
))
self
.
saveSettings
()
def
showHelp
(
self
):
def
showHelp
(
self
):
msg
=
(
"This macro converts assembly CAD model to X3D. It tries to recognize "
msg
=
(
"This macro converts assembly CAD model to X3D. It tries to recognize "
...
@@ -1208,15 +1444,21 @@ class X3dStepAssyDialog(QtGui.QWidget):
...
@@ -1208,15 +1444,21 @@ class X3dStepAssyDialog(QtGui.QWidget):
# msgBox.exec_()
# msgBox.exec_()
def
executeMacro
(
self
):
def
executeMacro
(
self
):
global
ROOT_DIR
,
ASSEMBLY_PATH
,
STEP_PARTS
,
COMPONENTS
global
ROOT_DIR
,
ASSEMBLY_PATH
,
STEP_PARTS
,
COMPONENTS
,
PRECISION
,
PRECISION_AREA
,
PRECISION_VOLUME
,
PRECISION_GYRATION
,
PRECISION_INSIDE
COMPONENTS
=
None
# Start with new ones
# print ("ROOT_DIR=%s"%(ROOT_DIR))
# msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, "About STEP->X3D Assembly converter", "ROOT_DIR=%s"%(ROOT_DIR))
# msgBox.exec_()
FreeCAD
.
Console
.
PrintMessage
(
"Starting execution..."
);
FreeCAD
.
Console
.
PrintMessage
(
"Starting execution..."
);
ASSEMBLY_PATH
=
self
.
assembly_path
COMPONENTS
=
None
# Start with new ones
ROOT_DIR
=
self
.
x3d_root_path
ASSEMBLY_PATH
=
self
.
assembly_path
STEP_PARTS
=
self
.
step_parts_path
ROOT_DIR
=
self
.
x3d_root_path
STEP_PARTS
=
self
.
step_parts_path
PRECISION
=
self
.
precision
PRECISION_AREA
=
self
.
precision_area
PRECISION_VOLUME
=
self
.
precision_volume
PRECISION_GYRATION
=
self
.
precision_gyration
PRECISION_INSIDE
=
self
.
precision_inside
self
.
saveSettings
()
if
self
.
log_file
:
if
self
.
log_file
:
sys
.
stdout
=
open
(
self
.
log_file
,
"w"
)
sys
.
stdout
=
open
(
self
.
log_file
,
"w"
)
else
:
else
:
...
@@ -1239,16 +1481,76 @@ class X3dStepAssyDialog(QtGui.QWidget):
...
@@ -1239,16 +1481,76 @@ class X3dStepAssyDialog(QtGui.QWidget):
#----------------------------------------------------------------------
#----------------------------------------------------------------------
def
saveSettings
():
# when working w/o dialog
config
=
SafeConfigParser
()
config
.
read
(
CONFIG_PATH
)
# OK not to have any file
try
:
config
.
add_section
(
'paths'
)
except
:
pass
# OK if the section already exists
config
.
set
(
'paths'
,
'assembly_path'
,
ASSEMBLY_PATH
)
config
.
set
(
'paths'
,
'x3d_root_path'
,
ROOT_DIR
)
config
.
set
(
'paths'
,
'step_parts_path'
,
STEP_PARTS
)
config
.
set
(
'paths'
,
'log_file'
,
""
)
try
:
config
.
add_section
(
'precisions'
)
except
:
pass
# OK if the section already exists
config
.
set
(
'precisions'
,
'precision'
,
'
%
f'
%
(
PRECISION
))
config
.
set
(
'precisions'
,
'precision_area'
,
'
%
f'
%
(
PRECISION_AREA
))
config
.
set
(
'precisions'
,
'precision_volume'
,
'
%
f'
%
(
PRECISION_VOLUME
))
config
.
set
(
'precisions'
,
'precision_gyration'
,
'
%
f'
%
(
PRECISION_GYRATION
))
config
.
set
(
'precisions'
,
'precision_inside'
,
'
%
f'
%
(
PRECISION_INSIDE
))
with
open
(
CONFIG_PATH
,
'w'
)
as
f
:
config
.
write
(
f
)
def
restoreSettings
():
global
ROOT_DIR
,
ASSEMBLY_PATH
,
STEP_PARTS
,
COMPONENTS
,
PRECISION
,
PRECISION_AREA
,
PRECISION_VOLUME
,
PRECISION_GYRATION
,
PRECISION_INSIDE
config
=
SafeConfigParser
()
config
.
read
(
CONFIG_PATH
)
# OK not to have any file
try
:
ASSEMBLY_PATH
=
config
.
get
(
'paths'
,
'assembly_path'
)
except
:
pass
try
:
ROOT_DIR
=
config
.
get
(
'paths'
,
'x3d_root_path'
)
except
:
pass
try
:
STEP_PARTS
=
config
.
get
(
'paths'
,
'step_parts_path'
)
except
:
pass
try
:
PRECISION
=
float
(
config
.
get
(
'precisions'
,
'precision'
))
except
:
pass
try
:
PRECISION_AREA
=
float
(
config
.
get
(
'precisions'
,
'precision_area'
))
except
:
pass
try
:
PRECISION_VOLUME
=
float
(
config
.
get
(
'precisions'
,
'precision_volume'
))
except
:
pass
try
:
PRECISION_GYRATION
=
float
(
config
.
get
(
'precisions'
,
'precision_gyration'
))
except
:
pass
try
:
PRECISION_INSIDE
=
float
(
config
.
get
(
'precisions'
,
'precision_inside'
))
except
:
pass
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
form
=
X3dStepAssyDialog
(
assembly_path
=
ASSEMBLY_PATH
,
x3d_root_path
=
ROOT_DIR
,
step_parts_path
=
STEP_PARTS
)
# FreeCADGui.getMainWindow())
form
=
X3dStepAssyDialog
()
# FreeCADGui.getMainWindow())
form
.
show
()
form
.
show
()
# run()
# def __init__(self, assembly_path=None, x3d_root_path=None, step_parts_path = None):
"""
"""
reload (x3d_step_assy)
reload (x3d_step_assy)
form = x3d_step_assy.X3dStepAssyDialog(assembly_path= x3d_step_assy.ASSEMBLY_PATH, x3d_root_path = x3d_step_assy.ROOT_DIR, step_parts_path = x3d_step_assy.STEP_PARTS)
#form = x3d_step_assy.X3dStepAssyDialog(assembly_path= x3d_step_assy.ASSEMBLY_PATH, x3d_root_path = x3d_step_assy.ROOT_DIR, step_parts_path = x3d_step_assy.STEP_PARTS)
form = x3d_step_assy.X3dStepAssyDialog()
form.show()
form.show()
components= x3d_step_assy.generateAssemblyX3d(x3d_step_assy.ASSEMBLY_PATH)
components= x3d_step_assy.generateAssemblyX3d(x3d_step_assy.ASSEMBLY_PATH)
>>> Gui.getDocument("Unnamed").getObject("Part__Feature").Visibility=False
>>> Gui.getDocument("Unnamed").getObject("Part__Feature").Visibility=False
...
...
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