Merge branch 'main' into Draft-fix-handling-of-shapes-in-shape2dview.py
@@ -12,6 +12,7 @@ jobs:
|
||||
build:
|
||||
name: issue metrics
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'FreeCAD'
|
||||
|
||||
steps:
|
||||
|
||||
@@ -19,7 +20,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
# Calculate the first day of the previous month
|
||||
first_day=$(date -d "last month" +%Y-%m-01)
|
||||
first_day=$(date -d "last month" +%Y-%m-15)
|
||||
|
||||
# Calculate the last day of the previous month
|
||||
last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d)
|
||||
@@ -32,7 +33,7 @@ jobs:
|
||||
uses: github/issue-metrics@v2
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }} -reason:"not planned"'
|
||||
SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }}'
|
||||
|
||||
- name: Create issue
|
||||
uses: peter-evans/create-issue-from-file@v4
|
||||
@@ -40,4 +41,4 @@ jobs:
|
||||
title: Monthly issue metrics report
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
content-filepath: ./issue_metrics.md
|
||||
assignees: maxwxyz
|
||||
assignees: maxwxyz
|
||||
|
||||
@@ -3,6 +3,5 @@ channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- conda-devenv
|
||||
- mamba==1.4.9 # NOTE: Pin to highest version supported by the devenv
|
||||
- python==3.11.* # dependencies. If a higher version is installed, a crash
|
||||
# occurs during the downgrade process.
|
||||
- mamba
|
||||
- python==3.11.*
|
||||
|
||||
@@ -73,7 +73,7 @@ dependencies:
|
||||
- graphviz
|
||||
- hdf5
|
||||
- libcxx
|
||||
- mamba==1.4.9
|
||||
- mamba
|
||||
- matplotlib
|
||||
- ninja
|
||||
- numpy
|
||||
|
||||
@@ -281,31 +281,6 @@ but slower response to any scene changes.</string>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>View</cstring>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Line Smoothing</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MSAA 2x</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MSAA 4x</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MSAA 8x</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#ifndef _PreComp_
|
||||
# include <QApplication>
|
||||
# include <QMessageBox>
|
||||
# include <QOffscreenSurface>
|
||||
# include <QOpenGLContext>
|
||||
# include <QSurfaceFormat>
|
||||
#endif
|
||||
|
||||
#include <App/Application.h>
|
||||
@@ -40,42 +43,23 @@ using namespace Gui::Dialog;
|
||||
|
||||
/* TRANSLATOR Gui::Dialog::DlgSettings3DViewImp */
|
||||
|
||||
bool DlgSettings3DViewImp::showMsg = true;
|
||||
|
||||
/**
|
||||
* Constructs a DlgSettings3DViewImp which is a child of 'parent', with the
|
||||
* name 'name' and widget flags set to 'f'
|
||||
*/
|
||||
DlgSettings3DViewImp::DlgSettings3DViewImp(QWidget* parent)
|
||||
: PreferencePage( parent )
|
||||
, ui(new Ui_DlgSettings3DView)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
addAntiAliasing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the object and frees any allocated resources
|
||||
*/
|
||||
DlgSettings3DViewImp::~DlgSettings3DViewImp() = default;
|
||||
|
||||
void DlgSettings3DViewImp::saveSettings()
|
||||
{
|
||||
// must be done as very first because we create a new instance of NavigatorStyle
|
||||
// where we set some attributes afterwards
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/View");
|
||||
|
||||
int index = ui->comboAliasing->currentIndex();
|
||||
hGrp->SetInt("AntiAliasing", index);
|
||||
|
||||
index = ui->renderCache->currentIndex();
|
||||
hGrp->SetInt("RenderCache", index);
|
||||
saveAntiAliasing();
|
||||
saveRenderCache();
|
||||
saveMarkerSize();
|
||||
|
||||
ui->comboTransparentRender->onSave();
|
||||
|
||||
QVariant const &vBoxMarkerSize = ui->boxMarkerSize->itemData(ui->boxMarkerSize->currentIndex());
|
||||
hGrp->SetInt("MarkerSize", vBoxMarkerSize.toInt());
|
||||
|
||||
ui->CheckBox_CornerCoordSystem->onSave();
|
||||
ui->SpinBox_CornerCoordSystemSize->onSave();
|
||||
ui->CheckBox_ShowAxisCross->onSave();
|
||||
@@ -106,23 +90,139 @@ void DlgSettings3DViewImp::loadSettings()
|
||||
ui->sliderIntensity->onRestore();
|
||||
ui->radioPerspective->onRestore();
|
||||
ui->radioOrthographic->onRestore();
|
||||
ui->comboTransparentRender->onRestore();
|
||||
|
||||
loadAntiAliasing();
|
||||
loadRenderCache();
|
||||
loadMarkerSize();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class GLFormatCheck {
|
||||
public:
|
||||
GLFormatCheck() {
|
||||
context.setFormat(format);
|
||||
context.create();
|
||||
offscreen.setFormat(format);
|
||||
offscreen.create();
|
||||
context.makeCurrent(&offscreen);
|
||||
}
|
||||
|
||||
bool testSamples(int num) {
|
||||
QOpenGLFramebufferObjectFormat fboFormat;
|
||||
fboFormat.setAttachment(QOpenGLFramebufferObject::Depth);
|
||||
fboFormat.setSamples(num);
|
||||
QOpenGLFramebufferObject fbo(100, 100, fboFormat); // NOLINT
|
||||
return fbo.format().samples() == num;
|
||||
}
|
||||
|
||||
private:
|
||||
QSurfaceFormat format;
|
||||
QOpenGLContext context;
|
||||
QOffscreenSurface offscreen;
|
||||
};
|
||||
}
|
||||
|
||||
void DlgSettings3DViewImp::addAntiAliasing()
|
||||
{
|
||||
QString none = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "None");
|
||||
QString line = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "Line Smoothing");
|
||||
QString msaa2x = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "MSAA 2x");
|
||||
QString msaa4x = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "MSAA 4x");
|
||||
QString msaa6x = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "MSAA 6x");
|
||||
QString msaa8x = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "MSAA 8x");
|
||||
ui->comboAliasing->clear();
|
||||
ui->comboAliasing->addItem(none, int(Gui::View3DInventorViewer::None));
|
||||
ui->comboAliasing->addItem(line, int(Gui::View3DInventorViewer::Smoothing));
|
||||
|
||||
// Do the samples checks only once
|
||||
static std::vector<std::pair<QString, int>> modes;
|
||||
static bool formatCheck = true;
|
||||
if (formatCheck) {
|
||||
formatCheck = false;
|
||||
|
||||
GLFormatCheck check;
|
||||
// NOLINTBEGIN
|
||||
if (check.testSamples(2)) {
|
||||
modes.emplace_back(msaa2x, int(Gui::View3DInventorViewer::MSAA2x));
|
||||
}
|
||||
if (check.testSamples(4)) {
|
||||
modes.emplace_back(msaa4x, int(Gui::View3DInventorViewer::MSAA4x));
|
||||
}
|
||||
if (check.testSamples(6)) {
|
||||
modes.emplace_back(msaa6x, int(Gui::View3DInventorViewer::MSAA6x));
|
||||
}
|
||||
if (check.testSamples(8)) {
|
||||
modes.emplace_back(msaa8x, int(Gui::View3DInventorViewer::MSAA8x));
|
||||
}
|
||||
// NOLINTEND
|
||||
}
|
||||
|
||||
for (const auto& it : modes) {
|
||||
ui->comboAliasing->addItem(it.first, it.second);
|
||||
}
|
||||
}
|
||||
|
||||
void DlgSettings3DViewImp::saveAntiAliasing()
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/View");
|
||||
|
||||
int index = hGrp->GetInt("AntiAliasing", int(Gui::View3DInventorViewer::None));
|
||||
index = Base::clamp(index, 0, ui->comboAliasing->count()-1);
|
||||
ui->comboAliasing->setCurrentIndex(index);
|
||||
int index = ui->comboAliasing->currentIndex();
|
||||
int aliasing = ui->comboAliasing->itemData(index).toInt();
|
||||
hGrp->SetInt("AntiAliasing", aliasing);
|
||||
}
|
||||
|
||||
void DlgSettings3DViewImp::loadAntiAliasing()
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/View");
|
||||
|
||||
int aliasing = int(hGrp->GetInt("AntiAliasing", int(Gui::View3DInventorViewer::None)));
|
||||
int index = ui->comboAliasing->findData(aliasing);
|
||||
if (index != -1) {
|
||||
ui->comboAliasing->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
// connect after setting current item of the combo box
|
||||
connect(ui->comboAliasing, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
this, &DlgSettings3DViewImp::onAliasingChanged);
|
||||
}
|
||||
|
||||
index = hGrp->GetInt("RenderCache", 0);
|
||||
ui->renderCache->setCurrentIndex(index);
|
||||
void DlgSettings3DViewImp::saveRenderCache()
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/View");
|
||||
|
||||
ui->comboTransparentRender->onRestore();
|
||||
int cache = ui->renderCache->currentIndex();
|
||||
hGrp->SetInt("RenderCache", cache);
|
||||
}
|
||||
|
||||
int const current = hGrp->GetInt("MarkerSize", 9L);
|
||||
void DlgSettings3DViewImp::loadRenderCache()
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/View");
|
||||
|
||||
long cache = hGrp->GetInt("RenderCache", 0);
|
||||
ui->renderCache->setCurrentIndex(int(cache));
|
||||
}
|
||||
|
||||
void DlgSettings3DViewImp::saveMarkerSize()
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/View");
|
||||
|
||||
QVariant const &vBoxMarkerSize = ui->boxMarkerSize->itemData(ui->boxMarkerSize->currentIndex());
|
||||
hGrp->SetInt("MarkerSize", vBoxMarkerSize.toInt());
|
||||
}
|
||||
|
||||
void DlgSettings3DViewImp::loadMarkerSize()
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/View");
|
||||
|
||||
// NOLINTBEGIN
|
||||
int marker = hGrp->GetInt("MarkerSize", 9L);
|
||||
ui->boxMarkerSize->addItem(tr("5px"), QVariant(5));
|
||||
ui->boxMarkerSize->addItem(tr("7px"), QVariant(7));
|
||||
ui->boxMarkerSize->addItem(tr("9px"), QVariant(9));
|
||||
@@ -132,9 +232,12 @@ void DlgSettings3DViewImp::loadSettings()
|
||||
ui->boxMarkerSize->addItem(tr("20px"), QVariant(20));
|
||||
ui->boxMarkerSize->addItem(tr("25px"), QVariant(25));
|
||||
ui->boxMarkerSize->addItem(tr("30px"), QVariant(30));
|
||||
index = ui->boxMarkerSize->findData(QVariant(current));
|
||||
if (index < 0) index = 2;
|
||||
ui->boxMarkerSize->setCurrentIndex(index);
|
||||
marker = ui->boxMarkerSize->findData(QVariant(marker));
|
||||
if (marker < 0) {
|
||||
marker = 2;
|
||||
}
|
||||
ui->boxMarkerSize->setCurrentIndex(marker);
|
||||
// NOLINTEND
|
||||
}
|
||||
|
||||
void DlgSettings3DViewImp::resetSettingsToDefaults()
|
||||
@@ -161,24 +264,29 @@ void DlgSettings3DViewImp::changeEvent(QEvent *e)
|
||||
ui->comboAliasing->blockSignals(true);
|
||||
int aliasing = ui->comboAliasing->currentIndex();
|
||||
ui->retranslateUi(this);
|
||||
addAntiAliasing();
|
||||
ui->comboAliasing->setCurrentIndex(aliasing);
|
||||
ui->comboAliasing->blockSignals(false);
|
||||
}
|
||||
else {
|
||||
QWidget::changeEvent(e);
|
||||
PreferencePage::changeEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
void DlgSettings3DViewImp::onAliasingChanged(int index)
|
||||
{
|
||||
if (index < 0 || !isVisible())
|
||||
if (index < 0 || !isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show this message only once per application session to reduce
|
||||
// annoyance when showing it too often.
|
||||
static bool showMsg = true;
|
||||
if (showMsg) {
|
||||
showMsg = false;
|
||||
QMessageBox::information(this, tr("Anti-aliasing"),
|
||||
tr("Open a new viewer or restart %1 to apply anti-aliasing changes.").arg(qApp->applicationName()));
|
||||
tr("Open a new viewer or restart %1 to apply anti-aliasing changes.")
|
||||
.arg(qApp->applicationName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +56,19 @@ private Q_SLOTS:
|
||||
protected:
|
||||
void changeEvent(QEvent *e) override;
|
||||
|
||||
private:
|
||||
void addAntiAliasing();
|
||||
void saveAntiAliasing();
|
||||
void loadAntiAliasing();
|
||||
void saveRenderCache();
|
||||
void loadRenderCache();
|
||||
void saveMarkerSize();
|
||||
void loadMarkerSize();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui_DlgSettings3DView> ui;
|
||||
static bool showMsg;
|
||||
|
||||
Q_DISABLE_COPY_MOVE(DlgSettings3DViewImp)
|
||||
};
|
||||
|
||||
} // namespace Dialog
|
||||
|
||||
@@ -1963,6 +1963,8 @@ int View3DInventorViewer::getNumSamples()
|
||||
return 2;
|
||||
case View3DInventorViewer::MSAA4x:
|
||||
return 4;
|
||||
case View3DInventorViewer::MSAA6x:
|
||||
return 6;
|
||||
case View3DInventorViewer::MSAA8x:
|
||||
return 8;
|
||||
case View3DInventorViewer::Smoothing:
|
||||
|
||||
@@ -117,11 +117,12 @@ public:
|
||||
*/
|
||||
//@{
|
||||
enum AntiAliasing {
|
||||
None,
|
||||
Smoothing,
|
||||
MSAA2x,
|
||||
MSAA4x,
|
||||
MSAA8x
|
||||
None = 0,
|
||||
Smoothing = 1,
|
||||
MSAA2x = 2,
|
||||
MSAA4x = 3,
|
||||
MSAA6x = 5,
|
||||
MSAA8x = 4
|
||||
};
|
||||
//@}
|
||||
|
||||
|
||||
@@ -124,5 +124,6 @@ WidgetFactorySupplier::WidgetFactorySupplier()
|
||||
new WidgetProducer<Gui::IntSpinBox>;
|
||||
new WidgetProducer<Gui::DoubleSpinBox>;
|
||||
new WidgetProducer<Gui::QuantitySpinBox>;
|
||||
new WidgetProducer<Gui::ExpLineEdit>;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
@@ -265,7 +265,12 @@ def export(exportList, filename, colors=None, preferences=None):
|
||||
ifcfile = ifcopenshell.open(templatefile)
|
||||
ifcfile = exportIFCHelper.writeUnits(ifcfile,preferences["IFC_UNIT"])
|
||||
|
||||
history = ifcfile.by_type("IfcOwnerHistory")[0]
|
||||
history = ifcfile.by_type("IfcOwnerHistory")
|
||||
if history:
|
||||
history = history[0]
|
||||
else:
|
||||
# IFC4 allows to not write any history
|
||||
history = None
|
||||
objectslist = Draft.get_group_contents(exportList, walls=True,
|
||||
addgroups=True)
|
||||
|
||||
@@ -296,7 +301,9 @@ def export(exportList, filename, colors=None, preferences=None):
|
||||
|
||||
if existing_file:
|
||||
project = ifcfile.by_type("IfcProject")[0]
|
||||
context = ifcfile.by_type("IFcGeometricRepresentationContext")[-1]
|
||||
body_contexts = [c for c in ifcfile.by_type("IfcGeometricRepresentationSubContext") if c.ContextIdentifier in ["Body", "Facetation"]]
|
||||
body_contexts.extend([c for c in ifcfile.by_type("IfcGeometricRepresentationContext", include_subtypes=False) if c.ContextType == "Model"])
|
||||
context = body_contexts[0] # we take the first one (subcontext if existing, or context if not)
|
||||
else:
|
||||
contextCreator = exportIFCHelper.ContextCreator(ifcfile, objectslist)
|
||||
context = contextCreator.model_view_subcontext
|
||||
|
||||
@@ -393,14 +393,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="l_use_iterations_param">
|
||||
<property name="text">
|
||||
<string>Time incrementation control parameter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<item row="3" column="2">
|
||||
<widget class="Gui::PrefCheckBox" name="cb_use_iterations_param">
|
||||
<property name="text">
|
||||
<string>Use non ccx defaults</string>
|
||||
@@ -416,6 +416,38 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="l_ccx_max_iterations">
|
||||
<property name="text">
|
||||
<string>Maximum number of iterations</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="Gui::PrefSpinBox" name="sb_ccx_max_iterations">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2000</number>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>AnalysisMaxIterations</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Fem/Ccx</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="l_ccx_initial_time_step">
|
||||
<property name="text">
|
||||
@@ -445,10 +477,10 @@
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.010000000000000</double>
|
||||
<double>0.000000001000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
@@ -465,7 +497,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="3">
|
||||
<widget class="QLabel" name="l_hz_3">
|
||||
<widget class="QLabel" name="l_hz_1">
|
||||
<property name="text">
|
||||
<string>s</string>
|
||||
</property>
|
||||
@@ -484,10 +516,10 @@
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.010000000000000</double>
|
||||
<double>0.000000001000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
@@ -504,20 +536,130 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="3">
|
||||
<widget class="QLabel" name="l_hz_2">
|
||||
<property name="text">
|
||||
<string>s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="l_ccx_minimum_time_step">
|
||||
<property name="text">
|
||||
<string>Time Minimum Step</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<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 row="7" column="2">
|
||||
<widget class="Gui::PrefDoubleSpinBox" name="dsb_ccx_minimum_time_step">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000001000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000010000000000</double>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>AnalysisTimeMinimumStep</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Fem/Ccx</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="3">
|
||||
<widget class="QLabel" name="l_hz_3">
|
||||
<property name="text">
|
||||
<string>s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="l_ccx_maximum_time_step">
|
||||
<property name="text">
|
||||
<string>Time Maximum Step</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<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 row="8" column="2">
|
||||
<widget class="Gui::PrefDoubleSpinBox" name="dsb_ccx_maximum_time_step">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000001000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>AnalysisTimeMaximumStep</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Fem/Ccx</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="3">
|
||||
<widget class="QLabel" name="l_hz_4">
|
||||
<property name="text">
|
||||
<string>s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="l_BeamShellOutput">
|
||||
<property name="text">
|
||||
<string>Beam, shell element 3D output format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<item row="9" column="2">
|
||||
<widget class="Gui::PrefCheckBox" name="cb_BeamShellOutput">
|
||||
<property name="text">
|
||||
<string>3D Output, unchecked for 2D</string>
|
||||
@@ -569,35 +711,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="l_ccx_max_iterations">
|
||||
<property name="text">
|
||||
<string>Maximum number of iterations</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="Gui::PrefSpinBox" name="sb_ccx_max_iterations">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2000</number>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>AnalysisMaxIterations</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Fem/Ccx</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -695,7 +808,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QLabel" name="l_hz_2">
|
||||
<widget class="QLabel" name="l_hz_5">
|
||||
<property name="text">
|
||||
<string>Hz</string>
|
||||
</property>
|
||||
|
||||
@@ -73,6 +73,8 @@ void DlgSettingsFemCcxImp::saveSettings()
|
||||
ui->sb_ccx_max_iterations->onSave(); // Max number of iterations
|
||||
ui->dsb_ccx_initial_time_step->onSave(); // Initial time step
|
||||
ui->dsb_ccx_analysis_time->onSave(); // Analysis time
|
||||
ui->dsb_ccx_minimum_time_step->onSave(); // Minimum time step
|
||||
ui->dsb_ccx_maximum_time_step->onSave(); // Maximum time step
|
||||
|
||||
ui->cb_analysis_type->onSave();
|
||||
ui->cb_BeamShellOutput->onSave(); // Beam shell output 3d or 2d
|
||||
@@ -98,6 +100,8 @@ void DlgSettingsFemCcxImp::loadSettings()
|
||||
ui->sb_ccx_max_iterations->onRestore(); // Max number of iterations
|
||||
ui->dsb_ccx_initial_time_step->onRestore(); // Initial time step
|
||||
ui->dsb_ccx_analysis_time->onRestore(); // Analysis time
|
||||
ui->dsb_ccx_minimum_time_step->onRestore(); // Minimum time step
|
||||
ui->dsb_ccx_maximum_time_step->onRestore(); // Maximum time step
|
||||
|
||||
ui->cb_analysis_type->onRestore();
|
||||
ui->cb_BeamShellOutput->onRestore(); // Beam shell output 3d or 2d
|
||||
|
||||
@@ -145,7 +145,7 @@ def setup(doc=None, solvertype="ccxtools"):
|
||||
# solver_obj.MatrixSolverType = "default"
|
||||
solver_obj.MatrixSolverType = "spooles" # thomas
|
||||
solver_obj.SplitInputWriter = False
|
||||
solver_obj.IterationsThermoMechMaximum = 2000
|
||||
solver_obj.IterationsMaximum = 2000
|
||||
# solver_obj.IterationsControlParameterTimeUse = True # thermomech spine
|
||||
analysis.addObject(solver_obj)
|
||||
|
||||
|
||||
@@ -198,19 +198,23 @@ def add_attributes(obj, ccx_prefs):
|
||||
ehl = ccx_prefs.GetFloat("EigenmodeHighLimit", 1000000.0)
|
||||
obj.EigenmodeHighLimit = (ehl, 0.0, 1000000.0, 10000.0)
|
||||
|
||||
if not hasattr(obj, "IterationsThermoMechMaximum"):
|
||||
help_string_IterationsThermoMechMaximum = (
|
||||
"Maximum Number of thermo mechanical iterations "
|
||||
if not hasattr(obj, "IterationsMaximum"):
|
||||
help_string_IterationsMaximum = (
|
||||
"Maximum Number of iterations "
|
||||
"in each time step before stopping jobs"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyIntegerConstraint",
|
||||
"IterationsThermoMechMaximum",
|
||||
"IterationsMaximum",
|
||||
"Fem",
|
||||
help_string_IterationsThermoMechMaximum
|
||||
help_string_IterationsMaximum
|
||||
)
|
||||
niter = ccx_prefs.GetInt("AnalysisMaxIterations", 200)
|
||||
obj.IterationsThermoMechMaximum = niter
|
||||
obj.IterationsMaximum = niter
|
||||
|
||||
if hasattr(obj, "IterationsThermoMechMaximum"):
|
||||
obj.IterationsMaximum = obj.IterationsThermoMechMaximum
|
||||
obj.removeProperty("IterationsThermoMechMaximum")
|
||||
|
||||
if not hasattr(obj, "BucklingFactors"):
|
||||
obj.addProperty(
|
||||
@@ -242,6 +246,26 @@ def add_attributes(obj, ccx_prefs):
|
||||
eni = ccx_prefs.GetFloat("AnalysisTime", 1.0)
|
||||
obj.TimeEnd = eni
|
||||
|
||||
if not hasattr(obj, "TimeMinimumStep"):
|
||||
obj.addProperty(
|
||||
"App::PropertyFloatConstraint",
|
||||
"TimeMinimumStep",
|
||||
"Fem",
|
||||
"Minimum time step"
|
||||
)
|
||||
mini = ccx_prefs.GetFloat("AnalysisTimeMinimumStep", 0.00001)
|
||||
obj.TimeMinimumStep = mini
|
||||
|
||||
if not hasattr(obj, "TimeMaximumStep"):
|
||||
obj.addProperty(
|
||||
"App::PropertyFloatConstraint",
|
||||
"TimeMaximumStep",
|
||||
"Fem",
|
||||
"Maximum time step"
|
||||
)
|
||||
maxi = ccx_prefs.GetFloat("AnalysisTimeMaximumStep", 1.0)
|
||||
obj.TimeMaximumStep = maxi
|
||||
|
||||
if not hasattr(obj, "ThermoMechSteadyState"):
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
@@ -332,7 +356,7 @@ def add_attributes(obj, ccx_prefs):
|
||||
if not hasattr(obj, "IterationsUserDefinedTimeStepLength"):
|
||||
help_string_IterationsUserDefinedTimeStepLength = (
|
||||
"Set to True to use the user defined time steps. "
|
||||
"The time steps are set with TimeInitialStep and TimeEnd"
|
||||
"They are set with TimeInitialStep, TimeEnd, TimeMinimum and TimeMaximum"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
|
||||
@@ -45,12 +45,11 @@ def write_step_equation(f, ccxwriter):
|
||||
"Analysis type frequency and geometrical nonlinear "
|
||||
"analysis are not allowed together, linear is used instead!\n"
|
||||
)
|
||||
if ccxwriter.solver_obj.IterationsThermoMechMaximum:
|
||||
if ccxwriter.analysis_type == "thermomech":
|
||||
step += ", INC={}".format(ccxwriter.solver_obj.IterationsThermoMechMaximum)
|
||||
if ccxwriter.solver_obj.IterationsMaximum:
|
||||
if ccxwriter.analysis_type == "thermomech" or ccxwriter.analysis_type == "static":
|
||||
step += ", INC={}".format(ccxwriter.solver_obj.IterationsMaximum)
|
||||
elif (
|
||||
ccxwriter.analysis_type == "static"
|
||||
or ccxwriter.analysis_type == "frequency"
|
||||
ccxwriter.analysis_type == "frequency"
|
||||
or ccxwriter.analysis_type == "buckling"
|
||||
):
|
||||
# parameter is for thermomechanical analysis only, see ccx manual *STEP
|
||||
@@ -124,9 +123,11 @@ def write_step_equation(f, ccxwriter):
|
||||
if ccxwriter.analysis_type == "static" or ccxwriter.analysis_type == "check":
|
||||
if ccxwriter.solver_obj.IterationsUserDefinedIncrementations is True \
|
||||
or ccxwriter.solver_obj.IterationsUserDefinedTimeStepLength is True:
|
||||
analysis_parameter = "{},{}".format(
|
||||
analysis_parameter = "{},{},{},{}".format(
|
||||
ccxwriter.solver_obj.TimeInitialStep,
|
||||
ccxwriter.solver_obj.TimeEnd
|
||||
ccxwriter.solver_obj.TimeEnd,
|
||||
ccxwriter.solver_obj.TimeMinimumStep,
|
||||
ccxwriter.solver_obj.TimeMaximumStep
|
||||
)
|
||||
elif ccxwriter.analysis_type == "frequency":
|
||||
if ccxwriter.solver_obj.EigenmodeLowLimit == 0.0 \
|
||||
@@ -140,9 +141,11 @@ def write_step_equation(f, ccxwriter):
|
||||
)
|
||||
elif ccxwriter.analysis_type == "thermomech":
|
||||
# OvG: 1.0 increment, total time 1 for steady state will cut back automatically
|
||||
analysis_parameter = "{},{}".format(
|
||||
analysis_parameter = "{},{},{},{}".format(
|
||||
ccxwriter.solver_obj.TimeInitialStep,
|
||||
ccxwriter.solver_obj.TimeEnd
|
||||
ccxwriter.solver_obj.TimeEnd,
|
||||
ccxwriter.solver_obj.TimeMinimumStep,
|
||||
ccxwriter.solver_obj.TimeMaximumStep
|
||||
)
|
||||
elif ccxwriter.analysis_type == "buckling":
|
||||
analysis_parameter = "{}\n".format(ccxwriter.solver_obj.BucklingFactors)
|
||||
|
||||
@@ -489,7 +489,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ Eedges
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ Eedges
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ Eedges
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -385,7 +385,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ Efaces
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ Efaces
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ Eedges
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ Eedges
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -1562,7 +1562,7 @@ Efaces
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ Efaces
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -359,7 +359,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -359,7 +359,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -377,7 +377,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -38373,7 +38373,7 @@ DEPConstraintContact, INDConstraintContact
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -3401,7 +3401,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -2153,7 +2153,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -18612,7 +18612,7 @@ TIE_DEPConstraintTie, TIE_INDConstraintTie
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -3639,7 +3639,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -10980,7 +10980,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -27634,7 +27634,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -2548,7 +2548,7 @@ Efaces
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -1231,7 +1231,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -20004,7 +20004,7 @@ Evolumes
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP, NLGEOM
|
||||
*STEP, NLGEOM, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -2560,7 +2560,7 @@ Efaces
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -2560,7 +2560,7 @@ Efaces
|
||||
|
||||
***********************************************************
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP
|
||||
*STEP, INC=200
|
||||
*STATIC
|
||||
|
||||
|
||||
|
||||
@@ -7081,7 +7081,7 @@ Nall,273.0
|
||||
** At least one step is needed to run an CalculiX analysis of FreeCAD
|
||||
*STEP, INC=2000
|
||||
*COUPLED TEMPERATURE-DISPLACEMENT, SOLVER=SPOOLES, STEADY STATE
|
||||
1.0,1.0
|
||||
1.0,1.0,1e-05,1.0
|
||||
|
||||
***********************************************************
|
||||
** Fixed Constraints
|
||||
|
||||
@@ -36,22 +36,24 @@
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
|
||||
|
||||
namespace PartGui {
|
||||
namespace PartGui
|
||||
{
|
||||
|
||||
class Ui_DlgProjectionOnSurface;
|
||||
class Ui_DlgProjectionOnSurface;
|
||||
|
||||
namespace Ui {
|
||||
class DlgProjectionOnSurface;
|
||||
}
|
||||
namespace Ui
|
||||
{
|
||||
class DlgProjectionOnSurface;
|
||||
}
|
||||
|
||||
class DlgProjectionOnSurface : public QWidget,
|
||||
public Gui::SelectionObserver,
|
||||
public App::DocumentObserver
|
||||
class DlgProjectionOnSurface: public QWidget,
|
||||
public Gui::SelectionObserver,
|
||||
public App::DocumentObserver
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DlgProjectionOnSurface(QWidget *parent = nullptr);
|
||||
explicit DlgProjectionOnSurface(QWidget* parent = nullptr);
|
||||
~DlgProjectionOnSurface() override;
|
||||
|
||||
void apply();
|
||||
@@ -74,57 +76,64 @@ private:
|
||||
void onDoubleSpinBoxSolidDepthValueChanged(double arg1);
|
||||
|
||||
private:
|
||||
struct SShapeStore
|
||||
{
|
||||
TopoDS_Shape inputShape;
|
||||
TopoDS_Face surfaceToProject;
|
||||
gp_Dir aProjectionDir;
|
||||
TopoDS_Face aFace;
|
||||
TopoDS_Edge aEdge;
|
||||
std::vector<TopoDS_Wire> aWireVec;
|
||||
std::vector<TopoDS_Wire> aProjectedWireVec;
|
||||
std::vector<TopoDS_Edge> aProjectedEdgeVec;
|
||||
std::vector<TopoDS_Wire> aProjectedWireInParametricSpaceVec;
|
||||
TopoDS_Face aProjectedFace;
|
||||
TopoDS_Shape aProjectedSolid;
|
||||
Part::Feature* partFeature = nullptr;
|
||||
std::string partName;
|
||||
bool is_selectable = false;
|
||||
long transparency = 0;
|
||||
double extrudeValue = 0.0;
|
||||
};
|
||||
|
||||
struct SShapeStore
|
||||
{
|
||||
TopoDS_Shape inputShape;
|
||||
TopoDS_Face surfaceToProject;
|
||||
gp_Dir aProjectionDir;
|
||||
TopoDS_Face aFace;
|
||||
TopoDS_Edge aEdge;
|
||||
std::vector<TopoDS_Wire> aWireVec;
|
||||
std::vector<TopoDS_Wire> aProjectedWireVec;
|
||||
std::vector<TopoDS_Edge> aProjectedEdgeVec;
|
||||
std::vector<TopoDS_Wire> aProjectedWireInParametricSpaceVec;
|
||||
TopoDS_Face aProjectedFace;
|
||||
TopoDS_Shape aProjectedSolid;
|
||||
Part::Feature* partFeature = nullptr;
|
||||
std::string partName;
|
||||
bool is_selectable = false;
|
||||
long transparency = 0;
|
||||
float extrudeValue = 0.0f;
|
||||
};
|
||||
|
||||
//from Gui::SelectionObserver
|
||||
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
|
||||
// from Gui::SelectionObserver
|
||||
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
|
||||
|
||||
|
||||
void get_camera_direction();
|
||||
void store_current_selected_parts(std::vector<SShapeStore>& iStoreVec, const unsigned int iColor);
|
||||
bool store_part_in_vector(SShapeStore& iCurrentShape, std::vector<SShapeStore>& iStoreVec);
|
||||
void create_projection_wire(std::vector<SShapeStore>& iCurrentShape);
|
||||
TopoDS_Shape create_compound(const std::vector<SShapeStore>& iShapeVec);
|
||||
void show_projected_shapes(const std::vector<SShapeStore>& iShapeStoreVec);
|
||||
void disable_ui_elements(const std::vector<QWidget*>& iObjectVec, QWidget* iExceptThis);
|
||||
void enable_ui_elements(const std::vector<QWidget*>& iObjectVec, QWidget* iExceptThis);
|
||||
void higlight_object(Part::Feature* iCurrentObject, const std::string& iShapeName, bool iHighlight, const unsigned int iColor);
|
||||
void get_all_wire_from_face(SShapeStore& ioCurrentSahpe);
|
||||
void create_projection_face_from_wire(std::vector<SShapeStore>& iCurrentShape);
|
||||
TopoDS_Wire sort_and_heal_wire(const TopoDS_Shape& iShape, const TopoDS_Face& iFaceToProject);
|
||||
TopoDS_Wire sort_and_heal_wire(const std::vector<TopoDS_Edge>& iEdgeVec, const TopoDS_Face& iFaceToProject);
|
||||
void create_face_extrude(std::vector<SShapeStore>& iCurrentShape);
|
||||
void store_wire_in_vector(const SShapeStore& iCurrentShape, const TopoDS_Shape& iParentShape, std::vector<SShapeStore>& iStoreVec, const unsigned int iColor);
|
||||
void set_xyz_dir_spinbox(QDoubleSpinBox* icurrentSpinBox);
|
||||
void transform_shape_to_global_position(TopoDS_Shape& ioShape, Part::Feature* iPart);
|
||||
void get_camera_direction();
|
||||
void store_current_selected_parts(std::vector<SShapeStore>& iStoreVec,
|
||||
unsigned int iColor);
|
||||
bool store_part_in_vector(SShapeStore& iCurrentShape, std::vector<SShapeStore>& iStoreVec);
|
||||
void create_projection_wire(std::vector<SShapeStore>& iCurrentShape);
|
||||
TopoDS_Shape create_compound(const std::vector<SShapeStore>& iShapeVec);
|
||||
void show_projected_shapes(const std::vector<SShapeStore>& iShapeStoreVec);
|
||||
void disable_ui_elements(const std::vector<QWidget*>& iObjectVec, QWidget* iExceptThis);
|
||||
void enable_ui_elements(const std::vector<QWidget*>& iObjectVec, QWidget* iExceptThis);
|
||||
void higlight_object(Part::Feature* iCurrentObject,
|
||||
const std::string& iShapeName,
|
||||
bool iHighlight,
|
||||
unsigned int iColor);
|
||||
void get_all_wire_from_face(SShapeStore& ioCurrentSahpe);
|
||||
void create_projection_face_from_wire(std::vector<SShapeStore>& iCurrentShape);
|
||||
TopoDS_Wire sort_and_heal_wire(const TopoDS_Shape& iShape, const TopoDS_Face& iFaceToProject);
|
||||
TopoDS_Wire sort_and_heal_wire(const std::vector<TopoDS_Edge>& iEdgeVec,
|
||||
const TopoDS_Face& iFaceToProject);
|
||||
void create_face_extrude(std::vector<SShapeStore>& iCurrentShape);
|
||||
void store_wire_in_vector(const SShapeStore& iCurrentShape,
|
||||
const TopoDS_Shape& iParentShape,
|
||||
std::vector<SShapeStore>& iStoreVec,
|
||||
unsigned int iColor);
|
||||
void set_xyz_dir_spinbox(QDoubleSpinBox* icurrentSpinBox);
|
||||
void transform_shape_to_global_position(TopoDS_Shape& ioShape, Part::Feature* iPart);
|
||||
|
||||
private:
|
||||
/** Checks if the given document is about to be closed */
|
||||
void slotDeletedDocument(const App::Document& Doc) override;
|
||||
/** Checks if the given object is about to be removed. */
|
||||
void slotDeletedObject(const App::DocumentObject& Obj) override;
|
||||
/** Checks if the given document is about to be closed */
|
||||
void slotDeletedDocument(const App::Document& Doc) override;
|
||||
/** Checks if the given object is about to be removed. */
|
||||
void slotDeletedObject(const App::DocumentObject& Obj) override;
|
||||
|
||||
private:
|
||||
Ui::DlgProjectionOnSurface *ui;
|
||||
Ui::DlgProjectionOnSurface* ui;
|
||||
std::vector<SShapeStore> m_shapeVec;
|
||||
std::vector<SShapeStore> m_projectionSurfaceVec;
|
||||
|
||||
@@ -134,9 +143,9 @@ private:
|
||||
std::vector<QWidget*> m_guiObjectVec;
|
||||
|
||||
const QString m_projectionObjectName;
|
||||
Part::Feature* m_projectionObject;
|
||||
App::Document* m_partDocument;
|
||||
float m_lastDepthVal;
|
||||
Part::Feature* m_projectionObject = nullptr;
|
||||
App::Document* m_partDocument = nullptr;
|
||||
double m_lastDepthVal;
|
||||
|
||||
class EdgeSelection;
|
||||
EdgeSelection* filterEdge;
|
||||
@@ -145,28 +154,28 @@ private:
|
||||
FaceSelection* filterFace;
|
||||
};
|
||||
|
||||
class TaskProjectionOnSurface : public Gui::TaskView::TaskDialog
|
||||
class TaskProjectionOnSurface: public Gui::TaskView::TaskDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TaskProjectionOnSurface();
|
||||
TaskProjectionOnSurface();
|
||||
|
||||
public:
|
||||
bool accept() override;
|
||||
bool reject() override;
|
||||
void clicked(int) override;
|
||||
bool accept() override;
|
||||
bool reject() override;
|
||||
void clicked(int id) override;
|
||||
|
||||
QDialogButtonBox::StandardButtons getStandardButtons() const override
|
||||
{
|
||||
return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
|
||||
}
|
||||
QDialogButtonBox::StandardButtons getStandardButtons() const override
|
||||
{
|
||||
return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
|
||||
}
|
||||
|
||||
private:
|
||||
DlgProjectionOnSurface* widget;
|
||||
Gui::TaskView::TaskBox* taskbox;
|
||||
DlgProjectionOnSurface* widget = nullptr;
|
||||
Gui::TaskView::TaskBox* taskbox = nullptr;
|
||||
};
|
||||
|
||||
|
||||
} // namespace PartGui
|
||||
#endif // PARTGUI_DLGPROJECTIONONSURFACE_H
|
||||
} // namespace PartGui
|
||||
#endif // PARTGUI_DLGPROJECTIONONSURFACE_H
|
||||
|
||||
@@ -232,79 +232,12 @@ App::DocumentObjectExecReturn* Helix::execute()
|
||||
|
||||
// generate the helix path
|
||||
TopoDS_Shape path = generateHelixPath();
|
||||
TopoDS_Shape auxpath = generateHelixPath(1.0);
|
||||
|
||||
// Use MakePipe for frenet ( Angle is 0 ) calculations, faster than MakePipeShell
|
||||
if ( Angle.getValue() == 0 ) {
|
||||
TopoDS_Shape face = Part::FaceMakerCheese::makeFace(wires);
|
||||
face.Move(invObjLoc);
|
||||
BRepOffsetAPI_MakePipe mkPS(TopoDS::Wire(path), face, GeomFill_Trihedron::GeomFill_IsFrenet, Standard_False);
|
||||
mkPS.Build();
|
||||
result = mkPS.Shape();
|
||||
} else {
|
||||
std::vector<std::vector<TopoDS_Wire>> wiresections;
|
||||
for (TopoDS_Wire& wire : wires)
|
||||
wiresections.emplace_back(1, wire);
|
||||
|
||||
//build all shells
|
||||
std::vector<TopoDS_Shape> shells;
|
||||
std::vector<TopoDS_Wire> frontwires, backwires;
|
||||
for (std::vector<TopoDS_Wire>& wires : wiresections) {
|
||||
|
||||
BRepOffsetAPI_MakePipeShell mkPS(TopoDS::Wire(path));
|
||||
|
||||
// Frenet mode doesn't place the face quite right on an angled helix, so
|
||||
// use the auxiliary spine to force that.
|
||||
mkPS.SetMode(TopoDS::Wire(auxpath), true); // this is for auxiliary
|
||||
|
||||
for (TopoDS_Wire& wire : wires) {
|
||||
wire.Move(invObjLoc);
|
||||
mkPS.Add(wire);
|
||||
}
|
||||
|
||||
if (!mkPS.IsReady())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Could not build"));
|
||||
mkPS.Build();
|
||||
|
||||
shells.push_back(mkPS.Shape());
|
||||
|
||||
if (!mkPS.Shape().Closed()) {
|
||||
// // shell is not closed - use simulate to get the end wires
|
||||
TopTools_ListOfShape sim;
|
||||
mkPS.Simulate(2, sim);
|
||||
|
||||
frontwires.push_back(TopoDS::Wire(sim.First()));
|
||||
backwires.push_back(TopoDS::Wire(sim.Last()));
|
||||
}
|
||||
BRepBuilderAPI_MakeSolid mkSolid;
|
||||
|
||||
if (!frontwires.empty()) {
|
||||
// build the end faces, sew the shell and build the final solid
|
||||
TopoDS_Shape front = Part::FaceMakerCheese::makeFace(frontwires);
|
||||
TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires);
|
||||
|
||||
BRepBuilderAPI_Sewing sewer;
|
||||
sewer.SetTolerance(Precision::Confusion());
|
||||
sewer.Add(front);
|
||||
sewer.Add(back);
|
||||
|
||||
for (TopoDS_Shape& s : shells)
|
||||
sewer.Add(s);
|
||||
sewer.Perform();
|
||||
mkSolid.Add(TopoDS::Shell(sewer.SewedShape()));
|
||||
}
|
||||
else {
|
||||
// shells are already closed - add them directly
|
||||
for (TopoDS_Shape& s : shells) {
|
||||
mkSolid.Add(TopoDS::Shell(s));
|
||||
}
|
||||
}
|
||||
if (!mkSolid.IsDone())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Result is not a solid"));
|
||||
|
||||
result = mkSolid.Shape();
|
||||
}
|
||||
}
|
||||
TopoDS_Shape face = Part::FaceMakerCheese::makeFace(wires);
|
||||
face.Move(invObjLoc);
|
||||
BRepOffsetAPI_MakePipe mkPS(TopoDS::Wire(path), face, GeomFill_Trihedron::GeomFill_IsFrenet, Standard_False);
|
||||
mkPS.Build();
|
||||
result = mkPS.Shape();
|
||||
|
||||
BRepClass3d_SolidClassifier SC(result);
|
||||
SC.PerformInfinitePoint(Precision::Confusion());
|
||||
@@ -403,7 +336,7 @@ void Helix::updateAxis()
|
||||
Axis.setValue(dir.x, dir.y, dir.z);
|
||||
}
|
||||
|
||||
TopoDS_Shape Helix::generateHelixPath(double startOffset0)
|
||||
TopoDS_Shape Helix::generateHelixPath()
|
||||
{
|
||||
double turns = Turns.getValue();
|
||||
double height = Height.getValue();
|
||||
@@ -449,7 +382,7 @@ TopoDS_Shape Helix::generateHelixPath(double startOffset0)
|
||||
bool turned = axisOffset < 0;
|
||||
// since the factor does not only change the radius but also the path position, we must shift its offset back
|
||||
// using the square of the factor
|
||||
double startOffset = 10000.0 * std::fabs(startOffset0 + profileCenter * axisVector - baseVector * axisVector);
|
||||
double startOffset = 10000.0 * std::fabs(baseVector * axisVector);
|
||||
|
||||
if (radius < Precision::Confusion()) {
|
||||
// in this case ensure that axis is not in the sketch plane
|
||||
@@ -466,8 +399,7 @@ TopoDS_Shape Helix::generateHelixPath(double startOffset0)
|
||||
radiusTop = radius + height * tan(Base::toRadians(angle));
|
||||
|
||||
//build the helix path
|
||||
//TopoShape helix = TopoShape().makeLongHelix(pitch, height, radius, angle, leftHanded);
|
||||
TopoDS_Shape path = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, 1, leftHanded);
|
||||
TopoDS_Shape path = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, 1000, leftHanded);
|
||||
|
||||
/*
|
||||
* The helix wire is created with the axis coinciding with z-axis and the start point at (radius, 0, 0)
|
||||
|
||||
@@ -80,7 +80,7 @@ protected:
|
||||
void updateAxis();
|
||||
|
||||
/// generate helix and move it to the right location.
|
||||
TopoDS_Shape generateHelixPath(double startOffset0 = 0.0);
|
||||
TopoDS_Shape generateHelixPath();
|
||||
|
||||
// project shape on plane. Used for detecting self intersection.
|
||||
TopoDS_Shape projectShape(const TopoDS_Shape& input, const gp_Ax2& plane);
|
||||
|
||||
@@ -87,15 +87,15 @@ class TestHelix(unittest.TestCase):
|
||||
helix.Angle = 0
|
||||
helix.Mode = 1
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(helix.Shape.Volume, 78.95687956849457,places=5)
|
||||
self.assertAlmostEqual(helix.Shape.Volume, 78.957,places=3)
|
||||
|
||||
helix.Angle = 25
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(helix.Shape.Volume, 134.17450779511307,places=5)
|
||||
self.assertAlmostEqual(helix.Shape.Volume, 134.17,places=2)
|
||||
|
||||
profileSketch.addGeometry(Part.Circle(FreeCAD.Vector(2, 0, 0), FreeCAD.Vector(0,0,1), 0.5) )
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(helix.Shape.Volume, 100.63088079046352,places=5)
|
||||
self.assertAlmostEqual(helix.Shape.Volume, 100.63,places=2)
|
||||
|
||||
|
||||
def testRectangle(self):
|
||||
@@ -174,7 +174,7 @@ class TestHelix(unittest.TestCase):
|
||||
helix.Mode = 0
|
||||
helix.Reversed = True
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(helix.Shape.Volume, 388285.4117047924,places=5)
|
||||
self.assertAlmostEqual(helix.Shape.Volume/1e5, 3.8828,places=4)
|
||||
|
||||
def tearDown(self):
|
||||
FreeCAD.closeDocument("PartDesignTestHelix")
|
||||
|
||||
@@ -4227,105 +4227,298 @@ bool SketchObject::isCarbonCopyAllowed(App::Document* pDoc, App::DocumentObject*
|
||||
}
|
||||
|
||||
int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
Sketcher::PointPos refPosId /*=Sketcher::PointPos::none*/)
|
||||
Sketcher::PointPos refPosId /*=Sketcher::PointPos::none*/,
|
||||
bool addSymmetryConstraints /*= false*/)
|
||||
{
|
||||
// no need to check input data validity as this is an sketchobject managed operation.
|
||||
Base::StateLocker lock(managedoperation, true);
|
||||
|
||||
const std::vector<Part::Geometry*>& geovals = getInternalGeometry();
|
||||
std::vector<Part::Geometry*> newgeoVals(geovals);
|
||||
|
||||
const std::vector<Constraint*>& constrvals = this->Constraints.getValues();
|
||||
std::vector<Constraint*> newconstrVals(constrvals);
|
||||
|
||||
newgeoVals.reserve(geovals.size() + geoIdList.size());
|
||||
|
||||
int cgeoid = getHighestCurveIndex() + 1;
|
||||
|
||||
std::map<int, int> geoIdMap;
|
||||
std::map<int, bool> isStartEndInverted;
|
||||
|
||||
// Find out if reference is aligned with V or H axis,
|
||||
// if so we can keep Vertical and Horizontal constraints in the mirrored geometry.
|
||||
bool refIsLine = refPosId == Sketcher::PointPos::none;
|
||||
bool refIsAxisAligned = false;
|
||||
if (refGeoId == Sketcher::GeoEnum::VAxis || refGeoId == Sketcher::GeoEnum::HAxis) {
|
||||
if (refGeoId == Sketcher::GeoEnum::VAxis || refGeoId == Sketcher::GeoEnum::HAxis || !refIsLine) {
|
||||
refIsAxisAligned = true;
|
||||
}
|
||||
else {
|
||||
for (std::vector<Constraint*>::const_iterator it = constrvals.begin();
|
||||
it != constrvals.end();
|
||||
++it) {
|
||||
Constraint* constr = *(it);
|
||||
for (auto* constr : constrvals) {
|
||||
if (constr->First == refGeoId
|
||||
&& (constr->Type == Sketcher::Vertical || constr->Type == Sketcher::Horizontal))
|
||||
&& (constr->Type == Sketcher::Vertical || constr->Type == Sketcher::Horizontal)){
|
||||
refIsAxisAligned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reference is a line
|
||||
if (refPosId == Sketcher::PointPos::none) {
|
||||
const Part::Geometry* georef = getGeometry(refGeoId);
|
||||
if (georef->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
||||
Base::Console().Error("Reference for symmetric is neither a point nor a line.\n");
|
||||
return -1;
|
||||
// add the geometry
|
||||
std::map<int, int> geoIdMap;
|
||||
std::map<int, bool> isStartEndInverted;
|
||||
std::vector<Part::Geometry*> newgeoVals(getInternalGeometry());
|
||||
std::vector<Part::Geometry*> symmetricVals = getSymmetric(geoIdList, geoIdMap, isStartEndInverted, refGeoId, refPosId);
|
||||
newgeoVals.insert(newgeoVals.end(), symmetricVals.begin(), symmetricVals.end());
|
||||
|
||||
// Block acceptGeometry in OnChanged to avoid unnecessary checks and updates
|
||||
{
|
||||
Base::StateLocker lock(internaltransaction, true);
|
||||
Geometry.setValues(std::move(newgeoVals));
|
||||
|
||||
|
||||
for (auto* constr : constrvals) {
|
||||
// we look in the map, because we might have skipped internal alignment geometry
|
||||
auto fit = geoIdMap.find(constr->First);
|
||||
|
||||
if (fit != geoIdMap.end()) {// if First of constraint is in geoIdList
|
||||
if (addSymmetryConstraints && constr->Type != Sketcher::InternalAlignment) {
|
||||
// if we are making symmetric constraints, then we don't want to copy all constraints
|
||||
continue;
|
||||
}
|
||||
|
||||
if (constr->Second == GeoEnum::GeoUndef /*&& constr->Third == GeoEnum::GeoUndef*/) {
|
||||
if (refIsAxisAligned) {
|
||||
// in this case we want to keep the Vertical, Horizontal constraints
|
||||
// DistanceX ,and DistanceY constraints should also be possible to keep in
|
||||
// this case, but keeping them causes segfault, not sure why.
|
||||
|
||||
if (constr->Type != Sketcher::DistanceX
|
||||
&& constr->Type != Sketcher::DistanceY) {
|
||||
Constraint* constNew = constr->copy();
|
||||
constNew->First = fit->second;
|
||||
newconstrVals.push_back(constNew);
|
||||
}
|
||||
}
|
||||
else if (constr->Type != Sketcher::DistanceX
|
||||
&& constr->Type != Sketcher::DistanceY
|
||||
&& constr->Type != Sketcher::Vertical
|
||||
&& constr->Type != Sketcher::Horizontal) {
|
||||
// this includes all non-directional single GeoId constraints, as radius,
|
||||
// diameter, weight,...
|
||||
|
||||
Constraint* constNew = constr->copy();
|
||||
constNew->First = fit->second;
|
||||
newconstrVals.push_back(constNew);
|
||||
}
|
||||
}
|
||||
else {// other geoids intervene in this constraint
|
||||
|
||||
auto sit = geoIdMap.find(constr->Second);
|
||||
|
||||
if (sit != geoIdMap.end()) {// Second is also in the list
|
||||
|
||||
if (constr->Third == GeoEnum::GeoUndef) {
|
||||
if (constr->Type == Sketcher::Coincident
|
||||
|| constr->Type == Sketcher::Perpendicular
|
||||
|| constr->Type == Sketcher::Parallel
|
||||
|| constr->Type == Sketcher::Tangent
|
||||
|| constr->Type == Sketcher::Distance
|
||||
|| constr->Type == Sketcher::Equal || constr->Type == Sketcher::Angle
|
||||
|| constr->Type == Sketcher::PointOnObject
|
||||
|| constr->Type == Sketcher::InternalAlignment) {
|
||||
Constraint* constNew = constr->copy();
|
||||
|
||||
constNew->First = fit->second;
|
||||
constNew->Second = sit->second;
|
||||
if (isStartEndInverted[constr->First]) {
|
||||
if (constr->FirstPos == Sketcher::PointPos::start)
|
||||
constNew->FirstPos = Sketcher::PointPos::end;
|
||||
else if (constr->FirstPos == Sketcher::PointPos::end)
|
||||
constNew->FirstPos = Sketcher::PointPos::start;
|
||||
}
|
||||
if (isStartEndInverted[constr->Second]) {
|
||||
if (constr->SecondPos == Sketcher::PointPos::start)
|
||||
constNew->SecondPos = Sketcher::PointPos::end;
|
||||
else if (constr->SecondPos == Sketcher::PointPos::end)
|
||||
constNew->SecondPos = Sketcher::PointPos::start;
|
||||
}
|
||||
|
||||
if (constNew->Type == Tangent || constNew->Type == Perpendicular)
|
||||
AutoLockTangencyAndPerpty(constNew, true);
|
||||
|
||||
if ((constr->Type == Sketcher::Angle)
|
||||
&& (refPosId == Sketcher::PointPos::none)) {
|
||||
constNew->setValue(-constr->getValue());
|
||||
}
|
||||
|
||||
newconstrVals.push_back(constNew);
|
||||
}
|
||||
}
|
||||
else {// three GeoIds intervene in constraint
|
||||
auto tit = geoIdMap.find(constr->Third);
|
||||
|
||||
if (tit != geoIdMap.end()) {// Third is also in the list
|
||||
Constraint* constNew = constr->copy();
|
||||
constNew->First = fit->second;
|
||||
constNew->Second = sit->second;
|
||||
constNew->Third = tit->second;
|
||||
if (isStartEndInverted[constr->First]) {
|
||||
if (constr->FirstPos == Sketcher::PointPos::start)
|
||||
constNew->FirstPos = Sketcher::PointPos::end;
|
||||
else if (constr->FirstPos == Sketcher::PointPos::end)
|
||||
constNew->FirstPos = Sketcher::PointPos::start;
|
||||
}
|
||||
if (isStartEndInverted[constr->Second]) {
|
||||
if (constr->SecondPos == Sketcher::PointPos::start)
|
||||
constNew->SecondPos = Sketcher::PointPos::end;
|
||||
else if (constr->SecondPos == Sketcher::PointPos::end)
|
||||
constNew->SecondPos = Sketcher::PointPos::start;
|
||||
}
|
||||
if (isStartEndInverted[constr->Third]) {
|
||||
if (constr->ThirdPos == Sketcher::PointPos::start)
|
||||
constNew->ThirdPos = Sketcher::PointPos::end;
|
||||
else if (constr->ThirdPos == Sketcher::PointPos::end)
|
||||
constNew->ThirdPos = Sketcher::PointPos::start;
|
||||
}
|
||||
newconstrVals.push_back(constNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Part::GeomLineSegment* refGeoLine = static_cast<const Part::GeomLineSegment*>(georef);
|
||||
if (addSymmetryConstraints) {
|
||||
auto createSymConstr = [&]
|
||||
(int first, int second, Sketcher::PointPos firstPos, Sketcher::PointPos secondPos) {
|
||||
auto symConstr = new Constraint();
|
||||
symConstr->Type = Symmetric;
|
||||
symConstr->First = first;
|
||||
symConstr->Second = second;
|
||||
symConstr->Third = refGeoId;
|
||||
symConstr->FirstPos = firstPos;
|
||||
symConstr->SecondPos = secondPos;
|
||||
symConstr->ThirdPos = refPosId;
|
||||
newconstrVals.push_back(symConstr);
|
||||
};
|
||||
auto createEqualityConstr = [&]
|
||||
(int first, int second) {
|
||||
auto symConstr = new Constraint();
|
||||
symConstr->Type = Equal;
|
||||
symConstr->First = first;
|
||||
symConstr->Second = second;
|
||||
newconstrVals.push_back(symConstr);
|
||||
};
|
||||
|
||||
for (auto geoIdPair : geoIdMap) {
|
||||
int geoId1 = geoIdPair.first;
|
||||
int geoId2 = geoIdPair.second;
|
||||
const Part::Geometry* geo = getGeometry(geoId1);
|
||||
|
||||
if (geo->is<Part::GeomLineSegment>()) {
|
||||
auto gf = GeometryFacade::getFacade(geo);
|
||||
if (!gf->isInternalAligned()) {
|
||||
// Note internal aligned lines (ellipse, parabola, hyperbola) are causing redundant constraint.
|
||||
createSymConstr(geoId1, geoId2, PointPos::start, isStartEndInverted[geoId1] ? PointPos::end : PointPos::start);
|
||||
createSymConstr(geoId1, geoId2, PointPos::end, isStartEndInverted[geoId1] ? PointPos::start : PointPos::end);
|
||||
}
|
||||
}
|
||||
else if (geo->is<Part::GeomCircle>() || geo->is<Part::GeomEllipse>()) {
|
||||
createEqualityConstr(geoId1, geoId2);
|
||||
createSymConstr(geoId1, geoId2, PointPos::mid, PointPos::mid);
|
||||
}
|
||||
else if (geo->is<Part::GeomArcOfCircle>()
|
||||
|| geo->is<Part::GeomArcOfEllipse>()
|
||||
|| geo->is<Part::GeomArcOfHyperbola>()
|
||||
|| geo->is<Part::GeomArcOfParabola>()) {
|
||||
createEqualityConstr(geoId1, geoId2);
|
||||
createSymConstr(geoId1, geoId2, PointPos::start, isStartEndInverted[geoId1] ? PointPos::end : PointPos::start);
|
||||
createSymConstr(geoId1, geoId2, PointPos::end, isStartEndInverted[geoId1] ? PointPos::start : PointPos::end);
|
||||
}
|
||||
else if (geo->is<Part::GeomPoint>()) {
|
||||
auto gf = GeometryFacade::getFacade(geo);
|
||||
if (!gf->isInternalAligned()) {
|
||||
createSymConstr(geoId1, geoId2, PointPos::start, PointPos::start);
|
||||
}
|
||||
}
|
||||
// Note bspline has symmetric by the internal aligned circles.
|
||||
}
|
||||
}
|
||||
|
||||
if (newconstrVals.size() > constrvals.size()){
|
||||
Constraints.setValues(std::move(newconstrVals));
|
||||
}
|
||||
}
|
||||
|
||||
// we delayed update, so trigger it now.
|
||||
// Update geometry indices and rebuild vertexindex now via onChanged, so that
|
||||
// ViewProvider::UpdateData is triggered.
|
||||
Geometry.touch();
|
||||
|
||||
return Geometry.getSize() - 1;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Part::Geometry*> SketchObject::getSymmetric(const std::vector<int>& geoIdList,
|
||||
std::map<int, int>& geoIdMap,
|
||||
std::map<int, bool>& isStartEndInverted,
|
||||
int refGeoId,
|
||||
Sketcher::PointPos refPosId)
|
||||
{
|
||||
std::vector<Part::Geometry*> symmetricVals;
|
||||
bool refIsLine = refPosId == Sketcher::PointPos::none;
|
||||
int cgeoid = getHighestCurveIndex() + 1;
|
||||
|
||||
auto shouldCopyGeometry = [&](auto* geo, int geoId) -> bool {
|
||||
auto gf = GeometryFacade::getFacade(geo);
|
||||
if (gf->isInternalAligned()) {
|
||||
// only add if the corresponding geometry it defines is also in the list.
|
||||
int definedGeo = GeoEnum::GeoUndef;
|
||||
for (auto c : Constraints.getValues()) {
|
||||
if (c->Type == Sketcher::InternalAlignment && c->First == geoId) {
|
||||
definedGeo = c->Second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Return true if definedGeo is in geoIdList, false otherwise
|
||||
return std::find(geoIdList.begin(), geoIdList.end(), definedGeo) != geoIdList.end();
|
||||
}
|
||||
// Return true if not internal aligned, indicating it should always be copied
|
||||
return true;
|
||||
};
|
||||
|
||||
if (refIsLine) {
|
||||
const Part::Geometry* georef = getGeometry(refGeoId);
|
||||
if (!georef->is<Part::GeomLineSegment>()) {
|
||||
Base::Console().Error("Reference for symmetric is neither a point nor a line.\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* refGeoLine = static_cast<const Part::GeomLineSegment*>(georef);
|
||||
// line
|
||||
Base::Vector3d refstart = refGeoLine->getStartPoint();
|
||||
Base::Vector3d vectline = refGeoLine->getEndPoint() - refstart;
|
||||
|
||||
for (std::vector<int>::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) {
|
||||
const Part::Geometry* geo = getGeometry(*it);
|
||||
for (auto geoId : geoIdList) {
|
||||
const Part::Geometry* geo = getGeometry(geoId);
|
||||
Part::Geometry* geosym;
|
||||
|
||||
auto gf = GeometryFacade::getFacade(geo);
|
||||
|
||||
if (gf->isInternalAligned()) {
|
||||
// only add this geometry if the corresponding geometry it defines is also in the
|
||||
// list.
|
||||
int definedGeo = GeoEnum::GeoUndef;
|
||||
|
||||
for (auto c : Constraints.getValues()) {
|
||||
if (c->Type == Sketcher::InternalAlignment && c->First == *it) {
|
||||
definedGeo = c->Second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (std::find(geoIdList.begin(), geoIdList.end(), definedGeo) != geoIdList.end())
|
||||
geosym = geo->copy();
|
||||
else {
|
||||
// we should not mirror internal alignment geometry, unless the element they
|
||||
// define is also mirrored
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
geosym = geo->copy();
|
||||
if (!shouldCopyGeometry(geo, geoId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
geosym = geo->copy();
|
||||
|
||||
// Handle Geometry
|
||||
if (geosym->is<Part::GeomLineSegment>()) {
|
||||
Part::GeomLineSegment* geosymline = static_cast<Part::GeomLineSegment*>(geosym);
|
||||
auto* geosymline = static_cast<Part::GeomLineSegment*>(geosym);
|
||||
Base::Vector3d sp = geosymline->getStartPoint();
|
||||
Base::Vector3d ep = geosymline->getEndPoint();
|
||||
|
||||
geosymline->setPoints(
|
||||
sp + 2.0 * (sp.Perpendicular(refGeoLine->getStartPoint(), vectline) - sp),
|
||||
ep + 2.0 * (ep.Perpendicular(refGeoLine->getStartPoint(), vectline) - ep));
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomCircle>()) {
|
||||
Part::GeomCircle* geosymcircle = static_cast<Part::GeomCircle*>(geosym);
|
||||
auto* geosymcircle = static_cast<Part::GeomCircle*>(geosym);
|
||||
Base::Vector3d cp = geosymcircle->getCenter();
|
||||
|
||||
geosymcircle->setCenter(
|
||||
cp + 2.0 * (cp.Perpendicular(refGeoLine->getStartPoint(), vectline) - cp));
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomArcOfCircle>()) {
|
||||
Part::GeomArcOfCircle* geoaoc = static_cast<Part::GeomArcOfCircle*>(geosym);
|
||||
auto* geoaoc = static_cast<Part::GeomArcOfCircle*>(geosym);
|
||||
Base::Vector3d sp = geoaoc->getStartPoint(true);
|
||||
Base::Vector3d ep = geoaoc->getEndPoint(true);
|
||||
Base::Vector3d cp = geoaoc->getCenter();
|
||||
@@ -4342,10 +4535,10 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
|
||||
geoaoc->setCenter(scp);
|
||||
geoaoc->setRange(theta1, theta2, true);
|
||||
isStartEndInverted.insert(std::make_pair(*it, true));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, true));
|
||||
}
|
||||
else if (geosym->is<Part::GeomEllipse>()) {
|
||||
Part::GeomEllipse* geosymellipse = static_cast<Part::GeomEllipse*>(geosym);
|
||||
auto* geosymellipse = static_cast<Part::GeomEllipse*>(geosym);
|
||||
Base::Vector3d cp = geosymellipse->getCenter();
|
||||
|
||||
Base::Vector3d majdir = geosymellipse->getMajorAxisDir();
|
||||
@@ -4362,10 +4555,10 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
geosymellipse->setMajorAxisDir(sf1 - scp);
|
||||
|
||||
geosymellipse->setCenter(scp);
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomArcOfEllipse>()) {
|
||||
Part::GeomArcOfEllipse* geosymaoe = static_cast<Part::GeomArcOfEllipse*>(geosym);
|
||||
auto* geosymaoe = static_cast<Part::GeomArcOfEllipse*>(geosym);
|
||||
Base::Vector3d cp = geosymaoe->getCenter();
|
||||
|
||||
Base::Vector3d majdir = geosymaoe->getMajorAxisDir();
|
||||
@@ -4394,11 +4587,10 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
}
|
||||
|
||||
geosymaoe->setRange(theta1, theta2, true);
|
||||
isStartEndInverted.insert(std::make_pair(*it, true));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, true));
|
||||
}
|
||||
else if (geosym->is<Part::GeomArcOfHyperbola>()) {
|
||||
Part::GeomArcOfHyperbola* geosymaoe =
|
||||
static_cast<Part::GeomArcOfHyperbola*>(geosym);
|
||||
auto* geosymaoe = static_cast<Part::GeomArcOfHyperbola*>(geosym);
|
||||
Base::Vector3d cp = geosymaoe->getCenter();
|
||||
|
||||
Base::Vector3d majdir = geosymaoe->getMajorAxisDir();
|
||||
@@ -4423,10 +4615,10 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
std::swap(theta1, theta2);
|
||||
|
||||
geosymaoe->setRange(theta1, theta2, true);
|
||||
isStartEndInverted.insert(std::make_pair(*it, true));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, true));
|
||||
}
|
||||
else if (geosym->is<Part::GeomArcOfParabola>()) {
|
||||
Part::GeomArcOfParabola* geosymaoe = static_cast<Part::GeomArcOfParabola*>(geosym);
|
||||
auto* geosymaoe = static_cast<Part::GeomArcOfParabola*>(geosym);
|
||||
Base::Vector3d cp = geosymaoe->getCenter();
|
||||
|
||||
// double df= geosymaoe->getFocal();
|
||||
@@ -4447,45 +4639,41 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
std::swap(theta1, theta2);
|
||||
|
||||
geosymaoe->setRange(theta1, theta2, true);
|
||||
isStartEndInverted.insert(std::make_pair(*it, true));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, true));
|
||||
}
|
||||
else if (geosym->is<Part::GeomBSplineCurve>()) {
|
||||
Part::GeomBSplineCurve* geosymbsp = static_cast<Part::GeomBSplineCurve*>(geosym);
|
||||
auto* geosymbsp = static_cast<Part::GeomBSplineCurve*>(geosym);
|
||||
|
||||
std::vector<Base::Vector3d> poles = geosymbsp->getPoles();
|
||||
|
||||
for (std::vector<Base::Vector3d>::iterator jt = poles.begin(); jt != poles.end();
|
||||
++jt) {
|
||||
|
||||
(*jt) = (*jt)
|
||||
+ 2.0
|
||||
* ((*jt).Perpendicular(refGeoLine->getStartPoint(), vectline) - (*jt));
|
||||
for (auto& pole : poles) {
|
||||
pole = pole
|
||||
+ 2.0 * (pole.Perpendicular(refGeoLine->getStartPoint(), vectline) - pole);
|
||||
}
|
||||
|
||||
geosymbsp->setPoles(poles);
|
||||
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomPoint>()) {
|
||||
Part::GeomPoint* geosympoint = static_cast<Part::GeomPoint*>(geosym);
|
||||
auto* geosympoint = static_cast<Part::GeomPoint*>(geosym);
|
||||
Base::Vector3d cp = geosympoint->getPoint();
|
||||
|
||||
geosympoint->setPoint(
|
||||
cp + 2.0 * (cp.Perpendicular(refGeoLine->getStartPoint(), vectline) - cp));
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else {
|
||||
Base::Console().Error("Unsupported Geometry!! Just copying it.\n");
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
|
||||
newgeoVals.push_back(geosym);
|
||||
geoIdMap.insert(std::make_pair(*it, cgeoid));
|
||||
symmetricVals.push_back(geosym);
|
||||
geoIdMap.insert(std::make_pair(geoId, cgeoid));
|
||||
cgeoid++;
|
||||
}
|
||||
}
|
||||
else {// reference is a point
|
||||
refIsAxisAligned = true;
|
||||
Vector3d refpoint;
|
||||
const Part::Geometry* georef = getGeometry(refGeoId);
|
||||
|
||||
@@ -4496,160 +4684,43 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
refpoint = Vector3d(0, 0, 0);
|
||||
}
|
||||
else {
|
||||
switch (refPosId) {
|
||||
case Sketcher::PointPos::start:
|
||||
if (georef->is<Part::GeomLineSegment>()) {
|
||||
const Part::GeomLineSegment* geosymline =
|
||||
static_cast<const Part::GeomLineSegment*>(georef);
|
||||
refpoint = geosymline->getStartPoint();
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfCircle>()) {
|
||||
const Part::GeomArcOfCircle* geoaoc =
|
||||
static_cast<const Part::GeomArcOfCircle*>(georef);
|
||||
refpoint = geoaoc->getStartPoint(true);
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfEllipse>()) {
|
||||
const Part::GeomArcOfEllipse* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfEllipse*>(georef);
|
||||
refpoint = geosymaoe->getStartPoint(true);
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfHyperbola>()) {
|
||||
const Part::GeomArcOfHyperbola* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfHyperbola*>(georef);
|
||||
refpoint = geosymaoe->getStartPoint(true);
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfParabola>()) {
|
||||
const Part::GeomArcOfParabola* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfParabola*>(georef);
|
||||
refpoint = geosymaoe->getStartPoint(true);
|
||||
}
|
||||
else if (georef->is<Part::GeomBSplineCurve>()) {
|
||||
const Part::GeomBSplineCurve* geosymbsp =
|
||||
static_cast<const Part::GeomBSplineCurve*>(georef);
|
||||
refpoint = geosymbsp->getStartPoint();
|
||||
}
|
||||
break;
|
||||
case Sketcher::PointPos::end:
|
||||
if (georef->is<Part::GeomLineSegment>()) {
|
||||
const Part::GeomLineSegment* geosymline =
|
||||
static_cast<const Part::GeomLineSegment*>(georef);
|
||||
refpoint = geosymline->getEndPoint();
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfCircle>()) {
|
||||
const Part::GeomArcOfCircle* geoaoc =
|
||||
static_cast<const Part::GeomArcOfCircle*>(georef);
|
||||
refpoint = geoaoc->getEndPoint(true);
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfEllipse>()) {
|
||||
const Part::GeomArcOfEllipse* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfEllipse*>(georef);
|
||||
refpoint = geosymaoe->getEndPoint(true);
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfHyperbola>()) {
|
||||
const Part::GeomArcOfHyperbola* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfHyperbola*>(georef);
|
||||
refpoint = geosymaoe->getEndPoint(true);
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfParabola>()) {
|
||||
const Part::GeomArcOfParabola* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfParabola*>(georef);
|
||||
refpoint = geosymaoe->getEndPoint(true);
|
||||
}
|
||||
else if (georef->is<Part::GeomBSplineCurve>()) {
|
||||
const Part::GeomBSplineCurve* geosymbsp =
|
||||
static_cast<const Part::GeomBSplineCurve*>(georef);
|
||||
refpoint = geosymbsp->getEndPoint();
|
||||
}
|
||||
break;
|
||||
case Sketcher::PointPos::mid:
|
||||
if (georef->is<Part::GeomCircle>()) {
|
||||
const Part::GeomCircle* geosymcircle =
|
||||
static_cast<const Part::GeomCircle*>(georef);
|
||||
refpoint = geosymcircle->getCenter();
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfCircle>()) {
|
||||
const Part::GeomArcOfCircle* geoaoc =
|
||||
static_cast<const Part::GeomArcOfCircle*>(georef);
|
||||
refpoint = geoaoc->getCenter();
|
||||
}
|
||||
else if (georef->is<Part::GeomEllipse>()) {
|
||||
const Part::GeomEllipse* geosymellipse =
|
||||
static_cast<const Part::GeomEllipse*>(georef);
|
||||
refpoint = geosymellipse->getCenter();
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfEllipse>()) {
|
||||
const Part::GeomArcOfEllipse* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfEllipse*>(georef);
|
||||
refpoint = geosymaoe->getCenter();
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfHyperbola>()) {
|
||||
const Part::GeomArcOfHyperbola* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfHyperbola*>(georef);
|
||||
refpoint = geosymaoe->getCenter();
|
||||
}
|
||||
else if (georef->is<Part::GeomArcOfParabola>()) {
|
||||
const Part::GeomArcOfParabola* geosymaoe =
|
||||
static_cast<const Part::GeomArcOfParabola*>(georef);
|
||||
refpoint = geosymaoe->getCenter();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Base::Console().Error("Wrong PointPosId.\n");
|
||||
return -1;
|
||||
if (refPosId == Sketcher::PointPos::none) {
|
||||
Base::Console().Error("Wrong PointPosId.\n");
|
||||
return {};
|
||||
}
|
||||
refpoint = getPoint(georef, refPosId);
|
||||
}
|
||||
|
||||
for (std::vector<int>::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) {
|
||||
const Part::Geometry* geo = getGeometry(*it);
|
||||
|
||||
for (auto geoId : geoIdList) {
|
||||
const Part::Geometry* geo = getGeometry(geoId);
|
||||
Part::Geometry* geosym;
|
||||
|
||||
auto gf = GeometryFacade::getFacade(geo);
|
||||
|
||||
if (gf->isInternalAligned()) {
|
||||
// only add this geometry if the corresponding geometry it defines is also in the
|
||||
// list.
|
||||
int definedGeo = GeoEnum::GeoUndef;
|
||||
|
||||
for (auto c : Constraints.getValues()) {
|
||||
if (c->Type == Sketcher::InternalAlignment && c->First == *it) {
|
||||
definedGeo = c->Second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (std::find(geoIdList.begin(), geoIdList.end(), definedGeo) != geoIdList.end())
|
||||
geosym = geo->copy();
|
||||
else {
|
||||
// we should not mirror internal alignment geometry, unless the element they
|
||||
// define is also mirrored
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
geosym = geo->copy();
|
||||
if (!shouldCopyGeometry(geo, geoId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
geosym = geo->copy();
|
||||
|
||||
// Handle Geometry
|
||||
if (geosym->is<Part::GeomLineSegment>()) {
|
||||
Part::GeomLineSegment* geosymline = static_cast<Part::GeomLineSegment*>(geosym);
|
||||
auto* geosymline = static_cast<Part::GeomLineSegment*>(geosym);
|
||||
Base::Vector3d sp = geosymline->getStartPoint();
|
||||
Base::Vector3d ep = geosymline->getEndPoint();
|
||||
Base::Vector3d ssp = sp + 2.0 * (refpoint - sp);
|
||||
Base::Vector3d sep = ep + 2.0 * (refpoint - ep);
|
||||
|
||||
geosymline->setPoints(ssp, sep);
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomCircle>()) {
|
||||
Part::GeomCircle* geosymcircle = static_cast<Part::GeomCircle*>(geosym);
|
||||
auto* geosymcircle = static_cast<Part::GeomCircle*>(geosym);
|
||||
Base::Vector3d cp = geosymcircle->getCenter();
|
||||
|
||||
geosymcircle->setCenter(cp + 2.0 * (refpoint - cp));
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomArcOfCircle>()) {
|
||||
Part::GeomArcOfCircle* geoaoc = static_cast<Part::GeomArcOfCircle*>(geosym);
|
||||
auto* geoaoc = static_cast<Part::GeomArcOfCircle*>(geosym);
|
||||
Base::Vector3d sp = geoaoc->getStartPoint(true);
|
||||
Base::Vector3d ep = geoaoc->getEndPoint(true);
|
||||
Base::Vector3d cp = geoaoc->getCenter();
|
||||
@@ -4663,10 +4734,10 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
|
||||
geoaoc->setCenter(scp);
|
||||
geoaoc->setRange(theta1, theta2, true);
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomEllipse>()) {
|
||||
Part::GeomEllipse* geosymellipse = static_cast<Part::GeomEllipse*>(geosym);
|
||||
auto* geosymellipse = static_cast<Part::GeomEllipse*>(geosym);
|
||||
Base::Vector3d cp = geosymellipse->getCenter();
|
||||
|
||||
Base::Vector3d majdir = geosymellipse->getMajorAxisDir();
|
||||
@@ -4681,10 +4752,10 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
geosymellipse->setMajorAxisDir(sf1 - scp);
|
||||
|
||||
geosymellipse->setCenter(scp);
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomArcOfEllipse>()) {
|
||||
Part::GeomArcOfEllipse* geosymaoe = static_cast<Part::GeomArcOfEllipse*>(geosym);
|
||||
auto* geosymaoe = static_cast<Part::GeomArcOfEllipse*>(geosym);
|
||||
Base::Vector3d cp = geosymaoe->getCenter();
|
||||
|
||||
Base::Vector3d majdir = geosymaoe->getMajorAxisDir();
|
||||
@@ -4699,11 +4770,10 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
geosymaoe->setMajorAxisDir(sf1 - scp);
|
||||
|
||||
geosymaoe->setCenter(scp);
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomArcOfHyperbola>()) {
|
||||
Part::GeomArcOfHyperbola* geosymaoe =
|
||||
static_cast<Part::GeomArcOfHyperbola*>(geosym);
|
||||
auto* geosymaoe = static_cast<Part::GeomArcOfHyperbola*>(geosym);
|
||||
Base::Vector3d cp = geosymaoe->getCenter();
|
||||
|
||||
Base::Vector3d majdir = geosymaoe->getMajorAxisDir();
|
||||
@@ -4718,10 +4788,10 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
geosymaoe->setMajorAxisDir(sf1 - scp);
|
||||
|
||||
geosymaoe->setCenter(scp);
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomArcOfParabola>()) {
|
||||
Part::GeomArcOfParabola* geosymaoe = static_cast<Part::GeomArcOfParabola*>(geosym);
|
||||
auto* geosymaoe = static_cast<Part::GeomArcOfParabola*>(geosym);
|
||||
Base::Vector3d cp = geosymaoe->getCenter();
|
||||
|
||||
/*double df= geosymaoe->getFocal();*/
|
||||
@@ -4733,167 +4803,39 @@ int SketchObject::addSymmetric(const std::vector<int>& geoIdList, int refGeoId,
|
||||
geosymaoe->setXAxisDir(sf1 - scp);
|
||||
geosymaoe->setCenter(scp);
|
||||
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomBSplineCurve>()) {
|
||||
Part::GeomBSplineCurve* geosymbsp = static_cast<Part::GeomBSplineCurve*>(geosym);
|
||||
auto* geosymbsp = static_cast<Part::GeomBSplineCurve*>(geosym);
|
||||
|
||||
std::vector<Base::Vector3d> poles = geosymbsp->getPoles();
|
||||
|
||||
for (std::vector<Base::Vector3d>::iterator it = poles.begin(); it != poles.end();
|
||||
++it) {
|
||||
(*it) = (*it) + 2.0 * (refpoint - (*it));
|
||||
for (auto& pole : poles) {
|
||||
pole = pole + 2.0 * (refpoint - pole);
|
||||
}
|
||||
|
||||
geosymbsp->setPoles(poles);
|
||||
|
||||
// isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
// isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else if (geosym->is<Part::GeomPoint>()) {
|
||||
Part::GeomPoint* geosympoint = static_cast<Part::GeomPoint*>(geosym);
|
||||
auto* geosympoint = static_cast<Part::GeomPoint*>(geosym);
|
||||
Base::Vector3d cp = geosympoint->getPoint();
|
||||
|
||||
geosympoint->setPoint(cp + 2.0 * (refpoint - cp));
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
else {
|
||||
Base::Console().Error("Unsupported Geometry!! Just copying it.\n");
|
||||
isStartEndInverted.insert(std::make_pair(*it, false));
|
||||
isStartEndInverted.insert(std::make_pair(geoId, false));
|
||||
}
|
||||
|
||||
newgeoVals.push_back(geosym);
|
||||
geoIdMap.insert(std::make_pair(*it, cgeoid));
|
||||
symmetricVals.push_back(geosym);
|
||||
geoIdMap.insert(std::make_pair(geoId, cgeoid));
|
||||
cgeoid++;
|
||||
}
|
||||
}
|
||||
|
||||
// add the geometry
|
||||
|
||||
// Block acceptGeometry in OnChanged to avoid unnecessary checks and updates
|
||||
{
|
||||
Base::StateLocker lock(internaltransaction, true);
|
||||
Geometry.setValues(std::move(newgeoVals));
|
||||
|
||||
for (std::vector<Constraint*>::const_iterator it = constrvals.begin();
|
||||
it != constrvals.end();
|
||||
++it) {
|
||||
// we look in the map, because we might have skipped internal alignment geometry
|
||||
auto fit = geoIdMap.find((*it)->First);
|
||||
|
||||
if (fit != geoIdMap.end()) {// if First of constraint is in geoIdList
|
||||
|
||||
if ((*it)->Second == GeoEnum::GeoUndef /*&& (*it)->Third == GeoEnum::GeoUndef*/) {
|
||||
if (refIsAxisAligned) {
|
||||
// in this case we want to keep the Vertical, Horizontal constraints
|
||||
// DistanceX ,and DistanceY constraints should also be possible to keep in
|
||||
// this case, but keeping them causes segfault, not sure why.
|
||||
|
||||
if ((*it)->Type != Sketcher::DistanceX
|
||||
&& (*it)->Type != Sketcher::DistanceY) {
|
||||
Constraint* constNew = (*it)->copy();
|
||||
constNew->First = fit->second;
|
||||
newconstrVals.push_back(constNew);
|
||||
}
|
||||
}
|
||||
else if ((*it)->Type != Sketcher::DistanceX
|
||||
&& (*it)->Type != Sketcher::DistanceY
|
||||
&& (*it)->Type != Sketcher::Vertical
|
||||
&& (*it)->Type != Sketcher::Horizontal) {
|
||||
// this includes all non-directional single GeoId constraints, as radius,
|
||||
// diameter, weight,...
|
||||
|
||||
Constraint* constNew = (*it)->copy();
|
||||
constNew->First = fit->second;
|
||||
newconstrVals.push_back(constNew);
|
||||
}
|
||||
}
|
||||
else {// other geoids intervene in this constraint
|
||||
|
||||
auto sit = geoIdMap.find((*it)->Second);
|
||||
|
||||
if (sit != geoIdMap.end()) {// Second is also in the list
|
||||
|
||||
if ((*it)->Third == GeoEnum::GeoUndef) {
|
||||
if ((*it)->Type == Sketcher::Coincident
|
||||
|| (*it)->Type == Sketcher::Perpendicular
|
||||
|| (*it)->Type == Sketcher::Parallel
|
||||
|| (*it)->Type == Sketcher::Tangent
|
||||
|| (*it)->Type == Sketcher::Distance
|
||||
|| (*it)->Type == Sketcher::Equal || (*it)->Type == Sketcher::Angle
|
||||
|| (*it)->Type == Sketcher::PointOnObject
|
||||
|| (*it)->Type == Sketcher::InternalAlignment) {
|
||||
Constraint* constNew = (*it)->copy();
|
||||
|
||||
constNew->First = fit->second;
|
||||
constNew->Second = sit->second;
|
||||
if (isStartEndInverted[(*it)->First]) {
|
||||
if ((*it)->FirstPos == Sketcher::PointPos::start)
|
||||
constNew->FirstPos = Sketcher::PointPos::end;
|
||||
else if ((*it)->FirstPos == Sketcher::PointPos::end)
|
||||
constNew->FirstPos = Sketcher::PointPos::start;
|
||||
}
|
||||
if (isStartEndInverted[(*it)->Second]) {
|
||||
if ((*it)->SecondPos == Sketcher::PointPos::start)
|
||||
constNew->SecondPos = Sketcher::PointPos::end;
|
||||
else if ((*it)->SecondPos == Sketcher::PointPos::end)
|
||||
constNew->SecondPos = Sketcher::PointPos::start;
|
||||
}
|
||||
|
||||
if (constNew->Type == Tangent || constNew->Type == Perpendicular)
|
||||
AutoLockTangencyAndPerpty(constNew, true);
|
||||
|
||||
if (((*it)->Type == Sketcher::Angle)
|
||||
&& (refPosId == Sketcher::PointPos::none)) {
|
||||
constNew->setValue(-(*it)->getValue());
|
||||
}
|
||||
|
||||
newconstrVals.push_back(constNew);
|
||||
}
|
||||
}
|
||||
else {// three GeoIds intervene in constraint
|
||||
auto tit = geoIdMap.find((*it)->Third);
|
||||
|
||||
if (tit != geoIdMap.end()) {// Third is also in the list
|
||||
Constraint* constNew = (*it)->copy();
|
||||
constNew->First = fit->second;
|
||||
constNew->Second = sit->second;
|
||||
constNew->Third = tit->second;
|
||||
if (isStartEndInverted[(*it)->First]) {
|
||||
if ((*it)->FirstPos == Sketcher::PointPos::start)
|
||||
constNew->FirstPos = Sketcher::PointPos::end;
|
||||
else if ((*it)->FirstPos == Sketcher::PointPos::end)
|
||||
constNew->FirstPos = Sketcher::PointPos::start;
|
||||
}
|
||||
if (isStartEndInverted[(*it)->Second]) {
|
||||
if ((*it)->SecondPos == Sketcher::PointPos::start)
|
||||
constNew->SecondPos = Sketcher::PointPos::end;
|
||||
else if ((*it)->SecondPos == Sketcher::PointPos::end)
|
||||
constNew->SecondPos = Sketcher::PointPos::start;
|
||||
}
|
||||
if (isStartEndInverted[(*it)->Third]) {
|
||||
if ((*it)->ThirdPos == Sketcher::PointPos::start)
|
||||
constNew->ThirdPos = Sketcher::PointPos::end;
|
||||
else if ((*it)->ThirdPos == Sketcher::PointPos::end)
|
||||
constNew->ThirdPos = Sketcher::PointPos::start;
|
||||
}
|
||||
newconstrVals.push_back(constNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newconstrVals.size() > constrvals.size())
|
||||
Constraints.setValues(std::move(newconstrVals));
|
||||
}
|
||||
|
||||
// we delayed update, so trigger it now.
|
||||
// Update geometry indices and rebuild vertexindex now via onChanged, so that
|
||||
// ViewProvider::UpdateData is triggered.
|
||||
Geometry.touch();
|
||||
|
||||
return Geometry.getSize() - 1;
|
||||
return symmetricVals;
|
||||
}
|
||||
|
||||
int SketchObject::addCopy(const std::vector<int>& geoIdList, const Base::Vector3d& displacement,
|
||||
|
||||
@@ -368,7 +368,16 @@ public:
|
||||
/// adds symmetric geometric elements with respect to the refGeoId (line or point)
|
||||
int addSymmetric(const std::vector<int>& geoIdList,
|
||||
int refGeoId,
|
||||
Sketcher::PointPos refPosId = Sketcher::PointPos::none);
|
||||
Sketcher::PointPos refPosId = Sketcher::PointPos::none,
|
||||
bool addSymmetryConstraints = false);
|
||||
// get the symmetric geometries of the geoIdList
|
||||
std::vector<Part::Geometry*>
|
||||
getSymmetric(const std::vector<int>& geoIdList,
|
||||
std::map<int, int>& geoIdMap,
|
||||
std::map<int, bool>& isStartEndInverted,
|
||||
int refGeoId,
|
||||
Sketcher::PointPos refPosId = Sketcher::PointPos::none);
|
||||
|
||||
/// with default parameters adds a copy of the geometric elements displaced by the displacement
|
||||
/// vector. It creates an array of csize elements in the direction of the displacement vector by
|
||||
/// rsize elements in the direction perpendicular to the displacement vector, wherein the
|
||||
|
||||
@@ -80,6 +80,7 @@ SET(SketcherGui_SRCS
|
||||
DrawSketchHandlerOffset.h
|
||||
DrawSketchHandlerRotate.h
|
||||
DrawSketchHandlerScale.h
|
||||
DrawSketchHandlerSymmetry.h
|
||||
CommandCreateGeo.cpp
|
||||
CommandConstraints.h
|
||||
CommandConstraints.cpp
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "DrawSketchHandlerOffset.h"
|
||||
#include "DrawSketchHandlerRotate.h"
|
||||
#include "DrawSketchHandlerScale.h"
|
||||
#include "DrawSketchHandlerSymmetry.h"
|
||||
|
||||
// Hint: this is to prevent to re-format big parts of the file. Remove it later again.
|
||||
// clang-format off
|
||||
@@ -1092,7 +1093,7 @@ CmdSketcherSymmetry::CmdSketcherSymmetry()
|
||||
sGroup = "Sketcher";
|
||||
sMenuText = QT_TR_NOOP("Symmetry");
|
||||
sToolTipText =
|
||||
QT_TR_NOOP("Creates symmetric geometry with respect to the last selected line or point");
|
||||
QT_TR_NOOP("Creates symmetric of selected geometry. After starting the tool select the reference line or point.");
|
||||
sWhatsThis = "Sketcher_Symmetry";
|
||||
sStatusTip = sToolTipText;
|
||||
sPixmap = "Sketcher_Symmetry";
|
||||
@@ -1103,190 +1104,12 @@ CmdSketcherSymmetry::CmdSketcherSymmetry()
|
||||
void CmdSketcherSymmetry::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
std::vector<int> listOfGeoIds = getListOfSelectedGeoIds(true);
|
||||
|
||||
// Cancel any in-progress operation
|
||||
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
||||
SketcherGui::ReleaseHandler(doc);
|
||||
|
||||
// get the selection
|
||||
std::vector<Gui::SelectionObject> selection;
|
||||
selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
|
||||
|
||||
// only one sketch with its subelements are allowed to be selected
|
||||
if (selection.size() != 1) {
|
||||
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
|
||||
QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select elements from a single sketch."));
|
||||
return;
|
||||
if (!listOfGeoIds.empty()) {
|
||||
ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerSymmetry(listOfGeoIds));
|
||||
}
|
||||
|
||||
// get the needed lists and objects
|
||||
const std::vector<std::string>& SubNames = selection[0].getSubNames();
|
||||
if (SubNames.empty()) {
|
||||
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
|
||||
QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select elements from a single sketch."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
||||
getSelection().clearSelection();
|
||||
|
||||
int LastGeoId = 0;
|
||||
Sketcher::PointPos LastPointPos = Sketcher::PointPos::none;
|
||||
const Part::Geometry* LastGeo;
|
||||
using GeoType = enum { invalid = -1, line = 0, point = 1 };
|
||||
|
||||
GeoType lastgeotype = invalid;
|
||||
|
||||
// create python command with list of elements
|
||||
std::stringstream stream;
|
||||
int geoids = 0;
|
||||
|
||||
for (std::vector<std::string>::const_iterator it = SubNames.begin(); it != SubNames.end();
|
||||
++it) {
|
||||
// only handle non-external edges
|
||||
if ((it->size() > 4 && it->substr(0, 4) == "Edge")
|
||||
|| (it->size() > 12 && it->substr(0, 12) == "ExternalEdge")) {
|
||||
|
||||
if (it->substr(0, 4) == "Edge") {
|
||||
LastGeoId = std::atoi(it->substr(4, 4000).c_str()) - 1;
|
||||
LastPointPos = Sketcher::PointPos::none;
|
||||
}
|
||||
else {
|
||||
LastGeoId = -std::atoi(it->substr(12, 4000).c_str()) - 2;
|
||||
LastPointPos = Sketcher::PointPos::none;
|
||||
}
|
||||
|
||||
// reference can be external or non-external
|
||||
LastGeo = Obj->getGeometry(LastGeoId);
|
||||
// Only for supported types
|
||||
if (LastGeo->is<Part::GeomLineSegment>())
|
||||
lastgeotype = line;
|
||||
else
|
||||
lastgeotype = invalid;
|
||||
|
||||
// lines to make symmetric (only non-external)
|
||||
if (LastGeoId >= 0) {
|
||||
geoids++;
|
||||
stream << LastGeoId << ",";
|
||||
}
|
||||
}
|
||||
else if (it->size() > 6 && it->substr(0, 6) == "Vertex") {
|
||||
// only if it is a GeomPoint
|
||||
int VtId = std::atoi(it->substr(6, 4000).c_str()) - 1;
|
||||
int GeoId;
|
||||
Sketcher::PointPos PosId;
|
||||
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
|
||||
|
||||
if (Obj->getGeometry(GeoId)->is<Part::GeomPoint>()) {
|
||||
LastGeoId = GeoId;
|
||||
LastPointPos = Sketcher::PointPos::start;
|
||||
lastgeotype = point;
|
||||
|
||||
// points to make symmetric
|
||||
if (LastGeoId >= 0) {
|
||||
geoids++;
|
||||
stream << LastGeoId << ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool lastvertexoraxis = false;
|
||||
// check if last selected element is a Vertex, not being a GeomPoint
|
||||
if (SubNames.rbegin()->size() > 6 && SubNames.rbegin()->substr(0, 6) == "Vertex") {
|
||||
int VtId = std::atoi(SubNames.rbegin()->substr(6, 4000).c_str()) - 1;
|
||||
int GeoId;
|
||||
Sketcher::PointPos PosId;
|
||||
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
|
||||
if (Obj->getGeometry(GeoId)->getTypeId() != Part::GeomPoint::getClassTypeId()) {
|
||||
LastGeoId = GeoId;
|
||||
LastPointPos = PosId;
|
||||
lastgeotype = point;
|
||||
lastvertexoraxis = true;
|
||||
}
|
||||
}
|
||||
// check if last selected element is horizontal axis
|
||||
else if (SubNames.rbegin()->size() == 6 && SubNames.rbegin()->substr(0, 6) == "H_Axis") {
|
||||
LastGeoId = Sketcher::GeoEnum::HAxis;
|
||||
LastPointPos = Sketcher::PointPos::none;
|
||||
lastgeotype = line;
|
||||
lastvertexoraxis = true;
|
||||
}
|
||||
// check if last selected element is vertical axis
|
||||
else if (SubNames.rbegin()->size() == 6 && SubNames.rbegin()->substr(0, 6) == "V_Axis") {
|
||||
LastGeoId = Sketcher::GeoEnum::VAxis;
|
||||
LastPointPos = Sketcher::PointPos::none;
|
||||
lastgeotype = line;
|
||||
lastvertexoraxis = true;
|
||||
}
|
||||
// check if last selected element is the root point
|
||||
else if (SubNames.rbegin()->size() == 9 && SubNames.rbegin()->substr(0, 9) == "RootPoint") {
|
||||
LastGeoId = Sketcher::GeoEnum::RtPnt;
|
||||
LastPointPos = Sketcher::PointPos::start;
|
||||
lastgeotype = point;
|
||||
lastvertexoraxis = true;
|
||||
}
|
||||
|
||||
if (geoids == 0 || (geoids == 1 && LastGeoId >= 0 && !lastvertexoraxis)) {
|
||||
Gui::TranslatedUserWarning(Obj,
|
||||
QObject::tr("Wrong selection"),
|
||||
QObject::tr("A symmetric construction requires "
|
||||
"at least two geometric elements, "
|
||||
"the last geometric element being the reference "
|
||||
"for the symmetry construction."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastgeotype == invalid) {
|
||||
Gui::TranslatedUserWarning(Obj,
|
||||
QObject::tr("Wrong selection"),
|
||||
QObject::tr("The last element must be a point "
|
||||
"or a line serving as reference "
|
||||
"for the symmetry construction."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string geoIdList = stream.str();
|
||||
|
||||
// missing cases:
|
||||
// 1- Last element is an edge, and is V or H axis
|
||||
// 2- Last element is a point GeomPoint
|
||||
// 3- Last element is a point (Vertex)
|
||||
|
||||
if (LastGeoId >= 0 && !lastvertexoraxis) {
|
||||
// if LastGeoId was added remove the last element
|
||||
int index = geoIdList.rfind(',');
|
||||
index = geoIdList.rfind(',', index - 1);
|
||||
geoIdList.resize(index);
|
||||
}
|
||||
else {
|
||||
int index = geoIdList.rfind(',');
|
||||
geoIdList.resize(index);
|
||||
}
|
||||
|
||||
geoIdList.insert(0, 1, '[');
|
||||
geoIdList.append(1, ']');
|
||||
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create symmetric geometry"));
|
||||
|
||||
try {
|
||||
Gui::cmdAppObjectArgs(Obj,
|
||||
"addSymmetric(%s, %d, %d)",
|
||||
geoIdList.c_str(),
|
||||
LastGeoId,
|
||||
static_cast<int>(LastPointPos));
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Gui::NotifyUserError(
|
||||
Obj, QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
tryAutoRecomputeIfNotSolve(Obj);
|
||||
}
|
||||
|
||||
bool CmdSketcherSymmetry::isActive()
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Boyer Pierre-Louis <pierrelouis.boyer@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerSymmetry_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerSymmetry_H
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Gui/Notifications.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/CommandT.h>
|
||||
|
||||
#include <Mod/Sketcher/App/GeometryFacade.h>
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "DrawSketchDefaultWidgetController.h"
|
||||
#include "DrawSketchControllableHandler.h"
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace Sketcher;
|
||||
|
||||
namespace SketcherGui
|
||||
{
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerSymmetry;
|
||||
|
||||
using DSHSymmetryController =
|
||||
DrawSketchDefaultWidgetController<DrawSketchHandlerSymmetry,
|
||||
StateMachines::OneSeekEnd,
|
||||
/*PAutoConstraintSize =*/0,
|
||||
/*OnViewParametersT =*/OnViewParameters<0>,
|
||||
/*WidgetParametersT =*/WidgetParameters<0>,
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<2>,
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>>;
|
||||
|
||||
using DSHSymmetryControllerBase = DSHSymmetryController::ControllerBase;
|
||||
|
||||
using DrawSketchHandlerSymmetryBase = DrawSketchControllableHandler<DSHSymmetryController>;
|
||||
|
||||
class DrawSketchHandlerSymmetry: public DrawSketchHandlerSymmetryBase
|
||||
{
|
||||
friend DSHSymmetryController;
|
||||
friend DSHSymmetryControllerBase;
|
||||
|
||||
public:
|
||||
explicit DrawSketchHandlerSymmetry(std::vector<int> listOfGeoIds)
|
||||
: listOfGeoIds(listOfGeoIds)
|
||||
, refGeoId(Sketcher::GeoEnum::GeoUndef)
|
||||
, refPosId(Sketcher::PointPos::none)
|
||||
, deleteOriginal(false)
|
||||
, createSymConstraints(false)
|
||||
{}
|
||||
|
||||
DrawSketchHandlerSymmetry(const DrawSketchHandlerSymmetry&) = delete;
|
||||
DrawSketchHandlerSymmetry(DrawSketchHandlerSymmetry&&) = delete;
|
||||
DrawSketchHandlerSymmetry& operator=(const DrawSketchHandlerSymmetry&) = delete;
|
||||
DrawSketchHandlerSymmetry& operator=(DrawSketchHandlerSymmetry&&) = delete;
|
||||
|
||||
~DrawSketchHandlerSymmetry() override = default;
|
||||
|
||||
private:
|
||||
void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
switch (state()) {
|
||||
case SelectMode::SeekFirst: {
|
||||
int VtId = getPreselectPoint();
|
||||
int CrvId = getPreselectCurve();
|
||||
int CrsId = getPreselectCross();
|
||||
|
||||
if (VtId >= 0) { // Vertex
|
||||
SketchObject* Obj = sketchgui->getSketchObject();
|
||||
Obj->getGeoVertexIndex(VtId, refGeoId, refPosId);
|
||||
}
|
||||
else if (CrsId == 0) { // RootPoint
|
||||
refGeoId = Sketcher::GeoEnum::RtPnt;
|
||||
refPosId = Sketcher::PointPos::start;
|
||||
}
|
||||
else if (CrsId == 1) { // H_Axis
|
||||
refGeoId = Sketcher::GeoEnum::HAxis;
|
||||
refPosId = Sketcher::PointPos::none;
|
||||
}
|
||||
else if (CrsId == 2) { // V_Axis
|
||||
refGeoId = Sketcher::GeoEnum::VAxis;
|
||||
refPosId = Sketcher::PointPos::none;
|
||||
}
|
||||
else if (CrvId >= 0 || CrvId <= Sketcher::GeoEnum::RefExt) { // Curves
|
||||
refGeoId = CrvId;
|
||||
refPosId = Sketcher::PointPos::none;
|
||||
}
|
||||
else {
|
||||
refGeoId = Sketcher::GeoEnum::GeoUndef;
|
||||
refPosId = Sketcher::PointPos::none;
|
||||
}
|
||||
|
||||
|
||||
CreateAndDrawShapeGeometry();
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void executeCommands() override
|
||||
{
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Symmetry geometries"));
|
||||
|
||||
SketchObject* Obj = sketchgui->getSketchObject();
|
||||
createSymConstraints = !deleteOriginal && createSymConstraints;
|
||||
Obj->addSymmetric(listOfGeoIds, refGeoId, refPosId, createSymConstraints);
|
||||
|
||||
if (deleteOriginal) {
|
||||
deleteOriginalGeos();
|
||||
}
|
||||
tryAutoRecomputeIfNotSolve(Obj);
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
e.ReportException();
|
||||
Gui::NotifyError(sketchgui,
|
||||
QT_TRANSLATE_NOOP("Notifications", "Error"),
|
||||
QT_TRANSLATE_NOOP("Notifications", "Failed to create symmetry"));
|
||||
|
||||
Gui::Command::abortCommand();
|
||||
THROWM(Base::RuntimeError,
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Notifications",
|
||||
"Tool execution aborted") "\n") // This prevents constraints from being
|
||||
// applied on non existing geometry
|
||||
}
|
||||
}
|
||||
|
||||
void createAutoConstraints() override
|
||||
{
|
||||
// none
|
||||
}
|
||||
|
||||
std::string getToolName() const override
|
||||
{
|
||||
return "DSH_Symmetry";
|
||||
}
|
||||
|
||||
QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Symmetry");
|
||||
}
|
||||
|
||||
std::unique_ptr<QWidget> createWidget() const override
|
||||
{
|
||||
return std::make_unique<SketcherToolDefaultWidget>();
|
||||
}
|
||||
|
||||
bool isWidgetVisible() const override
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
QPixmap getToolIcon() const override
|
||||
{
|
||||
return Gui::BitmapFactory().pixmap("Sketcher_Symmetry");
|
||||
}
|
||||
|
||||
QString getToolWidgetText() const override
|
||||
{
|
||||
return QString(QObject::tr("Symmetry parameters"));
|
||||
}
|
||||
|
||||
void activated() override
|
||||
{
|
||||
DrawSketchDefaultHandler::activated();
|
||||
continuousMode = false;
|
||||
}
|
||||
|
||||
bool canGoToNextMode() override
|
||||
{
|
||||
if (state() == SelectMode::SeekFirst && refGeoId == Sketcher::GeoEnum::GeoUndef) {
|
||||
// Prevent validation if no reference selected.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int> listOfGeoIds;
|
||||
int refGeoId;
|
||||
Sketcher::PointPos refPosId;
|
||||
bool deleteOriginal, createSymConstraints;
|
||||
|
||||
void deleteOriginalGeos()
|
||||
{
|
||||
std::stringstream stream;
|
||||
for (size_t j = 0; j < listOfGeoIds.size() - 1; j++) {
|
||||
stream << listOfGeoIds[j] << ",";
|
||||
}
|
||||
stream << listOfGeoIds[listOfGeoIds.size() - 1];
|
||||
try {
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(),
|
||||
"delGeometries([%s])",
|
||||
stream.str().c_str());
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void createShape(bool onlyeditoutline) override
|
||||
{
|
||||
SketchObject* Obj = sketchgui->getSketchObject();
|
||||
|
||||
ShapeGeometry.clear();
|
||||
|
||||
if (refGeoId == Sketcher::GeoEnum::GeoUndef) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (onlyeditoutline) {
|
||||
std::map<int, int> dummy1;
|
||||
std::map<int, bool> dummy2;
|
||||
std::vector<Part::Geometry*> symGeos =
|
||||
Obj->getSymmetric(listOfGeoIds, dummy1, dummy2, refGeoId, refPosId);
|
||||
|
||||
for (auto* geo : symGeos) {
|
||||
ShapeGeometry.emplace_back(std::move(std::unique_ptr<Part::Geometry>(geo)));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
void DSHSymmetryController::configureToolWidget()
|
||||
{
|
||||
if (!init) { // Code to be executed only upon initialisation
|
||||
toolWidget->setCheckboxLabel(WCheckbox::FirstBox,
|
||||
QApplication::translate("TaskSketcherTool_c1_symmetry",
|
||||
"Delete original geometries (U)"));
|
||||
toolWidget->setCheckboxLabel(WCheckbox::SecondBox,
|
||||
QApplication::translate("TaskSketcherTool_c2_symmetry",
|
||||
"Create Symmetry Constraints (J)"));
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DSHSymmetryController::adaptDrawingToCheckboxChange(int checkboxindex, bool value)
|
||||
{
|
||||
switch (checkboxindex) {
|
||||
case WCheckbox::FirstBox: {
|
||||
handler->deleteOriginal = value;
|
||||
if (value && toolWidget->getCheckboxChecked(WCheckbox::SecondBox)) {
|
||||
toolWidget->setCheckboxChecked(WCheckbox::SecondBox, false);
|
||||
}
|
||||
} break;
|
||||
case WCheckbox::SecondBox: {
|
||||
handler->createSymConstraints = value;
|
||||
if (value && toolWidget->getCheckboxChecked(WCheckbox::FirstBox)) {
|
||||
toolWidget->setCheckboxChecked(WCheckbox::FirstBox, false);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerSymmetry_H
|
||||
@@ -246,6 +246,7 @@
|
||||
<file>icons/pointers/Sketcher_Pointer_Create_Offset.svg</file>
|
||||
<file>icons/pointers/Sketcher_Pointer_Create_Rotate.svg</file>
|
||||
<file>icons/pointers/Sketcher_Pointer_Create_Scale.svg</file>
|
||||
<file>icons/pointers/Sketcher_Pointer_Create_Symmetry.svg</file>
|
||||
<file>icons/pointers/Sketcher_Pointer_Extension.svg</file>
|
||||
<file>icons/pointers/Sketcher_Pointer_External.svg</file>
|
||||
<file>icons/pointers/Sketcher_Pointer_Heptagon.svg</file>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
height="64"
|
||||
width="64"
|
||||
id="svg12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs16">
|
||||
<marker
|
||||
style="overflow:visible"
|
||||
id="Arrow1Lstart"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;fill:context-stroke;stroke:context-stroke;stroke-width:1.0pt"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path4362" />
|
||||
</marker>
|
||||
</defs>
|
||||
<g
|
||||
id="crosshair"
|
||||
style="stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter">
|
||||
<path
|
||||
d="m16,3v9m0,8v9m-13-13h9m8,0h9"
|
||||
id="path9" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#cc0000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 33.612274,29.732474 v 0 c 0,0 -6.759671,4.19863 -6.731131,13.462262 0.02854,9.263631 6.731131,13.58246 6.731131,13.58246"
|
||||
id="path1221" />
|
||||
<path
|
||||
style="fill:none;stroke:#cc0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2, 2;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 43.348375,32.497046 V 55.455009"
|
||||
id="path1256" />
|
||||
<path
|
||||
style="fill:none;stroke:#cc0000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 52.363237,56.837294 v 0 c 0,0 6.759671,-4.19863 6.731131,-13.462262 -0.02854,-9.263631 -6.731131,-13.58246 -6.731131,-13.58246"
|
||||
id="path1221-9" />
|
||||
<circle
|
||||
cx="33.612274"
|
||||
cy="29.732473"
|
||||
r="4"
|
||||
id="circle6"
|
||||
style="fill:none;stroke:#cc0000;stroke-width:2" />
|
||||
<circle
|
||||
cx="33.612274"
|
||||
cy="56.777195"
|
||||
r="4"
|
||||
id="circle6-4"
|
||||
style="fill:none;stroke:#cc0000;stroke-width:2" />
|
||||
<circle
|
||||
cx="52.363235"
|
||||
cy="29.792572"
|
||||
r="4"
|
||||
id="circle6-4-0"
|
||||
style="fill:none;stroke:#cc0000;stroke-width:2" />
|
||||
<circle
|
||||
cx="52.363235"
|
||||
cy="56.837296"
|
||||
r="4"
|
||||
id="circle6-4-0-2"
|
||||
style="fill:none;stroke:#cc0000;stroke-width:2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -554,13 +554,13 @@ def handle():
|
||||
|
||||
SECTION_NEW_FILE = "<h2>" + TranslationTexts.get("T_NEWFILE") + "</h2>"
|
||||
SECTION_NEW_FILE += "<ul>"
|
||||
SECTION_NEW_FILE += build_new_file_card("empty_file")
|
||||
SECTION_NEW_FILE += build_new_file_card("open_file")
|
||||
SECTION_NEW_FILE += build_new_file_card("parametric_part")
|
||||
SECTION_NEW_FILE += build_new_file_card("assembly")
|
||||
# SECTION_NEW_FILE += build_new_file_card("csg_part")
|
||||
SECTION_NEW_FILE += build_new_file_card("2d_draft")
|
||||
SECTION_NEW_FILE += build_new_file_card("architecture")
|
||||
SECTION_NEW_FILE += build_new_file_card("empty_file")
|
||||
SECTION_NEW_FILE += build_new_file_card("open_file")
|
||||
SECTION_NEW_FILE += "</ul>"
|
||||
HTML = HTML.replace("SECTION_NEW_FILE", SECTION_NEW_FILE)
|
||||
|
||||
|
||||
@@ -51,12 +51,14 @@ def get(handle):
|
||||
T_TEMPLATE_EMPTYFILE_DESC = translate("StartPage", "Create an empty FreeCAD file")
|
||||
T_TEMPLATE_OPENFILE_NAME = translate("StartPage", "Open File")
|
||||
T_TEMPLATE_OPENFILE_DESC = translate("StartPage", "Open an existing CAD file or 3D model")
|
||||
T_TEMPLATE_PARAMETRICPART_NAME = translate("StartPage", "Standard Part")
|
||||
T_TEMPLATE_PARAMETRICPART_NAME = translate("StartPage", "Parametric Part")
|
||||
T_TEMPLATE_PARAMETRICPART_DESC = translate(
|
||||
"StartPage", "Create a part with the Part Design workbench"
|
||||
)
|
||||
T_TEMPLATE_ASSEMBLY_NAME = translate("StartPage", "Assembly")
|
||||
T_TEMPLATE_ASSEMBLY_DESC = translate("StartPage", "Create an assembly project")
|
||||
T_TEMPLATE_ASSEMBLY_DESC = translate(
|
||||
"StartPage", "Create an assembly with the Assembly workbench"
|
||||
)
|
||||
# T_TEMPLATE_CSGPART_NAME = translate("StartPage", "CSG Part")
|
||||
# T_TEMPLATE_CSGPART_DESC = translate("StartPage", "Create a part with the Part workbench")
|
||||
T_TEMPLATE_2DDRAFT_NAME = translate("StartPage", "2D Draft")
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.1 KiB |
@@ -132,10 +132,10 @@ QString DrawSVGTemplate::processTemplate()
|
||||
query.processItems(QString::fromUtf8(
|
||||
"declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
|
||||
[&substitutions, &templateDocument](QDomElement& tspan) -> bool {
|
||||
// Replace the editable text spans with new nodes holding actual values
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE));
|
||||
std::map<std::string, std::string>::iterator item =
|
||||
substitutions.find(editableName.toStdString());
|
||||
if (item != substitutions.end()) {
|
||||
@@ -296,15 +296,28 @@ std::map<std::string, std::string> DrawSVGTemplate::getEditableTextsFromTemplate
|
||||
query.processItems(QString::fromUtf8(
|
||||
"declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
[&editables](QDomElement& tspan) -> bool {
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
|
||||
QString editableValue = tspan.firstChild().nodeValue();
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
|
||||
[this, &editables](QDomElement& tspan) -> bool {
|
||||
QDomElement parent = tspan.parentNode().toElement();
|
||||
QString editableName = parent.attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE));
|
||||
|
||||
editables[std::string(editableName.toUtf8().constData())] =
|
||||
std::string(editableValue.toUtf8().constData());
|
||||
return true;
|
||||
});
|
||||
QString editableValue;
|
||||
if (parent.hasAttribute(QString::fromUtf8(FREECAD_ATTR_AUTOFILL))) {
|
||||
QString autofillValue = getAutofillValue(parent.attribute(QString::fromUtf8(FREECAD_ATTR_AUTOFILL)));
|
||||
if (!autofillValue.isNull()) {
|
||||
editableValue = autofillValue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the autofill value is not specified or unsupported, use the default text value
|
||||
if (editableValue.isNull()) {
|
||||
editableValue = tspan.firstChild().nodeValue();
|
||||
}
|
||||
|
||||
editables[std::string(editableName.toUtf8().constData())] =
|
||||
std::string(editableValue.toUtf8().constData());
|
||||
return true;
|
||||
});
|
||||
|
||||
return editables;
|
||||
}
|
||||
|
||||
@@ -24,13 +24,19 @@
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <sstream>
|
||||
# include <QCollator>
|
||||
# include <QDateTime>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
|
||||
#include "DrawTemplate.h"
|
||||
#include "DrawTemplatePy.h"
|
||||
#include "DrawPage.h"
|
||||
#include "DrawUtil.h"
|
||||
|
||||
|
||||
using namespace TechDraw;
|
||||
@@ -94,6 +100,67 @@ DrawPage* DrawTemplate::getParentPage() const
|
||||
return page;
|
||||
}
|
||||
|
||||
QString DrawTemplate::getAutofillValue(const QString &id) const
|
||||
{
|
||||
// author
|
||||
if (id.compare(QString::fromUtf8(Autofill::Author)) == 0) {
|
||||
std::string value = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")
|
||||
->GetGroup("Document")->GetASCII("prefAuthor");
|
||||
if (!value.empty()) {
|
||||
return QString::fromUtf8(value.c_str());
|
||||
}
|
||||
}
|
||||
// date
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Date)) == 0) {
|
||||
QDateTime date = QDateTime::currentDateTime();
|
||||
return date.toString(QLocale().dateFormat(QLocale::ShortFormat));
|
||||
}
|
||||
// organization
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Organization)) == 0) {
|
||||
std::string value = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")
|
||||
->GetGroup("Document")->GetASCII("prefCompany");
|
||||
if (!value.empty()) {
|
||||
return QString::fromUtf8(value.c_str());
|
||||
}
|
||||
}
|
||||
// scale
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Scale)) == 0) {
|
||||
DrawPage *page = getParentPage();
|
||||
if (page) {
|
||||
std::pair<int, int> scale = DrawUtil::nearestFraction(page->Scale.getValue());
|
||||
return QString::asprintf("%d : %d", scale.first, scale.second);
|
||||
}
|
||||
}
|
||||
// sheet
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Sheet)) == 0) {
|
||||
std::vector<DocumentObject *> pages = getDocument()->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
|
||||
std::vector<QString> pageNames;
|
||||
for (auto page : pages) {
|
||||
pageNames.push_back(QString::fromUtf8(page->Label.getValue()));
|
||||
}
|
||||
|
||||
QCollator collator;
|
||||
std::sort(pageNames.begin(), pageNames.end(), collator);
|
||||
|
||||
int pos = 0;
|
||||
DrawPage *page = getParentPage();
|
||||
if (page) {
|
||||
auto it = std::find(pageNames.begin(), pageNames.end(), QString::fromUtf8(page->Label.getValue()));
|
||||
if (it != pageNames.end()) {
|
||||
pos = it - pageNames.begin() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return QString::asprintf("%d / %d", pos, (int) pageNames.size());
|
||||
}
|
||||
// title
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Title)) == 0) {
|
||||
return QString::fromUtf8(getDocument()->Label.getValue());
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Python Template feature ---------------------------------------------------------
|
||||
|
||||
namespace App {
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
|
||||
virtual DrawPage* getParentPage() const;
|
||||
|
||||
virtual QString getAutofillValue(const QString &id) const;
|
||||
|
||||
/// returns the type name of the ViewProvider
|
||||
const char* getViewProviderName(void) const override{
|
||||
return "TechDrawGui::ViewProviderTemplate";
|
||||
@@ -65,6 +67,17 @@ public:
|
||||
// from base class
|
||||
PyObject *getPyObject(void) override;
|
||||
|
||||
class Autofill
|
||||
{
|
||||
public:
|
||||
static constexpr const char *Author = "author";
|
||||
static constexpr const char *Date = "date";
|
||||
static constexpr const char *Organization = "organization";
|
||||
static constexpr const char *Scale = "scale";
|
||||
static constexpr const char *Sheet = "sheet";
|
||||
static constexpr const char *Title = "title";
|
||||
};
|
||||
|
||||
private:
|
||||
static const char* OrientationEnums[];
|
||||
|
||||
|
||||
@@ -1312,6 +1312,80 @@ double DrawUtil::angleDifference(double fi1, double fi2, bool reflex)
|
||||
return fi1;
|
||||
}
|
||||
|
||||
std::pair<int, int> DrawUtil::nearestFraction(double val, int maxDenom)
|
||||
{
|
||||
// Find rational approximation to given real number
|
||||
// David Eppstein / UC Irvine / 8 Aug 1993
|
||||
//
|
||||
// With corrections from Arno Formella, May 2008
|
||||
// and additional fiddles by WF 2017
|
||||
// usage: a.out r d
|
||||
// r is real number to approx
|
||||
// d is the maximum denominator allowed
|
||||
//
|
||||
// Based on the theory of continued fractions
|
||||
// if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
|
||||
// then best approximation is found by truncating this series
|
||||
// (with some adjustments in the last term).
|
||||
//
|
||||
// Note the fraction can be recovered as the first column of the matrix
|
||||
// ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
|
||||
// ( 1 0 ) ( 1 0 ) ( 1 0 )
|
||||
// Instead of keeping the sequence of continued fraction terms,
|
||||
// we just keep the last partial product of these matrices.
|
||||
std::pair<int, int> result;
|
||||
long m[2][2];
|
||||
long maxden = maxDenom;
|
||||
long ai;
|
||||
double x = val;
|
||||
double startx = x;
|
||||
|
||||
/* initialize matrix */
|
||||
m[0][0] = m[1][1] = 1;
|
||||
m[0][1] = m[1][0] = 0;
|
||||
|
||||
/* loop finding terms until denom gets too big */
|
||||
while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) {
|
||||
long t;
|
||||
t = m[0][0] * ai + m[0][1];
|
||||
m[0][1] = m[0][0];
|
||||
m[0][0] = t;
|
||||
t = m[1][0] * ai + m[1][1];
|
||||
m[1][1] = m[1][0];
|
||||
m[1][0] = t;
|
||||
if(x == (double) ai)
|
||||
break; // AF: division by zero
|
||||
x = 1/(x - (double) ai);
|
||||
if(x > (double) std::numeric_limits<int>::max())
|
||||
break; // AF: representation failure
|
||||
}
|
||||
|
||||
/* now remaining x is between 0 and 1/ai */
|
||||
/* approx as either 0 or 1/m where m is max that will fit in maxden */
|
||||
/* first try zero */
|
||||
double error1 = startx - ((double) m[0][0] / (double) m[1][0]);
|
||||
int n1 = m[0][0];
|
||||
int d1 = m[1][0];
|
||||
|
||||
/* now try other possibility */
|
||||
ai = (maxden - m[1][1]) / m[1][0];
|
||||
m[0][0] = m[0][0] * ai + m[0][1];
|
||||
m[1][0] = m[1][0] * ai + m[1][1];
|
||||
double error2 = startx - ((double) m[0][0] / (double) m[1][0]);
|
||||
int n2 = m[0][0];
|
||||
int d2 = m[1][0];
|
||||
|
||||
if (std::fabs(error1) <= std::fabs(error2)) {
|
||||
result.first = n1;
|
||||
result.second = d1;
|
||||
} else {
|
||||
result.first = n2;
|
||||
result.second = d2;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Interval marking functions
|
||||
// ==========================
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@
|
||||
#define SVG_NS_URI "http://www.w3.org/2000/svg"
|
||||
#define FREECAD_SVG_NS_URI "https://www.freecad.org/wiki/index.php?title=Svg_Namespace"
|
||||
|
||||
#define FREECAD_ATTR_EDITABLE "freecad:editable"
|
||||
#define FREECAD_ATTR_AUTOFILL "freecad:autofill"
|
||||
|
||||
//some shapes are being passed in where edges that should be connected are in fact
|
||||
//separated by more than 2*Precision::Confusion (expected tolerance for 2 TopoDS_Vertex)
|
||||
//this value is used in EdgeWalker, DrawProjectSplit and DrawUtil and needs to be in sync in
|
||||
@@ -217,6 +220,7 @@ public:
|
||||
static void angleNormalize(double& fi);
|
||||
static double angleComposition(double fi, double delta);
|
||||
static double angleDifference(double fi1, double fi2, bool reflex = false);
|
||||
static std::pair<int, int> nearestFraction(double val, int maxDenom = 999);
|
||||
|
||||
// Interval marking functions
|
||||
static unsigned int intervalMerge(std::vector<std::pair<double, bool>>& marking,
|
||||
|
||||
@@ -385,7 +385,7 @@ TopoDS_Shape DrawViewSection::getShapeForDetail() const
|
||||
|
||||
App::DocumentObjectExecReturn* DrawViewSection::execute()
|
||||
{
|
||||
// Base::Console().Message("DVS::execute() - %s\n", getNameInDocument());
|
||||
// Base::Console().Message("DVS::execute() - %s\n", Label.getValue());
|
||||
if (!keepUpdated()) {
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
@@ -445,9 +445,7 @@ bool DrawViewSection::isBaseValid() const
|
||||
|
||||
void DrawViewSection::sectionExec(TopoDS_Shape& baseShape)
|
||||
{
|
||||
// Base::Console().Message("DVS::sectionExec() - %s baseShape.IsNull:
|
||||
// %d\n",
|
||||
// getNameInDocument(), baseShape.IsNull());
|
||||
// Base::Console().Message("DVS::sectionExec() - %s baseShape.IsNull: %d\n", Label.getValue(), baseShape.IsNull());
|
||||
|
||||
if (waitingForHlr() || waitingForCut()) {
|
||||
return;
|
||||
@@ -486,9 +484,7 @@ void DrawViewSection::sectionExec(TopoDS_Shape& baseShape)
|
||||
|
||||
void DrawViewSection::makeSectionCut(const TopoDS_Shape& baseShape)
|
||||
{
|
||||
// Base::Console().Message("DVS::makeSectionCut() - %s - baseShape.IsNull:
|
||||
// %d\n",
|
||||
// getNameInDocument(), baseShape.IsNull());
|
||||
// Base::Console().Message("DVS::makeSectionCut() - %s - baseShape.IsNull:%d\n", Label.getValue(), baseShape.IsNull());
|
||||
|
||||
showProgressMessage(getNameInDocument(), "is making section cut");
|
||||
|
||||
@@ -638,8 +634,7 @@ void DrawViewSection::onSectionCutFinished()
|
||||
// activities that depend on updated geometry object
|
||||
void DrawViewSection::postHlrTasks(void)
|
||||
{
|
||||
// Base::Console().Message("DVS::postHlrTasks() - %s\n",
|
||||
// getNameInDocument());
|
||||
// Base::Console().Message("DVS::postHlrTasks() - %s\n", Label.getValue());
|
||||
|
||||
DrawViewPart::postHlrTasks();
|
||||
|
||||
@@ -1172,8 +1167,10 @@ gp_Ax2 DrawViewSection::getProjectionCS(const Base::Vector3d pt) const
|
||||
|
||||
std::vector<LineSet> DrawViewSection::getDrawableLines(int i)
|
||||
{
|
||||
// Base::Console().Message("DVS::getDrawableLines(%d) - lineSets: %d\n", i,
|
||||
// m_lineSets.size());
|
||||
// Base::Console().Message("DVS::getDrawableLines(%d) - lineSets: %d\n", i, m_lineSets.size());
|
||||
if (m_lineSets.empty()) {
|
||||
makeLineSets();
|
||||
}
|
||||
std::vector<LineSet> result;
|
||||
return DrawGeomHatch::getTrimmedLinesSection(this,
|
||||
m_lineSets,
|
||||
@@ -1236,7 +1233,7 @@ void DrawViewSection::setupObject()
|
||||
// create geometric hatch lines
|
||||
void DrawViewSection::makeLineSets(void)
|
||||
{
|
||||
// Base::Console().Message("DVS::makeLineSets()\n");
|
||||
// Base::Console().Message("DVS::makeLineSets()\n");
|
||||
if (PatIncluded.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1277,8 +1274,7 @@ void DrawViewSection::replaceSvgIncluded(std::string newSvgFile)
|
||||
|
||||
void DrawViewSection::replacePatIncluded(std::string newPatFile)
|
||||
{
|
||||
// Base::Console().Message("DVS::replacePatIncluded(%s)\n",
|
||||
// newPatFile.c_str());
|
||||
// Base::Console().Message("DVS::replacePatIncluded(%s)\n", newPatFile.c_str());
|
||||
if (newPatFile.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ std::vector<std::string> DrawViewSymbol::getEditableFields()
|
||||
// has "freecad:editable" attribute
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
|
||||
[&editables](QDomElement& tspan) -> bool {
|
||||
QString editableValue = tspan.firstChild().nodeValue();
|
||||
editables.emplace_back(editableValue.toStdString());
|
||||
@@ -154,7 +154,7 @@ void DrawViewSymbol::updateFieldsInSymbol()
|
||||
// has "freecad:editable" attribute
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
|
||||
[&symbolDocument, &editText, &count](QDomElement& tspanElement) -> bool {
|
||||
|
||||
if (count >= editText.size()) {
|
||||
|
||||
@@ -362,6 +362,17 @@ std::string LineGenerator::getLineStandardsBody()
|
||||
{
|
||||
int activeStandard = Preferences::lineStandard();
|
||||
std::vector<std::string> choices = getAvailableLineStandards();
|
||||
if (activeStandard < 0 ||
|
||||
(size_t) activeStandard >= choices.size()) {
|
||||
// there is a condition where the LineStandard parameter exists, but is -1 (the
|
||||
// qt value for no current index in a combobox). This is likely caused by an old
|
||||
// development version writing an unvalidated value. In this case, the existing but
|
||||
// invalid value will be returned. This is a temporary fix and can be removed for
|
||||
// production.
|
||||
// Preferences::lineStandard() will print a message about this every time it is called
|
||||
// (lots of messages!).
|
||||
activeStandard = 0;
|
||||
}
|
||||
return getBodyFromString(choices.at(activeStandard));
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <string>
|
||||
|
||||
# include <QApplication>
|
||||
# include <QString>
|
||||
#endif
|
||||
|
||||
@@ -422,6 +422,18 @@ bool Preferences::SectionUsePreviousCut()
|
||||
//! an index into the list of available line standards/version found in LineGroupDirectory
|
||||
int Preferences::lineStandard()
|
||||
{
|
||||
// there is a condition where the LineStandard parameter exists, but is -1 (the
|
||||
// qt value for no current index in a combobox). This is likely caused by an old
|
||||
// development version writing an unvalidated value. In this case, the
|
||||
// existing but invalid value will be returned. This is a temporary fix and
|
||||
// can be removed for production.
|
||||
// this message will appear many times if the parameter is invalid.
|
||||
int parameterValue = getPreferenceGroup("Standards")->GetInt("LineStandard", 1);
|
||||
if (parameterValue < 0) {
|
||||
Base::Console().Warning(qPrintable(QApplication::translate(
|
||||
"Preferences", "The LineStandard parameter is invalid. Using zero instead.", nullptr)));
|
||||
return 0;
|
||||
}
|
||||
return getPreferenceGroup("Standards")->GetInt("LineStandard", 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QDomDocument>
|
||||
#endif
|
||||
|
||||
#include "DrawUtil.h"
|
||||
#include "XMLQuery.h"
|
||||
|
||||
|
||||
@@ -51,7 +52,7 @@ static bool processElements(const QDomElement& element, const QString& queryStr,
|
||||
for(int i = 0; i < editable.count(); i++) {
|
||||
QDomNode node = editable.item(i);
|
||||
QDomElement element = node.toElement();
|
||||
if (element.hasAttribute(QString(QLatin1String("freecad:editable")))) {
|
||||
if (element.hasAttribute(QString(QLatin1String(FREECAD_ATTR_EDITABLE)))) {
|
||||
if (find_tspan) {
|
||||
element = element.firstChildElement();
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include <Mod/TechDraw/App/DrawPage.h>
|
||||
#include <Mod/TechDraw/App/DrawProjGroup.h>
|
||||
#include <Mod/TechDraw/App/DrawUtil.h>
|
||||
#include <Mod/TechDraw/App/DrawSVGTemplate.h>
|
||||
#include <Mod/TechDraw/App/DrawViewArch.h>
|
||||
#include <Mod/TechDraw/App/DrawViewClip.h>
|
||||
#include <Mod/TechDraw/App/DrawViewDetail.h>
|
||||
@@ -105,40 +106,33 @@ void CmdTechDrawPageDefault::activated(int iMsg)
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
QString templateFileName = Preferences::defaultTemplate();
|
||||
|
||||
std::string PageName = getUniqueObjectName("Page");
|
||||
std::string TemplateName = getUniqueObjectName("Template");
|
||||
|
||||
QFileInfo tfi(templateFileName);
|
||||
if (tfi.isReadable()) {
|
||||
Gui::WaitCursor wc;
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Drawing create page"));
|
||||
doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawPage', '%s')",
|
||||
PageName.c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawPage', 'Page', '%s')",
|
||||
PageName.c_str(), PageName.c_str());
|
||||
|
||||
doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawSVGTemplate', '%s')",
|
||||
TemplateName.c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawSVGTemplate', 'Template', '%s')",
|
||||
TemplateName.c_str(), TemplateName.c_str());
|
||||
auto page = dynamic_cast<TechDraw::DrawPage *>
|
||||
(getDocument()->addObject("TechDraw::DrawPage", "Page"));
|
||||
if (!page) {
|
||||
throw Base::TypeError("CmdTechDrawPageDefault - page not created");
|
||||
}
|
||||
page->translateLabel("DrawPage", "Page", page->getNameInDocument());
|
||||
|
||||
doCommand(Doc, "App.activeDocument().%s.Template = '%s'", TemplateName.c_str(),
|
||||
templateFileName.toStdString().c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.Template = App.activeDocument().%s",
|
||||
PageName.c_str(), TemplateName.c_str());
|
||||
auto svgTemplate = dynamic_cast<TechDraw::DrawSVGTemplate *>
|
||||
(getDocument()->addObject("TechDraw::DrawSVGTemplate", "Template"));
|
||||
if (!svgTemplate) {
|
||||
throw Base::TypeError("CmdTechDrawPageDefault - template not created");
|
||||
}
|
||||
svgTemplate->translateLabel("DrawSVGTemplate", "Template", svgTemplate->getNameInDocument());
|
||||
|
||||
page->Template.setValue(svgTemplate);
|
||||
svgTemplate->Template.setValue(templateFileName.toStdString());
|
||||
|
||||
updateActive();
|
||||
commitCommand();
|
||||
TechDraw::DrawPage* fp =
|
||||
dynamic_cast<TechDraw::DrawPage*>(getDocument()->getObject(PageName.c_str()));
|
||||
if (!fp) {
|
||||
throw Base::TypeError("CmdTechDrawPageDefault fp not found\n");
|
||||
}
|
||||
|
||||
Gui::ViewProvider* vp =
|
||||
Gui::Application::Instance->getDocument(getDocument())->getViewProvider(fp);
|
||||
TechDrawGui::ViewProviderPage* dvp = dynamic_cast<TechDrawGui::ViewProviderPage*>(vp);
|
||||
TechDrawGui::ViewProviderPage *dvp = dynamic_cast<TechDrawGui::ViewProviderPage *>
|
||||
(Gui::Application::Instance->getViewProvider(page));
|
||||
if (dvp) {
|
||||
dvp->show();
|
||||
}
|
||||
@@ -182,44 +176,33 @@ void CmdTechDrawPageTemplate::activated(int iMsg)
|
||||
return;
|
||||
}
|
||||
|
||||
std::string PageName = getUniqueObjectName("Page");
|
||||
std::string TemplateName = getUniqueObjectName("Template");
|
||||
|
||||
QFileInfo tfi(templateFileName);
|
||||
if (tfi.isReadable()) {
|
||||
Gui::WaitCursor wc;
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Drawing create page"));
|
||||
doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawPage', '%s')",
|
||||
PageName.c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawPage', 'Page', '%s')",
|
||||
PageName.c_str(), PageName.c_str());
|
||||
|
||||
// Create the Template Object to attach to the page
|
||||
doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawSVGTemplate', '%s')",
|
||||
TemplateName.c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawSVGTemplate', 'Template', '%s')",
|
||||
TemplateName.c_str(), TemplateName.c_str());
|
||||
auto page = dynamic_cast<TechDraw::DrawPage *>
|
||||
(getDocument()->addObject("TechDraw::DrawPage", "Page"));
|
||||
if (!page) {
|
||||
throw Base::TypeError("CmdTechDrawPageTemplate - page not created");
|
||||
}
|
||||
page->translateLabel("DrawPage", "Page", page->getNameInDocument());
|
||||
|
||||
//why is "Template" property set twice? -wf
|
||||
// once to set DrawSVGTemplate.Template to OS template file name
|
||||
templateFileName = Base::Tools::escapeEncodeFilename(templateFileName);
|
||||
doCommand(Doc, "App.activeDocument().%s.Template = \"%s\"", TemplateName.c_str(),
|
||||
templateFileName.toUtf8().constData());
|
||||
// once to set Page.Template to DrawSVGTemplate.Name
|
||||
doCommand(Doc, "App.activeDocument().%s.Template = App.activeDocument().%s",
|
||||
PageName.c_str(), TemplateName.c_str());
|
||||
// consider renaming DrawSVGTemplate.Template property?
|
||||
auto svgTemplate = dynamic_cast<TechDraw::DrawSVGTemplate *>
|
||||
(getDocument()->addObject("TechDraw::DrawSVGTemplate", "Template"));
|
||||
if (!svgTemplate) {
|
||||
throw Base::TypeError("CmdTechDrawPageTemplate - template not created");
|
||||
}
|
||||
svgTemplate->translateLabel("DrawSVGTemplate", "Template", svgTemplate->getNameInDocument());
|
||||
|
||||
page->Template.setValue(svgTemplate);
|
||||
svgTemplate->Template.setValue(templateFileName.toStdString());
|
||||
|
||||
updateActive();
|
||||
commitCommand();
|
||||
TechDraw::DrawPage* fp =
|
||||
dynamic_cast<TechDraw::DrawPage*>(getDocument()->getObject(PageName.c_str()));
|
||||
if (!fp) {
|
||||
throw Base::TypeError("CmdTechDrawNewPagePick fp not found\n");
|
||||
}
|
||||
Gui::ViewProvider* vp =
|
||||
Gui::Application::Instance->getDocument(getDocument())->getViewProvider(fp);
|
||||
TechDrawGui::ViewProviderPage* dvp = dynamic_cast<TechDrawGui::ViewProviderPage*>(vp);
|
||||
|
||||
TechDrawGui::ViewProviderPage *dvp = dynamic_cast<TechDrawGui::ViewProviderPage *>
|
||||
(Gui::Application::Instance->getViewProvider(page));
|
||||
if (dvp) {
|
||||
dvp->show();
|
||||
}
|
||||
|
||||
@@ -178,9 +178,9 @@ void QGISVGTemplate::createClickHandles()
|
||||
// XPath query to select all <text> nodes with "freecad:editable" attribute
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]"),
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]"),
|
||||
[&](QDomElement& textElement) -> bool {
|
||||
QString name = textElement.attribute(QString::fromUtf8("freecad:editable"));
|
||||
QString name = textElement.attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE));
|
||||
double x = Rez::guiX(
|
||||
textElement.attribute(QString::fromUtf8("x"), QString::fromUtf8("0.0")).toDouble());
|
||||
double y = Rez::guiX(
|
||||
|
||||
@@ -47,6 +47,7 @@ void QGIViewSection::draw()
|
||||
|
||||
void QGIViewSection::drawSectionFace()
|
||||
{
|
||||
// Base::Console().Message("QGIVS::drawSectionFace()\n");
|
||||
auto section( dynamic_cast<TechDraw::DrawViewSection *>(getViewObject()) );
|
||||
if (!section) {
|
||||
return;
|
||||
@@ -85,13 +86,14 @@ void QGIViewSection::drawSectionFace()
|
||||
return;
|
||||
}
|
||||
|
||||
QColor faceColor = (sectionVp->CutSurfaceColor.getValue()).asValue<QColor>();
|
||||
faceColor.setAlpha((100 - sectionVp->CutSurfaceTransparency.getValue())*255/100);
|
||||
newFace->setFillColor(faceColor);
|
||||
|
||||
if (section->CutSurfaceDisplay.isValue("Color")) {
|
||||
newFace->isHatched(true);
|
||||
QColor faceColor = (sectionVp->CutSurfaceColor.getValue()).asValue<QColor>();
|
||||
faceColor.setAlpha((100 - sectionVp->CutSurfaceTransparency.getValue())*255/100);
|
||||
newFace->setFillColor(faceColor);
|
||||
newFace->setFillMode(faceColor.alpha() ? QGIFace::PlainFill : QGIFace::NoFill);
|
||||
} else if (section->CutSurfaceDisplay.isValue("SvgHatch")) {
|
||||
newFace->isHatched(true);
|
||||
newFace->setFillMode(QGIFace::SvgFill);
|
||||
newFace->setHatchColor(sectionVp->HatchColor.getValue());
|
||||
newFace->setHatchScale(section->HatchScale.getValue());
|
||||
@@ -104,9 +106,9 @@ void QGIViewSection::drawSectionFace()
|
||||
newFace->setFillMode(QGIFace::GeomHatchFill);
|
||||
newFace->setHatchColor(sectionVp->GeomHatchColor.getValue());
|
||||
newFace->setHatchScale(section->HatchScale.getValue());
|
||||
newFace->setLineWeight(sectionVp->WeightPattern.getValue());
|
||||
newFace->setHatchRotation(section->HatchRotation.getValue());
|
||||
newFace->setHatchOffset(section->HatchOffset.getValue());
|
||||
newFace->setLineWeight(sectionVp->WeightPattern.getValue());
|
||||
std::vector<TechDraw::LineSet> lineSets = section->getDrawableLines(i);
|
||||
if (!lineSets.empty()) {
|
||||
newFace->clearLineSets();
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <Mod/TechDraw/App/DrawPage.h>
|
||||
#include <Mod/TechDraw/App/DrawProjGroupItem.h>
|
||||
#include <Mod/TechDraw/App/DrawProjGroup.h>
|
||||
#include <Mod/TechDraw/App/DrawUtil.h>
|
||||
|
||||
#include "TaskProjGroup.h"
|
||||
#include "ui_TaskProjGroup.h"
|
||||
@@ -306,81 +307,6 @@ void TaskProjGroup::spacingChanged()
|
||||
multiView->recomputeFeature();
|
||||
}
|
||||
|
||||
std::pair<int, int> TaskProjGroup::nearestFraction(const double val, const long int maxDenom) const
|
||||
{
|
||||
/*
|
||||
** find rational approximation to given real number
|
||||
** David Eppstein / UC Irvine / 8 Aug 1993
|
||||
**
|
||||
** With corrections from Arno Formella, May 2008
|
||||
** and additional fiddles by WF 2017
|
||||
** usage: a.out r d
|
||||
** r is real number to approx
|
||||
** d is the maximum denominator allowed
|
||||
**
|
||||
** based on the theory of continued fractions
|
||||
** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
|
||||
** then best approximation is found by truncating this series
|
||||
** (with some adjustments in the last term).
|
||||
**
|
||||
** Note the fraction can be recovered as the first column of the matrix
|
||||
** ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
|
||||
** ( 1 0 ) ( 1 0 ) ( 1 0 )
|
||||
** Instead of keeping the sequence of continued fraction terms,
|
||||
** we just keep the last partial product of these matrices.
|
||||
*/
|
||||
std::pair<int, int> result;
|
||||
long m[2][2];
|
||||
long maxden = maxDenom;
|
||||
long ai;
|
||||
double x = val;
|
||||
double startx = x;
|
||||
|
||||
/* initialize matrix */
|
||||
m[0][0] = m[1][1] = 1;
|
||||
m[0][1] = m[1][0] = 0;
|
||||
|
||||
/* loop finding terms until denom gets too big */
|
||||
while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) {
|
||||
long t;
|
||||
t = m[0][0] * ai + m[0][1];
|
||||
m[0][1] = m[0][0];
|
||||
m[0][0] = t;
|
||||
t = m[1][0] * ai + m[1][1];
|
||||
m[1][1] = m[1][0];
|
||||
m[1][0] = t;
|
||||
if(x == (double) ai)
|
||||
break; // AF: division by zero
|
||||
x = 1/(x - (double) ai);
|
||||
if(x > (double) std::numeric_limits<int>::max())
|
||||
break; // AF: representation failure
|
||||
}
|
||||
|
||||
/* now remaining x is between 0 and 1/ai */
|
||||
/* approx as either 0 or 1/m where m is max that will fit in maxden */
|
||||
/* first try zero */
|
||||
double error1 = startx - ((double) m[0][0] / (double) m[1][0]);
|
||||
int n1 = m[0][0];
|
||||
int d1 = m[1][0];
|
||||
|
||||
/* now try other possibility */
|
||||
ai = (maxden - m[1][1]) / m[1][0];
|
||||
m[0][0] = m[0][0] * ai + m[0][1];
|
||||
m[1][0] = m[1][0] * ai + m[1][1];
|
||||
double error2 = startx - ((double) m[0][0] / (double) m[1][0]);
|
||||
int n2 = m[0][0];
|
||||
int d2 = m[1][0];
|
||||
|
||||
if (std::fabs(error1) <= std::fabs(error2)) {
|
||||
result.first = n1;
|
||||
result.second = d1;
|
||||
} else {
|
||||
result.first = n2;
|
||||
result.second = d2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TaskProjGroup::updateTask()
|
||||
{
|
||||
// Update the scale type
|
||||
@@ -398,7 +324,7 @@ void TaskProjGroup::setFractionalScale(double newScale)
|
||||
{
|
||||
blockUpdate = true;
|
||||
|
||||
std::pair<int, int> fraction = nearestFraction(newScale);
|
||||
std::pair<int, int> fraction = DrawUtil::nearestFraction(newScale);
|
||||
|
||||
ui->sbScaleNum->setValue(fraction.first);
|
||||
ui->sbScaleDen->setValue(fraction.second);
|
||||
|
||||
@@ -60,7 +60,6 @@ public:
|
||||
QPushButton* btnApply);
|
||||
|
||||
void updateTask();
|
||||
std::pair<int, int> nearestFraction(double val, long int maxDenom = 999) const;
|
||||
// Sets the numerator and denominator widgets to match newScale
|
||||
void setFractionalScale(double newScale);
|
||||
void setCreateMode(bool mode) { m_createMode = mode;}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <App/DocumentObject.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Gui/CommandT.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/MainWindow.h>
|
||||
#include <Gui/ViewProviderDocumentObject.h>
|
||||
@@ -262,6 +263,16 @@ void ViewProviderPage::unsetEdit(int ModNum)
|
||||
|
||||
bool ViewProviderPage::doubleClicked(void)
|
||||
{
|
||||
// assure the TechDraw workbench
|
||||
if (App::GetApplication()
|
||||
.GetUserParameter()
|
||||
.GetGroup("BaseApp")
|
||||
->GetGroup("Preferences")
|
||||
->GetGroup("Mod/TechDraw")
|
||||
->GetBool("SwitchToWB", true)) {
|
||||
Gui::Command::assureWorkbench("TechDrawWorkbench");
|
||||
}
|
||||
|
||||
show();
|
||||
if (m_mdiView) {
|
||||
Gui::getMainWindow()->setActiveWindow(m_mdiView);
|
||||
|
||||
@@ -71,6 +71,22 @@ bool ViewProviderPageExtension::extensionCanDropObject(App::DocumentObject* obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewProviderPageExtension::extensionCanDropObjectEx(App::DocumentObject* obj, App::DocumentObject* owner,
|
||||
const char* subname,
|
||||
const std::vector<std::string>& elements) const
|
||||
{
|
||||
//only DrawView objects can live on pages (except special case Template)
|
||||
if (obj->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) {
|
||||
return true;
|
||||
}
|
||||
if (obj->isDerivedFrom(TechDraw::DrawTemplate::getClassTypeId())) {
|
||||
//don't let another extension try to drop templates
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ViewProviderPageExtension::extensionDropObject(App::DocumentObject* obj)
|
||||
{
|
||||
if (obj->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) {
|
||||
|
||||
@@ -46,6 +46,9 @@ public:
|
||||
void extensionDragObject(App::DocumentObject*) override;
|
||||
bool extensionCanDropObjects() const override;
|
||||
bool extensionCanDropObject(App::DocumentObject*) const override;
|
||||
bool extensionCanDropObjectEx(App::DocumentObject* obj, App::DocumentObject* owner,
|
||||
const char* subname,
|
||||
const std::vector<std::string>& elements) const override;
|
||||
void extensionDropObject(App::DocumentObject*) override;
|
||||
|
||||
void dropObject(App::DocumentObject* docObj);
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
#endif
|
||||
|
||||
#include <App/DocumentObject.h>
|
||||
#include <Mod/TechDraw/App/DrawPage.h>
|
||||
#include <Mod/TechDraw/App/DrawProjGroupItem.h>
|
||||
|
||||
#include "ViewProviderViewClip.h"
|
||||
|
||||
using namespace TechDrawGui;
|
||||
@@ -104,3 +107,40 @@ TechDraw::DrawViewClip* ViewProviderViewClip::getObject() const
|
||||
{
|
||||
return getViewObject();
|
||||
}
|
||||
|
||||
|
||||
void ViewProviderViewClip::dragObject(App::DocumentObject* docObj)
|
||||
{
|
||||
if (!docObj->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dv = static_cast<TechDraw::DrawView*>(docObj);
|
||||
|
||||
getObject()->removeView(dv);
|
||||
}
|
||||
|
||||
void ViewProviderViewClip::dropObject(App::DocumentObject* docObj)
|
||||
{
|
||||
if (docObj->isDerivedFrom(TechDraw::DrawProjGroupItem::getClassTypeId())) {
|
||||
//DPGI can not be dropped onto the Page as it belongs to DPG, not Page
|
||||
return;
|
||||
}
|
||||
if (!docObj->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dv = static_cast<TechDraw::DrawView*>(docObj);
|
||||
TechDraw::DrawPage* pageClip = getObject()->findParentPage();
|
||||
TechDraw::DrawPage* pageView = dv->findParentPage();
|
||||
if (!pageClip || !pageView) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pageClip != pageView) {
|
||||
pageView->removeView(dv);
|
||||
pageClip->addView(dv);
|
||||
}
|
||||
|
||||
getObject()->addView(dv);
|
||||
}
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
|
||||
bool canDelete(App::DocumentObject* obj) const override;
|
||||
|
||||
void dragObject(App::DocumentObject* docObj) override;
|
||||
void dropObject(App::DocumentObject* docObj) override;
|
||||
};
|
||||
} // namespace TechDrawGui
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ using namespace TechDrawGui;
|
||||
qApp->translate("Workbench", "TechDraw Annotation");
|
||||
qApp->translate("Workbench", "TechDraw Attributes");
|
||||
qApp->translate("Workbench", "TechDraw Centerlines");
|
||||
qApp->translate("Workbench", "TechDraw Clips");
|
||||
qApp->translate("Workbench", "TechDraw Decoration");
|
||||
qApp->translate("Workbench", "TechDraw Dimensions");
|
||||
qApp->translate("Workbench", "TechDraw Extend Dimensions");
|
||||
@@ -217,11 +216,11 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
*views << "TechDraw_ComplexSection";
|
||||
*views << "TechDraw_DetailView";
|
||||
*views << "TechDraw_ProjectionGroup";
|
||||
*views << "TechDraw_ClipGroup";
|
||||
*views << "Separator";
|
||||
*views << "TechDraw_Symbol";
|
||||
*views << "TechDraw_Image";
|
||||
*views << "Separator";
|
||||
*views << "TechDraw_MoveView";
|
||||
*views << "TechDraw_ShareView";
|
||||
*views << "Separator";
|
||||
*views << "TechDraw_ToggleFrame";
|
||||
@@ -236,13 +235,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
*other << "TechDraw_ArchView";
|
||||
*other << "TechDraw_SpreadsheetView";
|
||||
|
||||
// clip groups
|
||||
Gui::MenuItem* clips = new Gui::MenuItem;
|
||||
clips->setCommand("Clipped Views");
|
||||
*clips << "TechDraw_ClipGroup";
|
||||
*clips << "TechDraw_ClipGroupAdd";
|
||||
*clips << "TechDraw_ClipGroupRemove";
|
||||
|
||||
// hatching
|
||||
Gui::MenuItem* hatch = new Gui::MenuItem;
|
||||
hatch->setCommand("Hatching");
|
||||
@@ -264,8 +256,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
*draw << "Separator";
|
||||
*draw << other;
|
||||
*draw << "Separator";
|
||||
*draw << clips;
|
||||
*draw << "Separator";
|
||||
*draw << dimensions;
|
||||
*draw << "Separator";
|
||||
*draw << hatch;
|
||||
@@ -308,16 +298,10 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
|
||||
*views << "TechDraw_DraftView";
|
||||
*views << "TechDraw_ArchView";
|
||||
*views << "TechDraw_SpreadsheetView";
|
||||
*views << "TechDraw_MoveView";
|
||||
*views << "TechDraw_ClipGroup";
|
||||
*views << "TechDraw_ShareView";
|
||||
*views << "TechDraw_ProjectShape";
|
||||
|
||||
Gui::ToolBarItem* clips = new Gui::ToolBarItem(root);
|
||||
clips->setCommand("TechDraw Clips");
|
||||
*clips << "TechDraw_ClipGroup";
|
||||
*clips << "TechDraw_ClipGroupAdd";
|
||||
*clips << "TechDraw_ClipGroupRemove";
|
||||
|
||||
Gui::ToolBarItem* stacking = new Gui::ToolBarItem(root);
|
||||
stacking->setCommand("TechDraw Stacking");
|
||||
*stacking << "TechDraw_StackGroup";
|
||||
@@ -422,16 +406,10 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const
|
||||
*views << "TechDraw_DetailView";
|
||||
*views << "TechDraw_DraftView";
|
||||
*views << "TechDraw_SpreadsheetView";
|
||||
*views << "TechDraw_MoveView";
|
||||
*views << "TechDraw_ClipGroup";
|
||||
*views << "TechDraw_ShareView";
|
||||
*views << "TechDraw_ProjectShape";
|
||||
|
||||
Gui::ToolBarItem* clips = new Gui::ToolBarItem(root);
|
||||
clips->setCommand("TechDraw Clips");
|
||||
*clips << "TechDraw_ClipGroup";
|
||||
*clips << "TechDraw_ClipGroupAdd";
|
||||
*clips << "TechDraw_ClipGroupRemove";
|
||||
|
||||
Gui::ToolBarItem* stacking = new Gui::ToolBarItem(root);
|
||||
stacking->setCommand("TechDraw Stacking");
|
||||
*stacking << "TechDraw_StackGroup";
|
||||
|
||||
@@ -35,53 +35,86 @@ import os
|
||||
|
||||
translate = App.Qt.translate
|
||||
|
||||
|
||||
class TaskHoleShaftFit:
|
||||
def __init__(self,sel):
|
||||
def __init__(self, sel):
|
||||
|
||||
loose = translate("TechDraw_HoleShaftFit", "loose fit")
|
||||
snug = translate("TechDraw_HoleShaftFit", "snug fit")
|
||||
press = translate("TechDraw_HoleShaftFit", "press fit")
|
||||
self.isHole = True
|
||||
self.sel = sel
|
||||
self.holeValues = [["h9","D10",loose],["h9","E9",loose],["h9","F8",loose],["h6","G7",loose],
|
||||
["c11","H11",loose],["f7","H8",loose],["h6","H7",loose],["h7","H8",loose],
|
||||
["k6","H7",snug],["n6","H7",snug],["r6","H7",press],["s6","H7",press],
|
||||
["h6","K7",snug],["h6","N7",snug],["h6","R7",press],["h6","S7",press]]
|
||||
self.shaftValues = [["H11","c11",loose],["H8","f7",loose],["H7","h6",loose],["H8","h7",loose],
|
||||
["D10","h9",loose],["E9","h9",loose],["F8","h9",loose],["G7","h6",loose],
|
||||
["K7","h6",snug],["N7","h6",snug],["R7","h6",press],["S7","h6",press],
|
||||
["H7","k6",snug],["H7","n6",snug],["H7","r6",press],["H7","s6",press]]
|
||||
self.holeValues = [
|
||||
["h9", "D10", loose],
|
||||
["h9", "E9", loose],
|
||||
["h9", "F8", loose],
|
||||
["h6", "G7", loose],
|
||||
["c11", "H11", loose],
|
||||
["f7", "H8", loose],
|
||||
["h6", "H7", loose],
|
||||
["h7", "H8", loose],
|
||||
["k6", "H7", snug],
|
||||
["n6", "H7", snug],
|
||||
["r6", "H7", press],
|
||||
["s6", "H7", press],
|
||||
["h6", "K7", snug],
|
||||
["h6", "N7", snug],
|
||||
["h6", "R7", press],
|
||||
["h6", "S7", press],
|
||||
]
|
||||
self.shaftValues = [
|
||||
["H11", "c11", loose],
|
||||
["H8", "f7", loose],
|
||||
["H7", "h6", loose],
|
||||
["H8", "h7", loose],
|
||||
["D10", "h9", loose],
|
||||
["E9", "h9", loose],
|
||||
["F8", "h9", loose],
|
||||
["G7", "h6", loose],
|
||||
["K7", "h6", snug],
|
||||
["N7", "h6", snug],
|
||||
["R7", "h6", press],
|
||||
["S7", "h6", press],
|
||||
["H7", "k6", snug],
|
||||
["H7", "n6", snug],
|
||||
["H7", "r6", press],
|
||||
["H7", "s6", press],
|
||||
]
|
||||
|
||||
self._uiPath = App.getHomePath()
|
||||
self._uiPath = os.path.join(self._uiPath, "Mod/TechDraw/TechDrawTools/Gui/TaskHoleShaftFit.ui")
|
||||
self._uiPath = os.path.join(
|
||||
self._uiPath, "Mod/TechDraw/TechDrawTools/Gui/TaskHoleShaftFit.ui"
|
||||
)
|
||||
self.form = Gui.PySideUic.loadUi(self._uiPath)
|
||||
|
||||
self.form.setWindowTitle(translate("TechDraw_HoleShaftFit", "Hole / Shaft Fit ISO 286"))
|
||||
self.form.setWindowTitle(
|
||||
translate("TechDraw_HoleShaftFit", "Hole / Shaft Fit ISO 286")
|
||||
)
|
||||
|
||||
self.form.rbHoleBase.clicked.connect(partial(self.on_HoleShaftChanged,True))
|
||||
self.form.rbShaftBase.clicked.connect(partial(self.on_HoleShaftChanged,False))
|
||||
self.form.rbHoleBase.clicked.connect(partial(self.on_HoleShaftChanged, True))
|
||||
self.form.rbShaftBase.clicked.connect(partial(self.on_HoleShaftChanged, False))
|
||||
self.form.cbField.currentIndexChanged.connect(self.on_FieldChanged)
|
||||
|
||||
def setHoleFields(self):
|
||||
'''set hole fields in the combo box'''
|
||||
"""set hole fields in the combo box"""
|
||||
for i in range(self.form.cbField.count()):
|
||||
self.form.cbField.removeItem(0)
|
||||
for value in self.holeValues:
|
||||
self.form.cbField.addItem(value[1])
|
||||
self.form.lbBaseField.setText(' '+self.holeValues[0][0]+" /")
|
||||
self.form.lbBaseField.setText(" " + self.holeValues[0][0] + " /")
|
||||
self.form.lbFitType.setText(self.holeValues[0][2])
|
||||
|
||||
def setShaftFields(self):
|
||||
'''set shaft fields in the combo box'''
|
||||
"""set shaft fields in the combo box"""
|
||||
for i in range(self.form.cbField.count()):
|
||||
self.form.cbField.removeItem(0)
|
||||
for value in self.shaftValues:
|
||||
self.form.cbField.addItem(value[1])
|
||||
self.form.lbBaseField.setText(' '+self.shaftValues[0][0]+" /")
|
||||
self.form.lbBaseField.setText(" " + self.shaftValues[0][0] + " /")
|
||||
self.form.lbFitType.setText(self.shaftValues[0][2])
|
||||
|
||||
def on_HoleShaftChanged(self,isHole):
|
||||
'''slot: change the used base fit hole/shaft'''
|
||||
def on_HoleShaftChanged(self, isHole):
|
||||
"""slot: change the used base fit hole/shaft"""
|
||||
if isHole:
|
||||
self.isHole = isHole
|
||||
self.setShaftFields()
|
||||
@@ -90,17 +123,21 @@ class TaskHoleShaftFit:
|
||||
self.setHoleFields()
|
||||
|
||||
def on_FieldChanged(self):
|
||||
'''slot: change of the desired field'''
|
||||
"""slot: change of the desired field"""
|
||||
currentIndex = self.form.cbField.currentIndex()
|
||||
if self.isHole:
|
||||
self.form.lbBaseField.setText(' '+self.shaftValues[currentIndex][0]+" /")
|
||||
self.form.lbBaseField.setText(
|
||||
" " + self.shaftValues[currentIndex][0] + " /"
|
||||
)
|
||||
self.form.lbFitType.setText(self.shaftValues[currentIndex][2])
|
||||
else:
|
||||
self.form.lbBaseField.setText(' '+self.holeValues[currentIndex][0]+" /")
|
||||
self.form.lbBaseField.setText(
|
||||
" " + self.holeValues[currentIndex][0] + " /"
|
||||
)
|
||||
self.form.lbFitType.setText(self.holeValues[currentIndex][2])
|
||||
|
||||
def accept(self):
|
||||
'''slot: OK pressed'''
|
||||
"""slot: OK pressed"""
|
||||
currentIndex = self.form.cbField.currentIndex()
|
||||
if self.isHole:
|
||||
selectedField = self.shaftValues[currentIndex][1]
|
||||
@@ -111,77 +148,726 @@ class TaskHoleShaftFit:
|
||||
dim = self.sel[0].Object
|
||||
value = dim.getRawValue()
|
||||
iso = ISO286()
|
||||
iso.calculate(value,fieldChar,quality)
|
||||
iso.calculate(value, fieldChar, quality)
|
||||
rangeValues = iso.getValues()
|
||||
mainFormat = dim.FormatSpec
|
||||
dim.FormatSpec = mainFormat+' '+selectedField
|
||||
dim.FormatSpec = mainFormat + " " + selectedField
|
||||
dim.EqualTolerance = False
|
||||
dim.FormatSpecOverTolerance = '(%-0.6w)'
|
||||
dim.FormatSpecUnderTolerance = '(%-0.6w)'
|
||||
dim.OverTolerance = rangeValues[0]
|
||||
dim.UnderTolerance = rangeValues[1]
|
||||
if dim.OverTolerance < 0:
|
||||
dim.FormatSpecOverTolerance = "(%-0.6w)"
|
||||
elif dim.OverTolerance > 0:
|
||||
dim.FormatSpecOverTolerance = "(+%-0.6w)"
|
||||
else:
|
||||
dim.FormatSpecOverTolerance = "( %-0.6w)"
|
||||
if dim.UnderTolerance < 0:
|
||||
dim.FormatSpecUnderTolerance = "(%-0.6w)"
|
||||
elif dim.UnderTolerance > 0:
|
||||
dim.FormatSpecUnderTolerance = "(+%-0.6w)"
|
||||
else:
|
||||
dim.FormatSpecUnderTolerance = "( %-0.6w)"
|
||||
Gui.Control.closeDialog()
|
||||
|
||||
def reject(self):
|
||||
return True
|
||||
|
||||
class ISO286:
|
||||
'''This class represents a subset of the ISO 286 standard'''
|
||||
|
||||
def getNominalRange(self,measureValue):
|
||||
'''return index of selected nominal range field, 0 < measureValue < 500 mm'''
|
||||
measureRanges = [0,3,6,10,14,18,24,30,40,50,65,80,100,120,140,160,180,200,225,250,280,315,355,400,450,500]
|
||||
class ISO286:
|
||||
"""This class represents a subset of the ISO 286 standard"""
|
||||
|
||||
def getNominalRange(self, measureValue):
|
||||
"""return index of selected nominal range field, 0 < measureValue < 500 mm"""
|
||||
measureRanges = [
|
||||
0,
|
||||
3,
|
||||
6,
|
||||
10,
|
||||
14,
|
||||
18,
|
||||
24,
|
||||
30,
|
||||
40,
|
||||
50,
|
||||
65,
|
||||
80,
|
||||
100,
|
||||
120,
|
||||
140,
|
||||
160,
|
||||
180,
|
||||
200,
|
||||
225,
|
||||
250,
|
||||
280,
|
||||
315,
|
||||
355,
|
||||
400,
|
||||
450,
|
||||
500,
|
||||
]
|
||||
index = 1
|
||||
while measureValue > measureRanges[index]:
|
||||
index = index+1
|
||||
return index-1
|
||||
index = index + 1
|
||||
return index - 1
|
||||
|
||||
def getITValue(self,valueQuality,valueNominalRange):
|
||||
'''return IT-value (value of quality in micrometers)'''
|
||||
'''tables IT6 to IT11 from 0 to 500 mm'''
|
||||
IT6 = [6,8,9,11,11,13,13,16,16,19,19,22,22,25,25,25,29,29,29,32,32,36,36,40,40]
|
||||
IT7 = [10,12,15,18,18,21,21,25,25,30,30,35,35,40,40,40,46,46,46,52,52,57,57,63,63]
|
||||
IT8 = [14,18,22,27,27,33,33,39,39,46,46,54,54,63,63,63,72,72,72,81,81,89,89,97,97]
|
||||
IT9 = [25,30,36,43,43,52,52,62,62,74,74,87,87,100,100,100,115,115,115,130,130,140,140,155,155]
|
||||
IT10 = [40,48,58,70,70,84,84,100,100,120,120,140,140,160,160,160,185,185,185,210,210,230,230,250,250]
|
||||
IT11 = [60,75,90,110,110,130,130,160,160,190,190,220,220,250,250,250,290,290,290,320,320,360,360,400,400]
|
||||
qualityTable = [IT6,IT7,IT8,IT9,IT10,IT11]
|
||||
return qualityTable[valueQuality-6][valueNominalRange]
|
||||
def getITValue(self, valueQuality, valueNominalRange):
|
||||
"""return IT-value (value of quality in micrometers)"""
|
||||
"""tables IT6 to IT11 from 0 to 500 mm"""
|
||||
IT6 = [
|
||||
6,
|
||||
8,
|
||||
9,
|
||||
11,
|
||||
11,
|
||||
13,
|
||||
13,
|
||||
16,
|
||||
16,
|
||||
19,
|
||||
19,
|
||||
22,
|
||||
22,
|
||||
25,
|
||||
25,
|
||||
25,
|
||||
29,
|
||||
29,
|
||||
29,
|
||||
32,
|
||||
32,
|
||||
36,
|
||||
36,
|
||||
40,
|
||||
40,
|
||||
]
|
||||
IT7 = [
|
||||
10,
|
||||
12,
|
||||
15,
|
||||
18,
|
||||
18,
|
||||
21,
|
||||
21,
|
||||
25,
|
||||
25,
|
||||
30,
|
||||
30,
|
||||
35,
|
||||
35,
|
||||
40,
|
||||
40,
|
||||
40,
|
||||
46,
|
||||
46,
|
||||
46,
|
||||
52,
|
||||
52,
|
||||
57,
|
||||
57,
|
||||
63,
|
||||
63,
|
||||
]
|
||||
IT8 = [
|
||||
14,
|
||||
18,
|
||||
22,
|
||||
27,
|
||||
27,
|
||||
33,
|
||||
33,
|
||||
39,
|
||||
39,
|
||||
46,
|
||||
46,
|
||||
54,
|
||||
54,
|
||||
63,
|
||||
63,
|
||||
63,
|
||||
72,
|
||||
72,
|
||||
72,
|
||||
81,
|
||||
81,
|
||||
89,
|
||||
89,
|
||||
97,
|
||||
97,
|
||||
]
|
||||
IT9 = [
|
||||
25,
|
||||
30,
|
||||
36,
|
||||
43,
|
||||
43,
|
||||
52,
|
||||
52,
|
||||
62,
|
||||
62,
|
||||
74,
|
||||
74,
|
||||
87,
|
||||
87,
|
||||
100,
|
||||
100,
|
||||
100,
|
||||
115,
|
||||
115,
|
||||
115,
|
||||
130,
|
||||
130,
|
||||
140,
|
||||
140,
|
||||
155,
|
||||
155,
|
||||
]
|
||||
IT10 = [
|
||||
40,
|
||||
48,
|
||||
58,
|
||||
70,
|
||||
70,
|
||||
84,
|
||||
84,
|
||||
100,
|
||||
100,
|
||||
120,
|
||||
120,
|
||||
140,
|
||||
140,
|
||||
160,
|
||||
160,
|
||||
160,
|
||||
185,
|
||||
185,
|
||||
185,
|
||||
210,
|
||||
210,
|
||||
230,
|
||||
230,
|
||||
250,
|
||||
250,
|
||||
]
|
||||
IT11 = [
|
||||
60,
|
||||
75,
|
||||
90,
|
||||
110,
|
||||
110,
|
||||
130,
|
||||
130,
|
||||
160,
|
||||
160,
|
||||
190,
|
||||
190,
|
||||
220,
|
||||
220,
|
||||
250,
|
||||
250,
|
||||
250,
|
||||
290,
|
||||
290,
|
||||
290,
|
||||
320,
|
||||
320,
|
||||
360,
|
||||
360,
|
||||
400,
|
||||
400,
|
||||
]
|
||||
qualityTable = [IT6, IT7, IT8, IT9, IT10, IT11]
|
||||
return qualityTable[valueQuality - 6][valueNominalRange]
|
||||
|
||||
def getFieldValue(self,fieldCharacter,valueNominalRange):
|
||||
'''return es or ES value of the field in micrometers'''
|
||||
cField = [-60,-70,-80,-95,-95,-110,-110,-120,-130,-140,-150,-170,-180,-200,-210,-230,-240,-260,-280,-300,-330,-360,-400,-440,-480]
|
||||
fField = [-6,-10,-13,-16,-16,-20,-20,-25,-25,-30,-30,-36,-36,-43,-43,-43,-50,-50,-50,-56,-56,-62,-62,-68,-68]
|
||||
gField = [-2,-4,-5,-6,-6,-7,-7,-9,-9,-10,-10,-12,-12,-14,-14,-14,-15,-15,-15,-17,-17,-18,-18,-20,-20]
|
||||
hField = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
||||
kField = [6,9,10,12,12,15,15,18,18,21,21,25,25,28,28,28,33,33,33,36,36,40,40,45,45]
|
||||
nField = [10,16,19,23,23,28,28,33,33,39,39,45,45,52,52,60,60,66,66,73,73,80,80]
|
||||
rField = [16,23,28,34,34,41,41,50,50,60,62,73,76,88,90,93,106,109,113,126,130,144,150,166,172]
|
||||
sField = [20,27,32,39,39,48,48,59,59,72,78,93,101,117,125,133,151,159,169,190,202,226,244,272,292]
|
||||
DField = [60,78,98,120,120,149,149,180,180,220,220,260,260,305,305,305,355,355,355,400,400,440,440,480,480]
|
||||
EField = [39,50,61,75,75,92,92,112,112,134,134,159,159,185,185,185,215,215,215,240,240,265,265,290,290]
|
||||
FField = [20,28,35,43,43,53,53,64,64,76,76,90,90,106,106,106,122,122,122,137,137,151,151,165,165]
|
||||
GField = [12,16,20,24,24,28,28,34,34,40,40,47,47,54,54,54,61,61,61,69,69,75,75,83,83]
|
||||
HField = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
||||
KField = [0,3,5,6,6,6,6,7,7,9,9,10,10,12,12,12,13,13,13,16,16,17,17,18,18]
|
||||
NField = [-4,-4,-4,-5,-5,-7,-7,-8,-8,-9,-9,-10,-10,-12,-12,-12,-14,-14,-14,-14,-14,-16,-16,-17,-17]
|
||||
RField = [-10,-11,-13,-16,-16,-20,-20,-25,-25,-30,-32,-38,-41,-48,-50,-53,-60,-63,-67,-74,-78,-87,-93,-103,-109]
|
||||
SField = [-14,-15,-17,-21,-21,-27,-27,-34,-34,-42,-48,-58,-66,-77,-85,-93,-105,-113,-123,-138,-150,-169,-187,-209,-229]
|
||||
fieldDict = {'c':cField,'f':fField,'g':gField,'h':hField,'k':kField,'n':nField,'r':rField,'s':sField,
|
||||
'D':DField,'E':EField,'F':FField,'G':GField,'H':HField,'K':KField,'N':NField,'R':RField,'S':SField}
|
||||
def getFieldValue(self, fieldCharacter, valueNominalRange):
|
||||
"""return es or ES value of the field in micrometers"""
|
||||
cField = [
|
||||
-60,
|
||||
-70,
|
||||
-80,
|
||||
-95,
|
||||
-95,
|
||||
-110,
|
||||
-110,
|
||||
-120,
|
||||
-130,
|
||||
-140,
|
||||
-150,
|
||||
-170,
|
||||
-180,
|
||||
-200,
|
||||
-210,
|
||||
-230,
|
||||
-240,
|
||||
-260,
|
||||
-280,
|
||||
-300,
|
||||
-330,
|
||||
-360,
|
||||
-400,
|
||||
-440,
|
||||
-480,
|
||||
]
|
||||
fField = [
|
||||
-6,
|
||||
-10,
|
||||
-13,
|
||||
-16,
|
||||
-16,
|
||||
-20,
|
||||
-20,
|
||||
-25,
|
||||
-25,
|
||||
-30,
|
||||
-30,
|
||||
-36,
|
||||
-36,
|
||||
-43,
|
||||
-43,
|
||||
-43,
|
||||
-50,
|
||||
-50,
|
||||
-50,
|
||||
-56,
|
||||
-56,
|
||||
-62,
|
||||
-62,
|
||||
-68,
|
||||
-68,
|
||||
]
|
||||
gField = [
|
||||
-2,
|
||||
-4,
|
||||
-5,
|
||||
-6,
|
||||
-6,
|
||||
-7,
|
||||
-7,
|
||||
-9,
|
||||
-9,
|
||||
-10,
|
||||
-10,
|
||||
-12,
|
||||
-12,
|
||||
-14,
|
||||
-14,
|
||||
-14,
|
||||
-15,
|
||||
-15,
|
||||
-15,
|
||||
-17,
|
||||
-17,
|
||||
-18,
|
||||
-18,
|
||||
-20,
|
||||
-20,
|
||||
]
|
||||
hField = [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
kField = [
|
||||
6,
|
||||
9,
|
||||
10,
|
||||
12,
|
||||
12,
|
||||
15,
|
||||
15,
|
||||
18,
|
||||
18,
|
||||
21,
|
||||
21,
|
||||
25,
|
||||
25,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
33,
|
||||
33,
|
||||
33,
|
||||
36,
|
||||
36,
|
||||
40,
|
||||
40,
|
||||
45,
|
||||
45,
|
||||
]
|
||||
nField = [
|
||||
10,
|
||||
16,
|
||||
19,
|
||||
23,
|
||||
23,
|
||||
28,
|
||||
28,
|
||||
33,
|
||||
33,
|
||||
39,
|
||||
39,
|
||||
45,
|
||||
45,
|
||||
52,
|
||||
52,
|
||||
60,
|
||||
60,
|
||||
66,
|
||||
66,
|
||||
73,
|
||||
73,
|
||||
80,
|
||||
80,
|
||||
]
|
||||
rField = [
|
||||
16,
|
||||
23,
|
||||
28,
|
||||
34,
|
||||
34,
|
||||
41,
|
||||
41,
|
||||
50,
|
||||
50,
|
||||
60,
|
||||
62,
|
||||
73,
|
||||
76,
|
||||
88,
|
||||
90,
|
||||
93,
|
||||
106,
|
||||
109,
|
||||
113,
|
||||
126,
|
||||
130,
|
||||
144,
|
||||
150,
|
||||
166,
|
||||
172,
|
||||
]
|
||||
sField = [
|
||||
20,
|
||||
27,
|
||||
32,
|
||||
39,
|
||||
39,
|
||||
48,
|
||||
48,
|
||||
59,
|
||||
59,
|
||||
72,
|
||||
78,
|
||||
93,
|
||||
101,
|
||||
117,
|
||||
125,
|
||||
133,
|
||||
151,
|
||||
159,
|
||||
169,
|
||||
190,
|
||||
202,
|
||||
226,
|
||||
244,
|
||||
272,
|
||||
292,
|
||||
]
|
||||
DField = [
|
||||
60,
|
||||
78,
|
||||
98,
|
||||
120,
|
||||
120,
|
||||
149,
|
||||
149,
|
||||
180,
|
||||
180,
|
||||
220,
|
||||
220,
|
||||
260,
|
||||
260,
|
||||
305,
|
||||
305,
|
||||
305,
|
||||
355,
|
||||
355,
|
||||
355,
|
||||
400,
|
||||
400,
|
||||
440,
|
||||
440,
|
||||
480,
|
||||
480,
|
||||
]
|
||||
EField = [
|
||||
39,
|
||||
50,
|
||||
61,
|
||||
75,
|
||||
75,
|
||||
92,
|
||||
92,
|
||||
112,
|
||||
112,
|
||||
134,
|
||||
134,
|
||||
159,
|
||||
159,
|
||||
185,
|
||||
185,
|
||||
185,
|
||||
215,
|
||||
215,
|
||||
215,
|
||||
240,
|
||||
240,
|
||||
265,
|
||||
265,
|
||||
290,
|
||||
290,
|
||||
]
|
||||
FField = [
|
||||
20,
|
||||
28,
|
||||
35,
|
||||
43,
|
||||
43,
|
||||
53,
|
||||
53,
|
||||
64,
|
||||
64,
|
||||
76,
|
||||
76,
|
||||
90,
|
||||
90,
|
||||
106,
|
||||
106,
|
||||
106,
|
||||
122,
|
||||
122,
|
||||
122,
|
||||
137,
|
||||
137,
|
||||
151,
|
||||
151,
|
||||
165,
|
||||
165,
|
||||
]
|
||||
GField = [
|
||||
12,
|
||||
16,
|
||||
20,
|
||||
24,
|
||||
24,
|
||||
28,
|
||||
28,
|
||||
34,
|
||||
34,
|
||||
40,
|
||||
40,
|
||||
47,
|
||||
47,
|
||||
54,
|
||||
54,
|
||||
54,
|
||||
61,
|
||||
61,
|
||||
61,
|
||||
69,
|
||||
69,
|
||||
75,
|
||||
75,
|
||||
83,
|
||||
83,
|
||||
]
|
||||
HField = [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
KField = [
|
||||
0,
|
||||
3,
|
||||
5,
|
||||
6,
|
||||
6,
|
||||
6,
|
||||
6,
|
||||
7,
|
||||
7,
|
||||
9,
|
||||
9,
|
||||
10,
|
||||
10,
|
||||
12,
|
||||
12,
|
||||
12,
|
||||
13,
|
||||
13,
|
||||
13,
|
||||
16,
|
||||
16,
|
||||
17,
|
||||
17,
|
||||
18,
|
||||
18,
|
||||
]
|
||||
NField = [
|
||||
-4,
|
||||
-4,
|
||||
-4,
|
||||
-5,
|
||||
-5,
|
||||
-7,
|
||||
-7,
|
||||
-8,
|
||||
-8,
|
||||
-9,
|
||||
-9,
|
||||
-10,
|
||||
-10,
|
||||
-12,
|
||||
-12,
|
||||
-12,
|
||||
-14,
|
||||
-14,
|
||||
-14,
|
||||
-14,
|
||||
-14,
|
||||
-16,
|
||||
-16,
|
||||
-17,
|
||||
-17,
|
||||
]
|
||||
RField = [
|
||||
-10,
|
||||
-11,
|
||||
-13,
|
||||
-16,
|
||||
-16,
|
||||
-20,
|
||||
-20,
|
||||
-25,
|
||||
-25,
|
||||
-30,
|
||||
-32,
|
||||
-38,
|
||||
-41,
|
||||
-48,
|
||||
-50,
|
||||
-53,
|
||||
-60,
|
||||
-63,
|
||||
-67,
|
||||
-74,
|
||||
-78,
|
||||
-87,
|
||||
-93,
|
||||
-103,
|
||||
-109,
|
||||
]
|
||||
SField = [
|
||||
-14,
|
||||
-15,
|
||||
-17,
|
||||
-21,
|
||||
-21,
|
||||
-27,
|
||||
-27,
|
||||
-34,
|
||||
-34,
|
||||
-42,
|
||||
-48,
|
||||
-58,
|
||||
-66,
|
||||
-77,
|
||||
-85,
|
||||
-93,
|
||||
-105,
|
||||
-113,
|
||||
-123,
|
||||
-138,
|
||||
-150,
|
||||
-169,
|
||||
-187,
|
||||
-209,
|
||||
-229,
|
||||
]
|
||||
fieldDict = {
|
||||
"c": cField,
|
||||
"f": fField,
|
||||
"g": gField,
|
||||
"h": hField,
|
||||
"k": kField,
|
||||
"n": nField,
|
||||
"r": rField,
|
||||
"s": sField,
|
||||
"D": DField,
|
||||
"E": EField,
|
||||
"F": FField,
|
||||
"G": GField,
|
||||
"H": HField,
|
||||
"K": KField,
|
||||
"N": NField,
|
||||
"R": RField,
|
||||
"S": SField,
|
||||
}
|
||||
return fieldDict[fieldCharacter][valueNominalRange]
|
||||
|
||||
def calculate(self,value,fieldChar,quality):
|
||||
'''calculate upper and lower field values'''
|
||||
self.nominalRange = self. getNominalRange(value)
|
||||
self.upperValue = self.getFieldValue(fieldChar,self.nominalRange)
|
||||
self.lowerValue = self.upperValue-self.getITValue(quality,self.nominalRange)
|
||||
if fieldChar == 'H':
|
||||
def calculate(self, value, fieldChar, quality):
|
||||
"""calculate upper and lower field values"""
|
||||
self.nominalRange = self.getNominalRange(value)
|
||||
self.upperValue = self.getFieldValue(fieldChar, self.nominalRange)
|
||||
self.lowerValue = self.upperValue - self.getITValue(quality, self.nominalRange)
|
||||
if fieldChar == "H":
|
||||
self.upperValue = -self.lowerValue
|
||||
self.lowerValue = 0
|
||||
|
||||
def getValues(self):
|
||||
'''return range values in mm'''
|
||||
return (self.upperValue/1000,self.lowerValue/1000)
|
||||
|
||||
|
||||
"""return range values in mm"""
|
||||
return (self.upperValue / 1000, self.lowerValue / 1000)
|
||||
|
||||
@@ -192,13 +192,13 @@
|
||||
</g>
|
||||
</g>
|
||||
<g id="g3298" fill="#000000" font-family="Arial" letter-spacing="0px" word-spacing="0px">
|
||||
<text id="text3266" x="147.9312" y="160.67236" style="line-height:0%" freecad:editable="Designed_by_Name"><tspan id="tspan3268" x="147.9312" y="160.67236" font-size="3.95px" style="line-height:1.25">Designed by Name</tspan></text>
|
||||
<text id="text3270" x="147.93056" y="168.59135" style="line-height:0%" freecad:editable="FC-Date"><tspan id="tspan3272" x="147.93056" y="168.59135" font-size="3.95px" style="line-height:1.25">Date</tspan></text>
|
||||
<text id="text3274" x="154.46243" y="191.45177" text-align="center" text-anchor="middle" style="line-height:0%" freecad:editable="FC-SC"><tspan id="tspan3276" x="154.46243" y="191.45177" font-size="3.95px" style="line-height:1.25">Scale</tspan></text>
|
||||
<text id="text3266" x="147.9312" y="160.67236" style="line-height:0%" freecad:editable="Designed_by_Name" freecad:autofill="author"><tspan id="tspan3268" x="147.9312" y="160.67236" font-size="3.95px" style="line-height:1.25">Designed by Name</tspan></text>
|
||||
<text id="text3270" x="147.93056" y="168.59135" style="line-height:0%" freecad:editable="FC-Date" freecad:autofill="date"><tspan id="tspan3272" x="147.93056" y="168.59135" font-size="3.95px" style="line-height:1.25">Date</tspan></text>
|
||||
<text id="text3274" x="154.46243" y="191.45177" text-align="center" text-anchor="middle" style="line-height:0%" freecad:editable="FC-SC" freecad:autofill="scale"><tspan id="tspan3276" x="154.46243" y="191.45177" font-size="3.95px" style="line-height:1.25">Scale</tspan></text>
|
||||
<text id="text3278" x="173.94231" y="191.44733" text-align="center" text-anchor="middle" style="line-height:0%" freecad:editable="Weight"><tspan id="tspan3280" x="173.94231" y="191.44733" font-size="3.95px" style="line-height:1.25">Weight</tspan></text>
|
||||
<text id="text3282" x="186.05237" y="158.72597" style="line-height:0%" freecad:editable="FC-Title"><tspan id="tspan3284" x="186.05237" y="158.72597" font-size="5.6444px" style="line-height:1.25">Title</tspan></text>
|
||||
<text id="text3282" x="186.05237" y="158.72597" style="line-height:0%" freecad:editable="FC-Title" freecad:autofill="title"><tspan id="tspan3284" x="186.05237" y="158.72597" font-size="5.6444px" style="line-height:1.25">Title</tspan></text>
|
||||
<text id="text3286" x="185.99422" y="165.73558" style="line-height:0%" freecad:editable="Subtitle"><tspan id="tspan3288" x="185.99422" y="165.73558" font-size="3.9511px" style="line-height:1.25">Subtitle</tspan></text>
|
||||
<text id="text3290" x="185.6927" y="191.31752" style="line-height:0%" freecad:editable="Drawing_number"><tspan id="tspan3292" x="185.6927" y="191.31752" font-size="3.9511px" style="line-height:1.25">Drawing number</tspan></text>
|
||||
<text id="text3294" x="248.32477" y="191.45177" style="line-height:0%" freecad:editable="FC-SH"><tspan id="tspan3296" x="248.32477" y="191.45177" font-size="3.9511px" style="line-height:1.25">Sheet</tspan></text>
|
||||
<text id="text3294" x="248.32477" y="191.45177" style="line-height:0%" freecad:editable="FC-SH" freecad:autofill="sheet"><tspan id="tspan3296" x="248.32477" y="191.45177" font-size="3.9511px" style="line-height:1.25">Sheet</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -314,18 +314,18 @@
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<text id="text3331" x="392.60703" y="372.66077" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="AuthorName"><tspan id="tspan3333" x="392.60703" y="372.66077" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">AUTHOR NAME</tspan></text>
|
||||
<text id="text3331-5" x="392.4371" y="378.867" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="CreationDate"><tspan id="tspan3333-6" x="392.4371" y="378.867" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">CREATION DATE</tspan></text>
|
||||
<text id="text3331" x="392.60703" y="372.66077" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="AuthorName" freecad:autofill="author"><tspan id="tspan3333" x="392.60703" y="372.66077" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">AUTHOR NAME</tspan></text>
|
||||
<text id="text3331-5" x="392.4371" y="378.867" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="CreationDate" freecad:autofill="date"><tspan id="tspan3333-6" x="392.4371" y="378.867" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">CREATION DATE</tspan></text>
|
||||
<text id="text3331-5-5" x="392.40024" y="384.9765" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="SupervisorName"><tspan id="tspan3333-6-9" x="392.40024" y="384.9765" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">SUPERVISOR NAME</tspan></text>
|
||||
<text id="text3331-5-3" x="392.35773" y="391.07263" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="CheckDate"><tspan id="tspan3333-6-1" x="392.35773" y="391.07263" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">CHECK DATE</tspan></text>
|
||||
<text id="text3331-5-9-1" x="399.95898" y="399.68958" fill="#000000" font-family="Arial" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="A3"><tspan id="tspan3316" x="399.95898" y="399.68958" font-size="4.1756px" stroke-width=".10686" style="line-height:1.25">ANSI C</tspan></text>
|
||||
<text id="text3331-5-9" x="399.75266" y="410.16138" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="Scale"><tspan id="tspan3333-6-6" x="399.75266" y="410.16138" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">SCALE</tspan></text>
|
||||
<text id="text3331-5-9" x="399.75266" y="410.16138" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="Scale" freecad:autofill="scale"><tspan id="tspan3333-6-6" x="399.75266" y="410.16138" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">SCALE</tspan></text>
|
||||
<text id="text3331-5-9-4" x="418.39166" y="410.16138" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="Weight"><tspan id="tspan3333-6-6-3" x="418.39166" y="410.16138" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">WEIGHT</tspan></text>
|
||||
<text id="text3331-5-9-4-3" x="430.0199" y="410.16138" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="DrawingNumber"><tspan id="tspan3333-6-6-3-8" x="430.0199" y="410.16138" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">NUMBER</tspan></text>
|
||||
<text id="text3331-5-9-4-3-9" x="516.71234" y="410.16138" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="SheetNumber"><tspan id="tspan3333-6-6-3-8-7" x="516.71234" y="410.16138" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">SHEET</tspan></text>
|
||||
<text id="text3331-0" x="467.7522" y="373.39777" fill="#000000" font-family="Arial" font-size="9.8636px" font-weight="bold" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="Title"><tspan id="tspan3333-5" x="467.7522" y="373.39777" font-family="Arial" font-size="6.9593px" font-weight="bold" stroke-width=".10686" text-align="center" text-anchor="middle" style="line-height:125%">TITLE</tspan></text>
|
||||
<text id="text3331-5-9-4-3-9" x="516.71234" y="410.16138" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="SheetNumber" freecad:autofill="sheet"><tspan id="tspan3333-6-6-3-8-7" x="516.71234" y="410.16138" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">SHEET</tspan></text>
|
||||
<text id="text3331-0" x="467.7522" y="373.39777" fill="#000000" font-family="Arial" font-size="9.8636px" font-weight="bold" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="Title" freecad:autofill="title"><tspan id="tspan3333-5" x="467.7522" y="373.39777" font-family="Arial" font-size="6.9593px" font-weight="bold" stroke-width=".10686" text-align="center" text-anchor="middle" style="line-height:125%">TITLE</tspan></text>
|
||||
<text id="text3331-0-4" x="467.74326" y="381.90521" fill="#000000" font-family="Arial" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:0%" freecad:editable="Subtitle"><tspan id="tspan3333-5-6" x="467.74326" y="381.90521" font-size="4.6396px" stroke-width=".10686" style="line-height:1.25">SUBTITLE</tspan></text>
|
||||
<text id="text241" x="430.58435" y="398.39545" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="Company_name"><tspan id="tspan243" x="430.58435" y="398.39545" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">COMPANY NAME</tspan></text>
|
||||
<text id="text241" x="430.58435" y="398.39545" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="Company_name" freecad:autofill="organization"><tspan id="tspan243" x="430.58435" y="398.39545" font-size="3.2879px" stroke-width=".10686" style="line-height:1.25">COMPANY NAME</tspan></text>
|
||||
<text id="text287" x="392.32947" y="416.26276" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="COPYRIGHT"><tspan id="tspan289" x="392.32947" y="416.26276" font-size="2.5518px" stroke-width=".10686" style="line-height:1.25">COPYRIGHT</tspan></text>
|
||||
<text id="text275" x="530.49188" y="415.13251" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="I__"><tspan id="tspan3245-0" x="530.49188" y="415.13251" font-size="2.5518px" stroke-width=".10686" style="line-height:1.25">_________</tspan></text>
|
||||
<text id="text278" x="530.49188" y="409.67816" fill="#000000" font-family="sans" font-size="9.8636px" letter-spacing="0px" stroke-width=".10686" word-spacing="0px" style="line-height:0%" freecad:editable="H__"><tspan id="tspan280" x="530.49188" y="409.67816" font-size="2.5518px" stroke-width=".10686" style="line-height:1.25">_________</tspan></text>
|
||||
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
@@ -12,9 +12,11 @@ using namespace Attacher;
|
||||
using namespace PartTestHelpers;
|
||||
|
||||
/*
|
||||
* Testing note: It looks like there are about 45 different attachment modes, and these tests all
|
||||
* only look at one of them - to prove that adding elementMap code doesn't break anything. A
|
||||
* comprehensive test of the Attacher would definitely want to try many more code paths.
|
||||
* Testing note: It looks like there are about 45 different attachment modes, and these tests
|
||||
* mostly only look at some of them - to prove that adding elementMap code doesn't break anything.
|
||||
* While a trivial bounding box test is used to ensure no hard crashes in any of the modes, any
|
||||
* mode that requires additional shapes beyond a couple of boxes would need a more comprehensive
|
||||
* test.
|
||||
*/
|
||||
|
||||
class AttacherTest: public ::testing::Test, public PartTestHelpers::PartTestHelperClass
|
||||
@@ -105,3 +107,247 @@ TEST_F(AttacherTest, TestCalculateAttachedPlacement)
|
||||
EXPECT_EQ(placement.getPosition().y, 0);
|
||||
EXPECT_EQ(placement.getPosition().z, 0);
|
||||
}
|
||||
|
||||
TEST_F(AttacherTest, TestAllStringModesValid)
|
||||
{
|
||||
// Arrange
|
||||
const char* modes[] = {
|
||||
"Deactivated",
|
||||
"Translate",
|
||||
"ObjectXY",
|
||||
"ObjectXZ",
|
||||
"ObjectYZ",
|
||||
"FlatFace",
|
||||
"TangentPlane",
|
||||
"NormalToEdge",
|
||||
"FrenetNB",
|
||||
"FrenetTN",
|
||||
"FrenetTB",
|
||||
"Concentric",
|
||||
"SectionOfRevolution",
|
||||
"ThreePointsPlane",
|
||||
"ThreePointsNormal",
|
||||
"Folding",
|
||||
|
||||
"ObjectX",
|
||||
"ObjectY",
|
||||
"ObjectZ",
|
||||
"AxisOfCurvature",
|
||||
"Directrix1",
|
||||
"Directrix2",
|
||||
"Asymptote1",
|
||||
"Asymptote2",
|
||||
"Tangent",
|
||||
"Normal",
|
||||
"Binormal",
|
||||
"TangentU",
|
||||
"TangentV",
|
||||
"TwoPointLine",
|
||||
"IntersectionLine",
|
||||
"ProximityLine",
|
||||
|
||||
"ObjectOrigin",
|
||||
"Focus1",
|
||||
"Focus2",
|
||||
"OnEdge",
|
||||
"CenterOfCurvature",
|
||||
"CenterOfMass",
|
||||
"IntersectionPoint",
|
||||
"Vertex",
|
||||
"ProximityPoint1",
|
||||
"ProximityPoint2",
|
||||
|
||||
"AxisOfInertia1",
|
||||
"AxisOfInertia2",
|
||||
"AxisOfInertia3",
|
||||
|
||||
"InertialCS",
|
||||
|
||||
"FaceNormal",
|
||||
|
||||
"OZX",
|
||||
"OZY",
|
||||
"OXY",
|
||||
"OXZ",
|
||||
"OYZ",
|
||||
"OYX",
|
||||
};
|
||||
int index = 0;
|
||||
for (auto mode : modes) {
|
||||
_boxes[1]->MapMode.setValue(mode); // There are lots of attachment modes!
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_STREQ(_boxes[1]->MapMode.getValueAsString(), mode);
|
||||
EXPECT_EQ(_boxes[1]->MapMode.getValue(), index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(AttacherTest, TestAllModesBoundaries)
|
||||
{
|
||||
_boxes[1]->MapMode.setValue(mmTranslate);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 1, 2, 3)));
|
||||
_boxes[1]->MapMode.setValue(mmObjectXY);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 1, 2, 3)));
|
||||
_boxes[1]->MapMode.setValue(mmObjectXZ);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, -3, 0, 1, 0, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmObjectYZ);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
|
||||
_boxes[1]->MapMode.setValue(mmFlatFace);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmTangentPlane);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Normal);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
|
||||
_boxes[1]->MapMode.setValue(mmFrenetNB);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmFrenetTN);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmFrenetTB);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmConcentric);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmRevolutionSection);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmThreePointsNormal);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmThreePointsPlane);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mmFolding);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
|
||||
_boxes[1]->MapMode.setValue(mm1AxisX);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1AxisY);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1AxisZ);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1AxisCurv);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Directrix1);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Directrix2);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Asymptote1);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Asymptote2);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Tangent);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1TangentU);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1TangentV);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1TwoPoints);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Intersection);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Proximity);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
|
||||
_boxes[1]->MapMode.setValue(mm0Origin);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm0Focus1);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm0Focus2);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm0OnEdge);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm0CenterOfCurvature);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm0CenterOfMass);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1Intersection);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm0Vertex);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm0ProximityPoint1);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm0ProximityPoint2);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
|
||||
_boxes[1]->MapMode.setValue(mm1AxisInertia1);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1AxisInertia2);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
_boxes[1]->MapMode.setValue(mm1AxisInertia3);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2)));
|
||||
|
||||
_boxes[1]->MapMode.setValue(mmInertialCS);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(
|
||||
boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5)));
|
||||
|
||||
_boxes[1]->MapMode.setValue(mm1FaceNormal);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(
|
||||
boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5)));
|
||||
|
||||
_boxes[1]->MapMode.setValue(mmOZX);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(
|
||||
boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5)));
|
||||
_boxes[1]->MapMode.setValue(mmOZY);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(
|
||||
boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5)));
|
||||
_boxes[1]->MapMode.setValue(mmOXY);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(
|
||||
boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5)));
|
||||
_boxes[1]->MapMode.setValue(mmOXZ);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(
|
||||
boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5)));
|
||||
_boxes[1]->MapMode.setValue(mmOYZ);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(
|
||||
boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5)));
|
||||
_boxes[1]->MapMode.setValue(mmOYX);
|
||||
_boxes[1]->recomputeFeature();
|
||||
EXPECT_TRUE(
|
||||
boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5)));
|
||||
}
|
||||
|
||||