├── transportationwb ├── test.md ├── __init__.py ├── traffic │ ├── __init__.py │ └── Test_traffic.py ├── readme.md ├── mydoxypy.py ├── tests │ └── GeometryTests │ │ ├── test_support.py │ │ └── test_geometry.py ├── myStyle.css ├── makefile ├── TestCommand.py ├── startTests.py ├── Test_All.py ├── Doxyfile ├── embed_test │ ├── mainwindow2.py │ ├── mainwindow.ui │ ├── mainwindow.py │ ├── mainwindow3.py │ └── ui_mainwindow.py ├── usecases.md ├── menues.md ├── createTestdata.py ├── vehicle │ ├── Test_vehicle.py │ └── __init__.py ├── say.py ├── Test_miki.py ├── videos.md ├── Test_labeltools.py ├── gentests.py ├── transversmercator.py ├── sketch_manager.py ├── clipplane.py └── property_creator.ui ├── Resources ├── icons │ └── vehicle_01.png ├── data │ ├── alignment │ │ ├── il2_curves.xlsx │ │ ├── IL_2_Verify.FCStd │ │ ├── IL_2_Verify.FCStd1 │ │ ├── SugarGroveRd.FCStd │ │ ├── SugarGroveRoad.png │ │ ├── SugarGroveRd.FCStd1 │ │ ├── arc_xml_construction_sample.FCStd │ │ ├── spiral_curve_back_calculator.xlsx │ │ ├── Transition curves in Road Design.odt │ │ ├── arc_xml_construction_sample.FCStd1 │ │ ├── hc_bd_deg.csv │ │ ├── hc_bd_rad.csv │ │ ├── hc_ne_deg.csv │ │ ├── hc_ne_r.csv │ │ ├── geo_test.csv │ │ ├── Sugar_Grove_Rd_Horizontal_NE.csv │ │ ├── Sugar_Grove_Rd_Horizontal.csv │ │ ├── il2_curves.csv │ │ ├── mainline_demo.csv │ │ ├── il2_vc.csv │ │ ├── il2_ss_vc.csv │ │ ├── SugarGroveRd_mixed.xml │ │ ├── SugarGroveRd.xml │ │ ├── HC_IL2_Rev4.csv │ │ └── complete_demo.csv │ ├── drainage │ │ ├── box_culvert.FCStd │ │ ├── box_culvert.FCStd1 │ │ └── box_culvert1.FCStd1 │ ├── landXML-meter.xml │ └── landXML-foot.xml └── snippets │ ├── test_controller.py │ ├── OpenOfficeBridge.py │ ├── Task │ └── TaskViewDelegate_template.py │ ├── python_templates │ ├── Task │ │ └── TaskViewDelegate_template.py │ ├── cmd_template.py │ └── fpo_template.py │ ├── cmd_template.py │ ├── Observer.py │ ├── FeedbackSketcherUtils.py │ └── fpo_template.py ├── Corridor ├── Templates │ └── pavement │ │ └── default_pavement.FCStd ├── loft │ ├── tasks │ │ ├── interval_task_panel.ui │ │ └── IntervalViewDelegate.py │ └── EditIntervals.py ├── template │ ├── SketchTemplate.py │ └── ViewTemplateLibrary.py └── alignment │ ├── ImportVerticalCurve.py │ └── AlignmentGroup.Py ├── .gitignore ├── README.md ├── Project ├── Tasks │ └── alignment │ │ ├── import_alignment_task_panel.ui │ │ ├── DraftAlignmentTask.py │ │ ├── ImportAlignmentViewDelegate.py │ │ ├── ImportXmlSubtask.py │ │ └── ImportAlignmentModel.py ├── Commands │ ├── AlignmentTracker.py │ ├── ImportAlignmentCmd.py │ └── NewProjectCmd.py ├── Support │ ├── Singleton.py │ ├── Const.py │ ├── Units.py │ ├── Properties.py │ ├── DocumentProperties.py │ └── WidgetModel.py ├── ProjectObserver.py └── XML │ └── KeyMaps.py └── Geometry ├── Spiral.py ├── Line.py └── Support.py /transportationwb/test.md: -------------------------------------------------------------------------------- 1 | Test page: Das ist eine Testseite 2 | ================================ 3 | 4 | huhuhu 5 | -------------------------------------------------------------------------------- /Resources/icons/vehicle_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/icons/vehicle_01.png -------------------------------------------------------------------------------- /Resources/data/alignment/il2_curves.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/il2_curves.xlsx -------------------------------------------------------------------------------- /Resources/data/drainage/box_culvert.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/drainage/box_culvert.FCStd -------------------------------------------------------------------------------- /Resources/data/alignment/IL_2_Verify.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/IL_2_Verify.FCStd -------------------------------------------------------------------------------- /Resources/data/alignment/IL_2_Verify.FCStd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/IL_2_Verify.FCStd1 -------------------------------------------------------------------------------- /Resources/data/alignment/SugarGroveRd.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/SugarGroveRd.FCStd -------------------------------------------------------------------------------- /Resources/data/alignment/SugarGroveRoad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/SugarGroveRoad.png -------------------------------------------------------------------------------- /Resources/data/drainage/box_culvert.FCStd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/drainage/box_culvert.FCStd1 -------------------------------------------------------------------------------- /Resources/data/drainage/box_culvert1.FCStd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/drainage/box_culvert1.FCStd1 -------------------------------------------------------------------------------- /Resources/data/alignment/SugarGroveRd.FCStd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/SugarGroveRd.FCStd1 -------------------------------------------------------------------------------- /transportationwb/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | '''tranportation workbench history ''' 3 | 4 | date=" So 25. Mär 14:33:02 CEST 2018 " 5 | version=112 6 | -------------------------------------------------------------------------------- /Corridor/Templates/pavement/default_pavement.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Corridor/Templates/pavement/default_pavement.FCStd -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .vscode/ 3 | test.py 4 | _* 5 | *~ 6 | pylintrc 7 | freecad-transportation-wb-docu/ 8 | transportationwb/corridor/alignment/data/~$il2_curves.xlsx 9 | *~lock* -------------------------------------------------------------------------------- /Resources/data/alignment/arc_xml_construction_sample.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/arc_xml_construction_sample.FCStd -------------------------------------------------------------------------------- /Resources/data/alignment/spiral_curve_back_calculator.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/spiral_curve_back_calculator.xlsx -------------------------------------------------------------------------------- /Resources/data/alignment/Transition curves in Road Design.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/Transition curves in Road Design.odt -------------------------------------------------------------------------------- /Resources/data/alignment/arc_xml_construction_sample.FCStd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelgraff/freecad-transportation-wb/HEAD/Resources/data/alignment/arc_xml_construction_sample.FCStd1 -------------------------------------------------------------------------------- /transportationwb/traffic/__init__.py: -------------------------------------------------------------------------------- 1 | '''traffic simulation tools 2 | 3 | swept path calculation 4 | 5 | drive forward 6 | drive backward 7 | 8 | ''' 9 | 10 | print "traffice version " 11 | -------------------------------------------------------------------------------- /Resources/data/alignment/hc_bd_deg.csv: -------------------------------------------------------------------------------- 1 | random_col,my_bearing,random_col,my_distance,curve.degree 2 | 12345,123.45,4545,123.45,555 3 | 54321,543.21,5454,543.21,444 4 | 45678,456.78,7878,456.78,888 5 | 87654,876.54,8787,876.54,777 6 | -------------------------------------------------------------------------------- /Resources/data/alignment/hc_bd_rad.csv: -------------------------------------------------------------------------------- 1 | random_col,my_bearing,random_col,my_distance,curve.radius 2 | 12345,123.45,4545,123.45,555 3 | 54321,543.21,5454,543.21,444 4 | 45678,456.78,7878,456.78,888 5 | 87654,876.54,8787,876.54,777 6 | -------------------------------------------------------------------------------- /Resources/data/alignment/hc_ne_deg.csv: -------------------------------------------------------------------------------- 1 | random_col,my_easting,random_col,my_northing,curve.degree 2 | 12345,123.45,4545,123.45,555 3 | 54321,543.21,5454,543.21,444 4 | 45678,456.78,7878,456.78,888 5 | 87654,876.54,8787,876.54,777 6 | -------------------------------------------------------------------------------- /Resources/data/alignment/hc_ne_r.csv: -------------------------------------------------------------------------------- 1 | "random_col,my_easting,random_col,my_northing,curve_radius" 2 | "12345,123.45,4545,123.45,555" 3 | "54321,543.21,5454,543.21,444" 4 | "45678,456.78,7878,456.78,888" 5 | "87654,876.54,8787,876.54,777" 6 | -------------------------------------------------------------------------------- /Resources/data/alignment/geo_test.csv: -------------------------------------------------------------------------------- 1 | NORTHING,EASTING,BEARING,DISTANCE,DEGREE,RADIUS,BACK,FORWARD,PARENT_ID,ID 2 | ,,,,,,,50000,,Sugar Grove Road 3 | ,,139.3986,930,,670,,,, 4 | ,,89.0825,1850,,670,,,, 5 | ,,180.5808,1680,,670,,,, 6 | ,,137.29639,644.048,,0,,,, 7 | -------------------------------------------------------------------------------- /transportationwb/readme.md: -------------------------------------------------------------------------------- 1 | 2 | This is the main page for the transportation Workbench. 3 | 4 | - [Documentation pages](pages.html) 5 | - [Use Cases](md_usecases.html) 6 | - [Menus](md_menues.html) 7 | - [Videos](md_videos.html) 8 | 9 | 10 | 11 | Links 12 | ===== 13 | 14 | 15 | - [Federal Highway Administration (FHWA)] (https://www.fhwa.dot.gov/) 16 | -------------------------------------------------------------------------------- /Resources/snippets/test_controller.py: -------------------------------------------------------------------------------- 1 | from pivy import coin 2 | import FreeCAD 3 | import FreeCADGui 4 | 5 | def handler(arg): 6 | FreeCAD.Console.PrintMessage("dragger moved") 7 | 8 | dragger = coin.SoTranslate1Dragger() 9 | view = FreeCADGui.ActiveDocument.ActiveView 10 | sg = view.getSceneGraph() 11 | sg.addChild(dragger) 12 | view.addDraggerCallback(dragger, "addFinishCallback", handler) -------------------------------------------------------------------------------- /transportationwb/mydoxypy.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | #print 'Number of arguments:', len(sys.argv), 'arguments.' 4 | #print 'Argument List:', str(sys.argv) 5 | 6 | ignore=True 7 | with open(sys.argv[1]) as fp: 8 | for i,line in enumerate(fp): 9 | if not ignore: 10 | print line.rstrip() 11 | else: 12 | if not line.startswith('#'): 13 | print line.rstrip() 14 | ignore=False 15 | -------------------------------------------------------------------------------- /transportationwb/tests/GeometryTests/test_support.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from Geometry import Support 4 | import unittest 5 | 6 | class Test_Support(unittest.TestCase): 7 | 8 | def test_vector_from_angle(self): 9 | 10 | self.assertIsNone( 11 | Support.vector_from_angle('1fdsa.0'), 12 | 'Supprt.vector_from_angle() fails non-float test' 13 | ) -------------------------------------------------------------------------------- /transportationwb/tests/GeometryTests/test_geometry.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from Geometry import * 3 | 4 | def suite(): 5 | suite = unittest.TestSuite() 6 | 7 | suite.addTest(unittest.defaultTestLoader.loadTestsFromName('transportationwb.tests.GeometryTests.test_support')) 8 | 9 | return suite 10 | 11 | def execute(): 12 | runner = unittest.TextTestRunner() 13 | runner.run(suite()) -------------------------------------------------------------------------------- /transportationwb/myStyle.css: -------------------------------------------------------------------------------- 1 | .image 2 | { 3 | text-align: left; 4 | } 5 | 6 | .aaa 7 | { 8 | float: left; 9 | text-align: left; 10 | border-right: 2px solid #fff; 11 | border-bottom: 4px solid #fff; 12 | } 13 | 14 | .bbb 15 | { 16 | border: 1px solid red; 17 | 18 | } 19 | 20 | 21 | div.image { 22 | border: 1px solid green; 23 | float: left !important; 24 | display:inline!important; 25 | } 26 | 27 | 28 | h1 { 29 | clear:both; 30 | } 31 | -------------------------------------------------------------------------------- /transportationwb/makefile: -------------------------------------------------------------------------------- 1 | gendoc: 2 | 3 | pepa: 4 | autopep8 --ignore=W191,E101,E111 labeltools.py >t.py 5 | #pylint -E labeltools.py 6 | #pylint labeltools.py 7 | 8 | 9 | pep: 10 | #autopep8 --ignore=W191,E101,E111 miki_g.py >t.py 11 | #pylint -E miki_g.py 12 | pylint miki_g.py 13 | 14 | gendoc: 15 | doxygen 16 | 17 | 18 | genlog: 19 | echo 'date="' `date` '"' >>__init__.py 20 | echo 'version='`git rev-list --all --count` >>__init__.py 21 | 22 | 23 | format: 24 | autopep8 -i Test_labeltools.py 25 | -------------------------------------------------------------------------------- /transportationwb/TestCommand.py: -------------------------------------------------------------------------------- 1 | import FreeCAD as App 2 | import FreeCADGui as Gui 3 | import Part 4 | 5 | 6 | class TestCommand(): 7 | ''' 8 | A command for testing 9 | ''' 10 | def __init__(self): 11 | pass 12 | 13 | def GetResources(self): 14 | """ 15 | Icon resources. 16 | """ 17 | 18 | return {'Pixmap' : '', 19 | 'Accel' : '', 20 | 'MenuText': "Test Command", 21 | 'ToolTip' : "Command for testing", 22 | 'CmdType' : "ForEdit"} 23 | 24 | def Activated(self): 25 | pass 26 | 27 | Gui.addCommand('TestCommand', TestCommand()) -------------------------------------------------------------------------------- /transportationwb/startTests.py: -------------------------------------------------------------------------------- 1 | # testall 2 | 3 | 4 | def startTests(): 5 | 6 | import QtUnitGui 7 | import transportationwb 8 | import transportationwb.Test_top 9 | reload(transportationwb.Test_top) 10 | #current work tests 11 | QtUnitGui.addTest("transportationwb.Test_top") 12 | #generated tests by gentest.py 13 | QtUnitGui.addTest("transportationwb.Test_top_gen") 14 | 15 | QtUnitGui.addTest("transportationwb.Test_labeltools") 16 | QtUnitGui.addTest("transportationwb.Test_miki") 17 | QtUnitGui.addTest("transportationwb.traffic.Test_traffic") 18 | QtUnitGui.addTest("transportationwb.vehicle.Test_vehicle") 19 | QtUnitGui.addTest("transportationwb.Test_All.Col1") 20 | -------------------------------------------------------------------------------- /Resources/data/alignment/Sugar_Grove_Rd_Horizontal_NE.csv: -------------------------------------------------------------------------------- 1 | NORTHING,EASTING,BEARING,DISTANCE,DEGREE,RADIUS,BACK,FORWARD,PARENT_ID,ID 2 | ,,,,,,,50000,,Sugar Grove Road 3 | -706.107527295728,605.237275699633,139.3986,930,,670,,,, 4 | -676.484010996592,2455.00008408401,89.0825,1850,,670,,,, 5 | -2356.39769654456,2437.97043028129,180.5808,1680,,670,,,, 6 | -2829.69045164815,2874.76763067348,137.29639,644.048,,0,,,, 7 | -641.542,632.8773,,,,,,1000,,Penrose Road W 8 | ,,,,,,50909,1000,Sugar Grove Road, 9 | -707.541739849086,493.629607922706,244.64028,154.097,,175,,,, 10 | -707.541739849086,-65.3963920772944,270,559.026,,0,,,, 11 | -662.4901,2900.3838,,,,,,2000,,Penrose Road E 12 | ,,,,,,52586.22,3008.866,Sugar Grove Road, 13 | -662.4901,2470.7258,270,429.658,,175,,,, 14 | -883.2349,2247.23112780924,224.9725,312.032,,0,,,, 15 | -------------------------------------------------------------------------------- /Resources/data/landXML-meter.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /transportationwb/Test_All.py: -------------------------------------------------------------------------------- 1 | 2 | import FreeCAD 3 | import sys 4 | import unittest 5 | 6 | 7 | def Col1(): 8 | suite = unittest.TestSuite() 9 | suite.addTest(unittest.defaultTestLoader.loadTestsFromName("transportationwb.Test_labeltools")) 10 | suite.addTest(unittest.defaultTestLoader.loadTestsFromName("transportationwb.Test_miki")) 11 | suite.addTest(unittest.defaultTestLoader.loadTestsFromName("transportationwb.traffic.Test_traffic")) 12 | suite.addTest(unittest.defaultTestLoader.loadTestsFromName("transportationwb.vehicle.Test_vehicle")) 13 | # if (FreeCAD.GuiUp == 1): 14 | # suite.addTest(unittest.defaultTestLoader.loadTestsFromName("nurbswb.TestNurbsGui")) 15 | return suite 16 | 17 | 18 | #def Col2(): 19 | # suite = unittest.TestSuite() 20 | # suite.addTest(unittest.defaultTestLoader.loadTestsFromName("nurbswb.TestNurbs")) 21 | # return suite 22 | -------------------------------------------------------------------------------- /Resources/data/landXML-foot.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /transportationwb/traffic/Test_traffic.py: -------------------------------------------------------------------------------- 1 | # Unit test for the transportationwb.traffic module 2 | 3 | 4 | import FreeCAD, os, unittest, FreeCADGui, transportationwb 5 | 6 | class TrafficTest(unittest.TestCase): 7 | '''testcade blabla''' 8 | 9 | def setUp(self): 10 | # setting a new document to hold the tests 11 | if FreeCAD.ActiveDocument: 12 | if FreeCAD.ActiveDocument.Name != "TransportationTest": 13 | FreeCAD.newDocument("TransportationTest") 14 | else: 15 | FreeCAD.newDocument("TransportationTest") 16 | 17 | FreeCAD.setActiveDocument("TransportationTest") 18 | 19 | def testPivy(self): 20 | FreeCAD.Console.PrintLog ('Checking Pivy...\n') 21 | from pivy import coin 22 | c = coin.SoCube() 23 | FreeCADGui.ActiveDocument.ActiveView.getSceneGraph().addChild(c) 24 | self.failUnless(c,"Pivy is not working properly") 25 | 26 | 27 | 28 | def tearDown(self): 29 | # FreeCAD.closeDocument("TransportationTest") 30 | pass 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /transportationwb/Doxyfile: -------------------------------------------------------------------------------- 1 | FILTER_SOURCE_FILES = YES 2 | INPUT_FILTER = "python /home/thomas/.FreeCAD/Mod/freecad-transportation-wb/transportationwb//mydoxypy.py" 3 | INPUT_FILTER = "python mydoxypy.py" 4 | 5 | PROJECT_NAME = "Transportation Workbench for FreeCAD" 6 | 7 | # EXCLUDE_SYMBOLS = __init__ 8 | 9 | ENABLE_PREPROCESSING = YES 10 | # PREDEFINED = DOXYGEN_SHOULD_SKIP_THISY 11 | 12 | QUIET = YES 13 | 14 | OUTPUT_DIRECTORY = ../freecad-transportation-wb-docu 15 | GENERATE_LATEX = NO 16 | 17 | 18 | INPUT = traffic/__init__.py 19 | # INPUT += traffic/Test_traffic.py 20 | 21 | INPUT += vehicle/__init__.py 22 | # INPUT += traffic/Test_traffic.py 23 | 24 | INPUT += miki_g.py 25 | INPUT += beziersketch.py 26 | INPUT += labeltools.py 27 | #INPUT += t.py 28 | INPUT += clipplane.py 29 | INPUT += createTestdata.py 30 | 31 | INPUT += usecases.md menues.md videos.md 32 | INPUT += test.md 33 | 34 | INPUT += readme.md 35 | USE_MDFILE_AS_MAINPAGE = readme.md 36 | 37 | IMAGE_PATH = ../../icons 38 | 39 | HTML_EXTRA_STYLESHEET = myStyle.css 40 | 41 | -------------------------------------------------------------------------------- /transportationwb/embed_test/mainwindow2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | #sys.path.append("") 3 | 4 | from PySide import QtCore, QtGui 5 | import FreeCADGui 6 | 7 | class MainWindow(QtGui.QMainWindow): 8 | def __init__(self, parent = None): 9 | super(MainWindow, self).__init__(parent) 10 | from PySide import QtNetwork 11 | # Webkit is used to create icons from SVG files. This could cause a deadlock 12 | # when setting up the internally used network interface. Doing this before 13 | # creating the icons fixes the issue. 14 | QtNetwork.QNetworkConfigurationManager() 15 | 16 | def showEvent(self, event): 17 | FreeCADGui.showMainWindow() 18 | self.setCentralWidget(FreeCADGui.getMainWindow()) 19 | # Need version >= 0.16.5949 20 | class BlankWorkbench(FreeCADGui.Workbench): 21 | MenuText = "Blank" 22 | ToolTip = "Blank workbench" 23 | def Initialize(self): 24 | self.appendMenu("Menu",["Std_New", "Part_Box"]) 25 | return 26 | def GetClassName(self): 27 | return "Gui::PythonBlankWorkbench" 28 | FreeCADGui.addWorkbench(BlankWorkbench) 29 | FreeCADGui.activateWorkbench("BlankWorkbench") 30 | 31 | app=QtGui.QApplication(sys.argv) 32 | mw=MainWindow() 33 | mw.resize(1200,800) 34 | mw.show() 35 | app.exec_() -------------------------------------------------------------------------------- /transportationwb/usecases.md: -------------------------------------------------------------------------------- 1 | Use Cases 2 | ========= 3 | 4 | ## To do 5 | - [ ] collect use cases 6 | - [ ] describe and illustrate them 7 | - [ ] connect to details of the used dialogs and interaction videos 8 | 9 | # Import Data into a project 10 | 11 | The simplest way to get some real data into the project is 12 | the use of the geodat workbench to import openstreetmap data. 13 | 14 | For testing purposes there are 3 location in the Demo menu. 15 | Eichleite is a street with strong curves and a height difference of near 100 meters. 16 | It is an interesting real testcase for swept paths. The street is forbidden for long vehicles. 17 | 18 | After calling the methods some groups are created. The GRP paths contains all paths found, 19 | the GRP highways contains street objects. 20 | The center of the area square is the origin and corresponds to the lat long values used in the scripts. 21 | 22 | For other locations use the geodat workbench's "Import OSM Map". 23 | 24 | ## import terrain 25 | 26 | 27 | ## import gpx-track 28 | 29 | # Design 30 | 31 | ## create a street segment 32 | 33 | ## create a crossing 34 | 35 | # Simulation 36 | 37 | ## simulate a swept path along a street segment 38 | 39 | 40 | 41 | [link to Google!](http://google.com) 42 | 43 | [Home!](index.html) 44 | [Doku pages!](pages.html) 45 | 46 | 47 | https://help.github.com/articles/basic-writing-and-formatting-syntax 48 | -------------------------------------------------------------------------------- /transportationwb/menues.md: -------------------------------------------------------------------------------- 1 | Menus 2 | ===== 3 | This pages contains the description of all menu entries for the workbench and how they are to use. 4 | 5 | # Transportation 6 | # Simulation 7 | # Terrain 8 | # Demos 9 | Here are some methods to create different kinds of dialogs. 10 | This menu is temporary for developer to provide some coding examples 11 | for dialogs 12 | ## Dialog in a mainWindow 13 | ## Dialog in Combo view tab 14 | A dialog is created in the Combo view window behind the Tasks Tab. 15 | ## Dialog dockable to a Dockwindow 16 | ## Dialog with common widgets 17 | ## Dialog with functionality to run an animation 18 | 19 | There are some methods to import real data from openstreetmap 20 | using geodat workbench 21 | 22 | ## create Eichleite 23 | ## create Woosung 24 | ## create Japanese Knot 25 | 26 | # Curves 27 | ##create a Bezier Sketch 28 | ##create a simple Bezier sketch 29 | A simple Bezier sketch is a curve defined by 4 poles. 30 | Two poles are the endpoints of the curve and the others are used to 31 | configure the tangents from the endpoints. 32 | ##create Marker 33 | creates a Label on a selected curve 34 | ##combine Curves 35 | combine two 2D curves to a 3D curve 36 | # Labels 37 | ##create Geolocation 38 | ##create Stationing 39 | ##create Placement 40 | ##create all Labels 41 | creates Labels for all elements (faces, edges, vertexes) of a selected object. 42 | Unused Labels can be deleted in a next step. 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Freecad Transportation Workbench 2 | Python transportation engineeering workbench for FreeCAD 3 | 4 | This workbench is being developed to provide functionality specific to transportation engineering (road and rail). 5 | 6 | ## Usage 7 | Examples coming soon. Will include screencast and screenshots. 8 | 9 | ## Installation 10 | Clone the project into the `~/.FreeCAD/Mod/Transportation` folder to use. 11 | 12 | **Note:** This project has a dependency in another FreeCAD workbench, [@microelly2's](https://github.com/microelly2) [Nurbs Workbench](https://github.com/microelly2/freecad-nurbs). A symlink exists which points to it in the Transportation source. 13 | 14 | You can satisfy this dependency one of two ways: 15 | 16 | 1. Clone the Nurbs workbench into the `~/.FreeCAD/Mod/nurbs` folder 17 | 2. Delete the symlink and copy the `freecad-nurbs/sketcher/feedbacksketch.py` file to the `~/.FreeCAD/Mod/Transportation` folder. 18 | 19 | ## Feedback 20 | Discuss this Workbench on the FreeCAD forum thread dedicated to this topic: 21 | [Civil engineering feature implementation (Transportation Engineering)](https://forum.freecadweb.org/viewtopic.php?f=8&t=22277). 22 | 23 | FYI, this thread was born of a parent thread: 24 | [Civil Engineering Design functions](https://forum.freecadweb.org/viewtopic.php?f=8&t=6973) 25 | 26 | ## Developer 27 | Joel Graff (@graffy76) with inspiration and help from the FreeCAD community 28 | 29 | -------------------------------------------------------------------------------- /transportationwb/createTestdata.py: -------------------------------------------------------------------------------- 1 | '''create some real environments with hard coded coordinates 2 | 3 | these scripts use the geodat workbench. 4 | ''' 5 | 6 | def createEichleite(): 7 | '''Wilhelmsthal Eichleite Friesener Berg steiler kurviger Anstieg 50.2725064,11.3946793''' 8 | import geodat 9 | from geodat import import_osm 10 | 11 | bk=0.5 12 | b,l = 50.2725064,11.3946793 13 | 14 | progressbar=False 15 | status=False 16 | elevation=False 17 | 18 | if not geodat.import_osm.import_osm2(b,l,bk,progressbar,status,elevation): 19 | geodat.import_osm.import_osm2(b,l,bk,progressbar,status,elevation) 20 | 21 | 22 | def createWoosung(): 23 | '''testcase by Joel 41.8846258,-89.5806548''' 24 | import geodat 25 | from geodat import import_osm 26 | 27 | bk=2 28 | b,l = 41.8846258,-89.5806548 29 | 30 | progressbar=False 31 | status=False 32 | elevation=False 33 | 34 | if not geodat.import_osm.import_osm2(b,l,bk,progressbar,status,elevation): 35 | geodat.import_osm.import_osm2(b,l,bk,progressbar,status,elevation) 36 | 37 | 38 | 39 | def createJapaneseKnot(): 40 | '''complex highway crossing 35.624446,139.263''' 41 | 42 | import geodat 43 | from geodat import import_osm 44 | 45 | bk=0.5 46 | b,l = 35.624446,139.263 47 | 48 | progressbar=False 49 | status=False 50 | elevation=False 51 | 52 | if not geodat.import_osm.import_osm2(b,l,bk,progressbar,status,elevation): 53 | geodat.import_osm.import_osm2(b,l,bk,progressbar,status,elevation) 54 | -------------------------------------------------------------------------------- /Project/Tasks/alignment/import_alignment_task_panel.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | TaskPanel 4 | 5 | 6 | 7 | 0 8 | 0 9 | 478 10 | 411 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | Import Alignment 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 1 30 | 0 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ... 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /transportationwb/vehicle/Test_vehicle.py: -------------------------------------------------------------------------------- 1 | # Unit test for the transportationwb.vehicle module 2 | 3 | 4 | import FreeCAD, os, unittest, FreeCADGui, transportationwb 5 | 6 | class VehicleTest(unittest.TestCase): 7 | 8 | def setUp(self): 9 | # setting a new document to hold the tests 10 | if FreeCAD.ActiveDocument: 11 | if FreeCAD.ActiveDocument.Name != "TransportationTest": 12 | FreeCAD.newDocument("TransportationTest") 13 | else: 14 | FreeCAD.newDocument("TransportationTest") 15 | FreeCAD.setActiveDocument("TransportationTest") 16 | 17 | 18 | def testPivy(self): 19 | FreeCAD.Console.PrintLog ('Checking Pivy...\n') 20 | from pivy import coin 21 | c = coin.SoCube() 22 | FreeCADGui.ActiveDocument.ActiveView.getSceneGraph().addChild(c) 23 | self.failUnless(c,"Pivy is not working properly") 24 | 25 | 26 | def testVehicle(self): 27 | FreeCAD.Console.PrintLog ('Checking createVehicle ...\n') 28 | FreeCAD.Console.PrintMessage ('Checking createVehicle...\n') 29 | import transportationwb 30 | import transportationwb.vehicle 31 | reload(transportationwb.vehicle) 32 | c=transportationwb.vehicle.createVehicle() 33 | self.failUnless(c,"createVehicle is not working properly") 34 | 35 | 36 | 37 | def tearDown(self): 38 | FreeCAD.closeDocument("TransportationTest") 39 | pass 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /transportationwb/embed_test/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 0 21 | 0 22 | 800 23 | 25 24 | 25 | 26 | 27 | 28 | FreeCAD 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Embed 40 | 41 | 42 | 43 | 44 | Document 45 | 46 | 47 | 48 | 49 | Cube 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /transportationwb/say.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #------------------------------------------------- 3 | #-- Animation workbench 4 | #-- 5 | #-- microelly 2016 6 | #-- 7 | #-- GNU Lesser General Public License (LGPL) 8 | #------------------------------------------------- 9 | 10 | import FreeCAD 11 | 12 | import FreeCADGui 13 | App=FreeCAD 14 | Gui=FreeCADGui 15 | 16 | import PySide 17 | from PySide import QtCore, QtGui 18 | 19 | import FreeCAD 20 | import Draft, Part 21 | 22 | import numpy as np 23 | import matplotlib 24 | import matplotlib.pyplot as plt 25 | from matplotlib.pyplot import cm 26 | 27 | import os,random,time,sys,traceback 28 | 29 | 30 | 31 | 32 | def log(s): 33 | logon = False 34 | if logon: 35 | f = open('/tmp/log.txt', 'a') 36 | f.write(str(s) +'\n') 37 | f.close() 38 | 39 | def sayd(s): 40 | if hasattr(FreeCAD,'animation_debug'): 41 | pass 42 | log(str(s)) 43 | FreeCAD.Console.PrintMessage(str(s)+"\n") 44 | 45 | def say(s): 46 | log(str(s)) 47 | FreeCAD.Console.PrintMessage(str(s)+"\n") 48 | 49 | def sayErr(s): 50 | log(str(s)) 51 | FreeCAD.Console.PrintError(str(s)+"\n") 52 | 53 | 54 | def sayW(s): 55 | log(str(s)) 56 | FreeCAD.Console.PrintWarning(str(s)+"\n") 57 | 58 | 59 | def errorDialog(msg): 60 | diag = QtGui.QMessageBox(QtGui.QMessageBox.Critical,u"Error Message",msg ) 61 | diag.setWindowFlags(PySide.QtCore.Qt.WindowStaysOnTopHint) 62 | diag.exec_() 63 | 64 | 65 | def sayexc(mess=''): 66 | exc_type, exc_value, exc_traceback = sys.exc_info() 67 | ttt=repr(traceback.format_exception(exc_type, exc_value,exc_traceback)) 68 | lls=eval(ttt) 69 | l=len(lls) 70 | l2=lls[(l-3):] 71 | FreeCAD.Console.PrintError(mess + "\n" +"--> ".join(l2)) 72 | -------------------------------------------------------------------------------- /transportationwb/Test_miki.py: -------------------------------------------------------------------------------- 1 | # Unit test for the transportationwb.vehicle module 2 | 3 | 4 | import FreeCAD, os, unittest, FreeCADGui #, transportationwb 5 | 6 | class MikiTestU(unittest.TestCase): 7 | 8 | def setUp(self): 9 | # setting a new document to hold the tests 10 | if FreeCAD.ActiveDocument: 11 | if FreeCAD.ActiveDocument.Name != "TransportationTest": 12 | FreeCAD.newDocument("TransportationTestA") 13 | else: 14 | FreeCAD.newDocument("TransportationTestA") 15 | FreeCAD.setActiveDocument("TransportationTestA") 16 | 17 | 18 | def testMiki(self): 19 | FreeCAD.Console.PrintMessage('!-! Checking miki...\n') 20 | import transportationwb 21 | import transportationwb.miki_g 22 | reload(transportationwb.miki_g) 23 | FreeCAD.Console.PrintMessage('!####! Checking miki...\n') 24 | 25 | c=transportationwb.miki_g.testme() 26 | FreeCAD.Console.PrintMessage('Checking miki...\n' + str(c)+"\n") 27 | self.failUnless(c<>None,"Miki is not working properly") 28 | 29 | c=transportationwb.miki_g.testDialogTab() 30 | FreeCAD.Console.PrintMessage('Checking miki...\n' + str(c)+"\n") 31 | self.failUnless(c<>None,"Miki is not working properly") 32 | 33 | c=transportationwb.miki_g.testDialogMainWindow() 34 | FreeCAD.Console.PrintMessage('Checking miki...\n' + str(c)+"\n") 35 | self.failUnless(c<>None,"Miki is not working properly") 36 | 37 | c=transportationwb.miki_g.testDialogDockWidget() 38 | FreeCAD.Console.PrintMessage('Checking miki...\n' + str(c)+"\n") 39 | self.failUnless(c<>None,"Miki is not working properly") 40 | 41 | 42 | def tearDown(self): 43 | # FreeCAD.closeDocument("TransportationTestA") 44 | pass 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Project/Tasks/alignment/DraftAlignmentTask.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Task to draft an alignment 26 | ''' -------------------------------------------------------------------------------- /Resources/data/alignment/Sugar_Grove_Rd_Horizontal.csv: -------------------------------------------------------------------------------- 1 | NORTHING,EASTING,BEARING,DISTANCE,DEGREE,RADIUS,BACK,FORWARD,PARENT_ID,ID,curve_length,curve tangent,curve delta,radians,cum len,tan len 2 | ,,,,,,,50000,,Sugar Grove Road,,,,,,, 3 | 605.237275699633,-706.107527295728,139.3986,930,,670,,,,,588.381679881022,314.679100637121,50.3161,0.878181611762719,1203.7025792439,615.320899362878 4 | 2455.00008408401,-676.484010996592,89.0825,1850,,670,,,,,1069.95421863494,687.753825063403,91.4983,1.59694659497753,1917.52129293442,847.567074299475 5 | 2437.97043028129,-2356.39769654456,180.5808,1680,,670,,,,,506.155164419716,265.8428311204,43.28441,0.755455469283159,1232.55850823591,726.403343816197 6 | 2874.76763067348,-2829.69045164815,137.29639,644.048,,0,,,,,,,,,378.2051688796,600.76359 7 | ,,,,,,,1000,,Penrose Road W,,,,,4731.98754929383, 8 | ,,,,,,50909,1000,Sugar Grove Road,,,,,,, 9 | 452.322935461379,-756.162903625233,244.64028,154.097,,175,,,,,77.4568569921749,39.3733201436167,25.35972,0.442610611383857,,114.723679856383 10 | -106.703064538621,-756.162903625233,270,559.026,,0,,,,,,,,,, 11 | ,,,,,,,2000,,Penrose Road E,,,,,, 12 | 2247.23112780924,-1148.28677303149,,,,,52586.22,3008.866,Sugar Grove Road,,,,,,, 13 | 2371.03419296732,-1024.60249328073,270,429.658,,175,,,,,137.528672564806,72.5365808362728,45.0275,0.785878128941747,449.560672564806, 14 | 2591.56821113411,-803.856675981262,224.9725,312.032,,0,,,,,,,,,, 15 | ,,,,,,,,,,,,,,, 16 | ,,,,,,,,,,,,,,, 17 | 2455.00008408401,-676.484010996592,,,,,,,,,,,,,, 18 | 1767.33443709678,-680.740875559135,,,,,,,,,,,,,, 19 | 479.896690712462,-467.545897472353,52051.2696535434,534.950346456622,0.798433352920332,,,,,,,,,,, 20 | 2247.23112780924,-1148.28677303149,,,,,,,,,,,,,, 21 | 123.803065158084,123.684279750756,,,,,,,,,,,,,, 22 | -220.534018166788,-220.74581729947,,,,,,,,,,,,,, 23 | -------------------------------------------------------------------------------- /transportationwb/videos.md: -------------------------------------------------------------------------------- 1 | Videos 2 | ====== 3 | 4 | 5 | - [Design of a street curve with Sketcher @youtube 2018/05/16](https://youtu.be/4eFycT6zY7s) 6 | - [Steering simulation ](https://www.youtube.com/watch?v=SBsuudzi0x0)(22.03.2018) 7 | Simulation Ackermann steering swept curves 8 | 9 | 10 | ## Links 11 | 12 | [Home](index.html) + [Documentation pages](pages.html) + [Use Cases](md_usecases.html) 13 | 14 | #japan autobahnkreuz 15 | 35.624446,139.263 16 | 17 | # near woosung 18 | 41.8846258,-89.5806548 19 | 20 | # Eichleite 21 | 50.2725064,11.3946793 22 | 23 | https://www.google.com/maps/place/35%C2%B037'28.0%22N+139%C2%B015'48.0%22E/@35.6244444,139.2611446,17z/data=!4m5!3m4!1s0x0:0x0!8m2!3d35.6244444!4d139.2633333 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Models 35 | ====== 36 | Ikarus 180 37 | 38 | Mercedes Economic 39 | 40 | 41 | 42 | 43 | Audi A6 44 | 45 | -------------------------------------------------------------------------------- /transportationwb/Test_labeltools.py: -------------------------------------------------------------------------------- 1 | # Unit test for the transportationwb.labeltools module 2 | 3 | 4 | import FreeCAD 5 | import unittest 6 | 7 | # pylint: disable=invalid-name 8 | 9 | 10 | class LabeltoolsTest(unittest.TestCase): 11 | 12 | def setUp(self): 13 | 14 | import transportationwb 15 | import transportationwb.labeltools 16 | reload(transportationwb.labeltools) 17 | 18 | if FreeCAD.ActiveDocument: 19 | if FreeCAD.ActiveDocument.Name != "TransportationTest": 20 | FreeCAD.newDocument("TransportationTest") 21 | else: 22 | FreeCAD.newDocument("TransportationTest") 23 | FreeCAD.setActiveDocument("TransportationTest") 24 | self.windows = [] 25 | 26 | def testLabelGUI(self): 27 | 28 | if 1: 29 | import transportationwb.labeltools 30 | 31 | c = transportationwb.labeltools.createStationingGUI() 32 | self.windows += [c] 33 | 34 | c = transportationwb.labeltools.createAllLabelsGUI() 35 | self.windows += [c] 36 | 37 | c = transportationwb.labeltools.createGeoLocationGUI() 38 | self.windows += [c] 39 | 40 | c = transportationwb.labeltools.createGraphicLabelGUI() 41 | self.windows += [c] 42 | 43 | def testLabelcmds(self): 44 | 45 | import transportationwb.labeltools 46 | 47 | c = transportationwb.labeltools.createBillBoard() 48 | assert c 49 | 50 | c = transportationwb.labeltools.createLatLonMarker('51.34', '12.56') 51 | assert c 52 | 53 | def testFail(self): 54 | a = 1 / 0 55 | assert a 56 | 57 | def tearDown(self): 58 | FreeCAD.closeDocument("TransportationTest") 59 | for c in self.windows: 60 | c.hide() 61 | -------------------------------------------------------------------------------- /transportationwb/embed_test/mainwindow.py: -------------------------------------------------------------------------------- 1 | import sys 2 | #sys.path.append("") 3 | 4 | from PySide import QtCore, QtGui 5 | import FreeCAD, FreeCADGui 6 | 7 | from transportationwb.embed_test.ui_mainwindow import Ui_MainWindow 8 | 9 | class MainWindow(QtGui.QMainWindow): 10 | def __init__(self, parent = None): 11 | super(MainWindow, self).__init__(parent) 12 | from PySide import QtNetwork 13 | # Webkit is used to create icons from SVG files. This could cause a deadlock 14 | # when setting up the internally used network interface. Doing this before 15 | # creating the icons fixes the issue. 16 | QtNetwork.QNetworkConfigurationManager() 17 | 18 | def showEvent(self, event): 19 | FreeCADGui.showMainWindow() 20 | self.setCentralWidget(FreeCADGui.getMainWindow()) 21 | # Need version >= 0.16.5949 22 | class BlankWorkbench(FreeCADGui.Workbench): 23 | MenuText = "Blank" 24 | ToolTip = "Blank workbench" 25 | def Initialize(self): 26 | return 27 | def GetClassName(self): 28 | return "Gui::BlankWorkbench" 29 | FreeCADGui.addWorkbench(BlankWorkbench) 30 | FreeCADGui.activateWorkbench("BlankWorkbench") 31 | 32 | @QtCore.Slot() 33 | def on_actionEmbed_triggered(self): 34 | return 35 | 36 | @QtCore.Slot() 37 | def on_actionDocument_triggered(self): 38 | FreeCAD.newDocument() 39 | 40 | @QtCore.Slot() 41 | def on_actionCube_triggered(self): 42 | FreeCAD.ActiveDocument.addObject("Part::Box") 43 | FreeCAD.ActiveDocument.recompute() 44 | FreeCADGui.ActiveDocument.ActiveView.fitAll() 45 | 46 | app=QtGui.QApplication(sys.argv) 47 | ui=Ui_MainWindow() 48 | mw=MainWindow() 49 | ui.setupUi(mw) 50 | ui.actionEmbed.setVisible(False) 51 | mw.resize(1200,800) 52 | mw.show() 53 | app.exec_() -------------------------------------------------------------------------------- /Corridor/loft/tasks/interval_task_panel.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | TaskPanel 4 | 5 | 6 | 7 | 0 8 | 0 9 | 307 10 | 268 11 | 12 | 13 | 14 | Interval Schedule 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | false 23 | 24 | 25 | true 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Qt::Horizontal 37 | 38 | 39 | 40 | 40 41 | 20 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Add 50 | 51 | 52 | 53 | 54 | 55 | 56 | Remove 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Project/Commands/AlignmentTracker.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | ''' 24 | Tracker for alignment drafting 25 | ''' 26 | 27 | from DraftTrackers import Tracker 28 | 29 | class AlignmentTracker(Tracker): 30 | ''' 31 | Tracker class for alignment design 32 | ''' 33 | 34 | pass -------------------------------------------------------------------------------- /transportationwb/embed_test/mainwindow3.py: -------------------------------------------------------------------------------- 1 | import sys 2 | #sys.path.append("") 3 | 4 | from PySide import QtCore, QtGui 5 | import FreeCAD, FreeCADGui 6 | import ctypes 7 | 8 | from ui_mainwindow import Ui_MainWindow 9 | 10 | class MainWindow(QtGui.QMainWindow): 11 | def __init__(self, parent = None): 12 | super(MainWindow, self).__init__(parent) 13 | from PySide import QtNetwork 14 | # Webkit is used to create icons from SVG files. This could cause a deadlock 15 | # when setting up the internally used network interface. Doing this before 16 | # creating the icons fixes the issue. 17 | QtNetwork.QNetworkConfigurationManager() 18 | 19 | @QtCore.Slot() 20 | def on_actionEmbed_triggered(self): 21 | FreeCADGui.showMainWindow() 22 | hwnd=self.winId() 23 | PyCObject_AsVoidPtr = ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object)(('PyCObject_AsVoidPtr', ctypes.pythonapi)) 24 | addr = PyCObject_AsVoidPtr(hwnd) 25 | FreeCADGui.embedToWindow(hex(addr)) 26 | # Need version >= 0.16.5949 27 | class BlankWorkbench(FreeCADGui.Workbench): 28 | MenuText = "Blank" 29 | ToolTip = "Blank workbench" 30 | def Initialize(self): 31 | return 32 | def GetClassName(self): 33 | return "Gui::BlankWorkbench" 34 | FreeCADGui.addWorkbench(BlankWorkbench) 35 | FreeCADGui.activateWorkbench("BlankWorkbench") 36 | 37 | @QtCore.Slot() 38 | def on_actionDocument_triggered(self): 39 | FreeCAD.newDocument() 40 | 41 | @QtCore.Slot() 42 | def on_actionCube_triggered(self): 43 | FreeCAD.ActiveDocument.addObject("Part::Box") 44 | FreeCAD.ActiveDocument.recompute() 45 | FreeCADGui.ActiveDocument.ActiveView.fitAll() 46 | 47 | app=QtGui.QApplication(sys.argv) 48 | ui=Ui_MainWindow() 49 | mw=MainWindow() 50 | ui.setupUi(mw) 51 | mw.resize(1200,800) 52 | mw.show() 53 | app.exec_() -------------------------------------------------------------------------------- /Resources/data/alignment/il2_curves.csv: -------------------------------------------------------------------------------- 1 | Curve,Sta,Length,PI,T1,T2 2 | 1,113100,100,723.45,-1.15,0.3 3 | 2,113215,130,723.79,,-1.5 4 | 3,113430,200,720.57,,-0.91 5 | 4,114050,300,714.95,,-3.39 6 | 5,114880,840,686.84,,0.5 7 | 6,115750,180,691.19,,-0.6 8 | 7,116096,180,689.11,,0.47 9 | 8,116820,320,691.12,,-1.61 10 | 9,117283,500,678.85,,2.34 11 | 10,118020,350,696.07,,-0.8 12 | 11,118800,210,689.83,,0.5 13 | 12,119300,160,692.33,,-0.5 14 | 13,119760,160,690.03,,0.5 15 | 14,120200,160,692.23,,-0.5 16 | 15,120540,160,690.53,,0.5 17 | 16,121150,300,693.58,,-0.54 18 | 17,121850,500,689.81,,0.3 19 | 18,123780,500,695.6,,1.1 20 | 19,124620,800,704.84,,-1.4 21 | 20,125520,500,692.24,,-0.01 22 | 21,130130,500,691.99,,-0.26 23 | 22,131030,500,689.75,,0.1 24 | 23,134020,300,692.77,,1.7 25 | 24,134320,300,697.87,,-0.07 26 | 25,134800,300,697.53,,0.2 27 | 26,135750,300,699.43,,-0.18 28 | 27,136400,300,698.26,,-0.09 29 | 28,137930,300,696.89,,-1.16 30 | 29,138407,200,691.35,,0.56 31 | 30,138890,300,694.04,,-1 32 | 31,139190,300,691.04,,1.32 33 | 32,139490,300,695.01,,-1.29 34 | 33,139820,360,690.74,,0.23 35 | 34,140300,400,691.85,,-0.15 36 | 35,140700,400,691.25,,0.03 37 | 36,141850,300,691.59,,0.25 38 | 37,143500,2000,695.72,,-0.3 39 | 38,145120,1000,690.86,,0.24 40 | 39,146303,200,693.66,,-0.22 41 | 40,146536.33,0,693.15,,0.7 42 | 41,147150,200,695.11,,0.84 43 | 42,147751.22,200,700.16,,0.69 44 | STEQ,148292.43,0,,, 45 | 43,180,600,705.17,,0.2 46 | 44,2940,500,710.69,,-1.93 47 | 45,3661,300,696.78,,-0.75 48 | 46,3993,144,696.42,,1.24 49 | 47,4065,220,697.31,,2.19 50 | 48,4370,390,703.99,,0.2 51 | 49,5570,440,706.4,,2.12 52 | 50,6850,620,733.5,,4.55 53 | 51,7385,450,757.86,,2.77 54 | 52,8635,1800,792.5,,-2.87 55 | 53,9825,580,758.3,,-4.72 56 | 54,10970,1000,704.3,,0.3 57 | 55,12600,300,709.19,,0.03 58 | 56,14750,450,709.74,,3.08 59 | 57,15265,580,725.61,,-0.55 60 | 58,17070,600,71.69,,0.9 61 | 59,18280,800,726.58,,-1.45 62 | 60,19200,600,713.19,,-0.69 63 | 61,19960,300,707.94,,0.5 64 | 62,20260,300,709.44,,-1.36 65 | 63,20560,300,705.35,,1 66 | -------------------------------------------------------------------------------- /Resources/data/alignment/mainline_demo.csv: -------------------------------------------------------------------------------- 1 | ,BEARING,NORTHING,EASTING,RADIUS,DISTANCE,ID,PARENT_ID,BACK,FORWARD,SPIRAL 2 | ,,0,0,,,Mainline,,,113050, 3 | ,,,,,,,,148292.43,0, 4 | 114383.75,87.64805556,,,1008.7,1791.416859,,,,, 5 | 115243.04,38.83861111,,,2000,613.7386026,,,,, 6 | 116456.78,29.91444444,,,3800,2466.731841,,,,, 7 | 119410.29,349.2408333,,,2300,1867.764662,,,,, 8 | 120017.7,359.3480556,,,4000,643.452579,,,35242.43,, 9 | 120493.91,6.169166667,,,2000,559.8602734,,,20742.76,, 10 | 121385.05,24.43222222,,,1585.53,990.2138983,,,55985.19,, 11 | 122536.31,355.0911111,,,3600,1320.838479,,,55985.184,, 12 | 124488.52,12.97499999,,,6000,2126.327426,,,0.006,, 13 | 125944.07,26.87444444,,,3500,1017.510108,,,1.07E-07,, 14 | 129512.97,36.22222222,,,4000,3458.064833,,,1.07E-05,, 15 | 129952.83,41.205,,,10000,516.0634721,,,,, 16 | 131070.39,38.34055556,,,2000,1218.168726,,,,, 17 | 132520.88,58.22361111,,,3814.39,1578.688482,,,,, 18 | 133459.51,72.32277778,,,2257.87,1174.118968,,,,, 19 | 135030.2,106.8844444,,,1836.457,1875.903176,,,,,663.8898101 20 | 138885.55,69.04861111,,,1520,3467.460005,,,,, 21 | 140062.91,32.20722222,,,2100,917.2014913,,,,, 22 | 142178.09,43.68,,,2500,2815.151589,,,,, 23 | 144158.68,83.66333333,,,3375,1726.118739,,,,, 24 | 146689.32,64.14111111,,,2293.84,2294.867597,,,,, 25 | 2335.46,80.68944444,,,6000,3999.045104,,,,, 26 | 3539.45,88.11611111,,,1800,776.0678555,,,,, 27 | 3973.19,77.9325,,,1800,718.3420265,,,,, 28 | 4458.51,62.48416667,,,20000,497.5683627,,,,, 29 | 5783.34,63.93611111,,,2883.78,1413.070865,,,,, 30 | ,50.4225,,,0,747.074293,,,,, 31 | 6868.92,50.4225,,,2193.485,1045.122534,,,,,725.5594 32 | 10325.67,84.83444444,,,3687.8,3056.256401,,,,, 33 | 12214.42,66.91583333,,,3400,2329.312532,,,,, 34 | 14795.06,33.68861111,,,4656.43,1811.674163,,,,, 35 | 15453.37,38.27138889,,,2855.9299,764.0416766,,,,, 36 | 16349.07,26.60138889,,,4000,1265.204375,,,,, 37 | 17656.01,45.32194444,,,7200,1198.102444,,,,, 38 | 18991.96,53.88055555,,,4149.52,1217.285038,,,,, 39 | 20119.36,42.37416667,,,950,916.4121979,,,,, 40 | 20521.76,66.64361111,,,0,425.2646071,,,,, 41 | -------------------------------------------------------------------------------- /Resources/data/alignment/il2_vc.csv: -------------------------------------------------------------------------------- 1 | IL 72,,,, 2 | 148292.43,0,,, 3 | 113100,100,723.45,-1.15,0.3 4 | 113215,130,723.79,0.3,-1.5 5 | 113430,200,720.57,-1.5,-0.91 6 | 114050,300,714.95,-0.91,-3.39 7 | 114880,840,686.84,-3.39,0.5 8 | 115750,180,691.19,0.5,-0.6 9 | 116096,180,689.11,-0.6,0.47 10 | 116520,320,691.12,0.47,-1.61 11 | 117283,500,678.85,-1.61,2.34 12 | 118020,350,696.07,2.34,-0.8 13 | 118800,210,689.83,-0.8,0.5 14 | 119300,160,692.33,0.5,-0.5 15 | 119760,160,690.03,-0.5,0.5 16 | 120200,160,692.23,0.5,-0.5 17 | 120540,160,690.53,-0.5,0.5 18 | 121150,300,693.58,0.5,-0.54 19 | 121850,500,689.81,-0.54,0.3 20 | 123780,500,695.6,0.3,1.1 21 | 124620,800,704.84,1.1,-1.4 22 | 125520,500,692.24,-1.4,-0.01 23 | 130130,500,691.99,-0.01,-0.26 24 | 131030,500,689.75,-0.26,0.1 25 | 134020,300,692.77,0.1,1.7 26 | 134320,300,697.87,1.7,-0.07 27 | 134800,300,697.53,-0.07,0.2 28 | 135750,300,699.43,0.2,-0.18 29 | 136400,300,698.26,-0.18,-0.09 30 | 137930,300,696.89,-0.09,-1.16 31 | 138407,200,691.35,-1.16,0.56 32 | 138890,300,694.04,0.56,-1 33 | 139190,300,691.04,-1,1.32 34 | 139490,300,695.01,1.32,-1.29 35 | 139820,360,690.74,-1.29,0.23 36 | 140300,400,691.85,0.23,-0.15 37 | 140700,400,691.25,-0.15,0.03 38 | 141850,300,691.59,0.03,0.25 39 | 143500,2000,695.72,0.25,-0.3 40 | 145120,1000,690.86,-0.3,0.24 41 | 146303,200,693.66,0.24,-0.22 42 | 146725,450,692.29,-0.22,0.664 43 | 147150,200,695.11,0.664,0.84 44 | 147751.22,200,700.16,0.84,0.69 45 | 180,600,705.17,0.69,0.2 46 | 2940,500,710.69,0.2,-1.93 47 | 3661,300,696.78,-1.93,-0.75 48 | 3839.57,57.14,695.45,-0.75,1.24 49 | 4065,220,697.31,1.24,2.19 50 | 4370,390,703.99,2.19,0.2 51 | 5570,440,706.4,0.2,2.12 52 | 6850,620,733.5,2.12,4.55 53 | 7385,450,757.86,4.55,2.77 54 | 8635,1800,792.5,2.77,-2.87 55 | 9825,580,758.3,-2.87,-4.72 56 | 10970,1000,704.3,-4.72,0.3 57 | 12600,300,709.19,0.3,0.03 58 | 14750,450,709.74,0.03,3.08 59 | 15265,580,725.61,3.08,-0.55 60 | 17070,600,715.69,-0.55,0.9 61 | 18280,800,726.58,0.9,-1.45 62 | 19200,600,713.19,-1.45,-0.69 63 | 19960,300,707.94,-0.69,0.5 64 | 20260,300,709.44,0.5,-1.36 65 | 20560,300,705.35,-1.36,1 66 | -------------------------------------------------------------------------------- /transportationwb/gentests.py: -------------------------------------------------------------------------------- 1 | #create testcases from source file 2 | 3 | head=''' 4 | class {}_Test(unittest.TestCase): 5 | 6 | def setUp(self): 7 | 8 | if FreeCAD.ActiveDocument: 9 | if FreeCAD.ActiveDocument.Name != "TransportationTest": 10 | FreeCAD.newDocument("TransportationTest") 11 | else: 12 | FreeCAD.newDocument("TransportationTest") 13 | FreeCAD.setActiveDocument("TransportationTest") 14 | 15 | ''' 16 | 17 | foot=''' 18 | def tearDown(self): 19 | FreeCAD.closeDocument("TransportationTest") 20 | ''' 21 | 22 | def run(out,ifn,fn): 23 | print (ifn,fn) 24 | import re 25 | 26 | res = re.search(r".*/(\S+)\.py", fn) 27 | modul='clipplane' 28 | modul=res.group(1) 29 | # print modul 30 | out.write(head.format(modul)) 31 | 32 | fp=open(fn) 33 | anz=0 34 | for i,line in enumerate(fp): 35 | if line.startswith('def'): 36 | res = re.search(r"def\s+(\S+)\((.*)\):\s*", line) 37 | if res: 38 | anz +=1 39 | fun=res.group(1) 40 | args=res.group(2) 41 | print (" ",anz,fun,args) 42 | out.write( " def test_"+fun+"(self):\n") 43 | out.write("\n" ) 44 | out.write(" import transportationwb."+modul+"\n") 45 | out.write(" reload (transportationwb."+modul+")\n") 46 | out.write(" rc=transportationwb."+modul+"."+fun+"("+args+")\n") 47 | out.write("\n") 48 | out.write(foot) 49 | return anz 50 | 51 | 52 | def runall(fns): 53 | out=open('/home/thomas/.FreeCAD/Mod/freecad-transportation-wb/transportationwb/Test_top_generated.py', 'w') 54 | out.write('''import FreeCAD\nimport unittest\n''') 55 | 56 | anzsum=0 57 | for i,fn in enumerate(fns): 58 | anzsum += run(out,i,fn) 59 | print ("gesamt",anzsum) 60 | 61 | 62 | #----------------------------- 63 | 64 | 65 | t='''beziersketch.py 66 | bogen_zeichnen.py 67 | clipplane.py 68 | create_circle_sketch.py 69 | geodesic_lines.py 70 | labeltools.py 71 | miki_g.py 72 | mydoxypy.py 73 | say.py 74 | stationing.py 75 | transversmercator.py 76 | ''' 77 | 78 | fns=['/home/thomas/.FreeCAD/Mod/freecad-transportation-wb/transportationwb/'+l for l in t.splitlines()] 79 | 80 | runall(fns) 81 | -------------------------------------------------------------------------------- /Project/Support/Singleton.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2018 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Singleton class definition 26 | ''' 27 | 28 | class Singleton(type): 29 | 30 | _instances = {} 31 | 32 | def __call__(cls, *args, **kwargs): 33 | 34 | if cls not in cls._instances: 35 | cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) 36 | 37 | return cls._instances[cls] 38 | 39 | def instance_of(cls): 40 | 41 | if cls in cls._instances: 42 | return cls._instances[cls] 43 | 44 | return None -------------------------------------------------------------------------------- /Resources/snippets/OpenOfficeBridge.py: -------------------------------------------------------------------------------- 1 | """ 2 | OpenOfficeBridge - Bridge to interact directly with a running instance of open 3 | office, providing key support functions to read / write parameters between 4 | FreeCAD and the Calc application 5 | """ 6 | 7 | import uno 8 | import os 9 | import subprocess 10 | import time 11 | 12 | import Sketcher 13 | import FreeCAD as App 14 | import Part 15 | import FreeCADGui as Gui 16 | import FeedbackSketcherUtils 17 | import os 18 | 19 | class DataSource(): 20 | 21 | def GetResources(self): 22 | 23 | icon_path = os.path.dirname(os.path.abspath(__file__)) 24 | 25 | icon_path += "/icons/new_alignment.svg" 26 | 27 | return {'Pixmap' : icon_path, 28 | 'MenuText': "Data Source", 29 | 'ToolTip' : "Create a data source", 30 | 'CmdType' : "ForEdit"} 31 | 32 | def Activated(self): 33 | 34 | uno_context = uno.getComponentContext() 35 | 36 | url_resolver = uno_context.ServiceManager.createInstanceWithContext( \ 37 | "com.sun.star.bridge.UnoUrlResolver", uno_context) 38 | 39 | print ("data source activated") 40 | self.launch = 'soffice --accept="socket,host=localhost,port=2002;urp;"' 41 | 42 | self.start_office() 43 | 44 | return 45 | 46 | def IsActive(self): 47 | return True 48 | 49 | def start_office (self, filepath = None): 50 | """ 51 | Initiates an office instance in a separate python process 52 | """ 53 | 54 | def _start_instance(filepath, socket = 2002): 55 | """ 56 | Starts office listening on a socket 57 | """ 58 | 59 | print ("starting instance") 60 | 61 | #quit if this is the main process 62 | if os.fork(): 63 | return 64 | 65 | time.sleep(1.0) 66 | 67 | #try: 68 | #after migration to PY3, update calling methods 69 | result = subprocess.run('soffice', shell=True) 70 | 71 | # except OSError as ose: 72 | #raise OSError(ose) 73 | 74 | #print (result) 75 | 76 | _start_instance(self.launch) 77 | 78 | Gui.addCommand('Data Source',DataSource()) 79 | -------------------------------------------------------------------------------- /transportationwb/embed_test/ui_mainwindow.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'mainwindow.ui' 4 | # 5 | # Created: Fri Nov 20 18:03:04 2015 6 | # by: pyside-uic 0.2.13 running on PySide 1.1.0 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide import QtCore, QtGui 11 | 12 | class Ui_MainWindow(object): 13 | def setupUi(self, MainWindow): 14 | MainWindow.setObjectName("MainWindow") 15 | MainWindow.resize(800, 600) 16 | self.centralwidget = QtGui.QWidget(MainWindow) 17 | self.centralwidget.setObjectName("centralwidget") 18 | MainWindow.setCentralWidget(self.centralwidget) 19 | self.menubar = QtGui.QMenuBar(MainWindow) 20 | self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25)) 21 | self.menubar.setObjectName("menubar") 22 | self.menuFreeCAD = QtGui.QMenu(self.menubar) 23 | self.menuFreeCAD.setObjectName("menuFreeCAD") 24 | MainWindow.setMenuBar(self.menubar) 25 | self.statusbar = QtGui.QStatusBar(MainWindow) 26 | self.statusbar.setObjectName("statusbar") 27 | MainWindow.setStatusBar(self.statusbar) 28 | self.actionEmbed = QtGui.QAction(MainWindow) 29 | self.actionEmbed.setObjectName("actionEmbed") 30 | self.actionDocument = QtGui.QAction(MainWindow) 31 | self.actionDocument.setObjectName("actionDocument") 32 | self.actionCube = QtGui.QAction(MainWindow) 33 | self.actionCube.setObjectName("actionCube") 34 | self.menuFreeCAD.addAction(self.actionEmbed) 35 | self.menuFreeCAD.addAction(self.actionDocument) 36 | self.menuFreeCAD.addAction(self.actionCube) 37 | self.menubar.addAction(self.menuFreeCAD.menuAction()) 38 | 39 | self.retranslateUi(MainWindow) 40 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 41 | 42 | def retranslateUi(self, MainWindow): 43 | MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) 44 | self.menuFreeCAD.setTitle(QtGui.QApplication.translate("MainWindow", "FreeCAD", None, QtGui.QApplication.UnicodeUTF8)) 45 | self.actionEmbed.setText(QtGui.QApplication.translate("MainWindow", "Embed", None, QtGui.QApplication.UnicodeUTF8)) 46 | self.actionDocument.setText(QtGui.QApplication.translate("MainWindow", "Document", None, QtGui.QApplication.UnicodeUTF8)) 47 | self.actionCube.setText(QtGui.QApplication.translate("MainWindow", "Cube", None, QtGui.QApplication.UnicodeUTF8)) -------------------------------------------------------------------------------- /Project/Support/Const.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2018 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Constant class definition 26 | ''' 27 | 28 | __title__ = "Const.py" 29 | __author__ = "Joel Graff" 30 | __url__ = "https://www.freecadweb.org" 31 | 32 | class MetaConst(type): 33 | ''' 34 | Metaclass to enforce constant-like behaviors 35 | ''' 36 | 37 | def __getattr__(cls, key): 38 | ''' 39 | Default getter 40 | ''' 41 | return cls[key] 42 | 43 | def __setattr__(cls, key, value): 44 | ''' 45 | Default setter 46 | ''' 47 | raise TypeError 48 | 49 | class Const(object, metaclass=MetaConst): 50 | ''' 51 | Const class for subclassing 52 | ''' 53 | 54 | def __getattr__(self, name): 55 | ''' 56 | Default getter 57 | ''' 58 | 59 | return self[name] 60 | 61 | def __setattr__(self, name, value): 62 | ''' 63 | Default setter 64 | ''' 65 | 66 | raise TypeError 67 | -------------------------------------------------------------------------------- /Resources/snippets/Task/TaskViewDelegate_template.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | QItemStyledDelegate class for Task QTableView 26 | ''' 27 | 28 | from PySide import QtGui, QtCore 29 | 30 | class TaskViewDelegate(QtGui.QStyledItemDelegate): 31 | 32 | def __init__(self, parent=None): 33 | 34 | QtGui.QStyledItemDelegate.__init__(self, parent) 35 | self._is_editing = False 36 | 37 | def createEditor(self, parent, option, index): 38 | 39 | return super(TaskViewDelegate, self).createEditor(parent, option, index) 40 | 41 | def setEditorData(self, editor, index): 42 | 43 | self._is_editing = True 44 | 45 | value = index.data(QtCore.Qt.EditRole) or index.data(QtCore.Qt.DisplayRole) 46 | 47 | if editor.metaObject().className() in ['QSpinBox', 'QDoubleSpinBox']: 48 | editor.setValue(value) 49 | else: 50 | editor.setText(value) 51 | 52 | def setModelData(self, editor, model, index): 53 | 54 | super(TaskViewDelegate, self).setModelData(editor, model, index) 55 | 56 | self._is_editing = False 57 | 58 | def isEditing(self): 59 | 60 | return self._is_editing -------------------------------------------------------------------------------- /Resources/snippets/python_templates/Task/TaskViewDelegate_template.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | QItemStyledDelegate class for Task QTableView 26 | ''' 27 | 28 | from PySide import QtGui, QtCore 29 | 30 | class TaskViewDelegate(QtGui.QStyledItemDelegate): 31 | 32 | def __init__(self, parent=None): 33 | 34 | QtGui.QStyledItemDelegate.__init__(self, parent) 35 | self._is_editing = False 36 | 37 | def createEditor(self, parent, option, index): 38 | 39 | return super(TaskViewDelegate, self).createEditor(parent, option, index) 40 | 41 | def setEditorData(self, editor, index): 42 | 43 | self._is_editing = True 44 | 45 | value = index.data(QtCore.Qt.EditRole) or index.data(QtCore.Qt.DisplayRole) 46 | 47 | if editor.metaObject().className() in ['QSpinBox', 'QDoubleSpinBox']: 48 | editor.setValue(value) 49 | else: 50 | editor.setText(value) 51 | 52 | def setModelData(self, editor, model, index): 53 | 54 | super(TaskViewDelegate, self).setModelData(editor, model, index) 55 | 56 | self._is_editing = False 57 | 58 | def isEditing(self): 59 | 60 | return self._is_editing -------------------------------------------------------------------------------- /Resources/snippets/cmd_template.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import os 25 | import FreeCAD as App 26 | import FreeCADGui as Gui 27 | 28 | class Command(): 29 | ''' 30 | Command Description 31 | ''' 32 | def __init__(self): 33 | pass 34 | 35 | def GetResources(self): 36 | """ 37 | Icon resources. 38 | """ 39 | 40 | icon_path = os.path.dirname(os.path.abspath(__file__)) 41 | 42 | icon_path += "../../../icons/new_alignment.svg" 43 | 44 | return {'Pixmap' : icon_path, 45 | 'Accel' : '', 46 | 'MenuText': '', 47 | 'ToolTip' : '', 48 | 'CmdType' : 'ForEdit'} 49 | 50 | def _validate_selection(self): 51 | ''' 52 | Validate the selected items in the Gui before operating on them 53 | ''' 54 | 55 | _items = Gui.Selection.getSelection() 56 | 57 | return _items 58 | 59 | def Activated(self): 60 | ''' 61 | Command activation method 62 | ''' 63 | result = self._validate_selection() 64 | 65 | if not result: 66 | print('Invalid selection') 67 | return 68 | 69 | return 70 | 71 | Gui.addCommand('Command', Command()) 72 | -------------------------------------------------------------------------------- /Resources/snippets/python_templates/cmd_template.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import os 25 | import FreeCAD as App 26 | import FreeCADGui as Gui 27 | 28 | class Command(): 29 | ''' 30 | Command Description 31 | ''' 32 | def __init__(self): 33 | pass 34 | 35 | def GetResources(self): 36 | """ 37 | Icon resources. 38 | """ 39 | 40 | icon_path = os.path.dirname(os.path.abspath(__file__)) 41 | 42 | icon_path += "../../../icons/new_alignment.svg" 43 | 44 | return {'Pixmap' : icon_path, 45 | 'Accel' : '', 46 | 'MenuText': '', 47 | 'ToolTip' : '', 48 | 'CmdType' : 'ForEdit'} 49 | 50 | def _validate_selection(self): 51 | ''' 52 | Validate the selected items in the Gui before operating on them 53 | ''' 54 | 55 | _items = Gui.Selection.getSelection() 56 | 57 | return _items 58 | 59 | def Activated(self): 60 | ''' 61 | Command activation method 62 | ''' 63 | result = self._validate_selection() 64 | 65 | if not result: 66 | print('Invalid selection') 67 | return 68 | 69 | return 70 | 71 | Gui.addCommand('Command', Command()) 72 | -------------------------------------------------------------------------------- /transportationwb/transversmercator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ''' 4 | TransverseMercator: 5 | "author": "Vladimir Elistratov and gtoonstra", 6 | "wiki_url": "https://github.com/vvoovv/blender-geo/wiki/Import-OpenStreetMap-(.osm)", 7 | "tracker_url": "https://github.com/vvoovv/blender-geo/issues", 8 | ''' 9 | 10 | import FreeCAD 11 | import os 12 | import math 13 | 14 | # see conversion formulas at 15 | # http://en.wikipedia.org/wiki/Transverse_Mercator_projection 16 | # and 17 | # http://mathworld.wolfram.com/MercatorProjection.html 18 | 19 | class TransverseMercator: 20 | radius = 6378137 21 | radius = 6378137000 22 | 23 | def __init__(self, **kwargs): 24 | # setting default values 25 | self.lat = 0 # in degrees 26 | self.lon = 0 # in degrees 27 | self.k = 1.0 # scale factor 28 | 29 | for attr in kwargs: 30 | setattr(self, attr, kwargs[attr]) 31 | self.latInRadians = math.radians(self.lat) 32 | print "lat in radinans",self.latInRadians 33 | 34 | def fromGeographic(self, lat, lon): 35 | lat = math.radians(lat) 36 | lon = math.radians(lon-self.lon) 37 | B = math.sin(lon) * math.cos(lat) 38 | x = 0.5 * self.k * self.radius * math.log((1+B)/(1-B)) 39 | y = self.k * self.radius * ( math.atan(math.tan(lat)/math.cos(lon)) - self.latInRadians ) 40 | return (x,y) 41 | 42 | def toGeographic(self, x, y): 43 | x = x/(self.k * self.radius) 44 | y = y/(self.k * self.radius) 45 | D = y + self.latInRadians 46 | lon = math.atan(math.sinh(x)/math.cos(D)) 47 | lat = math.asin(math.sin(D)/math.cosh(x)) 48 | # print(x,y,lat,lon) 49 | 50 | lon = self.lon + math.degrees(lon) 51 | lat = math.degrees(lat) 52 | return (lat, lon) 53 | 54 | 55 | 56 | 57 | 58 | def getpos(latc,lonc,lat,lon): 59 | '''calculate the xy position for lat lon relative to latc,lonc''' 60 | 61 | tm=TransverseMercator() 62 | tm.lat=latc 63 | tm.lon=lonc 64 | tm.latInRadians = math.radians(latc) 65 | 66 | center=tm.fromGeographic(latc,lonc) 67 | 68 | ll=tm.fromGeographic(lat,lon) 69 | pos=FreeCAD.Vector(ll[0]-center[0],ll[1]-center[1]) 70 | 71 | print ("Distance between") 72 | print (latc,lonc) 73 | print (lat,lon) 74 | print ( "in km ",pos.Length/1000000) 75 | #pos *= 0.001 76 | #pos *= 0.01 77 | 78 | return pos 79 | 80 | 81 | def getlatlon(latc,lonc,x,y): 82 | 83 | tm=TransverseMercator() 84 | tm.lat=latc 85 | tm.lon=lonc 86 | tm.latInRadians = math.radians(latc) 87 | 88 | lat,lon=tm.toGeographic(x,y) 89 | 90 | return lat,lon 91 | 92 | ''' 93 | v=getpos(51.0,15.0,52.0,15.0) 94 | v2 = v*0.000001 95 | print v2 96 | 97 | getlatlon(51.0,15.0,v.x,v.y) 98 | ''' 99 | -------------------------------------------------------------------------------- /Geometry/Spiral.py: -------------------------------------------------------------------------------- 1 | import FreeCAD as App 2 | import math 3 | 4 | def discretize_spiral(start_coord, bearing, radius, angle, length, interval, interval_type): 5 | ''' 6 | Discretizes a spiral curve using the length parameter. 7 | ''' 8 | 9 | #generate inbound spiral 10 | #generate circular arc 11 | #generate outbound spiral 12 | 13 | points = [] 14 | 15 | length_mm = length * 304.80 16 | radius_mm = radius * 304.80 17 | 18 | bearing_in = App.Vector(math.sin(bearing), math.cos(bearing)) 19 | 20 | curve_dir = 1.0 21 | 22 | if angle < 0.0: 23 | curve_dir = -1.0 24 | angle = abs(angle) 25 | 26 | _Xc = ((length_mm**2) / (6.0 * radius_mm)) 27 | _Yc = (length_mm - ((length_mm**3) / (40 * radius_mm**2))) 28 | 29 | _dY = App.Vector(bearing_in).multiply(_Yc) 30 | _dX = App.Vector(bearing_in.y, -bearing_in.x, 0.0).multiply(curve_dir).multiply(_Xc) 31 | 32 | theta_spiral = length_mm/(2 * radius_mm) 33 | arc_start = start_coord.add(_dX.add(_dY)) 34 | arc_coords = [arc_start] 35 | #arc_coords.extend(_HorizontalAlignment.discretize_arc(arc_start, bearing + (theta_spiral * curve_dir), radius, curve_dir * (angle - (2 * theta_spiral)), interval, interval_type)) 36 | 37 | if len(arc_coords) < 2: 38 | print('Invalid central arc defined for spiral') 39 | return None 40 | 41 | segment_length = arc_coords[0].distanceToPoint(arc_coords[1]) 42 | segments = int(length_mm / segment_length) + 1 43 | 44 | for _i in range(0, segments): 45 | 46 | _len = float(_i) * segment_length 47 | 48 | _x = (_len ** 3) / (6.0 * radius_mm * length_mm) 49 | _y = _len - ((_len**5) / (40 * (radius_mm ** 2) * (length_mm**2))) 50 | 51 | _dY = App.Vector(bearing_in).multiply(_y) 52 | _dX = App.Vector(bearing_in.y, -bearing_in.x, 0.0).multiply(curve_dir).multiply(_x) 53 | 54 | points.append(start_coord.add(_dY.add(_dX))) 55 | 56 | points.extend(arc_coords) 57 | 58 | exit_bearing = bearing + (angle * curve_dir) 59 | bearing_out = App.Vector(math.sin(exit_bearing), math.cos(exit_bearing), 0.0) 60 | 61 | _dY = App.Vector(bearing_out).multiply(_Yc) 62 | _dX = App.Vector(-bearing_out.y, bearing_out.x, 0.0).multiply(curve_dir).multiply(_Xc) 63 | 64 | end_coord = points[-1].add(_dY.add(_dX)) 65 | 66 | temp = [end_coord] 67 | 68 | for _i in range(1, segments): 69 | 70 | _len = float(_i) * segment_length 71 | 72 | if _len > length_mm: 73 | _len = length_mm 74 | 75 | _x = (_len ** 3) / (6.0 * radius_mm * length_mm) 76 | _y = _len - ((_len ** 5) / (40 * (radius_mm ** 2) * (length_mm**2))) 77 | 78 | _dY = App.Vector(-bearing_out).multiply(_y) 79 | _dX = App.Vector(bearing_out.y, -bearing_out.x, 0.0).multiply(curve_dir).multiply(_x) 80 | 81 | temp.append(end_coord.add(_dY.add(_dX))) 82 | 83 | points.extend(temp[::-1]) 84 | 85 | return points -------------------------------------------------------------------------------- /Corridor/loft/tasks/IntervalViewDelegate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | QItemStyledDelegate class for Task QTableView 26 | ''' 27 | 28 | from PySide import QtGui, QtCore 29 | 30 | class IntervalViewDelegate(QtGui.QStyledItemDelegate): 31 | 32 | def __init__(self, parent=None): 33 | 34 | QtGui.QStyledItemDelegate.__init__(self, parent) 35 | self._is_editing = False 36 | 37 | def createEditor(self, parent, option, index): 38 | 39 | return super(IntervalViewDelegate, self).createEditor(parent, option, index) 40 | 41 | def setEditorData(self, editor, index): 42 | 43 | self._is_editing = True 44 | 45 | value = index.data(QtCore.Qt.EditRole) or index.data(QtCore.Qt.DisplayRole) 46 | 47 | if editor.metaObject().className() in ['QSpinBox', 'QDoubleSpinBox']: 48 | editor.setValue(value) 49 | else: 50 | editor.setText(value) 51 | 52 | def setModelData(self, editor, model, index): 53 | 54 | super(IntervalViewDelegate, self).setModelData(editor, model, index) 55 | 56 | self._is_editing = False 57 | 58 | #force a sort if data is set on the second column 59 | #assumption: User is done adding / edditing an interval, so now it's safe to re-sort 60 | if index.column() == 1: 61 | model.sort(2) 62 | 63 | def isEditing(self): 64 | 65 | return self._is_editing 66 | -------------------------------------------------------------------------------- /Corridor/template/SketchTemplate.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | #http://free-cad.sourceforge.net/SrcDocu/dc/d77/classSketcher_1_1SketchObjectPy.html 4 | #https://forum.freecadweb.org/viewtopic.php?t=6121 5 | #https://forum.freecadweb.org/viewtopic.php?t=12829 6 | import FreeCAD as App 7 | from Project.Support import Properties 8 | 9 | if App.Gui: 10 | import FreeCADGui as Gui 11 | 12 | def create(sketch_object, template_name): 13 | ''' 14 | Constructor method foor creating a new sketch template 15 | ''' 16 | 17 | obj = App.ActiveDocument.addObject("Sketcher::SketchObjectPython", template_name) 18 | 19 | _o = _Sketch(obj) 20 | 21 | _o.duplicate(sketch_object) 22 | 23 | App.activeDocument().recompute() 24 | 25 | return _o 26 | 27 | 28 | class _Sketch(object): 29 | 30 | def __init__(self, obj): 31 | 32 | obj.Proxy = self 33 | self.Type = self.__class__.__name__ 34 | self.Object = None 35 | 36 | _ViewProvider(obj.ViewObject) 37 | 38 | Properties.add(obj, 'LinkList', 'Lofts', 'List of dependent lofts', is_read_only=True, default_value=[]) 39 | 40 | self.Object = obj 41 | 42 | def __getstate__(self): 43 | ''' 44 | State method for serialization 45 | ''' 46 | return self.Type 47 | 48 | def __setstate__(self, state): 49 | ''' 50 | State method for serialization 51 | ''' 52 | if state: 53 | self.Type = state 54 | 55 | def duplicate(self, sketch): 56 | ''' 57 | Duplicate the source sketch in the SketchObjectPython 58 | ''' 59 | 60 | for geo in sketch.Geometry: 61 | index = self.Object.addGeometry(geo) 62 | self.Object.setConstruction(index, geo.Construction) 63 | 64 | for constraint in sketch.Constraints: 65 | self.Object.addConstraint(constraint) 66 | 67 | self.Object.Placement = sketch.Placement 68 | 69 | for expr in sketch.ExpressionEngine: 70 | self.Object.setExpression(expr[0], expr[1]) 71 | 72 | self.Object.solve() 73 | self.Object.recompute() 74 | 75 | def onChanged(self, obj, prop): 76 | 77 | #avoid call during object initializtion 78 | if not hasattr(self, 'Object'): 79 | return 80 | 81 | self.myExecute(obj) 82 | 83 | 84 | def myExecute(self, obj): 85 | 86 | #if self.Object.Lofts: 87 | pass 88 | # try: fa=App.ActiveDocument.curve 89 | # except: fa=App.ActiveDocument.addObject('Part::Spline','curve') 90 | # fa.Shape=bc.toShape() 91 | # fa.ViewObject.LineColor=(.0,1.0,.0) 92 | 93 | class _ViewProvider(): 94 | 95 | def execute(self, obj): 96 | obj.recompute() 97 | #self.myExecute(obj) 98 | 99 | def __init__(self, vobj): 100 | self.Object = vobj.Object 101 | vobj.Proxy = self 102 | 103 | def getIcon(self): 104 | return '' 105 | 106 | def __getstate__(self): 107 | return None 108 | 109 | def __setstate__(self, state): 110 | return None -------------------------------------------------------------------------------- /Project/ProjectObserver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import FreeCAD as App 25 | import FreeCADGui 26 | 27 | class ProjectObserver(object): 28 | 29 | docs = {} 30 | 31 | @staticmethod 32 | def get(doc): 33 | ''' 34 | Factory method to create an observer attached to doc 35 | If already created, return it 36 | ''' 37 | 38 | if not doc in ProjectObserver.docs: 39 | print('building new observer') 40 | ProjectObserver.docs[doc] = ProjectObserver(doc) 41 | App.addDocumentObserver(ProjectObserver.docs[doc]) 42 | 43 | return ProjectObserver.docs[doc] 44 | 45 | def __init__(self, doc): 46 | 47 | self.doc = doc 48 | self.targets = {'StartSaveDocument': []} 49 | 50 | def register(self, event_name, callback): 51 | ''' 52 | Registration for target objects to be updated for specific tasks 53 | ''' 54 | 55 | self.targets[event_name].append(callback) 56 | 57 | def slotStartSaveDocument(self, doc, file): 58 | ''' 59 | Administrative work to do before saving a document 60 | ''' 61 | 62 | if not doc == self.doc: 63 | return 64 | 65 | for _cb in self.targets['StartSaveDocument']: 66 | _cb() 67 | 68 | def slotDeletedDocument(self, doc): 69 | 70 | if not doc == self.doc: 71 | return 72 | 73 | print('removing observer') 74 | App.removeDocumentObserver(self) 75 | -------------------------------------------------------------------------------- /Project/Commands/ImportAlignmentCmd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import os 25 | import FreeCAD as App 26 | import FreeCADGui as Gui 27 | 28 | from Project.Tasks.alignment.ImportAlignmentTask import ImportAlignmentTask 29 | 30 | class ImportAlignmentCmd(): 31 | ''' 32 | Initiates the ImportAlignmentTask class for 2D horizontal and vertical curves 33 | ''' 34 | def __init__(self): 35 | 36 | self.alignment_data = None 37 | 38 | def GetResources(self): 39 | """ 40 | Icon resources. 41 | """ 42 | 43 | icon_path = os.path.dirname(os.path.abspath(__file__)) 44 | 45 | icon_path += "../../../icons/new_alignment.svg" 46 | 47 | return {'Pixmap' : icon_path, 48 | 'Accel' : 'Ctrl+Shift+A', 49 | 'MenuText': 'Import Alignment', 50 | 'ToolTip' : 'Import a horizontal or vertical alignment from CSV', 51 | 'CmdType' : 'ForEdit'} 52 | 53 | def _update_callback(self, alignment_data): 54 | ''' 55 | Update callback called when task activities are completed / accepted 56 | ''' 57 | 58 | #run create alignment command from here 59 | 60 | print('return!') 61 | 62 | def Activated(self): 63 | ''' 64 | Command activation method 65 | ''' 66 | 67 | panel = ImportAlignmentTask(self._update_callback) 68 | 69 | Gui.Control.showDialog(panel) 70 | 71 | panel.setup() 72 | 73 | return 74 | 75 | Gui.addCommand('ImportAlignmentCmd', ImportAlignmentCmd()) 76 | -------------------------------------------------------------------------------- /Resources/data/alignment/il2_ss_vc.csv: -------------------------------------------------------------------------------- 1 | Union St,,,, 2 | 40007,,723.61,1.5,-1.5 3 | 40022,,723.38,-1.5,-1 4 | 40036,28,723.24,-1,1.99 5 | IL 72,,,, 6 | 126474,,722.9,-1.5,-1 7 | 126510,60,722.54,-1,-0.7 8 | 126570,60,722.11,-0.7,-1.05 9 | Chestnut St,,,, 10 | 41868,22,714.61,3.84,5.9 11 | 41936,44,717.55,5.9,1 12 | 41965,,717.84,1,2 13 | 42007,,718.68,2,-2 14 | 42020,,718.42,-2,-1 15 | 42060,50,718.02,-1,4.49 16 | 42100,30,720,4.49,1.18 17 | Market St,,,, 18 | 43930,60,712.02,7.66,1.1 19 | 43980,,712.57,1.1,2 20 | 44007,,713.11,2,-2 21 | 44020,,712.85,-2,-1 22 | 44065,40,712.4,-1,5.4 23 | 44138,4,716.34,5.4,1.15 24 | Peru St,,,, 25 | 45979.84,,699.63,1.34,-4.12 26 | E 3rd St,,,, 27 | 47005.98,,690.15,-2,1.34 28 | 46990.43,,689.94,1.34,0.93 29 | 46981.36,,689.86,0.93,0.5 30 | 46969.4,,689.8,0.5,0.416666667 31 | 46967,,689.79,0.416666667,1 32 | 46936,,689.48,1,-8.93 33 | Kysor Dr,,,, 34 | 48224,142,681.92,-0.21,-4 35 | 48037.87,,689.37,-4,-2.01 36 | Luther Dr,,,, 37 | 50979.88,,687.63,2.24,1 38 | 50926,46,687.09,1,-8.2 39 | Old State Rd,,,, 40 | 51980.86,,694.23,2.19,1.09 41 | 51910,80,693.46,1.09,-8 42 | 51730,170,707.86,-8,-4.11 43 | Ashelford Dr,,,, 44 | 53190,155,684.59,0.45,-3.6 45 | 53031,,690.32,-3.6,-2.06 46 | 53019,,690.57,-2.06,2.13 47 | River Dr S,,,, 48 | 54019,,689.74,-2.7,-1.57 49 | 54114,50,688.25,-1.57,-1.35 50 | Mobile Home Access Road,,,, 51 | 1013,,687.39,-2,-1 52 | 1050,60,687.02,-1,5 53 | 1135,110,691.27,5,0.5 54 | 1320,100,692.19,0.5,-0.5 55 | 1440,140,691.59,-0.5,-5 56 | 1545,70,686.34,-5,-0.3 57 | 1650,110,686.03,-0.3,3.44 58 | 1708.38,,688.04,3.44,-3.22 59 | River Dr N Connector,,,, 60 | 55019.02,,692.02,-2.87,-3.63 61 | 55120,90,688.36,-3.63,1.03 62 | Lake Louise Entrance,,,, 63 | 54924,60,692.46,6.47,1.15 64 | 54980.99,,693.11,1.15,-2.87 65 | River Dr N,,,, 66 | 51000,90,689.72,0.63,0.83 67 | Kennedy Hill Rd,,,, 68 | 60019.42,,692.76,3.41,3.39 69 | 60033.21,,693.23,3.39,-1 70 | 60146,135,692.1,-1,2.59 71 | Frontage Rd Connector,,,, 72 | 60052,,697.44,-1.5,-1 73 | 601,96,696.96,-1,1.6 74 | Meridian Rd,,,, 75 | 62007.08,,703.97,1.57,-1.4 76 | 62160,270,702.39,-1.4,4.5 77 | 62645,510,724.22,4.5,0.09 78 | Gold River Ave N,,,, 79 | 63007.06,,710.07,2.58,-2.05 80 | 63050,60,709.75,-2.05,-1.15 81 | Gold River Ave S,,,, 82 | 62800,160,695.94,1.2,8.78 83 | 62930,100,707.36,8.78,4 84 | 62980.82,,709.39,4,2.58 85 | Blue Lake Ave,,,, 86 | 64007.01,,709.26,2.59,2.49 87 | 64019.02,,709.56,2.49,-2 88 | 64031.04,,709.32,-2,-1 89 | 64095,65,708.68,-1,2.99 90 | Willow Run,,,, 91 | 64918,32,700.08,1.24,5.59 92 | 64950,32,701.87,5.59,1 93 | 64980.92,,702.18,1,-5.69 94 | Antler Trail,,,, 95 | 66008.52,,696.49,-2.82,-2.53 96 | 66023.13,,686.12,-2.53,-4 97 | 66086,98,693.61,-4,-1.48 98 | Silver Creek Rd S,,,, 99 | 66886,60,697.83,0.35,1.16 100 | 66968.64,,698.79,1.16,2.25 101 | 66980.78,,699.06,2.25,-5.3 102 | Silver Creek Rd N,,,, 103 | 67822,116,708.57,0.96,9 104 | 67915,70,716.94,9,3.56 105 | 67980.81,,719.28,3.56,-4.06 106 | Prairie Rd N,,,, 107 | 163931.4,,776.24,4.27,-1.11 108 | 164072,200,775.13,-1.11,0.71 109 | Prairie Rd S,,,, 110 | 29916,100,773.64,2.71,2.25 111 | 29980.97,,775.1,2.25,4.01 112 | CEL 201+44.09,,,, 113 | 40028,40028,710.05,4.37,-1 114 | 40173,240,708.6,-1,0.45 115 | -------------------------------------------------------------------------------- /Resources/snippets/Observer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import FreeCAD as App 25 | import FreeCADGui as Gui 26 | import random 27 | 28 | from Project.Support import Singleton 29 | 30 | def create(doc, callback): 31 | ''' 32 | Creation method for _Document class 33 | ''' 34 | if _saveObserver.instance() is None: 35 | App.addDocumentObserver(_saveObserver(doc)) 36 | else: 37 | _saveObserver.instance().set_document(doc) 38 | 39 | class _saveObserver(metaclass=Singleton.Singleton): 40 | 41 | def __init__(self, doc, callback): 42 | 43 | self.target_doc = doc 44 | self.callback = callback 45 | 46 | def set_document(self, doc): 47 | ''' 48 | Set the passed document as the active document for the observer 49 | ''' 50 | 51 | self.target_doc = doc 52 | 53 | @staticmethod 54 | def instance(): 55 | ''' 56 | Return the singleton instance of the class 57 | ''' 58 | 59 | return Singleton.Singleton.instance_of(_saveObserver) 60 | 61 | def slotStartSaveDocument(self, doc, value): 62 | 63 | print('calling save document...', doc.Label, value) 64 | 65 | if _saveObserver.instance() is None: 66 | App.removeDocumentObserver(self) 67 | return 68 | 69 | #if the document is deleted, remove the observer 70 | try: 71 | self.target_doc.__doc__ 72 | except: 73 | App.removeDocumentObserver(self) 74 | return 75 | 76 | if doc == self.target_doc: 77 | self.target_doc.before_save('slotStartSaveDocument') 78 | -------------------------------------------------------------------------------- /Resources/data/alignment/SugarGroveRd_mixed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 2437.9704 -2356.3977 0.0 14 | 15 | 16 | 17 | 18 | 2455.0000 -676.4840 0.0 19 | 20 | 21 | 22 | 23 | 605.2372 -706.1075 0.0 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 493.6296 -707.5417 0.0 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 2470.7258 -662.4901 0.0 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /transportationwb/sketch_manager.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | /************************************************************************** 3 | * * 4 | * Copyright (c) 2018 Joel Graff * 5 | * * 6 | * This program is free software; you can redistribute it and/or modify * 7 | * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | * as published by the Free Software Foundation; either version 2 of * 9 | * the License, or (at your option) any later version. * 10 | * for detail see the LICENCE text file. * 11 | * * 12 | * This program is distributed in the hope that it will be useful, * 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | * GNU Library General Public License for more details. * 16 | * * 17 | * You should have received a copy of the GNU Library General Public * 18 | * License along with this program; if not, write to the Free Software * 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | * USA * 21 | * * 22 | **************************************************************************/ 23 | 24 | """ 25 | SketchManager module based on nurbswb/sketchmanager.py from microelly2 26 | """ 27 | 28 | __title__ = "SketchManger.py" 29 | __author__ = "Joel Graff" 30 | __url__ = "https://www.freecadweb.org" 31 | 32 | import FreeCAD as App 33 | import FreeCADGui as Gui 34 | 35 | import Sketcher 36 | 37 | def copy_sketch(sketch, target_name): 38 | ''' 39 | kopiert sketch in sketchobjectpython 40 | ''' 41 | doc = App.ActiveDocument 42 | 43 | target = doc.addObject('Sketcher::SketchObjectPython', target_name) 44 | 45 | #_ViewProvider(target.ViewObject) 46 | 47 | for geo in sketch.Geometry: 48 | index=target.addGeometry(geo) 49 | target.setConstruction(index, geo.Construction) 50 | 51 | for constraint in sketch.Constraints: 52 | target.addConstraint(constraint) 53 | 54 | target.Placement = sketch.Placement 55 | 56 | target.solve() 57 | target.recompute() 58 | doc.recompute() 59 | 60 | return target 61 | 62 | def load_sketch(lib_path, source_name='Sketch', target_name=None): 63 | ''' 64 | load sketch from file into sketcher object with name. 65 | Repalces existing sketch if it exists. 66 | ''' 67 | 68 | doc = App.ActiveDocument 69 | library = App.open(lib_path) 70 | 71 | App.setActiveDocument(doc.Name) 72 | 73 | source = library.getObjectsByLabel(source_name)[0] 74 | 75 | if source is None: 76 | print("Cannot find library sketch " + source_name) 77 | return 78 | 79 | if target_name is None: 80 | target_name = source_name 81 | 82 | sketch = copy_sketch(source, target_name) 83 | 84 | sketch.Label = "Copy of " + source_name + "@" + lib_path 85 | 86 | App.closeDocument(library.Label) 87 | 88 | return sketch 89 | -------------------------------------------------------------------------------- /Resources/data/alignment/SugarGroveRd.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 605.2372 -706.1075 0.0 14 | 15 | 16 | 17 | 18 | 2455.0000 -676.4840 0.0 19 | 20 | 21 | 22 | 23 | 2437.9704 -2356.3977 0.0 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 493.6296 -707.5417 0.0 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 2470.7258 -662.4901 0.0 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Geometry/Line.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Line generation tools 26 | ''' 27 | 28 | from Geometry import Support 29 | 30 | def get_parameters(line): 31 | ''' 32 | Return a fully-defined line 33 | ''' 34 | 35 | _coord_truth = [not line.get('Start') is None, not line.get('End') is None] 36 | _param_truth = [not line.get('BearingIn') is None, not line.get('Length') is None] 37 | 38 | #both coordinates defined 39 | _case_one = all(_coord_truth) 40 | 41 | #only one coordinate defined, plus both length and bearing 42 | _case_two = any(_coord_truth) and not all(_coord_truth) and all(_param_truth) 43 | 44 | if _case_one: 45 | 46 | line_vec = line['End'].sub(line['Start']) 47 | _bearing = Support.get_bearing(line_vec) 48 | _length = line_vec.Length 49 | 50 | #test for missing parameters, preserving the existing ones 51 | if line.get('BearingIn'): 52 | if Support.within_tolerance(line['BearingIn'], _bearing): 53 | _bearing = line['BearingIn'] 54 | 55 | line['BearingIn'] = line['BearingOut'] = _bearing 56 | 57 | if line.get('Length'): 58 | if Support.within_tolerance(line['Length'], _length): 59 | _length = line['Length'] 60 | 61 | line['Length'] = _length 62 | 63 | elif _case_two: 64 | 65 | _vec = Support.vector_from_angle(line['BearingIn']).multiply(line['Length']) 66 | 67 | if line.get('Start'): 68 | line['End'] = line['Start'].add(_vec) 69 | else: 70 | line['Start'] = line['End'].add(_vec) 71 | 72 | else: 73 | print('Unable to calculate line parametters') 74 | 75 | result = None 76 | 77 | if _case_one or _case_two: 78 | result = {**{'Type': 'line'}, **line} 79 | 80 | return result 81 | -------------------------------------------------------------------------------- /transportationwb/vehicle/__init__.py: -------------------------------------------------------------------------------- 1 | '''simulation of traffic 2 | swept paths, traffic flow, traffic simulator etc. 3 | ''' 4 | 5 | import transportationwb 6 | reload (transportationwb) 7 | 8 | print "Transportation WB ",transportationwb.date," Version ", transportationwb.version 9 | 10 | print "vehicle version 0.0" 11 | 12 | 13 | 14 | def createVehicle(model='generic'): 15 | '''creates a car''' 16 | print "Not yet implemented" 17 | # return 1 18 | raise Exception ("Not yet implemented") 19 | 20 | 21 | 22 | def createTrailer(truck=None,model='generic'): 23 | '''creates a Trailer for a selected truck''' 24 | raise Exception ("Not yet implemented") 25 | 26 | 27 | 28 | 29 | 30 | #------------- 31 | 32 | 33 | import transportationwb 34 | import transportationwb.miki_g 35 | reload(transportationwb.miki_g) 36 | 37 | from transportationwb.miki_g import createMikiGui, Controller 38 | 39 | 40 | class VehicleController(Controller): 41 | 42 | 43 | def run(self): 44 | '''example button clicked method''' 45 | print "Button clicked" 46 | print self.root 47 | print self.root.widget 48 | 49 | 50 | def itemClicked(self, item): 51 | '''example for item clicked''' 52 | print item 53 | print item.text() 54 | 55 | 56 | 57 | 58 | def createVehicle(model='generic'): 59 | 60 | 61 | 62 | layout = '''#MainWindow: 63 | VerticalLayoutTab: 64 | 65 | QtGui.QLabel: 66 | setStyleSheet: "QWidget { font: bold 32px;color:brown;}" 67 | setText:"*** Configure Vehicle Demo ***" 68 | 69 | QtGui.QLabel: 70 | setText: "Select Vehicle from Database" 71 | QtGui.QComboBox: 72 | addItem: "Truck" 73 | addItem: "S-Bus" 74 | addItem: "Bicycle" 75 | itemClicked.connect: app.itemClicked 76 | 77 | HorizontalGroup: 78 | setTitle: "Picture of the vehicle" 79 | QtGui.QLabel: 80 | setPixmap: QtGui.QPixmap(FreeCAD.ConfigGet('UserAppData') + '/Mod/freecad-transportation-wb/icons/vehicle_01.png') 81 | HorizontalGroup: 82 | setTitle: "Main parameters" 83 | QtGui.QLabel: 84 | setText: "B" 85 | QtGui.QLineEdit: 86 | setText:"1800" 87 | QtGui.QLabel: 88 | setText: "A" 89 | QtGui.QLineEdit: 90 | setText:"4000" 91 | QtGui.QLabel: 92 | setText: "O" 93 | QtGui.QLineEdit: 94 | setText:"1800" 95 | 96 | QtGui.QLabel: 97 | setText: "C" 98 | QtGui.QLineEdit: 99 | setText:"1800" 100 | 101 | QtGui.QLabel: 102 | setText: "Width" 103 | QtGui.QLineEdit: 104 | setText:"2500" 105 | 106 | HorizontalGroup: 107 | setTitle: "Additional parameters" 108 | VerticalLayout: 109 | HorizontalLayout: 110 | QtGui.QLabel: 111 | setText: "R" 112 | QtGui.QLineEdit: 113 | setText:"1800" 114 | HorizontalLayout: 115 | QtGui.QLabel: 116 | setText: "P" 117 | QtGui.QLineEdit: 118 | setText:"1800" 119 | 120 | VerticalLayout: 121 | HorizontalLayout: 122 | QtGui.QLabel: 123 | setText: "F" 124 | QtGui.QLineEdit: 125 | setText:"1800" 126 | HorizontalLayout: 127 | QtGui.QLabel: 128 | setText: "E" 129 | QtGui.QLineEdit: 130 | setText:"1800" 131 | 132 | VerticalLayout: 133 | HorizontalLayout: 134 | QtGui.QPushButton: 135 | setText: "Run Action" 136 | clicked.connect: app.run 137 | setIcon: QtGui.QIcon('icons:freecad.svg') 138 | setIconSize: QtCore.QSize(20,20) 139 | QtGui.QPushButton: 140 | setText: "Close" 141 | clicked.connect: app.close 142 | setIcon: QtGui.QIcon('icons:edit_Cancel.svg') 143 | setIconSize: QtCore.QSize(20,20) 144 | 145 | setSpacer: 146 | ''' 147 | 148 | 149 | mikigui = createMikiGui(layout, VehicleController) 150 | return mikigui 151 | 152 | 153 | 154 | #-------------- 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /Corridor/loft/EditIntervals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import os 25 | import FreeCAD as App 26 | import FreeCADGui as Gui 27 | from Corridor.loft.tasks import IntervalTask 28 | 29 | class EditIntervals(): 30 | ''' 31 | EditIntervals starts the Edit Intervals task to adjust the section interval schedule for a loft. 32 | ''' 33 | def __init__(self): 34 | 35 | self.loft = None 36 | 37 | def GetResources(self): 38 | """ 39 | Icon resources. 40 | """ 41 | 42 | icon_path = os.path.dirname(os.path.abspath(__file__)) 43 | 44 | icon_path += "../../../icons/new_alignment.svg" 45 | 46 | return {'Pixmap' : icon_path, 47 | 'Accel' : '', 48 | 'MenuText': 'Edit Intervals...', 49 | 'ToolTip' : 'Add / remove section intervals along a loft.', 50 | 'CmdType' : 'ForEdit'} 51 | 52 | def update_callback(self, task): 53 | ''' 54 | Callback for return from task 55 | ''' 56 | 57 | print('updating with: ', task.get_model()) 58 | setattr(self.loft.Proxy.Object, 'Interval_Schedule', task.get_model()) 59 | 60 | App.ActiveDocument.recompute() 61 | 62 | def _validate_selection(self): 63 | ''' 64 | Validate the current selection as a single loft object 65 | ''' 66 | 67 | _obj = Gui.Selection.getSelection()[0] 68 | 69 | _is_valid = False 70 | 71 | try: 72 | _is_valid = _obj.Proxy.Type == '_ElementLoft' 73 | 74 | except: 75 | pass 76 | 77 | if _is_valid: 78 | return _obj 79 | 80 | return None 81 | 82 | def Activated(self): 83 | 84 | self.loft = self._validate_selection() 85 | 86 | if not self.loft: 87 | print('Invalid selection') 88 | return 89 | 90 | panel = IntervalTask.IntervalTask(self.update_callback) 91 | 92 | Gui.Control.showDialog(panel) 93 | 94 | panel.setup(self.loft.Proxy.Object.Interval_Schedule, ['Station', 'Interval', 'StationRaw']) 95 | 96 | Gui.addCommand('EditIntervals', EditIntervals()) 97 | -------------------------------------------------------------------------------- /Project/Support/Units.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2018 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Helper functions for scripted object properties 26 | ''' 27 | 28 | __title__ = "Properties.py" 29 | __author__ = "Joel Graff" 30 | __url__ = "https://www.freecadweb.org" 31 | 32 | import FreeCAD as App 33 | from Project.Support.Const import Const 34 | 35 | def get_doc_units(): 36 | ''' 37 | Return the units (feet / meters) of active document 38 | 39 | format - format of string (0 = abbreviated, 1 = singular, 2 = plural) 40 | ''' 41 | #need to add support for international spellings for metric units 42 | 43 | english = ['ft', 'foot', 'feet'] 44 | metric = ['m', 'meter', 'meters'] 45 | 46 | if App.ParamGet('User parameter:BaseApp/Preferences/Units').GetInt('UserSchema') == 7: 47 | return english 48 | 49 | return metric 50 | 51 | def is_metric_doc(): 52 | ''' 53 | Returns true if the passed document is using metric units 54 | ''' 55 | 56 | return 'm' in get_doc_units() 57 | 58 | def scale_factor(): 59 | ''' 60 | Return the scale factor to convert the document units to mm 61 | ''' 62 | 63 | if get_doc_units()[0] == 'ft': 64 | return 304.80 65 | 66 | return 1000.0 67 | 68 | class UnitNames(Const): 69 | ''' 70 | Unit names, including international variations 71 | ''' 72 | metric_units = [ 'mm', 'cm', 'dm', 'm', 'km', 73 | 'millimeter', 'millimeters', 'centimeter', 'centimeters', 74 | 'decimeter', 'decimeters', 'meter', 'meters', 'kilometer', 'kilometers', 75 | 'millimetre', 'millimetres', 'centimetre', 'centimetres', 76 | 'decimetre', 'decimetres', 'metre', 'metres', 'kilometre', 'kilometres' 77 | ] 78 | 79 | @staticmethod 80 | def IsMetric(unit_name): 81 | ''' 82 | Given a string token representing a system of units, 83 | return whether or not it is a metric system 84 | ''' 85 | 86 | return unit_name.lower() in UnitNames.metric_units 87 | 88 | class ToMetric(Const): 89 | ''' 90 | The ToMetric class returns constants which define the metric equivalent of 91 | specific non-metric unit lengths 92 | ''' 93 | 94 | Foot = 0.3048 95 | SurveyFoot = 12.0 / 39.37 96 | ClarkeFoot = 12 / 39.370432 97 | -------------------------------------------------------------------------------- /Resources/snippets/FeedbackSketcherUtils.py: -------------------------------------------------------------------------------- 1 | #************************************************************************** 2 | # * * 3 | # * Copyright (c) 2017 Joel Graff * 4 | # * * 5 | # * This program is free software; you can redistribute it and/or modify * 6 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 7 | # * as published by the Free Software Foundation; either version 2 of * 8 | # * the License, or (at your option) any later version. * 9 | # * for detail see the LICENCE text file. * 10 | # * * 11 | # * This program is distributed in the hope that it will be useful, * 12 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | # * GNU Library General Public License for more details. * 15 | # * * 16 | # * You should have received a copy of the GNU Library General Public * 17 | # * License along with this program; if not, write to the Free Software * 18 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 19 | # * USA * 20 | # * * 21 | # ************************************************************************** 22 | 23 | # FeedbackSketcherUtils - A series of utilities to augment the feedback 24 | # sketcher class 25 | 26 | 27 | import feedbacksketch 28 | import FreeCAD 29 | 30 | def _addGroup (fbs,grpname): 31 | 32 | #construct the property group for the supplied property 33 | 34 | if hasattr (fbs,'active'+grpname): 35 | return 36 | 37 | fbs.addProperty ("App::PropertyBool",'active'+grpname, grpname, ) 38 | fbs.addProperty ("App::PropertyLink",'base'+grpname, grpname, ) 39 | fbs.addProperty ("App::PropertyStringList",'get'+grpname, grpname, ) 40 | fbs.addProperty ("App::PropertyStringList",'set'+grpname, grpname, ) 41 | fbs.addProperty ("App::PropertyStringList",'seton'+grpname, grpname, ) 42 | fbs.addProperty ("App::PropertyStringList",'setoff'+grpname, grpname, ) 43 | 44 | 45 | def _buildFbs (name): 46 | 47 | #construct feedback sketch 48 | 49 | fbs = FreeCAD.ActiveDocument.addObject ("Sketcher::SketchObjectPython",name) 50 | 51 | feedbacksketch.FeedbackSketch (fbs) 52 | 53 | return fbs 54 | 55 | 56 | def _addProperties (fbs, clients): 57 | 58 | #add base and client properties to feedback sketch 59 | 60 | fbs.addProperty ("App::PropertyBool",'active', 'Base', ) 61 | fbs.addProperty ("App::PropertyStringList",'bases', 'Base', ) 62 | 63 | fbs.bases = clients 64 | 65 | for b in fbs.bases: 66 | _addGroup (fbs,b) 67 | setattr(fbs, "active"+b, True) 68 | 69 | return fbs 70 | 71 | def _createClients (clientList): 72 | 73 | clients=[] 74 | 75 | for client in clientList: 76 | clients.append (FreeCAD.ActiveDocument.addObject ( 77 | "Sketcher::SketchObjectPython", client)) 78 | 79 | return clients 80 | 81 | def _assignClients (fbs, clients): 82 | 83 | for client in clients: 84 | setattr (fbs, "base"+client.Label, client) 85 | 86 | def buildFeedbackSketch (sketchName, clientList): 87 | 88 | #construct a feedback sketch using the supplied sketch name 89 | #with clients in the supplied client list 90 | 91 | fbs = _buildFbs (sketchName) 92 | fbs = _addProperties (fbs, clientList) 93 | clients = _createClients (clientList) 94 | _assignClients (fbs, clients) 95 | 96 | result = [fbs] 97 | result.extend (clients) 98 | 99 | return result 100 | -------------------------------------------------------------------------------- /Project/Tasks/alignment/ImportAlignmentViewDelegate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | QItemStyledDelegate class for Task QTableView 26 | ''' 27 | 28 | from PySide import QtGui, QtCore 29 | 30 | class ImportAlignmentViewDelegate(QtGui.QStyledItemDelegate): 31 | 32 | def __init__(self, model, parent=None): 33 | 34 | QtGui.QStyledItemDelegate.__init__(self, parent) 35 | self._is_editing = False 36 | self.combo_box = None 37 | self.model = [''] 38 | self.model.extend(model) 39 | 40 | def paint(self, painter, option, index): 41 | 42 | painter.save() 43 | 44 | painter.setPen(QtGui.QPen(QtCore.Qt.NoPen)) 45 | painter.setBrush(QtGui.QColor(215, 215, 215)) 46 | painter.drawRect(option.rect) 47 | 48 | if index.isValid(): 49 | painter.setPen(QtGui.QPen(QtGui.QColor(128, 128, 128))) 50 | value = index.data(QtCore.Qt.DisplayRole) 51 | painter.drawText(option.rect, QtCore.Qt.AlignCenter, value) 52 | 53 | painter.restore() 54 | 55 | def createEditor(self, parent, option, index): 56 | 57 | if index.row() > 0: 58 | return super(ImportAlignmentViewDelegate, self).createEditor(parent, option, index) 59 | 60 | self.combo_box = QtGui.QComboBox(parent) 61 | 62 | self.combo_box.addItems(self.model) 63 | 64 | value = index.data(QtCore.Qt.DisplayRole) 65 | 66 | self.combo_box.setCurrentIndex(self.combo_box.findText(value)) 67 | 68 | return self.combo_box 69 | 70 | def setEditorData(self, editor, index): 71 | 72 | self._is_editing = True 73 | 74 | value = index.data(QtCore.Qt.EditRole) or index.data(QtCore.Qt.DisplayRole) 75 | 76 | editor_class = editor.metaObject().className() 77 | 78 | if editor_class == 'QComboBox': 79 | return 80 | 81 | if editor_class in ['QSpinBox', 'QDoubleSpinBox']: 82 | editor.setValue(value) 83 | elif editor_class == 'QComboBox': 84 | editor.setCurrentIndex() 85 | else: 86 | editor.setText(value) 87 | 88 | def setModelData(self, editor, model, index): 89 | 90 | QtGui.QStyledItemDelegate(self).setModelData(editor, model, index) 91 | #super(ImportAlignmentViewDelegate, self).setModelData(editor, model, index) 92 | 93 | self._is_editing = False 94 | 95 | def isEditing(self): 96 | 97 | return self._is_editing 98 | -------------------------------------------------------------------------------- /Resources/data/alignment/HC_IL2_Rev4.csv: -------------------------------------------------------------------------------- 1 | 87.648,rad/dist,dir,deg,m,sec,deg_dec,BEARING,curve_tan,curve length,tangent,NORTHING,EASTING,RADIUS,DISTANCE,ID,PARENT_ID,BACK,FORWARD 2 | ,Alignment datum,,,,,,,,,,0,0,,,IL 2,,,113050 3 | ,Station equation,,,,,,,,,,,,,,,,148292.43,0 4 | 114383.75,1008.7,L,48,48,34,48.80944444,87.64805556,457.6668591,859.2969155,,,,1008.7,1791.416859,,,, 5 | 115243.04,2000,L,8,55,27,8.924166667,38.83861111,156.0717435,311.5121827,0,,,2000,613.7386026,,,, 6 | 116456.78,3800,L,40,40,25,40.67361111,29.91444444,1408.438397,2697.576044,902.2217005,,,3800,2466.731841,,,, 7 | 119410.29,2300,R,10,6,26,10.10722222,349.2408333,203.3926648,405.7299037,255.9336002,,,2300,1867.764662,,,, 8 | 120017.7,4000,R,6,49,16,6.821111111,359.3480556,238.3833142,476.2033901,201.6766,,,4000,643.452579,,,35242.43, 9 | 120493.91,2000,R,18,15,47,18.26305556,6.169166667,321.4769591,637.500902,1.00E-07,,,2000,559.8602734,,,20742.76, 10 | 121385.05,1585.53,L,29,20,28,29.34111111,24.43222222,415.0852392,811.9483196,253.6517,,,1585.53,990.2138983,,,55985.19, 11 | 122536.31,3600,R,17,53,2,17.88388889,355.0911111,566.4453399,1123.677879,339.3078999,,,3600,1320.838479,,,55985.184, 12 | 124488.52,6000,R,13,53,58,13.89944444,12.97499999,731.3634865,1455.546418,828.5185996,,,6000,2126.327426,,,0.006, 13 | 125944.07,3500,R,9,20,52,9.347777778,26.87444444,286.1466219,571.0232499,-4.00E-07,,,3500,1017.510108,,,1.07E-07, 14 | 129512.97,4000,R,4,58,58,4.982777778,36.22222222,174.0414606,347.8635125,2996.8808,,,4000,3458.064833,,,1.07E-05, 15 | 129952.83,10000,L,2,51,52,2.864444444,41.205,250.0220115,499.9398679,92,,,10000,516.0634721,,,, 16 | 131070.39,2000,R,19,52,59,19.88305556,38.34055556,350.5498146,694.0495698,617.5968999,,,2000,1218.168726,,,, 17 | 132520.88,3814.39,R,14,5,57,14.09916667,58.22361111,471.6992677,938.633191,756.4393997,,,3814.39,1578.688482,,,, 18 | 133459.51,2257.87,R,34,33,42,34.56166667,72.32277778,702.4197007,1361.980777,-4.00E-07,,,2257.87,1174.118968,,,, 19 | 135030.2,2815,L,37,50,9,37.83583333,106.8844444,964.7734753,1858.913025,208.71,,,2815,1875.903176,,,, 20 | 138885.55,1520,L,36,50,29,36.84138889,69.04861111,506.2465299,977.3653764,1996.44,,,1520,3467.460005,,,, 21 | 140062.91,2100,R,11,28,22,11.47277778,32.20722222,210.9549614,420.4992679,200,,,2100,917.2014913,,,, 22 | 142178.09,2500,R,39,59,0,39.98333333,43.68,909.5138281,1744.602031,1694.6828,,,2500,2815.151589,,,, 23 | 144158.68,3375,L,19,31,20,19.52222222,83.66333333,580.6049112,1149.953811,235.9999997,,,3375,1726.118739,,,, 24 | 146689.32,2293.84,R,16,32,54,16.54833333,64.14111111,333.5788858,662.5135263,1380.6838,,,2293.84,2294.867597,,,, 25 | 2335.46,6000,R,7,25,36,7.426666667,80.68944444,389.4047185,777.7187147,3276.0615,,,6000,3999.045104,,,, 26 | 3539.45,1800,L,10,11,1,10.18361111,88.11611111,160.386237,319.9275785,226.2769,,,1800,776.0678555,,,, 27 | 3973.19,1800,L,15,26,54,15.44833333,77.9325,244.1426895,485.323705,313.8131,,,1800,718.3420265,,,, 28 | 4458.51,20000,R,1,27,7,1.451944444,62.48416667,253.4256732,506.8242221,0,,,20000,497.5683627,,,, 29 | 5783.34,2883.78,L,13,30,49,13.51361111,63.93611111,341.6651922,680.1597217,817.9799996,,,2883.78,1413.070865,,,, 30 | 6868.92,3375,R,34,24,43,34.41194444,50.4225,1045.122534,2027.030847,405.4091008,,,3375,1792.196827,,,, 31 | 10325.67,3687.8,L,17,55,7,17.91861111,84.83444444,581.4054671,1153.318004,1429.7284,,,3687.8,3056.256401,,,, 32 | 12214.42,3400,L,33,13,38,33.22722222,66.91583333,1014.463565,1971.743059,733.4434999,,,3400,2329.312532,,,, 33 | 14795.06,4656.43,R,4,34,58,4.582777778,33.68861111,186.3205982,372.4425099,610.8899998,,,4656.43,1811.674163,,,, 34 | 15453.37,2855.9299,L,11,40,12,11.67,38.27138889,291.8574785,581.6955841,285.8635999,,,2855.9299,764.0416766,,,, 35 | 16349.07,4000,R,18,43,14,18.72055556,26.60138889,659.3468965,1306.941329,314,,,4000,1265.204375,,,, 36 | 17656.01,7200,R,8,33,31,8.558611111,45.32194444,538.7555474,1075.506792,1.00E-07,,,7200,1198.102444,,,, 37 | 18991.96,4149.52,L,11,30,23,11.50638889,53.88055555,418.0683907,833.3247446,260.4610999,,,4149.52,1217.285038,,,, 38 | 20119.36,950,R,24,16,10,24.26944444,42.37416667,204.2646071,402.4026275,294.0792001,,,950,916.4121979,,,, 39 | 20521.76,221,NE,66,38,37,66.64361111,66.64361111,,,221,,,0,425.2646071,,,, 40 | -------------------------------------------------------------------------------- /Project/Support/Properties.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2018 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Helper functions for scripted object properties 26 | ''' 27 | 28 | __title__ = "Properties.py" 29 | __author__ = "Joel Graff" 30 | __url__ = "https://www.freecadweb.org" 31 | 32 | import FreeCAD as App 33 | 34 | def add(obj, p_type, name, desc, default_value=None, is_read_only=False, is_hidden=False): 35 | 36 | ''' 37 | Build FPO properties 38 | 39 | p_type The property type, either formal ('App::PropertyFloat') or shortended ('Float') 40 | name The Property name as "GROUP.NAME" If group is omitted, the default is 'Base' 41 | desc Tooltip description string 42 | default_value Default property value 43 | isReadOnly Boolean property (read-only = True) 44 | ''' 45 | 46 | tple = name.split('.') 47 | 48 | p_name = tple[0] 49 | p_group = 'Base' 50 | 51 | if len(tple) == 2: 52 | p_name = tple[1] 53 | p_group = tple[0] 54 | 55 | if p_type in [ 56 | 'Length', 57 | 'Distance', 58 | 'Percent', 59 | 'Angle', 60 | 'Link', 61 | 'LinkList', 62 | 'Float', 63 | 'FloatList', 64 | 'Bool', 65 | 'String', 66 | 'StringList', 67 | 'Vector', 68 | 'VectorList', 69 | 'Integer', 70 | 'Enumeration', 71 | 'FileIncluded' 72 | ]: 73 | p_type = 'App::Property' + p_type 74 | 75 | else: 76 | print('Invalid property type specified: ', p_type) 77 | return None 78 | 79 | p_name = p_name.replace(' ', '_') 80 | 81 | obj.addProperty(p_type, p_name, p_group, desc) 82 | 83 | App.ActiveDocument.recompute() 84 | 85 | prop = obj.getPropertyByName(p_name) 86 | 87 | 88 | if p_type in [ 89 | 'App::PropertyBool', 90 | 'App::PropertyPercent', 91 | 'App::PropertyLinkList', 92 | 'App::PropertyLink', 93 | 'App::PropertyString', 94 | 'App::PropertyStringList', 95 | 'App::PropertyFloatList', 96 | 'App::PropertyVector', 97 | 'App::PropertyVectorList', 98 | 'App::PropertyInteger', 99 | 'App::PropertyEnumeration', 100 | 'App::PropertyFloat', 101 | 'App::PropertyFileIncluded' 102 | 103 | ]: 104 | setattr(obj, p_name, default_value) 105 | 106 | else: 107 | prop.Value = default_value 108 | 109 | editor_mode = 0 110 | 111 | if is_read_only: 112 | editor_mode += 1 113 | 114 | if is_hidden: 115 | editor_mode += 2 116 | 117 | obj.setEditorMode(p_name, editor_mode) 118 | 119 | return prop 120 | -------------------------------------------------------------------------------- /Corridor/template/ViewTemplateLibrary.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import Draft 25 | import Part 26 | import os 27 | import time 28 | import FreeCAD as App 29 | import FreeCADGui as Gui 30 | from Corridor.template import TemplateLibrary, SketchTemplate 31 | 32 | class ViewTemplateLibrary(): 33 | ''' 34 | View library class. 35 | Open the template library for selecting templates 36 | ''' 37 | def __init__(self): 38 | pass 39 | 40 | def GetResources(self): 41 | """ 42 | Icon resources. 43 | """ 44 | 45 | icon_path = os.path.dirname(os.path.abspath(__file__)) 46 | 47 | icon_path += "../../../icons/new_alignment.svg" 48 | 49 | return {'Pixmap' : icon_path, 50 | 'Accel' : "Ctrl+Alt+G", 51 | 'MenuText': "Open Template Library", 52 | 'ToolTip' : "Open the template library", 53 | 'CmdType' : "ForEdit"} 54 | 55 | def _validate_tree(self): 56 | ''' 57 | Validate the tree of the active document, ensuring there is a templates folder 58 | ''' 59 | 60 | result = App.ActiveDocument.findObjects('App::DocumentObjectGroup', 'Templates') 61 | 62 | if not result: 63 | result = [App.ActiveDocument.addObject('App::DocumentObjectGroup', 'Templates')] 64 | 65 | return result[0] 66 | 67 | def _library_call_back(self, path): 68 | ''' 69 | Library Callback 70 | Merge the selected template into the project and add it to the templates group 71 | ''' 72 | 73 | #create a snapshot of the tree root where new objects will be merged 74 | snapshot = [] 75 | 76 | for obj in App.ActiveDocument.RootObjects: 77 | snapshot += obj.Name 78 | 79 | #merge new objects 80 | App.ActiveDocument.mergeProject(path) 81 | 82 | #validate the template folder structure 83 | folder = self._validate_tree() 84 | 85 | #iterate the root objects, looking for objects not in the snapshot 86 | #relocate the skether objects 87 | new_objects = [] 88 | 89 | for obj in App.ActiveDocument.RootObjects: 90 | 91 | if not obj.Name in snapshot: 92 | if obj.TypeId != 'Sketcher::SketchObject': 93 | continue 94 | 95 | new_objects.append(obj.Name) 96 | _o = SketchTemplate.create(obj, obj.Label) 97 | folder.addObject(_o.Object) 98 | 99 | #remove the root objects... 100 | for obj_name in new_objects: 101 | App.ActiveDocument.removeObject(obj_name) 102 | 103 | def Activated(self): 104 | 105 | TemplateLibrary.show(self._library_call_back) 106 | 107 | 108 | Gui.addCommand('ViewTemplateLibrary', ViewTemplateLibrary()) -------------------------------------------------------------------------------- /Resources/snippets/fpo_template.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | # ************************************************************************** 4 | # * * 5 | # * Copyright (c) 20XX AUTHOR_NAME * 6 | # * * 7 | # * This program is free software; you can redistribute it and/or modify * 8 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 9 | # * as published by the Free Software Foundation; either version 2 of * 10 | # * the License, or (at your option) any later version. * 11 | # * for detail see the LICENCE text file. * 12 | # * * 13 | # * This program is distributed in the hope that it will be useful, * 14 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | # * GNU Library General Public License for more details. * 17 | # * * 18 | # * You should have received a copy of the GNU Library General Public * 19 | # * License along with this program; if not, write to the Free Software * 20 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 21 | # * USA * 22 | # * * 23 | # ************************************************************************** 24 | 25 | ''' 26 | DESCRIPTION 27 | ''' 28 | import FreeCAD as App 29 | import Part 30 | from transportationwb import ScriptedObjectSupport as Sos 31 | 32 | _CLASS_NAME = 'CLASSNAME' 33 | _TYPE = 'Part::FeaturePython' 34 | 35 | __title__ = _CLASS_NAME + '.py' 36 | __author__ = "AUTHOR_NAME" 37 | __url__ = "https://www.freecadweb.org" 38 | 39 | def createTEMPLATE_CLASS(object_name='', parent=None): 40 | ''' 41 | Class construction method 42 | object_name - Optional. Name of new object. Defaults to class name. 43 | parent - Optional. Reference to existing DocumentObjectGroup. Defaults to ActiveDocument 44 | ''' 45 | 46 | _obj = None 47 | _name = _CLASS_NAME 48 | 49 | if object_name: 50 | _name = object_name 51 | 52 | if parent: 53 | _obj = parent.newObject(_TYPE, _name) 54 | else: 55 | _obj = App.ActiveDocument.addObject(_TYPE, _name) 56 | 57 | result = _CLASS_TEMPLATE(_obj) 58 | _ViewProviderCLASS_TEMPLATE(_obj.ViewObject) 59 | 60 | return result 61 | 62 | class _TEMPLATE_CLASS(): 63 | 64 | def __init__(self, obj): 65 | ''' 66 | Main class intialization 67 | ''' 68 | 69 | self.Enabled = False 70 | self.Type = "_" + CLASS_NAME 71 | self.Object = obj 72 | 73 | obj.Proxy = self 74 | 75 | #add class properties 76 | #Sos._add_property(self, 'PROPERTY_TYPE', 'PROPERTY_NAME', 'PROPERTY_DESC', DEFUALT_VALUE, isReadOnly=False, isHidden=False) 77 | 78 | self.Enabled = True 79 | 80 | def __getstate__(self): 81 | ''' 82 | State method for serialization 83 | ''' 84 | return self.Type 85 | 86 | def __setstate__(self, state): 87 | ''' 88 | State method for serialization 89 | ''' 90 | if state: 91 | self.Type = state 92 | 93 | def execute(self, obj): 94 | ''' 95 | Class execute for recompute calls 96 | ''' 97 | pass 98 | 99 | class _ViewProviderTEMPLATE_CLASS(object): 100 | 101 | def __init__(self, vobj): 102 | ''' 103 | View Provider initialization 104 | ''' 105 | self.Object = vobj.Object 106 | 107 | def getIcon(self): 108 | ''' 109 | Object icon 110 | ''' 111 | return '' 112 | 113 | def attach(self, vobj): 114 | ''' 115 | View Provider Scene subgraph 116 | ''' 117 | pass 118 | 119 | def __getstate__(self): 120 | ''' 121 | State method for serialization 122 | ''' 123 | return None 124 | 125 | def __setstate__(self,state): 126 | 127 | ''' 128 | State method for serialization 129 | ''' 130 | return None 131 | -------------------------------------------------------------------------------- /Resources/snippets/python_templates/fpo_template.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | # ************************************************************************** 4 | # * * 5 | # * Copyright (c) 20XX AUTHOR_NAME * 6 | # * * 7 | # * This program is free software; you can redistribute it and/or modify * 8 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 9 | # * as published by the Free Software Foundation; either version 2 of * 10 | # * the License, or (at your option) any later version. * 11 | # * for detail see the LICENCE text file. * 12 | # * * 13 | # * This program is distributed in the hope that it will be useful, * 14 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | # * GNU Library General Public License for more details. * 17 | # * * 18 | # * You should have received a copy of the GNU Library General Public * 19 | # * License along with this program; if not, write to the Free Software * 20 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 21 | # * USA * 22 | # * * 23 | # ************************************************************************** 24 | 25 | ''' 26 | DESCRIPTION 27 | ''' 28 | import FreeCAD as App 29 | import Part 30 | from transportationwb import ScriptedObjectSupport as Sos 31 | 32 | _CLASS_NAME = 'CLASSNAME' 33 | _TYPE = 'Part::FeaturePython' 34 | 35 | __title__ = _CLASS_NAME + '.py' 36 | __author__ = "AUTHOR_NAME" 37 | __url__ = "https://www.freecadweb.org" 38 | 39 | def createTEMPLATE_CLASS(object_name='', parent=None): 40 | ''' 41 | Class construction method 42 | object_name - Optional. Name of new object. Defaults to class name. 43 | parent - Optional. Reference to existing DocumentObjectGroup. Defaults to ActiveDocument 44 | ''' 45 | 46 | _obj = None 47 | _name = _CLASS_NAME 48 | 49 | if object_name: 50 | _name = object_name 51 | 52 | if parent: 53 | _obj = parent.newObject(_TYPE, _name) 54 | else: 55 | _obj = App.ActiveDocument.addObject(_TYPE, _name) 56 | 57 | result = _CLASS_TEMPLATE(_obj) 58 | _ViewProviderCLASS_TEMPLATE(_obj.ViewObject) 59 | 60 | return result 61 | 62 | class _TEMPLATE_CLASS(): 63 | 64 | def __init__(self, obj): 65 | ''' 66 | Main class intialization 67 | ''' 68 | 69 | self.Enabled = False 70 | self.Type = "_" + CLASS_NAME 71 | self.Object = obj 72 | 73 | obj.Proxy = self 74 | 75 | #add class properties 76 | #Sos._add_property(self, 'PROPERTY_TYPE', 'PROPERTY_NAME', 'PROPERTY_DESC', DEFUALT_VALUE, isReadOnly=False, isHidden=False) 77 | 78 | self.Enabled = True 79 | 80 | def __getstate__(self): 81 | ''' 82 | State method for serialization 83 | ''' 84 | return self.Type 85 | 86 | def __setstate__(self, state): 87 | ''' 88 | State method for serialization 89 | ''' 90 | if state: 91 | self.Type = state 92 | 93 | def execute(self, obj): 94 | ''' 95 | Class execute for recompute calls 96 | ''' 97 | pass 98 | 99 | class _ViewProviderTEMPLATE_CLASS(object): 100 | 101 | def __init__(self, vobj): 102 | ''' 103 | View Provider initialization 104 | ''' 105 | self.Object = vobj.Object 106 | 107 | def getIcon(self): 108 | ''' 109 | Object icon 110 | ''' 111 | return '' 112 | 113 | def attach(self, vobj): 114 | ''' 115 | View Provider Scene subgraph 116 | ''' 117 | pass 118 | 119 | def __getstate__(self): 120 | ''' 121 | State method for serialization 122 | ''' 123 | return None 124 | 125 | def __setstate__(self,state): 126 | 127 | ''' 128 | State method for serialization 129 | ''' 130 | return None 131 | -------------------------------------------------------------------------------- /Project/Support/DocumentProperties.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2018 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Module to manage document properties for the transportation workbench 26 | ''' 27 | 28 | __title__ = "DocumentProperties.py" 29 | __author__ = "Joel Graff" 30 | __url__ = "https://www.freecadweb.org" 31 | 32 | import FreeCAD as App 33 | 34 | class DocumentProperty: 35 | 36 | Param = lambda _x: App.ParamGet('User parameter:BaseApp/Preferences/' + _x) 37 | 38 | @staticmethod 39 | def _set_string(path, key, value): 40 | DocumentProperty.Param(path).SetString(key, value) 41 | 42 | @staticmethod 43 | def _get_string(path, key, default=''): 44 | return DocumentProperty.Param(path).GetString(key, default) 45 | 46 | @staticmethod 47 | def _set_int(path, key, value): 48 | DocumentProperty.Param(path).SetInt(key, value) 49 | 50 | @staticmethod 51 | def _get_int(path, key, value=0): 52 | return DocumentProperty.Param(path).GetInt(key, value) 53 | 54 | @staticmethod 55 | def _set_float(path, key, value): 56 | DocumentProperty.Param(path).SetFloat(key, value) 57 | 58 | @staticmethod 59 | def _get_float(path, key, value=0.0): 60 | return DocumentProperty.Param(path).GetFloat(key, value) 61 | 62 | @staticmethod 63 | def _set_bool_string(value): 64 | result = '0' 65 | 66 | if value: 67 | result = '1' 68 | 69 | return result 70 | 71 | class DocumentPreferences(): 72 | 73 | class SaveThumbnail(): 74 | 75 | @staticmethod 76 | def set_value(value = True): 77 | DocumentProperty._set_string('Document', 'SaveThumbnail', DocumentProperty._set_bool_string(value)) 78 | 79 | @staticmethod 80 | def get_value(): 81 | return DocumentProperty._get_string('Document', 'SaveThmbnail') 82 | 83 | class AddThumbnailLogo(): 84 | 85 | @staticmethod 86 | def set_value(value = False): 87 | DocumentProperty._set_string('Document', 'AddThumbnailLogo', DocumentProperty._set_bool_string(value)) 88 | 89 | @staticmethod 90 | def get_value(): 91 | return DocumentProperty._get_string('Document', 'AddThumbnailLogo') 92 | 93 | 94 | class TemplateLibrary(): 95 | 96 | class Path(): 97 | 98 | @staticmethod 99 | def get_value(): 100 | return DocumentProperty._get_string('Mod/Transportation', 'TemplateLibPath') 101 | 102 | @staticmethod 103 | def set_value(value): 104 | DocumentProperty._set_string('Mod/Transportation', 'TemplateLibPath', value) 105 | 106 | class Policy(): 107 | 108 | class MinimumTangentLength(): 109 | 110 | @staticmethod 111 | def get_value(): 112 | return DocumentProperty._get_float('Mod/Transportation', 'MinimumTangentLength', 500.0) 113 | 114 | @staticmethod 115 | def set_value(value): 116 | DocumentProperty._set_float('Mod/Trasnportation', 'MinimumTangentLength', value) 117 | -------------------------------------------------------------------------------- /Project/Support/WidgetModel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | QAbstractTableModel class for Task QTableView 26 | ''' 27 | 28 | import re 29 | import operator 30 | 31 | from PySide import QtCore 32 | 33 | def create(data, headers=None, parent=None): 34 | ''' 35 | Creation method for widget models. 36 | 37 | Model is a dictionary or nested list providing the data set in a tabular format 38 | ''' 39 | 40 | model = WidgetModel(parent) 41 | 42 | model.data_model = data 43 | model.headers = headers 44 | 45 | return model 46 | 47 | class WidgetModel(QtCore.QAbstractTableModel): 48 | 49 | def __init__(self, parent=None): 50 | 51 | QtCore.QAbstractTableModel.__init__(self) 52 | 53 | self.data_model = None 54 | self.headers = None 55 | 56 | self.default_flags = QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable 57 | self.data_roles = [QtCore.Qt.DisplayRole, QtCore.Qt.EditRole] 58 | 59 | def rowCount(self, parent=QtCore.QModelIndex()): 60 | ''' 61 | Number of rows currently in the model 62 | ''' 63 | 64 | if self.data_model: 65 | return len(self.data_model) 66 | 67 | return 0 68 | 69 | def columnCount(self, parent=QtCore.QModelIndex()): 70 | ''' 71 | Number of columns currently in the model 72 | ''' 73 | if self.data_model: 74 | if isinstance(self.data_model[0], list): 75 | return len(self.data_model[0]) 76 | 77 | return 1 78 | 79 | return 0 80 | 81 | def data(self, index, role): 82 | ''' 83 | Return data for valid indices and display role 84 | ''' 85 | 86 | if not index.isValid(): 87 | return None 88 | 89 | if not role in [QtCore.Qt.DisplayRole, QtCore.Qt.EditRole]: 90 | return None 91 | 92 | if not 0 <= index.row() < len(self.data_model): 93 | return None 94 | 95 | if self.columnCount(index) == 1: 96 | return self.data_model[index.row()] 97 | 98 | return self.data_model[index.row()][index.column()] 99 | 100 | def setData(self, index, value, role): 101 | ''' 102 | Update existing model data 103 | ''' 104 | 105 | if role != QtCore.Qt.EditRole: 106 | return False 107 | 108 | self.data_model[index.row()][index.column()] = value 109 | self.dataChanged.emit(index, index) 110 | 111 | return True 112 | 113 | def headerData(self, col, orientation, role): 114 | ''' 115 | Headers to be displated 116 | ''' 117 | 118 | if not self.headers: 119 | return None 120 | 121 | if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: 122 | return self.headers[col] 123 | 124 | return None 125 | 126 | def flags(self, index): 127 | 128 | if index.row() > 0: 129 | return self.default_flags & ~QtCore.Qt.ItemIsEditable 130 | 131 | return self.default_flags 132 | 133 | -------------------------------------------------------------------------------- /Project/Commands/NewProjectCmd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2019 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import os 25 | 26 | import FreeCAD as App 27 | import FreeCADGui as Gui 28 | 29 | from PySide import QtGui, QtCore 30 | 31 | from Project.Support import DocumentProperties 32 | 33 | import Resources 34 | import Corridor 35 | 36 | class NewProject(): 37 | 38 | icon_path = os.path.dirname(Resources.__file__) 39 | 40 | resources = { 41 | 'Pixmap' : icon_path + '/icons/workbench.svg', 42 | 'Accel' : "Shift+N", 43 | 'MenuText': "New Project", 44 | 'ToolTip' : "Create a new project document and make it active", 45 | 'CmdType' : "ForEdit" 46 | } 47 | 48 | def GetResources(self): 49 | return self.resources 50 | 51 | def Activated(self): 52 | 53 | if Gui.ActiveDocument == None: 54 | self._create_document() 55 | 56 | self._set_preferences() 57 | return 58 | 59 | def IsActive(self): 60 | return True 61 | 62 | def _attach_handlers(self): 63 | Gui.ActiveDocument.ActiveView.addDraggable 64 | 65 | def _set_preferences(self): 66 | App.ParamGet("User parameter:BaseApp/Preferences/Units").SetInt("UserSchema", 7) 67 | App.ParamGet('User parameter:BaseApp/Preferences/Mod/Sketcher').SetBool('AutoRecompute', False) 68 | App.ParamGet('User parameter:BaseApp/Preferences/Document').SetBool('DuplicateLabels', True) 69 | 70 | template_path = os.path.dirname(Corridor.__file__) 71 | 72 | DocumentProperties.TemplateLibrary.Path.set_value(template_path + '/Templates') 73 | 74 | def _create_document(self): 75 | 76 | ''' 77 | Create a new project with default groups 78 | ''' 79 | 80 | #new project dialog 81 | dlg = QtGui.QInputDialog() 82 | dlg.setWindowTitle("New Proejct") 83 | dlg.setLabelText('Enter project name:') 84 | dlg.setWindowModality(QtCore.Qt.ApplicationModal) 85 | dlg.setTextValue('New Project') 86 | dlg.exec_() 87 | 88 | if dlg.result() == False: 89 | return 90 | 91 | #assign project name, null names not accepted. 92 | project_name = dlg.textValue() 93 | 94 | if project_name =='': 95 | return 96 | 97 | App.newDocument(project_name) 98 | 99 | #substitute underscores for spaces for internal naming 100 | project_name = project_name.replace(' ', '_') 101 | 102 | #set up intial references 103 | App.setActiveDocument(project_name) 104 | 105 | App.ActiveDocument = App.getDocument(project_name) 106 | Gui.ActiveDocument = Gui.getDocument(project_name) 107 | 108 | #create default groups 109 | App.ActiveDocument.addObject('App::DocumentObjectGroup', 'Templates') 110 | App.ActiveDocument.addObject('App::DocumentObjectGroup', 'Alignments') 111 | App.ActiveDocument.addObject('App::DocumentObjectGroup', 'Element Lofts') 112 | 113 | #create observers to handle tasks when document-level events occur 114 | #Observer.create(App.ActiveDocument) 115 | 116 | Gui.addCommand('NewProject', NewProject()) 117 | -------------------------------------------------------------------------------- /Resources/data/alignment/complete_demo.csv: -------------------------------------------------------------------------------- 1 | BEARING,NORTHING,EASTING,RADIUS,DISTANCE,ID,PARENT_ID,BACK,FORWARD 2 | ,0,0,,,Mainline,,,113050 3 | ,,,,,,,148292.43,0 4 | ,,,0,0,,,, 5 | 87.64805556,,,1008.7,1791.416859,,,, 6 | 38.83861111,,,2000,613.7386026,,,, 7 | 29.91444444,,,3800,2466.731841,,,, 8 | 349.2408333,,,2300,1867.764662,,,, 9 | 359.3480556,,,4000,643.452579,,,, 10 | 6.169166667,,,2000,559.8602734,,,, 11 | 24.43222222,,,1585.53,990.2138983,,,, 12 | 355.0911111,,,3600,1320.838479,,,, 13 | 12.97499999,,,6000,2126.327426,,,, 14 | 26.87444444,,,3500,1017.510108,,,, 15 | 36.22222222,,,4000,3457.068882,,,, 16 | 41.205,,,10000,516.0634721,,,, 17 | 38.34055556,,,2000,1218.168726,,,, 18 | 58.22361111,,,3814.39,1578.688482,,,, 19 | 72.32277778,,,2257.87,1174.118968,,,, 20 | 106.8844444,,,2815,1875.903176,,,, 21 | 69.04861111,,,1520,3467.460005,,,, 22 | 36.84138889,,,2100,917.2014913,,,, 23 | 43.68,,,2500,2815.151589,,,, 24 | 83.66333333,,,3375,1726.118739,,,, 25 | 64.14111111,,,2293.84,2294.867597,,,, 26 | 80.68944444,,,6000,3999.045104,,,, 27 | 88.11611111,,,1800,776.0678555,,,, 28 | 77.9325,,,1800,718.3420265,,,, 29 | 62.48416667,,,20000,497.5683627,,,, 30 | 63.93611111,,,2883.78,1413.070865,,,, 31 | 50.4225,,,3375,1792.196827,,,, 32 | 84.83444444,,,3687.8,3056.256401,,,, 33 | 66.91583333,,,3400,2329.312532,,,, 34 | 33.68861111,,,4656.43,1811.674163,,,, 35 | 38.27138889,,,2855.9299,764.0416766,,,, 36 | 26.60138889,,,4000,1265.204375,,,, 37 | 45.32194444,,,7200,1198.102444,,,, 38 | 53.88055555,,,4149.52,1217.285038,,,, 39 | 42.37416667,,,950,916.4121979,,,, 40 | 66.64361111,,,0,425.2646071,,,, 41 | ,,,0,,Union St,IL 2,113214.23,40000 42 | ,,,,,,,,40000 43 | 357.5391667,,,0,430,,,, 44 | ,,,0,,Ogle St,IL 2,113214.23,126433.69 45 | ,,,,,,,,126433.69 46 | 177.5391667,,,0,724.3802,,,, 47 | ,,,0,,Chestnut St,IL 2,113653.6592,42000 48 | ,,,,,,,,41785.66 49 | 357.3955556,,,0,627.6679,,,, 50 | ,,,0,,Market St,IL 2,114094.9939,44000 51 | ,,,,,,,,43803.25 52 | 357.2841667,,,0,599.2619,,,, 53 | ,,,0,,Peru St,IL 2,114530.1275,46000 54 | ,,,,,,,,45792.11 55 | 356.7163889,,,0,207.8916,,,, 56 | ,,,0,,E 3rd St,IL 2,115950.62,47014.35 57 | ,,,,,,,,46549.82 58 | 86.65333333,,,0,464.5329,,,, 59 | ,,,0,,Kysor Dr,IL 2,115974.23,48014.2 60 | ,,,,,,,,48014.2 61 | 87.57722222,,,0,455.3657,,,, 62 | 88.18638889,,,0,545.2175,,,, 63 | ,,,0,,Luther Dr,IL 2,116708.83,51000 64 | ,,,,,,,,50432.84 65 | 122.2963889,,,0,567.1632,,,, 66 | ,,,0,,Old State Rd,IL 2,118014.32,52000 67 | ,,,,,,,,51472.43 68 | 89.62861111,,,0,527.5677,,,, 69 | ,,,0,,Ashelford Dr,IL 2,118841.45,53000 70 | ,,,,,,,,53000 71 | 84.21916667,,,150,113.9944609,,,, 72 | 144.3802778,,,0,804.9156609,,,, 73 | ,,,0,,River Dr S,IL 2,119786.59,54000 74 | ,,,,,,,,54000 75 | 89.07583333,,,0,402.1318,,,, 76 | ,,,0,,Mobile Home Access Road,River Dr S,54148,1000 77 | ,,,,,,,,1000 78 | 359.0758333,,,30,69,,,, 79 | 269.0758333,,,30,104.8928749,,,, 80 | 359.3480556,,,30,236.9399825,,,, 81 | 86.30416667,,,25,104.6366996,,,, 82 | 179.0758333,,,200,212.9609166,,,, 83 | 186.6686111,,,0,42.98132463,,,, 84 | ,,,0,,River Dr N Connector,IL 2,120946.6,55000 85 | ,,,,,,,,55000 86 | 111.5105556,,,0,171.92,,,, 87 | ,,,0,,River Dr N,River Dr N Connector,55171.92,4998.07 88 | ,,,,,,,,4687.89 89 | 357.8197222,,,948.97,310.1730796,,,, 90 | 347.065,,,0,242.4586796,,,, 91 | ,,,0,,Lake Entrance,IL 2,120946.6,55000 92 | ,,,,,,,,55000 93 | 291.5105556,,,0,132.9,,,, 94 | ,,,0,,Kennedy Rd,IL 2,126049.05,60003.34 95 | ,,,,,,,,60003.34 96 | 328.1911111,,,800,337.917195,,,, 97 | 4.701944444,,,0,1157.208195,,,, 98 | ,,,0,,Frontage Rd Connector,IL 2,137115.5597,60040 99 | ,,,,,,,,60040 100 | 159.0483333,,,0,144.48,,,, 101 | ,,,0,,Merdi Rd,IL 2,148292.43,62000 102 | ,,,,,,,,62000 103 | 358.0888889,,,0,1312.4904,,,, 104 | ,,,0,,Gold River Ave N,IL 2,2541.35,63000 105 | ,,,,,,,,62700 106 | 0.418333333,,,0,208.4648,,,, 107 | ,,,0,,Gold River Ave S,IL 2,2541.35,63000 108 | ,,,,,,,,62700 109 | 0.418333333,,,0,300,,,, 110 | ,,,0,,Lake Ave,IL 2,2969.26,64000 111 | ,,,,,,,,64000 112 | 359.6530556,,,0,300,,,, 113 | ,,,0,,Willow Trail,IL 2,3436.72,65000 114 | ,,,,,,,,64800 115 | 357.8552778,,,0,200,,,, 116 | ,,,0,,Antler Trail,IL 2,3700,66000 117 | ,,,,,,,,66000 118 | 22.68638889,,,0,200,,,, 119 | ,,,0,,Silver Creek Rd S,IL 2,4091.07,67000 120 | ,,,,,,,,66585.01 121 | 352.9947222,,,0,441.9881,,,, 122 | ,,,0,,Silver Creek Rd N,IL 2,6141.58,68000 123 | ,,,,,,,,67762.37 124 | 331.5819444,,,1500,142.0858944,,,, 125 | 334.8425,,,0,95.57279442,,,, 126 | ,,,0,,Prairie Rd S,IL 2,8117.55,30000 127 | ,,,,,,,,29700 128 | 341.4019444,,,4000,165.8368233,,,, 129 | 338.5352778,,,0,134.1964233,,,, 130 | ,,,0,,Prairie Rd N,IL 2,8117.55,163911.98 131 | ,,,,,,,,163911.98 132 | 353.62,,,1600,115.6538,,,, 133 | 359.085,,,0,450.347,,,, 134 | ,,,0,,Entrance,IL 2,20144.09,40000 135 | ,,,,,,,,40000 136 | 314.5891667,,,50,189.7434364,,,, 137 | 83.35722222,,,85,192.8701112,,,, 138 | 53.5825,,,35,77.27015004,,,, 139 | 344.7636111,,,0,87.80347524,,,, 140 | -------------------------------------------------------------------------------- /Project/Tasks/alignment/ImportXmlSubtask.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Subtask to populate the XML dialog when an XML file is chosen for import 26 | ''' 27 | 28 | from PySide import QtGui, QtCore 29 | 30 | import FreeCAD as App 31 | import FreeCADGui as Gui 32 | 33 | from Project.XML.AlignmentImporter import AlignmentImporter 34 | from Project.Support import WidgetModel, Units, Utils 35 | import math 36 | 37 | def create(panel, filepath): 38 | 39 | return ImportXmlSubtask(panel, filepath) 40 | 41 | class ImportXmlSubtask: 42 | 43 | def __init__(self, panel, filepath): 44 | 45 | self.panel = panel 46 | 47 | self.parser = AlignmentImporter() 48 | self.data = self.parser.import_file(filepath) 49 | 50 | if self.parser.errors: 51 | for _err in self.parser.errors: 52 | print(_err) 53 | 54 | self._setup_panel() 55 | 56 | self.errors = [] 57 | 58 | def _setup_panel(self): 59 | 60 | self.panel.projectName.setText(self.data['Project']['ID']) 61 | alignment_model = list(self.data['Alignments'].keys()) 62 | widget_model = WidgetModel.create(alignment_model) 63 | self.panel.alignmentsComboBox.setModel(widget_model) 64 | self.panel.alignmentsComboBox.currentTextChanged.connect(self._update_alignment) 65 | 66 | self._update_alignment(self.panel.alignmentsComboBox.currentText()) 67 | 68 | def _update_alignment(self, value): 69 | 70 | subset = self.data['Alignments'][value] 71 | 72 | if subset['meta'].get('StartStation'): 73 | self.panel.startStationValueLabel.setText('{0:.2f}'.format(subset['meta']['StartStation'])) 74 | 75 | if subset['meta'].get('Length'): 76 | self.panel.lengthTitleLabel.setText('Length ({0:s}):'.format(Units.get_doc_units()[0])) 77 | self.panel.lengthValueLabel.setText('{0:.2f}'.format(subset['meta']['Length'])) 78 | 79 | sta_model = [] 80 | 81 | if subset['station']: 82 | for st_eq in subset['station']: 83 | sta_model.append([st_eq['Back'], st_eq['Ahead']]) 84 | 85 | widget_model = WidgetModel.create(sta_model, ['Back'], ['Ahead']) 86 | 87 | self.panel.staEqTableView.setModel(widget_model) 88 | 89 | curve_model = [] 90 | 91 | for curve in subset['geometry']: 92 | 93 | _vals = [curve[_k] if curve.get(_k) else 0.0 for _k in [ 94 | 'Direction', 'StartStation', 'BearingIn', 'BearingOut', 'Radius'] 95 | ] 96 | 97 | row = '{0:s}, {1:f}, {2:.2f}, {3:.2f}, {4:.2f}, {5:.2f}'.format( 98 | curve['Type'], _vals[0], _vals[1], _vals[2], _vals[3], _vals[4] 99 | ) 100 | #curve['Type'], curve['Direction'], curve['StartStation'], 101 | #curve['BearingIn'], curve['BearingOut'], curve['Radius'] 102 | #) 103 | curve_model.append(row.split(',')) 104 | 105 | widget_model_2 = WidgetModel.create(curve_model, ['Type', 'Dir', 'Start', 106 | 'In', 'Out', 'Radius']) 107 | 108 | self.panel.curveTableView.setModel(widget_model_2) 109 | self.panel.curveTableView.resizeColumnsToContents() 110 | 111 | def import_model(self): 112 | ''' 113 | Return the model data 114 | ''' 115 | return self.data 116 | -------------------------------------------------------------------------------- /Project/Tasks/alignment/ImportAlignmentModel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | QAbstractTableModel class for Task QTableView 26 | ''' 27 | 28 | import re 29 | import operator 30 | 31 | from PySide import QtCore 32 | 33 | class ImportAlignmentModel(QtCore.QAbstractTableModel): 34 | 35 | default_flags = QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable 36 | 37 | def __init__(self, nam, headers, data, parent=None): 38 | 39 | QtCore.QAbstractTableModel.__init__(self, parent) 40 | 41 | self.data_model = data 42 | self.headers = headers 43 | self.model_name = nam 44 | 45 | def rowCount(self, parent=QtCore.QModelIndex()): 46 | ''' 47 | Number of rows currently in the model 48 | ''' 49 | 50 | if self.data_model: 51 | return len(self.data_model) 52 | 53 | #default 54 | return 0 55 | 56 | def columnCount(self, parent=QtCore.QModelIndex()): 57 | ''' 58 | Number of columns currently in the model 59 | ''' 60 | 61 | if self.data_model: 62 | if isinstance(self.data_model[0], list): 63 | return len(self.data_model[0]) 64 | 65 | return 1 66 | 67 | #default 68 | return 0 69 | 70 | def data(self, index, role): 71 | ''' 72 | Return data for valid indices and display role 73 | ''' 74 | 75 | if not index.isValid(): 76 | return None 77 | 78 | if not role in [QtCore.Qt.DisplayRole, QtCore.Qt.EditRole]: 79 | return None 80 | 81 | if not 0 <= index.row() < len(self.data_model): 82 | return None 83 | 84 | if self.columnCount(index) == 1: 85 | return self.data_model[index.row()] 86 | 87 | return self.data_model[index.row()][index.column()] 88 | 89 | def setData(self, index, value, role): 90 | ''' 91 | Update existing model data 92 | ''' 93 | 94 | self.data_model[index.row()][index.column()] = value 95 | self.dataChanged.emit(index, index) 96 | 97 | return True 98 | 99 | if role != QtCore.Qt.EditRole: 100 | return False 101 | 102 | if index.isValid() and 0 <= index.row() < len(self.data_model): 103 | 104 | #test for valid data types on a per-column basis 105 | if index.column() == 0: 106 | value = self.validate_station(value) 107 | 108 | else: 109 | 110 | try: 111 | value = float(value) 112 | 113 | except: 114 | return False 115 | 116 | #set the value 117 | self.data_model[index.row()][index.column()] = value 118 | 119 | self.dataChanged.emit(index, index) 120 | 121 | return True 122 | 123 | return False 124 | 125 | def headerData(self, col, orientation, role): 126 | ''' 127 | Headers to be displated 128 | ''' 129 | 130 | if not self.headers: 131 | return None 132 | 133 | if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: 134 | return self.headers[col] 135 | 136 | return None 137 | 138 | def flags(self, index): 139 | 140 | flags = ImportAlignmentModel.default_flags 141 | 142 | if index.row() > 0: 143 | flags = flags & ~QtCore.Qt.ItemIsEditable 144 | 145 | return flags 146 | -------------------------------------------------------------------------------- /transportationwb/clipplane.py: -------------------------------------------------------------------------------- 1 | '''an example for use of miki for both Qt and IV''' 2 | 3 | #\cond 4 | import FreeCAD 5 | import FreeCADGui 6 | Gui = FreeCADGui 7 | App=FreeCAD 8 | #\endcond 9 | 10 | import time 11 | from pivy import coin 12 | 13 | import transportationwb 14 | from transportationwb.miki_g import createMikiGui2, MikiApp 15 | reload(transportationwb.miki_g) 16 | 17 | # reset the scene by a fresh document 18 | App.closeDocument("Unnamed") 19 | App.newDocument("Unnamed") 20 | App.setActiveDocument("Unnamed") 21 | App.ActiveDocument=App.getDocument("Unnamed") 22 | Gui.ActiveDocument=Gui.getDocument("Unnamed") 23 | 24 | # create the scene 25 | 26 | 27 | layout='''#: from pivy.coin import * 28 | SoSeparator: 29 | id: 'IVRoot' 30 | SoSeparator: 31 | SoMaterial: 32 | emissiveColor: 0.2, 0., 0.0 33 | SoBaseColor: 34 | rgb: 1, 0 ,0 35 | SoRotationXYZ: 36 | axis: 2 37 | angle:1.5708 38 | SoTranslation: 39 | 40 | id:'pos' 41 | translation: 0,-40,20 42 | SoCylinder: 43 | radius: 30 44 | height:200 45 | 46 | SoSeparator: 47 | SoMaterial: 48 | emissiveColor:0, 0, .1 49 | transparency: 0.1 50 | SoBaseColor: 51 | rgb: 0,0,1 52 | SoClipPlane: 53 | plane: coin.SbPlane(coin.SbVec3f(0.,1.,0.),0.) 54 | id:'cp_blue_a' 55 | SoClipPlane: 56 | plane: coin.SbPlane(coin.SbVec3f(-1.,0.,0.),-80.) 57 | id:'cp_blue_b' 58 | SoCube: 59 | height: 150 60 | width:200 61 | depth:150 62 | 63 | SoSeparator: 64 | SoTranslation: 65 | translation: 100,10,-50 66 | SoClipPlane: 67 | id:'cp_green' 68 | plane: coin.SbPlane(coin.SbVec3f(-1.,0.,0.),0.) 69 | SoMaterial: 70 | emissiveColor: 0, .1, 0 71 | transparency: 0.2 72 | SoBaseColor: 73 | rgb: 0, 1, 0 74 | SoCube: 75 | height: 100 76 | width:150 77 | depth:100 78 | 79 | MainWindow: 80 | id: 'QtRoot' 81 | #resize: QtCore.QSize(400,200) 82 | QtGui.QLabel: 83 | setText:"*** Clip Plane Demo Animation ***" 84 | 85 | VerticalGroup: 86 | setTitle: "actions" 87 | QtGui.QPushButton: 88 | id: "run" 89 | setText: "list all ids" 90 | clicked.connect: app.run 91 | QtGui.QPushButton: 92 | setText: "run green clipplane animation" 93 | clicked.connect: app.run_green 94 | QtGui.QPushButton: 95 | setText: "run blue clipplane animation A" 96 | clicked.connect: app.run_blue_a 97 | QtGui.QPushButton: 98 | setText: "run blue clipplane animation B" 99 | clicked.connect: app.run_blue_b 100 | 101 | QtGui.QPushButton: 102 | setText: "activate all clip planes" 103 | clicked.connect: app.activate_all 104 | 105 | QtGui.QPushButton: 106 | setText: "deactivate all clip planes" 107 | clicked.connect: app.deactivate_all 108 | 109 | QtGui.QPushButton: 110 | setText: "reset all clip planes" 111 | clicked.connect: app.reset_planes 112 | 113 | QtGui.QPushButton: 114 | setText: "close" 115 | clicked.connect: app.close 116 | 117 | ''' 118 | 119 | class ComboApp(MikiApp): 120 | '''the controller for the use case some widgets start animation of SoClipPlanes''' 121 | 122 | def close(self): 123 | '''close the Gui dialog and delete the scenegraph''' 124 | Gui.ActiveDocument.ActiveView.getSceneGraph().removeChild(self.root.ids['IVRoot']) 125 | self.root.ids['QtRoot'].hide() 126 | 127 | def run(self): 128 | '''list all ids''' 129 | for mid in self.root.ids: 130 | print mid 131 | 132 | def run_green(self): 133 | '''run animation green''' 134 | ids=self.root.ids 135 | for i in range(-40,14): 136 | ids['cp_green'].plane.setValue(coin.SbPlane(coin.SbVec3f(-1.,0.,0.),i*5)) 137 | Gui.updateGui() 138 | time.sleep(0.01) 139 | 140 | def run_blue_b(self): 141 | '''run animation''' 142 | ids=self.root.ids 143 | for i in range(-70,10): 144 | ids['cp_blue_b'].plane.setValue(coin.SbPlane(coin.SbVec3f(-1.,0.,0.),i*2)) 145 | Gui.updateGui() 146 | time.sleep(0.01) 147 | 148 | def run_blue_a(self): 149 | '''run animation''' 150 | ids=self.root.ids 151 | for i in range(-40,20): 152 | ids['cp_blue_a'].plane.setValue(coin.SbPlane(coin.SbVec3f(0.,1.,0.),i*2)) 153 | Gui.updateGui() 154 | time.sleep(0.01) 155 | 156 | def activate_all(self): 157 | '''activate all clip planes''' 158 | 159 | ids=self.root.ids 160 | ids['cp_blue_a'].on.setValue(1) 161 | ids['cp_blue_b'].on.setValue(1) 162 | ids['cp_green'].on.setValue(1) 163 | 164 | def reset_planes(self): 165 | '''put the clip planes to an initial position''' 166 | ids=self.root.ids 167 | ids['cp_green'].plane.setValue(coin.SbPlane(coin.SbVec3f(-1.,0.,0.),-200)) 168 | ids['cp_blue_b'].plane.setValue(coin.SbPlane(coin.SbVec3f(-1.,0.,0.),-140)) 169 | ids['cp_blue_a'].plane.setValue(coin.SbPlane(coin.SbVec3f(0.,1.,0.),-80)) 170 | 171 | def deactivate_all(self): 172 | '''deactivate the clip planes - show the whole bodies''' 173 | ids=self.root.ids 174 | ids['cp_blue_a'].on.setValue(0) 175 | ids['cp_blue_b'].on.setValue(0) 176 | ids['cp_green'].on.setValue(0) 177 | 178 | 179 | def demoClipPlaneAnimation(): 180 | '''starts the demo file and Gui''' 181 | app = createMikiGui2(layout, ComboApp) 182 | Gui.activeDocument().activeView().viewAxonometric() 183 | Gui.updateGui() 184 | Gui.SendMsgToActiveView("ViewFit") 185 | 186 | -------------------------------------------------------------------------------- /Corridor/alignment/ImportVerticalCurve.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2018 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | import FreeCAD as App 25 | import FreeCADGui as Gui 26 | import os 27 | import json 28 | from PySide import QtGui 29 | from PySide import QtCore 30 | from Corridor.Alignment import VerticalCurve, Metadata 31 | 32 | class ImportVerticalCurve(): 33 | 34 | def __init__(self): 35 | pass 36 | 37 | def GetResources(self): 38 | """ 39 | Icon resources. 40 | """ 41 | 42 | icon_path = os.path.dirname(os.path.abspath(__file__)) 43 | 44 | icon_path += "../../../icons/new_alignment.svg" 45 | 46 | return {'Pixmap' : icon_path, 47 | 'Accel' : "Shift+V", 48 | 'MenuText': "Import Vertical Alignment", 49 | 'ToolTip' : "Import a Vertical Alignment from JSON", 50 | 'CmdType' : "ForEdit"} 51 | 52 | def getFile(self): 53 | ''' 54 | Displays the file browser dialog to pick the JSON file 55 | ''' 56 | dlg = QtGui.QFileDialog() 57 | options = dlg.Options() 58 | options |= dlg.DontUseNativeDialog 59 | dlg.setDefaultSuffix('.json') 60 | file_name, _ = dlg.getOpenFileName(dlg,"QFileDialog.getOpenFileName()", "","All Files (*);;JSON Files (*.json)", options=options) 61 | 62 | return file_name 63 | 64 | def build_alignment(self, group, data): 65 | ''' 66 | Build the curve objects describing the alignemtn 67 | ''' 68 | 69 | for vc_data in data['geometry']: 70 | 71 | vc_obj = VerticalCurve.createVerticalCurve(vc_data, data['meta']['units']) 72 | group.addObject(vc_obj.Object) 73 | 74 | def validate_heirarchy(self, _id, _units): 75 | ''' 76 | Validates the alignment heirarchy, adding missing groups 77 | ''' 78 | 79 | grand_parent = App.ActiveDocument.getObject('Alignments') 80 | 81 | if grand_parent is None: 82 | grand_parent = App.ActiveDocument.addObject('App::DocumentObjectGroup', 'Alignments') 83 | 84 | #replace non-printable characters with underscore 85 | for _x in [' ', '.', '+', '(', ')']: 86 | _id = _id.replace(_x, '_') 87 | 88 | parent = grand_parent.getObject(_id) 89 | 90 | if parent is None: 91 | parent = grand_parent.newObject('App::DocumentObjectGroup', _id) 92 | 93 | group = App.ActiveDocument.getObject('Vertical_' + _id) 94 | 95 | if group is None: 96 | group = parent.newObject("App::DocumentObjectGroup", 'Vertical_' + _id) 97 | 98 | return group 99 | 100 | def Activated(self): 101 | ''' 102 | Executes the tangent construction. 103 | ''' 104 | 105 | file_name = self.getFile() 106 | 107 | if file_name == '': 108 | return 109 | 110 | data = None 111 | 112 | with open(file_name, 'r') as json_data: 113 | data = json.load(json_data) 114 | 115 | for alignment in data: 116 | 117 | _id = alignment['meta']['id'] 118 | _units = alignment['meta']['units'] 119 | 120 | if _units.lower() in ['english', 'british']: 121 | _units = _units[0].upper() + _units[1:].lower() 122 | else: 123 | print("Invalid units specified in json") 124 | return 125 | 126 | group = self.validate_heirarchy(_id, _units) 127 | 128 | if group is None: 129 | return 130 | 131 | self.build_alignment(group, alignment) 132 | 133 | Gui.addCommand('ImportVerticalCurve', ImportVerticalCurve()) -------------------------------------------------------------------------------- /Project/XML/KeyMaps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 20XX AUTHOR_NAME * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Key maps for LandXML elements and the internal dictionary keys 26 | ''' 27 | from Project.Support.Const import Const 28 | 29 | class KeyMaps(Const): 30 | 31 | XML_DEFAULT = { 32 | 33 | } 34 | #lists of tags for different units of measurements / geometry types 35 | XML_TAGS = { 36 | 'length': 37 | ['radius', 'radiusStart', 'radiusEnd', 'chord', 'external', 'midOrd', 'tangent', 38 | 'length'], 39 | 40 | 'angle': 41 | ['delta', 'dir', 'dirStart', 'dirEnd'], 42 | 43 | 'coordinate': 44 | ['Start', 'End', 'Center', 'PI'] 45 | 46 | } 47 | 48 | #lists of tags for different Python data types. These are the datatypes that 49 | #they represent in XML, not in the dictionary 50 | #(e.g. 'rot' = 'ccw' / 'cw' in XML, but -1.0 / 1.0 in dictionary) 51 | XML_TYPES = { 52 | 'float': 53 | ['chord', 'constant', 'delta', 'dir', 'dirEnd', 'dirStart', 'external', 54 | 'length', 'midOrd', 'radius', 'radiusEnd', 'radiusStart', 'staBack', 55 | 'staIncrement', 'staInternal', 'staStart', 'tangent'], 56 | 57 | 'string': 58 | ['crvType', 'desc', 'name', 'note', 'manufacturer', 'manufacturerURL', 'oID', 59 | 'rot','spiType', 'state', 'timeStamp', 'version'] 60 | } 61 | 62 | #map of LandXML tags to internal Python dictionary 63 | XML_MAP = {'chord': 'Chord','crvType': 'CurveType','delta': 'Delta','desc': 'Description', 64 | 'dir': 'BearingIn', 'dirEnd': 'BearingOut','dirStart': 'BearingIn', 65 | 'external': 'External', 'length': 'Length', 'midOrd': 'MiddleOrdinate', 66 | 'name': 'ID', 'note': 'Note', 'oID': 'ObjectID', 'radius': 'Radius', 67 | 'radiusStart': 'StartRadius', 'radiusEnd': 'EndRadius','rot': 'Direction', 68 | 'spiType': 'SpiralType', 'staAhead': 'Ahead', 'staBack': 'Back', 69 | 'staIncrement': 'Direction', 'staInternal': 'Position', 70 | 'staStart': 'StartStation', 'staEnd': 'EndStation', 'state': 'Status', 71 | 'tangent': 'Tangent', 'version': 'Version'} 72 | 73 | #attributes for each LandXML Tag. Attribute names are divided into two lists. 74 | #The first list is required attributes, the second is optional 75 | XML_ATTRIBS = { 76 | 'Alignment': [ 77 | ['name', 'length', 'staStart'], 78 | ['desc', 'oID', 'state'] 79 | ], 80 | 'Alignments': [ 81 | [], 82 | ['desc', 'name', 'state'] 83 | ], 84 | 'Application': [ 85 | ['name'], 86 | ['desc', 'manufacturer', 'version', 'manufacturerURL', 'timeStamp'] 87 | ], 88 | 'CoordGeom': [ 89 | [], 90 | ['desc', 'name', 'state', 'oID'] 91 | ], 92 | 'Curve': [ 93 | ['rot'], 94 | ['chord', 'crvType', 'delta', 'desc', 'dirEnd', 'dirStart', 'external', 'length', 95 | 'midOrd', 'name', 'radius', 'staStart', 'state', 'tangent', 'oID', 'note'] 96 | ], 97 | 'Line': [ 98 | [], 99 | ['desc', 'dir', 'length', 'name', 'staStart', 'state', 'oID', 'note'] 100 | ], 101 | 'Project': [ 102 | ['name'], 103 | ['desc', 'state'] 104 | ], 105 | 'Spiral': [ 106 | ['length', 'radiusEnd', 'radiusStart', 'rot', 'spiType'], 107 | ['chord', 'constant', 'desc', 'dirEnd', 'dirStart', 'external', 'length', 'midOrd', 108 | 'name', 'note', 'oID', 'radius', 'staStart', 'state', 'tangent'] 109 | ], 110 | 'StaEquation': [ 111 | ['staAhead', 'staInternal'], 112 | ['desc', 'staBack', 'staIncrement'] 113 | ] 114 | } 115 | -------------------------------------------------------------------------------- /Corridor/alignment/AlignmentGroup.Py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2018 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Alignment DocumentObjectGroupPython class for highway alignments 26 | ''' 27 | 28 | __title__ = "AlignmentGroup.py" 29 | __author__ = "Joel Graff" 30 | __url__ = "https://www.freecadweb.org" 31 | 32 | import FreeCAD as App 33 | 34 | from Project.Support import Properties, Units 35 | from Project.ProjectObserver import ProjectObserver 36 | from Project.XML.AlignmentExporter import AlignmentExporter 37 | 38 | def get(grp_id=''): 39 | ''' 40 | Find the existing alignments object 41 | ''' 42 | 43 | return App.ActiveDocument.getObject('Alignments') 44 | 45 | def create(): 46 | ''' 47 | Factory method for alignment group 48 | ''' 49 | 50 | #return an existing instance of the same name, if found 51 | obj = App.ActiveDocument.getObject('Alignments') 52 | 53 | if obj: 54 | return obj.Proxy 55 | 56 | obj = App.ActiveDocument.addObject("App::DocumentObjectGroupPython", 'Alignments') 57 | 58 | fpo = _AlignmentGroup(obj) 59 | _ViewProviderAlignmentGroup(obj.ViewObject) 60 | 61 | return fpo 62 | 63 | class _AlignmentGroup(): 64 | 65 | def __init__(self, obj): 66 | 67 | obj.Proxy = self 68 | self.Type = "AlignmentGroup" 69 | self.Object = obj 70 | 71 | Properties.add(obj, 'String', 'ID', 'Alignment group name', '', 72 | is_read_only=True) 73 | 74 | Properties.add(obj, 'String', 'Description', 'Alignment group description', '', 75 | is_read_only=True) 76 | 77 | Properties.add(obj, 'FileIncluded', 'Xml_Path', '', '', is_hidden=True) 78 | 79 | ProjectObserver.get(App.ActiveDocument).register('StartSaveDocument', self.write_xml) 80 | 81 | 82 | def onDocumentRestored(self, fp): 83 | ''' 84 | Restore object references on reload 85 | ''' 86 | 87 | ProjectObserver.get(App.ActiveDocument).register('StartSaveDocument', self.write_xml) 88 | 89 | def write_xml(self): 90 | ''' 91 | Serialize the object data and it's children to xml files 92 | ''' 93 | 94 | _list = [] 95 | 96 | #iterate the list of children, acquiring their data sets 97 | #and creating a total data set for alignments. 98 | for _obj in self.Object.OutList: 99 | _list.append(_obj.Proxy.get_geometry()) 100 | 101 | exporter = AlignmentExporter() 102 | 103 | template_path = App.getUserAppDataDir() + 'Mod/freecad-transportation-wb/Resources/data/' 104 | template_file = 'landXML-' + Units.get_doc_units()[1] + '.xml' 105 | 106 | xml_path = App.ActiveDocument.TransientDir + '/alignment.xml' 107 | 108 | print('writing xml...') 109 | exporter.write(_list, template_path + template_file, xml_path) 110 | 111 | self.Object.Xml_Path = xml_path 112 | 113 | def __getstate__(self): 114 | return self.Type 115 | 116 | def __setstate__(self, state): 117 | if state: 118 | self.Type = state 119 | 120 | def _get_child(self, object_type): 121 | 122 | for obj in self.Object.Group: 123 | if obj.TypeId == object_type: 124 | return obj 125 | 126 | return None 127 | 128 | def execute(self, obj): 129 | pass 130 | 131 | class _ViewProviderAlignmentGroup(object): 132 | 133 | def getIcon(self): 134 | return '' 135 | 136 | def __init__(self,vobj): 137 | self.Object = vobj.Object 138 | vobj.Proxy = self 139 | 140 | def attach(self,vobj): 141 | 142 | self.Object = vobj.Object 143 | return 144 | 145 | def claimChildren(self): 146 | return self.Object.Group 147 | 148 | def __getstate__(self): 149 | return None 150 | 151 | def __setstate__(self,state): 152 | return None 153 | 154 | def setEdit(self,vobj,mode=0): 155 | return True 156 | 157 | def unsetEdit(self,vobj,mode=0): 158 | return False 159 | 160 | def doubleClicked(self,vobj): 161 | pass 162 | 163 | def setupContextMenu(self, obj, menu): 164 | pass 165 | 166 | def edit(self): 167 | pass -------------------------------------------------------------------------------- /transportationwb/property_creator.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 160 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 50 20 | 120 21 | 341 22 | 32 23 | 24 | 25 | 26 | Qt::Horizontal 27 | 28 | 29 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 30 | 31 | 32 | 33 | 34 | 35 | 20 36 | 0 37 | 371 38 | 111 39 | 40 | 41 | 42 | 43 | QLayout::SetMaximumSize 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 0 53 | 0 54 | 55 | 56 | 57 | 58 | 16777215 59 | 16777215 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 0 69 | 0 70 | 71 | 72 | 73 | Name 74 | 75 | 76 | Qt::PlainText 77 | 78 | 79 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 0 91 | 0 92 | 93 | 94 | 95 | Category 96 | 97 | 98 | Qt::PlainText 99 | 100 | 101 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 0 110 | 0 111 | 112 | 113 | 114 | Default Value 115 | 116 | 117 | Qt::PlainText 118 | 119 | 120 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 0 129 | 0 130 | 131 | 132 | 133 | Type 134 | 135 | 136 | Qt::PlainText 137 | 138 | 139 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | buttonBox 153 | accepted() 154 | Dialog 155 | accept() 156 | 157 | 158 | 248 159 | 254 160 | 161 | 162 | 157 163 | 274 164 | 165 | 166 | 167 | 168 | buttonBox 169 | rejected() 170 | Dialog 171 | reject() 172 | 173 | 174 | 316 175 | 260 176 | 177 | 178 | 286 179 | 274 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /Geometry/Support.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ************************************************************************** 3 | # * * 4 | # * Copyright (c) 2018 Joel Graff * 5 | # * * 6 | # * This program is free software; you can redistribute it and/or modify * 7 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 8 | # * as published by the Free Software Foundation; either version 2 of * 9 | # * the License, or (at your option) any later version. * 10 | # * for detail see the LICENCE text file. * 11 | # * * 12 | # * This program is distributed in the hope that it will be useful, * 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 | # * GNU Library General Public License for more details. * 16 | # * * 17 | # * You should have received a copy of the GNU Library General Public * 18 | # * License along with this program; if not, write to the Free Software * 19 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 20 | # * USA * 21 | # * * 22 | # ************************************************************************** 23 | 24 | ''' 25 | Useful math functions and constants 26 | ''' 27 | import math 28 | import FreeCAD as App 29 | 30 | from Project.Support import Utils 31 | from Project.Support.Utils import Constants as C 32 | 33 | def safe_sub(lhs, rhs, return_None=False): 34 | ''' 35 | Safely subtract two vectors. 36 | Returns an empty vector or None if either vector is None 37 | ''' 38 | 39 | if not lhs or not rhs: 40 | 41 | if return_None: 42 | return None 43 | 44 | return App.Vector() 45 | 46 | return lhs.sub(rhs) 47 | 48 | def safe_radians(value): 49 | ''' 50 | Convert a floating point value from degrees to radians, 51 | handle None / invalid type conditions 52 | ''' 53 | 54 | if value is None: 55 | return 0.0 56 | 57 | if not isinstance(value, float): 58 | return 0.0 59 | 60 | return math.radians(value) 61 | 62 | def get_rotation(in_vector, out_vector = None): 63 | ''' 64 | Returns the rotation as a signed integer: 65 | 1 = cw, -1 = ccw, 0 = fail 66 | 67 | if in_vector is an instance, out_vector is ignored. 68 | ''' 69 | _in = in_vector 70 | _out = out_vector 71 | 72 | if isinstance(_in, list): 73 | if not all(_in): 74 | return 0 75 | 76 | _out = in_vector[1] 77 | _in = in_vector[0] 78 | 79 | if not all([_out, _in]): 80 | return 0 81 | 82 | if not (_in.Length and _out.Length): 83 | return 0 84 | 85 | return -1 * math.copysign(1, _in.cross(_out).z) 86 | 87 | def get_ortho(vector, rot): 88 | ''' 89 | Calculate the normalized orthogonal of the passed vector 90 | ''' 91 | 92 | result = vector 93 | 94 | if isinstance(vector, list): 95 | result = App.Vector(vector) 96 | 97 | if not isinstance(result, App.Vector): 98 | return None 99 | 100 | return App.Vector(result.y, -result.x, 0.0).normalize().multiply(rot) 101 | 102 | def get_bearing(vector): 103 | ''' 104 | Returns the absolute bearing of the passed vector. 105 | Bearing is measured clockwise from +y 'north' (0,1,0) 106 | Vector is a list of coordinates or an App.Vector 107 | ''' 108 | 109 | result = vector 110 | 111 | if isinstance(vector, list): 112 | result = App.Vector(vector) 113 | 114 | if not isinstance(vector, App.Vector): 115 | return None 116 | 117 | rot = get_rotation(C.UP, result) 118 | angle = rot * C.UP.getAngle(result) 119 | 120 | if angle < 0.0: 121 | angle += C.TWO_PI 122 | 123 | return angle 124 | 125 | def within_tolerance(lhs, rhs=None): 126 | ''' 127 | Determine if two values are within a pre-defined tolerance 128 | 129 | lhs / rhs - values to compare. 130 | If rhs is none, lhs mat be an array, or a single value to compare directly with tolerance 131 | 132 | Array comparisons check every value against every other, and error if any checks fail 133 | ''' 134 | 135 | if lhs is None: 136 | return False 137 | 138 | if isinstance(lhs, list): 139 | 140 | for _i in range(0, len(lhs) - 1): 141 | 142 | rhs = lhs[_i:] 143 | 144 | for _val in rhs: 145 | 146 | if not abs(lhs[_i] - _val) < C.TOLERANCE: 147 | return False 148 | 149 | elif rhs is None: 150 | return abs(lhs) < C.TOLERANCE 151 | 152 | else: 153 | return abs(lhs-rhs) < C.TOLERANCE 154 | 155 | return True 156 | 157 | def vector_ortho(vector): 158 | ''' 159 | Returns the orthogonal of a 2D vector as (-y, x) 160 | ''' 161 | 162 | vec_list = vector 163 | 164 | if not isinstance(vector, list): 165 | vec_list = [vector] 166 | 167 | result = [] 168 | 169 | for vec in vec_list: 170 | result.append(App.Vector(-vec.y, vec.x, 0.0)) 171 | 172 | if len(result) == 1: 173 | return result[0] 174 | 175 | return result 176 | 177 | def vector_from_angle(angle): 178 | ''' 179 | Returns a vector form a given angle in radians 180 | ''' 181 | 182 | _angle = Utils.to_float(angle) 183 | 184 | if not _angle: 185 | return None 186 | 187 | return App.Vector(math.sin(_angle), math.cos(_angle), 0.0) 188 | --------------------------------------------------------------------------------