Add "Show Exploded View" menu option

Where each entity in the active workplane sketch is projected a
different amount normal to the workplane, to allow inspection and
easier selection of entities that entirely overlap each other and are
thus otherwise difficult to see or select.

The distance between the exploded "layers" can be controlled in the
configuration page. Negative distances mean the layers are projected in
the opposite direction, relative to the workplane normal.
This commit is contained in:
Tom Sutcliffe 2021-04-04 14:14:37 +01:00 committed by ruevs
parent 3e595002fe
commit 5edb2eebf6
10 changed files with 200 additions and 38 deletions

View File

@ -68,6 +68,11 @@ void TextWindow::ScreenChangeGridSpacing(int link, uint32_t v) {
SS.TW.edit.meaning = Edit::GRID_SPACING; SS.TW.edit.meaning = Edit::GRID_SPACING;
} }
void TextWindow::ScreenChangeExplodeDistance(int link, uint32_t v) {
SS.TW.ShowEditControl(3, SS.MmToString(SS.explodeDistance, true));
SS.TW.edit.meaning = Edit::EXPLODE_DISTANCE;
}
void TextWindow::ScreenChangeDigitsAfterDecimal(int link, uint32_t v) { void TextWindow::ScreenChangeDigitsAfterDecimal(int link, uint32_t v) {
SS.TW.ShowEditControl(14, ssprintf("%d", SS.UnitDigitsAfterDecimal())); SS.TW.ShowEditControl(14, ssprintf("%d", SS.UnitDigitsAfterDecimal()));
SS.TW.edit.meaning = Edit::DIGITS_AFTER_DECIMAL; SS.TW.edit.meaning = Edit::DIGITS_AFTER_DECIMAL;
@ -269,6 +274,10 @@ void TextWindow::ShowConfiguration() {
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E", Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
SS.MmToString(SS.gridSpacing).c_str(), SS.MmToString(SS.gridSpacing).c_str(),
&ScreenChangeGridSpacing, 0); &ScreenChangeGridSpacing, 0);
Printf(false, "%Ft explode distance%E");
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
SS.MmToString(SS.explodeDistance).c_str(),
&ScreenChangeExplodeDistance, 0);
Printf(false, ""); Printf(false, "");
Printf(false, "%Ft digits after decimal point to show%E"); Printf(false, "%Ft digits after decimal point to show%E");
@ -459,6 +468,11 @@ bool TextWindow::EditControlDoneForConfiguration(const std::string &s) {
SS.GW.Invalidate(); SS.GW.Invalidate();
break; break;
} }
case Edit::EXPLODE_DISTANCE: {
SS.explodeDistance = min(1e4, max(-1e4, SS.StringToMm(s)));
SS.MarkGroupDirty(SS.GW.activeGroup, true);
break;
}
case Edit::DIGITS_AFTER_DECIMAL: { case Edit::DIGITS_AFTER_DECIMAL: {
int v = atoi(s.c_str()); int v = atoi(s.c_str());
if(v < 0 || v > 8) { if(v < 0 || v > 8) {

View File

@ -267,7 +267,7 @@ void Constraint::DoEqualRadiusTicks(Canvas *canvas, Canvas::hStroke hcs,
const Camera &camera = canvas->GetCamera(); const Camera &camera = canvas->GetCamera();
Entity *circ = SK.GetEntity(he); Entity *circ = SK.GetEntity(he);
Vector center = SK.GetEntity(circ->point[0])->PointGetNum(); Vector center = SK.GetEntity(circ->point[0])->PointGetDrawNum();
double r = circ->CircleGetRadiusNum(); double r = circ->CircleGetRadiusNum();
Quaternion q = circ->Normal()->NormalGetNum(); Quaternion q = circ->Normal()->NormalGetNum();
Vector u = q.RotationU(), v = q.RotationV(); Vector u = q.RotationU(), v = q.RotationV();
@ -291,7 +291,8 @@ void Constraint::DoEqualRadiusTicks(Canvas *canvas, Canvas::hStroke hcs,
void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs, void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
Vector a0, Vector da, Vector b0, Vector db, Vector a0, Vector da, Vector b0, Vector db,
Vector offset, Vector *ref, bool trim) Vector offset, Vector *ref, bool trim,
Vector explodeOffset)
{ {
const Camera &camera = canvas->GetCamera(); const Camera &camera = canvas->GetCamera();
double pixels = 1.0 / camera.scale; double pixels = 1.0 / camera.scale;
@ -305,6 +306,9 @@ void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
db = db.ProjectVectorInto(workplane); db = db.ProjectVectorInto(workplane);
} }
a0 = a0.Plus(explodeOffset);
b0 = b0.Plus(explodeOffset);
Vector a1 = a0.Plus(da); Vector a1 = a0.Plus(da);
Vector b1 = b0.Plus(db); Vector b1 = b0.Plus(db);
@ -534,6 +538,15 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
DoProjectedPoint(canvas, hcs, &bp); DoProjectedPoint(canvas, hcs, &bp);
} }
if(ShouldDrawExploded()) {
// Offset A and B by the same offset so the constraint is drawn
// in the plane of one of the exploded points (rather than at an
// angle)
Vector offset = SK.GetEntity(ptA)->ExplodeOffset();
ap = ap.Plus(offset);
bp = bp.Plus(offset);
}
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
if(refs) refs->push_back(ref); if(refs) refs->push_back(ref);
@ -548,6 +561,19 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
dp = (bp.Minus(ap)), dp = (bp.Minus(ap)),
pp = SK.GetEntity(entityA)->VectorGetNum(); pp = SK.GetEntity(entityA)->VectorGetNum();
if(ShouldDrawExploded()) {
// explode for whichever point is in the workplane (or the first if both are)
Entity *pt = SK.GetEntity(ptA);
if(pt->group != group) {
pt = SK.GetEntity(ptB);
}
if(pt->group == group) {
Vector offset = pt->ExplodeOffset();
ap = ap.Plus(offset);
bp = bp.Plus(offset);
}
}
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
if(refs) refs->push_back(ref); if(refs) refs->push_back(ref);
@ -564,7 +590,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
case Type::PT_FACE_DISTANCE: case Type::PT_FACE_DISTANCE:
case Type::PT_PLANE_DISTANCE: { case Type::PT_PLANE_DISTANCE: {
Vector pt = SK.GetEntity(ptA)->PointGetNum(); Vector pt = SK.GetEntity(ptA)->PointGetDrawNum();
Entity *enta = SK.GetEntity(entityA); Entity *enta = SK.GetEntity(entityA);
Vector n, p; Vector n, p;
if(type == Type::PT_PLANE_DISTANCE) { if(type == Type::PT_PLANE_DISTANCE) {
@ -590,7 +616,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
} }
case Type::PT_LINE_DISTANCE: { case Type::PT_LINE_DISTANCE: {
Vector pt = SK.GetEntity(ptA)->PointGetNum(); Entity *ptEntity = SK.GetEntity(ptA);
Vector pt = ptEntity->PointGetNum();
Entity *line = SK.GetEntity(entityA); Entity *line = SK.GetEntity(entityA);
Vector lA = SK.GetEntity(line->point[0])->PointGetNum(); Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
Vector lB = SK.GetEntity(line->point[1])->PointGetNum(); Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
@ -602,6 +629,19 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
DoProjectedPoint(canvas, hcs, &pt); DoProjectedPoint(canvas, hcs, &pt);
} }
// Only explode if the point and line are in the same group (and that group is a sketch
// with explode enabled) otherwise it's too visually confusing to figure out what the
// correct projections should be.
bool shouldExplode = ShouldDrawExploded()
&& ptEntity->group == group
&& line->group == group;
if(shouldExplode) {
Vector explodeOffset = ptEntity->ExplodeOffset();
pt = pt.Plus(explodeOffset);
lA = lA.Plus(explodeOffset);
lB = lB.Plus(explodeOffset);
}
// Find the closest point on the line // Find the closest point on the line
Vector closest = pt.ClosestPointOnLine(lA, dl); Vector closest = pt.ClosestPointOnLine(lA, dl);
@ -655,7 +695,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
case Type::DIAMETER: { case Type::DIAMETER: {
Entity *circle = SK.GetEntity(entityA); Entity *circle = SK.GetEntity(entityA);
Vector center = SK.GetEntity(circle->point[0])->PointGetNum(); Vector center = SK.GetEntity(circle->point[0])->PointGetDrawNum();
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum(); Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
Vector n = q.RotationN().WithMagnitude(1); Vector n = q.RotationN().WithMagnitude(1);
double r = circle->CircleGetRadiusNum(); double r = circle->CircleGetRadiusNum();
@ -697,7 +737,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector r = camera.projRight.ScaledBy((a+1)/camera.scale); Vector r = camera.projRight.ScaledBy((a+1)/camera.scale);
Vector d = camera.projUp.ScaledBy((2-a)/camera.scale); Vector d = camera.projUp.ScaledBy((2-a)/camera.scale);
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
Vector p = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum(); Vector p = SK.GetEntity(i == 0 ? ptA : ptB)->PointGetDrawNum();
if(refs) refs->push_back(p); if(refs) refs->push_back(p);
canvas->DrawQuad(p.Plus (r).Plus (d), canvas->DrawQuad(p.Plus (r).Plus (d),
p.Plus (r).Minus(d), p.Plus (r).Minus(d),
@ -715,7 +755,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
case Type::PT_ON_FACE: case Type::PT_ON_FACE:
case Type::PT_IN_PLANE: { case Type::PT_IN_PLANE: {
double s = 8/camera.scale; double s = 8/camera.scale;
Vector p = SK.GetEntity(ptA)->PointGetNum(); Vector p = SK.GetEntity(ptA)->PointGetDrawNum();
if(refs) refs->push_back(p); if(refs) refs->push_back(p);
Vector r, d; Vector r, d;
if(type == Type::PT_ON_FACE) { if(type == Type::PT_ON_FACE) {
@ -740,7 +780,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
} }
case Type::WHERE_DRAGGED: { case Type::WHERE_DRAGGED: {
Vector p = SK.GetEntity(ptA)->PointGetNum(); Vector p = SK.GetEntity(ptA)->PointGetDrawNum();
if(refs) refs->push_back(p); if(refs) refs->push_back(p);
Vector u = p.Plus(gu.WithMagnitude(8/camera.scale)).Plus( Vector u = p.Plus(gu.WithMagnitude(8/camera.scale)).Plus(
gr.WithMagnitude(8/camera.scale)), gr.WithMagnitude(8/camera.scale)),
@ -797,10 +837,10 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
} }
DoArcForAngle(canvas, hcs, a0, da, b0, db, DoArcForAngle(canvas, hcs, a0, da, b0, db,
da.WithMagnitude(40/camera.scale), &ref, /*trim=*/false); da.WithMagnitude(40/camera.scale), &ref, /*trim=*/false, a->ExplodeOffset());
if(refs) refs->push_back(ref); if(refs) refs->push_back(ref);
DoArcForAngle(canvas, hcs, c0, dc, d0, dd, DoArcForAngle(canvas, hcs, c0, dc, d0, dd,
dc.WithMagnitude(40/camera.scale), &ref, /*trim=*/false); dc.WithMagnitude(40/camera.scale), &ref, /*trim=*/false, c->ExplodeOffset());
if(refs) refs->push_back(ref); if(refs) refs->push_back(ref);
return; return;
@ -820,7 +860,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
} }
Vector ref; Vector ref;
DoArcForAngle(canvas, hcs, a0, da, b0, db, disp.offset, &ref, /*trim=*/true); DoArcForAngle(canvas, hcs, a0, da, b0, db, disp.offset, &ref, /*trim=*/true, a->ExplodeOffset());
DoLabel(canvas, hcs, ref, labelPos, gr, gu); DoLabel(canvas, hcs, ref, labelPos, gr, gu);
if(refs) refs->push_back(ref); if(refs) refs->push_back(ref);
return; return;
@ -855,7 +895,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
if(u.Dot(ru) < 0) u = u.ScaledBy(-1); if(u.Dot(ru) < 0) u = u.ScaledBy(-1);
} }
Vector p = e->VectorGetRefPoint(); Vector p = e->VectorGetRefPoint().Plus(e->ExplodeOffset());
Vector s = p.Plus(u).Plus(v); Vector s = p.Plus(u).Plus(v);
DoLine(canvas, hcs, s, s.Plus(v)); DoLine(canvas, hcs, s, s.Plus(v));
Vector m = s.Plus(v.ScaledBy(0.5)); Vector m = s.Plus(v.ScaledBy(0.5));
@ -873,9 +913,9 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
if(type == Type::ARC_LINE_TANGENT) { if(type == Type::ARC_LINE_TANGENT) {
Entity *arc = SK.GetEntity(entityA); Entity *arc = SK.GetEntity(entityA);
Entity *norm = SK.GetEntity(arc->normal); Entity *norm = SK.GetEntity(arc->normal);
Vector c = SK.GetEntity(arc->point[0])->PointGetNum(); Vector c = SK.GetEntity(arc->point[0])->PointGetDrawNum();
Vector p = Vector p =
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetNum(); SK.GetEntity(arc->point[other ? 2 : 1])->PointGetDrawNum();
Vector r = p.Minus(c); Vector r = p.Minus(c);
textAt = p.Plus(r.WithMagnitude(14/camera.scale)); textAt = p.Plus(r.WithMagnitude(14/camera.scale));
u = norm->NormalU(); u = norm->NormalU();
@ -896,6 +936,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Entity *cubic = SK.GetEntity(entityA); Entity *cubic = SK.GetEntity(entityA);
Vector p = other ? cubic->CubicGetFinishNum() : Vector p = other ? cubic->CubicGetFinishNum() :
cubic->CubicGetStartNum(); cubic->CubicGetStartNum();
p = p.Plus(cubic->ExplodeOffset());
Vector dir = SK.GetEntity(entityB)->VectorGetNum(); Vector dir = SK.GetEntity(entityB)->VectorGetNum();
Vector out = n.Cross(dir); Vector out = n.Cross(dir);
textAt = p.Plus(out.WithMagnitude(14/camera.scale)); textAt = p.Plus(out.WithMagnitude(14/camera.scale));
@ -905,12 +946,12 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
u = wn->NormalU(); u = wn->NormalU();
v = wn->NormalV(); v = wn->NormalV();
n = wn->NormalN(); n = wn->NormalN();
EntityBase *eA = SK.GetEntity(entityA); Entity *eA = SK.GetEntity(entityA);
// Big pain; we have to get a vector tangent to the curve // Big pain; we have to get a vector tangent to the curve
// at the shared point, which could be from either a cubic // at the shared point, which could be from either a cubic
// or an arc. // or an arc.
if(other) { if(other) {
textAt = eA->EndpointFinish(); textAt = eA->EndpointFinish().Plus(eA->ExplodeOffset());
if(eA->type == Entity::Type::CUBIC) { if(eA->type == Entity::Type::CUBIC) {
dir = eA->CubicGetFinishTangentNum(); dir = eA->CubicGetFinishTangentNum();
} else { } else {
@ -919,7 +960,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
dir = n.Cross(dir); dir = n.Cross(dir);
} }
} else { } else {
textAt = eA->EndpointStart(); textAt = eA->EndpointStart().Plus(eA->ExplodeOffset());
if(eA->type == Entity::Type::CUBIC) { if(eA->type == Entity::Type::CUBIC) {
dir = eA->CubicGetStartTangentNum(); dir = eA->CubicGetStartTangentNum();
} else { } else {
@ -947,6 +988,10 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector u = (gn.Cross(n)).WithMagnitude(4/camera.scale); Vector u = (gn.Cross(n)).WithMagnitude(4/camera.scale);
Vector p = e->VectorGetRefPoint(); Vector p = e->VectorGetRefPoint();
if(ShouldDrawExploded()) {
p = p.Plus(e->ExplodeOffset());
}
DoLine(canvas, hcs, p.Plus(u), p.Plus(u).Plus(n)); DoLine(canvas, hcs, p.Plus(u), p.Plus(u).Plus(n));
DoLine(canvas, hcs, p.Minus(u), p.Minus(u).Plus(n)); DoLine(canvas, hcs, p.Minus(u), p.Minus(u).Plus(n));
if(refs) refs->push_back(p.Plus(n.ScaledBy(0.5))); if(refs) refs->push_back(p.Plus(n.ScaledBy(0.5)));
@ -967,8 +1012,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Entity *line = SK.GetEntity(entityA); Entity *line = SK.GetEntity(entityA);
Vector ref; Vector ref;
DoEqualLenTicks(canvas, hcs, DoEqualLenTicks(canvas, hcs,
SK.GetEntity(line->point[0])->PointGetNum(), SK.GetEntity(line->point[0])->PointGetDrawNum(),
SK.GetEntity(line->point[1])->PointGetNum(), SK.GetEntity(line->point[1])->PointGetDrawNum(),
gn, &ref); gn, &ref);
if(refs) refs->push_back(ref); if(refs) refs->push_back(ref);
DoEqualRadiusTicks(canvas, hcs, entityB, &ref); DoEqualRadiusTicks(canvas, hcs, entityB, &ref);
@ -990,6 +1035,12 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
DoProjectedPoint(canvas, hcs, &b); DoProjectedPoint(canvas, hcs, &b);
} }
if(ShouldDrawExploded()) {
Vector offset = e->ExplodeOffset();
a = a.Plus(offset);
b = b.Plus(offset);
}
Vector ref; Vector ref;
DoEqualLenTicks(canvas, hcs, a, b, gn, &ref); DoEqualLenTicks(canvas, hcs, a, b, gn, &ref);
if(refs) refs->push_back(ref); if(refs) refs->push_back(ref);
@ -1044,6 +1095,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
DoProjectedPoint(canvas, hcs, &a); DoProjectedPoint(canvas, hcs, &a);
DoProjectedPoint(canvas, hcs, &b); DoProjectedPoint(canvas, hcs, &b);
} }
if(ShouldDrawExploded()) {
Vector offset = forLen->ExplodeOffset();
a = a.Plus(offset);
b = b.Plus(offset);
}
Vector refa; Vector refa;
DoEqualLenTicks(canvas, hcs, a, b, gn, &refa); DoEqualLenTicks(canvas, hcs, a, b, gn, &refa);
if(refs) refs->push_back(refa); if(refs) refs->push_back(refa);
@ -1059,6 +1115,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
} }
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la)); Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
if(ShouldDrawExploded()) {
Vector offset = SK.GetEntity(ptA)->ExplodeOffset();
pt = pt.Plus(offset);
closest = closest.Plus(offset);
}
DoLine(canvas, hcs, pt, closest); DoLine(canvas, hcs, pt, closest);
Vector refb; Vector refb;
DoEqualLenTicks(canvas, hcs, pt, closest, gn, &refb); DoEqualLenTicks(canvas, hcs, pt, closest, gn, &refb);
@ -1081,6 +1142,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
} }
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la)); Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
if(ShouldDrawExploded()) {
Vector offset = pte->ExplodeOffset();
pt = pt.Plus(offset);
closest = closest.Plus(offset);
}
DoLine(canvas, hcs, pt, closest); DoLine(canvas, hcs, pt, closest);
Vector ref; Vector ref;
@ -1110,8 +1176,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
goto s; goto s;
} }
s: s:
Vector a = SK.GetEntity(ptA)->PointGetNum(); Vector a = SK.GetEntity(ptA)->PointGetDrawNum();
Vector b = SK.GetEntity(ptB)->PointGetNum(); Vector b = SK.GetEntity(ptB)->PointGetDrawNum();
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
Vector tail = (i == 0) ? a : b; Vector tail = (i == 0) ? a : b;
@ -1148,8 +1214,8 @@ s:
} }
// For "at midpoint", this branch is always taken. // For "at midpoint", this branch is always taken.
Entity *e = SK.GetEntity(entityA); Entity *e = SK.GetEntity(entityA);
Vector a = SK.GetEntity(e->point[0])->PointGetNum(); Vector a = SK.GetEntity(e->point[0])->PointGetDrawNum();
Vector b = SK.GetEntity(e->point[1])->PointGetNum(); Vector b = SK.GetEntity(e->point[1])->PointGetDrawNum();
Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5)); Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5));
Vector offset = (a.Minus(b)).Cross(n); Vector offset = (a.Minus(b)).Cross(n);
offset = offset.WithMagnitude(textHeight); offset = offset.WithMagnitude(textHeight);
@ -1173,8 +1239,8 @@ s:
r.WithMagnitude(1), u.WithMagnitude(1), hcs); r.WithMagnitude(1), u.WithMagnitude(1), hcs);
if(refs) refs->push_back(o); if(refs) refs->push_back(o);
} else { } else {
Vector a = SK.GetEntity(ptA)->PointGetNum(); Vector a = SK.GetEntity(ptA)->PointGetDrawNum();
Vector b = SK.GetEntity(ptB)->PointGetNum(); Vector b = SK.GetEntity(ptB)->PointGetDrawNum();
Entity *w = SK.GetEntity(workplane); Entity *w = SK.GetEntity(workplane);
Vector cu = w->Normal()->NormalU(); Vector cu = w->Normal()->NormalU();
@ -1291,3 +1357,7 @@ bool Constraint::HasLabel() const {
return false; return false;
} }
} }
bool Constraint::ShouldDrawExploded() const {
return SK.GetGroup(group)->ShouldDrawExploded();
}

