304 lines
12 KiB
Python
304 lines
12 KiB
Python
# ***************************************************************************
|
|
# * *
|
|
# * Copyright (c) 2018 Yorik van Havre <yorik@uncreated.net> *
|
|
# * *
|
|
# * This program is free software; you can redistribute it and/or modify *
|
|
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
# * as published by the Free Software Foundation; either version 2 of *
|
|
# * the License, or (at your option) any later version. *
|
|
# * for detail see the LICENCE text file. *
|
|
# * *
|
|
# * This program is distributed in the hope that it will be useful, *
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
# * GNU Library General Public License for more details. *
|
|
# * *
|
|
# * You should have received a copy of the GNU Library General Public *
|
|
# * License along with this program; if not, write to the Free Software *
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
# * USA *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
|
|
"""The BIM Box command"""
|
|
|
|
import FreeCAD
|
|
import FreeCADGui
|
|
|
|
QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP
|
|
translate = FreeCAD.Qt.translate
|
|
|
|
|
|
class BIM_Box:
|
|
def GetResources(self):
|
|
return {
|
|
"Pixmap": "BIM_Box",
|
|
"MenuText": QT_TRANSLATE_NOOP("BIM_Box", "Box"),
|
|
"ToolTip": QT_TRANSLATE_NOOP(
|
|
"BIM_Box", "Graphically creates a generic box in the current document"
|
|
),
|
|
}
|
|
|
|
def IsActive(self):
|
|
v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph")
|
|
return v
|
|
|
|
def Activated(self):
|
|
import draftguitools.gui_trackers as DraftTrackers
|
|
|
|
# here we will store our points
|
|
self.points = []
|
|
# we build a special cube tracker which is a list of 4 rectangle trackers
|
|
self.cubetracker = []
|
|
self.LengthValue = 0
|
|
self.WidthValue = 0
|
|
self.HeightValue = 0
|
|
self.currentpoint = None
|
|
for i in range(4):
|
|
self.cubetracker.append(DraftTrackers.rectangleTracker())
|
|
if hasattr(FreeCADGui, "Snapper"):
|
|
FreeCADGui.Snapper.getPoint(
|
|
callback=self.PointCallback,
|
|
movecallback=self.MoveCallback,
|
|
extradlg=self.taskbox(),
|
|
)
|
|
|
|
def MoveCallback(self, point, snapinfo):
|
|
import DraftGeomUtils
|
|
import DraftVecUtils
|
|
|
|
self.currentpoint = point
|
|
if len(self.points) == 1:
|
|
# we have the base point already
|
|
self.Length.setText(
|
|
FreeCAD.Units.Quantity(
|
|
self.points[-1].sub(point).Length, FreeCAD.Units.Length
|
|
).UserString
|
|
)
|
|
self.Length.selectAll()
|
|
self.Length.setFocus()
|
|
elif len(self.points) == 2:
|
|
# now we already have our base line, we update the 1st rectangle
|
|
p = point
|
|
v1 = point.sub(self.points[1])
|
|
v4 = v1.cross(self.points[1].sub(self.points[0]))
|
|
if v4 and v4.Length:
|
|
n = (self.points[1].sub(self.points[0])).cross(v4)
|
|
if n and n.Length:
|
|
n = DraftVecUtils.project(v1, n)
|
|
p = self.points[1].add(n)
|
|
self.cubetracker[0].p3(p)
|
|
self.Width.setText(
|
|
FreeCAD.Units.Quantity(
|
|
self.cubetracker[0].getSize()[1], FreeCAD.Units.Length
|
|
).UserString
|
|
)
|
|
self.Width.selectAll()
|
|
self.Width.setFocus()
|
|
elif len(self.points) == 3:
|
|
h = DraftGeomUtils.distance_to_plane(point, self.cubetracker[0].p3(), self.normal)
|
|
w = self.normal * h
|
|
# then we update all rectangles
|
|
self.cubetracker[1].p3((self.cubetracker[0].p2()).add(w))
|
|
self.cubetracker[2].p3((self.cubetracker[0].p4()).add(w))
|
|
self.cubetracker[3].p1((self.cubetracker[0].p1()).add(w))
|
|
self.cubetracker[3].p3((self.cubetracker[0].p3()).add(w))
|
|
self.Height.setText(FreeCAD.Units.Quantity(h, FreeCAD.Units.Length).UserString)
|
|
self.Height.selectAll()
|
|
self.Height.setFocus()
|
|
|
|
def PointCallback(self, point, snapinfo):
|
|
if not point:
|
|
# cancelled
|
|
self._finish()
|
|
return
|
|
|
|
if len(self.points) == 0:
|
|
# this is our first clicked point, nothing to do just yet
|
|
self.points.append(point)
|
|
FreeCADGui.Snapper.getPoint(
|
|
last=point,
|
|
callback=self.PointCallback,
|
|
movecallback=self.MoveCallback,
|
|
extradlg=self.taskbox(),
|
|
)
|
|
elif len(self.points) == 1:
|
|
# this is our second point
|
|
baseline = point.sub(self.points[0])
|
|
self.points.append(point)
|
|
self._setupForWidthInput(baseline)
|
|
elif len(self.points) == 2:
|
|
# this is our third point
|
|
self.points.append(point)
|
|
self._setupForHeightInput()
|
|
elif len(self.points) == 3:
|
|
# finally we have all our points. Let's create the actual cube
|
|
self._makeBox()
|
|
self._finish()
|
|
|
|
def taskbox(self):
|
|
"sets up a taskbox widget"
|
|
|
|
from PySide import QtGui
|
|
|
|
wid = QtGui.QWidget()
|
|
ui = FreeCADGui.UiLoader()
|
|
wid.setWindowTitle(translate("BIM", "Box dimensions"))
|
|
grid = QtGui.QGridLayout(wid)
|
|
|
|
label1 = QtGui.QLabel(translate("BIM", "Length"))
|
|
self.Length = ui.createWidget("Gui::InputField")
|
|
l = FreeCAD.Units.Quantity(self.LengthValue, FreeCAD.Units.Length)
|
|
self.Length.setText(l.UserString)
|
|
grid.addWidget(label1, 0, 0, 1, 1)
|
|
grid.addWidget(self.Length, 0, 1, 1, 1)
|
|
if self.LengthValue:
|
|
self.Length.setEnabled(False)
|
|
|
|
label2 = QtGui.QLabel(translate("BIM", "Width"))
|
|
self.Width = ui.createWidget("Gui::InputField")
|
|
l = FreeCAD.Units.Quantity(self.WidthValue, FreeCAD.Units.Length)
|
|
self.Width.setText(l.UserString)
|
|
grid.addWidget(label2, 1, 0, 1, 1)
|
|
grid.addWidget(self.Width, 1, 1, 1, 1)
|
|
if self.WidthValue or (not self.LengthValue):
|
|
self.Width.setEnabled(False)
|
|
|
|
label3 = QtGui.QLabel(translate("BIM", "Height"))
|
|
self.Height = ui.createWidget("Gui::InputField")
|
|
l = FreeCAD.Units.Quantity(self.HeightValue, FreeCAD.Units.Length)
|
|
self.Height.setText(l.UserString)
|
|
grid.addWidget(label3, 2, 0, 1, 1)
|
|
grid.addWidget(self.Height, 2, 1, 1, 1)
|
|
if not self.WidthValue:
|
|
self.Height.setEnabled(False)
|
|
|
|
self.Length.valueChanged.connect(self.setLength)
|
|
self.Width.valueChanged.connect(self.setWidth)
|
|
self.Height.valueChanged.connect(self.setHeight)
|
|
self.Length.returnPressed.connect(self.setLengthUI)
|
|
self.Width.returnPressed.connect(self.setWidthUI)
|
|
self.Height.returnPressed.connect(self.setHeightUI)
|
|
return wid
|
|
|
|
def setLength(self, d):
|
|
self.LengthValue = d
|
|
|
|
def setWidth(self, d):
|
|
self.WidthValue = d
|
|
|
|
def setHeight(self, d):
|
|
self.HeightValue = d
|
|
|
|
def setLengthUI(self):
|
|
if (len(self.points) == 1) and self.currentpoint and self.LengthValue:
|
|
baseline = self.currentpoint.sub(self.points[0])
|
|
baseline.normalize()
|
|
baseline.multiply(self.LengthValue)
|
|
p2 = self.points[0].add(baseline)
|
|
self.points.append(p2)
|
|
self._setupForWidthInput(baseline)
|
|
|
|
def setWidthUI(self):
|
|
if (len(self.points) == 2) and self.currentpoint and self.WidthValue:
|
|
self.normal = self.cubetracker[0].getNormal()
|
|
if self.normal:
|
|
n = (self.points[1].sub(self.points[0])).cross(self.normal)
|
|
if n and n.Length:
|
|
n.normalize()
|
|
n.multiply(self.WidthValue)
|
|
p2 = self.points[1].add(n)
|
|
self.cubetracker[0].p3(p2)
|
|
self.points.append(p2)
|
|
self._setupForHeightInput()
|
|
|
|
def setHeightUI(self):
|
|
if (len(self.points) == 3) and self.HeightValue:
|
|
self._makeBox()
|
|
self._finish()
|
|
|
|
def _setupForWidthInput(self, baseline):
|
|
# we turn on only one of the rectangles
|
|
self.cubetracker[0].setPlane(baseline)
|
|
self.cubetracker[0].p1(self.points[0])
|
|
self.cubetracker[0].on()
|
|
FreeCADGui.Snapper.getPoint(
|
|
last=self.points[-1],
|
|
callback=self.PointCallback,
|
|
movecallback=self.MoveCallback,
|
|
extradlg=self.taskbox(),
|
|
)
|
|
|
|
def _setupForHeightInput(self):
|
|
# we can get the cubes Z axis from our first rectangle
|
|
self.normal = self.cubetracker[0].getNormal()
|
|
# we can therefore define the (u,v) planes of all rectangles
|
|
u = self.cubetracker[0].u
|
|
v = self.cubetracker[0].v
|
|
self.cubetracker[1].setPlane(u, self.normal)
|
|
self.cubetracker[2].setPlane(u, self.normal)
|
|
self.cubetracker[3].setPlane(u, v)
|
|
# and the origin points of the vertical rectangles
|
|
self.cubetracker[1].p1(self.cubetracker[0].p1())
|
|
self.cubetracker[2].p1(self.cubetracker[0].p3())
|
|
# finally we turn all rectangles on
|
|
for r in self.cubetracker:
|
|
r.on()
|
|
if hasattr(FreeCAD, "DraftWorkingPlane"):
|
|
FreeCAD.DraftWorkingPlane.save()
|
|
FreeCAD.DraftWorkingPlane.position = self.cubetracker[0].p3()
|
|
FreeCAD.DraftWorkingPlane.u = (
|
|
self.cubetracker[0].p3().sub(self.cubetracker[0].p4())
|
|
).normalize()
|
|
FreeCAD.DraftWorkingPlane.v = self.normal
|
|
FreeCAD.DraftWorkingPlane.axis = (
|
|
self.cubetracker[0].p2().sub(self.cubetracker[0].p3())
|
|
).normalize()
|
|
FreeCADGui.Snapper.setGrid()
|
|
FreeCADGui.Snapper.getPoint(
|
|
last=self.cubetracker[0].p3(),
|
|
callback=self.PointCallback,
|
|
movecallback=self.MoveCallback,
|
|
extradlg=self.taskbox(),
|
|
)
|
|
|
|
def _makeBox(self):
|
|
import DraftGeomUtils
|
|
|
|
p1 = self.cubetracker[0].p1()
|
|
p2 = self.cubetracker[0].p2()
|
|
p3 = self.cubetracker[0].p4()
|
|
pla = DraftGeomUtils.placement_from_points(p1, p2, p3)
|
|
if self.normal.isEqual(pla.Rotation.multVec(FreeCAD.Vector(0, 0, 1)), 1e-6):
|
|
if self.HeightValue < 0.0:
|
|
pla = DraftGeomUtils.placement_from_points(p1, p3, p2)
|
|
self.LengthValue, self.WidthValue = self.WidthValue, self.LengthValue
|
|
else:
|
|
if self.HeightValue > 0.0:
|
|
pla = DraftGeomUtils.placement_from_points(p1, p3, p2)
|
|
self.LengthValue, self.WidthValue = self.WidthValue, self.LengthValue
|
|
doc = FreeCAD.ActiveDocument
|
|
doc.openTransaction(translate("Arch","Create Box"))
|
|
cube = doc.addObject("Part::Box", "Cube")
|
|
cube.Placement = pla
|
|
cube.Length = self.LengthValue
|
|
cube.Width = self.WidthValue
|
|
cube.Height = abs(self.HeightValue)
|
|
doc.commitTransaction()
|
|
doc.recompute()
|
|
|
|
def _finish(self):
|
|
FreeCADGui.Snapper.getPoint()
|
|
FreeCADGui.Snapper.off()
|
|
for c in self.cubetracker:
|
|
c.finalize()
|
|
if hasattr(FreeCADGui, "draftToolBar"):
|
|
FreeCADGui.draftToolBar.offUi()
|
|
if hasattr(FreeCAD, "DraftWorkingPlane"):
|
|
FreeCAD.DraftWorkingPlane.restore()
|
|
FreeCADGui.Snapper.setGrid()
|
|
|
|
|
|
FreeCADGui.addCommand("BIM_Box", BIM_Box())
|