| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <limits>
|
| |
|
| | #include <QFuture>
|
| | #include <QKeyEvent>
|
| |
|
| | #include <BRep_Builder.hxx>
|
| | #include <TopoDS.hxx>
|
| | #include <TopoDS_Compound.hxx>
|
| |
|
| | #include <Inventor/nodes/SoBaseColor.h>
|
| | #include <Inventor/nodes/SoCoordinate3.h>
|
| | #include <Inventor/nodes/SoDrawStyle.h>
|
| | #include <Inventor/nodes/SoLineSet.h>
|
| | #include <Inventor/nodes/SoSeparator.h>
|
| |
|
| | #include <App/Document.h>
|
| | #include <Base/Sequencer.h>
|
| | #include <Base/UnitsApi.h>
|
| | #include <Gui/Application.h>
|
| | #include <Gui/BitmapFactory.h>
|
| | #include <Gui/Command.h>
|
| | #include <Gui/Document.h>
|
| | #include <Gui/View3DInventor.h>
|
| | #include <Gui/View3DInventorViewer.h>
|
| | #include <Gui/ViewProvider.h>
|
| | #include <Mod/Part/App/CrossSection.h>
|
| | #include <Mod/Part/App/PartFeature.h>
|
| |
|
| | #include "CrossSections.h"
|
| | #include "ui_CrossSections.h"
|
| |
|
| | #include <QMessageBox>
|
| | #include <Base/Interpreter.h>
|
| | #include <Gui/MainWindow.h>
|
| |
|
| |
|
| | using namespace PartGui;
|
| | namespace sp = std::placeholders;
|
| | #undef CS_FUTURE
|
| |
|
| | namespace PartGui
|
| | {
|
| | class ViewProviderCrossSections: public Gui::ViewProvider
|
| | {
|
| | public:
|
| | ViewProviderCrossSections()
|
| | {
|
| | coords = new SoCoordinate3();
|
| | coords->ref();
|
| | planes = new SoLineSet();
|
| | planes->ref();
|
| | SoBaseColor* color = new SoBaseColor();
|
| | color->rgb.setValue(1.0f, 0.447059f, 0.337255f);
|
| | SoDrawStyle* style = new SoDrawStyle();
|
| | style->lineWidth.setValue(2.0f);
|
| | this->pcRoot->addChild(color);
|
| | this->pcRoot->addChild(style);
|
| | this->pcRoot->addChild(coords);
|
| | this->pcRoot->addChild(planes);
|
| | }
|
| | ~ViewProviderCrossSections() override
|
| | {
|
| | coords->unref();
|
| | planes->unref();
|
| | }
|
| | void updateData(const App::Property*) override
|
| | {}
|
| | const char* getDefaultDisplayMode() const override
|
| | {
|
| | return "";
|
| | }
|
| | std::vector<std::string> getDisplayModes() const override
|
| | {
|
| | return {};
|
| | }
|
| | void setCoords(const std::vector<Base::Vector3f>& v)
|
| | {
|
| | coords->point.setNum(v.size());
|
| | SbVec3f* p = coords->point.startEditing();
|
| | for (unsigned int i = 0; i < v.size(); i++) {
|
| | const Base::Vector3f& pt = v[i];
|
| | p[i].setValue(pt.x, pt.y, pt.z);
|
| | }
|
| | coords->point.finishEditing();
|
| | unsigned int count = v.size() / 5;
|
| | planes->numVertices.setNum(count);
|
| | int32_t* l = planes->numVertices.startEditing();
|
| | for (unsigned int i = 0; i < count; i++) {
|
| | l[i] = 5;
|
| | }
|
| | planes->numVertices.finishEditing();
|
| | }
|
| |
|
| | private:
|
| | SoCoordinate3* coords;
|
| | SoLineSet* planes;
|
| | };
|
| | }
|
| |
|
| | CrossSections::CrossSections(const Base::BoundBox3d& bb, QWidget* parent, Qt::WindowFlags fl)
|
| | : QDialog(parent, fl)
|
| | , ui(new Ui_CrossSections)
|
| | , bbox(bb)
|
| | {
|
| | ui->setupUi(this);
|
| | setupConnections();
|
| |
|
| | constexpr double max = std::numeric_limits<double>::max();
|
| | ui->position->setRange(-max, max);
|
| | ui->position->setUnit(Base::Unit::Length);
|
| | ui->distance->setRange(0, max);
|
| | ui->distance->setUnit(Base::Unit::Length);
|
| | vp = new ViewProviderCrossSections();
|
| |
|
| | Base::Vector3d c = bbox.GetCenter();
|
| | calcPlane(CrossSections::XY, c.z);
|
| | ui->position->setValue(c.z);
|
| |
|
| | Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
| | view = qobject_cast<Gui::View3DInventor*>(doc->getActiveView());
|
| | if (view) {
|
| | view->getViewer()->addViewProvider(vp);
|
| | }
|
| | }
|
| |
|
| | |
| | |
| |
|
| | CrossSections::~CrossSections()
|
| | {
|
| |
|
| | if (view) {
|
| | view->getViewer()->removeViewProvider(vp);
|
| | }
|
| | delete vp;
|
| | }
|
| |
|
| | void CrossSections::setupConnections()
|
| | {
|
| | connect(ui->xyPlane, &QRadioButton::clicked, this, &CrossSections::xyPlaneClicked);
|
| | connect(ui->xzPlane, &QRadioButton::clicked, this, &CrossSections::xzPlaneClicked);
|
| | connect(ui->yzPlane, &QRadioButton::clicked, this, &CrossSections::yzPlaneClicked);
|
| | connect(
|
| | ui->position,
|
| | qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
|
| | this,
|
| | &CrossSections::positionValueChanged
|
| | );
|
| | connect(
|
| | ui->distance,
|
| | qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
|
| | this,
|
| | &CrossSections::distanceValueChanged
|
| | );
|
| | connect(
|
| | ui->countSections,
|
| | qOverload<int>(&QSpinBox::valueChanged),
|
| | this,
|
| | &CrossSections::countSectionsValueChanged
|
| | );
|
| | connect(ui->checkBothSides, &QCheckBox::toggled, this, &CrossSections::checkBothSidesToggled);
|
| | connect(ui->sectionsBox, &QGroupBox::toggled, this, &CrossSections::sectionsBoxToggled);
|
| | }
|
| |
|
| | CrossSections::Plane CrossSections::plane() const
|
| | {
|
| | if (ui->xyPlane->isChecked()) {
|
| | return CrossSections::XY;
|
| | }
|
| | else if (ui->xzPlane->isChecked()) {
|
| | return CrossSections::XZ;
|
| | }
|
| | else {
|
| | return CrossSections::YZ;
|
| | }
|
| | }
|
| |
|
| | void CrossSections::changeEvent(QEvent* e)
|
| | {
|
| | if (e->type() == QEvent::LanguageChange) {
|
| | ui->retranslateUi(this);
|
| | }
|
| | else {
|
| | QDialog::changeEvent(e);
|
| | }
|
| | }
|
| |
|
| | void CrossSections::keyPressEvent(QKeyEvent* ke)
|
| | {
|
| |
|
| |
|
| | ke->ignore();
|
| | }
|
| |
|
| | void CrossSections::accept()
|
| | {
|
| | if (apply()) {
|
| | QDialog::accept();
|
| | }
|
| | }
|
| |
|
| | bool CrossSections::apply()
|
| | {
|
| | std::vector<App::DocumentObject*> docobjs = Gui::Selection().getObjectsOfType(
|
| | App::DocumentObject::getClassTypeId()
|
| | );
|
| | std::vector<App::DocumentObject*> obj;
|
| | for (auto it : docobjs) {
|
| | if (!Part::Feature::getTopoShape(it, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform)
|
| | .isNull()) {
|
| | obj.push_back(it);
|
| | }
|
| | }
|
| |
|
| | std::vector<double> d;
|
| | if (ui->sectionsBox->isChecked()) {
|
| | d = getPlanes();
|
| | }
|
| | else {
|
| | d.push_back(ui->position->value().getValue());
|
| | }
|
| | double a = 0, b = 0, c = 0;
|
| | switch (plane()) {
|
| | case CrossSections::XY:
|
| | c = 1.0;
|
| | break;
|
| | case CrossSections::XZ:
|
| | b = 1.0;
|
| | break;
|
| | case CrossSections::YZ:
|
| | a = 1.0;
|
| | break;
|
| | }
|
| |
|
| | #ifdef CS_FUTURE
|
| | Standard::SetReentrant(Standard_True);
|
| | for (std::vector<App::DocumentObject*>::iterator it = obj.begin(); it != obj.end(); ++it) {
|
| | Part::CrossSection cs(a, b, c, static_cast<Part::Feature*>(*it)->Shape.getValue());
|
| | QFuture<std::list<TopoDS_Wire>> future
|
| | = QtConcurrent::mapped(d, std::bind(&Part::CrossSection::section, &cs, sp::_1));
|
| | future.waitForFinished();
|
| | QFuture<std::list<TopoDS_Wire>>::const_iterator ft;
|
| | TopoDS_Compound comp;
|
| | BRep_Builder builder;
|
| | builder.MakeCompound(comp);
|
| |
|
| | for (ft = future.begin(); ft != future.end(); ++ft) {
|
| | const std::list<TopoDS_Wire>& w = *ft;
|
| | for (std::list<TopoDS_Wire>::const_iterator wt = w.begin(); wt != w.end(); ++wt) {
|
| | if (!wt->IsNull()) {
|
| | builder.Add(comp, *wt);
|
| | }
|
| | }
|
| | }
|
| |
|
| | App::Document* doc = (*it)->getDocument();
|
| | std::string s = (*it)->getNameInDocument();
|
| | s += "_cs";
|
| | auto* section = doc->addObject<Part::Feature>(s.c_str());
|
| | section->Shape.setValue(comp);
|
| | section->purgeTouched();
|
| | }
|
| | #else
|
| | Base::SequencerLauncher seq("Cross-sections…", obj.size() * (d.size() + 1));
|
| | try {
|
| | Gui::Command::runCommand(Gui::Command::App, "import Part\n");
|
| | Gui::Command::runCommand(Gui::Command::App, "from FreeCAD import Base\n");
|
| | for (auto it : obj) {
|
| | App::Document* doc = it->getDocument();
|
| | std::string s = it->getNameInDocument();
|
| | s += "_cs";
|
| | Gui::Command::runCommand(
|
| | Gui::Command::App,
|
| | QStringLiteral(
|
| | "wires=list()\n"
|
| | "shape=FreeCAD.getDocument(\"%1\").%2.Shape\n"
|
| | )
|
| | .arg(QLatin1String(doc->getName()), QLatin1String(it->getNameInDocument()))
|
| | .toLatin1()
|
| | );
|
| |
|
| | for (double jt : d) {
|
| | Gui::Command::runCommand(
|
| | Gui::Command::App,
|
| | QStringLiteral(
|
| | "for i in shape.slice(Base.Vector(%1,%2,%3),%4):\n"
|
| | " wires.append(i)\n"
|
| | )
|
| | .arg(a)
|
| | .arg(b)
|
| | .arg(c)
|
| | .arg(jt)
|
| | .toLatin1()
|
| | );
|
| | seq.next();
|
| | }
|
| |
|
| | Gui::Command::runCommand(
|
| | Gui::Command::App,
|
| | QStringLiteral(
|
| | "comp=Part.makeCompound(wires)\n"
|
| | "slice=FreeCAD.getDocument(\"%1\").addObject(\"Part::Feature\",\"%2\")\n"
|
| | "slice.Shape=comp\n"
|
| | "slice.purgeTouched()\n"
|
| | "del slice,comp,wires,shape"
|
| | )
|
| | .arg(QLatin1String(doc->getName()), QLatin1String(s.c_str()))
|
| | .toLatin1()
|
| | );
|
| | }
|
| | seq.next();
|
| | }
|
| | catch (Base::Exception& e) {
|
| | e.reportException();
|
| | QMessageBox::critical(
|
| | Gui::getMainWindow(),
|
| | tr("Cannot compute cross-sections"),
|
| | QString::fromStdString(e.getMessage())
|
| | );
|
| | return false;
|
| | }
|
| |
|
| | seq.next();
|
| | #endif
|
| |
|
| | return true;
|
| | }
|
| |
|
| | void CrossSections::xyPlaneClicked()
|
| | {
|
| | Base::Vector3d c = bbox.GetCenter();
|
| | ui->position->setValue(c.z);
|
| | if (!ui->sectionsBox->isChecked()) {
|
| | calcPlane(CrossSections::XY, c.z);
|
| | }
|
| | else {
|
| | double dist = bbox.LengthZ() / ui->countSections->value();
|
| | if (!ui->checkBothSides->isChecked()) {
|
| | dist *= 0.5f;
|
| | }
|
| | ui->distance->setValue(dist);
|
| | calcPlanes(CrossSections::XY);
|
| | }
|
| | }
|
| |
|
| | void CrossSections::xzPlaneClicked()
|
| | {
|
| | Base::Vector3d c = bbox.GetCenter();
|
| | ui->position->setValue(c.y);
|
| | if (!ui->sectionsBox->isChecked()) {
|
| | calcPlane(CrossSections::XZ, c.y);
|
| | }
|
| | else {
|
| | double dist = bbox.LengthY() / ui->countSections->value();
|
| | if (!ui->checkBothSides->isChecked()) {
|
| | dist *= 0.5f;
|
| | }
|
| | ui->distance->setValue(dist);
|
| | calcPlanes(CrossSections::XZ);
|
| | }
|
| | }
|
| |
|
| | void CrossSections::yzPlaneClicked()
|
| | {
|
| | Base::Vector3d c = bbox.GetCenter();
|
| | ui->position->setValue(c.x);
|
| | if (!ui->sectionsBox->isChecked()) {
|
| | calcPlane(CrossSections::YZ, c.x);
|
| | }
|
| | else {
|
| | double dist = bbox.LengthX() / ui->countSections->value();
|
| | if (!ui->checkBothSides->isChecked()) {
|
| | dist *= 0.5f;
|
| | }
|
| | ui->distance->setValue(dist);
|
| | calcPlanes(CrossSections::YZ);
|
| | }
|
| | }
|
| |
|
| | void CrossSections::positionValueChanged(double v)
|
| | {
|
| | if (!ui->sectionsBox->isChecked()) {
|
| | calcPlane(plane(), v);
|
| | }
|
| | else {
|
| | calcPlanes(plane());
|
| | }
|
| | }
|
| |
|
| | void CrossSections::sectionsBoxToggled(bool b)
|
| | {
|
| | if (b) {
|
| | countSectionsValueChanged(ui->countSections->value());
|
| | }
|
| | else {
|
| | CrossSections::Plane type = plane();
|
| | Base::Vector3d c = bbox.GetCenter();
|
| | double value = 0;
|
| | switch (type) {
|
| | case CrossSections::XY:
|
| | value = c.z;
|
| | break;
|
| | case CrossSections::XZ:
|
| | value = c.y;
|
| | break;
|
| | case CrossSections::YZ:
|
| | value = c.x;
|
| | break;
|
| | }
|
| |
|
| | ui->position->setValue(value);
|
| | calcPlane(type, value);
|
| | }
|
| | }
|
| |
|
| | void CrossSections::checkBothSidesToggled(bool b)
|
| | {
|
| | double d = ui->distance->value().getValue();
|
| | d = b ? 2.0 * d : 0.5 * d;
|
| | ui->distance->setValue(d);
|
| | calcPlanes(plane());
|
| | }
|
| |
|
| | void CrossSections::countSectionsValueChanged(int v)
|
| | {
|
| | CrossSections::Plane type = plane();
|
| | double dist = 0;
|
| | switch (type) {
|
| | case CrossSections::XY:
|
| | dist = bbox.LengthZ() / v;
|
| | break;
|
| | case CrossSections::XZ:
|
| | dist = bbox.LengthY() / v;
|
| | break;
|
| | case CrossSections::YZ:
|
| | dist = bbox.LengthX() / v;
|
| | break;
|
| | }
|
| | if (!ui->checkBothSides->isChecked()) {
|
| | dist *= 0.5f;
|
| | }
|
| | ui->distance->setValue(dist);
|
| | calcPlanes(type);
|
| | }
|
| |
|
| | void CrossSections::distanceValueChanged(double)
|
| | {
|
| | calcPlanes(plane());
|
| | }
|
| |
|
| | void CrossSections::calcPlane(Plane type, double pos)
|
| | {
|
| | double bound[4];
|
| | switch (type) {
|
| | case XY:
|
| | bound[0] = bbox.MinX;
|
| | bound[1] = bbox.MaxX;
|
| | bound[2] = bbox.MinY;
|
| | bound[3] = bbox.MaxY;
|
| | break;
|
| | case XZ:
|
| | bound[0] = bbox.MinX;
|
| | bound[1] = bbox.MaxX;
|
| | bound[2] = bbox.MinZ;
|
| | bound[3] = bbox.MaxZ;
|
| | break;
|
| | case YZ:
|
| | bound[0] = bbox.MinY;
|
| | bound[1] = bbox.MaxY;
|
| | bound[2] = bbox.MinZ;
|
| | bound[3] = bbox.MaxZ;
|
| | break;
|
| | }
|
| |
|
| | std::vector<double> d;
|
| | d.push_back(pos);
|
| | makePlanes(type, d, bound);
|
| | }
|
| |
|
| | void CrossSections::calcPlanes(Plane type)
|
| | {
|
| | double bound[4];
|
| | switch (type) {
|
| | case XY:
|
| | bound[0] = bbox.MinX;
|
| | bound[1] = bbox.MaxX;
|
| | bound[2] = bbox.MinY;
|
| | bound[3] = bbox.MaxY;
|
| | break;
|
| | case XZ:
|
| | bound[0] = bbox.MinX;
|
| | bound[1] = bbox.MaxX;
|
| | bound[2] = bbox.MinZ;
|
| | bound[3] = bbox.MaxZ;
|
| | break;
|
| | case YZ:
|
| | bound[0] = bbox.MinY;
|
| | bound[1] = bbox.MaxY;
|
| | bound[2] = bbox.MinZ;
|
| | bound[3] = bbox.MaxZ;
|
| | break;
|
| | }
|
| |
|
| | std::vector<double> d = getPlanes();
|
| | makePlanes(type, d, bound);
|
| | }
|
| |
|
| | std::vector<double> CrossSections::getPlanes() const
|
| | {
|
| | int count = ui->countSections->value();
|
| | double pos = ui->position->value().getValue();
|
| | double stp = ui->distance->value().getValue();
|
| | bool both = ui->checkBothSides->isChecked();
|
| |
|
| | std::vector<double> d;
|
| | if (both) {
|
| | double start = pos - 0.5f * (count - 1) * stp;
|
| | for (int i = 0; i < count; i++) {
|
| | d.push_back(start + i * stp);
|
| | }
|
| | }
|
| | else {
|
| | for (int i = 0; i < count; i++) {
|
| | d.push_back(pos + i * stp);
|
| | }
|
| | }
|
| | return d;
|
| | }
|
| |
|
| | void CrossSections::makePlanes(Plane type, const std::vector<double>& d, double bound[4])
|
| | {
|
| | std::vector<Base::Vector3f> points;
|
| | for (double it : d) {
|
| | Base::Vector3f v[4];
|
| | switch (type) {
|
| | case XY:
|
| | v[0].Set(bound[0], bound[2], it);
|
| | v[1].Set(bound[1], bound[2], it);
|
| | v[2].Set(bound[1], bound[3], it);
|
| | v[3].Set(bound[0], bound[3], it);
|
| | break;
|
| | case XZ:
|
| | v[0].Set(bound[0], it, bound[2]);
|
| | v[1].Set(bound[1], it, bound[2]);
|
| | v[2].Set(bound[1], it, bound[3]);
|
| | v[3].Set(bound[0], it, bound[3]);
|
| | break;
|
| | case YZ:
|
| | v[0].Set(it, bound[0], bound[2]);
|
| | v[1].Set(it, bound[1], bound[2]);
|
| | v[2].Set(it, bound[1], bound[3]);
|
| | v[3].Set(it, bound[0], bound[3]);
|
| | break;
|
| | }
|
| |
|
| | points.push_back(v[0]);
|
| | points.push_back(v[1]);
|
| | points.push_back(v[2]);
|
| | points.push_back(v[3]);
|
| | points.push_back(v[0]);
|
| | }
|
| | vp->setCoords(points);
|
| | }
|
| |
|
| |
|
| |
|
| | TaskCrossSections::TaskCrossSections(const Base::BoundBox3d& bb)
|
| | {
|
| | widget = new CrossSections(bb);
|
| | addTaskBox(Gui::BitmapFactory().pixmap("Part_CrossSections"), widget);
|
| | }
|
| |
|
| | bool TaskCrossSections::accept()
|
| | {
|
| | widget->accept();
|
| | return (widget->result() == QDialog::Accepted);
|
| | }
|
| |
|
| | void TaskCrossSections::clicked(int id)
|
| | {
|
| | if (id == QDialogButtonBox::Apply) {
|
| | widget->apply();
|
| | }
|
| | }
|
| |
|
| | #include "moc_CrossSections.cpp"
|
| |
|