FreeCAD-macros

Форк
0
/
HalfHull.FCMacro 
1197 строк · 46.4 Кб
1
#
2
#
3
#			Half Hull
4
# (c) piffpoof 2015
5
#
6
################################
7

8
import FreeCAD
9
from FreeCAD import Base, Draft
10
import Part, PartGui, sys, math, collections
11
from collections import OrderedDict
12
from os.path import expanduser		# default input directory
13
from PySide import QtGui, QtCore
14

15
# UI Class definitions
16
global ConfigParams
17
global GetConfigParams
18
global OrderedDict
19

20
class ConfigParams:
21
	"""carrier for the user selection parameters"""
22
	def __init__(self):
23
		self.result				= None
24
		self.cb1a				= None
25
		self.cb1b				= None
26
		self.cb1c				= None
27
		self.cb2a				= None
28
		self.cb2b				= None
29
		self.cb2c				= None
30
		self.cb3a				= None
31
		self.cb3b				= None
32
		self.cb3c				= None
33
		self.cb4a				= None
34
		self.rb4b				= None
35
		self.rb5b				= None
36
		self.skipAtBow			= None
37
		self.skipAtStern		= None
38
		self.deckWidth			= None
39
		self.deckThrow			= None
40
		self.coachhouseRise		= None
41
		self.coachhouseIncline	= None
42
		self.documentFileName	= None
43

44
class GetConfigParams(QtGui.QDialog):
45
	""""""
46
	def __init__(self):
47
		super(GetConfigParams, self).__init__()
48
		self.initUI()
49
	def initUI(self):
50
		self.configParams			= ConfigParams()
51
		# set default return value
52
		self.configParams.result 	= userCancelled
53
		# set default values
54
		skipAtBowDefault			= str(2)
55
		skipAtSternDefault			= str(2)
56
		deckWidthDefault			= str(50)
57
		deckThrowDefault			= str(2)
58
		coachhouseRiseDefault		= str(50)
59
		coachhouseInclineDefault	= str(8)
60
		# field descriptors
61
		self.promptLbl = QtGui.QLabel("Please Choose Options:", self)
62
		self.promptLbl.move(20, 20)
63
		# checkboxes - define first so  signals can be set up
64
		self.cb1a = QtGui.QCheckBox("Starboard half-hull", self)
65
		self.cb1b = QtGui.QCheckBox("Mounting plaque", self)
66
		self.cb1c = QtGui.QCheckBox("Allow space for keel", self)
67
		self.cb2a = QtGui.QCheckBox("Port half-hull", self)
68
		self.cb2b = QtGui.QCheckBox("Mounting plaque", self)
69
		self.cb2c = QtGui.QCheckBox("Allow space for keel", self)
70
		self.cb3a = QtGui.QCheckBox("Complete hull", self)
71
		self.cb3b = QtGui.QCheckBox("Bottle for complete hull", self)
72
		self.cb3c = QtGui.QCheckBox("Allow space for keel", self)
73
		self.rb4b = QtGui.QRadioButton("Bulkheads for flush deck",self)
74
		self.rb5b = QtGui.QRadioButton("Bulkheads for coachhouse",self)
75
		#
76
		self.cb1a.clicked.connect(self.onCb1a)
77
		self.cb1a.toggle() # set default value
78
		self.cb1c.setEnabled(False)
79
		#self.cb1a.stateChanged.connect(self.onCb1a)
80
		self.cb1a.move(20,50)
81
		#
82
		self.cb1b.clicked.connect(self.onCb1b)
83
		self.cb1b.move(250,50)
84
		#
85
		self.cb1c.clicked.connect(self.onCb1c)
86
		self.cb1c.move(450,50)
87
		#
88
		self.cb2a.clicked.connect(self.onCb2a)
89
		self.cb2b.setEnabled(False)
90
		self.cb2c.setEnabled(False)
91
		self.cb2a.move(20,80)
92
		#
93
		self.cb2b.clicked.connect(self.onCb2b)
94
		self.cb2b.move(250,80)
95
		#
96
		self.cb2c.clicked.connect(self.onCb2c)
97
		self.cb2c.move(450,80)
98
		#
99
		self.cb3a.clicked.connect(self.onCb3a)
100
		self.cb3b.setEnabled(False)
101
		self.cb3c.setEnabled(False)
102
		self.cb3a.move(20,110)
103
		#
104
		self.cb3b.clicked.connect(self.onCb3b)
105
		self.cb3b.move(250,110)
106
		#
107
		self.cb3c.clicked.connect(self.onCb3c)
108
		self.cb3c.move(450,110)
109
		#
110
		self.cb4a = QtGui.QCheckBox("Bulkheads for complete hull", self)
111
		self.cb4a.clicked.connect(self.onCb4a)
112
		self.rb4b.setEnabled(False)
113
		self.rb5b.setEnabled(False)
114
		#self.hideCoachhouseFields(True) # grey out coachhouse fields
115
		self.cb4a.move(20,140)
116
		# radio buttons
117
		self.rb4b.move(250,140)
118
		self.rb4b.clicked.connect(self.onRb4b)
119
		#
120
		self.rb5b.move(250,170)
121
		self.rb5b.clicked.connect(self.onRb5b)
122
		#
123
		self.skipAtBowLabel = QtGui.QLabel("Cross-sections to skip at bow:", self)
124
		self.skipAtBowLabel.move(270, 200)
125
		self.skipAtBow = 0
126
		#
127
		self.skipAtSternLabel = QtGui.QLabel("Cross-sections to skip at stern:", self)
128
		self.skipAtSternLabel.move(270, 230)
129
		self.skipAtStern = 0
130
		#
131
		self.deckWidthLabel = QtGui.QLabel("Deck Width:", self)
132
		self.deckWidthLabel.move(270, 260)
133
		self.deckWidth = QtGui.QLineEdit(self)
134
		self.deckWidth.setInputMask("999")
135
		self.deckWidth.setText(deckWidthDefault)
136
		self.deckWidth.setFixedWidth(35)
137
		self.deckWidth.move(493, 260)
138
		#
139
		self.deckThrowLabel = QtGui.QLabel("Deck throw:", self)
140
		self.deckThrowLabel.move(270, 290)
141
		self.deckThrow = QtGui.QLineEdit(self)
142
		self.deckThrow.setInputMask("999")
143
		self.deckThrow.setText(deckThrowDefault)
144
		self.deckThrow.setFixedWidth(35)
145
		self.deckThrow.move(493, 290)
146
		#
147
		self.coachhouseRiseLabel = QtGui.QLabel("Coachhouse Rise:", self)
148
		self.coachhouseRiseLabel.move(270, 320)
149
		self.coachhouseRise = QtGui.QLineEdit(self)
150
		self.coachhouseRise.setInputMask("999")
151
		self.coachhouseRise.setText(coachhouseRiseDefault)
152
		self.coachhouseRise.setFixedWidth(35)
153
		self.coachhouseRise.move(493, 320)
154
		#
155
		self.coachhouseInclineLabel = QtGui.QLabel("Coachhouse Incline:", self)
156
		self.coachhouseInclineLabel.move(270, 350)
157
		self.coachhouseIncline = QtGui.QLineEdit(self)
158
		self.coachhouseIncline.setInputMask("999")
159
		self.coachhouseIncline.setText(coachhouseInclineDefault)
160
		self.coachhouseIncline.setFixedWidth(35)
161
		self.coachhouseIncline.move(493, 350)
162
		# set up lists for pop-ups
163
		self.popupItemsB = OrderedDict([("2",""),("3",""),("4",""),("5",""),("6",""),("7",""),("8",""),("9",""),("10","")])
164
		self.popupItemsS = OrderedDict([("1",""),("2",""),("3",""),("4",""),("5",""),("6",""),("7",""),("8",""),("9",""),("10","")])
165
		# set up pop-up menu of bulkheads to skip bulkheads at bow
166
		self.skipAtBowPop = QtGui.QComboBox(self)
167
		self.skipAtBowPop.addItems(self.popupItemsB.keys())
168
		self.skipAtBowPop.setCurrentIndex(self.popupItemsB.keys().index(skipAtBowDefault))
169
		self.skipAtBow = skipAtBowDefault
170
		self.skipAtBowPop.move(490, 200)
