Ajout d'un script pour générer des dessins 2D pour tous les tubes
This commit is contained in:
parent
43d7036751
commit
98924d44a1
5 changed files with 348 additions and 9 deletions
|
|
@ -1,5 +1,9 @@
|
|||
from pathlib import Path
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'utils'))
|
||||
import vspt_freecad
|
||||
|
||||
import FreeCAD as App
|
||||
import Import
|
||||
|
|
@ -9,10 +13,6 @@ project_folder = os.getcwd()
|
|||
output_folder = project_folder + '/dist/STEP'
|
||||
assemblies_output_folder = project_folder + '/dist/FCStd'
|
||||
|
||||
def close_all_docs():
|
||||
while len(FreeCAD.listDocuments().values()) > 0:
|
||||
FreeCAD.closeDocument(list(FreeCAD.listDocuments().values())[0].Name)
|
||||
|
||||
def convert_file(file_name, output_format):
|
||||
doc = App.open(project_folder + '/' + file_name)
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ def convert_file(file_name, output_format):
|
|||
|
||||
if 'experimental' in main_object.Label or 'expérimental' in main_object.Label:
|
||||
print('Document ' + doc.Name + ' is marked as experimental and will be ignored')
|
||||
close_all_docs()
|
||||
vspt_freecad.close_all_docs()
|
||||
return
|
||||
|
||||
secondary_objects = []
|
||||
|
|
@ -70,7 +70,7 @@ def convert_file(file_name, output_format):
|
|||
else:
|
||||
Import.export([main_object], output_path)
|
||||
|
||||
close_all_docs()
|
||||
vspt_freecad.close_all_docs()
|
||||
|
||||
def export_assembly(doc, file_name, output_format):
|
||||
root_objects = []
|
||||
|
|
@ -115,7 +115,7 @@ def convert_assembly(file_name, output_format):
|
|||
print("Exporting assembly " + file_name + "...")
|
||||
doc = App.open(project_folder + '/' + file_name)
|
||||
export_assembly(doc, file_name, output_format)
|
||||
close_all_docs()
|
||||
vspt_freecad.close_all_docs()
|
||||
|
||||
def export_configuration(file_name, config_name, output_filename, output_format):
|
||||
print('Generating assembly for configuration '+config_name+'...')
|
||||
|
|
@ -131,7 +131,7 @@ def export_configuration(file_name, config_name, output_filename, output_format)
|
|||
|
||||
#doc.saveAs(assemblies_output_folder + '/' + output_filename + '.FCStd')
|
||||
|
||||
close_all_docs()
|
||||
vspt_freecad.close_all_docs()
|
||||
|
||||
try:
|
||||
folders = [
|
||||
|
|
@ -158,6 +158,6 @@ except Exception as e:
|
|||
print(e)
|
||||
|
||||
# exit FreeCAD
|
||||
close_all_docs()
|
||||
vspt_freecad.close_all_docs()
|
||||
FreeCADGui.getMainWindow().close()
|
||||
|
||||
|
|
|
|||
279
tools/generate-2d-drawings.py
Normal file
279
tools/generate-2d-drawings.py
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
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()
|
||||
|
||||
async def generate_2d_drawing(file_name):
|
||||
doc = App.open(project_folder + '/' + file_name)
|
||||
|
||||
page_name = doc.Name + '_Drawing'
|
||||
|
||||
if doc.getObject(page_name) is not None:
|
||||
print('2D drawing already exists - skipped')
|
||||
return
|
||||
|
||||
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.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.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.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 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
|
||||
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())
|
||||
|
||||
16
tools/generate-2d-drawings.sh
Executable file
16
tools/generate-2d-drawings.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Set the path to your FreeCAD executable here
|
||||
FREECAD=~/dev/FreeCAD-asm3-Daily-Conda-Py3.10-20221128-glibc2.12-x86_64.AppImage
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
cd $SCRIPT_DIR/..
|
||||
|
||||
# Check script syntax before starting freecad
|
||||
python3 -m py_compile tools/generate-2d-drawings.py
|
||||
|
||||
# Start freecad to run the script. We must start freecad with GUI. We start it hidden in a virtual framebuffer (xvfb) so that it can run cleanly in the background.
|
||||
xvfb-run $FREECAD tools/generate-2d-drawings.py
|
||||
#$FREECAD tools/generate-2d-drawings.py
|
||||
38
tools/utils/vspt_coroutine.py
Normal file
38
tools/utils/vspt_coroutine.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import asyncio
|
||||
from PySide.QtCore import QTimer
|
||||
|
||||
class EventLoop:
|
||||
loop = None
|
||||
|
||||
def __init__(self):
|
||||
self.loop = asyncio.new_event_loop()
|
||||
|
||||
def create_task(self, coro):
|
||||
self.loop.create_task(coro)
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
self.loop.stop()
|
||||
self.loop.run_forever()
|
||||
|
||||
async def wait(self, time_milliseconds):
|
||||
#print("waiting " + str(time_milliseconds) + "ms...")
|
||||
currentLoop = self
|
||||
fut = self.loop.create_future()
|
||||
def callback():
|
||||
#print("wait callback")
|
||||
fut.set_result(True)
|
||||
currentLoop.update()
|
||||
QTimer.singleShot(time_milliseconds, callback)
|
||||
await fut
|
||||
#print("end wait")
|
||||
|
||||
main_loop = None
|
||||
|
||||
def get_main_loop():
|
||||
global main_loop
|
||||
if main_loop is None:
|
||||
#print("Creating main loop")
|
||||
main_loop = EventLoop()
|
||||
return main_loop
|
||||
|
||||
6
tools/utils/vspt_freecad.py
Normal file
6
tools/utils/vspt_freecad.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import FreeCAD as App
|
||||
|
||||
def close_all_docs():
|
||||
#print("close_all_docs")
|
||||
while len(App.listDocuments().values()) > 0:
|
||||
App.closeDocument(list(App.listDocuments().values())[0].Name)
|
||||
Loading…
Add table
Add a link
Reference in a new issue