View File

@ -88,7 +88,7 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
case Type::POINT_N_ROT_AXIS_TRANS: case Type::POINT_N_ROT_AXIS_TRANS:
case Type::POINT_IN_3D: case Type::POINT_IN_3D:
case Type::POINT_IN_2D: case Type::POINT_IN_2D:
refs->push_back(PointGetNum()); refs->push_back(PointGetDrawNum());
break; break;
case Type::NORMAL_N_COPY: case Type::NORMAL_N_COPY:
@ -103,12 +103,12 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
case Type::CUBIC_PERIODIC: case Type::CUBIC_PERIODIC:
case Type::TTF_TEXT: case Type::TTF_TEXT:
case Type::IMAGE: case Type::IMAGE:
refs->push_back(SK.GetEntity(point[0])->PointGetNum()); refs->push_back(SK.GetEntity(point[0])->PointGetDrawNum());
break; break;
case Type::LINE_SEGMENT: { case Type::LINE_SEGMENT: {
Vector a = SK.GetEntity(point[0])->PointGetNum(), Vector a = SK.GetEntity(point[0])->PointGetDrawNum(),
b = SK.GetEntity(point[1])->PointGetNum(); b = SK.GetEntity(point[1])->PointGetDrawNum();
refs->push_back(b.Plus(a.Minus(b).ScaledBy(0.5))); refs->push_back(b.Plus(a.Minus(b).ScaledBy(0.5)));
break; break;
} }
@ -466,6 +466,26 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const {
} }
} }
bool Entity::ShouldDrawExploded() const {
return SK.GetGroup(group)->ShouldDrawExploded();
}
Vector Entity::ExplodeOffset() const {
if(ShouldDrawExploded() && workplane.v != 0) {
int requestIdx = SK.GetRequest(h.request())->groupRequestIndex;
double offset = SS.explodeDistance * (requestIdx + 1);
return SK.GetEntity(workplane)->Normal()->NormalN().ScaledBy(offset);
} else {
return Vector::From(0, 0, 0);
}
}
Vector Entity::PointGetDrawNum() const {
// As per EntityBase::PointGetNum but specifically for when drawing/rendering the point
// (and not when solving), so we can potentially draw it somewhere different
return PointGetNum().Plus(ExplodeOffset());
}
void Entity::Draw(DrawAs how, Canvas *canvas) { void Entity::Draw(DrawAs how, Canvas *canvas) {
if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) && if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) &&
!IsVisible()) return; !IsVisible()) return;
@ -557,16 +577,17 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
pointStroke.unit = Canvas::Unit::PX; pointStroke.unit = Canvas::Unit::PX;
Canvas::hStroke hcsPoint = canvas->GetStroke(pointStroke); Canvas::hStroke hcsPoint = canvas->GetStroke(pointStroke);
Vector p = PointGetDrawNum();
if(free) { if(free) {
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE); Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
analyzeStroke.width = 14.0; analyzeStroke.width = 14.0;
analyzeStroke.layer = Canvas::Layer::FRONT; analyzeStroke.layer = Canvas::Layer::FRONT;
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke); Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
canvas->DrawPoint(PointGetNum(), hcsAnalyze); canvas->DrawPoint(p, hcsAnalyze);
} }
canvas->DrawPoint(PointGetNum(), hcsPoint); canvas->DrawPoint(p, hcsPoint);
return; return;
} }
@ -621,7 +642,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
tail = camera.projRight.ScaledBy(w/s).Plus( tail = camera.projRight.ScaledBy(w/s).Plus(
camera.projUp. ScaledBy(h/s)).Minus(camera.offset); camera.projUp. ScaledBy(h/s)).Minus(camera.offset);
} else { } else {
tail = SK.GetEntity(point[0])->PointGetNum(); tail = SK.GetEntity(point[0])->PointGetDrawNum();
} }
tail = camera.AlignToPixelGrid(tail); tail = camera.AlignToPixelGrid(tail);
@ -709,8 +730,32 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
case Type::TTF_TEXT: { case Type::TTF_TEXT: {
// Generate the rational polynomial curves, then piecewise linearize // Generate the rational polynomial curves, then piecewise linearize
// them, and display those. // them, and display those.
if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcs)) { // Calculating the draw offset, if necessary.
canvas->DrawEdges(*GetOrGenerateEdges(), hcs); const bool shouldExplode = ShouldDrawExploded();
Vector explodeOffset;
SBezierList offsetBeziers = {};
SBezierList *beziers = GetOrGenerateBezierCurves();
if(shouldExplode) {
explodeOffset = ExplodeOffset();
for(const SBezier& b : beziers->l) {
SBezier offset = b.TransformedBy(explodeOffset, Quaternion::IDENTITY, 1.0);
offsetBeziers.l.Add(&offset);
}
beziers = &offsetBeziers;
}
SEdgeList *edges = nullptr;
SEdgeList offsetEdges = {};
if(!canvas->DrawBeziers(*beziers, hcs)) {
edges = GetOrGenerateEdges();
if(shouldExplode) {
for(const SEdge &e : edges->l) {
offsetEdges.AddEdge(e.a.Plus(explodeOffset), e.b.Plus(explodeOffset), e.auxA, e.auxB, e.tag);
}
edges = &offsetEdges;
}
canvas->DrawEdges(*edges, hcs);
} }
if(type == Type::CIRCLE) { if(type == Type::CIRCLE) {
Entity *dist = SK.GetEntity(distance); Entity *dist = SK.GetEntity(distance);
@ -720,12 +765,14 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE); Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
analyzeStroke.layer = Canvas::Layer::FRONT; analyzeStroke.layer = Canvas::Layer::FRONT;
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke); Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcsAnalyze)) { if(!canvas->DrawBeziers(*beziers, hcsAnalyze)) {
canvas->DrawEdges(*GetOrGenerateEdges(), hcsAnalyze); canvas->DrawEdges(*edges, hcsAnalyze);
} }
} }
} }
} }
offsetBeziers.Clear();
offsetEdges.Clear();
return; return;
} }
case Type::IMAGE: { case Type::IMAGE: {
@ -757,7 +804,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
Canvas::hFill hf = canvas->GetFill(fill); Canvas::hFill hf = canvas->GetFill(fill);
Vector v[4] = {}; Vector v[4] = {};
for(int i = 0; i < 4; i++) { for(int i = 0; i < 4; i++) {
v[i] = SK.GetEntity(point[i])->PointGetNum(); v[i] = SK.GetEntity(point[i])->PointGetDrawNum();
} }
Vector iu = v[3].Minus(v[0]); Vector iu = v[3].Minus(v[0]);
Vector iv = v[1].Minus(v[0]); Vector iv = v[1].Minus(v[0]);

View File

@ -224,9 +224,11 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
if(PruneGroups(hg)) if(PruneGroups(hg))
goto pruned; goto pruned;
int groupRequestIndex = 0;
for(auto &req : SK.request) { for(auto &req : SK.request) {
Request *r = &req; Request *r = &req;
if(r->group != hg) continue; if(r->group != hg) continue;
r->groupRequestIndex = groupRequestIndex++;
r->Generate(&(SK.entity), &(SK.param)); r->Generate(&(SK.entity), &(SK.param));
} }

View File

@ -94,6 +94,7 @@ const MenuEntry Menu[] = {
{ 1, N_("Show Snap &Grid"), Command::SHOW_GRID, '>', KC, mView }, { 1, N_("Show Snap &Grid"), Command::SHOW_GRID, '>', KC, mView },
{ 1, N_("Darken Inactive Solids"), Command::DIM_SOLID_MODEL, 0, KC, mView }, { 1, N_("Darken Inactive Solids"), Command::DIM_SOLID_MODEL, 0, KC, mView },
{ 1, N_("Use &Perspective Projection"), Command::PERSPECTIVE_PROJ, '`', KC, mView }, { 1, N_("Use &Perspective Projection"), Command::PERSPECTIVE_PROJ, '`', KC, mView },
{ 1, N_("Show E&xploded View"), Command::EXPLODE_SKETCH, '\\', KC, mView },
{ 1, N_("Dimension &Units"), Command::NONE, 0, KN, NULL }, { 1, N_("Dimension &Units"), Command::NONE, 0, KN, NULL },
{ 2, N_("Dimensions in &Millimeters"), Command::UNITS_MM, 0, KR, mView }, { 2, N_("Dimensions in &Millimeters"), Command::UNITS_MM, 0, KR, mView },
{ 2, N_("Dimensions in M&eters"), Command::UNITS_METERS, 0, KR, mView }, { 2, N_("Dimensions in M&eters"), Command::UNITS_METERS, 0, KR, mView },
@ -318,6 +319,8 @@ void GraphicsWindow::PopulateMainMenu() {
dimSolidModelMenuItem = menuItem; dimSolidModelMenuItem = menuItem;
} else if(Menu[i].cmd == Command::PERSPECTIVE_PROJ) { } else if(Menu[i].cmd == Command::PERSPECTIVE_PROJ) {
perspectiveProjMenuItem = menuItem; perspectiveProjMenuItem = menuItem;
} else if(Menu[i].cmd == Command::EXPLODE_SKETCH) {
explodeMenuItem = menuItem;
} else if(Menu[i].cmd == Command::SHOW_TOOLBAR) { } else if(Menu[i].cmd == Command::SHOW_TOOLBAR) {
showToolbarMenuItem = menuItem; showToolbarMenuItem = menuItem;
} else if(Menu[i].cmd == Command::SHOW_TEXT_WND) { } else if(Menu[i].cmd == Command::SHOW_TEXT_WND) {
@ -753,6 +756,12 @@ void GraphicsWindow::MenuView(Command id) {
} }
break; break;
case Command::EXPLODE_SKETCH:
SS.explode = !SS.explode;
SS.GW.EnsureValidActives();
SS.MarkGroupDirty(SS.GW.activeGroup, true);
break;
case Command::ONTO_WORKPLANE: case Command::ONTO_WORKPLANE:
if(SS.GW.LockedInWorkplane()) { if(SS.GW.LockedInWorkplane()) {
SS.GW.AnimateOntoWorkplane(); SS.GW.AnimateOntoWorkplane();
@ -951,6 +960,7 @@ void GraphicsWindow::EnsureValidActives() {
showGridMenuItem->SetActive(SS.GW.showSnapGrid); showGridMenuItem->SetActive(SS.GW.showSnapGrid);
dimSolidModelMenuItem->SetActive(SS.GW.dimSolidModel); dimSolidModelMenuItem->SetActive(SS.GW.dimSolidModel);
perspectiveProjMenuItem->SetActive(SS.usePerspectiveProj); perspectiveProjMenuItem->SetActive(SS.usePerspectiveProj);
explodeMenuItem->SetActive(SS.explode);
showToolbarMenuItem->SetActive(SS.showToolbar); showToolbarMenuItem->SetActive(SS.showToolbar);
fullScreenMenuItem->SetActive(SS.GW.window->IsFullScreen()); fullScreenMenuItem->SetActive(SS.GW.window->IsFullScreen());

View File

@ -1190,3 +1190,6 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
el->Add(&en); el->Add(&en);
} }
bool Group::ShouldDrawExploded() const {
return SS.explode && h == SS.GW.activeGroup && type == Type::DRAWING_WORKPLANE && !SS.exportMode;
}

View File

@ -326,6 +326,7 @@ public:
void DrawPolyError(Canvas *canvas); void DrawPolyError(Canvas *canvas);
void DrawFilledPaths(Canvas *canvas); void DrawFilledPaths(Canvas *canvas);
void DrawContourAreaLabels(Canvas *canvas); void DrawContourAreaLabels(Canvas *canvas);
bool ShouldDrawExploded() const;
SPolygon GetPolygon(); SPolygon GetPolygon();
@ -371,6 +372,7 @@ public:
std::string font; std::string font;
Platform::Path file; Platform::Path file;
double aspectRatio; double aspectRatio;
int groupRequestIndex;
static hParam AddParam(ParamList *param, hParam hp); static hParam AddParam(ParamList *param, hParam hp);
void Generate(EntityList *entity, ParamList *param); void Generate(EntityList *entity, ParamList *param);
@ -594,6 +596,10 @@ public:
beziers.l.Clear(); beziers.l.Clear();
edges.l.Clear(); edges.l.Clear();
} }
bool ShouldDrawExploded() const;
Vector ExplodeOffset() const;
Vector PointGetDrawNum() const;
}; };
class EntReqTable { class EntReqTable {
@ -763,7 +769,7 @@ public:
Vector p0, Vector p1, Vector pt, double salient); Vector p0, Vector p1, Vector pt, double salient);
void DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs, void DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
Vector a0, Vector da, Vector b0, Vector db, Vector a0, Vector da, Vector b0, Vector db,
Vector offset, Vector *ref, bool trim); Vector offset, Vector *ref, bool trim, Vector explodeOffset);
void DoArrow(Canvas *canvas, Canvas::hStroke hcs, void DoArrow(Canvas *canvas, Canvas::hStroke hcs,
Vector p, Vector dir, Vector n, double width, double angle, double da); Vector p, Vector dir, Vector n, double width, double angle, double da);
void DoLineWithArrows(Canvas *canvas, Canvas::hStroke hcs, void DoLineWithArrows(Canvas *canvas, Canvas::hStroke hcs,
@ -785,6 +791,8 @@ public:
std::string DescriptionString() const; std::string DescriptionString() const;
bool ShouldDrawExploded() const;
static hConstraint AddConstraint(Constraint *c, bool rememberForUndo = true); static hConstraint AddConstraint(Constraint *c, bool rememberForUndo = true);
static void MenuConstrain(Command id); static void MenuConstrain(Command id);
static void DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA); static void DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA);

