From 110f8cc420464a56a0011e1a567a9bba9763526c Mon Sep 17 00:00:00 2001 From: Youen Date: Fri, 14 Oct 2022 21:02:50 +0200 Subject: [PATCH] refactored code so that balloon selection logic is independent from "annotate" button --- InitGui.py | 11 +++- ahb_cmd_view_annotate.py | 112 ++----------------------------------- ahb_parts_cache.py | 19 ++----- ahb_techdraw_extensions.py | 112 +++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 123 deletions(-) create mode 100644 ahb_techdraw_extensions.py diff --git a/InitGui.py b/InitGui.py index c7cd6e2..ae6f840 100644 --- a/InitGui.py +++ b/InitGui.py @@ -24,10 +24,9 @@ class AssemblyHandbookWorkbench(Gui.Workbench): dev = True # indicates development mode (enables additional tools, log, debug checks, etc.) context = None - docObserver = None - partsCache = None + techDrawExtensions = None def GetClassName(self): return "Gui::PythonWorkbench" @@ -53,6 +52,10 @@ class AssemblyHandbookWorkbench(Gui.Workbench): if self.partsCache is None: import ahb_parts_cache self.partsCache = ahb_parts_cache.PartsCache() + + if self.techDrawExtensions is None: + import ahb_techdraw_extensions + self.techDrawExtensions = ahb_techdraw_extensions.TechDrawExtensions() def Deactivated(self): """ @@ -109,6 +112,10 @@ class AssemblyHandbookWorkbench(Gui.Workbench): import ahb_parts_cache importlib.reload(ahb_parts_cache) self.partsCache = ahb_parts_cache.PartsCache() + + import ahb_techdraw_extensions + importlib.reload(ahb_techdraw_extensions) + self.techDrawExtensions = ahb_techdraw_extensions.TechDrawExtensions() self.initializeContext() self.registerCommands() diff --git a/ahb_cmd_view_annotate.py b/ahb_cmd_view_annotate.py index a8f58b3..a9466de 100644 --- a/ahb_cmd_view_annotate.py +++ b/ahb_cmd_view_annotate.py @@ -14,9 +14,8 @@ class AHB_View_Annotate: return True def Activated(self): - workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") + workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench workbench.docObserver.onObjectTypeChanged('view_annotate_balloon_changed', 'TechDraw::DrawViewBalloon', lambda obj, prop: self.onBalloonChanged(obj, prop)) - workbench.docObserver.onObjectTypeSelected('view_annotate_balloon_selected', 'TechDraw::DrawViewBalloon', lambda operation, obj, sub, point: self.onBalloonSelected(operation, obj, sub, point)) doc = App.activeDocument() @@ -27,7 +26,7 @@ class AHB_View_Annotate: if view.TypeId != 'TechDraw::DrawViewPart': raise Exception("Selected object is not a TechDraw view") - page = self.getViewPage(view) + page = workbench.techDrawExtensions.getViewPage(view) if page is None: raise Exception("Can't find page in which the selected view is located") @@ -76,96 +75,15 @@ class AHB_View_Annotate: self.updating_balloon = True self.updateBalloon(obj) self.updating_balloon = False - - def onBalloonSelected(self, operation, balloon, sub, point): - import time - - #print(operation, obj.Name, sub, point) - if "AssemblyHandbook_PartName" in balloon.PropertiesList: - if operation == 'added': - #print("Selected balloon of " + balloon.AssemblyHandbook_PartName) - - workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench - doc = balloon.Document - view = balloon.SourceView - page = self.getViewPage(view) - - partLink = doc.getObject(balloon.AssemblyHandbook_PartName) - - cache_edges_start_time = time.perf_counter() - - part_view = workbench.partsCache.getPart2DView(view, partLink) - - cache_edges_end_time = time.perf_counter() - - update_final_edges_start_time = time.perf_counter() - - # iterate edges of actual view and highlight matching edges - center = self.computePartCenter(view, partLink) - for edgeIdx in range(10000): - hasEdge = False - try: - edge = view.getEdgeByIndex(edgeIdx) - hasEdge = True - except: - pass - if not hasEdge: - break - - # reset edge format - #view.formatGeometricEdge(edgeIdx,1,0.25,0,True) - - if not hasattr(edge.Curve, 'Degree') or edge.Curve.Degree == 1: - 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.001: - view.formatGeometricEdge(edgeIdx,1,0.25,(0,0.85,0),True) - - view.requestPaint() - - update_final_edges_end_time = time.perf_counter() - - #print("Part render time: " + str(round((cache_edges_end_time - cache_edges_start_time) * 1000) / 1000) + "s") - #print("Update view time: " + str(round((update_final_edges_end_time - update_final_edges_start_time) * 1000) / 1000) + "s") - - elif operation == 'removed': - #print("Deselected balloon of " + balloon.AssemblyHandbook_PartName) - - view = balloon.SourceView - - # reset edges format - for edgeIdx in range(10000): - hasEdge = False - try: - edge = view.getEdgeByIndex(edgeIdx) - hasEdge = True - except: - pass - if not hasEdge: - break - - view.formatGeometricEdge(edgeIdx,1,0.25,0,True) - - view.requestPaint() def updateBalloon(self, balloon): + workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench + doc = App.activeDocument() view = balloon.SourceView obj = doc.getObject(balloon.AssemblyHandbook_PartName) - objectCenterView = self.computePartCenter(view, obj) + objectCenterView = workbench.techDrawExtensions.computePartCenter(view, obj) balloon.OriginX = objectCenterView.x balloon.OriginY = objectCenterView.y @@ -175,26 +93,6 @@ class AHB_View_Annotate: balloon.ViewObject.Fontsize = 4 balloon.BubbleShape = 'Inspection' balloon.EndTypeScale = 4 - - def getViewPage(self, view): - for obj in view.InList: - if obj.TypeId == 'TechDraw::DrawPage': - if view in obj.Views: - return obj - return None - - def computePartCenter(self, view, obj): - if obj.TypeId == 'App::Link': - partLink = obj - objectCenterWorld = partLink.LinkPlacement.Matrix.multiply(partLink.LinkedObject.Shape.CenterOfGravity) - else: - objectCenterWorld = obj.Shape.CenterOfGravity - - vertId = view.makeCosmeticVertex3d(objectCenterWorld) - vert = view.getCosmeticVertex(vertId) - objectCenterView = vert.Point - view.removeCosmeticVertex(vertId) - return objectCenterView from ahb_command import AHB_CommandWrapper AHB_CommandWrapper.addGuiCommand('AHB_view_annotate', AHB_View_Annotate()) diff --git a/ahb_parts_cache.py b/ahb_parts_cache.py index 06b7ac2..4abcbf0 100644 --- a/ahb_parts_cache.py +++ b/ahb_parts_cache.py @@ -13,6 +13,10 @@ class PartCachedView: import numpy as np import os + print("Rendering " + self.obj_name + " in cache") + + workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench + doc = App.getDocument(self.doc_name) #: :type doc: App.Document obj = doc.getObject(self.obj_name) #: :type obj: App.DocumentObject @@ -35,7 +39,7 @@ class PartCachedView: tmpView.recompute() # copy edges relative to center - tmpCenter = self.computePartCenter(tmpView, obj) + tmpCenter = workbench.techDrawExtensions.computePartCenter(tmpView, obj) # count lines tmpEdges = tmpView.getVisibleEdges() @@ -60,19 +64,6 @@ class PartCachedView: # delete temporary view doc.removeObject(page.Name) - - def computePartCenter(self, view, obj): - if obj.TypeId == 'App::Link': - partLink = obj - objectCenterWorld = partLink.LinkPlacement.Matrix.multiply(partLink.LinkedObject.Shape.CenterOfGravity) - else: - objectCenterWorld = obj.Shape.CenterOfGravity - - vertId = view.makeCosmeticVertex3d(objectCenterWorld) - vert = view.getCosmeticVertex(vertId) - objectCenterView = vert.Point - view.removeCosmeticVertex(vertId) - return objectCenterView class PartsCache: part_views = {} diff --git a/ahb_techdraw_extensions.py b/ahb_techdraw_extensions.py new file mode 100644 index 0000000..c78e3da --- /dev/null +++ b/ahb_techdraw_extensions.py @@ -0,0 +1,112 @@ +import FreeCAD as App +import FreeCADGui as Gui + +from PySide.QtCore import QTimer +from pickle import TRUE + +class TechDrawExtensions: + views_to_repaint = {} + + def __init__(self): + workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench + workbench.docObserver.onObjectTypeSelected('view_annotate_balloon_selected', 'TechDraw::DrawViewBalloon', lambda operation, obj, sub, point: self.onBalloonSelected(operation, obj, sub, point)) + + def repaint(self, view): + self.views_to_repaint[view] = True + QTimer.singleShot(100, self._do_repaint) + + def _do_repaint(self): + import time + + workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench + + to_repaint = self.views_to_repaint.keys() + self.views_to_repaint = {} + + for view in to_repaint: + print("Repainting " + view.Name) + + selected_balloons = [] + for obj in Gui.Selection.getSelection(): + if obj.TypeId == 'TechDraw::DrawViewBalloon' and obj.SourceView == view and 'AssemblyHandbook_PartName' in obj.PropertiesList: + selected_balloons.append(obj) + + is_first_part = True + + if len(selected_balloons) == 0: + selected_balloons.append(None) + + for balloon in selected_balloons: + if balloon is not None: + doc = balloon.Document + partLink = doc.getObject(balloon.AssemblyHandbook_PartName) + + part_view = workbench.partsCache.getPart2DView(view, partLink) + + center = self.computePartCenter(view, partLink) + + # 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 + + # reset edge format + if is_first_part: + view.formatGeometricEdge(edgeIdx,1,0.25,0,True) + + if balloon is not None and (not hasattr(edge.Curve, 'Degree') or edge.Curve.Degree == 1): + 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.001: + view.formatGeometricEdge(edgeIdx,1,0.25,(0,0.85,0),True) + + is_first_part = False + + view.requestPaint() + + def onBalloonSelected(self, operation, balloon, sub, point): + import time + + #print(operation, obj.Name, sub, point) + if "AssemblyHandbook_PartName" in balloon.PropertiesList: + #print(operation + " " + balloon.Name) + view = balloon.SourceView + self.repaint(view) + + def getViewPage(self, view): + for obj in view.InList: + if obj.TypeId == 'TechDraw::DrawPage': + if view in obj.Views: + return obj + return None + + def computePartCenter(self, view, obj): + if obj.TypeId == 'App::Link': + partLink = obj + objectCenterWorld = partLink.LinkPlacement.Matrix.multiply(partLink.LinkedObject.Shape.CenterOfGravity) + else: + objectCenterWorld = obj.Shape.CenterOfGravity + + vertId = view.makeCosmeticVertex3d(objectCenterWorld) + vert = view.getCosmeticVertex(vertId) + objectCenterView = vert.Point + view.removeCosmeticVertex(vertId) + return objectCenterView