Macro Sheet Metal Unfolder: Difference between revisions
(Marked this version for translation) |
(Removed script from the page, added GitHub link) |
||
Line 17: | Line 17: | ||
<!--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 25: | Line 25: | ||
==Script== <!--T:6--> |
==Script== <!--T:6--> |
||
The script can be viewed and downloaded from the FreeCAD-macros repository: [https://github.com/FreeCAD/FreeCAD-macros/blob/master/SheetMetalUnfolder.FCMacro SheetMetalUnfolder.FCMacro] |
|||
The version below is revision 12 of sheet_ufo. |
|||
</translate> |
|||
(Right-click on the "Raw" button and select "Save target as..." to download) |
|||
{{Code|code= |
|||
#!/usr/bin/env python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# sheet_ufo.py |
|||
# |
|||
# Copyright 2014 Ulrich Brammer <ulrich1a[at]users.sourceforge.net> |
|||
# |
|||
# 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. |
|||
# |
|||
# |
|||
''' |
|||
def main(): |
|||
return 0 |
|||
if __name__ == '__main__': |
|||
main() |
|||
''' |
|||
import Part, FreeCADGui |
|||
from PySide import QtGui |
|||
from FreeCAD import Base |
|||
import DraftVecUtils, DraftGeomUtils, math |
|||
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: '), |
|||
# 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 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): |
|||
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 |
|||
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 |
|||
self.sheet_edges = [] # List of edges without child-face |
|||
self.axis = 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 |
|||
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 |
|||
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. |
|||
self.index_list =[] |
|||
for i in range(len (self.__Shape.Faces)): |
|||
# if i<>(f_idx): |
|||
self.index_list.append(i) |
|||
#print self.index_list |
|||
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 make_new_face_node(self, face_idx, P_node, P_edge ): |
|||
# analyze the face and get type of face |
|||
# 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. |
|||
such_list = [] |
|||
for k in range(len(self.index_list)): |
|||
such_list.append(self.index_list[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 |
|||
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 range(len(such_list)): |
|||
counter_found = True |
|||
for F_vert in self.__Shape.Faces[such_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[such_list[i]].OuterWire.Vertexes: |
|||
counterMiddle = counterMiddle.add(Vvec.Point) |
|||
counterMiddle = counterMiddle.multiply(1.0/len(self.__Shape.Faces[such_list[i]].OuterWire.Vertexes)) |
|||
distVector = counterMiddle.sub(faceMiddle) |
|||
counterDistance = distVector.Length |
|||
if counterDistance < 3*self.__thickness: |
|||
print "found counter-face", such_list[i]+1 |
|||
newNode.c_face_idx = such_list[i] |
|||
self.index_list.remove(such_list[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 |
|||
edge_vec = P_edge.Vertexes[0].copy().Point |
|||
if P_node.node_type == 'Flat': |
|||
dist_c = edge_vec.distanceToPlane (s_Center, P_node.axis) |
|||
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) |
|||
''' |
|||
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: |
|||
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 |
|||
# 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 |
|||
# Calculate the signed rotation angle |
|||
vec1 = radial_vector(edge_vec, s_Center, s_Axis) |
|||
vec2 = radial_vector(c_edge.Vertexes[0].Point, s_Center, s_Axis) |
|||
#newNode.bend_angle = DraftVecUtils.angle(vec2, vec1, s_Axis) |
|||
draftAngle = DraftVecUtils.angle(vec2, vec1, s_Axis) |
|||
newNode.bend_angle = self.__Shape.Faces[face_idx].ParameterRange[1] \ |
|||
-self.__Shape.Faces[face_idx].ParameterRange[0] |
|||
if (newNode.bend_angle < math.pi) and (newNode.bend_angle > -math.pi): |
|||
if draftAngle * newNode.bend_angle < 0.0: |
|||
newNode.bend_angle = -newNode.bend_angle |
|||
else: |
|||
if draftAngle * newNode.bend_angle > 0.0: |
|||
newNode.bend_angle = -newNode.bend_angle |
|||
print "Face_idx ", face_idx, " bend_angle ", math.degrees(newNode.bend_angle) |
|||
tan_vec = vec1.cross(s_Axis) |
|||
# Compare sign of the angle between vector vec3 and vec1 with the sign of the bend_angle |
|||
vec3 = radial_vector(edge_vec + tan_vec, s_Center, s_Axis) |
|||
test_angle = DraftVecUtils.angle(vec3, vec1, s_Axis) |
|||
#vecTest = (edge_vec + tan_vec) |
|||
#vec1_test = Part.makeLine(edge_vec, edge_vec.add(vec1)) |
|||
#Part.show(vec1_test) |
|||
#angle_test = Part.makeLine(vecTest, vecTest.add(vec3)) |
|||
#Part.show(angle_test) |
|||
if (test_angle * newNode.bend_angle) < 0.0: |
|||
newNode.tan_vec = tan_vec.multiply(-1.0) |
|||
else: |
|||
newNode.tan_vec = tan_vec |
|||
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 |
|||
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 |
|||
# 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)): |
|||
counter_found = True |
|||
for F_vert in self.__Shape.Faces[such_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: |
|||
# calculate mean point of counter face |
|||
print "found counter Face", such_list[i]+1 |
|||
newNode.c_face_idx = such_list[i] |
|||
self.index_list.remove(such_list[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 |
|||
if P_node == None: |
|||
self.root = newNode |
|||
else: |
|||
P_node.child_list.append(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): |
|||
# search for the connecting face to sf_edges in the faces |
|||
the_index = None |
|||
for i in self.index_list: |
|||
for n_edge in self.__Shape.Faces[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 = [] |
|||
for n_edge in self.__Shape.Faces[face_idx].Edges: |
|||
if parent_edge: |
|||
if not parent_edge.isSame(n_edge): |
|||
edge_list.append(n_edge) |
|||
# |
|||
else: |
|||
edge_list.append(n_edge) |
|||
if parent_node: |
|||
print " Parent Face", parent_node.idx + 1 |
|||
t_node = self.make_new_face_node(face_idx, parent_node, parent_edge) |
|||
for n_edge in edge_list: |
|||
if self.is_sheet_edge(n_edge, t_node): |
|||
#if self.is_sheet_edge2(n_edge, t_node): |
|||
t_node.sheet_edges.append(n_edge) |
|||
else: |
|||
next_face = self.search_face(n_edge, t_node) |
|||
if (next_face <> None) and (self.error_code == None): |
|||
self.Bend_analysis(next_face, t_node, n_edge) |
|||
if self.error_code <> None: |
|||
break |
|||
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.__Shape.Faces[W_node.idx].Surface.Center |
|||
s_Axis = self.__Shape.Faces[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: |
|||
singleEdge = Sf1.section(self.__Shape.Faces[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.__Shape.Faces[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" |
|||
else: |
|||
# print "found line in c_edge search" |
|||
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" |
|||
#Part.show(bend_edge) |
|||
t_idx = self.search_face(bend_edge, bend_node) |
|||
#Part.show(self.__Shape.Faces[t_idx]) |
|||
if t_idx <> None: |
|||
if t_idx in self.index_list: |
|||
self.index_list.remove(t_idx) |
|||
topFace = self.__Shape.Faces[t_idx].copy() |
|||
topFace.rotate(self.__Shape.Faces[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.__Shape.Faces[bend_node.idx].Surface.Center |
|||
s_Axis = self.__Shape.Faces[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.__Shape.Faces[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) |
|||
b_wire.rotate(self.__Shape.Faces[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 makeCutTool(self, theEdge, W_node): |
|||
print "mCT Face", W_node.idx +1 |
|||
# makes a Section wire through the shape |
|||
# and generates a cutTool to be used to cut faces at the sheet edges |
|||
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) |
|||
vec1 = Base.Vector(W_node.axis.x, W_node.axis.y, W_node.axis.z) |
|||
vec1.multiply(self.__thickness) |
|||
# 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 |
|||
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) |
|||
lostShape = self.__Shape.copy() |
|||
Sedges = Sf1.section(lostShape) |
|||
#Part.show(Sedges) |
|||
# find edge in Sedges equal to theEdge |
|||
other_edges = [] |
|||
for lu_edge in Sedges.Edges: |
|||
if not ((equal_vertex(lu_edge.Vertexes[0], theEdge.Vertexes[0]) and |
|||
equal_vertex(lu_edge.Vertexes[1], theEdge.Vertexes[1])) or |
|||
(equal_vertex(lu_edge.Vertexes[1], theEdge.Vertexes[0]) and |
|||
equal_vertex(lu_edge.Vertexes[0], theEdge.Vertexes[1]))): |
|||
other_edges.append(lu_edge) |
|||
next_pnt = theEdge.Vertexes[0] |
|||
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 |
|||
if not startFound: |
|||
W_node.analysis_ok = False # |
|||
W_node.error_code = 23 # Unfold: Unfold: CutToolWire not closed |
|||
self.error_code = 23 |
|||
self.failed_face_idx = W_node.idx |
|||
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) |
|||
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: |
|||
singleEdge = Sf1.section(self.__Shape.Faces[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) |
|||
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 |
|||
# beginning at the outermost nodes. |
|||
print "unfold_tree face", node.idx + 1 |
|||
theShell = [] |
|||
for n_node in node.child_list: |
|||
if self.error_code == None: |
|||
theShell = theShell + self.unfold_tree(n_node) |
|||
if node.node_type == 'Bend': |
|||
trans_vec = node.tan_vec * node._trans_length |
|||
for bFaces in theShell: |
|||
bFaces.rotate(self.__Shape.Faces[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: |
|||
nodeShell = [] |
|||
else: |
|||
if self.error_code == None: |
|||
nodeShell = self.generateShell(node) |
|||
else: |
|||
nodeShell = [] |
|||
print "ufo finish face",node.idx +1 |
|||
return (theShell + nodeShell) |
|||
def showFaces(self): |
|||
for i in self.index_list: |
|||
Part.show(self.__Shape.Faces[i]) |
|||
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 |
|||
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 |
|||
if TheTree.error_code == None: |
|||
# TheTree.showFaces() |
|||
theFaceList = TheTree.unfold_tree(TheTree.root) # traverses the tree-structure |
|||
if TheTree.error_code == None: |
|||
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) |
|||
except: |
|||
print("couldn't make a solid, show only a shell") |
|||
Part.show(newShell) |
|||
else: |
|||
Part.show(TheSolid) |
|||
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.""") |
|||
}} |
|||
[https://github.com/FreeCAD/FreeCAD-macros/raw/master/SheetMetalUnfolder.FCMacro Direct link] (right-click and select "Save target as...") |
|||
</translate> |
|||
<languages/> |
<languages/> |
Revision as of 22:19, 10 May 2015
File:Text-x-python Sheet_ufo |
Description |
---|
This macro unfolds simple sheet-metal-designs. Author: ulrich1a |
Author |
ulrich1a |
Download |
None |
Links |
Macros recipes How to install macros How to customize toolbars |
Macro Version |
1.0 |
Date last modified |
None |
FreeCAD Version(s) |
None |
Default shortcut |
None |
See also |
None |
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:
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
Script
The script can be viewed and downloaded from the FreeCAD-macros repository: SheetMetalUnfolder.FCMacro
(Right-click on the "Raw" button and select "Save target as..." to download)
Direct link (right-click and select "Save target as...")