171
		self.skipAtBowPop.activated[str].connect(self.onSkipBowActivated)
172
		# set up pop-up menu of bulkheads to skip bulkheads at stern
173
		self.skipAtSternPop = QtGui.QComboBox(self)
174
		self.skipAtSternPop.addItems(self.popupItemsS.keys())
175
		self.skipAtSternPop.setCurrentIndex(self.popupItemsS.keys().index(skipAtSternDefault))
176
		self.skipAtStern = skipAtSternDefault
177
		self.skipAtSternPop.move(490, 230)
178
		self.skipAtSternPop.activated[str].connect(self.onSkipSternActivated)
179
		# cancel button
180
		cancelButton = QtGui.QPushButton('Cancel', self)
181
		cancelButton.clicked.connect(self.onCancel)
182
		cancelButton.move(260, 390)
183
		# last used button
184
		lastFileButton = QtGui.QPushButton('Re-use last file', self)
185
		lastFileButton.clicked.connect(self.onLastFile)
186
		lastFileButton.move(345, 390)
187
		# OK button
188
		sfButton = QtGui.QPushButton('Select File', self)
189
		sfButton.clicked.connect(self.onSf)
190
		sfButton.move(480, 390)
191
		# define window		xLoc,yLoc,xDim,yDim
192
		self.setGeometry(	250, 250, 630, 445)
193
		self.setWindowTitle("Macro Configuration")
194
		self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
195
		self.disableCoachhouseFields(True) # grey out coachhouse fields
196
		self.show()
197
		#
198
	def onCb1a(self):
199
		if self.cb1a.isChecked():
200
			self.cb1b.setEnabled(True)
201
		else:
202
			self.cb1b.setEnabled(False)
203
			self.cb1b.setChecked(False)
204
			self.cb1c.setEnabled(False)
205
			self.cb1c.setChecked(False)
206
	def onCb1b(self):
207
		if self.cb1b.isChecked():
208
			self.cb1c.setEnabled(True)
209
		else:
210
			self.cb1c.setEnabled(False)
211
			self.cb1c.setChecked(False)
212
	def onCb1c(self):
213
		pass
214
	def onCb2a(self):
215
		if self.cb2a.isChecked():
216
			self.cb2b.setEnabled(True)
217
		else:
218
			self.cb2b.setEnabled(False)
219
			self.cb2b.setChecked(False)
220
			self.cb2c.setEnabled(False)
221
			self.cb2c.setChecked(False)
222
	def onCb2b(self):
223
		if self.cb2b.isChecked():
224
			self.cb2c.setEnabled(True)
225
		else:
226
			self.cb2c.setEnabled(False)
227
			self.cb2c.setChecked(False)
228
	def onCb2c(self):
229
		pass
230
	def onCb3a(self):
231
		if self.cb3a.isChecked():
232
			self.cb3b.setEnabled(True)
233
		else:
234
			self.cb3b.setEnabled(False)
235
			self.cb3b.setChecked(False)
236
			self.cb3c.setEnabled(False)
237
			self.cb3c.setChecked(False)
238
	def onCb3b(self):
239
		if self.cb3b.isChecked():
240
			self.cb3c.setEnabled(True)
241
		else:
242
			self.cb3c.setEnabled(False)
243
			self.cb3c.setChecked(False)
244
	def onCb3c(self):
245
		pass
246
	def onCb4a(self):
247
		if self.cb4a.isChecked():
248
			self.rb4b.setEnabled(True)
249
			self.rb4b.setChecked(True)
250
			self.rb5b.setEnabled(True)
251
			self.rb5b.setChecked(False)
252
		else:
253
			self.rb4b.setChecked(False)
254
			self.rb4b.setEnabled(False)
255
			self.rb5b.setChecked(False)
256
			self.rb5b.setEnabled(False)
257
			self.disableCoachhouseFields(True)
258
	def onRb4b(self):
259
		if self.rb4b.isChecked():
260
			self.disableCoachhouseFields(True)
261
		else:
262
			self.disableCoachhouseFields(False)
263
	def onRb5b(self):
264
		if self.rb5b.isChecked():
265
			self.disableCoachhouseFields(False)
266
		else:
267
			self.disableCoachhouseFields(True)
268
	def onSkipBowActivated(self, text):
269
		self.skipAtBow = text
270
	def onSkipSternActivated(self, text):
271
		self.skipAtStern = text
272
	def disableCoachhouseFields(self, aFlag):
273
		# enable or disable coachhouse parameter fields
274
		if aFlag:
275
			self.skipAtBowLabel.setEnabled(False)
276
			self.skipAtBowPop.setEnabled(False)
277
			self.skipAtSternLabel.setEnabled(False)
278
			self.skipAtSternPop.setEnabled(False)
279
			self.deckWidthLabel.setEnabled(False)
280
			self.deckWidth.setEnabled(False)
281
			self.deckThrowLabel.setEnabled(False)
282
			self.deckThrow.setEnabled(False)
283
			self.coachhouseRiseLabel.setEnabled(False)
284
			self.coachhouseRise.setEnabled(False)
285
			self.coachhouseInclineLabel.setEnabled(False)
286
			self.coachhouseIncline.setEnabled(False)
287
		else:
288
			self.skipAtBowLabel.setEnabled(True)
289
			self.skipAtBowPop.setEnabled(True)
290
			self.skipAtSternLabel.setEnabled(True)
291
			self.skipAtSternPop.setEnabled(True)
292
			self.deckWidthLabel.setEnabled(True)
293
			self.deckWidth.setEnabled(True)
294
			self.deckThrowLabel.setEnabled(True)
295
			self.deckThrow.setEnabled(True)
296
			self.coachhouseRiseLabel.setEnabled(True)
297
			self.coachhouseRise.setEnabled(True)
298
			self.coachhouseInclineLabel.setEnabled(True)
299
			self.coachhouseIncline.setEnabled(True)
300
	def onCancel(self):
301
		self.configParams.result			= userCancelled
302
		self.close()
303
	def transferConfigParams(self):
304
		self.configParams.cb1a				= self.cb1a.isChecked()
305
		self.configParams.cb1b				= self.cb1b.isChecked()
306
		self.configParams.cb1c				= self.cb1c.isChecked()
307
		self.configParams.cb2a				= self.cb2a.isChecked()
308
		self.configParams.cb2b				= self.cb2b.isChecked()
309
		self.configParams.cb2c				= self.cb2c.isChecked()
310
		self.configParams.cb3a				= self.cb3a.isChecked()
311
		self.configParams.cb3b				= self.cb3b.isChecked()
312
		self.configParams.cb3c				= self.cb3c.isChecked()
313
		self.configParams.cb4a				= self.cb4a.isChecked()
314
		self.configParams.rb5b				= self.rb5b.isChecked()
315
		self.configParams.skipAtBow			= self.skipAtBow
316
		self.configParams.skipAtStern		= self.skipAtStern
317
		self.configParams.deckWidth			= self.deckWidth
318
		self.configParams.deckThrow			= self.deckThrow
319
		self.configParams.coachhouseRise	= self.coachhouseRise
320
		self.configParams.coachhouseIncline	= self.coachhouseIncline
321
	def onLastFile(self):
322
		self.configParams.result			= userLastFile
323
		self.transferConfigParams()
324
		self.close()
325
	def onSf(self):
326
		self.configParams.result			= userOK
327
		self.transferConfigParams()
328
		self.close()
329

330
# Class definitions
331

332
class HullCrossSection:
333
	"Holder of information pertaining to a cross section profile"
334
	#persistentInstance = ""
335
	#import copy
336
	def __init__(self,aSketch):
337
		self.sketch =		aSketch
338
		self.geometryCount = aSketch.GeometryCount
339
		self.geometryS =	None # geometry for starboard side
340
		self.geometryP =	None # geometry for port side
341
		self.geometryC =	None # geometry for complete hull (i.e. both halves as one)
342
		self.label =		aSketch.Label
343
		# next 2 lines due to mysterious label morphing routine of FreeCAD
344
		self.altLabel =		self.label.replace(" ", "_")
345
		self.altLabel =		self.altLabel.replace("-", "_")
346
		#
347
		self.xPos =			0.0 # normalise sketch to axis
348
		self.yPos =			aSketch.Placement.Base.y
