├── Gui
└── Resources
│ ├── dd_resources.rcc
│ ├── compile_resources_pack.py
│ └── icons
│ ├── welding
│ ├── Flachennaht.svg
│ ├── square_groove.svg
│ ├── J_groove.svg
│ ├── Bevel_groove.svg
│ ├── Stirnflachnaht.svg
│ ├── Y_groove.svg
│ ├── Schragnaht.svg
│ ├── V_groove.svg
│ ├── Steilflankennaht.svg
│ ├── J_groove_(alt).svg
│ ├── fillet.svg
│ ├── double_square_groove.svg
│ ├── Plug_or_slot.svg
│ ├── fillet_with_dashed_line.svg
│ ├── U_groove.svg
│ ├── Double_J_groove.svg
│ ├── Double_bevel_groove.svg
│ ├── Double_V_groove.svg
│ ├── double_fillet.svg
│ ├── flange_edge.svg
│ ├── Double_Y_groove.svg
│ ├── Double_J_groove_(alt).svg
│ ├── VU_groove.svg
│ ├── fillet_with_circle.svg
│ ├── Spot_or_projection.svg
│ ├── Double_U_groove.svg
│ ├── Surfacing.svg
│ ├── Falznnaht.svg
│ ├── plain.svg
│ └── Seam.svg
│ ├── drawLine.svg
│ ├── drawLineWithArrow.svg
│ ├── table_dd.svg
│ ├── bendingNote.svg
│ ├── help.svg
│ ├── drawCircularArc.svg
│ ├── drawCircle.svg
│ └── noteCircle.svg
├── .gitignore
├── escapeDimensioning.py
├── grabPointAdd.py
├── crudeDebugger.py
├── textMove.py
├── README.md
├── textAdd.py
├── InitGui.py
├── deleteDimension.py
├── dimensionSvgConstructor.py
├── cgpr.py
├── table_dd.py
├── toleranceDialog.py
├── freeDrawing.py
├── textEdit.py
├── dimensionSvgConstructor_testCenterLines.py
├── toleranceDialog.ui
├── previewDimension.ui
├── centerView.py
├── noteCircle.py
├── toleranceAdd.py
├── radiusDimension.py
├── circularDimension.py
└── lineSearches.py
/Gui/Resources/dd_resources.rcc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easyw/FreeCAD_drawing_dimensioning/master/Gui/Resources/dd_resources.rcc
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.pyc
3 | crudeDebugger_output*
4 | crudeDebugger_output/*
5 | .syncthing.*
6 | \#*
7 | testCases_selectionOverlay/
8 | dxfwrite/
--------------------------------------------------------------------------------
/escapeDimensioning.py:
--------------------------------------------------------------------------------
1 |
2 | from dimensioning import *
3 |
4 | class EscapeDimensioning:
5 | def Activated(self):
6 | drawingVars = getDrawingPageGUIVars()
7 | drawingVars.page.touch()
8 | App.ActiveDocument.recompute()
9 |
10 | def GetResources(self):
11 | return {
12 | 'Pixmap' : ':/dd/icons/escape.svg' ,
13 | 'MenuText': 'escape Dimensioning',
14 | 'ToolTip': 'escape Dimensioning'
15 | }
16 |
17 | FreeCADGui.addCommand('dd_escapeDimensioning', EscapeDimensioning())
18 |
--------------------------------------------------------------------------------
/Gui/Resources/compile_resources_pack.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 | import os, glob
3 |
4 | qrc_filename = 'drawing_dimensioning.qrc'
5 | assert not os.path.exists(qrc_filename)
6 |
7 | qrc = '''
8 | '''
9 | for fn in glob.glob('icons/*.svg') + glob.glob('icons/welding/*.svg') + glob.glob('ui/*.ui'):
10 | qrc = qrc + '\n\t\t%s' % fn
11 | qrc = qrc + '''\n\t
12 | '''
13 |
14 | print(qrc)
15 |
16 | f = open(qrc_filename,'w')
17 | f.write(qrc)
18 | f.close()
19 |
20 | os.system('rcc -binary %s -o dd_resources.rcc' % qrc_filename)
21 | os.remove(qrc_filename)
22 |
--------------------------------------------------------------------------------
/grabPointAdd.py:
--------------------------------------------------------------------------------
1 |
2 | from dimensioning import *
3 | import previewDimension
4 |
5 |
6 | def grabPointDrawSVG( x, y, preview=False): # draw a cross
7 | if preview:
8 | return '''
9 |
10 |
11 | ''' % ( x-1, y, x+1, y, x, y-1, x, y+1)
12 | else:
13 | return ' ''' % ( x, y, x, y)
14 |
15 |
16 | def grabPoint_preview( x, y):
17 | return grabPointDrawSVG( x, y, preview=True)
18 |
19 | def grabPoint_clickHandler( x, y):
20 | d.selections = [ PlacementClick( x, y) ]
21 | return 'createDimension:%s' % findUnusedObjectName('grabPoint')
22 |
23 | class Proxy_grabPoint( Proxy_DimensionObject_prototype ):
24 | def dimensionProcess( self ):
25 | return d
26 | d = DimensioningProcessTracker( grabPointDrawSVG )
27 | d.ProxyClass = Proxy_grabPoint
28 |
29 | class AddGrabPoint:
30 | def Activated(self):
31 | V = getDrawingPageGUIVars()
32 | d.activate( V, dialogTitle='Add Grab Point', dialogIconPath=':/dd/icons/grabPointAdd.svg', endFunction=self.Activated )
33 | previewDimension.initializePreview(
34 | d,
35 | grabPoint_preview,
36 | grabPoint_clickHandler )
37 |
38 | def GetResources(self):
39 | return {
40 | 'Pixmap' : ':/dd/icons/grabPointAdd.svg' ,
41 | 'MenuText': 'Add grab point to draw a free dimension',
42 | 'ToolTip': 'Add grab point to draw a free dimension'
43 | }
44 | FreeCADGui.addCommand('dd_grabPoint', AddGrabPoint())
45 |
46 |
47 |
--------------------------------------------------------------------------------
/crudeDebugger.py:
--------------------------------------------------------------------------------
1 | '''
2 | Python Code for generating crude debugging functionality for the Dimensioning FreeCAD module.
3 | '''
4 |
5 | import os, sys
6 |
7 | __dir__ = os.path.dirname(__file__)
8 |
9 | debug_output_directory = os.path.join( __dir__ , 'crudeDebugger_output' )
10 | if not os.path.exists(debug_output_directory):
11 | os.mkdir(debug_output_directory)
12 | sys.path.append(debug_output_directory)
13 |
14 | global crudeDebuggerOutputFile
15 | crudeDebuggerOutputFile = None
16 |
17 | def crudeDebuggerPrint( txt, epilogue='\n' ):
18 | global crudeDebuggerOutputFile
19 | if crudeDebuggerOutputFile == None:
20 | fn = os.path.join(debug_output_directory, 'output')
21 | crudeDebuggerOutputFile = open(fn, 'w')
22 | crudeDebuggerOutputFile.write(txt+epilogue)
23 | crudeDebuggerOutputFile.flush()
24 |
25 |
26 | def printingDebugging( pythonFile, debugExt='_crudeDebugging.py' ):
27 | #headers = [] #i.e. [class , function, loop, ... #nah no nessary for now
28 | assert pythonFile.endswith('.py')
29 | output = ['from crudeDebugger import crudeDebuggerPrint' ]
30 | insideTextblock = False
31 | bracketBalance = 0
32 | bB2 = 0 #for ''' special bracket case
33 | for lineNo, line in enumerate(open(pythonFile)):
34 | indent = line[: len(line)-len(line.lstrip())]
35 | prev_bracketBalance = bracketBalance
36 | bracketBalance = prev_bracketBalance + line.count('(') - line.count(')') + line.count('{') - line.count('}') + line.count('[') - line.count(']')
37 | prev_bB2 = bB2
38 | bB2 = (prev_bB2 + line.count("'''"))%2
39 | if len(line.strip()) == 0:
40 | pass
41 | elif line.strip().startswith("'''"):
42 | insideTextblock = not insideTextblock
43 | if insideTextblock:
44 | pass
45 | elif insideTextblock:
46 | pass
47 | elif any( line.lstrip().startswith(s) for s in ['elif','def','class','else','except','"',"'"] ):
48 | pass
49 | elif prev_bB2 <> 0:
50 | pass
51 | elif prev_bracketBalance == 0: #and bracketBalance == 0:
52 | lineCleaned = line.rstrip().replace("'''", "''' +" + '"' + "'''" + '"' + " +'''")
53 |
54 | output.append('%scrudeDebuggerPrint(%s%s:%i \t%s %s)' % (indent,"'''", os.path.basename(pythonFile), lineNo, lineCleaned, "'''"))
55 | output.append(line[:-1]) #remove \n caracter
56 |
57 | output_fn = os.path.join(debug_output_directory, os.path.basename(pythonFile[:-3]) + debugExt)
58 | f = open(output_fn,'w')
59 | f.write('\n'.join(output))
60 | f.close()
61 |
62 |
63 |
64 | if __name__ == '__main__':
65 | print('testing crudeDebugger for Dimensioning module')
66 | printingDebugging('crudeDebugger.py')
67 |
--------------------------------------------------------------------------------
/textMove.py:
--------------------------------------------------------------------------------
1 |
2 | from dimensioning import *
3 | import selectionOverlay, previewDimension
4 | import XMLlib
5 |
6 | d = DimensioningProcessTracker()
7 |
8 | def moveTextSvg( x, y):
9 | e = d.elementXML
10 | xml = e.XML[e.pStart:e.pEnd]
11 | xml = XMLlib.replaceParm(xml, 'x', '%f' % x )
12 | xml = XMLlib.replaceParm(xml, 'y', '%f' % y )
13 | if e.parms.has_key('transform'):
14 | xml = XMLlib.replaceParm(xml, 'transform', "rotate(%s %f,%f)" % (d.textRotation,x,y) )
15 | return e.XML[:e.pStart] + xml + e.XML[e.pEnd:]
16 |
17 | def placeText( x, y):
18 | FreeCAD.ActiveDocument.openTransaction("move text")
19 | d.dimToEdit.ViewResult = moveTextSvg(x, y )
20 | FreeCAD.ActiveDocument.commitTransaction()
21 | return 'stopPreview'
22 |
23 | def MoveDimensionText( event, referer, elementXML, elementParms, elementViewObject ):
24 | d.dimToEdit = elementViewObject
25 | d.elementXML = elementXML
26 | debugPrint(2, 'moving %s' % elementViewObject.Name)
27 | if elementXML.parms.has_key('transform'):
28 | transform = elementXML.parms['transform']
29 | t = transform[ XMLlib.findOffset(transform,'rotate(',0): ]
30 | d.textRotation = XMLlib.splitMultiSep(t, ', ')[0]
31 | debugPrint(3, 'd.textRotation %s' % d.textRotation)
32 | else:
33 | d.textRotation = None
34 | selectionOverlay.hideSelectionGraphicsItems()
35 | previewDimension.initializePreview( d, moveTextSvg, placeText )
36 |
37 | maskBrush = QtGui.QBrush( QtGui.QColor(0,160,0,100) )
38 | maskPen = QtGui.QPen( QtGui.QColor(0,160,0,100) )
39 | maskPen.setWidth(0.0)
40 | maskHoverPen = QtGui.QPen( QtGui.QColor(0,255,0,255) )
41 | maskHoverPen.setWidth(0.0)
42 |
43 | class MoveText:
44 | def Activated(self):
45 | V = getDrawingPageGUIVars() #needs to be done before dialog show, else Qt active is dialog and not freecads
46 | d.activate( V, dialogTitle='Move Text', dialogIconPath=':/dd/icons/textMove.svg', endFunction=self.Activated )
47 | selectGraphicsItems = selectionOverlay.generateSelectionGraphicsItems(
48 | [obj for obj in V.page.Group if obj.Name.startswith('dim')],
49 | MoveDimensionText ,
50 | sceneToAddTo = V.graphicsScene,
51 | transform = V.transform,
52 | doTextItems = True,
53 | pointWid=2.0,
54 | maskPen=maskPen,
55 | maskHoverPen=maskHoverPen,
56 | maskBrush = maskBrush
57 | )
58 |
59 | def GetResources(self):
60 | msg = "Move a dimension's text"
61 | return {
62 | 'Pixmap' : ':/dd/icons/textMove.svg' ,
63 | 'MenuText': msg,
64 | 'ToolTip': msg
65 | }
66 | FreeCADGui.addCommand('dd_moveText', MoveText())
67 |
68 |
69 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Flachennaht.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/square_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/J_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Bevel_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Stirnflachnaht.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Y_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Schragnaht.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/V_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Steilflankennaht.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/J_groove_(alt).svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/fillet.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/double_square_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | FreeCAD_drawing_dimensioning
2 | ============================
3 |
4 | Drawing dimensioning workbench for FreeCAD v0.15 and v0.16.
5 |
6 | Take note that hamish2014/FreeCAD_drawing_dimensioning is no longer maintained.
7 |
8 | Intended work-flow:
9 | * create a drawing page and a drawing of the part using the drawing workbench
10 | * switch to the drawing dimensioning workbench to add dimensions to that drawing
11 |
12 | Features
13 | * linear dimensioning
14 | * circular and radial dimensioning
15 | * angular dimension
16 | * center lines
17 | * adding, editing and moving dimension text
18 | * deleting dimensions
19 | * Draw a dimension or a symbol anywhere on the drawing with "Add grab point"
20 |
21 | Limitations
22 | * No parametric updating, if the drawing is updated the dimensions need to be redone
23 | * only works with FreeCAD version 0.15+
24 |
25 |
26 |
27 | Linux Installation Instructions
28 | -------------------------------
29 |
30 | To use this workbench clone this git repository under your FreeCAD MyScripts directory, and install the pyside and numpy python libraries.
31 | On a Linux Debian based system such as Ubuntu, installation can be done through BASH as follows
32 |
33 | ```bash
34 | $ sudo apt-get install git python-numpy python-pyside
35 | $ mkdir ~/.FreeCAD/Mod
36 | $ cd ~/.FreeCAD/Mod
37 | $ git clone https://github.com/hamish2014/FreeCAD_drawing_dimensioning.git
38 | ```
39 |
40 | Once installed, use git to easily update to the latest version:
41 | ```bash
42 | $ cd ~/.FreeCAD/Mod/FreeCAD_drawing_dimensioning
43 | $ git pull
44 | $ rm *.pyc
45 | ```
46 | Windows Installation Instructions
47 | ---------------------------------
48 |
49 | Tested with 015.4415 Development Snapshot on a Windows 7 64bit-System (thanks BPLRFE )
50 |
51 | * download the git repository as ZIP
52 | * assuming FreeCAD is installed in "C:\PortableApps\FreeCAD 0_15", go to "C:\PortableApps\FreeCAD 0_15\Mod" within Windows Explorer
53 | * create new directory named "DrawingDimensioning"
54 | * unzip downloaded repository in "C:\PortableApps\FreeCAD 0_15\Mod\DrawingDimensioning"
55 |
56 | FreeCAD will now have a new workbench-entry called "DrawingDimensioning".
57 |
58 | *Pyside and Numpy are integrated in the FreeCAD dev-Snapshots 0.15, so these Python packages do not need to be installed individually*
59 |
60 | To update to the latest version, delete the DrawingDimensioning folder and redownload the git repository.
61 |
62 | Mac Installation Instructions
63 | -----------------------------
64 |
65 | Copy or unzip the drawing dimensioning folder to the directory *FreeCAD.app*/Contents/Mod
66 |
67 | where *FreeCAD.app* is the folder where FreeCAD is installed. (thanks PLChris)
68 |
69 | Setting your dimensioning preferences
70 | -------------------------------------
71 |
72 | Unit preferences are taken from the General unit preferences (excluding number of decimal places!).
73 | To set unit preferences goto edit -> preferences -> general -> units
74 |
75 | To set up your desired dimensioning style
76 | 1. open FreeCAD
77 | 2. switch to the Drawing dimensioning workbench
78 | 3. edit -> preferences -> drawing dimensioning
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Plug_or_slot.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/fillet_with_dashed_line.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/U_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Double_J_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Double_bevel_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Double_V_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/double_fillet.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/flange_edge.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Double_Y_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/textAdd.py:
--------------------------------------------------------------------------------
1 | # This Python file uses the following encoding: utf-8
2 | from dimensioning import *
3 | import previewDimension
4 | from dimensionSvgConstructor import *
5 |
6 | d = DimensioningProcessTracker()
7 |
8 | def textSVG( x, y, text='text', rotation=0.0, textRenderer_addText= defaultTextRenderer):
9 | return ' %s ' % textRenderer_addText(x,y,text,rotation=rotation)
10 |
11 | d.registerPreference( 'textRenderer_addText', ['inherit','5', 0], 'text properties (AddText)', kind='font' )
12 |
13 | class text_widget:
14 | def valueChanged( self, arg1):
15 | d.text = arg1
16 | def generateWidget( self, dimensioningProcess ):
17 | self.lineEdit = QtGui.QLineEdit()
18 | self.lineEdit.setText('text')
19 | d.text = 'text'
20 | self.lineEdit.textChanged.connect(self.valueChanged)
21 | return self.lineEdit
22 | def add_properties_to_dimension_object( self, obj ):
23 | obj.addProperty("App::PropertyString", 'text', 'Parameters')
24 | obj.text = d.text.encode('utf8')
25 | def get_values_from_dimension_object( self, obj, KWs ):
26 | KWs['text'] = obj.text #should be unicode
27 | d.dialogWidgets.append( text_widget() )
28 | class rotation_widget:
29 | def valueChanged( self, arg1):
30 | d.rotation = arg1
31 | def generateWidget( self, dimensioningProcess ):
32 | self.spinbox = QtGui.QDoubleSpinBox()
33 | self.spinbox.setValue(0)
34 | d.rotation = 0
35 | self.spinbox.setMinimum( -180 )
36 | self.spinbox.setMaximum( 180 )
37 | self.spinbox.setDecimals( 1 )
38 | self.spinbox.setSingleStep( 5 )
39 | self.spinbox.setSuffix(unicode('°','utf8'))
40 | self.spinbox.valueChanged.connect(self.valueChanged)
41 | return DimensioningTaskDialog_generate_row_hbox('rotation', self.spinbox)
42 | def add_properties_to_dimension_object( self, obj ):
43 | obj.addProperty("App::PropertyAngle", 'rotation', 'Parameters')
44 | obj.rotation = d.rotation
45 | def get_values_from_dimension_object( self, obj, KWs ):
46 | KWs['rotation'] = obj.rotation #should be unicode
47 | d.dialogWidgets.append( rotation_widget() )
48 |
49 |
50 | def addText_preview(mouseX, mouseY):
51 | return textSVG(mouseX, mouseY, d.text, d.rotation, **d.dimensionConstructorKWs )
52 |
53 | def addText_clickHandler( x, y ):
54 | d.selections = [ PlacementClick( x, y) ]
55 | return 'createDimension:%s' % findUnusedObjectName('text')
56 |
57 | class Proxy_textAdd( Proxy_DimensionObject_prototype ):
58 | def dimensionProcess( self ):
59 | return d
60 | d.ProxyClass = Proxy_textAdd
61 | d.proxy_svgFun = textSVG
62 |
63 |
64 | class AddText:
65 | def Activated(self):
66 | V = getDrawingPageGUIVars()
67 | d.activate( V, dialogTitle='Add Text', dialogIconPath= ':/dd/icons/textAdd.svg', endFunction=self.Activated )
68 | previewDimension.initializePreview( d, addText_preview, addText_clickHandler)
69 |
70 | def GetResources(self):
71 | return {
72 | 'Pixmap' : ':/dd/icons/textAdd.svg' ,
73 | 'MenuText': 'Add text to drawing',
74 | 'ToolTip': 'Add text to drawing'
75 | }
76 | FreeCADGui.addCommand('dd_addText', AddText())
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Double_J_groove_(alt).svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/VU_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/fillet_with_circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Spot_or_projection.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Double_U_groove.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Surfacing.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/drawLine.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
76 |
--------------------------------------------------------------------------------
/InitGui.py:
--------------------------------------------------------------------------------
1 | import dimensioning #QtCore.QResource.registerResource happens in there
2 |
3 | class DrawingDimensioningWorkbench (Workbench):
4 | Icon = ':/dd/icons/linearDimension.svg'
5 | MenuText = 'Drawing Dimensioning'
6 | def Initialize(self):
7 | import linearDimension
8 | import linearDimension_stack
9 | import deleteDimension
10 | import circularDimension
11 | import grabPointAdd
12 | import textAdd
13 | import textEdit
14 | import textMove
15 | import escapeDimensioning
16 | import angularDimension
17 | import radiusDimension
18 | import centerLines
19 | import noteCircle
20 | import table_dd
21 | import centerView
22 | import toleranceAdd
23 | import recomputeDimensions
24 | from drawing_wb_shortcuts import newpageShortcuts
25 | self.appendToolbar('Drawing Workbench shortcuts', newpageShortcuts + [
26 | 'dd_new_drawing_page_preferences',
27 | 'dd_Drawing_OrthoViews',
28 | ] )
29 | # copy the Drawing toolbar
30 | import DrawingGui
31 | self.appendToolbar('Drawing Workbench Commands',["Drawing_NewPage",
32 | "Drawing_NewView","Drawing_OrthoViews","Drawing_OpenBrowserView",
33 | "Drawing_Annotation","Drawing_Clip","Drawing_Symbol",
34 | "Drawing_DraftView","Drawing_ExportPage"])
35 |
36 | commandslist = [
37 | 'dd_linearDimension', #where dd is short-hand for drawing dimensioning
38 | 'dd_linearDimensionStack',
39 | 'dd_circularDimension',
40 | 'dd_radiusDimension',
41 | 'dd_angularDimension',
42 | 'dd_centerLines',
43 | 'dd_centerLine',
44 | 'dd_noteCircle',
45 | 'dd_grabPoint',
46 | 'dd_addText',
47 | # 'dd_editText', # no longer available to user, else to complicated! In particular multiple avenues available to user to change text properties
48 | # 'dd_moveText', # therefore sticking with the FreeCAD way of doing things
49 | 'dd_addTolerance',
50 | 'dd_addTable',
51 | 'dd_deleteDimension',
52 | 'dd_escapeDimensioning',
53 | 'dd_recomputeDimensions',
54 | ]
55 | self.appendToolbar('Drawing Dimensioning', commandslist)
56 | import unfold
57 | import unfold_bending_note
58 | import unfold_export_to_dxf
59 | unfold_cmds = [
60 | 'dd_unfold',
61 | 'dd_bendingNote',
62 | 'dd_centerView',
63 | 'dd_exportToDxf'
64 | ]
65 | self.appendToolbar( 'Drawing Dimensioning Folding', unfold_cmds )
66 | import weldingSymbols
67 | git_commit_no = int( FreeCAD.Version()[2].split()[0] )
68 | if git_commit_no > 5166:
69 | weldingCommandList = ['dd_weldingGroupCommand']
70 | else:
71 | weldingCommandList = weldingSymbols.weldingCmds
72 | self.appendToolbar('Drawing Dimensioning Welding Symbols', weldingCommandList)
73 | self.appendToolbar('Drawing Dimensioning Help', [ 'dd_help' ])
74 | FreeCADGui.addIconPath(':/dd/icons')
75 | FreeCADGui.addPreferencePage( ':/dd/ui/drawing_dimensioing_prefs-base.ui','Drawing Dimensioning' )
76 |
77 |
78 | Gui.addWorkbench(DrawingDimensioningWorkbench())
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Falznnaht.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/drawLineWithArrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
77 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/plain.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
77 |
--------------------------------------------------------------------------------
/deleteDimension.py:
--------------------------------------------------------------------------------
1 |
2 | from dimensioning import *
3 | import selectionOverlay, previewDimension
4 |
5 | d = DimensioningProcessTracker()
6 |
7 | def deleteDimension( event, referer, elementXML, elementParms, elementViewObject ):
8 | debugPrint(2, 'deleting dimension %s' % elementViewObject.Name)
9 | FreeCAD.ActiveDocument.openTransaction("Delete %s" % elementViewObject.Name)
10 | FreeCAD.ActiveDocument.removeObject( elementViewObject.Name )
11 | FreeCAD.ActiveDocument.commitTransaction()
12 | recomputeWithOutViewReset(d.drawingVars)
13 | FreeCADGui.Control.closeDialog()
14 | if d.endFunction <> None:
15 | previewDimension.preview.dimensioningProcessTracker = d
16 | previewDimension.timer.start( 1 )
17 |
18 | class deleteAllButton:
19 | def deleteAllDimensions( self, arg1=None):
20 | try :
21 | FreeCAD.ActiveDocument.openTransaction("Delete All Dimensions")
22 | debugPrint(2,'Deleting all dimensioning objects')
23 | #FreeCAD.ActiveDocument.openTransaction("Delete All Dim. Objects")
24 | for obj in d.drawingVars.page.Group:
25 | if hasattr(obj,'Proxy') and isinstance(obj.Proxy, Proxy_DimensionObject_prototype):
26 | FreeCAD.ActiveDocument.removeObject( obj.Name )
27 | FreeCAD.ActiveDocument.commitTransaction()
28 | #FreeCAD.ActiveDocument.commitTransaction()# ah undo not working ...
29 | recomputeWithOutViewReset(d.drawingVars)
30 | FreeCADGui.Control.closeDialog()
31 | except:
32 | errorMessagebox_with_traceback()
33 | def generateWidget( self, dimensioningProcess ):
34 | button = QtGui.QPushButton('Delete All')
35 | button.clicked.connect( self.deleteAllDimensions )
36 | return button
37 | d.dialogWidgets.append( deleteAllButton() )
38 | class UndoInfoText:
39 | def generateWidget( self, dimensioningProcess ):
40 | vbox = QtGui.QVBoxLayout()
41 | vbox.addWidget( QtGui.QLabel('To undo a deletion:') )
42 | vbox.addWidget( QtGui.QLabel(' 1) Undo') )
43 | vbox.addWidget( QtGui.QLabel(' 2) Recompute Document') )
44 | return vbox
45 | d.dialogWidgets.append( UndoInfoText() )
46 |
47 |
48 | maskBrush = QtGui.QBrush( QtGui.QColor(160,0,0,100) )
49 | maskPen = QtGui.QPen( QtGui.QColor(160,0,0,100) )
50 | maskPen.setWidth(0.0)
51 | maskHoverPen = QtGui.QPen( QtGui.QColor(255,0,0,255) )
52 | maskHoverPen.setWidth(0.0)
53 |
54 | class DeleteDimension:
55 | def Activated(self):
56 | V = getDrawingPageGUIVars()
57 | d.activate(V, dialogTitle='Delete Dimension', dialogIconPath=':/dd/icons/deleteDimension.svg' , endFunction=self.Activated, grid=False)
58 | selectionOverlay.generateSelectionGraphicsItems(
59 | [obj for obj in V.page.Group if hasattr(obj,'Proxy') and isinstance(obj.Proxy, Proxy_DimensionObject_prototype)],
60 | doSelectViewObjectPoints = True,
61 | onClickFun=deleteDimension,
62 | sceneToAddTo = V.graphicsScene,
63 | transform = V.transform,
64 | pointWid=1.0,
65 | maskPen=maskPen,
66 | maskHoverPen=maskHoverPen,
67 | maskBrush = maskBrush
68 | )
69 | selectionOverlay.addProxyRectToRescaleGraphicsSelectionItems( V.graphicsScene, V.graphicsView, V.width, V.height)
70 |
71 | def GetResources(self):
72 | return {
73 | 'Pixmap' : ':/dd/icons/deleteDimension.svg',
74 | 'MenuText': 'Delete Dimension',
75 | 'ToolTip': 'Delete a dimension'
76 | }
77 |
78 | FreeCADGui.addCommand('dd_deleteDimension', DeleteDimension())
79 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/table_dd.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
95 |
--------------------------------------------------------------------------------
/dimensionSvgConstructor.py:
--------------------------------------------------------------------------------
1 | # This Python file uses the following encoding: utf-8
2 | '''
3 | library containing commonly used SVGs construction functions
4 | '''
5 |
6 |
7 | import numpy
8 | from numpy import dot, pi, arctan2, sin, cos, arccos
9 | import math
10 | from math import atan2
11 | from numpy.linalg import norm
12 | from svgLib_dd import SvgTextRenderer
13 |
14 | def directionVector( A, B ):
15 | if norm(B-A) == 0:
16 | return numpy.array([0.0,0.0])
17 | else:
18 | return (B-A)/norm(B-A)
19 |
20 | def dimensionSVG_trimLine(A,B,trimA, trimB):
21 | d = directionVector( A, B)
22 | return (A + d*trimA).tolist() + (B - d*trimB).tolist()
23 |
24 | def rotate2D( v, angle ):
25 | return numpy.dot( [[ cos(angle), -sin(angle)],[ sin(angle), cos(angle)]], v)
26 |
27 | def arrowHeadSVG( tipPos, d, L1, L2, W, clr='blue'):
28 | d2 = numpy.dot( [[ 0, -1],[ 1, 0]], d) #same as rotate2D( d, pi/2 )
29 | R = numpy.array( [d, d2]).transpose()
30 | p2 = numpy.dot( R, [ L1, W/2.0 ]) + tipPos
31 | p3 = numpy.dot( R, [ L1+L2, 0 ]) + tipPos
32 | p4 = numpy.dot( R, [ L1, -W/2.0 ]) + tipPos
33 | return '' % (tipPos[0], tipPos[1], p2[0], p2[1], p3[0], p3[1], p4[0], p4[1], clr, clr)
34 |
35 | def remove_tailing_zeros(s):
36 | if '.' in s:
37 | return s.rstrip('0').rstrip('.')
38 | else:
39 | return s
40 |
41 | def dimensionText( V, formatStr, roundingDigit=6, comma=False):
42 | try:
43 | s1 = remove_tailing_zeros(formatStr % {'value':V})
44 | except TypeError:
45 | s1 = remove_tailing_zeros(formatStr % V)
46 | Vrounded = numpy.round(V, roundingDigit)
47 | try:
48 | s2 = remove_tailing_zeros(formatStr % {'value':Vrounded})
49 | except TypeError:
50 | s2 = remove_tailing_zeros(formatStr % Vrounded)
51 | s = s2 if len(s2) < len(s1) else s1
52 | if comma: s = s.replace('.',',')
53 | return s
54 |
55 | defaultTextRenderer = SvgTextRenderer(font_family='Verdana', font_size='5pt', fill="red")
56 |
57 | def svgLine( x1, y1, x2, y2, lineColor, strokeWidth):
58 | return '' % (x1, y1, x2, y2, lineColor, strokeWidth )
59 |
60 | def lineIntersection(line1, line2):
61 | x1,y1 = line1[0:2]
62 | dx1 = line1[2] - x1
63 | dy1 = line1[3] - y1
64 | x2,y2 = line2[0:2]
65 | dx2 = line2[2] - x2
66 | dy2 = line2[3] - y2
67 | # x1 + dx1*t1 = x2 + dx2*t2
68 | # y1 + dy1*t1 = y2 + dy2*t2
69 | A = numpy.array([
70 | [ dx1, -dx2 ],
71 | [ dy1, -dy2 ],
72 | ])
73 | b = numpy.array([ x2 - x1, y2 - y1])
74 | t1,t2 = numpy.linalg.solve(A,b)
75 | x_int = x1 + dx1*t1
76 | y_int = y1 + dy1*t1
77 | #assert x1 + dx1*t1 == x2 + dx2*t2
78 | return x_int, y_int
79 |
80 | def textPlacement_common_procedure( A, B, text, x_text, y_text, textRotation, textRenderer, autoPlaceText, autoPlaceOffset):
81 | if textRotation > 90:
82 | textRotation = textRotation - 180
83 | if textRotation > 88:
84 | textRotation = textRotation - 180
85 | elif textRotation > 12 :
86 | textRotation = textRotation - 90
87 | elif textRotation < -92:
88 | textRotation = textRotation + 90
89 | if not autoPlaceText:
90 | if x_text <> None and y_text <> None:
91 | return textRenderer( x_text, y_text, text, rotation=textRotation )
92 | else :
93 | return ''
94 | else:
95 | theta = (textRotation - 90)/180.0*pi
96 | pos_text = (A + B)/2 + autoPlaceOffset * numpy.array([ cos(theta), sin(theta)])
97 | return textRenderer( pos_text[0], pos_text[1], text, rotation=textRotation, text_anchor='middle' )
98 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/bendingNote.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
77 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/help.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
101 |
--------------------------------------------------------------------------------
/cgpr.py:
--------------------------------------------------------------------------------
1 | """conjugate gradient optimisation method using Polak-Ribiere search directions (CGPR)"""
2 | from numpy import array, zeros, nan, dot, inf
3 | from numpy.linalg import norm
4 | from numpy.random import rand
5 | from lineSearches import quadraticLineSearch
6 |
7 |
8 | def toStdOut(txt):
9 | print(txt)
10 |
11 | def CGPR( x0, f, grad_f, x_tol=10**-6, f_tol=None, maxIt=100,
12 | debugPrintLevel=0, printF=toStdOut, lineSearchIt=20):
13 | '''
14 | search for minimum using conjugate gradient optimisation with Polak-Ribiere search directions
15 | '''
16 | n = len(x0)
17 | x = array(x0)
18 | x_c = zeros(n) * nan
19 | u_hist = []
20 | g = nan
21 | for i in range(maxIt):
22 | f_x = f(x)
23 | printF('cgpr it %02i: norm(prev. step) %1.1e, f(x) %1.3e' % (i, norm(x_c), f_x))
24 | if debugPrintLevel > 1:
25 | printF(' x %s' % x)
26 | printF(' f(x) %s' % f_x)
27 | if norm(x_c) <= x_tol:
28 | break
29 | if f_x < f_tol:
30 | break
31 | grad_prev = g
32 | g = grad_f(x, f0=f_x)
33 | if debugPrintLevel > 1:
34 | printF(' grad_f : %s' % g)
35 | if i == 0:
36 | u = -g
37 | u_hist = [u]
38 | else:
39 | beta = dot(g - grad_prev,g) / norm(grad_prev)**2
40 | u = -g + beta*u_hist[-1]
41 | u_hist.append(u)
42 | if debugPrintLevel > 1:
43 | printF(' u %s' % u)
44 | x_next = quadraticLineSearch( f, x, f_x, u, lineSearchIt, debugPrintLevel-2, printF, tol_x=x_tol, tol_stag=inf )
45 | x_c = x_next - x
46 | x = x_next
47 | return x
48 |
49 |
50 |
51 | class GradientApproximatorForwardDifference:
52 | def __init__(self, f):
53 | self.f = f
54 | def __call__(self, x, f0, eps=10**-7):
55 | n = len(x)
56 | grad_f = zeros(n)
57 | for i in range(n):
58 | x_eps = x.copy()
59 | x_eps[i] = x_eps[i] + eps
60 | grad_f[i] = (self.f(x_eps) - f0)/eps
61 | return grad_f
62 |
63 | if __name__ == '__main__':
64 | print('Testing CGPR algorithm')
65 | print('-GradientApproximator-')
66 | def f(X) :
67 | y,z=X
68 | return y + y*z + (1.0-y)**3
69 | def grad_f(X):
70 | y,z=X
71 | return array([ 1 + z - 3*(1.0-y)**2, y ])
72 | grad_f_fd = GradientApproximatorForwardDifference(f)
73 | for i in range(2):
74 | X = rand(2)*10-5
75 | print(' X %s' % X)
76 | print(' grad_f(X) analytical: %s' % grad_f(X))
77 | print(' grad_f(X) forwardDiff.: %s' % grad_f_fd(X, f(X)))
78 | print(' norm(analytical-forwardDiff) %e' % norm(grad_f(X) - grad_f_fd(X, f(X))) )
79 |
80 | def f_basic(X):
81 | y,z = X
82 | return 2*(y-3)**2 + 2*(z+1)**2
83 |
84 | CGPR( -10+rand(2), f_basic, GradientApproximatorForwardDifference(f_basic),
85 | debugPrintLevel=3, printF=toStdOut, lineSearchIt=5)
86 |
87 | def f1(x) :
88 | "Rosenbrocks's parabloic valley "
89 | x1,x2 = x[0],x[1]
90 | return 100*(x2 -x1 **2) ** 2 + (1 - x1)**2
91 | def f2(x) :
92 | "Quadratic function"
93 | x1,x2 = x[0],x[1]
94 | return (x1 + 2*x2 - 7)**2 + (2*x1 + x2 - 5)**2
95 | def f3(x) :
96 | "Powells Quadratic function"
97 | x1,x2,x3,x4 = x[0],x[1],x[2],x[3]
98 | return (x1 + 10*x2)**2 + 5 * (x3 - x4)**2 + (x2 - 2*x3)**4 + 10*(x1-x4)**4
99 |
100 | for t,n in zip( [f1,f2,f3], [2,2,4]):
101 | print(t.__doc__)
102 | x0 = -10+20*rand(n)
103 | CGPR( x0, t, GradientApproximatorForwardDifference(t),
104 | debugPrintLevel=1, printF=toStdOut, lineSearchIt=20)
105 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/welding/Seam.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/table_dd.py:
--------------------------------------------------------------------------------
1 |
2 | from dimensioning import *
3 | import selectionOverlay, previewDimension
4 | from dimensionSvgConstructor import *
5 |
6 | d = DimensioningProcessTracker()
7 |
8 | def tableSVG( top_left_x, top_left_y, column_widths, contents, row_heights,
9 | border_width=0.5, border_color='black', padding_x=1.0, padding_y=1.0, extra_rows=0,
10 | textRenderer_table=defaultTextRenderer):
11 | no_columns = len(column_widths)
12 | no_rows = int( numpy.ceil( len(contents) / float(no_columns) ) + extra_rows )
13 | XML_body = [ ]
14 | def addLine(x1,y1,x2,y2):
15 | 'relative to top_left corner of table'
16 | XML_body.append('' % (x1, y1, x2, y2, border_color, border_width) )
17 | #building table body
18 | width = sum( column_widths )
19 | addLine( 0, 0, width, 0 )
20 | y_offset = 0
21 | row_y = []
22 | for i in range(no_rows):
23 | y_offset += row_heights[ i % len(row_heights) ]
24 | row_y.append( y_offset )
25 | addLine( 0, y_offset, width, y_offset )
26 | #debugPrint(1, 'row y : ' + str(row_y))
27 | x_offset = 0
28 | column_x = []
29 | addLine( 0, 0, 0, y_offset )
30 | for j in range(no_columns):
31 | column_x.append( x_offset )
32 | x_offset += column_widths[j]
33 | addLine( x_offset, 0, x_offset, y_offset )
34 | for i, text in enumerate( contents ):
35 | row = int(i / no_columns)
36 | col = i % no_columns
37 | XML_body.append( textRenderer_table(
38 | column_x[col] + padding_x,
39 | row_y[row] - padding_y,
40 | text ) )
41 | return ''' %s ''' % ( top_left_x, top_left_y, '\n'.join(XML_body) )
42 |
43 | d.registerPreference( 'column_widths', [20.0, 30.0], kind='float_list')
44 | d.registerPreference( 'contents', 'number of rows adjusted to fit table contents'.split(' '), kind='string_list')
45 | d.registerPreference( 'row_heights', [7.0], kind='float_list')
46 | d.registerPreference( 'border_width', 0.3, increment=0.05 )
47 | d.registerPreference( 'border_color', RGBtoUnsigned(0, 0, 0), kind='color')
48 | d.registerPreference( 'padding_x', 1.0 )
49 | d.registerPreference( 'padding_y', 1.0 )
50 | d.registerPreference( 'extra_rows', 0, decimals=0 )
51 | d.registerPreference( 'textRenderer_table', ['inherit','5', 0], 'text properties (table)', kind='font' )
52 |
53 | d.max_selections = 1
54 | def table_preview(mouseX, mouseY):
55 | selections = d.selections + [ PlacementClick( mouseX, mouseY) ] if len(d.selections) < d.max_selections else d.selections
56 | return tableSVG( *selections_to_svg_fun_args(selections), **d.dimensionConstructorKWs )
57 |
58 | def table_clickHandler( x, y ):
59 | d.selections.append( PlacementClick( x, y) )
60 | if len(d.selections) == d.max_selections:
61 | return 'createDimension:%s' % findUnusedObjectName('table')
62 |
63 | def selectFun( event, referer, elementXML, elementParms, elementViewObject ):
64 | viewInfo = selectionOverlay.DrawingsViews_info[elementViewObject.Name]
65 | d.selections = [ PointSelection( elementParms, elementXML, viewInfo ) ]
66 | selectionOverlay.hideSelectionGraphicsItems()
67 | previewDimension.initializePreview( d, noteCircle_preview, noteCircle_clickHandler)
68 |
69 |
70 | class Proxy_table( Proxy_DimensionObject_prototype ):
71 | def dimensionProcess( self ):
72 | return d
73 | d.ProxyClass = Proxy_table
74 | d.proxy_svgFun = tableSVG
75 |
76 |
77 | class AddTable:
78 | def Activated(self):
79 | V = getDrawingPageGUIVars()
80 | d.activate( V, dialogTitle='Add Table', dialogIconPath= ':/dd/icons/table_dd.svg', endFunction=self.Activated )
81 | previewDimension.initializePreview( d, table_preview, table_clickHandler)
82 |
83 | def GetResources(self):
84 | return {
85 | 'Pixmap' : ':/dd/icons/table_dd.svg' ,
86 | 'MenuText': 'Add table to drawing',
87 | 'ToolTip': 'Add table to drawing'
88 | }
89 | FreeCADGui.addCommand('dd_addTable', AddTable())
90 |
91 |
--------------------------------------------------------------------------------
/toleranceDialog.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'toleranceDialog.ui'
4 | #
5 | # Created: Mon Jun 8 14:50:14 2015
6 | # by: pyside-uic 0.2.15 running on PySide 1.2.1
7 | #
8 | # WARNING! All changes made in this file will be lost!
9 |
10 | from PySide import QtCore, QtGui
11 |
12 | class Ui_Dialog(object):
13 | def setupUi(self, Dialog):
14 | Dialog.setObjectName("Dialog")
15 | Dialog.setWindowModality(QtCore.Qt.ApplicationModal)
16 | Dialog.resize(209, 149)
17 | self.gridLayout_2 = QtGui.QGridLayout(Dialog)
18 | self.gridLayout_2.setObjectName("gridLayout_2")
19 | self.placeButton = QtGui.QPushButton(Dialog)
20 | self.placeButton.setObjectName("placeButton")
21 | self.gridLayout_2.addWidget(self.placeButton, 2, 0, 1, 1)
22 | self.gridLayout = QtGui.QGridLayout()
23 | self.gridLayout.setSizeConstraint(QtGui.QLayout.SetMaximumSize)
24 | self.gridLayout.setObjectName("gridLayout")
25 | self.label = QtGui.QLabel(Dialog)
26 | self.label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
27 | self.label.setObjectName("label")
28 | self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
29 | self.label_2 = QtGui.QLabel(Dialog)
30 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
31 | sizePolicy.setHorizontalStretch(1)
32 | sizePolicy.setVerticalStretch(0)
33 | sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
34 | self.label_2.setSizePolicy(sizePolicy)
35 | self.label_2.setMinimumSize(QtCore.QSize(61, 0))
36 | self.label_2.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
37 | self.label_2.setObjectName("label_2")
38 | self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
39 | self.upperLineEdit = QtGui.QLineEdit(Dialog)
40 | self.upperLineEdit.setObjectName("upperLineEdit")
41 | self.gridLayout.addWidget(self.upperLineEdit, 0, 1, 1, 1)
42 | self.lowerLineEdit = QtGui.QLineEdit(Dialog)
43 | self.lowerLineEdit.setObjectName("lowerLineEdit")
44 | self.gridLayout.addWidget(self.lowerLineEdit, 1, 1, 1, 1)
45 | self.label_3 = QtGui.QLabel(Dialog)
46 | self.label_3.setObjectName("label_3")
47 | self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
48 | self.scaleDoubleSpinBox = QtGui.QDoubleSpinBox(Dialog)
49 | self.scaleDoubleSpinBox.setMinimum(0.05)
50 | self.scaleDoubleSpinBox.setSingleStep(0.05)
51 | self.scaleDoubleSpinBox.setProperty("value", 0.8)
52 | self.scaleDoubleSpinBox.setObjectName("scaleDoubleSpinBox")
53 | self.gridLayout.addWidget(self.scaleDoubleSpinBox, 2, 1, 1, 1)
54 | self.gridLayout_2.addLayout(self.gridLayout, 1, 0, 1, 1)
55 |
56 | self.retranslateUi(Dialog)
57 | QtCore.QObject.connect(self.placeButton, QtCore.SIGNAL("released()"), Dialog.accept)
58 | QtCore.QObject.connect(self.upperLineEdit, QtCore.SIGNAL("returnPressed()"), Dialog.accept)
59 | QtCore.QObject.connect(self.lowerLineEdit, QtCore.SIGNAL("returnPressed()"), Dialog.accept)
60 | QtCore.QMetaObject.connectSlotsByName(Dialog)
61 |
62 | def retranslateUi(self, Dialog):
63 | Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Add tolerance", None, QtGui.QApplication.UnicodeUTF8))
64 | self.placeButton.setText(QtGui.QApplication.translate("Dialog", "Add", None, QtGui.QApplication.UnicodeUTF8))
65 | self.label.setText(QtGui.QApplication.translate("Dialog", "upper", None, QtGui.QApplication.UnicodeUTF8))
66 | self.label_2.setText(QtGui.QApplication.translate("Dialog", "lower", None, QtGui.QApplication.UnicodeUTF8))
67 | self.upperLineEdit.setText(QtGui.QApplication.translate("Dialog", "+0", None, QtGui.QApplication.UnicodeUTF8))
68 | self.lowerLineEdit.setText(QtGui.QApplication.translate("Dialog", "-0", None, QtGui.QApplication.UnicodeUTF8))
69 | self.label_3.setText(QtGui.QApplication.translate("Dialog", "font scale", None, QtGui.QApplication.UnicodeUTF8))
70 |
71 |
--------------------------------------------------------------------------------
/freeDrawing.py:
--------------------------------------------------------------------------------
1 | from dimensioning import *
2 | import previewDimension
3 | import textAddDialog
4 |
5 | dimensioning = DimensioningProcessTracker()
6 |
7 | def lineSVG( x1, y1, x2, y2, svgTag='g', svgParms='', strokeWidth=0.5, lineColor='blue'):
8 | XML = '''<%s %s >
9 |
10 | %s> ''' % ( svgTag, svgParms, x1, y1, x2, y2, lineColor, strokeWidth, svgTag )
11 | return XML
12 |
13 | def line_ClickEvent( x, y):
14 | if dimensioning.stage == 0:
15 | dimensioning.x1 = x
16 | dimensioning.y1 = y
17 | dimensioning.stage = 1
18 | return None, None
19 | else: #dimensioning.stage == 1 :
20 | viewName = findUnusedObjectName('dimLine')
21 | XML = lineSVG( dimensioning.x1, dimensioning.y1,
22 | x, y, **dimensioning.dimensionConstructorKWs )
23 | return viewName, XML
24 |
25 | def line_hoverEvent( x, y):
26 | if dimensioning.stage == 1 :
27 | return lineSVG( dimensioning.x1, dimensioning.y1,
28 | x, y, **dimensioning.svg_preview_KWs )
29 |
30 | class lineFreeDrawing:
31 | def Activated(self):
32 | V = getDrawingPageGUIVars()
33 | dimensioning.activate( V, ['strokeWidth'],['lineColor'] )
34 | previewDimension.initializePreview(
35 | dimensioning.drawingVars,
36 | line_ClickEvent,
37 | line_hoverEvent,
38 | )
39 |
40 | def GetResources(self):
41 | return {
42 | 'Pixmap' : ':/dd/icons/drawLine.svg',
43 | 'MenuText': 'Draw a line'
44 | }
45 | FreeCADGui.addCommand('DrawingDimensioning_drawLine', lineFreeDrawing())
46 |
47 |
48 | from dimensionSvgConstructor import arrowHeadSVG, numpy, directionVector
49 |
50 | def ArrowWithTail_SVG( c_x, c_y, radialLine_x=None, radialLine_y=None, tail_x=None, tail_y=None, arrowL1=3,arrowL2=1,arrowW=2, svgTag='g', svgParms='', strokeWidth=0.5, lineColor='blue'):
51 | XML_body = []
52 | if radialLine_x <> None and radialLine_y <> None:
53 | XML_body.append( '' % (radialLine_x, radialLine_y, c_x, c_y, lineColor, strokeWidth) )
54 | d = directionVector(
55 | numpy.array([ c_x, c_y]),
56 | numpy.array([radialLine_x, radialLine_y]),
57 | )
58 | XML_body.append( arrowHeadSVG( numpy.array([c_x, c_y]), d, arrowL1, arrowL2, arrowW, lineColor ) )
59 | if tail_x <> None and tail_y <> None:
60 | XML_body.append( '' % (radialLine_x, radialLine_y, tail_x, radialLine_y, lineColor, strokeWidth) )
61 | return '''<%s %s >
62 | %s
63 | %s> ''' % ( svgTag, svgParms, "\n".join(XML_body), svgTag )
64 |
65 | def ArrowWithTail_ClickEvent( x, y):
66 | dimensioning.dArgs = dimensioning.dArgs + [x,y]
67 | dimensioning.stage = dimensioning.stage + 1
68 | if dimensioning.stage == 3:
69 | viewName = findUnusedObjectName('dimLine')
70 | XML = ArrowWithTail_SVG(
71 | *dimensioning.dArgs,
72 | **dimensioning.dimensionConstructorKWs )
73 | return viewName, XML
74 | else:
75 | return None,None
76 |
77 | def ArrowWithTail_hoverEvent( x, y):
78 | if dimensioning.stage > 0 :
79 | return ArrowWithTail_SVG(
80 | *(dimensioning.dArgs + [x, y]),
81 | **dimensioning.svg_preview_KWs
82 | )
83 |
84 | class ArrowWithTail_Drawing:
85 | def Activated(self):
86 | V = getDrawingPageGUIVars()
87 | dimensioning.activate( V, ['strokeWidth','arrowL1','arrowL2','arrowW'],['lineColor'] )
88 | dimensioning.dArgs = []
89 | previewDimension.initializePreview(
90 | dimensioning.drawingVars,
91 | ArrowWithTail_ClickEvent,
92 | ArrowWithTail_hoverEvent,
93 | )
94 |
95 | def GetResources(self):
96 | return {
97 | 'Pixmap' : ':/dd/icons/drawLineWithArrow.svg',
98 | 'MenuText': 'Draw an arrow with a tail',
99 | }
100 | FreeCADGui.addCommand('DrawingDimensioning_drawArrowWithTail', ArrowWithTail_Drawing())
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/drawCircularArc.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/textEdit.py:
--------------------------------------------------------------------------------
1 | '''
2 | Dialog notes
3 | Use Qt Designer to edit the textAddDialog.ui
4 | Once completed $ pyside-uic textAddDialog.ui > textAddDialog.py
5 |
6 | To test inside Freecad
7 | from addTextDialog import DialogWidget
8 | dialog = DialogWidget()
9 | dialogUi = addTextDialog.Ui_Dialog()
10 | dialogUi.setupUi(dialog)
11 | dialog.show()
12 |
13 | '''
14 |
15 | from dimensioning import *
16 | import selectionOverlay
17 | import textAddDialog
18 | from svgLib_dd import SvgTextParser
19 | import previewDimension
20 |
21 | d = DimensioningProcessTracker()
22 |
23 | def EditDimensionText( event, referer, elementXML, elementParms, elementViewObject ):
24 | d.dimToEdit = elementViewObject
25 | d.elementXML = elementXML
26 | selectionOverlay.hideSelectionGraphicsItems()
27 | e = elementXML
28 | debugPrint(3, e.XML[e.pStart:e.pEnd] )
29 | svgText = SvgTextParser( e.XML[e.pStart:e.pEnd] )
30 | d.svgText = svgText
31 | debugPrint(3, u'editing %s' % unicode(svgText))
32 | widgets = dict( [c.objectName(), c] for c in dialog.children() )
33 | widgets['textLineEdit'].setText( svgText.text )
34 | widgets['sizeLineEdit'].setText( svgText.font_size)
35 | widgets['colorLineEdit'].setText( svgText.fill )
36 | widgets['familyLineEdit'].setText( svgText.font_family )
37 | widgets['doubleSpinBox_rotation'].setValue(svgText.rotation)
38 | widgets['placeButton'].setText('Change')
39 | dialog.setWindowTitle('Editing %s' % elementViewObject.Name)
40 | dialog.show()
41 |
42 | class EditTextDialogWidget( QtGui.QWidget ):
43 | def accept( self ):
44 | debugPrint(3, 'EditTextDialogWidget accept pressed')
45 | widgets = dict( [c.objectName(), c] for c in self.children() )
46 | debugPrint(4, 'widgets %s' % widgets)
47 | if widgets['textLineEdit'].text() == '':
48 | debugPrint(1, 'Aborting placing empty text.')
49 | return
50 | self.hide()
51 | svgText = d.svgText
52 | svgText.text = widgets['textLineEdit'].text()
53 | widgets['textLineEdit'].setText('')
54 | svgText.font_size = widgets['sizeLineEdit'].text()
55 | svgText.font_family = widgets['familyLineEdit'].text()
56 | svgText.fill = widgets['colorLineEdit'].text()
57 | svgText.rotation = widgets['doubleSpinBox_rotation'].value()
58 | debugPrint(3,'updating XML in %s to' % d.dimToEdit.Name)
59 | xml = svgText.toXML()
60 | debugPrint(4,xml)
61 | e = d.elementXML
62 | newXML = e.XML[:e.pStart] + xml + e.XML[e.pEnd:]
63 | debugPrint(3,newXML)
64 | d.dimToEdit.ViewResult = newXML
65 | recomputeWithOutViewReset(d.drawingVars)
66 | if d.taskDialog <> None: #unessary check
67 | FreeCADGui.Control.closeDialog()
68 | if d.endFunction <> None:
69 | previewDimension.preview.dimensioningProcessTracker = d
70 | previewDimension.timer.start( 100 ) # 100 ms, need some time for dialog to close
71 |
72 | dialog = EditTextDialogWidget()
73 | dialogUi = textAddDialog.Ui_Dialog()
74 | dialogUi.setupUi(dialog)
75 |
76 | maskBrush = QtGui.QBrush( QtGui.QColor(0,160,0,100) )
77 | maskPen = QtGui.QPen( QtGui.QColor(0,160,0,100) )
78 | maskPen.setWidth(0.0)
79 | maskHoverPen = QtGui.QPen( QtGui.QColor(0,255,0,255) )
80 | maskHoverPen.setWidth(0.0)
81 |
82 | class EditText:
83 | def Activated(self):
84 | V = getDrawingPageGUIVars()
85 | d.activate( V, dialogTitle='Edit Text', dialogIconPath= ':/dd/icons/textEdit.svg', endFunction=self.Activated, grid=False )
86 | selectGraphicsItems = selectionOverlay.generateSelectionGraphicsItems(
87 | [obj for obj in V.page.Group if obj.Name.startswith('dim')],
88 | EditDimensionText ,
89 | sceneToAddTo = V.graphicsScene,
90 | transform = V.transform,
91 | doTextItems = True,
92 | pointWid=2.0,
93 | maskPen=maskPen,
94 | maskHoverPen=maskHoverPen,
95 | maskBrush = maskBrush
96 | )
97 |
98 | def GetResources(self):
99 | msg = "Edit a dimension's text"
100 | return {
101 | 'Pixmap' : ':/dd/icons/textEdit.svg',
102 | 'MenuText': msg,
103 | 'ToolTip': msg
104 | }
105 | FreeCADGui.addCommand('dd_editText', EditText())
106 |
107 |
108 |
--------------------------------------------------------------------------------
/dimensionSvgConstructor_testCenterLines.py:
--------------------------------------------------------------------------------
1 | print('Testing dimensionSvgConstructor.py centerLines')
2 |
3 | from dimensionSvgConstructor import *
4 | import sys
5 | from PySide import QtGui, QtCore, QtSvg
6 |
7 | app = QtGui.QApplication(sys.argv)
8 | width = 640
9 | height = 480
10 |
11 | graphicsScene = QtGui.QGraphicsScene(0,0,width,height)
12 | graphicsScene.addText("Center Lines testing app.\nEsc to Exit")
13 |
14 | dimensions = []
15 |
16 | class DimensioningRect(QtGui.QGraphicsRectItem):
17 | def __init__(self,*args):
18 | super(DimensioningRect, self).__init__(*args)
19 | svgRenderer = QtSvg.QSvgRenderer()
20 | self.action_ind = 0
21 | self.dimPreview = QtSvg.QGraphicsSvgItem()
22 | self.dimSVGRenderer = QtSvg.QSvgRenderer()
23 | self.dimSVGRenderer.load( QtCore.QByteArray( '''''' % (args[2],args[3])))
24 | self.dimPreview.setSharedRenderer( self.dimSVGRenderer )
25 | self.dimPreview.setZValue(100)
26 | graphicsScene.addItem( self.dimPreview )
27 | self.dim_svg_KWs = dict(
28 | svgTag='svg', svgParms='width="%i" height="%i"' % (args[2],args[3]),
29 | centerLine_width=2.0, centerLine_len_dot=5, centerLine_len_dash=15, centerLine_len_gap=5
30 | )
31 | assert not hasattr(self, 'topLeft')
32 | assert not hasattr(self, 'bottomRight')
33 | assert not hasattr(self, 'center')
34 |
35 | def mousePressEvent( self, event ):
36 | if event.button() == QtCore.Qt.MouseButton.LeftButton:
37 | pos = event.scenePos()
38 | x, y = pos.x(), pos.y()
39 | if self.action_ind == 0:
40 | self.center = x, y
41 | print('center set to x=%3.1f y=%3.1f' % (x,y))
42 | self.action_ind = self.action_ind + 1
43 | elif self.action_ind == 1:
44 | self.topLeft = x, y
45 | print('topLeft set to x=%3.1f y=%3.1f' % (x,y))
46 | self.action_ind = self.action_ind + 1
47 | elif self.action_ind == 2: # then place
48 | self.bottomRight = x, y
49 | self.action_ind = 0
50 | XML = centerLinesSVG( self.center, self.topLeft, self.bottomRight,
51 | **self.dim_svg_KWs )
52 | if XML <> None:
53 | print(XML)
54 | newSvg = QtSvg.QGraphicsSvgItem( )
55 | svgRenderer = QtSvg.QSvgRenderer()
56 | svgRenderer.load( QtCore.QByteArray( XML ))
57 | newSvg.setSharedRenderer( svgRenderer )
58 | dimensions.append([ newSvg, svgRenderer]) #as to prevent the garbage collector from freeing these resources (which causes a crash)
59 | self.scene().addItem( newSvg )
60 |
61 | def hoverMoveEvent(self, event):
62 | if self.action_ind == 0:
63 | return
64 | pos = event.scenePos()
65 | x, y = pos.x(), pos.y()
66 | XML = None
67 | if self.action_ind == 1: # then placeDimensionBaseLine action
68 | XML = centerLinesSVG( self.center, [x, y],
69 | **self.dim_svg_KWs )
70 | elif self.action_ind == 2: # then placeDimensionText
71 | XML = centerLinesSVG( self.center, self.topLeft, [ x, y ], **self.dim_svg_KWs )
72 | if XML <> None:
73 | self.dimSVGRenderer.load( QtCore.QByteArray( XML ) )
74 | self.dimPreview.update()
75 | self.dimPreview.show()
76 | else:
77 | self.dimPreview.hide()
78 |
79 | def wheelEvent( self, event):
80 | if event.delta() > 0:
81 | view.scale(1.1, 1.1)
82 | else:
83 | view.scale(0.9, 0.9)
84 |
85 | def keyPressEvent(self, event):
86 | if len(event.text()) == 1:
87 | print('key pressed: event.text %s (ord %i)' % (event.text(), ord(event.text())))
88 | if event.text() == chr(27): #escape key
89 | sys.exit(2)
90 |
91 | dimensioningRect = DimensioningRect(0,0,width,height)
92 | dimensioningRect.setAcceptHoverEvents(True)
93 | dimensioningRect.setFlag( QtGui.QGraphicsItem.GraphicsItemFlag.ItemIsFocusable, True )
94 | graphicsScene.addItem(dimensioningRect)
95 |
96 | view = QtGui.QGraphicsView(graphicsScene)
97 | #view.scale(2, 2)
98 | view.show()
99 |
100 | sys.exit(app.exec_())
101 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/drawCircle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
83 |
--------------------------------------------------------------------------------
/toleranceDialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 | Qt::ApplicationModal
7 |
8 |
9 |
10 | 0
11 | 0
12 | 209
13 | 149
14 |
15 |
16 |
17 | Add tolerance
18 |
19 |
20 | -
21 |
22 |
23 | Add
24 |
25 |
26 |
27 | -
28 |
29 |
30 | QLayout::SetMaximumSize
31 |
32 |
-
33 |
34 |
35 | upper
36 |
37 |
38 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
39 |
40 |
41 |
42 | -
43 |
44 |
45 |
46 | 1
47 | 0
48 |
49 |
50 |
51 |
52 | 61
53 | 0
54 |
55 |
56 |
57 | lower
58 |
59 |
60 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
61 |
62 |
63 |
64 | -
65 |
66 |
67 | +0
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -0
75 |
76 |
77 |
78 | -
79 |
80 |
81 | font scale
82 |
83 |
84 |
85 | -
86 |
87 |
88 | 0.050000000000000
89 |
90 |
91 | 0.050000000000000
92 |
93 |
94 | 0.800000000000000
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | placeButton
106 | released()
107 | Dialog
108 | accept()
109 |
110 |
111 | 177
112 | 149
113 |
114 |
115 | 142
116 | 96
117 |
118 |
119 |
120 |
121 | upperLineEdit
122 | returnPressed()
123 | Dialog
124 | accept()
125 |
126 |
127 | 177
128 | 43
129 |
130 |
131 | 142
132 | 96
133 |
134 |
135 |
136 |
137 | lowerLineEdit
138 | returnPressed()
139 | Dialog
140 | accept()
141 |
142 |
143 | 140
144 | 56
145 |
146 |
147 | 104
148 | 74
149 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/previewDimension.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 338
10 | 400
11 |
12 |
13 |
14 | Preview Dialog
15 |
16 |
17 | -
18 |
19 |
20 |
21 | 0
22 | 1
23 |
24 |
25 |
26 | Unit Used
27 |
28 |
29 |
-
30 |
31 |
-
32 |
33 |
34 | scheme
35 |
36 |
37 |
38 | -
39 |
40 |
41 | Qt::Horizontal
42 |
43 |
44 |
45 | 40
46 | 20
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 | 1
56 | 0
57 |
58 |
59 |
-
60 |
61 | default
62 |
63 |
64 | -
65 |
66 | mm
67 |
68 |
69 | -
70 |
71 | inch
72 |
73 |
74 | -
75 |
76 | m
77 |
78 |
79 | -
80 |
81 | custom
82 |
83 |
84 |
85 |
86 |
87 |
88 | -
89 |
90 |
-
91 |
92 |
93 | default Unit:
94 |
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
-
102 |
103 |
104 | custom/mm
105 |
106 |
107 |
108 | -
109 |
110 |
111 | Qt::Horizontal
112 |
113 |
114 |
115 | 40
116 | 20
117 |
118 |
119 |
120 |
121 | -
122 |
123 |
124 | 3
125 |
126 |
127 | 0.001000000000000
128 |
129 |
130 | 1000000.000000000000000
131 |
132 |
133 | 0.500000000000000
134 |
135 |
136 | 1.000000000000000
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/centerView.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import FreeCAD,FreeCADGui,os,re
4 | from XMLlib import SvgXMLTreeNode
5 | from svgLib_dd import SvgPath
6 | from dimensioning import debugPrint
7 |
8 |
9 | def getPoints(svg):
10 | "returns a series of (x,y) points from an SVG fragment"
11 | # adapted from selectionOverlay.py
12 | points = []
13 | XML_tree = SvgXMLTreeNode(svg,0)
14 | scaling = XML_tree.scaling()
15 | SelectViewObjectPoint_loc = None
16 | for element in XML_tree.getAllElements():
17 | if element.tag == 'circle':
18 | x, y = element.applyTransforms( float( element.parms['cx'] ), float( element.parms['cy'] ) )
19 | points.append((x,y))
20 | elif element.tag == 'ellipse':
21 | x, y = element.applyTransforms( float( element.parms['cx'] ), float( element.parms['cy'] ) )
22 | points.append((x,y))
23 | elif element.tag == 'text' and element.parms.has_key('x'):
24 | x, y = element.applyTransforms( float( element.parms['x'] ), float( element.parms['y'] ) )
25 | points.append((x,y))
26 | elif element.tag == 'path':
27 | path = SvgPath( element )
28 | for p in path.points:
29 | points.append((p.x, p.y))
30 | elif element.tag == 'line':
31 | x1, y1 = element.applyTransforms( float( element.parms['x1'] ), float( element.parms['y1'] ) )
32 | x2, y2 = element.applyTransforms( float( element.parms['x2'] ), float( element.parms['y2'] ) )
33 | points.append((x1, y1))
34 | points.append((x2, y2))
35 | return points
36 |
37 |
38 | def getCenterPoint(viewObject):
39 | "returns the (x,y) center point of a DrawingView object"
40 | if not hasattr(viewObject, 'ViewResult'):
41 | return None
42 | if viewObject.ViewResult.strip() == '':
43 | return None
44 | points = getPoints(viewObject.ViewResult)
45 | xmin = 9999999999999999
46 | xmax = -9999999999999999
47 | ymin = 9999999999999999
48 | ymax = -9999999999999999
49 | for p in points:
50 | if p[0] < xmin:
51 | xmin = p[0]
52 | if p[0] > xmax:
53 | xmax = p[0]
54 | if p[1] < ymin:
55 | ymin = p[1]
56 | if p[1] > ymax:
57 | ymax = p[1]
58 | x = xmin + (xmax-xmin)/2
59 | y = ymin + (ymax-ymin)/2
60 | return (x,y)
61 |
62 |
63 | def getPageDimensions(pageObject):
64 | "returns the (x,y) dimensions of a page"
65 | if not pageObject.PageResult:
66 | return None
67 | if not os.path.exists(pageObject.PageResult):
68 | return None
69 | f = open(pageObject.PageResult)
70 | svg = f.read()
71 | f.close()
72 | svg = svg.replace("\n"," ")
73 | width = re.findall("",svg)
74 | if width:
75 | width = float(width[0].strip("mm").strip("px"))
76 | else:
77 | return None
78 | height = re.findall("",svg)
79 | if height:
80 | height = float(height[0].strip("mm").strip("px"))
81 | else:
82 | return None
83 | return (width,height)
84 |
85 |
86 | class CenterView:
87 | "Defines the CenterView command"
88 |
89 | def GetResources(self):
90 | return {
91 | 'Pixmap' : ':/dd/icons/centerView.svg' ,
92 | 'MenuText': 'Centers a view on its page',
93 | 'ToolTip': 'Centers a view on its page'
94 | }
95 |
96 | def Activated(self):
97 | sel = FreeCADGui.Selection.getSelection()
98 | for obj in sel:
99 | done = False
100 | if obj.isDerivedFrom("Drawing::FeatureView"):
101 | for parent in obj.InList:
102 | if parent.isDerivedFrom("Drawing::FeaturePage"):
103 | pagedims = getPageDimensions(parent)
104 | if pagedims:
105 | viewcenter = getCenterPoint(obj)
106 | if viewcenter:
107 | debugPrint( 2, "current center point: %3.3f, %3.3f" % viewcenter )
108 | debugPrint( 2, "page center: %3.3f, %3.3f" % (pagedims[0]/2, pagedims[1]/2) )
109 | dx = pagedims[0]/2 - viewcenter[0]
110 | dy = pagedims[1]/2 - viewcenter[1]
111 | debugPrint( 2, "delta: %3.3f, %3.3f" % (dx, dy) )
112 | FreeCAD.ActiveDocument.openTransaction("Center View")
113 | obj.X = obj.X+dx
114 | obj.Y = obj.Y+dy
115 | FreeCAD.ActiveDocument.commitTransaction()
116 | done = True
117 | if not done:
118 | FreeCAD.Console.PrintError("Unable to move view "+obj.Label+"\n")
119 | FreeCAD.ActiveDocument.recompute()
120 |
121 |
122 | FreeCADGui.addCommand('dd_centerView', CenterView())
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/noteCircle.py:
--------------------------------------------------------------------------------
1 |
2 | from dimensioning import *
3 | import selectionOverlay, previewDimension
4 | from dimensionSvgConstructor import *
5 |
6 | d = DimensioningProcessTracker()
7 |
8 | def noteCircleSVG( start_x, start_y, radialLine_x=None, radialLine_y=None, tail_x=None, tail_y=None,
9 | noteCircleText= '0', strokeWidth=0.5, lineColor='blue', noteCircle_radius=4.5, noteCircle_fill='white',
10 | textRenderer_noteCircle=defaultTextRenderer):
11 | XML_body = [ ]
12 | if radialLine_x <> None and radialLine_y <> None:
13 | XML_body.append( svgLine(radialLine_x, radialLine_y, start_x, start_y, lineColor, strokeWidth) )
14 | if tail_x <> None and tail_y <> None:
15 | XML_body.append( svgLine(radialLine_x, radialLine_y, tail_x, radialLine_y, lineColor, strokeWidth) )
16 | XML_body.append(' ' % (tail_x, radialLine_y, noteCircle_radius, lineColor, noteCircle_fill) )
17 | XML_body.append( textRenderer_noteCircle( tail_x - 1.5, radialLine_y + 1.5, noteCircleText ) )
18 | return ' %s ' % '\n'.join(XML_body)
19 |
20 | d.registerPreference( 'strokeWidth')
21 | d.registerPreference( 'lineColor' )
22 | d.registerPreference( 'noteCircle_radius', 4.5, increment=0.5, label='radius')
23 | d.registerPreference( 'noteCircle_fill', RGBtoUnsigned(255, 255, 255), kind='color', label='fill' )
24 | d.registerPreference( 'textRenderer_noteCircle', ['inherit','5', 150<<16], 'text properties (Note Circle)', kind='font' )
25 | d.max_selections = 3
26 |
27 | class NoteCircleText_widget:
28 | def __init__(self):
29 | self.counter = 1
30 | def valueChanged( self, arg1):
31 | d.noteCircleText = '%i' % arg1
32 | self.counter = arg1 + 1
33 | def generateWidget( self, dimensioningProcess ):
34 | self.spinbox = QtGui.QSpinBox()
35 | self.spinbox.setValue(self.counter)
36 | d.noteCircleText = '%i' % self.counter
37 | self.spinbox.valueChanged.connect(self.valueChanged)
38 | self.counter = self.counter + 1
39 | return DimensioningTaskDialog_generate_row_hbox('no.', self.spinbox)
40 | def add_properties_to_dimension_object( self, obj ):
41 | obj.addProperty("App::PropertyString", 'noteText', 'Parameters')
42 | obj.noteText = d.noteCircleText.encode('utf8')
43 | def get_values_from_dimension_object( self, obj, KWs ):
44 | KWs['noteCircleText'] = obj.noteText #should be unicode
45 | d.dialogWidgets.append( NoteCircleText_widget() )
46 |
47 |
48 |
49 | def noteCircle_preview(mouseX, mouseY):
50 | selections = d.selections + [ PlacementClick( mouseX, mouseY) ] if len(d.selections) < d.max_selections else d.selections
51 | return noteCircleSVG( *selections_to_svg_fun_args(selections), noteCircleText=d.noteCircleText, **d.dimensionConstructorKWs )
52 |
53 | def noteCircle_clickHandler( x, y ):
54 | d.selections.append( PlacementClick( x, y) )
55 | if len(d.selections) == d.max_selections:
56 | return 'createDimension:%s' % findUnusedObjectName('noteCircle')
57 |
58 | def selectFun( event, referer, elementXML, elementParms, elementViewObject ):
59 | viewInfo = selectionOverlay.DrawingsViews_info[elementViewObject.Name]
60 | d.selections = [ PointSelection( elementParms, elementXML, viewInfo ) ]
61 | selectionOverlay.hideSelectionGraphicsItems()
62 | previewDimension.initializePreview( d, noteCircle_preview, noteCircle_clickHandler)
63 |
64 | class Proxy_noteCircle( Proxy_DimensionObject_prototype ):
65 | def dimensionProcess( self ):
66 | return d
67 | d.ProxyClass = Proxy_noteCircle
68 | d.proxy_svgFun = noteCircleSVG
69 |
70 | maskBrush = QtGui.QBrush( QtGui.QColor(0,160,0,100) )
71 | maskPen = QtGui.QPen( QtGui.QColor(0,160,0,100) )
72 | maskPen.setWidth(0.0)
73 | maskHoverPen = QtGui.QPen( QtGui.QColor(0,255,0,255) )
74 | maskHoverPen.setWidth(0.0)
75 |
76 | class NoteCircle:
77 | def Activated(self):
78 | V = getDrawingPageGUIVars()
79 | d.activate(V, dialogTitle='Add Note Circle', dialogIconPath=':/dd/icons/noteCircle.svg', endFunction=self.Activated )
80 | from grabPointAdd import Proxy_grabPoint
81 | selectionOverlay.generateSelectionGraphicsItems(
82 | dimensionableObjects( V.page ) + [obj for obj in V.page.Group if hasattr(obj,'Proxy') and isinstance( obj.Proxy, Proxy_grabPoint) ],
83 | selectFun,
84 | transform = V.transform,
85 | sceneToAddTo = V.graphicsScene,
86 | doPoints=True, doMidPoints=True, doSelectViewObjectPoints = True,
87 | pointWid=1.0,
88 | maskPen=maskPen,
89 | maskHoverPen=maskHoverPen,
90 | maskBrush = maskBrush
91 | )
92 | selectionOverlay.addProxyRectToRescaleGraphicsSelectionItems( V.graphicsScene, V.graphicsView, V.width, V.height)
93 |
94 | def GetResources(self):
95 | return {
96 | 'Pixmap' : ':/dd/icons/noteCircle.svg' ,
97 | 'MenuText': 'Notation',
98 | 'ToolTip': 'Creates a notation indicator'
99 | }
100 |
101 | FreeCADGui.addCommand('dd_noteCircle', NoteCircle())
102 |
--------------------------------------------------------------------------------
/toleranceAdd.py:
--------------------------------------------------------------------------------
1 | '''
2 | Dialog notes
3 | ------------
4 | Use Qt Designer to edit the toleranceDialog.ui
5 | Once completed
6 | $ pyside-uic toleranceDialog.ui > toleranceDialog.py
7 | '''
8 |
9 | from dimensioning import *
10 | import previewDimension, selectionOverlay
11 | import toleranceDialog
12 | from textEdit import maskBrush, maskPen, maskHoverPen
13 | from dimensionSvgConstructor import *
14 |
15 | d = DimensioningProcessTracker()
16 |
17 | def _textSVG_sub(x_offset, y_offset, text, comma_decimal_place, rotation, text_x, text_y ):
18 | offset = rotate2D([x_offset, y_offset], rotation*numpy.pi/180)
19 | x = text_x + offset[0]
20 | y = svgText.y + offset[1]
21 | return d.textRenderer(x, y, text if not comma_decimal_place else text.replace('.',','),
22 | text_anchor='end', rotation=svgText.rotation )
23 |
24 | def textSVG( text_x, text_y, text, font_size, rotation, font_family, font_fill, x, y, text_upper, text_lower, toleranceText_sizeRatio=0.8, comma_decimal_place=False ):
25 | fS = float(font_size) * toleranceText_sizeRatio
26 | textRenderer = SvgTextRenderer(
27 | font_family = font_family,
28 | fill = font_fill,
29 | font_size = fS
30 | )
31 | w = rotate2D([x - text_x, y - text_y], -rotation*numpy.pi/180)[0]
32 | def _textSVG_sub(x_offset, y_offset, text ):
33 | offset = rotate2D([x_offset, y_offset], rotation*numpy.pi/180)
34 | x = text_x + offset[0]
35 | y = text_y + offset[1]
36 | return textRenderer(x, y, text if not comma_decimal_place else text.replace('.',','),
37 | text_anchor='end', rotation=rotation )
38 | textXML_lower = _textSVG_sub( w, 0.0*fS, text_lower )
39 | textXML_upper = _textSVG_sub( w, -fS, text_upper )
40 | return ' %s \n %s ' % ( textXML_lower, textXML_upper )
41 | d.registerPreference( 'toleranceText_sizeRatio', 0.8, increment=0.1, label='size ratio')
42 | d.registerPreference( 'comma_decimal_place')
43 |
44 | class boundText_widget:
45 | def __init__(self, name, default):
46 | self.name = name
47 | self.default = default
48 | def valueChanged( self, arg1):
49 | setattr(d, self.name, arg1)
50 | def generateWidget( self, dimensioningProcess ):
51 | self.lineEdit = QtGui.QLineEdit()
52 | self.lineEdit.setText(self.default)
53 | setattr(d, self.name, self.default)
54 | self.lineEdit.textChanged.connect(self.valueChanged)
55 | return DimensioningTaskDialog_generate_row_hbox(self.name, self.lineEdit)
56 | def add_properties_to_dimension_object( self, obj ):
57 | obj.addProperty("App::PropertyString", self.name+'_text', 'Parameters')
58 | setattr( obj, self.name+'_text', getattr( d, self.name ).encode('utf8') )
59 | def get_values_from_dimension_object( self, obj, KWs ):
60 | KWs['text_'+self.name] = getattr( obj, self.name+'_text') #should be unicode
61 |
62 | d.dialogWidgets.append( boundText_widget('upper','+0.0') )
63 | d.dialogWidgets.append( boundText_widget('lower','-0.0') )
64 |
65 |
66 | def toleranceAdd_preview( mouse_x, mouse_y ):
67 | s = d.selections + [PlacementClick( mouse_x, mouse_y)] if len(d.selections) == 1 else d.selections
68 | return textSVG( *selections_to_svg_fun_args(s), text_upper=d.upper, text_lower=d.lower, **d.dimensionConstructorKWs )
69 |
70 | def toleranceAdd_clickHandler(x, y):
71 | d.selections.append( PlacementClick( x, y) )
72 | return 'createDimension:%s' % findUnusedObjectName('tolerance')
73 |
74 | def AddToleranceToText( event, referer, elementXML, elementParms, elementViewObject ):
75 | viewInfo = selectionOverlay.DrawingsViews_info[elementViewObject.Name]
76 | d.selections = [ TextSelection( elementParms, elementXML, viewInfo ) ]
77 | selectionOverlay.hideSelectionGraphicsItems()
78 | previewDimension.initializePreview(d, toleranceAdd_preview, toleranceAdd_clickHandler)
79 |
80 | class Proxy_toleranceAdd( Proxy_DimensionObject_prototype ):
81 | def dimensionProcess( self ):
82 | return d
83 | d.ProxyClass = Proxy_toleranceAdd
84 | d.proxy_svgFun = textSVG
85 |
86 | class AddTolerance:
87 | def Activated(self):
88 | V = getDrawingPageGUIVars()
89 | d.activate( V, dialogTitle='Add Tolerance', dialogIconPath=':/dd/icons/toleranceAdd.svg', endFunction=self.Activated )
90 | selectGraphicsItems = selectionOverlay.generateSelectionGraphicsItems(
91 | [obj for obj in V.page.Group if hasattr(obj,'Proxy') and isinstance(obj.Proxy, Proxy_DimensionObject_prototype)],
92 | AddToleranceToText ,
93 | sceneToAddTo = V.graphicsScene,
94 | transform = V.transform,
95 | doTextItems = True,
96 | pointWid=2.0,
97 | maskPen=maskPen,
98 | maskHoverPen=maskHoverPen,
99 | maskBrush = maskBrush
100 | )
101 |
102 | def GetResources(self):
103 | return {
104 | 'Pixmap' : ':/dd/icons/toleranceAdd.svg' ,
105 | 'MenuText': 'Add tolerance super and subscript to dimension',
106 | }
107 | FreeCADGui.addCommand('dd_addTolerance', AddTolerance())
108 |
109 |
110 |
--------------------------------------------------------------------------------
/radiusDimension.py:
--------------------------------------------------------------------------------
1 |
2 | from dimensioning import *
3 | import selectionOverlay, previewDimension
4 | from dimensionSvgConstructor import *
5 |
6 | d = DimensioningProcessTracker()
7 |
8 | def radiusDimensionSVG( center_x, center_y, radius, radialLine_x=None, radialLine_y=None, tail_x=None, tail_y=None, text_x=None, text_y=None, autoPlaceText=False, autoPlaceOffset=2.0,
9 | textFormat_radial='R%3.3f', comma_decimal_place=False,
10 | centerPointDia = 1, arrowL1=3, arrowL2=1, arrowW=2, strokeWidth=0.5, scale=1.0, lineColor='blue', arrow_scheme='auto',
11 | textRenderer=defaultTextRenderer):
12 | XML_body = [ ' ' % (center_x, center_y, centerPointDia*0.5, lineColor) ]
13 | if radialLine_x <> None and radialLine_y <> None:
14 | theta = math.atan2( radialLine_y - center_y, radialLine_x - center_x )
15 | A = numpy.array([ center_x + radius*numpy.cos(theta) , center_y + radius*numpy.sin(theta) ])
16 | B = numpy.array([ center_x - radius*numpy.cos(theta) , center_y - radius*numpy.sin(theta) ])
17 | XML_body.append( svgLine(radialLine_x, radialLine_y, center_x, center_y, lineColor, strokeWidth) )
18 | if radius > 0:
19 | if arrow_scheme <> 'off':
20 | if arrow_scheme == 'auto':
21 | s = 1 if radius > arrowL1 + arrowL2 + 0.5*centerPointDia else -1
22 | elif arrow_scheme == 'in':
23 | s = 1
24 | elif arrow_scheme == 'out':
25 | s = -1
26 | XML_body.append( arrowHeadSVG( A, s*directionVector(A,B), arrowL1, arrowL2, arrowW, lineColor ) )
27 | if tail_x <> None and tail_y <> None:
28 | XML_body.append( svgLine(radialLine_x, radialLine_y, tail_x, radialLine_y, lineColor, strokeWidth) )
29 | text = dimensionText( radius*scale,textFormat_radial, comma=comma_decimal_place)
30 | XML_body.append( textPlacement_common_procedure(numpy.array([radialLine_x, radialLine_y]), numpy.array([tail_x, radialLine_y]), text, text_x, text_y, 0, textRenderer, autoPlaceText, autoPlaceOffset) )
31 | return ' %s ' % "\n".join(XML_body)
32 |
33 | d.dialogWidgets.append( unitSelectionWidget )
34 | d.registerPreference( 'textFormat_radial', 'R%(value)3.3f', 'format mask')
35 | d.registerPreference( 'arrow_scheme')
36 | d.registerPreference( 'autoPlaceText')
37 | d.registerPreference( 'comma_decimal_place')
38 | d.registerPreference( 'centerPointDia')
39 | d.registerPreference( 'arrowL1')
40 | d.registerPreference( 'arrowL2')
41 | d.registerPreference( 'arrowW')
42 | d.registerPreference( 'strokeWidth')
43 | d.registerPreference( 'lineColor')
44 | d.registerPreference( 'textRenderer' )
45 | d.registerPreference( 'autoPlaceOffset')
46 | d.max_selections = 4
47 |
48 | def radiusDimensionSVG_preview(mouseX, mouseY):
49 | selections = d.selections + [ PlacementClick( mouseX, mouseY ) ] if len(d.selections) < d.max_selections else d.selections
50 | return radiusDimensionSVG( *selections_to_svg_fun_args(selections), scale=d.viewScale*d.unitConversionFactor, **d.dimensionConstructorKWs )
51 |
52 | def radiusDimensionSVG_clickHandler( x, y ):
53 | d.selections.append( PlacementClick( x, y ) )
54 | if len(d.selections) == d.max_selections - 1 and d.dimensionConstructorKWs['autoPlaceText']:
55 | d.selections.append( PlacementClick( x, y ) ) # to avoid crash when auto place turned off
56 | return 'createDimension:%s' % findUnusedObjectName('rad')
57 | elif len(d.selections) == d.max_selections :
58 | return 'createDimension:%s' % findUnusedObjectName('rad')
59 |
60 | def selectFun( event, referer, elementXML, elementParms, elementViewObject ):
61 | viewInfo = selectionOverlay.DrawingsViews_info[elementViewObject.Name]
62 | d.viewScale = 1/elementXML.rootNode().scaling()
63 | d.selections.append( CircularArcSelection( elementParms, elementXML, viewInfo ) )
64 | selectionOverlay.hideSelectionGraphicsItems()
65 | previewDimension.initializePreview( d, radiusDimensionSVG_preview, radiusDimensionSVG_clickHandler)
66 |
67 | class Proxy_RadiusDimension( Proxy_DimensionObject_prototype ):
68 | def dimensionProcess( self ):
69 | return d
70 | d.ProxyClass = Proxy_RadiusDimension
71 | d.proxy_svgFun = radiusDimensionSVG
72 |
73 | maskPen = QtGui.QPen( QtGui.QColor(0,255,0,100) )
74 | maskPen.setWidth(2.0)
75 | maskHoverPen = QtGui.QPen( QtGui.QColor(0,255,0,255) )
76 | maskHoverPen.setWidth(2.0)
77 |
78 | class RadiusDimension:
79 | def Activated(self):
80 | V = getDrawingPageGUIVars()
81 | d.activate(V, 'Add Radial Dimension', dialogIconPath=':/dd/icons/radiusDimension.svg', endFunction=self.Activated)
82 | selectionOverlay.generateSelectionGraphicsItems(
83 | dimensionableObjects( V.page ),
84 | selectFun ,
85 | transform = V.transform,
86 | sceneToAddTo = V.graphicsScene,
87 | doCircles=True, doFittedCircles=True,
88 | maskPen=maskPen,
89 | maskHoverPen=maskHoverPen,
90 | maskBrush = QtGui.QBrush() #clear
91 | )
92 | selectionOverlay.addProxyRectToRescaleGraphicsSelectionItems( V.graphicsScene, V.graphicsView, V.width, V.height)
93 |
94 | def GetResources(self):
95 | return {
96 | 'Pixmap' : ':/dd/icons/radiusDimension.svg',
97 | 'MenuText': 'Radius Dimension',
98 | 'ToolTip': 'Creates a radius dimension'
99 | }
100 |
101 | FreeCADGui.addCommand('dd_radiusDimension', RadiusDimension())
102 |
--------------------------------------------------------------------------------
/Gui/Resources/icons/noteCircle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
124 |
--------------------------------------------------------------------------------
/circularDimension.py:
--------------------------------------------------------------------------------
1 | # This Python file uses the following encoding: utf-8
2 |
3 | from dimensioning import *
4 | import selectionOverlay, previewDimension
5 | from dimensionSvgConstructor import *
6 |
7 | d = DimensioningProcessTracker()
8 |
9 | def circularDimensionSVG( center_x, center_y, radius, radialLine_x=None, radialLine_y=None, tail_x=None, tail_y=None, text_x=None, text_y=None, autoPlaceText=False, autoPlaceOffset=2.0,
10 | scale=1.0, textFormat_circular='Ø%3.3f', comma_decimal_place=False,
11 | centerPointDia = 1, arrowL1=3, arrowL2=1, arrowW=2, strokeWidth=0.5, lineColor='blue', arrow_scheme='auto',
12 | textRenderer=defaultTextRenderer):
13 | XML_body = [ ' ' % (center_x, center_y, centerPointDia*0.5, lineColor) ]
14 | #XML_body.append( '' % (center_x, center_y, radius, strokeWidth) )
15 | if radialLine_x <> None and radialLine_y <> None:
16 | theta = math.atan2( radialLine_y - center_y, radialLine_x - center_x )
17 | A = numpy.array([ center_x + radius*numpy.cos(theta) , center_y + radius*numpy.sin(theta) ])
18 | B = numpy.array([ center_x - radius*numpy.cos(theta) , center_y - radius*numpy.sin(theta) ])
19 | XML_body.append( svgLine(radialLine_x, radialLine_y, B[0], B[1], lineColor, strokeWidth) )
20 | if radius > 0:
21 | if arrow_scheme <> 'off':
22 | if arrow_scheme == 'auto':
23 | s = 1 if radius > arrowL1 + arrowL2 + 0.5*centerPointDia else -1
24 | elif arrow_scheme == 'in':
25 | s = 1
26 | elif arrow_scheme == 'out':
27 | s = -1
28 | XML_body.append( arrowHeadSVG( A, s*directionVector(A,B), arrowL1, arrowL2, arrowW, lineColor ) )
29 | XML_body.append( arrowHeadSVG( B, s*directionVector(B,A), arrowL1, arrowL2, arrowW, lineColor ) )
30 | if tail_x <> None and tail_y <> None:
31 | XML_body.append( svgLine( radialLine_x, radialLine_y, tail_x, radialLine_y, lineColor, strokeWidth ) )
32 | text = dimensionText(2*radius*scale,textFormat_circular, comma=comma_decimal_place)
33 | XML_body.append( textPlacement_common_procedure(numpy.array([radialLine_x, radialLine_y]), numpy.array([tail_x, radialLine_y]), text, text_x, text_y, 0, textRenderer, autoPlaceText, autoPlaceOffset) )
34 | return ' %s ' % "\n".join(XML_body)
35 |
36 | d.dialogWidgets.append( unitSelectionWidget )
37 | d.registerPreference( 'textFormat_circular', 'Ø%(value)3.3f', 'format mask')
38 | d.registerPreference( 'arrow_scheme')
39 | d.registerPreference( 'autoPlaceText')
40 | d.registerPreference( 'comma_decimal_place')
41 | d.registerPreference( 'centerPointDia', 0.5, increment=0.5)
42 | d.registerPreference( 'arrowL1')
43 | d.registerPreference( 'arrowL2')
44 | d.registerPreference( 'arrowW')
45 | d.registerPreference( 'strokeWidth')
46 | d.registerPreference( 'lineColor')
47 | d.registerPreference( 'textRenderer' )
48 | d.registerPreference( 'autoPlaceOffset')
49 | d.max_selections = 4
50 |
51 |
52 | def circularDimensionSVG_preview(mouseX, mouseY):
53 | selections = d.selections + [ PlacementClick( mouseX, mouseY ) ] if len(d.selections) < d.max_selections else d.selections
54 | return circularDimensionSVG( *selections_to_svg_fun_args(selections), scale=d.viewScale*d.unitConversionFactor, **d.dimensionConstructorKWs )
55 |
56 | def circularDimensionSVG_clickHandler( x, y ):
57 | d.selections.append( PlacementClick( x, y ) )
58 | if len(d.selections) == d.max_selections - 1 and d.dimensionConstructorKWs['autoPlaceText']:
59 | d.selections.append( PlacementClick( x, y ) ) # to avoid crash when auto place turned off
60 | return 'createDimension:%s' % findUnusedObjectName('dia')
61 | elif len(d.selections) == d.max_selections :
62 | return 'createDimension:%s' % findUnusedObjectName('dia')
63 |
64 | def selectFun( event, referer, elementXML, elementParms, elementViewObject ):
65 | viewInfo = selectionOverlay.DrawingsViews_info[elementViewObject.Name]
66 | d.viewScale = 1/elementXML.rootNode().scaling()
67 | d.selections.append( CircularArcSelection( elementParms, elementXML, viewInfo ) )
68 | selectionOverlay.hideSelectionGraphicsItems()
69 | previewDimension.initializePreview( d, circularDimensionSVG_preview, circularDimensionSVG_clickHandler )
70 |
71 | class Proxy_CircularDimension( Proxy_DimensionObject_prototype ):
72 | def dimensionProcess( self ):
73 | return d
74 | d.ProxyClass = Proxy_CircularDimension
75 | d.proxy_svgFun = circularDimensionSVG
76 |
77 |
78 |
79 | maskPen = QtGui.QPen( QtGui.QColor(0,255,0,100) )
80 | maskPen.setWidth(2.0)
81 | maskHoverPen = QtGui.QPen( QtGui.QColor(0,255,0,255) )
82 | maskHoverPen.setWidth(2.0)
83 |
84 |
85 | class CircularDimension:
86 | def Activated(self):
87 | V = getDrawingPageGUIVars()
88 | d.activate(V, dialogTitle='Add Circular Dimension', dialogIconPath=':/dd/icons/circularDimension.svg', endFunction=self.Activated )
89 | selectionOverlay.generateSelectionGraphicsItems(
90 | dimensionableObjects( V.page ),
91 | selectFun ,
92 | transform = V.transform,
93 | sceneToAddTo = V.graphicsScene,
94 | doCircles=True, doFittedCircles=True,
95 | maskPen=maskPen,
96 | maskHoverPen=maskHoverPen,
97 | maskBrush = QtGui.QBrush() #clear
98 | )
99 | selectionOverlay.addProxyRectToRescaleGraphicsSelectionItems( V.graphicsScene, V.graphicsView, V.width, V.height)
100 |
101 | def GetResources(self):
102 | return {
103 | 'Pixmap' : ':/dd/icons/circularDimension.svg' ,
104 | 'MenuText': 'Circular Dimension',
105 | 'ToolTip': 'Creates a circular dimension'
106 | }
107 |
108 | FreeCADGui.addCommand('dd_circularDimension', CircularDimension())
109 |
--------------------------------------------------------------------------------
/lineSearches.py:
--------------------------------------------------------------------------------
1 |
2 | import numpy, math
3 | from numpy.linalg import norm
4 |
5 | class LineSearchEvaluation:
6 | def __init__(self, f, x, searchDirection, lam, fv=None):
7 | self.lam = lam
8 | self.xv = x + lam*searchDirection
9 | self.fv = f(x + lam*searchDirection) if not fv else fv
10 | def __lt__(self, b):
11 | return self.fv < b.fv
12 | def __eq__(self, b):
13 | return self.lam == b.lam
14 | def str(self, prefix='LSEval', lamFmt='%1.6f', fvFmt='%1.2e'):
15 | return '%s %s,%s' % (prefix, lamFmt % self.lam, fvFmt % self.fv )
16 |
17 |
18 |
19 | phi = (5.0**0.5 - 1)/2
20 | def goldenSectionSearch( f, x1, f1, intialStep, it, debugPrintLevel, printF, it_min_at_x1=12): #f1 added to save resources...
21 | def LSEval(lam, fv=None):
22 | return LineSearchEvaluation( f, x1, intialStep, lam, fv )
23 | y1 = LSEval( 0.0, f1)
24 | y2 = LSEval( phi**2 )
25 | y3 = LSEval( phi )
26 | y4 = LSEval( 1.0 )
27 | if debugPrintLevel > 0:
28 | printF(' goldenSection search it 0: lam %1.3f %1.3f %1.3f %1.3f f(lam) %1.2e %1.2e %1.2e %1.2e' % ( y1.lam, y2.lam, y3.lam, y4.lam, y1.fv, y2.fv, y3.fv, y4.fv))
29 | for k in range(it_min_at_x1):
30 | y_min = min([ y1, y2, y3, y4 ])
31 | if y_min == y2 or y_min == y1 :
32 | y4 = y3
33 | y3 = y2
34 | y2 = LSEval( y1.lam + phi**2 * (y4.lam - y1.lam) )
35 | elif y_min == y3 :
36 | y1 = y2
37 | y2 = y3
38 | y3 = LSEval( y1.lam + phi*(y4.lam - y1.lam) )
39 | elif y_min == y4 :
40 | y2 = y3
41 | y3 = y4
42 | y4 = LSEval( y1.lam + (phi**-1) * (y4.lam - y1.lam) )
43 | if debugPrintLevel > 0:
44 | printF(' goldenSection search it %i: lam %1.3f %1.3f %1.3f %1.3f f(lam) %1.2e %1.2e %1.2e %1.2e' % ( it,y1.lam,y2.lam,y3.lam,y4.lam,y1.fv,y2.fv,y3.fv,y4.fv))
45 | if y1.lam > 0 and k+1 >= it:
46 | break
47 | return min([ y1, y2, y3, y4 ]).xv
48 |
49 | def quadraticLineSearch( f, x1, f1, intialStep, it, debugPrintLevel, printF, tol_stag=3, tol_x=10**-6):
50 | if norm(intialStep) == 0:
51 | printF(' quadraticLineSearch: norm search direction is 0, aborting!')
52 | return x1
53 | def LSEval(lam, fv=None):
54 | return LineSearchEvaluation( f, x1, intialStep, lam, fv )
55 | Y = [ LSEval( 0.0, f1), LSEval( 1 ), LSEval( 2 )]
56 | y_min_prev = min(Y)
57 | count_stagnation = 0
58 | tol_lambda = tol_x / norm(intialStep)
59 | for k in range(it):
60 | Y.sort()
61 | if debugPrintLevel > 0:
62 | printF(' quadratic line search it %i, fmin %1.2e, lam %1.6f %1.6f %1.6f, f(lam) %1.2e %1.2e %1.2e'%( k+1, Y[0].fv, Y[0].lam,Y[1].lam,Y[2].lam,Y[0].fv,Y[1].fv,Y[2].fv ))
63 | #``p[0]*x**(N-1) + p[1]*x**(N-2) + ... + p[N-2]*x + p[N-1]``
64 | quadraticCoefs, residuals, rank, singular_values, rcond = numpy.polyfit( [y.lam for y in Y], [y.fv for y in Y], 2, full=True)
65 | if quadraticCoefs[0] > 0 and rank == 3:
66 | lam_c = -quadraticCoefs[1] / (2*quadraticCoefs[0]) #diff poly a*x**2 + b*x + c -> grad_poly = 2*a*x + b
67 | lam_c = min( max( [y.lam for y in Y])*4, lam_c)
68 | if lam_c < 0:
69 | if debugPrintLevel > 1: printF(' quadratic line search lam_c < 0')
70 | lam_c = 1.0 / (k + 1) ** 2
71 | else:
72 | if debugPrintLevel > 1: printF(' quadratic fit invalid, using interval halving instead')
73 | lam_c = ( Y[0].lam + Y[1].lam )/2
74 | del Y[2] # Y sorted at start of each iteration
75 | Y.append( LSEval( lam_c ))
76 | y_min = min(Y)
77 | if y_min == y_min_prev:
78 | count_stagnation = count_stagnation + 1
79 | if count_stagnation > tol_stag:
80 | if debugPrintLevel > 0: printF(' terminating quadratic line search as count_stagnation > tol_stag')
81 | break
82 | else:
83 | y_min_prev = y_min
84 | count_stagnation = 0
85 | Lam = [y.lam for y in Y]
86 | if max(Lam) - min(Lam) < tol_lambda:
87 | if debugPrintLevel > 0: printF(' terminating quadratic max(Lam)-min(Lam) < tol_lambda (%e < %e)' % (max(Lam) - min(Lam), tol_lambda))
88 | break
89 |
90 | return min(Y).xv
91 |
92 |
93 | if __name__ == '__main__':
94 | print('Testing linesearches')
95 | from matplotlib import pyplot
96 | from numpy import sin
97 |
98 | def f1(x):
99 | '(1+sin(x))*(x-0.6)**2'
100 | return float( (1+sin(x))*(x-0.6)**2 )
101 | def f2(x):
102 | '(1+sin(x))*(x-0.0001)**2'
103 | return float( (1+sin(x))*(x-0.0001)**2 )
104 | class recordingWrapper:
105 | def __init__(self, f):
106 | self.f = f
107 | self.f_hist = []
108 | self.x_hist = []
109 | def __call__(self, x):
110 | self.x_hist.append(x)
111 | self.f_hist.append(self.f(x))
112 | return self.f_hist[-1]
113 | def printF(text):
114 | print(text)
115 |
116 | lineSearchesToTest = [ goldenSectionSearch, quadraticLineSearch ]
117 | names = ['golden','quadratic']
118 |
119 | for testFunction in [f1,f2]:
120 | print(testFunction.func_doc)
121 | T =[]
122 | for L,name in zip(lineSearchesToTest,names):
123 | t = recordingWrapper(testFunction)
124 | T.append(t)
125 | xOpt = L(t, numpy.array([0.0]), t( numpy.array([0.0]) ), numpy.array([0.5]), 10, debugPrintLevel=1, printF=printF)
126 | print(' %s line search, xOpt %f, f(xOpt) %e' % (name, xOpt, t(xOpt) ) )
127 | x_max = max( max(T[0].x_hist), max(T[1].x_hist ) )
128 | pyplot.figure()
129 | x_plot = numpy.linspace(0, x_max, 100)
130 | y_plot = [ testFunction(x) for x in x_plot ]
131 | pyplot.plot(x_plot, y_plot)
132 | pyplot.title(testFunction.func_doc)
133 | for t,s,label in zip(T, ['r^','go'], names):
134 | pyplot.plot( t.x_hist, t.f_hist, s ,label=label)
135 | pyplot.legend()
136 |
137 | #pyplot.show()
138 |
--------------------------------------------------------------------------------