diff --git a/draw.cpp b/draw.cpp index ddefaf7..73bd6a0 100644 --- a/draw.cpp +++ b/draw.cpp @@ -1018,7 +1018,9 @@ void GraphicsWindow::Paint(int w, int h) { glDepthFunc(GL_LEQUAL); if(SS.AllGroupsOkay()) { - glClearColor(0, 0, 0, 1.0f); + glClearColor(REDf(SS.backgroundColor), + GREENf(SS.backgroundColor), + BLUEf(SS.backgroundColor), 1.0f); } else { // Draw a different background whenever we're having solve problems. DWORD rgb = Style::Color(Style::DRAW_ERROR); diff --git a/drawentity.cpp b/drawentity.cpp index 1153579..19360a6 100644 --- a/drawentity.cpp +++ b/drawentity.cpp @@ -10,14 +10,70 @@ char *Entity::DescriptionString(void) { } } -void Entity::LineDrawOrGetDistance(Vector a, Vector b) { +void Entity::FatLineEndcap(Vector p, Vector u, Vector v) { + // A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10 + static const double Circle[11][2] = { + { 0.0000, 1.0000 }, + { -0.3090, 0.9511 }, + { -0.5878, 0.8090 }, + { -0.8090, 0.5878 }, + { -0.9511, 0.3090 }, + { -1.0000, 0.0000 }, + { -0.9511, -0.3090 }, + { -0.8090, -0.5878 }, + { -0.5878, -0.8090 }, + { -0.3090, -0.9511 }, + { 0.0000, -1.0000 }, + }; + glBegin(GL_TRIANGLE_FAN); + for(int i = 0; i <= 10; i++) { + double c = Circle[i][0], s = Circle[i][1]; + glxVertex3v(p.Plus(u.ScaledBy(c)).Plus(v.ScaledBy(s))); + } + glEnd(); +} + +void Entity::FatLine(Vector a, Vector b) { + // The half-width of the line we're drawing. + double hw = (dogd.lineWidth/SS.GW.scale) / 2; + Vector ab = b.Minus(a); + Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp); + Vector abn = (ab.Cross(gn)).WithMagnitude(1); + abn = abn.Minus(gn.ScaledBy(gn.Dot(abn))); + // So now abn is normal to the projection of ab into the screen, so the + // line will always have constant thickness as the view is rotated. + + abn = abn.WithMagnitude(hw); + ab = gn.Cross(abn); + ab = ab. WithMagnitude(hw); + + // The body of a line is a quad + glBegin(GL_QUADS); + glxVertex3v(a.Minus(abn)); + glxVertex3v(b.Minus(abn)); + glxVertex3v(b.Plus (abn)); + glxVertex3v(a.Plus (abn)); + glEnd(); + // And the line has two semi-circular end caps. + FatLineEndcap(a, ab, abn); + FatLineEndcap(b, ab.ScaledBy(-1), abn); +} + +void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat) { if(dogd.drawing) { // Draw lines from active group in front of those from previous glxDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3); - glBegin(GL_LINES); - glxVertex3v(a); - glxVertex3v(b); - glEnd(); + // Narrow lines are drawn as lines, but fat lines must be drawn as + // filled polygons, to get the line join style right. + if(!maybeFat || dogd.lineWidth < 3) { + glBegin(GL_LINES); + glxVertex3v(a); + glxVertex3v(b); + glEnd(); + } else { + FatLine(a, b); + } + glxDepthRangeOffset(0); } else { Point2d ap = SS.GW.ProjectPoint(a); @@ -100,7 +156,8 @@ void Entity::DrawAll(void) { void Entity::Draw(void) { hStyle hs = Style::ForEntity(h); - glLineWidth(Style::Width(hs)); + dogd.lineWidth = Style::Width(hs); + glLineWidth((float)dogd.lineWidth); glxColorRGB(Style::Color(hs)); dogd.drawing = true; @@ -479,7 +536,7 @@ void Entity::DrawOrGetDistance(void) { int i; for(i = 0; i < sel.l.n; i++) { SEdge *se = &(sel.l.elem[i]); - LineDrawOrGetDistance(se->a, se->b); + LineDrawOrGetDistance(se->a, se->b, true); } sel.Clear(); } diff --git a/dsc.h b/dsc.h index 4af4082..11403a8 100644 --- a/dsc.h +++ b/dsc.h @@ -219,15 +219,18 @@ public: int n; int elemsAllocated; - H AddAndAssignId(T *t) { - int i; + DWORD MaximumId(void) { DWORD id = 0; + int i; for(i = 0; i < n; i++) { id = max(id, elem[i].h.v); } + return id; + } - t->h.v = (id + 1); + H AddAndAssignId(T *t) { + t->h.v = (MaximumId() + 1); Add(t); return t->h; diff --git a/file.cpp b/file.cpp index 814df0f..17dee19 100644 --- a/file.cpp +++ b/file.cpp @@ -152,6 +152,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'c', "Constraint.disp.style", 'x', &(SS.sv.c.disp.style) }, { 's', "Style.h.v", 'x', &(SS.sv.s.h.v) }, + { 's', "Style.name", 'N', &(SS.sv.s.name) }, { 's', "Style.width", 'f', &(SS.sv.s.width) }, { 's', "Style.widthHow", 'd', &(SS.sv.s.widthHow) }, { 's', "Style.color", 'x', &(SS.sv.s.color) }, diff --git a/graphicswin.cpp b/graphicswin.cpp index 4555327..510af86 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -585,9 +585,16 @@ void GraphicsWindow::MenuEdit(int id) { switch(id) { case MNU_UNSELECT_ALL: SS.GW.GroupSelection(); + // If there's nothing selected to de-select, and no operation + // to cancel, then perhaps they want to return to the home + // screen in the text window. if(SS.GW.gs.n == 0 && SS.GW.pending.operation == 0) { if(!TextEditControlIsVisible()) { - SS.TW.ClearSuper(); + if(SS.TW.shown.screen == TextWindow::SCREEN_STYLE_INFO) { + SS.TW.GoToScreen(TextWindow::SCREEN_LIST_OF_STYLES); + } else { + SS.TW.ClearSuper(); + } } } SS.GW.ClearSuper(); diff --git a/request.cpp b/request.cpp index 21f16e4..f45abe5 100644 --- a/request.cpp +++ b/request.cpp @@ -65,6 +65,7 @@ void Request::Generate(IdList *entity, // Generate the entity that's specific to this request. e.type = et; e.group = group; + e.style = style; e.workplane = workplane; e.construction = construction; e.str.strcpy(str.str); @@ -79,6 +80,7 @@ void Request::Generate(IdList *entity, // points start from entity 1, except for datum point case p.h = h.entity(i+(et ? 1 : 0)); p.group = group; + p.style = style; if(workplane.v == Entity::FREE_IN_3D.v) { p.type = Entity::POINT_IN_3D; @@ -101,6 +103,7 @@ void Request::Generate(IdList *entity, n.workplane = workplane; n.h = h.entity(32); n.group = group; + n.style = style; if(workplane.v == Entity::FREE_IN_3D.v) { n.type = Entity::NORMAL_IN_3D; n.param[0] = AddParam(param, h.param(32+0)); @@ -125,6 +128,7 @@ void Request::Generate(IdList *entity, d.workplane = workplane; d.h = h.entity(64); d.group = group; + d.style = style; d.type = Entity::DISTANCE; d.param[0] = AddParam(param, h.param(64)); entity->Add(&d); diff --git a/sketch.h b/sketch.h index 9aef0ec..5eb23a1 100644 --- a/sketch.h +++ b/sketch.h @@ -414,8 +414,11 @@ public: Point2d mp; double dmin; Vector refp; + double lineWidth; } dogd; // state for drawing or getting distance (for hit testing) - void LineDrawOrGetDistance(Vector a, Vector b); + void FatLine(Vector a, Vector b); + void FatLineEndcap(Vector p, Vector u, Vector v); + void LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat=false); void DrawOrGetDistance(void); bool IsVisible(void); @@ -620,12 +623,12 @@ public: static const int DRAW_ERROR = 12; static const int DIM_SOLID = 13; - static const int FIRST_CUSTOM = 0x1000; + static const int FIRST_CUSTOM = 0x100; NameStr name; - static const int WIDTH_MM = 0; - static const int WIDTH_PIXELS = 1; + static const int WIDTH_AS_MM = 0; + static const int WIDTH_AS_PIXELS = 1; double width; int widthHow; DWORD color; @@ -645,6 +648,8 @@ public: static char *CnfColor(char *prefix); static char *CnfWidth(char *prefix); static char *CnfPrefixToName(char *prefix); + + static void CreateAllDefaultStyles(void); static void CreateDefaultStyle(hStyle h); static void FreezeDefaultStyles(void); static void LoadFactoryDefaults(void); @@ -655,6 +660,8 @@ public: static DWORD Color(int hs, bool forExport=false); static float Width(int hs); static hStyle ForEntity(hEntity he); + + char *DescriptionString(void); }; diff --git a/solvespace.cpp b/solvespace.cpp index 2f5333e..579bc60 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -74,6 +74,8 @@ void SolveSpace::Init(char *cmdLine) { exportShadedTriangles = CnfThawDWORD(1, "ExportShadedTriangles"); // Export pwl curves (instead of exact) always exportPwlCurves = CnfThawDWORD(0, "ExportPwlCurves"); + // Background color on-screen + backgroundColor = CnfThawDWORD(RGB(0, 0, 0), "BackgroundColor"); // Whether export canvas size is fixed or derived from bbox exportCanvasSizeAuto = CnfThawDWORD(1, "ExportCanvasSizeAuto"); // Margins for automatic canvas size @@ -156,6 +158,8 @@ void SolveSpace::Exit(void) { CnfFreezeDWORD(exportShadedTriangles, "ExportShadedTriangles"); // Export pwl curves (instead of exact) always CnfFreezeDWORD(exportPwlCurves, "ExportPwlCurves"); + // Background color on-screen + CnfFreezeDWORD(backgroundColor, "BackgroundColor"); // Whether export canvas size is fixed or derived from bbox CnfFreezeDWORD(exportCanvasSizeAuto, "ExportCanvasSizeAuto"); // Margins for automatic canvas size @@ -274,6 +278,10 @@ void SolveSpace::AfterNewFile(void) { // Then zoom to fit again, to fit the triangles GW.ZoomToFit(false); + // Create all the default styles; they'll get created on the fly anyways, + // but can't hurt to do it now. + Style::CreateAllDefaultStyles(); + UpdateWindowTitle(); } diff --git a/solvespace.h b/solvespace.h index 9de5233..1adba2c 100644 --- a/solvespace.h +++ b/solvespace.h @@ -207,6 +207,7 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, double a41, double a42, double a43, double a44); void MakePathRelative(char *base, char *path); void MakePathAbsolute(char *base, char *path); +bool StringAllPrintable(char *str); class System { public: @@ -531,6 +532,7 @@ public: float exportOffset; int drawBackFaces; int showToolbar; + DWORD backgroundColor; int exportShadedTriangles; int exportPwlCurves; int exportCanvasSizeAuto; diff --git a/style.cpp b/style.cpp index f63bed4..2f0c178 100644 --- a/style.cpp +++ b/style.cpp @@ -1,5 +1,7 @@ #include "solvespace.h" +#define clamp01(x) (max(0, min(1, (x)))) + const Style::Default Style::Defaults[] = { { ACTIVE_GRP, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, }, { CONSTRUCTION, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, }, @@ -30,7 +32,9 @@ char *Style::CnfWidth(char *prefix) { char *Style::CnfPrefixToName(char *prefix) { static char name[100]; - int i = 0, j = 0; + int i = 0, j; + strcpy(name, "def-"); + j = 4; while(prefix[i] && j < 90) { if(isupper(prefix[i]) && i != 0) { name[j++] = '-'; @@ -42,7 +46,15 @@ char *Style::CnfPrefixToName(char *prefix) { return name; } +void Style::CreateAllDefaultStyles(void) { + const Default *d; + for(d = &(Defaults[0]); d->h.v; d++) { + (void)Get(d->h); + } +} + void Style::CreateDefaultStyle(hStyle h) { + bool isDefaultStyle = true; const Default *d; for(d = &(Defaults[0]); d->h.v; d++) { if(d->h.v == h.v) break; @@ -51,17 +63,22 @@ void Style::CreateDefaultStyle(hStyle h) { // Not a default style; so just create it the same as our default // active group entity style. d = &(Defaults[0]); + isDefaultStyle = false; } Style ns; ZERO(&ns); ns.color = CnfThawDWORD(d->color, CnfColor(d->cnfPrefix)); ns.width = CnfThawFloat((float)(d->width), CnfWidth(d->cnfPrefix)); - ns.widthHow = WIDTH_PIXELS; + ns.widthHow = WIDTH_AS_PIXELS; ns.visible = true; ns.exportable = true; - ns.name.strcpy(CnfPrefixToName(d->cnfPrefix)); ns.h = h; + if(isDefaultStyle) { + ns.name.strcpy(CnfPrefixToName(d->cnfPrefix)); + } else { + ns.name.strcpy("new-custom-style"); + } SK.style.Add(&ns); } @@ -73,11 +90,12 @@ void Style::LoadFactoryDefaults(void) { s->color = d->color; s->width = d->width; - s->widthHow = WIDTH_PIXELS; + s->widthHow = WIDTH_AS_PIXELS; s->visible = true; s->exportable = true; s->name.strcpy(CnfPrefixToName(d->cnfPrefix)); } + SS.backgroundColor = RGB(0, 0, 0); } void Style::FreezeDefaultStyles(void) { @@ -131,9 +149,9 @@ DWORD Style::Color(hStyle h, bool forExport) { float Style::Width(hStyle h) { double r = 1.0; Style *s = Get(h); - if(s->widthHow == WIDTH_MM) { + if(s->widthHow == WIDTH_AS_MM) { r = s->width * SS.GW.scale; - } else if(s->widthHow == WIDTH_PIXELS) { + } else if(s->widthHow == WIDTH_AS_PIXELS) { r = s->width; } // This returns a float because glLineWidth expects a float, avoid casts. @@ -165,3 +183,255 @@ hStyle Style::ForEntity(hEntity he) { return hs; } +char *Style::DescriptionString(void) { + static char ret[100]; + if(name.str[0]) { + sprintf(ret, "s%03x-%s", h.v, name.str); + } else { + sprintf(ret, "s%03x-(unnamed)", h.v); + } + return ret; +} + + +void TextWindow::ScreenShowListOfStyles(int link, DWORD v) { + SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); +} +void TextWindow::ScreenShowStyleInfo(int link, DWORD v) { + SS.TW.GoToScreen(SCREEN_STYLE_INFO); + SS.TW.shown.style.v = v; +} + +void TextWindow::ScreenLoadFactoryDefaultStyles(int link, DWORD v) { + Style::LoadFactoryDefaults(); + SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); +} + +void TextWindow::ScreenCreateCustomStyle(int link, DWORD v) { + SS.UndoRemember(); + DWORD vs = max(Style::FIRST_CUSTOM, SK.style.MaximumId() + 1); + hStyle hs = { vs }; + (void)Style::Get(hs); +} + +void TextWindow::ScreenChangeBackgroundColor(int link, DWORD v) { + DWORD rgb = SS.backgroundColor; + char str[300]; + sprintf(str, "%.2f, %.2f, %.2f", REDf(rgb), GREENf(rgb), BLUEf(rgb)); + ShowTextEditControl(v, 3, str); + SS.TW.edit.meaning = EDIT_BACKGROUND_COLOR; +} + +void TextWindow::ShowListOfStyles(void) { + Printf(true, "%Ft color style-name"); + + bool darkbg = false; + Style *s; + for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) { + Printf(false, "%Bp %Bp %Bp %Fl%Ll%f%D%s%E", + darkbg ? 'd' : 'a', + 0x80000000 | s->color, + darkbg ? 'd' : 'a', + ScreenShowStyleInfo, s->h.v, + s->DescriptionString()); + + darkbg = !darkbg; + } + + Printf(true, " %Fl%Ll%fcreate a new custom style%E", + &ScreenCreateCustomStyle); + + Printf(false, ""); + + DWORD rgb = SS.backgroundColor; + Printf(false, "%Ft background color (r, g, b)%E"); + Printf(false, "%Ba %@, %@, %@ %Fl%D%f%Ll[change]%E", + REDf(rgb), GREENf(rgb), BLUEf(rgb), + top[rows-1] + 2, &ScreenChangeBackgroundColor); + + Printf(false, ""); + Printf(false, " %Fl%Ll%fload factory defaults%E", + &ScreenLoadFactoryDefaultStyles); +} + + +void TextWindow::ScreenChangeStyleName(int link, DWORD v) { + hStyle hs = { v }; + Style *s = Style::Get(hs); + ShowTextEditControl(10, 13, s->name.str); + SS.TW.edit.style = hs; + SS.TW.edit.meaning = EDIT_STYLE_NAME; +} + +void TextWindow::ScreenDeleteStyle(int link, DWORD v) { + SS.UndoRemember(); + hStyle hs = { v }; + Style *s = SK.style.FindByIdNoOops(hs); + if(s) { + SK.style.RemoveById(hs); + // And it will get recreated automatically if something is still using + // the style, so no need to do anything else. + } + SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); + InvalidateGraphics(); +} + +void TextWindow::ScreenChangeStyleWidth(int link, DWORD v) { + hStyle hs = { v }; + Style *s = Style::Get(hs); + char str[300]; + if(s->widthHow == Style::WIDTH_AS_PIXELS) { + sprintf(str, "%.2f", s->width); + } else { + strcpy(str, SS.MmToString(s->width)); + } + ShowTextEditControl(16, 8, str); + SS.TW.edit.style = hs; + SS.TW.edit.meaning = EDIT_STYLE_WIDTH; +} + +void TextWindow::ScreenChangeStyleColor(int link, DWORD v) { + hStyle hs = { v }; + Style *s = Style::Get(hs); + char str[300]; + sprintf(str, "%.2f, %.2f, %.2f", + REDf(s->color), GREENf(s->color), BLUEf(s->color)); + ShowTextEditControl(13, 12, str); + SS.TW.edit.style = hs; + SS.TW.edit.meaning = EDIT_STYLE_COLOR; +} + +void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) { + SS.UndoRemember(); + hStyle hs = { v }; + Style *s = Style::Get(hs); + switch(link) { + case 'w': + if(s->widthHow == Style::WIDTH_AS_PIXELS) { + s->widthHow = Style::WIDTH_AS_MM; + } else { + s->widthHow = Style::WIDTH_AS_PIXELS; + } + break; + + case 'e': + s->exportable = !(s->exportable); + break; + + case 'v': + s->visible = !(s->visible); + break; + } + InvalidateGraphics(); +} + +bool TextWindow::EditControlDoneForStyles(char *str) { + Style *s; + switch(edit.meaning) { + case EDIT_STYLE_WIDTH: + SS.UndoRemember(); + s = Style::Get(edit.style); + if(s->widthHow == Style::WIDTH_AS_MM) { + s->width = SS.StringToMm(str); + } else { + s->width = atof(str); + } + s->width = max(0, s->width); + return true; + + case EDIT_BACKGROUND_COLOR: + case EDIT_STYLE_COLOR: { + double r, g, b; + if(sscanf(str, "%lf, %lf, %lf", &r, &g, &b)==3) { + r = clamp01(r); + g = clamp01(g); + b = clamp01(b); + if(edit.meaning == EDIT_STYLE_COLOR) { + SS.UndoRemember(); + s = Style::Get(edit.style); + s->color = RGBf(r, g, b); + } else { + SS.backgroundColor = RGBf(r, g, b); + } + } else { + Error("Bad format: specify color as r, g, b"); + } + return true; + } + case EDIT_STYLE_NAME: + if(!StringAllPrintable(str) || !*str) { + Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); + } else { + SS.UndoRemember(); + s = Style::Get(edit.style); + s->name.strcpy(str); + } + return true; + + default: return false; + } +} + +void TextWindow::ShowStyleInfo(void) { + Printf(true, "%Fl%f%Ll(back to list of styles)%E", &ScreenShowListOfStyles); + + Style *s = Style::Get(shown.style); + + if(s->h.v < Style::FIRST_CUSTOM) { + Printf(true, "%FtSTYLE %E%s ", s->DescriptionString()); + } else { + Printf(true, "%FtSTYLE %E%s " + "[%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]", + s->DescriptionString(), + s->h.v, &ScreenChangeStyleName, + s->h.v, &ScreenDeleteStyle); + } + + Printf(true, "%FtCOLOR %E%Bp %Bd (%@, %@, %@) %D%f%Ll%Fl[change]%E", + 0x80000000 | s->color, + REDf(s->color), GREENf(s->color), BLUEf(s->color), + s->h.v, ScreenChangeStyleColor); + + if(s->widthHow == Style::WIDTH_AS_PIXELS) { + Printf(true, "%FtWIDTH %E%@ %D%f%Ll%Fl[change]%E", + s->width, + s->h.v, &ScreenChangeStyleWidth); + } else { + Printf(true, "%FtWIDTH %E%s %D%f%Ll%Fl[change]%E", + SS.MmToString(s->width), + s->h.v, &ScreenChangeStyleWidth); + } + + char *unit = (SS.viewUnits == SolveSpace::UNIT_INCHES) ? "inches" : "mm"; + bool widthpx = (s->widthHow == Style::WIDTH_AS_PIXELS); + + if(s->h.v < Style::FIRST_CUSTOM) { + Printf(false,"%FtUNITS %Fspixels%E"); + } else { + Printf(false,"%FtUNITS %Fh%D%f%Lw%s%E%Fs%s%E / %Fh%D%f%Lw%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ( widthpx ? "" : "pixels"), + ( widthpx ? "pixels" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (!widthpx ? "" : unit), + (!widthpx ? unit : "")); + } + + if(s->h.v >= Style::FIRST_CUSTOM) { + Printf(true, "%FtSHOW %Fh%D%f%Lv%s%E%Fs%s%E / %Fh%D%f%Lv%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ( s->visible ? "" : "yes"), + ( s->visible ? "yes" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (!s->visible ? "" : "no"), + (!s->visible ? "no" : "")); + Printf(false,"%FtEXPORT %Fh%D%f%Le%s%E%Fs%s%E / %Fh%D%f%Le%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ( s->exportable ? "" : "yes"), + ( s->exportable ? "yes" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (!s->exportable ? "" : "no"), + (!s->exportable ? "no" : "")); + } +} + diff --git a/textscreens.cpp b/textscreens.cpp index c9a9cd2..53125f6 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -143,7 +143,8 @@ void TextWindow::ShowListOfGroups(void) { Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E", &(TextWindow::ScreenShowGroupsSpecial), &(TextWindow::ScreenShowGroupsSpecial)); - Printf(false, " %Fl%Ls%fconfiguration%E", + Printf(true, " %Fl%Ls%fline styles%E / %Fl%Ls%fconfiguration%E", + &(TextWindow::ScreenShowListOfStyles), &(TextWindow::ScreenShowConfiguration)); // Show license info @@ -915,7 +916,8 @@ void TextWindow::EditControlDone(char *s) { // already constrained, because that would break // convergence. if(c == 0) { - SK.GetParam(g->h.param(3))->val = PI/(2*ev); + double copies = (g->skipFirst) ? (ev + 1) : ev; + SK.GetParam(g->h.param(3))->val = PI/(2*copies); } } @@ -927,14 +929,7 @@ void TextWindow::EditControlDone(char *s) { break; } case EDIT_GROUP_NAME: { - char *t; - bool invalid = false; - for(t = s; *t; t++) { - if(!(isalnum(*t) || *t == '-' || *t == '_')) { - invalid = true; - } - } - if(invalid || !*s) { + if(!StringAllPrintable(s) || !*s) { Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); } else { SS.UndoRemember(); @@ -942,7 +937,6 @@ void TextWindow::EditControlDone(char *s) { Group *g = SK.GetGroup(edit.group); g->name.strcpy(s); } - SS.unsaved = true; break; } case EDIT_LIGHT_INTENSITY: @@ -1081,6 +1075,10 @@ void TextWindow::EditControlDone(char *s) { } break; } + + default: + EditControlDoneForStyles(s); + break; } InvalidateGraphics(); SS.later.showTW = true; diff --git a/textwin.cpp b/textwin.cpp index bee48e9..2ae8bd1 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -224,6 +224,8 @@ void TextWindow::Show(void) { case SCREEN_CONFIGURATION: ShowConfiguration(); break; case SCREEN_STEP_DIMENSION: ShowStepDimension(); break; case SCREEN_MESH_VOLUME: ShowMeshVolume(); break; + case SCREEN_LIST_OF_STYLES: ShowListOfStyles(); break; + case SCREEN_STYLE_INFO: ShowStyleInfo(); break; } } Printf(false, ""); diff --git a/ui.h b/ui.h index 0e3b0f2..32cc89f 100644 --- a/ui.h +++ b/ui.h @@ -52,10 +52,13 @@ public: static const int SCREEN_CONFIGURATION = 3; static const int SCREEN_STEP_DIMENSION = 4; static const int SCREEN_MESH_VOLUME = 5; + static const int SCREEN_LIST_OF_STYLES = 6; + static const int SCREEN_STYLE_INFO = 7; typedef struct { int screen; hGroup group; + hStyle style; hConstraint constraint; bool dimIsDistance; @@ -89,11 +92,17 @@ public: // For the step dimension screen static const int EDIT_STEP_DIM_FINISH = 40; static const int EDIT_STEP_DIM_STEPS = 41; + // For the styles stuff + static const int EDIT_STYLE_WIDTH = 50; + static const int EDIT_STYLE_COLOR = 51; + static const int EDIT_STYLE_NAME = 52; + static const int EDIT_BACKGROUND_COLOR = 53; struct { int meaning; int i; hGroup group; hRequest request; + hStyle style; } edit; static void ReportHowGroupSolved(hGroup hg); @@ -107,6 +116,8 @@ public: void ShowGroupInfo(void); void ShowGroupSolveInfo(void); void ShowConfiguration(void); + void ShowListOfStyles(void); + void ShowStyleInfo(void); void ShowStepDimension(void); void ShowMeshVolume(void); // Special screen, based on selection @@ -142,6 +153,13 @@ public: static void ScreenChangeHelixParameter(int link, DWORD v); static void ScreenColor(int link, DWORD v); + static void ScreenShowListOfStyles(int link, DWORD v); + static void ScreenShowStyleInfo(int link, DWORD v); + static void ScreenDeleteStyle(int link, DWORD v); + static void ScreenChangeStyleYesNo(int link, DWORD v); + static void ScreenCreateCustomStyle(int link, DWORD v); + static void ScreenLoadFactoryDefaultStyles(int link, DWORD v); + static void ScreenShowConfiguration(int link, DWORD v); static void ScreenGoToWebsite(int link, DWORD v); @@ -168,7 +186,12 @@ public: static void ScreenChangeCameraTangent(int link, DWORD v); static void ScreenChangeExportScale(int link, DWORD v); static void ScreenChangeExportOffset(int link, DWORD v); + static void ScreenChangeStyleName(int link, DWORD v); + static void ScreenChangeStyleWidth(int link, DWORD v); + static void ScreenChangeStyleColor(int link, DWORD v); + static void ScreenChangeBackgroundColor(int link, DWORD v); + bool EditControlDoneForStyles(char *s); void EditControlDone(char *s); }; diff --git a/util.cpp b/util.cpp index 9a462a7..02fd0b1 100644 --- a/util.cpp +++ b/util.cpp @@ -77,6 +77,17 @@ void MakePathAbsolute(char *basep, char *pathp) { strcpy(pathp, out); } +bool StringAllPrintable(char *str) +{ + char *t; + for(t = str; *t; t++) { + if(!(isalnum(*t) || *t == '-' || *t == '_')) { + return false; + } + } + return true; +} + void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, double a21, double a22, double a23, double a24, double a31, double a32, double a33, double a34,