Merge branch 'master' into ArchWall_BugFixes_14
This commit is contained in:
@@ -156,7 +156,7 @@ void UnitsApi::setSchema(UnitSystem s)
|
||||
|
||||
QString UnitsApi::toString(const Base::Quantity& q, const QuantityFormat& f)
|
||||
{
|
||||
QString value = QString::fromLatin1("'%1 %2'").arg(q.getValue(), 0, f.toFormat(), f.precision+1)
|
||||
QString value = QString::fromLatin1("'%1 %2'").arg(q.getValue(), 0, f.toFormat(), f.precision+2)
|
||||
.arg(q.getUnit().getString());
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -2173,6 +2173,21 @@ void Application::runApplication(void)
|
||||
SoDebugError::setHandlerCallback( messageHandlerCoin, 0 );
|
||||
#endif
|
||||
|
||||
// Now run the background autoload, for workbenches that should be loaded at startup, but not
|
||||
// displayed to the user immediately
|
||||
std::string autoloadCSV = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
|
||||
GetASCII("BackgroundAutoloadModules", "");
|
||||
|
||||
// Tokenize the comma-separated list and load the requested workbenches if they exist in this installation
|
||||
std::vector<std::string> backgroundAutoloadedModules;
|
||||
std::stringstream stream(autoloadCSV);
|
||||
std::string workbench;
|
||||
while (std::getline(stream, workbench, ','))
|
||||
if (wb.contains(QString::fromLatin1(workbench.c_str())))
|
||||
app.activateWorkbench(workbench.c_str());
|
||||
|
||||
// Reactivate the startup workbench
|
||||
app.activateWorkbench(start.c_str());
|
||||
|
||||
Instance->d->startingUp = false;
|
||||
|
||||
|
||||
@@ -14,53 +14,37 @@
|
||||
<string>Unloaded Workbenches</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="loadButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Load the selected workbenches, adding their preference windows to the preferences dialog.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load Selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QListWidget" name="workbenchList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<widget class="QTableWidget" name="workbenchTable">
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Available unloaded workbenches</p></body></html></string>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Workbench Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Autoload?</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Load Now</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
@@ -78,26 +62,13 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>To preserve resources, FreeCAD does not load workbenches until they are used. Loading them may provide access to additional preferences related to their functionality.</p><p>The following workbenches are available in your installation, but are not yet loaded:</p></body></html></string>
|
||||
<string><html><head/><body><p>To preserve resources, FreeCAD does not load workbenches until they are used. Loading them may provide access to additional preferences related to their functionality.</p><p>The following workbenches are available in your installation:</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>429</width>
|
||||
<height>37</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
#include "WorkbenchManager.h"
|
||||
#include "Workbench.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace Gui::Dialog;
|
||||
|
||||
const uint DlgSettingsLazyLoadedImp::WorkbenchNameRole = Qt::UserRole;
|
||||
@@ -46,8 +50,6 @@ DlgSettingsLazyLoadedImp::DlgSettingsLazyLoadedImp( QWidget* parent )
|
||||
, ui(new Ui_DlgSettingsLazyLoaded)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
buildUnloadedWorkbenchList();
|
||||
connect(ui->loadButton, SIGNAL(clicked()), this, SLOT(onLoadClicked()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,22 +62,45 @@ DlgSettingsLazyLoadedImp::~DlgSettingsLazyLoadedImp()
|
||||
|
||||
void DlgSettingsLazyLoadedImp::saveSettings()
|
||||
{
|
||||
|
||||
std::ostringstream csv;
|
||||
for (const auto& checkbox : _autoloadCheckboxes) {
|
||||
if (checkbox.second->isChecked()) {
|
||||
if (!csv.str().empty())
|
||||
csv << ",";
|
||||
csv << checkbox.first.toStdString();
|
||||
}
|
||||
}
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
|
||||
SetASCII("BackgroundAutoloadModules", csv.str().c_str());
|
||||
}
|
||||
|
||||
void DlgSettingsLazyLoadedImp::loadSettings()
|
||||
{
|
||||
|
||||
// There are two different "autoload" settings: the first, in FreeCAD since 2004,
|
||||
// controls the module the user sees first when starting FreeCAD, and defaults to the Start workbench
|
||||
std::string start = App::Application::Config()["StartWorkbench"];
|
||||
_startupModule = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
|
||||
GetASCII("AutoloadModule", start.c_str());
|
||||
|
||||
// The second autoload setting does a background autoload of any number of other modules
|
||||
std::string autoloadCSV = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
|
||||
GetASCII("BackgroundAutoloadModules", "");
|
||||
|
||||
// Tokenize the comma-separated list
|
||||
_backgroundAutoloadedModules.clear();
|
||||
std::stringstream stream(autoloadCSV);
|
||||
std::string workbench;
|
||||
while (std::getline(stream, workbench, ','))
|
||||
_backgroundAutoloadedModules.push_back(workbench);
|
||||
|
||||
|
||||
buildUnloadedWorkbenchList();
|
||||
}
|
||||
|
||||
void DlgSettingsLazyLoadedImp::onLoadClicked()
|
||||
void DlgSettingsLazyLoadedImp::onLoadClicked(const QString &wbName)
|
||||
{
|
||||
Workbench* originalActiveWB = WorkbenchManager::instance()->active();
|
||||
auto selection = ui->workbenchList->selectedItems();
|
||||
for (const auto& item : selection) {
|
||||
auto name = item->data(WorkbenchNameRole).toString().toStdString();
|
||||
Application::Instance->activateWorkbench(name.c_str());
|
||||
}
|
||||
Application::Instance->activateWorkbench(wbName.toStdString().c_str());
|
||||
Application::Instance->activateWorkbench(originalActiveWB->name().c_str());
|
||||
buildUnloadedWorkbenchList();
|
||||
}
|
||||
@@ -86,21 +111,87 @@ Build the list of unloaded workbenches.
|
||||
*/
|
||||
void DlgSettingsLazyLoadedImp::buildUnloadedWorkbenchList()
|
||||
{
|
||||
ui->workbenchList->clear();
|
||||
QStringList workbenches = Application::Instance->workbenches();
|
||||
workbenches.sort();
|
||||
|
||||
ui->workbenchTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
|
||||
ui->workbenchTable->setRowCount(0);
|
||||
_autoloadCheckboxes.clear(); // setRowCount(0) just invalidated all of these pointers
|
||||
ui->workbenchTable->setColumnCount(4);
|
||||
ui->workbenchTable->setSelectionMode(QAbstractItemView::SelectionMode::NoSelection);
|
||||
ui->workbenchTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents);
|
||||
ui->workbenchTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
|
||||
ui->workbenchTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeMode::ResizeToContents);
|
||||
ui->workbenchTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeMode::ResizeToContents);
|
||||
QStringList columnHeaders;
|
||||
columnHeaders << QString() << tr("Workbench") << tr("Autoload") << QString();
|
||||
ui->workbenchTable->setHorizontalHeaderLabels(columnHeaders);
|
||||
|
||||
unsigned int rowNumber = 0;
|
||||
for (const auto& wbName : workbenches) {
|
||||
const auto& wb = WorkbenchManager::instance()->getWorkbench(wbName.toStdString());
|
||||
if (!wb) {
|
||||
auto wbIcon = Application::Instance->workbenchIcon(wbName);
|
||||
auto wbDisplayName = Application::Instance->workbenchMenuText(wbName);
|
||||
auto wbTooltip = Application::Instance->workbenchToolTip(wbName);
|
||||
QListWidgetItem *wbRow = new QListWidgetItem(wbIcon, wbDisplayName);
|
||||
wbRow->setData(WorkbenchNameRole, QVariant(wbName)); // Store the actual internal name for easier loading
|
||||
wbRow->setToolTip(wbTooltip);
|
||||
ui->workbenchList->addItem(wbRow); // Transfers ownership to the QListWidget
|
||||
if (wbName.toStdString() == "NoneWorkbench")
|
||||
continue; // Do not list the default empty Workbench
|
||||
|
||||
ui->workbenchTable->insertRow(rowNumber);
|
||||
auto wbTooltip = Application::Instance->workbenchToolTip(wbName);
|
||||
|
||||
// Column 1: Workbench Icon
|
||||
auto wbIcon = Application::Instance->workbenchIcon(wbName);
|
||||
auto iconLabel = new QLabel();
|
||||
iconLabel->setPixmap(wbIcon.scaled(QSize(20,20), Qt::AspectRatioMode::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
|
||||
iconLabel->setToolTip(wbTooltip);
|
||||
iconLabel->setContentsMargins(5, 3, 3, 3); // Left, top, right, bottom
|
||||
ui->workbenchTable->setCellWidget(rowNumber, 0, iconLabel);
|
||||
|
||||
// Column 2: Workbench Display Name
|
||||
auto wbDisplayName = Application::Instance->workbenchMenuText(wbName);
|
||||
auto textLabel = new QLabel(wbDisplayName);
|
||||
textLabel->setToolTip(wbTooltip);
|
||||
ui->workbenchTable->setCellWidget(rowNumber, 1, textLabel);
|
||||
|
||||
// Column 3: Autoloaded checkbox
|
||||
//
|
||||
// To get the checkbox centered, we have to jump through some hoops...
|
||||
QWidget* checkWidget = new QWidget(this);
|
||||
auto autoloadCheckbox = new QCheckBox(this);
|
||||
autoloadCheckbox->setToolTip(tr("If checked") +
|
||||
QString::fromUtf8(", ") + wbDisplayName + QString::fromUtf8(" ") +
|
||||
tr("will be loaded automatically when FreeCAD starts up"));
|
||||
QHBoxLayout* checkLayout = new QHBoxLayout(checkWidget);
|
||||
checkLayout->addWidget(autoloadCheckbox);
|
||||
checkLayout->setAlignment(Qt::AlignCenter);
|
||||
checkLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// Figure out whether to check and/or disable this checkbox:
|
||||
if (wbName.toStdString() == _startupModule) {
|
||||
autoloadCheckbox->setChecked(true);
|
||||
autoloadCheckbox->setEnabled(false);
|
||||
autoloadCheckbox->setToolTip(tr("This is the current startup module, and must be autoloaded. See Preferences/General/Autoload to change."));
|
||||
}
|
||||
else if (std::find(_backgroundAutoloadedModules.begin(), _backgroundAutoloadedModules.end(),
|
||||
wbName.toStdString()) != _backgroundAutoloadedModules.end()) {
|
||||
autoloadCheckbox->setChecked(true);
|
||||
_autoloadCheckboxes.insert(std::make_pair(wbName, autoloadCheckbox));
|
||||
}
|
||||
else {
|
||||
_autoloadCheckboxes.insert(std::make_pair(wbName, autoloadCheckbox));
|
||||
}
|
||||
ui->workbenchTable->setCellWidget(rowNumber, 2, checkWidget);
|
||||
|
||||
// Column 4: Load button/loaded indicator
|
||||
if (WorkbenchManager::instance()->getWorkbench(wbName.toStdString())) {
|
||||
auto label = new QLabel(tr("Loaded"));
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
ui->workbenchTable->setCellWidget(rowNumber, 3, label);
|
||||
}
|
||||
else {
|
||||
auto button = new QPushButton(tr("Load now"));
|
||||
connect(button, &QPushButton::clicked, this, [this,wbName]() { onLoadClicked(wbName); });
|
||||
ui->workbenchTable->setCellWidget(rowNumber, 3, button);
|
||||
}
|
||||
|
||||
++rowNumber;
|
||||
}
|
||||
ui->workbenchList->sortItems();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include "PropertyPage.h"
|
||||
#include <memory>
|
||||
|
||||
class QCheckBox;
|
||||
|
||||
namespace Gui {
|
||||
namespace Dialog {
|
||||
class Ui_DlgSettingsLazyLoaded;
|
||||
@@ -49,7 +51,7 @@ public:
|
||||
void loadSettings();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void onLoadClicked();
|
||||
void onLoadClicked(const QString& wbName);
|
||||
|
||||
protected:
|
||||
void buildUnloadedWorkbenchList();
|
||||
@@ -58,6 +60,10 @@ protected:
|
||||
private:
|
||||
std::unique_ptr<Ui_DlgSettingsLazyLoaded> ui;
|
||||
static const uint WorkbenchNameRole;
|
||||
|
||||
std::vector<std::string> _backgroundAutoloadedModules;
|
||||
std::string _startupModule;
|
||||
std::map<QString, QCheckBox*> _autoloadCheckboxes;
|
||||
};
|
||||
|
||||
} // namespace Dialog
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
FC_VIEW_PARAM(MarkerSize,int,Int,9) \
|
||||
FC_VIEW_PARAM(DefaultLinkColor,unsigned long,Unsigned,0x66FFFF00) \
|
||||
FC_VIEW_PARAM(DefaultShapeLineColor,unsigned long,Unsigned,421075455UL) \
|
||||
FC_VIEW_PARAM(DefaultShapeVertexColor,unsigned long,Unsigned,421075455UL) \
|
||||
FC_VIEW_PARAM(DefaultShapeColor,unsigned long,Unsigned,0xCCCCCC00) \
|
||||
FC_VIEW_PARAM(DefaultShapeLineWidth,int,Int,2) \
|
||||
FC_VIEW_PARAM(DefaultShapePointSize,int,Int,2) \
|
||||
|
||||
+1
-1
@@ -373,7 +373,7 @@ private:
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
class PropertyListEditor : public QPlainTextEdit
|
||||
class GuiExport PropertyListEditor : public QPlainTextEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
@@ -1051,7 +1051,7 @@ void PropertyUnitItem::setValue(const QVariant& value)
|
||||
return;
|
||||
const Base::Quantity& val = value.value<Base::Quantity>();
|
||||
|
||||
Base::QuantityFormat format(Base::QuantityFormat::Default, decimals());
|
||||
Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals());
|
||||
QString unit = Base::UnitsApi::toString(val, format);
|
||||
setPropertyValue(unit);
|
||||
}
|
||||
@@ -1644,7 +1644,7 @@ void PropertyVectorDistanceItem::setValue(const QVariant& variant)
|
||||
Base::Quantity y = Base::Quantity(value.y, Base::Unit::Length);
|
||||
Base::Quantity z = Base::Quantity(value.z, Base::Unit::Length);
|
||||
|
||||
Base::QuantityFormat format(Base::QuantityFormat::Default, decimals());
|
||||
Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals());
|
||||
QString data = QString::fromLatin1("(%1, %2, %3)")
|
||||
.arg(Base::UnitsApi::toNumber(x, format))
|
||||
.arg(Base::UnitsApi::toNumber(y, format))
|
||||
@@ -2370,7 +2370,7 @@ void PropertyPlacementItem::setValue(const QVariant& value)
|
||||
const Base::Placement& val = value.value<Base::Placement>();
|
||||
Base::Vector3d pos = val.getPosition();
|
||||
|
||||
Base::QuantityFormat format(Base::QuantityFormat::Default, decimals());
|
||||
Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals());
|
||||
QString data = QString::fromLatin1("App.Placement("
|
||||
"App.Vector(%1,%2,%3),"
|
||||
"App.Rotation(App.Vector(%4,%5,%6),%7))")
|
||||
|
||||
@@ -1095,7 +1095,7 @@ class DraftToolBar:
|
||||
self.taskUi(title, icon="Draft_Trimex")
|
||||
self.radiusUi()
|
||||
self.labelRadius.setText(translate("draft","Distance"))
|
||||
self.radiusValue.setToolTip(translate("draft", "Trim distance"))
|
||||
self.radiusValue.setToolTip(translate("draft", "Offset distance"))
|
||||
self.radiusValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
|
||||
todo.delay(self.radiusValue.setFocus,None)
|
||||
self.radiusValue.selectAll()
|
||||
|
||||
@@ -203,9 +203,21 @@ class Trimex(gui_base_original.Modifier):
|
||||
if self.extrudeMode:
|
||||
dist = self.extrude(self.shift)
|
||||
else:
|
||||
dist = self.redraw(self.point, self.snapped,
|
||||
self.shift, self.alt)
|
||||
self.ui.setRadiusValue(dist, unit="Length")
|
||||
# If the geomType of the edge is "Line" ang will be None,
|
||||
# else dist will be None.
|
||||
dist, ang = self.redraw(self.point, self.snapped,
|
||||
self.shift, self.alt)
|
||||
|
||||
if dist:
|
||||
self.ui.labelRadius.setText(translate("draft", "Distance"))
|
||||
self.ui.radiusValue.setToolTip(translate("draft",
|
||||
"Offset distance"))
|
||||
self.ui.setRadiusValue(dist, unit="Length")
|
||||
else:
|
||||
self.ui.labelRadius.setText(translate("draft", "Angle"))
|
||||
self.ui.radiusValue.setToolTip(translate("draft",
|
||||
"Offset angle"))
|
||||
self.ui.setRadiusValue(ang, unit="Angle")
|
||||
self.ui.radiusValue.setFocus()
|
||||
self.ui.radiusValue.selectAll()
|
||||
gui_tool_utils.redraw3DView()
|
||||
@@ -301,6 +313,7 @@ class Trimex(gui_base_original.Modifier):
|
||||
|
||||
# modifying active edge
|
||||
if DraftGeomUtils.geomType(edge) == "Line":
|
||||
ang = None
|
||||
ve = DraftGeomUtils.vec(edge)
|
||||
chord = v1.sub(point)
|
||||
n = ve.cross(chord)
|
||||
@@ -313,9 +326,6 @@ class Trimex(gui_base_original.Modifier):
|
||||
dist = v1.sub(self.newpoint).Length
|
||||
ghost.p1(self.newpoint)
|
||||
ghost.p2(v2)
|
||||
self.ui.labelRadius.setText(translate("draft", "Distance"))
|
||||
self.ui.radiusValue.setToolTip(translate("draft",
|
||||
"The offset distance"))
|
||||
if real:
|
||||
if self.force:
|
||||
ray = self.newpoint.sub(v1)
|
||||
@@ -323,16 +333,14 @@ class Trimex(gui_base_original.Modifier):
|
||||
self.newpoint = App.Vector.add(v1, ray)
|
||||
newedges.append(Part.LineSegment(self.newpoint, v2).toShape())
|
||||
else:
|
||||
dist = None
|
||||
center = edge.Curve.Center
|
||||
rad = edge.Curve.Radius
|
||||
ang1 = DraftVecUtils.angle(v2.sub(center))
|
||||
ang2 = DraftVecUtils.angle(point.sub(center))
|
||||
_rot_rad = DraftVecUtils.rotate(App.Vector(rad, 0, 0), -ang2)
|
||||
self.newpoint = App.Vector.add(center, _rot_rad)
|
||||
self.ui.labelRadius.setText(translate("draft", "Angle"))
|
||||
self.ui.radiusValue.setToolTip(translate("draft",
|
||||
"The offset angle"))
|
||||
dist = math.degrees(-ang2)
|
||||
ang = math.degrees(-ang2)
|
||||
# if ang1 > ang2:
|
||||
# ang1, ang2 = ang2, ang1
|
||||
# print("last calculated:",
|
||||
@@ -384,7 +392,7 @@ class Trimex(gui_base_original.Modifier):
|
||||
if real:
|
||||
return newedges
|
||||
else:
|
||||
return dist
|
||||
return [dist, ang]
|
||||
|
||||
def trimObject(self):
|
||||
"""Trim the actual object."""
|
||||
|
||||
@@ -35,7 +35,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": [],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "frequency"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "force", "pressure"],
|
||||
"solvers": ["calculix", "elmer"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria6",
|
||||
"constraints": ["displacement", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "buckling"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria6",
|
||||
"constraints": ["displacement", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "buckling"
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Hexa8",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "buckling"
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ def setup_cantilever_base_solid(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "CalculiX-Steel"
|
||||
mat["YoungsModulus"] = "210000 MPa"
|
||||
mat["PoissonRatio"] = "0.30"
|
||||
mat["Density"] = "7900 kg/m^3"
|
||||
material_obj.Material = mat
|
||||
analysis.addObject(material_obj)
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ def get_information():
|
||||
"meshtype": "edge",
|
||||
"meshelement": "Seg3",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ def get_information():
|
||||
"meshtype": "edge",
|
||||
"meshelement": "Seg3",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ def get_information():
|
||||
"meshtype": "edge",
|
||||
"meshelement": "Seg3",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Hexa20",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "elmer", "z88"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer", "z88"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Quad4",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "mystran"],
|
||||
"solvers": ["calculix", "ccxtools", "mystran"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Quad8",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "edge",
|
||||
"meshelement": "Seg2",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "mystran"],
|
||||
"solvers": ["calculix", "ccxtools", "mystran"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ def get_information():
|
||||
"meshtype": "edge",
|
||||
"meshelement": "Seg3",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tetra4",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "elmer", "mystran", "z88"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer", "mystran", "z88"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria3",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "mystran"],
|
||||
"solvers": ["calculix", "ccxtools", "mystran"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria6",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "z88"],
|
||||
"solvers": ["calculix", "ccxtools", "z88"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "elmer", "mystran", "z88"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer", "mystran", "z88"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "elmer", "mystran", "z88"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer", "mystran", "z88"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "displacement"],
|
||||
"solvers": ["calculix", "elmer"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["centrif", "fixed"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "multimaterial",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria3",
|
||||
"constraints": ["fixed", "force", "contact"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "pressure", "contact"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["section_print", "fixed", "pressure"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "self weight"],
|
||||
"solvers": ["calculix", "elmer"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "force", "tie"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["pressure", "displacement", "transform"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
@@ -141,8 +141,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "CalculiX-Steel"
|
||||
mat["YoungsModulus"] = "210000 MPa"
|
||||
mat["PoissonRatio"] = "0.30"
|
||||
mat["Density"] = "7900 kg/m^3"
|
||||
mat["ThermalExpansionCoefficient"] = "0.012 mm/m/K"
|
||||
material_obj.Material = mat
|
||||
analysis.addObject(material_obj)
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "force", "transform"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "frequency"
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "multimaterial",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
@@ -148,7 +148,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "Concrete-Generic"
|
||||
mat["YoungsModulus"] = "32000 MPa"
|
||||
mat["PoissonRatio"] = "0.17"
|
||||
mat["Density"] = "0 kg/m^3"
|
||||
material_obj1.Material = mat
|
||||
analysis.addObject(material_obj1)
|
||||
|
||||
@@ -158,7 +157,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "PLA"
|
||||
mat["YoungsModulus"] = "3640 MPa"
|
||||
mat["PoissonRatio"] = "0.36"
|
||||
mat["Density"] = "0 kg/m^3"
|
||||
material_obj2.Material = mat
|
||||
analysis.addObject(material_obj2)
|
||||
|
||||
@@ -168,7 +166,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "Steel-Generic"
|
||||
mat["YoungsModulus"] = "200000 MPa"
|
||||
mat["PoissonRatio"] = "0.30"
|
||||
mat["Density"] = "7900 kg/m^3"
|
||||
material_obj3.Material = mat
|
||||
analysis.addObject(material_obj3)
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria6",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "multimaterial",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
@@ -141,7 +141,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "Concrete-Generic"
|
||||
mat["YoungsModulus"] = "32000 MPa"
|
||||
mat["PoissonRatio"] = "0.17"
|
||||
mat["Density"] = "0 kg/m^3"
|
||||
material_obj1.Material = mat
|
||||
analysis.addObject(material_obj1)
|
||||
|
||||
@@ -154,7 +153,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "PLA"
|
||||
mat["YoungsModulus"] = "3640 MPa"
|
||||
mat["PoissonRatio"] = "0.36"
|
||||
mat["Density"] = "0 kg/m^3"
|
||||
material_obj2.Material = mat
|
||||
analysis.addObject(material_obj2)
|
||||
|
||||
@@ -164,7 +162,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "Steel-Generic"
|
||||
mat["YoungsModulus"] = "200000 MPa"
|
||||
mat["PoissonRatio"] = "0.30"
|
||||
mat["Density"] = "7900 kg/m^3"
|
||||
material_obj3.Material = mat
|
||||
analysis.addObject(material_obj3)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "pressure"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "multimaterial",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
@@ -132,7 +132,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "Aluminium-Generic"
|
||||
mat["YoungsModulus"] = "70000 MPa"
|
||||
mat["PoissonRatio"] = "0.35"
|
||||
mat["Density"] = "2700 kg/m^3"
|
||||
material_obj_low.Material = mat
|
||||
material_obj_low.References = [(boxlow, "Solid1")]
|
||||
analysis.addObject(material_obj_low)
|
||||
@@ -142,7 +141,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "Steel-Generic"
|
||||
mat["YoungsModulus"] = "200000 MPa"
|
||||
mat["PoissonRatio"] = "0.30"
|
||||
mat["Density"] = "7980 kg/m^3"
|
||||
material_obj_upp.Material = mat
|
||||
material_obj_upp.References = [(boxupp, "Solid1")]
|
||||
analysis.addObject(material_obj_upp)
|
||||
|
||||
@@ -49,7 +49,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "nonlinear",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
@@ -143,7 +143,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
matprop["Name"] = "CalculiX-Steel"
|
||||
matprop["YoungsModulus"] = "210000 MPa"
|
||||
matprop["PoissonRatio"] = "0.30"
|
||||
matprop["Density"] = "7900 kg/m^3"
|
||||
material_obj.Material = matprop
|
||||
analysis.addObject(material_obj)
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ def get_information():
|
||||
"name": "Mystran Plate",
|
||||
"meshtype": "face",
|
||||
"meshelement": "Quad4",
|
||||
"constraints": ["displacement", "force"],
|
||||
"solvers": ["calculix", "elmer", "mystran"],
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer", "mystran"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria6",
|
||||
"constraints": ["fixed", "force", "displacement"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "reinforced",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
@@ -133,7 +133,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
matrixprop["CompressiveStrength"] = "15.75 MPa"
|
||||
# make some hint on the possible angle units in material system
|
||||
matrixprop["AngleOfFriction"] = "30 deg"
|
||||
matrixprop["Density"] = "2500 kg/m^3"
|
||||
reinfoprop = {}
|
||||
reinfoprop["Name"] = "Reinforcement-FIB-B500"
|
||||
reinfoprop["YieldStrength"] = "315 MPa"
|
||||
@@ -160,6 +159,7 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
# constraint displacement
|
||||
con_disp = ObjectsFem.makeConstraintDisplacement(doc, "ConstraintDisplacmentPrescribed")
|
||||
con_disp.References = [(geom_obj, "Face1")]
|
||||
con_disp.zFree = False
|
||||
con_disp.zFix = True
|
||||
analysis.addObject(con_disp)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria6",
|
||||
"constraints": ["force", "fixed"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
@@ -120,7 +120,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "Steel-Generic"
|
||||
mat["YoungsModulus"] = "200000 MPa"
|
||||
mat["PoissonRatio"] = "0.30"
|
||||
mat["Density"] = "7900 kg/m^3"
|
||||
material_obj.Material = mat
|
||||
analysis.addObject(material_obj)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ def get_information():
|
||||
"meshtype": "face",
|
||||
"meshelement": "Tria6",
|
||||
"constraints": ["force", "fixed"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
@@ -230,7 +230,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["Name"] = "Steel-Generic"
|
||||
mat["YoungsModulus"] = "200000 MPa"
|
||||
mat["PoissonRatio"] = "0.30"
|
||||
mat["Density"] = "7900 kg/m^3"
|
||||
material_obj.Material = mat
|
||||
analysis.addObject(material_obj)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "initial temperature", "temperature"],
|
||||
"solvers": ["calculix", "elmer"],
|
||||
"solvers": ["calculix", "ccxtools", "elmer"],
|
||||
"material": "multimaterial",
|
||||
"equation": "thermomechanical"
|
||||
}
|
||||
@@ -174,7 +174,6 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
mat["SpecificHeat"] = "510 J/kg/K"
|
||||
mat["ThermalConductivity"] = "13 W/m/K"
|
||||
mat["ThermalExpansionCoefficient"] = "0.0000012 m/m/K"
|
||||
mat["Density"] = "1.00 kg/m^3"
|
||||
material_obj_top.Material = mat
|
||||
material_obj_top.References = [(geom_obj, "Solid2")]
|
||||
analysis.addObject(material_obj_top)
|
||||
|
||||
@@ -41,7 +41,7 @@ def get_information():
|
||||
"meshtype": "edge",
|
||||
"meshelement": "Seg3",
|
||||
"constraints": ["self weight"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "fluid",
|
||||
"equation": "thermomechanical"
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ def get_information():
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["fixed", "initial temperature", "temperature", "heatflux"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "thermomechanical"
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ def get_information():
|
||||
"meshtype": "edge",
|
||||
"meshelement": "Seg3",
|
||||
"constraints": ["fixed", "force"],
|
||||
"solvers": ["calculix"],
|
||||
"solvers": ["calculix", "ccxtools"],
|
||||
"material": "solid",
|
||||
"equation": "mechanical"
|
||||
}
|
||||
|
||||
@@ -2225,7 +2225,7 @@ def get_femmesh_eletype(
|
||||
if not femmesh:
|
||||
FreeCAD.Console.PrintError("Error: No femmesh.\n")
|
||||
if not femelement_table:
|
||||
FreeCAD.Console.PrintError("The femelement_table need to be calculated.\n")
|
||||
FreeCAD.Console.PrintWarning("The femelement_table need to be calculated.\n")
|
||||
femelement_table = get_femelement_table(femmesh)
|
||||
# in some cases lowest key in femelement_table is not [1]
|
||||
for elem in sorted(femelement_table):
|
||||
|
||||
@@ -51,7 +51,7 @@ _inputFileName = None
|
||||
class Check(run.Check):
|
||||
|
||||
def run(self):
|
||||
self.pushStatus("Checking analysis...\n")
|
||||
self.pushStatus("Checking analysis member...\n")
|
||||
self.check_mesh_exists()
|
||||
|
||||
# workaround use Calculix ccxtools pre checks
|
||||
@@ -73,13 +73,12 @@ class Prepare(run.Prepare):
|
||||
|
||||
def run(self):
|
||||
global _inputFileName
|
||||
self.pushStatus("Preparing input files...\n")
|
||||
|
||||
mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already
|
||||
self.pushStatus("Preparing input...\n")
|
||||
|
||||
# get mesh set data
|
||||
# TODO evaluate if it makes sense to add new task
|
||||
# between check and prepare to the solver frame work
|
||||
mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already
|
||||
meshdatagetter = meshsetsgetter.MeshSetsGetter(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
@@ -88,7 +87,7 @@ class Prepare(run.Prepare):
|
||||
)
|
||||
meshdatagetter.get_mesh_sets()
|
||||
|
||||
# write input file
|
||||
# write solver input
|
||||
w = writer.FemInputWriterCcx(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
@@ -100,9 +99,9 @@ class Prepare(run.Prepare):
|
||||
path = w.write_solver_input()
|
||||
# report to user if task succeeded
|
||||
if path != "" and os.path.isfile(path):
|
||||
self.pushStatus("Write completed.")
|
||||
self.pushStatus("Writing solver input completed.")
|
||||
else:
|
||||
self.pushStatus("Writing CalculiX solver input file failed,")
|
||||
self.pushStatus("Writing solver input failed.")
|
||||
self.fail()
|
||||
_inputFileName = os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
@@ -112,7 +111,13 @@ class Solve(run.Solve):
|
||||
def run(self):
|
||||
self.pushStatus("Executing solver...\n")
|
||||
|
||||
# get solver binary
|
||||
self.pushStatus("Get solver binary...\n")
|
||||
binary = settings.get_binary("Calculix")
|
||||
if binary is None:
|
||||
self.fail() # a print has been made in settings module
|
||||
|
||||
# run solver
|
||||
self._process = subprocess.Popen(
|
||||
[binary, "-i", _inputFileName],
|
||||
cwd=self.directory,
|
||||
@@ -131,10 +136,6 @@ class Solve(run.Solve):
|
||||
class Results(run.Results):
|
||||
|
||||
def run(self):
|
||||
if not _inputFileName:
|
||||
# TODO do not run solver
|
||||
# do not try to read results in a smarter way than an Exception
|
||||
raise Exception("Error on writing CalculiX input file.\n")
|
||||
prefs = FreeCAD.ParamGet(
|
||||
"User parameter:BaseApp/Preferences/Mod/Fem/General")
|
||||
if not prefs.GetBool("KeepResultsOnReRun", False):
|
||||
@@ -142,10 +143,12 @@ class Results(run.Results):
|
||||
self.load_results()
|
||||
|
||||
def purge_results(self):
|
||||
|
||||
# dat file will not be removed
|
||||
# results from other solvers will be removed too
|
||||
# the user should decide if purge should only delete the solver results or all results
|
||||
self.pushStatus("Purge existing results...\n")
|
||||
# TODO dat file will not be removed
|
||||
# TODO implement a generic purge method
|
||||
# TODO results from other solvers will be removed too
|
||||
# the user should decide if purge should only
|
||||
# delete this solver results or results from all solvers
|
||||
for m in membertools.get_member(self.analysis, "Fem::FemResultObject"):
|
||||
if m.Mesh and femutils.is_of_type(m.Mesh, "Fem::MeshResult"):
|
||||
self.analysis.Document.removeObject(m.Mesh.Name)
|
||||
@@ -153,10 +156,11 @@ class Results(run.Results):
|
||||
self.analysis.Document.recompute()
|
||||
|
||||
def load_results(self):
|
||||
self.load_results_ccxfrd()
|
||||
self.load_results_ccxdat()
|
||||
self.pushStatus("Import new results...\n")
|
||||
self.load_ccxfrd_results()
|
||||
self.load_ccxdat_results()
|
||||
|
||||
def load_results_ccxfrd(self):
|
||||
def load_ccxfrd_results(self):
|
||||
frd_result_file = os.path.join(
|
||||
self.directory, _inputFileName + ".frd")
|
||||
if os.path.isfile(frd_result_file):
|
||||
@@ -164,18 +168,26 @@ class Results(run.Results):
|
||||
importCcxFrdResults.importFrd(
|
||||
frd_result_file, self.analysis, result_name_prefix)
|
||||
else:
|
||||
raise Exception(
|
||||
"FEM: No results found at {}!".format(frd_result_file))
|
||||
# TODO: use solver framework status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n"
|
||||
.format(frd_result_file)
|
||||
)
|
||||
self.fail()
|
||||
|
||||
def load_results_ccxdat(self):
|
||||
def load_ccxdat_results(self):
|
||||
dat_result_file = os.path.join(
|
||||
self.directory, _inputFileName + ".dat")
|
||||
if os.path.isfile(dat_result_file):
|
||||
mode_frequencies = importCcxDatResults.import_dat(
|
||||
dat_result_file, self.analysis)
|
||||
else:
|
||||
raise Exception(
|
||||
"FEM: No .dat results found at {}!".format(dat_result_file))
|
||||
# TODO: use solver framework status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n"
|
||||
.format(dat_result_file)
|
||||
)
|
||||
self.fail()
|
||||
if mode_frequencies:
|
||||
for m in membertools.get_member(self.analysis, "Fem::FemResultObject"):
|
||||
if m.Eigenmode > 0:
|
||||
|
||||
@@ -60,8 +60,9 @@ def write_step_output(f, ccxwriter):
|
||||
# reaction forces: freecadweb.org/tracker/view.php?id=2934
|
||||
# some hint can be found in this topic:
|
||||
# https://forum.freecadweb.org/viewtopic.php?f=18&t=20664&start=10#p520642
|
||||
if ccxwriter.member.cons_fixed:
|
||||
if ccxwriter.member.cons_fixed or ccxwriter.member.cons_displacement:
|
||||
f.write("** outputs --> dat file\n")
|
||||
if ccxwriter.member.cons_fixed:
|
||||
# reaction forces for all Constraint fixed
|
||||
f.write("** reaction forces for Constraint fixed\n")
|
||||
for femobj in ccxwriter.member.cons_fixed:
|
||||
@@ -69,7 +70,16 @@ def write_step_output(f, ccxwriter):
|
||||
fix_obj_name = femobj["Object"].Name
|
||||
f.write("*NODE PRINT, NSET={}, TOTALS=ONLY\n".format(fix_obj_name))
|
||||
f.write("RF\n")
|
||||
# TODO: add Constraint Displacement if nodes are restrained
|
||||
if ccxwriter.member.cons_displacement:
|
||||
# reaction forces for Constraint displacement constraining translation
|
||||
f.write("** reaction forces for Constraint displacement constraining translations\n")
|
||||
for femobj in ccxwriter.member.cons_displacement:
|
||||
if not femobj["Object"].xFree or not femobj["Object"].yFree or not femobj["Object"].zFree:
|
||||
# femobj --> dict, FreeCAD document object is femobj["Object"]
|
||||
disp_obj_name = femobj["Object"].Name
|
||||
f.write("*NODE PRINT, NSET={}, TOTALS=ONLY\n".format(disp_obj_name))
|
||||
f.write("RF\n")
|
||||
if ccxwriter.member.cons_fixed or ccxwriter.member.cons_displacement:
|
||||
f.write("\n")
|
||||
|
||||
# there is no need to write all integration point results
|
||||
|
||||
@@ -35,7 +35,7 @@ def add_con_fixed(f, model, mystran_writer):
|
||||
# spc1 card
|
||||
spc_ids = []
|
||||
fixed_code = "# spc1 card, Defines a set of single-point constraints\n"
|
||||
for i, femobj in enumerate(mystran_writer.fixed_objects):
|
||||
for i, femobj in enumerate(mystran_writer.member.cons_fixed):
|
||||
|
||||
conid = i + 2 # 1 will be the conid of the spcadd card
|
||||
spc_ids.append(conid)
|
||||
|
||||
@@ -36,7 +36,7 @@ def add_con_force(f, model, mystran_writer):
|
||||
scale_factors = []
|
||||
load_ids = []
|
||||
force_code = "# force cards, mesh node loads\n"
|
||||
for i, femobj in enumerate(mystran_writer.force_objects):
|
||||
for i, femobj in enumerate(mystran_writer.member.cons_force):
|
||||
|
||||
sid = i + 2 # 1 will be the id of the load card
|
||||
scale_factors.append(1.0)
|
||||
|
||||
@@ -57,7 +57,7 @@ _inputFileName = None
|
||||
class Check(run.Check):
|
||||
|
||||
def run(self):
|
||||
self.pushStatus("Checking analysis...\n")
|
||||
self.pushStatus("Checking analysis member...\n")
|
||||
self.check_mesh_exists()
|
||||
self.check_material_exists()
|
||||
self.check_material_single() # no multiple material
|
||||
@@ -70,13 +70,11 @@ class Prepare(run.Prepare):
|
||||
|
||||
def run(self):
|
||||
global _inputFileName
|
||||
self.pushStatus("Preparing input files...\n")
|
||||
|
||||
mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already
|
||||
self.pushStatus("Preparing solver input...\n")
|
||||
|
||||
# get mesh set data
|
||||
# TODO evaluate if it makes sense to add new task
|
||||
# between check and prepare to the solver frame work
|
||||
# TODO see calculix tasks get mesh set data
|
||||
mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already
|
||||
meshdatagetter = meshsetsgetter.MeshSetsGetter(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
@@ -85,44 +83,36 @@ class Prepare(run.Prepare):
|
||||
)
|
||||
meshdatagetter.get_mesh_sets()
|
||||
|
||||
# write input file
|
||||
# write solver input
|
||||
w = writer.FemInputWriterMystran(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
mesh_obj,
|
||||
meshdatagetter.member,
|
||||
self.directory,
|
||||
meshdatagetter.mat_geo_sets
|
||||
)
|
||||
path = w.write_solver_input()
|
||||
# report to user if task succeeded
|
||||
if path != "":
|
||||
self.pushStatus("Write completed!")
|
||||
self.pushStatus("Writing solver input completed.")
|
||||
else:
|
||||
self.pushStatus("Writing CalculiX input file failed!")
|
||||
self.pushStatus("Writing solver input failed.")
|
||||
self.fail()
|
||||
_inputFileName = os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
|
||||
class Solve(run.Solve):
|
||||
|
||||
def run(self):
|
||||
# print(_inputFileName)
|
||||
if not _inputFileName:
|
||||
# TODO do not run solver, do not try to read results in a smarter way than an Exception
|
||||
raise Exception("Error on writing Mystran input file.\n")
|
||||
self.pushStatus("Executing solver...\n")
|
||||
|
||||
infile = _inputFileName + ".bdf"
|
||||
|
||||
# TODO use solver framework status system
|
||||
FreeCAD.Console.PrintMessage("Mystran: solver input file: {} \n\n".format(infile))
|
||||
|
||||
# get binary
|
||||
self.pushStatus("Get solver...\n")
|
||||
# get solver binary
|
||||
self.pushStatus("Get solver binary...\n")
|
||||
binary = settings.get_binary("Mystran")
|
||||
# use preferences editor to add a group Mystran and the prefs:
|
||||
# "UseStandardMystranLocation" --> bool, set to False
|
||||
# "mystranBinaryPath, string" --> the binary path
|
||||
if binary is None:
|
||||
return # a print has been made in settings module
|
||||
self.fail() # a print has been made in settings module
|
||||
|
||||
# run solver
|
||||
self.pushStatus("Executing solver...\n")
|
||||
@@ -147,19 +137,19 @@ class Results(run.Results):
|
||||
if not prefs.GetBool("KeepResultsOnReRun", False):
|
||||
self.purge_results()
|
||||
if result_reading is True:
|
||||
self.load_results() # ToDo in all solvers generischer name
|
||||
self.load_results()
|
||||
|
||||
def purge_results(self):
|
||||
self.pushStatus("Purge existing results...\n")
|
||||
# TODO see calculix result tasks
|
||||
for m in membertools.get_member(self.analysis, "Fem::FemResultObject"):
|
||||
if femutils.is_of_type(m.Mesh, "Fem::MeshResult"):
|
||||
self.analysis.Document.removeObject(m.Mesh.Name)
|
||||
self.analysis.Document.removeObject(m.Name)
|
||||
self.analysis.Document.recompute()
|
||||
# deletes all results from any solver
|
||||
# TODO: delete only the mystran results, fix in all solver
|
||||
|
||||
def load_results(self):
|
||||
self.pushStatus("Import results...\n")
|
||||
self.pushStatus("Import new results...\n")
|
||||
neu_result_file = os.path.join(self.directory, _inputFileName + ".NEU")
|
||||
if os.path.isfile(neu_result_file):
|
||||
hfcMystranNeuIn.import_neu(neu_result_file)
|
||||
@@ -169,11 +159,11 @@ class Results(run.Results):
|
||||
self.analysis.addObject(o)
|
||||
break
|
||||
else:
|
||||
# TODO: use solver framework error and status message system
|
||||
# TODO: use solver framework status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n".format(neu_result_file)
|
||||
"FEM: No results found at {}!\n"
|
||||
.format(neu_result_file)
|
||||
)
|
||||
return
|
||||
|
||||
self.fail()
|
||||
|
||||
## @}
|
||||
|
||||
@@ -450,7 +450,15 @@ class Check(BaseTask):
|
||||
def check_material_single(self):
|
||||
objs = self.get_several_member("App::MaterialObjectPython")
|
||||
if len(objs) > 1:
|
||||
self.report.error("Only one Material allowed for this solver.")
|
||||
self.report.error("Only one Material is supported for this solver.")
|
||||
self.fail()
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_geos_beamsection_no(self):
|
||||
objs = self.get_several_member("Fem::ElementGeometry1D")
|
||||
if len(objs) > 0:
|
||||
self.report.error("Beamsections are not supported for this solver.")
|
||||
self.fail()
|
||||
return False
|
||||
return True
|
||||
@@ -458,7 +466,15 @@ class Check(BaseTask):
|
||||
def check_geos_beamsection_single(self):
|
||||
objs = self.get_several_member("Fem::ElementGeometry1D")
|
||||
if len(objs) > 1:
|
||||
self.report.error("Only one beamsection allowed for this solver.")
|
||||
self.report.error("Only one beamsection is supported for this solver.")
|
||||
self.fail()
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_geos_shellthickness_no(self):
|
||||
objs = self.get_several_member("Fem::ElementGeometry2D")
|
||||
if len(objs) > 0:
|
||||
self.report.error("Shellsections are not supported for this solver.")
|
||||
self.fail()
|
||||
return False
|
||||
return True
|
||||
@@ -466,7 +482,7 @@ class Check(BaseTask):
|
||||
def check_geos_shellthickness_single(self):
|
||||
objs = self.get_several_member("Fem::ElementGeometry2D")
|
||||
if len(objs) > 1:
|
||||
self.report.error("Only one shellthickness allowed for this solver.")
|
||||
self.report.error("Only one shellthickness is supported for this solver.")
|
||||
self.fail()
|
||||
return False
|
||||
return True
|
||||
@@ -476,8 +492,8 @@ class Check(BaseTask):
|
||||
shellth_obj = self.get_several_member("Fem::ElementGeometry2D")
|
||||
if len(beamsec_obj) > 0 and len(shellth_obj) > 0:
|
||||
self.report.error(
|
||||
"Either beamsection or shellthickness objects are allowed for this solver, "
|
||||
"but not both in one analysis."
|
||||
"Either beamsection or shellthickness objects are "
|
||||
"supported for this solver, but not both in one analysis."
|
||||
)
|
||||
self.fail()
|
||||
return False
|
||||
|
||||
@@ -76,9 +76,9 @@ class ControlTaskPanel(QtCore.QObject):
|
||||
# as soon as the widget of the task panel gets destroyed.
|
||||
self.form.destroyed.connect(self._disconnectMachine)
|
||||
self.form.destroyed.connect(self._timer.stop)
|
||||
self.form.destroyed.connect(
|
||||
lambda: self.machineStatusChanged.disconnect(
|
||||
self.form.appendStatus))
|
||||
# self.form.destroyed.connect(
|
||||
# lambda: self.machineStatusChanged.disconnect(
|
||||
# self.form.appendStatus))
|
||||
|
||||
# Connect all proxy signals.
|
||||
self.machineStarted.connect(self._timer.start)
|
||||
|
||||
@@ -38,6 +38,7 @@ from . import writer
|
||||
from .. import run
|
||||
from .. import settings
|
||||
from feminout import importZ88O2Results
|
||||
from femmesh import meshsetsgetter
|
||||
from femtools import femutils
|
||||
from femtools import membertools
|
||||
|
||||
@@ -45,7 +46,7 @@ from femtools import membertools
|
||||
class Check(run.Check):
|
||||
|
||||
def run(self):
|
||||
self.pushStatus("Checking analysis...\n")
|
||||
self.pushStatus("Checking analysis member...\n")
|
||||
self.check_mesh_exists()
|
||||
self.check_material_exists()
|
||||
self.check_material_single() # no multiple material
|
||||
@@ -57,44 +58,69 @@ class Check(run.Check):
|
||||
class Prepare(run.Prepare):
|
||||
|
||||
def run(self):
|
||||
self.pushStatus("Preparing input files...\n")
|
||||
self.pushStatus("Preparing solver input...\n")
|
||||
|
||||
# get mesh set data
|
||||
# TODO see calculix tasks get mesh set data
|
||||
mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already
|
||||
meshdatagetter = meshsetsgetter.MeshSetsGetter(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
mesh_obj,
|
||||
membertools.AnalysisMember(self.analysis),
|
||||
)
|
||||
meshdatagetter.get_mesh_sets()
|
||||
|
||||
# write solver input
|
||||
w = writer.FemInputWriterZ88(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
membertools.get_mesh_to_solve(self.analysis)[0], # pre check has been done already
|
||||
membertools.AnalysisMember(self.analysis),
|
||||
mesh_obj,
|
||||
meshdatagetter.member,
|
||||
self.directory
|
||||
)
|
||||
path = w.write_solver_input()
|
||||
# report to user if task succeeded
|
||||
if path is not None:
|
||||
self.pushStatus("Write completed!")
|
||||
self.pushStatus("Writing solver input completed.")
|
||||
else:
|
||||
self.pushStatus("Writing Z88 solver input files failed!")
|
||||
self.pushStatus("Writing solver input failed.")
|
||||
self.fail()
|
||||
# print(path)
|
||||
# z88 does not pass a main input file to the solver
|
||||
# it passes the directory all input files are in
|
||||
# not _inputFileName is needed
|
||||
|
||||
|
||||
class Solve(run.Solve):
|
||||
|
||||
def run(self):
|
||||
# AFAIK: z88r needs to be run twice, once in test mode and once in real solve mode
|
||||
# the subprocess was just copied, it seems to work :-)
|
||||
# TODO: search out for "Vektor GS" and "Vektor KOI" and print values
|
||||
# may be compared with the used ones
|
||||
self.pushStatus("Executing test solver...\n")
|
||||
|
||||
# get solver binary
|
||||
self.pushStatus("Get solver binary...\n")
|
||||
binary = settings.get_binary("Z88")
|
||||
if binary is None:
|
||||
self.fail() # a print has been made in settings module
|
||||
|
||||
# run solver test mode
|
||||
# AFAIK: z88r needs to be run twice
|
||||
# once in test mode and once in real solve mode
|
||||
# the subprocess was just copied, it works :-)
|
||||
# TODO: search out for "Vektor GS" and "Vektor KOI" and print values
|
||||
# may be compare with the used ones
|
||||
self.pushStatus("Executing solver in test mode...\n")
|
||||
self._process = subprocess.Popen(
|
||||
[binary, "-t", "-choly"],
|
||||
cwd=self.directory,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
self.signalAbort.add(self._process.terminate)
|
||||
# output = self._observeSolver(self._process)
|
||||
self._process.communicate()
|
||||
self.signalAbort.remove(self._process.terminate)
|
||||
|
||||
self.pushStatus("Executing real solver...\n")
|
||||
# run solver real mode
|
||||
self.pushStatus("Executing solver in real mode...\n")
|
||||
binary = settings.get_binary("Z88")
|
||||
self._process = subprocess.Popen(
|
||||
[binary, "-c", "-choly"],
|
||||
@@ -102,12 +128,10 @@ class Solve(run.Solve):
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
self.signalAbort.add(self._process.terminate)
|
||||
# output = self._observeSolver(self._process)
|
||||
self._process.communicate()
|
||||
self.signalAbort.remove(self._process.terminate)
|
||||
# if not self.aborted:
|
||||
# self._updateOutput(output)
|
||||
# del output # get flake8 quiet
|
||||
|
||||
# for chatching the output see CalculiX or Elmer solver tasks module
|
||||
|
||||
|
||||
class Results(run.Results):
|
||||
@@ -120,6 +144,8 @@ class Results(run.Results):
|
||||
self.load_results()
|
||||
|
||||
def purge_results(self):
|
||||
self.pushStatus("Purge existing results...\n")
|
||||
# TODO see calculix result tasks
|
||||
for m in membertools.get_member(self.analysis, "Fem::FemResultObject"):
|
||||
if femutils.is_of_type(m.Mesh, "Fem::MeshResult"):
|
||||
self.analysis.Document.removeObject(m.Mesh.Name)
|
||||
@@ -127,6 +153,7 @@ class Results(run.Results):
|
||||
self.analysis.Document.recompute()
|
||||
|
||||
def load_results(self):
|
||||
self.pushStatus("Import new results...\n")
|
||||
# displacements from z88o2 file
|
||||
disp_result_file = os.path.join(
|
||||
self.directory, "z88o2.txt")
|
||||
@@ -135,7 +162,11 @@ class Results(run.Results):
|
||||
importZ88O2Results.import_z88_disp(
|
||||
disp_result_file, self.analysis, result_name_prefix)
|
||||
else:
|
||||
raise Exception(
|
||||
"FEM: No results found at {}!".format(disp_result_file))
|
||||
# TODO: use solver framework status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n"
|
||||
.format(disp_result_file)
|
||||
)
|
||||
self.fail()
|
||||
|
||||
## @}
|
||||
|
||||
@@ -29,6 +29,7 @@ __url__ = "https://www.freecadweb.org"
|
||||
# @{
|
||||
|
||||
import time
|
||||
from os.path import join
|
||||
|
||||
import FreeCAD
|
||||
|
||||
@@ -54,23 +55,26 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
member,
|
||||
dir_name
|
||||
)
|
||||
from os.path import join
|
||||
self.file_name = join(self.dir_name, "z88")
|
||||
FreeCAD.Console.PrintLog(
|
||||
"FemInputWriterZ88 --> self.dir_name --> " + self.dir_name + "\n"
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"FemInputWriterZ88 --> self.file_name --> " + self.file_name + "\n"
|
||||
)
|
||||
|
||||
# ********************************************************************************************
|
||||
# write solver input
|
||||
def write_solver_input(self):
|
||||
timestart = time.process_time()
|
||||
FreeCAD.Console.PrintMessage("Write z88 input files to: {}\n".format(self.dir_name))
|
||||
if not self.femnodes_mesh:
|
||||
self.femnodes_mesh = self.femmesh.Nodes
|
||||
if not self.femelement_table:
|
||||
self.femelement_table = meshtools.get_femelement_table(self.femmesh)
|
||||
self.element_count = len(self.femelement_table)
|
||||
FreeCAD.Console.PrintMessage("\n") # because of time print in separate line
|
||||
FreeCAD.Console.PrintMessage("Z88 solver input writing...\n")
|
||||
FreeCAD.Console.PrintLog(
|
||||
"FemInputWriterZ88 --> self.dir_name --> {}\n"
|
||||
.format(self.dir_name)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"FemInputWriterZ88 --> self.file_name --> {}\n"
|
||||
.format(self.file_name)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Write z88 input files to: {}\n"
|
||||
.format(self.dir_name)
|
||||
)
|
||||
control = self.set_z88_elparam()
|
||||
if control is False:
|
||||
return None
|
||||
@@ -86,9 +90,11 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
"Writing time input file: {} seconds"
|
||||
.format(round((time.process_time() - timestart), 2))
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(writing_time_string + " \n\n")
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"{}\n\n".format(writing_time_string))
|
||||
return self.dir_name
|
||||
|
||||
# ********************************************************************************************
|
||||
def set_z88_elparam(self):
|
||||
# TODO: param should be moved to the solver object like the known analysis
|
||||
z8804 = {"INTORD": "0", "INTOS": "0", "IHFLAG": "0", "ISFLAG": "1"} # seg2 --> stab4
|
||||
@@ -114,7 +120,13 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
FreeCAD.Console.PrintMessage("\n")
|
||||
return True
|
||||
|
||||
# ********************************************************************************************
|
||||
def write_z88_mesh(self):
|
||||
if not self.femnodes_mesh:
|
||||
self.femnodes_mesh = self.femmesh.Nodes
|
||||
if not self.femelement_table:
|
||||
self.femelement_table = meshtools.get_femelement_table(self.femmesh)
|
||||
self.element_count = len(self.femelement_table)
|
||||
mesh_file_path = self.file_name + "i1.txt"
|
||||
f = open(mesh_file_path, "w")
|
||||
importZ88Mesh.write_z88_mesh_to_file(
|
||||
@@ -125,25 +137,22 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
)
|
||||
f.close()
|
||||
|
||||
# ********************************************************************************************
|
||||
def write_z88_constraints(self):
|
||||
constraints_data = [] # will be a list of tuple for better sorting
|
||||
|
||||
# fixed constraints
|
||||
# get nodes
|
||||
self.get_constraints_fixed_nodes()
|
||||
# write nodes to constraints_data (different from writing to file in ccxInpWriter
|
||||
for femobj in self.fixed_objects:
|
||||
for femobj in self.member.cons_fixed:
|
||||
for n in femobj["Nodes"]:
|
||||
constraints_data.append((n, str(n) + " 1 2 0\n"))
|
||||
constraints_data.append((n, str(n) + " 2 2 0\n"))
|
||||
constraints_data.append((n, str(n) + " 3 2 0\n"))
|
||||
constraints_data.append((n, "{} 1 2 0\n".format(n)))
|
||||
constraints_data.append((n, "{} 2 2 0\n".format(n)))
|
||||
constraints_data.append((n, "{} 3 2 0\n".format(n)))
|
||||
|
||||
# forces constraints
|
||||
# check shape type of reference shape and get node loads
|
||||
self.get_constraints_force_nodeloads()
|
||||
# write node loads to constraints_data
|
||||
# a bit different from writing to file for ccxInpWriter
|
||||
for femobj in self.force_objects:
|
||||
for femobj in self.member.cons_force:
|
||||
# femobj --> dict, FreeCAD document object is femobj["Object"]
|
||||
direction_vec = femobj["Object"].DirectionVector
|
||||
for ref_shape in femobj["NodeLoadTable"]:
|
||||
@@ -151,13 +160,13 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
node_load = ref_shape[1][n]
|
||||
if (direction_vec.x != 0.0):
|
||||
v1 = direction_vec.x * node_load
|
||||
constraints_data.append((n, str(n) + " 1 1 " + str(v1) + "\n"))
|
||||
constraints_data.append((n, "{} 1 1 {}\n".format(n, v1)))
|
||||
if (direction_vec.y != 0.0):
|
||||
v2 = direction_vec.y * node_load
|
||||
constraints_data.append((n, str(n) + " 2 1 " + str(v2) + "\n"))
|
||||
constraints_data.append((n, "{} 2 1 {}\n".format(n, v2)))
|
||||
if (direction_vec.z != 0.0):
|
||||
v3 = direction_vec.z * node_load
|
||||
constraints_data.append((n, str(n) + " 3 1 " + str(v3) + "\n"))
|
||||
constraints_data.append((n, "{} 3 1 {}\n".format(n, v3)))
|
||||
|
||||
# write constraints_data to file
|
||||
constraints_file_path = self.file_name + "i2.txt"
|
||||
@@ -167,6 +176,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
f.write(c[1])
|
||||
f.close()
|
||||
|
||||
# ********************************************************************************************
|
||||
def write_z88_face_loads(self):
|
||||
# not yet supported
|
||||
face_load_file_path = self.file_name + "i5.txt"
|
||||
@@ -175,16 +185,17 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
f.write("\n")
|
||||
f.close()
|
||||
|
||||
# ********************************************************************************************
|
||||
def write_z88_materials(self):
|
||||
mat_obj = self.material_objects[0]["Object"]
|
||||
mat_obj = self.member.mats_linear[0]["Object"]
|
||||
material_data_file_name = "51.txt"
|
||||
materials_file_path = self.file_name + "mat.txt"
|
||||
fms = open(materials_file_path, "w")
|
||||
fms.write("1\n")
|
||||
fms.write("1 " + str(self.element_count) + " " + material_data_file_name)
|
||||
fms.write("1 {} {}".format(self.element_count, material_data_file_name))
|
||||
fms.write("\n")
|
||||
fms.close()
|
||||
material_data_file_path = self.dir_name + "/" + material_data_file_name
|
||||
material_data_file_path = join(self.dir_name, material_data_file_name)
|
||||
fmd = open(material_data_file_path, "w")
|
||||
YM = FreeCAD.Units.Quantity(mat_obj.Material["YoungsModulus"])
|
||||
YM_in_MPa = YM.getValueAs("MPa")
|
||||
@@ -193,11 +204,12 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
fmd.write("\n")
|
||||
fmd.close()
|
||||
|
||||
# ********************************************************************************************
|
||||
def write_z88_elements_properties(self):
|
||||
element_properties_file_path = self.file_name + "elp.txt"
|
||||
elements_data = []
|
||||
if meshtools.is_edge_femmesh(self.femmesh):
|
||||
beam_obj = self.beamsection_objects[0]["Object"]
|
||||
beam_obj = self.member.geos_beamsection[0]["Object"]
|
||||
area = 0
|
||||
if beam_obj.SectionType == "Rectangular":
|
||||
width = beam_obj.RectWidth.getValueAs("mm").Value
|
||||
@@ -224,13 +236,17 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
"Be aware, only trusses are supported for edge meshes!\n"
|
||||
)
|
||||
elif meshtools.is_face_femmesh(self.femmesh):
|
||||
thick_obj = self.shellthickness_objects[0]["Object"]
|
||||
thickness = str(thick_obj.Thickness.getValueAs("mm"))
|
||||
thick_obj = self.member.geos_shellthickness[0]["Object"]
|
||||
thickness = thick_obj.Thickness.getValueAs("mm").Value
|
||||
elements_data.append(
|
||||
"1 " + str(self.element_count) + " " + thickness + " 0 0 0 0 0 0 "
|
||||
"1 {} {} 0 0 0 0 0 0 "
|
||||
.format(self.element_count, thickness)
|
||||
)
|
||||
elif meshtools.is_solid_femmesh(self.femmesh):
|
||||
elements_data.append("1 " + str(self.element_count) + " 0 0 0 0 0 0 0")
|
||||
elements_data.append(
|
||||
"1 {} 0 0 0 0 0 0 0"
|
||||
.format(self.element_count)
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintError("Error!\n")
|
||||
f = open(element_properties_file_path, "w")
|
||||
@@ -240,6 +256,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
f.write("\n")
|
||||
f.close()
|
||||
|
||||
# ********************************************************************************************
|
||||
def write_z88_integration_properties(self):
|
||||
integration_data = []
|
||||
integration_data.append("1 {} {} {}".format(
|
||||
@@ -249,12 +266,13 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
))
|
||||
integration_properties_file_path = self.file_name + "int.txt"
|
||||
f = open(integration_properties_file_path, "w")
|
||||
f.write(str(len(integration_data)) + "\n")
|
||||
f.write("{}\n".format(len(integration_data)))
|
||||
for i in integration_data:
|
||||
f.write(i)
|
||||
f.write("\n")
|
||||
f.close()
|
||||
|
||||
# ********************************************************************************************
|
||||
def write_z88_solver_parameter(self):
|
||||
global z88_man_template
|
||||
z88_man_template = z88_man_template.replace(
|
||||
@@ -268,13 +286,14 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
f.write(z88_man_template)
|
||||
f.close()
|
||||
|
||||
# ********************************************************************************************
|
||||
def write_z88_memory_parameter(self):
|
||||
# self.z88_param_maxgs = 6000000
|
||||
self.z88_param_maxgs = 50000000 # vierkantrohr
|
||||
global z88_dyn_template
|
||||
z88_dyn_template = z88_dyn_template.replace(
|
||||
"$z88_param_maxgs",
|
||||
str(self.z88_param_maxgs)
|
||||
"{}".format(self.z88_param_maxgs)
|
||||
)
|
||||
solver_parameter_file_path = self.file_name + ".dyn"
|
||||
f = open(solver_parameter_file_path, "w")
|
||||
|
||||
@@ -545,8 +545,10 @@ class _TaskPanel:
|
||||
old_value = Units.Quantity(self.material[matProperty]).getValueAs(qUnit)
|
||||
else:
|
||||
# for example PoissonRatio
|
||||
value = float(inputfield_text)
|
||||
old_value = float(self.material[matProperty])
|
||||
value = Units.Quantity(inputfield_text).Value
|
||||
old_value = Units.Quantity(self.material[matProperty]).Value
|
||||
# value = float(inputfield_text) # this fails on locale with komma
|
||||
# https://forum.freecadweb.org/viewtopic.php?f=18&t=56912&p=523313#p523313
|
||||
if value:
|
||||
if not (1 - variation < float(old_value) / value < 1 + variation):
|
||||
material = self.material
|
||||
|
||||
@@ -407,6 +407,9 @@ S, E
|
||||
** reaction forces for Constraint fixed
|
||||
*NODE PRINT, NSET=ConstraintFixed, TOTALS=ONLY
|
||||
RF
|
||||
** reaction forces for Constraint displacement constraining translation
|
||||
*NODE PRINT, NSET=ConstraintDisplacmentPrescribed, TOTALS=ONLY
|
||||
RF
|
||||
|
||||
|
||||
***********************************************************
|
||||
|
||||
@@ -17062,6 +17062,13 @@ Fix_YZ,3
|
||||
U
|
||||
*EL FILE
|
||||
S, E
|
||||
** outputs --> dat file
|
||||
** reaction forces for Constraint displacement constraining translation
|
||||
*NODE PRINT, NSET=Fix_XYZ, TOTALS=ONLY
|
||||
RF
|
||||
*NODE PRINT, NSET=Fix_YZ, TOTALS=ONLY
|
||||
RF
|
||||
|
||||
|
||||
***********************************************************
|
||||
*END STEP
|
||||
|
||||
@@ -66,19 +66,22 @@ def shallHide(subject):
|
||||
return True
|
||||
return False
|
||||
|
||||
def setColorRecursively(obj,color,transp):
|
||||
if(obj.TypeId=="Part::Fuse" or obj.TypeId=="Part::MultiFuse"):
|
||||
for currentObject in obj.OutList:
|
||||
if (currentObject.TypeId=="Part::Fuse" or currentObject.TypeId=="Part::MultiFuse"):
|
||||
setColorRecursively(currentObject,color,transp)
|
||||
else:
|
||||
print("Fixing up colors for: "+str(currentObject.FullName))
|
||||
if(currentObject not in hassetcolor):
|
||||
currentObject.ViewObject.ShapeColor=color
|
||||
currentObject.ViewObject.Transparency=transp
|
||||
setColorRecursively(currentObject,color,transp)
|
||||
else:
|
||||
setColorRecursively(currentObject,color,transp)
|
||||
def setColorRecursively(obj, color, transp):
|
||||
'''
|
||||
For some reason a part made by cutting or fusing other parts do not have a color
|
||||
unless its constituents are also colored. This code sets colors for those
|
||||
constituents unless already set elsewhere.
|
||||
'''
|
||||
obj.ViewObject.ShapeColor = color
|
||||
obj.ViewObject.Transparency = transp
|
||||
# Add any other relevant features to this list
|
||||
boolean_features = ["Part::Fuse", "Part::MultiFuse", "Part::Cut",
|
||||
"Part::Common", "Part::MultiCommon"]
|
||||
if obj.TypeId in boolean_features:
|
||||
for currentObject in obj.OutList:
|
||||
print(f"Fixing up colors for: {currentObject.FullName}")
|
||||
if currentObject not in hassetcolor:
|
||||
setColorRecursively(currentObject, color, transp)
|
||||
|
||||
def fixVisibility():
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
@@ -555,22 +558,7 @@ def p_color_action(p):
|
||||
if "Group" in obj.FullName:
|
||||
obj.ViewObject.Visibility=False
|
||||
alreadyhidden.append(obj)
|
||||
if(obj.TypeId=="Part::Fuse" or obj.TypeId=="Part::MultiFuse"):
|
||||
for currentObject in obj.OutList:
|
||||
if (currentObject.TypeId=="Part::Fuse" or currentObject.TypeId=="Part::MultiFuse"):
|
||||
setColorRecursively(currentObject,color,transp)
|
||||
if(currentObject not in hassetcolor):
|
||||
currentObject.ViewObject.ShapeColor=color
|
||||
currentObject.ViewObject.Transparency=transp
|
||||
setColorRecursively(currentObject,color,transp)
|
||||
else:
|
||||
setColorRecursively(currentObject,color,transp)
|
||||
else:
|
||||
obj.ViewObject.ShapeColor =color
|
||||
obj.ViewObject.Transparency = transp
|
||||
else:
|
||||
obj.ViewObject.ShapeColor =color
|
||||
obj.ViewObject.Transparency = transp
|
||||
setColorRecursively(obj, color, transp)
|
||||
hassetcolor.append(obj)
|
||||
p[0] = p[6]
|
||||
|
||||
|
||||
@@ -447,10 +447,10 @@ PyObject* BSplineSurfacePy::setVKnot(PyObject *args)
|
||||
Handle(Geom_BSplineSurface) surf = Handle(Geom_BSplineSurface)::DownCast
|
||||
(getGeometryPtr()->handle());
|
||||
if (M == -1) {
|
||||
surf->SetUKnot(Index, K);
|
||||
surf->SetVKnot(Index, K);
|
||||
}
|
||||
else {
|
||||
surf->SetUKnot(Index, K, M);
|
||||
surf->SetVKnot(Index, K, M);
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
|
||||
@@ -236,11 +236,18 @@ ViewProviderPartExt::ViewProviderPartExt()
|
||||
forceUpdateCount = 0;
|
||||
NormalsFromUV = true;
|
||||
|
||||
// get default line color
|
||||
unsigned long lcol = Gui::ViewParams::instance()->getDefaultShapeLineColor(); // dark grey (25,25,25)
|
||||
float r,g,b;
|
||||
r = ((lcol >> 24) & 0xff) / 255.0; g = ((lcol >> 16) & 0xff) / 255.0; b = ((lcol >> 8) & 0xff) / 255.0;
|
||||
float lr,lg,lb;
|
||||
lr = ((lcol >> 24) & 0xff) / 255.0; lg = ((lcol >> 16) & 0xff) / 255.0; lb = ((lcol >> 8) & 0xff) / 255.0;
|
||||
// get default vertex color
|
||||
unsigned long vcol = Gui::ViewParams::instance()->getDefaultShapeVertexColor();
|
||||
float vr,vg,vb;
|
||||
vr = ((vcol >> 24) & 0xff) / 255.0; vg = ((vcol >> 16) & 0xff) / 255.0; vb = ((vcol >> 8) & 0xff) / 255.0;
|
||||
int lwidth = Gui::ViewParams::instance()->getDefaultShapeLineWidth();
|
||||
int psize = Gui::ViewParams::instance()->getDefaultShapePointSize();
|
||||
|
||||
|
||||
|
||||
ParameterGrp::handle hPart = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/Mod/Part");
|
||||
@@ -256,17 +263,26 @@ ViewProviderPartExt::ViewProviderPartExt()
|
||||
|
||||
static const char *osgroup = "Object Style";
|
||||
|
||||
App::Material mat;
|
||||
mat.ambientColor.set(0.2f,0.2f,0.2f);
|
||||
mat.diffuseColor.set(r,g,b);
|
||||
mat.specularColor.set(0.0f,0.0f,0.0f);
|
||||
mat.emissiveColor.set(0.0f,0.0f,0.0f);
|
||||
mat.shininess = 1.0f;
|
||||
mat.transparency = 0.0f;
|
||||
ADD_PROPERTY_TYPE(LineMaterial,(mat), osgroup, App::Prop_None, "Object line material.");
|
||||
ADD_PROPERTY_TYPE(PointMaterial,(mat), osgroup, App::Prop_None, "Object point material.");
|
||||
ADD_PROPERTY_TYPE(LineColor, (mat.diffuseColor), osgroup, App::Prop_None, "Set object line color.");
|
||||
ADD_PROPERTY_TYPE(PointColor, (mat.diffuseColor), osgroup, App::Prop_None, "Set object point color");
|
||||
App::Material lmat;
|
||||
lmat.ambientColor.set(0.2f,0.2f,0.2f);
|
||||
lmat.diffuseColor.set(lr,lg,lb);
|
||||
lmat.specularColor.set(0.0f,0.0f,0.0f);
|
||||
lmat.emissiveColor.set(0.0f,0.0f,0.0f);
|
||||
lmat.shininess = 1.0f;
|
||||
lmat.transparency = 0.0f;
|
||||
|
||||
App::Material vmat;
|
||||
vmat.ambientColor.set(0.2f,0.2f,0.2f);
|
||||
vmat.diffuseColor.set(vr,vg,vb);
|
||||
vmat.specularColor.set(0.0f,0.0f,0.0f);
|
||||
vmat.emissiveColor.set(0.0f,0.0f,0.0f);
|
||||
vmat.shininess = 1.0f;
|
||||
vmat.transparency = 0.0f;
|
||||
|
||||
ADD_PROPERTY_TYPE(LineMaterial,(lmat), osgroup, App::Prop_None, "Object line material.");
|
||||
ADD_PROPERTY_TYPE(PointMaterial,(vmat), osgroup, App::Prop_None, "Object point material.");
|
||||
ADD_PROPERTY_TYPE(LineColor, (lmat.diffuseColor), osgroup, App::Prop_None, "Set object line color.");
|
||||
ADD_PROPERTY_TYPE(PointColor, (vmat.diffuseColor), osgroup, App::Prop_None, "Set object point color");
|
||||
ADD_PROPERTY_TYPE(PointColorArray, (PointColor.getValue()), osgroup, App::Prop_None, "Object point color array.");
|
||||
ADD_PROPERTY_TYPE(DiffuseColor,(ShapeColor.getValue()), osgroup, App::Prop_None, "Object diffuse color.");
|
||||
ADD_PROPERTY_TYPE(LineColorArray,(LineColor.getValue()), osgroup, App::Prop_None, "Object line color array.");
|
||||
|
||||
@@ -691,7 +691,7 @@ bool TaskBoxPrimitives::setPrimitive(App::DocumentObject *obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
Base::QuantityFormat format(Base::QuantityFormat::Default, Base::UnitsApi::getDecimals());
|
||||
Base::QuantityFormat format(Base::QuantityFormat::Fixed, Base::UnitsApi::getDecimals());
|
||||
switch(ui->widgetStack->currentIndex()) {
|
||||
case 1: // box
|
||||
cmd = QString::fromLatin1(
|
||||
|
||||
@@ -13,108 +13,120 @@ import math
|
||||
|
||||
# 3d vector class
|
||||
class Vector:
|
||||
def __init__(self,x,y,z):
|
||||
self.x=x
|
||||
self.y=y
|
||||
self.z=z
|
||||
def add(self,vec):
|
||||
return Vector(self.x+vec.x,self.y+vec.y,self.z+vec.z)
|
||||
def sub(self,vec):
|
||||
return Vector(self.x-vec.x,self.y-vec.y,self.z-vec.z)
|
||||
def dot(self,vec):
|
||||
return self.x*vec.x+self.y*vec.y+self.z*vec.z
|
||||
def mult(self,s):
|
||||
return Vector(self.x*s,self.y*s,self.z*s)
|
||||
def cross(self,vec):
|
||||
return Vector(
|
||||
self.y * vec.z - self.z * vec.y,
|
||||
self.z * vec.x - self.x * vec.z,
|
||||
self.x * vec.y - self.y * vec.x)
|
||||
def length(self):
|
||||
return math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z)
|
||||
def norm(self):
|
||||
l = self.length()
|
||||
if l > 0:
|
||||
self.x /= l
|
||||
self.y /= l
|
||||
self.z /= l
|
||||
def __repr__(self):
|
||||
return "(%f,%f,%f)" % (self.x,self.y,self.z)
|
||||
def __init__(self, x, y, z):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
def add(self, vec):
|
||||
return Vector(self.x+vec.x, self.y+vec.y, self.z+vec.z)
|
||||
|
||||
def sub(self, vec):
|
||||
return Vector(self.x-vec.x, self.y-vec.y, self.z-vec.z)
|
||||
|
||||
def dot(self, vec):
|
||||
return self.x*vec.x+self.y*vec.y+self.z*vec.z
|
||||
|
||||
def mult(self, s):
|
||||
return Vector(self.x*s, self.y*s, self.z*s)
|
||||
|
||||
def cross(self,vec):
|
||||
return Vector(
|
||||
self.y * vec.z - self.z * vec.y,
|
||||
self.z * vec.x - self.x * vec.z,
|
||||
self.x * vec.y - self.y * vec.x)
|
||||
|
||||
def length(self):
|
||||
return math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z)
|
||||
|
||||
def norm(self):
|
||||
l = self.length()
|
||||
if l > 0:
|
||||
self.x /= l
|
||||
self.y /= l
|
||||
self.z /= l
|
||||
|
||||
def __repr__(self):
|
||||
return "(%f,%f,%f)" % (self.x, self.y, self.z)
|
||||
|
||||
|
||||
# A signum function
|
||||
def sgn(val):
|
||||
if val > 0:
|
||||
return 1
|
||||
elif val < 0:
|
||||
return -1
|
||||
else:
|
||||
return 0
|
||||
if val > 0:
|
||||
return 1
|
||||
elif val < 0:
|
||||
return -1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
# M1 ... is the center of the arc
|
||||
# P ... is the end point of the arc and start point of the line
|
||||
# Q .. is a second point on the line
|
||||
# N ... is the normal of the plane where the arc and the line lie on, usually N=(0,0,1)
|
||||
# N ... is the normal of the plane where the arc and the line lie on, usually N=(0,0,1)
|
||||
# r2 ... the fillet radius
|
||||
# ccw ... counter-clockwise means which part of the arc is given. ccw must be either True or False
|
||||
# ccw ... counter-clockwise means which part of the arc is given. ccw must be either True or False
|
||||
|
||||
|
||||
def makeFilletArc(M1,P,Q,N,r2,ccw):
|
||||
u = Q.sub(P)
|
||||
v = P.sub(M1)
|
||||
if ccw:
|
||||
b = u.cross(N)
|
||||
else:
|
||||
b = N.cross(u)
|
||||
b.norm()
|
||||
|
||||
uu = u.dot(u)
|
||||
uv = u.dot(v)
|
||||
r1 = v.length()
|
||||
u = Q.sub(P)
|
||||
v = P.sub(M1)
|
||||
if ccw:
|
||||
b = u.cross(N)
|
||||
else:
|
||||
b = N.cross(u)
|
||||
b.norm()
|
||||
|
||||
uu = u.dot(u)
|
||||
uv = u.dot(v)
|
||||
r1 = v.length()
|
||||
|
||||
# distinguish between internal and external fillets
|
||||
r2 *= sgn(uv)
|
||||
|
||||
cc = 2.0 * r2 * (b.dot(v)-r1)
|
||||
dd = uv * uv - uu * cc
|
||||
if dd < 0:
|
||||
raise RuntimeError("Unable to calculate intersection points")
|
||||
t1 = (-uv + math.sqrt(dd)) / uu
|
||||
t2 = (-uv - math.sqrt(dd)) / uu
|
||||
|
||||
if (abs(t1) < abs(t2)):
|
||||
t = t1
|
||||
else:
|
||||
t = t2
|
||||
|
||||
br2 = b.mult(r2)
|
||||
print(br2)
|
||||
ut = u.mult(t)
|
||||
print(ut)
|
||||
M2 = P.add(ut).add(br2)
|
||||
S1 = M1.mult(r2/(r1+r2)).add(M2.mult(r1/(r1+r2)))
|
||||
S2 = M2.sub(br2)
|
||||
|
||||
return (S1, S2, M2)
|
||||
|
||||
# distinguish between internal and external fillets
|
||||
r2 *= sgn(uv);
|
||||
|
||||
cc = 2.0 * r2 * (b.dot(v)-r1)
|
||||
dd = uv * uv - uu * cc
|
||||
if dd < 0:
|
||||
raise RuntimeError("Unable to calculate intersection points")
|
||||
t1 = (-uv + math.sqrt(dd)) / uu
|
||||
t2 = (-uv - math.sqrt(dd)) / uu
|
||||
|
||||
if (abs(t1) < abs(t2)):
|
||||
t = t1
|
||||
else:
|
||||
t = t2
|
||||
|
||||
br2 = b.mult(r2)
|
||||
print(br2)
|
||||
ut = u.mult(t)
|
||||
print(ut)
|
||||
M2 = P.add(ut).add(br2)
|
||||
S1 = M1.mult(r2/(r1+r2)).add(M2.mult(r1/(r1+r2)))
|
||||
S2 = M2.sub(br2)
|
||||
|
||||
return (S1,S2,M2)
|
||||
|
||||
|
||||
|
||||
def test():
|
||||
from FreeCAD import Base
|
||||
import Part
|
||||
from FreeCAD import Base
|
||||
import Part
|
||||
|
||||
P1=Base.Vector(1,-5,0)
|
||||
P2=Base.Vector(-5,2,0)
|
||||
P3=Base.Vector(1,5,0)
|
||||
#Q=Base.Vector(5,10,0)
|
||||
#Q=Base.Vector(5,11,0)
|
||||
Q=Base.Vector(5,0,0)
|
||||
r2=3.0
|
||||
axis=Base.Vector(0,0,1)
|
||||
ccw=False
|
||||
P1 = Base.Vector(1, -5, 0)
|
||||
P2 = Base.Vector(-5, 2, 0)
|
||||
P3 = Base.Vector(1, 5, 0)
|
||||
# Q = Base.Vector(5, 10, 0)
|
||||
# Q = Base.Vector(5, 11, 0)
|
||||
Q = Base.Vector(5, 0, 0)
|
||||
r2 = 3.0
|
||||
axis = Base.Vector(0, 0, 1)
|
||||
ccw = False
|
||||
|
||||
arc=Part.ArcOfCircle(P1,P2,P3)
|
||||
C=arc.Center
|
||||
Part.show(Part.makeLine(P3,Q))
|
||||
Part.show(arc.toShape())
|
||||
arc = Part.ArcOfCircle(P1, P2, P3)
|
||||
C = arc.Center
|
||||
Part.show(Part.makeLine(P3, Q))
|
||||
Part.show(arc.toShape())
|
||||
|
||||
(S1,S2,M2) = makeArc(Vector(C.x,C.y,C.z),Vector(P3.x,P3.y,P3.z),Vector(Q.x,Q.y,Q.z),Vector(axis.x,axis.y,axis.z),r2,ccw)
|
||||
circle=Part.Circle(Base.Vector(M2.x,M2.y,M2.z), Base.Vector(0,0,1), math.fabs(r2))
|
||||
Part.show(circle.toShape())
|
||||
(S1, S2, M2) = makeArc(Vector(C.x,C.y,C.z), Vector(P3.x,P3.y,P3.z), Vector(Q.x, Q.y, Q.z), Vector(axis.x, axis.y, axis.z), r2, ccw)
|
||||
circle = Part.Circle(Base.Vector(M2.x, M2.y, M2.z), Base.Vector(0, 0, 1), math.fabs(r2))
|
||||
Part.show(circle.toShape())
|
||||
|
||||
+138
-130
@@ -1,12 +1,20 @@
|
||||
#Involute Gears Generation Script
|
||||
#by Marcin Wanczyk (dj_who)
|
||||
#(c) 2011 LGPL
|
||||
# Involute Gears Generation Script
|
||||
# by Marcin Wanczyk (dj_who)
|
||||
# (c) 2011 LGPL
|
||||
|
||||
|
||||
import FreeCAD, FreeCADGui, Part, Draft, math, MeshPart, Mesh
|
||||
from PySide import QtGui,QtCore
|
||||
App=FreeCAD
|
||||
Gui=FreeCADGui
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Part
|
||||
import Draft
|
||||
import MeshPart
|
||||
import Mesh
|
||||
import math
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
App = FreeCAD
|
||||
Gui = FreeCADGui
|
||||
|
||||
|
||||
def proceed():
|
||||
try:
|
||||
@@ -15,150 +23,150 @@ def proceed():
|
||||
hide()
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
|
||||
def compute():
|
||||
|
||||
def compute():
|
||||
QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
|
||||
|
||||
if FreeCAD.ActiveDocument is None:
|
||||
FreeCAD.newDocument("Gear")
|
||||
|
||||
oldDocumentObjects=App.ActiveDocument.Objects
|
||||
oldDocumentObjects = App.ActiveDocument.Objects
|
||||
|
||||
try:
|
||||
N = int(l1.text())
|
||||
N = int(l1.text())
|
||||
p = float(l2.text())
|
||||
alfa = int(l3.text())
|
||||
y = float(l4.text()) #standard value y<1 for gear drives y>1 for Gear pumps
|
||||
m=p/math.pi #standard value 0.06, 0.12, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 60 (polish norm)
|
||||
c = float(l5.text())*m #standard value 0,1*m - 0,3*m
|
||||
j = float(l6.text())*m #standard value 0,015 - 0,04*m
|
||||
width = float(l7.text()) #gear width
|
||||
y = float(l4.text()) # standard value y<1 for gear drives y>1 for Gear pumps
|
||||
m = p/math.pi # standard value 0.06, 0.12, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 60 (polish norm)
|
||||
c = float(l5.text())*m # standard value 0,1*m - 0,3*m
|
||||
j = float(l6.text())*m # standard value 0,015 - 0,04*m
|
||||
width = float(l7.text()) # gear width
|
||||
except ValueError:
|
||||
FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n")
|
||||
|
||||
|
||||
#tooth height
|
||||
h=2*y*m+c
|
||||
|
||||
#pitch diameter
|
||||
d=N*m
|
||||
|
||||
#root diameter
|
||||
df=d - 2*y*m - 2*c #df=d-2hf where and hf=y*m+c
|
||||
# tooth height
|
||||
h = 2*y*m+c
|
||||
|
||||
#addendum diameter
|
||||
da=d + 2*y*m #da=d+2ha where ha=y*m
|
||||
# pitch diameter
|
||||
d = N*m
|
||||
|
||||
#base diameter for involute
|
||||
db=d * math.cos(math.radians(alfa))
|
||||
# root diameter
|
||||
df = d - 2*y*m - 2*c # df=d-2hf where and hf=y*m+c
|
||||
|
||||
# addendum diameter
|
||||
da = d + 2*y*m # da=d+2ha where ha=y*m
|
||||
|
||||
# base diameter for involute
|
||||
db = d * math.cos(math.radians(alfa))
|
||||
|
||||
|
||||
#Base circle
|
||||
baseCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","BaseCircle")
|
||||
baseCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "BaseCircle")
|
||||
Draft._Circle(baseCircle)
|
||||
Draft._ViewProviderDraft(baseCircle.ViewObject)
|
||||
baseCircle.Radius = db/2
|
||||
baseCircle.FirstAngle=0.0
|
||||
baseCircle.LastAngle=0.0
|
||||
|
||||
#Root circle
|
||||
rootCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","RootCircle")
|
||||
baseCircle.FirstAngle = 0.0
|
||||
baseCircle.LastAngle = 0.0
|
||||
|
||||
# Root circle
|
||||
rootCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "RootCircle")
|
||||
Draft._Circle(rootCircle)
|
||||
Draft._ViewProviderDraft(rootCircle.ViewObject)
|
||||
rootCircle.Radius = df/2
|
||||
rootCircle.FirstAngle=0.0
|
||||
rootCircle.LastAngle=0.0
|
||||
rootCircle.FirstAngle = 0.0
|
||||
rootCircle.LastAngle = 0.0
|
||||
|
||||
#Addendum circle
|
||||
addendumCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","AddendumCircle")
|
||||
# Addendum circle
|
||||
addendumCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "AddendumCircle")
|
||||
Draft._Circle(addendumCircle)
|
||||
Draft._ViewProviderDraft(addendumCircle.ViewObject)
|
||||
addendumCircle.Radius = da/2
|
||||
addendumCircle.FirstAngle=0.0
|
||||
addendumCircle.LastAngle=0.0
|
||||
addendumCircle.FirstAngle = 0.0
|
||||
addendumCircle.LastAngle = 0.0
|
||||
|
||||
#Pitch circle
|
||||
pitchCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","PitchCircle")
|
||||
# Pitch circle
|
||||
pitchCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PitchCircle")
|
||||
Draft._Circle(pitchCircle)
|
||||
Draft._ViewProviderDraft(pitchCircle.ViewObject)
|
||||
pitchCircle.Radius = d/2
|
||||
pitchCircle.FirstAngle=0.0
|
||||
pitchCircle.LastAngle=0.0
|
||||
pitchCircle.FirstAngle = 0.0
|
||||
pitchCircle.LastAngle = 0.0
|
||||
|
||||
#************ Calculating right sides of teeth
|
||||
#Involute of base circle
|
||||
involute=[]
|
||||
involutee=[]
|
||||
involutesav=[]
|
||||
# Involute of base circle
|
||||
involute = []
|
||||
involutee = []
|
||||
involutesav = []
|
||||
|
||||
for t in range(0,60,1):
|
||||
x=db/2*(math.cos(math.radians(t))+math.radians(t)*math.sin(math.radians(t)))
|
||||
y=db/2*(math.sin(math.radians(t))-math.radians(t)*math.cos(math.radians(t)))
|
||||
involute.append(Part.Vertex(x,y,0).Point)
|
||||
for t in range(0, 60, 1):
|
||||
x = db/2*(math.cos(math.radians(t))+math.radians(t)*math.sin(math.radians(t)))
|
||||
y = db/2*(math.sin(math.radians(t))-math.radians(t)*math.cos(math.radians(t)))
|
||||
involute.append(Part.Vertex(x, y, 0).Point)
|
||||
|
||||
#************ Drawing right sides of teeth
|
||||
#************ Drawing right sides of teeth
|
||||
involutesav.extend(involute)
|
||||
involutee.extend(involute)
|
||||
|
||||
for angle in range(1,N+1,1):
|
||||
involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature","InvoluteL"+str(angle))
|
||||
involutee.insert(0,(0,0,0))
|
||||
involuteshape = Part.makePolygon(involutee)
|
||||
for angle in range(1, N+1, 1):
|
||||
involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature", "InvoluteL" + str(angle))
|
||||
involutee.insert(0, (0, 0, 0))
|
||||
involuteshape = Part.makePolygon(involutee)
|
||||
involuteobj.Shape=involuteshape
|
||||
involutee=[]
|
||||
for num in range(0,60,1):
|
||||
point=involute.pop()
|
||||
pointt=Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point
|
||||
involutee = []
|
||||
for num in range(0, 60, 1):
|
||||
point = involute.pop()
|
||||
pointt = Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point
|
||||
involutee.insert(0,pointt)
|
||||
involute.extend(involutesav)
|
||||
involutee=[]
|
||||
|
||||
involutee = []
|
||||
|
||||
#************ Calculating difference between tooth spacing on BaseCircle and PitchCircle
|
||||
|
||||
pc=App.ActiveDocument.getObject("PitchCircle")
|
||||
inv=App.ActiveDocument.getObject("InvoluteL1")
|
||||
cut=inv.Shape.cut(pc.Shape)
|
||||
pc = App.ActiveDocument.getObject("PitchCircle")
|
||||
inv = App.ActiveDocument.getObject("InvoluteL1")
|
||||
cut = inv.Shape.cut(pc.Shape)
|
||||
# FreeCAD.ActiveDocument.addObject("Part::Feature","CutInv").Shape=cut
|
||||
invPoint=cut.Vertexes[0].Point
|
||||
invPoint = cut.Vertexes[0].Point
|
||||
|
||||
|
||||
diff=invPoint.y*2 # instead of making axial symmetry and calculating point distance.
|
||||
anglediff=2*math.asin(diff/d)
|
||||
diff = invPoint.y*2 # instead of making axial symmetry and calculating point distance.
|
||||
anglediff = 2*math.asin(diff/d)
|
||||
|
||||
#************ Calculating left sides of teeth
|
||||
|
||||
#************ Inversing Involute
|
||||
for num in range(0,60,1):
|
||||
point=involute.pop()
|
||||
pointt=Part.Vertex(point.x,point.y*-1,0).Point
|
||||
involutee.insert(0,pointt)
|
||||
for num in range(0, 60, 1):
|
||||
point = involute.pop()
|
||||
pointt = Part.Vertex(point.x, point.y*-1, 0).Point
|
||||
involutee.insert(0, pointt)
|
||||
involute.extend(involutee)
|
||||
involutee=[]
|
||||
involutee = []
|
||||
|
||||
#Normal tooth size calculated as: 0,5* p - j j=m * 0,1 below are calculations
|
||||
#Normal tooth size calculated as: 0,5* p - j j = m * 0,1 below are calculations
|
||||
# 0,5* p - m * 0,1
|
||||
# 0,5* p - p /pi * 0,1
|
||||
# 0,5*360/N - ((360/N)/pi)* 0,1
|
||||
# 0,5*360/N - (360/N)*((1/pi)*0,1) j=(p/pi)*0,1
|
||||
# 0,5*360/N - (360/N)*((1/pi)*0,1) j = (p/pi)*0,1
|
||||
# 0,5*360/N - (360/N)*((p/pi)*0,1)/p
|
||||
# 0,5*360/N - (360/N)*( j )/p
|
||||
for num in range(0,60,1):
|
||||
point=involute.pop()
|
||||
pointt=Part.Vertex(point.x*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff) - point.y*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff),point.x*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff) + point.y*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff),0).Point
|
||||
involutee.insert(0,pointt)
|
||||
for num in range(0, 60, 1):
|
||||
point = involute.pop()
|
||||
pointt = Part.Vertex(point.x*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff) - point.y*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff),point.x*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff) + point.y*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff),0).Point
|
||||
involutee.insert(0, pointt)
|
||||
involute.extend(involutee)
|
||||
involutesav=[]
|
||||
involutesav = []
|
||||
involutesav.extend(involute)
|
||||
|
||||
#************ Drawing left sides of teeth
|
||||
for angle in range(1,N+1,1):
|
||||
involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature","InvoluteR"+str(angle))
|
||||
involutee.insert(0,(0,0,0))
|
||||
for angle in range(1, N+1, 1):
|
||||
involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature", "InvoluteR" + str(angle))
|
||||
involutee.insert(0, (0, 0, 0))
|
||||
involuteshape = Part.makePolygon(involutee)
|
||||
involuteobj.Shape=involuteshape
|
||||
involutee=[]
|
||||
involuteobj.Shape = involuteshape
|
||||
involutee = []
|
||||
for num in range(0,60,1):
|
||||
point=involute.pop()
|
||||
pointt=Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point
|
||||
point = involute.pop()
|
||||
pointt = Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point
|
||||
involutee.insert(0,pointt)
|
||||
involute.extend(involutesav)
|
||||
|
||||
@@ -166,56 +174,56 @@ def compute():
|
||||
|
||||
#************ Forming teeth
|
||||
|
||||
cutCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","CutCircle")
|
||||
cutCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CutCircle")
|
||||
Draft._Circle(cutCircle)
|
||||
Draft._ViewProviderDraft(cutCircle.ViewObject)
|
||||
cutCircle.Radius = da # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be.
|
||||
cutCircle.FirstAngle=0.0
|
||||
cutCircle.LastAngle=0.0
|
||||
cutCircle.Radius = da # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be.
|
||||
cutCircle.FirstAngle = 0.0
|
||||
cutCircle.LastAngle = 0.0
|
||||
|
||||
|
||||
cutTool=cutCircle.Shape.cut(addendumCircle.Shape)
|
||||
#cutshape=Part.show(cutTool)
|
||||
|
||||
gearShape=rootCircle.Shape
|
||||
|
||||
for invNum in range(1,N+1,1):
|
||||
invL=App.ActiveDocument.getObject("InvoluteL"+str(invNum))
|
||||
invR=App.ActiveDocument.getObject("InvoluteR"+str(invNum))
|
||||
cutL=invL.Shape.cut(cutTool)
|
||||
cutR=invR.Shape.cut(cutTool)
|
||||
pointL=cutL.Vertexes.pop().Point
|
||||
pointR=cutR.Vertexes.pop().Point
|
||||
faceEdge=Part.makeLine(pointL,pointR)
|
||||
|
||||
toothWhole=cutL.fuse(cutR)
|
||||
toothWhole=toothWhole.fuse(faceEdge)
|
||||
toothWire=Part.Wire(toothWhole.Edges)
|
||||
toothShape=Part.Face(toothWire)
|
||||
# tooth=App.ActiveDocument.addObject("Part::Feature","Tooth"+str(invNum))
|
||||
cutTool = cutCircle.Shape.cut(addendumCircle.Shape)
|
||||
# cutshape = Part.show(cutTool)
|
||||
|
||||
gearShape = rootCircle.Shape
|
||||
|
||||
for invNum in range(1, N+1, 1):
|
||||
invL = App.ActiveDocument.getObject("InvoluteL" + str(invNum))
|
||||
invR = App.ActiveDocument.getObject("InvoluteR" + str(invNum))
|
||||
cutL = invL.Shape.cut(cutTool)
|
||||
cutR = invR.Shape.cut(cutTool)
|
||||
pointL = cutL.Vertexes.pop().Point
|
||||
pointR = cutR.Vertexes.pop().Point
|
||||
faceEdge = Part.makeLine(pointL, pointR)
|
||||
|
||||
toothWhole = cutL.fuse(cutR)
|
||||
toothWhole = toothWhole.fuse(faceEdge)
|
||||
toothWire = Part.Wire(toothWhole.Edges)
|
||||
toothShape = Part.Face(toothWire)
|
||||
# tooth = App.ActiveDocument.addObject("Part::Feature", "Tooth" +str(invNum))
|
||||
# tooth.Shape=toothShape
|
||||
gearShape=gearShape.fuse(toothShape)
|
||||
gearShape = gearShape.fuse(toothShape)
|
||||
|
||||
|
||||
for o in App.ActiveDocument.Objects:
|
||||
if oldDocumentObjects.count(o)==0:
|
||||
if oldDocumentObjects.count(o) == 0:
|
||||
App.ActiveDocument.removeObject(o.Name)
|
||||
|
||||
gearFlat=App.ActiveDocument.addObject("Part::Feature","GearFlat")
|
||||
gearFlat.Shape=gearShape
|
||||
Gui.ActiveDocument.getObject(gearFlat.Name).Visibility=False
|
||||
gearFlat = App.ActiveDocument.addObject("Part::Feature", "GearFlat")
|
||||
gearFlat.Shape = gearShape
|
||||
Gui.ActiveDocument.getObject(gearFlat.Name).Visibility = False
|
||||
|
||||
gear=App.ActiveDocument.addObject("Part::Extrusion","Gear3D")
|
||||
gear.Base=gearFlat
|
||||
gear.Dir=(0,0,width)
|
||||
gear = App.ActiveDocument.addObject("Part::Extrusion", "Gear3D")
|
||||
gear.Base = gearFlat
|
||||
gear.Dir = (0, 0, width)
|
||||
App.ActiveDocument.recompute()
|
||||
|
||||
|
||||
|
||||
if c1.isChecked()==True:
|
||||
gearMesh=App.ActiveDocument.addObject("Mesh::Feature","Gear3D-mesh")
|
||||
gearMesh = App.ActiveDocument.addObject("Mesh::Feature", "Gear3D-mesh")
|
||||
|
||||
faces = []
|
||||
triangles = gear.Shape.tessellate(1) # the number represents the precision of the tessellation)
|
||||
triangles = gear.Shape.tessellate(1) # the number represents the precision of the tessellation)
|
||||
for tri in triangles[1]:
|
||||
face = []
|
||||
for i in range(3):
|
||||
@@ -224,16 +232,16 @@ def compute():
|
||||
faces.append(face)
|
||||
mesh = Mesh.Mesh(faces)
|
||||
|
||||
gearMesh.Mesh=mesh
|
||||
gearMesh.Mesh = mesh
|
||||
App.ActiveDocument.removeObject(gear.Name)
|
||||
App.ActiveDocument.removeObject(gearFlat.Name)
|
||||
|
||||
|
||||
App.ActiveDocument.recompute()
|
||||
Gui.SendMsgToActiveView("ViewFit")
|
||||
|
||||
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
|
||||
|
||||
|
||||
hide()
|
||||
|
||||
@@ -259,22 +267,22 @@ la.addWidget(t3)
|
||||
l3 = QtGui.QLineEdit()
|
||||
l3.setText("20")
|
||||
la.addWidget(l3)
|
||||
t4 = QtGui.QLabel("Tooth height factor (y)")
|
||||
t4 = QtGui.QLabel("Tooth height factor (y)")
|
||||
la.addWidget(t4)
|
||||
l4 = QtGui.QLineEdit()
|
||||
l4.setText("1.0")
|
||||
la.addWidget(l4)
|
||||
t5 = QtGui.QLabel("Tooth clearance (c)")
|
||||
t5 = QtGui.QLabel("Tooth clearance (c)")
|
||||
la.addWidget(t5)
|
||||
l5 = QtGui.QLineEdit()
|
||||
l5.setText("0.1")
|
||||
la.addWidget(l5)
|
||||
t6 = QtGui.QLabel("Tooth lateral clearance (j)")
|
||||
t6 = QtGui.QLabel("Tooth lateral clearance (j)")
|
||||
la.addWidget(t6)
|
||||
l6 = QtGui.QLineEdit()
|
||||
l6.setText("0.04")
|
||||
la.addWidget(l6)
|
||||
t7 = QtGui.QLabel("Gear width")
|
||||
t7 = QtGui.QLabel("Gear width")
|
||||
la.addWidget(t7)
|
||||
l7 = QtGui.QLineEdit()
|
||||
l7.setText("6.0")
|
||||
@@ -282,7 +290,7 @@ la.addWidget(l7)
|
||||
c1 = QtGui.QCheckBox("Create as a Mesh")
|
||||
la.addWidget(c1)
|
||||
e1 = QtGui.QLabel("(for faster rendering)")
|
||||
commentFont=QtGui.QFont("Times",8,True)
|
||||
commentFont = QtGui.QFont("Times", 8, True)
|
||||
e1.setFont(commentFont)
|
||||
la.addWidget(e1)
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ from FreeCAD import Base
|
||||
class MySpring:
|
||||
def __init__(self, obj):
|
||||
''' Add the properties: Pitch, Diameter, Height, BarDiameter '''
|
||||
obj.addProperty("App::PropertyLength","Pitch","MySpring","Pitch of the helix").Pitch=5.0
|
||||
obj.addProperty("App::PropertyLength","Diameter","MySpring","Diameter of the helix").Diameter=6.0
|
||||
obj.addProperty("App::PropertyLength","Height","MySpring","Height of the helix").Height=30.0
|
||||
obj.addProperty("App::PropertyLength","BarDiameter","MySpring","Diameter of the bar").BarDiameter=3.0
|
||||
obj.addProperty("App::PropertyLength", "Pitch", "MySpring", "Pitch of the helix").Pitch = 5.0
|
||||
obj.addProperty("App::PropertyLength", "Diameter", "MySpring", "Diameter of the helix").Diameter = 6.0
|
||||
obj.addProperty("App::PropertyLength", "Height", "MySpring", "Height of the helix").Height = 30.0
|
||||
obj.addProperty("App::PropertyLength", "BarDiameter", "MySpring", "Diameter of the bar").BarDiameter = 3.0
|
||||
obj.Proxy = self
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
@@ -24,28 +24,29 @@ class MySpring:
|
||||
radius = fp.Diameter/2
|
||||
height = fp.Height
|
||||
barradius = fp.BarDiameter/2
|
||||
myhelix=Part.makeHelix(pitch,height,radius)
|
||||
g=myhelix.Edges[0].Curve
|
||||
c=Part.Circle()
|
||||
c.Center=g.value(0) # start point of the helix
|
||||
c.Axis=(0,1,0)
|
||||
c.Radius=barradius
|
||||
p=c.toShape()
|
||||
myhelix = Part.makeHelix(pitch, height, radius)
|
||||
g = myhelix.Edges[0].Curve
|
||||
c = Part.Circle()
|
||||
c.Center = g.value(0) # start point of the helix
|
||||
c.Axis = (0, 1, 0)
|
||||
c.Radius = barradius
|
||||
p = c.toShape()
|
||||
section = Part.Wire([p])
|
||||
makeSolid=1 #change to 1 to make a solid
|
||||
isFrenet=1
|
||||
myspring=Part.Wire(myhelix).makePipeShell([section],makeSolid,isFrenet)
|
||||
makeSolid = 1 # change to 1 to make a solid
|
||||
isFrenet = 1
|
||||
myspring = Part.Wire(myhelix).makePipeShell([section], makeSolid, isFrenet)
|
||||
fp.Shape = myspring
|
||||
|
||||
def makeMySpring():
|
||||
doc = FreeCAD.activeDocument()
|
||||
if doc is None:
|
||||
doc = FreeCAD.newDocument()
|
||||
spring=doc.addObject("Part::FeaturePython","My_Spring")
|
||||
spring = doc.addObject("Part::FeaturePython", "My_Spring")
|
||||
spring.Label = "My Spring"
|
||||
MySpring(spring)
|
||||
spring.ViewObject.Proxy=0
|
||||
spring.ViewObject.Proxy = 0
|
||||
doc.recompute()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
makeMySpring()
|
||||
|
||||
@@ -31,7 +31,7 @@ class TaskWizardShaft:
|
||||
"Shaft Wizard"
|
||||
App = FreeCAD
|
||||
Gui = FreeCADGui
|
||||
|
||||
|
||||
def __init__(self, doc):
|
||||
mw = QtGui.QApplication.activeWindow()
|
||||
#cw = mw.centralWidget() # This is a qmdiarea widget
|
||||
@@ -48,20 +48,20 @@ class TaskWizardShaft:
|
||||
featureWindow = cw.subWindowList()[-1]
|
||||
else:
|
||||
featureWindow = cw.activeSubWindow()
|
||||
|
||||
|
||||
# Buttons for diagram display
|
||||
buttonLayout = QtGui.QGridLayout()
|
||||
bnames = [["All [x]", "All [y]", "All [z]" ],
|
||||
["N [x]", "Q [y]", "Q [z]"],
|
||||
["Mt [x]", "Mb [z]", "Mb [y]"],
|
||||
["", "w [y]", "w [z]"],
|
||||
["sigma [x]", "sigma [y]", "sigma [z]"],
|
||||
buttonLayout = QtGui.QGridLayout()
|
||||
bnames = [["All [x]", "All [y]", "All [z]" ],
|
||||
["N [x]", "Q [y]", "Q [z]"],
|
||||
["Mt [x]", "Mb [z]", "Mb [y]"],
|
||||
["", "w [y]", "w [z]"],
|
||||
["sigma [x]", "sigma [y]", "sigma [z]"],
|
||||
["tau [x]", "sigmab [z]", "sigmab [y]"]]
|
||||
slots = [[self.slotAllx, self.slotAlly, self.slotAllz],
|
||||
[self.slotFx, self.slotQy, self.slotQz],
|
||||
[self.slotMx, self.slotMz, self.slotMy],
|
||||
[self.slotNone, self.slotWy, self.slotWz],
|
||||
[self.slotSigmax, self.slotSigmay, self.slotSigmaz],
|
||||
slots = [[self.slotAllx, self.slotAlly, self.slotAllz],
|
||||
[self.slotFx, self.slotQy, self.slotQz],
|
||||
[self.slotMx, self.slotMz, self.slotMy],
|
||||
[self.slotNone, self.slotWy, self.slotWz],
|
||||
[self.slotSigmax, self.slotSigmay, self.slotSigmaz],
|
||||
[self.slotTaut, self.slotSigmabz, self.slotSigmaby]]
|
||||
self.buttons = [[None, None, None], [None, None, None], [None, None, None], [None, None, None], [None, None, None], [None, None, None]]
|
||||
|
||||
@@ -71,23 +71,23 @@ class TaskWizardShaft:
|
||||
buttonLayout.addWidget(button, row, col)
|
||||
self.buttons[row][col] = button
|
||||
button.clicked.connect(slots[row][col])
|
||||
|
||||
|
||||
# Create Shaft object
|
||||
self.shaft = Shaft(self)
|
||||
# Create table widget
|
||||
self.form = QtGui.QWidget()
|
||||
self.table = WizardShaftTable(self, self.shaft)
|
||||
|
||||
|
||||
# The top layout will contain the Shaft Wizard layout plus the elements of the FEM constraints dialog
|
||||
layout = QtGui.QVBoxLayout()
|
||||
layout.setObjectName("ShaftWizard") # Do not change or translate: Required to detect whether Shaft Wizard is running in FemGui::ViewProviderFemConstraintXXX
|
||||
sublayout = QtGui.QVBoxLayout()
|
||||
sublayout.setObjectName("ShaftWizardLayout") # Do not change or translate
|
||||
sublayout.setObjectName("ShaftWizardLayout") # Do not change or translate
|
||||
sublayout.addWidget(self.table.widget)
|
||||
sublayout.addLayout(buttonLayout)
|
||||
layout.addLayout(sublayout)
|
||||
self.form.setLayout(layout)
|
||||
|
||||
|
||||
# Switch to feature window
|
||||
mdi=FreeCADGui.getMainWindow().findChild(QtGui.QMdiArea)
|
||||
cw.setActiveSubWindow(featureWindow)
|
||||
@@ -108,49 +108,49 @@ class TaskWizardShaft:
|
||||
self.showDiagram("Ally")
|
||||
def slotAllz(self):
|
||||
self.showDiagram("Allz")
|
||||
|
||||
|
||||
def slotFx(self):
|
||||
self.showDiagram("Nx")
|
||||
def slotQy(self):
|
||||
self.showDiagram("Qy")
|
||||
def slotQz(self):
|
||||
self.showDiagram("Qz")
|
||||
|
||||
|
||||
def slotMx(self):
|
||||
self.showDiagram("Mx")
|
||||
def slotMz(self):
|
||||
self.showDiagram("Mz")
|
||||
def slotMy(self):
|
||||
self.showDiagram("My")
|
||||
|
||||
|
||||
def slotNone(self):
|
||||
pass
|
||||
def slotWy(self):
|
||||
self.showDiagram("wy")
|
||||
def slotWz(self):
|
||||
self.showDiagram("wz")
|
||||
|
||||
|
||||
def slotSigmax(self):
|
||||
self.showDiagram("sigmax")
|
||||
def slotSigmay(self):
|
||||
self.showDiagram("sigmay")
|
||||
def slotSigmaz(self):
|
||||
self.showDiagram("sigmaz")
|
||||
|
||||
|
||||
def slotTaut(self):
|
||||
self.showDiagram("taut")
|
||||
def slotSigmabz(self):
|
||||
self.showDiagram("sigmabz")
|
||||
def slotSigmaby(self):
|
||||
self.showDiagram("sigmaby")
|
||||
|
||||
|
||||
def updateButton(self, row, col, flag):
|
||||
self.buttons[row][col].setEnabled(flag)
|
||||
|
||||
|
||||
def updateButtons(self, col, flag):
|
||||
for row in range(len(self.buttons)):
|
||||
self.updateButton(row, col, flag)
|
||||
|
||||
|
||||
def getStandardButtons(self):
|
||||
return int(QtGui.QDialogButtonBox.Ok)
|
||||
|
||||
@@ -162,7 +162,7 @@ class TaskWizardShaft:
|
||||
if self.form:
|
||||
del self.form
|
||||
return True
|
||||
|
||||
|
||||
def isAllowedAlterDocument(self):
|
||||
return False
|
||||
|
||||
@@ -170,44 +170,48 @@ class TaskWizardShaft:
|
||||
# Problem: From the FemConstraint ViewProvider, we need to tell the Shaft instance that the user finished editing the constraint
|
||||
# We can find the Shaft Wizard dialog object from C++, but there is no way to reach the Shaft instance
|
||||
# Also it seems to be impossible to access the active dialog from Python, so Gui::Command::runCommand() is not an option either
|
||||
# Note: Another way would be to create a hidden widget in the Shaft Wizard dialog and write some data to it, triggering a slot
|
||||
# Note: Another way would be to create a hidden widget in the Shaft Wizard dialog and write some data to it, triggering a slot
|
||||
# in the python code
|
||||
WizardShaftDlg = None
|
||||
|
||||
class WizardShaftGui:
|
||||
class WizardShaftGui:
|
||||
def Activated(self):
|
||||
global WizardShaftDlg
|
||||
WizardShaftDlg = TaskWizardShaft(FreeCAD.ActiveDocument)
|
||||
FreeCADGui.Control.showDialog(WizardShaftDlg)
|
||||
|
||||
|
||||
def GetResources(self):
|
||||
IconPath = FreeCAD.ConfigGet("AppHomePath") + "Mod/PartDesign/WizardShaft/WizardShaft.svg"
|
||||
MenuText = 'Shaft design wizard...'
|
||||
ToolTip = 'Start the shaft design wizard'
|
||||
return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
|
||||
MenuText = QtCore.QT_TRANSLATE_NOOP("WizardShaft", "Shaft design wizard...")
|
||||
ToolTip = QtCore.QT_TRANSLATE_NOOP("WizardShaft", "Start the shaft design wizard")
|
||||
return {'Pixmap': IconPath,
|
||||
'MenuText': MenuText,
|
||||
'ToolTip': ToolTip}
|
||||
|
||||
def IsActive(self):
|
||||
return FreeCAD.ActiveDocument != None
|
||||
|
||||
|
||||
def __del__(self):
|
||||
global WizardShaftDlg
|
||||
WizardShaftDlg = None
|
||||
|
||||
class WizardShaftGuiCallback:
|
||||
|
||||
class WizardShaftGuiCallback:
|
||||
def Activated(self):
|
||||
global WizardShaftDlg
|
||||
if WizardShaftDlg != None and WizardShaftDlg.table != None:
|
||||
WizardShaftDlg.table.finishEditConstraint()
|
||||
|
||||
|
||||
def isActive(self):
|
||||
global WizardShaftDlg
|
||||
return (WizardShaftDlg is not None)
|
||||
|
||||
|
||||
def GetResources(self):
|
||||
IconPath = FreeCAD.ConfigGet("AppHomePath") + "Mod/PartDesign/WizardShaft/WizardShaft.svg"
|
||||
MenuText = 'Shaft design wizard...'
|
||||
ToolTip = 'Start the shaft design wizard'
|
||||
return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
|
||||
MenuText = QtCore.QT_TRANSLATE_NOOP("WizardShaft", "Shaft design wizard...")
|
||||
ToolTip = QtCore.QT_TRANSLATE_NOOP("WizardShaft", "Start the shaft design wizard")
|
||||
return {'Pixmap': IconPath,
|
||||
'MenuText': MenuText,
|
||||
'ToolTip': ToolTip}
|
||||
|
||||
FreeCADGui.addCommand('PartDesign_WizardShaft', WizardShaftGui())
|
||||
FreeCADGui.addCommand('PartDesign_WizardShaftCallBack', WizardShaftGuiCallback())
|
||||
|
||||
@@ -32,7 +32,7 @@ class WizardShaftTable:
|
||||
"Length" : 0,
|
||||
"Diameter" : 1,
|
||||
"InnerDiameter" : 2,
|
||||
"ConstraintType" : 3,
|
||||
"ConstraintType": 3,
|
||||
"StartEdgeType" : 4,
|
||||
"StartEdgeSize" : 5,
|
||||
"EndEdgeType" : 6,
|
||||
@@ -40,15 +40,15 @@ class WizardShaftTable:
|
||||
}
|
||||
rowDictReverse = {}
|
||||
headers = [
|
||||
"Length [mm]",
|
||||
"Diameter [mm]",
|
||||
"Inner diameter [mm]",
|
||||
"Constraint type",
|
||||
"Start edge type",
|
||||
"Start edge size",
|
||||
"End edge type",
|
||||
"End edge size"
|
||||
]
|
||||
QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Length [mm]"),
|
||||
QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Diameter [mm]"),
|
||||
QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Inner diameter [mm]"),
|
||||
QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Constraint type"),
|
||||
QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Start edge type"),
|
||||
QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Start edge size"),
|
||||
QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "End edge type"),
|
||||
QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "End edge size")
|
||||
]
|
||||
|
||||
def __init__(self, w, s):
|
||||
for key in iter(self.rowDict.keys()):
|
||||
@@ -57,7 +57,7 @@ class WizardShaftTable:
|
||||
self.wizard = w
|
||||
self.shaft = s
|
||||
# Create table widget
|
||||
self.widget = QtGui.QTableWidget(len(self.rowDict), 0)
|
||||
self.widget = QtGui.QTableWidget(len(self.rowDict), 0)
|
||||
self.widget.setObjectName("ShaftWizardTable") # Do not change or translate: Used in ViewProviderFemConstraintXXX
|
||||
self.widget.setWindowTitle("Shaft wizard")
|
||||
self.widget.resize(QtCore.QSize(300,200))
|
||||
@@ -102,7 +102,7 @@ class WizardShaftTable:
|
||||
index = self.widget.columnCount()
|
||||
# Make an intelligent guess at the length/dia of the next segment
|
||||
if index > 0:
|
||||
length = self.shaft.segments[index-1].length
|
||||
length = self.shaft.segments[index-1].length
|
||||
diameter = self.shaft.segments[index-1].diameter
|
||||
if index > 2:
|
||||
diameter -= 5.0
|
||||
@@ -156,7 +156,7 @@ class WizardShaftTable:
|
||||
widget.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
||||
self.widget.setCellWidget(self.rowDict["ConstraintType"], index, widget)
|
||||
widget.setCurrentIndex(0)
|
||||
self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotConstraintType)
|
||||
self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotConstraintType)
|
||||
# Start edge type
|
||||
widget = QtGui.QComboBox(self.widget)
|
||||
widget.insertItem(0, "None",)
|
||||
@@ -221,13 +221,13 @@ class WizardShaftTable:
|
||||
elif rowName == "EndEdgeSize":
|
||||
pass
|
||||
|
||||
def slotEditConstraint(self):
|
||||
def slotEditConstraint(self):
|
||||
(self.editedRow, self.editedColumn) = self.getFocusedCell() # Because finishEditConstraint() will trigger slotEditingFinished() which requires this information
|
||||
self.shaft.editConstraint(self.editedColumn)
|
||||
|
||||
|
||||
def finishEditConstraint(self):
|
||||
self.shaft.updateConstraint(self.editedColumn, self.getConstraintType(self.editedColumn))
|
||||
|
||||
|
||||
def setLength(self, column, l):
|
||||
self.setDoubleValue("Length", column, l)
|
||||
self.shaft.updateSegment(column, length = l)
|
||||
|
||||
@@ -737,9 +737,19 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def SetupProperties():
|
||||
setup = ["Side", "OperationType", "Tolerance", "StepOver",
|
||||
"LiftDistance", "KeepToolDownRatio", "StockToLeave",
|
||||
"ForceInsideOut", "FinishingProfile", "Stopped",
|
||||
"StopProcessing", "UseHelixArcs", "AdaptiveInputState",
|
||||
"AdaptiveOutputState", "HelixAngle", "HelixConeAngle",
|
||||
"HelixDiameterLimit", "UseOutline"]
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Adaptive operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = PathAdaptive(obj, name)
|
||||
obj.Proxy = PathAdaptive(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -70,9 +70,9 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Custom operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
proxy = ObjectCustom(obj, name)
|
||||
obj.Proxy = ObjectCustom(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -291,10 +291,9 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Deburr operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
|
||||
obj.Proxy = ObjectDeburr(obj, name)
|
||||
obj.Proxy = ObjectDeburr(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -35,6 +35,11 @@ PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
def _vstr(v):
|
||||
if v:
|
||||
return "(%.2f, %.2f, %.2f)" % (v.x, v.y, v.z)
|
||||
@@ -77,7 +82,29 @@ class DressupPathBoundary(object):
|
||||
obj.Stock = None
|
||||
return True
|
||||
|
||||
def boundaryCommands(self, obj, begin, end, verticalFeed):
|
||||
def execute(self, obj):
|
||||
pb = PathBoundary(obj.Base, obj.Stock.Shape, obj.Inside)
|
||||
obj.Path = pb.execute()
|
||||
# Eclass
|
||||
|
||||
|
||||
class PathBoundary:
|
||||
"""class PathBoundary...
|
||||
This class requires a base operation, boundary shape, and optional inside boolean (default is True).
|
||||
The `execute()` method returns a Path object with path commands limited to cut paths inside or outside
|
||||
the provided boundary shape.
|
||||
"""
|
||||
|
||||
def __init__(self, baseOp, boundaryShape, inside=True):
|
||||
self.baseOp = baseOp
|
||||
self.boundary = boundaryShape
|
||||
self.inside = inside
|
||||
self.safeHeight = None
|
||||
self.clearanceHeight = None
|
||||
self.strG1ZsafeHeight = None
|
||||
self.strG0ZclearanceHeight = None
|
||||
|
||||
def boundaryCommands(self, begin, end, verticalFeed):
|
||||
PathLog.track(_vstr(begin), _vstr(end))
|
||||
if end and PathGeom.pointsCoincide(begin, end):
|
||||
return []
|
||||
@@ -94,117 +121,117 @@ class DressupPathBoundary(object):
|
||||
cmds.append(Path.Command('G1', {'Z': end.z, 'F': verticalFeed}))
|
||||
return cmds
|
||||
|
||||
def execute(self, obj):
|
||||
if not obj.Base or not obj.Base.isDerivedFrom('Path::Feature') or not obj.Base.Path:
|
||||
return
|
||||
def execute(self):
|
||||
if not self.baseOp or not self.baseOp.isDerivedFrom('Path::Feature') or not self.baseOp.Path:
|
||||
return None
|
||||
|
||||
tc = PathDressup.toolController(obj.Base)
|
||||
if len(self.baseOp.Path.Commands) == 0:
|
||||
PathLog.warning("No Path Commands for %s" % self.baseOp.Label)
|
||||
return []
|
||||
|
||||
if len(obj.Base.Path.Commands) > 0:
|
||||
self.safeHeight = float(PathUtil.opProperty(obj.Base, 'SafeHeight'))
|
||||
self.clearanceHeight = float(PathUtil.opProperty(obj.Base, 'ClearanceHeight'))
|
||||
self.strG1ZsafeHeight = Path.Command('G1', {'Z': self.safeHeight, 'F': tc.VertFeed.Value})
|
||||
self.strG0ZclearanceHeight = Path.Command('G0', {'Z': self.clearanceHeight})
|
||||
tc = PathDressup.toolController(self.baseOp)
|
||||
|
||||
boundary = obj.Stock.Shape
|
||||
cmd = obj.Base.Path.Commands[0]
|
||||
pos = cmd.Placement.Base # bogus m/c position to create first edge
|
||||
bogusX = True
|
||||
bogusY = True
|
||||
commands = [cmd]
|
||||
lastExit = None
|
||||
for cmd in obj.Base.Path.Commands[1:]:
|
||||
if cmd.Name in PathGeom.CmdMoveAll:
|
||||
if bogusX == True :
|
||||
bogusX = ( 'X' not in cmd.Parameters )
|
||||
if bogusY :
|
||||
bogusY = ( 'Y' not in cmd.Parameters )
|
||||
edge = PathGeom.edgeForCmd(cmd, pos)
|
||||
if edge:
|
||||
inside = edge.common(boundary).Edges
|
||||
outside = edge.cut(boundary).Edges
|
||||
if not obj.Inside: # UI "inside boundary" param
|
||||
tmp = inside
|
||||
inside = outside
|
||||
outside = tmp
|
||||
# it's really a shame that one cannot trust the sequence and/or
|
||||
# orientation of edges
|
||||
if 1 == len(inside) and 0 == len(outside):
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), ' + ', cmd)
|
||||
# cmd fully included by boundary
|
||||
if lastExit:
|
||||
self.safeHeight = float(PathUtil.opProperty(self.baseOp, 'SafeHeight'))
|
||||
self.clearanceHeight = float(PathUtil.opProperty(self.baseOp, 'ClearanceHeight'))
|
||||
self.strG1ZsafeHeight = Path.Command('G1', {'Z': self.safeHeight, 'F': tc.VertFeed.Value})
|
||||
self.strG0ZclearanceHeight = Path.Command('G0', {'Z': self.clearanceHeight})
|
||||
|
||||
cmd = self.baseOp.Path.Commands[0]
|
||||
pos = cmd.Placement.Base # bogus m/c position to create first edge
|
||||
bogusX = True
|
||||
bogusY = True
|
||||
commands = [cmd]
|
||||
lastExit = None
|
||||
for cmd in self.baseOp.Path.Commands[1:]:
|
||||
if cmd.Name in PathGeom.CmdMoveAll:
|
||||
if bogusX == True :
|
||||
bogusX = ( 'X' not in cmd.Parameters )
|
||||
if bogusY :
|
||||
bogusY = ( 'Y' not in cmd.Parameters )
|
||||
edge = PathGeom.edgeForCmd(cmd, pos)
|
||||
if edge:
|
||||
inside = edge.common(self.boundary).Edges
|
||||
outside = edge.cut(self.boundary).Edges
|
||||
if not self.inside: # UI "inside boundary" param
|
||||
tmp = inside
|
||||
inside = outside
|
||||
outside = tmp
|
||||
# it's really a shame that one cannot trust the sequence and/or
|
||||
# orientation of edges
|
||||
if 1 == len(inside) and 0 == len(outside):
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), ' + ', cmd)
|
||||
# cmd fully included by boundary
|
||||
if lastExit:
|
||||
if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position
|
||||
commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value))
|
||||
lastExit = None
|
||||
commands.append(cmd)
|
||||
pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
elif 0 == len(inside) and 1 == len(outside):
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), ' - ', cmd)
|
||||
# cmd fully excluded by boundary
|
||||
if not lastExit:
|
||||
lastExit = pos
|
||||
pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
else:
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd)
|
||||
# cmd pierces boundary
|
||||
while inside or outside:
|
||||
ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)]
|
||||
PathLog.track(ie)
|
||||
if ie:
|
||||
e = ie[0]
|
||||
LastPt = e.valueAt(e.LastParameter)
|
||||
flip = PathGeom.pointsCoincide(pos, LastPt)
|
||||
newPos = e.valueAt(e.FirstParameter) if flip else LastPt
|
||||
# inside edges are taken at this point (see swap of inside/outside
|
||||
# above - so we can just connect the dots ...
|
||||
if lastExit:
|
||||
if not ( bogusX or bogusY ) : commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value))
|
||||
lastExit = None
|
||||
PathLog.track(e, flip)
|
||||
if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position
|
||||
commands.extend(self.boundaryCommands(obj, lastExit, pos, tc.VertFeed.Value))
|
||||
lastExit = None
|
||||
commands.append(cmd)
|
||||
pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
elif 0 == len(inside) and 1 == len(outside):
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), ' - ', cmd)
|
||||
# cmd fully excluded by boundary
|
||||
if not lastExit:
|
||||
lastExit = pos
|
||||
pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
else:
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd)
|
||||
# cmd pierces boundary
|
||||
while inside or outside:
|
||||
ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)]
|
||||
PathLog.track(ie)
|
||||
if ie:
|
||||
e = ie[0]
|
||||
LastPt = e.valueAt(e.LastParameter)
|
||||
flip = PathGeom.pointsCoincide(pos, LastPt)
|
||||
newPos = e.valueAt(e.FirstParameter) if flip else LastPt
|
||||
# inside edges are taken at this point (see swap of inside/outside
|
||||
# above - so we can just connect the dots ...
|
||||
if lastExit:
|
||||
if not ( bogusX or bogusY ) : commands.extend(self.boundaryCommands(obj, lastExit, pos, tc.VertFeed.Value))
|
||||
lastExit = None
|
||||
PathLog.track(e, flip)
|
||||
if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position
|
||||
commands.extend(PathGeom.cmdsForEdge(e, flip, False, 50, tc.HorizFeed.Value, tc.VertFeed.Value))
|
||||
inside.remove(e)
|
||||
commands.extend(PathGeom.cmdsForEdge(e, flip, False, 50, tc.HorizFeed.Value, tc.VertFeed.Value))
|
||||
inside.remove(e)
|
||||
pos = newPos
|
||||
lastExit = newPos
|
||||
else:
|
||||
oe = [e for e in outside if PathGeom.edgeConnectsTo(e, pos)]
|
||||
PathLog.track(oe)
|
||||
if oe:
|
||||
e = oe[0]
|
||||
ptL = e.valueAt(e.LastParameter)
|
||||
flip = PathGeom.pointsCoincide(pos, ptL)
|
||||
newPos = e.valueAt(e.FirstParameter) if flip else ptL
|
||||
# outside edges are never taken at this point (see swap of
|
||||
# inside/outside above) - so just move along ...
|
||||
outside.remove(e)
|
||||
pos = newPos
|
||||
lastExit = newPos
|
||||
else:
|
||||
oe = [e for e in outside if PathGeom.edgeConnectsTo(e, pos)]
|
||||
PathLog.track(oe)
|
||||
if oe:
|
||||
e = oe[0]
|
||||
ptL = e.valueAt(e.LastParameter)
|
||||
flip = PathGeom.pointsCoincide(pos, ptL)
|
||||
newPos = e.valueAt(e.FirstParameter) if flip else ptL
|
||||
# outside edges are never taken at this point (see swap of
|
||||
# inside/outside above) - so just move along ...
|
||||
outside.remove(e)
|
||||
pos = newPos
|
||||
else:
|
||||
PathLog.error('huh?')
|
||||
import Part
|
||||
Part.show(Part.Vertex(pos), 'pos')
|
||||
for e in inside:
|
||||
Part.show(e, 'ei')
|
||||
for e in outside:
|
||||
Part.show(e, 'eo')
|
||||
raise Exception('This is not supposed to happen')
|
||||
# Eif
|
||||
PathLog.error('huh?')
|
||||
import Part
|
||||
Part.show(Part.Vertex(pos), 'pos')
|
||||
for e in inside:
|
||||
Part.show(e, 'ei')
|
||||
for e in outside:
|
||||
Part.show(e, 'eo')
|
||||
raise Exception('This is not supposed to happen')
|
||||
# Eif
|
||||
# Ewhile
|
||||
# Eif
|
||||
# pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
# Eif
|
||||
# Ewhile
|
||||
# Eif
|
||||
else:
|
||||
PathLog.track('no-move', cmd)
|
||||
commands.append(cmd)
|
||||
if lastExit:
|
||||
commands.extend(self.boundaryCommands(obj, lastExit, None, tc.VertFeed.Value))
|
||||
lastExit = None
|
||||
else:
|
||||
PathLog.warning("No Path Commands for %s" % obj.Base.Label)
|
||||
commands = []
|
||||
PathLog.track(commands)
|
||||
obj.Path = Path.Path(commands)
|
||||
# pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
# Eif
|
||||
else:
|
||||
PathLog.track('no-move', cmd)
|
||||
commands.append(cmd)
|
||||
if lastExit:
|
||||
commands.extend(self.boundaryCommands(lastExit, None, tc.VertFeed.Value))
|
||||
lastExit = None
|
||||
|
||||
PathLog.track(commands)
|
||||
return Path.Path(commands)
|
||||
# Eclass
|
||||
|
||||
def Create(base, name='DressupPathBoundary'):
|
||||
'''Create(base, name='DressupPathBoundary') ... creates a dressup limiting base's Path to a boundary.'''
|
||||
|
||||
@@ -159,12 +159,13 @@ def SetupProperties():
|
||||
setup.append("RetractHeight")
|
||||
return setup
|
||||
|
||||
def Create(name, obj = None):
|
||||
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Drilling operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
|
||||
obj.Proxy = ObjectDrilling(obj, name)
|
||||
obj.Proxy = ObjectDrilling(obj, name, parentJob)
|
||||
if obj.Proxy:
|
||||
obj.Proxy.findAllHoles(obj)
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ def translate(context, text, disambig=None):
|
||||
class ObjectEngrave(PathEngraveBase.ObjectOp):
|
||||
'''Proxy class for Engrave operation.'''
|
||||
|
||||
def __init__(self, obj, name):
|
||||
super(ObjectEngrave, self).__init__(obj, name)
|
||||
def __init__(self, obj, name, parentJob):
|
||||
super(ObjectEngrave, self).__init__(obj, name, parentJob)
|
||||
self.wires = []
|
||||
|
||||
def opFeatures(self, obj):
|
||||
@@ -144,9 +144,9 @@ def SetupProperties():
|
||||
return ["StartVertex"]
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns an Engrave operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectEngrave(obj, name)
|
||||
obj.Proxy = ObjectEngrave(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -68,10 +68,10 @@ def updateInputField(obj, prop, widget, onBeforeChange=None):
|
||||
isDiff = True
|
||||
break
|
||||
if noExpr:
|
||||
widget.setProperty('readonly', False)
|
||||
widget.setReadOnly(False)
|
||||
widget.setStyleSheet("color: black")
|
||||
else:
|
||||
widget.setProperty('readonly', True)
|
||||
widget.setReadOnly(True)
|
||||
widget.setStyleSheet("color: gray")
|
||||
widget.update()
|
||||
|
||||
@@ -100,6 +100,7 @@ class QuantitySpinBox:
|
||||
self.widget = widget
|
||||
self.onBeforeChange = onBeforeChange
|
||||
self.prop = None
|
||||
self.obj = obj
|
||||
self.attachTo(obj, prop)
|
||||
|
||||
def attachTo(self, obj, prop = None):
|
||||
@@ -139,9 +140,14 @@ class QuantitySpinBox:
|
||||
If no value is provided the value of the bound property is used.
|
||||
quantity can be of type Quantity or Float.'''
|
||||
PathLog.track(self.prop, self.valid)
|
||||
|
||||
if self.valid:
|
||||
expr = self._hasExpression()
|
||||
if quantity is None:
|
||||
quantity = PathUtil.getProperty(self.obj, self.prop)
|
||||
if expr:
|
||||
quantity = FreeCAD.Units.Quantity(self.obj.evalExpression(expr))
|
||||
else:
|
||||
quantity = PathUtil.getProperty(self.obj, self.prop)
|
||||
value = quantity.Value if hasattr(quantity, 'Value') else quantity
|
||||
self.widget.setProperty('rawValue', value)
|
||||
|
||||
@@ -151,3 +157,9 @@ class QuantitySpinBox:
|
||||
if self.valid:
|
||||
return updateInputField(self.obj, self.prop, self.widget, self.onBeforeChange)
|
||||
return None
|
||||
|
||||
def _hasExpression(self):
|
||||
for (prop, exp) in self.obj.ExpressionEngine:
|
||||
if prop == self.prop:
|
||||
return exp
|
||||
return None
|
||||
|
||||
@@ -214,11 +214,11 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Helix operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectHelix(obj, name)
|
||||
obj.Proxy = ObjectHelix(obj, name, parentJob)
|
||||
if obj.Proxy:
|
||||
obj.Proxy.findAllHoles(obj)
|
||||
return obj
|
||||
|
||||
@@ -175,8 +175,8 @@ class ViewProvider:
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCADGui.Control.showDialog(self.taskPanel)
|
||||
self.taskPanel.setupUi(activate)
|
||||
self.deleteOnReject = False
|
||||
self.showOriginAxis(True)
|
||||
self.deleteOnReject = False
|
||||
|
||||
def resetTaskPanel(self):
|
||||
self.showOriginAxis(False)
|
||||
|
||||
@@ -304,9 +304,9 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Mill Facing operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectFace(obj, name)
|
||||
obj.Proxy = ObjectFace(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -119,7 +119,7 @@ class ObjectOp(object):
|
||||
obj.addProperty("App::PropertyDistance", "OpStockZMin", "Op Values", QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the min Z value of Stock"))
|
||||
obj.setEditorMode('OpStockZMin', 1) # read-only
|
||||
|
||||
def __init__(self, obj, name):
|
||||
def __init__(self, obj, name, parentJob=None):
|
||||
PathLog.track()
|
||||
|
||||
obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Make False, to prevent operation from generating code"))
|
||||
@@ -190,6 +190,8 @@ class ObjectOp(object):
|
||||
self.initOperation(obj)
|
||||
|
||||
if not hasattr(obj, 'DoNotSetDefaultValues') or not obj.DoNotSetDefaultValues:
|
||||
if parentJob:
|
||||
self.job = PathUtils.addToJob(obj, jobname=parentJob.Name)
|
||||
job = self.setDefaultValues(obj)
|
||||
if job:
|
||||
job.SetupSheet.Proxy.setOperationProperties(obj, name)
|
||||
@@ -322,7 +324,10 @@ class ObjectOp(object):
|
||||
def setDefaultValues(self, obj):
|
||||
'''setDefaultValues(obj) ... base implementation.
|
||||
Do not overwrite, overwrite opSetDefaultValues() instead.'''
|
||||
job = PathUtils.addToJob(obj)
|
||||
if self.job:
|
||||
job = self.job
|
||||
else:
|
||||
job = PathUtils.addToJob(obj)
|
||||
|
||||
obj.Active = True
|
||||
|
||||
@@ -622,5 +627,3 @@ class ObjectOp(object):
|
||||
This function can safely be overwritten by subclasses.'''
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -214,11 +214,6 @@ class TaskPanelPage(object):
|
||||
def _installTCUpdate(self):
|
||||
return hasattr(self.form, 'toolController')
|
||||
|
||||
def setParent(self, parent):
|
||||
'''setParent() ... used to transfer parent object link to child class.
|
||||
Do not overwrite.'''
|
||||
self.parent = parent
|
||||
|
||||
def onDirtyChanged(self, callback):
|
||||
'''onDirtyChanged(callback) ... set callback when dirty state changes.'''
|
||||
self.signalDirtyChanged = callback
|
||||
@@ -1000,8 +995,10 @@ class TaskPanel(object):
|
||||
def __init__(self, obj, deleteOnReject, opPage, selectionFactory):
|
||||
PathLog.track(obj.Label, deleteOnReject, opPage, selectionFactory)
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path", "AreaOp Operation"))
|
||||
self.obj = obj
|
||||
self.deleteOnReject = deleteOnReject
|
||||
self.featurePages = []
|
||||
self.parent = None
|
||||
|
||||
# members initialized later
|
||||
self.clearanceHeight = None
|
||||
@@ -1050,9 +1047,9 @@ class TaskPanel(object):
|
||||
self.featurePages.append(opPage)
|
||||
|
||||
for page in self.featurePages:
|
||||
page.parent = self # save pointer to this current class as "parent"
|
||||
page.initPage(obj)
|
||||
page.onDirtyChanged(self.pageDirtyChanged)
|
||||
page.setParent(self)
|
||||
|
||||
taskPanelLayout = PathPreferences.defaultTaskPanelLayout()
|
||||
|
||||
@@ -1092,7 +1089,6 @@ class TaskPanel(object):
|
||||
self.form = forms
|
||||
|
||||
self.selectionFactory = selectionFactory
|
||||
self.obj = obj
|
||||
self.isdirty = deleteOnReject
|
||||
self.visibility = obj.ViewObject.Visibility
|
||||
obj.ViewObject.Visibility = True
|
||||
@@ -1207,7 +1203,18 @@ class TaskPanel(object):
|
||||
page.clearBase()
|
||||
page.addBaseGeometry(sel)
|
||||
|
||||
# Update properties based upon expressions in case expression value has changed
|
||||
for (prp, expr) in self.obj.ExpressionEngine:
|
||||
val = FreeCAD.Units.Quantity(self.obj.evalExpression(expr))
|
||||
value = val.Value if hasattr(val, 'Value') else val
|
||||
prop = getattr(self.obj, prp)
|
||||
if hasattr(prop, "Value"):
|
||||
prop.Value = value
|
||||
else:
|
||||
prop = value
|
||||
|
||||
self.panelSetFields()
|
||||
|
||||
for page in self.featurePages:
|
||||
page.pageRegisterSignalHandlers()
|
||||
|
||||
@@ -1280,12 +1287,12 @@ def Create(res):
|
||||
this function directly, but calls the Activated() function of the Command object
|
||||
that is created in each operations Gui implementation.'''
|
||||
FreeCAD.ActiveDocument.openTransaction("Create %s" % res.name)
|
||||
obj = res.objFactory(res.name)
|
||||
obj = res.objFactory(res.name, obj=None, parentJob=res.job)
|
||||
if obj.Proxy:
|
||||
obj.ViewObject.Proxy = ViewProvider(obj.ViewObject, res)
|
||||
obj.ViewObject.Visibility = False
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
|
||||
obj.ViewObject.Document.setEdit(obj.ViewObject, 0)
|
||||
return obj
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
@@ -1329,6 +1336,7 @@ class CommandResources:
|
||||
self.menuText = menuText
|
||||
self.accelKey = accelKey
|
||||
self.toolTip = toolTip
|
||||
self.job = None
|
||||
|
||||
|
||||
def SetupOperation(name,
|
||||
|
||||
@@ -717,9 +717,9 @@ def SetupProperties():
|
||||
return PathPocketBase.SetupProperties() + ["HandleMultipleFeatures"]
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Pocket operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectPocket(obj, name)
|
||||
obj.Proxy = ObjectPocket(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -84,6 +84,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
def areaOpShapes(self, obj):
|
||||
'''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
|
||||
PathLog.track()
|
||||
self.removalshapes = []
|
||||
|
||||
# self.isDebug = True if PathLog.getLevel(PathLog.thisModule()) == 4 else False
|
||||
self.removalshapes = []
|
||||
@@ -162,7 +163,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
# shape.tessellate(0.05) # originally 0.1
|
||||
|
||||
if self.removalshapes:
|
||||
obj.removalshape = self.removalshapes[0][0]
|
||||
obj.removalshape = Part.makeCompound([tup[0] for tup in self.removalshapes])
|
||||
|
||||
return self.removalshapes
|
||||
|
||||
# Support methods
|
||||
@@ -242,11 +244,11 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Pocket operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', name)
|
||||
|
||||
obj.Proxy = ObjectPocket(obj, name)
|
||||
obj.Proxy = ObjectPocket(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
return obj
|
||||
@@ -97,9 +97,9 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Probing operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
proxy = ObjectProbing(obj, name)
|
||||
proxy = ObjectProbing(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -1301,9 +1301,9 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Profile based on faces operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectProfile(obj, name)
|
||||
obj.Proxy = ObjectProfile(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -42,9 +42,9 @@ def SetupProperties():
|
||||
return PathProfile.SetupProperties()
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Profile operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectContour(obj, name)
|
||||
obj.Proxy = ObjectContour(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -43,9 +43,9 @@ def SetupProperties():
|
||||
return PathProfile.SetupProperties()
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Profile operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectProfile(obj, name)
|
||||
obj.Proxy = ObjectProfile(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -44,9 +44,9 @@ def SetupProperties():
|
||||
return PathProfile.SetupProperties()
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Profile operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectProfile(obj, name)
|
||||
obj.Proxy = ObjectProfile(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -34,7 +34,7 @@ __doc__ = "A container for all default values and job specific configuration val
|
||||
|
||||
_RegisteredOps = {}
|
||||
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import PathScripts.PathDressup as PathDressup
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PathJob as PathJob
|
||||
import PathSimulator
|
||||
import math
|
||||
import os
|
||||
@@ -75,6 +76,7 @@ class PathSimulation:
|
||||
self.simperiod = 20
|
||||
self.accuracy = 0.1
|
||||
self.resetSimulation = False
|
||||
self.jobs = []
|
||||
|
||||
def Connect(self, but, sig):
|
||||
QtCore.QObject.connect(but, QtCore.SIGNAL("clicked()"), sig)
|
||||
@@ -96,13 +98,9 @@ class PathSimulation:
|
||||
self.onSpeedBarChange()
|
||||
form.sliderAccuracy.valueChanged.connect(self.onAccuracyBarChange)
|
||||
self.onAccuracyBarChange()
|
||||
self._populateJobSelection(form)
|
||||
form.comboJobs.currentIndexChanged.connect(self.onJobChange)
|
||||
jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*")
|
||||
form.comboJobs.clear()
|
||||
self.jobs = []
|
||||
for j in jobList:
|
||||
self.jobs.append(j)
|
||||
form.comboJobs.addItem(j.ViewObject.Icon, j.Label)
|
||||
self.onJobChange()
|
||||
FreeCADGui.Control.showDialog(self.taskForm)
|
||||
self.disableAnim = False
|
||||
self.isVoxel = True
|
||||
@@ -111,6 +109,40 @@ class PathSimulation:
|
||||
self.SimulateMill()
|
||||
self.initdone = True
|
||||
|
||||
def _populateJobSelection(self, form):
|
||||
# Make Job selection combobox
|
||||
setJobIdx = 0
|
||||
jobName = ''
|
||||
jIdx = 0
|
||||
# Get list of Job objects in active document
|
||||
jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*")
|
||||
jCnt = len(jobList)
|
||||
|
||||
# Check if user has selected a specific job for simulation
|
||||
guiSelection = FreeCADGui.Selection.getSelectionEx()
|
||||
if guiSelection: # Identify job selected by user
|
||||
sel = guiSelection[0]
|
||||
if hasattr(sel.Object, "Proxy") and isinstance(sel.Object.Proxy, PathJob.ObjectJob):
|
||||
jobName = sel.Object.Name
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
|
||||
# populate the job selection combobox
|
||||
form.comboJobs.blockSignals(True)
|
||||
form.comboJobs.clear()
|
||||
form.comboJobs.blockSignals(False)
|
||||
for j in jobList:
|
||||
form.comboJobs.addItem(j.ViewObject.Icon, j.Label)
|
||||
self.jobs.append(j)
|
||||
if j.Name == jobName or jCnt == 1:
|
||||
setJobIdx = jIdx
|
||||
jIdx += 1
|
||||
|
||||
# Pre-select GUI-selected job in the combobox
|
||||
if jobName or jCnt == 1:
|
||||
form.comboJobs.setCurrentIndex(setJobIdx)
|
||||
else:
|
||||
form.comboJobs.setCurrentIndex(0)
|
||||
|
||||
def SetupSimulation(self):
|
||||
form = self.taskForm.form
|
||||
self.activeOps = []
|
||||
|
||||
@@ -1767,9 +1767,9 @@ def SetupProperties():
|
||||
return [tup[1] for tup in ObjectSlot.opPropertyDefinitions(False)]
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Slot operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectSlot(obj, name)
|
||||
obj.Proxy = ObjectSlot(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -2124,9 +2124,9 @@ def SetupProperties():
|
||||
return [tup[1] for tup in ObjectSurface.opPropertyDefinitions(False)]
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Surface operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectSurface(obj, name)
|
||||
obj.Proxy = ObjectSurface(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -327,11 +327,11 @@ def SetupProperties():
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a thread milling operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectThreadMilling(obj, name)
|
||||
obj.Proxy = ObjectThreadMilling(obj, name, parentJob)
|
||||
if obj.Proxy:
|
||||
obj.Proxy.findAllHoles(obj)
|
||||
return obj
|
||||
|
||||
@@ -363,9 +363,9 @@ def SetupProperties():
|
||||
return ["Discretize"]
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Vcarve operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
ObjectVcarve(obj, name)
|
||||
obj.Proxy = ObjectVcarve(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -1814,9 +1814,9 @@ def SetupProperties():
|
||||
return [tup[1] for tup in ObjectWaterline.opPropertyDefinitions(False)]
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Waterline operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectWaterline(obj, name)
|
||||
obj.Proxy = ObjectWaterline(obj, name, parentJob)
|
||||
return obj
|
||||
|
||||
@@ -324,7 +324,10 @@ short DrawViewPart::mustExecute() const
|
||||
SeamHidden.isTouched() ||
|
||||
IsoHidden.isTouched() ||
|
||||
IsoCount.isTouched() ||
|
||||
CoarseView.isTouched());
|
||||
CoarseView.isTouched() ||
|
||||
CosmeticVertexes.isTouched() ||
|
||||
CosmeticEdges.isTouched() ||
|
||||
CenterLines.isTouched());
|
||||
}
|
||||
|
||||
if (result) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user