1 //-----------------------------------------------------------------------------
2 // The text-based browser window, used to view the structure of the model by
3 // groups and for other similar purposes.
4 //
5 // Copyright 2008-2013 Jonathan Westhues.
6 //-----------------------------------------------------------------------------
7 #include "solvespace.h"
8
9 //-----------------------------------------------------------------------------
10 // A navigation bar that always appears at the top of the window, with a
11 // link to bring us back home.
12 //-----------------------------------------------------------------------------
ScreenHome(int link,uint32_t v)13 void TextWindow::ScreenHome(int link, uint32_t v) {
14 SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
15 }
ShowHeader(bool withNav)16 void TextWindow::ShowHeader(bool withNav) {
17 ClearScreen();
18
19 const char *header;
20 std::string desc;
21 if(SS.GW.LockedInWorkplane()) {
22 header = "in plane: ";
23 desc = SK.GetEntity(SS.GW.ActiveWorkplane())->DescriptionString();
24 } else {
25 header = "drawing / constraining in 3d";
26 desc = "";
27 }
28
29 // Navigation buttons
30 if(withNav) {
31 Printf(false, " %Fl%Lh%fhome%E %Ft%s%E%s",
32 (&TextWindow::ScreenHome), header, desc.c_str());
33 } else {
34 Printf(false, " %Ft%s%E%s", header, desc.c_str());
35 }
36
37 // Leave space for the icons that are painted here.
38 Printf(false, "");
39 Printf(false, "");
40 }
41
42 //-----------------------------------------------------------------------------
43 // The screen that shows a list of every group in the sketch, with options
44 // to hide or show them, and to view them in detail. This is our home page.
45 //-----------------------------------------------------------------------------
ScreenSelectGroup(int link,uint32_t v)46 void TextWindow::ScreenSelectGroup(int link, uint32_t v) {
47 SS.TW.GoToScreen(SCREEN_GROUP_INFO);
48 SS.TW.shown.group.v = v;
49 }
ScreenToggleGroupShown(int link,uint32_t v)50 void TextWindow::ScreenToggleGroupShown(int link, uint32_t v) {
51 hGroup hg = { v };
52 Group *g = SK.GetGroup(hg);
53 g->visible = !(g->visible);
54 // If a group was just shown, then it might not have been generated
55 // previously, so regenerate.
56 SS.GenerateAll();
57 }
ScreenShowGroupsSpecial(int link,uint32_t v)58 void TextWindow::ScreenShowGroupsSpecial(int link, uint32_t v) {
59 bool state = link == 's';
60 for(int i = 0; i < SK.groupOrder.n; i++) {
61 Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
62 g->visible = state;
63 }
64 }
ScreenActivateGroup(int link,uint32_t v)65 void TextWindow::ScreenActivateGroup(int link, uint32_t v) {
66 SS.GW.activeGroup.v = v;
67 SK.GetGroup(SS.GW.activeGroup)->Activate();
68 SS.GW.ClearSuper();
69 }
ReportHowGroupSolved(hGroup hg)70 void TextWindow::ReportHowGroupSolved(hGroup hg) {
71 SS.GW.ClearSuper();
72 SS.TW.GoToScreen(SCREEN_GROUP_SOLVE_INFO);
73 SS.TW.shown.group.v = hg.v;
74 SS.ScheduleShowTW();
75 }
ScreenHowGroupSolved(int link,uint32_t v)76 void TextWindow::ScreenHowGroupSolved(int link, uint32_t v) {
77 if(SS.GW.activeGroup.v != v) {
78 ScreenActivateGroup(link, v);
79 }
80 SS.TW.GoToScreen(SCREEN_GROUP_SOLVE_INFO);
81 SS.TW.shown.group.v = v;
82 }
ScreenShowConfiguration(int link,uint32_t v)83 void TextWindow::ScreenShowConfiguration(int link, uint32_t v) {
84 SS.TW.GoToScreen(SCREEN_CONFIGURATION);
85 }
ScreenShowEditView(int link,uint32_t v)86 void TextWindow::ScreenShowEditView(int link, uint32_t v) {
87 SS.TW.GoToScreen(SCREEN_EDIT_VIEW);
88 }
ScreenGoToWebsite(int link,uint32_t v)89 void TextWindow::ScreenGoToWebsite(int link, uint32_t v) {
90 OpenWebsite("http://solvespace.com/txtlink");
91 }
ShowListOfGroups(void)92 void TextWindow::ShowListOfGroups(void) {
93 const char *radioTrue = " " RADIO_TRUE " ",
94 *radioFalse = " " RADIO_FALSE " ",
95 *checkTrue = " " CHECK_TRUE " ",
96 *checkFalse = " " CHECK_FALSE " ";
97
98 Printf(true, "%Ft active");
99 Printf(false, "%Ft shown ok group-name%E");
100 int i;
101 bool afterActive = false;
102 for(i = 0; i < SK.groupOrder.n; i++) {
103 Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
104 std::string s = g->DescriptionString();
105 bool active = (g->h.v == SS.GW.activeGroup.v);
106 bool shown = g->visible;
107 bool ok = g->IsSolvedOkay();
108 bool ref = (g->h.v == Group::HGROUP_REFERENCES.v);
109 Printf(false, "%Bp%Fd "
110 "%Ft%s%Fb%D%f%Ll%s%E "
111 "%Fb%s%D%f%Ll%s%E "
112 "%Fp%D%f%s%Ll%s%E "
113 "%Fl%Ll%D%f%s",
114 // Alternate between light and dark backgrounds, for readability
115 (i & 1) ? 'd' : 'a',
116 // Link that activates the group
117 ref ? " " : "",
118 g->h.v, (&TextWindow::ScreenActivateGroup),
119 ref ? "" : (active ? radioTrue : radioFalse),
120 // Link that hides or shows the group
121 afterActive ? " - " : "",
122 g->h.v, (&TextWindow::ScreenToggleGroupShown),
123 afterActive ? "" : (shown ? checkTrue : checkFalse),
124 // Link to the errors, if a problem occured while solving
125 ok ? 's' : 'x', g->h.v, (&TextWindow::ScreenHowGroupSolved),
126 ok ? "ok" : "",
127 ok ? "" : "NO",
128 // Link to a screen that gives more details on the group
129 g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str());
130
131 if(active) afterActive = true;
132 }
133
134 Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E",
135 &(TextWindow::ScreenShowGroupsSpecial),
136 &(TextWindow::ScreenShowGroupsSpecial));
137 Printf(true, " %Fl%Ls%fline styles%E /"
138 " %Fl%Ls%fview%E /"
139 " %Fl%Ls%fconfiguration%E",
140 &(TextWindow::ScreenShowListOfStyles),
141 &(TextWindow::ScreenShowEditView),
142 &(TextWindow::ScreenShowConfiguration));
143 }
144
145
146 //-----------------------------------------------------------------------------
147 // The screen that shows information about a specific group, and allows the
148 // user to edit various things about it.
149 //-----------------------------------------------------------------------------
ScreenHoverConstraint(int link,uint32_t v)150 void TextWindow::ScreenHoverConstraint(int link, uint32_t v) {
151 if(!SS.GW.showConstraints) return;
152
153 hConstraint hc = { v };
154 Constraint *c = SK.GetConstraint(hc);
155 if(c->group.v != SS.GW.activeGroup.v) {
156 // Only constraints in the active group are visible
157 return;
158 }
159 SS.GW.hover.Clear();
160 SS.GW.hover.constraint = hc;
161 SS.GW.hover.emphasized = true;
162 }
ScreenHoverRequest(int link,uint32_t v)163 void TextWindow::ScreenHoverRequest(int link, uint32_t v) {
164 SS.GW.hover.Clear();
165 hRequest hr = { v };
166 SS.GW.hover.entity = hr.entity(0);
167 SS.GW.hover.emphasized = true;
168 }
ScreenSelectConstraint(int link,uint32_t v)169 void TextWindow::ScreenSelectConstraint(int link, uint32_t v) {
170 SS.GW.ClearSelection();
171 GraphicsWindow::Selection sel = {};
172 sel.constraint.v = v;
173 SS.GW.selection.Add(&sel);
174 }
ScreenSelectRequest(int link,uint32_t v)175 void TextWindow::ScreenSelectRequest(int link, uint32_t v) {
176 SS.GW.ClearSelection();
177 GraphicsWindow::Selection sel = {};
178 hRequest hr = { v };
179 sel.entity = hr.entity(0);
180 SS.GW.selection.Add(&sel);
181 }
182
ScreenChangeGroupOption(int link,uint32_t v)183 void TextWindow::ScreenChangeGroupOption(int link, uint32_t v) {
184 SS.UndoRemember();
185 Group *g = SK.GetGroup(SS.TW.shown.group);
186
187 switch(link) {
188 case 's': g->subtype = Group::ONE_SIDED; break;
189 case 'S': g->subtype = Group::TWO_SIDED; break;
190
191 case 'k': g->skipFirst = true; break;
192 case 'K': g->skipFirst = false; break;
193
194 case 'c':
195 // When an extrude group is first created, it's positioned for a union
196 // extrusion. If no constraints were added, flip it when we switch between
197 // union and difference modes to avoid manual work doing the smae.
198 if(g->meshCombine != (int)v && g->GetNumConstraints() == 0 &&
199 (v == Group::COMBINE_AS_DIFFERENCE ||
200 g->meshCombine == Group::COMBINE_AS_DIFFERENCE)) {
201 g->ExtrusionForceVectorTo(g->ExtrusionGetVector().Negated());
202 }
203 g->meshCombine = v;
204 break;
205
206 case 'P': g->suppress = !(g->suppress); break;
207
208 case 'r': g->relaxConstraints = !(g->relaxConstraints); break;
209
210 case 'e': g->allowRedundant = !(g->allowRedundant); break;
211
212 case 'v': g->visible = !(g->visible); break;
213
214 case 'd': g->allDimsReference = !(g->allDimsReference); break;
215
216 case 'f': g->forceToMesh = !(g->forceToMesh); break;
217 }
218
219 SS.MarkGroupDirty(g->h);
220 SS.GenerateAll();
221 SS.GW.ClearSuper();
222 }
223
ScreenColor(int link,uint32_t v)224 void TextWindow::ScreenColor(int link, uint32_t v) {
225 SS.UndoRemember();
226
227 Group *g = SK.GetGroup(SS.TW.shown.group);
228 SS.TW.ShowEditControlWithColorPicker(3, g->color);
229 SS.TW.edit.meaning = EDIT_GROUP_COLOR;
230 }
ScreenOpacity(int link,uint32_t v)231 void TextWindow::ScreenOpacity(int link, uint32_t v) {
232 Group *g = SK.GetGroup(SS.TW.shown.group);
233
234 SS.TW.ShowEditControl(11, ssprintf("%.2f", g->color.alphaF()));
235 SS.TW.edit.meaning = EDIT_GROUP_OPACITY;
236 SS.TW.edit.group.v = g->h.v;
237 }
ScreenChangeExprA(int link,uint32_t v)238 void TextWindow::ScreenChangeExprA(int link, uint32_t v) {
239 Group *g = SK.GetGroup(SS.TW.shown.group);
240
241 SS.TW.ShowEditControl(10, ssprintf("%d", (int)g->valA));
242 SS.TW.edit.meaning = EDIT_TIMES_REPEATED;
243 SS.TW.edit.group.v = v;
244 }
ScreenChangeGroupName(int link,uint32_t v)245 void TextWindow::ScreenChangeGroupName(int link, uint32_t v) {
246 Group *g = SK.GetGroup(SS.TW.shown.group);
247 SS.TW.ShowEditControl(12, g->DescriptionString().substr(5));
248 SS.TW.edit.meaning = EDIT_GROUP_NAME;
249 SS.TW.edit.group.v = v;
250 }
ScreenChangeGroupScale(int link,uint32_t v)251 void TextWindow::ScreenChangeGroupScale(int link, uint32_t v) {
252 Group *g = SK.GetGroup(SS.TW.shown.group);
253
254 SS.TW.ShowEditControl(13, ssprintf("%.3f", g->scale));
255 SS.TW.edit.meaning = EDIT_GROUP_SCALE;
256 SS.TW.edit.group.v = v;
257 }
ScreenDeleteGroup(int link,uint32_t v)258 void TextWindow::ScreenDeleteGroup(int link, uint32_t v) {
259 SS.UndoRemember();
260
261 hGroup hg = SS.TW.shown.group;
262 if(hg.v == SS.GW.activeGroup.v) {
263 SS.GW.activeGroup = SK.GetGroup(SS.GW.activeGroup)->PreviousGroup()->h;
264 }
265
266 // Reset the text window, since we're displaying information about
267 // the group that's about to get deleted.
268 SS.TW.ClearSuper();
269
270 // This is a major change, so let's re-solve everything.
271 SK.group.RemoveById(hg);
272 SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
273
274 // Reset the graphics window. This will also recreate the default
275 // group if it was removed.
276 SS.GW.ClearSuper();
277 }
ShowGroupInfo(void)278 void TextWindow::ShowGroupInfo(void) {
279 Group *g = SK.GetGroup(shown.group);
280 const char *s = "???";
281
282 if(shown.group.v == Group::HGROUP_REFERENCES.v) {
283 Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str());
284 goto list_items;
285 } else {
286 Printf(true, "%FtGROUP %E%s [%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]",
287 g->DescriptionString().c_str(),
288 g->h.v, &TextWindow::ScreenChangeGroupName,
289 g->h.v, &TextWindow::ScreenDeleteGroup);
290 }
291
292 if(g->type == Group::LATHE) {
293 Printf(true, " %Ftlathe plane sketch");
294 } else if(g->type == Group::EXTRUDE || g->type == Group::ROTATE ||
295 g->type == Group::TRANSLATE)
296 {
297 if(g->type == Group::EXTRUDE) {
298 s = "extrude plane sketch";
299 } else if(g->type == Group::TRANSLATE) {
300 s = "translate original sketch";
301 } else if(g->type == Group::ROTATE) {
302 s = "rotate original sketch";
303 }
304 Printf(true, " %Ft%s%E", s);
305
306 bool one = (g->subtype == Group::ONE_SIDED);
307 Printf(false,
308 "%Ba %f%Ls%Fd%s one-sided%E "
309 "%f%LS%Fd%s two-sided%E",
310 &TextWindow::ScreenChangeGroupOption,
311 one ? RADIO_TRUE : RADIO_FALSE,
312 &TextWindow::ScreenChangeGroupOption,
313 !one ? RADIO_TRUE : RADIO_FALSE);
314
315 if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) {
316 if(g->subtype == Group::ONE_SIDED) {
317 bool skip = g->skipFirst;
318 Printf(false,
319 "%Bd %Ftstart %f%LK%Fd%s with original%E "
320 "%f%Lk%Fd%s with copy #1%E",
321 &ScreenChangeGroupOption,
322 !skip ? RADIO_TRUE : RADIO_FALSE,
323 &ScreenChangeGroupOption,
324 skip ? RADIO_TRUE : RADIO_FALSE);
325 }
326
327 int times = (int)(g->valA);
328 Printf(false, "%Bp %Ftrepeat%E %d time%s %Fl%Ll%D%f[change]%E",
329 (g->subtype == Group::ONE_SIDED) ? 'a' : 'd',
330 times, times == 1 ? "" : "s",
331 g->h.v, &TextWindow::ScreenChangeExprA);
332 }
333 } else if(g->type == Group::LINKED) {
334 Printf(true, " %Ftlink geometry from file%E");
335 Printf(false, "%Ba '%s'", g->linkFileRel.c_str());
336 Printf(false, "%Bd %Ftscaled by%E %# %Fl%Ll%f%D[change]%E",
337 g->scale,
338 &TextWindow::ScreenChangeGroupScale, g->h.v);
339 } else if(g->type == Group::DRAWING_3D) {
340 Printf(true, " %Ftsketch in 3d%E");
341 } else if(g->type == Group::DRAWING_WORKPLANE) {
342 Printf(true, " %Ftsketch in new workplane%E");
343 } else {
344 Printf(true, "???");
345 }
346 Printf(false, "");
347
348 if(g->type == Group::EXTRUDE ||
349 g->type == Group::LATHE ||
350 g->type == Group::LINKED)
351 {
352 bool un = (g->meshCombine == Group::COMBINE_AS_UNION);
353 bool diff = (g->meshCombine == Group::COMBINE_AS_DIFFERENCE);
354 bool asy = (g->meshCombine == Group::COMBINE_AS_ASSEMBLE);
355 bool asa = (g->type == Group::LINKED);
356
357 Printf(false, " %Ftsolid model as");
358 Printf(false, "%Ba %f%D%Lc%Fd%s union%E "
359 "%f%D%Lc%Fd%s difference%E "
360 "%f%D%Lc%Fd%s%s%E ",
361 &TextWindow::ScreenChangeGroupOption,
362 Group::COMBINE_AS_UNION,
363 un ? RADIO_TRUE : RADIO_FALSE,
364 &TextWindow::ScreenChangeGroupOption,
365 Group::COMBINE_AS_DIFFERENCE,
366 diff ? RADIO_TRUE : RADIO_FALSE,
367 &TextWindow::ScreenChangeGroupOption,
368 Group::COMBINE_AS_ASSEMBLE,
369 asa ? (asy ? RADIO_TRUE : RADIO_FALSE) : " ",
370 asa ? " assemble" : "");
371
372 if(g->type == Group::EXTRUDE ||
373 g->type == Group::LATHE)
374 {
375 Printf(false,
376 "%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E",
377 &g->color,
378 g->color.redF(), g->color.greenF(), g->color.blueF(),
379 ScreenColor, top[rows-1] + 2);
380 Printf(false, "%Bd %Ftopacity%E %@ %f%Lf%Fl[change]%E",
381 g->color.alphaF(),
382 &TextWindow::ScreenOpacity);
383 } else if(g->type == Group::LINKED) {
384 Printf(false, " %Fd%f%LP%s suppress this group's solid model",
385 &TextWindow::ScreenChangeGroupOption,
386 g->suppress ? CHECK_TRUE : CHECK_FALSE);
387 }
388
389 Printf(false, "");
390 }
391
392 Printf(false, " %f%Lv%Fd%s show entities from this group",
393 &TextWindow::ScreenChangeGroupOption,
394 g->visible ? CHECK_TRUE : CHECK_FALSE);
395
396 Group *pg; pg = g->PreviousGroup();
397 if(pg && pg->runningMesh.IsEmpty() && g->thisMesh.IsEmpty()) {
398 Printf(false, " %f%Lf%Fd%s force NURBS surfaces to triangle mesh",
399 &TextWindow::ScreenChangeGroupOption,
400 g->forceToMesh ? CHECK_TRUE : CHECK_FALSE);
401 } else {
402 Printf(false, " (model already forced to triangle mesh)");
403 }
404
405 Printf(true, " %f%Lr%Fd%s relax constraints and dimensions",
406 &TextWindow::ScreenChangeGroupOption,
407 g->relaxConstraints ? CHECK_TRUE : CHECK_FALSE);
408
409 Printf(false, " %f%Le%Fd%s allow redundant constraints",
410 &TextWindow::ScreenChangeGroupOption,
411 g->allowRedundant ? CHECK_TRUE : CHECK_FALSE);
412
413 Printf(false, " %f%Ld%Fd%s treat all dimensions as reference",
414 &TextWindow::ScreenChangeGroupOption,
415 g->allDimsReference ? CHECK_TRUE : CHECK_FALSE);
416
417 if(g->booleanFailed) {
418 Printf(false, "");
419 Printf(false, "The Boolean operation failed. It may be ");
420 Printf(false, "possible to fix the problem by choosing ");
421 Printf(false, "'force NURBS surfaces to triangle mesh'.");
422 }
423
424 list_items:
425 Printf(false, "");
426 Printf(false, "%Ft requests in group");
427
428 int i, a = 0;
429 for(i = 0; i < SK.request.n; i++) {
430 Request *r = &(SK.request.elem[i]);
431
432 if(r->group.v == shown.group.v) {
433 std::string s = r->DescriptionString();
434 Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
435 (a & 1) ? 'd' : 'a',
436 r->h.v, (&TextWindow::ScreenSelectRequest),
437 &(TextWindow::ScreenHoverRequest), s.c_str());
438 a++;
439 }
440 }
441 if(a == 0) Printf(false, "%Ba (none)");
442
443 a = 0;
444 Printf(false, "");
445 Printf(false, "%Ft constraints in group (%d DOF)", g->solved.dof);
446 for(i = 0; i < SK.constraint.n; i++) {
447 Constraint *c = &(SK.constraint.elem[i]);
448
449 if(c->group.v == shown.group.v) {
450 std::string s = c->DescriptionString();
451 Printf(false, "%Bp %Fl%Ll%D%f%h%s%E %s",
452 (a & 1) ? 'd' : 'a',
453 c->h.v, (&TextWindow::ScreenSelectConstraint),
454 (&TextWindow::ScreenHoverConstraint), s.c_str(),
455 c->reference ? "(ref)" : "");
456 a++;
457 }
458 }
459 if(a == 0) Printf(false, "%Ba (none)");
460 }
461
462 //-----------------------------------------------------------------------------
463 // The screen that's displayed when the sketch fails to solve. A report of
464 // what failed, and (if the problem is a singular Jacobian) a list of
465 // constraints that could be removed to fix it.
466 //-----------------------------------------------------------------------------
ScreenAllowRedundant(int link,uint32_t v)467 void TextWindow::ScreenAllowRedundant(int link, uint32_t v) {
468 SS.UndoRemember();
469
470 Group *g = SK.GetGroup(SS.TW.shown.group);
471 g->allowRedundant = true;
472 SS.GenerateAll();
473
474 SS.TW.shown.screen = SCREEN_GROUP_INFO;
475 SS.TW.Show();
476 }
ShowGroupSolveInfo(void)477 void TextWindow::ShowGroupSolveInfo(void) {
478 Group *g = SK.GetGroup(shown.group);
479 if(g->IsSolvedOkay()) {
480 // Go back to the default group info screen
481 shown.screen = SCREEN_GROUP_INFO;
482 Show();
483 return;
484 }
485
486 Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str());
487 switch(g->solved.how) {
488 case System::DIDNT_CONVERGE:
489 Printf(true, "%FxSOLVE FAILED!%Fd unsolvable constraints");
490 Printf(true, "the following constraints are incompatible");
491 break;
492
493 case System::REDUNDANT_DIDNT_CONVERGE:
494 Printf(true, "%FxSOLVE FAILED!%Fd unsolvable constraints");
495 Printf(true, "the following constraints are unsatisfied");
496 break;
497
498 case System::REDUNDANT_OKAY:
499 Printf(true, "%FxSOLVE FAILED!%Fd redundant constraints");
500 Printf(true, "remove any one of these to fix it");
501 break;
502
503 case System::TOO_MANY_UNKNOWNS:
504 Printf(true, "Too many unknowns in a single group!");
505 return;
506 }
507
508 for(int i = 0; i < g->solved.remove.n; i++) {
509 hConstraint hc = g->solved.remove.elem[i];
510 Constraint *c = SK.constraint.FindByIdNoOops(hc);
511 if(!c) continue;
512
513 Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
514 (i & 1) ? 'd' : 'a',
515 c->h.v, (&TextWindow::ScreenSelectConstraint),
516 (&TextWindow::ScreenHoverConstraint),
517 c->DescriptionString().c_str());
518 }
519
520 Printf(true, "It may be possible to fix the problem ");
521 Printf(false, "by selecting Edit -> Undo.");
522
523 if(g->solved.how == System::REDUNDANT_OKAY) {
524 Printf(true, "It is possible to suppress this error ");
525 Printf(false, "by %Fl%f%Llallowing redundant constraints%E in ",
526 &TextWindow::ScreenAllowRedundant);
527 Printf(false, "this group.");
528 }
529 }
530
531 //-----------------------------------------------------------------------------
532 // When we're stepping a dimension. User specifies the finish value, and
533 // how many steps to take in between current and finish, re-solving each
534 // time.
535 //-----------------------------------------------------------------------------
ScreenStepDimFinish(int link,uint32_t v)536 void TextWindow::ScreenStepDimFinish(int link, uint32_t v) {
537 SS.TW.edit.meaning = EDIT_STEP_DIM_FINISH;
538 std::string edit_value;
539 if(SS.TW.shown.dimIsDistance) {
540 edit_value = SS.MmToString(SS.TW.shown.dimFinish);
541 } else {
542 edit_value = ssprintf("%.3f", SS.TW.shown.dimFinish);
543 }
544 SS.TW.ShowEditControl(12, edit_value);
545 }
ScreenStepDimSteps(int link,uint32_t v)546 void TextWindow::ScreenStepDimSteps(int link, uint32_t v) {
547 SS.TW.edit.meaning = EDIT_STEP_DIM_STEPS;
548 SS.TW.ShowEditControl(12, ssprintf("%d", SS.TW.shown.dimSteps));
549 }
ScreenStepDimGo(int link,uint32_t v)550 void TextWindow::ScreenStepDimGo(int link, uint32_t v) {
551 hConstraint hc = SS.TW.shown.constraint;
552 Constraint *c = SK.constraint.FindByIdNoOops(hc);
553 if(c) {
554 SS.UndoRemember();
555 double start = c->valA, finish = SS.TW.shown.dimFinish;
556 int i, n = SS.TW.shown.dimSteps;
557 for(i = 1; i <= n; i++) {
558 c = SK.GetConstraint(hc);
559 c->valA = start + ((finish - start)*i)/n;
560 SS.MarkGroupDirty(c->group);
561 SS.GenerateAll();
562 if(!SS.ActiveGroupsOkay()) {
563 // Failed to solve, so quit
564 break;
565 }
566 PaintGraphics();
567 }
568 }
569 InvalidateGraphics();
570 SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
571 }
ShowStepDimension(void)572 void TextWindow::ShowStepDimension(void) {
573 Constraint *c = SK.constraint.FindByIdNoOops(shown.constraint);
574 if(!c) {
575 shown.screen = SCREEN_LIST_OF_GROUPS;
576 Show();
577 return;
578 }
579
580 Printf(true, "%FtSTEP DIMENSION%E %s", c->DescriptionString().c_str());
581
582 if(shown.dimIsDistance) {
583 Printf(true, "%Ba %Ftstart%E %s", SS.MmToString(c->valA).c_str());
584 Printf(false, "%Bd %Ftfinish%E %s %Fl%Ll%f[change]%E",
585 SS.MmToString(shown.dimFinish).c_str(), &ScreenStepDimFinish);
586 } else {
587 Printf(true, "%Ba %Ftstart%E %@", c->valA);
588 Printf(false, "%Bd %Ftfinish%E %@ %Fl%Ll%f[change]%E",
589 shown.dimFinish, &ScreenStepDimFinish);
590 }
591 Printf(false, "%Ba %Ftsteps%E %d %Fl%Ll%f%D[change]%E",
592 shown.dimSteps, &ScreenStepDimSteps);
593
594 Printf(true, " %Fl%Ll%fstep dimension now%E", &ScreenStepDimGo);
595
596 Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
597 }
598
599 //-----------------------------------------------------------------------------
600 // When we're creating tangent arcs (as requests, not as some parametric
601 // thing). User gets to specify the radius, and whether the old untrimmed
602 // curves are kept or deleted.
603 //-----------------------------------------------------------------------------
ScreenChangeTangentArc(int link,uint32_t v)604 void TextWindow::ScreenChangeTangentArc(int link, uint32_t v) {
605 switch(link) {
606 case 'r': {
607 SS.TW.edit.meaning = EDIT_TANGENT_ARC_RADIUS;
608 SS.TW.ShowEditControl(3, SS.MmToString(SS.tangentArcRadius));
609 break;
610 }
611
612 case 'a': SS.tangentArcManual = !SS.tangentArcManual; break;
613 case 'd': SS.tangentArcDeleteOld = !SS.tangentArcDeleteOld; break;
614 }
615 }
ShowTangentArc(void)616 void TextWindow::ShowTangentArc(void) {
617 Printf(true, "%FtTANGENT ARC PARAMETERS%E");
618
619 Printf(true, "%Ft radius of created arc%E");
620 if(SS.tangentArcManual) {
621 Printf(false, "%Ba %s %Fl%Lr%f[change]%E",
622 SS.MmToString(SS.tangentArcRadius).c_str(),
623 &(TextWindow::ScreenChangeTangentArc));
624 } else {
625 Printf(false, "%Ba automatic");
626 }
627
628 Printf(false, "");
629 Printf(false, " %Fd%f%La%s choose radius automatically%E",
630 &ScreenChangeTangentArc,
631 !SS.tangentArcManual ? CHECK_TRUE : CHECK_FALSE);
632 Printf(false, " %Fd%f%Ld%s delete original entities afterward%E",
633 &ScreenChangeTangentArc,
634 SS.tangentArcDeleteOld ? CHECK_TRUE : CHECK_FALSE);
635
636 Printf(false, "");
637 Printf(false, "To create a tangent arc at a point,");
638 Printf(false, "select that point and then choose");
639 Printf(false, "Sketch -> Tangent Arc at Point.");
640 Printf(true, "(or %Fl%Ll%fback to home screen%E)", &ScreenHome);
641 }
642
643 //-----------------------------------------------------------------------------
644 // The edit control is visible, and the user just pressed enter.
645 //-----------------------------------------------------------------------------
EditControlDone(const char * s)646 void TextWindow::EditControlDone(const char *s) {
647 edit.showAgain = false;
648
649 switch(edit.meaning) {
650 case EDIT_TIMES_REPEATED: {
651 Expr *e = Expr::From(s, true);
652 if(e) {
653 SS.UndoRemember();
654
655 double ev = e->Eval();
656 if((int)ev < 1) {
657 Error("Can't repeat fewer than 1 time.");
658 break;
659 }
660 if((int)ev > 999) {
661 Error("Can't repeat more than 999 times.");
662 break;
663 }
664
665 Group *g = SK.GetGroup(edit.group);
666 g->valA = ev;
667
668 if(g->type == Group::ROTATE) {
669 int i, c = 0;
670 for(i = 0; i < SK.constraint.n; i++) {
671 if(SK.constraint.elem[i].group.v == g->h.v) c++;
672 }
673 // If the group does not contain any constraints, then
674 // set the numerical guess to space the copies uniformly
675 // over one rotation. Don't touch the guess if we're
676 // already constrained, because that would break
677 // convergence.
678 if(c == 0) {
679 double copies = (g->skipFirst) ? (ev + 1) : ev;
680 SK.GetParam(g->h.param(3))->val = PI/(2*copies);
681 }
682 }
683
684 SS.MarkGroupDirty(g->h);
685 SS.ScheduleGenerateAll();
686 }
687 break;
688 }
689 case EDIT_GROUP_NAME: {
690 if(!*s) {
691 Error("Group name cannot be empty");
692 } else {
693 SS.UndoRemember();
694
695 Group *g = SK.GetGroup(edit.group);
696 g->name = s;
697 }
698 break;
699 }
700 case EDIT_GROUP_SCALE: {
701 Expr *e = Expr::From(s, true);
702 if(e) {
703 double ev = e->Eval();
704 if(fabs(ev) < 1e-6) {
705 Error("Scale cannot be zero.");
706 } else {
707 Group *g = SK.GetGroup(edit.group);
708 g->scale = ev;
709 SS.MarkGroupDirty(g->h);
710 SS.ScheduleGenerateAll();
711 }
712 }
713 break;
714 }
715 case EDIT_GROUP_COLOR: {
716 Vector rgb;
717 if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
718 rgb = rgb.ClampWithin(0, 1);
719
720 Group *g = SK.group.FindByIdNoOops(SS.TW.shown.group);
721 if(!g) break;
722 g->color = RGBf(rgb.x, rgb.y, rgb.z);
723
724 SS.MarkGroupDirty(g->h);
725 SS.ScheduleGenerateAll();
726 SS.GW.ClearSuper();
727 } else {
728 Error("Bad format: specify color as r, g, b");
729 }
730 break;
731 }
732 case EDIT_GROUP_OPACITY: {
733 Expr *e = Expr::From(s, true);
734 if(e) {
735 double alpha = e->Eval();
736 if(alpha < 0 || alpha > 1) {
737 Error("Opacity must be between zero and one.");
738 } else {
739 Group *g = SK.GetGroup(edit.group);
740 g->color.alpha = (int)(255.1f * alpha);
741 SS.MarkGroupDirty(g->h);
742 SS.ScheduleGenerateAll();
743 SS.GW.ClearSuper();
744 }
745 }
746 break;
747 }
748 case EDIT_TTF_TEXT: {
749 SS.UndoRemember();
750 Request *r = SK.request.FindByIdNoOops(edit.request);
751 if(r) {
752 r->str = s;
753 SS.MarkGroupDirty(r->group);
754 SS.ScheduleGenerateAll();
755 }
756 break;
757 }
758 case EDIT_STEP_DIM_FINISH: {
759 Expr *e = Expr::From(s, true);
760 if(!e) {
761 break;
762 }
763 if(shown.dimIsDistance) {
764 shown.dimFinish = SS.ExprToMm(e);
765 } else {
766 shown.dimFinish = e->Eval();
767 }
768 break;
769 }
770 case EDIT_STEP_DIM_STEPS:
771 shown.dimSteps = min(300, max(1, atoi(s)));
772 break;
773
774 case EDIT_TANGENT_ARC_RADIUS: {
775 Expr *e = Expr::From(s, true);
776 if(!e) break;
777 if(e->Eval() < LENGTH_EPS) {
778 Error("Radius cannot be zero or negative.");
779 break;
780 }
781 SS.tangentArcRadius = SS.ExprToMm(e);
782 break;
783 }
784
785 default: {
786 int cnt = 0;
787 if(EditControlDoneForStyles(s)) cnt++;
788 if(EditControlDoneForConfiguration(s)) cnt++;
789 if(EditControlDoneForPaste(s)) cnt++;
790 if(EditControlDoneForView(s)) cnt++;
791 if(cnt > 1) {
792 // The identifiers were somehow assigned not uniquely?
793 oops();
794 }
795 break;
796 }
797 }
798 InvalidateGraphics();
799 SS.ScheduleShowTW();
800
801 if(!edit.showAgain) {
802 HideEditControl();
803 edit.meaning = EDIT_NOTHING;
804 }
805 }
806
807