Skip to content

Commit

Permalink
Sketcher: Ellipse implementation enhancements
Browse files Browse the repository at this point in the history
- ArcOfEllipse enhancement: Tangency ArcOfEllipse to ArcOfEllipse or ArcOfCircle by selecting end/starting points...
- Minor bug corrections (Thanks DeepSOIC)
- ExposeInternalGeometry python command
- DeleteUnusedInternalGeometry python command
- On deletion of an Ellipse/ArcOfEllipse all further unconstrained internal geometry is also deleted.
- This cleans up the code by eliminating code repetition in the creation methods.
- Major bug fix for autoconstraints for ellipse and arc of ellipse creation (for both creation methods)
- Major bug fix Start and Endpoint constraints of an arc of ellipse where not taking into account that Sketcher arcs are always CCW, so they have to be exchanged if we convert a CW arc into a CCW arc.

Sketcher: General bug fix: Tangency wrongly suggested

What?
=====
- On creation of a shape autoconstraints are suggested.
- Tangent autoconstraint was suggested even with lines perpendicular to the tangency direction

Reproduce
=========
- Make a circle on the origin and move the mouse along the X axis, it will suggest a tangency that is impossible
- Click on the axis and no circle will be created

Solution
========
- The SeekConstraint now can use the parameter dir to give a direction that is substantially perpendicular to the expected tangency, so that
if an object having a direction (a line) is hit, a tangency will not be suggested if within around 6 degrees of being parallel.
- Additionally, if such a line is an X,Y axis of the sketch, tangency will only be suggested if the direction is within 6 degrees of being perpendicular (i.e. it is almost tangent already while sketching).
- This difference is due to the fact that an X or Y axis can not "move" to meet the object under creation, whereas a line can.
  • Loading branch information
abdullahtahiriyo authored and wwmayer committed Dec 20, 2014
1 parent 80120a6 commit 501c40f
Show file tree
Hide file tree
Showing 10 changed files with 832 additions and 268 deletions.
2 changes: 1 addition & 1 deletion src/Mod/Sketcher/App/Sketch.cpp
Expand Up @@ -2307,7 +2307,7 @@ int Sketch::initMove(int geoId, PointPos pos, bool fine)

GCS::Point &center = Points[Geoms[geoId].midPointId];
GCS::Point p0,p1;
if (pos == mid | pos == none) {
if (pos == mid || pos == none) {
MoveParameters.resize(2); // cx,cy
p0.x = &MoveParameters[0];
p0.y = &MoveParameters[1];
Expand Down
272 changes: 261 additions & 11 deletions src/Mod/Sketcher/App/SketchObject.cpp
Expand Up @@ -355,6 +355,8 @@ int SketchObject::delGeometry(int GeoId)
if (GeoId < 0 || GeoId >= int(vals.size()))
return -1;

this->DeleteUnusedInternalGeometry(GeoId);

std::vector< Part::Geometry * > newVals(vals);
newVals.erase(newVals.begin()+GeoId);

Expand Down Expand Up @@ -1358,17 +1360,7 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point)
delConstraintOnPoint(GeoId, end, false);
Part::GeomArcOfEllipse *aoe1 = dynamic_cast<Part::GeomArcOfEllipse*>(geomlist[GeoId]);
aoe1->setRange(startAngle, startAngle + theta1);
Sketcher::Constraint *newConstr = new Sketcher::Constraint();
newConstr->Type = constrType;
newConstr->First = GeoId;
newConstr->FirstPos = end;
newConstr->Second = GeoId1;

if (constrType == Sketcher::Coincident)
newConstr->SecondPos = secondPos;

addConstraint(newConstr);
delete newConstr;

return 0;
}
}
Expand All @@ -1378,6 +1370,264 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point)
return -1;
}

