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.
pull/1095/head
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;
}
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) {
SS.TW.ShowEditControl(14, ssprintf("%d", SS.UnitDigitsAfterDecimal()));
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",
SS.MmToString(SS.gridSpacing).c_str(),
&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, "%Ft digits after decimal point to show%E");
@ -459,6 +468,11 @@ bool TextWindow::EditControlDoneForConfiguration(const std::string &s) {
SS.GW.Invalidate();
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: {
int v = atoi(s.c_str());
if(v < 0 || v > 8) {

View File

@ -267,7 +267,7 @@ void Constraint::DoEqualRadiusTicks(Canvas *canvas, Canvas::hStroke hcs,
const Camera &camera = canvas->GetCamera();
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();
Quaternion q = circ->Normal()->NormalGetNum();
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,
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();
double pixels = 1.0 / camera.scale;
@ -305,6 +306,9 @@ void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
db = db.ProjectVectorInto(workplane);
}
a0 = a0.Plus(explodeOffset);
b0 = b0.Plus(explodeOffset);
Vector a1 = a0.Plus(da);
Vector b1 = b0.Plus(db);
@ -534,6 +538,15 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
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);
if(refs) refs->push_back(ref);
@ -548,6 +561,19 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
dp = (bp.Minus(ap)),
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);
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_PLANE_DISTANCE: {
Vector pt = SK.GetEntity(ptA)->PointGetNum();
Vector pt = SK.GetEntity(ptA)->PointGetDrawNum();
Entity *enta = SK.GetEntity(entityA);
Vector n, p;
if(type == Type::PT_PLANE_DISTANCE) {
@ -590,7 +616,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
}
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);
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
@ -602,6 +629,19 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
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
Vector closest = pt.ClosestPointOnLine(lA, dl);
@ -655,7 +695,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
case Type::DIAMETER: {
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();
Vector n = q.RotationN().WithMagnitude(1);
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 d = camera.projUp.ScaledBy((2-a)/camera.scale);
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);
canvas->DrawQuad(p.Plus (r).Plus (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_IN_PLANE: {
double s = 8/camera.scale;
Vector p = SK.GetEntity(ptA)->PointGetNum();
Vector p = SK.GetEntity(ptA)->PointGetDrawNum();
if(refs) refs->push_back(p);
Vector r, d;
if(type == Type::PT_ON_FACE) {
@ -740,7 +780,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
}
case Type::WHERE_DRAGGED: {
Vector p = SK.GetEntity(ptA)->PointGetNum();
Vector p = SK.GetEntity(ptA)->PointGetDrawNum();
if(refs) refs->push_back(p);
Vector u = p.Plus(gu.WithMagnitude(8/camera.scale)).Plus(
gr.WithMagnitude(8/camera.scale)),
@ -797,10 +837,10 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
}
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);
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);
return;
@ -820,7 +860,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
}
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);
if(refs) refs->push_back(ref);
return;
@ -855,7 +895,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
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);
DoLine(canvas, hcs, s, s.Plus(v));
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) {
Entity *arc = SK.GetEntity(entityA);
Entity *norm = SK.GetEntity(arc->normal);
Vector c = SK.GetEntity(arc->point[0])->PointGetNum();
Vector c = SK.GetEntity(arc->point[0])->PointGetDrawNum();
Vector p =
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetNum();
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetDrawNum();
Vector r = p.Minus(c);
textAt = p.Plus(r.WithMagnitude(14/camera.scale));
u = norm->NormalU();
@ -896,6 +936,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Entity *cubic = SK.GetEntity(entityA);
Vector p = other ? cubic->CubicGetFinishNum() :
cubic->CubicGetStartNum();
p = p.Plus(cubic->ExplodeOffset());
Vector dir = SK.GetEntity(entityB)->VectorGetNum();
Vector out = n.Cross(dir);
textAt = p.Plus(out.WithMagnitude(14/camera.scale));
@ -905,12 +946,12 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
u = wn->NormalU();
v = wn->NormalV();
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
// at the shared point, which could be from either a cubic
// or an arc.
if(other) {
textAt = eA->EndpointFinish();
textAt = eA->EndpointFinish().Plus(eA->ExplodeOffset());
if(eA->type == Entity::Type::CUBIC) {
dir = eA->CubicGetFinishTangentNum();
} else {
@ -919,7 +960,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
dir = n.Cross(dir);
}
} else {
textAt = eA->EndpointStart();
textAt = eA->EndpointStart().Plus(eA->ExplodeOffset());
if(eA->type == Entity::Type::CUBIC) {
dir = eA->CubicGetStartTangentNum();
} else {
@ -947,6 +988,10 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector u = (gn.Cross(n)).WithMagnitude(4/camera.scale);
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.Minus(u), p.Minus(u).Plus(n));
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);
Vector ref;
DoEqualLenTicks(canvas, hcs,
SK.GetEntity(line->point[0])->PointGetNum(),
SK.GetEntity(line->point[1])->PointGetNum(),
SK.GetEntity(line->point[0])->PointGetDrawNum(),
SK.GetEntity(line->point[1])->PointGetDrawNum(),
gn, &ref);
if(refs) refs->push_back(ref);
DoEqualRadiusTicks(canvas, hcs, entityB, &ref);
@ -990,6 +1035,12 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
DoProjectedPoint(canvas, hcs, &b);
}
if(ShouldDrawExploded()) {
Vector offset = e->ExplodeOffset();
a = a.Plus(offset);
b = b.Plus(offset);
}
Vector ref;
DoEqualLenTicks(canvas, hcs, a, b, gn, &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, &b);
}
if(ShouldDrawExploded()) {
Vector offset = forLen->ExplodeOffset();
a = a.Plus(offset);
b = b.Plus(offset);
}
Vector refa;
DoEqualLenTicks(canvas, hcs, a, b, gn, &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));
if(ShouldDrawExploded()) {
Vector offset = SK.GetEntity(ptA)->ExplodeOffset();
pt = pt.Plus(offset);
closest = closest.Plus(offset);
}
DoLine(canvas, hcs, pt, closest);
Vector 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));
if(ShouldDrawExploded()) {
Vector offset = pte->ExplodeOffset();
pt = pt.Plus(offset);
closest = closest.Plus(offset);
}
DoLine(canvas, hcs, pt, closest);
Vector ref;
@ -1110,8 +1176,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
goto s;
}
s:
Vector a = SK.GetEntity(ptA)->PointGetNum();
Vector b = SK.GetEntity(ptB)->PointGetNum();
Vector a = SK.GetEntity(ptA)->PointGetDrawNum();
Vector b = SK.GetEntity(ptB)->PointGetDrawNum();
for(int i = 0; i < 2; i++) {
Vector tail = (i == 0) ? a : b;
@ -1148,8 +1214,8 @@ s:
}
// For "at midpoint", this branch is always taken.
Entity *e = SK.GetEntity(entityA);
Vector a = SK.GetEntity(e->point[0])->PointGetNum();
Vector b = SK.GetEntity(e->point[1])->PointGetNum();
Vector a = SK.GetEntity(e->point[0])->PointGetDrawNum();
Vector b = SK.GetEntity(e->point[1])->PointGetDrawNum();
Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5));
Vector offset = (a.Minus(b)).Cross(n);
offset = offset.WithMagnitude(textHeight);
@ -1173,8 +1239,8 @@ s:
r.WithMagnitude(1), u.WithMagnitude(1), hcs);
if(refs) refs->push_back(o);
} else {
Vector a = SK.GetEntity(ptA)->PointGetNum();
Vector b = SK.GetEntity(ptB)->PointGetNum();
Vector a = SK.GetEntity(ptA)->PointGetDrawNum();
Vector b = SK.GetEntity(ptB)->PointGetDrawNum();
Entity *w = SK.GetEntity(workplane);
Vector cu = w->Normal()->NormalU();
@ -1291,3 +1357,7 @@ bool Constraint::HasLabel() const {
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_IN_3D:
case Type::POINT_IN_2D:
refs->push_back(PointGetNum());
refs->push_back(PointGetDrawNum());
break;
case Type::NORMAL_N_COPY:
@ -103,12 +103,12 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
case Type::CUBIC_PERIODIC:
case Type::TTF_TEXT:
case Type::IMAGE:
refs->push_back(SK.GetEntity(point[0])->PointGetNum());
refs->push_back(SK.GetEntity(point[0])->PointGetDrawNum());
break;
case Type::LINE_SEGMENT: {
Vector a = SK.GetEntity(point[0])->PointGetNum(),
b = SK.GetEntity(point[1])->PointGetNum();
Vector a = SK.GetEntity(point[0])->PointGetDrawNum(),
b = SK.GetEntity(point[1])->PointGetDrawNum();
refs->push_back(b.Plus(a.Minus(b).ScaledBy(0.5)));
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) {
if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) &&
!IsVisible()) return;
@ -557,16 +577,17 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
pointStroke.unit = Canvas::Unit::PX;
Canvas::hStroke hcsPoint = canvas->GetStroke(pointStroke);
Vector p = PointGetDrawNum();
if(free) {
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
analyzeStroke.width = 14.0;
analyzeStroke.layer = Canvas::Layer::FRONT;
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
canvas->DrawPoint(PointGetNum(), hcsAnalyze);
canvas->DrawPoint(p, hcsAnalyze);
}
canvas->DrawPoint(PointGetNum(), hcsPoint);
canvas->DrawPoint(p, hcsPoint);
return;
}
@ -621,7 +642,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
tail = camera.projRight.ScaledBy(w/s).Plus(
camera.projUp. ScaledBy(h/s)).Minus(camera.offset);
} else {
tail = SK.GetEntity(point[0])->PointGetNum();
tail = SK.GetEntity(point[0])->PointGetDrawNum();
}
tail = camera.AlignToPixelGrid(tail);
@ -709,8 +730,32 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
case Type::TTF_TEXT: {
// Generate the rational polynomial curves, then piecewise linearize
// them, and display those.
if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcs)) {
canvas->DrawEdges(*GetOrGenerateEdges(), hcs);
// Calculating the draw offset, if necessary.
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) {
Entity *dist = SK.GetEntity(distance);
@ -720,12 +765,14 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
analyzeStroke.layer = Canvas::Layer::FRONT;
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcsAnalyze)) {
canvas->DrawEdges(*GetOrGenerateEdges(), hcsAnalyze);
if(!canvas->DrawBeziers(*beziers, hcsAnalyze)) {
canvas->DrawEdges(*edges, hcsAnalyze);
}
}
}
}
offsetBeziers.Clear();
offsetEdges.Clear();
return;
}
case Type::IMAGE: {
@ -757,7 +804,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
Canvas::hFill hf = canvas->GetFill(fill);
Vector v[4] = {};
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 iv = v[1].Minus(v[0]);

View File

@ -224,9 +224,11 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
if(PruneGroups(hg))
goto pruned;
int groupRequestIndex = 0;
for(auto &req : SK.request) {
Request *r = &req;
if(r->group != hg) continue;
r->groupRequestIndex = groupRequestIndex++;
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_("Darken Inactive Solids"), Command::DIM_SOLID_MODEL, 0, 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 },
{ 2, N_("Dimensions in &Millimeters"), Command::UNITS_MM, 0, KR, mView },
{ 2, N_("Dimensions in M&eters"), Command::UNITS_METERS, 0, KR, mView },
@ -318,6 +319,8 @@ void GraphicsWindow::PopulateMainMenu() {
dimSolidModelMenuItem = menuItem;
} else if(Menu[i].cmd == Command::PERSPECTIVE_PROJ) {
perspectiveProjMenuItem = menuItem;
} else if(Menu[i].cmd == Command::EXPLODE_SKETCH) {
explodeMenuItem = menuItem;
} else if(Menu[i].cmd == Command::SHOW_TOOLBAR) {
showToolbarMenuItem = menuItem;
} else if(Menu[i].cmd == Command::SHOW_TEXT_WND) {
@ -753,6 +756,12 @@ void GraphicsWindow::MenuView(Command id) {
}
break;
case Command::EXPLODE_SKETCH:
SS.explode = !SS.explode;
SS.GW.EnsureValidActives();
SS.MarkGroupDirty(SS.GW.activeGroup, true);
break;
case Command::ONTO_WORKPLANE:
if(SS.GW.LockedInWorkplane()) {
SS.GW.AnimateOntoWorkplane();
@ -951,6 +960,7 @@ void GraphicsWindow::EnsureValidActives() {
showGridMenuItem->SetActive(SS.GW.showSnapGrid);
dimSolidModelMenuItem->SetActive(SS.GW.dimSolidModel);
perspectiveProjMenuItem->SetActive(SS.usePerspectiveProj);
explodeMenuItem->SetActive(SS.explode);
showToolbarMenuItem->SetActive(SS.showToolbar);
fullScreenMenuItem->SetActive(SS.GW.window->IsFullScreen());

View File

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

View File

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

View File

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

View File

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