[TD]fix area calculation
This commit is contained in:
committed by
Yorik van Havre
parent
cb328a6ce7
commit
2ff1a2b6de
@@ -365,6 +365,7 @@ void arcPoints::dump(const std::string& text) const
|
||||
|
||||
areaPoint::areaPoint() :
|
||||
area(0.0),
|
||||
actualArea(0.0),
|
||||
center(Base::Vector3d())
|
||||
{
|
||||
}
|
||||
@@ -373,6 +374,7 @@ areaPoint& areaPoint::operator=(const areaPoint& ap)
|
||||
{
|
||||
area = ap.area;
|
||||
center = ap.center;
|
||||
actualArea = ap.actualArea;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -383,10 +385,14 @@ void areaPoint::move(const Base::Vector3d& offset)
|
||||
|
||||
void areaPoint::project(const DrawViewPart* dvp)
|
||||
{
|
||||
area = area * dvp->getScale();
|
||||
center = dvp->projectPoint(center) * dvp->getScale();
|
||||
}
|
||||
|
||||
void areaPoint::invertY()
|
||||
{
|
||||
center = DU::invertY(center);
|
||||
}
|
||||
|
||||
void areaPoint::dump(const std::string& text) const
|
||||
{
|
||||
Base::Console().Message("areaPoint - %s\n", text.c_str());
|
||||
|
||||
@@ -167,11 +167,16 @@ public:
|
||||
|
||||
void move(const Base::Vector3d& offset);
|
||||
void project(const DrawViewPart* dvp);
|
||||
void invertY();
|
||||
void dump(const std::string& text) const;
|
||||
|
||||
//TODO: setters and getters
|
||||
double area;
|
||||
Base::Vector3d center;
|
||||
double getFilledArea() const { return area; }
|
||||
double getActualArea() const { return actualArea; }
|
||||
Base::Vector3d getCenter() const { return center; }
|
||||
|
||||
double area{0}; // this is the outer area without considering holes
|
||||
double actualArea{0}; // this is the net area after holes are removed
|
||||
Base::Vector3d center; // this is geometric center of the outer face
|
||||
};
|
||||
|
||||
} //end namespace TechDraw
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <BRepGProp.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepTools.hxx>
|
||||
#include <BRepAdaptor_Curve.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
@@ -47,6 +48,7 @@
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <Geom2d_Curve.hxx>
|
||||
#include <Geom2dAPI_ProjectPointOnCurve.hxx>
|
||||
#include <ShapeAnalysis.hxx>
|
||||
#include <TopExp.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
@@ -224,6 +226,11 @@ DrawViewDimension::DrawViewDimension()
|
||||
UnderTolerance.setStatus(App::Property::ReadOnly, true);
|
||||
FormatSpecUnderTolerance.setStatus(App::Property::ReadOnly, true);
|
||||
|
||||
// legacy behaviour if this is false
|
||||
ADD_PROPERTY_TYPE(UseActualArea, (true), "Area", App::Prop_Output,
|
||||
"If true, area dimensions return the area of the face minus the areas of any enclosed faces. \
|
||||
If false, the area of the face's outer boundary is returned.");
|
||||
|
||||
measurement = new Measure::Measurement();
|
||||
// TODO: should have better initial datumLabel position than (0, 0) in the DVP?? something
|
||||
// closer to the object being measured?
|
||||
@@ -277,6 +284,7 @@ void DrawViewDimension::resetArea()
|
||||
{
|
||||
m_areaPoint.center = Base::Vector3d(0, 0, 0);
|
||||
m_areaPoint.area = 0.0;
|
||||
m_areaPoint.actualArea = 0.0;
|
||||
}
|
||||
|
||||
void DrawViewDimension::onChanged(const App::Property* prop)
|
||||
@@ -686,7 +694,6 @@ double DrawViewDimension::getDimValue()
|
||||
//! retrieve the dimension value for "true" dimensions. The returned value is in internal units (mm).
|
||||
double DrawViewDimension::getTrueDimValue() const
|
||||
{
|
||||
// Base::Console().Message("DVD::getTrueDimValue()\n");
|
||||
double result = 0.0;
|
||||
|
||||
if (Type.isValue("Distance") || Type.isValue("DistanceX") || Type.isValue("DistanceY")) {
|
||||
@@ -763,7 +770,17 @@ double DrawViewDimension::getProjectedDimValue() const
|
||||
result = legAngle;
|
||||
}
|
||||
else if (Type.isValue("Area")) {
|
||||
result = m_areaPoint.area / scale / scale;
|
||||
// 2d reference makes scaled values in areaPoint
|
||||
// 3d reference makes actual values in areaPoint :p
|
||||
double divisor{scale / scale};
|
||||
if (has3DReferences()) {
|
||||
divisor = 1.0;
|
||||
}
|
||||
if (UseActualArea.getValue()) {
|
||||
result = m_areaPoint.actualArea / divisor;
|
||||
} else {
|
||||
result = m_areaPoint.area / divisor;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1383,27 +1400,71 @@ areaPoint DrawViewDimension::getAreaParameters(ReferenceVector references)
|
||||
ssMessage << getNameInDocument() << " can not find geometry for 2d reference (4)";
|
||||
throw Base::RuntimeError(ssMessage.str());
|
||||
}
|
||||
auto dvp = static_cast<DrawViewPart*>(refObject);
|
||||
|
||||
pts.area = face->getArea();
|
||||
pts.center = face->getCenter();
|
||||
auto filteredFaces = GeometryUtils::findHolesInFace(dvp, references.front().getSubName());
|
||||
auto perforatedFace = GeometryUtils::makePerforatedFace(face, filteredFaces);
|
||||
|
||||
// these areas are scaled because the source geometry is scaled, but it makes no sense to
|
||||
// report a scaled area.
|
||||
auto unscale = getViewPart()->getScale() * getViewPart()->getScale();
|
||||
pts.area = face->getArea() / unscale; // this will be the 2d area as projected onto the page? not really filled area?
|
||||
pts.actualArea = getActualArea(perforatedFace) / unscale;
|
||||
pts.center = getFaceCenter(perforatedFace);
|
||||
pts.invertY(); // geometry class is over, back to -Y up/.
|
||||
}
|
||||
else {
|
||||
// this is a 3d reference
|
||||
// this is a 3d reference. perforations should be handled for us by OCC
|
||||
TopoDS_Shape geometry = references[0].getGeometry();
|
||||
if (geometry.IsNull() || geometry.ShapeType() != TopAbs_FACE) {
|
||||
throw Base::RuntimeError("Geometry for dimension reference is null.");
|
||||
}
|
||||
const TopoDS_Face& face = TopoDS::Face(geometry);
|
||||
|
||||
GProp_GProps props;
|
||||
BRepGProp::SurfaceProperties(face, props);
|
||||
pts.area = props.Mass();
|
||||
pts.center = DrawUtil::toVector3d(props.CentreOfMass());
|
||||
// these areas are unscaled as the source is 3d geometry.
|
||||
pts.area = getFilledArea(face);
|
||||
pts.actualArea = getActualArea(face);
|
||||
pts.center = getFaceCenter(face);
|
||||
pts.move(getViewPart()->getCurrentCentroid());
|
||||
pts.project(getViewPart());
|
||||
}
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
|
||||
//! returns the center of mass of a face (density = k)
|
||||
Base::Vector3d DrawViewDimension::getFaceCenter(const TopoDS_Face& face)
|
||||
{
|
||||
GProp_GProps props;
|
||||
BRepGProp::SurfaceProperties(face, props);
|
||||
auto center = DrawUtil::toVector3d(props.CentreOfMass());
|
||||
return center;
|
||||
}
|
||||
|
||||
|
||||
//! returns the "net" area of a face (area of the face's outer boundary less the area of any holes)
|
||||
double DrawViewDimension::getActualArea(const TopoDS_Face& face)
|
||||
{
|
||||
GProp_GProps props;
|
||||
BRepGProp::SurfaceProperties(face, props);
|
||||
return props.Mass();
|
||||
}
|
||||
|
||||
|
||||
//! returns the "gross" area of a face (area of the face's outer boundary)
|
||||
double DrawViewDimension::getFilledArea(const TopoDS_Face& face)
|
||||
{
|
||||
TopoDS_Wire outerwire = ShapeAnalysis::OuterWire(face);
|
||||
if (outerwire.IsNull()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double area = ShapeAnalysis::ContourArea(outerwire);
|
||||
return area;
|
||||
}
|
||||
|
||||
|
||||
DrawViewPart* DrawViewDimension::getViewPart() const
|
||||
{
|
||||
if (References2D.getValues().empty()) {
|
||||
|
||||
@@ -92,6 +92,7 @@ public:
|
||||
|
||||
Part::PropertyTopoShapeList SavedGeometry;
|
||||
App::PropertyVectorList BoxCorners;
|
||||
App::PropertyBool UseActualArea;
|
||||
|
||||
enum RefType
|
||||
{
|
||||
@@ -212,6 +213,10 @@ public:
|
||||
Base::BoundBox3d getSavedBox();
|
||||
Base::BoundBox3d getFeatureBox();
|
||||
|
||||
static double getActualArea(const TopoDS_Face& face);
|
||||
static double getFilledArea(const TopoDS_Face& face);
|
||||
static Base::Vector3d getFaceCenter(const TopoDS_Face& face);
|
||||
|
||||
protected:
|
||||
void handleChangedPropertyType(Base::XMLReader&, const char*, App::Property*) override;
|
||||
void Restore(Base::XMLReader& reader) override;
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
<UserDocu>getAnglePoints() - returns list of points for angle Dimension</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getAreaPoints">
|
||||
<Documentation>
|
||||
<UserDocu>getAreaPoints() - returns list of values (center, filled area, actual area) for area Dimension.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getArrowPositions">
|
||||
<Documentation>
|
||||
<UserDocu>getArrowPositions() - returns list of locations or Dimension Arrowheads. Locations are in unscaled coordinates of parent View </UserDocu>
|
||||
|
||||
@@ -111,6 +111,22 @@ PyObject* DrawViewDimensionPy::getAnglePoints(PyObject* args)
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
|
||||
|
||||
PyObject* DrawViewDimensionPy::getAreaPoints(PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DrawViewDimension* dvd = getDrawViewDimensionPtr();
|
||||
areaPoint pts = dvd->getAreaPoint();
|
||||
Py::List ret;
|
||||
ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.getCenter()))));
|
||||
ret.append(Py::asObject(PyFloat_FromDouble(pts.getFilledArea())));
|
||||
ret.append(Py::asObject(PyFloat_FromDouble(pts.getActualArea())));
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
|
||||
PyObject* DrawViewDimensionPy::getArrowPositions(PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
|
||||
@@ -82,9 +82,11 @@
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
|
||||
#include <Mod/Part/App/FaceMakerCheese.h>
|
||||
#include <Mod/Part/App/Geometry.h>
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
|
||||
#include "DrawViewPart.h"
|
||||
#include "Geometry.h"
|
||||
#include "ShapeUtils.h"
|
||||
#include "DrawUtil.h"
|
||||
@@ -145,10 +147,14 @@ void Wire::dump(std::string s)
|
||||
BRepTools::Write(toOccWire(), s.c_str()); //debug
|
||||
}
|
||||
|
||||
// note that the face returned is inverted in Y
|
||||
TopoDS_Face Face::toOccFace() const
|
||||
{
|
||||
if (wires.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
TopoDS_Face result;
|
||||
//if (!wires.empty) {
|
||||
BRepBuilderAPI_MakeFace mkFace(wires.front()->toOccWire(), true);
|
||||
int limit = wires.size();
|
||||
int iwire = 1;
|
||||
@@ -1734,3 +1740,105 @@ double GeometryUtils::edgeLength(TopoDS_Edge occEdge)
|
||||
}
|
||||
}
|
||||
|
||||
//! return a perforated shape/face (using Part::FaceMakerCheese) formed by creating holes in the input face.
|
||||
TopoDS_Face GeometryUtils::makePerforatedFace(FacePtr bigCheese, const std::vector<FacePtr> &holesAll)
|
||||
{
|
||||
std::vector<TopoDS_Wire> cheeseIngredients;
|
||||
|
||||
// v0.0 brute force
|
||||
|
||||
// Note: TD Faces are not perforated and should only ever have 1 wire. They are capable of
|
||||
// having voids, but for now we will just take the first contour wire in all cases.
|
||||
|
||||
if (bigCheese->wires.empty()) {
|
||||
// run in circles. scream and shout.
|
||||
return {};
|
||||
}
|
||||
|
||||
auto flippedFace = ShapeUtils::fromQtAsFace(bigCheese->toOccFace());
|
||||
|
||||
if (holesAll.empty()) {
|
||||
return flippedFace;
|
||||
}
|
||||
|
||||
auto outer = ShapeUtils::fromQtAsWire(bigCheese->wires.front()->toOccWire());
|
||||
cheeseIngredients.push_back(outer);
|
||||
for (auto& hole : holesAll) {
|
||||
if (hole->wires.empty()) {
|
||||
continue;
|
||||
}
|
||||
auto holeR3 = ShapeUtils::fromQtAsWire(hole->wires.front()->toOccWire());
|
||||
cheeseIngredients.push_back(holeR3);
|
||||
}
|
||||
|
||||
TopoDS_Shape faceShape;
|
||||
try {
|
||||
faceShape = Part::FaceMakerCheese::makeFace(cheeseIngredients);
|
||||
}
|
||||
catch (const Standard_Failure &e) {
|
||||
Base::Console().Warning("Area - could not make holes in face\n");
|
||||
return flippedFace;
|
||||
}
|
||||
|
||||
|
||||
// v0.0 just grab the first face
|
||||
TopoDS_Face foundFace;
|
||||
TopExp_Explorer expFaces(faceShape, TopAbs_FACE);
|
||||
if (expFaces.More()) {
|
||||
foundFace = TopoDS::Face(expFaces.Current());
|
||||
}
|
||||
// TODO: sort out the compound => shape but !compound => face business in FaceMakerCheese here.
|
||||
// first guess is it does not affect us?
|
||||
|
||||
return foundFace;
|
||||
}
|
||||
|
||||
|
||||
//! find faces within the bounds of the input face
|
||||
std::vector<FacePtr> GeometryUtils::findHolesInFace(const DrawViewPart* dvp, const std::string& bigCheeseSubRef)
|
||||
{
|
||||
if (!dvp || bigCheeseSubRef.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<FacePtr> holes;
|
||||
auto bigCheeseIndex = DU::getIndexFromName(bigCheeseSubRef);
|
||||
|
||||
// v0.0 brute force
|
||||
auto facesAll = dvp->getFaceGeometry();
|
||||
if (facesAll.empty()) {
|
||||
// tarfu
|
||||
throw Base::RuntimeError("GU::findHolesInFace - no holes to find!!");
|
||||
}
|
||||
|
||||
auto bigCheeseFace = facesAll.at(bigCheeseIndex);
|
||||
auto bigCheeseOCCFace = bigCheeseFace->toOccFace();
|
||||
auto bigCheeseArea = bigCheeseFace->getArea();
|
||||
|
||||
int iFace{0};
|
||||
for (auto& face : facesAll) {
|
||||
if (iFace == bigCheeseIndex) {
|
||||
iFace++;
|
||||
continue;
|
||||
}
|
||||
if (face->getArea() > bigCheeseArea) {
|
||||
iFace++;
|
||||
continue;
|
||||
}
|
||||
auto faceCenter = DU::togp_Pnt(face->getCenter());
|
||||
auto faceCenterVertex = BRepBuilderAPI_MakeVertex(faceCenter);
|
||||
auto distance = DU::simpleMinDist(faceCenterVertex, bigCheeseOCCFace);
|
||||
if (distance > EWTOLERANCE) {
|
||||
// hole center not within outer contour. not the best test but cheese maker handles it
|
||||
// for us?
|
||||
// FaceMakerCheese does not support partial overlaps and just ignores them?
|
||||
iFace++;
|
||||
continue;
|
||||
}
|
||||
holes.push_back(face);
|
||||
iFace++;
|
||||
}
|
||||
|
||||
return holes;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/Writer.h>
|
||||
|
||||
#include <Mod/TechDraw/TechDrawGlobal.h>
|
||||
|
||||
#include <TopoDS_Edge.hxx>
|
||||
@@ -42,6 +43,8 @@ class TopoShape;
|
||||
|
||||
namespace TechDraw {
|
||||
|
||||
class DrawViewPart;
|
||||
|
||||
enum ExtractionType { //obs
|
||||
Plain,
|
||||
WithHidden,
|
||||
@@ -339,6 +342,7 @@ class TechDrawExport Wire
|
||||
void dump(std::string s);
|
||||
BaseGeomPtrVector geoms;
|
||||
};
|
||||
using WirePtr = std::shared_ptr<Wire>;
|
||||
|
||||
/// Simple Collection of geometric features based on BaseGeom inherited classes in order
|
||||
class TechDrawExport Face
|
||||
@@ -429,6 +433,7 @@ class TechDrawExport GeometryUtils
|
||||
{}
|
||||
};
|
||||
|
||||
// TODO: prune unused methods
|
||||
/// Find an unused geom starts or ends at atPoint.
|
||||
/*!
|
||||
* returns index[1:geoms.size()), reversed [true, false]
|
||||
@@ -452,6 +457,9 @@ class TechDrawExport GeometryUtils
|
||||
|
||||
static double edgeLength(TopoDS_Edge occEdge);
|
||||
|
||||
static TopoDS_Face makePerforatedFace(FacePtr bigCheese, const std::vector<FacePtr>& holesAll);
|
||||
static std::vector<FacePtr> findHolesInFace(const DrawViewPart* dvp, const std::string& bigCheeseSubRef);
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -222,6 +222,7 @@ Base::Vector3d ShapeUtils::findCentroidVec(const TopoDS_Shape& shape, const gp_A
|
||||
return Base::Vector3d(p.X(), p.Y(), p.Z());
|
||||
}
|
||||
|
||||
|
||||
//!scales & mirrors a shape about a center
|
||||
TopoDS_Shape ShapeUtils::mirrorShapeVec(const TopoDS_Shape& input, const Base::Vector3d& inputCenter,
|
||||
double scale)
|
||||
@@ -349,6 +350,34 @@ TopoDS_Shape ShapeUtils::fromQt(const TopoDS_Shape& inShape)
|
||||
return mkTrf.Shape();
|
||||
}
|
||||
|
||||
//! specialization offromQt for Faces. should be templated?
|
||||
TopoDS_Face ShapeUtils::fromQtAsFace(const TopoDS_Shape& inShape)
|
||||
{
|
||||
auto flippedShape = ShapeUtils::fromQt(inShape);
|
||||
|
||||
TopoDS_Face foundFace;
|
||||
TopExp_Explorer expFaces(flippedShape, TopAbs_FACE);
|
||||
if (expFaces.More()) {
|
||||
foundFace = TopoDS::Face(expFaces.Current());
|
||||
}
|
||||
|
||||
return foundFace;
|
||||
}
|
||||
|
||||
//! specialization offromQt for Wire. should be templated?
|
||||
TopoDS_Wire ShapeUtils::fromQtAsWire(const TopoDS_Shape& inShape)
|
||||
{
|
||||
auto flippedShape = ShapeUtils::fromQt(inShape);
|
||||
|
||||
TopoDS_Wire foundWire;
|
||||
TopExp_Explorer expWires(flippedShape, TopAbs_WIRE);
|
||||
if (expWires.More()) {
|
||||
foundWire = TopoDS::Wire(expWires.Current());
|
||||
}
|
||||
|
||||
return foundWire;
|
||||
}
|
||||
|
||||
//! transforms a shape defined in conventional coordinates coordinates into one defined by
|
||||
//! invertedY (Qt) coordinates
|
||||
TopoDS_Shape ShapeUtils::toQt(const TopoDS_Shape& inShape)
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <gp_Ax2.hxx>
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
@@ -113,6 +115,8 @@ public:
|
||||
|
||||
static TopoDS_Shape fromQt(const TopoDS_Shape& inShape);
|
||||
static TopoDS_Shape toQt(const TopoDS_Shape& inShape);
|
||||
static TopoDS_Wire fromQtAsWire(const TopoDS_Shape& inShape);
|
||||
static TopoDS_Face fromQtAsFace(const TopoDS_Shape& inShape);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user