int SketchObject::ExposeInternalGeometry(int GeoId)
{
if (GeoId < 0 || GeoId > getHighestCurveIndex())
return -1;

const Part::Geometry *geo = getGeometry(GeoId);
// Only for supported types
if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
// First we search what has to be restored
bool major=false;
bool minor=false;
bool focus1=false;
bool focus2=false;

int majorelementindex=-1;
int minorelementindex=-1;
int focus1elementindex=-1;
int focus2elementindex=-1;

const std::vector< Sketcher::Constraint * > &vals = Constraints.getValues();

for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
it != vals.end(); ++it) {
if((*it)->Type == Sketcher::InternalAlignment && (*it)->Second == GeoId)
{
switch((*it)->AlignmentType){
case Sketcher::EllipseMajorDiameter:
major=true;
majorelementindex=(*it)->First;
break;
case Sketcher::EllipseMinorDiameter:
minor=true;
minorelementindex=(*it)->First;
break;
case Sketcher::EllipseFocus1:
focus1=true;
focus1elementindex=(*it)->First;
break;
case Sketcher::EllipseFocus2:
focus2=true;
focus2elementindex=(*it)->First;
break;
}
}
}

int currentgeoid= getHighestCurveIndex();
int incrgeo= 0;

Base::Vector3d center;
double majord;
double minord;
double phi;

if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId()){
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo);

center=ellipse->getCenter();
majord=ellipse->getMajorRadius();
minord=ellipse->getMinorRadius();
phi=ellipse->getAngleXU();
}
else {
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geo);

center=aoe->getCenter();
majord=aoe->getMajorRadius();
minord=aoe->getMinorRadius();
phi=aoe->getAngleXU();
}

Base::Vector3d majorpositiveend = center + majord * Base::Vector3d(cos(phi),sin(phi),0);
Base::Vector3d majornegativeend = center - majord * Base::Vector3d(cos(phi),sin(phi),0);
Base::Vector3d minorpositiveend = center + minord * Base::Vector3d(-sin(phi),cos(phi),0);
Base::Vector3d minornegativeend = center - minord * Base::Vector3d(-sin(phi),cos(phi),0);

double df= sqrt(majord*majord-minord*minord);

Base::Vector3d focus1P = center + df * Base::Vector3d(cos(phi),sin(phi),0);
Base::Vector3d focus2P = center - df * Base::Vector3d(cos(phi),sin(phi),0);

if(!major)
{
Part::GeomLineSegment *lmajor = new Part::GeomLineSegment();
lmajor->setPoints(majorpositiveend,majornegativeend);

this->addGeometry(lmajor); // create line for major axis
this->setConstruction(currentgeoid+incrgeo+1,true);
delete lmajor;

Sketcher::Constraint *newConstr = new Sketcher::Constraint();
newConstr->Type = Sketcher::InternalAlignment;
newConstr->AlignmentType = EllipseMajorDiameter;
newConstr->First = currentgeoid+incrgeo+1;
newConstr->Second = GeoId;

addConstraint(newConstr);
delete newConstr;
incrgeo++;
}
if(!minor)
{
Part::GeomLineSegment *lminor = new Part::GeomLineSegment();
lminor->setPoints(minorpositiveend,minornegativeend);

this->addGeometry(lminor); // create line for major axis
this->setConstruction(currentgeoid+incrgeo+1,true);
delete lminor;

Sketcher::Constraint *newConstr = new Sketcher::Constraint();
newConstr->Type = Sketcher::InternalAlignment;
newConstr->AlignmentType = EllipseMinorDiameter;
newConstr->First = currentgeoid+incrgeo+1;
newConstr->Second = GeoId;

addConstraint(newConstr);
delete newConstr;
incrgeo++;
}
if(!focus1)
{
Part::GeomPoint *pf1 = new Part::GeomPoint();
pf1->setPoint(focus1P);
this->addGeometry(pf1);
delete pf1;

Sketcher::Constraint *newConstr = new Sketcher::Constraint();
newConstr->Type = Sketcher::InternalAlignment;
newConstr->AlignmentType = EllipseFocus1;
newConstr->First = currentgeoid+incrgeo+1;
newConstr->FirstPos = Sketcher::start;
newConstr->Second = GeoId;

addConstraint(newConstr);
delete newConstr;
incrgeo++;
}
if(!focus2)
{
Part::GeomPoint *pf2 = new Part::GeomPoint();
pf2->setPoint(focus2P);
this->addGeometry(pf2);
delete pf2;

Sketcher::Constraint *newConstr = new Sketcher::Constraint();
newConstr->Type = Sketcher::InternalAlignment;
newConstr->AlignmentType = EllipseFocus2;
newConstr->First = currentgeoid+incrgeo+1;
newConstr->FirstPos = Sketcher::start;
newConstr->Second = GeoId;

addConstraint(newConstr);
delete newConstr;
}

