Sketcher: Fix: Arc of ellipse when first and second X coordinates are the same (#27327)

This commit is contained in:
Loke Strøm
2026-02-13 18:57:27 +01:00
committed by GitHub
parent ba030237b7
commit 5d81f8ac16

View File

@@ -50,26 +50,23 @@ class DrawSketchHandlerArcOfEllipse: public DrawSketchHandler
public:
DrawSketchHandlerArcOfEllipse()
: Mode(STATUS_SEEK_First)
: Mode(SelectMode::First)
, EditCurve(34)
, rx(0)
, ry(0)
, startAngle(0)
, endAngle(0)
, arcAngle(0)
, arcAngle_t(0)
{}
~DrawSketchHandlerArcOfEllipse() override = default;
/// mode table
enum SelectMode
enum class SelectMode
{
STATUS_SEEK_First,
STATUS_SEEK_Second,
STATUS_SEEK_Third,
STATUS_SEEK_Fourth,
STATUS_Close
First,
Second,
Third,
Fourth,
End
};
void mouseMove(SnapManager::SnapHandle snapHandle) override
@@ -77,13 +74,13 @@ public:
using std::numbers::pi;
Base::Vector2d onSketchPos = snapHandle.compute();
if (Mode == STATUS_SEEK_First) {
if (Mode == SelectMode::First) {
setPositionText(onSketchPos);
seekAndRenderAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f, 0.f)); // TODO:
// ellipse
// prio 1
}
else if (Mode == STATUS_SEEK_Second) {
else if (Mode == SelectMode::Second) {
double rx0 = onSketchPos.x - EditCurve[0].x;
double ry0 = onSketchPos.y - EditCurve[0].y;
for (int i = 0; i < 16; i++) {
@@ -113,23 +110,33 @@ public:
AutoConstraint::CURVE
);
}
else if (Mode == STATUS_SEEK_Third) {
// angle between the major axis of the ellipse and the X axis
double a = (EditCurve[1] - EditCurve[0]).Length();
double phi = atan2(EditCurve[1].y - EditCurve[0].y, EditCurve[1].x - EditCurve[0].x);
else if (Mode == SelectMode::Third) {
Base::Vector2d delta12 = axisPoint - centerPoint;
// This is the angle at cursor point
double angleatpoint = acos(
(onSketchPos.x - EditCurve[0].x + (onSketchPos.y - EditCurve[0].y) * tan(phi))
/ (a * (cos(phi) + tan(phi) * sin(phi)))
double a = delta12.Length();
Base::Vector2d aDir = delta12.Normalize();
Base::Vector2d bDir(-aDir.y, aDir.x);
Base::Vector2d delta13 = onSketchPos - centerPoint;
Base::Vector2d delta13Prime(
delta13.x * aDir.x + delta13.y * aDir.y,
delta13.x * bDir.x + delta13.y * bDir.y
);
double b = (onSketchPos.y - EditCurve[0].y - a * cos(angleatpoint) * sin(phi))
/ (sin(angleatpoint) * cos(phi));
double cosT = max(-1.0, min(1.0, delta13Prime.x / a));
double sinT = sqrt(max(0.0, 1 - cosT * cosT));
double b = abs(delta13Prime.y) / sinT;
if (sinT == 0.0) {
b = 0.0;
a = 0.0;
}
for (int i = 1; i < 16; i++) {
double angle = i * pi / 16.0;
double rx1 = a * cos(angle) * cos(phi) - b * sin(angle) * sin(phi);
double ry1 = a * cos(angle) * sin(phi) + b * sin(angle) * cos(phi);
double rx1 = a * cos(angle) * aDir.x + b * sin(angle) * bDir.x;
double ry1 = a * cos(angle) * aDir.y + b * sin(angle) * bDir.y;
EditCurve[1 + i] = Base::Vector2d(EditCurve[0].x + rx1, EditCurve[0].y + ry1);
EditCurve[17 + i] = Base::Vector2d(EditCurve[0].x - rx1, EditCurve[0].y - ry1);
}
@@ -148,49 +155,43 @@ public:
drawEdit(EditCurve);
seekAndRenderAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f, 0.f));
}
else if (Mode == STATUS_SEEK_Fourth) { // here we differ from ellipse creation
// angle between the major axis of the ellipse and the X axis
double a = (axisPoint - centerPoint).Length();
double phi = atan2(axisPoint.y - centerPoint.y, axisPoint.x - centerPoint.x);
else if (Mode == SelectMode::Fourth) { // here we differ from ellipse creation
Base::Vector2d delta12 = axisPoint - centerPoint;
// This is the angle at cursor point
double angleatpoint = acos(
(startingPoint.x - centerPoint.x + (startingPoint.y - centerPoint.y) * tan(phi))
/ (a * (cos(phi) + tan(phi) * sin(phi)))
);
double b = abs(
(startingPoint.y - centerPoint.y - a * cos(angleatpoint) * sin(phi))
/ (sin(angleatpoint) * cos(phi))
double a = delta12.Length();
Base::Vector2d aDir = delta12.Normalize();
Base::Vector2d bDir(-aDir.y, aDir.x);
Base::Vector2d delta13 = startingPoint - centerPoint;
Base::Vector2d delta13Prime(
delta13.x * aDir.x + delta13.y * aDir.y,
delta13.x * bDir.x + delta13.y * bDir.y
);
double rxs = startingPoint.x - centerPoint.x;
double rys = startingPoint.y - centerPoint.y;
startAngle = atan2(
a * (rys * cos(phi) - rxs * sin(phi)),
b * (rxs * cos(phi) + rys * sin(phi))
); // eccentric anomaly angle
double cosT = max(-1.0, min(1.0, delta13Prime.x / a));
double sinT = sqrt(max(0.0, 1 - cosT * cosT));
double angle1 = atan2(
a
* ((onSketchPos.y - centerPoint.y) * cos(phi)
- (onSketchPos.x - centerPoint.x) * sin(phi)),
b
* ((onSketchPos.x - centerPoint.x) * cos(phi)
+ (onSketchPos.y - centerPoint.y) * sin(phi))
)
- startAngle;
double b = abs(delta13Prime.y) / sinT;
startAngle = atan2(delta13Prime.y / b, delta13Prime.x / a);
Base::Vector2d delta14 = onSketchPos - centerPoint;
Base::Vector2d delta14Prime(
delta14.x * aDir.x + delta14.y * aDir.y,
delta14.x * bDir.x + delta14.y * bDir.y
);
double angle1 = atan2(delta14Prime.y / b, delta14Prime.x / a) - startAngle;
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * pi;
arcAngle = abs(angle1 - arcAngle) < abs(angle2 - arcAngle) ? angle1 : angle2;
for (int i = 0; i < 34; i++) {
double angle = startAngle + i * arcAngle / 34.0;
double rx1 = a * cos(angle) * cos(phi) - b * sin(angle) * sin(phi);
double ry1 = a * cos(angle) * sin(phi) + b * sin(angle) * cos(phi);
double angle = startAngle + i * arcAngle / 33.0;
double rx1 = a * cos(angle) * aDir.x + b * sin(angle) * bDir.x;
double ry1 = a * cos(angle) * aDir.y + b * sin(angle) * bDir.y;
EditCurve[i] = Base::Vector2d(centerPoint.x + rx1, centerPoint.y + ry1);
}
// EditCurve[33] = EditCurve[1];
// EditCurve[17] = EditCurve[16];
// Display radii and angle for user
if (showCursorCoords()) {
@@ -202,35 +203,43 @@ public:
setPositionText(onSketchPos, text);
}
drawEdit(EditCurve);
if (onSketchPos != centerPoint) {
drawEdit(EditCurve);
}
else {
drawEdit(std::vector<Base::Vector2d>());
}
seekAndRenderAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f, 0.f));
}
}
bool pressButton(Base::Vector2d onSketchPos) override
{
if (Mode == STATUS_SEEK_First) {
using std::numbers::pi;
if (Mode == SelectMode::First) {
EditCurve[0] = onSketchPos;
centerPoint = onSketchPos;
setAngleSnapping(true, centerPoint);
Mode = STATUS_SEEK_Second;
Mode = SelectMode::Second;
}
else if (Mode == STATUS_SEEK_Second) {
else if (Mode == SelectMode::Second
&& (centerPoint - onSketchPos).Length() >= Precision::Confusion()) {
EditCurve[1] = onSketchPos;
axisPoint = onSketchPos;
Mode = STATUS_SEEK_Third;
Mode = SelectMode::Third;
}
else if (Mode == STATUS_SEEK_Third) {
else if (Mode == SelectMode::Third && validThirdPoint(onSketchPos)) {
startingPoint = onSketchPos;
arcAngle = 0.;
arcAngle_t = 0.;
Mode = STATUS_SEEK_Fourth;
Mode = SelectMode::Fourth;
}
else { // Fourth
else if (Mode == SelectMode::Fourth && centerPoint != onSketchPos && arcAngle != 0
&& abs(arcAngle) != 2 * pi) {
endPoint = onSketchPos;
setAngleSnapping(false);
Mode = STATUS_Close;
Mode = SelectMode::End;
}
updateHint();
@@ -243,35 +252,36 @@ public:
using std::numbers::pi;
if (Mode == STATUS_Close) {
if (Mode == SelectMode::End) {
unsetCursor();
resetPositionText();
// angle between the major axis of the ellipse and the X axisEllipse
double a = (axisPoint - centerPoint).Length();
double phi = atan2(axisPoint.y - centerPoint.y, axisPoint.x - centerPoint.x);
Base::Vector2d delta12 = axisPoint - centerPoint;
// This is the angle at cursor point
double angleatpoint = acos(
(startingPoint.x - centerPoint.x + (startingPoint.y - centerPoint.y) * tan(phi))
/ (a * (cos(phi) + tan(phi) * sin(phi)))
);
double b = abs(
(startingPoint.y - centerPoint.y - a * cos(angleatpoint) * sin(phi))
/ (sin(angleatpoint) * cos(phi))
double a = delta12.Length();
Base::Vector2d aDir = delta12.Normalize();
Base::Vector2d bDir(-aDir.y, aDir.x);
Base::Vector2d delta13 = startingPoint - centerPoint;
Base::Vector2d delta13Prime(
delta13.x * aDir.x + delta13.y * aDir.y,
delta13.x * bDir.x + delta13.y * bDir.y
);
double angle1 = atan2(
a
* ((endPoint.y - centerPoint.y) * cos(phi)
- (endPoint.x - centerPoint.x) * sin(phi)),
b
* ((endPoint.x - centerPoint.x) * cos(phi)
+ (endPoint.y - centerPoint.y) * sin(phi))
)
- startAngle;
double cosT = max(-1.0, min(1.0, delta13Prime.x / a));
double sinT = sqrt(max(0.0, 1 - cosT * cosT));
double b = abs(delta13Prime.y) / sinT;
Base::Vector2d delta14 = endPoint - centerPoint;
Base::Vector2d delta14Prime(
delta14.x * aDir.x + delta14.y * aDir.y,
delta14.x * bDir.x + delta14.y * bDir.y
);
double angle1 = atan2(delta14Prime.y / b, delta14Prime.x / a) - startAngle;
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * pi;
arcAngle = abs(angle1 - arcAngle) < abs(angle2 - arcAngle) ? angle1 : angle2;
bool isOriginalArcCCW = true;
@@ -391,7 +401,7 @@ public:
bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true);
if (continuousMode) {
// This code enables the continuous creation mode.
Mode = STATUS_SEEK_First;
Mode = SelectMode::First;
EditCurve.clear();
drawEdit(EditCurve);
EditCurve.resize(34);
@@ -422,7 +432,7 @@ protected:
SelectMode Mode;
std::vector<Base::Vector2d> EditCurve;
Base::Vector2d centerPoint, axisPoint, startingPoint, endPoint;
double rx, ry, startAngle, endAngle, arcAngle, arcAngle_t;
double startAngle, endAngle, arcAngle;
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3, sugConstr4;
private:
@@ -433,28 +443,47 @@ private:
return Gui::lookupHints<SelectMode>(
Mode,
{
{.state = STATUS_SEEK_First,
{.state = SelectMode::First,
.hints =
{
{tr("%1 pick ellipse center"), {MouseLeft}},
}},
{.state = STATUS_SEEK_Second,
{.state = SelectMode::Second,
.hints =
{
{tr("%1 pick axis point"), {MouseLeft}},
}},
{.state = STATUS_SEEK_Third,
{.state = SelectMode::Third,
.hints =
{
{tr("%1 pick arc start point"), {MouseLeft}},
}},
{.state = STATUS_SEEK_Fourth,
{.state = SelectMode::Fourth,
.hints =
{
{tr("%1 pick arc end point"), {MouseLeft}},
}},
});
}
bool validThirdPoint(Base::Vector2d onSketchPos)
{
Base::Vector2d delta12 = axisPoint - centerPoint;
double a = delta12.Length();
Base::Vector2d aDir = delta12.Normalize();
Base::Vector2d bDir(-aDir.y, aDir.x);
Base::Vector2d delta13 = onSketchPos - centerPoint;
Base::Vector2d delta13Prime(
delta13.x * aDir.x + delta13.y * aDir.y,
delta13.x * bDir.x + delta13.y * bDir.y
);
double cosT = max(-1.0, min(1.0, delta13Prime.x / a));
return cosT != -1.0 && cosT != 1.0 && delta13Prime.y != 0;
}
};
} // namespace SketcherGui