349
		self.zPos =			aSketch.Placement.Base.z
350
		self.xMin =			infinity # will hold min X value in Sketch
351
		self.yMin =			infinity # will hold min Y value in Sketch
352
		self.xMax =			infinityNegative # will hold max X value in Sketch
353
		self.yMax =			infinityNegative # will hold max Y value in Sketch
354
		self.endPoint =		None # will be the 'top' point on the polyline
355
		self.key =			int(self.yPos)
356
		self.rotation =		aSketch.Placement.Rotation
357
		self.rotAngle =		aSketch.Placement.Rotation.Angle
358
		self.rotAxis =		aSketch.Placement.Rotation.Axis
359
		self.rotQ =			aSketch.Placement.Rotation.Q
360
		# following 4 statements seem necessary to pass the Rotation quad-value
361
		self.rotQ1 =		aSketch.Placement.Rotation.Q[0]
362
		self.rotQ2 =		aSketch.Placement.Rotation.Q[1]
363
		self.rotQ3 =		aSketch.Placement.Rotation.Q[2]
364
		self.rotQ4 =		aSketch.Placement.Rotation.Q[3]
365
		# set flags for either stemline or transom or suspected transom cross-section
366
		if eqRotation(self.rotation,yzPlane):
367
			# if we have the stemline then wait to give it the foremost placement
368
			FreeCAD.Console.PrintMessage("Stemline identified '" + self.label + "'\n")
369
			self.stemlineFlag = True
370
		else:
371
			self.stemlineFlag = False
372
		if eqRotation(self.rotation,xyPlane):
373
			# if we have the transom then wait to give it the aftmost placement
374
			FreeCAD.Console.PrintMessage("Transom identified '" + self.label + "'\n")
375
			self.transomFlag = True
376
		else:
377
			self.transomFlag = False
378
		if eqRotation(self.rotation,xzPlane):
379
			# the most numerous sketches will be the cross-sections, so don't flag it
380
			self.possibleTransomCS = False
381
		else:
382
			# it's not lying in any of the 3 planes so it's either an error
383
			# or it could be an inclined cross-section for the transom
384
			# (although there should only be one or none of these)
385
			# flag it as such and sort it out later once all the other
386
			# cross-sections are placed
387
			if not (self.stemlineFlag or self.transomFlag):
388
				FreeCAD.Console.PrintMessage("Possible Transom cross-section identified '" + self.label + "'\n")
389
				self.possibleTransomCS = True
390
		self.defineGeometries() # make S & P & C geometries from the geometry of the supplied Sketch
391
				
392
	def defineGeometries(self):
393
		# the supplied geometry is for the starboard side and is part of the user supplied Sketch
394
		# - make a direct copy for the starboard half-hull
395
		# - negate the X coordinates for the Port side
396
		# - append a negated reversed copy to each starboard piece for the complete hull
397
		self.geometryS =	list()
398
		self.geometryP =	list()
399
		self.geometryC =	list()
400
		#grab the endPoint which will be used for covering the half-hull model
401
		epX = max(self.sketch.Geometry[-1].StartPoint.x, self.sketch.Geometry[-1].EndPoint.x)
402
		epY = self.yPos
403
		epZ = max(self.sketch.Geometry[-1].StartPoint.y, self.sketch.Geometry[-1].EndPoint.y)
404
		self.endPoint = Base.Vector(epX,epY,epZ)
405
		# first pass through segment of sketch is to determine the min and max X & Y values
406
		for seg in self.sketch.Geometry:
407
			# determine the minimum X & Y values
408
			self.xMin = min(self.xMin, seg.StartPoint.x, seg.EndPoint.x)	
409
			self.yMin = min(self.yMin, seg.StartPoint.y, seg.EndPoint.y)
410
			self.xMax = max(self.xMax, seg.StartPoint.x, seg.EndPoint.x)	
411
			self.yMax = max(self.yMax, seg.StartPoint.y, seg.EndPoint.y)
412
		# second pass is to create the S, P and starboard side of the C geometries
413
		for seg in self.sketch.Geometry:
414
			# extract the X,Y,Z for start and end
415
			segStartX = seg.StartPoint.x
416
			segStartY = seg.StartPoint.y
417
			segStartZ = seg.StartPoint.z
418
			segEndX   = seg.EndPoint.x
419
			segEndY   = seg.EndPoint.y
420
			segEndZ   = seg.EndPoint.z
421
			absMinX   = abs(self.xMin)
422
			# normalise segments within drawing to X axis if not stemline
423
			if not self.stemlineFlag:
424
				if abs(segStartX) == absMinX:
425
					if 0<absMinX<1:
426
						segStartX = 0
427
					elif absMinX >= 1:
428
						FreeCAD.Console.PrintMessage("Move to Y axis of '" + self.label + "'\n")
429
						if segStartX<0:
430
							segStartX = segStartX + absMinX
431
						elif segEndX>0:
432
							segStartX = segStartX - absMinX
433
				if abs(segEndX) == absMinX:
434
					if 0<absMinX<1:
435
						segEndX = 0
436
					elif absMinX >= 1:
437
						FreeCAD.Console.PrintMessage("Move to Y axis of '" + self.label + "'\n")
438
						if segEndX<0:
439
							segEndX = segEndX + absMinX
440
						elif segEndX>0:
441
							segEndX = segEndX - absMinX
442
			# now create starboard, port, complete geometries
443
			self.geometryS.append(Part.Line(
444
				Base.Vector(segStartX, segStartY, segStartZ),
445
				Base.Vector(segEndX, segEndY, segEndZ)))
446
			if self.stemlineFlag:
447
				# stemline is on the YZ axis and is common to both half-hulls
448
				# so don't flip it's X coordinates
449
				multiplicand = 1
450
			else:
451
				multiplicand = -1
452
			self.geometryP.append(Part.Line(
453
				Base.Vector(segStartX*multiplicand, segStartY, segStartZ),
454
				Base.Vector(segEndX*multiplicand, segEndY, segEndZ)))
455
			# starboard geometry is first half of complete hull geometry
456
			self.geometryC.append(self.geometryS[-1])
457
		# third pass is to create the Complete geometry so the segments have
458
		# constant direction from starboard to port:
459
		# 1) reverse segment order of starboard side
460
		# 2 copy port side geometry
461
		segCnt = len(self.sketch.Geometry)
462
		completeGeometry = list()
463
		for i in range(segCnt):
464
			completeGeometry.append(self.reverseLineDirection(self.geometryS[segCnt-1-i]))	
465
		for i in range(segCnt):
466
			completeGeometry.append(self.reverseLineDirection(self.geometryP[i]))	
467
		self.geometryC = completeGeometry
468
		
469
	def reverseLineDirection(self,aLine):
470
		return Part.Line(aLine.EndPoint, aLine.StartPoint)
471

472
# Function definitions
473

474
def createBottle(aKeelFlag):
475
	# create a bottle around the hull
476
	bottleRadius = 250
477
	neckRadius = 75
478
	bottleBottom = -1000
479
	bottleTop = 400
480
	neckBottom = 800
481
	neckTop = 1150
482
	corkHeight = 100
483
	# get some dimensions for the plaque based on the size of the hull or half-hull
484
	minusX = crossSections[-2].yPos * 1.1
485
	plusX = (crossSections[0].yPos + crossSections[0].yMax) * 1.1
486
	minusY = 0; plusY = 0
487
	for cs in crossSections:
488
		#minusY = max(minusY, abs(cs.yMin))
489
		plusY = max(plusY, cs.yMax)
490
	minusY = plusY * -0.75
491
	plusY = plusY * 1.1
492
	#print minusX, " ", plusX, " ", minusY, " ", plusY
493
	
494
	bs0 = FreeCAD.ActiveDocument.addObject("Part::Vertex","Ring0")
495
	bs0.Label='Ring0'
496
	bs0.X=0.00
497
	bs0.Y=bottleBottom*0.97
498
	bs0.Z=0.00
499
	bs0.Placement = Base.Placement(	Base.Vector(0.00,0.00,0.00),
500
									Base.Rotation(0.00,0.00,0.00,1.00))
501
	bs0.ViewObject.Visibility=False
502
	#
503
	bs1 = FreeCAD.activeDocument().addObject('Sketcher::SketchObject','Ring1')
