diff --git a/Makefile b/Makefile index f3dbebe..8bfc6e1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ DEFINES = /D_WIN32_WINNT=0x500 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN32_LEAN_AND_MEAN /DWIN32 # Use the multi-threaded static libc because libpng and zlib do; not sure if anything bad # happens if those mix, but don't want to risk it. -CFLAGS = /W3 /nologo -MT -Iextlib -I..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /Zi /EHs /O2 +CFLAGS = /W3 /nologo -MT -Iextlib -I..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /Zi /EHs # /O2 HEADERS = ..\common\win32\freeze.h ui.h solvespace.h dsc.h sketch.h expr.h polygon.h srf\surface.h @@ -61,7 +61,7 @@ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib opengl32.lib g all: $(OBJDIR)/solvespace.exe @cp $(OBJDIR)/solvespace.exe . - solvespace fail.slvs + solvespace t8.slvs clean: rm -f obj/* diff --git a/draw.cpp b/draw.cpp index f81b128..ddefaf7 100644 --- a/draw.cpp +++ b/draw.cpp @@ -798,11 +798,9 @@ void GraphicsWindow::Selection::Clear(void) { void GraphicsWindow::Selection::Draw(void) { Vector refp; if(entity.v) { - glLineWidth(1.5); Entity *e = SK.GetEntity(entity); e->Draw(); if(emphasized) refp = e->GetReferencePos(); - glLineWidth(1); } if(constraint.v) { Constraint *c = SK.GetConstraint(constraint); @@ -810,13 +808,17 @@ void GraphicsWindow::Selection::Draw(void) { if(emphasized) refp = c->GetReferencePos(); } if(emphasized && (constraint.v || entity.v)) { + // We want to emphasize this constraint or entity, by drawing a thick + // line from the top left corner of the screen to the reference point + // of that entity or constraint. double s = 0.501/SS.GW.scale; Vector topLeft = SS.GW.projRight.ScaledBy(-SS.GW.width*s); topLeft = topLeft.Plus(SS.GW.projUp.ScaledBy(SS.GW.height*s)); topLeft = topLeft.Minus(SS.GW.offset); glLineWidth(40); - glColor4d(1.0, 1.0, 0, 0.2); + DWORD rgb = Style::Color(Style::HOVERED); + glColor4d(REDf(rgb), GREENf(rgb), BLUEf(rgb), 0.2); glBegin(GL_LINES); glxVertex3v(topLeft); glxVertex3v(refp); @@ -1018,8 +1020,9 @@ void GraphicsWindow::Paint(int w, int h) { if(SS.AllGroupsOkay()) { glClearColor(0, 0, 0, 1.0f); } else { - // Draw a red background whenever we're having solve problems. - glClearColor(0.4f, 0, 0, 1.0f); + // Draw a different background whenever we're having solve problems. + DWORD rgb = Style::Color(Style::DRAW_ERROR); + glClearColor(0.4f*REDf(rgb), 0.4f*GREENf(rgb), 0.4f*BLUEf(rgb), 1.0f); // And show the text window, which has info to debug it ForceTextWindowShown(); } @@ -1085,8 +1088,8 @@ void GraphicsWindow::Paint(int w, int h) { } // Draw the traced path, if one exists - glLineWidth(1); - glColor3d(0, 1, 1); + glLineWidth(Style::Width(Style::ANALYZE)); + glxColorRGB(Style::Color(Style::ANALYZE)); SContour *sc = &(SS.traced.path); glBegin(GL_LINE_STRIP); for(i = 0; i < sc->l.n; i++) { @@ -1095,21 +1098,23 @@ void GraphicsWindow::Paint(int w, int h) { glEnd(); // And the naked edges, if the user did Analyze -> Show Naked Edges. - glLineWidth(7); - glColor3d(1, 0, 0); + glLineWidth(Style::Width(Style::DRAW_ERROR)); + glxColorRGB(Style::Color(Style::DRAW_ERROR)); glxDrawEdges(&(SS.nakedEdges), true); // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); - glxLockColorTo(1, 1, 0); + glxLockColorTo(Style::Color(Style::HOVERED)); hover.Draw(); // And finally draw the selection, same mechanism. - glxLockColorTo(1, 0, 0); + glxLockColorTo(Style::Color(Style::SELECTED)); for(i = 0; i < MAX_SELECTED; i++) { selection[i].Draw(); } + glxUnlockColor(); + // And finally the toolbar. if(SS.showToolbar) { ToolbarDraw(); diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 6eab77e..49b22c0 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -350,7 +350,6 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale); Vector gn = (gr.Cross(gu)).WithMagnitude(1/SS.GW.scale); - glxColor3d(1, 0.1, 1); switch(type) { case PT_PT_DISTANCE: { Vector ap = SK.GetEntity(ptA)->PointGetNum(); @@ -465,12 +464,25 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + // Let's adjust the color of this constraint to have the same + // rough luma as the point color, so that the constraint does not + // stand out in an ugly way. + DWORD cd = Style::Color(Style::DATUM), + cc = Style::Color(Style::CONSTRAINT); + // convert from 8-bit color to a vector + Vector vd = Vector::From(REDf(cd), GREENf(cd), BLUEf(cd)), + vc = Vector::From(REDf(cc), GREENf(cc), BLUEf(cc)); + // and scale the constraint color to have the same magnitude as + // the datum color, maybe a bit dimmer + vc = vc.WithMagnitude(vd.Magnitude()*0.9); + // and set the color to that. + glxColorRGB(RGBf(vc.x, vc.y, vc.z)); + for(int a = 0; a < 2; a++) { Vector r = SS.GW.projRight.ScaledBy((a+1)/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy((2-a)/SS.GW.scale); for(int i = 0; i < 2; i++) { Vector p = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum(); - glxColor3d(0.4, 0, 0.4); glBegin(GL_QUADS); glxVertex3v(p.Plus (r).Plus (d)); glxVertex3v(p.Plus (r).Minus(d)); @@ -878,7 +890,10 @@ s: void Constraint::Draw(void) { dogd.drawing = true; dogd.sel = NULL; - glLineWidth(1); + + glLineWidth(Style::Width(Style::CONSTRAINT)); + glxColorRGB(Style::Color(Style::CONSTRAINT)); + DrawOrGetDistance(NULL); } diff --git a/drawentity.cpp b/drawentity.cpp index ecf4b88..1153579 100644 --- a/drawentity.cpp +++ b/drawentity.cpp @@ -40,7 +40,7 @@ void Entity::DrawAll(void) { double s = 3.5/SS.GW.scale; Vector r = SS.GW.projRight.ScaledBy(s); Vector d = SS.GW.projUp.ScaledBy(s); - glxColor3d(0, 0.8, 0); + glxColorRGB(Style::Color(Style::DATUM)); glxDepthRangeOffset(6); glBegin(GL_QUADS); for(i = 0; i < SK.entity.n; i++) { @@ -52,6 +52,9 @@ void Entity::DrawAll(void) { Vector v = e->PointGetNum(); + // If we're analyzing the sketch to show the degrees of freedom, + // then we draw big colored squares over the points that are + // free to move. bool free = false; if(e->type == POINT_IN_3D) { Param *px = SK.GetParam(e->param[0]), @@ -68,12 +71,12 @@ void Entity::DrawAll(void) { if(free) { Vector re = r.ScaledBy(2.5), de = d.ScaledBy(2.5); - glxColor3d(0, 1.0, 1.0); + glxColorRGB(Style::Color(Style::ANALYZE)); glxVertex3v(v.Plus (re).Plus (de)); glxVertex3v(v.Plus (re).Minus(de)); glxVertex3v(v.Minus(re).Minus(de)); glxVertex3v(v.Minus(re).Plus (de)); - glxColor3d(0, 0.8, 0); + glxColorRGB(Style::Color(Style::DATUM)); } glxVertex3v(v.Plus (r).Plus (d)); @@ -85,7 +88,6 @@ void Entity::DrawAll(void) { glxDepthRangeOffset(0); } - glLineWidth(1.5); for(i = 0; i < SK.entity.n; i++) { Entity *e = &(SK.entity.elem[i]); if(e->IsPoint()) @@ -94,10 +96,13 @@ void Entity::DrawAll(void) { } e->Draw(); } - glLineWidth(1); } void Entity::Draw(void) { + hStyle hs = Style::ForEntity(h); + glLineWidth(Style::Width(hs)); + glxColorRGB(Style::Color(hs)); + dogd.drawing = true; DrawOrGetDistance(); } @@ -302,14 +307,6 @@ void Entity::DrawOrGetDistance(void) { Group *g = SK.GetGroup(group); - if(group.v != SS.GW.activeGroup.v) { - glxColor3d(0.5, 0.3, 0.0); - } else if(construction) { - glxColor3d(0.1, 0.7, 0.1); - } else { - glxColor3d(1, 1, 1); - } - switch(type) { case POINT_N_COPY: case POINT_N_TRANS: @@ -325,7 +322,7 @@ void Entity::DrawOrGetDistance(void) { Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); - glxColor3d(0, 0.8, 0); + glxColorRGB(Style::Color(Style::DATUM)); glxDepthRangeOffset(6); glBegin(GL_QUADS); glxVertex3v(v.Plus (r).Plus (d)); @@ -356,15 +353,18 @@ void Entity::DrawOrGetDistance(void) { } hRequest hr = h.request(); - double f = (i == 0 ? 0.4 : 1); + // Always draw the x, y, and z axes in red, green, and blue; + // brighter for the ones at the bottom left of the screen, + // dimmer for the ones at the model origin. + int f = (i == 0 ? 100 : 255); if(hr.v == Request::HREQUEST_REFERENCE_XY.v) { - glxColor3d(0, 0, f); + glxColorRGB(RGB(0, 0, f)); } else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) { - glxColor3d(f, 0, 0); + glxColorRGB(RGB(f, 0, 0)); } else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) { - glxColor3d(0, f, 0); + glxColorRGB(RGB(0, f, 0)); } else { - glxColor3d(0, 0.4, 0.4); + glxColorRGB(Style::Color(Style::NORMALS)); if(i > 0) break; } @@ -396,7 +396,6 @@ void Entity::DrawOrGetDistance(void) { LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis,-0.6))); } glxDepthRangeLockToFront(false); - glLineWidth(1.5); break; } @@ -423,7 +422,7 @@ void Entity::DrawOrGetDistance(void) { Vector mp = p.Minus(us).Plus (vs); glLineWidth(1); - glxColor3d(0, 0.3, 0.3); + glxColorRGB(Style::Color(Style::NORMALS)); glEnable(GL_LINE_STIPPLE); glLineStipple(3, 0x1111); if(!h.isFromRequest()) { @@ -436,7 +435,6 @@ void Entity::DrawOrGetDistance(void) { LineDrawOrGetDistance(mm, mp); LineDrawOrGetDistance(mp, pp); glDisable(GL_LINE_STIPPLE); - glLineWidth(1.5); char *str = DescriptionString()+5; if(dogd.drawing) { diff --git a/file.cpp b/file.cpp index 353b0d5..814df0f 100644 --- a/file.cpp +++ b/file.cpp @@ -30,6 +30,7 @@ void SolveSpace::NewFile(void) { SK.constraint.Clear(); SK.request.Clear(); SK.group.Clear(); + SK.style.Clear(); SK.entity.Clear(); SK.param.Clear(); @@ -150,6 +151,13 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'c', "Constraint.disp.offset.z", 'f', &(SS.sv.c.disp.offset.z) }, { 'c', "Constraint.disp.style", 'x', &(SS.sv.c.disp.style) }, + { 's', "Style.h.v", 'x', &(SS.sv.s.h.v) }, + { 's', "Style.width", 'f', &(SS.sv.s.width) }, + { 's', "Style.widthHow", 'd', &(SS.sv.s.widthHow) }, + { 's', "Style.color", 'x', &(SS.sv.s.color) }, + { 's', "Style.visible", 'b', &(SS.sv.s.visible) }, + { 's', "Style.exportable", 'b', &(SS.sv.s.exportable) }, + { 0, NULL, NULL, NULL }, }; @@ -239,6 +247,14 @@ bool SolveSpace::SaveToFile(char *filename) { fprintf(fh, "AddConstraint\n\n"); } + for(i = 0; i < SK.style.n; i++) { + sv.s = SK.style.elem[i]; + if(sv.s.h.v >= Style::FIRST_CUSTOM) { + SaveUsingTable('s'); + fprintf(fh, "AddStyle\n\n"); + } + } + // A group will have either a mesh or a shell, but not both; but the code // to print either of those just does nothing if the mesh/shell is empty. @@ -363,6 +379,7 @@ bool SolveSpace::LoadFromFile(char *filename) { SK.group.Clear(); SK.entity.Clear(); SK.param.Clear(); + SK.style.Clear(); memset(&sv, 0, sizeof(sv)); char line[1024]; @@ -383,20 +400,23 @@ bool SolveSpace::LoadFromFile(char *filename) { LoadUsingTable(key, val); } else if(strcmp(line, "AddGroup")==0) { SK.group.Add(&(sv.g)); - memset(&(sv.g), 0, sizeof(sv.g)); + ZERO(&(sv.g)); } else if(strcmp(line, "AddParam")==0) { // params are regenerated, but we want to preload the values // for initial guesses SK.param.Add(&(sv.p)); - memset(&(sv.p), 0, sizeof(sv.p)); + ZERO(&(sv.p)); } else if(strcmp(line, "AddEntity")==0) { // entities are regenerated } else if(strcmp(line, "AddRequest")==0) { SK.request.Add(&(sv.r)); - memset(&(sv.r), 0, sizeof(sv.r)); + ZERO(&(sv.r)); } else if(strcmp(line, "AddConstraint")==0) { SK.constraint.Add(&(sv.c)); - memset(&(sv.c), 0, sizeof(sv.c)); + ZERO(&(sv.c)); + } else if(strcmp(line, "AddStyle")==0) { + SK.style.Add(&(sv.s)); + ZERO(&(sv.s)); } else if(strcmp(line, VERSION_STRING)==0) { // do nothing, version string } else if(StrStartsWith(line, "Triangle ") || diff --git a/glhelper.cpp b/glhelper.cpp index 22760e9..b8112a8 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -44,6 +44,7 @@ void glxWriteTextRefCenter(char *str, Vector t, Vector u, Vector v, static void LineDrawCallback(void *fndata, Vector a, Vector b) { + glLineWidth(1); glBegin(GL_LINES); glxVertex3v(a); glxVertex3v(b); @@ -95,26 +96,28 @@ void glxVertex3v(Vector u) glVertex3f((GLfloat)u.x, (GLfloat)u.y, (GLfloat)u.z); } -void glxLockColorTo(double r, double g, double b) +void glxLockColorTo(DWORD rgb) { - ColorLocked = false; - glxColor3d(r, g, b); + ColorLocked = false; + glColor3d(REDf(rgb), GREENf(rgb), BLUEf(rgb)); ColorLocked = true; } void glxUnlockColor(void) { - ColorLocked = false; + ColorLocked = false; } -void glxColor3d(double r, double g, double b) +void glxColorRGB(DWORD rgb) { - if(!ColorLocked) glColor3d(r, g, b); + // Is there a bug in some graphics drivers where this is not equivalent + // to glColor3d? There seems to be... + glxColorRGBa(rgb, 1.0); } -void glxColor4d(double r, double g, double b, double a) +void glxColorRGBa(DWORD rgb, double a) { - if(!ColorLocked) glColor4d(r, g, b, a); + if(!ColorLocked) glColor4d(REDf(rgb), GREENf(rgb), BLUEf(rgb), a); } static void Stipple(BOOL forSel) @@ -148,11 +151,11 @@ static void Stipple(BOOL forSel) } } -static void StippleTriangle(STriangle *tr, BOOL s, double r, double g, double b) +static void StippleTriangle(STriangle *tr, BOOL s, DWORD rgb) { glEnd(); glDisable(GL_LIGHTING); - glColor3d(r, g, b); + glxColorRGB(rgb); Stipple(s); glBegin(GL_TRIANGLES); glxVertex3v(tr->a); @@ -166,6 +169,9 @@ static void StippleTriangle(STriangle *tr, BOOL s, double r, double g, double b) void glxFillMesh(int specColor, SMesh *m, DWORD h, DWORD s1, DWORD s2) { + DWORD rgbHovered = Style::Color(Style::HOVERED), + rgbSelected = Style::Color(Style::SELECTED); + glEnable(GL_NORMALIZE); int prevColor = -1; glBegin(GL_TRIANGLES); @@ -208,10 +214,10 @@ void glxFillMesh(int specColor, SMesh *m, DWORD h, DWORD s1, DWORD s2) if((s1 != 0 && tr->meta.face == s1) || (s2 != 0 && tr->meta.face == s2)) { - StippleTriangle(tr, TRUE, 1, 0, 0); + StippleTriangle(tr, TRUE, rgbSelected); } if(h != 0 && tr->meta.face == h) { - StippleTriangle(tr, FALSE, 1, 1, 0); + StippleTriangle(tr, FALSE, rgbHovered); } } glEnd(); @@ -283,13 +289,13 @@ void glxDebugPolygon(SPolygon *p) Vector a = (sc->l.elem[j]).p; Vector b = (sc->l.elem[j+1]).p; - glxLockColorTo(0, 0, 1); + glxLockColorTo(RGB(0, 0, 255)); Vector d = (a.Minus(b)).WithMagnitude(-0); glBegin(GL_LINES); glxVertex3v(a.Plus(d)); glxVertex3v(b.Minus(d)); glEnd(); - glxLockColorTo(1, 0, 0); + glxLockColorTo(RGB(255, 0, 0)); glBegin(GL_POINTS); glxVertex3v(a.Plus(d)); glxVertex3v(b.Minus(d)); @@ -327,7 +333,7 @@ void glxDebugMesh(SMesh *m) glxDepthRangeOffset(1); glxUnlockColor(); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glxColor4d(0, 1, 0, 1.0); + glxColorRGBa(RGB(0, 255, 0), 1.0); glBegin(GL_TRIANGLES); for(i = 0; i < m->l.n; i++) { STriangle *t = &(m->l.elem[i]); diff --git a/groupmesh.cpp b/groupmesh.cpp index b7d9512..53862ce 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -356,7 +356,8 @@ Group *Group::RunningMeshGroup(void) { void Group::DrawDisplayItems(int t) { int specColor; if(t == DRAWING_3D || t == DRAWING_WORKPLANE) { - specColor = RGB(25, 25, 25); // force the color to something dim + // force the color to something dim + specColor = Style::Color(Style::DIM_SOLID); } else { specColor = -1; // use the model color } @@ -382,11 +383,9 @@ void Group::DrawDisplayItems(int t) { glDisable(GL_LIGHTING); } if(SS.GW.showEdges) { - glLineWidth(1); glxDepthRangeOffset(2); - glxColor3d(REDf (SS.edgeColor), - GREENf(SS.edgeColor), - BLUEf (SS.edgeColor)); + glxColorRGB(Style::Color(Style::SOLID_EDGE)); + glLineWidth(Style::Width(Style::SOLID_EDGE)); glxDrawEdges(&displayEdges, false); } @@ -418,14 +417,13 @@ void Group::Draw(void) { // it's just a nuisance. if(type == DRAWING_WORKPLANE) { glDisable(GL_DEPTH_TEST); - glxColor4d(1, 0, 0, 0.2); - glLineWidth(10); + glxColorRGBa(Style::Color(Style::DRAW_ERROR), 0.2); + glLineWidth (Style::Width(Style::DRAW_ERROR)); glBegin(GL_LINES); glxVertex3v(polyError.notClosedAt.a); glxVertex3v(polyError.notClosedAt.b); glEnd(); - glLineWidth(1); - glxColor3d(1, 0, 0); + glxColorRGB(Style::Color(Style::DRAW_ERROR)); glxWriteText("not closed contour!", polyError.notClosedAt.b, SS.GW.projRight, SS.GW.projUp, NULL, NULL); @@ -437,7 +435,7 @@ void Group::Draw(void) { // These errors occur at points, not lines if(type == DRAWING_WORKPLANE) { glDisable(GL_DEPTH_TEST); - glxColor3d(1, 0, 0); + glxColorRGB(Style::Color(Style::DRAW_ERROR)); char *msg = (polyError.how == POLY_NOT_COPLANAR) ? "points not all coplanar!" : "contour is self-intersecting!"; @@ -447,7 +445,7 @@ void Group::Draw(void) { glEnable(GL_DEPTH_TEST); } } else { - glxColor4d(0, 0.1, 0.1, 0.5); + glxColorRGBa(Style::Color(Style::CONTOUR_FILL), 0.5); glxDepthRangeOffset(1); glxFillPolygon(&poly); glxDepthRangeOffset(0); diff --git a/sketch.h b/sketch.h index 8ff08a7..9aef0ec 100644 --- a/sketch.h +++ b/sketch.h @@ -597,28 +597,64 @@ public: class Style { +public: int tag; hStyle h; - - static const int ACTIVE = 1; + + // If an entity has no style, then it will be colored according to + // whether the group that it's in is active or not, whether it's + // construction or not, and so on. + static const int NO_STYLE = 0; + + static const int ACTIVE_GRP = 1; static const int CONSTRUCTION = 2; - static const int INACTIVE = 3; + static const int INACTIVE_GRP = 3; static const int DATUM = 4; static const int SOLID_EDGE = 5; static const int CONSTRAINT = 6; static const int SELECTED = 7; static const int HOVERED = 8; + static const int CONTOUR_FILL = 9; + static const int NORMALS = 10; + static const int ANALYZE = 11; + static const int DRAW_ERROR = 12; + static const int DIM_SOLID = 13; static const int FIRST_CUSTOM = 0x1000; - static const int WIDTH_ABSOLUTE = 0; - static const int WIDTH_RELATIVE = 1; - static const int WIDTH_PIXELS = 2; + NameStr name; + + static const int WIDTH_MM = 0; + static const int WIDTH_PIXELS = 1; double width; int widthHow; DWORD color; bool visible; bool exportable; + + // The default styles, for entities that don't have a style assigned yet, + // and for datums and such. + typedef struct { + hStyle h; + char *cnfPrefix; + DWORD color; + double width; + } Default; + static const Default Defaults[]; + + static char *CnfColor(char *prefix); + static char *CnfWidth(char *prefix); + static char *CnfPrefixToName(char *prefix); + static void CreateDefaultStyle(hStyle h); + static void FreezeDefaultStyles(void); + static void LoadFactoryDefaults(void); + + static Style *Get(hStyle hs); + static DWORD Color(hStyle hs, bool forExport=false); + static float Width(hStyle hs); + static DWORD Color(int hs, bool forExport=false); + static float Width(int hs); + static hStyle ForEntity(hEntity he); }; diff --git a/solvespace.cpp b/solvespace.cpp index 354e886..2f5333e 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -64,8 +64,6 @@ void SolveSpace::Init(char *cmdLine) { viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits"); // Camera tangent (determines perspective) cameraTangent = CnfThawFloat(0.0f, "CameraTangent"); - // Color for edges (drawn as lines for emphasis) - edgeColor = CnfThawDWORD(RGB(200, 200, 200), "EdgeColor"); // Export scale factor exportScale = CnfThawFloat(1.0f, "ExportScale"); // Export offset (cutter radius comp) @@ -99,6 +97,10 @@ void SolveSpace::Init(char *cmdLine) { } RefreshRecentMenus(); + // The default styles (colors, line widths, etc.) are also stored in the + // configuration file, but we will automatically load those as we need + // them. + // Start with either an empty file, or the file specified on the // command line. NewFile(); @@ -144,8 +146,6 @@ void SolveSpace::Exit(void) { CnfFreezeDWORD((DWORD)viewUnits, "ViewUnits"); // Camera tangent (determines perspective) CnfFreezeFloat((float)cameraTangent, "CameraTangent"); - // Color for edges (drawn as lines for emphasis) - CnfFreezeDWORD(edgeColor, "EdgeColor"); // Export scale (a float, stored as a DWORD) CnfFreezeFloat(exportScale, "ExportScale"); // Export offset (cutter radius comp) @@ -171,6 +171,9 @@ void SolveSpace::Exit(void) { // Show toolbar in the graphics window CnfFreezeDWORD(showToolbar, "ShowToolbar"); + // And the default styles, colors and line widths and such. + Style::FreezeDefaultStyles(); + ExitNow(); } diff --git a/solvespace.h b/solvespace.h index 2e6ebe0..9de5233 100644 --- a/solvespace.h +++ b/solvespace.h @@ -191,10 +191,10 @@ void glxWriteTextRefCenter(char *str, Vector t, Vector u, Vector v, glxLineFn *fn, void *fndata); double glxStrWidth(char *str); double glxStrHeight(void); -void glxLockColorTo(double r, double g, double b); +void glxLockColorTo(DWORD rgb); void glxUnlockColor(void); -void glxColor3d(double r, double g, double b); -void glxColor4d(double r, double g, double b, double a); +void glxColorRGB(DWORD rgb); +void glxColorRGBa(DWORD rgb, double a); void glxDepthRangeOffset(int units); void glxDepthRangeLockToFront(bool yes); @@ -470,6 +470,7 @@ public: IdList<Group,hGroup> group; IdList<CONSTRAINT,hConstraint> constraint; IdList<Request,hRequest> request; + IdList<Style,hStyle> style; // These are generated from the above. IdList<ENTITY,hEntity> entity; @@ -481,7 +482,10 @@ public: inline Param *GetParam (hParam h) { return param. FindById(h); } inline Request *GetRequest(hRequest h) { return request.FindById(h); } inline Group *GetGroup (hGroup h) { return group. FindById(h); } + // Styles are handled a bit differently. }; +#undef ENTITY +#undef CONSTRAINT class SolveSpace { public: @@ -494,6 +498,7 @@ public: IdList<Request,hRequest> request; IdList<Constraint,hConstraint> constraint; IdList<Param,hParam> param; + IdList<Style,hStyle> style; hGroup activeGroup; } UndoState; static const int MAX_UNDO = 16; @@ -522,7 +527,6 @@ public: double chordTol; int maxSegments; double cameraTangent; - DWORD edgeColor; float exportScale; float exportOffset; int drawBackFaces; @@ -582,6 +586,7 @@ public: Entity e; Param p; Constraint c; + Style s; } sv; static void MenuFile(int id); bool GetFilenameAndSave(bool saveAs); diff --git a/style.cpp b/style.cpp index aeebeb5..f63bed4 100644 --- a/style.cpp +++ b/style.cpp @@ -1,5 +1,167 @@ #include "solvespace.h" -//DWORD Style::Color(hStyle h) { -//} +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, }, + { INACTIVE_GRP, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, }, + { DATUM, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, }, + { SOLID_EDGE, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, }, + { CONSTRAINT, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, }, + { SELECTED, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, }, + { HOVERED, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, }, + { CONTOUR_FILL, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, }, + { NORMALS, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, }, + { ANALYZE, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, }, + { DRAW_ERROR, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, }, + { DIM_SOLID, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, }, + { 0, NULL, 0, 0.0, }, +}; + +char *Style::CnfColor(char *prefix) { + static char name[100]; + sprintf(name, "Style_%s_Color", prefix); + return name; +} +char *Style::CnfWidth(char *prefix) { + static char name[100]; + sprintf(name, "Style_%s_Width", prefix); + return name; +} + +char *Style::CnfPrefixToName(char *prefix) { + static char name[100]; + int i = 0, j = 0; + while(prefix[i] && j < 90) { + if(isupper(prefix[i]) && i != 0) { + name[j++] = '-'; + } + name[j++] = tolower(prefix[i]); + i++; + } + name[j++] = '\0'; + return name; +} + +void Style::CreateDefaultStyle(hStyle h) { + const Default *d; + for(d = &(Defaults[0]); d->h.v; d++) { + if(d->h.v == h.v) break; + } + if(!d->h.v) { + // Not a default style; so just create it the same as our default + // active group entity style. + d = &(Defaults[0]); + } + + 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.visible = true; + ns.exportable = true; + ns.name.strcpy(CnfPrefixToName(d->cnfPrefix)); + ns.h = h; + + SK.style.Add(&ns); +} + +void Style::LoadFactoryDefaults(void) { + const Default *d; + for(d = &(Defaults[0]); d->h.v; d++) { + Style *s = Get(d->h); + + s->color = d->color; + s->width = d->width; + s->widthHow = WIDTH_PIXELS; + s->visible = true; + s->exportable = true; + s->name.strcpy(CnfPrefixToName(d->cnfPrefix)); + } +} + +void Style::FreezeDefaultStyles(void) { + const Default *d; + for(d = &(Defaults[0]); d->h.v; d++) { + CnfFreezeDWORD(Color(d->h), CnfColor(d->cnfPrefix)); + CnfFreezeFloat((float)Width(d->h), CnfWidth(d->cnfPrefix)); + } +} + +//----------------------------------------------------------------------------- +// Look up a style by its handle. If that style does not exist, then create +// the style, according to our table of default styles. +//----------------------------------------------------------------------------- +Style *Style::Get(hStyle h) { + Style *s = SK.style.FindByIdNoOops(h); + if(s) { + // It exists, good. + return s; + } else { + // It doesn't exist; so we should create it and then return that. + CreateDefaultStyle(h); + return SK.style.FindById(h); + } +} + +//----------------------------------------------------------------------------- +// A couple of wrappers, so that I can call these functions with either an +// hStyle or with the integer corresponding to that hStyle.v. +//----------------------------------------------------------------------------- +DWORD Style::Color(int s, bool forExport) { + hStyle hs = { s }; + return Color(hs, forExport); +} +float Style::Width(int s) { + hStyle hs = { s }; + return Width(hs); +} + +//----------------------------------------------------------------------------- +// Return the color associated with our style as 8-bit RGB. +//----------------------------------------------------------------------------- +DWORD Style::Color(hStyle h, bool forExport) { + Style *s = Get(h); + return s->color; +} + +//----------------------------------------------------------------------------- +// Return the width associated with our style in pixels.. +//----------------------------------------------------------------------------- +float Style::Width(hStyle h) { + double r = 1.0; + Style *s = Get(h); + if(s->widthHow == WIDTH_MM) { + r = s->width * SS.GW.scale; + } else if(s->widthHow == WIDTH_PIXELS) { + r = s->width; + } + // This returns a float because glLineWidth expects a float, avoid casts. + return (float)r; +} + +//----------------------------------------------------------------------------- +// Return the appropriate style for our entity. If the entity has a style +// explicitly assigned, then it's that style. Otherwise it's the appropriate +// default style. +//----------------------------------------------------------------------------- +hStyle Style::ForEntity(hEntity he) { + Entity *e = SK.GetEntity(he); + // If the entity has a special style, use that. If that style doesn't + // exist yet, then it will get created automatically later. + if(e->style.v != 0) { + return e->style; + } + + // Otherwise, we use the default rules. + hStyle hs; + if(e->group.v != SS.GW.activeGroup.v) { + hs.v = INACTIVE_GRP; + } else if(e->construction) { + hs.v = CONSTRUCTION; + } else { + hs.v = ACTIVE_GRP; + } + return hs; +} diff --git a/textscreens.cpp b/textscreens.cpp index 02b5e50..c9a9cd2 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -606,41 +606,33 @@ void TextWindow::ScreenChangeColor(int link, DWORD v) { SS.TW.edit.meaning = EDIT_COLOR; SS.TW.edit.i = v; } -void TextWindow::ScreenChangeEdgeColor(int link, DWORD v) { - char str[1024]; - sprintf(str, "%.3f, %.3f, %.3f", - REDf(SS.edgeColor), GREENf(SS.edgeColor), BLUEf(SS.edgeColor)); - - ShowTextEditControl(37, 3, str); - SS.TW.edit.meaning = EDIT_EDGE_COLOR; -} void TextWindow::ScreenChangeChordTolerance(int link, DWORD v) { char str[1024]; sprintf(str, "%.2f", SS.chordTol); - ShowTextEditControl(43, 3, str); + ShowTextEditControl(37, 3, str); SS.TW.edit.meaning = EDIT_CHORD_TOLERANCE; } void TextWindow::ScreenChangeMaxSegments(int link, DWORD v) { char str[1024]; sprintf(str, "%d", SS.maxSegments); - ShowTextEditControl(47, 3, str); + ShowTextEditControl(41, 3, str); SS.TW.edit.meaning = EDIT_MAX_SEGMENTS; } void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) { char str[1024]; sprintf(str, "%.3f", 1000*SS.cameraTangent); - ShowTextEditControl(53, 3, str); + ShowTextEditControl(47, 3, str); SS.TW.edit.meaning = EDIT_CAMERA_TANGENT; } void TextWindow::ScreenChangeExportScale(int link, DWORD v) { char str[1024]; sprintf(str, "%.3f", (double)SS.exportScale); - ShowTextEditControl(59, 3, str); + ShowTextEditControl(53, 3, str); SS.TW.edit.meaning = EDIT_EXPORT_SCALE; } void TextWindow::ScreenChangeExportOffset(int link, DWORD v) { - ShowTextEditControl(63, 3, SS.MmToString(SS.exportOffset)); + ShowTextEditControl(57, 3, SS.MmToString(SS.exportOffset)); SS.TW.edit.meaning = EDIT_EXPORT_OFFSET; } void TextWindow::ScreenChangeBackFaces(int link, DWORD v) { @@ -674,7 +666,7 @@ void TextWindow::ScreenChangeCanvasSize(int link, DWORD v) { default: return; } - int row = 77, col; + int row = 71, col; if(v < 10) { row += v*2; col = 11; @@ -711,12 +703,6 @@ void TextWindow::ShowConfiguration(void) { SS.lightIntensity[i], i, &ScreenChangeLightIntensity); } - Printf(false, ""); - Printf(false, "%Ft edge color r,g,b%E"); - Printf(false, "%Ba %@, %@, %@ %Fl%Ll%f%D[change]%E", - REDf(SS.edgeColor), GREENf(SS.edgeColor), BLUEf(SS.edgeColor), - &ScreenChangeEdgeColor, 0); - Printf(false, ""); Printf(false, "%Ft chord tolerance (in screen pixels)%E"); Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E; now %d triangles", @@ -997,16 +983,6 @@ void TextWindow::EditControlDone(char *s) { InvalidateGraphics(); break; } - case EDIT_EDGE_COLOR: { - double r, g, b; - if(sscanf(s, "%lf, %lf, %lf", &r, &g, &b)==3) { - SS.edgeColor = RGB(r*255, g*255, b*255); - } else { - Error("Bad format: specify color as r, g, b"); - } - SS.GenerateAll(0, INT_MAX); - break; - } case EDIT_EXPORT_SCALE: { Expr *e = Expr::From(s); if(e) { diff --git a/ui.h b/ui.h index ab6bf5b..0e3b0f2 100644 --- a/ui.h +++ b/ui.h @@ -77,10 +77,9 @@ public: static const int EDIT_CHORD_TOLERANCE = 13; static const int EDIT_MAX_SEGMENTS = 14; static const int EDIT_CAMERA_TANGENT = 15; - static const int EDIT_EDGE_COLOR = 16; - static const int EDIT_EXPORT_SCALE = 17; - static const int EDIT_EXPORT_OFFSET = 18; - static const int EDIT_CANVAS_SIZE = 19; + static const int EDIT_EXPORT_SCALE = 16; + static const int EDIT_EXPORT_OFFSET = 17; + static const int EDIT_CANVAS_SIZE = 18; // For the helical sweep static const int EDIT_HELIX_TURNS = 20; static const int EDIT_HELIX_PITCH = 21; @@ -167,7 +166,6 @@ public: static void ScreenChangeChordTolerance(int link, DWORD v); static void ScreenChangeMaxSegments(int link, DWORD v); static void ScreenChangeCameraTangent(int link, DWORD v); - static void ScreenChangeEdgeColor(int link, DWORD v); static void ScreenChangeExportScale(int link, DWORD v); static void ScreenChangeExportOffset(int link, DWORD v); diff --git a/undoredo.cpp b/undoredo.cpp index 87c68d6..f48f59e 100644 --- a/undoredo.cpp +++ b/undoredo.cpp @@ -77,6 +77,9 @@ void SolveSpace::PushFromCurrentOnto(UndoStack *uk) { for(i = 0; i < SK.param.n; i++) { ut->param.Add(&(SK.param.elem[i])); } + for(i = 0; i < SK.style.n; i++) { + ut->style.Add(&(SK.style.elem[i])); + } ut->activeGroup = SS.GW.activeGroup; uk->write = WRAP(uk->write + 1, MAX_UNDO); @@ -110,12 +113,14 @@ void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) { SK.request.Clear(); SK.constraint.Clear(); SK.param.Clear(); + SK.style.Clear(); // And then do a shallow copy of the state from the undo list ut->group.MoveSelfInto(&(SK.group)); ut->request.MoveSelfInto(&(SK.request)); ut->constraint.MoveSelfInto(&(SK.constraint)); ut->param.MoveSelfInto(&(SK.param)); + ut->style.MoveSelfInto(&(SK.style)); SS.GW.activeGroup = ut->activeGroup; // No need to free it, since a shallow copy was made above @@ -150,6 +155,7 @@ void SolveSpace::UndoClearState(UndoState *ut) { ut->request.Clear(); ut->constraint.Clear(); ut->param.Clear(); + ut->style.Clear(); ZERO(ut); }