diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp
index 67b61722f..c8d70a85f 100644
--- a/src/Gui/CommandView.cpp
+++ b/src/Gui/CommandView.cpp
@@ -1054,6 +1054,54 @@ void StdCmdViewAxo::activated(int iMsg)
doCommand(Command::Gui,"Gui.activeDocument().activeView().viewAxometric()");
}
+//===========================================================================
+// Std_ViewRotateLeft
+//===========================================================================
+DEF_3DV_CMD(StdCmdViewRotateLeft);
+
+StdCmdViewRotateLeft::StdCmdViewRotateLeft()
+ : Command("Std_ViewRotateLeft")
+{
+ sGroup = QT_TR_NOOP("Standard-View");
+ sMenuText = QT_TR_NOOP("Rotate Left");
+ sToolTipText = QT_TR_NOOP("Rotate the view by 90° counter-clockwise");
+ sWhatsThis = "Std_ViewXX";
+ sStatusTip = QT_TR_NOOP("Rotate the view by 90° counter-clockwise");
+ sPixmap = "view-rotate-left";
+ //sAccel = "Shift Left";
+ eType = Alter3DView;
+}
+
+void StdCmdViewRotateLeft::activated(int iMsg)
+{
+ doCommand(Command::Gui,"Gui.activeDocument().activeView().viewRotateLeft()");
+}
+
+
+//===========================================================================
+// Std_ViewRotateRight
+//===========================================================================
+DEF_3DV_CMD(StdCmdViewRotateRight);
+
+StdCmdViewRotateRight::StdCmdViewRotateRight()
+ : Command("Std_ViewRotateRight")
+{
+ sGroup = QT_TR_NOOP("Standard-View");
+ sMenuText = QT_TR_NOOP("Rotate Right");
+ sToolTipText = QT_TR_NOOP("Rotate the view by 90° clockwise");
+ sWhatsThis = "Std_ViewXX";
+ sStatusTip = QT_TR_NOOP("Rotate the view by 90° clockwise");
+ sPixmap = "view-rotate-right";
+ //sAccel = "Shift Right";
+ eType = Alter3DView;
+}
+
+void StdCmdViewRotateRight::activated(int iMsg)
+{
+ doCommand(Command::Gui,"Gui.activeDocument().activeView().viewRotateRight()");
+}
+
+
//===========================================================================
// Std_ViewFitAll
//===========================================================================
@@ -2030,6 +2078,8 @@ void CreateViewStdCommands(void)
rcCmdMgr.addCommand(new StdCmdViewAxo());
rcCmdMgr.addCommand(new StdCmdViewFitAll());
rcCmdMgr.addCommand(new StdCmdViewFitSelection());
+ rcCmdMgr.addCommand(new StdCmdViewRotateLeft());
+ rcCmdMgr.addCommand(new StdCmdViewRotateRight());
rcCmdMgr.addCommand(new StdCmdViewExample1());
rcCmdMgr.addCommand(new StdCmdViewExample2());
diff --git a/src/Gui/Icons/Makefile.am b/src/Gui/Icons/Makefile.am
index 9366066f5..70c30f0f4 100644
--- a/src/Gui/Icons/Makefile.am
+++ b/src/Gui/Icons/Makefile.am
@@ -86,6 +86,8 @@ EXTRA_DIST = \
view-zoom-in.svg \
view-zoom-out.svg \
view-zoom-selection.svg \
+ view-rotate-left.svg \
+ view-rotate-right.svg \
Tree_Annotation.svg \
Tree_Dimension.svg \
Tree_Python.svg \
diff --git a/src/Gui/Icons/resource.qrc b/src/Gui/Icons/resource.qrc
index f64cb89bd..78a314d16 100644
--- a/src/Gui/Icons/resource.qrc
+++ b/src/Gui/Icons/resource.qrc
@@ -68,6 +68,8 @@
view-zoom-fit.svg
view-zoom-in.svg
view-zoom-out.svg
+ view-rotate-left.svg
+ view-rotate-right.svg
view-measurement.svg
view-zoom-selection.svg
Tree_Annotation.svg
diff --git a/src/Gui/Icons/view-rotate-left.svg b/src/Gui/Icons/view-rotate-left.svg
new file mode 100644
index 000000000..71f10ca01
--- /dev/null
+++ b/src/Gui/Icons/view-rotate-left.svg
@@ -0,0 +1,230 @@
+
+
+
+
diff --git a/src/Gui/Icons/view-rotate-right.svg b/src/Gui/Icons/view-rotate-right.svg
new file mode 100644
index 000000000..4cbb4fc6b
--- /dev/null
+++ b/src/Gui/Icons/view-rotate-right.svg
@@ -0,0 +1,242 @@
+
+
+
+
diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp
index 9ef5e9ccb..b1e1e51a6 100644
--- a/src/Gui/View3DPy.cpp
+++ b/src/Gui/View3DPy.cpp
@@ -76,6 +76,8 @@ void View3DInventorPy::init_type()
add_varargs_method("viewRight",&View3DInventorPy::viewRight,"viewRight()");
add_varargs_method("viewTop",&View3DInventorPy::viewTop,"viewTop()");
add_varargs_method("viewAxometric",&View3DInventorPy::viewAxometric,"viewAxometric()");
+ add_varargs_method("viewRotateLeft",&View3DInventorPy::viewRotateLeft,"viewRotateLeft()");
+ add_varargs_method("viewRotateRight",&View3DInventorPy::viewRotateRight,"viewRotateRight()");
add_varargs_method("viewPosition",&View3DInventorPy::viewPosition,"viewPosition()");
add_varargs_method("startAnimating",&View3DInventorPy::startAnimating,"startAnimating()");
add_varargs_method("stopAnimating",&View3DInventorPy::stopAnimating,"stopAnimating()");
@@ -407,6 +409,58 @@ Py::Object View3DInventorPy::viewAxometric(const Py::Tuple& args)
return Py::None();
}
+Py::Object View3DInventorPy::viewRotateLeft(const Py::Tuple& args)
+{
+ if (!PyArg_ParseTuple(args.ptr(), ""))
+ throw Py::Exception();
+
+ try {
+ SoCamera* cam = _view->getViewer()->getCamera();
+ SbRotation rot = cam->orientation.getValue();
+ SbVec3f vdir(0, 0, -1);
+ rot.multVec(vdir, vdir);
+ SbRotation nrot(vdir, M_PI/2);
+ cam->orientation.setValue(rot*nrot);
+ }
+ catch (const Base::Exception& e) {
+ throw Py::Exception(e.what());
+ }
+ catch (const std::exception& e) {
+ throw Py::Exception(e.what());
+ }
+ catch(...) {
+ throw Py::Exception("Unknown C++ exception");
+ }
+
+ return Py::None();
+}
+
+Py::Object View3DInventorPy::viewRotateRight(const Py::Tuple& args)
+{
+ if (!PyArg_ParseTuple(args.ptr(), ""))
+ throw Py::Exception();
+
+ try {
+ SoCamera* cam = _view->getViewer()->getCamera();
+ SbRotation rot = cam->orientation.getValue();
+ SbVec3f vdir(0, 0, -1);
+ rot.multVec(vdir, vdir);
+ SbRotation nrot(vdir, -M_PI/2);
+ cam->orientation.setValue(rot*nrot);
+ }
+ catch (const Base::Exception& e) {
+ throw Py::Exception(e.what());
+ }
+ catch (const std::exception& e) {
+ throw Py::Exception(e.what());
+ }
+ catch(...) {
+ throw Py::Exception("Unknown C++ exception");
+ }
+
+ return Py::None();
+}
+
Py::Object View3DInventorPy::setCameraOrientation(const Py::Tuple& args)
{
PyObject* o;
diff --git a/src/Gui/View3DPy.h b/src/Gui/View3DPy.h
index 3e9e1218b..43fd65318 100644
--- a/src/Gui/View3DPy.h
+++ b/src/Gui/View3DPy.h
@@ -55,6 +55,8 @@ public:
Py::Object viewTop(const Py::Tuple&);
Py::Object viewAxometric(const Py::Tuple&);
Py::Object viewPosition(const Py::Tuple&);
+ Py::Object viewRotateLeft(const Py::Tuple&);
+ Py::Object viewRotateRight(const Py::Tuple&);
Py::Object startAnimating(const Py::Tuple&);
Py::Object stopAnimating(const Py::Tuple&);
Py::Object setAnimationEnabled(const Py::Tuple&);
diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp
index e173b22e2..b3e1115d0 100644
--- a/src/Gui/Workbench.cpp
+++ b/src/Gui/Workbench.cpp
@@ -395,7 +395,8 @@ void StdWorkbench::setupContextMenu(const char* recipient, MenuItem* item) const
StdViews->setCommand( "Standard views" );
*StdViews << "Std_ViewAxo" << "Separator" << "Std_ViewFront" << "Std_ViewTop" << "Std_ViewRight"
- << "Std_ViewRear" << "Std_ViewBottom" << "Std_ViewLeft";
+ << "Std_ViewRear" << "Std_ViewBottom" << "Std_ViewLeft"
+ << "Separator" << "Std_ViewRotateLeft" << "Std_ViewRotateRight";
*item << "Std_ViewFitAll" << "Std_ViewFitSelection" << StdViews
<< "Separator" << "Std_ViewDockUndockFullscreen";
@@ -447,7 +448,8 @@ MenuItem* StdWorkbench::setupMenuBar() const
*stdviews << "Std_ViewFitAll" << "Std_ViewFitSelection" << "Std_ViewAxo"
<< "Separator" << "Std_ViewFront" << "Std_ViewRight"
<< "Std_ViewTop" << "Separator" << "Std_ViewRear"
- << "Std_ViewLeft" << "Std_ViewBottom";
+ << "Std_ViewLeft" << "Std_ViewBottom"
+ << "Separator" << "Std_ViewRotateLeft" << "Std_ViewRotateRight";
// stereo
MenuItem* view3d = new MenuItem;
diff --git a/src/Mod/Arch/ArchAxis.py b/src/Mod/Arch/ArchAxis.py
index 42af3160e..7dc9f9f8d 100644
--- a/src/Mod/Arch/ArchAxis.py
+++ b/src/Mod/Arch/ArchAxis.py
@@ -21,10 +21,10 @@
#* *
#***************************************************************************
-import FreeCAD,FreeCADGui,Draft,ArchComponent,math
+import FreeCAD,FreeCADGui,Draft,math
from draftlibs import fcvec
from FreeCAD import Vector
-from PyQt4 import QtCore
+from PyQt4 import QtCore, QtGui
from pivy import coin
__title__="FreeCAD Axis System"
@@ -61,17 +61,15 @@ class _CommandAxis:
makeAxis(5,1)
FreeCAD.ActiveDocument.commitTransaction()
-class _Axis(ArchComponent.Component):
+class _Axis:
"The Axis object"
def __init__(self,obj):
obj.addProperty("App::PropertyFloatList","Distances","Base", "The intervals between axes")
obj.addProperty("App::PropertyFloatList","Angles","Base", "The angles of each axis")
- obj.addProperty("App::PropertyFloatList","Limits","Base", "The inferior and superior drawing limits")
+ obj.addProperty("App::PropertyFloat","Length","Base", "The length of the axes")
self.Type = "Axis"
-
- obj.Limits=[0.0,1.0]
+ obj.Length=1.0
obj.Proxy = self
- self.Object = obj
def execute(self,obj):
self.createGeometry(obj)
@@ -90,25 +88,29 @@ class _Axis(ArchComponent.Component):
for i in range(len(obj.Distances)):
dist += obj.Distances[i]
ang = math.radians(obj.Angles[i])
- p1 = Vector(dist,obj.Limits[0],0)
- p2 = Vector(dist+(obj.Limits[1]/math.cos(ang))*math.sin(ang),obj.Limits[1],0)
+ p1 = Vector(dist,0,0)
+ p2 = Vector(dist+(obj.Length/math.cos(ang))*math.sin(ang),obj.Length,0)
geoms.append(Part.Line(p1,p2).toShape())
if geoms:
obj.Shape = Part.Compound(geoms)
obj.Placement = pl
-class _ViewProviderAxis(ArchComponent.ViewProviderComponent):
+class _ViewProviderAxis:
"A View Provider for the Axis object"
def __init__(self,vobj):
vobj.addProperty("App::PropertyLength","BubbleSize","Base", "The size of the axis bubbles")
vobj.addProperty("App::PropertyEnumeration","NumerationStyle","Base", "The numeration style")
- vobj.NumerationStyle = ["1,2,3","01,02,03","A,B,C","a,b,c","I,II,III"]
+ vobj.addProperty("App::PropertyEnumeration","DrawStyle","Base", "The representation style")
+ vobj.NumerationStyle = ["1,2,3","01,02,03","001,002,003","A,B,C","a,b,c","I,II,III","L0,L1,L2"]
+ vobj.DrawStyle = ["solid","dotted","dashed","dashdot"]
vobj.Proxy = self
self.Object = vobj.Object
self.ViewObject = vobj
vobj.BubbleSize = .1
vobj.LineWidth = 1
+ vobj.LineColor = (0.13,0.15,0.37)
+ vobj.DrawStyle = "dashdot"
def getIcon(self):
return ":/icons/Arch_Axis_Tree.svg"
@@ -117,10 +119,59 @@ class _ViewProviderAxis(ArchComponent.ViewProviderComponent):
return []
def attach(self, vobj):
- self.Object = vobj.Object
self.ViewObject = vobj
self.bubbles = None
+ def getNumber(self,num):
+ chars = "abcdefghijklmnopqrstuvwxyz"
+ roman=(('M',1000),('CM',900),('D',500),('CD',400),
+ ('C',100),('XC',90),('L',50),('XL',40),
+ ('X',10),('IX',9),('V',5),('IV',4),('I',1))
+ if self.ViewObject.NumerationStyle == "1,2,3":
+ return str(num+1)
+ elif self.ViewObject.NumerationStyle == "01,02,03":
+ return str(num+1).zfill(2)
+ elif self.ViewObject.NumerationStyle == "001,002,003":
+ return str(num+1).zfill(3)
+ elif self.ViewObject.NumerationStyle == "A,B,C":
+ result = ""
+ base = num/26
+ if base:
+ result += chars[base].upper()
+ remainder = num % 26
+ result += chars[remainder].upper()
+ return result
+ elif self.ViewObject.NumerationStyle == "a,b,c":
+ result = ""
+ base = num/26
+ if base:
+ result += chars[base]
+ remainder = num % 26
+ result += chars[remainder]
+ return result
+ elif self.ViewObject.NumerationStyle == "I,II,III":
+ result = ""
+ num += 1
+ for numeral, integer in roman:
+ while num >= integer:
+ result += numeral
+ num -= integer
+ return result
+ elif self.ViewObject.NumerationStyle == "L0,L1,L2":
+ return "L"+str(num)
+ return ""
+
+ def setStyle(self):
+ ds = self.ViewObject.RootNode.getChild(2).getChild(0).getChild(0).getChild(1)
+ if self.ViewObject.DrawStyle == "solid":
+ ds.linePattern = 0xffff
+ elif self.ViewObject.DrawStyle == "dotted":
+ ds.linePattern = 0x0f0f
+ elif self.ViewObject.DrawStyle == "dashed":
+ ds.linePattern = 0xf00f
+ elif self.ViewObject.DrawStyle == "dashdot":
+ ds.linePattern = 0xff88
+
def makeBubbles(self):
import Part
rn = self.ViewObject.RootNode.getChild(2).getChild(0).getChild(0)
@@ -128,9 +179,13 @@ class _ViewProviderAxis(ArchComponent.ViewProviderComponent):
rn.removeChild(self.bubbles)
self.bubbles = None
self.bubbles = coin.SoSeparator()
- for i in range(len(self.Object.Distances)):
- invpl = self.Object.Placement.inverse()
- verts = self.Object.Shape.Edges[i].Vertexes
+ isep = coin.SoSeparator()
+ self.bubblestyle = coin.SoDrawStyle()
+ self.bubblestyle.linePattern = 0xffff
+ self.bubbles.addChild(self.bubblestyle)
+ for i in range(len(self.ViewObject.Object.Distances)):
+ invpl = self.ViewObject.Object.Placement.inverse()
+ verts = self.ViewObject.Object.Shape.Edges[i].Vertexes
p1 = invpl.multVec(verts[0].Point)
p2 = invpl.multVec(verts[1].Point)
dv = p2.sub(p1)
@@ -153,12 +208,12 @@ class _ViewProviderAxis(ArchComponent.ViewProviderComponent):
fo.size = rad*100
tx = coin.SoText2()
tx.justification = coin.SoText2.CENTER
- tx.string = str(i)
+ tx.string = self.getNumber(i)
st.addChild(tr)
st.addChild(fo)
st.addChild(tx)
- self.bubbles.addChild(st)
-
+ isep.addChild(st)
+ self.bubbles.addChild(isep)
rn.addChild(self.bubbles)
def updateData(self, obj, prop):
@@ -169,7 +224,140 @@ class _ViewProviderAxis(ArchComponent.ViewProviderComponent):
def onChanged(self, vobj, prop):
if prop in ["NumerationStyle","BubbleSize"]:
self.makeBubbles()
+ elif prop == "DrawStyle":
+ self.setStyle()
+ elif prop == "LineWidth":
+ if self.bubbles:
+ self.bubblestyle.lineWidth = vobj.LineWidth
+ return
+
+ def setEdit(self,vobj,mode):
+ taskd = _AxisTaskPanel()
+ taskd.obj = vobj.Object
+ taskd.update()
+ FreeCADGui.Control.showDialog(taskd)
+ return True
+
+ def unsetEdit(self,vobj,mode):
+ FreeCADGui.Control.closeDialog()
return
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self,state):
+ return None
+
+
+
+class _AxisTaskPanel:
+ '''The editmode TaskPanel for Axis objects'''
+ def __init__(self):
+ # the panel has a tree widget that contains categories
+ # for the subcomponents, such as additions, subtractions.
+ # the categories are shown only if they are not empty.
+
+ self.obj = None
+ self.form = QtGui.QWidget()
+ self.form.setObjectName("TaskPanel")
+ self.grid = QtGui.QGridLayout(self.form)
+ self.grid.setObjectName("grid")
+ self.title = QtGui.QLabel(self.form)
+ self.grid.addWidget(self.title, 0, 0, 1, 2)
+
+ # tree
+ self.tree = QtGui.QTreeWidget(self.form)
+ self.grid.addWidget(self.tree, 1, 0, 1, 2)
+ self.tree.setColumnCount(3)
+ self.tree.header().resizeSection(0,50)
+ self.tree.header().resizeSection(1,80)
+ self.tree.header().resizeSection(2,60)
+
+ # buttons
+ self.addButton = QtGui.QPushButton(self.form)
+ self.addButton.setObjectName("addButton")
+ self.addButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg"))
+ self.grid.addWidget(self.addButton, 3, 0, 1, 1)
+ self.addButton.setEnabled(True)
+
+ self.delButton = QtGui.QPushButton(self.form)
+ self.delButton.setObjectName("delButton")
+ self.delButton.setIcon(QtGui.QIcon(":/icons/Arch_Remove.svg"))
+ self.grid.addWidget(self.delButton, 3, 1, 1, 1)
+ self.delButton.setEnabled(True)
+
+ self.okButton = QtGui.QPushButton(self.form)
+ self.okButton.setObjectName("okButton")
+ self.okButton.setIcon(QtGui.QIcon(":/icons/edit_OK.svg"))
+ self.grid.addWidget(self.okButton, 4, 0, 1, 2)
+
+ QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement)
+ QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement)
+ QtCore.QObject.connect(self.okButton, QtCore.SIGNAL("clicked()"), self.finish)
+ self.update()
+
+ def isAllowedAlterSelection(self):
+ return False
+
+ def isAllowedAlterView(self):
+ return True
+
+ def getStandardButtons(self):
+ return 0
+ def update(self):
+ 'fills the treewidget'
+ self.tree.clear()
+ if self.obj:
+ for i in range(len(self.obj.Distances)):
+ item = QtGui.QTreeWidgetItem(self.tree)
+ item.setText(0,str(i+1))
+ item.setText(1,str(self.obj.Distances[i]))
+ item.setText(2,str(self.obj.Angles[i]))
+ item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
+ item.setTextAlignment(0,QtCore.Qt.AlignLeft)
+ self.retranslateUi(self.form)
+
+ def addElement(self):
+ item = QtGui.QTreeWidgetItem(self.tree)
+ item.setText(0,str(self.tree.topLevelItemCount()))
+ item.setText(1,"1.0")
+ item.setText(2,"0.0")
+ item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
+ self.resetObject()
+
+ def removeElement(self):
+ it = self.tree.currentItem()
+ if it:
+ nr = int(it.text(0))-1
+ self.resetObject(remove=nr)
+ self.update()
+
+ def resetObject(self,remove=None):
+ d = []
+ a = []
+ for i in range(self.tree.topLevelItemCount()):
+ it = self.tree.findItems(str(i+1),QtCore.Qt.MatchExactly,0)[0]
+ if (remove == None) or (remove != i):
+ d.append(float(it.text(1)))
+ a.append(float(it.text(2)))
+ self.obj.Distances = d
+ self.obj.Angles = a
+ FreeCAD.ActiveDocument.recompute()
+
+ def finish(self):
+ self.resetObject()
+ if self.obj:
+ self.obj.ViewObject.finishEditing()
+
+ def retranslateUi(self, TaskPanel):
+ TaskPanel.setWindowTitle(QtGui.QApplication.translate("Arch", "Axes", None, QtGui.QApplication.UnicodeUTF8))
+ self.delButton.setText(QtGui.QApplication.translate("Arch", "Remove", None, QtGui.QApplication.UnicodeUTF8))
+ self.addButton.setText(QtGui.QApplication.translate("Arch", "Add", None, QtGui.QApplication.UnicodeUTF8))
+ self.okButton.setText(QtGui.QApplication.translate("Arch", "Done", None, QtGui.QApplication.UnicodeUTF8))
+ self.title.setText(QtGui.QApplication.translate("Arch", "Distances and angles between axes", None, QtGui.QApplication.UnicodeUTF8))
+ self.tree.setHeaderLabels([QtGui.QApplication.translate("Arch", "Axis", None, QtGui.QApplication.UnicodeUTF8),
+ QtGui.QApplication.translate("Arch", "Distance", None, QtGui.QApplication.UnicodeUTF8),
+ QtGui.QApplication.translate("Arch", "Angle", None, QtGui.QApplication.UnicodeUTF8)])
+
FreeCADGui.addCommand('Arch_Axis',_CommandAxis())
diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py
index f749ff634..b1607b971 100644
--- a/src/Mod/Arch/ArchComponent.py
+++ b/src/Mod/Arch/ArchComponent.py
@@ -34,6 +34,7 @@ def addToComponent(compobject,addobject,mod=None):
"Components", the first one that exists in the component. Mod
can be set to one of those attributes ("Objects", Base", etc...)
to override the default.'''
+ import Draft
if compobject == addobject: return
# first check is already there
found = False
@@ -51,11 +52,17 @@ def addToComponent(compobject,addobject,mod=None):
if hasattr(compobject,mod):
if mod == "Base":
setattr(compobject,mod,addobject)
+ addobject.ViewObject.hide()
+ elif mod == "Axes":
+ if Draft.getType(addobject) == "Axis":
+ l = getattr(compobject,mod)
+ l.append(addobject)
+ setattr(compobject,mod,l)
else:
l = getattr(compobject,mod)
l.append(addobject)
setattr(compobject,mod,l)
- addobject.ViewObject.hide()
+ addobject.ViewObject.hide()
else:
for a in attribs[:3]:
if hasattr(compobject,a):
@@ -103,7 +110,7 @@ class ComponentTaskPanel:
# the categories are shown only if they are not empty.
self.obj = None
- self.attribs = ["Base","Additions","Subtractions","Objects","Components"]
+ self.attribs = ["Base","Additions","Subtractions","Objects","Components","Axes"]
self.form = QtGui.QWidget()
self.form.setObjectName("TaskPanel")
self.grid = QtGui.QGridLayout(self.form)
@@ -150,9 +157,6 @@ class ComponentTaskPanel:
def getStandardButtons(self):
return 0
- def removeElement(self):
- return
-
def check(self,wid,col):
if not wid.parent():
self.delButton.setEnabled(False)
@@ -236,6 +240,7 @@ class ComponentTaskPanel:
self.treeAdditions.setText(0,QtGui.QApplication.translate("Arch", "Additions", None, QtGui.QApplication.UnicodeUTF8))
self.treeSubtractions.setText(0,QtGui.QApplication.translate("Arch", "Subtractions", None, QtGui.QApplication.UnicodeUTF8))
self.treeObjects.setText(0,QtGui.QApplication.translate("Arch", "Objects", None, QtGui.QApplication.UnicodeUTF8))
+ self.treeAxes.setText(0,QtGui.QApplication.translate("Arch", "Axes", None, QtGui.QApplication.UnicodeUTF8))
self.treeComponents.setText(0,QtGui.QApplication.translate("Arch", "Components", None, QtGui.QApplication.UnicodeUTF8))
class Component:
@@ -251,7 +256,6 @@ class Component:
"The normal extrusion direction of this wall (keep (0,0,0) for automatic normal)")
obj.Proxy = self
self.Type = "Component"
- self.Object = obj
self.Subvolume = None
diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py
index af7674e42..daa7b25e9 100644
--- a/src/Mod/Arch/ArchStructure.py
+++ b/src/Mod/Arch/ArchStructure.py
@@ -84,15 +84,32 @@ class _Structure(ArchComponent.Component):
"The width of this element, if not based on a profile")
obj.addProperty("App::PropertyLength","Height","Base",
"The height or extrusion depth of this element. Keep 0 for automatic")
+ obj.addProperty("App::PropertyLinkList","Axes","Base",
+ "Axes systems this structure is built on")
self.Type = "Structure"
def execute(self,obj):
self.createGeometry(obj)
def onChanged(self,obj,prop):
- if prop in ["Base","Length","Width","Height","Normal","Additions","Subtractions"]:
+ if prop in ["Base","Length","Width","Height","Normal","Additions","Subtractions","Axes"]:
self.createGeometry(obj)
+ def getAxisPoints(self,obj):
+ "returns the gridpoints of linked axes"
+ from draftlibs import fcgeo
+ pts = []
+ if len(obj.Axes) == 1:
+ for e in obj.Axes[0].Shape.Edges:
+ pts.append(e.Vertexes[0].Point)
+ elif len(obj.Axes) >= 2:
+ set1 = obj.Axes[0].Shape.Edges
+ set2 = obj.Axes[1].Shape.Edges
+ for e1 in set1:
+ for e2 in set2:
+ pts.extend(fcgeo.findIntersection(e1,e2))
+ return pts
+
def createGeometry(self,obj):
import Part
from draftlibs import fcgeo
@@ -158,7 +175,16 @@ class _Structure(ArchComponent.Component):
base = base.cut(hole.Shape)
hole.ViewObject.hide() # to be removed
if base:
- obj.Shape = base
+ pts = self.getAxisPoints(obj)
+ if pts:
+ fsh = []
+ for p in pts:
+ sh = base.copy()
+ sh.translate(p)
+ fsh.append(sh)
+ obj.Shape = Part.makeCompound(fsh)
+ else:
+ obj.Shape = base
if not fcgeo.isNull(pl): obj.Placement = pl
class _ViewProviderStructure(ArchComponent.ViewProviderComponent):
diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py
index bbca0e2b1..347d25071 100644
--- a/src/Mod/Arch/importIFC.py
+++ b/src/Mod/Arch/importIFC.py
@@ -86,36 +86,37 @@ def readOpenShell(filename):
import Mesh
getIfcOpenShell()
if IfcOpenShell:
- IfcOpenShell.Init(filename)
- while True:
- obj = IfcOpenShell.Get()
- print "parsing ",obj.guid,": ",obj.name," of type ",obj.type
- meshdata = []
- n = obj.name
- if not n: n = "Unnamed"
- f = obj.mesh.faces
- v = obj.mesh.verts
- m = obj.matrix
- print "verts: ",len(v)," faces: ",len(f)
- mat = FreeCAD.Matrix(m[0], m[1], m[2], 0,
- m[3], m[4], m[5], 0,
- m[6], m[7], m[8], 0,
- m[9], m[10], m[11], 1)
- for i in range(0, len(f), 3):
- print "face ",f[i],f[i+1],f[i+2]
- face = []
- print "i:",i
- for j in range(3):
- vi = f[i+j]*3
- print "vi:",vi
- face.append([v[vi],v[vi+1],v[vi+2]])
- meshdata.append(face)
- newmesh = Mesh.Mesh(meshdata)
- mobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature",n)
- mobj.Mesh = newmesh
- mobj.Placement = FreeCAD.Placement(mat)
- if not IfcOpenShell.Next():
- break
+ if IfcOpenShell.Init(filename):
+ while True:
+ obj = IfcOpenShell.Get()
+ print "parsing ",obj.guid,": ",obj.name," of type ",obj.type
+ meshdata = []
+ n = obj.name
+ if not n: n = "Unnamed"
+ f = obj.mesh.faces
+ v = obj.mesh.verts
+ m = obj.matrix
+ print "verts: ",len(v)," faces: ",len(f)
+ mat = FreeCAD.Matrix(m[0], m[3], m[6], m[9],
+ m[1], m[4], m[7], m[10],
+ m[2], m[5], m[8], m[11],
+ 0, 0, 0, 1)
+ for i in range(0, len(f), 3):
+ print "face ",f[i],f[i+1],f[i+2]
+ face = []
+ print "i:",i
+ for j in range(3):
+ vi = f[i+j]*3
+ print "vi:",vi
+ face.append([v[vi],v[vi+1],v[vi+2]])
+ meshdata.append(face)
+ newmesh = Mesh.Mesh(meshdata)
+ mobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature",n)
+ mobj.Mesh = newmesh
+ mobj.Placement = FreeCAD.Placement(mat)
+ if not IfcOpenShell.Next():
+ break
+ IfcOpenShell.CleanUp()
return None
def read(filename):
diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py
index 22457a4e2..b6e22cd6e 100644
--- a/src/Mod/Draft/Draft.py
+++ b/src/Mod/Draft/Draft.py
@@ -283,21 +283,40 @@ def select(objs=None):
FreeCADGui.Selection.addSelection(obj)
def makeCircle(radius, placement=None, face=True, startangle=None, endangle=None, support=None):
- '''makeCircle(radius,[placement,face,startangle,endangle]): Creates a circle
- object with given radius. If placement is given, it is
+ '''makeCircle(radius,[placement,face,startangle,endangle])
+ or makeCircle(edge,[face]):
+ Creates a circle object with given radius. If placement is given, it is
used. If face is False, the circle is shown as a
wireframe, otherwise as a face. If startangle AND endangle are given
- (in degrees), they are used and the object appears as an arc.'''
+ (in degrees), they are used and the object appears as an arc. If an edge
+ is passed, its Curve must be a Part.Circle'''
+ import Part
if placement: typecheck([(placement,FreeCAD.Placement)], "makeCircle")
obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Circle")
_Circle(obj)
_ViewProviderDraft(obj.ViewObject)
- obj.Radius = radius
+ if isinstance(radius,Part.Edge):
+ edge = radius
+ if isinstance(edge.Curve,Part.Circle):
+ obj.Radius = edge.Curve.Radius
+ placement = FreeCAD.Placement(edge.Placement)
+ delta = edge.Curve.Center.sub(placement.Base)
+ placement.move(delta)
+ if len(edge.Vertexes) > 1:
+ ref = placement.multVec(FreeCAD.Vector(1,0,0))
+ v1 = (edge.Vertexes[0].Point).sub(edge.Curve.Center)
+ v2 = (edge.Vertexes[-1].Point).sub(edge.Curve.Center)
+ a1 = -math.degrees(fcvec.angle(v1,ref))
+ a2 = -math.degrees(fcvec.angle(v2,ref))
+ obj.FirstAngle = a1
+ obj.LastAngle = a2
+ else:
+ obj.Radius = radius
+ if (startangle != None) and (endangle != None):
+ if startangle == -0: startangle = 0
+ obj.FirstAngle = startangle
+ obj.LastAngle = endangle
if not face: obj.ViewObject.DisplayMode = "Wireframe"
- if (startangle != None) and (endangle != None):
- if startangle == -0: startangle = 0
- obj.FirstAngle = startangle
- obj.LastAngle = endangle
obj.Support = support
if placement: obj.Placement = placement
formatObject(obj)
@@ -517,13 +536,13 @@ def makeCopy(obj):
_Block(newobj)
_ViewProviderDraftPart(newobj.ViewObject)
elif getType(obj) == "Structure":
- import Structure
- Structure._Structure(newobj)
- Structure._ViewProviderStructure(newobj.ViewObject)
+ import Arch
+ Arch._Structure(newobj)
+ Arch._ViewProviderStructure(newobj.ViewObject)
elif getType(obj) == "Wall":
- import Wall
- Wall._Wall(newobj)
- Wall._ViewProviderWall(newobj.ViewObject)
+ import Arch
+ Arch._Wall(newobj)
+ Arch._ViewProviderWall(newobj.ViewObject)
elif obj.isDerivedFrom("Part::Feature"):
newobj.Shape = obj.Shape
else:
@@ -937,6 +956,7 @@ def draftify(objectslist,makeblock=False):
(objectslist can also be a single object) into a Draft parametric
wire. If makeblock is True, multiple objects will be grouped in a block'''
from draftlibs import fcgeo
+ import Part
if not isinstance(objectslist,list):
objectslist = [objectslist]
newobjlist = []
@@ -944,8 +964,11 @@ def draftify(objectslist,makeblock=False):
if obj.isDerivedFrom('Part::Feature'):
for w in obj.Shape.Wires:
if fcgeo.hasCurves(w):
- nobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
- nobj.Shape = w
+ if (len(w.Edges) == 1) and isinstance(w.Edges[0].Curve,Part.Circle):
+ nobj = makeCircle(w.Edges[0])
+ else:
+ nobj = FreeCAD.ActiveDocument.addObject("Part::Feature",obj.Name)
+ nobj.Shape = w
else:
nobj = makeWire(w)
if obj.Shape.Faces:
@@ -1222,6 +1245,7 @@ def makeSketch(objectslist,autoconstraints=False,addTo=None,name="Sketch"):
nobj = addTo
else:
nobj = FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject",name)
+ nobj.ViewObject.Autoconstraints = False
for obj in objectslist:
ok = False
tp = getType(obj)
@@ -1274,7 +1298,9 @@ def makeSketch(objectslist,autoconstraints=False,addTo=None,name="Sketch"):
if fcgeo.hasOnlyWires(obj.Shape):
for w in obj.Shape.Wires:
for edge in fcgeo.sortEdges(w.Edges):
- nobj.addGeometry(fcgeo.geom(edge))
+ g = fcgeo.geom(edge)
+ if g:
+ nobj.addGeometry(g)
if autoconstraints:
last = nobj.GeometryCount
segs = range(last-len(w.Edges),last-1)
@@ -1429,7 +1455,7 @@ class _ViewProviderDimension:
obj.addProperty("App::PropertyLength","LineWidth","Base","Line width")
obj.addProperty("App::PropertyColor","LineColor","Base","Line color")
obj.addProperty("App::PropertyLength","ExtLines","Base","Ext lines")
- obj.addProperty("App::PropertyVector","Position","Base","The position of the text. Leave (0,0,0) for automatic position")
+ obj.addProperty("App::PropertyVector","TextPosition","Base","The position of the text. Leave (0,0,0) for automatic position")
obj.addProperty("App::PropertyString","Override","Base","Text override. Use 'dim' to insert the dimension length")
obj.Proxy = self
obj.FontSize=getParam("textheight")
@@ -1476,10 +1502,10 @@ class _ViewProviderDimension:
if hasattr(obj.ViewObject,"DisplayMode"):
if obj.ViewObject.DisplayMode == "3D":
offset = fcvec.neg(offset)
- if obj.ViewObject.Position == Vector(0,0,0):
+ if obj.ViewObject.TextPosition == Vector(0,0,0):
tbase = midpoint.add(offset)
else:
- tbase = obj.ViewObject.Position
+ tbase = obj.ViewObject.TextPosition
rot = FreeCAD.Placement(fcvec.getPlaneRotation(u,v,norm)).Rotation.Q
return p1,p2,p3,p4,tbase,norm,rot
@@ -1719,7 +1745,7 @@ class _ViewProviderAngularDimension:
obj.addProperty("App::PropertyString","FontName","Base","Font name")
obj.addProperty("App::PropertyLength","LineWidth","Base","Line width")
obj.addProperty("App::PropertyColor","LineColor","Base","Line color")
- obj.addProperty("App::PropertyVector","Position","Base","The position of the text. Leave (0,0,0) for automatic position")
+ obj.addProperty("App::PropertyVector","TextPosition","Base","The position of the text. Leave (0,0,0) for automatic position")
obj.addProperty("App::PropertyString","Override","Base","Text override. Use 'dim' to insert the dimension length")
obj.Proxy = self
obj.FontSize=getParam("textheight")
diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py
index 7bd5ce5da..14d409009 100644
--- a/src/Mod/Draft/DraftSnap.py
+++ b/src/Mod/Draft/DraftSnap.py
@@ -152,7 +152,7 @@ class Snapper:
if self.extLine:
self.extLine.off()
- point = FreeCADGui.ActiveDocument.ActiveView.getPoint(screenpos[0],screenpos[1])
+ point = self.getApparentPoint(screenpos[0],screenpos[1])
# check if we snapped to something
info = FreeCADGui.ActiveDocument.ActiveView.getObjectInfo((screenpos[0],screenpos[1]))
@@ -259,6 +259,12 @@ class Snapper:
# return the final point
return cstr(winner[2])
+
+ def getApparentPoint(self,x,y):
+ "returns a 3D point, projected on the current working plane"
+ pt = FreeCADGui.ActiveDocument.ActiveView.getPoint(x,y)
+ dv = FreeCADGui.ActiveDocument.ActiveView.getViewDirection()
+ return FreeCAD.DraftWorkingPlane.projectPoint(pt,dv)
def snapToExtensions(self,point,last,constrain):
"returns a point snapped to extension or parallel line to last object, if any"
@@ -515,7 +521,8 @@ class Snapper:
delta = point.sub(self.basepoint)
# setting constraint axis
- self.affinity = FreeCAD.DraftWorkingPlane.getClosestAxis(delta)
+ if not self.affinity:
+ self.affinity = FreeCAD.DraftWorkingPlane.getClosestAxis(delta)
if isinstance(axis,FreeCAD.Vector):
self.constraintAxis = axis
elif axis == "x":
diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py
index 62d7643e6..6e21bb347 100644
--- a/src/Mod/Draft/DraftTools.py
+++ b/src/Mod/Draft/DraftTools.py
@@ -455,9 +455,9 @@ class Line(Creator):
if len(edges) > 1:
edges.pop()
newshape = Part.Wire(edges)
+ self.obj.Shape = newshape
else:
- newshape = Part.Shape()
- self.obj.Shape = newshape
+ self.obj.ViewObject.hide()
# DNC: report on removal
msg(translate("draft", "Last point has been removed\n"))
@@ -2114,7 +2114,8 @@ class Offset(Modifier):
v2 = fcgeo.getTangent(self.shape.Edges[dist[1]],point)
a = -fcvec.angle(v1,v2)
self.dvec = fcvec.rotate(d,a,plane.axis)
- self.ghost.update(fcgeo.offsetWire(self.shape,self.dvec,occ=self.ui.occOffset.isChecked()))
+ occmode = self.ui.occOffset.isChecked()
+ self.ghost.update(fcgeo.offsetWire(self.shape,self.dvec,occ=occmode),forceclosed=occmode)
elif self.mode == "Circle":
self.dvec = point.sub(self.center).Length
self.ghost.setRadius(self.dvec)
@@ -3080,8 +3081,9 @@ class Edit(Modifier):
if self.obj:
self.obj = self.obj[0]
# store selectable state of the object
- self.selectstate = self.obj.ViewObject.Selectable
- self.obj.ViewObject.Selectable = False
+ if hasattr(self.obj.ViewObject,"Selectable"):
+ self.selectstate = self.obj.ViewObject.Selectable
+ self.obj.ViewObject.Selectable = False
if not Draft.getType(self.obj) in ["Wire","BSpline"]:
self.ui.setEditButtons(False)
else:
@@ -3148,7 +3150,8 @@ class Edit(Modifier):
t.finalize()
if self.constraintrack:
self.constraintrack.finalize()
- self.obj.ViewObject.Selectable = self.selectstate
+ if hasattr(self.obj.ViewObject,"Selectable"):
+ self.obj.ViewObject.Selectable = self.selectstate
Modifier.finish(self)
plane.restore()
self.running = False
@@ -3188,13 +3191,15 @@ class Edit(Modifier):
self.ui.isRelative.show()
self.editing = int(snapped['Component'][8:])
self.trackers[self.editing].off()
- self.obj.ViewObject.Selectable = False
+ if hasattr(self.obj.ViewObject,"Selectable"):
+ self.obj.ViewObject.Selectable = False
if "Points" in self.obj.PropertiesList:
self.node.append(self.obj.Points[self.editing])
else:
print "finishing edit"
self.trackers[self.editing].on()
- self.obj.ViewObject.Selectable = True
+ if hasattr(self.obj.ViewObject,"Selectable"):
+ self.obj.ViewObject.Selectable = True
self.numericInput(self.trackers[self.editing].get())
def update(self,v):
@@ -3540,6 +3545,9 @@ class Draft2Sketch():
allDraft = False
elif obj.isDerivedFrom("Part::Part2DObjectPython"):
allSketches = False
+ else:
+ allDraft = False
+ allSketches = False
if not sel:
return
elif allDraft:
@@ -3554,9 +3562,12 @@ class Draft2Sketch():
FreeCAD.ActiveDocument.openTransaction("Convert")
for obj in sel:
if obj.isDerivedFrom("Sketcher::SketchObject"):
- Draft.makeSketch(sel,autoconstraints=True)
+ Draft.draftify(obj)
elif obj.isDerivedFrom("Part::Part2DObjectPython"):
- Draft.draftify(sel,makeblock=True)
+ Draft.makeSketch(obj,autoconstraints=True)
+ elif obj.isDerivedFrom("Part::Feature"):
+ if len(obj.Shape.Wires) == 1:
+ Draft.makeSketch(obj,autoconstraints=False)
FreeCAD.ActiveDocument.commitTransaction()
diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py
index 7311d23f5..c071f6ef1 100644
--- a/src/Mod/Draft/DraftTrackers.py
+++ b/src/Mod/Draft/DraftTrackers.py
@@ -505,16 +505,16 @@ class wireTracker(Tracker):
self.update(wire)
Tracker.__init__(self,children=[self.coords,self.line])
- def update(self,wire):
+ def update(self,wire,forceclosed=False):
if wire:
- if self.closed:
+ if self.closed or forceclosed:
self.line.numVertices.setValue(len(wire.Vertexes)+1)
else:
self.line.numVertices.setValue(len(wire.Vertexes))
for i in range(len(wire.Vertexes)):
p=wire.Vertexes[i].Point
self.coords.point.set1Value(i,[p.x,p.y,p.z])
- if self.closed:
+ if self.closed or forceclosed:
t = len(wire.Vertexes)
p = wire.Vertexes[0].Point
self.coords.point.set1Value(t,[p.x,p.y,p.z])
diff --git a/src/Mod/Draft/draftlibs/fcgeo.py b/src/Mod/Draft/draftlibs/fcgeo.py
index 2d1355260..0c5c95089 100755
--- a/src/Mod/Draft/draftlibs/fcgeo.py
+++ b/src/Mod/Draft/draftlibs/fcgeo.py
@@ -361,14 +361,17 @@ def geom(edge):
return edge.Curve
elif isinstance(edge.Curve,Part.Circle):
if len(edge.Vertexes) == 1:
- return edge.Curve
+ return Part.Circle(edge.Curve.Center,edge.Curve.Axis,edge.Curve.Radius)
else:
+ ref = edge.Placement.multVec(Vector(1,0,0))
v1 = edge.Vertexes[0].Point
v2 = edge.Vertexes[-1].Point
c = edge.Curve.Center
- a1 = -fcvec.angle(v1.sub(c))
- a2 = -fcvec.angle(v2.sub(c))
- return Part.ArcOfCircle(edge.Curve,a1,a2)
+ cu = Part.Circle(edge.Curve.Center,edge.Curve.Axis,edge.Curve.Radius)
+ a1 = -fcvec.angle(v1.sub(c),ref)
+ a2 = -fcvec.angle(v2.sub(c),ref)
+ p= Part.ArcOfCircle(cu,a1,a2)
+ return p
else:
return edge.Curve
@@ -764,12 +767,20 @@ def connect(edges,closed=False):
next = None
if prev:
# print "debug: fcgeo.connect prev : ",prev.Vertexes[0].Point,prev.Vertexes[-1].Point
- v1 = findIntersection(curr,prev,True,True)[0]
+ i = findIntersection(curr,prev,True,True)
+ if i:
+ v1 = i[0]
+ else:
+ v1 = curr.Vertexes[0].Point
else:
v1 = curr.Vertexes[0].Point
if next:
# print "debug: fcgeo.connect next : ",next.Vertexes[0].Point,next.Vertexes[-1].Point
- v2 = findIntersection(curr,next,True,True)[0]
+ i = findIntersection(curr,next,True,True)
+ if i:
+ v2 = i[0]
+ else:
+ v2 = curr.Vertexes[-1].Point
else:
v2 = curr.Vertexes[-1].Point
if isinstance(curr.Curve,Part.Line):
@@ -778,7 +789,10 @@ def connect(edges,closed=False):
elif isinstance(curr.Curve,Part.Circle):
if v1 != v2:
nedges.append(Part.Arc(v1,findMidPoint(curr),v2))
- return Part.Wire(nedges)
+ try:
+ return Part.Wire(nedges)
+ except:
+ return None
def findDistance(point,edge,strict=False):
'''