FreeCAD/src/Mod/Part/App/PartFeature.cpp

386 lines
13 KiB
C++

/***************************************************************************
* Copyright (c) Jürgen Riegel (juergen.riegel@web.de) 2002 *
* *
* 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 *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <gp_Trsf.hxx>
# include <gp_Ax1.hxx>
# include <BRepBuilderAPI_MakeShape.hxx>
# include <BRepAlgoAPI_Fuse.hxx>
# include <BRepAlgoAPI_Common.hxx>
# include <TopTools_ListIteratorOfListOfShape.hxx>
# include <TopExp.hxx>
# include <TopTools_IndexedMapOfShape.hxx>
# include <Standard_Failure.hxx>
# include <TopoDS_Face.hxx>
# include <gp_Dir.hxx>
# include <gp_Pln.hxx> // for Precision::Confusion()
# include <Bnd_Box.hxx>
# include <BRepBndLib.hxx>
#endif
#include <strstream>
#include <Base/Console.h>
#include <Base/Writer.h>
#include <Base/Reader.h>
#include <Base/Exception.h>
#include <Base/FileInfo.h>
#include <Base/Stream.h>
#include <Base/Placement.h>
#include <Base/Rotation.h>
#include <App/FeaturePythonPyImp.h>
#include "PartFeature.h"
#include "PartFeaturePy.h"
using namespace Part;
PROPERTY_SOURCE(Part::Feature, App::GeoFeature)
Feature::Feature(void)
{
ADD_PROPERTY(Shape, (TopoDS_Shape()));
}
Feature::~Feature()
{
}
short Feature::mustExecute(void) const
{
return GeoFeature::mustExecute();
}
App::DocumentObjectExecReturn *Feature::recompute(void)
{
try {
return App::GeoFeature::recompute();
}
catch (Standard_Failure) {
Handle_Standard_Failure e = Standard_Failure::Caught();
App::DocumentObjectExecReturn* ret = new App::DocumentObjectExecReturn(e->GetMessageString());
if (ret->Why.empty()) ret->Why = "Unknown OCC exception";
return ret;
}
}
App::DocumentObjectExecReturn *Feature::execute(void)
{
return App::DocumentObject::StdReturn;
}
PyObject *Feature::getPyObject(void)
{
if (PythonObject.is(Py::_None())){
// ref counter is set to 1
PythonObject = Py::Object(new PartFeaturePy(this),true);
}
return Py::new_reference_to(PythonObject);
}
std::vector<PyObject *> Feature::getPySubObjects(const std::vector<std::string>& NameVec) const
{
std::vector<PyObject *> temp;
for(std::vector<std::string>::const_iterator it=NameVec.begin();it!=NameVec.end();++it){
PyObject *obj = Shape.getShape().getPySubShape((*it).c_str());
if(obj)
temp.push_back(obj);
}
return temp;
}
void Feature::onChanged(const App::Property* prop)
{
// if the placement has changed apply the change to the point data as well
if (prop == &this->Placement) {
TopoShape& shape = const_cast<TopoShape&>(this->Shape.getShape());
shape.setTransform(this->Placement.getValue().toMatrix());
}
// if the point data has changed check and adjust the transformation as well
else if (prop == &this->Shape) {
if (this->isRecomputing()) {
TopoShape& shape = const_cast<TopoShape&>(this->Shape.getShape());
shape.setTransform(this->Placement.getValue().toMatrix());
}
else {
Base::Placement p;
// shape must not be null to override the placement
if (!this->Shape.getValue().IsNull()) {
p.fromMatrix(this->Shape.getShape().getTransform());
if (p != this->Placement.getValue())
this->Placement.setValue(p);
}
}
}
GeoFeature::onChanged(prop);
}
TopLoc_Location Feature::getLocation() const
{
Base::Placement pl = this->Placement.getValue();
Base::Rotation rot(pl.getRotation());
Base::Vector3d axis;
double angle;
rot.getValue(axis, angle);
gp_Trsf trf;
trf.SetRotation(gp_Ax1(gp_Pnt(), gp_Dir(axis.x, axis.y, axis.z)), angle);
trf.SetTranslationPart(gp_Vec(pl.getPosition().x,pl.getPosition().y,pl.getPosition().z));
return TopLoc_Location(trf);
}
ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type,
const TopoDS_Shape& newS, const TopoDS_Shape& oldS)
{
ShapeHistory history;
history.type = type;
TopTools_IndexedMapOfShape newM, oldM;
TopExp::MapShapes(newS, type, newM); // map containing all old objects of type "type"
TopExp::MapShapes(oldS, type, oldM); // map containing all new objects of type "type"
// Look at all objects in the old shape and try to find the modified object in the new shape
for (int i=1; i<=oldM.Extent(); i++) {
bool found = false;
TopTools_ListIteratorOfListOfShape it;
// Find all new objects that are a modification of the old object (e.g. a face was resized)
for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) {
found = true;
for (int j=1; j<=newM.Extent(); j++) { // one old object might create several new ones!
if (newM(j).IsPartner(it.Value())) {
history.shapeMap[i-1].push_back(j-1); // adjust indices to start at zero
break;
}
}
}
// Find all new objects that were generated from an old object (e.g. a face generated from an edge)
for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) {
found = true;
for (int j=1; j<=newM.Extent(); j++) {
if (newM(j).IsPartner(it.Value())) {
history.shapeMap[i-1].push_back(j-1);
break;
}
}
}
if (!found) {
// Find all old objects that don't exist any more (e.g. a face was completely cut away)
if (mkShape.IsDeleted(oldM(i))) {
history.shapeMap[i-1] = std::vector<int>();
}
else {
// Mop up the rest (will this ever be reached?)
for (int j=1; j<=newM.Extent(); j++) {
if (newM(j).IsPartner(oldM(i))) {
history.shapeMap[i-1].push_back(j-1);
break;
}
}
}
}
}
return history;
}
ShapeHistory Feature::joinHistory(const ShapeHistory& oldH, const ShapeHistory& newH)
{
ShapeHistory join;
join.type = oldH.type;
for (ShapeHistory::MapList::const_iterator it = oldH.shapeMap.begin(); it != oldH.shapeMap.end(); ++it) {
int old_shape_index = it->first;
if (it->second.empty())
join.shapeMap[old_shape_index] = ShapeHistory::List();
for (ShapeHistory::List::const_iterator jt = it->second.begin(); jt != it->second.end(); ++jt) {
ShapeHistory::MapList::const_iterator kt = newH.shapeMap.find(*jt);
if (kt != newH.shapeMap.end()) {
ShapeHistory::List& ary = join.shapeMap[old_shape_index];
ary.insert(ary.end(), kt->second.begin(), kt->second.end());
}
}
}
return join;
}
const TopoDS_Shape Feature::findOriginOf(const TopoDS_Shape& reference) {
/* Base::Console().Error("Looking for origin of face in %s\n", this->getName());
if (reference.ShapeType() == TopAbs_FACE) {
// Find index of reference in the history
}
*/
return TopoDS_Shape();
}
/// returns the type name of the ViewProvider
const char* Feature::getViewProviderName(void) const {
return "PartGui::ViewProviderPart";
}
// ---------------------------------------------------------
PROPERTY_SOURCE(Part::FilletBase, Part::Feature)
FilletBase::FilletBase()
{
ADD_PROPERTY(Base,(0));
ADD_PROPERTY(Edges,(0,0,0));
Edges.setSize(0);
}
short FilletBase::mustExecute() const
{
if (Base.isTouched() || Edges.isTouched())
return 1;
return 0;
}
// ---------------------------------------------------------
PROPERTY_SOURCE(Part::FeatureExt, Part::Feature)
namespace App {
/// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(Part::FeaturePython, Part::Feature)
template<> const char* Part::FeaturePython::getViewProviderName(void) const {
return "PartGui::ViewProviderPython";
}
template<> PyObject* Part::FeaturePython::getPyObject(void) {
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new FeaturePythonPyT<Part::PartFeaturePy>(this),true);
}
return Py::new_reference_to(PythonObject);
}
/// @endcond
// explicit template instantiation
template class PartExport FeaturePythonT<Part::Feature>;
}
// ----------------------------------------------------------------
#include <GProp_GProps.hxx>
#include <BRepGProp.hxx>
#include <gce_MakeLin.hxx>
#include <BRepIntCurveSurface_Inter.hxx>
#include <IntCurveSurface_IntersectionPoint.hxx>
#include <gce_MakeDir.hxx>
std::vector<Part::cutFaces> Part::findAllFacesCutBy(
const TopoDS_Shape& shape, const TopoDS_Shape& face, const gp_Dir& dir)
{
// Find the centre of gravity of the face
GProp_GProps props;
BRepGProp::SurfaceProperties(face,props);
gp_Pnt cog = props.CentreOfMass();
// create a line through the centre of gravity
gp_Lin line = gce_MakeLin(cog, dir);
// Find intersection of line with all faces of the shape
std::vector<cutFaces> result;
BRepIntCurveSurface_Inter mkSection;
// TODO: Less precision than Confusion() should be OK?
for (mkSection.Init(shape, line, Precision::Confusion()); mkSection.More(); mkSection.Next()) {
gp_Pnt iPnt = mkSection.Pnt();
double dsq = cog.SquareDistance(iPnt);
if (dsq < Precision::Confusion())
continue; // intersection with original face
// Find out which side of the original face the intersection is on
gce_MakeDir mkDir(cog, iPnt);
if (!mkDir.IsDone())
continue; // some error (appears highly unlikely to happen, though...)
if (mkDir.Value().IsOpposite(dir, Precision::Confusion()))
continue; // wrong side of face (opposite to extrusion direction)
cutFaces newF;
newF.face = mkSection.Face();
newF.distsq = dsq;
result.push_back(newF);
}
return result;
}
const bool Part::checkIntersection(const TopoDS_Shape& first, const TopoDS_Shape& second,
const bool quick, const bool touch_is_intersection) {
Bnd_Box first_bb, second_bb;
BRepBndLib::Add(first, first_bb);
first_bb.SetGap(0);
BRepBndLib::Add(second, second_bb);
second_bb.SetGap(0);
// Note: This test fails if the objects are touching one another at zero distance
if (first_bb.IsOut(second_bb))
return false; // no intersection
if (quick)
return true; // assumed intersection
// Try harder
if (touch_is_intersection) {
// If both shapes fuse to a single solid, then they intersect
BRepAlgoAPI_Fuse mkFuse(first, second);
if (!mkFuse.IsDone())
return false;
if (mkFuse.Shape().IsNull())
return false;
// Did we get one or two solids?
TopExp_Explorer xp;
xp.Init(mkFuse.Shape(),TopAbs_SOLID);
if (xp.More()) {
// At least one solid
xp.Next();
return (xp.More() == Standard_False);
} else {
return false;
}
} else {
// If both shapes have common material, then they intersect
BRepAlgoAPI_Common mkCommon(first, second);
if (!mkCommon.IsDone())
return false;
if (mkCommon.Shape().IsNull())
return false;
// Did we get a solid?
TopExp_Explorer xp;
xp.Init(mkCommon.Shape(),TopAbs_SOLID);
return (xp.More() == Standard_True);
}
}