504
	bs1.addGeometry(Part.Circle(Base.Vector(0,0,0), Base.Vector(0,0,1), bottleRadius))
505
	bs1.Placement = FreeCAD.Placement(FreeCAD.Vector(0.0,bottleBottom,0.0),
506
									FreeCAD.Rotation(-0.707107,0.0,0.0,-0.707107))
507
	#
508
	bs2 = FreeCAD.activeDocument().addObject('Sketcher::SketchObject','Ring2')
509
	bs2.addGeometry(Part.Circle(Base.Vector(0,0,0), Base.Vector(0,0,1), bottleRadius))
510
	bs2.Placement = FreeCAD.Placement(FreeCAD.Vector(0.0,bottleTop,0.0),
511
									FreeCAD.Rotation(-0.707107,0.0,0.0,-0.707107))
512
	#
513
	bs3 = FreeCAD.activeDocument().addObject('Sketcher::SketchObject','Ring3')
514
	bs3.addGeometry(Part.Circle(Base.Vector(0,0,0), Base.Vector(0,0,1), neckRadius))
515
	bs3.Placement = FreeCAD.Placement(FreeCAD.Vector(0.0,neckBottom,0.0),
516
									FreeCAD.Rotation(-0.707107,0.0,0.0,-0.707107))
517
	#
518
	bs4 = FreeCAD.activeDocument().addObject('Sketcher::SketchObject','Ring4')
519
	bs4.addGeometry(Part.Circle(Base.Vector(0,0,0), Base.Vector(0,0,1), neckRadius))
520
	bs4.Placement = FreeCAD.Placement(FreeCAD.Vector(0.0,neckTop,0.0),
521
									FreeCAD.Rotation(-0.707107,0.0,0.0,-0.707107))
522
	#
523
	bot0 = FreeCAD.getDocument('hull_complete').addObject('Part::Loft','Loft0')
524
	bot0.Sections=[bs0, bs1,]
525
	bot0.Solid=False; bot0.Ruled=False; bot0.Closed=False
526
	#
527
	bot1 = FreeCAD.getDocument('hull_complete').addObject('Part::Loft','Loft1')
528
	bot1.Sections=[bs1, bs2,]
529
	bot1.Solid=False; bot1.Ruled=False; bot1.Closed=False
530
	#
531
	bot2 = FreeCAD.getDocument('hull_complete').addObject('Part::Loft','Loft2')
532
	bot2.Sections=[bs2, bs3,]
533
	bot2.Solid=False; bot2.Ruled=False; bot2.Closed=False
534
	#
535
	bot3 = FreeCAD.getDocument('hull_complete').addObject('Part::Loft','Loft3')
536
	bot3.Sections=[bs3,bs4,]
537
	bot3.Solid=False; bot3.Ruled=False; bot3.Closed=False
538
	#
539
	bottle = FreeCAD.activeDocument().addObject("Part::MultiFuse","Bottle")
540
	bottle.Shapes = [bot0,bot1,bot2,bot3,]
541
	bot1.ViewObject.Visibility=False
542
	bot2.ViewObject.Visibility=False
543
	bot3.ViewObject.Visibility=False
544
	#bottle.ViewObject.ShapeColor=Gui.ActiveDocument.Loft1.ShapeColor
545
	bottle.ViewObject.DisplayMode="Shaded"
546
	bottle.ViewObject.Transparency=80
547
	bottle.ViewObject.ShapeColor=(0.4, 0.8, 0.5, 0.0)
548
	FreeCAD.ActiveDocument.recompute()
549
	#
550
	cork = FreeCAD.ActiveDocument.addObject("Part::Cylinder","Cylinder")
551
	cork.Label = "Cork"
552
	cork.Radius = neckRadius-1
553
	cork.Height = corkHeight
554
	cork.Placement = FreeCAD.Placement(	FreeCAD.Vector(0.0,neckTop-(corkHeight/2),0.0),
555
										FreeCAD.Rotation(FreeCAD.Vector(1,0,0),-90))
556
	cork.ViewObject.ShapeColor=(0.78, 0.65, 0.35, 0.0)
557
	cork.ViewObject.DisplayMode = "Shaded"
558
	#
559
	FreeCADGui.activeDocument().activeView().viewAxometric()
560
	FreeCADGui.SendMsgToActiveView("ViewFit")
561
	FreeCAD.ActiveDocument.recompute()
562

563
def createBulkheads(aDictionary):
564
	userAction = None
565

566
	docKey = findOpenDocumentName(outputWorkspaceB)
567
	print docKey
568
	if docKey!=None:
