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.

826 lines
28 KiB

import FreeCAD as App
import FreeCADGui as Gui
from PySide.QtCore import QTimer
import TechDraw, TechDrawGui
from PySide import QtGui, QtCore
TDG = TechDrawGui
from ahb_material import Material
class CursorItem(QtGui.QGraphicsItem):
def __init__(self, parent = None, view = None):
super().__init__(parent)
self.Type = QtGui.QGraphicsItem.UserType + 501
self.setZValue(500)
self.margin = 10.0
self.size = 100.0
self.view = view
def removeSceneEventFilter(self, a, b):
print('removeSceneEventFilter', a, b)
def onViewPosChange(self, callback):
self.viewPosChangeCallback = callback
def boundingRect(self):
return QtCore.QRectF(-self.size/2 - self.margin, -self.size/2 - self.margin,
self.size + 2.0*self.margin, self.size + 2.0*self.margin)
def paint(self, painter, option, widget):
#print("paint")
painter.setBrush(QtCore.Qt.darkRed)
#painter.drawRoundedRect(-100, -100, 200, 200, 50, 50)
h = self.size/2.0
painter.drawLine(-h, 0, h, 0)
painter.drawLine(0, -h, 0, h)
def mousePressEvent(self, event):
#print('mouse press', event)
self.startMovePos = event.pos()
def mouseMoveEvent(self, event):
#print('mouse move', event)
offset = event.pos() - self.startMovePos
self.moveBy(offset.x(), offset.y())
def mouseReleaseEvent(self, event):
#print('mouse release', event)
#print('new pos', self.x(), self.y())
if self.viewPosChangeCallback is not None:
self.viewPosChangeCallback(self.getViewPos())
def getViewPos(self):
scale = self.view.Scale * 10.0
return App.Vector(self.x() / scale, -self.y() / scale)
def setViewPos(self, p):
scale = self.view.Scale * 10.0
self.setPos(p.x * scale, -p.y * scale)
class ViewCache:
def __init__(self):
self.reset()
def reset(self):
self.projected_origin = None
class TechDrawExtensions:
views_to_repaint = {}
view_cursors = {}
view_cache = {}
updating_balloon = False
edited_view = None
enable_selected_part_highlight = False # disable for now, for performance reasons
initialized_documents = []
def __init__(self):
workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench
workbench.docObserver.onObjectTypeChanged('balloon_changed', 'TechDraw::DrawViewBalloon', lambda obj, prop: self.onBalloonChanged(obj, prop))
workbench.docObserver.onObjectTypeSelected('balloon_selected', 'TechDraw::DrawViewBalloon', lambda operation, obj, sub, point: self.onBalloonSelected(operation, obj, sub, point))
workbench.docObserver.onDocumentEvent('techdrawext_doc_event', lambda doc, event: self.onDocumentEvent(doc, event))
if App.ActiveDocument is not None:
self.onDocumentEvent(App.ActiveDocument, 'activate')
def repaint(self, view, fast_render = True):
self.views_to_repaint[view] = fast_render
QTimer.singleShot(10, self._do_repaint)
def _do_repaint(self):
from ahb_raster_view import RasterView
import Draft
workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench
selection = Gui.Selection.getSelection()
to_repaint = self.views_to_repaint.copy()
self.views_to_repaint = {}
for view, fast_render in to_repaint.items():
if '_overlay' in view.Label:
continue
#print("Repainting " + view.Name)
page = self.getViewPage(view)
view_cache = self.getViewCache(view)
view_cache.reset()
doc = view.Document
if not 'Assembly_handbook_RasterView' in view.PropertiesList:
view.addProperty("App::PropertyBool", "Assembly_handbook_RasterView", "Assembly_handbook")
view.Assembly_handbook_RasterView = True
if view.Assembly_handbook_RasterView:
print("Rasterizing view " + view.Label + "...")
raster_view = RasterView(view)
raster_view.render(fast_render)
view.Visibility = False
overlayName = view.Label + "_overlay"
overlay = doc.getObject(overlayName)
if overlay is None:
overlay = doc.addObject('TechDraw::DrawViewPart', overlayName)
page.addView(overlay)
overlay_frame_name = view.Label + "_frame"
overlay_frame = doc.getObject(overlay_frame_name)
if overlay_frame is not None:
doc.removeObject(overlay_frame.Name)
#overlay_frame = Draft.makeWire(points, closed=False, face=False, support=None)
overlay_frame = doc.addObject("Part::Part2DObjectPython", overlay_frame_name)
Draft.Wire(overlay_frame)
pos = raster_view.projectImageViewPointTo3D(App.Vector(0,0,0))
pos2 = raster_view.projectImageViewPointTo3D(App.Vector(0.001,0.001,1))
overlay_frame.Points = [pos, pos2]
Draft.ViewProviderWire(overlay_frame.ViewObject)
overlay_frame.recompute()
overlay_frame2_name = view.Label + "_frame2"
overlay_frame2 = doc.getObject(overlay_frame2_name)
if overlay_frame2 is not None:
doc.removeObject(overlay_frame2.Name)
overlay_frame2 = doc.addObject("Part::Part2DObjectPython", overlay_frame2_name)
Draft.Wire(overlay_frame2)
pos = raster_view.projectImageViewPointTo3D(App.Vector(1,1,0))
pos2 = raster_view.projectImageViewPointTo3D(App.Vector(1.001,1.001,1))
overlay_frame2.Points = [pos, pos2]
Draft.ViewProviderWire(overlay_frame2.ViewObject)
overlay_frame2.recompute()
overlay.Source = [overlay_frame, overlay_frame2]
overlay.X = view.X
overlay.Y = view.Y
overlay.Direction = view.Direction
overlay.XDirection = view.XDirection
overlay.ScaleType = view.ScaleType
overlay.Scale = view.Scale
overlay.ViewObject.LineWidth = 0.01
# migrate balloons from source view to overlay
for balloon in page.Views:
if balloon.TypeId == 'TechDraw::DrawViewBalloon' and "Assembly_handbook_Source" in balloon.PropertiesList and balloon.SourceView == view:
if balloon.SourceView == view:
old_source = balloon.Assembly_handbook_Source
old_OriginOffsetX = balloon.Assembly_handbook_OriginOffsetX
old_OriginOffsetY = balloon.Assembly_handbook_OriginOffsetY
old_X = balloon.X
old_Y = balloon.Y
old_Visibility = balloon.ViewObject.Visibility
balloonName = balloon.Name
doc.removeObject(balloon.Name)
balloon = doc.addObject("TechDraw::DrawViewBalloon", balloonName)
balloon.SourceView = overlay
balloon.addProperty("App::PropertyXLink", "Assembly_handbook_Source", "Assembly_handbook")
balloon.Assembly_handbook_Source = old_source
balloon.addProperty("App::PropertyFloat", "Assembly_handbook_OriginOffsetX", "Assembly_handbook")
balloon.addProperty("App::PropertyFloat", "Assembly_handbook_OriginOffsetY", "Assembly_handbook")
balloon.Assembly_handbook_OriginOffsetX = old_OriginOffsetX
balloon.Assembly_handbook_OriginOffsetY = old_OriginOffsetY
page.addView(balloon)
self.updateBalloon(balloon)
balloon.X = old_X
balloon.Y = old_Y
balloon.ViewObject.Visibility = old_Visibility
balloon.recompute()
overlay.recompute()
page.recompute()
else:
fast_rendering = False
#try:
# fast_rendering = view.Assembly_handbook_FastRendering
#except:
# pass
if view.CoarseView:
fast_rendering = True
selected_balloons = []
for obj in Gui.Selection.getSelection():
if obj.TypeId == 'TechDraw::DrawViewBalloon' and obj.SourceView == view and 'Assembly_handbook_Source' in obj.PropertiesList:
selected_balloons.append(obj)
#view.clearGeomFormats() # for an unknown reason, this will crash freecad
if not fast_rendering:
is_first_part = True
parts_to_paint = []
# repaint parts that are highlighted by selection
if self.enable_selected_part_highlight:
for balloon in selected_balloons:
part = self.getBalloonSourcePart(balloon)
if part in view.XSource:
parts_to_paint.append(part)
# repaint parts that are new in this step (thick line)
prev_view = None
if 'Assembly_handbook_PreviousStepView' in view.PropertiesList:
prev_view = doc.getObject(view.Assembly_handbook_PreviousStepView)
for part in view.XSource:
if (prev_view is None or part not in prev_view.XSource) and part not in parts_to_paint:
parts_to_paint.append(part)
# make sure the list is not empty, so that we reset all lines
if len(parts_to_paint) == 0:
parts_to_paint.append(None)
for part in parts_to_paint:
default_line_thickness = 0.05
line_thickness = default_line_thickness
default_color = (0.0, 0.0, 0.0) if fast_rendering else (0.5, 0.5, 0.5)
color = default_color
if part is not None:
part_view = workbench.partsCache.getPart2DView(view, part)
center = self.computePartCenter(view, part)
if self.isNewPartInView(view, part):
line_thickness = 0.2
color = (0, 0, 0)
if self.enable_selected_part_highlight:
for balloon in selected_balloons:
if part == self.getBalloonSourcePart(balloon):
color = (0.0, 0.85, 0.0) # selection highlighting
# iterate edges of actual view and highlight matching edges
for edgeIdx in range(10000):
hasEdge = False
try:
edge = view.getEdgeByIndex(edgeIdx)
hasEdge = True
except:
pass
if not hasEdge:
break
is_edge_of_part = False
if part is not None and (not hasattr(edge.Curve, 'Degree') or edge.Curve.Degree == 1) and len(edge.Vertexes) == 2:
edgeData = [
edge.Vertexes[0].X - center.x,
edge.Vertexes[0].Y - center.y,
edge.Vertexes[1].X - center.x,
edge.Vertexes[1].Y - center.y
]
v0 = App.Vector(edgeData[0], edgeData[1])
v1 = App.Vector(edgeData[2], edgeData[3])
for line in part_view.cached_lines:
l0 = App.Vector(line[0], line[1])
l1 = App.Vector(line[2], line[3])
#d = abs(edgeData[0] - line[0]) + abs(edgeData[1] - line[1]) + abs(edgeData[2] - line[2]) + abs(edgeData[3] - line[3])
d = v0.distanceToLineSegment(l0, l1).Length + v1.distanceToLineSegment(l0, l1).Length
if d < 0.01:
is_edge_of_part = True
break
if is_edge_of_part:
view.formatGeometricEdge(edgeIdx,1,line_thickness,color,True)
elif is_first_part:
# reset edge format
view.formatGeometricEdge(edgeIdx,1,default_line_thickness,default_color,True)
is_first_part = False
view.requestPaint()
def updateBalloonCursor(self, view):
selected_balloons = []
for obj in Gui.Selection.getSelection():
if obj.TypeId == 'TechDraw::DrawViewBalloon' and obj.SourceView == view and 'Assembly_handbook_Source' in obj.PropertiesList:
selected_balloons.append(obj)
cursor = self.view_cursors.get(view, None)
if cursor is not None:
try:
cursor.x() # this can throw an exception if the Qt item has been deleted (for example when closing the page)
except Exception as ex:
print("Re-generating cursor...")
cursor = None
if cursor is None:
cursor = CursorItem(view = view)
TDG.addQGIToView(view, cursor);
cursor.onViewPosChange(lambda new_pos: self.onCursorMoved(view, new_pos))
self.view_cursors[view] = cursor
if len(selected_balloons) == 0:
cursor.setVisible(False)
else:
cursor.setViewPos(App.Vector(selected_balloons[0].OriginX, selected_balloons[0].OriginY))
cursor.setVisible(True)
def setCurrentViewDirection(self, view):
from pivy import coin
doc = view.Document
if doc != Gui.ActiveDocument.Document:
raise Exception("Current view is not for the same document as TechDraw view " + view.Name)
activeView = Gui.ActiveDocument.ActiveView
if str(type(activeView)) not in ["<class 'View3DInventorPy'>", "<class 'Gui.View3DInventor'>"]:
raise Exception("Current view is not a 3D view")
cam = activeView.getCameraNode()
dir = cam.orientation.getValue().multVec(coin.SbVec3f(0,0,1)).getValue()
xdir = cam.orientation.getValue().multVec(coin.SbVec3f(1,0,0)).getValue()
view.Direction = App.Vector(dir[0], dir[1], dir[2])
view.XDirection = App.Vector(xdir[0], xdir[1], xdir[2])
def refreshView(self, view):
doc = view.Document
page = self.getViewPage(view)
for balloon in page.Views:
if balloon.TypeId == 'TechDraw::DrawViewBalloon' and "Assembly_handbook_Source" in balloon.PropertiesList and balloon.SourceView == view:
obj = self.getBalloonSourcePart(balloon)
balloonColor = (0.0, 0.0, 0.0)
if obj is None or not obj in view.XSource:
balloonColor = (1.0, 0.0, 0.0)
balloon.ViewObject.Color = balloonColor
self.view_cache[view] = None
def toggleEditViewSourceParts(self, view):
workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench
button = self.getToolbarButton('AHB_view_edit_source_parts')
if self.edited_view is None:
workbench.docLinkObserver.select_link_mode = True
self.edited_view = view
button.setChecked(True)
button.setText('End source parts edition')
Gui.Selection.clearSelection()
for obj in view.XSource:
Gui.Selection.addSelection(obj)
else:
workbench.docLinkObserver.select_link_mode = False
Gui.Selection.clearSelection()
Gui.Selection.addSelection(self.edited_view)
self.edited_view = None
button.setChecked(False)
button.setText('Edit view source parts')
def editViewSourceParts(self, parts, add):
if self.edited_view is None: return
xsource = self.edited_view.XSource
modified = False
for part in parts:
if (part in xsource) != add:
if add:
xsource.append(part)
else:
xsource.remove(part)
modified = True
if modified:
self.edited_view.XSource = xsource
def getToolbarButton(self, buttonName):
mainwin = Gui.getMainWindow()
toolbar = None
for tb in mainwin.findChildren(QtGui.QToolBar):
if tb.objectName()=='Assembly Handbook':
toolbar = tb
button = None
if toolbar is not None:
for action in toolbar.actions():
if action.objectName() == buttonName:
button = action
return button
def onCursorMoved(self, view, new_pos):
if len(Gui.Selection.getSelection()) == 0: return
balloon = Gui.Selection.getSelection()[0]
if balloon.TypeId != 'TechDraw::DrawViewBalloon' or not 'Assembly_handbook_Source' in balloon.PropertiesList: return
if balloon.SourceView != view: return
balloon.OriginX = new_pos.x
balloon.OriginY = new_pos.y
obj = self.getBalloonSourcePart(balloon)
view = balloon.SourceView
center = self.computePartCenter(view, obj, self.getBalloonSourcePartPath(balloon))
balloon.Assembly_handbook_OriginOffsetX = new_pos.x - center.x
balloon.Assembly_handbook_OriginOffsetY = new_pos.y - center.y
def onBalloonSelected(self, operation, balloon, sub, point):
#print(operation, obj.Name, sub, point)
if 'Assembly_handbook_Source' in balloon.PropertiesList:
#print(operation + " " + balloon.Name)
view = balloon.SourceView
self.updateBalloonCursor(view)
if self.enable_selected_part_highlight:
self.repaint(view)
def onBalloonChanged(self, obj, prop):
# Avoid reentry
if self.updating_balloon:
return
#print('Balloon changed: ' + obj.Name + '.' + prop)
if prop == 'Y' and 'Assembly_handbook_Source' in obj.PropertiesList:
self.updating_balloon = True
self.updateBalloon(obj)
self.updating_balloon = False
def add_or_update_balloon(self, view, part, parent_path):
balloonsCreated = []
page = self.getViewPage(view)
overlay_view = self.getOverlayView(view)
doc = page.Document
path = parent_path
if path == '':
path = part.Document.Name + '#' + part.Name
else:
path += '.'
path += part.Name
# Search an existing balloon to update
balloon = None
for obj in page.Views:
if obj.TypeId == 'TechDraw::DrawViewBalloon' and self.getBalloonSourcePart(obj) == part and self.getBalloonSourcePartPath(obj) == path:
if obj.SourceView != overlay_view: continue
balloon = obj
# Create a new balloon if needed
if balloon is None:
if self.isNewPartInView(view, part):
partName = part.Name
balloonName = partName + "_Balloon"
balloon = doc.addObject("TechDraw::DrawViewBalloon", balloonName)
balloon.SourceView = overlay_view
balloon.addProperty("App::PropertyXLink", "Assembly_handbook_Source", "Assembly_handbook")
balloon.Assembly_handbook_Source = (part, part.Name)
balloon.addProperty("App::PropertyString", "Assembly_handbook_SourcePath", "Assembly_handbook")
balloon.Assembly_handbook_SourcePath = path
balloon.addProperty("App::PropertyFloat", "Assembly_handbook_OriginOffsetX", "Assembly_handbook")
balloon.addProperty("App::PropertyFloat", "Assembly_handbook_OriginOffsetY", "Assembly_handbook")
page.addView(balloon)
self.updateBalloon(balloon)
if not self.isNewPartInView(view, part):
balloon.ViewObject.Visibility = False
else:
balloonsCreated.append(balloon)
else:
self.updateBalloon(balloon)
return balloonsCreated
def updateBalloon(self, balloon):
workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench
view = balloon.SourceView
obj = self.getBalloonSourcePart(balloon)
path = self.getBalloonSourcePartPath(balloon)
if obj is not None:
objectCenterView = workbench.techDrawExtensions.computePartCenter(view, obj, path)
balloon.OriginX = objectCenterView.x + balloon.Assembly_handbook_OriginOffsetX
balloon.OriginY = objectCenterView.y + balloon.Assembly_handbook_OriginOffsetY
partDisplayName = 'Inconnu' if obj is None else self.getPartDisplayName(obj)
balloon.Text = partDisplayName
balloon.ViewObject.Font = 'DejaVu Sans'
balloon.ViewObject.Fontsize = 4
balloon.BubbleShape = 'Inspection'
balloon.EndTypeScale = 0.5
def getBalloonSourcePart(self, balloon):
try:
return balloon.Assembly_handbook_Source[0]
except:
return None
def getBalloonSourcePartPath(self, balloon):
try:
return balloon.Assembly_handbook_SourcePath
except:
part = self.getBalloonSourcePart(balloon)
if part is None:
return ''
return part.Document.Name + '#' + part.Name
def isPartLink(self, obj):
if obj is None:
return False
if obj.TypeId == 'App::Link':
return True
if obj.TypeId == 'Part::FeaturePython' and hasattr(obj, 'LinkedObject'): # variant link
return True
return False
def getPartDisplayName(self, obj):
if self.isPartLink(obj):
linked_obj = obj.SourceObject if hasattr(obj, 'SourceObject') else obj.LinkedObject
if 'Assembly_handbook_PartDisplayName' in linked_obj.PropertiesList:
return linked_obj.Assembly_handbook_PartDisplayName
else:
return linked_obj.Document.Name
return obj.Name
def isNewPartInView(self, view, obj):
doc = view.Document
prev_view = None
if 'Assembly_handbook_PreviousStepView' in view.PropertiesList:
prev_view = doc.getObject(view.Assembly_handbook_PreviousStepView)
if prev_view is None:
return True
else:
if not obj in prev_view.XSource:
return True
return False
def getActivePage(self):
activeView = Gui.activeView()
if activeView is None: return None
activePage = activeView.getPage() if hasattr(activeView, 'getPage') else None
return activePage
def getViewPage(self, view):
for obj in view.InList:
if obj.TypeId == 'TechDraw::DrawPage':
if view in obj.Views:
return obj
return None
def forceRedrawPage(self, page, callback = None, fast_render = True):
for view in page.Views:
if view.TypeId == 'TechDraw::DrawViewPart' and 'Assembly_handbook_PreviousStepView' in view.PropertiesList:
if not 'Assembly_handbook_RasterView' in view.PropertiesList:
view.addProperty("App::PropertyBool", "Assembly_handbook_RasterView", "Assembly_handbook")
view.Assembly_handbook_RasterView = True
if 'Assembly_handbook_RasterView' in view.PropertiesList and view.Assembly_handbook_RasterView:
view.purgeTouched() # make sure we don't trigger rendering of source views (this is awfully slow and doesn't even work for a lot of models)
else:
view.touch()
self.refreshView(view)
elif view.TypeId == 'TechDraw::DrawViewBalloon':
if view.ViewObject.Visibility:
# workaround for a TechDraw bug: sometimes the balloon should be visible but doesn't appear, showing it again fixes the issue
view.ViewObject.Visibility = False
def makeRedrawCallback(view):
def redrawBalloon():
view.ViewObject.Visibility = True
return redrawBalloon
QTimer.singleShot(0, makeRedrawCallback(view))
else:
# workaround for a TechDraw bug: sometimes the balloon text is visible even if the balloon is hidden, hiding it again fixes the issue
view.ViewObject.Visibility = True
view.ViewObject.Visibility = False
if page.KeepUpdated:
for view in page.Views:
if view.TypeId != 'TechDraw::DrawViewPart':
view.recompute()
for view in page.Views:
if view.TypeId == 'TechDraw::DrawViewPart':
view.recompute()
self.repaint(view, fast_render)
if callback is not None:
callback()
else:
page.KeepUpdated = True
def restoreKeepUpdated():
for view in page.Views:
if view.TypeId == 'TechDraw::DrawViewPart':
if view.Name.endswith('_overlay'):
view.touch()
view.recompute()
for sub_view in page.Views:
try:
if sub_view.SourceView == view:
sub_view.recompute()
except:
pass
else:
view.recompute()
self.repaint(view, fast_render)
page.KeepUpdated = False
if callback is not None:
callback()
QTimer.singleShot(10, restoreKeepUpdated)
def refreshOverlays(self, page, callback = None):
import os
for view in page.Views:
if view.TypeId == 'TechDraw::DrawViewPart' and 'Assembly_handbook_RasterView' in view.PropertiesList and view.Assembly_handbook_RasterView:
view.purgeTouched() # make sure we don't trigger rendering of source views (this is awfully slow and doesn't even work for a lot of models)
doc = page.Document
for image in page.Views:
if image.TypeId == 'TechDraw::DrawViewImage':
folder_name = '/' + os.path.basename(doc.FileName).replace('.FCStd', '') + '_raster/'
if folder_name in image.ImageFile:
full_path = doc.FileName.replace('.FCStd', '') + '_raster/' + image.ImageFile.split(folder_name)[1]
if image.ImageFile != full_path:
image.ImageFile = full_path
if page.KeepUpdated:
if callback:
callback()
else:
page.KeepUpdated = True
def restoreKeepUpdated():
for view in page.Views:
if view.TypeId == 'TechDraw::DrawViewPart':
if view.Name.endswith('_overlay'):
view.touch()
view.recompute()
for sub_view in page.Views:
try:
if sub_view.SourceView == view:
sub_view.recompute()
except:
pass
page.KeepUpdated = False
for view in page.Views:
if view.TypeId == 'TechDraw::DrawViewBalloon':
if view.ViewObject.Visibility:
# workaround for a TechDraw bug: sometimes the balloon should be visible but doesn't appear, showing it again fixes the issue
view.ViewObject.Visibility = False
def makeRedrawCallback(view):
def redrawBalloon():
view.ViewObject.Visibility = True
return redrawBalloon
QTimer.singleShot(0, makeRedrawCallback(view))
else:
# workaround for a TechDraw bug: sometimes the balloon text is visible even if the balloon is hidden, hiding it again fixes the issue
view.ViewObject.Visibility = True
view.ViewObject.Visibility = False
if callback is not None:
callback()
QTimer.singleShot(10, restoreKeepUpdated)
def getSourceView(self, view):
if view.Name.endswith('_overlay'):
view = view.Document.getObject(view.Name[0:-8])
if view is None:
raise Exception("Can't find source view of " + view.Name)
return view
def getOverlayView(self, view):
if view.Name.endswith('_overlay'):
return view
overlay = view.Document.getObject(view.Name + '_overlay')
return overlay if overlay is not None else view
def computePartCenter(self, view, obj, path = None):
view = self.getSourceView(view)
mat = App.Matrix()
if path is not None:
path_parts = path.split('.')
path_parts.pop()
parent = None
for part in path_parts:
if parent is None:
doc_obj = part.split('#')
doc = App.getDocument(doc_obj[0])
link = doc.getObject(doc_obj[1])
else:
link = parent.Document.getObject(part)
mat = link.LinkPlacement * mat
parent = link.LinkedObject
if obj.TypeId == 'App::Link':
partLink = obj
mat = mat.multiply(partLink.LinkPlacement.Matrix)
objectCenterWorld = partLink.LinkedObject.Shape.CenterOfGravity
elif obj.TypeId == 'Part::FeaturePython' and hasattr(obj, 'LinkedObject'): # variant link
partLink = obj
mat = mat.multiply(partLink.Placement.Matrix)
objectCenterWorld = partLink.LinkedObject.Shape.CenterOfGravity
else:
objectCenterWorld = obj.Shape.CenterOfGravity
objectCenterWorld = mat.multiply(objectCenterWorld)
'''view_cache = self.getViewCache(view)
key = (objectCenterWorld.x, objectCenterWorld.y, objectCenterWorld.z)
projected_point = view_cache.projected_points.get(key, None)
if projected_point is None:
# TechDraw does not expose a way to project a 3D point to 2D view coordinates ; this is a hack to get this value indirectly. The view should be hidden before calling this method, to avoid costly repaints.
vertId = view.makeCosmeticVertex3d(objectCenterWorld)
vert = view.getCosmeticVertex(vertId)
projected_point = vert.Point
view.removeCosmeticVertex(vertId)
view_cache.projected_points[key] = projected_point
return projected_point'''
return self.projectPoint(view, objectCenterWorld)
def projectPoint(self, view, point3d):
if 'Assembly_handbook_RasterView' in view.PropertiesList and view.Assembly_handbook_RasterView:
from ahb_raster_view import RasterView
raster_view = RasterView(view)
if raster_view.init_image_projection():
return raster_view.project3DPointToSourceView(point3d)
# DrawViewPart::projectPoint should be exposed to python in freecad 0.21, but for 0.20 we have to use a workaround
view_cache = self.getViewCache(view)
if view_cache.projected_origin is None:
vertId = view.makeCosmeticVertex3d(App.Vector(0,0,0))
vert = view.getCosmeticVertex(vertId)
view_cache.projected_origin = vert.Point
view.removeCosmeticVertex(vertId)
YDirection = view.Direction.cross(view.XDirection)
return App.Vector(view.XDirection.dot(point3d) + view_cache.projected_origin.x, YDirection.dot(point3d) + view_cache.projected_origin.y, 0)
def getViewCache(self, view):
cache = self.view_cache.get(view, None)
if cache is None:
cache = ViewCache()
self.view_cache[view] = cache
return cache
def onDocumentEvent(self, doc, event):
if event == 'activate':
if doc not in self.initialized_documents:
self.initialized_documents.append(doc)
self.initializeDocument(doc)
elif event == 'deleted':
if doc in self.initialized_documents:
self.initialized_documents.remove(doc)
def initializeDocument(self, doc):
def doInit():
main_part = None
try:
for obj in doc.Objects:
if obj.TypeId == 'TechDraw::DrawPage':
self.onPageLoaded(obj)
main_parts = doc.getObjectsByLabel(doc.Name)
if len(main_parts) == 1:
main_part = main_parts[0]
except:
pass
if main_part is not None:
current_material = 'Unknown'
if 'Assembly_handbook_Material' in main_part.PropertiesList:
current_material = main_part.Assembly_handbook_Material
else:
main_part.addProperty("App::PropertyEnumeration", "Assembly_handbook_Material", "Assembly_handbook")
material_list = ['Unknown'] + Material.GetMaterialIDs()
main_part.Assembly_handbook_Material = material_list
main_part.Assembly_handbook_Material = material_list.index(current_material) if current_material in material_list else 0
if 'Assembly_handbook_Weight' not in main_part.PropertiesList:
main_part.addProperty("App::PropertyFloat", "Assembly_handbook_Weight", "Assembly_handbook", 'Part weight in grams. Set a negative number if weight is unknown.')
main_part.Assembly_handbook_Weight = -1
QTimer.singleShot(0, doInit)
def onPageLoaded(self, page):
self.refreshOverlays(page)