View File

@ -19,6 +19,7 @@ void SolveSpaceUI::Init() {
Platform::SettingsRef settings = Platform::GetSettings(); Platform::SettingsRef settings = Platform::GetSettings();
SS.tangentArcRadius = 10.0; SS.tangentArcRadius = 10.0;
SS.explodeDistance = 1.0;
// Then, load the registry settings. // Then, load the registry settings.
// Default list of colors for the model material // Default list of colors for the model material
@ -1070,6 +1071,7 @@ void SolveSpaceUI::Clear() {
GW.showGridMenuItem = NULL; GW.showGridMenuItem = NULL;
GW.dimSolidModelMenuItem = NULL; GW.dimSolidModelMenuItem = NULL;
GW.perspectiveProjMenuItem = NULL; GW.perspectiveProjMenuItem = NULL;
GW.explodeMenuItem = NULL;
GW.showToolbarMenuItem = NULL; GW.showToolbarMenuItem = NULL;
GW.showTextWndMenuItem = NULL; GW.showTextWndMenuItem = NULL;
GW.fullScreenMenuItem = NULL; GW.fullScreenMenuItem = NULL;

View File

@ -609,6 +609,8 @@ public:
int afterDecimalDegree; int afterDecimalDegree;
bool useSIPrefixes; bool useSIPrefixes;
int autosaveInterval; // in minutes int autosaveInterval; // in minutes
bool explode;
double explodeDistance;
std::string MmToString(double v, bool editable=false); std::string MmToString(double v, bool editable=false);
std::string MmToStringSI(double v, int dim = 0); std::string MmToStringSI(double v, int dim = 0);

View File

@ -82,6 +82,7 @@ enum class Command : uint32_t {
SHOW_GRID, SHOW_GRID,
DIM_SOLID_MODEL, DIM_SOLID_MODEL,
PERSPECTIVE_PROJ, PERSPECTIVE_PROJ,
EXPLODE_SKETCH,
ONTO_WORKPLANE, ONTO_WORKPLANE,
NEAREST_ORTHO, NEAREST_ORTHO,
NEAREST_ISO, NEAREST_ISO,
@ -319,6 +320,7 @@ public:
AUTOSAVE_INTERVAL = 116, AUTOSAVE_INTERVAL = 116,
LIGHT_AMBIENT = 117, LIGHT_AMBIENT = 117,
FIND_CONSTRAINT_TIMEOUT = 118, FIND_CONSTRAINT_TIMEOUT = 118,
EXPLODE_DISTANCE = 119,
// For TTF text // For TTF text
TTF_TEXT = 300, TTF_TEXT = 300,
// For the step dimension screen // For the step dimension screen
@ -488,6 +490,7 @@ public:
static void ScreenChangeExportMaxSegments(int link, uint32_t v); static void ScreenChangeExportMaxSegments(int link, uint32_t v);
static void ScreenChangeCameraTangent(int link, uint32_t v); static void ScreenChangeCameraTangent(int link, uint32_t v);
static void ScreenChangeGridSpacing(int link, uint32_t v); static void ScreenChangeGridSpacing(int link, uint32_t v);
static void ScreenChangeExplodeDistance(int link, uint32_t v);
static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v); static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v);
static void ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v); static void ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v);
static void ScreenChangeUseSIPrefixes(int link, uint32_t v); static void ScreenChangeUseSIPrefixes(int link, uint32_t v);
@ -540,6 +543,7 @@ public:
Platform::MenuItemRef showGridMenuItem; Platform::MenuItemRef showGridMenuItem;
Platform::MenuItemRef dimSolidModelMenuItem; Platform::MenuItemRef dimSolidModelMenuItem;
Platform::MenuItemRef perspectiveProjMenuItem; Platform::MenuItemRef perspectiveProjMenuItem;
Platform::MenuItemRef explodeMenuItem;
Platform::MenuItemRef showToolbarMenuItem; Platform::MenuItemRef showToolbarMenuItem;
Platform::MenuItemRef showTextWndMenuItem; Platform::MenuItemRef showTextWndMenuItem;
Platform::MenuItemRef fullScreenMenuItem; Platform::MenuItemRef fullScreenMenuItem;