Toponaming: Final piece nine of fixing Save/Restore sketch external geometry
This commit is contained in:
@@ -833,70 +833,85 @@ void AttachEngine::readLinks(const std::vector<App::DocumentObject*> &objs,
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
for (std::size_t i = 0; i < objs.size(); i++) {
|
||||
if (!objs[i]->getTypeId().isDerivedFrom(App::GeoFeature::getClassTypeId())) {
|
||||
FC_THROWM(AttachEngineException, "AttachEngine3D: attached to a non App::GeoFeature '"
|
||||
<< objs[i]->getNameInDocument() << "'");
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: attached to a non App::GeoFeature '"
|
||||
<< objs[i]->getNameInDocument() << "'");
|
||||
}
|
||||
App::GeoFeature *geof = static_cast<App::GeoFeature *>(objs[i]);
|
||||
auto* geof = dynamic_cast<App::GeoFeature*>(objs[i]);
|
||||
geofs[i] = geof;
|
||||
Part::TopoShape shape;
|
||||
if (geof->isDerivedFrom(App::Plane::getClassTypeId())) {
|
||||
//obtain Z axis and origin of placement
|
||||
// obtain Z axis and origin of placement
|
||||
Base::Vector3d norm;
|
||||
geof->Placement.getValue().getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), norm);
|
||||
Base::Vector3d org;
|
||||
geof->Placement.getValue().multVec(Base::Vector3d(), org);
|
||||
//make shape - an local-XY plane infinite face
|
||||
gp_Pln pl = gp_Pln(gp_Pnt(org.x, org.y, org.z), gp_Dir(norm.x, norm.y, norm.z));
|
||||
TopoDS_Shape myShape = BRepBuilderAPI_MakeFace(pl).Shape();
|
||||
// make shape - an local-XY plane infinite face
|
||||
gp_Pln plane = gp_Pln(gp_Pnt(org.x, org.y, org.z), gp_Dir(norm.x, norm.y, norm.z));
|
||||
TopoDS_Shape myShape = BRepBuilderAPI_MakeFace(plane).Shape();
|
||||
myShape.Infinite(true);
|
||||
storage.push_back(myShape);
|
||||
storage.emplace_back(myShape);
|
||||
shapes[i] = &(storage[storage.size() - 1]);
|
||||
} else if (geof->isDerivedFrom(App::Line::getClassTypeId())) {
|
||||
//obtain X axis and origin of placement
|
||||
//note an inconsistency: App::Line is along local X, PartDesign::DatumLine is along local Z.
|
||||
}
|
||||
else if (geof->isDerivedFrom(App::Line::getClassTypeId())) {
|
||||
// obtain X axis and origin of placement
|
||||
// note an inconsistency: App::Line is along local X, PartDesign::DatumLine is along
|
||||
// local Z.
|
||||
Base::Vector3d dir;
|
||||
geof->Placement.getValue().getRotation().multVec(Base::Vector3d(1.0, 0.0, 0.0), dir);
|
||||
Base::Vector3d org;
|
||||
geof->Placement.getValue().multVec(Base::Vector3d(), org);
|
||||
//make shape - an infinite line along local X axis
|
||||
gp_Lin l = gp_Lin(gp_Pnt(org.x, org.y, org.z), gp_Dir(dir.x, dir.y, dir.z));
|
||||
TopoDS_Shape myShape = BRepBuilderAPI_MakeEdge(l).Shape();
|
||||
// make shape - an infinite line along local X axis
|
||||
gp_Lin line = gp_Lin(gp_Pnt(org.x, org.y, org.z), gp_Dir(dir.x, dir.y, dir.z));
|
||||
TopoDS_Shape myShape = BRepBuilderAPI_MakeEdge(line).Shape();
|
||||
myShape.Infinite(true);
|
||||
storage.push_back(myShape);
|
||||
storage.emplace_back(myShape);
|
||||
shapes[i] = &(storage[storage.size() - 1]);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
shape = Part::Feature::getTopoShape(geof, sub[i].c_str(), true);
|
||||
for (;;) {
|
||||
if (shape.isNull())
|
||||
FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found "
|
||||
<< objs[i]->getNameInDocument() << '.' << sub[i]);
|
||||
if (shape.isNull()) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: subshape not found "
|
||||
<< objs[i]->getNameInDocument() << '.' << sub[i]);
|
||||
}
|
||||
if (shape.shapeType() != TopAbs_COMPOUND
|
||||
|| shape.countSubShapes(TopAbs_SHAPE) != 1)
|
||||
|| shape.countSubShapes(TopAbs_SHAPE) != 1) {
|
||||
break;
|
||||
}
|
||||
// auto extract the single sub-shape from a compound
|
||||
shape = shape.getSubTopoShape(TopAbs_SHAPE, 1);
|
||||
}
|
||||
storage.push_back(shape.getShape());
|
||||
} catch (Standard_Failure &e) {
|
||||
FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found "
|
||||
<< objs[i]->getNameInDocument() << '.' << sub[i]
|
||||
<< std::endl << e.GetMessageString());
|
||||
} catch (Base::CADKernelError &e) {
|
||||
FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found "
|
||||
<< objs[i]->getNameInDocument() << '.' << sub[i]
|
||||
<< std::endl << e.what());
|
||||
storage.emplace_back(shape.getShape());
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
|
||||
<< '.' << sub[i] << std::endl
|
||||
<< e.GetMessageString());
|
||||
}
|
||||
catch (Base::CADKernelError& e) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
|
||||
<< '.' << sub[i] << std::endl
|
||||
<< e.what());
|
||||
}
|
||||
if (storage.back().IsNull()) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.'
|
||||
<< sub[i]);
|
||||
}
|
||||
if (storage.back().IsNull())
|
||||
FC_THROWM(AttachEngineException, "AttachEngine3D: null subshape "
|
||||
<< objs[i]->getNameInDocument() << '.' << sub[i]);
|
||||
shapes[i] = &(storage.back());
|
||||
}
|
||||
|
||||
//FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be considered later, when the need arises.
|
||||
// FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be
|
||||
// considered later, when the need arises.
|
||||
types[i] = getShapeType(*(shapes[i]));
|
||||
if (sub[i].length() == 0)
|
||||
if (sub[i].length() == 0) {
|
||||
types[i] = eRefType(types[i] | rtFlagHasPlacement);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -37,7 +37,7 @@ constexpr std::array<const char*, InternalType::NumInternalGeometryType>
|
||||
constexpr std::array<const char*, GeometryMode::NumGeometryMode>
|
||||
SketchGeometryExtension::geometrymode2str;
|
||||
|
||||
TYPESYSTEM_SOURCE(Sketcher::SketchGeometryExtension, Part::GeometryPersistenceExtension)
|
||||
TYPESYSTEM_SOURCE(Sketcher::SketchGeometryExtension, Part::GeometryMigrationPersistenceExtension)
|
||||
|
||||
// scoped within the class, multithread ready
|
||||
std::atomic<long> SketchGeometryExtension::_GeometryID;
|
||||
@@ -86,11 +86,9 @@ void SketchGeometryExtension::saveAttributes(Base::Writer& writer) const
|
||||
{
|
||||
Part::GeometryPersistenceExtension::saveAttributes(writer);
|
||||
|
||||
// This is removed as the stored Id is not used and it may interfere with RT's future
|
||||
// implementation
|
||||
writer.Stream() // << "\" id=\"" << Id
|
||||
<< "\" internalGeometryType=\"" << (int)InternalGeometryType << "\" geometryModeFlags=\""
|
||||
<< GeometryModeFlags.to_string() << "\" geometryLayer=\"" << GeometryLayer;
|
||||
writer.Stream() << "\" id=\"" << Id << "\" internalGeometryType=\"" << (int)InternalGeometryType
|
||||
<< "\" geometryModeFlags=\"" << GeometryModeFlags.to_string()
|
||||
<< "\" geometryLayer=\"" << GeometryLayer;
|
||||
}
|
||||
|
||||
void SketchGeometryExtension::preSave(Base::Writer& writer) const
|
||||
|
||||
@@ -31,19 +31,15 @@
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
#include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
#include <BRepOffsetAPI_NormalProjection.hxx>
|
||||
#include <BRepTools_WireExplorer.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <ElCLib.hxx>
|
||||
#include <GCPnts_AbscissaPoint.hxx>
|
||||
#include <GCPnts_AbscissaPoint.hxx>
|
||||
#include <GC_MakeArcOfCircle.hxx>
|
||||
#include <GC_MakeCircle.hxx>
|
||||
#include <GeomAPI_ProjectPointOnCurve.hxx>
|
||||
#include <GeomAPI_ProjectPointOnCurve.hxx>
|
||||
#include <GeomAPI_ProjectPointOnCurve.hxx>
|
||||
#include <GeomAPI_ProjectPointOnSurf.hxx>
|
||||
#include <GeomConvert_BSplineCurveKnotSplitting.hxx>
|
||||
#include <GeomLProp_CLProps.hxx>
|
||||
@@ -138,7 +134,7 @@ SketchObject::SketchObject()
|
||||
ADD_PROPERTY_TYPE(ExternalGeometry,
|
||||
(nullptr, nullptr),
|
||||
"Sketch",
|
||||
(App::PropertyType)(App::Prop_None),
|
||||
(App::PropertyType)(App::Prop_None | App::Prop_ReadOnly),
|
||||
"Sketch external geometry");
|
||||
ADD_PROPERTY_TYPE(FullyConstrained,
|
||||
(false),
|
||||
@@ -615,12 +611,13 @@ namespace bg = boost::geometry;
|
||||
namespace bgi = boost::geometry::index;
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
BOOST_GEOMETRY_REGISTER_POINT_3D(
|
||||
Base::Vector3d,double,bg::cs::cartesian,x,y,z)
|
||||
BOOST_GEOMETRY_REGISTER_POINT_3D(Base::Vector3d, double, bg::cs::cartesian, x, y, z)
|
||||
|
||||
class SketchObject::GeoHistory {
|
||||
class SketchObject::GeoHistory
|
||||
{
|
||||
private:
|
||||
static constexpr int bgiMaxElements = 16;
|
||||
|
||||
public:
|
||||
using Parameters = bgi::linear<bgiMaxElements>;
|
||||
|
||||
@@ -628,7 +625,7 @@ public:
|
||||
using IdSets = std::pair<IdSet, IdSet>;
|
||||
using AdjList = std::list<IdSet>;
|
||||
|
||||
//associate a geo with connected ones on both points
|
||||
// associate a geo with connected ones on both points
|
||||
using AdjMap = std::map<long, IdSets>;
|
||||
|
||||
// maps start/end points to all existing geo to query and update adjacencies
|
||||
@@ -640,8 +637,8 @@ public:
|
||||
|
||||
AdjList::iterator find(const Base::Vector3d &pt,bool strict=true){
|
||||
std::vector<Value> ret;
|
||||
rtree.query(bgi::nearest(pt,1),std::back_inserter(ret));
|
||||
if(!ret.empty()) {
|
||||
rtree.query(bgi::nearest(pt, 1), std::back_inserter(ret));
|
||||
if (!ret.empty()) {
|
||||
// NOTE: we are using square distance here, the 1e-6 threshold is
|
||||
// very forgiving. We should have used Precision::SquareConfisuion(),
|
||||
// which is 1e-14. However, there is a problem with current
|
||||
@@ -722,15 +719,16 @@ public:
|
||||
void SketchObject::updateGeoHistory() {
|
||||
if(!geoHistoryLevel) return;
|
||||
|
||||
if(!geoHistory)
|
||||
if (!geoHistory) {
|
||||
geoHistory = std::make_unique<GeoHistory>();
|
||||
}
|
||||
|
||||
FC_TIME_INIT(t);
|
||||
const auto &geos = getInternalGeometry();
|
||||
geoHistory->clear();
|
||||
for(auto geo : geos) {
|
||||
auto pstart = getPoint(geo,PointPos::start);
|
||||
auto pend = getPoint(geo,PointPos::end);
|
||||
for (auto geo : geos) {
|
||||
auto pstart = getPoint(geo, PointPos::start);
|
||||
auto pend = getPoint(geo, PointPos::end);
|
||||
int id = GeometryFacade::getId(geo);
|
||||
geoHistory->update(pstart,id);
|
||||
if(pstart!=pend)
|
||||
@@ -1872,6 +1870,8 @@ int SketchObject::toggleConstruction(int GeoId)
|
||||
this->Geometry.set1Value(GeoId, std::move(geo));
|
||||
|
||||
solverNeedsUpdate = true;
|
||||
signalSolverUpdate(); // FIXME: In theory this is totally redundant, but now seems required
|
||||
// for UI to update.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7094,9 +7094,11 @@ bool SketchObject::modifyBSplineKnotMultiplicity(int GeoId, int knotIndex, int m
|
||||
// no need to check input data validity as this is an sketchobject managed operation.
|
||||
Base::StateLocker lock(managedoperation, true);
|
||||
|
||||
if (GeoId < 0 || GeoId > getHighestCurveIndex())
|
||||
THROWMT(Base::ValueError,
|
||||
QT_TRANSLATE_NOOP("Exceptions", "B-spline Geometry Index (GeoID) is out of bounds."))
|
||||
if (GeoId < 0 || GeoId > getHighestCurveIndex()) {
|
||||
THROWMT(
|
||||
Base::ValueError,
|
||||
QT_TRANSLATE_NOOP("Exceptions", "B-spline Geometry Index (GeoID) is out of bounds."))
|
||||
}
|
||||
|
||||
if (multiplicityincr == 0)// no change in multiplicity
|
||||
THROWMT(
|
||||
@@ -8265,7 +8267,7 @@ Part::Geometry* projectLine(const BRepAdaptor_Curve& curve, const Handle(Geom_Pl
|
||||
invPlm.multVec(p1, p1);
|
||||
invPlm.multVec(p2, p2);
|
||||
|
||||
if (Base::DistanceP2(p1, p2) < Precision::SquareConfusion()) {
|
||||
if (Base::Distance(p1, p2) < Precision::Confusion()) {
|
||||
Base::Vector3d p = (p1 + p2) / 2;
|
||||
Part::GeomPoint* point = new Part::GeomPoint(p);
|
||||
GeometryFacade::setConstruction(point, true);
|
||||
@@ -8279,7 +8281,7 @@ Part::Geometry* projectLine(const BRepAdaptor_Curve& curve, const Handle(Geom_Pl
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
bool SketchObject::evaluateSupport()
|
||||
{
|
||||
@@ -10092,21 +10094,26 @@ void SketchObject::onChanged(const App::Property* prop)
|
||||
if (prop == &Geometry) {
|
||||
if (isRestoring() && checkMigration(Geometry)) {
|
||||
// Construction migration to extension
|
||||
for( auto geometryValue : Geometry.getValues()) {
|
||||
if(geometryValue->hasExtension(Part::GeometryMigrationExtension::getClassTypeId())) {
|
||||
for (auto geometryValue : Geometry.getValues()) {
|
||||
if (geometryValue->hasExtension(
|
||||
Part::GeometryMigrationExtension::getClassTypeId())) {
|
||||
auto ext = std::static_pointer_cast<Part::GeometryMigrationExtension>(
|
||||
geometryValue->getExtension(Part::GeometryMigrationExtension::getClassTypeId()).lock());
|
||||
geometryValue
|
||||
->getExtension(Part::GeometryMigrationExtension::getClassTypeId())
|
||||
.lock());
|
||||
|
||||
auto gf = GeometryFacade::getFacade(geometryValue); // at this point IA geometry is already migrated
|
||||
auto gf = GeometryFacade::getFacade(
|
||||
geometryValue); // at this point IA geometry is already migrated
|
||||
|
||||
if(ext->testMigrationType(Part::GeometryMigrationExtension::Construction)) {
|
||||
bool oldconstr = ext->getConstruction();
|
||||
if( geometryValue->getTypeId() == Part::GeomPoint::getClassTypeId() && !gf->isInternalAligned()){
|
||||
if (ext->testMigrationType(Part::GeometryMigrationExtension::Construction)) {
|
||||
bool oldconstr = ext->getConstruction();
|
||||
if (geometryValue->getTypeId() == Part::GeomPoint::getClassTypeId()
|
||||
&& !gf->isInternalAligned()) {
|
||||
oldconstr = true;
|
||||
}
|
||||
gf->setConstruction(oldconstr);
|
||||
}
|
||||
if(ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) {
|
||||
if (ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) {
|
||||
gf->setId(ext->getId());
|
||||
}
|
||||
}
|
||||
@@ -10117,13 +10124,15 @@ void SketchObject::onChanged(const App::Property* prop)
|
||||
for(long i=0;i<(long)vals.size();++i) {
|
||||
auto geo = vals[i];
|
||||
auto gf = GeometryFacade::getFacade(geo);
|
||||
if(gf->getId() == 0) {
|
||||
if (gf->getId() == 0) {
|
||||
gf->setId(++geoLastId);
|
||||
} else if(gf->getId() > geoLastId) {
|
||||
}
|
||||
else if (gf->getId() > geoLastId) {
|
||||
geoLastId = gf->getId();
|
||||
}
|
||||
while(!geoMap.insert(std::make_pair(gf->getId(),i)).second) {
|
||||
FC_WARN("duplicate geometry id " << gf->getId() << " -> " << geoLastId+1); // NOLINT
|
||||
while (!geoMap.insert(std::make_pair(gf->getId(), i)).second) {
|
||||
FC_WARN("duplicate geometry id " << gf->getId() << " -> "
|
||||
<< geoLastId + 1); // NOLINT
|
||||
gf->setId(++geoLastId);
|
||||
}
|
||||
}
|
||||
@@ -10198,23 +10207,28 @@ void SketchObject::onChanged(const App::Property* prop)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ( prop == &ExternalGeo && !prop->testStatus(App::Property::User3) ) {
|
||||
if(doc && doc->isPerformingTransaction()) {
|
||||
}
|
||||
else if (prop == &ExternalGeo && !prop->testStatus(App::Property::User3)) {
|
||||
if (doc && doc->isPerformingTransaction()) {
|
||||
setStatus(App::PendingTransactionUpdate, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isRestoring() && checkMigration(ExternalGeo)) {
|
||||
for( auto geometryValue : ExternalGeo.getValues()) {
|
||||
if(geometryValue->hasExtension(Part::GeometryMigrationExtension::getClassTypeId())) {
|
||||
for (auto geometryValue : ExternalGeo.getValues()) {
|
||||
if (geometryValue->hasExtension(
|
||||
Part::GeometryMigrationExtension::getClassTypeId())) {
|
||||
auto ext = std::static_pointer_cast<Part::GeometryMigrationExtension>(
|
||||
geometryValue->getExtension(Part::GeometryMigrationExtension::getClassTypeId()).lock());
|
||||
geometryValue
|
||||
->getExtension(Part::GeometryMigrationExtension::getClassTypeId())
|
||||
.lock());
|
||||
std::unique_ptr<ExternalGeometryFacade> egf;
|
||||
if(ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) {
|
||||
if (ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) {
|
||||
egf = ExternalGeometryFacade::getFacade(geometryValue);
|
||||
egf->setId(ext->getId());
|
||||
}
|
||||
|
||||
if(ext->testMigrationType(Part::GeometryMigrationExtension::ExternalReference)) {
|
||||
if (ext->testMigrationType(
|
||||
Part::GeometryMigrationExtension::ExternalReference)) {
|
||||
if (!egf) {
|
||||
egf = ExternalGeometryFacade::getFacade(geometryValue);
|
||||
}
|
||||
@@ -10231,38 +10245,39 @@ void SketchObject::onChanged(const App::Property* prop)
|
||||
for(int i=0;i<ExternalGeo.getSize();++i) {
|
||||
auto geo = ExternalGeo[i];
|
||||
auto egf = ExternalGeometryFacade::getFacade(geo);
|
||||
if(egf->testFlag(ExternalGeometryExtension::Detached)) {
|
||||
if(!egf->getRef().empty()) {
|
||||
if (egf->testFlag(ExternalGeometryExtension::Detached)) {
|
||||
if (!egf->getRef().empty()) {
|
||||
detached.insert(egf->getRef());
|
||||
egf->setRef(std::string());
|
||||
}
|
||||
egf->setFlag(ExternalGeometryExtension::Detached,false);
|
||||
egf->setFlag(ExternalGeometryExtension::Missing,false);
|
||||
}
|
||||
if(egf->getId() > geoLastId) {
|
||||
if (egf->getId() > geoLastId) {
|
||||
geoLastId = egf->getId();
|
||||
}
|
||||
if(!externalGeoMap.emplace(egf->getId(),i).second) {
|
||||
FC_WARN("duplicate geometry id " << egf->getId() << " -> " << geoLastId+1); // NOLINT
|
||||
if (!externalGeoMap.emplace(egf->getId(), i).second) {
|
||||
FC_WARN("duplicate geometry id " << egf->getId() << " -> "
|
||||
<< geoLastId + 1); // NOLINT
|
||||
egf->setId(++geoLastId);
|
||||
externalGeoMap[egf->getId()] = i;
|
||||
}
|
||||
if(!egf->getRef().empty()) {
|
||||
if (!egf->getRef().empty()) {
|
||||
externalGeoRefMap[egf->getRef()].push_back(egf->getId());
|
||||
}
|
||||
}
|
||||
if(!detached.empty()) {
|
||||
if (!detached.empty()) {
|
||||
auto objs = ExternalGeometry.getValues();
|
||||
assert(externalGeoRef.size() == objs.size());
|
||||
auto itObj = objs.begin();
|
||||
auto subs = ExternalGeometry.getSubValues();
|
||||
auto itSub = subs.begin();
|
||||
for(const auto & i : externalGeoRef) {
|
||||
if(detached.count(i) != 0U) {
|
||||
for (const auto& i : externalGeoRef) {
|
||||
if (detached.count(i) != 0U) {
|
||||
itObj = objs.erase(itObj);
|
||||
itSub = subs.erase(itSub);
|
||||
auto &refs = externalGeoRefMap[i];
|
||||
for(long id : refs) {
|
||||
auto& refs = externalGeoRefMap[i];
|
||||
for (long id : refs) {
|
||||
auto it = externalGeoMap.find(id);
|
||||
if(it!=externalGeoMap.end()) {
|
||||
auto geo = ExternalGeo[it->second];
|
||||
@@ -10275,7 +10290,7 @@ void SketchObject::onChanged(const App::Property* prop)
|
||||
++itSub;
|
||||
}
|
||||
}
|
||||
ExternalGeometry.setValues(objs,subs);
|
||||
ExternalGeometry.setValues(objs, subs);
|
||||
}
|
||||
else {
|
||||
signalElementsChanged();
|
||||
@@ -10283,7 +10298,7 @@ void SketchObject::onChanged(const App::Property* prop)
|
||||
}
|
||||
else if (prop == &ExternalGeometry) {
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
if(doc && doc->isPerformingTransaction()) {
|
||||
if (doc && doc->isPerformingTransaction()) {
|
||||
setStatus(App::PendingTransactionUpdate, true);
|
||||
}
|
||||
|
||||
@@ -10293,27 +10308,28 @@ void SketchObject::onChanged(const App::Property* prop)
|
||||
updateGeometryRefs();
|
||||
signalElementsChanged();
|
||||
}
|
||||
} else if (prop == &Placement) {
|
||||
}
|
||||
else if (prop == &Placement) {
|
||||
if (ExternalGeometry.getSize() > 0) {
|
||||
touch();
|
||||
}
|
||||
} else if (prop == &ExpressionEngine) {
|
||||
if(!isRestoring()
|
||||
&& doc && !doc->isPerformingTransaction()
|
||||
&& noRecomputes
|
||||
&& !managedoperation)
|
||||
{
|
||||
}
|
||||
else if (prop == &ExpressionEngine) {
|
||||
if (!isRestoring() && doc && !doc->isPerformingTransaction() && noRecomputes
|
||||
&& !managedoperation) {
|
||||
// if we do not have a recompute, the sketch must be solved to
|
||||
// update the DoF of the solver, constraints and UI
|
||||
try {
|
||||
auto res = ExpressionEngine.execute();
|
||||
if(res) {
|
||||
FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": " << res->Why); // NOLINT
|
||||
if (res) {
|
||||
FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": "
|
||||
<< res->Why); // NOLINT
|
||||
delete res;
|
||||
}
|
||||
} catch (Base::Exception &e) {
|
||||
e.ReportException();
|
||||
FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": " << e.what()); // NOLINT
|
||||
FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": "
|
||||
<< e.what()); // NOLINT
|
||||
}
|
||||
solve();
|
||||
}
|
||||
@@ -10373,7 +10389,7 @@ void SketchObject::updateGeometryRefs() {
|
||||
std::unordered_map<std::string, int> legacyMap;
|
||||
for(int i=0;i<(int)objs.size();++i) {
|
||||
auto obj = objs[i];
|
||||
const std::string &sub = shadows[i].newName.empty() ? subs[i] : shadows[i].newName;
|
||||
const std::string& sub = shadows[i].newName.empty() ? subs[i] : shadows[i].newName;
|
||||
externalGeoRef.emplace_back(obj->getNameInDocument());
|
||||
auto &key = externalGeoRef.back();
|
||||
key += '.';
|
||||
@@ -10392,7 +10408,7 @@ void SketchObject::updateGeometryRefs() {
|
||||
if(refMap.empty()) {
|
||||
for(auto geo : geos) {
|
||||
auto egf = ExternalGeometryFacade::getFacade(geo);
|
||||
if(egf->getRefIndex()<0) {
|
||||
if (egf->getRefIndex() < 0) {
|
||||
if (egf->getId() < 0 && !egf->getRef().empty()) {
|
||||
// NOLINTNEXTLINE
|
||||
FC_ERR("External geometry reference corrupted in " << getFullName()
|
||||
@@ -10416,21 +10432,23 @@ void SketchObject::updateGeometryRefs() {
|
||||
// if not undo/redo.
|
||||
//
|
||||
// NOLINTNEXTLINE
|
||||
FC_WARN("Update legacy external reference " << egf->getRef() << " -> "
|
||||
<< externalGeoRef[it->second] << " in " << getFullName());
|
||||
} else {
|
||||
FC_WARN("Update legacy external reference "
|
||||
<< egf->getRef() << " -> " << externalGeoRef[it->second] << " in "
|
||||
<< getFullName());
|
||||
}
|
||||
else {
|
||||
// NOLINTNEXTLINE
|
||||
FC_LOG("Update undo/redo external reference " << egf->getRef() << " -> "
|
||||
<< externalGeoRef[it->second] << " in " << getFullName());
|
||||
FC_LOG("Update undo/redo external reference "
|
||||
<< egf->getRef() << " -> " << externalGeoRef[it->second] << " in "
|
||||
<< getFullName());
|
||||
}
|
||||
touched = true;
|
||||
egf->setRef(externalGeoRef[it->second]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(egf->getRefIndex() < (int)externalGeoRef.size()
|
||||
&& egf->getRef() != externalGeoRef[egf->getRefIndex()])
|
||||
{
|
||||
if (egf->getRefIndex() < (int)externalGeoRef.size()
|
||||
&& egf->getRef() != externalGeoRef[egf->getRefIndex()]) {
|
||||
touched = true;
|
||||
egf->setRef(externalGeoRef[egf->getRefIndex()]);
|
||||
}
|
||||
@@ -10442,22 +10460,22 @@ void SketchObject::updateGeometryRefs() {
|
||||
if (it == externalGeoRefMap.end()) {
|
||||
continue;
|
||||
}
|
||||
for(long id : it->second) {
|
||||
for (long id : it->second) {
|
||||
auto iter = externalGeoMap.find(id);
|
||||
if(iter!=externalGeoMap.end()) {
|
||||
auto &geo = geos[iter->second];
|
||||
geo = geo->clone();
|
||||
auto egf = ExternalGeometryFacade::getFacade(geo);
|
||||
// NOLINTNEXTLINE
|
||||
FC_LOG(getFullName() << " ref change on ExternalEdge"
|
||||
<< iter->second-1 << ' ' << egf->getRef() << " -> " << v.second);
|
||||
FC_LOG(getFullName() << " ref change on ExternalEdge" << iter->second - 1 << ' '
|
||||
<< egf->getRef() << " -> " << v.second);
|
||||
egf->setRef(v.second);
|
||||
touched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(touched) {
|
||||
if (touched) {
|
||||
ExternalGeo.setValues(std::move(geos));
|
||||
}
|
||||
}
|
||||
@@ -11474,14 +11492,15 @@ Data::IndexedName SketchObject::checkSubName(const char *subname) const{
|
||||
return Data::IndexedName();
|
||||
}
|
||||
|
||||
Data::IndexedName SketchObject::shapeTypeFromGeoId(int geoId, PointPos posId) const {
|
||||
if(geoId == GeoEnum::HAxis) {
|
||||
if(posId == PointPos::start) {
|
||||
Data::IndexedName SketchObject::shapeTypeFromGeoId(int geoId, PointPos posId) const
|
||||
{
|
||||
if (geoId == GeoEnum::HAxis) {
|
||||
if (posId == PointPos::start) {
|
||||
return Data::IndexedName::fromConst("RootPoint", 0);
|
||||
}
|
||||
return Data::IndexedName::fromConst("H_Axis", 0);
|
||||
}
|
||||
if(geoId == GeoEnum::VAxis) {
|
||||
if (geoId == GeoEnum::VAxis) {
|
||||
return Data::IndexedName::fromConst("V_Axis", 0);
|
||||
}
|
||||
|
||||
@@ -11493,15 +11512,15 @@ Data::IndexedName SketchObject::shapeTypeFromGeoId(int geoId, PointPos posId) co
|
||||
}
|
||||
if(posId != PointPos::none) {
|
||||
int idx = getVertexIndexGeoPos(geoId, posId);
|
||||
if(idx < 0){
|
||||
if (idx < 0) {
|
||||
return Data::IndexedName();
|
||||
}
|
||||
return Data::IndexedName::fromConst("Vertex", idx+1);
|
||||
return Data::IndexedName::fromConst("Vertex", idx + 1);
|
||||
}
|
||||
if (geoId >= 0) {
|
||||
return Data::IndexedName::fromConst("Edge", geoId+1);
|
||||
return Data::IndexedName::fromConst("Edge", geoId + 1);
|
||||
}
|
||||
return Data::IndexedName::fromConst("ExternalEdge", -geoId-2);
|
||||
return Data::IndexedName::fromConst("ExternalEdge", -geoId - 2);
|
||||
}
|
||||
|
||||
bool SketchObject::geoIdFromShapeType(const Data::IndexedName & indexedName,
|
||||
@@ -11608,22 +11627,22 @@ std::string SketchObject::getGeometryReference(int GeoId) const {
|
||||
|
||||
const std::string &ref = egf->getRef();
|
||||
|
||||
if(egf->testFlag(ExternalGeometryExtension::Missing)) {
|
||||
if (egf->testFlag(ExternalGeometryExtension::Missing)) {
|
||||
return std::string("? ") + ref;
|
||||
}
|
||||
|
||||
auto pos = ref.find('.');
|
||||
if(pos == std::string::npos) {
|
||||
if (pos == std::string::npos) {
|
||||
return ref;
|
||||
}
|
||||
std::string objName = ref.substr(0,pos);
|
||||
std::string objName = ref.substr(0, pos);
|
||||
auto obj = getDocument()->getObject(objName.c_str());
|
||||
if(!obj) {
|
||||
if (!obj) {
|
||||
return ref;
|
||||
}
|
||||
|
||||
App::ElementNamePair elementName;
|
||||
App::GeoFeature::resolveElement(obj,ref.c_str()+pos+1,elementName);
|
||||
App::GeoFeature::resolveElement(obj, ref.c_str() + pos + 1, elementName);
|
||||
if (!elementName.oldName.empty()) {
|
||||
return objName + "." + elementName.oldName;
|
||||
}
|
||||
|
||||
@@ -538,6 +538,7 @@ class TestSketcherSolver(unittest.TestCase):
|
||||
self.assertEqual(len(sketch2.ExternalGeometry), 1)
|
||||
|
||||
def testSaveLoadWithExternalGeometryReference(self):
|
||||
# Arrange
|
||||
body = self.Doc.addObject("PartDesign::Body", "Body")
|
||||
sketch = self.Doc.addObject("Sketcher::SketchObject", "Sketch")
|
||||
CreateRectangleSketch(sketch, (0, 0), (30, 30))
|
||||
@@ -551,7 +552,7 @@ class TestSketcherSolver(unittest.TestCase):
|
||||
sketch1.addExternal("Pad", "Edge12")
|
||||
self.Doc.recompute()
|
||||
|
||||
# Try changing it before the save
|
||||
# Act: Try changing sketch before the save
|
||||
sketch = self.Doc.getObject("Sketch")
|
||||
g1 = sketch.Constraints[11].First
|
||||
d1 = sketch.Constraints[11].Value
|
||||
@@ -559,14 +560,16 @@ class TestSketcherSolver(unittest.TestCase):
|
||||
sketch.addConstraint(Sketcher.Constraint("Distance", g1, d1 - 1.0))
|
||||
self.Doc.recompute()
|
||||
|
||||
# Act: Save and reload the file
|
||||
filename = self.Doc.Name + ".FCStd"
|
||||
self.Doc.saveAs(filename)
|
||||
FreeCAD.closeDocument(self.Doc.Name)
|
||||
self.Doc = FreeCAD.openDocument(filename)
|
||||
pad = self.Doc.getObject("Pad")
|
||||
sketch1 = self.Doc.getObject("Sketch1")
|
||||
self.Doc.recompute()
|
||||
pad = self.Doc.getObject("Pad")
|
||||
|
||||
# Act: change sketch after restore ( trigger missing references if there is a bug )
|
||||
sketch = self.Doc.getObject("Sketch")
|
||||
g1 = sketch.Constraints[11].First
|
||||
d1 = sketch.Constraints[11].Value
|
||||
@@ -574,10 +577,10 @@ class TestSketcherSolver(unittest.TestCase):
|
||||
sketch.addConstraint(Sketcher.Constraint("Distance", g1, d1 - 1.0))
|
||||
self.Doc.recompute()
|
||||
|
||||
pad = self.Doc.getObject("Pad")
|
||||
# TODO: Assert some stuff when the bug is fixed
|
||||
# self.assertEqual(pad.Shape.ElementMapSize,0)
|
||||
# self.assertNotNull(pad.Shape.ElementReverseMap["Edge12"])
|
||||
# Assert
|
||||
self.assertEqual(pad.Shape.ElementMapSize, 30)
|
||||
self.assertIn("Edge12", pad.Shape.ElementReverseMap)
|
||||
self.assertIn((pad, ("Edge12",)), sketch1.ExternalGeometry) # Not "?Edge12"
|
||||
|
||||
def testTNPExternalGeometryStored(self):
|
||||
# Arrange
|
||||
@@ -589,7 +592,6 @@ class TestSketcherSolver(unittest.TestCase):
|
||||
pad = self.Doc.addObject("PartDesign::Pad", "Pad")
|
||||
pad.Profile = sketch
|
||||
self.Doc.recompute()
|
||||
# sketch1.addExternal("Sketch", "Edge3")
|
||||
sketch1.addExternal("Pad", "Edge12")
|
||||
self.Doc.recompute()
|
||||
# Act
|
||||
@@ -622,7 +624,7 @@ class TestSketcherSolver(unittest.TestCase):
|
||||
self.assertEqual(len(extRefs), 2)
|
||||
self.assertEqual(len(extRefsAll), 3)
|
||||
self.assertEqual(root.tag, "all")
|
||||
# Act
|
||||
# Act to change the constraint
|
||||
sketch = self.Doc.getObject("Sketch")
|
||||
g1 = sketch.Constraints[11].First
|
||||
d1 = sketch.Constraints[11].Value
|
||||
@@ -641,6 +643,62 @@ class TestSketcherSolver(unittest.TestCase):
|
||||
self.assertEqual(len(extRefsAll), 3)
|
||||
self.assertEqual(root.tag, "all")
|
||||
|
||||
def testConstructionToggleTNP(self):
|
||||
"""Bug 15484"""
|
||||
# Arrange
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
body = self.Doc.addObject("PartDesign::Body", "Body")
|
||||
sketch = self.Doc.addObject("Sketcher::SketchObject", "Sketch")
|
||||
CreateRectangleSketch(sketch, (0, 0), (30, 30))
|
||||
# Add zigsag geo as construction lines
|
||||
i = sketch.GeometryCount
|
||||
sketch.addGeometry(
|
||||
Part.LineSegment(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(-10, 10, 0)), True
|
||||
)
|
||||
sketch.addGeometry(
|
||||
Part.LineSegment(FreeCAD.Vector(-10, 10, 0), FreeCAD.Vector(-5, 20, 0)), True
|
||||
)
|
||||
sketch.addGeometry(
|
||||
Part.LineSegment(FreeCAD.Vector(-5, 20, 0), FreeCAD.Vector(-10, 25, 0)), True
|
||||
)
|
||||
sketch.addGeometry(
|
||||
Part.LineSegment(FreeCAD.Vector(-10, 25, 0), FreeCAD.Vector(0, 30, 0)), True
|
||||
)
|
||||
sketch.addConstraint(Sketcher.Constraint("Coincident", i + 0, 2, i + 1, 1))
|
||||
sketch.addConstraint(Sketcher.Constraint("Coincident", i + 1, 2, i + 2, 1))
|
||||
sketch.addConstraint(Sketcher.Constraint("Coincident", i + 2, 2, i + 3, 1))
|
||||
sketch.addConstraint(Sketcher.Constraint("Coincident", i + 3, 2, 0, 1))
|
||||
sketch.addConstraint(Sketcher.Constraint("Coincident", i + 0, 1, 2, 2))
|
||||
|
||||
pad = self.Doc.addObject("PartDesign::Pad", "Pad")
|
||||
pad.Profile = sketch
|
||||
body.addObject(sketch)
|
||||
body.addObject(pad)
|
||||
self.Doc.recompute()
|
||||
sketch1 = self.Doc.addObject("Sketcher::SketchObject", "Sketch1")
|
||||
sketch1.AttachmentSupport = (pad, ("Face6"))
|
||||
sketch1.MapMode = "FlatFace"
|
||||
self.Doc.recompute()
|
||||
|
||||
CreateCircleSketch(sketch1, (2, 2, 0), 1)
|
||||
CreateCircleSketch(sketch1, (6, 2, 0), 1)
|
||||
body.addObject(sketch1)
|
||||
self.Doc.recompute()
|
||||
# Act toggle construction lines on in sketch; pad now has 9 instead of 6 faces.
|
||||
sketch.setConstruction(4, False)
|
||||
sketch.setConstruction(5, False)
|
||||
sketch.setConstruction(6, False)
|
||||
sketch.setConstruction(7, False)
|
||||
sketch.setConstruction(3, True)
|
||||
self.Doc.recompute()
|
||||
# Assert
|
||||
# AttachmentSupport is a list of (object,(subobject list)) with 1 entry. Get the
|
||||
# first and only subobject name in second part of that first tuple and see that it moved
|
||||
# from the Face6 we set above.
|
||||
self.assertEqual(sketch1.AttachmentSupport[0][1][0], "Face9")
|
||||
self.assertIn("Face6", pad.Shape.ElementReverseMap) # different Face6 exists
|
||||
|
||||
# TODO other tests:
|
||||
# getHigherElement
|
||||
|
||||
|
||||
Reference in New Issue
Block a user