569
		reply = QtGui.QMessageBox.question(None, "",
570
			"The previous 'Bulkheads' output file is still open, close it",
571
			QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
572
		if reply == QtGui.QMessageBox.Yes:
573
			FreeCAD.closeDocument(outputWorkspaceB)
574
		else:
575
			userAction = userCancelled
576

577
	if userAction!=userCancelled:
578
		# bring in values from user dialogue
579
		coachhouseFlag			= aDictionary["coachhouseDeckBulkheadsFlag"]
580
		forwardBulkheadsToSkip	= aDictionary["forwardBulkheadsToSkip"]
581
		aftBulkheadsToSkip		= aDictionary["aftBulkheadsToSkip"]
582
		deckWidth				= aDictionary["deckWidth"]
583
		deckThrow				= aDictionary["deckThrow"]
584
		coachhouseRise			= aDictionary["coachhouseRise"]
585
		coachhouseIncline		= aDictionary["coachhouseIncline"]
586
		# set up output workspaces
587
		doc = FreeCAD.newDocument(outputWorkspaceB)
588
		FreeCAD.setActiveDocument(outputWorkspaceB)
589
		FreeCAD.ActiveDocument = FreeCAD.getDocument(outputWorkspaceB)
590
		FreeCADGui.ActiveDocument = FreeCADGui.getDocument(outputWorkspaceB)
591
	
592
		for i in range(forwardBulkheadsToSkip,len(crossSections)-aftBulkheadsToSkip):
593
			# add new bulkhead
594
			cs = crossSections[i]
595
			newSketch = FreeCAD.activeDocument().addObject('Sketcher::SketchObject',cs.label)
596
			#place bulkhead along keel
597
			newSketch.Placement = FreeCAD.Placement(
598
				FreeCAD.Vector(cs.xPos,cs.yPos,cs.zPos),
599
				FreeCAD.Rotation(cs.rotQ1,cs.rotQ2,cs.rotQ3,cs.rotQ4))
600
			# insert geometry segments from both half-hulls plus bulkhead into new Sketch
601
			for seg in cs.geometryC:
602
				newSketch.addGeometry(
603
					Part.Line(FreeCAD.Vector(seg.StartPoint.x,
604
						seg.StartPoint.y,	0),
605
					FreeCAD.Vector(seg.EndPoint.x,
606
						seg.EndPoint.y,0)))
607
				FreeCAD.ActiveDocument.recompute()
608
			xMin = cs.xMax * -1
609
			if coachhouseFlag:
610
				# user wants a coachhouse bulkhead
611
				newSketch.addGeometry(
612
					Part.Line(	FreeCAD.Vector(xMin, cs.yMax, 0),
613
								FreeCAD.Vector(xMin+deckWidth, cs.yMax+deckThrow, 0)))
614
				FreeCAD.ActiveDocument.recompute()
615
				newSketch.addGeometry(
616
					Part.Line(	FreeCAD.Vector(xMin+deckWidth, cs.yMax+deckThrow, 0),
617
								FreeCAD.Vector(xMin+deckWidth+coachhouseIncline, cs.yMax+deckThrow+coachhouseRise, 0)))
618
				FreeCAD.ActiveDocument.recompute()
619
				#
620
				newSketch.addGeometry(
621
					Part.Line(	FreeCAD.Vector(xMin+deckWidth+coachhouseIncline, cs.yMax+deckThrow+coachhouseRise, 0),
622
								FreeCAD.Vector(cs.xMax-deckWidth-coachhouseIncline, cs.yMax+deckThrow+coachhouseRise, 0)))
623
				FreeCAD.ActiveDocument.recompute()
624
				# focus at about -800
625
				#newSketch.addGeometry(
626
				#	Part.ArcOfCircle(Part.Circle(App.Vector(0.0,-190,0),App.Vector(0,0,1),240.0),1.078868,2.064096))
627
				newSketch.addGeometry(
628
					Part.Line(	FreeCAD.Vector(cs.xMax-deckWidth-coachhouseIncline, cs.yMax+deckThrow+coachhouseRise, 0),
629
								FreeCAD.Vector(cs.xMax-deckWidth, cs.yMax+deckThrow, 0)))
630
				FreeCAD.ActiveDocument.recompute()
631
				newSketch.addGeometry(
632
					Part.Line(	FreeCAD.Vector(cs.xMax-deckWidth, cs.yMax+deckThrow, 0),
633
								FreeCAD.Vector(cs.xMax, cs.yMax, 0)))
634
			else:
635
				# generate bulkheads for flush deck
636
				newSketch.addGeometry(
637
					Part.Line(	FreeCAD.Vector(xMin, cs.yMax, 0),
638
								FreeCAD.Vector(cs.xMax, cs.yMax, 0)))
639
				FreeCAD.ActiveDocument.recompute()
640
	
641
			newPad = App.activeDocument().addObject("PartDesign::Pad","Bulkhead")
642
			newPad.Sketch = newSketch
643
			newPad.Length = 1.0
644
			newPad.Sketch.ViewObject.hide()
645
			FreeCAD.ActiveDocument.recompute()
646
			
647
		FreeCADGui.SendMsgToActiveView("ViewFit")
648
		FreeCADGui.activeDocument().activeView().viewAxometric()
649

650
def createPlaque(aSideFlag, aKeelFlag):
651
	# create plaque to mount half-hull on
652
	# get some dimensions for the plaque based on the size of the hull or half-hull
653
	# note: the X & Y in this routine are to do with the XY of the plaque, not the Sketches
654
	woodColour = (0.53, 0.42, 0.23, 0.0)
655
	# find the overall max & min for X & Y
656
	minusY = crossSections[1].yMin; plusY = crossSections[1].yMin
657
	# get the plaque's Y min & max for the cross-sections (not the stemline or transom)
658
	for i in range(1,len(crossSections)-1):
659
		minusY = min(minusY, crossSections[i].yMin)
660
		plusY = max(plusY, crossSections[i].yMax)
661
	# now allow for the extent of the stemline
662
	minusY = min(minusY, crossSections[0].yMin)
663
	plusY = max(plusY, crossSections[0].yMax)
664
	# get extent of aftmost cross-section and add what the transom sticks out
665
	minusX = crossSections[-2].yPos + crossSections[-1].yMin - crossSections[-1].yMax
666
	# get the placement of the stemline and add what the stemline extends forward
667
	plusX = crossSections[0].yPos + crossSections[0].xMax
668
	# some scaling factors to provide margin space around the half-hull
669
	minusX = minusX * 1.1
670
	plusX = plusX * 1.1
671
	minusY = minusY * 1.5
672
	plusY = plusY * 1.25
673

674
	ps = FreeCAD.activeDocument().addObject('Sketcher::SketchObject','PlainPlaqueSketch')
675
	ps.Placement = FreeCAD.Placement(FreeCAD.Vector(0.0,0.0,0.0),
676
									FreeCAD.Rotation(0.5,0.5,0.5,0.5))		
677
	if aSideFlag==starboardSideFlag: ps.Placement.Base.x = -10
678
	ps.addGeometry(Part.Line(FreeCAD.Vector(minusX,plusY,0),FreeCAD.Vector(plusX,plusY,0)))
679
	ps.addGeometry(Part.Line(FreeCAD.Vector(plusX,plusY,0),FreeCAD.Vector(plusX,minusY,0)))
680
	ps.addGeometry(Part.Line(FreeCAD.Vector(plusX,minusY,0),FreeCAD.Vector(minusX,minusY,0)))
681
	ps.addGeometry(Part.Line(FreeCAD.Vector(minusX,minusY,0),FreeCAD.Vector(minusX,plusY,0)))
682
	#
683
	ps.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) 
684
	ps.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) 
685
	ps.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1)) 
686
	ps.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1)) 
687
	ps.addConstraint(Sketcher.Constraint('Horizontal',0)) 
688
	ps.addConstraint(Sketcher.Constraint('Horizontal',2)) 
689
	ps.addConstraint(Sketcher.Constraint('Vertical',1)) 
690
	ps.addConstraint(Sketcher.Constraint('Vertical',3)) 
691
	FreeCAD.ActiveDocument.recompute()
692
	#
693
	pad = FreeCAD.activeDocument().addObject("PartDesign::Pad","PlainPlaquePad")
694
	pad.Sketch = ps
695
	pad.Length = 10.0
696
	pad.Sketch.ViewObject.hide()
697
	pad.ViewObject.ShapeColor = woodColour
698
	FreeCAD.ActiveDocument.recompute()
699
	#
700
	cyl1 = FreeCAD.ActiveDocument.addObject("Part::Cylinder","Cylinder1")
701
	cyl1.Label = "Cylinder1"
702
	cyl1.Radius = plusY/5
703
	cyl1.Placement =	FreeCAD.Placement(FreeCAD.Vector(0,plusX,plusY),
704
						FreeCAD.Rotation(FreeCAD.Vector(0,1,0),90))
705
	if aSideFlag==starboardSideFlag: cyl1.Placement.Base.x = -10
706
	cut1 = FreeCAD.activeDocument().addObject("Part::Cut","Cut1")
707
	cut1.Base = App.activeDocument().PlainPlaquePad
708
	cut1.Tool = App.activeDocument().Cylinder1
709
	cut1.ViewObject.ShapeColor = woodColour
710
	FreeCAD.ActiveDocument.recompute()
711
	#
712
	cyl2 = FreeCAD.ActiveDocument.addObject("Part::Cylinder","Cylinder2")
713
	cyl2.Label = "Cylinder2"
714
	cyl2.Radius = plusY/5
715
	cyl2.Placement =	FreeCAD.Placement(FreeCAD.Vector(0,plusX,minusY),
716
						FreeCAD.Rotation(FreeCAD.Vector(0,1,0),90))
717
	if aSideFlag==starboardSideFlag: cyl2.Placement.Base.x = -10
718
	cut2 = FreeCAD.activeDocument().addObject("Part::Cut","Cut2")
719
	cut2.Base = App.activeDocument().Cut1
720
	cut2.Tool = App.activeDocument().Cylinder2
721
	cut2.ViewObject.ShapeColor = woodColour
722
	FreeCAD.ActiveDocument.recompute()
723
	#
724
	cyl3 = FreeCAD.ActiveDocument.addObject("Part::Cylinder","Cylinder3")
725
	cyl3.Label = "Cylinder3"
726
	cyl3.Radius = plusY/5
727
	cyl3.Placement =	FreeCAD.Placement(FreeCAD.Vector(0,minusX,minusY),
728
						FreeCAD.Rotation(FreeCAD.Vector(0,1,0),90))
729
	if aSideFlag==starboardSideFlag: cyl3.Placement.Base.x = -10
730
	cut3 = FreeCAD.activeDocument().addObject("Part::Cut","Cut3")
731
	cut3.Base = App.activeDocument().Cut2
732
	cut3.Tool = App.activeDocument().Cylinder3
733
	cut3.ViewObject.ShapeColor = woodColour
734
	FreeCAD.ActiveDocument.recompute()
735
	#
736
	cyl4 = FreeCAD.ActiveDocument.addObject("Part::Cylinder","Cylinder4")
737
	cyl4.Label = "Cylinder4"
738
	cyl4.Radius = plusY/5
739
	cyl4.Placement =	FreeCAD.Placement(FreeCAD.Vector(0,minusX,plusY),
740
						FreeCAD.Rotation(FreeCAD.Vector(0,1,0),90))
741
	if aSideFlag==starboardSideFlag: cyl4.Placement.Base.x = -10
742
	cut4 = FreeCAD.activeDocument().addObject("Part::Cut","Cut4")
743
	cut4.Base = App.activeDocument().Cut3
744
	cut4.Tool = App.activeDocument().Cylinder4
745
	cut4.ViewObject.ShapeColor = woodColour
746
	FreeCAD.ActiveDocument.recompute()
747
	#
748
	cham = FreeCAD.ActiveDocument.addObject("Part::Chamfer","Plaque")