return incrgeo; //number of added elements
}
else
return -1; // not supported type
}

int SketchObject::DeleteUnusedInternalGeometry(int GeoId)
{
if (GeoId < 0 || GeoId > getHighestCurveIndex())
return -1;

const Part::Geometry *geo = getGeometry(GeoId);
// Only for supported types
if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
// First we search what has to be deleted
bool major=false;
bool minor=false;
bool focus1=false;
bool focus2=false;

int majorelementindex=-1;
int minorelementindex=-1;
int focus1elementindex=-1;
int focus2elementindex=-1;

const std::vector< Sketcher::Constraint * > &vals = Constraints.getValues();

for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
it != vals.end(); ++it) {
if((*it)->Type == Sketcher::InternalAlignment && (*it)->Second == GeoId)
{
switch((*it)->AlignmentType){
case Sketcher::EllipseMajorDiameter:
major=true;
majorelementindex=(*it)->First;
break;
case Sketcher::EllipseMinorDiameter:
minor=true;
minorelementindex=(*it)->First;
break;
case Sketcher::EllipseFocus1:
focus1=true;
focus1elementindex=(*it)->First;
break;
case Sketcher::EllipseFocus2:
focus2=true;
focus2elementindex=(*it)->First;
break;
}
}
}

// Hide unused geometry here
int majorconstraints=0; // number of constraints associated to the geoid of the major axis
int minorconstraints=0;
int focus1constraints=0;
int focus2constraints=0;

int decrgeo=0;

for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
it != vals.end(); ++it) {

if((*it)->Second == majorelementindex || (*it)->First == majorelementindex || (*it)->Third == majorelementindex)
majorconstraints++;
else if((*it)->Second == minorelementindex || (*it)->First == minorelementindex || (*it)->Third == minorelementindex)
minorconstraints++;
else if((*it)->Second == focus1elementindex || (*it)->First == focus1elementindex || (*it)->Third == focus1elementindex)
focus1constraints++;
else if((*it)->Second == focus2elementindex || (*it)->First == focus2elementindex || (*it)->Third == focus2elementindex)
focus2constraints++;
}

std::vector<int> delgeometries;

// those with less than 2 constraints must be removed
if(focus2constraints<2)
delgeometries.push_back(focus2elementindex);

if(focus1constraints<2)
delgeometries.push_back(focus1elementindex);

if(minorconstraints<2)
delgeometries.push_back(minorelementindex);

if(majorconstraints<2)
delgeometries.push_back(majorelementindex);

std::sort(delgeometries.begin(), delgeometries.end()); // indices over an erased element get automatically updated!!

if(delgeometries.size()>0)
{
for (std::vector<int>::reverse_iterator it=delgeometries.rbegin(); it!=delgeometries.rend(); ++it) {
delGeometry(*it);
}
}

return delgeometries.size(); //number of deleted elements
}
else
return -1; // not supported type
}

