Merge branch 'master' into master

This commit is contained in:
sliptonic
2020-06-21 10:38:47 -05:00
committed by GitHub
20 changed files with 1181 additions and 880 deletions

View File

@@ -1889,7 +1889,7 @@ void Application::initConfig(int argc, char ** argv)
Branding brand;
QString binDir = QString::fromUtf8((mConfig["AppHomePath"] + "bin").c_str());
QFileInfo fi(binDir, QString::fromLatin1("branding.xml"));
if (brand.readFile(fi.absoluteFilePath())) {
if (fi.exists() && brand.readFile(fi.absoluteFilePath())) {
Branding::XmlConfig cfg = brand.getUserDefines();
for (Branding::XmlConfig::iterator it = cfg.begin(); it != cfg.end(); ++it) {
App::Application::Config()[it.key()] = it.value();

View File

@@ -126,8 +126,6 @@ ConsoleSingleton::ConsoleSingleton(void)
,_defaultLogLevel(FC_LOGLEVEL_MSG)
#endif
{
// make sure this object is part of the main thread
ConsoleOutput::getInstance();
}
ConsoleSingleton::~ConsoleSingleton()
@@ -233,6 +231,11 @@ bool ConsoleSingleton::IsMsgTypeEnabled(const char* sObs, FreeCAD_ConsoleMsgType
void ConsoleSingleton::SetConnectionMode(ConnectionMode mode)
{
connectionMode = mode;
// make sure this method gets called from the main thread
if (connectionMode == Queued) {
ConsoleOutput::getInstance();
}
}
/** Prints a Message

View File

@@ -24,6 +24,7 @@
#include "PreCompiled.h"
#include "PyExport.h"
#include "Exception.h"
#include <sstream>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-register"

View File

@@ -24,6 +24,7 @@
#include "PreCompiled.h"
#include "PyExport.h"
#include "Exception.h"
#include <sstream>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-register"

View File

@@ -24,6 +24,7 @@
#include "PreCompiled.h"
#include "PyExport.h"
#include "Exception.h"
#include <sstream>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-register"

View File

@@ -24,6 +24,7 @@
#include "PreCompiled.h"
#include "PyExport.h"
#include "Exception.h"
#include <sstream>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-register"

View File

@@ -24,6 +24,7 @@
#include "PreCompiled.h"
#include "PyExport.h"
#include "Exception.h"
#include <sstream>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-register"

View File

@@ -147,14 +147,17 @@ namespace Gui {
// Pimpl class
struct ApplicationP
{
ApplicationP() :
ApplicationP(bool GUIenabled) :
activeDocument(0L),
editDocument(0L),
isClosing(false),
startingUp(true)
{
// create the macro manager
macroMngr = new MacroManager();
if (GUIenabled)
macroMngr = new MacroManager();
else
macroMngr = nullptr;
}
~ApplicationP()
@@ -227,6 +230,47 @@ FreeCADGui_subgraphFromObject(PyObject * /*self*/, PyObject *args)
return Py_None;
}
static PyObject *
FreeCADGui_replaceSwitchNodes(PyObject * /*self*/, PyObject *args)
{
PyObject* proxy;
if (!PyArg_ParseTuple(args, "O", &proxy))
return nullptr;
void* ptr = 0;
try {
Base::Interpreter().convertSWIGPointerObj("pivy.coin", "SoNode *", proxy, &ptr, 0);
SoNode* node = reinterpret_cast<SoNode*>(ptr);
SoNode* replace = SoFCDB::replaceSwitches(node);
if (replace) {
replace->ref();
std::string prefix = "So";
std::string type = replace->getTypeId().getName().getString();
// doesn't start with the prefix 'So'
if (type.rfind("So", 0) != 0) {
type = prefix + type;
}
else if (type == "SoFCSelectionRoot") {
type = "SoSeparator";
}
type += " *";
PyObject* proxy = 0;
proxy = Base::Interpreter().createSWIGPointerObj("pivy.coin", type.c_str(), (void*)replace, 1);
return Py::new_reference_to(Py::Object(proxy, true));
}
else {
Py_INCREF(Py_None);
return Py_None;
}
}
catch (const Base::Exception& e) {
PyErr_SetString(PyExc_RuntimeError, e.what());
return nullptr;
}
}
static PyObject *
FreeCADGui_getSoDBVersion(PyObject * /*self*/, PyObject *args)
{
@@ -290,6 +334,9 @@ struct PyMethodDef FreeCADGui_methods[] = {
{"subgraphFromObject",FreeCADGui_subgraphFromObject,METH_VARARGS,
"subgraphFromObject(object) -> Node\n\n"
"Return the Inventor subgraph to an object"},
{"replaceSwitchNodes",FreeCADGui_replaceSwitchNodes,METH_VARARGS,
"replaceSwitchNodes(Node) -> Node\n\n"
"Replace Switch nodes with Separators"},
{"getSoDBVersion",FreeCADGui_getSoDBVersion,METH_VARARGS,
"getSoDBVersion() -> String\n\n"
"Return a text string containing the name\n"
@@ -451,7 +498,7 @@ Application::Application(bool GUIenabled)
View3DInventorViewerPy ::init_type();
AbstractSplitViewPy ::init_type();
d = new ApplicationP;
d = new ApplicationP(GUIenabled);
// global access
Instance = this;

View File

@@ -295,6 +295,11 @@ SoNode* replaceSwitchesInSceneGraph(SoNode* node)
return node;
}
SoNode* Gui::SoFCDB::replaceSwitches(SoNode* node)
{
return replaceSwitchesInSceneGraph(node);
}
bool Gui::SoFCDB::writeToVRML(SoNode* node, const char* filename, bool binary)
{
SoNode* noSwitches = replaceSwitchesInSceneGraph(node);

View File

@@ -40,6 +40,7 @@ public:
static SbBool isInitialized(void);
static void init();
static void finish();
static SoNode* replaceSwitches(SoNode* node);
/// helper to apply a SoWriteAction to a node and write it to a string
static const std::string& writeNodesToString(SoNode * root);
static bool writeToVRML(SoNode* node, const char* filename, bool binary);

View File

@@ -52,10 +52,30 @@
#include <Gui/SoFCDB.h>
#include <Gui/Quarter/Quarter.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoInteraction.h>
#include <Inventor/nodekits/SoNodeKit.h>
static bool _isSetupWithoutGui = false;
static
QWidget* setupMainWindow();
class QtApplication : public QApplication {
public:
QtApplication(int &argc, char **argv)
: QApplication(argc, argv) {
}
bool notify (QObject * receiver, QEvent * event) {
try {
return QApplication::notify(receiver, event);
}
catch (const Base::SystemExitException& e) {
exit(e.getExitCode());
return true;
}
}
};
#if defined(Q_OS_WIN)
HHOOK hhook;
@@ -70,6 +90,11 @@ FilterProc(int nCode, WPARAM wParam, LPARAM lParam) {
static PyObject *
FreeCADGui_showMainWindow(PyObject * /*self*/, PyObject *args)
{
if (_isSetupWithoutGui) {
PyErr_SetString(PyExc_RuntimeError, "Cannot call showMainWindow() after calling setupWithoutGUI()\n");
return nullptr;
}
PyObject* inThread = Py_False;
if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &inThread))
return NULL;
@@ -84,7 +109,10 @@ FreeCADGui_showMainWindow(PyObject * /*self*/, PyObject *args)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
#endif
QApplication app(argc, argv);
// This only works well if the QApplication is the very first created instance
// of a QObject. Otherwise the application lives in a different thread than the
// main thread which will cause hazardous behaviour.
QtApplication app(argc, argv);
if (setupMainWindow()) {
app.exec();
}
@@ -92,6 +120,9 @@ FreeCADGui_showMainWindow(PyObject * /*self*/, PyObject *args)
t.detach();
}
else {
// In order to get Jupiter notebook integration working we must create a direct instance
// of QApplication. Not even a sub-class can be used because otherwise PySide2 wraps it
// with a QtCore.QCoreApplication which will raise an exception in ipykernel
#if defined(Q_OS_WIN)
static int argc = 0;
static char **argv = {0};
@@ -154,6 +185,7 @@ FreeCADGui_setupWithoutGUI(PyObject * /*self*/, PyObject *args)
if (!Gui::Application::Instance) {
static Gui::Application *app = new Gui::Application(false);
_isSetupWithoutGui = true;
Q_UNUSED(app);
}
else {
@@ -164,7 +196,8 @@ FreeCADGui_setupWithoutGUI(PyObject * /*self*/, PyObject *args)
if (!SoDB::isInitialized()) {
// init the Inventor subsystem
SoDB::init();
SIM::Coin3D::Quarter::Quarter::init();
SoNodeKit::init();
SoInteraction::init();
}
if (!Gui::SoFCDB::isInitialized()) {
Gui::SoFCDB::init();

View File

@@ -67,6 +67,9 @@
</property>
<item row="0" column="1">
<widget class="QComboBox" name="geo1Reference">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -77,7 +80,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Choose what point to use on the first selected feature.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="editable">
<bool>true</bool>
<bool>false</bool>
</property>
<item>
<property name="text">
@@ -159,7 +162,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Choose what point to use on the second selected feature.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="editable">
<bool>true</bool>
<bool>false</bool>
</property>
<item>
<property name="text">

View File

@@ -46,8 +46,12 @@ Part = LazyLoader('Part', globals(), 'Part')
if FreeCAD.GuiUp:
import FreeCADGui
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
DEBUG = False
if DEBUG:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# Qt translation handling
@@ -91,6 +95,7 @@ class ObjectSlot(PathOp.ObjectOp):
# Set enumeration lists for enumeration properties
if len(self.addNewProps) > 0:
ENUMS = self.opPropertyEnumerations()
# ENUMS = self.getActiveEnumerations(obj)
for n in ENUMS:
if n in self.addNewProps:
setattr(obj, n, ENUMS[n])
@@ -114,6 +119,8 @@ class ObjectSlot(PathOp.ObjectOp):
QtCore.QT_TRANSLATE_NOOP("App::Property", "Enter custom start point for slot path.")),
("App::PropertyVectorDistance", "CustomPoint2", "Slot",
QtCore.QT_TRANSLATE_NOOP("App::Property", "Enter custom end point for slot path.")),
("App::PropertyEnumeration", "CutPattern", "Slot",
QtCore.QT_TRANSLATE_NOOP("App::Property", "Set the geometric clearing pattern to use for the operation.")),
("App::PropertyDistance", "ExtendPathStart", "Slot",
QtCore.QT_TRANSLATE_NOOP("App::Property", "Positive extends the beginning of the path, negative shortens.")),
("App::PropertyDistance", "ExtendPathEnd", "Slot",
@@ -138,6 +145,7 @@ class ObjectSlot(PathOp.ObjectOp):
def opPropertyEnumerations(self):
# Enumeration lists for App::PropertyEnumeration properties
return {
'CutPattern': ['Line', 'ZigZag'],
'LayerMode': ['Single-pass', 'Multi-pass'],
'PathOrientation': ['Start to End', 'Perpendicular'],
'Reference1': ['Center of Mass', 'Center of BoundBox',
@@ -157,7 +165,8 @@ class ObjectSlot(PathOp.ObjectOp):
'CustomPoint2': FreeCAD.Vector(10.0, 10.0, 0.0),
'ExtendPathEnd': 0.0,
'Reference2': 'Center of Mass',
'LayerMode': 'Single-pass',
'LayerMode': 'Multi-pass',
'CutPattern': 'ZigZag',
'PathOrientation': 'Start to End',
'ReverseDirection': False,
@@ -167,27 +176,64 @@ class ObjectSlot(PathOp.ObjectOp):
return defaults
def setEditorProperties(self, obj):
# Used to hide inputs in properties list
A = B = 2
def getActiveEnumerations(self, obj):
"""getActiveEnumerations(obj) ...
Method returns dictionary of property enumerations based on
active conditions in the operation."""
ENUMS = self.opPropertyEnumerations()
if hasattr(obj, 'Base'):
enums2 = self.opPropertyEnumerations()['Reference2']
if obj.Base:
(base, subsList) = obj.Base[0]
subCnt = len(subsList)
if subCnt == 1:
# Adjust available enumerations
obj.Reference1 = self._getReference1Enums(subsList[0], True)
A = 0
ENUMS['Reference1'] = self._makeReference1Enumerations(subsList[0], True)
elif subCnt == 2:
# Adjust available enumerations
obj.Reference1 = self._getReference1Enums(subsList[0])
obj.Reference2 = self._getReference2Enums(subsList[1])
ENUMS['Reference1'] = self._makeReference1Enumerations(subsList[0])
ENUMS['Reference2'] = self._makeReference2Enumerations(subsList[1])
return ENUMS
def updateEnumerations(self, obj):
"""updateEnumerations(obj) ...
Method updates property enumerations based on active conditions
in the operation. Returns the updated enumerations dictionary.
Existing property values must be stored, and then restored after
the assignment of updated enumerations."""
PathLog.debug('updateEnumerations()')
# Save existing values
pre_Ref1 = obj.Reference1
pre_Ref2 = obj.Reference2
# Update enumerations
ENUMS = self.getActiveEnumerations(obj)
obj.Reference1 = ENUMS['Reference1']
obj.Reference2 = ENUMS['Reference2']
# Restore pre-existing values if available with active enumerations.
# If not, set to first element in active enumeration list.
if pre_Ref1 in ENUMS['Reference1']:
obj.Reference1 = pre_Ref1
else:
obj.Reference1 = ENUMS['Reference1'][0]
if pre_Ref2 in ENUMS['Reference2']:
obj.Reference2 = pre_Ref2
else:
obj.Reference2 = ENUMS['Reference2'][0]
return ENUMS
def setEditorProperties(self, obj):
# Used to hide inputs in properties list
A = B = 2
if hasattr(obj, 'Base'):
if obj.Base:
(base, subsList) = obj.Base[0]
subCnt = len(subsList)
if subCnt == 1:
A = 0
elif subCnt == 2:
A = B = 0
else:
ENUMS = self.opPropertyEnumerations()
obj.Reference1 = ENUMS['Reference1']
obj.Reference2 = ENUMS['Reference2']
obj.setEditorMode('Reference1', A)
obj.setEditorMode('Reference2', B)
@@ -196,6 +242,7 @@ class ObjectSlot(PathOp.ObjectOp):
if hasattr(self, 'propertiesReady'):
if self.propertiesReady:
if prop in ['Base']:
self.updateEnumerations(obj)
self.setEditorProperties(obj)
def opOnDocumentRestored(self, obj):
@@ -209,15 +256,15 @@ class ObjectSlot(PathOp.ObjectOp):
obj.setEditorMode('ShowTempObjects', mode)
# Repopulate enumerations in case of changes
ENUMS = self.opPropertyEnumerations()
ENUMS = self.updateEnumerations(obj)
for n in ENUMS:
restore = False
if hasattr(obj, n):
val = obj.getPropertyByName(n)
restore = True
setattr(obj, n, ENUMS[n])
setattr(obj, n, ENUMS[n]) # set the enumerations list
if restore:
setattr(obj, n, val)
setattr(obj, n, val) # restore the value
self.setEditorProperties(obj)
@@ -320,6 +367,8 @@ class ObjectSlot(PathOp.ObjectOp):
self.tmpGrp = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroup', 'tmpDebugGrp')
tmpGrpNm = self.tmpGrp.Name
# self.updateEnumerations(obj)
# Identify parent Job
JOB = PathUtils.findParentJob(obj)
self.JOB = JOB
@@ -418,11 +467,14 @@ class ObjectSlot(PathOp.ObjectOp):
lenSL = len(subsList)
featureCnt = lenSL
if lenSL == 1:
PathLog.debug('Reference 1: {}'.format(obj.Reference1))
sub1 = subsList[0]
shape_1 = getattr(base.Shape, sub1)
self.shape1 = shape_1
pnts = self._processSingle(obj, shape_1, sub1)
else:
PathLog.debug('Reference 1: {}'.format(obj.Reference1))
PathLog.debug('Reference 2: {}'.format(obj.Reference2))
sub1 = subsList[0]
sub2 = subsList[1]
shape_1 = getattr(base.Shape, sub1)
@@ -467,7 +519,7 @@ class ObjectSlot(PathOp.ObjectOp):
(p1, p2) = pnts
if self.isDebug:
PathLog.debug('p1, p2: {}, {}'.format(p1, p2))
PathLog.debug('Path Points are:\np1 = {}\np2 = {}'.format(p1, p2))
if p1.sub(p2).Length != 0 and self.showTempObjects:
O = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmp_Path')
O.Shape = Part.makeLine(p1, p2)
@@ -493,7 +545,7 @@ class ObjectSlot(PathOp.ObjectOp):
It returns the slot gcode for the operation."""
CMDS = list()
def layerPass(p1, p2, depth):
def linePass(p1, p2, depth):
cmds = list()
# cmds.append(Path.Command('N (Tool type: {})'.format(toolType), {}))
cmds.append(Path.Command('G0', {'X': p1.x, 'Y': p1.y, 'F': self.horizRapid}))
@@ -502,16 +554,25 @@ class ObjectSlot(PathOp.ObjectOp):
return cmds
# CMDS.append(Path.Command('N (Tool type: {})'.format(toolType), {}))
# CMDS.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
if obj.LayerMode == 'Single-pass':
CMDS.extend(layerPass(p1, p2, obj.FinalDepth.Value))
CMDS.extend(linePass(p1, p2, obj.FinalDepth.Value))
CMDS.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
else:
prvDep = obj.StartDepth.Value
for dep in self.depthParams:
CMDS.extend(layerPass(p1, p2, dep))
CMDS.append(Path.Command('G0', {'Z': prvDep, 'F': self.vertRapid}))
prvDep = dep
if obj.CutPattern == 'Line':
for dep in self.depthParams:
CMDS.extend(linePass(p1, p2, dep))
CMDS.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
elif obj.CutPattern == 'ZigZag':
CMDS.append(Path.Command('G0', {'X': p1.x, 'Y': p1.y, 'F': self.horizRapid}))
i = 0
for dep in self.depthParams:
if i % 2.0 == 0: # even
CMDS.append(Path.Command('G1', {'Z': dep, 'F': self.vertFeed}))
CMDS.append(Path.Command('G1', {'X': p2.x, 'Y': p2.y, 'F': self.horizFeed}))
else: # odd
CMDS.append(Path.Command('G1', {'Z': dep, 'F': self.vertFeed}))
CMDS.append(Path.Command('G1', {'X': p1.x, 'Y': p1.y, 'F': self.horizFeed}))
i += 1
CMDS.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
return CMDS
@@ -528,7 +589,7 @@ class ObjectSlot(PathOp.ObjectOp):
pnts = False
norm = shape_1.normalAt(0.0, 0.0)
PathLog.debug('Face.normalAt(): {}'.format(norm))
PathLog.debug('{}.normalAt(): {}'.format(sub1, norm))
if norm.z == 1 or norm.z == -1:
pnts = self._processSingleHorizFace(obj, shape_1)
elif norm.z == 0:
@@ -571,6 +632,7 @@ class ObjectSlot(PathOp.ObjectOp):
def _processSingleHorizFace(self, obj, shape):
"""Determine slot path endpoints from a single horizontally oriented face."""
PathLog.debug('_processSingleHorizFace()')
lineTypes = ['Part::GeomLine']
def getRadians(self, E):
@@ -603,8 +665,6 @@ class ObjectSlot(PathOp.ObjectOp):
pairs = list()
eCnt = len(shape.Edges)
lstE = eCnt - 1
I = [i for i in range(0, eCnt)]
I.append(0)
for i in range(0, eCnt):
if i < lstE:
ni = i + 1
@@ -629,6 +689,11 @@ class ObjectSlot(PathOp.ObjectOp):
if pairCnt > 1:
pairs.sort(key=lambda tup: tup[0].Length, reverse=True)
if self.isDebug:
PathLog.debug(' -pairCnt: {}'.format(pairCnt))
for (a, b) in pairs:
PathLog.debug(' -pair: {}, {}'.format(round(a.Length, 4), round(b.Length,4)))
if pairCnt == 0:
msg = translate('PathSlot',
'No parallel edges identified.')
@@ -638,9 +703,9 @@ class ObjectSlot(PathOp.ObjectOp):
same = pairs[0]
else:
if obj.Reference1 == 'Long Edge':
same = pairs[0]
elif obj.Reference1 == 'Short Edge':
same = pairs[1]
elif obj.Reference1 == 'Short Edge':
same = pairs[0]
else:
msg = 'Reference1 '
msg += translate('PathSlot',
@@ -653,6 +718,7 @@ class ObjectSlot(PathOp.ObjectOp):
def _processSingleComplexFace(self, obj, shape):
"""Determine slot path endpoints from a single complex face."""
PathLog.debug('_processSingleComplexFace()')
PNTS = list()
def zVal(V):
@@ -667,6 +733,7 @@ class ObjectSlot(PathOp.ObjectOp):
def _processSingleVertFace(self, obj, shape):
"""Determine slot path endpoints from a single vertically oriented face
with no single bottom edge."""
PathLog.debug('_processSingleVertFace()')
eCnt = len(shape.Edges)
V0 = shape.Edges[0].Vertexes[0]
V1 = shape.Edges[eCnt - 1].Vertexes[1]
@@ -691,6 +758,7 @@ class ObjectSlot(PathOp.ObjectOp):
# Methods for processing double geometry
def _processDouble(self, obj, shape_1, sub1, shape_2, sub2):
PathLog.debug('_processDouble()')
"""This is the control method for slots based on a
two Base Geometry features."""
cmds = False
@@ -898,47 +966,12 @@ class ObjectSlot(PathOp.ObjectOp):
n2 = p2
return (n1, n2)
def _getEndMidPoints(self, same):
# Find mid-points between ends of equal, oppossing edges
e0va = same[0].Vertexes[0]
e0vb = same[0].Vertexes[1]
e1va = same[1].Vertexes[0]
e1vb = same[1].Vertexes[1]
if False:
midX1 = (e0va.X + e0vb.X) / 2.0
midY1 = (e0va.Y + e0vb.Y) / 2.0
midX2 = (e1va.X + e1vb.X) / 2.0
midY2 = (e1va.Y + e1vb.Y) / 2.0
m1 = FreeCAD.Vector(midX1, midY1, e0va.Z)
m2 = FreeCAD.Vector(midX2, midY2, e0va.Z)
p1 = FreeCAD.Vector(e0va.X, e0va.Y, e0va.Z)
p2 = FreeCAD.Vector(e0vb.X, e0vb.Y, e0vb.Z)
p3 = FreeCAD.Vector(e1va.X, e1va.Y, e1va.Z)
p4 = FreeCAD.Vector(e1vb.X, e1vb.Y, e1vb.Z)
L0 = Part.makeLine(p1, p2)
L1 = Part.makeLine(p3, p4)
comL0 = L0.CenterOfMass
comL1 = L1.CenterOfMass
m1 = FreeCAD.Vector(comL0.x, comL0.y, 0.0)
m2 = FreeCAD.Vector(comL1.x, comL1.y, 0.0)
return (m1, m2)
def _getOppMidPoints(self, same):
# Find mid-points between ends of equal, oppossing edges
v1 = same[0].Vertexes[0]
v2 = same[0].Vertexes[1]
a1 = same[1].Vertexes[0]
a2 = same[1].Vertexes[1]
midX1 = (v1.X + a2.X) / 2.0
midY1 = (v1.Y + a2.Y) / 2.0
midX2 = (v2.X + a1.X) / 2.0
midY2 = (v2.Y + a1.Y) / 2.0
p1 = FreeCAD.Vector(midX1, midY1, v1.Z)
p2 = FreeCAD.Vector(midX2, midY2, v1.Z)
com1 = same[0].CenterOfMass
com2 = same[1].CenterOfMass
p1 = FreeCAD.Vector(com1.x, com1.y, 0.0)
p2 = FreeCAD.Vector(com2.x, com2.y, 0.0)
return (p1, p2)
def _isParallel(self, dYdX1, dYdX2):
@@ -1152,19 +1185,31 @@ class ObjectSlot(PathOp.ObjectOp):
return ('Wire', wires[0])
return False
def _getReference1Enums(self, sub, single=False):
# Adjust available enumerations
enums1 = self.opPropertyEnumerations()['Reference1']
for ri in removeIndexesFromReference_1(sub, single):
enums1.pop(ri)
return enums1
def _makeReference1Enumerations(self, sub, single=False):
"""Customize Reference1 enumerations based on feature type."""
PathLog.debug('_makeReference1Enumerations()')
cat = sub[:4]
if single:
if cat == 'Face':
return ['Long Edge', 'Short Edge']
elif cat == 'Edge':
return ['Long Edge']
elif cat == 'Vert':
return ['Vertex']
elif cat == 'Vert':
return ['Vertex']
def _getReference2Enums(self, sub):
# Adjust available enumerations
enums2 = self.opPropertyEnumerations()['Reference2']
for ri in removeIndexesFromReference_2(sub):
enums2.pop(ri)
return enums2
return ['Center of Mass', 'Center of BoundBox',
'Lowest Point', 'Highest Point']
def _makeReference2Enumerations(self, sub):
"""Customize Reference2 enumerations based on feature type."""
PathLog.debug('_makeReference2Enumerations()')
cat = sub[:4]
if cat == 'Vert':
return ['Vertex']
return ['Center of Mass', 'Center of BoundBox',
'Lowest Point', 'Highest Point']
def _lineCollisionCheck(self, obj, p1, p2):
"""Make simple circle with diameter of tool, at start point.
@@ -1176,44 +1221,51 @@ class ObjectSlot(PathOp.ObjectOp):
def getPerp(p1, p2, dist):
toEnd = p2.sub(p1)
factor = dist / toEnd.Length
perp = FreeCAD.Vector(-1 * toEnd.y, toEnd.x, 0.0)
if perp.x == 0 and perp.y == 0:
return perp
perp.normalize()
perp.multiply(dist)
return perp
# Make first cylinder
ce1 = Part.Wire(Part.makeCircle(rad, p1).Edges)
ce2 = Part.Wire(Part.makeCircle(rad, p2).Edges)
C1 = Part.Face(ce1)
C2 = Part.Face(ce2)
zTrans = obj.FinalDepth.Value - C1.BoundBox.ZMin
C1.translate(FreeCAD.Vector(0.0, 0.0, zTrans))
zTrans = obj.FinalDepth.Value - C2.BoundBox.ZMin
C2.translate(FreeCAD.Vector(0.0, 0.0, zTrans))
extFwd = obj.StartDepth.Value - obj.FinalDepth.Value
extVect = FreeCAD.Vector(0.0, 0.0, extFwd)
startShp = C1.extrude(extVect)
endShp = C2.extrude(extVect)
perp = getPerp(p1, p2, rad)
v1 = p1.add(perp)
v2 = p1.sub(perp)
v3 = p2.sub(perp)
v4 = p2.add(perp)
e1 = Part.makeLine(v1, v2)
e2 = Part.makeLine(v2, v3)
e3 = Part.makeLine(v3, v4)
e4 = Part.makeLine(v4, v1)
edges = Part.__sortEdges__([e1, e2, e3, e4])
rectFace = Part.Face(Part.Wire(edges))
zTrans = obj.FinalDepth.Value - rectFace.BoundBox.ZMin
rectFace.translate(FreeCAD.Vector(0.0, 0.0, zTrans))
boxShp = rectFace.extrude(extVect)
if p2.sub(p1).Length > 0:
# Make second cylinder
ce2 = Part.Wire(Part.makeCircle(rad, p2).Edges)
C2 = Part.Face(ce2)
zTrans = obj.FinalDepth.Value - C2.BoundBox.ZMin
C2.translate(FreeCAD.Vector(0.0, 0.0, zTrans))
endShp = C2.extrude(extVect)
part1 = startShp.fuse(boxShp)
pathTravel = part1.fuse(endShp)
# Make extruded rectangle to connect cylinders
perp = getPerp(p1, p2, rad)
v1 = p1.add(perp)
v2 = p1.sub(perp)
v3 = p2.sub(perp)
v4 = p2.add(perp)
e1 = Part.makeLine(v1, v2)
e2 = Part.makeLine(v2, v3)
e3 = Part.makeLine(v3, v4)
e4 = Part.makeLine(v4, v1)
edges = Part.__sortEdges__([e1, e2, e3, e4])
rectFace = Part.Face(Part.Wire(edges))
zTrans = obj.FinalDepth.Value - rectFace.BoundBox.ZMin
rectFace.translate(FreeCAD.Vector(0.0, 0.0, zTrans))
boxShp = rectFace.extrude(extVect)
# Fuse two cylinders and box together
part1 = startShp.fuse(boxShp)
pathTravel = part1.fuse(endShp)
else:
pathTravel = startShp
if self.showTempObjects:
O = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmp_PathTravel')
@@ -1233,35 +1285,6 @@ class ObjectSlot(PathOp.ObjectOp):
# Eclass
# Determine applicable enumerations
def removeIndexesFromReference_1(sub, single=False):
"""Determine which enumerations to remove for Reference1 input
based upon the feature type(category)."""
cat = sub[:4]
remIdxs = [6, 5, 4]
if cat == 'Face':
if single:
remIdxs = [6, 3, 2, 1, 0]
elif cat == 'Edge':
if single:
remIdxs = [6, 5, 3, 2, 1, 0]
elif cat == 'Vert':
remIdxs = [5, 4, 3, 2, 1, 0]
return remIdxs
def removeIndexesFromReference_2(sub):
"""Determine which enumerations to remove for Reference2 input
based upon the feature type(category)."""
cat = sub[:4]
remIdxs = [4]
# Customize Reference combobox options
if cat == 'Vert':
remIdxs = [3, 2, 1, 0]
return remIdxs
def SetupProperties():
''' SetupProperties() ... Return list of properties required for operation.'''
return [tup[1] for tup in ObjectSlot.opPropertyDefinitions(False)]

View File

@@ -37,33 +37,75 @@ __doc__ = "Slot operation page controller and command implementation."
__contributors__ = ""
DEBUG = False
def debugMsg(msg):
global DEBUG
if DEBUG:
FreeCAD.Console.PrintMessage('PathSlotGui:: ' + msg + '\n')
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
'''Page controller class for the Slot operation.'''
def getForm(self):
'''getForm() ... returns UI'''
debugMsg('getForm()')
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpSlotEdit.ui")
def initPage(self, obj):
'''initPage(obj) ... Is called after getForm() to initiate the task panel.'''
debugMsg('initPage()')
# pylint: disable=attribute-defined-outside-init
self.CATS = [None, None]
self.propEnums = PathSlot.ObjectSlot.opPropertyEnumerations(False)
self.ENUMS = dict()
self.setTitle("Slot - " + obj.Label)
# retrieve property enumerations
self.propEnums = PathSlot.ObjectSlot.opPropertyEnumerations(False)
# Requirements due to Gui::QuantitySpinBox class use in UI panel
self.geo1Extension = PathGui.QuantitySpinBox(self.form.geo1Extension, obj, 'ExtendPathStart')
self.geo2Extension = PathGui.QuantitySpinBox(self.form.geo2Extension, obj, 'ExtendPathEnd')
# self.updateVisibility()
def getForm(self):
'''getForm() ... returns UI'''
# FreeCAD.Console.PrintMessage('getForm()\n')
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpSlotEdit.ui")
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
debugMsg('setFields()')
debugMsg('... calling updateVisibility()')
self.updateVisibility()
self.updateQuantitySpinBoxes()
self.setupToolController(obj, self.form.toolController)
self.setupCoolant(obj, self.form.coolantController)
enums = self.propEnums['Reference1']
if 'Reference1' in self.ENUMS:
enums = self.ENUMS['Reference1']
debugMsg(' -enums1: {}'.format(enums))
idx = enums.index(obj.Reference1)
self.form.geo1Reference.setCurrentIndex(idx)
enums = self.propEnums['Reference2']
if 'Reference2' in self.ENUMS:
enums = self.ENUMS['Reference2']
debugMsg(' -enums2: {}'.format(enums))
idx = enums.index(obj.Reference2)
self.form.geo2Reference.setCurrentIndex(idx)
self.selectInComboBox(obj.LayerMode, self.form.layerMode)
self.selectInComboBox(obj.PathOrientation, self.form.pathOrientation)
if obj.ReverseDirection:
self.form.reverseDirection.setCheckState(QtCore.Qt.Checked)
else:
self.form.reverseDirection.setCheckState(QtCore.Qt.Unchecked)
def updateQuantitySpinBoxes(self):
# FreeCAD.Console.PrintMessage('updateQuantitySpinBoxes()\n')
self.geo1Extension.updateSpinBox()
self.geo2Extension.updateSpinBox()
def getFields(self, obj):
'''getFields(obj) ... transfers values from UI to obj's proprties'''
# FreeCAD.Console.PrintMessage('getFields()\n')
debugMsg('getFields()')
self.updateToolController(obj, self.form.toolController)
self.updateCoolant(obj, self.form.coolantController)
@@ -79,36 +121,11 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
val = self.propEnums['PathOrientation'][self.form.pathOrientation.currentIndex()]
obj.PathOrientation = val
if hasattr(self.form, 'reverseDirection'):
obj.ReverseDirection = self.form.reverseDirection.isChecked()
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
# FreeCAD.Console.PrintMessage('setFields()\n')
self.updateQuantitySpinBoxes()
self.setupToolController(obj, self.form.toolController)
self.setupCoolant(obj, self.form.coolantController)
idx = self.propEnums['Reference1'].index(obj.Reference1)
self.form.geo1Reference.setCurrentIndex(idx)
idx = self.propEnums['Reference2'].index(obj.Reference2)
self.form.geo2Reference.setCurrentIndex(idx)
self.selectInComboBox(obj.LayerMode, self.form.layerMode)
self.selectInComboBox(obj.PathOrientation, self.form.pathOrientation)
if obj.ReverseDirection:
self.form.reverseDirection.setCheckState(QtCore.Qt.Checked)
else:
self.form.reverseDirection.setCheckState(QtCore.Qt.Unchecked)
# FreeCAD.Console.PrintMessage('... calling updateVisibility()\n')
self.updateVisibility()
obj.ReverseDirection = self.form.reverseDirection.isChecked()
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
# FreeCAD.Console.PrintMessage('getSignalsForUpdate()\n')
debugMsg('getSignalsForUpdate()')
signals = []
signals.append(self.form.toolController.currentIndexChanged)
signals.append(self.form.coolantController.currentIndexChanged)
@@ -123,7 +140,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
def updateVisibility(self, sentObj=None):
'''updateVisibility(sentObj=None)... Updates visibility of Tasks panel objects.'''
# FreeCAD.Console.PrintMessage('updateVisibility()\n')
# debugMsg('updateVisibility()')
hideFeatures = True
if hasattr(self.obj, 'Base'):
if self.obj.Base:
@@ -136,8 +153,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
subCnt = len(sublist)
if subCnt == 1:
debugMsg(' -subCnt == 1')
# Save value, then reset choices
self.resetRef1Choices()
n1 = sublist[0]
s1 = getattr(base.Shape, n1)
# Show Reference1 and cusomize options within
@@ -152,8 +169,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
if self.CATS[1]:
self.CATS[1] = None
elif subCnt == 2:
self.resetRef1Choices()
self.resetRef2Choices()
debugMsg(' -subCnt == 2')
n1 = sublist[0]
n2 = sublist[1]
s1 = getattr(base.Shape, n1)
@@ -171,64 +187,55 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
else:
self.form.pathOrientation_label.hide()
self.form.pathOrientation.hide()
if hideFeatures:
# reset values
self.CATS = [None, None]
self.selectInComboBox('Start to End', self.form.pathOrientation)
# hide inputs and show message
self.form.featureReferences.hide()
self.form.customPoints.show()
"""
'Reference1': ['Center of Mass', 'Center of BoundBox',
'Lowest Point', 'Highest Point', 'Long Edge',
'Short Edge', 'Vertex'],
'Reference2': ['Center of Mass', 'Center of BoundBox',
'Lowest Point', 'Highest Point', 'Vertex']
"""
def customizeReference_1(self, sub, single=False):
debugMsg('customizeReference_1()')
# Customize Reference1 combobox options
# by removing unavailable choices
cat = sub[:4]
if cat != self.CATS[0]:
self.CATS[0] = cat
cBox = self.form.geo1Reference
cBox.blockSignals(True)
for ri in PathSlot.removeIndexesFromReference_1(sub, single):
cBox.removeItem(ri)
cBox.blockSignals(False)
slot = PathSlot.ObjectSlot
enums = slot._makeReference1Enumerations(slot, sub, single)
self.ENUMS['Reference1'] = enums
debugMsg('Ref1: {}'.format(enums))
self._updateComboBox(self.form.geo1Reference, enums)
# self.form.geo1Reference.setCurrentIndex(0)
# self.form.geo1Reference.setCurrentText(enums[0])
def customizeReference_2(self, sub):
debugMsg('customizeReference_2()')
# Customize Reference2 combobox options
# by removing unavailable choices
cat = sub[:4]
if cat != self.CATS[1]:
self.CATS[1] = cat
cBox = self.form.geo2Reference
cBox.blockSignals(True)
for ri in PathSlot.removeIndexesFromReference_2(sub):
cBox.removeItem(ri)
cBox.blockSignals(False)
cBox.setCurrentIndex(0)
def resetRef1Choices(self):
# Reset Reference1 choices
ref1 = self.form.geo1Reference
ref1.blockSignals(True)
ref1.clear() # Empty the combobox
ref1.addItems(self.propEnums['Reference1'])
ref1.blockSignals(False)
def resetRef2Choices(self):
# Reset Reference2 choices
ref2 = self.form.geo2Reference
ref2.blockSignals(True)
ref2.clear() # Empty the combobox
ref2.addItems(self.propEnums['Reference2'])
ref2.blockSignals(False)
slot = PathSlot.ObjectSlot
enums = slot._makeReference2Enumerations(slot, sub)
self.ENUMS['Reference2'] = enums
debugMsg('Ref2: {}'.format(enums))
self._updateComboBox(self.form.geo2Reference, enums)
# self.form.geo2Reference.setCurrentIndex(0)
# self.form.geo2Reference.setCurrentText(enums[0])
def registerSignalHandlers(self, obj):
# FreeCAD.Console.PrintMessage('registerSignalHandlers()\n')
# debugMsg('registerSignalHandlers()')
# self.form.pathOrientation.currentIndexChanged.connect(self.updateVisibility)
pass
def _updateComboBox(self, cBox, enums):
cBox.blockSignals(True)
cBox.clear()
cBox.addItems(enums)
cBox.blockSignals(False)
Command = PathOpGui.SetupOperation('Slot',
PathSlot.Create,

View File

@@ -235,7 +235,6 @@ void CosmeticEdgePy::setStart(Py::Object arg)
pNew = DrawUtil::invertY(pNew);
Base::Vector3d pEnd = getCosmeticEdgePtr()->permaEnd;
pEnd = DrawUtil::invertY(pEnd);
gp_Pnt gp1(pNew.x,pNew.y,pNew.z);
gp_Pnt gp2(pEnd.x,pEnd.y,pEnd.z);
TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp1, gp2);
@@ -269,7 +268,6 @@ void CosmeticEdgePy::setEnd(Py::Object arg)
pNew = DrawUtil::invertY(pNew);
Base::Vector3d pStart = getCosmeticEdgePtr()->permaStart;
pStart = DrawUtil::invertY(pStart);
gp_Pnt gp1(pNew.x,pNew.y,pNew.z);
gp_Pnt gp2(pStart.x,pStart.y,pStart.z);
TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp2, gp1);

View File

@@ -117,6 +117,7 @@ DrawViewDetail::DrawViewDetail()
//hide Properties not relevant to DVDetail
Direction.setStatus(App::Property::ReadOnly,true); //Should be same as BaseView
Rotation.setStatus(App::Property::ReadOnly,true); //same as BaseView
ScaleType.setValue("Custom"); //dvd uses scale from BaseView
}
DrawViewDetail::~DrawViewDetail()
@@ -471,10 +472,8 @@ void DrawViewDetail::unsetupObject()
if (base != nullptr) {
base->requestPaint();
}
}
void DrawViewDetail::getParameters()
{
}

View File

@@ -472,6 +472,8 @@ void TaskDetail::createDetail()
m_detailName.c_str(),m_baseName.c_str());
Gui::Command::doCommand(Command::Doc,"App.activeDocument().%s.XDirection = App.activeDocument().%s.XDirection",
m_detailName.c_str(),m_baseName.c_str());
Gui::Command::doCommand(Command::Doc,"App.activeDocument().%s.Scale = App.activeDocument().%s.Scale",
m_detailName.c_str(),m_baseName.c_str());
Gui::Command::doCommand(Command::Doc,"App.activeDocument().%s.addView(App.activeDocument().%s)",
m_pageName.c_str(), m_detailName.c_str());

View File

@@ -45,16 +45,16 @@
#include <Mod/Part/App/PartFeature.h>
#include <Mod/TechDraw/App/DrawPage.h>
#include <Mod/TechDraw/App/DrawProjGroupItem.h>
#include <Mod/TechDraw/App/DrawProjGroup.h>
#include <Mod/TechDraw/App/DrawUtil.h>
#include <Mod/TechDraw/App/DrawView.h>
#include <Mod/TechDraw/App/DrawViewPart.h>
#include <Mod/TechDraw/App/DrawProjGroupItem.h>
#include <Mod/TechDraw/App/DrawProjGroup.h>
#include "ViewProviderPage.h"
#include "ViewProviderProjGroup.h"
#include "ViewProviderProjGroupItem.h"
#include "ViewProviderPage.h"
#include "TaskProjGroup.h"
#include <Mod/TechDraw/Gui/ui_TaskProjGroup.h>
@@ -86,6 +86,13 @@ TaskProjGroup::TaskProjGroup(TechDraw::DrawProjGroup* featView, bool mode) :
ui->sbScaleDen->setEnabled(false);
}
ui->cbAutoDistribute->setChecked(multiView->AutoDistribute.getValue());
// disable if no AutoDistribute
ui->sbXSpacing->setEnabled(multiView->AutoDistribute.getValue());
ui->sbYSpacing->setEnabled(multiView->AutoDistribute.getValue());
ui->sbXSpacing->setValue(multiView->spacingX.getValue());
ui->sbYSpacing->setValue(multiView->spacingY.getValue());
// Initially toggle view checkboxes if needed
setupViewCheckboxes(true);
@@ -112,6 +119,13 @@ TaskProjGroup::TaskProjGroup(TechDraw::DrawProjGroup* featView, bool mode) :
// connect(ui->projection, SIGNAL(currentIndexChanged(int)), this, SLOT(projectionTypeChanged(int)));
connect(ui->projection, SIGNAL(currentIndexChanged(QString)), this, SLOT(projectionTypeChanged(QString)));
// Spacing
connect(ui->cbAutoDistribute, SIGNAL(clicked(bool)), this, SLOT(AutoDistributeClicked(bool)));
connect(ui->sbXSpacing, SIGNAL(valueChanged(double)), this, SLOT(spacingChanged(void)));
connect(ui->sbYSpacing, SIGNAL(valueChanged(double)), this, SLOT(spacingChanged(void)));
ui->sbXSpacing->setUnit(Base::Unit::Length);
ui->sbYSpacing->setUnit(Base::Unit::Length);
m_page = multiView->findParentPage();
Gui::Document* activeGui = Gui::Application::Instance->getDocument(m_page->getDocument());
Gui::ViewProvider* vp = activeGui->getViewProvider(m_page);
@@ -135,6 +149,9 @@ void TaskProjGroup::saveGroupState()
m_saveProjType = multiView->ProjectionType.getValueAsString();
m_saveScaleType = multiView->ScaleType.getValueAsString();
m_saveScale = multiView->Scale.getValue();
m_saveAutoDistribute = multiView->AutoDistribute.getValue();
m_saveSpacingX = multiView->spacingX.getValue();
m_saveSpacingY = multiView->spacingY.getValue();
DrawProjGroupItem* anchor = multiView->getAnchor();
m_saveDirection = anchor->Direction.getValue();
}
@@ -154,6 +171,9 @@ void TaskProjGroup::restoreGroupState()
multiView->ProjectionType.setValue(m_saveProjType.c_str());
multiView->ScaleType.setValue(m_saveScaleType.c_str());
multiView->Scale.setValue(m_saveScale);
multiView->AutoDistribute.setValue(m_saveAutoDistribute);
multiView->spacingX.setValue(m_saveSpacingX);
multiView->spacingY.setValue(m_saveSpacingY);
multiView->purgeProjections();
for(auto & sv : m_saveViewNames) {
if (sv != "Front") {
@@ -269,6 +289,25 @@ void TaskProjGroup::scaleTypeChanged(int index)
}
}
void TaskProjGroup::AutoDistributeClicked(bool b)
{
if (blockUpdate) {
return;
}
multiView->AutoDistribute.setValue(b);
multiView->recomputeFeature();
}
void TaskProjGroup::spacingChanged(void)
{
if (blockUpdate) {
return;
}
multiView->spacingX.setValue(ui->sbXSpacing->value().getValue());
multiView->spacingY.setValue(ui->sbYSpacing->value().getValue());
multiView->recomputeFeature();
}
std::pair<int, int> TaskProjGroup::nearestFraction(const double val, const long int maxDenom) const
{
/*

View File

@@ -85,6 +85,9 @@ protected Q_SLOTS:
/* void projectionTypeChanged(int index);*/
void projectionTypeChanged(QString qText);
void scaleTypeChanged(int index);
void AutoDistributeClicked(bool b);
/// Updates item spacing
void spacingChanged(void);
void scaleManuallyChanged(int i);
protected:
@@ -122,6 +125,9 @@ private:
std::string m_saveProjType;
std::string m_saveScaleType;
double m_saveScale;
bool m_saveAutoDistribute;
double m_saveSpacingX;
double m_saveSpacingY;
Base::Vector3d m_saveDirection;
std::vector<std::string> m_saveViewNames;
};

File diff suppressed because it is too large Load Diff