Download src/Mod/Part/Gui/CrossSections.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 18.5 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Part/Gui/CrossSections.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Part/Gui/CrossSections.cpp
-
curl -L -o CrossSections.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Part/Gui/CrossSections.cpp
18.5 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net> * | |
| * * | |
| * 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 * | |
| * * | |
| ***************************************************************************/ | |
| using namespace PartGui; | |
| namespace sp = std::placeholders; | |
| 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; | |
| }; | |
| } // namespace PartGui | |
| 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); | |
| } | |
| } | |
| /* | |
| * Destroys the object and frees any allocated resources | |
| */ | |
| CrossSections::~CrossSections() | |
| { | |
| // no need to delete child widgets, Qt does it all for us | |
| 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) | |
| { | |
| // The cross-sections dialog is embedded into a task panel | |
| // which is a parent widget and will handle the event | |
| 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; | |
| } | |
| 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(); | |
| } | |
| 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(); | |
| 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(); | |
| } | |
| } | |