FreeCAD workbench to create assembly handbooks
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.

146 lines
4.9 KiB

import math
import FreeCADGui as Gui
import FreeCAD as App
import ahb_utils
class AHB_View_Annotate:
def GetResources(self):
return {"MenuText": "Annotate view",
"ToolTip": "Annotates a TechDraw view with object names",
"Pixmap": ""
}
def IsActive(self):
return True
def Activated(self):
workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench
if len(Gui.Selection.getSelection()) == 0:
page = workbench.techDrawExtensions.getActivePage()
workbench.techDrawExtensions.refreshOverlays(page)
return
if len(Gui.Selection.getSelection()) != 1:
raise Exception("Please select exactly one TechDraw view")
view = Gui.Selection.getSelection()[0]
if view.TypeId == 'TechDraw::DrawPage':
workbench.techDrawExtensions.refreshOverlays(view)
return
elif view.TypeId != 'TechDraw::DrawViewPart':
raise Exception("Selected object is not a TechDraw view")
overlay_view = workbench.techDrawExtensions.getOverlayView(view)
doc = view.Document
page = workbench.techDrawExtensions.getViewPage(view)
if page is None:
raise Exception("Can't find page in which the selected view is located")
def list_sub_parts(parts):
result = []
for part in parts:
if part.TypeId == 'App::Link':
result.append(part)
elif part.TypeId == 'Part::FeaturePython' and hasattr(part, 'LinkedObject'): # variant link
result.append(part)
if hasattr(part, 'Group'):
result.extend(list_sub_parts(part.Group))
return result
all_parts = list_sub_parts(view.XSource)
# Remove balloons referencing missing objects
for balloon in page.Views:
if balloon.TypeId == 'TechDraw::DrawViewBalloon' and "Assembly_handbook_Source" in balloon.PropertiesList:
if balloon.SourceView != view and balloon.SourceView != overlay_view: continue
partLink = workbench.techDrawExtensions.getBalloonSourcePart(balloon)
if partLink is None or partLink not in all_parts:
ref_name = "<no ref>"
try:
ref_name = balloon.Assembly_handbook_Source[1]
except:
pass
print(balloon.Name + " references missing object " + ref_name + ", removing balloon")
doc.removeObject(balloon.Name)
balloonsCreated = []
for partLink in view.XSource:
balloonsCreated.extend(workbench.techDrawExtensions.add_or_update_balloon(view, partLink, ''))
if len(balloonsCreated) > 0:
regroupedBalloons = self.RegroupNearestSimilarBalloons(balloonsCreated)
#self.PlaceBalloonsInCircle(regroupedBalloons)
self.PlaceBalloonsInCircle(balloonsCreated)
workbench.techDrawExtensions.refreshOverlays(page)
def CalculatePointsCenter(self, balloons):
totalX = 0
totalY = 0
for balloon in balloons:
realBalloon = balloon[0] if type(balloon) is list else balloon
totalX = totalX + int(realBalloon.OriginX)
totalY = totalY + int(realBalloon.OriginY)
return App.Vector(totalX / len(balloons), totalY / len(balloons))
def IsSimilarBalloonNear(self, balloonA, balloonB):
MAX_DISTANCE_BETWEEN_REGROUPED_BALLOONS = 50
if balloonA.Text == balloonB.Text:
pos = App.Vector(balloonA.OriginX, balloonA.OriginY)
dist = pos.distanceToPoint(App.Vector(balloonB.OriginX, balloonB.OriginY))
return dist < MAX_DISTANCE_BETWEEN_REGROUPED_BALLOONS
else:
return False
def RegroupNearestSimilarBalloons(self, balloons):
regroupedBalloons = []
for balloon in balloons:
nearestBalloons = []
for otherBalloon in balloons:
if otherBalloon != balloon and self.IsSimilarBalloonNear(balloon, otherBalloon):
nearestBalloons.append(otherBalloon)
balloons.remove(otherBalloon)
if len(nearestBalloons) == 0:
regroupedBalloons.append(balloon)
else:
nearestBalloons.append(balloon)
regroupedBalloons.append(nearestBalloons)
return regroupedBalloons
def PlaceBalloonsInCircle(self, balloons):
center = self.CalculatePointsCenter(balloons)
nbBalloons = len(balloons)
balloonPosStep = (math.pi * 2) / nbBalloons
for i in range(nbBalloons):
xPos = round(center.x + 600 * math.cos(balloonPosStep * i))
yPos = round(center.y + 600 * math.sin(balloonPosStep * i))
balloonPos = App.Vector(xPos, yPos)
# Find nearest arrow to avoid arrow crossing each other
smallestDistance = 0
balloonToUse = None
for balloon in balloons:
realBalloon = balloon[0] if type(balloon) is list else balloon
dist = balloonPos.distanceToPoint(App.Vector(realBalloon.OriginX, realBalloon.OriginY))
if smallestDistance == 0 or dist < smallestDistance:
smallestDistance = dist
balloonToUse = balloon
if balloonToUse is not None:
balloons.remove(balloonToUse)
if type(balloonToUse) is list:
for realBalloon in balloonToUse:
realBalloon.X = balloonPos.x
realBalloon.Y = balloonPos.y
else:
balloonToUse.X = balloonPos.x
balloonToUse.Y = balloonPos.y
from ahb_command import AHB_CommandWrapper
AHB_CommandWrapper.addGuiCommand('AHB_view_annotate', AHB_View_Annotate())