diff --git a/ahb_cmd_view_annotate.py b/ahb_cmd_view_annotate.py index 6157a9f..8af647f 100644 --- a/ahb_cmd_view_annotate.py +++ b/ahb_cmd_view_annotate.py @@ -21,6 +21,8 @@ class AHB_View_Annotate: if 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) @@ -30,7 +32,7 @@ class AHB_View_Annotate: # 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: continue + if balloon.SourceView != view and balloon.SourceView != overlay_view: continue partLink = workbench.techDrawExtensions.getBalloonSourcePart(balloon) if partLink is None or partLink not in view.XSource: ref_name = "" @@ -47,13 +49,9 @@ class AHB_View_Annotate: # Search an existing balloon to update for obj in page.Views: if obj.TypeId == 'TechDraw::DrawViewBalloon' and workbench.techDrawExtensions.getBalloonSourcePart(obj) == partLink: - if obj.SourceView != view: continue + if obj.SourceView != overlay_view: continue balloon = obj - overlay_view = doc.getObject(view.Name + '_overlay') - if overlay_view is None: - overlay_view = view - # Create a new balloon if needed if balloon is None: partName = partLink.Name diff --git a/ahb_raster_view.py b/ahb_raster_view.py index cd7539a..0ac8789 100644 --- a/ahb_raster_view.py +++ b/ahb_raster_view.py @@ -7,6 +7,21 @@ class RasterView: doc = view.Document self.image_file_name = doc.FileName.replace('.FCStd', '') + '_raster/' + view.Name + '.png' + def init_image_projection(self): + doc = self.source_view.Document + image_name = self.source_view.Label + "_raster" + image = doc.getObject(image_name) + if image is None: + return False + + self.image_view = image + + if image.Assembly_handbook_ViewVolumeWidth > 0: + self._precompute_image_projection() + return True + + return False + def init_image(self): workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench @@ -34,9 +49,9 @@ class RasterView: self.image_view = image if image.Assembly_handbook_ViewVolumeWidth > 0: - self.init_image_projection() + self._precompute_image_projection() - def init_image_projection(self): + def _precompute_image_projection(self): YDirection = self.source_view.Direction.cross(self.source_view.XDirection) self.image_x_dir = self.source_view.XDirection / self.image_view.Assembly_handbook_ViewVolumeWidth @@ -51,6 +66,15 @@ class RasterView: offset = self.image_view.Assembly_handbook_ViewVolumeOffset return App.Vector(self.image_x_dir.dot(point3d) + offset.x, self.image_y_dir.dot(point3d) + offset.y, self.image_z_dir.dot(point3d) + offset.z) + def project3DPointToSourceView(self, point3d): + offset = self.image_view.Assembly_handbook_ViewVolumeOffset + offset = App.Vector((offset.x-0.5) * self.image_view.Assembly_handbook_ViewVolumeWidth, (offset.y-0.5) * self.image_view.Assembly_handbook_ViewVolumeHeight, (offset.z-0.5) * self.image_view.Assembly_handbook_ViewVolumeDepth) + #image_view_point = App.Vector(self.image_x_dir.dot(point3d), self.image_y_dir.dot(point3d), self.image_z_dir.dot(point3d)) + #return App.Vector(image_view_point.x * self.image_view.Assembly_handbook_ViewVolumeWidth, image_view_point.y * self.image_view.Assembly_handbook_ViewVolumeHeight, 0) + + YDirection = self.source_view.Direction.cross(self.source_view.XDirection) + return App.Vector(self.source_view.XDirection.dot(point3d) + offset.x, YDirection.dot(point3d) + offset.y, self.image_z_dir.dot(point3d) + offset.z) + def projectImageViewPointTo3D(self, point2d): offset = self.image_view.Assembly_handbook_ViewVolumeOffset p = point2d - offset @@ -96,25 +120,15 @@ class RasterView: self.image_view.Assembly_handbook_ViewVolumeHeight = viewVolume.getHeight() self.image_view.Assembly_handbook_ViewVolumeDepth = viewVolume.getDepth() - sb_offset = viewVolume.projectToScreen(coin.SbVec3f(0,0,0)) - self.image_view.Assembly_handbook_ViewVolumeOffset = App.Vector(sb_offset[0], sb_offset[1], sb_offset[2]) - - self.init_image_projection() - docView.saveImage(self.image_file_name, 4096, 4096, "#ffffff") with Image.open(self.image_file_name) as img: original_size = img.size - p2dA = self.project3DPointToImageView(App.Vector(0,0,0)) - p2dB = self.project3DPointToImageView(view.XDirection) - imageScale = view.Scale / (p2dB.x - p2dA.x) / original_size[0] * 10 - #print('imageScale', imageScale) - bg = Image.new(img.mode, img.size, '#ffffff') # fills an image with the background color diff = ImageChops.difference(img, bg) # diff between the actual image and the background color bbox = diff.getbbox() # finds border size (non-black portion of the image) - print(bbox) + #print(bbox) #image_center = (bbox[0] + (bbox[2] - bbox[0])/2 - img.size[0]/2, bbox[1] + (bbox[3] - bbox[1])/2 - img.size[1]/2) #print(image_center) img = img.crop(bbox) @@ -135,6 +149,17 @@ class RasterView: #debugPoint(App.Vector(131.23702882966705, -655.0000021095163, 145.21130178331268)) img.save(self.image_file_name) + + sb_offset = viewVolume.projectToScreen(coin.SbVec3f(0,0,0)) + crop_offset = App.Vector(((bbox[0] + bbox[2])/2 - original_size[0]/2)/original_size[0], ((bbox[1] + bbox[3])/2 - original_size[1]/2)/original_size[1], 0) + self.image_view.Assembly_handbook_ViewVolumeOffset = App.Vector(sb_offset[0] - crop_offset.x, sb_offset[1] + crop_offset.y, sb_offset[2]) + + self._precompute_image_projection() + + p2dA = self.project3DPointToImageView(App.Vector(0,0,0)) + p2dB = self.project3DPointToImageView(view.XDirection) + imageScale = view.Scale / (p2dB.x - p2dA.x) / original_size[0] * 10 + #print('imageScale', imageScale) App.closeDocument(doc.Name) diff --git a/ahb_techdraw_extensions.py b/ahb_techdraw_extensions.py index e763b1d..e2423f5 100644 --- a/ahb_techdraw_extensions.py +++ b/ahb_techdraw_extensions.py @@ -16,6 +16,9 @@ class CursorItem(QtGui.QGraphicsItem): self.size = 100.0 self.view = view + def removeSceneEventFilter(self, a, b): + print('removeSceneEventFilter', a, b) + def onViewPosChange(self, callback): self.viewPosChangeCallback = callback @@ -173,11 +176,33 @@ class TechDrawExtensions: 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() page.KeepUpdated = True # once we have hidden the source view, there should be no performance issue @@ -293,6 +318,13 @@ class TechDrawExtensions: 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); @@ -412,7 +444,7 @@ class TechDrawExtensions: view = balloon.SourceView self.updateBalloonCursor(view) if self.enable_selected_part_highlight: - self.repaint(view) # disabled for now, for performance reasons + self.repaint(view) def onBalloonChanged(self, obj, prop): # Avoid reentry @@ -496,6 +528,10 @@ class TechDrawExtensions: needPageUpdate = False 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' not in view.PropertiesList or not view.Assembly_handbook_RasterView: needPageUpdate = True self.refreshView(view) @@ -534,7 +570,24 @@ class TechDrawExtensions: 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): + view = self.getSourceView(view) + if obj.TypeId == 'App::Link': partLink = obj objectCenterWorld = partLink.LinkPlacement.Matrix.multiply(partLink.LinkedObject.Shape.CenterOfGravity) @@ -561,6 +614,12 @@ class TechDrawExtensions: return self.projectPoint(view, objectCenterWorld) def projectPoint(self, view, point3d): + if 'Assembly_handbook_RasterView' in view.PropertiesList or 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: