Merge pull request #15811 from AjinkyaDahale/sk-refactor-stage-1

[Sketcher] Round 1 of refactors
This commit is contained in:
Chris Hennes
2024-12-03 22:44:10 -05:00
committed by GitHub
10 changed files with 1954 additions and 944 deletions

View File

@@ -793,6 +793,11 @@ GeomBSplineCurve* GeomCurve::toNurbs(double first, double last) const
return toBSpline(first, last);
}
GeomCurve* GeomCurve::createArc([[maybe_unused]] double first, [[maybe_unused]] double last) const
{
THROWM(Base::NotImplementedError, "createArc: not implemented for this type of curve");
}
bool GeomCurve::tangent(double u, gp_Dir& dir) const
{
Handle(Geom_Curve) curve = Handle(Geom_Curve)::DownCast(handle());
@@ -1380,6 +1385,14 @@ Geometry *GeomBSplineCurve::copy() const
}
}
GeomCurve* GeomBSplineCurve::createArc(double first, double last) const
{
auto newBsp = static_cast<Part::GeomBSplineCurve*>(this->copy());
newBsp->Trim(first, last);
return newBsp;
}
int GeomBSplineCurve::countPoles() const
{
return myCurve->NbPoles();
@@ -2270,6 +2283,14 @@ Geometry *GeomTrimmedCurve::copy() const
return newCurve;
}
GeomCurve* GeomTrimmedCurve::createArc(double first, double last) const
{
auto newArc = static_cast<Part::GeomTrimmedCurve*>(this->copy());
newArc->setRange(first, last);
return newArc;
}
// Persistence implementer
unsigned int GeomTrimmedCurve::getMemSize () const
{
@@ -2592,6 +2613,14 @@ Geometry *GeomCircle::copy() const
return newCirc;
}
GeomCurve* GeomCircle::createArc(double first, double last) const
{
auto newArc = new GeomArcOfCircle(Handle(Geom_Circle)::DownCast(this->handle()->Copy()));
newArc->setRange(first, last, false);
return newArc;
}
GeomBSplineCurve* GeomCircle::toNurbs(double first, double last) const
{
// for an arc of circle use the generic method
@@ -3030,6 +3059,14 @@ Geometry *GeomEllipse::copy() const
return newEllipse;
}
GeomCurve* GeomEllipse::createArc(double first, double last) const
{
auto newArc = new GeomArcOfEllipse(Handle(Geom_Ellipse)::DownCast(this->handle()->Copy()));
newArc->setRange(first, last, false);
return newArc;
}
GeomBSplineCurve* GeomEllipse::toNurbs(double first, double last) const
{
// for an arc of ellipse use the generic method
@@ -3569,13 +3606,21 @@ void GeomHyperbola::setHandle(const Handle(Geom_Hyperbola)& c)
myCurve = Handle(Geom_Hyperbola)::DownCast(c->Copy());
}
Geometry *GeomHyperbola::copy() const
Geometry* GeomHyperbola::copy() const
{
GeomHyperbola *newHyp = new GeomHyperbola(myCurve);
newHyp->copyNonTag(this);
return newHyp;
}
GeomCurve* GeomHyperbola::createArc(double first, double last) const
{
auto newArc = new GeomArcOfHyperbola(Handle(Geom_Hyperbola)::DownCast(this->handle()->Copy()));
newArc->setRange(first, last, false);
return newArc;
}
GeomBSplineCurve* GeomHyperbola::toNurbs(double first, double last) const
{
return GeomConic::toNurbs(first, last);
@@ -4021,6 +4066,14 @@ Geometry *GeomParabola::copy() const
return newPar;
}
GeomCurve* GeomParabola::createArc(double first, double last) const
{
auto newArc = new GeomArcOfParabola(Handle(Geom_Parabola)::DownCast(this->handle()->Copy()));
newArc->setRange(first, last, false);
return newArc;
}
GeomBSplineCurve* GeomParabola::toNurbs(double first, double last) const
{
// the default implementation suffices because a non-rational B-spline with

View File

@@ -209,8 +209,15 @@ public:
The default implementation does the same as \ref toBSpline.
In sub-classes this can be reimplemented to create a real
NURBS curve and not just an approximation.
*/
*/
virtual GeomBSplineCurve* toNurbs(double first, double last) const;
/*!
* \brief createArc Generates a curve that is an arc of this curve between given parameters
* \param first Parameter at start of arc
* \param last Parameter at end of arc. This may be < `first` for periodic curves.
* \return the new curve
*/
virtual GeomCurve* createArc(double first, double last) const;
bool tangent(double u, gp_Dir&) const;
bool tangent(double u, Base::Vector3d& dir) const;
Base::Vector3d pointAtParameter(double u) const;
@@ -294,6 +301,8 @@ public:
~GeomBSplineCurve() override;
Geometry *copy() const override;
GeomCurve* createArc(double first, double last) const override;
/*!
* Interpolate a spline passing through the given points without tangency.
*/
@@ -430,6 +439,7 @@ public:
~GeomTrimmedCurve() override;
Geometry *copy() const override;
GeomCurve* createArc(double first, double last) const override;
// Persistence implementer ---------------------
unsigned int getMemSize() const override;
void Save(Base::Writer &/*writer*/) const override;
@@ -514,6 +524,7 @@ public:
~GeomCircle() override;
Geometry *copy() const override;
GeomCurve* createArc(double first, double last) const override;
double getRadius() const;
void setRadius(double Radius);
@@ -575,6 +586,7 @@ public:
~GeomEllipse() override;
Geometry *copy() const override;
GeomCurve* createArc(double first, double last) const override;
double getMajorRadius() const;
void setMajorRadius(double Radius);
double getMinorRadius() const;
@@ -641,7 +653,8 @@ public:
GeomHyperbola();
explicit GeomHyperbola(const Handle(Geom_Hyperbola)&);
~GeomHyperbola() override;
Geometry *copy() const override;
Geometry* copy() const override;
GeomCurve* createArc(double first, double last) const override;
double getMajorRadius() const;
void setMajorRadius(double Radius);
@@ -705,7 +718,8 @@ public:
GeomParabola();
explicit GeomParabola(const Handle(Geom_Parabola)&);
~GeomParabola() override;
Geometry *copy() const override;
Geometry* copy() const override;
GeomCurve* createArc(double first, double last) const override;
double getFocal() const;
void setFocal(double length);

View File

@@ -2628,13 +2628,12 @@ class TestTopologicalNamingProblem(unittest.TestCase):
self.PadSketch.trim(2, App.Vector(7.337847, -25.000000, 0))
self.PadSketch.addConstraint(Sketcher.Constraint("Equal", 3, 1))
self.PadSketch.addConstraint(Sketcher.Constraint("Horizontal", 5))
self.PadSketch.addConstraint(Sketcher.Constraint("Radius", 4, 73.031111))
self.PadSketch.setDatum(18, App.Units.Quantity("70.000000 mm"))
self.PadSketch.addConstraint(
radConstr = self.PadSketch.addConstraint(Sketcher.Constraint("Radius", 4, 73.031111))
self.PadSketch.setDatum(radConstr, App.Units.Quantity("70.000000 mm"))
distYConstr = self.PadSketch.addConstraint(
Sketcher.Constraint("DistanceY", 4, 3, -1, 1, 88.867210)
)
self.PadSketch.setDatum(19, App.Units.Quantity("80.000000 mm"))
self.PadSketch.setDatum(distYConstr, App.Units.Quantity("80.000000 mm"))
self.Doc.recompute()
self.assertTrue(self.Sketch001.isValid())

View File

@@ -239,6 +239,25 @@ void Constraint::substituteIndex(int fromGeoId, int toGeoId)
}
}
void Constraint::substituteIndexAndPos(int fromGeoId,
PointPos fromPosId,
int toGeoId,
PointPos toPosId)
{
if (this->First == fromGeoId && this->FirstPos == fromPosId) {
this->First = toGeoId;
this->FirstPos = toPosId;
}
if (this->Second == fromGeoId && this->SecondPos == fromPosId) {
this->Second = toGeoId;
this->SecondPos = toPosId;
}
if (this->Third == fromGeoId && this->ThirdPos == fromPosId) {
this->Third = toGeoId;
this->ThirdPos = toPosId;
}
}
std::string Constraint::typeToString(ConstraintType type)
{
return type2str[type];

View File

@@ -132,6 +132,21 @@ public:
/// utility function to swap the index in First/Second/Third of the provided constraint from the
/// fromGeoId GeoId to toGeoId
void substituteIndex(int fromGeoId, int toGeoId);
/// utility function to swap the index and position in First/Second/Third of the provided
/// constraint from {fromGeoId, fromPosId} to {toGeoId, toPosId}.
void substituteIndexAndPos(int fromGeoId, PointPos fromPosId, int toGeoId, PointPos toPosId);
/// utility function to check if `geoId` is one of the geometries
bool involvesGeoId(int geoId) const
{
return First == geoId || Second == geoId || Third == geoId;
}
/// utility function to check if (`geoId`, `posId`) is one of the points/curves
bool involvesGeoIdAndPosId(int geoId, PointPos posId) const
{
return (First == geoId && FirstPos == posId) || (Second == geoId && SecondPos == posId)
|| (Third == geoId && ThirdPos == posId);
}
std::string typeToString() const
{

View File

@@ -55,7 +55,8 @@ GeometryFacade::~GeometryFacade()
}
}
std::unique_ptr<GeometryFacade> GeometryFacade::getFacade(Part::Geometry* geometry, bool owner)
std::unique_ptr<GeometryFacade> GeometryFacade::getFacade(const Part::Geometry* geometry,
bool owner)
{
if (geometry) {
return std::unique_ptr<GeometryFacade>(new GeometryFacade(geometry, owner));
@@ -67,18 +68,6 @@ std::unique_ptr<GeometryFacade> GeometryFacade::getFacade(Part::Geometry* geomet
// return std::make_unique<GeometryFacade>(geometry);
}
std::unique_ptr<const GeometryFacade> GeometryFacade::getFacade(const Part::Geometry* geometry)
{
if (geometry) {
return std::unique_ptr<const GeometryFacade>(new GeometryFacade(geometry));
}
else {
return std::unique_ptr<const GeometryFacade>(nullptr);
}
// make_unique has no access to private constructor
// return std::make_unique<const GeometryFacade>(geometry);
}
void GeometryFacade::setGeometry(Part::Geometry* geometry)
{
Geo = geometry;
@@ -152,7 +141,7 @@ int GeometryFacade::getId(const Part::Geometry* geometry)
return gf->getId();
}
void GeometryFacade::setId(Part::Geometry* geometry, int id)
void GeometryFacade::setId(const Part::Geometry* geometry, int id)
{
auto gf = GeometryFacade::getFacade(geometry);
gf->setId(id);

View File

@@ -118,8 +118,8 @@ protected:
friend class GeometryFacadePy;
public: // Factory methods
static std::unique_ptr<GeometryFacade> getFacade(Part::Geometry* geometry, bool owner = false);
static std::unique_ptr<const GeometryFacade> getFacade(const Part::Geometry* geometry);
static std::unique_ptr<GeometryFacade> getFacade(const Part::Geometry* geometry,
bool owner = false);
public: // Utility methods
static void ensureSketchGeometryExtension(Part::Geometry* geometry);
@@ -132,7 +132,7 @@ public: // Utility methods
static void setInternalType(Part::Geometry* geometry, InternalType::InternalType type);
static bool getBlocked(const Part::Geometry* geometry);
static int getId(const Part::Geometry* geometry);
static void setId(Part::Geometry* geometry, int id);
static void setId(const Part::Geometry* geometry, int id);
public:
// Explicit deletion to show intent (not that it is needed)

File diff suppressed because it is too large Load Diff

View File

@@ -218,6 +218,9 @@ public:
/// Sync frozen external geometries
int syncGeometry(const std::vector<int>& geoIds);
template<typename returnType>
returnType performActionByGeomType(const Part::Geometry* geo);
/** returns a pointer to a given Geometry index, possible indexes are:
* id>=0 for user defined geometries,
* id==-1 for the horizontal sketch axis,
@@ -357,6 +360,11 @@ public:
/// retrieves the coordinates of a point
static Base::Vector3d getPoint(const Part::Geometry* geo, PointPos PosId);
Base::Vector3d getPoint(int GeoId, PointPos PosId) const;
template<class geomType>
static Base::Vector3d getPointForGeometry(const geomType* geo, PointPos PosId)
{
return Base::Vector3d();
}
/// toggle geometry to draft line
int toggleConstruction(int GeoId);
@@ -398,6 +406,22 @@ public:
int trim(int geoId, const Base::Vector3d& point);
/// extend a curve
int extend(int geoId, double increment, PointPos endPoint);
/// Once smaller pieces have been created from a larger curve (by split or trim, say), derive
/// the constraint that will replace the given one (which is to be deleted). NOTE: Currently
/// assuming all constraints on the end points of the old curve have been transferred or
/// destroyed
/// Returns whether or not new constraint(s) was/were added.
bool deriveConstraintsForPieces(const int oldId,
const std::vector<int>& newIds,
const Constraint* con,
std::vector<Constraint*>& newConstraints);
// Explicitly giving `newGeos` for cases where they are not yet added
bool deriveConstraintsForPieces(const int oldId,
const std::vector<int>& newIds,
const std::vector<const Part::Geometry*>& newGeo,
const Constraint* con,
std::vector<Constraint*>& newConstraints);
/// split a curve
int split(int geoId, const Base::Vector3d& point);
/*!
@@ -439,6 +463,8 @@ public:
double perpscale = 1.0);
int removeAxesAlignment(const std::vector<int>& geoIdList);
static bool isClosedCurve(const Part::Geometry* geo);
static bool hasInternalGeometry(const Part::Geometry* geo);
/// Exposes all internal geometry of an object supporting internal geometry
/*!
* \return -1 on error
@@ -451,6 +477,14 @@ public:
geometry \retval int - returns -1 on error, otherwise the number of deleted elements
*/
int deleteUnusedInternalGeometry(int GeoId, bool delgeoid = false);
/*!
\brief Same as `deleteUnusedInternalGeometry`, but changes `GeoId` to the new Id of the
geometry, or to `GeoEnum::GeoUndef` if the geometry is deleted as well. \param GeoId - the
geometry having the internal geometry to delete \param delgeoid - if true in addition to the
unused internal geometry also deletes the GeoId geometry \retval int - returns -1 on error,
otherwise the number of deleted elements
*/
int deleteUnusedInternalGeometryAndUpdateGeoId(int& GeoId, bool delgeoid = false);
/*!
\brief Approximates the given geometry with a B-spline
\param GeoId - the geometry to approximate
@@ -865,7 +899,7 @@ protected:
supportedGeometry(const std::vector<Part::Geometry*>& geoList) const;
void updateGeoHistory();
void generateId(Part::Geometry* geo);
void generateId(const Part::Geometry* geo);
/*!
\brief Transfer constraints on lines being filleted.
@@ -937,6 +971,11 @@ protected:
int thirdGeoId = GeoEnum::GeoUndef,
Sketcher::PointPos thirdPos = Sketcher::PointPos::none);
std::unique_ptr<Constraint> getConstraintAfterDeletingGeo(const Constraint* constr,
const int deletedGeoId) const;
void changeConstraintAfterDeletingGeo(Constraint* constr, const int deletedGeoId) const;
private:
/// Flag to allow external geometry from other bodies than the one this sketch belongs to
bool allowOtherBody;

View File

@@ -12,6 +12,115 @@
#include <Mod/Sketcher/App/SketchObject.h>
#include <src/App/InitApplication.h>
void setupLineSegment(Part::GeomLineSegment& lineSeg)
{
Base::Vector3d coords1(1.0, 2.0, 0.0);
Base::Vector3d coords2(3.0, 4.0, 0.0);
lineSeg.setPoints(coords1, coords2);
}
void setupCircle(Part::GeomCircle& circle)
{
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
Base::Vector3d splitPoint(2.0, 3.1, 0.0);
double radius = 3.0;
circle.setCenter(coordsCenter);
circle.setRadius(radius);
}
void setupArcOfCircle(Part::GeomArcOfCircle& arcOfCircle)
{
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double radius = 3.0;
double startParam = M_PI / 3, endParam = M_PI * 1.5;
arcOfCircle.setCenter(coordsCenter);
arcOfCircle.setRadius(radius);
arcOfCircle.setRange(startParam, endParam, true);
}
void setupEllipse(Part::GeomEllipse& ellipse)
{
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double majorRadius = 4.0;
double minorRadius = 3.0;
ellipse.setCenter(coordsCenter);
ellipse.setMajorRadius(majorRadius);
ellipse.setMinorRadius(minorRadius);
}
void setupArcOfParabola(Part::GeomArcOfParabola& aop)
{
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double focal = 3.0;
double startParam = -M_PI * 1.5, endParam = M_PI * 1.5;
aop.setCenter(coordsCenter);
aop.setFocal(focal);
aop.setRange(startParam, endParam, true);
}
std::unique_ptr<Part::GeomBSplineCurve> createTypicalNonPeriodicBSpline()
{
int degree = 3;
std::vector<Base::Vector3d> poles;
poles.emplace_back(1, 0, 0);
poles.emplace_back(1, 1, 0);
poles.emplace_back(1, 0.5, 0);
poles.emplace_back(0, 1, 0);
poles.emplace_back(0, 0, 0);
std::vector<double> weights(5, 1.0);
std::vector<double> knotsNonPeriodic = {0.0, 1.0, 2.0};
std::vector<int> multiplicitiesNonPeriodic = {degree + 1, 1, degree + 1};
return std::make_unique<Part::GeomBSplineCurve>(poles,
weights,
knotsNonPeriodic,
multiplicitiesNonPeriodic,
degree,
false);
}
std::unique_ptr<Part::GeomBSplineCurve> createTypicalPeriodicBSpline()
{
int degree = 3;
std::vector<Base::Vector3d> poles;
poles.emplace_back(1, 0, 0);
poles.emplace_back(1, 1, 0);
poles.emplace_back(1, 0.5, 0);
poles.emplace_back(0, 1, 0);
poles.emplace_back(0, 0, 0);
std::vector<double> weights(5, 1.0);
std::vector<double> knotsPeriodic = {0.0, 0.3, 1.0, 1.5, 1.8, 2.0};
std::vector<int> multiplicitiesPeriodic(6, 1);
return std::make_unique<Part::GeomBSplineCurve>(poles,
weights,
knotsPeriodic,
multiplicitiesPeriodic,
degree,
true);
}
int countConstraintsOfType(const Sketcher::SketchObject* obj, const Sketcher::ConstraintType cType)
{
const std::vector<Sketcher::Constraint*>& constraints = obj->Constraints.getValues();
int result = std::count_if(constraints.begin(),
constraints.end(),
[&cType](const Sketcher::Constraint* constr) {
return constr->Type == cType;
});
return result;
}
// Get point at the parameter after scaling the range to [0, 1].
Base::Vector3d getPointAtNormalizedParameter(const Part::GeomCurve& curve, double param)
{
return curve.pointAtParameter(curve.getFirstParameter()
+ (curve.getLastParameter() - curve.getFirstParameter()) * param);
}
// TODO: How to set up B-splines here?
// It's not straightforward to change everything from a "default" one.
class SketchObjectTest: public ::testing::Test
{
protected:
@@ -161,6 +270,879 @@ TEST_F(SketchObjectTest, testGeoIdFromShapeTypeRootPoint)
EXPECT_EQ(posId, Sketcher::PointPos::start);
}
TEST_F(SketchObjectTest, testGetPointFromGeomPoint)
{
// Arrange
Base::Vector3d coords(1.0, 2.0, 0.0);
Part::GeomPoint point(coords);
// Act
auto ptStart = Sketcher::SketchObject::getPoint(&point, Sketcher::PointPos::start);
auto ptMid = Sketcher::SketchObject::getPoint(&point, Sketcher::PointPos::mid);
auto ptEnd = Sketcher::SketchObject::getPoint(&point, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&point, Sketcher::PointPos::none);
// Assert
EXPECT_DOUBLE_EQ(ptStart[0], 1.0);
EXPECT_DOUBLE_EQ(ptStart[1], 2.0);
EXPECT_DOUBLE_EQ(ptMid[0], 1.0);
EXPECT_DOUBLE_EQ(ptMid[1], 2.0);
EXPECT_DOUBLE_EQ(ptEnd[0], 1.0);
EXPECT_DOUBLE_EQ(ptEnd[1], 2.0);
}
TEST_F(SketchObjectTest, testGetPointFromGeomLineSegment)
{
// Arrange
Base::Vector3d coords1(1.0, 2.0, 0.0);
Base::Vector3d coords2(3.0, 4.0, 0.0);
Part::GeomLineSegment lineSeg;
lineSeg.setPoints(coords1, coords2);
// Act
auto ptStart = Sketcher::SketchObject::getPoint(&lineSeg, Sketcher::PointPos::start);
// TODO: Maybe we want this to give an error instead of some default value
auto ptMid = Sketcher::SketchObject::getPoint(&lineSeg, Sketcher::PointPos::mid);
auto ptEnd = Sketcher::SketchObject::getPoint(&lineSeg, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&lineSeg, Sketcher::PointPos::none);
// Assert
EXPECT_DOUBLE_EQ(ptStart[0], 1.0);
EXPECT_DOUBLE_EQ(ptStart[1], 2.0);
EXPECT_DOUBLE_EQ(ptEnd[0], 3.0);
EXPECT_DOUBLE_EQ(ptEnd[1], 4.0);
}
TEST_F(SketchObjectTest, testGetPointFromGeomCircle)
{
// Arrange
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double radius = 3.0;
Part::GeomCircle circle;
circle.setCenter(coordsCenter);
circle.setRadius(radius);
// Act
// TODO: Maybe we want this to give an error instead of some default value
auto ptStart = Sketcher::SketchObject::getPoint(&circle, Sketcher::PointPos::start);
auto ptMid = Sketcher::SketchObject::getPoint(&circle, Sketcher::PointPos::mid);
// TODO: Maybe we want this to give an error instead of some default value
auto ptEnd = Sketcher::SketchObject::getPoint(&circle, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&circle, Sketcher::PointPos::none);
// Assert
// NOTE: Presently, start/end points of a circle are defined as the point on circle right of the
// the center
EXPECT_DOUBLE_EQ(ptStart[0], 1.0 + radius);
EXPECT_DOUBLE_EQ(ptStart[1], 2.0);
EXPECT_DOUBLE_EQ(ptEnd[0], 1.0 + radius);
EXPECT_DOUBLE_EQ(ptEnd[1], 2.0);
EXPECT_DOUBLE_EQ(ptMid[0], 1.0);
EXPECT_DOUBLE_EQ(ptMid[1], 2.0);
}
TEST_F(SketchObjectTest, testGetPointFromGeomEllipse)
{
// Arrange
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double majorRadius = 4.0;
double minorRadius = 3.0;
Part::GeomEllipse ellipse;
ellipse.setCenter(coordsCenter);
ellipse.setMajorRadius(majorRadius);
ellipse.setMinorRadius(minorRadius);
// Act
// TODO: Maybe we want this to give an error instead of some default value
auto ptStart = Sketcher::SketchObject::getPoint(&ellipse, Sketcher::PointPos::start);
auto ptMid = Sketcher::SketchObject::getPoint(&ellipse, Sketcher::PointPos::mid);
// TODO: Maybe we want this to give an error instead of some default value
auto ptEnd = Sketcher::SketchObject::getPoint(&ellipse, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&ellipse, Sketcher::PointPos::none);
// Assert
// NOTE: Presently, start/end points of an ellipse are defined as the point on the major axis in
// it's "positive" direction
EXPECT_DOUBLE_EQ(ptStart[0], 1.0 + majorRadius);
EXPECT_DOUBLE_EQ(ptStart[1], 2.0);
EXPECT_DOUBLE_EQ(ptEnd[0], 1.0 + majorRadius);
EXPECT_DOUBLE_EQ(ptEnd[1], 2.0);
EXPECT_DOUBLE_EQ(ptMid[0], 1.0);
EXPECT_DOUBLE_EQ(ptMid[1], 2.0);
}
TEST_F(SketchObjectTest, testGetPointFromGeomArcOfCircle)
{
// Arrange
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double radius = 3.0, startParam = M_PI / 3, endParam = M_PI * 1.5;
Part::GeomArcOfCircle arcOfCircle;
arcOfCircle.setCenter(coordsCenter);
arcOfCircle.setRadius(radius);
arcOfCircle.setRange(startParam, endParam, true);
// Act
auto ptStart = Sketcher::SketchObject::getPoint(&arcOfCircle, Sketcher::PointPos::start);
auto ptMid = Sketcher::SketchObject::getPoint(&arcOfCircle, Sketcher::PointPos::mid);
auto ptEnd = Sketcher::SketchObject::getPoint(&arcOfCircle, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&arcOfCircle, Sketcher::PointPos::none);
// Assert
// NOTE: parameters for arc of circle are CCW angles from positive x-axis
EXPECT_DOUBLE_EQ(ptStart[0], 1.0 + cos(startParam) * radius);
EXPECT_DOUBLE_EQ(ptStart[1], 2.0 + sin(startParam) * radius);
EXPECT_DOUBLE_EQ(ptEnd[0], 1.0 + cos(endParam) * radius);
EXPECT_DOUBLE_EQ(ptEnd[1], 2.0 + sin(endParam) * radius);
EXPECT_DOUBLE_EQ(ptMid[0], 1.0);
EXPECT_DOUBLE_EQ(ptMid[1], 2.0);
}
TEST_F(SketchObjectTest, testGetPointFromGeomArcOfEllipse)
{
// Arrange
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double majorRadius = 4.0;
double minorRadius = 3.0;
double startParam = M_PI / 3, endParam = M_PI * 1.5;
Part::GeomArcOfEllipse arcOfEllipse;
arcOfEllipse.setCenter(coordsCenter);
arcOfEllipse.setMajorRadius(majorRadius);
arcOfEllipse.setMinorRadius(minorRadius);
arcOfEllipse.setRange(startParam, endParam, true);
// Act
auto ptStart = Sketcher::SketchObject::getPoint(&arcOfEllipse, Sketcher::PointPos::start);
auto ptMid = Sketcher::SketchObject::getPoint(&arcOfEllipse, Sketcher::PointPos::mid);
auto ptEnd = Sketcher::SketchObject::getPoint(&arcOfEllipse, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&arcOfEllipse, Sketcher::PointPos::none);
// Assert
// NOTE: parameters for arc of ellipse are CCW angles from positive x-axis
EXPECT_DOUBLE_EQ(ptStart[0], 1.0 + cos(startParam) * majorRadius);
EXPECT_DOUBLE_EQ(ptStart[1], 2.0 + sin(startParam) * minorRadius);
EXPECT_DOUBLE_EQ(ptEnd[0], 1.0 + cos(endParam) * majorRadius);
EXPECT_DOUBLE_EQ(ptEnd[1], 2.0 + sin(endParam) * minorRadius);
EXPECT_DOUBLE_EQ(ptMid[0], 1.0);
EXPECT_DOUBLE_EQ(ptMid[1], 2.0);
}
TEST_F(SketchObjectTest, testGetPointFromGeomArcOfHyperbola)
{
// Arrange
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double majorRadius = 4.0;
double minorRadius = 3.0;
double startParam = M_PI / 3, endParam = M_PI * 1.5;
Part::GeomArcOfHyperbola arcOfHyperbola;
arcOfHyperbola.setCenter(coordsCenter);
arcOfHyperbola.setMajorRadius(majorRadius);
arcOfHyperbola.setMinorRadius(minorRadius);
arcOfHyperbola.setRange(startParam, endParam, true);
// Act
auto ptStart = Sketcher::SketchObject::getPoint(&arcOfHyperbola, Sketcher::PointPos::start);
auto ptMid = Sketcher::SketchObject::getPoint(&arcOfHyperbola, Sketcher::PointPos::mid);
auto ptEnd = Sketcher::SketchObject::getPoint(&arcOfHyperbola, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&arcOfHyperbola, Sketcher::PointPos::none);
// Assert
// FIXME: Figure out how this is defined
// EXPECT_DOUBLE_EQ(ptStart[0], 1.0);
// EXPECT_DOUBLE_EQ(ptStart[1], 2.0);
// EXPECT_DOUBLE_EQ(ptEnd[0], 1.0);
// EXPECT_DOUBLE_EQ(ptEnd[1], 2.0);
EXPECT_DOUBLE_EQ(ptMid[0], 1.0);
EXPECT_DOUBLE_EQ(ptMid[1], 2.0);
}
TEST_F(SketchObjectTest, testGetPointFromGeomArcOfParabola)
{
// Arrange
Base::Vector3d coordsCenter(1.0, 2.0, 0.0);
double focal = 3.0;
double startParam = M_PI / 3, endParam = M_PI * 1.5;
Part::GeomArcOfParabola arcOfParabola;
arcOfParabola.setCenter(coordsCenter);
arcOfParabola.setFocal(focal);
arcOfParabola.setRange(startParam, endParam, true);
// Act
auto ptStart = Sketcher::SketchObject::getPoint(&arcOfParabola, Sketcher::PointPos::start);
auto ptMid = Sketcher::SketchObject::getPoint(&arcOfParabola, Sketcher::PointPos::mid);
auto ptEnd = Sketcher::SketchObject::getPoint(&arcOfParabola, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&arcOfParabola, Sketcher::PointPos::none);
// Assert
// FIXME: Figure out how this is defined
// EXPECT_DOUBLE_EQ(ptStart[0], 1.0);
// EXPECT_DOUBLE_EQ(ptStart[1], 2.0);
// EXPECT_DOUBLE_EQ(ptEnd[0], 1.0);
// EXPECT_DOUBLE_EQ(ptEnd[1], 2.0);
EXPECT_DOUBLE_EQ(ptMid[0], 1.0);
EXPECT_DOUBLE_EQ(ptMid[1], 2.0);
}
TEST_F(SketchObjectTest, testGetPointFromGeomBSplineCurveNonPeriodic)
{
// Arrange
int degree = 3;
std::vector<Base::Vector3d> poles;
poles.emplace_back(1, 0, 0);
poles.emplace_back(1, 1, 0);
poles.emplace_back(1, 0.5, 0);
poles.emplace_back(0, 1, 0);
poles.emplace_back(0, 0, 0);
std::vector<double> weights(5, 1.0);
std::vector<double> knotsNonPeriodic = {0.0, 1.0, 2.0};
std::vector<int> multiplicitiesNonPeriodic = {degree + 1, 1, degree + 1};
Part::GeomBSplineCurve nonPeriodicBSpline(poles,
weights,
knotsNonPeriodic,
multiplicitiesNonPeriodic,
degree,
false);
// Act
auto ptStart = Sketcher::SketchObject::getPoint(&nonPeriodicBSpline, Sketcher::PointPos::start);
// TODO: Maybe we want this to give an error instead of some default value
auto ptMid = Sketcher::SketchObject::getPoint(&nonPeriodicBSpline, Sketcher::PointPos::mid);
auto ptEnd = Sketcher::SketchObject::getPoint(&nonPeriodicBSpline, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&nonPeriodicBSpline, Sketcher::PointPos::none);
// Assert
EXPECT_DOUBLE_EQ(ptStart[0], poles.front()[0]);
EXPECT_DOUBLE_EQ(ptStart[1], poles.front()[1]);
EXPECT_DOUBLE_EQ(ptEnd[0], poles.back()[0]);
EXPECT_DOUBLE_EQ(ptEnd[1], poles.back()[1]);
}
TEST_F(SketchObjectTest, testGetPointFromGeomBSplineCurvePeriodic)
{
// Arrange
int degree = 3;
std::vector<Base::Vector3d> poles;
poles.emplace_back(1, 0, 0);
poles.emplace_back(1, 1, 0);
poles.emplace_back(1, 0.5, 0);
poles.emplace_back(0, 1, 0);
poles.emplace_back(0, 0, 0);
std::vector<double> weights(5, 1.0);
std::vector<double> knotsPeriodic = {0.0, 0.3, 1.0, 1.5, 1.8, 2.0};
std::vector<int> multiplicitiesPeriodic(6, 1);
Part::GeomBSplineCurve periodicBSpline(poles,
weights,
knotsPeriodic,
multiplicitiesPeriodic,
degree,
true);
// Act
// TODO: Maybe we want this to give an error instead of some default value
auto ptStart = Sketcher::SketchObject::getPoint(&periodicBSpline, Sketcher::PointPos::start);
// TODO: Maybe we want this to give an error instead of some default value
auto ptMid = Sketcher::SketchObject::getPoint(&periodicBSpline, Sketcher::PointPos::mid);
// TODO: Maybe we want this to give an error instead of some default value
auto ptEnd = Sketcher::SketchObject::getPoint(&periodicBSpline, Sketcher::PointPos::end);
// TODO: Maybe we want this to give an error instead of some default value
auto ptNone = Sketcher::SketchObject::getPoint(&periodicBSpline, Sketcher::PointPos::none);
// Assert
// With non-trivial values for weights, knots, mults, etc, getting the coordinates is
// non-trivial as well. This is the best we can do.
EXPECT_DOUBLE_EQ(ptStart[0], ptEnd[0]);
EXPECT_DOUBLE_EQ(ptStart[1], ptEnd[1]);
}
TEST_F(SketchObjectTest, testSplitLineSegment)
{
// Arrange
Base::Vector3d splitPoint(2.0, 3.1, 0.0);
Part::GeomLineSegment lineSeg;
setupLineSegment(lineSeg);
int geoId = getObject()->addGeometry(&lineSeg);
// Act
int result = getObject()->split(geoId, splitPoint);
// Assert
EXPECT_EQ(result, 0);
// One additional curve should be added
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId + 1);
// TODO: Expect the resultant curves are line segments and shape is conserved
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
}
TEST_F(SketchObjectTest, testSplitCircle)
{
// Arrange
Base::Vector3d splitPoint(2.0, 3.1, 0.0);
Part::GeomCircle circle;
setupCircle(circle);
int geoId = getObject()->addGeometry(&circle);
// Act
int result = getObject()->split(geoId, splitPoint);
// Assert
EXPECT_EQ(result, 0);
// The circle should be split into an arc now
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId);
}
TEST_F(SketchObjectTest, testSplitEllipse)
{
// Arrange
Base::Vector3d splitPoint(2.0, 3.1, 0.0);
Part::GeomEllipse ellipse;
setupEllipse(ellipse);
int geoId = getObject()->addGeometry(&ellipse);
// Act
int result = getObject()->split(geoId, splitPoint);
// Assert
EXPECT_EQ(result, 0);
// TODO: The ellipse should be split into an arc of ellipse now
// FIXME: Internal geometries may be added or removed which may cause some issues
// EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId);
}
TEST_F(SketchObjectTest, testSplitArcOfCircle)
{
// Arrange
Base::Vector3d splitPoint(-2.0, 3.1, 0.0);
Part::GeomArcOfCircle arcOfCircle;
setupArcOfCircle(arcOfCircle);
int geoId = getObject()->addGeometry(&arcOfCircle);
// Act
int result = getObject()->split(geoId, splitPoint);
// Assert
EXPECT_EQ(result, 0);
// The arcOfCircle should be split into an arc now
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId + 1);
// Expect the end points and centers of the resultant curve are coincident.
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 2);
}
TEST_F(SketchObjectTest, testSplitArcOfConic)
{
// Arrange
// TODO: Define a parabola/hyperbola as reference
Base::Vector3d splitPoint(1.0, -1.1, 0.0);
Part::GeomArcOfParabola arcOfConic;
setupArcOfParabola(arcOfConic);
int geoId = getObject()->addGeometry(&arcOfConic);
// Act
// TODO: Sample random points from both sides of the split
int result = getObject()->split(geoId, splitPoint);
for (int iterGeoId = 0; iterGeoId < getObject()->getHighestCurveIndex(); ++iterGeoId) {
getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(iterGeoId);
}
// Assert
EXPECT_EQ(result, 0);
// The arcOfConic should be split into two arcs of the same conic now
EXPECT_EQ(getObject()->getHighestCurveIndex(), 1);
// TODO: Expect the end points of the resultant curve are coincident.
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
}
TEST_F(SketchObjectTest, testSplitNonPeriodicBSpline)
{
// Arrange
auto nonPeriodicBSpline = createTypicalNonPeriodicBSpline();
Base::Vector3d splitPoint(-0.5, 1.1, 0.0);
int geoId = getObject()->addGeometry(nonPeriodicBSpline.get());
// TODO: Put a point on this
// Act
// TODO: sample before point(s) at a random parameter
int result = getObject()->split(geoId, splitPoint);
for (int iterGeoId = 0; iterGeoId < getObject()->getHighestCurveIndex(); ++iterGeoId) {
getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(iterGeoId);
}
// Assert
EXPECT_EQ(result, 0);
EXPECT_EQ(getObject()->getHighestCurveIndex(), 1);
// TODO: confirm sampled point(s) is/are at the same place
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
}
TEST_F(SketchObjectTest, testSplitPeriodicBSpline)
{
// Arrange
auto PeriodicBSpline = createTypicalPeriodicBSpline();
Base::Vector3d splitPoint(-0.5, 1.1, 0.0);
int geoId = getObject()->addGeometry(PeriodicBSpline.get());
// TODO: Put a point on this
// Act
// TODO: sample before point(s) at a random parameter
int result = getObject()->split(geoId, splitPoint);
for (int iterGeoId = 0; iterGeoId < getObject()->getHighestCurveIndex(); ++iterGeoId) {
getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(iterGeoId);
}
// Assert
EXPECT_EQ(result, 0);
EXPECT_EQ(getObject()->getHighestCurveIndex(), 0);
// TODO: confirm sampled point(s) is/are at the same place
}
TEST_F(SketchObjectTest, testTrimWithoutIntersection)
{
// Arrange
Part::GeomLineSegment lineSeg;
setupLineSegment(lineSeg);
int geoId = getObject()->addGeometry(&lineSeg);
Base::Vector3d trimPoint(2.0, 3.1, 0.0);
// Act
int result = getObject()->trim(geoId, trimPoint);
// Assert
EXPECT_EQ(result, 0);
// Once this line segment is trimmed, nothing should remain
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId - 1);
}
// TODO: There are other combinations of constraints we may want to test with trim.
TEST_F(SketchObjectTest, testTrimLineSegmentEnd)
{
// Arrange
Part::GeomLineSegment lineSeg;
setupLineSegment(lineSeg);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(lineSeg, 0.2));
Base::Vector3d p1(getPointAtNormalizedParameter(lineSeg, 0.5));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
int geoId = getObject()->addGeometry(&lineSeg);
// Act
int result = getObject()->trim(geoId, trimPoint);
// Assert
EXPECT_EQ(result, 0);
// TODO: Once this line segment is trimmed, the curve should be "smaller"
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId);
// TODO: There should be a "point-on-object" constraint on the intersecting curves
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
}
TEST_F(SketchObjectTest, testTrimLineSegmentMid)
{
// Arrange
Part::GeomLineSegment lineSeg;
setupLineSegment(lineSeg);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(lineSeg, 0.5));
Base::Vector3d p1(getPointAtNormalizedParameter(lineSeg, 0.3));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
Base::Vector3d p3(getPointAtNormalizedParameter(lineSeg, 0.7));
Base::Vector3d p4(p3.x + 0.1, p3.y - 0.1, p3.z);
// to ensure that this line clearly intersects the curve, not just have a point on object
// without explicit constraint
p3.x -= 0.1;
p3.y += 0.1;
Part::GeomLineSegment lineSegCut2;
lineSegCut2.setPoints(p3, p4);
getObject()->addGeometry(&lineSegCut2);
int geoId = getObject()->addGeometry(&lineSeg);
// Act
int result = getObject()->trim(geoId, trimPoint);
// Assert
EXPECT_EQ(result, 0);
// TODO: Once this line segment is trimmed, there should be two "smaller" curves in its place
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId + 1);
// TODO: There should be a "point-on-object" constraint on the intersecting curves
int numberOfPointOnObjectConstraints =
countConstraintsOfType(getObject(), Sketcher::PointOnObject);
EXPECT_EQ(numberOfPointOnObjectConstraints, 1);
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
// TODO: Ensure shape is preserved
}
TEST_F(SketchObjectTest, testTrimCircleEnd)
{
// Arrange
Part::GeomCircle circle;
setupCircle(circle);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(circle, 0.2));
Base::Vector3d p1(getPointAtNormalizedParameter(circle, 0.5));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
int geoId = getObject()->addGeometry(&circle);
// Act
int result = getObject()->trim(geoId, trimPoint);
// Assert
EXPECT_EQ(result, 0);
// TODO: Once this circle is trimmed, the circle should be deleted.
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId - 1);
}
TEST_F(SketchObjectTest, testTrimCircleMid)
{
// Arrange
Part::GeomCircle circle;
setupCircle(circle);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(circle, 0.5));
Base::Vector3d p1(getPointAtNormalizedParameter(circle, 0.3));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
Base::Vector3d p3(getPointAtNormalizedParameter(circle, 0.7));
Base::Vector3d p4(p3.x + 0.1, p3.y + 0.1, p3.z);
// to ensure that this line clearly intersects the curve, not just have a point on object
// without explicit constraint
p3.x -= 0.1;
p3.y -= 0.1;
Part::GeomLineSegment lineSegCut2;
lineSegCut2.setPoints(p3, p4);
getObject()->addGeometry(&lineSegCut2);
int geoId = getObject()->addGeometry(&circle);
// Act
int result = getObject()->trim(geoId, trimPoint);
// Assert
EXPECT_EQ(result, 0);
// TODO: Once this circle is trimmed, there should be one arc.
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId);
// There should be one "coincident" and one "point-on-object" constraint on the intersecting
// curves
int numberOfPointOnObjectConstraints =
countConstraintsOfType(getObject(), Sketcher::PointOnObject);
EXPECT_EQ(numberOfPointOnObjectConstraints, 1);
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
// TODO: Ensure shape is preserved
}
TEST_F(SketchObjectTest, testTrimArcOfCircleEnd)
{
// This should also cover as a representative of arc of conic
// Arrange
Part::GeomArcOfCircle arcOfCircle;
setupArcOfCircle(arcOfCircle);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(arcOfCircle, 0.2));
Base::Vector3d p1(getPointAtNormalizedParameter(arcOfCircle, 0.5));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
int geoId = getObject()->addGeometry(&arcOfCircle);
// Act
int result = getObject()->trim(geoId, trimPoint);
// Assert
EXPECT_EQ(result, 0);
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId);
// There should be a "point-on-object" constraint on the intersecting curves
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
}
TEST_F(SketchObjectTest, testTrimArcOfCircleMid)
{
// Arrange
Part::GeomArcOfCircle arcOfCircle;
setupArcOfCircle(arcOfCircle);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(arcOfCircle, 0.5));
Base::Vector3d p1(getPointAtNormalizedParameter(arcOfCircle, 0.3));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
Base::Vector3d p3(getPointAtNormalizedParameter(arcOfCircle, 0.7));
Base::Vector3d p4(p3.x + 0.1, p3.y + 0.1, p3.z);
// to ensure that this line clearly intersects the curve, not just have a point on object
// without explicit constraint
p3.x -= 0.1;
p3.y -= 0.1;
Part::GeomLineSegment lineSegCut2;
lineSegCut2.setPoints(p3, p4);
getObject()->addGeometry(&lineSegCut2);
int geoId = getObject()->addGeometry(&arcOfCircle);
// Act
int result = getObject()->trim(geoId, trimPoint);
// Assert
EXPECT_EQ(result, 0);
EXPECT_EQ(getObject()->getHighestCurveIndex(), geoId + 1);
// There should be a "point-on-object" constraint on the intersecting curves
int numberOfPointOnObjectConstraints =
countConstraintsOfType(getObject(), Sketcher::PointOnObject);
EXPECT_EQ(numberOfPointOnObjectConstraints, 1);
// There should be 2 coincident constraints: one with lineSegCut1 and one between centers of the
// new arcs
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 2);
// TODO: Ensure shape is preserved
}
TEST_F(SketchObjectTest, testTrimEllipseEnd)
{
// Arrange
Part::GeomEllipse ellipse;
setupEllipse(ellipse);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(ellipse, 0.2));
Base::Vector3d p1(getPointAtNormalizedParameter(ellipse, 0.5));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
int geoId = getObject()->addGeometry(&ellipse);
// Act
int result = getObject()->trim(geoId, trimPoint);
// remove all internal geometry
for (int iterGeoId = 0; iterGeoId < getObject()->getHighestCurveIndex(); ++iterGeoId) {
getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(iterGeoId);
}
// Assert
EXPECT_EQ(result, 0);
// Once this ellipse is trimmed, the ellipse should be deleted.
// Only remaining: line segment
EXPECT_EQ(getObject()->getHighestCurveIndex(), 0);
}
TEST_F(SketchObjectTest, testTrimEllipseMid)
{
// Arrange
Part::GeomEllipse ellipse;
setupEllipse(ellipse);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(ellipse, 0.5));
Base::Vector3d p1(getPointAtNormalizedParameter(ellipse, 0.3));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
Base::Vector3d p3(getPointAtNormalizedParameter(ellipse, 0.7));
Base::Vector3d p4(p3.x + 0.1, p3.y + 0.1, p3.z);
// to ensure that this line clearly intersects the curve, not just have a point on object
// without explicit constraint
p3.x -= 0.1;
p3.y -= 0.1;
Part::GeomLineSegment lineSegCut2;
lineSegCut2.setPoints(p3, p4);
getObject()->addGeometry(&lineSegCut2);
int geoId = getObject()->addGeometry(&ellipse);
// FIXME: Doing this to avoid trimming only until minor/major axes. Should not be needed.
getObject()->deleteUnusedInternalGeometry(geoId);
// Act
int result = getObject()->trim(geoId, trimPoint);
// remove all internal geometry
for (int iterGeoId = 0; iterGeoId < getObject()->getHighestCurveIndex(); ++iterGeoId) {
getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(iterGeoId);
}
// Assert
EXPECT_EQ(result, 0);
// Once this ellipse is trimmed, there should be one arc and line segments.
EXPECT_EQ(getObject()->getHighestCurveIndex(), 2);
// There should be one "coincident" and one "point-on-object" constraint on the intersecting
// curves
int numberOfPointOnObjectConstraints =
countConstraintsOfType(getObject(), Sketcher::PointOnObject);
EXPECT_EQ(numberOfPointOnObjectConstraints, 1);
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
// TODO: Ensure shape is preserved
}
// TODO: Tests for other arcs of conics?
TEST_F(SketchObjectTest, testTrimPeriodicBSplineEnd)
{
// Arrange
auto periodicBSpline = createTypicalPeriodicBSpline();
assert(periodicBSpline);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(*periodicBSpline, 0.2));
Base::Vector3d p1(getPointAtNormalizedParameter(*periodicBSpline, 0.5));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
int geoId = getObject()->addGeometry(periodicBSpline.get());
// Act
int result = getObject()->trim(geoId, trimPoint);
// Assert
EXPECT_EQ(result, 0);
// FIXME: This will fail because of deleted internal geometry
// Once this periodicBSpline is trimmed, the periodicBSpline should be deleted, leaving only the
// line segment.
EXPECT_EQ(getObject()->getHighestCurveIndex(), 0);
// TODO: There should be a "point-on-object" constraint on the intersecting curves
}
TEST_F(SketchObjectTest, testTrimPeriodicBSplineMid)
{
// Arrange
auto periodicBSpline = createTypicalPeriodicBSpline();
assert(periodicBSpline);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(*periodicBSpline, 0.5));
Base::Vector3d p1(getPointAtNormalizedParameter(*periodicBSpline, 0.3));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
Base::Vector3d p3(getPointAtNormalizedParameter(*periodicBSpline, 0.7));
Base::Vector3d p4(p3.x + 0.1, p3.y + 0.1, p3.z);
// to ensure that this line clearly intersects the curve, not just have a point on object
// without explicit constraint
p3.x -= 0.1;
p3.y -= 0.1;
Part::GeomLineSegment lineSegCut2;
lineSegCut2.setPoints(p3, p4);
getObject()->addGeometry(&lineSegCut2);
int geoId = getObject()->addGeometry(periodicBSpline.get());
// Act
int result = getObject()->trim(geoId, trimPoint);
// remove all internal geometry
for (int iterGeoId = 0; iterGeoId < getObject()->getHighestCurveIndex(); ++iterGeoId) {
getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(iterGeoId);
}
// Assert
EXPECT_EQ(result, 0);
// Only remaining: Two line segments and the B-spline
EXPECT_EQ(getObject()->getHighestCurveIndex(), 2);
// There should be one "coincident" and one "point-on-object" constraint on the intersecting
// curves
int numberOfPointOnObjectConstraints =
countConstraintsOfType(getObject(), Sketcher::PointOnObject);
EXPECT_EQ(numberOfPointOnObjectConstraints, 1);
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
// TODO: Ensure shape is preserved
}
TEST_F(SketchObjectTest, testTrimNonPeriodicBSplineEnd)
{
// This should also cover as a representative of arc of conic
// Arrange
auto nonPeriodicBSpline = createTypicalNonPeriodicBSpline();
assert(nonPeriodicBSpline);
// create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(*nonPeriodicBSpline, 0.2));
Base::Vector3d p1(getPointAtNormalizedParameter(*nonPeriodicBSpline, 0.5));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
int geoId = getObject()->addGeometry(nonPeriodicBSpline.get());
// Act
int result = getObject()->trim(geoId, trimPoint);
// remove all internal geometry
for (int iterGeoId = 0; iterGeoId < getObject()->getHighestCurveIndex(); ++iterGeoId) {
getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(iterGeoId);
}
// Assert
EXPECT_EQ(result, 0);
// Only remaining: one line segment and the trimmed B-spline
EXPECT_EQ(getObject()->getHighestCurveIndex(), 1);
// FIXME: There should be a "point-on-object" constraint on the intersecting curves
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
}
TEST_F(SketchObjectTest, testTrimNonPeriodicBSplineMid)
{
// Arrange
auto nonPeriodicBSpline = createTypicalNonPeriodicBSpline();
assert(nonPeriodicBSpline);
// TODO: create curves intersecting at the right spots
Base::Vector3d trimPoint(getPointAtNormalizedParameter(*nonPeriodicBSpline, 0.5));
Base::Vector3d p1(getPointAtNormalizedParameter(*nonPeriodicBSpline, 0.3));
Base::Vector3d p2(p1.x + 0.1, p1.y + 0.1, p1.z);
Part::GeomLineSegment lineSegCut1;
lineSegCut1.setPoints(p1, p2);
getObject()->addGeometry(&lineSegCut1);
Base::Vector3d p3(getPointAtNormalizedParameter(*nonPeriodicBSpline, 0.7));
Base::Vector3d p4(p3.x + 0.1, p3.y + 0.1, p3.z);
// to ensure that this line clearly intersects the curve, not just have a point on object
// without explicit constraint
p3.x -= 0.1;
p3.y -= 0.1;
Part::GeomLineSegment lineSegCut2;
lineSegCut2.setPoints(p3, p4);
getObject()->addGeometry(&lineSegCut2);
int geoId = getObject()->addGeometry(nonPeriodicBSpline.get());
// Act
int result = getObject()->trim(geoId, trimPoint);
// remove all internal geometry
for (int i = 0; i < getObject()->getHighestCurveIndex(); ++i) {
if (getObject()->getGeometry(i)->is<Part::GeomBSplineCurve>()) {
getObject()->deleteUnusedInternalGeometry(i);
}
}
// Assert
EXPECT_EQ(result, 0);
// Only remaining: one line segment and the trimmed B-spline
EXPECT_EQ(getObject()->getHighestCurveIndex(), 3);
// There should be a "point-on-object" constraint on the intersecting curves
int numberOfPointOnObjectConstraints =
countConstraintsOfType(getObject(), Sketcher::PointOnObject);
EXPECT_EQ(numberOfPointOnObjectConstraints, 1);
int numberOfCoincidentConstraints = countConstraintsOfType(getObject(), Sketcher::Coincident);
EXPECT_EQ(numberOfCoincidentConstraints, 1);
// TODO: Ensure shape is preserved
}
TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionNoUnits1)
{
std::string expr = Sketcher::SketchObject::reverseAngleConstraintExpression("180 - 60");