[TD]fix area calculation

This commit is contained in:
wandererfan
2024-10-17 15:27:11 -04:00
committed by Yorik van Havre
parent cb328a6ce7
commit 2ff1a2b6de
10 changed files with 261 additions and 14 deletions

View File

@@ -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());

View File

@@ -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

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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>

View File

@@ -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, "")) {

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -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)

View File

@@ -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);
};
}