749
	cham.Base = FreeCAD.ActiveDocument.Cut4
750
	edges = []
751
	if aSideFlag == starboardSideFlag:
752
		edges.append((3,3.00,3.00)); edges.append((13,3.00,3.00)); edges.append((14,3.00,3.00)); edges.append((15,3.00,3.00))
753
		edges.append((16,3.00,3.00)); edges.append((17,3.00,3.00)); edges.append((18,3.00,3.00)); edges.append((19,3.00,3.00))
754
	else:
755
		edges.append((1,3.00,3.00)); edges.append((5,3.00,3.00)); edges.append((6,3.00,3.00)); edges.append((7,3.00,3.00))
756
		edges.append((8,3.00,3.00)); edges.append((9,3.00,3.00)); edges.append((10,3.00,3.00)); edges.append((11,3.00,3.00))
757
	cham.Edges = edges
758
	cham.Base.ViewObject.Visibility = False
759
	cham.ViewObject.ShapeColor = woodColour
760

761
	createPlaqueCover()
762
	
763
	if aSideFlag == starboardSideFlag:
764
		FreeCADGui.activeDocument().activeView().viewRight()
765
	else:
766
		FreeCADGui.activeDocument().activeView().viewLeft()
767
	FreeCADGui.SendMsgToActiveView("ViewFit")
768
	FreeCAD.ActiveDocument.recompute()
769

770
def createPlaqueCover():
771
	# get upper bow point UBP
772
	# get upper stern point USP
773
	# get number of sections NS
774
	# divide the line UBP-USP into NS pieces
775
	#---
776
	# for each sketch, get the top point TP
777
	# make segments between each consecutive points
778
	#---
779
	# make RuledSurface between corresponding segments
780

781
	if len(crossSections)<5:
782
		FreeCAD.Console.PrintMessage("Insufficient cross-sections for plaque cover")
783
	else:
784
		# the number of cross-section and therefore chines determines how many
785
		# segments will be in the cover for the half-hull model
786
		segmentCount = len(crossSections)-2
787
		# determine endpoints for the line segments along the plaque
788
		bowPoint = Base.Vector(0,
789
			max(crossSections[0].geometryS[-1].StartPoint.x, crossSections[0].geometryS[-1].EndPoint.x),
790
			max(crossSections[0].geometryS[-1].StartPoint.y, crossSections[0].geometryS[-1].EndPoint.y))
791
		sternPoint = Base.Vector(0,
792
			crossSections[-2].yPos,
793
			max(crossSections[-2].geometryS[-1].StartPoint.y, crossSections[-2].geometryS[-1].EndPoint.y))
794
		plaquePoints = [bowPoint, sternPoint]
795
		lineAlongPlaqueToSplit = Part.Line(bowPoint,sternPoint)
796
		lapSection = lineAlongPlaqueToSplit.length()/segmentCount
797

798
		# build a list of the points that start/end the segments
799
		pointList = []
800
		pointList.append(lineAlongPlaqueToSplit.StartPoint)
801
		print lineAlongPlaqueToSplit.StartPoint
802
		for i in range(1, segmentCount):
803
			pointList.append(lineAlongPlaqueToSplit.value((i)*lapSection))
804
			print lineAlongPlaqueToSplit.value((i)*lapSection)
805
		pointList.append(lineAlongPlaqueToSplit.EndPoint)
806
		print lineAlongPlaqueToSplit.EndPoint
807
		print
808
		# iterate the list of points from the first segment to the last
809
		# do stemline first as it is in the YZ plane (cross-sections are in the XZ plane)
810
		a=FreeCAD.ActiveDocument.addObject('Part::Line', 'cs1k')
811
		a.X1=0; a.Y1=pointList[0].y; a.Z1=pointList[0].z
812
		a.X2=0; a.Y2=pointList[1].y; a.Z2=pointList[1].z
813
		b=FreeCAD.ActiveDocument.addObject('Part::Line', 'cs1h')
814
		# B1 wrong
815
		b.X1=0; b.Y1=crossSections[0].endPoint.x; b.Z1=crossSections[0].endPoint.z
816
		b.X2=crossSections[1].endPoint.x; b.Y2=crossSections[1].endPoint.y; b.Z2=crossSections[1].endPoint.z
817
		FreeCAD.ActiveDocument.addObject('Part::RuledSurface', 'coverSeg1')
818
		FreeCAD.ActiveDocument.ActiveObject.Curve1=(a,[''])
819
		FreeCAD.ActiveDocument.ActiveObject.Curve2=(b,[''])
820
		FreeCAD.ActiveDocument.recompute()
821
		# now do cross=sections
822
		for i in range(1, segmentCount):
823
			a=FreeCAD.ActiveDocument.addObject('Part::Line', 'cs'+str(i+1)+'k')
824
			a.X1=0; a.Y1=pointList[i].y; a.Z1=pointList[i].z
825
			a.X2=0; a.Y2=pointList[i+1].y;   a.Z2=pointList[i+1].z
826
			b=FreeCAD.ActiveDocument.addObject('Part::Line', 'cs'+str(i+1)+'h')
827
			b.X1=crossSections[i].endPoint.x;   b.Y1=crossSections[i].endPoint.y;   b.Z1=crossSections[i-1].endPoint.z
828
			b.X2=crossSections[i+1].endPoint.x; b.Y2=crossSections[i+1].endPoint.y; b.Z2=crossSections[i].endPoint.z
829
			FreeCAD.ActiveDocument.addObject('Part::RuledSurface', 'coverSeg'+str(i+1))
830
			FreeCAD.ActiveDocument.ActiveObject.Curve1=(a,[''])
831
			FreeCAD.ActiveDocument.ActiveObject.Curve2=(b,[''])
832
			FreeCAD.ActiveDocument.recompute()
833
				
834
		FreeCAD.ActiveDocument.recompute()
835

836
def displayChinePanels(crossSectionLabelA, crossSectionLabelB, aSideFlag):
837
	# accept 2 sketch labels and generate a ruled surface between them
838
	#print sketchLabelA
839
	placeholder = "_"
840
	csA = FreeCAD.ActiveDocument.getObjectsByLabel(crossSectionLabelA)[0]
841
	csB = FreeCAD.ActiveDocument.getObjectsByLabel(crossSectionLabelB)[0]
842
	labelA = csA.Label
843
	labelB = csB.Label
844
	lblA = labelA.split('_', 1)[0]
845
	lblB = labelB.split('_', 1)[0]
846
	if aSideFlag == portSideFlag:
847
		sideText = " Port"
848
	elif aSideFlag == starboardSideFlag:
849
		sideText = " Starboard"
850
	else:
851
		sideText = ""
852
	#print str(sketchLabelA) + " " + str(sketchLabelB)
853
	FreeCAD.ActiveDocument.addObject('Part::RuledSurface', placeholder+lblA+placeholder+lblB+sideText)
854
	csObjectA = FreeCAD.ActiveDocument.getObjectsByLabel(crossSectionLabelA)[0]
855
	csObjectB = FreeCAD.ActiveDocument.getObjectsByLabel(crossSectionLabelB)[0]
856
	#print "> " + str(sketchObjectA) + " " + str(sketchObjectB)
857
	FreeCAD.ActiveDocument.ActiveObject.Curve1=(csObjectA,[''])
858
	FreeCAD.ActiveDocument.ActiveObject.Curve2=(csObjectB,[''])
859

860
def displayCompleteHull():
861
	userAction = None
862

863
	docKey = findOpenDocumentName(outputWorkspaceC)
864
	if docKey!=None:
