@@ -1109,6 +1109,50 @@ int SketchObject::fillet(int GeoId1, int GeoId2,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SketchObject::extend(int GeoId, double increment, int endpoint) {
|
||||
if (GeoId < 0 || GeoId > getHighestCurveIndex())
|
||||
return -1;
|
||||
|
||||
const std::vector<Part::Geometry *> &geomList = getInternalGeometry();
|
||||
Part::Geometry *geom = geomList[GeoId];
|
||||
int retcode = -1;
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
Part::GeomLineSegment *seg = static_cast<Part::GeomLineSegment *>(geom);
|
||||
Base::Vector3d startVec = seg->getStartPoint();
|
||||
Base::Vector3d endVec = seg->getEndPoint();
|
||||
if (endpoint == start) {
|
||||
Base::Vector3d newPoint = startVec - endVec;
|
||||
double scaleFactor = newPoint.Length() + increment;
|
||||
newPoint.Normalize();
|
||||
newPoint.Scale(scaleFactor, scaleFactor, scaleFactor);
|
||||
newPoint = newPoint + endVec;
|
||||
retcode = movePoint(GeoId, Sketcher::start, newPoint, false, true);
|
||||
} else if (endpoint == end) {
|
||||
Base::Vector3d newPoint = endVec - startVec;
|
||||
double scaleFactor = newPoint.Length() + increment;
|
||||
newPoint.Normalize();
|
||||
newPoint.Scale(scaleFactor, scaleFactor, scaleFactor);
|
||||
newPoint = newPoint + startVec;
|
||||
retcode = movePoint(GeoId, Sketcher::end, newPoint, false, true);
|
||||
}
|
||||
} else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
Part::GeomArcOfCircle *arc = static_cast<Part::GeomArcOfCircle *>(geom);
|
||||
double startArc, endArc;
|
||||
arc->getRange(startArc, endArc, true);
|
||||
if (endpoint == start) {
|
||||
arc->setRange(startArc - increment, endArc, true);
|
||||
retcode = 0;
|
||||
} else if (endpoint == end) {
|
||||
arc->setRange(startArc, endArc + increment, true);
|
||||
retcode = 0;
|
||||
}
|
||||
}
|
||||
if (retcode == 0 && noRecomputes) {
|
||||
solve();
|
||||
}
|
||||
return retcode;
|
||||
}
|
||||
|
||||
int SketchObject::trim(int GeoId, const Base::Vector3d& point)
|
||||
{
|
||||
if (GeoId < 0 || GeoId > getHighestCurveIndex())
|
||||
|
||||
@@ -180,6 +180,9 @@ public:
|
||||
|
||||
/// trim a curve
|
||||
int trim(int geoId, const Base::Vector3d& point);
|
||||
/// extend a curve
|
||||
int extend(int geoId, double increment, int endPoint);
|
||||
|
||||
/// adds symmetric geometric elements with respect to the refGeoId (line or point)
|
||||
int addSymmetric(const std::vector<int> &geoIdList, int refGeoId, Sketcher::PointPos refPosId=Sketcher::none);
|
||||
/// with default parameters adds a copy of the geometric elements displaced by the displacement vector.
|
||||
|
||||
@@ -137,6 +137,11 @@
|
||||
<UserDocu>trim a curve with a given id at a given reference point</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="extend">
|
||||
<Documentation>
|
||||
<UserDocu>extend a curve to new start and end positions</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addSymmetric">
|
||||
<Documentation>
|
||||
<UserDocu>add a symmetric geometric objects to the sketch with respect to a reference point or line</UserDocu>
|
||||
|
||||
@@ -823,6 +823,26 @@ PyObject* SketchObjectPy::trim(PyObject *args)
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* SketchObjectPy::extend(PyObject *args)
|
||||
{
|
||||
double increment;
|
||||
int endPoint;
|
||||
|
||||
int GeoId;
|
||||
|
||||
if (PyArg_ParseTuple(args, "idi", &GeoId, &increment, &endPoint)) {
|
||||
if (this->getSketchObjectPtr()->extend(GeoId, increment, endPoint)) {
|
||||
std::stringstream str;
|
||||
str << "Not able to extend geometry with id : (" << GeoId << ") for increment (" << increment << ") and point position (" << endPoint << ")";
|
||||
PyErr_SetString(PyExc_ValueError, str.str().c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Py_Return;
|
||||
PyErr_SetString(PyExc_TypeError, "extend() method accepts:\n"
|
||||
"-- int,float,int\n");
|
||||
}
|
||||
|
||||
PyObject* SketchObjectPy::addSymmetric(PyObject *args)
|
||||
{
|
||||
PyObject *pcObj;
|
||||
|
||||
@@ -6083,6 +6083,323 @@ bool CmdSketcherTrimming::isActive(void)
|
||||
|
||||
// ======================================================================================
|
||||
|
||||
namespace SketcherGui {
|
||||
class ExtendSelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
App::DocumentObject* object;
|
||||
public:
|
||||
ExtendSelection(App::DocumentObject* obj)
|
||||
: Gui::SelectionFilterGate((Gui::SelectionFilter*)0)
|
||||
, object(obj)
|
||||
, disabled(false)
|
||||
{}
|
||||
|
||||
bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
|
||||
{
|
||||
if (pObj != this->object)
|
||||
return false;
|
||||
if (!sSubName || sSubName[0] == '\0')
|
||||
return false;
|
||||
if (disabled)
|
||||
return true;
|
||||
std::string element(sSubName);
|
||||
if (element.substr(0, 4) == "Edge") {
|
||||
int GeoId = std::atoi(element.substr(4, 4000).c_str()) - 1;
|
||||
Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
|
||||
const Part::Geometry *geom = Sketch->getGeometry(GeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
|
||||
geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()
|
||||
)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setDisabled(bool isDisabled) {
|
||||
disabled = isDisabled;
|
||||
}
|
||||
protected:
|
||||
bool disabled;
|
||||
};
|
||||
};
|
||||
|
||||
/* XPM */
|
||||
static const char *cursor_extension[]={
|
||||
"32 32 3 1",
|
||||
"+ c white",
|
||||
"* c red",
|
||||
". c None",
|
||||
"......+.........................",
|
||||
"......+.........................",
|
||||
"......+.........................",
|
||||
"......+.........................",
|
||||
"......+.........................",
|
||||
"................................",
|
||||
"+++++...+++++...................",
|
||||
"................................",
|
||||
"......+.........................",
|
||||
"......+.........................",
|
||||
"......+..................******.",
|
||||
"......+....................****.",
|
||||
"......+..................***.**.",
|
||||
"........................**....*.",
|
||||
"......................***.......",
|
||||
".....................***........",
|
||||
"................................",
|
||||
"................................",
|
||||
".................**.............",
|
||||
"...............***..............",
|
||||
"..............***...............",
|
||||
".............**.................",
|
||||
"................................",
|
||||
".........***....................",
|
||||
"........**......................",
|
||||
".......**.......................",
|
||||
"....**.*........................",
|
||||
"...****.........................",
|
||||
"...****.........................",
|
||||
"....**..........................",
|
||||
"................................",
|
||||
"................................"};
|
||||
|
||||
class DrawSketchHandlerExtend: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerExtend()
|
||||
: Mode(STATUS_SEEK_First)
|
||||
, EditCurve(2)
|
||||
, BaseGeoId(-1)
|
||||
, ExtendFromStart(false)
|
||||
{
|
||||
}
|
||||
virtual ~DrawSketchHandlerExtend()
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
}
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First,
|
||||
STATUS_SEEK_Second,
|
||||
};
|
||||
|
||||
virtual void activated(ViewProviderSketch *sketchgui)
|
||||
{
|
||||
Q_UNUSED(sketchgui)
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
filterGate = new ExtendSelection(sketchgui->getObject());
|
||||
Gui::Selection().addSelectionGate(filterGate);
|
||||
setCursor(QPixmap(cursor_extension),7,7);
|
||||
}
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos)
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode == STATUS_SEEK_Second) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geom);
|
||||
// project point to the existing curve
|
||||
Base::Vector3d startPoint = lineSeg->getStartPoint();
|
||||
Base::Vector3d endPoint = lineSeg->getEndPoint();
|
||||
|
||||
Base::Vector2d recenteredLine = Base::Vector2d(endPoint.x - startPoint.x,
|
||||
endPoint.y - startPoint.y);
|
||||
Base::Vector2d recenteredPoint = Base::Vector2d(onSketchPos.x - startPoint.x,
|
||||
onSketchPos.y - startPoint.y);
|
||||
Base::Vector2d projection;
|
||||
projection.ProjectToLine(recenteredPoint, recenteredLine);
|
||||
if (recenteredPoint.Length() < recenteredPoint.Distance(recenteredLine)) {
|
||||
EditCurve[0] = Base::Vector2d(startPoint.x + projection.x, startPoint.y + projection.y);
|
||||
EditCurve[1] = Base::Vector2d(endPoint.x, endPoint.y);
|
||||
} else {
|
||||
EditCurve[0] = Base::Vector2d(startPoint.x, startPoint.y);
|
||||
EditCurve[1] = Base::Vector2d(startPoint.x + projection.x, startPoint.y + projection.y);
|
||||
}
|
||||
ExtendFromStart = (onSketchPos.Distance(EditCurve[0]) < onSketchPos.Distance(EditCurve[1]));
|
||||
Increment = ExtendFromStart ? projection.Length() : projection.Length() - recenteredLine.Length();
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
|
||||
} else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
|
||||
Base::Vector3d center = arc->getCenter();
|
||||
double radius = arc->getRadius();
|
||||
|
||||
double start, end;
|
||||
arc->getRange(start, end, true);
|
||||
double arcAngle = end - start;
|
||||
|
||||
Base::Vector2d angle = Base::Vector2d(onSketchPos.x - center.x, onSketchPos.y - center.y);
|
||||
Base::Vector2d startAngle = Base::Vector2d(cos(start), sin(start));
|
||||
Base::Vector2d endAngle = Base::Vector2d(cos(end), sin(end));
|
||||
|
||||
Base::Vector2d arcHalf = Base::Vector2d(cos(start + arcAngle/ 2.0), sin(start+ arcAngle / 2.0));
|
||||
double angleToEndAngle = angle.GetAngle(endAngle);
|
||||
double angleToStartAngle = angle.GetAngle(startAngle);
|
||||
|
||||
if (arcHalf.GetAngle(angle) > arcAngle / 2) {
|
||||
double modStartAngle = start;
|
||||
double modArcAngle = end - start;
|
||||
if (ExtendFromStart) {
|
||||
if (crossProduct(angle, startAngle) < 0) {
|
||||
modStartAngle -= 2*M_PI - angleToStartAngle;
|
||||
modArcAngle += 2*M_PI - angleToStartAngle;
|
||||
} else {
|
||||
modStartAngle -= angleToStartAngle;
|
||||
modArcAngle += angleToStartAngle;
|
||||
}
|
||||
} else {
|
||||
if (crossProduct(angle, endAngle) >= 0) {
|
||||
modArcAngle += 2*M_PI - angleToEndAngle;
|
||||
} else {
|
||||
modArcAngle += angleToEndAngle;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 31; i++) {
|
||||
double angle = modStartAngle + i * modArcAngle/30.0;
|
||||
EditCurve[i] = Base::Vector2d(center.x + radius * cos(angle), center.y + radius * sin(angle));
|
||||
}
|
||||
Increment = modArcAngle - (end- start);
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
} else {
|
||||
// draw curve anyway to avoid 'stuck' appearance
|
||||
for (int i = 0; i < 31; i++) {
|
||||
double angle = start + i * arcAngle/30.0;
|
||||
EditCurve[i] = Base::Vector2d(center.x + radius * cos(angle), center.y + radius * sin(angle));
|
||||
}
|
||||
Increment = 0;
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
}
|
||||
}
|
||||
if (seekAutoConstraint(SugConstr, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(SugConstr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos)
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos)
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode == STATUS_SEEK_First) {
|
||||
BaseGeoId = sketchgui->getPreselectCurve();
|
||||
if (BaseGeoId > -1) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
Mode = STATUS_SEEK_Second;
|
||||
} else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
|
||||
double start, end;
|
||||
arc->getRange(start, end, true);
|
||||
|
||||
Base::Vector3d center = arc->getCenter();
|
||||
Base::Vector2d angle = Base::Vector2d(onSketchPos.x - center.x, onSketchPos.y - center.y);
|
||||
double angleToStart = angle.GetAngle(Base::Vector2d(cos(start), sin(start)));
|
||||
double angleToEnd = angle.GetAngle(Base::Vector2d(cos(end), sin(end)));
|
||||
ExtendFromStart = (angleToStart < angleToEnd); // move start point if closer to angle than end point
|
||||
EditCurve.resize(31);
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
filterGate->setDisabled(true);
|
||||
}
|
||||
} else if (Mode == STATUS_SEEK_Second) {
|
||||
try {
|
||||
Gui::Command::openCommand("Extend edge");
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"App.ActiveDocument.%s.extend(%d, %f, %d)\n", // GeoId, increment, PointPos
|
||||
sketchgui->getObject()->getNameInDocument(), BaseGeoId, Increment,
|
||||
ExtendFromStart ? Sketcher::start : Sketcher::end);
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool autoRecompute = hGrp->GetBool("AutoRecompute",false);
|
||||
if(autoRecompute)
|
||||
Gui::Command::updateActive();
|
||||
|
||||
// constrain chosen point
|
||||
if (SugConstr.size() > 0) {
|
||||
createAutoConstraints(SugConstr, BaseGeoId, (ExtendFromStart) ? Sketcher::start : Sketcher::end);
|
||||
SugConstr.clear();
|
||||
}
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
filterGate->setDisabled(false);
|
||||
EditCurve.clear();
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
} else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to extend edge: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
} else { // exit extension tool if user clicked on empty space
|
||||
BaseGeoId = -1;
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
int BaseGeoId;
|
||||
ExtendSelection* filterGate = nullptr;
|
||||
bool ExtendFromStart; // if true, extend from start, else extend from end (circle only)
|
||||
double Increment;
|
||||
std::vector<AutoConstraint> SugConstr;
|
||||
|
||||
private:
|
||||
int crossProduct(Base::Vector2d &vec1, Base::Vector2d &vec2) {
|
||||
return vec1.x * vec2.y - vec1.y * vec2.x;
|
||||
}
|
||||
};
|
||||
|
||||
DEF_STD_CMD_A(CmdSketcherExtend);
|
||||
|
||||
//TODO: fix the translations for this
|
||||
CmdSketcherExtend::CmdSketcherExtend()
|
||||
: Command("Sketcher_Extend")
|
||||
{
|
||||
sAppModule = "Sketcher";
|
||||
sGroup = QT_TR_NOOP("Sketcher");
|
||||
sMenuText = QT_TR_NOOP("Extend edge");
|
||||
sToolTipText = QT_TR_NOOP("Extend an edge with respect to the picked position");
|
||||
sWhatsThis = "Sketcher_Extend";
|
||||
sStatusTip = sToolTipText;
|
||||
sPixmap = "Sketcher_Extend";
|
||||
sAccel = "T,E";
|
||||
eType = ForEdit;
|
||||
}
|
||||
|
||||
void CmdSketcherExtend::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerExtend());
|
||||
}
|
||||
|
||||
bool CmdSketcherExtend::isActive(void)
|
||||
{
|
||||
return isCreateGeoActive(getActiveGuiDocument());
|
||||
}
|
||||
|
||||
|
||||
namespace SketcherGui {
|
||||
class ExternalSelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
@@ -7372,6 +7689,7 @@ void CreateSketcherCommandsCreateGeo(void)
|
||||
//rcCmdMgr.addCommand(new CmdSketcherCreateText());
|
||||
//rcCmdMgr.addCommand(new CmdSketcherCreateDraftLine());
|
||||
rcCmdMgr.addCommand(new CmdSketcherTrimming());
|
||||
rcCmdMgr.addCommand(new CmdSketcherExtend());
|
||||
rcCmdMgr.addCommand(new CmdSketcherExternal());
|
||||
rcCmdMgr.addCommand(new CmdSketcherCarbonCopy());
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
<file>icons/Sketcher_Element_SelectionTypeInvalid.svg</file>
|
||||
<file>icons/Sketcher_Elliptical_Arc.svg</file>
|
||||
<file>icons/Sketcher_Elliptical_Arc_Constr.svg</file>
|
||||
<file>icons/Sketcher_Extend.svg</file>
|
||||
<file>icons/Sketcher_External.svg</file>
|
||||
<file>icons/Sketcher_Hyperbolic_Arc.svg</file>
|
||||
<file>icons/Sketcher_Hyperbolic_Arc_Constr.svg</file>
|
||||
|
||||
@@ -894,6 +894,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe
|
||||
<< "Sketcher_CreateHexagon"
|
||||
<< "Sketcher_CreateFillet"
|
||||
<< "Sketcher_Trimming"
|
||||
<< "Sketcher_Extend"
|
||||
<< "Sketcher_External"
|
||||
<< "Sketcher_ToggleConstruction"
|
||||
/*<< "Sketcher_CreateText"*/
|
||||
|
||||
@@ -190,6 +190,7 @@ inline void SketcherAddWorkbenchGeometries(T& geom){
|
||||
<< "Separator"
|
||||
<< "Sketcher_CreateFillet"
|
||||
<< "Sketcher_Trimming"
|
||||
<< "Sketcher_Extend"
|
||||
<< "Sketcher_External"
|
||||
<< "Sketcher_CarbonCopy"
|
||||
<< "Sketcher_ToggleConstruction"
|
||||
|
||||
Reference in New Issue
Block a user