Macro Sheet Metal Unfolder: Difference between revisions
(Marked this version for translation) |
(Use {{MacroCode}}) |
||
(20 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
<languages/> |
|||
<translate> |
<translate> |
||
<!--T:1--> |
<!--T:1--> |
||
{{Macro |
|||
{{Macro|Icon=Text-x-python|Name=Sheet_ufo|Description=This macro unfolds simple sheet-metal-designs.|Author=ulrich1a}} |
|||
|Name=Sheet Metal Unfolder |
|||
|Icon=Macro_Sheet_Metal_Unfolder.png |
|||
|Description=The macro can unfold simple sheet-metal-parts. The parts must have everywhere the same material thickness as typical for sheet-metal-parts. |
|||
|Author=ulrich1a |
|||
|Version=17.0 |
|||
|Date=2018-08-14 |
|||
|FCVersion=17.0 |
|||
|Download=[https://www.freecadweb.org/wiki/images/1/16/Macro_Sheet_Metal_Unfolder.png ToolBar Icon] |
|||
}} |
|||
<!--T:2--> |
==Description== <!--T:2--> |
||
The macro can unfold simple sheet-metal-parts. The parts must have everywhere the same material thickness as typical for sheet-metal-parts. See example: |
The macro can unfold simple sheet-metal-parts. The parts must have everywhere the same material thickness as typical for sheet-metal-parts. See example: |
||
</translate> |
</translate> |
||
{{Codeextralink|https://raw.githubusercontent.com/ulrich1a/sheet_ufo/master/sheet_ufo.py}} |
|||
[[File:Unfolding test2.png]] |
[[File:Unfolding test2.png]] |
||
<translate> |
<translate> |
||
== Usage == <!--T:3--> |
== Usage == <!--T:3--> |
||
Select a flat face from your part in the 3D-view. Start the macro. |
Select a flat face from your part in the 3D-view. Start the macro. |
||
Line 17: | Line 30: | ||
<!--T:5--> |
<!--T:5--> |
||
The macro can be thought of as a companion to the AddWall tool from JMG published here: http://linuxforanengineer.blogspot.com.es/2014/12/freecad-sheet-metal-tool-add-wall.html |
The macro can be thought of as a companion to the AddWall tool from JMG published here: [http://linuxforanengineer.blogspot.com.es/2014/12/freecad-sheet-metal-tool-add-wall.html FreeCAD: Sheet metal tool "Add Wall"] |
||
==Link== <!--T:7--> |
==Link== <!--T:7--> |
||
Line 23: | Line 36: | ||
<!--T:8--> |
<!--T:8--> |
||
The forum discussion [http://forum.freecadweb.org/viewtopic.php?f=24&t=8789 Sheet metal: add wall tool] |
The forum discussion [http://forum.freecadweb.org/viewtopic.php?f=24&t=8789 Sheet metal: add wall tool] |
||
<!--T:11--> |
|||
An updated version is available at: [https://github.com/ulrich1a/sheet_ufo https://github.com/ulrich1a/sheet_ufo] |
|||
==Script== <!--T:6--> |
==Script== <!--T:6--> |
||
The version below is revision 12 of sheet_ufo. |
|||
</translate> |
</translate> |
||
ToolBar Icon [[Image:Macro_Sheet_Metal_Unfolder.png]] |
|||
{{Code|code= |
|||
'''Sheet Metal Unfolder.py''' |
|||
{{MacroCode|code= |
|||
#!/usr/bin/env python |
#!/usr/bin/env python |
||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 34: | Line 54: | ||
# sheet_ufo.py |
# sheet_ufo.py |
||
# |
# |
||
# Copyright 2014 Ulrich Brammer < |
# Copyright 2014 Ulrich Brammer <ulrich@Pauline> |
||
# |
# |
||
# This program is free software; you can redistribute it and/or modify |
# This program is free software; you can redistribute it and/or modify |
||
Line 53: | Line 73: | ||
# |
# |
||
# Refactored version December 2015 |
|||
# Clear division of tasks between analysis and folding |
|||
# To do: |
|||
# change code to handle face indexes in the node instead of faces |
|||
# sheet_ufo16.py |
|||
# Die Weiterreichung eines schon geschnittenen Seitenfaces macht Probleme. |
|||
# Die Seitenfaces passen hinterher nicht mehr mit den Hauptflächen zusammen. |
|||
# Geänderter Ansatz: lasse die Seitenflächen in der Suchliste und |
|||
# schneide jeweils nur den benötigten Teil raus. |
|||
# Ich brauche jetzt eine Suchliste und eine Unfoldliste für die |
|||
# Face-Indices. |
|||
# To do: |
|||
# - handle a selected seam |
|||
# - handle not-circle-curves in bends |
|||
# - detect features like welded screws |
|||
# - make a view-provider for bends |
|||
# - make the k-factor selectable |
|||
# - upfold or unfold single bends |
|||
''' |
''' |
||
Line 70: | Line 115: | ||
from PySide import QtGui |
from PySide import QtGui |
||
from FreeCAD import Base |
from FreeCAD import Base |
||
import DraftVecUtils, DraftGeomUtils, math |
import DraftVecUtils, DraftGeomUtils, math, time |
||
# to do: |
|||
# - Put error numbers into the text |
|||
# - Put user help into more texts |
|||
unfold_error = { |
unfold_error = { |
||
# error codes for the tree-object |
# error codes for the tree-object |
||
Line 86: | Line 133: | ||
14: ('Analysis: the code can not handle edges without neighbor faces'), |
14: ('Analysis: the code can not handle edges without neighbor faces'), |
||
15: ('Analysis: the code needs a face at all sheet edges'), |
15: ('Analysis: the code needs a face at all sheet edges'), |
||
16: ('Analysis: '), |
16: ('Analysis: did not find startangle of bend, please post failing sample for analysis'), |
||
# error codes for the unfolding |
# error codes for the unfolding |
||
20: ('Unfold: section wire with less than 4 edges'), |
20: ('Unfold: section wire with less than 4 edges'), |
||
Line 103: | Line 150: | ||
# compares two vertices |
# compares two vertices |
||
return (round(vert1.X - vert2.X,p)==0 and round(vert1.Y - vert2.Y,p)==0 and round(vert1.Z - vert2.Z,p)==0) |
return (round(vert1.X - vert2.X,p)==0 and round(vert1.Y - vert2.Y,p)==0 and round(vert1.Z - vert2.Z,p)==0) |
||
def equal_vector(vec1, vec2, p=5): |
|||
# compares two vectors |
|||
return (round(vec1.x - vec2.x,p)==0 and round(vec1.y - vec2.y,p)==0 and round(vec1.z - vec2.z,p)==0) |
|||
def radial_vector(point, axis_pnt, axis): |
def radial_vector(point, axis_pnt, axis): |
||
Line 121: | Line 172: | ||
class Simple_node(object): |
class Simple_node(object): |
||
''' This class defines the nodes of a tree, that is the result of |
|||
the analysis of a sheet-metal-part. |
|||
Each flat or bend part of the metal-sheet gets a node in the tree. |
|||
The indexes are the number of the face in the original part. |
|||
''' |
|||
def __init__(self, f_idx=None, Parent_node= None, Parent_edge = None): |
def __init__(self, f_idx=None, Parent_node= None, Parent_edge = None): |
||
self.idx = f_idx # index of the "top-face" |
self.idx = f_idx # index of the "top-face" |
||
self.c_face_idx = None # face index to the opposite face of the sheet |
self.c_face_idx = None # face index to the opposite face of the sheet (counter-face) |
||
self.node_type = None # 'Flat' or 'Bend' |
self.node_type = None # 'Flat' or 'Bend' |
||
self.p_node = Parent_node # Parent node |
self.p_node = Parent_node # Parent node |
||
self.p_edge = Parent_edge # the connecting edge to the parent node |
self.p_edge = Parent_edge # the connecting edge to the parent node |
||
self.child_list = [] # List of child-nodes |
self.child_list = [] # List of child-nodes = link to tree structure |
||
self.child_idx_lists = [] # List of lists with child_idx and child_edge |
|||
# need also a list of indices of child faces |
|||
self.sheet_edges = [] # List of edges without child-face |
self.sheet_edges = [] # List of edges without child-face |
||
self.axis = None |
self.axis = None |
||
self.facePosi = None |
|||
self.bendCenter = None |
|||
self.distCenter = None |
|||
# self.axis for 'Flat'-face: vector pointing from the surface into the metal |
# self.axis for 'Flat'-face: vector pointing from the surface into the metal |
||
self.bend_dir = None # bend direction values: "up" or "down" |
self.bend_dir = None # bend direction values: "up" or "down" |
||
Line 137: | Line 198: | ||
self.analysis_ok = True # indicator if something went wrong with the analysis of the face |
self.analysis_ok = True # indicator if something went wrong with the analysis of the face |
||
self.error_code = None # index to unfold_error dictionary |
self.error_code = None # index to unfold_error dictionary |
||
# here the new features of the nodes: |
|||
self.nfIndexes = [] # list of all face-indexes of a node (flat and bend: folded state) |
|||
self.seam_edges = [] # list with edges to seams |
|||
# bend faces are needed for movement simulation at single other bends. |
|||
# otherwise unfolded faces are recreated from self.b_edges |
|||
self.node_flattened_faces = [] # faces of a flattened bend node. |
|||
self.actual_angle = None # state of angle in refolded sheet metal part |
|||
self.p_wire = None # wire common with parent node, used for bend node |
|||
self.c_wire = None # wire common with child node, used for bend node |
|||
self.b_edges = [] # list of edges in a bend node, that needs to be recalculated, at unfolding |
|||
def get_Face_idx(self): |
def get_Face_idx(self): |
||
Line 150: | Line 221: | ||
self.cFaceTol = 0.002 # tolerance to detect counter-face vertices |
self.cFaceTol = 0.002 # tolerance to detect counter-face vertices |
||
# this high tolerance was needed for more real parts |
# this high tolerance was needed for more real parts |
||
self.root = None |
self.root = None # make_new_face_node adds the root node if parent_node == None |
||
self.__Shape = TheShape.copy() |
self.__Shape = TheShape.copy() |
||
self.error_code = None |
self.error_code = None |
||
Line 163: | Line 234: | ||
# List of indices to the shape.Faces. The list is used a lot for face searches. |
# List of indices to the shape.Faces. The list is used a lot for face searches. |
||
# Some faces will be cut and the new ones added to the list. |
|||
self.index_list =[] |
|||
# So a list of faces independent of the shape is needed. |
|||
self.f_list = [] #self.__Shape.Faces.copy() does not work |
|||
self.index_list =[] |
|||
self.index_unfold_list = [] # indexes needed for unfolding |
|||
for i in range(len (self.__Shape.Faces)): |
for i in range(len (self.__Shape.Faces)): |
||
#for i in range(len (self.f_list)): |
|||
# if i<>(f_idx): |
# if i<>(f_idx): |
||
self.index_list.append(i) |
self.index_list.append(i) |
||
self.index_unfold_list.append(i) |
|||
self.f_list.append(self.__Shape.Faces[i]) |
|||
#print self.index_list |
#print self.index_list |
||
self.max_f_idx = len(self.f_list) # need this value to make correct indices to new faces |
|||
theVol = self.__Shape.Volume |
theVol = self.__Shape.Volume |
||
Line 256: | Line 335: | ||
def get_node_faces(self, theNode, wires_e_lists): |
|||
''' This function searches for all faces making up the node, except |
|||
of the top and bottom face, which are already there. |
|||
wires_e_list is the list of wires lists of the top face without the parent-edge |
|||
theNode: the actual node to be filled with data. |
|||
''' |
|||
# How to begin? |
|||
# searching for all faces, that have two vertices in common with |
|||
# an edge from the list should give the sheet edge. |
|||
# But we also need to look at the sheet edge, in order to not claim |
|||
# faces from the next node! |
|||
# Then we have to treat thoses faces, that belongs to more than one |
|||
# node. Those faces needs to be cut and the face list needs to be updated. |
|||
# look also at the number of wires of the top face. More wires will |
|||
# indicate a hole or a feature. |
|||
print " When will this be called" |
|||
found_indices = [] |
|||
# A search strategy for faces based on the wires_e_lists is needed. |
|||
# |
|||
for theWire in wires_e_lists: |
|||
for theEdge in theWire: |
|||
analyVert = theEdge.Vertexes[0] |
|||
search_list = [] |
|||
for x in self.index_list: |
|||
search_list.append(x) |
|||
for i in search_list: |
|||
for lookVert in self.f_list[i].Vertexes: |
|||
if equal_vertex(lookVert, analyVert): |
|||
if len(theEdge.Vertexes) == 1: # Edge is a circle |
|||
if not self.is_sheet_edge_face(theEdge, theNode): |
|||
found_indices.append(i) # found a node face |
|||
theNode.child_idx_lists.append([i,theEdge]) |
|||
#self.index_list.remove(i) # remove this face from the index_list |
|||
#Part.show(self.f_list[i]) |
|||
else: |
|||
nextVert = theEdge.Vertexes[1] |
|||
for looknextVert in self.f_list[i].Vertexes: |
|||
if equal_vertex(looknextVert, nextVert): |
|||
if not self.is_sheet_edge_face(theEdge, theNode): |
|||
found_indices.append(i) # found a node face |
|||
theNode.child_idx_lists.append([i,theEdge]) |
|||
#self.index_list.remove(i) # remove this face from the index_list |
|||
#Part.show(self.f_list[i]) |
|||
print "found_indices: ", found_indices |
|||
def |
def is_sheet_edge_face(self, ise_edge, tree_node): # ise_edge: IsSheetEdge_edge |
||
# |
# idea: look at properties of neighbor face |
||
# look at edges with distance of sheet-thickness. |
|||
# if found and surface == cylinder, check if it could be a bend-node. |
|||
# look at number of edges: |
|||
# A face with 3 edges is at the sheet edge Cylinder-face or triangle (oh no!) |
|||
# need to look also at surface! |
|||
# A sheet edge face with more as 4 edges, is common to more than 1 node. |
|||
# get the face which has a common edge with ise_edge |
|||
the_index = None |
|||
has_sheet_distance_vertex = False |
|||
for i in self.index_list: |
|||
for sf_edge in self.f_list[i].Edges: |
|||
if sf_edge.isSame(ise_edge): |
|||
the_index = i |
|||
break |
|||
if the_index <> None: |
|||
break |
|||
# Simple strategy applied: look if the connecting face has vertexes |
|||
# with sheet-thickness distance to the top face. |
|||
# fix me: this will fail with sharpened sheet edges with two faces |
|||
# between top and bottom. |
|||
if the_index <> None: |
|||
distVerts = 0 |
|||
vertList = [] |
|||
F_type = str(self.f_list[tree_node.idx].Surface) |
|||
# now we need to search for vertexes with sheet_thickness_distance |
|||
#if F_type == "<Plane object>": |
|||
for F_vert in self.f_list[i].Vertexes: |
|||
if self.isVertOpposite(F_vert, tree_node): |
|||
has_sheet_distance_vertex = True |
|||
if len(self.f_list[i].Edges)<5: |
|||
tree_node.nfIndexes.append(i) |
|||
self.index_list.remove(i) |
|||
#Part.show(self.f_list[i]) |
|||
else: |
|||
# need to cut the face at the ends of ise_edge |
|||
self.divideEdgeFace(i, ise_edge, F_vert, tree_node) |
|||
break |
|||
else: |
|||
tree_node.analysis_ok = False |
|||
tree_node.error_code = 15 # Analysis: the code needs a face at all sheet edges |
|||
self.error_code = 15 |
|||
self.failed_face_idx = tree_node.idx |
|||
Part.show(self.f_list[tree_node.idx]) |
|||
return has_sheet_distance_vertex |
|||
def isVertOpposite(self, theVert, theNode): |
|||
F_type = str(self.f_list[theNode.idx].Surface) |
|||
vF_vert = Base.Vector(theVert.X, theVert.Y, theVert.Z) |
|||
if F_type == "<Plane object>": |
|||
distFailure = vF_vert.distanceToPlane (theNode.facePosi, theNode.axis) - self.__thickness |
|||
if F_type == "<Cylinder object>": |
|||
distFailure = vF_vert.distanceToLine (theNode.bendCenter, theNode.axis) - theNode.distCenter |
|||
# print "counter face distance: ", dist_v + self.__thickness |
|||
if (distFailure < self.cFaceTol) and (distFailure > -self.cFaceTol): |
|||
return True |
|||
else: |
|||
return False |
|||
def divideEdgeFace(self, fIdx, ise_edge, F_vert, tree_node): |
|||
print "Sheet edge face has more than 4 edges!" |
|||
# first find out where the Sheet edge face has no edge to the opposite side of the sheet |
|||
# There is a need to cut the face. |
|||
# make a cut-tool perpendicular to the ise_edge |
|||
# cut the face and select the good one to add to the node |
|||
# make another cut, in order to add the residual face(s) to the face list. |
|||
# Search edges in the face with a vertex common with ise_edge |
|||
F_type = str(self.f_list[tree_node.idx].Surface) |
|||
needCut0 = True |
|||
firstCutFaceIdx = None |
|||
for sEdge in self.f_list[fIdx].Edges: |
|||
if equal_vertex(ise_edge.Vertexes[0], sEdge.Vertexes[0]) and \ |
|||
self.isVertOpposite(sEdge.Vertexes[1], tree_node): |
|||
needCut0 = False |
|||
theEdge = sEdge |
|||
if equal_vertex(ise_edge.Vertexes[0], sEdge.Vertexes[1]) and \ |
|||
self.isVertOpposite(sEdge.Vertexes[0], tree_node): |
|||
needCut0 = False |
|||
theEdge = sEdge |
|||
if needCut0: |
|||
#print "need Cut at 0 with fIdx: ", fIdx |
|||
nFace = self.cutEdgeFace(0, fIdx, ise_edge, tree_node) |
|||
tree_node.nfIndexes.append(self.max_f_idx) |
|||
self.f_list.append(nFace) |
|||
firstCutFaceIdx = self.max_f_idx |
|||
self.max_f_idx += 1 |
|||
#self.f_list.append(rFace) |
|||
#self.index_list.append(self.max_f_idx) |
|||
#self.max_f_idx += 1 |
|||
#self.index_list.remove(fIdx) |
|||
#Part.show(nFace) |
|||
#else: |
|||
# Part.show(theEdge) |
|||
needCut1 = True |
|||
for sEdge in self.f_list[fIdx].Edges: |
|||
if equal_vertex(ise_edge.Vertexes[1], sEdge.Vertexes[0]): |
|||
if self.isVertOpposite(sEdge.Vertexes[1], tree_node): |
|||
needCut1 = False |
|||
theEdge = sEdge |
|||
if equal_vertex(ise_edge.Vertexes[1], sEdge.Vertexes[1]): |
|||
if self.isVertOpposite(sEdge.Vertexes[0], tree_node): |
|||
needCut1 = False |
|||
theEdge = sEdge |
|||
if needCut1: |
|||
if needCut0: |
|||
fIdx = firstCutFaceIdx |
|||
tree_node.nfIndexes.remove(fIdx) |
|||
#print "need Cut at 1 with fIdx: ", fIdx |
|||
nFace = self.cutEdgeFace(1, fIdx, ise_edge, tree_node) |
|||
tree_node.nfIndexes.append(self.max_f_idx) |
|||
self.f_list.append(nFace) |
|||
firstCutFaceIdx = self.max_f_idx |
|||
self.max_f_idx += 1 |
|||
#self.f_list.append(rFace) |
|||
#self.index_list.append(self.max_f_idx) |
|||
#self.max_f_idx += 1 |
|||
#if not needCut0: |
|||
# self.index_list.remove(fIdx) |
|||
#Part.show(nFace) |
|||
#else: |
|||
# Part.show(theEdge) |
|||
def cutEdgeFace(self, eIdx, fIdx, theEdge, theNode): |
|||
''' This function cuts a face in two pieces. |
|||
one piece is connected to the node. The residual pieces is given |
|||
for assignment to other nodes. |
|||
The function returns both pieces of the original face. |
|||
''' |
|||
# print "now the face cutter" |
|||
if eIdx == 0: |
|||
otherIdx = 1 |
|||
else: |
|||
otherIdx = 0 |
|||
origin = theEdge.Vertexes[eIdx].Point |
|||
F_type = str(self.f_list[theNode.idx].Surface) |
|||
if F_type == "<Plane object>": |
|||
tan_vec = theEdge.Vertexes[eIdx].Point - theEdge.Vertexes[otherIdx].Point |
|||
#o_thick = Base.Vector(o_vec.x, o_vec.y, o_vec.z) |
|||
tan_vec.normalize() |
|||
vec1 = Base.Vector(theNode.axis.x, theNode.axis.y, theNode.axis.z) # make a copy |
|||
crossVec = tan_vec.cross(vec1) |
|||
crossVec.multiply(3.0*self.__thickness) |
|||
vec1.multiply(self.__thickness) |
|||
# defining the points of the cutting plane: |
|||
Spnt1 = origin - theNode.axis - crossVec |
|||
Spnt2 = origin - theNode.axis + crossVec |
|||
Spnt3 = origin + theNode.axis + vec1 + crossVec |
|||
Spnt4 = origin + theNode.axis + vec1 - crossVec |
|||
if F_type == "<Cylinder object>": |
|||
ePar = theEdge.parameterAt(theEdge.Vertexes[eIdx]) |
|||
print "Idx: ", eIdx, " ePar: ", ePar |
|||
otherPar = theEdge.parameterAt(theEdge.Vertexes[otherIdx]) |
|||
tan_vec = theEdge.tangentAt(ePar) |
|||
if ePar < otherPar: |
|||
tan_vec.multiply(-1.0) |
|||
#tan_line = Part.makeLine(theEdge.Vertexes[eIdx].Point.add(tan_vec), theEdge.Vertexes[eIdx].Point) |
|||
#Part.show(tan_line) |
|||
edge_vec = theEdge.Vertexes[eIdx].copy().Point |
|||
radVector = radial_vector(edge_vec, theNode.bendCenter, theNode.axis) |
|||
if theNode.bend_dir == "down": |
|||
radVector.multiply(-1.0) |
|||
#rad_line = Part.makeLine(theEdge.Vertexes[eIdx].Point.add(radVector), theEdge.Vertexes[eIdx].Point) |
|||
#Part.show(rad_line) |
|||
crossVec = tan_vec.cross(radVector) |
|||
crossVec.multiply(3.0*self.__thickness) |
|||
vec1 = Base.Vector(radVector.x, radVector.y, radVector.z) # make a copy |
|||
vec1.multiply(self.__thickness) |
|||
# defining the points of the cutting plane: |
|||
Spnt1 = origin - radVector - crossVec |
|||
Spnt2 = origin - radVector + crossVec |
|||
Spnt3 = origin + radVector + vec1 + crossVec |
|||
Spnt4 = origin + radVector + vec1 - crossVec |
|||
Sedge1 = Part.makeLine(Spnt1,Spnt2) |
|||
Sedge2 = Part.makeLine(Spnt2,Spnt3) |
|||
Sedge3 = Part.makeLine(Spnt3,Spnt4) |
|||
Sedge4 = Part.makeLine(Spnt4,Spnt1) |
|||
Sw1 = Part.Wire([Sedge1, Sedge2, Sedge3, Sedge4]) |
|||
Sf1=Part.Face(Sw1) # |
|||
cut_solid = Sf1.extrude(tan_vec.multiply(5.0)) |
|||
#Part.show(cut_solid) |
|||
#cut_opposite = Sf1.extrude(tan_vec.multiply(-5.0)) |
|||
cutFaces_node = self.f_list[fIdx].cut(cut_solid) |
|||
for cFace in cutFaces_node.Faces: |
|||
for myVert in cFace.Vertexes: |
|||
if equal_vertex(theEdge.Vertexes[eIdx], myVert): |
|||
nodeFace = cFace |
|||
#print "The nodeFace" |
|||
#Part.show(nodeFace) |
|||
break |
|||
''' |
|||
cutFaces_residue = self.f_list[fIdx].cut(cut_opposite) |
|||
for cFace in cutFaces_residue.Faces: |
|||
for myVert in cFace.Vertexes: |
|||
if equal_vertex(theEdge.Vertexes[eIdx], myVert): |
|||
residueFace = cFace |
|||
#print "the residueFace" |
|||
#Part.show(residueFace) |
|||
break |
|||
''' |
|||
return nodeFace #, residueFace |
|||
def is_sheet_edge3(self, ise_edge, tree_node): # ise_edge: IsSheetEdge_edge |
|||
# idea: look at properties of neighbor face |
|||
# look at edges with distance of sheet-thickness. |
|||
# if found and surface == cylinder, check if it could be a bend-node. |
|||
# look at number of edges: |
|||
# A face with 3 edges is at the sheet edge Cylinder-face or triangle (oh no!) |
|||
# need to look also at surface! |
|||
# A sheet edge face with more as 4 edges, is common to more than 1 node. |
|||
# get the face which has a common edge with ise_edge |
|||
the_index = None |
|||
has_sheet_distance_vertex = False |
|||
for i in self.index_list: |
|||
for sf_edge in self.f_list[i].Edges: |
|||
if sf_edge.isSame(ise_edge): |
|||
the_index = i |
|||
break |
|||
if the_index <> None: |
|||
break |
|||
if the_index <> None: |
|||
distVerts = 0 |
|||
vertList = [] |
|||
F_type = str(self.f_list[tree_node.idx].Surface) |
|||
# now we need to search for vertexes with sheet_thickness_distance |
|||
#if F_type == "<Plane object>": |
|||
for F_vert in self.f_list[i].Vertexes: |
|||
vF_vert = Base.Vector(F_vert.X, F_vert.Y, F_vert.Z) |
|||
if F_type == "<Plane object>": |
|||
distFailure = vF_vert.distanceToPlane (tree_node.facePosi, tree_node.axis) - self.__thickness |
|||
if F_type == "<Cylinder object>": |
|||
distFailure = vF_vert.distanceToLine (tree_node.bendCenter, tree_node.axis) - tree_node.distCenter |
|||
# print "counter face distance: ", dist_v + self.__thickness |
|||
if (distFailure < self.cFaceTol) and (distFailure > -self.cFaceTol): |
|||
has_sheet_distance_vertex = True |
|||
break |
|||
else: |
|||
tree_node.analysis_ok = False |
|||
tree_node.error_code = 15 # Analysis: the code needs a face at all sheet edges |
|||
self.error_code = 15 |
|||
self.failed_face_idx = tree_node.idx |
|||
Part.show(self.f_list[tree_node.idx]) |
|||
return has_sheet_distance_vertex |
|||
def make_new_face_node(self, face_idx, P_node, P_edge, wires_e_lists): |
|||
# e_list: list of edges of the top face of a node without the parent-edge (P_edge) |
|||
# analyze the face and get type of face ("Flat" or "Bend") |
|||
# search the counter face, get axis of Face |
|||
# In case of "Bend" get angle, k_factor and trans_length |
|||
# put the node into the tree |
# put the node into the tree |
||
newNode = Simple_node(face_idx, P_node, P_edge) |
newNode = Simple_node(face_idx, P_node, P_edge) |
||
Line 269: | Line 682: | ||
self.index_list.remove(face_idx) |
self.index_list.remove(face_idx) |
||
# This means, it could also not be found as neighbor face anymore. |
# This means, it could also not be found as neighbor face anymore. |
||
#newNode.node_faces.append(self.f_list[face_idx].copy()) |
|||
newNode.nfIndexes.append(face_idx) |
|||
such_list = [] |
such_list = [] |
||
for k in |
for k in self.index_list: |
||
such_list.append( |
such_list.append(k) |
||
if F_type == "<Plane object>": |
if F_type == "<Plane object>": |
||
Line 279: | Line 694: | ||
s_Posi = self.__Shape.Faces[face_idx].Surface.Position |
s_Posi = self.__Shape.Faces[face_idx].Surface.Position |
||
newNode.facePosi = s_Posi |
|||
s_Ori = self.__Shape.Faces[face_idx].Orientation |
s_Ori = self.__Shape.Faces[face_idx].Orientation |
||
s_Axis = self.__Shape.Faces[face_idx].Surface.Axis |
s_Axis = self.__Shape.Faces[face_idx].Surface.Axis |
||
Line 297: | Line 713: | ||
# search for the counter face |
# search for the counter face |
||
for i in |
for i in such_list: |
||
counter_found = True |
counter_found = True |
||
for F_vert in self. |
for F_vert in self.f_list[i].Vertexes: |
||
vF_vert = Base.Vector(F_vert.X, F_vert.Y, F_vert.Z) |
vF_vert = Base.Vector(F_vert.X, F_vert.Y, F_vert.Z) |
||
dist_v = vF_vert.distanceToPlane (s_Posi, ext_Vec) - self.__thickness |
dist_v = vF_vert.distanceToPlane (s_Posi, ext_Vec) - self.__thickness |
||
Line 309: | Line 725: | ||
# nead a mean point of the face to avoid false counter faces |
# nead a mean point of the face to avoid false counter faces |
||
counterMiddle = Base.Vector(0.0,0.0,0.0) # calculating a mean vector |
counterMiddle = Base.Vector(0.0,0.0,0.0) # calculating a mean vector |
||
for Vvec in self.__Shape.Faces |
for Vvec in self.__Shape.Faces[i].OuterWire.Vertexes: |
||
counterMiddle = counterMiddle.add(Vvec.Point) |
counterMiddle = counterMiddle.add(Vvec.Point) |
||
counterMiddle = counterMiddle.multiply(1.0/len(self.__Shape.Faces |
counterMiddle = counterMiddle.multiply(1.0/len(self.__Shape.Faces[i].OuterWire.Vertexes)) |
||
distVector = counterMiddle.sub(faceMiddle) |
distVector = counterMiddle.sub(faceMiddle) |
||
Line 317: | Line 733: | ||
if counterDistance < 3*self.__thickness: |
if counterDistance < 3*self.__thickness: |
||
print "found counter-face", |
print "found counter-face", i + 1 |
||
newNode.c_face_idx = |
newNode.c_face_idx = i |
||
self.index_list.remove( |
self.index_list.remove(i) |
||
newNode.nfIndexes.append(i) |
|||
# Part.show(self.__Shape.Faces[newNode.c_face_idx]) |
# Part.show(self.__Shape.Faces[newNode.c_face_idx]) |
||
else: |
else: |
||
Line 335: | Line 753: | ||
s_Axis = self.__Shape.Faces[face_idx].Surface.Axis |
s_Axis = self.__Shape.Faces[face_idx].Surface.Axis |
||
newNode.axis = s_Axis |
newNode.axis = s_Axis |
||
newNode.bendCenter = s_Center |
|||
edge_vec = P_edge.Vertexes[0].copy().Point |
edge_vec = P_edge.Vertexes[0].copy().Point |
||
print "edge_vec: ", str(edge_vec) |
|||
if P_node.node_type == 'Flat': |
if P_node.node_type == 'Flat': |
||
dist_c = edge_vec.distanceToPlane (s_Center, P_node.axis) |
dist_c = edge_vec.distanceToPlane (s_Center, P_node.axis) # distance to center |
||
else: |
else: |
||
P_face = self.__Shape.Faces[P_node.idx] |
P_face = self.__Shape.Faces[P_node.idx] |
||
Line 346: | Line 766: | ||
else: |
else: |
||
dist_c = edge_vec.distanceToPlane (s_Center, radVector) |
dist_c = edge_vec.distanceToPlane (s_Center, radVector) |
||
newNode.analysis_ok = False |
|||
newNode.error_code = 11 #Analysis: double bends not implemented |
|||
self.error_code = 11 |
|||
self.failed_face_idx = face_idx |
|||
print "Error: Bend directly following a bend not implemented!" |
|||
''' |
|||
if dist_c < 0.0: |
if dist_c < 0.0: |
||
newNode.bend_dir = "down" |
newNode.bend_dir = "down" |
||
Line 359: | Line 773: | ||
newNode.bend_dir = "up" |
newNode.bend_dir = "up" |
||
thick_test = self.__Shape.Faces[face_idx].Surface.Radius + self.__thickness |
thick_test = self.__Shape.Faces[face_idx].Surface.Radius + self.__thickness |
||
newNode.distCenter = thick_test |
|||
# print "Face idx: ", face_idx, " bend_dir: ", newNode.bend_dir |
# print "Face idx: ", face_idx, " bend_dir: ", newNode.bend_dir |
||
print "Face", face_idx+1, " Type: ", newNode.node_type, " bend_dir: ", newNode.bend_dir |
print "Face", face_idx+1, " Type: ", newNode.node_type, " bend_dir: ", newNode.bend_dir |
||
Line 389: | Line 804: | ||
self.failed_face_idx = face_idx |
self.failed_face_idx = face_idx |
||
#Start to investigate the angles at self.__Shape.Faces[face_idx].ParameterRange[0] |
|||
# Calculate the signed rotation angle |
|||
angle_0 = self.__Shape.Faces[face_idx].ParameterRange[0] |
|||
vec1 = radial_vector(edge_vec, s_Center, s_Axis) |
|||
angle_1 = self.__Shape.Faces[face_idx].ParameterRange[1] |
|||
vec2 = radial_vector(c_edge.Vertexes[0].Point, s_Center, s_Axis) |
|||
length_0 = self.__Shape.Faces[face_idx].ParameterRange[2] |
|||
#newNode.bend_angle = DraftVecUtils.angle(vec2, vec1, s_Axis) |
|||
length_1 = self.__Shape.Faces[face_idx].ParameterRange[3] |
|||
draftAngle = DraftVecUtils.angle(vec2, vec1, s_Axis) |
|||
# idea: identify the angle at edge_vec = P_edge.Vertexes[0].copy().Point |
|||
# This will be = angle_start |
|||
# identify rotSign from angle_end minus angle_start |
|||
# The tangentvector will be in direction of position at angle_sta + rotSign*90° |
|||
# calculate the tan_vec from valueAt |
|||
parPos00 = self.__Shape.Faces[face_idx].valueAt(angle_0,length_0) |
|||
parPos01 = self.__Shape.Faces[face_idx].valueAt(angle_0,length_1) |
|||
parPos10 = self.__Shape.Faces[face_idx].valueAt(angle_1,length_0) |
|||
parPos11 = self.__Shape.Faces[face_idx].valueAt(angle_1,length_1) |
|||
if equal_vector(edge_vec, parPos00): |
|||
newNode.bend_angle = self.__Shape.Faces[face_idx].ParameterRange[1] \ |
|||
print "got case 00" |
|||
-self.__Shape.Faces[face_idx].ParameterRange[0] |
|||
angle_start = angle_0 |
|||
if (newNode.bend_angle < math.pi) and (newNode.bend_angle > -math.pi): |
|||
angle_end = angle_1 |
|||
if draftAngle * newNode.bend_angle < 0.0: |
|||
len_start = length_0 |
|||
else: |
else: |
||
if |
if equal_vector(edge_vec, parPos01): |
||
print "got case 01" |
|||
angle_start = angle_0 |
|||
angle_end = angle_1 |
|||
print "Face_idx ", face_idx, " bend_angle ", math.degrees(newNode.bend_angle) |
|||
len_start = length_1 |
|||
else: |
|||
if equal_vector(edge_vec, parPos10): |
|||
# Compare sign of the angle between vector vec3 and vec1 with the sign of the bend_angle |
|||
print "got case 10" |
|||
vec3 = radial_vector(edge_vec + tan_vec, s_Center, s_Axis) |
|||
angle_start = angle_1 |
|||
test_angle = DraftVecUtils.angle(vec3, vec1, s_Axis) |
|||
angle_end = angle_0 |
|||
len_start = length_0 |
|||
#vecTest = (edge_vec + tan_vec) |
|||
else: |
|||
#vec1_test = Part.makeLine(edge_vec, edge_vec.add(vec1)) |
|||
if equal_vector(edge_vec, parPos11): |
|||
#Part.show(vec1_test) |
|||
print "got case 11" |
|||
#angle_test = Part.makeLine(vecTest, vecTest.add(vec3)) |
|||
angle_start = angle_1 |
|||
#Part.show(angle_test) |
|||
angle_end = angle_0 |
|||
len_start = length_1 |
|||
if (test_angle * newNode.bend_angle) < 0.0: |
|||
else: |
|||
newNode.analysis_ok = False |
|||
newNode.error_code = 16 # Analysis: did not find startangle of bend |
|||
self.error_code = 16 |
|||
self.failed_face_idx = face_idx |
|||
print "did not found start angle, to do to fix" |
|||
newNode.bend_angle = angle_end - angle_start |
|||
if newNode.bend_angle < 0.0: |
|||
angle_tan = angle_start - math.pi/2.0 |
|||
# newNode.bend_angle = -newNode.bend_angle |
|||
else: |
else: |
||
angle_tan = angle_start + math.pi/2.0 |
|||
tanPos = self.__Shape.Faces[face_idx].valueAt(angle_tan,len_start) |
|||
tan_vec = radial_vector(tanPos, s_Center, s_Axis) |
|||
newNode.tan_vec = tan_vec |
|||
first_vec = radial_vector(edge_vec, s_Center, s_Axis) |
|||
cross_vec = first_vec.cross(tan_vec) |
|||
triple_prod = cross_vec.dot(s_Axis) |
|||
print " the new bend_angle: ", math.degrees(newNode.bend_angle), "triple_prod: ", triple_prod |
|||
# testing showed, that the bend_angle has to be changed in sign |
|||
# at the following conditions. |
|||
if ((triple_prod > 0.0) and (newNode.bend_angle > 0.0)) or \ |
|||
((triple_prod < 0.0) and (newNode.bend_angle < 0.0)): |
|||
newNode.bend_angle = -newNode.bend_angle |
|||
print "minus bend_angle" |
|||
Line 435: | Line 883: | ||
if newNode._trans_length < 0.0: |
if newNode._trans_length < 0.0: |
||
newNode._trans_length = -newNode._trans_length |
newNode._trans_length = -newNode._trans_length |
||
# the _trans_length is always positive, due to correct tan_vec |
|||
tan_test = Part.makeLine(s_Center, s_Center.add(newNode.tan_vec + newNode.tan_vec + newNode.tan_vec)) |
|||
# Part.show(tan_test) |
|||
print "angle: ", math.degrees(newNode.bend_angle)," test_angle: ", math.degrees(test_angle), " trans: ", newNode._trans_length |
|||
Line 445: | Line 890: | ||
# Search the face at the opposite site of the sheet: |
# Search the face at the opposite site of the sheet: |
||
for i in range(len(such_list)): |
#for i in range(len(such_list)): |
||
for i in such_list: |
|||
counter_found = True |
counter_found = True |
||
for F_vert in self. |
for F_vert in self.f_list[i].Vertexes: |
||
vF_vert = Base.Vector(F_vert.X, F_vert.Y, F_vert.Z) |
vF_vert = Base.Vector(F_vert.X, F_vert.Y, F_vert.Z) |
||
dist_c = vF_vert.distanceToLine (s_Center, s_Axis) - thick_test |
dist_c = vF_vert.distanceToLine (s_Center, s_Axis) - thick_test |
||
Line 454: | Line 900: | ||
if counter_found: |
if counter_found: |
||
# calculate mean point of counter face |
# to do calculate mean point of counter face |
||
#print "found counter Face", such_list[i]+1 |
|||
newNode.c_face_idx = i |
|||
self.index_list.remove(i) |
|||
newNode.nfIndexes.append(i) |
|||
newNode.c_face_idx = such_list[i] |
|||
self.index_list.remove(such_list[i]) |
|||
# Part.show(self.__Shape.Faces[newNode.c_face_idx]) |
# Part.show(self.__Shape.Faces[newNode.c_face_idx]) |
||
break |
break |
||
Line 473: | Line 918: | ||
self.failed_face_idx = face_idx |
self.failed_face_idx = face_idx |
||
print "No counter-face Debugging Thickness: ", self.__thickness |
print "No counter-face Debugging Thickness: ", self.__thickness |
||
Part.show(self.__Shape.Faces[face_idx]) |
|||
# now we call the new code |
|||
self.get_node_faces(newNode, wires_e_lists) |
|||
#for nFace in newNode.nfIndexes: |
|||
# Part.show(nFace) |
|||
if P_node == None: |
if P_node == None: |
||
Line 480: | Line 932: | ||
return newNode |
return newNode |
||
def analyzeSectionEdges(self, edList, startPnt): |
|||
# search for a wire in edList starting at startPnt |
|||
startVert = Part.Vertex(startPnt) |
|||
wireList = [] |
|||
startEdge = None |
|||
# searching the startEdge |
|||
lastCon = None |
|||
for edge in edList: |
|||
if equal_vertex(edge.Vertexes[0], startVert, 3): |
|||
lastCon = 1 |
|||
firstCon = 0 |
|||
startEdge = edge |
|||
if len(edge.Vertexes) > 1: |
|||
if equal_vertex(edge.Vertexes[1], startVert, 3): |
|||
lastCon = 0 |
|||
firstCon = 1 |
|||
startEdge = edge |
|||
# fixme remove edges with only one Vertex from edList |
|||
if lastCon <> None: |
|||
break |
|||
if startEdge <> None: |
|||
wireList.append(startEdge) |
|||
edList.remove(startEdge) |
|||
# append connecting edges to wireList at point lastCon |
|||
compVert = startEdge.Vertexes[lastCon] |
|||
for i in range(len(edList)): |
|||
for nextEdge in edList: |
|||
if equal_vertex(nextEdge.Vertexes[0], compVert): |
|||
wireList.append(nextEdge) |
|||
edList.remove(nextEdge) |
|||
compVert = nextEdge.Vertexes[1] |
|||
break |
|||
if equal_vertex(nextEdge.Vertexes[1], compVert): |
|||
wireList.append(nextEdge) |
|||
edList.remove(nextEdge) |
|||
compVert = nextEdge.Vertexes[0] |
|||
break |
|||
# now look at the other side of startEdge |
|||
compVert = startEdge.Vertexes[firstCon] |
|||
for i in range(len(edList)): |
|||
for nextEdge in edList: |
|||
if equal_vertex(nextEdge.Vertexes[0], compVert): |
|||
wireList.insert(0,nextEdge) |
|||
edList.remove(nextEdge) |
|||
compVert = nextEdge.Vertexes[1] |
|||
break |
|||
if equal_vertex(nextEdge.Vertexes[1], compVert): |
|||
wireList.insert(0,nextEdge) |
|||
edList.remove(nextEdge) |
|||
compVert = nextEdge.Vertexes[0] |
|||
break |
|||
sectWire = Part.Wire(wireList) |
|||
return sectWire |
|||
def is_sheet_edge(self, ise_edge, tree_node): |
|||
# analyzes ise_edge is at the edge of the metal sheet |
|||
result = True |
|||
# fixme test only for one common edge |
|||
factor = self.__thickness/2.0 |
|||
sc = 1.4 # multiplicator for sec_circle |
|||
# fixme: Axis for Bend face is parallel to face, works only with normal Axis of Flat face. |
|||
lookAt = (ise_edge.FirstParameter + ise_edge.LastParameter)/2.0 |
|||
ise_Posi = ise_edge.valueAt(lookAt) |
|||
s_axis = ise_edge.tangentAt(lookAt) |
|||
#if tree_node.idx == 151: |
|||
# Part.show(Part.Vertex(ise_Posi)) |
|||
# print "ise_Posi: ", str(ise_Posi) |
|||
F_type = str(self.__Shape.Faces[tree_node.idx].Surface) |
|||
if F_type == "<Plane object>": |
|||
halfThick = Base.Vector(tree_node.axis.x*factor,tree_node.axis.y*factor,tree_node.axis.z*factor) |
|||
sec_circle = Part.makeCircle(self.__thickness*sc, ise_Posi+halfThick, s_axis) |
|||
testAxis = Base.Vector(tree_node.axis.x, tree_node.axis.y, tree_node.axis.z) |
|||
if F_type == "<Cylinder object>": |
|||
a_pnt = self.__Shape.Faces[tree_node.idx].Surface.Center |
|||
axi = self.__Shape.Faces[tree_node.idx].Surface.Axis |
|||
r_vec = radial_vector(ise_Posi, a_pnt, axi) |
|||
testAxis = Base.Vector(r_vec.x, r_vec.y, r_vec.z) |
|||
r_vec.multiply(self.__thickness/2.0) |
|||
# sec_circle = Part.makeCircle(self.__thickness*2.0, ise_Posi, s_axis) # fixme: make center of sec_circle at half the thickness of the sheet |
|||
if tree_node.bend_dir == "down": |
|||
thick_test = self.__Shape.Faces[tree_node.idx].Surface.Radius - self.__thickness |
|||
sec_circle = Part.makeCircle(self.__thickness*sc, ise_Posi-r_vec, s_axis) # fixme: make center of sec_circle at half the thickness of the sheet |
|||
else: |
|||
thick_test = self.__Shape.Faces[tree_node.idx].Surface.Radius + self.__thickness |
|||
sec_circle = Part.makeCircle(self.__thickness*sc, ise_Posi+r_vec, s_axis) # fixme: make center of sec_circle at half the thickness of the sheet |
|||
s_wire = Part.Wire(sec_circle) |
|||
sec_face = Part.Face(s_wire) |
|||
#if tree_node.idx == 151: |
|||
# Part.show(sec_face) |
|||
#lostShape = self.__Shape.copy() |
|||
# Part.show(lostShape) |
|||
#sect = sec_face.section(lostShape) |
|||
sect = sec_face.section(self.__Shape) |
|||
#if tree_node.idx == 151: |
|||
# Part.show(sect) |
|||
foundWire = self.analyzeSectionEdges(sect.Edges, ise_Posi) |
|||
if len(foundWire.Edges) == 0: |
|||
Part.show(sec_face) |
|||
tree_node.analysis_ok = False |
|||
tree_node.error_code = 10 # Analysis: zero wires in sheet edge analysis |
|||
self.error_code = 10 |
|||
self.failed_face_idx = tree_node.idx |
|||
if F_type == "<Cylinder object>": |
|||
# Part.show(sec_face) |
|||
print "cylinder radius: ", self.__Shape.Faces[tree_node.idx].Surface.Radius, "length/2: ", lookAt / math.pi*4.0 |
|||
print "Orientation: ", ise_edge.Orientation |
|||
if (len(sect.Edges) - len(foundWire.Edges)) <> 0: |
|||
if F_type == "<Plane object>": |
|||
if len(foundWire.Edges)> 2: |
|||
# need to test, if there are 2 vertices in thickness distance |
|||
vertCount = 0 |
|||
for verts in foundWire.Vertexes: |
|||
v_verts = verts.Point |
|||
dist_v = v_verts.distanceToPlane (ise_Posi, tree_node.axis) - self.__thickness |
|||
if (dist_v < self.cFaceTol) and (dist_v > -self.cFaceTol): |
|||
vertCount = vertCount + 1 |
|||
#print "got result ", dist_v, " ", ise_Posi, " ", verts.Point |
|||
#result = True |
|||
if vertCount > 1: |
|||
result = True |
|||
else: |
|||
result = False |
|||
else: |
|||
result = False |
|||
if F_type == "<Cylinder object>": |
|||
print "testing isSheetEdge edges in wire: ", len(foundWire.Edges) |
|||
if len(foundWire.Edges)> 2: |
|||
s_Center = self.__Shape.Faces[tree_node.idx].Surface.Center |
|||
vertCount = 0 |
|||
for verts in foundWire.Vertexes: |
|||
v_verts = verts.Point |
|||
dist_c = v_verts.distanceToLine (s_Center, tree_node.axis) - thick_test |
|||
if (dist_c < self.cFaceTol) and (dist_c > -self.cFaceTol): |
|||
print "got cyl result ", dist_c, " ", ise_Posi, " ", verts.Point |
|||
vertCount = vertCount + 1 |
|||
if vertCount > 1: |
|||
result = True |
|||
else: |
|||
result = False |
|||
else: |
|||
result = False |
|||
else: |
|||
result = True |
|||
print "only one wire in section!" |
|||
# testAxis |
|||
neighborIdx = self.search_face(ise_edge, tree_node) |
|||
nextF_type = str(self.__Shape.Faces[neighborIdx].Surface) |
|||
if nextF_type == "<Plane object>": |
|||
dotProd = testAxis.dot(self.__Shape.Faces[neighborIdx].Surface.Axis) |
|||
if dotProd < 0.0: |
|||
print " dotProd: ", dotProd |
|||
dotProd = -dotProd |
|||
if (dotProd <1.001) and (dotProd > 0.999): |
|||
result = False |
|||
return result |
|||
''' |
|||
Blechkante in der Mitte mit Sektion Radius ca. 2 x Blechdicke quer schneiden: |
|||
nur eine geschlossene Kurve ist eine Blechkante. |
|||
Tangenten mit TangentAt ermitteln. |
|||
''' |
|||
def is_sheet_edge2(self, ise_edge, tree_node): |
|||
# another strategy to identify the sheet edge: |
|||
# get the face which has a common edge with ise_edge |
|||
# look if this face has a common edge with the counter-face of tree_node |
|||
# does not work in case of overlapping up- and down-faces. |
|||
the_index = None |
|||
connection_to_counter_face = False |
|||
for i in self.index_list: |
|||
for sf_edge in self.__Shape.Faces[i].Edges: |
|||
if sf_edge.isSame(ise_edge): |
|||
the_index = i |
|||
break |
|||
if the_index <> None: |
|||
break |
|||
if the_index == None: |
|||
tree_node.analysis_ok = False |
|||
tree_node.error_code = 15 # Analysis: the code needs a face at all sheet edges |
|||
self.error_code = 15 |
|||
self.failed_face_idx = tree_node.idx |
|||
Part.show(self.__Shape.Faces[tree_node.idx]) |
|||
else: |
|||
for c_edge in self.__Shape.Faces[tree_node.c_face_idx].Edges: |
|||
for side_edge in self.__Shape.Faces[the_index].Edges: |
|||
if c_edge.isSame(side_edge): |
|||
connection_to_counter_face = True |
|||
break |
|||
return connection_to_counter_face |
|||
def search_face(self, sf_edge, the_node): |
def search_face(self, sf_edge, the_node): |
||
# search for the connecting face to |
# search for the connecting face to sf_edge in the faces of a node |
||
search_List = the_node.nfIndexes[:] |
|||
search_List.remove(the_node.idx) |
|||
the_index = None |
the_index = None |
||
for i in self.index_list: |
for i in search_List: #self.index_list: |
||
for n_edge in self. |
for n_edge in self.f_list[i].Edges: |
||
if sf_edge.isSame(n_edge): |
if sf_edge.isSame(n_edge): |
||
the_index = i |
the_index = i |
||
if the_index == None: |
#if the_index == None: |
||
the_node.analysis_ok = False # the code can not handle? edges without neighbor faces |
# the_node.analysis_ok = False # the code can not handle? edges without neighbor faces |
||
the_node.error_code = 14 # Analysis: the code can not handle? edges without neighbor faces |
# the_node.error_code = 14 # Analysis: the code can not handle? edges without neighbor faces |
||
self.error_code = 14 |
# self.error_code = 14 |
||
self.failed_face_idx = the_node.idx |
# self.failed_face_idx = the_node.idx |
||
return the_index |
return the_index |
||
Line 726: | Line 958: | ||
# For each relevant face a t_node is created and linked into the tree |
# For each relevant face a t_node is created and linked into the tree |
||
# the linking is done in the call of self.make_new_face_node |
# the linking is done in the call of self.make_new_face_node |
||
print "Bend_analysis Face", face_idx +1 , |
#print "Bend_analysis Face", face_idx +1 , |
||
# analysis_ok = True # not used anymore? |
# analysis_ok = True # not used anymore? |
||
edge_list = [] |
# edge_list = [] |
||
wires_edge_lists = [] |
|||
for n_edge in self.__Shape.Faces[face_idx].Edges: |
|||
wire_idx = -1 |
|||
if parent_edge: |
|||
for n_wire in self.f_list[face_idx].Wires: |
|||
if not parent_edge.isSame(n_edge): |
|||
wire_idx += 1 |
|||
edge_list.append(n_edge) |
|||
wires_edge_lists.append([]) |
|||
# |
|||
#for n_edge in self.__Shape.Faces[face_idx].Edges: |
|||
else: |
|||
for n_edge in n_wire.Edges: |
|||
if parent_edge: |
|||
if not parent_edge.isSame(n_edge): |
|||
#edge_list.append(n_edge) |
|||
wires_edge_lists[wire_idx].append(n_edge) |
|||
# |
|||
else: |
|||
#edge_list.append(n_edge) |
|||
wires_edge_lists[wire_idx].append(n_edge) |
|||
if parent_node: |
if parent_node: |
||
print " Parent Face", parent_node.idx + 1 |
print " Parent Face", parent_node.idx + 1 |
||
print "Die Liste: ", self.index_list |
|||
t_node = self.make_new_face_node(face_idx, parent_node, parent_edge) |
t_node = self.make_new_face_node(face_idx, parent_node, parent_edge, wires_edge_lists) |
||
# Need also the edge_list in the node! |
|||
print "Die Liste nach make_new_face_node: ", self.index_list |
|||
for n_edge in edge_list: |
|||
if self.is_sheet_edge(n_edge, t_node): |
|||
# in the new code, only the list of child faces will be analyzed. |
|||
#if self.is_sheet_edge2(n_edge, t_node): |
|||
removalList = [] |
|||
t_node.sheet_edges.append(n_edge) |
|||
for child_info in t_node.child_idx_lists: |
|||
if child_info[0] in self.index_list: |
|||
print "child in List: ", child_info[0] |
|||
self.Bend_analysis(child_info[0], t_node, child_info[1]) |
|||
else: |
else: |
||
print "remove child from List: ", child_info[0] |
|||
next_face = self.search_face(n_edge, t_node) |
|||
t_node.seam_edges.append(child_info[1]) # give Information to the node, that it has a seam. |
|||
print "node faces before: ", t_node.nfIndexes |
|||
if (next_face <> None) and (self.error_code == None): |
|||
self.makeSeamFace(child_info[1], t_node) |
|||
removalList.append(child_info) |
|||
if self.error_code <> None: |
|||
print "node faces with seam: ", t_node.nfIndexes |
|||
break |
|||
otherSeamNode = self.searchNode(child_info[0], self.root) |
|||
print "counterface on otherSeamNode: Face", otherSeamNode.c_face_idx+1 |
|||
self.makeSeamFace(child_info[1], otherSeamNode) |
|||
#t_node.analysis_ok = False # the code can not handle? edges without neighbor faces |
|||
#t_node.error_code = 14 # Analysis: the code can not handle? edges without neighbor faces |
|||
#self.error_code = 14 |
|||
#self.failed_face_idx = t_node.idx |
|||
#break |
|||
for seams in removalList: |
|||
t_node.child_idx_lists.remove(seams) |
|||
def searchNode(self, theIdx, sNode): |
|||
# search for a Node with theIdx in sNode.idx |
|||
print "my Idx: ", sNode.idx |
|||
if sNode.idx == theIdx: |
|||
return sNode |
|||
else: |
|||
result = None |
|||
childFaces = [] |
|||
for n_node in sNode.child_list: |
|||
childFaces.append(n_node.idx) |
|||
print "my children: ", childFaces |
|||
for n_node in sNode.child_list: |
|||
nextSearch = self.searchNode(theIdx, n_node) |
|||
if nextSearch <> None: |
|||
result = nextSearch |
|||
break |
|||
if result<>None: |
|||
print "this is the result: ", result.idx |
|||
else: |
|||
print "this is the result: ", None |
|||
return result |
|||
# suche bei mir. wenn ja liefere ab |
|||
# sonst sind Kinder da? |
|||
# Wenn Kinder vorhanden, frag solange Kinder bis gefunden |
|||
# oder kein Kind mehr da. |
|||
Line 761: | Line 1,045: | ||
def makeSectionWire(self, theEdge, W_node, Dir = 'up'): |
def makeSectionWire(self, theEdge, W_node, Dir = 'up'): |
||
print "mSW Face", W_node.idx +1 |
#print "mSW Face", W_node.idx +1 |
||
# makes a Section wire through the shape |
# makes a Section wire through the shape |
||
# The section wire is used to generate a new flat shell |
# The section wire is used to generate a new flat shell |
||
Line 770: | Line 1,054: | ||
o_thick.normalize().multiply(2.0 * self.__thickness) |
o_thick.normalize().multiply(2.0 * self.__thickness) |
||
s_Center = self. |
s_Center = self.f_list[W_node.idx].Surface.Center |
||
s_Axis = self. |
s_Axis = self.f_list[W_node.idx].Surface.Axis |
||
vec1 = radial_vector(origin, s_Center, s_Axis) |
vec1 = radial_vector(origin, s_Center, s_Axis) |
||
vec1.multiply(self.__thickness) |
vec1.multiply(self.__thickness) |
||
Line 810: | Line 1,094: | ||
end_idx = 0 |
end_idx = 0 |
||
# for i in self.index_list: |
|||
for i in |
for i in W_node.nfIndexes: |
||
singleEdge = Sf1.section(self. |
singleEdge = Sf1.section(self.f_list[i]) |
||
# Part.show(singleEdge) |
# Part.show(singleEdge) |
||
print "section edges: ", len(singleEdge.Edges) |
#print "section edges: ", len(singleEdge.Edges) |
||
for j in range(len(singleEdge.Edges)): |
for j in range(len(singleEdge.Edges)): |
||
if (equal_vertex(singleEdge.Edges[j].Vertexes[0], start_pnt)): |
if (equal_vertex(singleEdge.Edges[j].Vertexes[0], start_pnt)): |
||
Line 844: | Line 1,128: | ||
def generateBendShell(self, bend_node): |
def generateBendShell(self, bend_node): |
||
print "genBendShell Face", bend_node.idx +1 |
#print "genBendShell Face", bend_node.idx +1 |
||
# make new flat faces for the bend_node and return them |
# make new flat faces for the bend_node and return them |
||
# the k-Factor is already included in bend_node._trans_length |
# the k-Factor is already included in bend_node._trans_length |
||
Line 874: | Line 1,158: | ||
else: |
else: |
||
number_c_edges = 0 |
number_c_edges = 0 |
||
for s_edge in self. |
for s_edge in self.f_list[bend_node.idx].Edges: |
||
c_edg_found = True |
c_edg_found = True |
||
type_str = str(s_edge.Curve) |
type_str = str(s_edge.Curve) |
||
if type_str.find('Line') == -1: |
if type_str.find('Line') == -1: |
||
c_edg_found = False |
c_edg_found = False |
||
print "found circle in c_edge search in bend Face", bend_node.idx+1 |
|||
else: |
else: |
||
print "found line in c_edge search in bend Face", bend_node.idx+1 |
|||
for E_vert in s_edge.Vertexes: |
for E_vert in s_edge.Vertexes: |
||
if equal_vertex(E_vert, bend_node.p_edge.Vertexes[0]): |
if equal_vertex(E_vert, bend_node.p_edge.Vertexes[0]): |
||
Line 888: | Line 1,172: | ||
bend_edge = s_edge |
bend_edge = s_edge |
||
number_c_edges = number_c_edges + 1 |
number_c_edges = number_c_edges + 1 |
||
print " found the second Line edge of the bend |
print " found the second Line edge of the bend Face", bend_node.idx+1 |
||
#Part.show(bend_edge) |
#Part.show(bend_edge) |
||
t_idx = self.search_face(bend_edge, bend_node) |
t_idx = self.search_face(bend_edge, bend_node) |
||
#Part.show(self. |
# Part.show(self.f_list[t_idx]) |
||
if t_idx <> None: |
if t_idx <> None: |
||
topFace = self.f_list[t_idx].copy() |
|||
topFace.rotate(self.f_list[bend_node.idx].Surface.Center,bend_node.axis,math.degrees(bend_node.bend_angle)) |
|||
self.index_list.remove(t_idx) |
|||
topFace.translate(trans_vec) |
|||
flat_shell.append(topFace) |
|||
topFace.rotate(self.__Shape.Faces[bend_node.idx].Surface.Center,bend_node.axis,math.degrees(bend_node.bend_angle)) |
|||
flat_shell.append(topFace) |
|||
s_Center = self.f_list[bend_node.idx].Surface.Center |
|||
s_Axis = self.f_list[bend_node.idx].Surface.Axis |
|||
# find the nearest vertex of bend_edge to a plane through s_Center |
|||
# The section-wire should start at this vertex |
|||
if (bend_edge.Vertexes[0].Point.distanceToPlane(s_Center, s_Axis) < |
|||
bend_edge.Vertexes[1].Point.distanceToPlane(s_Center, s_Axis)): |
|||
next_pnt = bend_edge.Vertexes[1].Point |
|||
start_pnt = bend_edge.Vertexes[0].Point |
|||
start_idx = 0 |
|||
end_idx = 1 |
|||
else: |
|||
next_pnt = bend_edge.Vertexes[0].Point |
|||
start_pnt = bend_edge.Vertexes[1].Point |
|||
start_idx = 1 |
|||
end_idx = 0 |
|||
b_wireList = self.f_list[t_idx].Edges[:] |
|||
#for remEdge in b_wireList: |
|||
# print "in b_wireList" |
|||
# Part.show(remEdge) |
|||
for remEdge in b_wireList: |
|||
# Part.show(remEdge) |
|||
if remEdge.isSame(bend_edge): |
|||
b_wireList.remove(remEdge) |
|||
break |
|||
for singleEdge in b_wireList: |
|||
#Part.show(singleEdge) |
|||
# print "section edges: ", len(singleEdge.Edges) |
|||
if len(singleEdge.Edges) == 1: |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[0].Point, start_pnt)): |
|||
lastEdge = singleEdge.Edges[0].copy() |
|||
lastConnect = 1 |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[1].Point, start_pnt)): |
|||
lastEdge = singleEdge.Edges[0].copy() |
|||
lastConnect = 0 |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[0].Point, next_pnt)): |
|||
for singleEdge in b_wireList: |
|||
nextEdge = singleEdge.Edges[0].copy() |
|||
nextConnect = 1 |
|||
if |
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[1].Point, next_pnt)): |
||
nextEdge = singleEdge.Edges[0].copy() |
|||
nextConnect = 0 |
|||
startEdge = Part.makeLine(start_pnt, next_pnt) |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[1].Point, start_pnt)): |
|||
middleEdge = Part.makeLine(nextEdge.Vertexes[nextConnect].Point, lastEdge.Vertexes[lastConnect].Point) |
|||
lastEdge = singleEdge.Edges[0].copy() |
|||
b_wire = Part.Wire([startEdge, nextEdge, middleEdge, lastEdge ]) |
|||
# Part.show(Swire1) |
|||
else: |
|||
print "Found no Face?!" |
|||
# there is a seam in the metal sheet. |
|||
# Generate a new face for the seam. |
|||
''' |
|||
b_wire = self.makeSectionWire(bend_edge, bend_node, bend_node.bend_dir).copy() |
|||
topFace = Part.Face(b_wire) |
|||
#self.f_list.append(topFace) |
|||
topFace.rotate(self.f_list[bend_node.idx].Surface.Center,bend_node.axis,math.degrees(bend_node.bend_angle)) |
|||
topFace.translate(trans_vec) |
|||
flat_shell.append(topFace) |
|||
''' |
|||
''' |
|||
# find the nearest vertex of bend_edge to a plane through s_Center |
|||
# The section-wire should start at this vertex |
|||
if (bend_edge.Vertexes[0].Point.distanceToPlane(s_Center, s_Axis) < |
|||
bend_edge.Vertexes[1].Point.distanceToPlane(s_Center, s_Axis)): |
|||
next_pnt = bend_edge.Vertexes[1].Point |
|||
start_pnt = bend_edge.Vertexes[0].Point |
|||
start_idx = 0 |
|||
end_idx = 1 |
|||
else: |
|||
next_pnt = bend_edge.Vertexes[0].Point |
|||
start_pnt = bend_edge.Vertexes[1].Point |
|||
start_idx = 1 |
|||
end_idx = 0 |
|||
# find the nextEdge in the node faces |
|||
search_List = bend_node.nfIndexes[:] |
|||
search_List.remove(bend_node.idx) |
|||
the_index = None |
|||
next_idx = None |
|||
for i in search_List: |
|||
for theEdge in self.f_list[i].Edges: |
|||
if len(theEdge.Vertexes)>1: |
|||
if equal_vector(theEdge.Vertexes[0].Point, next_pnt): |
|||
next_idx = 1 |
|||
if equal_vector(theEdge.Vertexes[1].Point, next_pnt): |
|||
next_idx = 0 |
|||
if next_idx <> None: |
|||
if self.isVertOpposite(theEdge.Vertexes[next_idx], bend_node): |
|||
nextEdge = theEdge.copy() |
|||
search_List.remove(i) |
|||
the_index = i |
|||
break |
|||
else: |
|||
next_idx = None |
|||
if the_index <> None: |
|||
break |
|||
#find the lastEdge |
|||
last_idx = None |
|||
for i in search_List: |
|||
for theEdge in self.f_list[i].Edges: |
|||
if len(theEdge.Vertexes)>1: |
|||
if equal_vector(theEdge.Vertexes[0].Point, start_pnt): |
|||
last_idx = 1 |
|||
if equal_vector(theEdge.Vertexes[1].Point, start_pnt): |
|||
last_idx = 0 |
|||
if last_idx <> None: |
|||
if self.isVertOpposite(theEdge.Vertexes[last_idx], bend_node): |
|||
lastEdge = theEdge.copy() |
|||
search_List.remove(i) |
|||
the_index = i |
|||
break |
|||
else: |
|||
last_idx = None |
|||
if the_index <> None: |
|||
break |
|||
# find the middleEdge |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[0].Point, next_pnt)): |
|||
for theEdge in self.f_list[bend_node.c_face_idx].Edges: |
|||
nextEdge = singleEdge.Edges[0].copy() |
|||
if len(theEdge.Vertexes)>1: |
|||
if equal_vector(theEdge.Vertexes[0].Point, start_pnt): |
|||
last_idx = 1 |
|||
if equal_vector(theEdge.Vertexes[1].Point, start_pnt): |
|||
nextConnect = 0 |
|||
last_idx = 0 |
|||
if last_idx <> None: |
|||
if self.isVertOpposite(theEdge.Vertexes[last_idx], bend_node): |
|||
lastEdge = theEdge.copy() |
|||
search_List.remove(i) |
|||
b_wire = Part.Wire([startEdge, nextEdge, middleEdge, lastEdge ]) |
|||
the_index = i |
|||
break |
|||
else: |
|||
last_idx = None |
|||
''' |
|||
b_wire.rotate(self. |
b_wire.rotate(self.f_list[bend_node.idx].Surface.Center,bend_node.axis,math.degrees(bend_node.bend_angle)) |
||
b_wire.translate(trans_vec) |
b_wire.translate(trans_vec) |
||
Line 975: | Line 1,350: | ||
#Part.show(self.__Shape.copy()) |
#Part.show(self.__Shape.copy()) |
||
print "finish genBendShell Face", bend_node.idx +1 |
#print "finish genBendShell Face", bend_node.idx +1 |
||
return flat_shell |
return flat_shell |
||
def makeSeamFace(self, sEdge, theNode): |
|||
''' This function creates a face at a seam of the sheet metal. |
|||
def makeCutTool(self, theEdge, W_node): |
|||
It works currently only at a flat node. |
|||
print "mCT Face", W_node.idx +1 |
|||
''' |
|||
# makes a Section wire through the shape |
|||
print "now make a seam Face" |
|||
# and generates a cutTool to be used to cut faces at the sheet edges |
|||
nextVert = sEdge.Vertexes[1] |
|||
startVert = sEdge.Vertexes[0] |
|||
start_idx = 0 |
|||
o_thick = Base.Vector(o_vec.x, o_vec.y, o_vec.z) |
|||
end_idx = 1 |
|||
o_thick.normalize().multiply(2.0 * self.__thickness) |
|||
vec1 = Base.Vector(W_node.axis.x, W_node.axis.y, W_node.axis.z) |
|||
vec1.multiply(self.__thickness) |
|||
search_List = theNode.nfIndexes[:] |
|||
# defining the points of the section plane: |
|||
print "This is the search_List: ", search_List |
|||
Spnt1 = origin - vec1 - o_thick |
|||
search_List.remove(theNode.idx) |
|||
Spnt2 = origin - vec1 + o_vec + o_thick |
|||
the_index = None |
|||
Spnt3 = origin + vec1 + vec1 + o_vec + o_thick |
|||
next_idx = None |
|||
Spnt4 = origin + vec1 + vec1 - o_thick |
|||
for i in search_List: |
|||
for theEdge in self.f_list[i].Edges: |
|||
if len(theEdge.Vertexes)>1: |
|||
if equal_vertex(theEdge.Vertexes[0], nextVert): |
|||
next_idx = 1 |
|||
if equal_vertex(theEdge.Vertexes[1], nextVert): |
|||
next_idx = 0 |
|||
if next_idx <> None: |
|||
if self.isVertOpposite(theEdge.Vertexes[next_idx], theNode): |
|||
nextEdge = theEdge.copy() |
|||
search_List.remove(i) |
|||
the_index = i |
|||
#Part.show(nextEdge) |
|||
break |
|||
else: |
|||
next_idx = None |
|||
if the_index <> None: |
|||
break |
|||
#find the lastEdge |
|||
Sedge1 = Part.makeLine(Spnt1,Spnt2) |
|||
last_idx = None |
|||
Sedge2 = Part.makeLine(Spnt2,Spnt3) |
|||
print "This is the search_List: ", search_List |
|||
Sedge3 = Part.makeLine(Spnt3,Spnt4) |
|||
for i in search_List: |
|||
Sedge4 = Part.makeLine(Spnt4,Spnt1) |
|||
#Part.show(self.f_list[i]) |
|||
for theEdge in self.f_list[i].Edges: |
|||
print "find last Edge in Face: ", i, " at Edge: ", theEdge |
|||
if len(theEdge.Vertexes)>1: |
|||
if equal_vertex(theEdge.Vertexes[0], startVert): |
|||
last_idx = 1 |
|||
if equal_vertex(theEdge.Vertexes[1], startVert): |
|||
last_idx = 0 |
|||
if last_idx <> None: |
|||
print "test for the last Edge" |
|||
if self.isVertOpposite(theEdge.Vertexes[last_idx], theNode): |
|||
lastEdge = theEdge.copy() |
|||
search_List.remove(i) |
|||
the_index = i |
|||
#Part.show(lastEdge) |
|||
break |
|||
else: |
|||
last_idx = None |
|||
if last_idx <> None: |
|||
break |
|||
# find the middleEdge |
|||
Sw1 = Part.Wire([Sedge1, Sedge2, Sedge3, Sedge4]) |
|||
mid_idx = None |
|||
midEdge = None |
|||
for theEdge in self.f_list[theNode.c_face_idx].Edges: |
|||
#Part.show(Sf1) |
|||
if len(theEdge.Vertexes)>1: |
|||
lostShape = self.__Shape.copy() |
|||
if equal_vertex(theEdge.Vertexes[0], nextEdge.Vertexes[next_idx]): |
|||
Sedges = Sf1.section(lostShape) |
|||
mid_idx = 1 |
|||
#Part.show(Sedges) |
|||
if equal_vertex(theEdge.Vertexes[1], nextEdge.Vertexes[next_idx]): |
|||
# find edge in Sedges equal to theEdge |
|||
mid_idx = 0 |
|||
if mid_idx <> None: |
|||
if |
if equal_vertex(theEdge.Vertexes[mid_idx], lastEdge.Vertexes[last_idx]): |
||
midEdge = theEdge.copy() |
|||
#Part.show(midEdge) |
|||
(equal_vertex(lu_edge.Vertexes[1], theEdge.Vertexes[0]) and |
|||
break |
|||
equal_vertex(lu_edge.Vertexes[0], theEdge.Vertexes[1]))): |
|||
else: |
|||
mid_idx = None |
|||
if midEdge: |
|||
next_pnt = theEdge.Vertexes[0] |
|||
break |
|||
start_pnt = theEdge.Vertexes[1] |
|||
S_edge_list = [theEdge] |
|||
e_counter = len(other_edges) |
|||
# print "e_counter: ", e_counter |
|||
startFound = False |
|||
while (e_counter > 0) and (not startFound): |
|||
found_next = False |
|||
for lu_edge in other_edges: |
|||
if equal_vertex(lu_edge.Vertexes[0], next_pnt): |
|||
found_next = True |
|||
next_next_pnt = lu_edge.Vertexes[1] |
|||
if equal_vertex(lu_edge.Vertexes[1], next_pnt): |
|||
found_next = True |
|||
next_next_pnt = lu_edge.Vertexes[0] |
|||
if found_next: |
|||
S_edge_list.append(lu_edge) |
|||
other_edges.remove(lu_edge) |
|||
next_pnt = next_next_pnt |
|||
if equal_vertex(start_pnt, next_pnt): |
|||
startFound = True |
|||
break |
|||
e_counter = e_counter -1 |
|||
seam_wire = Part.Wire([sEdge, nextEdge, midEdge, lastEdge ]) |
|||
if not startFound: |
|||
seamFace = Part.Face(seam_wire) |
|||
W_node.analysis_ok = False # |
|||
self.f_list.append(seamFace) |
|||
W_node.error_code = 23 # Unfold: Unfold: CutToolWire not closed |
|||
theNode.nfIndexes.append(self.max_f_idx) |
|||
self.error_code = 23 |
|||
self.max_f_idx += 1 |
|||
Swire1 = Part.Wire(S_edge_list) |
|||
# fixme: look if we have more than one wire |
|||
# Part.show(Swire1) |
|||
SectionNormal = o_vec.cross(W_node.axis) |
|||
SectionNormal.normalize() |
|||
lookAt = theEdge.LastParameter/2.0 |
|||
mCT_Posi = theEdge.valueAt(lookAt) + SectionNormal |
|||
# T_pnt = vertex_verwaltung[i][0] + Base.Vector(kante.x/2.0, kante.y/2.0, kante.z/2.0) + SectionNormal |
|||
Sf1=Part.Face(Swire1) |
|||
# Part.show(Sf1) |
|||
def showFaces(self): |
|||
s_face_body = lostShape.Faces[W_node.idx].extrude(vec1) |
|||
# Part.show(s_face_body) |
|||
if s_face_body.isInside(mCT_Posi, 0.00001, True): |
|||
print " T_pnt is inside!" |
|||
SectionNormal.multiply(-1.0) |
|||
else: |
|||
print "T_pnt is outside!" |
|||
cut_solid = Sf1.extrude(SectionNormal) |
|||
# Part.show(cut_solid) |
|||
print "finish mCT Face", W_node.idx +1 |
|||
return cut_solid |
|||
def makeCutTool2(self, theEdge, W_node, cutType = None): |
|||
print "mCT2 Face", W_node.idx +1 |
|||
# makes a Section wire through the face defining the sheet edge at theEdge |
|||
# This version did not cut the faces. Why??? |
|||
o_vec = theEdge.Vertexes[1].Point - theEdge.Vertexes[0].Point |
|||
o_thick = Base.Vector(o_vec.x, o_vec.y, o_vec.z) |
|||
o_thick.normalize().multiply(2.0 * self.__thickness) |
|||
vec1 = Base.Vector(W_node.axis.x, W_node.axis.y, W_node.axis.z) |
|||
vec1.multiply(self.__thickness) |
|||
if (cutType == None) or (cutType == 3): |
|||
origin = theEdge.Vertexes[0].Point |
|||
o_vec = theEdge.Vertexes[1].Point - theEdge.Vertexes[0].Point |
|||
# defining the points of the section plane: |
|||
Spnt1 = origin - vec1 - o_thick |
|||
Spnt2 = origin - vec1 + o_vec + o_thick |
|||
Spnt3 = origin + vec1 + vec1 + o_vec + o_thick |
|||
Spnt4 = origin + vec1 + vec1 - o_thick |
|||
elif cutType == 1: |
|||
origin = theEdge.Vertexes[0].Point |
|||
o_vec = (theEdge.Vertexes[1].Point - theEdge.Vertexes[0].Point)/2.0 |
|||
Spnt1 = origin - vec1 - o_thick |
|||
Spnt2 = origin - vec1 + o_vec |
|||
Spnt3 = origin + vec1 + vec1 + o_vec |
|||
Spnt4 = origin + vec1 + vec1 - o_thick |
|||
elif cutType == 2: |
|||
origin = theEdge.Vertexes[1].Point |
|||
o_vec = (theEdge.Vertexes[0].Point - theEdge.Vertexes[1].Point)/2.0 |
|||
# defining the points of the section plane: |
|||
Spnt1 = origin - vec1 + o_thick |
|||
Spnt2 = origin - vec1 + o_vec |
|||
Spnt3 = origin + vec1 + vec1 + o_vec |
|||
Spnt4 = origin + vec1 + vec1 + o_thick |
|||
Sedge1 = Part.makeLine(Spnt1,Spnt2) |
|||
Sedge2 = Part.makeLine(Spnt2,Spnt3) |
|||
Sedge3 = Part.makeLine(Spnt3,Spnt4) |
|||
Sedge4 = Part.makeLine(Spnt4,Spnt1) |
|||
Sw1 = Part.Wire([Sedge1, Sedge2, Sedge3, Sedge4]) |
|||
Sf1=Part.Face(Sw1) # |
|||
# Part.show(Sf1) |
|||
next_pnt = theEdge.Vertexes[1].Point |
|||
start_pnt = theEdge.Vertexes[0].Point |
|||
start_idx = 0 |
|||
end_idx = 1 |
|||
for i in self.index_list: |
for i in self.index_list: |
||
Part.show(self.f_list[i]) |
|||
#Part.show(singleEdge) |
|||
# print "section edges: ", len(singleEdge.Edges) |
|||
if len(singleEdge.Edges) == 1: |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[0].Point, start_pnt)): |
|||
lastEdge = singleEdge.Edges[0] |
|||
lastConnect = 1 |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[1].Point, start_pnt)): |
|||
lastEdge = singleEdge.Edges[0] |
|||
lastConnect = 0 |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[0].Point, next_pnt)): |
|||
nextEdge = singleEdge.Edges[0] |
|||
nextConnect = 1 |
|||
if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[1].Point, next_pnt)): |
|||
nextEdge = singleEdge.Edges[0] |
|||
nextConnect = 0 |
|||
if (cutType == None) or (cutType == 3): |
|||
origin = theEdge.Vertexes[0].Point |
|||
startEdge = Part.makeLine(start_pnt, next_pnt) |
|||
middleEdge = Part.makeLine(nextEdge.Vertexes[nextConnect].Point, lastEdge.Vertexes[lastConnect].Point) |
|||
elif cutType == 1: |
|||
origin = theEdge.Vertexes[0].Point |
|||
o_vec = (theEdge.Vertexes[1].Point - theEdge.Vertexes[0].Point)/2.0 |
|||
startEdge = Part.makeLine(start_pnt, start_pnt + o_vec) |
|||
nextEdge = Part.makeLine(start_pnt + o_vec, start_pnt + o_vec + vec1) |
|||
middleEdge = Part.makeLine(start_pnt + o_vec + vec1, lastEdge.Vertexes[lastConnect].Point) |
|||
elif cutType == 2: |
|||
origin = theEdge.Vertexes[1].Point |
|||
o_vec = (theEdge.Vertexes[0].Point - theEdge.Vertexes[1].Point)/2.0 |
|||
startEdge = Part.makeLine(next_pnt + o_vec, next_pnt) |
|||
middleEdge = Part.makeLine(nextEdge.Vertexes[nextConnect].Point, next_pnt + o_vec + vec1) |
|||
lastEdge = Part.makeLine(next_pnt + o_vec + vec1, next_pnt + o_vec) |
|||
def unfold_tree2(self, node): |
|||
Swire1 = Part.Wire([startEdge, nextEdge, middleEdge, lastEdge ]) |
|||
# Part.show(Swire1) |
|||
SectionNormal = o_vec.cross(W_node.axis) |
|||
SectionNormal.normalize() |
|||
lookAt = theEdge.LastParameter/2.0 |
|||
testPnt = theEdge.valueAt(lookAt) + SectionNormal |
|||
Sf1=Part.Face(Swire1) |
|||
# Part.show(Sf1) |
|||
s_face_body = self.__Shape.Faces[W_node.idx].extrude(vec1) |
|||
# Part.show(s_face_body) |
|||
if s_face_body.isInside(testPnt, 0.00001, True): |
|||
print " testPnt is inside!" |
|||
SectionNormal.multiply(-1.0) |
|||
else: |
|||
print "testPnt is outside!" |
|||
cut_solid = Sf1.extrude(SectionNormal) |
|||
# Part.show(cut_solid) |
|||
print "finish mCT2 Face", W_node.idx +1 |
|||
return cut_solid |
|||
def genVertEdgeList(self, theVert): |
|||
print "theVert tolerance: ", theVert.Tolerance |
|||
cornerEdges = [] |
|||
for foEd in self.__Shape.Edges: |
|||
#print "foEd0 tolerance: ", foEd.Vertexes[0].Tolerance |
|||
if equal_vertex(foEd.Vertexes[0], theVert): |
|||
cornerEdges.append(foEd) |
|||
#Part.show(foEd) |
|||
if len(foEd.Vertexes) > 1: |
|||
#print "foEd1 tolerance: ", foEd.Vertexes[1].Tolerance |
|||
if equal_vertex(foEd.Vertexes[1], theVert): |
|||
cornerEdges.append(foEd) |
|||
print "genVertEdgeList: ", len(cornerEdges) |
|||
return cornerEdges |
|||
def generateShell(self, flat_node): |
|||
print "genShell Face", flat_node.idx +1 |
|||
# collect faces from the flat_node and return them |
|||
flat_shell = [] |
|||
flat_shell.append(self.__Shape.Faces[flat_node.idx].copy()) |
|||
flat_shell.append(self.__Shape.Faces[flat_node.c_face_idx].copy()) |
|||
# Needed are all edges |
|||
edge_wires = DraftGeomUtils.findWires(flat_node.sheet_edges) |
|||
print "Face", flat_node.idx +1, " Wires found: ", len(edge_wires) |
|||
# the edges to the parent-node and the child-nodes are used to |
|||
# generate cut tools for the faces at the sheet edge |
|||
neighborCuts = [] # make list of cutTools |
|||
if flat_node.p_edge: |
|||
typeOfCut = 0 |
|||
#if len(self.genPntEdgeList(flat_node.p_edge.Vertexes[0].Point)) == 3: |
|||
if len(self.genVertEdgeList(flat_node.p_edge.Vertexes[0])) == 3: |
|||
print "mCT typeOfCut 1" |
|||
typeOfCut = typeOfCut + 1 |
|||
#if len(self.genPntEdgeList(flat_node.p_edge.Vertexes[1].Point)) == 3: |
|||
if len(self.genVertEdgeList(flat_node.p_edge.Vertexes[1])) == 3: |
|||
print "mCT typeOfCut 2" |
|||
typeOfCut = typeOfCut + 2 |
|||
if typeOfCut <> 0: |
|||
print "should call mCT" |
|||
#neighborCuts.append(self.makeCutTool2(flat_node.p_edge, flat_node, typeOfCut)) |
|||
neighborCuts.append(self.makeCutTool(flat_node.p_edge, flat_node)) |
|||
for child in flat_node.child_list: |
|||
typeOfCut = 0 |
|||
#if len(self.genPntEdgeList(child.p_edge.Vertexes[0].Point)) == 3: |
|||
if len(self.genVertEdgeList(child.p_edge.Vertexes[0])) == 3: |
|||
print "mCT child typeOfCut 1" |
|||
typeOfCut = typeOfCut + 1 |
|||
#if len(self.genPntEdgeList(child.p_edge.Vertexes[1].Point)) == 3: |
|||
if len(self.genVertEdgeList(child.p_edge.Vertexes[1])) == 3: |
|||
print "mCT child typeOfCut 2" |
|||
typeOfCut = typeOfCut + 2 |
|||
if typeOfCut <> 0: |
|||
print "should call mCT child" |
|||
#neighborCuts.append(self.makeCutTool2(child.p_edge, flat_node, typeOfCut)) |
|||
neighborCuts.append(self.makeCutTool(child.p_edge, flat_node)) |
|||
faces_list = [] |
|||
cut_faces =[] |
|||
for i in self.index_list: |
|||
faces_list.append(self.__Shape.Faces[i]) |
|||
# cut all residual faces which should define the edges of the sheet with the cutters where needed |
|||
if len(neighborCuts) > 0: |
|||
print "CutTools: ", len(neighborCuts), " for sheet_edges: ", len(flat_node.sheet_edges) |
|||
for cutter in neighborCuts: |
|||
print "next cutter in action" |
|||
if self.error_code <> None: |
|||
break |
|||
for c_face in faces_list: |
|||
cutted = c_face.cut(cutter) |
|||
# print "Anzahl Schnittteile: ", len(cutted.Faces) |
|||
# if len(cutted.Faces) == 2: |
|||
# Part.show(cutted.Faces[0]) |
|||
# Part.show(cutted.Faces[1]) |
|||
for cut_face in cutted.Faces: |
|||
cut_faces.append(cut_face) |
|||
faces_list = cut_faces[:] |
|||
cut_faces = [] |
|||
if self.error_code <> None: |
|||
return flat_shell |
|||
# select those faces, that have a common edge to the flat_node |
|||
for s_edge in flat_node.sheet_edges: |
|||
print "search faces for next edge" |
|||
for c_face in faces_list: |
|||
for c_edge in c_face.Edges: |
|||
if s_edge.isSame(c_edge): |
|||
if not c_face in flat_shell: |
|||
flat_shell.append(c_face.copy()) |
|||
# Part.show(c_face) |
|||
print "finisch genShell Face", flat_node.idx +1 |
|||
return flat_shell |
|||
''' |
|||
Den Baum rekursiv abwandern. Nur jeweils im höchsten Level unbend |
|||
ausführen. |
|||
''' |
|||
def unfold_tree(self, node): |
|||
# This function traverses the tree and unfolds the faces |
# This function traverses the tree and unfolds the faces |
||
# beginning at the outermost nodes. |
# beginning at the outermost nodes. |
||
print "unfold_tree face", node.idx + 1 |
#print "unfold_tree face", node.idx + 1 |
||
theShell = [] |
theShell = [] |
||
nodeShell = [] |
|||
for n_node in node.child_list: |
for n_node in node.child_list: |
||
if self.error_code == None: |
if self.error_code == None: |
||
theShell = theShell + self. |
theShell = theShell + self.unfold_tree2(n_node) |
||
if node.node_type == 'Bend': |
if node.node_type == 'Bend': |
||
trans_vec = node.tan_vec * node._trans_length |
trans_vec = node.tan_vec * node._trans_length |
||
for bFaces in theShell: |
for bFaces in theShell: |
||
bFaces.rotate(self. |
bFaces.rotate(self.f_list[node.idx].Surface.Center,node.axis,math.degrees(node.bend_angle)) |
||
bFaces.translate(trans_vec) |
bFaces.translate(trans_vec) |
||
if self.error_code == None: |
if self.error_code == None: |
||
nodeShell = self.generateBendShell(node) |
nodeShell = self.generateBendShell(node) |
||
else: |
|||
nodeShell = [] |
|||
else: |
else: |
||
if self.error_code == None: |
if self.error_code == None: |
||
nodeShell = self.generateShell(node) |
# nodeShell = self.generateShell(node) |
||
for idx in node.nfIndexes: |
|||
nodeShell.append(self.f_list[idx].copy()) |
|||
nodeShell = [] |
|||
#if len(node.seam_edges)>0: |
|||
# for seamEdge in node.seam_edges: |
|||
# self.makeSeamFace(seamEdge, node) |
|||
print "ufo finish face",node.idx +1 |
print "ufo finish face",node.idx +1 |
||
return (theShell + nodeShell) |
return (theShell + nodeShell) |
||
def showFaces(self): |
|||
for i in self.index_list: |
|||
Part.show(self.__Shape.Faces[i]) |
|||
Line 1,375: | Line 1,502: | ||
print "name: ",subelement |
print "name: ",subelement |
||
f_number = int(o.SubElementNames[0].lstrip('Face'))-1 |
f_number = int(o.SubElementNames[0].lstrip('Face'))-1 |
||
print f_number |
#print f_number |
||
startzeit = time.clock() |
|||
TheTree = SheetTree(o.Object.Shape, f_number) # initializes the tree-structure |
TheTree = SheetTree(o.Object.Shape, f_number) # initializes the tree-structure |
||
if TheTree.error_code == None: |
if TheTree.error_code == None: |
||
TheTree.Bend_analysis(f_number, None) # traverses the shape builds the tree-structure |
TheTree.Bend_analysis(f_number, None) # traverses the shape builds the tree-structure |
||
endzeit = time.clock() |
|||
print "Analytical time: ",endzeit-startzeit |
|||
if TheTree.error_code == None: |
if TheTree.error_code == None: |
||
# TheTree.showFaces() |
# TheTree.showFaces() |
||
theFaceList = TheTree. |
theFaceList = TheTree.unfold_tree2(TheTree.root) # traverses the tree-structure |
||
if TheTree.error_code == None: |
if TheTree.error_code == None: |
||
unfoldTime = time.clock() |
|||
print "time to run the unfold: ", unfoldTime - endzeit |
|||
try: |
try: |
||
Line 1,395: | Line 1,527: | ||
try: |
try: |
||
TheSolid = Part.Solid(newShell) |
TheSolid = Part.Solid(newShell) |
||
solidTime = time.clock() |
|||
print "time to make the solid: ", solidTime - unfoldTime |
|||
except: |
except: |
||
print("couldn't make a solid, show only a shell") |
print ("couldn't make a solid, show only a shell, Faces in List: ", len(theFaceList)) |
||
Part.show(newShell) |
Part.show(newShell) |
||
showTime = time.clock() |
|||
print "Show time: ", showTime - unfoldTime |
|||
else: |
else: |
||
Part.show(TheSolid) |
Part.show(TheSolid) |
||
showTime = time.clock() |
|||
print "Show time: ", showTime - solidTime, " total time: ", showTime - startzeit |
|||
if TheTree.error_code <> None: |
if TheTree.error_code <> None: |
||
Line 1,415: | Line 1,553: | ||
mw=FreeCADGui.getMainWindow() |
mw=FreeCADGui.getMainWindow() |
||
QtGui.QMessageBox.information(mw,"Selection Error","""Sheet UFO works only with a flat face as starter!\n Select a flat face.""") |
QtGui.QMessageBox.information(mw,"Selection Error","""Sheet UFO works only with a flat face as starter!\n Select a flat face.""") |
||
}} |
|||
}} |
|||
<languages/> |
Latest revision as of 02:23, 8 May 2020
Description |
---|
The macro can unfold simple sheet-metal-parts. The parts must have everywhere the same material thickness as typical for sheet-metal-parts. Macro version: 17.0 Last modified: 2018-08-14 FreeCAD version: 17.0 Download: ToolBar Icon Author: ulrich1a |
Author |
ulrich1a |
Download |
ToolBar Icon |
Links |
Macros recipes How to install macros How to customize toolbars |
Macro Version |
17.0 |
Date last modified |
2018-08-14 |
FreeCAD Version(s) |
17.0 |
Default shortcut |
None |
See also |
None |
Description
The macro can unfold simple sheet-metal-parts. The parts must have everywhere the same material thickness as typical for sheet-metal-parts. See example:
Temporary code for external macro link. Do not use this code. This code is used exclusively by Addon Manager. Link for optional manual installation: Macro
# This code is copied instead of the original macro code # to guide the user to the online download page. # Use it if the code of the macro is larger than 64 KB and cannot be included in the wiki # or if the RAW code URL is somewhere else in the wiki. from PySide import QtGui, QtCore diag = QtGui.QMessageBox(QtGui.QMessageBox.Information, "Information", "This macro must be downloaded from this link\n" "\n" "https://raw.githubusercontent.com/ulrich1a/sheet_ufo/master/sheet_ufo.py" + "\n" "\n" "Quit this window to access the download page") diag.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() import webbrowser webbrowser.open("https://raw.githubusercontent.com/ulrich1a/sheet_ufo/master/sheet_ufo.py")
Usage
Select a flat face from your part in the 3D-view. Start the macro.
Remarks
The macro creates a lot of text in the report-view. This is still a remains of the debugging process of this macro. The edges of the sheet-metal-part may be round or slanting. But if you "walk" from the upper side to the lower side of the sheet-metal-part you should not cross more than one face. In best case the macro generates a new flat solid from your part. If the macro has problems, you may get only a flat shell or a bunch of flattened faces or only an error message.
The macro can be thought of as a companion to the AddWall tool from JMG published here: FreeCAD: Sheet metal tool "Add Wall"
Link
The forum discussion Sheet metal: add wall tool
An updated version is available at: https://github.com/ulrich1a/sheet_ufo
Script
Sheet Metal Unfolder.py
#!/usr/bin/env python # -*- coding: utf-8 -*- # # sheet_ufo.py # # Copyright 2014 Ulrich Brammer <ulrich@Pauline> # # 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., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # # # Refactored version December 2015 # Clear division of tasks between analysis and folding # To do: # change code to handle face indexes in the node instead of faces # sheet_ufo16.py # Die Weiterreichung eines schon geschnittenen Seitenfaces macht Probleme. # Die Seitenfaces passen hinterher nicht mehr mit den Hauptflächen zusammen. # Geänderter Ansatz: lasse die Seitenflächen in der Suchliste und # schneide jeweils nur den benötigten Teil raus. # Ich brauche jetzt eine Suchliste und eine Unfoldliste für die # Face-Indices. # To do: # - handle a selected seam # - handle not-circle-curves in bends # - detect features like welded screws # - make a view-provider for bends # - make the k-factor selectable # - upfold or unfold single bends ''' def main(): return 0 if __name__ == '__main__': main() ''' import Part, FreeCADGui from PySide import QtGui from FreeCAD import Base import DraftVecUtils, DraftGeomUtils, math, time # to do: # - Put error numbers into the text # - Put user help into more texts unfold_error = { # error codes for the tree-object 1: ('starting: volume unusable, needs a real 3D-sheet-metal with thickness'), 2: ('Starting: invalid point for thickness measurement'), 3: ('Starting: invalid thickness'), 4: ('Starting: invalid shape'), # error codes for the bend-analysis 10: ('Analysis: zero wires in sheet edge analysis'), 11: ('Analysis: double bends not implemented'), 12: ('Analysis: more than one bend-child actually not supported'), 13: ('Analysis: counter face not found'), 14: ('Analysis: the code can not handle edges without neighbor faces'), 15: ('Analysis: the code needs a face at all sheet edges'), 16: ('Analysis: did not find startangle of bend, please post failing sample for analysis'), # error codes for the unfolding 20: ('Unfold: section wire with less than 4 edges'), 21: ('Unfold: Unfold: section wire not closed'), 22: ('Unfold: section failed'), 23: ('Unfold: CutToolWire not closed'), 24: ('Unfold: bend-face without child not implemented'), 25: ('Unfold: '), -1: ('unknown error')} def equal_vertex(vert1, vert2, p=5): # compares two vertices return (round(vert1.X - vert2.X,p)==0 and round(vert1.Y - vert2.Y,p)==0 and round(vert1.Z - vert2.Z,p)==0) def equal_vector(vec1, vec2, p=5): # compares two vectors return (round(vec1.x - vec2.x,p)==0 and round(vec1.y - vec2.y,p)==0 and round(vec1.z - vec2.z,p)==0) def radial_vector(point, axis_pnt, axis): chord = axis_pnt.sub(point) norm = axis.cross(chord) perp = axis.cross(norm) # print chord, norm, perp dist_rv = DraftVecUtils.project(chord,perp) #test_line = Part.makeLine(axis_pnt.add(dist_rv),axis_pnt) # test_line = Part.makeLine(axis_pnt.add(perp),axis_pnt) # test_line = Part.makeLine(point, axis_pnt) # Part.show(test_line) return perp.normalize() class Simple_node(object): ''' This class defines the nodes of a tree, that is the result of the analysis of a sheet-metal-part. Each flat or bend part of the metal-sheet gets a node in the tree. The indexes are the number of the face in the original part. ''' def __init__(self, f_idx=None, Parent_node= None, Parent_edge = None): self.idx = f_idx # index of the "top-face" self.c_face_idx = None # face index to the opposite face of the sheet (counter-face) self.node_type = None # 'Flat' or 'Bend' self.p_node = Parent_node # Parent node self.p_edge = Parent_edge # the connecting edge to the parent node self.child_list = [] # List of child-nodes = link to tree structure self.child_idx_lists = [] # List of lists with child_idx and child_edge # need also a list of indices of child faces self.sheet_edges = [] # List of edges without child-face self.axis = None self.facePosi = None self.bendCenter = None self.distCenter = None # self.axis for 'Flat'-face: vector pointing from the surface into the metal self.bend_dir = None # bend direction values: "up" or "down" self.bend_angle = None # angle in radians self.tan_vec = None # direction of translation for Bend nodes self._trans_length = None # length of translation for Bend nodes, k-factor used according to DIN 6935 self.analysis_ok = True # indicator if something went wrong with the analysis of the face self.error_code = None # index to unfold_error dictionary # here the new features of the nodes: self.nfIndexes = [] # list of all face-indexes of a node (flat and bend: folded state) self.seam_edges = [] # list with edges to seams # bend faces are needed for movement simulation at single other bends. # otherwise unfolded faces are recreated from self.b_edges self.node_flattened_faces = [] # faces of a flattened bend node. self.actual_angle = None # state of angle in refolded sheet metal part self.p_wire = None # wire common with parent node, used for bend node self.c_wire = None # wire common with child node, used for bend node self.b_edges = [] # list of edges in a bend node, that needs to be recalculated, at unfolding def get_Face_idx(self): # get the face index from the tree-element return self.idx class SheetTree(object): def __init__(self, TheShape, f_idx): self.cFaceTol = 0.002 # tolerance to detect counter-face vertices # this high tolerance was needed for more real parts self.root = None # make_new_face_node adds the root node if parent_node == None self.__Shape = TheShape.copy() self.error_code = None self.failed_face_idx = None if not self.__Shape.isValid(): print "The shape is not valid!" self.error_code = 4 # Starting: invalid shape self.failed_face_idx = f_idx #Part.show(self.__Shape) # List of indices to the shape.Faces. The list is used a lot for face searches. # Some faces will be cut and the new ones added to the list. # So a list of faces independent of the shape is needed. self.f_list = [] #self.__Shape.Faces.copy() does not work self.index_list =[] self.index_unfold_list = [] # indexes needed for unfolding for i in range(len (self.__Shape.Faces)): #for i in range(len (self.f_list)): # if i<>(f_idx): self.index_list.append(i) self.index_unfold_list.append(i) self.f_list.append(self.__Shape.Faces[i]) #print self.index_list self.max_f_idx = len(self.f_list) # need this value to make correct indices to new faces theVol = self.__Shape.Volume if theVol < 0.0001: print "Shape is not a real 3D-object or to small for a metal-sheet!" self.error_code = 1 self.failed_face_idx = f_idx else: # Make a first estimate of the thickness estimated_thickness = theVol/(self.__Shape.Area / 2.0) print "approximate Thickness: ", estimated_thickness # Measure the real thickness of the initial face: Use Orientation and # Axis to make an measurement vector if hasattr(self.__Shape.Faces[f_idx],'Surface'): # Part.show(self.__Shape.Faces[f_idx]) # print 'the object is a face! vertices: ', len(self.__Shape.Faces[f_idx].Vertexes) F_type = self.__Shape.Faces[f_idx].Surface # fixme: through an error, if not Plane Object print 'It is a: ', str(F_type) print 'Orientation: ', str(self.__Shape.Faces[f_idx].Orientation) # Need a point on the surface to measure the thickness. # Sheet edges could be sloping, so there is a danger to measure # right at the edge. # Try with Arithmetic mean of plane vertices m_vec = Base.Vector(0.0,0.0,0.0) # calculating a mean vector for Vvec in self.__Shape.Faces[f_idx].Vertexes: #m_vec = m_vec.add(Base.Vector(Vvec.X, Vvec.Y, Vvec.Z)) m_vec = m_vec.add(Vvec.Point) mvec = m_vec.multiply(1.0/len(self.__Shape.Faces[f_idx].Vertexes)) print "mvec: ", mvec if hasattr(self.__Shape.Faces[f_idx].Surface,'Position'): s_Posi = self.__Shape.Faces[f_idx].Surface.Position k = 0 # while k < len(self.__Shape.Faces[f_idx].Vertexes): # fixme: what if measurepoint is outside? pvert = self.__Shape.Faces[f_idx].Vertexes[k] pvec = Base.Vector(pvert.X, pvert.Y, pvert.Z) shiftvec = mvec.sub(pvec) shiftvec = shiftvec.normalize()*2.0*estimated_thickness measure_pos = pvec.add(shiftvec) # Description: Checks if a point is inside a solid with a certain tolerance. # If the 3rd parameter is True a point on a face is considered as inside if not self.__Shape.isInside(measure_pos, 0.00001, True): print "Starting measure_pos for thickness measurement is outside!" self.error_code = 2 self.failed_face_idx = f_idx if hasattr(self.__Shape.Faces[f_idx].Surface,'Axis'): s_Axis = self.__Shape.Faces[f_idx].Surface.Axis # print 'We have an axis: ', s_Axis if hasattr(self.__Shape.Faces[f_idx].Surface,'Position'): s_Posi = self.__Shape.Faces[f_idx].Surface.Position # print 'We have a position: ', s_Posi s_Ori = self.__Shape.Faces[f_idx].Orientation s_Axismp = Base.Vector(s_Axis.x, s_Axis.y, s_Axis.z).multiply(2.0*estimated_thickness) if s_Ori == 'Forward': Meassure_axis = Part.makeLine(measure_pos,measure_pos.sub(s_Axismp)) ext_Vec = Base.Vector(-s_Axis.x, -s_Axis.y, -s_Axis.z) # Meassure_axis = Part.makeLine(measure_pos,measure_pos.sub(s_Axis.multiply(2.0*estimated_thickness))) else: # Meassure_axis = Part.makeLine(measure_pos,measure_pos.add(s_Axis.multiply(2.0*estimated_thickness))) Meassure_axis = Part.makeLine(measure_pos,measure_pos.add(s_Axismp)) ext_Vec = Base.Vector(s_Axis.x, s_Axis.y, s_Axis.z) # Part.show(Meassure_axis) lostShape = self.__Shape.copy() lLine = Meassure_axis.common(lostShape) lLine = Meassure_axis.common(self.__Shape) print "lLine number edges: ", len(lLine.Edges) measVert = Part.Vertex(measure_pos) for mEdge in lLine.Edges: if equal_vertex(mEdge.Vertexes[0], measVert) or equal_vertex(mEdge.Vertexes[1], measVert): self.__thickness = mEdge.Length # self.__thickness = lLine.Length if (self.__thickness < estimated_thickness) or (self.__thickness > 1.9 * estimated_thickness): self.error_code = 3 self.failed_face_idx = f_idx print "estimated thickness: ", estimated_thickness, " measured thickness: ", self.__thickness Part.show(lLine) def get_node_faces(self, theNode, wires_e_lists): ''' This function searches for all faces making up the node, except of the top and bottom face, which are already there. wires_e_list is the list of wires lists of the top face without the parent-edge theNode: the actual node to be filled with data. ''' # How to begin? # searching for all faces, that have two vertices in common with # an edge from the list should give the sheet edge. # But we also need to look at the sheet edge, in order to not claim # faces from the next node! # Then we have to treat thoses faces, that belongs to more than one # node. Those faces needs to be cut and the face list needs to be updated. # look also at the number of wires of the top face. More wires will # indicate a hole or a feature. print " When will this be called" found_indices = [] # A search strategy for faces based on the wires_e_lists is needed. # for theWire in wires_e_lists: for theEdge in theWire: analyVert = theEdge.Vertexes[0] search_list = [] for x in self.index_list: search_list.append(x) for i in search_list: for lookVert in self.f_list[i].Vertexes: if equal_vertex(lookVert, analyVert): if len(theEdge.Vertexes) == 1: # Edge is a circle if not self.is_sheet_edge_face(theEdge, theNode): found_indices.append(i) # found a node face theNode.child_idx_lists.append([i,theEdge]) #self.index_list.remove(i) # remove this face from the index_list #Part.show(self.f_list[i]) else: nextVert = theEdge.Vertexes[1] for looknextVert in self.f_list[i].Vertexes: if equal_vertex(looknextVert, nextVert): if not self.is_sheet_edge_face(theEdge, theNode): found_indices.append(i) # found a node face theNode.child_idx_lists.append([i,theEdge]) #self.index_list.remove(i) # remove this face from the index_list #Part.show(self.f_list[i]) print "found_indices: ", found_indices def is_sheet_edge_face(self, ise_edge, tree_node): # ise_edge: IsSheetEdge_edge # idea: look at properties of neighbor face # look at edges with distance of sheet-thickness. # if found and surface == cylinder, check if it could be a bend-node. # look at number of edges: # A face with 3 edges is at the sheet edge Cylinder-face or triangle (oh no!) # need to look also at surface! # A sheet edge face with more as 4 edges, is common to more than 1 node. # get the face which has a common edge with ise_edge the_index = None has_sheet_distance_vertex = False for i in self.index_list: for sf_edge in self.f_list[i].Edges: if sf_edge.isSame(ise_edge): the_index = i break if the_index <> None: break # Simple strategy applied: look if the connecting face has vertexes # with sheet-thickness distance to the top face. # fix me: this will fail with sharpened sheet edges with two faces # between top and bottom. if the_index <> None: distVerts = 0 vertList = [] F_type = str(self.f_list[tree_node.idx].Surface) # now we need to search for vertexes with sheet_thickness_distance #if F_type == "<Plane object>": for F_vert in self.f_list[i].Vertexes: if self.isVertOpposite(F_vert, tree_node): has_sheet_distance_vertex = True if len(self.f_list[i].Edges)<5: tree_node.nfIndexes.append(i) self.index_list.remove(i) #Part.show(self.f_list[i]) else: # need to cut the face at the ends of ise_edge self.divideEdgeFace(i, ise_edge, F_vert, tree_node) break else: tree_node.analysis_ok = False tree_node.error_code = 15 # Analysis: the code needs a face at all sheet edges self.error_code = 15 self.failed_face_idx = tree_node.idx Part.show(self.f_list[tree_node.idx]) return has_sheet_distance_vertex def isVertOpposite(self, theVert, theNode): F_type = str(self.f_list[theNode.idx].Surface) vF_vert = Base.Vector(theVert.X, theVert.Y, theVert.Z) if F_type == "<Plane object>": distFailure = vF_vert.distanceToPlane (theNode.facePosi, theNode.axis) - self.__thickness if F_type == "<Cylinder object>": distFailure = vF_vert.distanceToLine (theNode.bendCenter, theNode.axis) - theNode.distCenter # print "counter face distance: ", dist_v + self.__thickness if (distFailure < self.cFaceTol) and (distFailure > -self.cFaceTol): return True else: return False def divideEdgeFace(self, fIdx, ise_edge, F_vert, tree_node): print "Sheet edge face has more than 4 edges!" # first find out where the Sheet edge face has no edge to the opposite side of the sheet # There is a need to cut the face. # make a cut-tool perpendicular to the ise_edge # cut the face and select the good one to add to the node # make another cut, in order to add the residual face(s) to the face list. # Search edges in the face with a vertex common with ise_edge F_type = str(self.f_list[tree_node.idx].Surface) needCut0 = True firstCutFaceIdx = None for sEdge in self.f_list[fIdx].Edges: if equal_vertex(ise_edge.Vertexes[0], sEdge.Vertexes[0]) and \ self.isVertOpposite(sEdge.Vertexes[1], tree_node): needCut0 = False theEdge = sEdge if equal_vertex(ise_edge.Vertexes[0], sEdge.Vertexes[1]) and \ self.isVertOpposite(sEdge.Vertexes[0], tree_node): needCut0 = False theEdge = sEdge if needCut0: #print "need Cut at 0 with fIdx: ", fIdx nFace = self.cutEdgeFace(0, fIdx, ise_edge, tree_node) tree_node.nfIndexes.append(self.max_f_idx) self.f_list.append(nFace) firstCutFaceIdx = self.max_f_idx self.max_f_idx += 1 #self.f_list.append(rFace) #self.index_list.append(self.max_f_idx) #self.max_f_idx += 1 #self.index_list.remove(fIdx) #Part.show(nFace) #else: # Part.show(theEdge) needCut1 = True for sEdge in self.f_list[fIdx].Edges: if equal_vertex(ise_edge.Vertexes[1], sEdge.Vertexes[0]): if self.isVertOpposite(sEdge.Vertexes[1], tree_node): needCut1 = False theEdge = sEdge if equal_vertex(ise_edge.Vertexes[1], sEdge.Vertexes[1]): if self.isVertOpposite(sEdge.Vertexes[0], tree_node): needCut1 = False theEdge = sEdge if needCut1: if needCut0: fIdx = firstCutFaceIdx tree_node.nfIndexes.remove(fIdx) #print "need Cut at 1 with fIdx: ", fIdx nFace = self.cutEdgeFace(1, fIdx, ise_edge, tree_node) tree_node.nfIndexes.append(self.max_f_idx) self.f_list.append(nFace) firstCutFaceIdx = self.max_f_idx self.max_f_idx += 1 #self.f_list.append(rFace) #self.index_list.append(self.max_f_idx) #self.max_f_idx += 1 #if not needCut0: # self.index_list.remove(fIdx) #Part.show(nFace) #else: # Part.show(theEdge) def cutEdgeFace(self, eIdx, fIdx, theEdge, theNode): ''' This function cuts a face in two pieces. one piece is connected to the node. The residual pieces is given for assignment to other nodes. The function returns both pieces of the original face. ''' # print "now the face cutter" if eIdx == 0: otherIdx = 1 else: otherIdx = 0 origin = theEdge.Vertexes[eIdx].Point F_type = str(self.f_list[theNode.idx].Surface) if F_type == "<Plane object>": tan_vec = theEdge.Vertexes[eIdx].Point - theEdge.Vertexes[otherIdx].Point #o_thick = Base.Vector(o_vec.x, o_vec.y, o_vec.z) tan_vec.normalize() vec1 = Base.Vector(theNode.axis.x, theNode.axis.y, theNode.axis.z) # make a copy crossVec = tan_vec.cross(vec1) crossVec.multiply(3.0*self.__thickness) vec1.multiply(self.__thickness) # defining the points of the cutting plane: Spnt1 = origin - theNode.axis - crossVec Spnt2 = origin - theNode.axis + crossVec Spnt3 = origin + theNode.axis + vec1 + crossVec Spnt4 = origin + theNode.axis + vec1 - crossVec if F_type == "<Cylinder object>": ePar = theEdge.parameterAt(theEdge.Vertexes[eIdx]) print "Idx: ", eIdx, " ePar: ", ePar otherPar = theEdge.parameterAt(theEdge.Vertexes[otherIdx]) tan_vec = theEdge.tangentAt(ePar) if ePar < otherPar: tan_vec.multiply(-1.0) #tan_line = Part.makeLine(theEdge.Vertexes[eIdx].Point.add(tan_vec), theEdge.Vertexes[eIdx].Point) #Part.show(tan_line) edge_vec = theEdge.Vertexes[eIdx].copy().Point radVector = radial_vector(edge_vec, theNode.bendCenter, theNode.axis) if theNode.bend_dir == "down": radVector.multiply(-1.0) #rad_line = Part.makeLine(theEdge.Vertexes[eIdx].Point.add(radVector), theEdge.Vertexes[eIdx].Point) #Part.show(rad_line) crossVec = tan_vec.cross(radVector) crossVec.multiply(3.0*self.__thickness) vec1 = Base.Vector(radVector.x, radVector.y, radVector.z) # make a copy vec1.multiply(self.__thickness) # defining the points of the cutting plane: Spnt1 = origin - radVector - crossVec Spnt2 = origin - radVector + crossVec Spnt3 = origin + radVector + vec1 + crossVec Spnt4 = origin + radVector + vec1 - crossVec Sedge1 = Part.makeLine(Spnt1,Spnt2) Sedge2 = Part.makeLine(Spnt2,Spnt3) Sedge3 = Part.makeLine(Spnt3,Spnt4) Sedge4 = Part.makeLine(Spnt4,Spnt1) Sw1 = Part.Wire([Sedge1, Sedge2, Sedge3, Sedge4]) Sf1=Part.Face(Sw1) # cut_solid = Sf1.extrude(tan_vec.multiply(5.0)) #Part.show(cut_solid) #cut_opposite = Sf1.extrude(tan_vec.multiply(-5.0)) cutFaces_node = self.f_list[fIdx].cut(cut_solid) for cFace in cutFaces_node.Faces: for myVert in cFace.Vertexes: if equal_vertex(theEdge.Vertexes[eIdx], myVert): nodeFace = cFace #print "The nodeFace" #Part.show(nodeFace) break ''' cutFaces_residue = self.f_list[fIdx].cut(cut_opposite) for cFace in cutFaces_residue.Faces: for myVert in cFace.Vertexes: if equal_vertex(theEdge.Vertexes[eIdx], myVert): residueFace = cFace #print "the residueFace" #Part.show(residueFace) break ''' return nodeFace #, residueFace def is_sheet_edge3(self, ise_edge, tree_node): # ise_edge: IsSheetEdge_edge # idea: look at properties of neighbor face # look at edges with distance of sheet-thickness. # if found and surface == cylinder, check if it could be a bend-node. # look at number of edges: # A face with 3 edges is at the sheet edge Cylinder-face or triangle (oh no!) # need to look also at surface! # A sheet edge face with more as 4 edges, is common to more than 1 node. # get the face which has a common edge with ise_edge the_index = None has_sheet_distance_vertex = False for i in self.index_list: for sf_edge in self.f_list[i].Edges: if sf_edge.isSame(ise_edge): the_index = i break if the_index <> None: break if the_index <> None: distVerts = 0 vertList = [] F_type = str(self.f_list[tree_node.idx].Surface) # now we need to search for vertexes with sheet_thickness_distance #if F_type == "<Plane object>": for F_vert in self.f_list[i].Vertexes: vF_vert = Base.Vector(F_vert.X, F_vert.Y, F_vert.Z) if F_type == "<Plane object>": distFailure = vF_vert.distanceToPlane (tree_node.facePosi, tree_node.axis) - self.__thickness if F_type == "<Cylinder object>": distFailure = vF_vert.distanceToLine (tree_node.bendCenter, tree_node.axis) - tree_node.distCenter # print "counter face distance: ", dist_v + self.__thickness if (distFailure < self.cFaceTol) and (distFailure > -self.cFaceTol): has_sheet_distance_vertex = True break else: tree_node.analysis_ok = False tree_node.error_code = 15 # Analysis: the code needs a face at all sheet edges self.error_code = 15 self.failed_face_idx = tree_node.idx Part.show(self.f_list[tree_node.idx]) return has_sheet_distance_vertex def make_new_face_node(self, face_idx, P_node, P_edge, wires_e_lists): # e_list: list of edges of the top face of a node without the parent-edge (P_edge) # analyze the face and get type of face ("Flat" or "Bend") # search the counter face, get axis of Face # In case of "Bend" get angle, k_factor and trans_length # put the node into the tree newNode = Simple_node(face_idx, P_node, P_edge) F_type = str(self.__Shape.Faces[face_idx].Surface) # This face should be a node in the tree, and is therefore known! # removed from the list of all unknown faces self.index_list.remove(face_idx) # This means, it could also not be found as neighbor face anymore. #newNode.node_faces.append(self.f_list[face_idx].copy()) newNode.nfIndexes.append(face_idx) such_list = [] for k in self.index_list: such_list.append(k) if F_type == "<Plane object>": newNode.node_type = 'Flat' # fixme print "Face", face_idx+1, " Type: ", newNode.node_type s_Posi = self.__Shape.Faces[face_idx].Surface.Position newNode.facePosi = s_Posi s_Ori = self.__Shape.Faces[face_idx].Orientation s_Axis = self.__Shape.Faces[face_idx].Surface.Axis if s_Ori == 'Forward': ext_Vec = Base.Vector(-s_Axis.x, -s_Axis.y, -s_Axis.z) else: ext_Vec = Base.Vector(s_Axis.x, s_Axis.y, s_Axis.z) newNode.axis = ext_Vec axis_line = Part.makeLine(s_Posi.add(ext_Vec), s_Posi) # Part.show(axis_line) # nead a mean point of the face to avoid false counter faces faceMiddle = Base.Vector(0.0,0.0,0.0) # calculating a mean vector for Vvec in self.__Shape.Faces[face_idx].OuterWire.Vertexes: faceMiddle = faceMiddle.add(Vvec.Point) faceMiddle = faceMiddle.multiply(1.0/len(self.__Shape.Faces[face_idx].OuterWire.Vertexes)) # search for the counter face for i in such_list: counter_found = True for F_vert in self.f_list[i].Vertexes: vF_vert = Base.Vector(F_vert.X, F_vert.Y, F_vert.Z) dist_v = vF_vert.distanceToPlane (s_Posi, ext_Vec) - self.__thickness # print "counter face distance: ", dist_v + self.__thickness if (dist_v > self.cFaceTol) or (dist_v < -self.cFaceTol): counter_found = False if counter_found: # nead a mean point of the face to avoid false counter faces counterMiddle = Base.Vector(0.0,0.0,0.0) # calculating a mean vector for Vvec in self.__Shape.Faces[i].OuterWire.Vertexes: counterMiddle = counterMiddle.add(Vvec.Point) counterMiddle = counterMiddle.multiply(1.0/len(self.__Shape.Faces[i].OuterWire.Vertexes)) distVector = counterMiddle.sub(faceMiddle) counterDistance = distVector.Length if counterDistance < 3*self.__thickness: print "found counter-face", i + 1 newNode.c_face_idx = i self.index_list.remove(i) newNode.nfIndexes.append(i) # Part.show(self.__Shape.Faces[newNode.c_face_idx]) else: counter_found = False print "faceMiddle: ", str(faceMiddle), "counterMiddle: ", str(counterMiddle) #if newNode.c_face_idx == None: # Part.show(axis_line) if F_type == "<Cylinder object>": newNode.node_type = 'Bend' # fixme s_Center = self.__Shape.Faces[face_idx].Surface.Center s_Axis = self.__Shape.Faces[face_idx].Surface.Axis newNode.axis = s_Axis newNode.bendCenter = s_Center edge_vec = P_edge.Vertexes[0].copy().Point print "edge_vec: ", str(edge_vec) if P_node.node_type == 'Flat': dist_c = edge_vec.distanceToPlane (s_Center, P_node.axis) # distance to center else: P_face = self.__Shape.Faces[P_node.idx] radVector = radial_vector(edge_vec, P_face.Surface.Center, P_face.Surface.Axis) if P_node.bend_dir == "down": dist_c = edge_vec.distanceToPlane (s_Center, radVector.multiply(-1.0)) else: dist_c = edge_vec.distanceToPlane (s_Center, radVector) if dist_c < 0.0: newNode.bend_dir = "down" thick_test = self.__Shape.Faces[face_idx].Surface.Radius - self.__thickness else: newNode.bend_dir = "up" thick_test = self.__Shape.Faces[face_idx].Surface.Radius + self.__thickness newNode.distCenter = thick_test # print "Face idx: ", face_idx, " bend_dir: ", newNode.bend_dir print "Face", face_idx+1, " Type: ", newNode.node_type, " bend_dir: ", newNode.bend_dir # need to define the bending angle relative to the Center and the # Axis of the bend surface: # search for line-edges with no common point with edge_vec and fixme: not in the same line # fixme: there could be more than one edge meeting the criteria! number_c_edges = 0 for s_edge in self.__Shape.Faces[face_idx].Edges: c_edg_found = True type_str = str(s_edge.Curve) if type_str.find('Line') == -1: c_edg_found = False # print "found circle in c_edge search" else: # print "found line in c_edge search" for E_vert in s_edge.Vertexes: if equal_vertex(E_vert, P_edge.Vertexes[0]): c_edg_found = False if c_edg_found: c_edge = s_edge number_c_edges = number_c_edges + 1 # print " found the second Line edge of the bend face" # Part.show(c_edge) if number_c_edges > 1: newNode.analysis_ok = False # the code can not handle bend faces with more than one child! newNode.error_code = 12 # ('more than one bend-childs') self.error_code = 12 self.failed_face_idx = face_idx #Start to investigate the angles at self.__Shape.Faces[face_idx].ParameterRange[0] angle_0 = self.__Shape.Faces[face_idx].ParameterRange[0] angle_1 = self.__Shape.Faces[face_idx].ParameterRange[1] length_0 = self.__Shape.Faces[face_idx].ParameterRange[2] length_1 = self.__Shape.Faces[face_idx].ParameterRange[3] # idea: identify the angle at edge_vec = P_edge.Vertexes[0].copy().Point # This will be = angle_start # identify rotSign from angle_end minus angle_start # The tangentvector will be in direction of position at angle_sta + rotSign*90° # calculate the tan_vec from valueAt parPos00 = self.__Shape.Faces[face_idx].valueAt(angle_0,length_0) parPos01 = self.__Shape.Faces[face_idx].valueAt(angle_0,length_1) parPos10 = self.__Shape.Faces[face_idx].valueAt(angle_1,length_0) parPos11 = self.__Shape.Faces[face_idx].valueAt(angle_1,length_1) if equal_vector(edge_vec, parPos00): print "got case 00" angle_start = angle_0 angle_end = angle_1 len_start = length_0 else: if equal_vector(edge_vec, parPos01): print "got case 01" angle_start = angle_0 angle_end = angle_1 len_start = length_1 else: if equal_vector(edge_vec, parPos10): print "got case 10" angle_start = angle_1 angle_end = angle_0 len_start = length_0 else: if equal_vector(edge_vec, parPos11): print "got case 11" angle_start = angle_1 angle_end = angle_0 len_start = length_1 else: newNode.analysis_ok = False newNode.error_code = 16 # Analysis: did not find startangle of bend self.error_code = 16 self.failed_face_idx = face_idx print "did not found start angle, to do to fix" newNode.bend_angle = angle_end - angle_start if newNode.bend_angle < 0.0: angle_tan = angle_start - math.pi/2.0 # newNode.bend_angle = -newNode.bend_angle else: angle_tan = angle_start + math.pi/2.0 tanPos = self.__Shape.Faces[face_idx].valueAt(angle_tan,len_start) tan_vec = radial_vector(tanPos, s_Center, s_Axis) newNode.tan_vec = tan_vec first_vec = radial_vector(edge_vec, s_Center, s_Axis) cross_vec = first_vec.cross(tan_vec) triple_prod = cross_vec.dot(s_Axis) print " the new bend_angle: ", math.degrees(newNode.bend_angle), "triple_prod: ", triple_prod # testing showed, that the bend_angle has to be changed in sign # at the following conditions. if ((triple_prod > 0.0) and (newNode.bend_angle > 0.0)) or \ ((triple_prod < 0.0) and (newNode.bend_angle < 0.0)): newNode.bend_angle = -newNode.bend_angle print "minus bend_angle" if newNode.bend_dir == 'up': k_Factor = 0.65 + 0.5*math.log10(self.__Shape.Faces[face_idx].Surface.Radius/self.__thickness) print "Face", newNode.idx+1, " k-factor: ", k_Factor newNode._trans_length = (self.__Shape.Faces[face_idx].Surface.Radius + k_Factor * self.__thickness/2.0) * newNode.bend_angle else: k_Factor = 0.65 + 0.5*math.log10((self.__Shape.Faces[face_idx].Surface.Radius - self.__thickness)/self.__thickness) newNode._trans_length = (self.__Shape.Faces[face_idx].Surface.Radius - self.__thickness \ + k_Factor * self.__thickness/2.0) * newNode.bend_angle if newNode._trans_length < 0.0: newNode._trans_length = -newNode._trans_length # the _trans_length is always positive, due to correct tan_vec # calculate mean point of face: # fixme implement also for cylindric faces # Search the face at the opposite site of the sheet: #for i in range(len(such_list)): for i in such_list: counter_found = True for F_vert in self.f_list[i].Vertexes: vF_vert = Base.Vector(F_vert.X, F_vert.Y, F_vert.Z) dist_c = vF_vert.distanceToLine (s_Center, s_Axis) - thick_test if (dist_c > self.cFaceTol) or (dist_c < -self.cFaceTol): counter_found = False if counter_found: # to do calculate mean point of counter face #print "found counter Face", such_list[i]+1 newNode.c_face_idx = i self.index_list.remove(i) newNode.nfIndexes.append(i) # Part.show(self.__Shape.Faces[newNode.c_face_idx]) break # Part.show(self.__Shape.Faces[newNode.c_face_idx]) # Part.show(self.__Shape.Faces[newNode.idx]) if newNode.c_face_idx == None: newNode.analysis_ok = False newNode.error_code = 13 # Analysis: counter face not found self.error_code = 13 self.failed_face_idx = face_idx print "No counter-face Debugging Thickness: ", self.__thickness Part.show(self.__Shape.Faces[face_idx]) # now we call the new code self.get_node_faces(newNode, wires_e_lists) #for nFace in newNode.nfIndexes: # Part.show(nFace) if P_node == None: self.root = newNode else: P_node.child_list.append(newNode) return newNode def search_face(self, sf_edge, the_node): # search for the connecting face to sf_edge in the faces of a node search_List = the_node.nfIndexes[:] search_List.remove(the_node.idx) the_index = None for i in search_List: #self.index_list: for n_edge in self.f_list[i].Edges: if sf_edge.isSame(n_edge): the_index = i #if the_index == None: # the_node.analysis_ok = False # the code can not handle? edges without neighbor faces # the_node.error_code = 14 # Analysis: the code can not handle? edges without neighbor faces # self.error_code = 14 # self.failed_face_idx = the_node.idx return the_index def Bend_analysis(self, face_idx, parent_node = None, parent_edge = None): # This functions traverses the shape in order to build the bend-tree # For each relevant face a t_node is created and linked into the tree # the linking is done in the call of self.make_new_face_node #print "Bend_analysis Face", face_idx +1 , # analysis_ok = True # not used anymore? # edge_list = [] wires_edge_lists = [] wire_idx = -1 for n_wire in self.f_list[face_idx].Wires: wire_idx += 1 wires_edge_lists.append([]) #for n_edge in self.__Shape.Faces[face_idx].Edges: for n_edge in n_wire.Edges: if parent_edge: if not parent_edge.isSame(n_edge): #edge_list.append(n_edge) wires_edge_lists[wire_idx].append(n_edge) # else: #edge_list.append(n_edge) wires_edge_lists[wire_idx].append(n_edge) if parent_node: print " Parent Face", parent_node.idx + 1 print "Die Liste: ", self.index_list t_node = self.make_new_face_node(face_idx, parent_node, parent_edge, wires_edge_lists) # Need also the edge_list in the node! print "Die Liste nach make_new_face_node: ", self.index_list # in the new code, only the list of child faces will be analyzed. removalList = [] for child_info in t_node.child_idx_lists: if child_info[0] in self.index_list: print "child in List: ", child_info[0] self.Bend_analysis(child_info[0], t_node, child_info[1]) else: print "remove child from List: ", child_info[0] t_node.seam_edges.append(child_info[1]) # give Information to the node, that it has a seam. print "node faces before: ", t_node.nfIndexes self.makeSeamFace(child_info[1], t_node) removalList.append(child_info) print "node faces with seam: ", t_node.nfIndexes otherSeamNode = self.searchNode(child_info[0], self.root) print "counterface on otherSeamNode: Face", otherSeamNode.c_face_idx+1 self.makeSeamFace(child_info[1], otherSeamNode) #t_node.analysis_ok = False # the code can not handle? edges without neighbor faces #t_node.error_code = 14 # Analysis: the code can not handle? edges without neighbor faces #self.error_code = 14 #self.failed_face_idx = t_node.idx #break for seams in removalList: t_node.child_idx_lists.remove(seams) def searchNode(self, theIdx, sNode): # search for a Node with theIdx in sNode.idx print "my Idx: ", sNode.idx if sNode.idx == theIdx: return sNode else: result = None childFaces = [] for n_node in sNode.child_list: childFaces.append(n_node.idx) print "my children: ", childFaces for n_node in sNode.child_list: nextSearch = self.searchNode(theIdx, n_node) if nextSearch <> None: result = nextSearch break if result<>None: print "this is the result: ", result.idx else: print "this is the result: ", None return result # suche bei mir. wenn ja liefere ab # sonst sind Kinder da? # Wenn Kinder vorhanden, frag solange Kinder bis gefunden # oder kein Kind mehr da. def makeSectionWire(self, theEdge, W_node, Dir = 'up'): #print "mSW Face", W_node.idx +1 # makes a Section wire through the shape # The section wire is used to generate a new flat shell # for the bend faces. origin = theEdge.Vertexes[0].Point o_vec = theEdge.Vertexes[1].Point - theEdge.Vertexes[0].Point o_thick = Base.Vector(o_vec.x, o_vec.y, o_vec.z) o_thick.normalize().multiply(2.0 * self.__thickness) s_Center = self.f_list[W_node.idx].Surface.Center s_Axis = self.f_list[W_node.idx].Surface.Axis vec1 = radial_vector(origin, s_Center, s_Axis) vec1.multiply(self.__thickness) # defining the points of the section plane: if W_node.bend_dir == 'up': Spnt1 = origin - vec1 - o_thick Spnt2 = origin - vec1 + o_vec + o_thick Spnt3 = origin + vec1 + vec1 + o_vec + o_thick Spnt4 = origin + vec1 + vec1 - o_thick else: Spnt4 = origin - vec1 - vec1 - o_thick Spnt3 = origin - vec1 - vec1 + o_vec + o_thick Spnt2 = origin + vec1 + o_vec + o_thick Spnt1 = origin + vec1 - o_thick Sedge1 = Part.makeLine(Spnt1,Spnt2) Sedge2 = Part.makeLine(Spnt2,Spnt3) Sedge3 = Part.makeLine(Spnt3,Spnt4) Sedge4 = Part.makeLine(Spnt4,Spnt1) Sw1 = Part.Wire([Sedge1, Sedge2, Sedge3, Sedge4]) Sf1=Part.Face(Sw1) # # Part.show(Sf1) # find the nearest vertex of theEdge to a plane through s_Center # The section-wire should start at this vertex if (theEdge.Vertexes[0].Point.distanceToPlane(s_Center, s_Axis) < theEdge.Vertexes[1].Point.distanceToPlane(s_Center, s_Axis)): next_pnt = theEdge.Vertexes[1] start_pnt = theEdge.Vertexes[0] start_idx = 0 end_idx = 1 else: next_pnt = theEdge.Vertexes[0] start_pnt = theEdge.Vertexes[1] start_idx = 1 end_idx = 0 # for i in self.index_list: for i in W_node.nfIndexes: singleEdge = Sf1.section(self.f_list[i]) # Part.show(singleEdge) #print "section edges: ", len(singleEdge.Edges) for j in range(len(singleEdge.Edges)): if (equal_vertex(singleEdge.Edges[j].Vertexes[0], start_pnt)): lastEdge = singleEdge.Edges[j].copy() lastConnect = 1 if (equal_vertex(singleEdge.Edges[j].Vertexes[1], start_pnt)): lastEdge = singleEdge.Edges[j].copy() lastConnect = 0 if (equal_vertex(singleEdge.Edges[j].Vertexes[0], next_pnt)): nextEdge = singleEdge.Edges[j].copy() nextConnect = 1 if (equal_vertex(singleEdge.Edges[j].Vertexes[1], next_pnt)): nextEdge = singleEdge.Edges[j].copy() nextConnect = 0 startEdge = Part.makeLine(start_pnt.Point, next_pnt.Point) middleEdge = Part.makeLine(nextEdge.Vertexes[nextConnect].Point, lastEdge.Vertexes[lastConnect].Point) Swire1 = Part.Wire([startEdge, nextEdge, middleEdge, lastEdge ]) # Part.show(Swire1) print "finisch mSW Face", W_node.idx +1 return Swire1 def generateBendShell(self, bend_node): #print "genBendShell Face", bend_node.idx +1 # make new flat faces for the bend_node and return them # the k-Factor is already included in bend_node._trans_length # Part.show(self.__Shape.copy()) flat_shell = [] trans_vec = bend_node.tan_vec * bend_node._trans_length # o_edge: originating edge of the bend = parent edge o_edge = bend_node.p_edge.copy() # We want a section wire at the start of the bend_node, in order # to regenerate a flat body with this section wire. # 3 vectors are needed to generate a section plane: vec1 and # a vector from o_edge and a vector with same direction of o_edge, # but with a length of two times the thickness o_wire = self.makeSectionWire(o_edge, bend_node, bend_node.bend_dir) #Part.show(o_wire) # The same vectors are needed for the other side of the bend face if len(bend_node.child_list)>=1: child_node = bend_node.child_list[0] # fixme: there could be more than one child node for a bend face. # bend_edge = bend_node.edge_pool[child_node.idx][0] bend_edge = child_node.p_edge.copy() b_wire = self.makeSectionWire(bend_edge, bend_node, bend_node.bend_dir).copy() else: number_c_edges = 0 for s_edge in self.f_list[bend_node.idx].Edges: c_edg_found = True type_str = str(s_edge.Curve) if type_str.find('Line') == -1: c_edg_found = False print "found circle in c_edge search in bend Face", bend_node.idx+1 else: print "found line in c_edge search in bend Face", bend_node.idx+1 for E_vert in s_edge.Vertexes: if equal_vertex(E_vert, bend_node.p_edge.Vertexes[0]): c_edg_found = False if c_edg_found: bend_edge = s_edge number_c_edges = number_c_edges + 1 print " found the second Line edge of the bend Face", bend_node.idx+1 #Part.show(bend_edge) t_idx = self.search_face(bend_edge, bend_node) # Part.show(self.f_list[t_idx]) if t_idx <> None: topFace = self.f_list[t_idx].copy() topFace.rotate(self.f_list[bend_node.idx].Surface.Center,bend_node.axis,math.degrees(bend_node.bend_angle)) topFace.translate(trans_vec) flat_shell.append(topFace) s_Center = self.f_list[bend_node.idx].Surface.Center s_Axis = self.f_list[bend_node.idx].Surface.Axis # find the nearest vertex of bend_edge to a plane through s_Center # The section-wire should start at this vertex if (bend_edge.Vertexes[0].Point.distanceToPlane(s_Center, s_Axis) < bend_edge.Vertexes[1].Point.distanceToPlane(s_Center, s_Axis)): next_pnt = bend_edge.Vertexes[1].Point start_pnt = bend_edge.Vertexes[0].Point start_idx = 0 end_idx = 1 else: next_pnt = bend_edge.Vertexes[0].Point start_pnt = bend_edge.Vertexes[1].Point start_idx = 1 end_idx = 0 b_wireList = self.f_list[t_idx].Edges[:] #for remEdge in b_wireList: # print "in b_wireList" # Part.show(remEdge) for remEdge in b_wireList: # Part.show(remEdge) if remEdge.isSame(bend_edge): b_wireList.remove(remEdge) break for singleEdge in b_wireList: #Part.show(singleEdge) # print "section edges: ", len(singleEdge.Edges) if len(singleEdge.Edges) == 1: if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[0].Point, start_pnt)): lastEdge = singleEdge.Edges[0].copy() lastConnect = 1 if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[1].Point, start_pnt)): lastEdge = singleEdge.Edges[0].copy() lastConnect = 0 if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[0].Point, next_pnt)): nextEdge = singleEdge.Edges[0].copy() nextConnect = 1 if (DraftVecUtils.equals(singleEdge.Edges[0].Vertexes[1].Point, next_pnt)): nextEdge = singleEdge.Edges[0].copy() nextConnect = 0 startEdge = Part.makeLine(start_pnt, next_pnt) middleEdge = Part.makeLine(nextEdge.Vertexes[nextConnect].Point, lastEdge.Vertexes[lastConnect].Point) b_wire = Part.Wire([startEdge, nextEdge, middleEdge, lastEdge ]) # Part.show(Swire1) else: print "Found no Face?!" # there is a seam in the metal sheet. # Generate a new face for the seam. ''' b_wire = self.makeSectionWire(bend_edge, bend_node, bend_node.bend_dir).copy() topFace = Part.Face(b_wire) #self.f_list.append(topFace) topFace.rotate(self.f_list[bend_node.idx].Surface.Center,bend_node.axis,math.degrees(bend_node.bend_angle)) topFace.translate(trans_vec) flat_shell.append(topFace) ''' ''' # find the nearest vertex of bend_edge to a plane through s_Center # The section-wire should start at this vertex if (bend_edge.Vertexes[0].Point.distanceToPlane(s_Center, s_Axis) < bend_edge.Vertexes[1].Point.distanceToPlane(s_Center, s_Axis)): next_pnt = bend_edge.Vertexes[1].Point start_pnt = bend_edge.Vertexes[0].Point start_idx = 0 end_idx = 1 else: next_pnt = bend_edge.Vertexes[0].Point start_pnt = bend_edge.Vertexes[1].Point start_idx = 1 end_idx = 0 # find the nextEdge in the node faces search_List = bend_node.nfIndexes[:] search_List.remove(bend_node.idx) the_index = None next_idx = None for i in search_List: for theEdge in self.f_list[i].Edges: if len(theEdge.Vertexes)>1: if equal_vector(theEdge.Vertexes[0].Point, next_pnt): next_idx = 1 if equal_vector(theEdge.Vertexes[1].Point, next_pnt): next_idx = 0 if next_idx <> None: if self.isVertOpposite(theEdge.Vertexes[next_idx], bend_node): nextEdge = theEdge.copy() search_List.remove(i) the_index = i break else: next_idx = None if the_index <> None: break #find the lastEdge last_idx = None for i in search_List: for theEdge in self.f_list[i].Edges: if len(theEdge.Vertexes)>1: if equal_vector(theEdge.Vertexes[0].Point, start_pnt): last_idx = 1 if equal_vector(theEdge.Vertexes[1].Point, start_pnt): last_idx = 0 if last_idx <> None: if self.isVertOpposite(theEdge.Vertexes[last_idx], bend_node): lastEdge = theEdge.copy() search_List.remove(i) the_index = i break else: last_idx = None if the_index <> None: break # find the middleEdge for theEdge in self.f_list[bend_node.c_face_idx].Edges: if len(theEdge.Vertexes)>1: if equal_vector(theEdge.Vertexes[0].Point, start_pnt): last_idx = 1 if equal_vector(theEdge.Vertexes[1].Point, start_pnt): last_idx = 0 if last_idx <> None: if self.isVertOpposite(theEdge.Vertexes[last_idx], bend_node): lastEdge = theEdge.copy() search_List.remove(i) the_index = i break else: last_idx = None ''' b_wire.rotate(self.f_list[bend_node.idx].Surface.Center,bend_node.axis,math.degrees(bend_node.bend_angle)) b_wire.translate(trans_vec) #Part.show(b_wire) #for vert in b_wire.Vertexes: # print "b_wire1 tol: ", vert.Tolerance #for ed in b_wire.Edges: # print "b_wire1 tol: ", ed.Vertexes[0].Tolerance, " ", ed.Vertexes[1].Tolerance sweep_path = Part.makeLine(o_wire.Vertexes[0].Point, b_wire.Vertexes[0].Point) #Part.show(sweep_path) Bend_shell = Part.makeRuledSurface (o_wire, b_wire) # Part.show(Bend_shell) for shell_face in Bend_shell.Faces: flat_shell.append(shell_face ) #Part.show(self.__Shape.copy()) #print "finish genBendShell Face", bend_node.idx +1 return flat_shell def makeSeamFace(self, sEdge, theNode): ''' This function creates a face at a seam of the sheet metal. It works currently only at a flat node. ''' print "now make a seam Face" nextVert = sEdge.Vertexes[1] startVert = sEdge.Vertexes[0] start_idx = 0 end_idx = 1 search_List = theNode.nfIndexes[:] print "This is the search_List: ", search_List search_List.remove(theNode.idx) the_index = None next_idx = None for i in search_List: for theEdge in self.f_list[i].Edges: if len(theEdge.Vertexes)>1: if equal_vertex(theEdge.Vertexes[0], nextVert): next_idx = 1 if equal_vertex(theEdge.Vertexes[1], nextVert): next_idx = 0 if next_idx <> None: if self.isVertOpposite(theEdge.Vertexes[next_idx], theNode): nextEdge = theEdge.copy() search_List.remove(i) the_index = i #Part.show(nextEdge) break else: next_idx = None if the_index <> None: break #find the lastEdge last_idx = None print "This is the search_List: ", search_List for i in search_List: #Part.show(self.f_list[i]) for theEdge in self.f_list[i].Edges: print "find last Edge in Face: ", i, " at Edge: ", theEdge if len(theEdge.Vertexes)>1: if equal_vertex(theEdge.Vertexes[0], startVert): last_idx = 1 if equal_vertex(theEdge.Vertexes[1], startVert): last_idx = 0 if last_idx <> None: print "test for the last Edge" if self.isVertOpposite(theEdge.Vertexes[last_idx], theNode): lastEdge = theEdge.copy() search_List.remove(i) the_index = i #Part.show(lastEdge) break else: last_idx = None if last_idx <> None: break # find the middleEdge mid_idx = None midEdge = None for theEdge in self.f_list[theNode.c_face_idx].Edges: if len(theEdge.Vertexes)>1: if equal_vertex(theEdge.Vertexes[0], nextEdge.Vertexes[next_idx]): mid_idx = 1 if equal_vertex(theEdge.Vertexes[1], nextEdge.Vertexes[next_idx]): mid_idx = 0 if mid_idx <> None: if equal_vertex(theEdge.Vertexes[mid_idx], lastEdge.Vertexes[last_idx]): midEdge = theEdge.copy() #Part.show(midEdge) break else: mid_idx = None if midEdge: break seam_wire = Part.Wire([sEdge, nextEdge, midEdge, lastEdge ]) seamFace = Part.Face(seam_wire) self.f_list.append(seamFace) theNode.nfIndexes.append(self.max_f_idx) self.max_f_idx += 1 def showFaces(self): for i in self.index_list: Part.show(self.f_list[i]) def unfold_tree2(self, node): # This function traverses the tree and unfolds the faces # beginning at the outermost nodes. #print "unfold_tree face", node.idx + 1 theShell = [] nodeShell = [] for n_node in node.child_list: if self.error_code == None: theShell = theShell + self.unfold_tree2(n_node) if node.node_type == 'Bend': trans_vec = node.tan_vec * node._trans_length for bFaces in theShell: bFaces.rotate(self.f_list[node.idx].Surface.Center,node.axis,math.degrees(node.bend_angle)) bFaces.translate(trans_vec) if self.error_code == None: nodeShell = self.generateBendShell(node) else: if self.error_code == None: # nodeShell = self.generateShell(node) for idx in node.nfIndexes: nodeShell.append(self.f_list[idx].copy()) #if len(node.seam_edges)>0: # for seamEdge in node.seam_edges: # self.makeSeamFace(seamEdge, node) print "ufo finish face",node.idx +1 return (theShell + nodeShell) mylist = Gui.Selection.getSelectionEx() # print 'Die Selektion: ',mylist # print 'Zahl der Selektionen: ', mylist.__len__() if mylist.__len__() == 0: mw=FreeCADGui.getMainWindow() QtGui.QMessageBox.information(mw,"Error","""One flat face needs to be selected!""") else: if mylist.__len__() > 1: mw=FreeCADGui.getMainWindow() QtGui.QMessageBox.information(mw,"Error","""Only one flat face has to be selected!""") else: o = Gui.Selection.getSelectionEx()[0] print o.ObjectName if len(o.SubObjects)>1: mw=FreeCADGui.getMainWindow() QtGui.QMessageBox.information(mw,"SubelementError","""Only one flat face has to be selected!""") else: subelement = o.SubObjects[0] if hasattr(subelement,'Surface'): s_type = str(subelement.Surface) if s_type == "<Plane object>": mw=FreeCADGui.getMainWindow() #QtGui.QMessageBox.information(mw,"Hurra","""Lets try unfolding!""") print "name: ",subelement f_number = int(o.SubElementNames[0].lstrip('Face'))-1 #print f_number startzeit = time.clock() TheTree = SheetTree(o.Object.Shape, f_number) # initializes the tree-structure if TheTree.error_code == None: TheTree.Bend_analysis(f_number, None) # traverses the shape builds the tree-structure endzeit = time.clock() print "Analytical time: ",endzeit-startzeit if TheTree.error_code == None: # TheTree.showFaces() theFaceList = TheTree.unfold_tree2(TheTree.root) # traverses the tree-structure if TheTree.error_code == None: unfoldTime = time.clock() print "time to run the unfold: ", unfoldTime - endzeit try: newShell = Part.Shell(theFaceList) except: print("couldn't join some faces, show only single faces") for newFace in theFaceList: Part.show(newFace) else: try: TheSolid = Part.Solid(newShell) solidTime = time.clock() print "time to make the solid: ", solidTime - unfoldTime except: print ("couldn't make a solid, show only a shell, Faces in List: ", len(theFaceList)) Part.show(newShell) showTime = time.clock() print "Show time: ", showTime - unfoldTime else: Part.show(TheSolid) showTime = time.clock() print "Show time: ", showTime - solidTime, " total time: ", showTime - startzeit if TheTree.error_code <> None: print "Error ", unfold_error[TheTree.error_code], print " at Face", TheTree.failed_face_idx+1 QtGui.QMessageBox.information(mw,"Error",unfold_error[TheTree.error_code]) else: print "unfold successful" else: mw=FreeCADGui.getMainWindow() QtGui.QMessageBox.information(mw,"Selection Error","""Sheet UFO works only with a flat face as starter!\n Select a flat face.""") else: mw=FreeCADGui.getMainWindow() QtGui.QMessageBox.information(mw,"Selection Error","""Sheet UFO works only with a flat face as starter!\n Select a flat face.""")