865
		reply = QtGui.QMessageBox.question(None, "",
866
			"The previous 'Complete Hull' output file is still open, close it",
867
			QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
868
		if reply == QtGui.QMessageBox.Yes:
869
			FreeCAD.closeDocument(outputWorkspaceC)
870
		else:
871
			userAction = userCancelled
872

873
	if userAction!=userCancelled:
874
		doc = App.newDocument(outputWorkspaceC)
875
		App.setActiveDocument(outputWorkspaceC)
876
		App.ActiveDocument=App.getDocument(outputWorkspaceC)
877
		docComplete = App.ActiveDocument
878
		Gui.ActiveDocument=Gui.getDocument(outputWorkspaceC)
879
	
880
		for cs in crossSections:
881
			# add new Sketch object
882
			newSketch = FreeCAD.activeDocument().addObject('Sketcher::SketchObject',cs.label)
883
			newSketch.Placement = FreeCAD.Placement(
884
				FreeCAD.Vector(cs.xPos,cs.yPos,cs.zPos),
885
				FreeCAD.Rotation(cs.rotQ1,cs.rotQ2,cs.rotQ3,cs.rotQ4))
886
			Gui.activeDocument().setEdit(cs.altLabel)
887
			# insert geometry segments from both half-hulls into new Sketch
888
			for seg in cs.geometryC:
889
				newSketch.addGeometry(
890
					Part.Line(FreeCAD.Vector(seg.StartPoint.x,
891
						seg.StartPoint.y,	0),
892
					FreeCAD.Vector(seg.EndPoint.x,
893
						seg.EndPoint.y,0)))
894
			Gui.getDocument(doc.Label).resetEdit()
895
			#print obj.Name + " " + obj.Label
896
	
897
		FreeCAD.cs = crossSections # debug statement
898
		for i in range(0, len(crossSections)-1):
899
			displayChinePanels(crossSections[i].altLabel,crossSections[i+1].altLabel, "C")
900
		# now draw the bow sections going to the stemline
901
		#displayChinePanels(crossSections[].altLabel,crossSections[i+1].altLabel, "C")
902
		FreeCAD.activeDocument().recompute()
903
		FreeCADGui.SendMsgToActiveView("ViewFit")
904
		FreeCADGui.activeDocument().activeView().viewAxometric()
905

906
def displayHalfHull(aSideFlag):
907
	userAction = None
908
	# create output workspace for one side
909
	if aSideFlag == starboardSideFlag:
910
		query = "The previous 'Port Half-Hull' output file is still open, close it"
911
		selectedOutputWorkspace = outputWorkspaceS
912
		outputWorkspace = outputWorkspaceS
913
		sideFlag = starboardSideFlag
914
	else:
915
		query = "The previous 'Starboard Half-Hull' output file is still open, close it"
916
		selectedOutputWorkspace = outputWorkspaceP
917
		outputWorkspace = outputWorkspaceP
918
		sideFlag = portSideFlag
919

920
	docKey = findOpenDocumentName(selectedOutputWorkspace)
921
	if docKey==None:
922
		doc = FreeCAD.newDocument(selectedOutputWorkspace)
923
	else:
924
		reply = QtGui.QMessageBox.question(None, "",
925
			query,
926
			QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
927
		if reply == QtGui.QMessageBox.Yes:
928
			FreeCAD.closeDocument(selectedOutputWorkspace)
929
			doc = FreeCAD.newDocument(selectedOutputWorkspace)
930
		else:
931
			userAction = userCancelled
932

933
	if userAction!=userCancelled:
934
		FreeCAD.setActiveDocument(outputWorkspace)
935
		FreeCAD.ActiveDocument=FreeCAD.getDocument(outputWorkspace)
936
		FreeCADGui.ActiveDocument=FreeCADGui.getDocument(outputWorkspace)
937
		
938
		# place the segments in the document
939
		for cs in crossSections:
940
			# add new Sketch object
941
			FreeCAD.activeDocument().addObject('Sketcher::SketchObject',cs.label)
942
			newSketch = FreeCAD.ActiveDocument.getObject(cs.altLabel)
943
			newSketch.Placement = FreeCAD.Placement(
944
				FreeCAD.Vector(cs.xPos,cs.yPos,cs.zPos),
945
				FreeCAD.Rotation(cs.rotQ1,cs.rotQ2,cs.rotQ3,cs.rotQ4))
946
			Gui.activeDocument().setEdit(cs.altLabel)
947
			# insert geometry segments into new Sketch
948
			if sideFlag == "S":
949
				geom = cs.geometryS
950
			else:
951
				geom = cs.geometryP
952
			for seg in geom:
953
				newSketch.addGeometry(
954
					Part.Line(FreeCAD.Vector(seg.StartPoint.x,
955
						seg.StartPoint.y,	0),
956
					FreeCAD.Vector(seg.EndPoint.x,
957
						seg.EndPoint.y,0)))
958
			Gui.getDocument(doc.Label).resetEdit()
959
		
960
		for i in range(0, len(crossSections)-1):
961
			displayChinePanels(crossSections[i].altLabel,crossSections[i+1].altLabel, sideFlag)
962
	
963
		FreeCAD.activeDocument().recompute()
964
		FreeCADGui.SendMsgToActiveView("ViewFit")
965
		FreeCADGui.activeDocument().activeView().viewAxometric()
966

967
def eqRotation(rotationA, rotationB, eps=0.0001):
968
	"takes 2 Rotations and compares for equality"
969
	eqFlag = True
970
	for i in range(0, 4):
971
		#print str(rotationA.Q[i]) + "#" + str(rotationB.Q[i])
972
		if rotationA.Q[i] == 0:
973
			if rotationB.Q[i] <> 0:
974
				eqFlag = False
975
		elif abs(abs(rotationA.Q[i])-abs(rotationB.Q[i]))/abs(rotationA.Q[i]) > eps:
976
			eqFlag = False
977
	return eqFlag
978

979
def resetSketchesVisibility():
980
	# set Visibility on all Sketches to False
981
	openWindows = list()
982
	if starboardHHFlag:
983
		openWindows.append(outputWorkspaceS)
984
	if portHHFlag:
985
		openWindows.append(outputWorkspaceP)
986
	if completeHullFlag:
987
		openWindows.append(outputWorkspaceC)
988
	#if fullHullBulkheadsFlag:
989
		#openWindows.append(outputWorkspaceB)
990

991
	for ws in openWindows:
992
		FreeCAD.setActiveDocument(ws)
993
		FreeCAD.ActiveDocument=FreeCAD.getDocument(ws)
994
		FreeCADGui.ActiveDocument=FreeCADGui.getDocument(ws)
995
		for obj in FreeCAD.ActiveDocument.Objects:
996
			if obj.TypeId == 'Sketcher::SketchObject':
997
				vo = FreeCADGui.ActiveDocument.getObject(obj.Name)
998
				vo.Visibility=False
999

1000
def sortOutFilesAndDocuments():
1001
	global keepSourceOpenFlag
1002
	# this routine uses the following variables from the main handler:
1003
	# docSrc (returns)
1004
	# fileName (reads)
1005
	# keepSourceOpenFlag (global, writes)
1006
	# it determines if the 'user selected input document' is already open,
1007
	# if it is then it is made the ActiveDocument, otherwise it is
1008
	# Opened which also sets it to the ActiveDocument
1009
	if len(FreeCAD.listDocuments())==0:
1010
		# no documents open
1011
		return FreeCAD.open(fileName)
1012
	else:
1013
		# some document(s) open so check if 'user selected input document' is already open
1014
		"""docKey = None
1015
		for key in FreeCAD.listDocuments():
1016
			if FreeCAD.listDocuments()[key].FileName==fileName:
1017
				docKey = key"""
1018
		docKey = findOpenDocumentFileSpec(fileName)
1019
		if docKey==None:
1020
			# 'user selected input document' is not open
1021
			return FreeCAD.open(fileName)
1022
		else:
1023
			# 'user selected input document' is among open documents
1024
			# set the 'user selected input document' to the active document (in case it isn't)
1025
			FreeCAD.setActiveDocument(docKey)
1026
			# user started with 'user selected input document' open, so this flag will allow
1027
			# us to leave it open when we finish
1028
			keepSourceOpenFlag = True
1029
			return FreeCAD.ActiveDocument
1030

1031
def findOpenDocumentFileSpec(aDocumentFileSpec):
1032
	# check if supplied document is already open
1033
	# return document name or None
1034
	docKey = None
1035
	for key in FreeCAD.listDocuments():
1036
		if FreeCAD.listDocuments()[key].FileName==aDocumentFileSpec:
1037
			docKey = key
1038
	return docKey
1039
	
1040
def findOpenDocumentName(aDocumentName):
1041
	# check if supplied document is already open
1042
	# return document name or None
1043
	docKey = None
1044
	for key in FreeCAD.listDocuments():
1045
		if FreeCAD.listDocuments()[key].Name==aDocumentName:
1046
			docKey = key
1047
	return docKey
1048
	
1049
# Constant definitions
1050
outputWorkspaceS	= "hull_starboard"
1051
outputWorkspaceP	= "hull_port"
1052
outputWorkspaceC	= "hull_complete"
1053
outputWorkspaceB	= "bulkheads"
1054
xyPlane				= Base.Rotation(0.0, 0.0, 0.0, 1.0) # transom
1055
xzPlane				= Base.Rotation(0.7071067811865475, 0.0, 0.0, 0.7071067811865476) # cross section profile
1056
yzPlane				= Base.Rotation(0.5,0.5,0.5,0.5) # stemline
1057
infinity			= +99999999999999.9 # will hold min Y value in Sketch
1058
infinityNegative	= -99999999999999.9 # will hold max X value in Sketch
1059
global starboardSideFlag, portSideFlag, userCancelled, userLastFile, userOK
1060
starboardSideFlag	= "S"
1061
portSideFlag		= "F"
1062
completeSidesFlag	= "C"
1063
userCancelled		= "Cancelled"
1064
userLastFile		= "Last File"
1065
userOK				= "OK"
1066
defaultDir = FreeCAD.ConfigGet("UserHomePath")
1067

1068
# code ***********************************************************************************
1069
allCrossSections	= {}
1070
crossSections		= list()
1071
docSrc				= None
1072
global keepSourceOpenFlag
1073
keepSourceOpenFlag	= False
1074
starboardHHFlag		= False; starboardHHPlaqueFlag	= False; starboardHHPKeelFlag		= False
1075
portHHFlag			= False; portHHPlaqueFlag		= False; portHHPKeelFlag			= False
1076
completeHullFlag	= False; completeHullBottleFlag	= False; completeHullKeelFlag		= False
1077
bulkheadsFlag		= False; flushDeckBulkheadsFlag	= True; coachhouseDeckBulkheadsFlag	= True
1078

1079
form = GetConfigParams()
1080
form.exec_()
1081
configParams = form.configParams
1082

1083
if configParams.result==userLastFile:
1084
	if hasattr(FreeCAD,"MacroHalfHullConfigParams"):
1085
		# global in FreeCAD exists so use the parameters stored there
1086
		#if type(FreeCAD.MacroHalfHullConfigParams)=='GetConfigParams':
1087
		configParams = FreeCAD.MacroHalfHullConfigParams
1088
		configParams.result = userLastFile
1089
	else:
1090
		# user requested to re-use last file but there isn't one
1091
		# so reset the choice to pick a file
1092
		configParams.result = userOK
1093
			
1094
if configParams.result != userCancelled:
1095
	# transfer results to control flags
1096
	starboardHHFlag				= configParams.cb1a
1097
	starboardHHPlaqueFlag		= configParams.cb1b
1098
	starboardHHPKeelFlag		= configParams.cb1c
1099
	portHHFlag					= configParams.cb2a
1100
	portHHPlaqueFlag			= configParams.cb2b
1101
	portHHPKeelFlag				= configParams.cb2c
1102
	completeHullFlag			= configParams.cb3a
1103
	completeHullBottleFlag		= configParams.cb3b
1104
	completeHullKeelFlag		= configParams.cb3c
1105
	bulkheadsFlag				= configParams.cb4a
1106
	# transfer bulkhead parameters
1107
	bulkheadParams = {}
1108
	bulkheadParams["coachhouseDeckBulkheadsFlag"]	= configParams.rb5b
1109
	bulkheadParams["forwardBulkheadsToSkip"]		= int(configParams.skipAtBow)
1110
	bulkheadParams["aftBulkheadsToSkip"]			= int(configParams.skipAtStern)
1111
	bulkheadParams["deckWidth"]						= float(configParams.deckWidth.text())
1112
	bulkheadParams["deckThrow"]						= float(configParams.deckThrow.text())
1113
	bulkheadParams["coachhouseRise"]				= float(configParams.coachhouseRise.text())
1114
	bulkheadParams["coachhouseIncline"]				= float(configParams.coachhouseIncline.text())
1115
		
1116
	defaultDir = expanduser("~")
1117
	defaultDir = "/Data Pool/Coding/FreeCAD/work items/hull mirroring/half-hull/"
1118
	if configParams.result==userOK:
1119
		# user wants to select file
1120
		fileName = QtGui.QFileDialog.getOpenFileName(dir=defaultDir, caption = "Select a 'Hull Profile' to Load", filter="*.FCStd")[0]
1121
		configParams.documentFileName = fileName
1122
	else:
1123
		# file is coming out of FreeCAD global
1124
		fileName = FreeCAD.MacroHalfHullConfigParams.documentFileName
1125

1126
	if len(fileName) != 0:
1127
		docSrc = sortOutFilesAndDocuments()
1128
		# read all the objects, saving the Sketcher objects
1129
		for obj in FreeCAD.ActiveDocument.Objects:
1130
			if obj.TypeId == 'Sketcher::SketchObject':
1131
				# ignore anything except Sketches
1132
				newObj = HullCrossSection(obj)
1133
				if newObj.transomFlag:
1134
					# if we have the transom sketch then wait to give
1135
					# it a placement further aft than anything else
1136
					transomObject = newObj
1137
				elif newObj.stemlineFlag:
1138
					# if we have the stemline sketch then wait
1139
					# to give it the first placement
1140
					stemlineObject = newObj
1141
					stemlineSegmentCount = stemlineObject.sketch.GeometryCount
1142
				else:
1143
					# must be a regular cross section profile
1144
					# so add object to our collection
1145
					allCrossSections[newObj.key] = newObj
1146
					#maxY = max(maxY, newObj.yPos)
1147

1148
		# discard sketches with wrong number of points
1149
		# i.e. cross-sections that have a different number
1150
		# of points than the stemline
1151
		for key, cs in allCrossSections.items():
1152
			if cs.geometryCount <> stemlineSegmentCount:
1153
				FreeCAD.Console.PrintMessage("Discard for wrong number of segments for '" + str(allCrossSections[key].label) + "'\n")
1154
				del allCrossSections[key]
1155
		
1156
		# construct a list with cross sections in order from stemline to transom
1157
		crossSections.append(stemlineObject)
1158
		for sk in sorted(allCrossSections.keys(), reverse=True):
1159
			crossSections.append(allCrossSections[sk])
1160
		transomObject.zPos = crossSections[-1].zPos+crossSections[-1].yMax
1161
		crossSections.append(transomObject)
1162

1163
		FreeCAD.cs = crossSections # debug statement
1164
		
1165
		# now set the transom elevation at the top of the last cross-section
1166
		crossSections[-1].zPos = crossSections[-2].zPos + crossSections[-2].yMax
1167
		
1168
		FreeCAD.cs = crossSections # debug statement
1169

1170
		# depending on user flags, invoke appropriate modules
1171
		if starboardHHFlag:			displayHalfHull(starboardSideFlag)
1172
		if starboardHHPlaqueFlag:	createPlaque(starboardSideFlag, starboardHHPKeelFlag)
1173
		if portHHFlag:				displayHalfHull(portSideFlag)
1174
		if portHHPlaqueFlag:		createPlaque(portSideFlag, portHHPKeelFlag)
1175
		if completeHullFlag:		displayCompleteHull()
1176
		if completeHullBottleFlag:	createBottle(completeHullKeelFlag)
1177
		if bulkheadsFlag:			createBulkheads(bulkheadParams)
1178

1179
		# save Config Params to FreeCAD global in case user wants to use it next run
1180
		FreeCAD.MacroHalfHullConfigParams = configParams
1181
		
1182
		if not keepSourceOpenFlag:
1183
			FreeCAD.closeDocument(docSrc.Name)
1184
		resetSketchesVisibility()
1185
#
1186
#OS: Mac OS X
1187
#Word size: 64-bit
1188
#Version: 0.14.3703 (Git)
1189
#Branch: releases/FreeCAD-0-14
1190
#Hash: c6edd47334a3e6f209e493773093db2b9b4f0e40
1191
#Python version: 2.7.5
1192
#Qt version: 4.8.6
1193
#Coin version: 3.1.3
1194
#SoQt version: 1.5.0
1195
#OCC version: 6.7.0
1196
#
1197
#and so the macro ends...
1198

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.