
Specifically, this enables -Wswitch=error on GCC/Clang and its MSVC equivalent; the exact way it is handled varies slightly, but what they all have in common is that in a switch statement over an enumeration, any enumerand that is not explicitly (via case:) or implicitly (via default:) handled in the switch triggers an error. Moreover, we also change the switch statements in three ways: * Switch statements that ought to be extended every time a new enumerand is added (e.g. Entity::DrawOrGetDistance(), are changed to explicitly list every single enumerand, and not have a default: branch. Note that the assertions are kept because it is legal for a enumeration to have a value unlike any of its defined enumerands, and we can e.g. read garbage from a file, or an uninitialized variable. This requires some rearranging if a default: branch is undesired. * Switch statements that ought to only ever see a few select enumerands, are changed to always assert in the default: branch. * Switch statements that do something meaningful for a few enumerands, and ignore everything else, are changed to do nothing in a default: branch, under the assumption that changing them every time an enumerand is added or removed would just result in noise and catch no bugs. This commit also removes the {Request,Entity,Constraint}::UNKNOWN and Entity::DATUM_POINT enumerands, as those were just fancy names for zeroes. They mess up switch exhaustiveness checks and most of the time were not the best way to implement what they did anyway.
806 lines
29 KiB
C++
806 lines
29 KiB
C++
//-----------------------------------------------------------------------------
|
|
// The text-based browser window, used to view the structure of the model by
|
|
// groups and for other similar purposes.
|
|
//
|
|
// Copyright 2008-2013 Jonathan Westhues.
|
|
//-----------------------------------------------------------------------------
|
|
#include "solvespace.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A navigation bar that always appears at the top of the window, with a
|
|
// link to bring us back home.
|
|
//-----------------------------------------------------------------------------
|
|
void TextWindow::ScreenHome(int link, uint32_t v) {
|
|
SS.TW.GoToScreen(Screen::LIST_OF_GROUPS);
|
|
}
|
|
void TextWindow::ShowHeader(bool withNav) {
|
|
ClearScreen();
|
|
|
|
const char *header;
|
|
std::string desc;
|
|
if(SS.GW.LockedInWorkplane()) {
|
|
header = "in plane: ";
|
|
desc = SK.GetEntity(SS.GW.ActiveWorkplane())->DescriptionString();
|
|
} else {
|
|
header = "drawing / constraining in 3d";
|
|
desc = "";
|
|
}
|
|
|
|
// Navigation buttons
|
|
if(withNav) {
|
|
Printf(false, " %Fl%Lh%fhome%E %Ft%s%E%s",
|
|
(&TextWindow::ScreenHome), header, desc.c_str());
|
|
} else {
|
|
Printf(false, " %Ft%s%E%s", header, desc.c_str());
|
|
}
|
|
|
|
// Leave space for the icons that are painted here.
|
|
Printf(false, "");
|
|
Printf(false, "");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The screen that shows a list of every group in the sketch, with options
|
|
// to hide or show them, and to view them in detail. This is our home page.
|
|
//-----------------------------------------------------------------------------
|
|
void TextWindow::ScreenSelectGroup(int link, uint32_t v) {
|
|
SS.TW.GoToScreen(Screen::GROUP_INFO);
|
|
SS.TW.shown.group.v = v;
|
|
}
|
|
void TextWindow::ScreenToggleGroupShown(int link, uint32_t v) {
|
|
hGroup hg = { v };
|
|
Group *g = SK.GetGroup(hg);
|
|
g->visible = !(g->visible);
|
|
// If a group was just shown, then it might not have been generated
|
|
// previously, so regenerate.
|
|
SS.GenerateAll();
|
|
}
|
|
void TextWindow::ScreenShowGroupsSpecial(int link, uint32_t v) {
|
|
bool state = link == 's';
|
|
for(int i = 0; i < SK.groupOrder.n; i++) {
|
|
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
|
|
g->visible = state;
|
|
}
|
|
}
|
|
void TextWindow::ScreenActivateGroup(int link, uint32_t v) {
|
|
SS.GW.activeGroup.v = v;
|
|
SK.GetGroup(SS.GW.activeGroup)->Activate();
|
|
SS.GW.ClearSuper();
|
|
}
|
|
void TextWindow::ReportHowGroupSolved(hGroup hg) {
|
|
SS.GW.ClearSuper();
|
|
SS.TW.GoToScreen(Screen::GROUP_SOLVE_INFO);
|
|
SS.TW.shown.group.v = hg.v;
|
|
SS.ScheduleShowTW();
|
|
}
|
|
void TextWindow::ScreenHowGroupSolved(int link, uint32_t v) {
|
|
if(SS.GW.activeGroup.v != v) {
|
|
ScreenActivateGroup(link, v);
|
|
}
|
|
SS.TW.GoToScreen(Screen::GROUP_SOLVE_INFO);
|
|
SS.TW.shown.group.v = v;
|
|
}
|
|
void TextWindow::ScreenShowConfiguration(int link, uint32_t v) {
|
|
SS.TW.GoToScreen(Screen::CONFIGURATION);
|
|
}
|
|
void TextWindow::ScreenShowEditView(int link, uint32_t v) {
|
|
SS.TW.GoToScreen(Screen::EDIT_VIEW);
|
|
}
|
|
void TextWindow::ScreenGoToWebsite(int link, uint32_t v) {
|
|
OpenWebsite("http://solvespace.com/txtlink");
|
|
}
|
|
void TextWindow::ShowListOfGroups() {
|
|
const char *radioTrue = " " RADIO_TRUE " ",
|
|
*radioFalse = " " RADIO_FALSE " ",
|
|
*checkTrue = " " CHECK_TRUE " ",
|
|
*checkFalse = " " CHECK_FALSE " ";
|
|
|
|
Printf(true, "%Ft active");
|
|
Printf(false, "%Ft shown ok group-name%E");
|
|
int i;
|
|
bool afterActive = false;
|
|
for(i = 0; i < SK.groupOrder.n; i++) {
|
|
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
|
|
std::string s = g->DescriptionString();
|
|
bool active = (g->h.v == SS.GW.activeGroup.v);
|
|
bool shown = g->visible;
|
|
bool ok = g->IsSolvedOkay();
|
|
bool ref = (g->h.v == Group::HGROUP_REFERENCES.v);
|
|
Printf(false, "%Bp%Fd "
|
|
"%Ft%s%Fb%D%f%Ll%s%E "
|
|
"%Fb%s%D%f%Ll%s%E "
|
|
"%Fp%D%f%s%Ll%s%E "
|
|
"%Fl%Ll%D%f%s",
|
|
// Alternate between light and dark backgrounds, for readability
|
|
(i & 1) ? 'd' : 'a',
|
|
// Link that activates the group
|
|
ref ? " " : "",
|
|
g->h.v, (&TextWindow::ScreenActivateGroup),
|
|
ref ? "" : (active ? radioTrue : radioFalse),
|
|
// Link that hides or shows the group
|
|
afterActive ? " - " : "",
|
|
g->h.v, (&TextWindow::ScreenToggleGroupShown),
|
|
afterActive ? "" : (shown ? checkTrue : checkFalse),
|
|
// Link to the errors, if a problem occured while solving
|
|
ok ? 's' : 'x', g->h.v, (&TextWindow::ScreenHowGroupSolved),
|
|
ok ? "ok" : "",
|
|
ok ? "" : "NO",
|
|
// Link to a screen that gives more details on the group
|
|
g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str());
|
|
|
|
if(active) afterActive = true;
|
|
}
|
|
|
|
Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E",
|
|
&(TextWindow::ScreenShowGroupsSpecial),
|
|
&(TextWindow::ScreenShowGroupsSpecial));
|
|
Printf(true, " %Fl%Ls%fline styles%E /"
|
|
" %Fl%Ls%fview%E /"
|
|
" %Fl%Ls%fconfiguration%E",
|
|
&(TextWindow::ScreenShowListOfStyles),
|
|
&(TextWindow::ScreenShowEditView),
|
|
&(TextWindow::ScreenShowConfiguration));
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The screen that shows information about a specific group, and allows the
|
|
// user to edit various things about it.
|
|
//-----------------------------------------------------------------------------
|
|
void TextWindow::ScreenHoverConstraint(int link, uint32_t v) {
|
|
if(!SS.GW.showConstraints) return;
|
|
|
|
hConstraint hc = { v };
|
|
Constraint *c = SK.GetConstraint(hc);
|
|
if(c->group.v != SS.GW.activeGroup.v) {
|
|
// Only constraints in the active group are visible
|
|
return;
|
|
}
|
|
SS.GW.hover.Clear();
|
|
SS.GW.hover.constraint = hc;
|
|
SS.GW.hover.emphasized = true;
|
|
}
|
|
void TextWindow::ScreenHoverRequest(int link, uint32_t v) {
|
|
SS.GW.hover.Clear();
|
|
hRequest hr = { v };
|
|
SS.GW.hover.entity = hr.entity(0);
|
|
SS.GW.hover.emphasized = true;
|
|
}
|
|
void TextWindow::ScreenSelectConstraint(int link, uint32_t v) {
|
|
SS.GW.ClearSelection();
|
|
GraphicsWindow::Selection sel = {};
|
|
sel.constraint.v = v;
|
|
SS.GW.selection.Add(&sel);
|
|
}
|
|
void TextWindow::ScreenSelectRequest(int link, uint32_t v) {
|
|
SS.GW.ClearSelection();
|
|
GraphicsWindow::Selection sel = {};
|
|
hRequest hr = { v };
|
|
sel.entity = hr.entity(0);
|
|
SS.GW.selection.Add(&sel);
|
|
}
|
|
|
|
void TextWindow::ScreenChangeGroupOption(int link, uint32_t v) {
|
|
SS.UndoRemember();
|
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
|
|
|
switch(link) {
|
|
case 's': g->subtype = Group::Subtype::ONE_SIDED; break;
|
|
case 'S': g->subtype = Group::Subtype::TWO_SIDED; break;
|
|
|
|
case 'k': g->skipFirst = true; break;
|
|
case 'K': g->skipFirst = false; break;
|
|
|
|
case 'c':
|
|
// When an extrude group is first created, it's positioned for a union
|
|
// extrusion. If no constraints were added, flip it when we switch between
|
|
// union and difference modes to avoid manual work doing the smae.
|
|
if(g->meshCombine != (Group::CombineAs)v && g->GetNumConstraints() == 0 &&
|
|
((Group::CombineAs)v == Group::CombineAs::DIFFERENCE ||
|
|
g->meshCombine == Group::CombineAs::DIFFERENCE)) {
|
|
g->ExtrusionForceVectorTo(g->ExtrusionGetVector().Negated());
|
|
}
|
|
g->meshCombine = (Group::CombineAs)v;
|
|
break;
|
|
|
|
case 'P': g->suppress = !(g->suppress); break;
|
|
|
|
case 'r': g->relaxConstraints = !(g->relaxConstraints); break;
|
|
|
|
case 'e': g->allowRedundant = !(g->allowRedundant); break;
|
|
|
|
case 'v': g->visible = !(g->visible); break;
|
|
|
|
case 'd': g->allDimsReference = !(g->allDimsReference); break;
|
|
|
|
case 'f': g->forceToMesh = !(g->forceToMesh); break;
|
|
}
|
|
|
|
SS.MarkGroupDirty(g->h);
|
|
SS.GenerateAll();
|
|
SS.GW.ClearSuper();
|
|
}
|
|
|
|
void TextWindow::ScreenColor(int link, uint32_t v) {
|
|
SS.UndoRemember();
|
|
|
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
|
SS.TW.ShowEditControlWithColorPicker(3, g->color);
|
|
SS.TW.edit.meaning = Edit::GROUP_COLOR;
|
|
}
|
|
void TextWindow::ScreenOpacity(int link, uint32_t v) {
|
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
|
|
|
SS.TW.ShowEditControl(11, ssprintf("%.2f", g->color.alphaF()));
|
|
SS.TW.edit.meaning = Edit::GROUP_OPACITY;
|
|
SS.TW.edit.group.v = g->h.v;
|
|
}
|
|
void TextWindow::ScreenChangeExprA(int link, uint32_t v) {
|
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
|
|
|
SS.TW.ShowEditControl(10, ssprintf("%d", (int)g->valA));
|
|
SS.TW.edit.meaning = Edit::TIMES_REPEATED;
|
|
SS.TW.edit.group.v = v;
|
|
}
|
|
void TextWindow::ScreenChangeGroupName(int link, uint32_t v) {
|
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
|
SS.TW.ShowEditControl(12, g->DescriptionString().substr(5));
|
|
SS.TW.edit.meaning = Edit::GROUP_NAME;
|
|
SS.TW.edit.group.v = v;
|
|
}
|
|
void TextWindow::ScreenChangeGroupScale(int link, uint32_t v) {
|
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
|
|
|
SS.TW.ShowEditControl(13, ssprintf("%.3f", g->scale));
|
|
SS.TW.edit.meaning = Edit::GROUP_SCALE;
|
|
SS.TW.edit.group.v = v;
|
|
}
|
|
void TextWindow::ScreenDeleteGroup(int link, uint32_t v) {
|
|
SS.UndoRemember();
|
|
|
|
hGroup hg = SS.TW.shown.group;
|
|
if(hg.v == SS.GW.activeGroup.v) {
|
|
SS.GW.activeGroup = SK.GetGroup(SS.GW.activeGroup)->PreviousGroup()->h;
|
|
}
|
|
|
|
// Reset the text window, since we're displaying information about
|
|
// the group that's about to get deleted.
|
|
SS.TW.ClearSuper();
|
|
|
|
// This is a major change, so let's re-solve everything.
|
|
SK.group.RemoveById(hg);
|
|
SS.GenerateAll(SolveSpaceUI::Generate::ALL);
|
|
|
|
// Reset the graphics window. This will also recreate the default
|
|
// group if it was removed.
|
|
SS.GW.ClearSuper();
|
|
}
|
|
void TextWindow::ShowGroupInfo() {
|
|
Group *g = SK.GetGroup(shown.group);
|
|
const char *s = "???";
|
|
|
|
if(shown.group.v == Group::HGROUP_REFERENCES.v) {
|
|
Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str());
|
|
goto list_items;
|
|
} else {
|
|
Printf(true, "%FtGROUP %E%s [%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]",
|
|
g->DescriptionString().c_str(),
|
|
g->h.v, &TextWindow::ScreenChangeGroupName,
|
|
g->h.v, &TextWindow::ScreenDeleteGroup);
|
|
}
|
|
|
|
if(g->type == Group::Type::LATHE) {
|
|
Printf(true, " %Ftlathe plane sketch");
|
|
} else if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::ROTATE ||
|
|
g->type == Group::Type::TRANSLATE)
|
|
{
|
|
if(g->type == Group::Type::EXTRUDE) {
|
|
s = "extrude plane sketch";
|
|
} else if(g->type == Group::Type::TRANSLATE) {
|
|
s = "translate original sketch";
|
|
} else if(g->type == Group::Type::ROTATE) {
|
|
s = "rotate original sketch";
|
|
}
|
|
Printf(true, " %Ft%s%E", s);
|
|
|
|
bool one = (g->subtype == Group::Subtype::ONE_SIDED);
|
|
Printf(false,
|
|
"%Ba %f%Ls%Fd%s one-sided%E "
|
|
"%f%LS%Fd%s two-sided%E",
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
one ? RADIO_TRUE : RADIO_FALSE,
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
!one ? RADIO_TRUE : RADIO_FALSE);
|
|
|
|
if(g->type == Group::Type::ROTATE || g->type == Group::Type::TRANSLATE) {
|
|
if(g->subtype == Group::Subtype::ONE_SIDED) {
|
|
bool skip = g->skipFirst;
|
|
Printf(false,
|
|
"%Bd %Ftstart %f%LK%Fd%s with original%E "
|
|
"%f%Lk%Fd%s with copy #1%E",
|
|
&ScreenChangeGroupOption,
|
|
!skip ? RADIO_TRUE : RADIO_FALSE,
|
|
&ScreenChangeGroupOption,
|
|
skip ? RADIO_TRUE : RADIO_FALSE);
|
|
}
|
|
|
|
int times = (int)(g->valA);
|
|
Printf(false, "%Bp %Ftrepeat%E %d time%s %Fl%Ll%D%f[change]%E",
|
|
(g->subtype == Group::Subtype::ONE_SIDED) ? 'a' : 'd',
|
|
times, times == 1 ? "" : "s",
|
|
g->h.v, &TextWindow::ScreenChangeExprA);
|
|
}
|
|
} else if(g->type == Group::Type::LINKED) {
|
|
Printf(true, " %Ftlink geometry from file%E");
|
|
Printf(false, "%Ba '%s'", g->linkFileRel.c_str());
|
|
Printf(false, "%Bd %Ftscaled by%E %# %Fl%Ll%f%D[change]%E",
|
|
g->scale,
|
|
&TextWindow::ScreenChangeGroupScale, g->h.v);
|
|
} else if(g->type == Group::Type::DRAWING_3D) {
|
|
Printf(true, " %Ftsketch in 3d%E");
|
|
} else if(g->type == Group::Type::DRAWING_WORKPLANE) {
|
|
Printf(true, " %Ftsketch in new workplane%E");
|
|
} else {
|
|
Printf(true, "???");
|
|
}
|
|
Printf(false, "");
|
|
|
|
if(g->type == Group::Type::EXTRUDE ||
|
|
g->type == Group::Type::LATHE ||
|
|
g->type == Group::Type::LINKED)
|
|
{
|
|
bool un = (g->meshCombine == Group::CombineAs::UNION);
|
|
bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE);
|
|
bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE);
|
|
bool asa = (g->type == Group::Type::LINKED);
|
|
|
|
Printf(false, " %Ftsolid model as");
|
|
Printf(false, "%Ba %f%D%Lc%Fd%s union%E "
|
|
"%f%D%Lc%Fd%s difference%E "
|
|
"%f%D%Lc%Fd%s%s%E ",
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
Group::CombineAs::UNION,
|
|
un ? RADIO_TRUE : RADIO_FALSE,
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
Group::CombineAs::DIFFERENCE,
|
|
diff ? RADIO_TRUE : RADIO_FALSE,
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
Group::CombineAs::ASSEMBLE,
|
|
asa ? (asy ? RADIO_TRUE : RADIO_FALSE) : " ",
|
|
asa ? " assemble" : "");
|
|
|
|
if(g->type == Group::Type::EXTRUDE ||
|
|
g->type == Group::Type::LATHE)
|
|
{
|
|
Printf(false,
|
|
"%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E",
|
|
&g->color,
|
|
g->color.redF(), g->color.greenF(), g->color.blueF(),
|
|
ScreenColor, top[rows-1] + 2);
|
|
Printf(false, "%Bd %Ftopacity%E %@ %f%Lf%Fl[change]%E",
|
|
g->color.alphaF(),
|
|
&TextWindow::ScreenOpacity);
|
|
} else if(g->type == Group::Type::LINKED) {
|
|
Printf(false, " %Fd%f%LP%s suppress this group's solid model",
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
g->suppress ? CHECK_TRUE : CHECK_FALSE);
|
|
}
|
|
|
|
Printf(false, "");
|
|
}
|
|
|
|
Printf(false, " %f%Lv%Fd%s show entities from this group",
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
g->visible ? CHECK_TRUE : CHECK_FALSE);
|
|
|
|
Group *pg; pg = g->PreviousGroup();
|
|
if(pg && pg->runningMesh.IsEmpty() && g->thisMesh.IsEmpty()) {
|
|
Printf(false, " %f%Lf%Fd%s force NURBS surfaces to triangle mesh",
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
g->forceToMesh ? CHECK_TRUE : CHECK_FALSE);
|
|
} else {
|
|
Printf(false, " (model already forced to triangle mesh)");
|
|
}
|
|
|
|
Printf(true, " %f%Lr%Fd%s relax constraints and dimensions",
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
g->relaxConstraints ? CHECK_TRUE : CHECK_FALSE);
|
|
|
|
Printf(false, " %f%Le%Fd%s allow redundant constraints",
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
g->allowRedundant ? CHECK_TRUE : CHECK_FALSE);
|
|
|
|
Printf(false, " %f%Ld%Fd%s treat all dimensions as reference",
|
|
&TextWindow::ScreenChangeGroupOption,
|
|
g->allDimsReference ? CHECK_TRUE : CHECK_FALSE);
|
|
|
|
if(g->booleanFailed) {
|
|
Printf(false, "");
|
|
Printf(false, "The Boolean operation failed. It may be ");
|
|
Printf(false, "possible to fix the problem by choosing ");
|
|
Printf(false, "'force NURBS surfaces to triangle mesh'.");
|
|
}
|
|
|
|
list_items:
|
|
Printf(false, "");
|
|
Printf(false, "%Ft requests in group");
|
|
|
|
int i, a = 0;
|
|
for(i = 0; i < SK.request.n; i++) {
|
|
Request *r = &(SK.request.elem[i]);
|
|
|
|
if(r->group.v == shown.group.v) {
|
|
std::string s = r->DescriptionString();
|
|
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
|
|
(a & 1) ? 'd' : 'a',
|
|
r->h.v, (&TextWindow::ScreenSelectRequest),
|
|
&(TextWindow::ScreenHoverRequest), s.c_str());
|
|
a++;
|
|
}
|
|
}
|
|
if(a == 0) Printf(false, "%Ba (none)");
|
|
|
|
a = 0;
|
|
Printf(false, "");
|
|
Printf(false, "%Ft constraints in group (%d DOF)", g->solved.dof);
|
|
for(i = 0; i < SK.constraint.n; i++) {
|
|
Constraint *c = &(SK.constraint.elem[i]);
|
|
|
|
if(c->group.v == shown.group.v) {
|
|
std::string s = c->DescriptionString();
|
|
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E %s",
|
|
(a & 1) ? 'd' : 'a',
|
|
c->h.v, (&TextWindow::ScreenSelectConstraint),
|
|
(&TextWindow::ScreenHoverConstraint), s.c_str(),
|
|
c->reference ? "(ref)" : "");
|
|
a++;
|
|
}
|
|
}
|
|
if(a == 0) Printf(false, "%Ba (none)");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The screen that's displayed when the sketch fails to solve. A report of
|
|
// what failed, and (if the problem is a singular Jacobian) a list of
|
|
// constraints that could be removed to fix it.
|
|
//-----------------------------------------------------------------------------
|
|
void TextWindow::ScreenAllowRedundant(int link, uint32_t v) {
|
|
SS.UndoRemember();
|
|
|
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
|
g->allowRedundant = true;
|
|
SS.GenerateAll();
|
|
|
|
SS.TW.shown.screen = Screen::GROUP_INFO;
|
|
SS.TW.Show();
|
|
}
|
|
void TextWindow::ShowGroupSolveInfo() {
|
|
Group *g = SK.GetGroup(shown.group);
|
|
if(g->IsSolvedOkay()) {
|
|
// Go back to the default group info screen
|
|
shown.screen = Screen::GROUP_INFO;
|
|
Show();
|
|
return;
|
|
}
|
|
|
|
Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str());
|
|
switch(g->solved.how) {
|
|
case SolveResult::DIDNT_CONVERGE:
|
|
Printf(true, "%FxSOLVE FAILED!%Fd unsolvable constraints");
|
|
Printf(true, "the following constraints are incompatible");
|
|
break;
|
|
|
|
case SolveResult::REDUNDANT_DIDNT_CONVERGE:
|
|
Printf(true, "%FxSOLVE FAILED!%Fd unsolvable constraints");
|
|
Printf(true, "the following constraints are unsatisfied");
|
|
break;
|
|
|
|
case SolveResult::REDUNDANT_OKAY:
|
|
Printf(true, "%FxSOLVE FAILED!%Fd redundant constraints");
|
|
Printf(true, "remove any one of these to fix it");
|
|
break;
|
|
|
|
case SolveResult::TOO_MANY_UNKNOWNS:
|
|
Printf(true, "Too many unknowns in a single group!");
|
|
return;
|
|
|
|
default: ssassert(false, "Unexpected solve result");
|
|
}
|
|
|
|
for(int i = 0; i < g->solved.remove.n; i++) {
|
|
hConstraint hc = g->solved.remove.elem[i];
|
|
Constraint *c = SK.constraint.FindByIdNoOops(hc);
|
|
if(!c) continue;
|
|
|
|
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
|
|
(i & 1) ? 'd' : 'a',
|
|
c->h.v, (&TextWindow::ScreenSelectConstraint),
|
|
(&TextWindow::ScreenHoverConstraint),
|
|
c->DescriptionString().c_str());
|
|
}
|
|
|
|
Printf(true, "It may be possible to fix the problem ");
|
|
Printf(false, "by selecting Edit -> Undo.");
|
|
|
|
if(g->solved.how == SolveResult::REDUNDANT_OKAY) {
|
|
Printf(true, "It is possible to suppress this error ");
|
|
Printf(false, "by %Fl%f%Llallowing redundant constraints%E in ",
|
|
&TextWindow::ScreenAllowRedundant);
|
|
Printf(false, "this group.");
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// When we're stepping a dimension. User specifies the finish value, and
|
|
// how many steps to take in between current and finish, re-solving each
|
|
// time.
|
|
//-----------------------------------------------------------------------------
|
|
void TextWindow::ScreenStepDimFinish(int link, uint32_t v) {
|
|
SS.TW.edit.meaning = Edit::STEP_DIM_FINISH;
|
|
std::string edit_value;
|
|
if(SS.TW.shown.dimIsDistance) {
|
|
edit_value = SS.MmToString(SS.TW.shown.dimFinish);
|
|
} else {
|
|
edit_value = ssprintf("%.3f", SS.TW.shown.dimFinish);
|
|
}
|
|
SS.TW.ShowEditControl(12, edit_value);
|
|
}
|
|
void TextWindow::ScreenStepDimSteps(int link, uint32_t v) {
|
|
SS.TW.edit.meaning = Edit::STEP_DIM_STEPS;
|
|
SS.TW.ShowEditControl(12, ssprintf("%d", SS.TW.shown.dimSteps));
|
|
}
|
|
void TextWindow::ScreenStepDimGo(int link, uint32_t v) {
|
|
hConstraint hc = SS.TW.shown.constraint;
|
|
Constraint *c = SK.constraint.FindByIdNoOops(hc);
|
|
if(c) {
|
|
SS.UndoRemember();
|
|
double start = c->valA, finish = SS.TW.shown.dimFinish;
|
|
int i, n = SS.TW.shown.dimSteps;
|
|
for(i = 1; i <= n; i++) {
|
|
c = SK.GetConstraint(hc);
|
|
c->valA = start + ((finish - start)*i)/n;
|
|
SS.MarkGroupDirty(c->group);
|
|
SS.GenerateAll();
|
|
if(!SS.ActiveGroupsOkay()) {
|
|
// Failed to solve, so quit
|
|
break;
|
|
}
|
|
PaintGraphics();
|
|
}
|
|
}
|
|
InvalidateGraphics();
|
|
SS.TW.GoToScreen(Screen::LIST_OF_GROUPS);
|
|
}
|
|
void TextWindow::ShowStepDimension() {
|
|
Constraint *c = SK.constraint.FindByIdNoOops(shown.constraint);
|
|
if(!c) {
|
|
shown.screen = Screen::LIST_OF_GROUPS;
|
|
Show();
|
|
return;
|
|
}
|
|
|
|
Printf(true, "%FtSTEP DIMENSION%E %s", c->DescriptionString().c_str());
|
|
|
|
if(shown.dimIsDistance) {
|
|
Printf(true, "%Ba %Ftstart%E %s", SS.MmToString(c->valA).c_str());
|
|
Printf(false, "%Bd %Ftfinish%E %s %Fl%Ll%f[change]%E",
|
|
SS.MmToString(shown.dimFinish).c_str(), &ScreenStepDimFinish);
|
|
} else {
|
|
Printf(true, "%Ba %Ftstart%E %@", c->valA);
|
|
Printf(false, "%Bd %Ftfinish%E %@ %Fl%Ll%f[change]%E",
|
|
shown.dimFinish, &ScreenStepDimFinish);
|
|
}
|
|
Printf(false, "%Ba %Ftsteps%E %d %Fl%Ll%f%D[change]%E",
|
|
shown.dimSteps, &ScreenStepDimSteps);
|
|
|
|
Printf(true, " %Fl%Ll%fstep dimension now%E", &ScreenStepDimGo);
|
|
|
|
Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// When we're creating tangent arcs (as requests, not as some parametric
|
|
// thing). User gets to specify the radius, and whether the old untrimmed
|
|
// curves are kept or deleted.
|
|
//-----------------------------------------------------------------------------
|
|
void TextWindow::ScreenChangeTangentArc(int link, uint32_t v) {
|
|
switch(link) {
|
|
case 'r': {
|
|
SS.TW.edit.meaning = Edit::TANGENT_ARC_RADIUS;
|
|
SS.TW.ShowEditControl(3, SS.MmToString(SS.tangentArcRadius));
|
|
break;
|
|
}
|
|
|
|
case 'a': SS.tangentArcManual = !SS.tangentArcManual; break;
|
|
case 'd': SS.tangentArcDeleteOld = !SS.tangentArcDeleteOld; break;
|
|
}
|
|
}
|
|
void TextWindow::ShowTangentArc() {
|
|
Printf(true, "%FtTANGENT ARC PARAMETERS%E");
|
|
|
|
Printf(true, "%Ft radius of created arc%E");
|
|
if(SS.tangentArcManual) {
|
|
Printf(false, "%Ba %s %Fl%Lr%f[change]%E",
|
|
SS.MmToString(SS.tangentArcRadius).c_str(),
|
|
&(TextWindow::ScreenChangeTangentArc));
|
|
} else {
|
|
Printf(false, "%Ba automatic");
|
|
}
|
|
|
|
Printf(false, "");
|
|
Printf(false, " %Fd%f%La%s choose radius automatically%E",
|
|
&ScreenChangeTangentArc,
|
|
!SS.tangentArcManual ? CHECK_TRUE : CHECK_FALSE);
|
|
Printf(false, " %Fd%f%Ld%s delete original entities afterward%E",
|
|
&ScreenChangeTangentArc,
|
|
SS.tangentArcDeleteOld ? CHECK_TRUE : CHECK_FALSE);
|
|
|
|
Printf(false, "");
|
|
Printf(false, "To create a tangent arc at a point,");
|
|
Printf(false, "select that point and then choose");
|
|
Printf(false, "Sketch -> Tangent Arc at Point.");
|
|
Printf(true, "(or %Fl%Ll%fback to home screen%E)", &ScreenHome);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The edit control is visible, and the user just pressed enter.
|
|
//-----------------------------------------------------------------------------
|
|
void TextWindow::EditControlDone(const char *s) {
|
|
edit.showAgain = false;
|
|
|
|
switch(edit.meaning) {
|
|
case Edit::TIMES_REPEATED: {
|
|
Expr *e = Expr::From(s, true);
|
|
if(e) {
|
|
SS.UndoRemember();
|
|
|
|
double ev = e->Eval();
|
|
if((int)ev < 1) {
|
|
Error("Can't repeat fewer than 1 time.");
|
|
break;
|
|
}
|
|
if((int)ev > 999) {
|
|
Error("Can't repeat more than 999 times.");
|
|
break;
|
|
}
|
|
|
|
Group *g = SK.GetGroup(edit.group);
|
|
g->valA = ev;
|
|
|
|
if(g->type == Group::Type::ROTATE) {
|
|
int i, c = 0;
|
|
for(i = 0; i < SK.constraint.n; i++) {
|
|
if(SK.constraint.elem[i].group.v == g->h.v) c++;
|
|
}
|
|
// If the group does not contain any constraints, then
|
|
// set the numerical guess to space the copies uniformly
|
|
// over one rotation. Don't touch the guess if we're
|
|
// already constrained, because that would break
|
|
// convergence.
|
|
if(c == 0) {
|
|
double copies = (g->skipFirst) ? (ev + 1) : ev;
|
|
SK.GetParam(g->h.param(3))->val = PI/(2*copies);
|
|
}
|
|
}
|
|
|
|
SS.MarkGroupDirty(g->h);
|
|
SS.ScheduleGenerateAll();
|
|
}
|
|
break;
|
|
}
|
|
case Edit::GROUP_NAME: {
|
|
if(!*s) {
|
|
Error("Group name cannot be empty");
|
|
} else {
|
|
SS.UndoRemember();
|
|
|
|
Group *g = SK.GetGroup(edit.group);
|
|
g->name = s;
|
|
}
|
|
break;
|
|
}
|
|
case Edit::GROUP_SCALE: {
|
|
Expr *e = Expr::From(s, true);
|
|
if(e) {
|
|
double ev = e->Eval();
|
|
if(fabs(ev) < 1e-6) {
|
|
Error("Scale cannot be zero.");
|
|
} else {
|
|
Group *g = SK.GetGroup(edit.group);
|
|
g->scale = ev;
|
|
SS.MarkGroupDirty(g->h);
|
|
SS.ScheduleGenerateAll();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Edit::GROUP_COLOR: {
|
|
Vector rgb;
|
|
if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
|
|
rgb = rgb.ClampWithin(0, 1);
|
|
|
|
Group *g = SK.group.FindByIdNoOops(SS.TW.shown.group);
|
|
if(!g) break;
|
|
g->color = RGBf(rgb.x, rgb.y, rgb.z);
|
|
|
|
SS.MarkGroupDirty(g->h);
|
|
SS.ScheduleGenerateAll();
|
|
SS.GW.ClearSuper();
|
|
} else {
|
|
Error("Bad format: specify color as r, g, b");
|
|
}
|
|
break;
|
|
}
|
|
case Edit::GROUP_OPACITY: {
|
|
Expr *e = Expr::From(s, true);
|
|
if(e) {
|
|
double alpha = e->Eval();
|
|
if(alpha < 0 || alpha > 1) {
|
|
Error("Opacity must be between zero and one.");
|
|
} else {
|
|
Group *g = SK.GetGroup(edit.group);
|
|
g->color.alpha = (int)(255.1f * alpha);
|
|
SS.MarkGroupDirty(g->h);
|
|
SS.ScheduleGenerateAll();
|
|
SS.GW.ClearSuper();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Edit::TTF_TEXT: {
|
|
SS.UndoRemember();
|
|
Request *r = SK.request.FindByIdNoOops(edit.request);
|
|
if(r) {
|
|
r->str = s;
|
|
SS.MarkGroupDirty(r->group);
|
|
SS.ScheduleGenerateAll();
|
|
}
|
|
break;
|
|
}
|
|
case Edit::STEP_DIM_FINISH: {
|
|
Expr *e = Expr::From(s, true);
|
|
if(!e) {
|
|
break;
|
|
}
|
|
if(shown.dimIsDistance) {
|
|
shown.dimFinish = SS.ExprToMm(e);
|
|
} else {
|
|
shown.dimFinish = e->Eval();
|
|
}
|
|
break;
|
|
}
|
|
case Edit::STEP_DIM_STEPS:
|
|
shown.dimSteps = min(300, max(1, atoi(s)));
|
|
break;
|
|
|
|
case Edit::TANGENT_ARC_RADIUS: {
|
|
Expr *e = Expr::From(s, true);
|
|
if(!e) break;
|
|
if(e->Eval() < LENGTH_EPS) {
|
|
Error("Radius cannot be zero or negative.");
|
|
break;
|
|
}
|
|
SS.tangentArcRadius = SS.ExprToMm(e);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
int cnt = 0;
|
|
if(EditControlDoneForStyles(s)) cnt++;
|
|
if(EditControlDoneForConfiguration(s)) cnt++;
|
|
if(EditControlDoneForPaste(s)) cnt++;
|
|
if(EditControlDoneForView(s)) cnt++;
|
|
ssassert(cnt == 1, "Expected exactly one parameter to be edited");
|
|
break;
|
|
}
|
|
}
|
|
InvalidateGraphics();
|
|
SS.ScheduleShowTW();
|
|
|
|
if(!edit.showAgain) {
|
|
HideEditControl();
|
|
edit.meaning = Edit::NOTHING;
|
|
}
|
|
}
|
|
|