@ -54,11 +54,17 @@ class CursorItem(QtGui.QGraphicsItem):
scale = self . view . Scale * 10.0
self . setPos ( p . x * scale , - p . y * scale )
class ViewCache :
def __init__ ( self ) :
self . projected_points = { } # maps 3D vectors to their 2D projection
class TechDrawExtensions :
views_to_repaint = { }
view_cursors = { }
view_cache = { }
updating_balloon = False
edited_view = None
@ -70,17 +76,21 @@ class TechDrawExtensions:
def repaint ( self , view ) :
self . views_to_repaint [ view ] = True
QTimer . singleShot ( 100 , self . _do_repaint )
QTimer . singleShot ( 10 , self . _do_repaint )
def _do_repaint ( self ) :
workbench = Gui . getWorkbench ( " AssemblyHandbookWorkbench " ) #: :type workbench: AssemblyHandbookWorkbench
selection = Gui . Selection . getSelection ( )
to_repaint = self . views_to_repaint . keys ( )
self . views_to_repaint = { }
for view in to_repaint :
#print("Repainting " + view.Name)
doc = view . Document
selected_balloons = [ ]
for obj in Gui . Selection . getSelection ( ) :
if obj . TypeId == ' TechDraw::DrawViewBalloon ' and obj . SourceView == view and ' Assembly_handbook_PartName ' in obj . PropertiesList :
@ -88,17 +98,47 @@ class TechDrawExtensions:
is_first_part = True
if len ( selected_balloons ) == 0 :
selected_balloons . append ( None )
parts_to_paint = [ ]
# repaint parts that are highlighted by selection
for balloon in selected_balloons :
if balloon is not None :
doc = balloon . Document
partLink = doc . getObject ( balloon . Assembly_handbook_PartName )
part = doc . getObject ( balloon . Assembly_handbook_PartName )
if part in view . XSource :
parts_to_paint . append ( part )
part_view = workbench . partsCache . getPart2DView ( view , partLink )
# repaint parts that are new in this step (thick line)
if ' Assembly_handbook_PreviousStepView ' in view . PropertiesList :
prev_view = doc . getObject ( view . Assembly_handbook_PreviousStepView )
if prev_view is not None :
for part in view . XSource :
if 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.1
line_thickness = default_line_thickness
default_color = ( 0 , 0 , 0 )
color = default_color
if part is not None :
part_view = workbench . partsCache . getPart2DView ( view , part )
center = self . computePartCenter ( view , partLink )
center = self . computePartCenter ( view , part )
if ' Assembly_handbook_PreviousStepView ' in view . PropertiesList :
prev_view = doc . getObject ( view . Assembly_handbook_PreviousStepView )
if prev_view is not None :
if not part in prev_view . XSource :
line_thickness * = 2.5
for balloon in selected_balloons :
if part . Name == balloon . Assembly_handbook_PartName :
color = ( 0.0 , 0.85 , 0.0 ) # selection highlighting
# iterate edges of actual view and highlight matching edges
for edgeIdx in range ( 10000 ) :
@ -111,11 +151,8 @@ class TechDrawExtensions:
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 ) and len ( edge . Vertexes ) == 2 :
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 ,
@ -131,7 +168,14 @@ class TechDrawExtensions:
#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 :
view . formatGeometricEdge ( edgeIdx , 1 , 0.25 , ( 0 , 0.85 , 0 ) , True )
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
@ -144,11 +188,12 @@ class TechDrawExtensions:
cursor . onViewPosChange ( lambda new_pos : self . onCursorMoved ( view , new_pos ) )
self . view_cursors [ view ] = cursor
if selected_balloons [ 0 ] is None :
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 refreshView ( self , view ) :
doc = view . Document
@ -161,6 +206,7 @@ class TechDrawExtensions:
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
@ -233,8 +279,6 @@ class TechDrawExtensions:
balloon . Assembly_handbook_OriginOffsetY = new_pos . y - center . y
def onBalloonSelected ( self , operation , balloon , sub , point ) :
import time
#print(operation, obj.Name, sub, point)
if " Assembly_handbook_PartName " in balloon . PropertiesList :
#print(operation + " " + balloon.Name)
@ -286,7 +330,7 @@ class TechDrawExtensions:
def forceRedrawPage ( self , page , callback = None ) :
for view in page . Views :
if view . TypeId == ' TechDraw::DrawViewPart ' and ' Assembly_handbook_PreviousStepView ' in view . PropertiesList :
self . refreshView ( view )
self . refreshView ( view )
if page . KeepUpdated :
for view in page . Views :
@ -294,13 +338,17 @@ class TechDrawExtensions:
view . recompute ( )
for view in page . Views :
if view . TypeId == ' TechDraw::DrawViewPart ' :
view . recompute ( )
view . recompute ( )
self . repaint ( view )
if callback is not None :
callback ( )
else :
page . KeepUpdated = True
def restoreKeepUpdated ( ) :
page . KeepUpdated = False
for view in page . Views :
if view . TypeId == ' TechDraw::DrawViewPart ' :
self . repaint ( view )
if callback is not None :
callback ( )
QTimer . singleShot ( 10 , restoreKeepUpdated )
@ -311,9 +359,25 @@ class TechDrawExtensions:
objectCenterWorld = partLink . LinkPlacement . Matrix . multiply ( partLink . LinkedObject . Shape . CenterOfGravity )
else :
objectCenterWorld = obj . Shape . CenterOfGravity
view_cache = self . getViewCache ( view )
key = ( objectCenterWorld . x , objectCenterWorld . y , objectCenterWorld . z )
projected_point = view_cache . projected_points . get ( key , None )
vertId = view . makeCosmeticVertex3d ( objectCenterWorld )
vert = view . getCosmeticVertex ( vertId )
objectCenterView = vert . Point
view . removeCosmeticVertex ( vertId )
return objectCenterView
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
def getViewCache ( self , view ) :
cache = self . view_cache . get ( view , None )
if cache is None :
cache = ViewCache ( )
self . view_cache [ view ] = cache
return cache