AttachableObjects are desired in multiple occasions, and the current AttachableObject is not flexible enough to handle all cases. Hence the code is portet to an extension, which gives the needed flexibility.
406 lines
14 KiB
C++
406 lines
14 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 <TopExp_Explorer.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>
|
|
# include <BRepExtrema_DistShapeShape.hxx>
|
|
#endif
|
|
|
|
|
|
#include <sstream>
|
|
#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)
|
|
{
|
|
this->Shape.touch();
|
|
return GeoFeature::execute();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// 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;
|
|
}
|
|
|
|
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
|
|
|
|
//extrema method
|
|
BRepExtrema_DistShapeShape extrema(first, second);
|
|
if (!extrema.IsDone())
|
|
return true;
|
|
if (extrema.Value() > Precision::Confusion())
|
|
return false;
|
|
if (extrema.InnerSolution())
|
|
return true;
|
|
//here we should have touching shapes.
|
|
if (touch_is_intersection)
|
|
{
|
|
//non manifold condition. 1 has to be a face
|
|
for (int index = 1; index < extrema.NbSolution() + 1; ++index)
|
|
{
|
|
if (extrema.SupportTypeShape1(index) == BRepExtrema_IsInFace || extrema.SupportTypeShape2(index) == BRepExtrema_IsInFace)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
//boolean method.
|
|
/*
|
|
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);
|
|
}
|
|
*/
|
|
}
|