forked from vhelio/vheliotech-freecad
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
9.1 KiB
304 lines
9.1 KiB
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()) |
|
|
|
|