Merge branch 'logari81/sketcher'
This commit is contained in:
commit
6cef178ae7
|
@ -1350,6 +1350,8 @@ void SketchObject::rebuildExternalGeometry(void)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rebuildVertexIndex();
|
||||
}
|
||||
|
||||
std::vector<Part::Geometry*> SketchObject::getCompleteGeometry(void) const
|
||||
|
@ -1491,8 +1493,6 @@ void SketchObject::Restore(XMLReader &reader)
|
|||
{
|
||||
// read the father classes
|
||||
Part::Part2DObject::Restore(reader);
|
||||
Constraints.acceptGeometry(getCompleteGeometry());
|
||||
rebuildVertexIndex();
|
||||
}
|
||||
|
||||
void SketchObject::onChanged(const App::Property* prop)
|
||||
|
|
|
@ -1636,15 +1636,7 @@ void CmdSketcherConstrainAngle::activated(int iMsg)
|
|||
const std::vector<Sketcher::Constraint *> &ConStr = dynamic_cast<Sketcher::SketchObject*>(selection[0].getObject())->Constraints.getValues();
|
||||
Sketcher::Constraint *constr = ConStr[ConStr.size() -1];
|
||||
|
||||
float sf = 1.f;
|
||||
Gui::Document *doc = getActiveGuiDocument();
|
||||
if (doc && doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
||||
SketcherGui::ViewProviderSketch *vp = dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
||||
sf = vp->getScaleFactor();
|
||||
|
||||
constr->LabelDistance = 2. * sf;
|
||||
vp->draw(); // Redraw
|
||||
}
|
||||
updateDatumDistance(getActiveGuiDocument(), constr);
|
||||
|
||||
//updateActive();
|
||||
getSelection().clearSelection();
|
||||
|
|
|
@ -142,15 +142,21 @@ public:
|
|||
|
||||
virtual void mouseMove(Base::Vector2D onSketchPos)
|
||||
{
|
||||
setPositionText(onSketchPos);
|
||||
|
||||
if (Mode==STATUS_SEEK_First) {
|
||||
setPositionText(onSketchPos);
|
||||
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Second){
|
||||
float length = (onSketchPos - EditCurve[0]).Length();
|
||||
float angle = (onSketchPos - EditCurve[0]).GetAngle(Base::Vector2D(1.f,0.f));
|
||||
char buf[40];
|
||||
sprintf(buf, " (%.1f,%.1f°)", length, angle * 180 / M_PI);
|
||||
std::string text = buf;
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
EditCurve[1] = onSketchPos;
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0])) {
|
||||
|
@ -301,15 +307,22 @@ public:
|
|||
|
||||
virtual void mouseMove(Base::Vector2D onSketchPos)
|
||||
{
|
||||
setPositionText(onSketchPos);
|
||||
|
||||
if (Mode==STATUS_SEEK_First) {
|
||||
setPositionText(onSketchPos);
|
||||
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Second) {
|
||||
float dx = onSketchPos.fX - EditCurve[0].fX;
|
||||
float dy = onSketchPos.fY - EditCurve[0].fY;
|
||||
char buf[40];
|
||||
sprintf(buf, " (%.1f x %.1f)", dx, dy);
|
||||
std::string text = buf;
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
EditCurve[2] = onSketchPos;
|
||||
EditCurve[1] = Base::Vector2D(onSketchPos.fX ,EditCurve[0].fY);
|
||||
EditCurve[3] = Base::Vector2D(EditCurve[0].fX,onSketchPos.fY);
|
||||
|
@ -488,50 +501,103 @@ class DrawSketchHandlerLineSet: public DrawSketchHandler
|
|||
{
|
||||
public:
|
||||
DrawSketchHandlerLineSet()
|
||||
: Mode(STATUS_SEEK_First),LineMode(LINE_MODE_Line),EditCurve(2),
|
||||
firstVertex(-1),firstCurve(-1),previousCurve(-1),previousPosId(-1),
|
||||
isTangent(false) {}
|
||||
: Mode(STATUS_SEEK_First),SegmentMode(SEGMENT_MODE_Line),
|
||||
TransitionMode(TRANSITION_MODE_Free),suppressTransition(false),EditCurve(2),
|
||||
firstVertex(-1),firstCurve(-1),previousCurve(-1),previousPosId(Sketcher::none) {}
|
||||
virtual ~DrawSketchHandlerLineSet() {}
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
enum SELECT_MODE {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_Do,
|
||||
STATUS_Close
|
||||
};
|
||||
|
||||
enum SelectLineMode
|
||||
enum SEGMENT_MODE
|
||||
{
|
||||
LINE_MODE_Arc,
|
||||
LINE_MODE_Line
|
||||
SEGMENT_MODE_Arc,
|
||||
SEGMENT_MODE_Line
|
||||
};
|
||||
|
||||
enum TRANSITION_MODE
|
||||
{
|
||||
TRANSITION_MODE_Free,
|
||||
TRANSITION_MODE_Tangent,
|
||||
TRANSITION_MODE_Perpendicular_L,
|
||||
TRANSITION_MODE_Perpendicular_R
|
||||
};
|
||||
|
||||
virtual void registerPressedKey(bool pressed, int key)
|
||||
{
|
||||
if (key == SoKeyboardEvent::A && pressed && previousCurve != -1) {
|
||||
if (LineMode != LINE_MODE_Arc) {
|
||||
Base::Vector2D onSketchPos = EditCurve[isTangent ? 2 : 1];
|
||||
LineMode = LINE_MODE_Arc;
|
||||
if (previousCurve != -1)
|
||||
isTangent = true;
|
||||
else
|
||||
isTangent = false;
|
||||
EditCurve.resize(32);
|
||||
mouseMove(onSketchPos); // trigger an update of EditCurve
|
||||
if (key == SoKeyboardEvent::M && pressed && previousCurve != -1) {
|
||||
// loop through the following modes:
|
||||
// SEGMENT_MODE_Line, TRANSITION_MODE_Free / TRANSITION_MODE_Tangent
|
||||
// SEGMENT_MODE_Line, TRANSITION_MODE_Perpendicular_L
|
||||
// SEGMENT_MODE_Line, TRANSITION_MODE_Tangent / TRANSITION_MODE_Free
|
||||
// SEGMENT_MODE_Arc, TRANSITION_MODE_Tangent
|
||||
// SEGMENT_MODE_Arc, TRANSITION_MODE_Perpendicular_L
|
||||
// SEGMENT_MODE_Arc, TRANSITION_MODE_Perpendicular_R
|
||||
|
||||
Base::Vector2D onSketchPos;
|
||||
if (SegmentMode == SEGMENT_MODE_Line)
|
||||
onSketchPos = EditCurve[EditCurve.size()-1];
|
||||
else
|
||||
onSketchPos = EditCurve[29];
|
||||
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(previousCurve);
|
||||
|
||||
if (SegmentMode == SEGMENT_MODE_Line) {
|
||||
switch (TransitionMode) {
|
||||
case TRANSITION_MODE_Free:
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { // 3rd mode
|
||||
SegmentMode = SEGMENT_MODE_Arc;
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
}
|
||||
else // 1st mode
|
||||
TransitionMode = TRANSITION_MODE_Perpendicular_L;
|
||||
break;
|
||||
case TRANSITION_MODE_Perpendicular_L: // 2nd mode
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
||||
TransitionMode = TRANSITION_MODE_Free;
|
||||
else
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
break;
|
||||
case TRANSITION_MODE_Tangent:
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) // 1st mode
|
||||
TransitionMode = TRANSITION_MODE_Perpendicular_L;
|
||||
else { // 3rd mode
|
||||
SegmentMode = SEGMENT_MODE_Arc;
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
}
|
||||
break;
|
||||
default: // unexpected mode
|
||||
TransitionMode = TRANSITION_MODE_Free;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Base::Vector2D onSketchPos = EditCurve[29];
|
||||
LineMode = LINE_MODE_Line;
|
||||
if (previousCurve != -1) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(previousCurve);
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
||||
isTangent = true;
|
||||
else
|
||||
isTangent = false;
|
||||
switch (TransitionMode) {
|
||||
case TRANSITION_MODE_Tangent: // 4th mode
|
||||
TransitionMode = TRANSITION_MODE_Perpendicular_L;
|
||||
break;
|
||||
case TRANSITION_MODE_Perpendicular_L: // 5th mode
|
||||
TransitionMode = TRANSITION_MODE_Perpendicular_R;
|
||||
break;
|
||||
default: // 6th mode (Perpendicular_R) + unexpexted mode
|
||||
SegmentMode = SEGMENT_MODE_Line;
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
else
|
||||
TransitionMode = TRANSITION_MODE_Free;
|
||||
break;
|
||||
}
|
||||
EditCurve.resize(isTangent ? 3 : 2);
|
||||
mouseMove(onSketchPos); // trigger an update of EditCurve
|
||||
}
|
||||
|
||||
if (SegmentMode == SEGMENT_MODE_Line)
|
||||
EditCurve.resize(TransitionMode == TRANSITION_MODE_Free ? 2 : 3);
|
||||
else
|
||||
EditCurve.resize(32);
|
||||
mouseMove(onSketchPos); // trigger an update of EditCurve
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -542,31 +608,60 @@ public:
|
|||
|
||||
virtual void mouseMove(Base::Vector2D onSketchPos)
|
||||
{
|
||||
setPositionText(onSketchPos);
|
||||
suppressTransition = false;
|
||||
if (Mode==STATUS_SEEK_First) {
|
||||
setPositionText(onSketchPos);
|
||||
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Second){
|
||||
if (LineMode == LINE_MODE_Line) {
|
||||
EditCurve[isTangent ? 2 : 1] = onSketchPos;
|
||||
if (isTangent) {
|
||||
if (SegmentMode == SEGMENT_MODE_Line) {
|
||||
EditCurve[EditCurve.size()-1] = onSketchPos;
|
||||
if (TransitionMode == TRANSITION_MODE_Tangent) {
|
||||
Base::Vector2D Tangent(dirVec.x,dirVec.y);
|
||||
EditCurve[1].ProjToLine(EditCurve[2] - EditCurve[0], Tangent);
|
||||
if (EditCurve[1] * Tangent < 0) {
|
||||
EditCurve[1] = EditCurve[2];
|
||||
suppressTransition = true;
|
||||
}
|
||||
else
|
||||
EditCurve[1] = EditCurve[0] + EditCurve[1];
|
||||
}
|
||||
else if (TransitionMode == TRANSITION_MODE_Perpendicular_L ||
|
||||
TransitionMode == TRANSITION_MODE_Perpendicular_R) {
|
||||
Base::Vector2D Perpendicular(-dirVec.y,dirVec.x);
|
||||
EditCurve[1].ProjToLine(EditCurve[2] - EditCurve[0], Perpendicular);
|
||||
EditCurve[1] = EditCurve[0] + EditCurve[1];
|
||||
}
|
||||
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
if (!isTangent) {
|
||||
|
||||
float length = (EditCurve[1] - EditCurve[0]).Length();
|
||||
float angle = (EditCurve[1] - EditCurve[0]).GetAngle(Base::Vector2D(1.f,0.f));
|
||||
|
||||
char buf[40];
|
||||
sprintf(buf, " (%.1f,%.1f°)", length, angle * 180 / M_PI);
|
||||
std::string text = buf;
|
||||
setPositionText(EditCurve[1], text);
|
||||
|
||||
if (TransitionMode == TRANSITION_MODE_Free) {
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0])) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (LineMode == LINE_MODE_Arc) {
|
||||
Base::Vector2D Tangent(dirVec.x,dirVec.y);
|
||||
else if (SegmentMode == SEGMENT_MODE_Arc) {
|
||||
Base::Vector2D Tangent;
|
||||
if (TransitionMode == TRANSITION_MODE_Tangent)
|
||||
Tangent = Base::Vector2D(dirVec.x,dirVec.y);
|
||||
else if (TransitionMode == TRANSITION_MODE_Perpendicular_L)
|
||||
Tangent = Base::Vector2D(-dirVec.y,dirVec.x);
|
||||
else if (TransitionMode == TRANSITION_MODE_Perpendicular_R)
|
||||
Tangent = Base::Vector2D(dirVec.y,-dirVec.x);
|
||||
|
||||
float theta = Tangent.GetAngle(onSketchPos - EditCurve[0]);
|
||||
arcRadius = (onSketchPos - EditCurve[0]).Length()/(2.0*sin(theta));
|
||||
// At this point we need a unit normal vector pointing torwards
|
||||
|
@ -581,10 +676,7 @@ public:
|
|||
if ((x2*y3-x3*y2)-(x1*y3-x3*y1)+(x1*y2-x2*y1) > 0)
|
||||
arcRadius *= -1;
|
||||
|
||||
Base::Vector3d centerVec = dirVec % Base::Vector3d(0.f,0.f,1.0);
|
||||
centerVec.Normalize(); // this step should actually be redundant
|
||||
|
||||
CenterPoint = EditCurve[0] + Base::Vector2D(arcRadius * centerVec.x, arcRadius * centerVec.y);
|
||||
CenterPoint = EditCurve[0] + Base::Vector2D(arcRadius * Tangent.fY, -arcRadius * Tangent.fX);
|
||||
|
||||
float rx = EditCurve[0].fX - CenterPoint.fX;
|
||||
float ry = EditCurve[0].fY - CenterPoint.fY;
|
||||
|
@ -611,6 +703,12 @@ public:
|
|||
EditCurve[31] = EditCurve[0];
|
||||
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
|
||||
char buf[40];
|
||||
sprintf(buf, " (%.1fR,%.1f°)", std::abs(arcRadius), arcAngle * 180 / M_PI);
|
||||
std::string text = buf;
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
|
@ -622,35 +720,45 @@ public:
|
|||
|
||||
virtual bool pressButton(Base::Vector2D onSketchPos)
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First) {
|
||||
// remember our first point
|
||||
firstVertex = getHighestVertexIndex() + 1;
|
||||
firstCurve = getHighestCurveIndex() + 1;
|
||||
// TODO: here we should check if there is a preselected point
|
||||
// and set up a transition from the neighbouring segment.
|
||||
// (peviousCurve, previousPosId, dirVec, isTangent)
|
||||
// in that case we should set firstCurve and firstVertex to -1
|
||||
// in order to disable closing the wire
|
||||
if (LineMode == LINE_MODE_Line)
|
||||
EditCurve.resize(isTangent ? 3 : 2);
|
||||
else if (LineMode == LINE_MODE_Arc)
|
||||
if (Mode == STATUS_SEEK_First) {
|
||||
|
||||
EditCurve[0] = onSketchPos; // this may be overwritten if previousCurve is found
|
||||
|
||||
// here we check if there is a preselected point and
|
||||
// we set up a transition from the neighbouring segment.
|
||||
// (peviousCurve, previousPosId, dirVec, TransitionMode)
|
||||
for (int i=0; i < sugConstr1.size(); i++)
|
||||
if (sugConstr1[i].Type == Sketcher::Coincident) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(sugConstr1[i].GeoId);
|
||||
if ((geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
|
||||
geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) &&
|
||||
(sugConstr1[i].PosId == Sketcher::start ||
|
||||
sugConstr1[i].PosId == Sketcher::end)) {
|
||||
previousCurve = sugConstr1[i].GeoId;
|
||||
previousPosId = sugConstr1[i].PosId;
|
||||
updateTransitionData(previousCurve,previousPosId); // -> dirVec, EditMode[0]
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
sugConstr1.erase(sugConstr1.begin()+i); // actually we should clear the vector completely
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// in case a transition is set up, firstCurve and firstVertex should
|
||||
// remain set to -1 in order to disable closing the wire
|
||||
if (previousCurve == -1) {
|
||||
// remember our first point
|
||||
firstVertex = getHighestVertexIndex() + 1;
|
||||
firstCurve = getHighestCurveIndex() + 1;
|
||||
}
|
||||
|
||||
if (SegmentMode == SEGMENT_MODE_Line)
|
||||
EditCurve.resize(TransitionMode == TRANSITION_MODE_Free ? 2 : 3);
|
||||
else if (SegmentMode == SEGMENT_MODE_Arc)
|
||||
EditCurve.resize(32);
|
||||
EditCurve[0] = onSketchPos;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Second) {
|
||||
if (LineMode == LINE_MODE_Line) {
|
||||
EditCurve[isTangent ? 2 : 1] = onSketchPos;
|
||||
if (isTangent) {
|
||||
Base::Vector2D Tangent(dirVec.x,dirVec.y);
|
||||
EditCurve[1].ProjToLine(EditCurve[2] - EditCurve[0], Tangent);
|
||||
EditCurve[1] = EditCurve[0] + EditCurve[1];
|
||||
}
|
||||
}
|
||||
else if (LineMode == LINE_MODE_Arc)
|
||||
EditCurve[29] = onSketchPos; // not so important
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
applyCursor();
|
||||
else if (Mode == STATUS_SEEK_Second) {
|
||||
// exit on clicking exactly at the same position (e.g. double click)
|
||||
if (onSketchPos == EditCurve[0]) {
|
||||
unsetCursor();
|
||||
|
@ -659,7 +767,7 @@ public:
|
|||
sketchgui->drawEdit(EditCurve);
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
if (sketchgui->getPreselectPoint() == firstVertex)
|
||||
if (sketchgui->getPreselectPoint() == firstVertex && firstVertex != -1)
|
||||
Mode = STATUS_Close;
|
||||
else
|
||||
Mode = STATUS_Do;
|
||||
|
@ -671,7 +779,7 @@ public:
|
|||
{
|
||||
if (Mode == STATUS_Do || Mode == STATUS_Close) {
|
||||
|
||||
if (LineMode == LINE_MODE_Line) {
|
||||
if (SegmentMode == SEGMENT_MODE_Line) {
|
||||
// open the transaction
|
||||
Gui::Command::openCommand("Add line to sketch wire");
|
||||
// issue the geometry
|
||||
|
@ -680,7 +788,7 @@ public:
|
|||
sketchgui->getObject()->getNameInDocument(),
|
||||
EditCurve[0].fX,EditCurve[0].fY,EditCurve[1].fX,EditCurve[1].fY);
|
||||
}
|
||||
else if (LineMode == LINE_MODE_Arc) { // We're dealing with an Arc
|
||||
else if (SegmentMode == SEGMENT_MODE_Arc) { // We're dealing with an Arc
|
||||
Gui::Command::openCommand("Add arc to sketch wire");
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"App.ActiveDocument.%s.addGeometry(Part.ArcOfCircle"
|
||||
|
@ -691,15 +799,24 @@ public:
|
|||
}
|
||||
// issue the constraint
|
||||
if (previousCurve != -1) {
|
||||
int lastCurve = previousCurve+1;
|
||||
int lastStartPosId = (LineMode == LINE_MODE_Arc && startAngle > endAngle) ? 2 : 1;
|
||||
int lastEndPosId = (LineMode == LINE_MODE_Arc && startAngle > endAngle) ? 1 : 2;
|
||||
// in case of a tangency constraint, the coincident constraint is redundant
|
||||
int lastCurve = getHighestCurveIndex();
|
||||
Sketcher::PointPos lastStartPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
|
||||
Sketcher::end : Sketcher::start;
|
||||
Sketcher::PointPos lastEndPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
|
||||
Sketcher::start : Sketcher::end;
|
||||
// in case of a tangency constraint, the coincident constraint is redundant
|
||||
std::string constrType = "Coincident";
|
||||
if (!suppressTransition) {
|
||||
if (TransitionMode == TRANSITION_MODE_Tangent)
|
||||
constrType = "Tangent";
|
||||
else if (TransitionMode == TRANSITION_MODE_Perpendicular_L ||
|
||||
TransitionMode == TRANSITION_MODE_Perpendicular_R)
|
||||
constrType = "Perpendicular";
|
||||
}
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('%s',%i,%i,%i,%i)) ",
|
||||
sketchgui->getObject()->getNameInDocument(),
|
||||
isTangent ? "Tangent" : "Coincident",
|
||||
previousCurve, previousPosId /* == 2 */, lastCurve, lastStartPosId);
|
||||
sketchgui->getObject()->getNameInDocument(), constrType.c_str(),
|
||||
previousCurve, previousPosId, lastCurve, lastStartPosId);
|
||||
if (Mode == STATUS_Close) {
|
||||
int firstGeoId;
|
||||
Sketcher::PointPos firstPosId;
|
||||
|
@ -750,61 +867,74 @@ public:
|
|||
|
||||
// remember the vertex for the next rounds constraint..
|
||||
previousCurve = getHighestCurveIndex();
|
||||
previousPosId = (LineMode == LINE_MODE_Arc && startAngle > endAngle) ? 1 : 2;
|
||||
previousPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
|
||||
Sketcher::start : Sketcher::end; // cw arcs are rendered in reverse
|
||||
|
||||
// setup for the next line segment
|
||||
// Use updated endPoint as autoconstraints can modify the position
|
||||
// Need to determine if the previous element was a line or an arc or ???
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(getHighestCurveIndex());
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment *lineSeg = dynamic_cast<const Part::GeomLineSegment *>(geom);
|
||||
EditCurve[0] = Base::Vector2D(lineSeg->getEndPoint().x, lineSeg->getEndPoint().y);
|
||||
dirVec.Set(lineSeg->getEndPoint().x - lineSeg->getStartPoint().x,
|
||||
lineSeg->getEndPoint().y - lineSeg->getStartPoint().y,
|
||||
0.f);
|
||||
}
|
||||
else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
assert(LineMode == LINE_MODE_Arc);
|
||||
const Part::GeomArcOfCircle *arcSeg = dynamic_cast<const Part::GeomArcOfCircle *>(geom);
|
||||
if (startAngle > endAngle) {
|
||||
EditCurve[0] = Base::Vector2D(arcSeg->getStartPoint().x,arcSeg->getStartPoint().y);
|
||||
dirVec = Base::Vector3d(0.f,0.f,-1.0) % (arcSeg->getStartPoint()-arcSeg->getCenter());
|
||||
}
|
||||
else { // cw arcs are rendered in reverse
|
||||
EditCurve[0] = Base::Vector2D(arcSeg->getEndPoint().x,arcSeg->getEndPoint().y);
|
||||
dirVec = Base::Vector3d(0.f,0.f,1.0) % (arcSeg->getEndPoint()-arcSeg->getCenter());
|
||||
}
|
||||
}
|
||||
dirVec.Normalize();
|
||||
// calculate dirVec and EditCurve[0]
|
||||
updateTransitionData(previousCurve,previousPosId);
|
||||
|
||||
applyCursor();
|
||||
Mode = STATUS_SEEK_Second;
|
||||
isTangent = (LineMode == LINE_MODE_Arc);
|
||||
LineMode = LINE_MODE_Line;
|
||||
EditCurve.resize(isTangent ? 3 : 2);
|
||||
EditCurve[1] = EditCurve[0];
|
||||
if (isTangent)
|
||||
if (SegmentMode == SEGMENT_MODE_Arc) {
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
EditCurve.resize(3);
|
||||
EditCurve[2] = EditCurve[0];
|
||||
}
|
||||
else {
|
||||
TransitionMode = TRANSITION_MODE_Free;
|
||||
EditCurve.resize(2);
|
||||
}
|
||||
SegmentMode = SEGMENT_MODE_Line;
|
||||
EditCurve[1] = EditCurve[0];
|
||||
sketchgui->drawEdit(EditCurve);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
SelectLineMode LineMode;
|
||||
SELECT_MODE Mode;
|
||||
SEGMENT_MODE SegmentMode;
|
||||
TRANSITION_MODE TransitionMode;
|
||||
bool suppressTransition;
|
||||
|
||||
std::vector<Base::Vector2D> EditCurve;
|
||||
int firstVertex;
|
||||
int firstCurve;
|
||||
int previousPosId;
|
||||
int previousCurve;
|
||||
Sketcher::PointPos previousPosId;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2;
|
||||
|
||||
Base::Vector2D CenterPoint;
|
||||
Base::Vector3d dirVec;
|
||||
bool isTangent;
|
||||
float startAngle, endAngle, arcRadius;
|
||||
|
||||
void updateTransitionData(int GeoId, Sketcher::PointPos PosId) {
|
||||
|
||||
// Use updated startPoint/endPoint as autoconstraints can modify the position
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment *lineSeg = dynamic_cast<const Part::GeomLineSegment *>(geom);
|
||||
EditCurve[0] = Base::Vector2D(lineSeg->getEndPoint().x, lineSeg->getEndPoint().y);
|
||||
dirVec.Set(lineSeg->getEndPoint().x - lineSeg->getStartPoint().x,
|
||||
lineSeg->getEndPoint().y - lineSeg->getStartPoint().y,
|
||||
0.f);
|
||||
if (PosId == Sketcher::start)
|
||||
dirVec *= -1;
|
||||
}
|
||||
else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
const Part::GeomArcOfCircle *arcSeg = dynamic_cast<const Part::GeomArcOfCircle *>(geom);
|
||||
if (PosId == Sketcher::start) {
|
||||
EditCurve[0] = Base::Vector2D(arcSeg->getStartPoint().x,arcSeg->getStartPoint().y);
|
||||
dirVec = Base::Vector3d(0.f,0.f,-1.0) % (arcSeg->getStartPoint()-arcSeg->getCenter());
|
||||
}
|
||||
else {
|
||||
EditCurve[0] = Base::Vector2D(arcSeg->getEndPoint().x,arcSeg->getEndPoint().y);
|
||||
dirVec = Base::Vector3d(0.f,0.f,1.0) % (arcSeg->getEndPoint()-arcSeg->getCenter());
|
||||
}
|
||||
}
|
||||
dirVec.Normalize();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -916,10 +1046,10 @@ public:
|
|||
|
||||
// Display radius and start angle
|
||||
float radius = (onSketchPos - EditCurve[0]).Length();
|
||||
float angle = atan2f(dy_ , dx_) * 180 / M_PI;
|
||||
float angle = atan2f(dy_ , dx_);
|
||||
|
||||
char buf[40];
|
||||
sprintf( buf, " (R%.1f,%.1f)", radius, angle);
|
||||
sprintf(buf, " (%.1fR,%.1f°)", radius, angle * 180 / M_PI);
|
||||
std::string text = buf;
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
|
@ -941,11 +1071,11 @@ public:
|
|||
EditCurve[i] = Base::Vector2D(CenterPoint.fX + dx, CenterPoint.fY + dy);
|
||||
}
|
||||
|
||||
// Display radius and end angle
|
||||
// Display radius and arc angle
|
||||
float radius = (onSketchPos - EditCurve[0]).Length();
|
||||
|
||||
char buf[40];
|
||||
sprintf( buf, " (R%.1f,%.1f)", radius, arcAngle * 180 / M_PI);
|
||||
sprintf(buf, " (%.1fR,%.1f°)", radius, arcAngle * 180 / M_PI);
|
||||
std::string text = buf;
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
|
@ -1155,7 +1285,7 @@ public:
|
|||
float radius = (onSketchPos - EditCurve[0]).Length();
|
||||
|
||||
char buf[40];
|
||||
sprintf( buf, "R%.1f", radius);
|
||||
sprintf(buf, " (%.1fR)", radius);
|
||||
std::string text = buf;
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
|
|
|
@ -1005,55 +1005,55 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2D &toPo
|
|||
draw(true);
|
||||
}
|
||||
|
||||
bool ViewProviderSketch::isConstraintAtPosition(const Base::Vector3d &constrPos, const SoNode *constraint)
|
||||
Base::Vector3d ViewProviderSketch::seekConstraintPosition(const Base::Vector3d &origPos,
|
||||
const Base::Vector3d &norm,
|
||||
const Base::Vector3d &dir, float step,
|
||||
const SoNode *constraint)
|
||||
{
|
||||
assert(edit);
|
||||
Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView();
|
||||
Gui::View3DInventorViewer *viewer = static_cast<Gui::View3DInventor *>(mdi)->getViewer();
|
||||
|
||||
SoRayPickAction rp(viewer->getViewportRegion());
|
||||
rp.setRadius(0.1f);
|
||||
rp.setPickAll(true);
|
||||
rp.setRay(SbVec3f(constrPos.x, constrPos.y, -1.f), SbVec3f(0, 0, 1) );
|
||||
//problem
|
||||
rp.apply(edit->constrGroup); // We could narrow it down to just the SoGroup containing the constraints
|
||||
|
||||
// returns a copy of the point
|
||||
SoPickedPoint *pp = rp.getPickedPoint();
|
||||
const SoPickedPointList ppl = rp.getPickedPointList();
|
||||
float scaled_step = step * getScaleFactor();
|
||||
|
||||
if(ppl.getLength() > 1)
|
||||
return true;
|
||||
if (pp) {
|
||||
SoPath *path = pp->getPath();
|
||||
int length = path->getLength();
|
||||
SoNode *tailFather1 = path->getNode(length-2);
|
||||
SoNode *tailFather2 = path->getNode(length-3);
|
||||
|
||||
// checking if a constraint is the same as the one selected
|
||||
if (tailFather1 == constraint || tailFather2 == constraint) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Base::Vector3d ViewProviderSketch::seekConstraintPosition(const Base::Vector3d &suggestedPos,
|
||||
const Base::Vector3d &dir, float step,
|
||||
const SoNode *constraint)
|
||||
{
|
||||
int multiplier = 0;
|
||||
Base::Vector3d freePos;
|
||||
do {
|
||||
Base::Vector3d relPos, freePos;
|
||||
bool isConstraintAtPosition = true;
|
||||
while (isConstraintAtPosition && multiplier < 10) {
|
||||
// Calculate new position of constraint
|
||||
freePos = suggestedPos + (dir * (multiplier * step));
|
||||
multiplier++; // Increment the multiplier
|
||||
relPos = norm * 0.5f + dir * multiplier;
|
||||
freePos = origPos + relPos * scaled_step;
|
||||
|
||||
rp.setRadius(0.1f);
|
||||
rp.setPickAll(true);
|
||||
rp.setRay(SbVec3f(freePos.x, freePos.y, -1.f), SbVec3f(0, 0, 1) );
|
||||
//problem
|
||||
rp.apply(edit->constrGroup); // We could narrow it down to just the SoGroup containing the constraints
|
||||
|
||||
// returns a copy of the point
|
||||
SoPickedPoint *pp = rp.getPickedPoint();
|
||||
const SoPickedPointList ppl = rp.getPickedPointList();
|
||||
|
||||
if (ppl.getLength() <= 1 && pp) {
|
||||
SoPath *path = pp->getPath();
|
||||
int length = path->getLength();
|
||||
SoNode *tailFather1 = path->getNode(length-2);
|
||||
SoNode *tailFather2 = path->getNode(length-3);
|
||||
|
||||
// checking if a constraint is the same as the one selected
|
||||
if (tailFather1 == constraint || tailFather2 == constraint)
|
||||
isConstraintAtPosition = false;
|
||||
} else
|
||||
isConstraintAtPosition = false;
|
||||
|
||||
multiplier *= -1; // search in both sides
|
||||
if (multiplier >= 0)
|
||||
multiplier++; // Increment the multiplier
|
||||
}
|
||||
while (isConstraintAtPosition(freePos, constraint));
|
||||
return freePos;
|
||||
if (multiplier == 10)
|
||||
relPos = norm * 0.5f; // no free position found
|
||||
return relPos * step;
|
||||
}
|
||||
|
||||
bool ViewProviderSketch::isSelectable(void) const
|
||||
|
@ -1716,7 +1716,7 @@ void ViewProviderSketch::draw(bool temp)
|
|||
Points.push_back(start);
|
||||
Points.push_back(end);
|
||||
}
|
||||
else if ((*it)->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) { // add a circle
|
||||
else if ((*it)->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) { // add a bspline
|
||||
const Part::GeomBSplineCurve *spline = dynamic_cast<const Part::GeomBSplineCurve *>(*it);
|
||||
Handle_Geom_BSplineCurve curve = Handle_Geom_BSplineCurve::DownCast(spline->handle());
|
||||
|
||||
|
@ -1842,19 +1842,12 @@ Restart:
|
|||
Base::Vector3d dir = (lineSeg->getEndPoint()-lineSeg->getStartPoint()).Normalize();
|
||||
Base::Vector3d norm(-dir.y,dir.x,0);
|
||||
|
||||
float scale = dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->getScaleFactor();
|
||||
Base::Vector3d constrPos = midpos + (norm * 2.5 * scale);
|
||||
|
||||
constrPos = seekConstraintPosition(constrPos, dir, 2.5 * scale, edit->constrGroup->getChild(i));
|
||||
|
||||
// Translate the Icon based on calculated position
|
||||
Base::Vector3d relPos = constrPos - midpos; // Relative Position of Icons to Midpoint
|
||||
relPos = relPos / scale; // Must Divide by Scale Factor
|
||||
Base::Vector3d relpos = seekConstraintPosition(midpos, norm, dir, 2.5, edit->constrGroup->getChild(i));
|
||||
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->abPos = SbVec3f(midpos.x, midpos.y, zConstr); //Absolute Reference
|
||||
|
||||
//Reference Position that is scaled according to zoom
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->translation = SbVec3f(relPos.x, relPos.y, 0);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->translation = SbVec3f(relpos.x, relpos.y, 0);
|
||||
|
||||
}
|
||||
break;
|
||||
|
@ -1921,25 +1914,16 @@ Restart:
|
|||
dir1 = Base::Vector3d(1,0,0);
|
||||
}
|
||||
|
||||
// Get Current Scale Factor
|
||||
float scale = dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->getScaleFactor();
|
||||
|
||||
Base::Vector3d constrPos1 = midpos1 + (norm1 * 2.5 * scale);
|
||||
constrPos1 = seekConstraintPosition(constrPos1, dir1, scale * 2.5, edit->constrGroup->getChild(i));
|
||||
|
||||
// Translate the Icon based on calculated position
|
||||
Base::Vector3d relPos1 = (constrPos1 - midpos1) / scale ; // Relative Position of Icons to Midpoint1
|
||||
Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 2.5, edit->constrGroup->getChild(i));
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->translation = SbVec3f(relPos1.x, relPos1.y, 0);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->translation = SbVec3f(relpos1.x, relpos1.y, 0);
|
||||
|
||||
if (Constr->FirstPos == Sketcher::none) {
|
||||
Base::Vector3d constrPos2 = midpos2 + (norm2 * 2.5 * scale);
|
||||
constrPos2 = seekConstraintPosition(constrPos2, dir2, 2.5 * scale, edit->constrGroup->getChild(i));
|
||||
Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 2.5, edit->constrGroup->getChild(i));
|
||||
|
||||
Base::Vector3d relPos2 = (constrPos2 - midpos2) / scale ; // Relative Position of Icons to Midpoint2
|
||||
Base::Vector3d secondPos = midpos2 - midpos1;
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->abPos = SbVec3f(secondPos.x, secondPos.y, zConstr);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->translation = SbVec3f(relPos2.x -relPos1.x, relPos2.y -relPos1.y, 0);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2012,32 +1996,19 @@ Restart:
|
|||
norm2 = Base::Vector3d(-dir2.y,dir2.x,0.);
|
||||
}
|
||||
|
||||
// Get Current Scale Factor
|
||||
float scale = dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->getScaleFactor();
|
||||
|
||||
Base::Vector3d constrPos1 = midpos1 + (norm1 * 2.5 * scale);
|
||||
constrPos1 = seekConstraintPosition(constrPos1, dir1, scale * 2.5, edit->constrGroup->getChild(i));
|
||||
|
||||
Base::Vector3d constrPos2 = midpos2 + (norm2 * 2.5 * scale);
|
||||
constrPos2 = seekConstraintPosition(constrPos2, dir2, 2.5 * scale, edit->constrGroup->getChild(i));
|
||||
|
||||
// Translate the Icon based on calculated position
|
||||
Base::Vector3d relPos1 = constrPos1 - midpos1 ; // Relative Position of Icons to Midpoint1
|
||||
Base::Vector3d relPos2 = constrPos2 - midpos2 ; // Relative Position of Icons to Midpoint2
|
||||
|
||||
relPos1 = relPos1 / scale;
|
||||
relPos2 = relPos2 / scale;
|
||||
Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 2.5, edit->constrGroup->getChild(i));
|
||||
Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 2.5, edit->constrGroup->getChild(i));
|
||||
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr); //Absolute Reference
|
||||
|
||||
//Reference Position that is scaled according to zoom
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->translation = SbVec3f(relPos1.x, relPos1.y, 0);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->translation = SbVec3f(relpos1.x, relpos1.y, 0);
|
||||
|
||||
Base::Vector3d secondPos = midpos2 - midpos1;
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->abPos = SbVec3f(secondPos.x, secondPos.y, zConstr); //Absolute Reference
|
||||
|
||||
//Reference Position that is scaled according to zoom
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->translation = SbVec3f(relPos2.x -relPos1.x, relPos2.y -relPos1.y, 0);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->translation = SbVec3f(relpos2.x - relpos1.x, relpos2.y -relpos1.y, 0);
|
||||
|
||||
}
|
||||
break;
|
||||
|
@ -2148,32 +2119,19 @@ Restart:
|
|||
Base::Vector3d norm1 = Base::Vector3d(-dir1.y,dir1.x,0.f);
|
||||
Base::Vector3d norm2 = Base::Vector3d(-dir2.y,dir2.x,0.f);
|
||||
|
||||
// Get Current Scale Factor
|
||||
float scale = dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->getScaleFactor();
|
||||
|
||||
Base::Vector3d constrPos1 = midpos1 + (norm1 * 2.5 * scale);
|
||||
constrPos1 = seekConstraintPosition(constrPos1, dir1, 2.5 * scale, edit->constrGroup->getChild(i));
|
||||
|
||||
Base::Vector3d constrPos2 = midpos2 + (norm2 * 2.5 * scale);
|
||||
constrPos2 = seekConstraintPosition(constrPos2, dir2, 2.5 * scale, edit->constrGroup->getChild(i));
|
||||
|
||||
// Translate the Icon based on calculated position
|
||||
Base::Vector3d relPos1 = constrPos1 - midpos1 ; // Relative Position of Icons to Midpoint1
|
||||
Base::Vector3d relPos2 = constrPos2 - midpos2 ; // Relative Position of Icons to Midpoint2
|
||||
|
||||
relPos1 = relPos1 / scale;
|
||||
relPos2 = relPos2 / scale;
|
||||
Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 2.5, edit->constrGroup->getChild(i));
|
||||
Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 2.5, edit->constrGroup->getChild(i));
|
||||
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr); //Absolute Reference
|
||||
|
||||
//Reference Position that is scaled according to zoom
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->translation = SbVec3f(relPos1.x, relPos1.y, 0);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(1))->translation = SbVec3f(relpos1.x, relpos1.y, 0);
|
||||
|
||||
Base::Vector3d secondPos = midpos2 - midpos1;
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->abPos = SbVec3f(secondPos.x, secondPos.y, zConstr); //Absolute Reference
|
||||
|
||||
//Reference Position that is scaled according to zoom
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->translation = SbVec3f(relPos2.x -relPos1.x, relPos2.y -relPos1.y, 0);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(3))->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -2876,7 +2834,7 @@ void ViewProviderSketch::setPositionText(const Base::Vector2D &Pos, const std::s
|
|||
void ViewProviderSketch::setPositionText(const Base::Vector2D &Pos)
|
||||
{
|
||||
char buf[40];
|
||||
sprintf( buf, " (%.1f,%.1f)", Pos.fX,Pos.fY );
|
||||
sprintf(buf, " (%.1f,%.1f)", Pos.fX, Pos.fY);
|
||||
edit->textX->string = buf;
|
||||
edit->textPos->translation = SbVec3f(Pos.fX,Pos.fY,zText);
|
||||
}
|
||||
|
|
|
@ -137,10 +137,9 @@ public:
|
|||
|
||||
/// moves a selected constraint
|
||||
void moveConstraint(int constNum, const Base::Vector2D &toPos);
|
||||
/// checks if there is a constraint object at position vector
|
||||
bool isConstraintAtPosition(const Base::Vector3d &constrPos, const SoNode *constraint);
|
||||
/// finds a free position for placing a constraint icon
|
||||
Base::Vector3d seekConstraintPosition(const Base::Vector3d &suggestedPos,
|
||||
Base::Vector3d seekConstraintPosition(const Base::Vector3d &origPos,
|
||||
const Base::Vector3d &norm,
|
||||
const Base::Vector3d &dir, float step,
|
||||
const SoNode *constraint);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user