import os
import sys
import asyncio
import math
sys . path . append ( os . path . join ( os . path . dirname ( __file__ ) , ' utils ' ) )
import vspt_freecad
import vspt_coroutine
verbose = False
project_folder = os . getcwd ( )
def apply_styles ( page ) :
modified = False
dim_font_size = 3.0
for view in page . Views :
if view . TypeId == ' TechDraw::DrawViewDimension ' :
if view . ViewObject . Fontsize != dim_font_size :
view . ViewObject . Fontsize = dim_font_size
modified = True
return modified
async def generate_2d_drawing ( file_name ) :
doc = App . open ( project_folder + ' / ' + file_name )
modified = False
page_name = doc . Name + ' _Drawing '
page = doc . getObject ( page_name )
if page is not None :
print ( ' 2D drawing already exists - skipped ' )
else :
modified = True
template_file_name = project_folder + ' /lib/A4_Landscape_VSPT.svg '
root_objects = [ ]
main_object = None
for obj in doc . Objects :
if len ( obj . Parents ) == 0 :
root_objects . append ( obj )
if obj . Label == doc . Name :
main_object = obj
if main_object is None and len ( root_objects ) == 1 :
main_object = root_objects [ 0 ]
if main_object is None :
raise Exception ( " Can ' t find main object in file " + doc . FileName + " (found " + str ( len ( root_objects ) ) + " root object(s), none named like the document " + doc . Name + " ) " )
code_obj = doc . getObjectsByLabel ( ' Code_Tube_Draft ' )
if len ( code_obj ) == 1 :
code_obj = code_obj [ 0 ]
else :
code_obj = None
sources = [ main_object ]
bound_box = main_object . Shape . BoundBox
proj_size = [ 0 , 0 , 0 ] # size of the original part front view after projection at scale 1:1
if bound_box . XLength > bound_box . YLength :
if bound_box . XLength > bound_box . ZLength :
main_axis = 0
proj_size [ 0 ] = bound_box . XLength
proj_size [ 1 ] = bound_box . ZLength
proj_size [ 2 ] = bound_box . YLength
else :
main_axis = 2
proj_size [ 0 ] = bound_box . ZLength
proj_size [ 1 ] = bound_box . XLength
proj_size [ 2 ] = bound_box . YLength
else :
if bound_box . YLength > bound_box . ZLength :
main_axis = 1
proj_size [ 0 ] = bound_box . YLength
proj_size [ 1 ] = bound_box . ZLength
proj_size [ 2 ] = bound_box . XLength
else :
main_axis = 2
proj_size [ 0 ] = bound_box . ZLength
proj_size [ 1 ] = bound_box . XLength
proj_size [ 2 ] = bound_box . YLength
if verbose : print ( " Adding drawing page... " ) ;
page = doc . addObject ( ' TechDraw::DrawPage ' , page_name )
template = doc . addObject ( ' TechDraw::DrawSVGTemplate ' , ' Template ' )
template . Template = template_file_name
page . Template = template
if verbose : print ( " Computing best scale... " ) ;
scale_denominators = [ 4.0 , 5.0 , 6.0 , 8.0 , 10.0 ]
scale_numerator = 1.0
scale_denominator = scale_denominators [ 0 ]
proj_total_size = [ proj_size [ 0 ] + proj_size [ 2 ] , proj_size [ 1 ] + proj_size [ 2 ] ] # projected size of all views (without spacing) at scale 1:1
spacingX = 20.0
spacingY = 50.0
maxSizeX = 280.0
maxSizeY = 160.0
for denom in scale_denominators :
scale_denominator = denom
if proj_total_size [ 0 ] * scale_numerator / denom + spacingX < = maxSizeX and proj_total_size [ 1 ] * scale_numerator / denom + spacingY < = maxSizeY :
break
if verbose : print ( " Adding projection group... " ) ;
projGroup = doc . addObject ( ' TechDraw::DrawProjGroup ' , doc . Name + ' _ProjGroup ' )
page . addView ( projGroup )
projGroup . ScaleType = ' Custom '
projGroup . Scale = scale_numerator / scale_denominator
projGroup . spacingX = 20.0
projGroup . spacingY = 50.0
projGroup . Source = sources
projGroup . addProjection ( ' Front ' )
if main_axis == 0 :
projGroup . Anchor . Direction = App . Vector ( 0 , 1 , 0 )
projGroup . Anchor . XDirection = App . Vector ( - 1 , 0 , 0 )
projGroup . Anchor . RotationVector = App . Vector ( - 1 , 0 , 0 )
elif main_axis == 1 :
projGroup . Anchor . Direction = App . Vector ( 1 , 0 , 0 )
projGroup . Anchor . XDirection = App . Vector ( 0 , 1 , 0 )
projGroup . Anchor . RotationVector = App . Vector ( 0 , 1 , 0 )
elif main_axis == 2 :
projGroup . Anchor . Direction = App . Vector ( 0 , 1 , 0 )
projGroup . Anchor . XDirection = App . Vector ( 0 , 0 , 1 )
projGroup . Anchor . RotationVector = App . Vector ( 0 , 0 , 1 )
projGroup . addProjection ( ' Top ' )
projGroup . addProjection ( ' Left ' )
projGroup . X = 130.0
projGroup . Y = 150.0
texts = page . Template . EditableTexts
texts [ ' SCALE ' ] = str ( int ( scale_numerator + 0.5 ) ) + ' : ' + str ( int ( scale_denominator + 0.5 ) )
try :
texts [ ' PM ' ] = main_object . Assembly_handbook_Material
except :
pass
texts [ ' PN ' ] = doc . Name
texts [ ' TITLELINE-1 ' ] = doc . Name
page . Template . EditableTexts = texts
async def addDimensions ( ) :
for view in projGroup . Views :
if verbose : print ( " View: " + view . Label + " ... " )
edges = [ ]
visibleEdges = view . getVisibleEdges ( )
edgeIdx = 0
lowestEdgeName = ' '
lowestEdgePos = 1000000
while True :
try :
edge = view . getEdgeByIndex ( edgeIdx )
except :
break
edges . append ( edge )
if edge . BoundBox . YLength < 0.01 and edge . BoundBox . Center . y < lowestEdgePos :
lowestEdgePos = edge . BoundBox . Center . y
lowestEdgeName = ' Edge ' + str ( edgeIdx )
edgeIdx = edgeIdx + 1
vertices = [ ]
vertIdx = 0
while True :
try :
vert = view . getVertexByIndex ( vertIdx )
except :
break
vertices . append ( vert )
vertIdx = vertIdx + 1
def getFeatureName ( edge ) :
if edge . Curve . TypeId == ' Part::GeomCircle ' :
vertIdx = 0
c = edge . BoundBox . Center
closestDist = 100000000
closestVert = None
for vert in vertices :
dx = vert . X - c . x
dy = vert . Y - c . y
dist = math . sqrt ( dx * dx + dy * dy )
if dist < closestDist :
closestDist = dist
closestVert = vert
vertIdx = vertIdx + 1
if closestVert is not None :
return ' Vertex ' + str ( vertices . index ( closestVert ) )
else :
return ' '
else :
return ' Edge ' + str ( edges . index ( edge ) )
if verbose : print ( " Listing features... " )
features = [ ]
for edge in edges :
if ( edge . Curve . TypeId == ' Part::GeomLine ' and edge . BoundBox . XLength < = 0.01 ) or ( edge . Curve . TypeId == ' Part::GeomCircle ' and abs ( edge . Curve . Radius * 2.0 - edge . BoundBox . XLength ) < 0.001 and abs ( edge . Curve . Radius * 2.0 - edge . BoundBox . YLength ) < 0.001 ) :
featureName = getFeatureName ( edge )
if featureName == ' ' :
continue
pos = edge . BoundBox . Center . x
duplicate = False
for otherFeature in features :
if abs ( otherFeature [ 0 ] - pos ) < 0.1 :
duplicate = True
break
if not duplicate :
features . append ( ( pos , edge , featureName ) )
features . sort ( key = lambda e : e [ 0 ] )
def addDimension ( edgeA , edgeB , posY ) :
dim = doc . addObject ( ' TechDraw::DrawViewDimension ' , ' Dimension ' )
dim . Type = ' DistanceX '
dim . FormatSpec = ' %.1f '
dim . References2D = [ ( view , ( getFeatureName ( edgeA ) , getFeatureName ( edgeB ) ) ) ]
visibleEdgeA = visibleEdges [ edges . index ( edgeA ) ]
visibleEdgeB = visibleEdges [ edges . index ( edgeB ) ]
dim . X = ( visibleEdgeA . BoundBox . Center . x + visibleEdgeB . BoundBox . Center . x ) * 0.5
dim . Y = posY
page . addView ( dim )
if edgeB . Curve . TypeId == ' Part::GeomCircle ' :
if abs ( edgeB . BoundBox . XLength - 6.5 ) > 0.01 :
dim = doc . addObject ( ' TechDraw::DrawViewDimension ' , ' Dimension ' )
dim . Type = ' Diameter '
dim . FormatSpec = ' %.1f '
dim . References2D = [ ( view , ( ' Edge ' + str ( edges . index ( edgeB ) ) , ) ) ]
dim . X = visibleEdgeB . BoundBox . Center . x + 6.0
dim . Y = - 6.0
page . addView ( dim )
if abs ( edgeB . BoundBox . Center . y ) > 0.01 and lowestEdgeName != ' ' :
dim = doc . addObject ( ' TechDraw::DrawViewDimension ' , ' Dimension ' )
dim . Type = ' DistanceY '
dim . FormatSpec = ' %.1f '
dim . References2D = [ ( view , ( getFeatureName ( edgeB ) , lowestEdgeName ) ) ]
dim . X = visibleEdgeB . BoundBox . Center . x + 2.0
dim . Y = - 6.0
page . addView ( dim )
if verbose : print ( " Adding dimensions... " )
if len ( features ) > = 2 :
if projGroup . Views . index ( view ) != 0 :
addDimension ( features [ 0 ] [ 1 ] , features [ len ( features ) - 1 ] [ 1 ] , - 25.0 )
if len ( features ) > 2 :
for featureIdx in range ( 0 , len ( features ) - 1 ) :
if featureIdx == 0 or features [ featureIdx ] [ 1 ] . Curve . TypeId != ' Part::GeomLine ' :
addDimension ( features [ featureIdx ] [ 1 ] , features [ featureIdx + 1 ] [ 1 ] , 15.0 )
if verbose : print ( " Adding secondary objects... " )
if code_obj is not None :
projGroup . Source = projGroup . Source + [ code_obj ]
page . recompute ( True )
await vspt_coroutine . get_main_loop ( ) . wait ( 1 )
await addDimensions ( )
if apply_styles ( page ) :
modified = True
if modified :
if verbose : print ( " Saving... " )
page . recompute ( True )
page . ViewObject . Visibility = False # don't save the document with the page open or it will automatically reopen on load
await vspt_coroutine . get_main_loop ( ) . wait ( 1 )
doc . save ( )
if verbose : print ( " Closing... " )
vspt_freecad . close_all_docs ( )
async def run ( ) :
try :
folders = [
' tubes '
]
for folder in folders :
files = os . listdir ( project_folder + ' / ' + folder )
for source_file in files :
if not source_file . endswith ( ' .FCStd ' ) : continue
source_path = folder + ' / ' + source_file
print ( source_path )
await generate_2d_drawing ( source_path )
# exit FreeCAD
vspt_freecad . close_all_docs ( )
FreeCADGui . getMainWindow ( ) . close ( )
except Exception as e :
print ( e )
vspt_coroutine . get_main_loop ( ) . create_task ( run ( ) )