User:Piffpoof
hi there
This is the user page for PiffPoof. Our use of FreeCAD and our membership on this forum started as a project to document a 40 year old sailing boat. Lots of information about the boat and the work done on it over the last 10 years is available at https://maringret.wordpress.com/. This page describes our FreeCAD specific activities.
The builders no longer had drawings and the son of the first owner of the boat building company doubted there ever were full drawings. Apparently they built things "by eye" before CAD. Consequently the hull dimensions had to be measured and then a model constructed within the software, Modern software packages like FreeCAD offer many beneficial features like symmetry constraints but when the final measurement is a model that is "easy on the eye" much "massaging" of models and data is required.
Current state of the project: 400+ objects and counting...
We started by modeling the interior (it was easiest to measure) and quickly reached 150 objects. We were barely started and so it was at that point that we started to realise that we would be swamped by the sheer complexity of the undertaking unless we could sub-divide the project and automate as much as possible.This also marked the point where we started to construct tools we needed to manage our complexity explosion.
We developed a few utilities and systems for our own purposes. Due to the open nature of the FreeCAD community we tried to keep them open enough to be used for other uses and by other persons. At present we have the following items in various stages of development:
Programming Software
Macro_Dump_Objects
- v 0.2
- operational
- documented
- uploaded
- a toolbar consisting of 3 macros that enhance managing the FreeCAD user interface, in addition 2 existing FreeCAD macros are included
-
- Fit All Content on Screen (existing FreeCAD macro)
- Set to Axometric View (existing FreeCAD macro)
- CAD Navigational Style - switches the Navigational Style to "CAD" Macro_Manage_Navigational_Style
- Inventor Navigational Style - switches the Navigational Style to "Inventor" Macro_Manage_Navigational_Style
- Toggle Views Visibility - toggles visibility of Python console, Report view & Combo view Macro_Toggle_Views_Visibility
- v 1.0 operational
- documented
- uploaded
Build Utility
- v 1.0 operational
- not documented
- not uploaded
Python Console Assistant
- under development
- working as prototype
Visibility Management Assistant
- anticipated
Marine Software
Half Hull Designer
- helps in the design of half-hull models of ship hulls
- under development
- working as prototype
Wiring & Hoses
- provides the facility to run wires and hoses through three dimensional models (e.g. buildings, ships, planes, machinery)
- under development
- working as prototype
#
# Dump Object
# v 0.2 - added report to CSV file
# v 0.1 - added report to window
# v 0.0 - report to Report view
#
#***********************************************************************************
# routine to dump object space for Geometric model in the currently active file
from PySide import QtGui, QtCore
from datetime import datetime # datestamp on output window
from os.path import expanduser # output directory for CSV
# Class definitions
class configureMacro(QtGui.QDialog):
""""""
def __init__(self):
super(configureMacro, self).__init__()
self.initUI()
def initUI(self):
self.result = None
# set up display only field for selected path type
self.cbss = QtGui.QCheckBox("Show Sketcher Segments?", self)
self.cbss.move(20,20)
self.cbp = QtGui.QCheckBox("Show Positions?", self)
self.cbp.move(220,20)
self.pathTypeLbl = QtGui.QLabel("Select Report Destination:", self)
self.pathTypeLbl.move(20, 70)
# cancel button
cancelButton = QtGui.QPushButton('Cancel', self)
cancelButton.clicked.connect(self.onCancel)
cancelButton.move(10, 100)
# button #1
button1 = QtGui.QPushButton(choice1, self)
button1.clicked.connect(self.onBtn1)
button1.move(120, 100)
# button #2
button2 = QtGui.QPushButton(choice2, self)
button2.clicked.connect(self.onBtn2)
button2.move(235, 100)
# button #3
button3 = QtGui.QPushButton(choice3, self)
button3.clicked.connect(self.onBtn3)
button3.move(327, 100)
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( 250, 250, 435, 150)
self.setWindowTitle("Select a Report Destination")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.show()
def onCancel(self):
self.result = "cancelled"
self.close()
def onBtn1(self):
self.result = choice1
self.close()
def onBtn2(self):
self.result = choice2
self.close()
def onBtn3(self):
self.result = choice3
self.close()
class DisplayText(QtGui.QWidget):
#class DisplayText(QtGui.QMainWindow):
""""""
def __init__(self, textToDisplay):
self.text = textToDisplay
super(DisplayText, self).__init__()
self.initUI(textToDisplay)
def initUI(self, textToDisplay):
"""Constructor"""
self.textToDisplay = textToDisplay
# some window dimensions
self.windowHome = screenWidth * 0.05
self.windowWidth = screenWidth * 0.9
self.windowHeight = 400
self.fieldMargin = 40
# some column titles
columnLabels = QtGui.QLabel(formatPrintLine("Type / WB","Shape","User Supplied Label","Name"))
columnLabels.setFont('Courier')
# set up text editing widget
text_editor = QtGui.QTextEdit(self)
#self.setCentralWidget(self.text_editor)
text_editor.setFont('Courier')
text_editor.setLineWrapMode(QtGui.QTextEdit.NoWrap)
text_editor.move(self.fieldMargin,self.fieldMargin)
text_editor.move(0,self.fieldMargin)
text_editor.resize(self.windowWidth-(2*self.fieldMargin),self.windowHeight-(2*self.fieldMargin))
text_editor.resize(self.windowWidth,self.windowHeight-(2*self.fieldMargin))
text_editor.append(self.textToDisplay)
# set up the layout
vBox = QtGui.QVBoxLayout()
vBox.addWidget(columnLabels)
vBox.addWidget(text_editor)
self.setLayout(vBox)
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( self.windowHome, self.windowHome, self.windowWidth, self.windowHeight)
self.setWindowTitle("Object Dump of '" + FreeCADGui.ActiveDocument.Document.Label + "' at " + str(datetime.now()))
self.show()
#----------------------------------------------------------------------
def onOk(self):
self.close()
# define functions
def countObjects():
printList = list()
objectTypeTable = {}
# build up dictionary of different classes and keep a count
for obj in FreeCAD.ActiveDocument.Objects:
if objectTypeTable.has_key(obj.TypeId):
objectTypeTable[obj.TypeId] = objectTypeTable[obj.TypeId]+1
else:
objectTypeTable[obj.TypeId] = 1
wb = obj.TypeId[0:obj.TypeId.find("::")]
shape = obj.TypeId[obj.TypeId.find("::")+2:]
placementString = ""
if obj.TypeId == "Sketcher::SketchObject":
printList.append(formatPrintLine("Sketch", "", str(obj.Label)))
if showSketcherSegmentsFlag:
for i in obj.Geometry:
printList.append(formatPrintLine("", " -segment", str(i)))
elif wb == "Part":
if showPlacementFlag:
placementString = str(obj.Placement)
if shape in ("Cylinder", "Cut", "Box", "Fuse", "Loft", "Feature", "Part2DObjectPython"):
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
elif obj.TypeId == "Part::Feature":
printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
elif obj.TypeId == "Part::Part2DObjectPython":
printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
elif wb == "PartDesign":
if showPlacementFlag:
placementString = str(obj.Placement)
if shape in ("Pad", "Fillet"):
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
elif obj.TypeId == "Part::Feature":
printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
elif obj.TypeId == "Part::Part2DObjectPython":
printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
elif obj.TypeId == "App::DocumentObjectGroup":
printList.append(formatPrintLine("Group", "", str(obj.Label)))
elif obj.TypeId == "Image::ImagePlane":
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
else:
printList.append(formatPrintLine(str(obj.TypeId), str(obj.Label), str(obj.Name)))
if showPlacementFlag and len(placementString)!=0:
printList.append(formatPrintLineMax("", " -placement", placementString))
printList.append("")
printList.append(summarySeparator)
from collections import OrderedDict
sortedByTags = OrderedDict(sorted(objectTypeTable.items(), key=lambda x: x[1], reverse=True))
for k, v in sortedByTags.items():
printList.append(formatPrintLineSum(k,v))
printList.append("")
objectClassCount = 0; objectTotalCount = 0
for i in objectTypeTable:
objectTotalCount = objectTotalCount + objectTypeTable[i]
objectCLassCount = len(objectTypeTable)
printList.append(formatPrintLineSum("Object Class Total is ", str(objectCLassCount)))
printList.append(formatPrintLineSum("Object Total is ", str(objectTotalCount)))
printList.append(summarySeparator)
return printList
def formatPrintLineSum(a,b):
return printLineFormatter(2, a, str(b), "", "")
def formatPrintLineMax(a,b,c):
return printLineFormatter(1, a, b, "", "")
def formatPrintLine(a,b,c, *args):
d = ""
if len(args)==1:
d = args[0]
return printLineFormatter(0, a, b, c, d)
def printLineFormatter(flag,a,b,c,d):
# flag = 0 standard print, spread values over 4 columns
# flag = 1 printing verbose things like Sketch details or Placements, combine columns 3 & 4
# flag = 2 printing the summary lines, combine columns 1 & 2
suffix = ""
if csvFlag:
pfs2 = printFormatString2csv
pfs3 = printFormatString3csv
pfs4 = printFormatString4csv
else:
pfs2 = printFormatString2
pfs3 = printFormatString3
pfs4 = printFormatString4
if flag==0:
aa = a[:f1]
bb = b[:f2]
cc = c[:f3]
dd = d[:f4]
return pfs4.format(aa,bb,cc,dd)
elif flag==1:
aa = a[:f1]
bb = b[:f2]
cc = c[:f3+f4]
dd = d[:f4]
return pfs3.format(aa,bb,cc)
else:
aa = a[:f1+f2]
bb = b[:f3+f4]
return pfs2.format(aa,bb)
# constants
# set some field widths
screenWidth = QtGui.QDesktopWidget().screenGeometry().width()
global f1, f2, f3, f4
# f1 = 15; f2 = 25; f3 = 45; f4 = 25 # 110 columns in 1000 pixels
f1 = 15*screenWidth/1000; f2 = 25*screenWidth/1000; f3 = 45*screenWidth/1000; f4 = 25*screenWidth/1000
# and some print format strings
global printFormatString2, printFormatString3, printFormatString4
global printFormatString2csv, printFormatString3csv, printFormatString4csv
printFormatString2 = "{0:<"+str(f1+f2)+"} {1:<"+str(f3)+"}"
printFormatString2csv = "{0}, {1}"
printFormatString3 = "{0:<"+str(f1)+"} {1:<"+str(f2)+"} {2:<"+str(f3+f4)+"}"
printFormatString3csv = "{0}, {1}, {2}"
printFormatString4 = "{0:<"+str(f1)+"} {1:<"+str(f2)+"} {2:<"+str(f3)+"} {3:<"+str(f4)+"}"
printFormatString4csv = "{0}, {1}, {2}, {3}"
# some button labels that are checked in the code
global choice1, choice2, choice3, csvFlag
choice1 = "Report View"; choice2 = "CSV File"; choice3 = "Window"
csvFlag = False
summarySeparator = "======================================================="
# code ***********************************************************************************
if FreeCAD.ActiveDocument != None:
# ask if to window or to Report View...
form = configureMacro()
form.exec_()
showSketcherSegmentsFlag = False
if form.cbss.isChecked():
showSketcherSegmentsFlag = True
showPlacementFlag = False
if form.cbp.isChecked():
showPlacementFlag = True
if form.result == choice2:
csvFlag = True
summarySeparator = "-------------------------------------------------------"
printList = countObjects()
if form.result == choice1: # report to Report View
print printFormatString4.format( "", "", "(User Supplied)", "")
print printFormatString4.format( "Type", "Shape", "Label", "Name")
print ""
for line in printList:
print line + "\n"
if form.result == choice2: # report to CSV file
homeDir = expanduser("~")
file = open(homeDir+"/test.csv","w")
for line in printList:
file.write(line + "\n")
file.close()
if form.result == choice3: # report to window
#----------------------------------------------------------------------
longPrintLine = ""
for line in printList:
longPrintLine = longPrintLine + line + "\n"
form = DisplayText(longPrintLine)
#
#OS: Mac OS X
#Word size: 64-bit
#Version: 0.14.3703 (Git)
#Branch: releases/FreeCAD-0-14
#Hash: c6edd47334a3e6f209e493773093db2b9b4f0e40
#Python version: 2.7.5
#Qt version: 4.8.6
#Coin version: 3.1.3
#SoQt version: 1.5.0
#OCC version: 6.7.0
#