int SketchObject::addExternal(App::DocumentObject *Obj, const char* SubName)
{
// so far only externals to the support of the sketch
Expand Down
8 changes: 7 additions & 1 deletion src/Mod/Sketcher/App/SketchObject.h
@@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (c) Jürgen Riegel (juergen.riegel@web.de) 2008 *
* Copyright (c) J�rgen Riegel (juergen.riegel@web.de) 2008 *
* *
* This file is part of the FreeCAD CAx development system. *
* *
Expand Down Expand Up @@ -47,6 +47,7 @@ class SketcherExport SketchObject : public Part::Part2DObject
Part ::PropertyGeometryList Geometry;
Sketcher::PropertyConstraintList Constraints;
App ::PropertyLinkSubList ExternalGeometry;
const char* ss;
/** @name methods overide Feature */
//@{
/// recalculate the Feature
Expand Down Expand Up @@ -127,6 +128,10 @@ class SketcherExport SketchObject : public Part::Part2DObject

/// trim a curve
int trim(int geoId, const Base::Vector3d& point);
/// Exposes all internal geometry of an object supporting internal geometry
int ExposeInternalGeometry(int GeoId);
/// Deletes all unused (not further constrained) internal geometry
int DeleteUnusedInternalGeometry(int GeoId);

/// retrieves for a Vertex number the corresponding GeoId and PosId
void getGeoVertexIndex(int VertexId, int &GeoId, PointPos &PosId) const;
Expand Down Expand Up @@ -158,6 +163,7 @@ class SketcherExport SketchObject : public Part::Part2DObject
virtual int getAxisCount(void) const;
/// retrieves an axis iterating through the construction lines of the sketch (indices start at 0)
virtual Base::Axis getAxis(int axId) const;
const char*& it(int arg1, int arg2);

protected:
/// get called by the container when a property has changed
Expand Down
10 changes: 10 additions & 0 deletions src/Mod/Sketcher/App/SketchObjectPy.xml
Expand Up @@ -112,6 +112,16 @@
<UserDocu>trim a curve with a given id at a given reference point</UserDocu>
</Documentation>
</Methode>
<Methode Name="ExposeInternalGeometry">
<Documentation>
<UserDocu>Exposes all internal geometry of an object supporting internal geometry</UserDocu>
</Documentation>
</Methode>
<Methode Name="DeleteUnusedInternalGeometry">
<Documentation>
<UserDocu>Deletes all unused (not further constrained) internal geometry</UserDocu>
</Documentation>
</Methode>
<Attribute Name="ConstraintCount" ReadOnly="true">
<Documentation>
<UserDocu>Number of Constraints in this sketch</UserDocu>
Expand Down
34 changes: 34 additions & 0 deletions src/Mod/Sketcher/App/SketchObjectPyImp.cpp
Expand Up @@ -596,6 +596,40 @@ PyObject* SketchObjectPy::trim(PyObject *args)

}

PyObject* SketchObjectPy::ExposeInternalGeometry(PyObject *args)
{
int GeoId;

if (!PyArg_ParseTuple(args, "i", &GeoId))
return 0;

if (this->getSketchObjectPtr()->ExposeInternalGeometry(GeoId)==-1) {
std::stringstream str;
str << "Object does not support internal geometry: " << GeoId;
PyErr_SetString(PyExc_ValueError, str.str().c_str());
return 0;
}

Py_Return;
}

PyObject* SketchObjectPy::DeleteUnusedInternalGeometry(PyObject *args)
{
int GeoId;

if (!PyArg_ParseTuple(args, "i", &GeoId))
return 0;

if (this->getSketchObjectPtr()->DeleteUnusedInternalGeometry(GeoId)==-1) {
std::stringstream str;
str << "Object does not support internal geometry: " << GeoId;
PyErr_SetString(PyExc_ValueError, str.str().c_str());
return 0;
}

Py_Return;
}

Py::Int SketchObjectPy::getConstraintCount(void) const
{
return Py::Int(this->getSketchObjectPtr()->Constraints.getSize());
Expand Down

0 comments on commit 501c40f

Please sign in to comment.