From 463496105436826de89b2c551b1209aa98d811be Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Thu, 24 Sep 2009 07:52:48 -0800 Subject: [PATCH] Add ability to assign styles to cosmetic text (in the form of Constraint::COMMENTs), including line width and color, and text height and origin location. [git-p4: depot-paths = "//depot/solvespace/": change = 2033] --- constraint.cpp | 7 +- draw.cpp | 72 +++++++++++-- drawconstraint.cpp | 76 ++++++++++--- drawentity.cpp | 58 +--------- file.cpp | 7 +- glhelper.cpp | 77 +++++++++++--- graphicswin.cpp | 4 +- groupmesh.cpp | 4 +- sketch.h | 16 ++- solvespace.h | 10 +- style.cpp | 260 +++++++++++++++++++++++++++++++++++---------- ui.h | 12 ++- wishlist.txt | 2 - 13 files changed, 436 insertions(+), 169 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index 110baab..0fae97b 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -654,10 +654,9 @@ void Constraint::MenuConstrain(int id) { break; case GraphicsWindow::MNU_COMMENT: - c.type = COMMENT; - c.comment.strcpy("NEW COMMENT -- DOUBLE-CLICK TO EDIT"); - c.disp.offset = SS.GW.offset.ScaledBy(-1); - AddConstraint(&c); + SS.GW.pending.operation = GraphicsWindow::MNU_COMMENT; + SS.GW.pending.description = "click center of comment text"; + SS.later.showTW = true; break; default: oops(); diff --git a/draw.cpp b/draw.cpp index e7b5eaf..4ee0dbc 100644 --- a/draw.cpp +++ b/draw.cpp @@ -409,17 +409,18 @@ void GraphicsWindow::MouseRightUp(double x, double y) { // selection. bool toggleForStyles = false, toggleForGroupInfo = false, - toggleForDelete = false; + toggleForDelete = false, + toggleForStyleInfo = false; if(!hover.IsEmpty()) { AddContextMenuItem("Toggle Hovered Item Selection", CMNU_TOGGLE_SELECTION); } - if(gs.entities > 0) { + if(gs.stylables > 0) { ContextMenuListStyles(); AddContextMenuItem("Assign Selection to Style", CONTEXT_SUBMENU); - } else if(hover.entity.v && gs.n == 0 && gs.constraints == 0) { + } else if(gs.n == 0 && gs.constraints == 0 && hover.IsStylable()) { ContextMenuListStyles(); AddContextMenuItem("Assign Hovered Item to Style", CONTEXT_SUBMENU); toggleForStyles = true; @@ -432,9 +433,16 @@ void GraphicsWindow::MouseRightUp(double x, double y) { toggleForGroupInfo = true; } + if(gs.n + gs.constraints == 1 && gs.stylables == 1) { + AddContextMenuItem("Style Info for Selected Item", CMNU_STYLE_INFO); + } else if(hover.IsStylable() && gs.n == 0 && gs.constraints == 0) { + AddContextMenuItem("Style Info for Hovered Item", CMNU_STYLE_INFO); + toggleForStyleInfo = true; + } + if(hover.constraint.v && gs.n == 0 && gs.constraints == 0) { Constraint *c = SK.GetConstraint(hover.constraint); - if(c->HasLabel()) { + if(c->HasLabel() && c->type != Constraint::COMMENT) { AddContextMenuItem("Toggle Reference Dimension", CMNU_REFERENCE_DIM); } @@ -483,8 +491,9 @@ void GraphicsWindow::MouseRightUp(double x, double y) { case CMNU_GROUP_INFO: { if(toggleForGroupInfo) ToggleSelectionStateOfHovered(); - GroupSelection(); + hGroup hg; + GroupSelection(); if(gs.entities == 1) { hg = SK.GetEntity(gs.entity[0])->group; } else if(gs.points == 1) { @@ -495,12 +504,36 @@ void GraphicsWindow::MouseRightUp(double x, double y) { break; } ClearSelection(); + SS.TW.GoToScreen(TextWindow::SCREEN_GROUP_INFO); SS.TW.shown.group = hg; SS.later.showTW = true; break; } + case CMNU_STYLE_INFO: { + if(toggleForStyleInfo) ToggleSelectionStateOfHovered(); + + hStyle hs; + GroupSelection(); + if(gs.entities == 1) { + hs = Style::ForEntity(gs.entity[0]); + } else if(gs.points == 1) { + hs = Style::ForEntity(gs.point[0]); + } else if(gs.constraints == 1) { + hs = SK.GetConstraint(gs.constraint[0])->disp.style; + if(!hs.v) hs.v = Style::CONSTRAINT; + } else { + break; + } + ClearSelection(); + + SS.TW.GoToScreen(TextWindow::SCREEN_STYLE_INFO); + SS.TW.shown.style = hs; + SS.later.showTW = true; + break; + } + case CMNU_NEW_CUSTOM_STYLE: { if(toggleForStyles) ToggleSelectionStateOfHovered(); DWORD v = Style::CreateCustomStyle(); @@ -602,8 +635,6 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { ConstrainPointByHovered(hr.entity(0)); ClearSuper(); - - pending.operation = 0; break; case MNU_LINE_SEGMENT: @@ -738,6 +769,19 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { break; } + case MNU_COMMENT: { + ClearSuper(); + Constraint c; + ZERO(&c); + c.group = SS.GW.activeGroup; + c.workplane = SS.GW.ActiveWorkplane(); + c.type = Constraint::COMMENT; + c.disp.offset = v; + c.comment.strcpy("NEW COMMENT -- DOUBLE-CLICK TO EDIT"); + Constraint::AddConstraint(&c); + break; + } + case DRAGGING_RADIUS: case DRAGGING_NEW_POINT: // The MouseMoved event has already dragged it as desired. @@ -925,6 +969,15 @@ bool GraphicsWindow::Selection::IsEmpty(void) { return true; } +bool GraphicsWindow::Selection::IsStylable(void) { + if(entity.v) return true; + if(constraint.v) { + Constraint *c = SK.GetConstraint(constraint); + if(c->type == Constraint::COMMENT) return true; + } + return false; +} + void GraphicsWindow::Selection::Clear(void) { entity.v = constraint.v = 0; emphasized = false; @@ -1045,6 +1098,7 @@ void GraphicsWindow::GroupSelection(void) { gs.point[(gs.points)++] = s->entity; } else { gs.entity[(gs.entities)++] = s->entity; + (gs.stylables)++; } // And an auxiliary list of normals, including normals from @@ -1081,6 +1135,10 @@ void GraphicsWindow::GroupSelection(void) { } if(s->constraint.v) { gs.constraint[(gs.constraints)++] = s->constraint; + Constraint *c = SK.GetConstraint(s->constraint); + if(c->type == Constraint::COMMENT) { + (gs.stylables)++; + } } } } diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 0cc035c..bda1af5 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -19,13 +19,26 @@ bool Constraint::HasLabel(void) { void Constraint::LineDrawOrGetDistance(Vector a, Vector b) { if(dogd.drawing) { - if(dogd.sel) { - dogd.sel->AddEdge(a, b, Style::CONSTRAINT); + // Draw comments in the specified style, but everything else in the + // default style for constraints. + hStyle hs; + if(type == COMMENT && disp.style.v) { + hs = disp.style; } else { - glBegin(GL_LINE_STRIP); - glxVertex3v(a); - glxVertex3v(b); - glEnd(); + hs.v = Style::CONSTRAINT; + } + + if(dogd.sel) { + dogd.sel->AddEdge(a, b, hs.v); + } else { + if(hs.v && Style::Width(disp.style) >= 3.0) { + glxFatLine(a, b, Style::Width(disp.style) / SS.GW.scale); + } else { + glBegin(GL_LINE_STRIP); + glxVertex3v(a); + glxVertex3v(b); + glEnd(); + } } } else { Point2d ap = SS.GW.ProjectPoint(a); @@ -70,8 +83,28 @@ char *Constraint::Label(void) { } void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { + double th; + if(type == COMMENT) { + th = Style::TextHeight(disp.style); + } else { + th = DEFAULT_TEXT_HEIGHT; + } + char *s = Label(); - double swidth = glxStrWidth(s), sheight = glxStrHeight(); + double swidth = glxStrWidth(s, th), + sheight = glxStrHeight(th); + + // By default, the reference is from the center; but the style could + // specify otherwise if one is present. + if(type == COMMENT && disp.style.v) { + Style *s = Style::Get(disp.style); + int o = s->textOrigin; + if(o & Style::ORIGIN_LEFT) ref = ref.Plus(gr.WithMagnitude(swidth/2)); + if(o & Style::ORIGIN_RIGHT) ref = ref.Minus(gr.WithMagnitude(swidth/2)); + if(o & Style::ORIGIN_BOT) ref = ref.Plus(gu.WithMagnitude(sheight/2)); + if(o & Style::ORIGIN_TOP) ref = ref.Minus(gu.WithMagnitude(sheight/2)); + } + if(labelPos) { // labelPos is from the top left corner (for the text box used to // edit things), but ref is from the center. @@ -79,8 +112,9 @@ void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { gu.WithMagnitude(sheight/2)); } + if(dogd.drawing) { - glxWriteTextRefCenter(s, ref, gr, gu, LineCallback, this); + glxWriteTextRefCenter(s, th, ref, gr, gu, LineCallback, this); } else { double l = swidth/2 - sheight/2; l = max(l, 5/SS.GW.scale); @@ -88,7 +122,7 @@ void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { Point2d b = SS.GW.ProjectPoint(ref.Plus (gr.WithMagnitude(l))); double d = dogd.mp.DistanceToLine(a, b.Minus(a), true); - dogd.dmin = min(dogd.dmin, d - 3); + dogd.dmin = min(dogd.dmin, d - (th / 2)); dogd.refp = ref; } } @@ -116,8 +150,8 @@ int Constraint::DoLineTrimmedAgainstBox(Vector ref, Vector a, Vector b) { double pixels = 1.0 / SS.GW.scale; char *s = Label(); - double swidth = glxStrWidth(s) + 4*pixels, - sheight = glxStrHeight() + 8*pixels; + double swidth = glxStrWidth(s, DEFAULT_TEXT_HEIGHT) + 4*pixels, + sheight = glxStrHeight(DEFAULT_TEXT_HEIGHT) + 8*pixels; struct { Vector n; @@ -322,15 +356,18 @@ void Constraint::DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db, // complex and this looks pretty good. double tl = atan2(rm.Dot(gu), rm.Dot(gr)); double adj = EllipticalInterpolation( - glxStrWidth(Label())/2, glxStrHeight()/2, tl); + glxStrWidth(Label(), DEFAULT_TEXT_HEIGHT)/2, + glxStrHeight(DEFAULT_TEXT_HEIGHT)/2, + tl); *ref = (*ref).Plus(rm.WithMagnitude(adj + 3/SS.GW.scale)); } else { // The lines are skew; no wonderful way to illustrate that. *ref = a0.Plus(b0); *ref = (*ref).ScaledBy(0.5).Plus(disp.offset); gu = gu.WithMagnitude(1); - Vector trans = (*ref).Plus(gu.ScaledBy(-1.5*glxStrHeight())); - glxWriteTextRefCenter("angle between skew lines", + Vector trans = + (*ref).Plus(gu.ScaledBy(-1.5*glxStrHeight(DEFAULT_TEXT_HEIGHT))); + glxWriteTextRefCenter("angle between skew lines", DEFAULT_TEXT_HEIGHT, trans, gr, gu, LineCallback, this); } } @@ -653,7 +690,8 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { } if(dogd.drawing) { - glxWriteTextRefCenter("T", textAt, u, v, LineCallback, this); + glxWriteTextRefCenter("T", DEFAULT_TEXT_HEIGHT, + textAt, u, v, LineCallback, this); } else { dogd.refp = textAt; Point2d ref = SS.GW.ProjectPoint(dogd.refp); @@ -835,8 +873,8 @@ s: (type == VERTICAL) ? "V" : ( (type == AT_MIDPOINT) ? "M" : NULL)); - glxWriteTextRefCenter(s, m.Plus(offset), r, u, - LineCallback, this); + glxWriteTextRefCenter(s, DEFAULT_TEXT_HEIGHT, + m.Plus(offset), r, u, LineCallback, this); } else { dogd.refp = m.Plus(offset); Point2d ref = SS.GW.ProjectPoint(dogd.refp); @@ -880,6 +918,10 @@ s: break; case COMMENT: + if(disp.style.v) { + glLineWidth(Style::Width(disp.style)); + glxColorRGB(Style::Color(disp.style)); + } DoLabel(disp.offset, labelPos, gr, gu); break; diff --git a/drawentity.cpp b/drawentity.cpp index 819984f..43a3766 100644 --- a/drawentity.cpp +++ b/drawentity.cpp @@ -10,55 +10,6 @@ char *Entity::DescriptionString(void) { } } -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 @@ -71,7 +22,7 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat) { glxVertex3v(b); glEnd(); } else { - FatLine(a, b); + glxFatLine(a, b, dogd.lineWidth/SS.GW.scale); } glxDepthRangeOffset(0); @@ -506,11 +457,12 @@ void Entity::DrawOrGetDistance(void) { glDisable(GL_LINE_STIPPLE); char *str = DescriptionString()+5; + double th = DEFAULT_TEXT_HEIGHT; if(dogd.drawing) { - glxWriteText(str, mm2, u, v, NULL, NULL); + glxWriteText(str, th, mm2, u, v, NULL, NULL); } else { - Vector pos = mm2.Plus(u.ScaledBy(glxStrWidth(str)/2)).Plus( - v.ScaledBy(glxStrHeight()/2)); + Vector pos = mm2.Plus(u.ScaledBy(glxStrWidth(str, th)/2)).Plus( + v.ScaledBy(glxStrHeight(th)/2)); Point2d pp = SS.GW.ProjectPoint(pos); dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 10); // If a line lies in a plane, then select the line, not diff --git a/file.cpp b/file.cpp index 17dee19..b7d89b9 100644 --- a/file.cpp +++ b/file.cpp @@ -154,7 +154,10 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { '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.widthAs", 'd', &(SS.sv.s.widthAs) }, + { 's', "Style.textHeight", 'f', &(SS.sv.s.textHeight) }, + { 's', "Style.textHeightAs", 'd', &(SS.sv.s.textHeightAs) }, + { 's', "Style.textOrigin", 'x', &(SS.sv.s.textOrigin) }, { 's', "Style.color", 'x', &(SS.sv.s.color) }, { 's', "Style.visible", 'b', &(SS.sv.s.visible) }, { 's', "Style.exportable", 'b', &(SS.sv.s.exportable) }, @@ -484,6 +487,8 @@ bool SolveSpace::LoadEntitiesFromFile(char *file, EntityList *le, } else if(strcmp(line, "AddConstraint")==0) { + } else if(strcmp(line, "AddStyle")==0) { + } else if(strcmp(line, VERSION_STRING)==0) { } else if(StrStartsWith(line, "Triangle ")) { diff --git a/glhelper.cpp b/glhelper.cpp index b8112a8..15c0677 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -6,8 +6,8 @@ static bool ColorLocked; static bool DepthOffsetLocked; -#define FONT_SCALE (0.55) -double glxStrWidth(char *str) { +#define FONT_SCALE(h) ((h)/22.0) +double glxStrWidth(char *str, double h) { int w = 0; for(; *str; str++) { int c = *str; @@ -16,21 +16,21 @@ double glxStrWidth(char *str) { w += Font[c].width; } - return w*FONT_SCALE/SS.GW.scale; + return w*FONT_SCALE(h)/SS.GW.scale; } -double glxStrHeight(void) { - // The characters have height ~21, as they appear in the table. - return 21.0*FONT_SCALE/SS.GW.scale; +double glxStrHeight(double h) { + // The characters have height ~22, as they appear in the table. + return 22.0*FONT_SCALE(h)/SS.GW.scale; } -void glxWriteTextRefCenter(char *str, Vector t, Vector u, Vector v, +void glxWriteTextRefCenter(char *str, double h, Vector t, Vector u, Vector v, glxLineFn *fn, void *fndata) { u = u.WithMagnitude(1); v = v.WithMagnitude(1); - double scale = FONT_SCALE/SS.GW.scale; - double fh = glxStrHeight(); - double fw = glxStrWidth(str); + double scale = FONT_SCALE(h)/SS.GW.scale; + double fh = glxStrHeight(h); + double fw = glxStrWidth(str, h); t = t.Plus(u.ScaledBy(-fw/2)); t = t.Plus(v.ScaledBy(-fh/2)); @@ -39,7 +39,7 @@ void glxWriteTextRefCenter(char *str, Vector t, Vector u, Vector v, t = t.Plus(u.ScaledBy(-5*scale)); t = t.Plus(v.ScaledBy(-5*scale)); - glxWriteText(str, t, u, v, fn, fndata); + glxWriteText(str, h, t, u, v, fn, fndata); } static void LineDrawCallback(void *fndata, Vector a, Vector b) @@ -51,14 +51,14 @@ static void LineDrawCallback(void *fndata, Vector a, Vector b) glEnd(); } -void glxWriteText(char *str, Vector t, Vector u, Vector v, +void glxWriteText(char *str, double h, Vector t, Vector u, Vector v, glxLineFn *fn, void *fndata) { if(!fn) fn = LineDrawCallback; u = u.WithMagnitude(1); v = v.WithMagnitude(1); - double scale = FONT_SCALE/SS.GW.scale; + double scale = FONT_SCALE(h)/SS.GW.scale; int xo = 5; int yo = 5; @@ -96,6 +96,57 @@ void glxVertex3v(Vector u) glVertex3f((GLfloat)u.x, (GLfloat)u.y, (GLfloat)u.z); } +static void 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 glxFatLine(Vector a, Vector b, double width) { + // The half-width of the line we're drawing. + double hw = width / 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 glxLockColorTo(DWORD rgb) { ColorLocked = false; diff --git a/graphicswin.cpp b/graphicswin.cpp index 1b0376c..96cd2bb 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -591,7 +591,9 @@ void GraphicsWindow::MenuEdit(int id) { // 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()) { + if(!(TextEditControlIsVisible() || + GraphicsEditControlIsVisible())) + { if(SS.TW.shown.screen == TextWindow::SCREEN_STYLE_INFO) { SS.TW.GoToScreen(TextWindow::SCREEN_LIST_OF_STYLES); } else { diff --git a/groupmesh.cpp b/groupmesh.cpp index 53862ce..ec84e84 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -424,7 +424,7 @@ void Group::Draw(void) { glxVertex3v(polyError.notClosedAt.b); glEnd(); glxColorRGB(Style::Color(Style::DRAW_ERROR)); - glxWriteText("not closed contour!", + glxWriteText("not closed contour!", DEFAULT_TEXT_HEIGHT, polyError.notClosedAt.b, SS.GW.projRight, SS.GW.projUp, NULL, NULL); glEnable(GL_DEPTH_TEST); @@ -439,7 +439,7 @@ void Group::Draw(void) { char *msg = (polyError.how == POLY_NOT_COPLANAR) ? "points not all coplanar!" : "contour is self-intersecting!"; - glxWriteText(msg, + glxWriteText(msg, DEFAULT_TEXT_HEIGHT, polyError.errorPointAt, SS.GW.projRight, SS.GW.projUp, NULL, NULL); glEnable(GL_DEPTH_TEST); diff --git a/sketch.h b/sketch.h index 07fc6f1..18f024c 100644 --- a/sketch.h +++ b/sketch.h @@ -415,8 +415,6 @@ public: Vector refp; double lineWidth; } dogd; // state for drawing or getting distance (for hit testing) - 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); @@ -626,10 +624,17 @@ public: NameStr name; - static const int WIDTH_AS_MM = 0; - static const int WIDTH_AS_PIXELS = 1; + static const int UNITS_AS_PIXELS = 0; + static const int UNITS_AS_MM = 1; double width; - int widthHow; + int widthAs; + double textHeight; + int textHeightAs; + static const int ORIGIN_LEFT = 0x01; + static const int ORIGIN_RIGHT = 0x02; + static const int ORIGIN_BOT = 0x04; + static const int ORIGIN_TOP = 0x08; + int textOrigin; DWORD color; bool visible; bool exportable; @@ -662,6 +667,7 @@ public: static DWORD Color(int hs, bool forExport=false); static float Width(int hs); static double WidthMm(int hs); + static double TextHeight(hStyle hs); static bool Exportable(int hs); static hStyle ForEntity(hEntity he); diff --git a/solvespace.h b/solvespace.h index 56973e1..3798c7b 100644 --- a/solvespace.h +++ b/solvespace.h @@ -181,6 +181,7 @@ typedef IdList ParamList; // Utility functions that are provided in the platform-independent code. void glxVertex3v(Vector u); +#define DEFAULT_TEXT_HEIGHT (11.5) #define GLX_CALLBACK __stdcall typedef void GLX_CALLBACK glxCallbackFptr(void); void glxTesselatePolygon(GLUtesselator *gt, SPolygon *p); @@ -191,13 +192,14 @@ void glxDrawEdges(SEdgeList *l, bool endpointsToo); void glxDebugMesh(SMesh *m); void glxMarkPolygonNormal(SPolygon *p); typedef void glxLineFn(void *data, Vector a, Vector b); -void glxWriteText(char *str, Vector t, Vector u, Vector v, +void glxWriteText(char *str, double h, Vector t, Vector u, Vector v, glxLineFn *fn, void *fndata); -void glxWriteTextRefCenter(char *str, Vector t, Vector u, Vector v, +void glxWriteTextRefCenter(char *str, double h, Vector t, Vector u, Vector v, glxLineFn *fn, void *fndata); -double glxStrWidth(char *str); -double glxStrHeight(void); +double glxStrWidth(char *str, double h); +double glxStrHeight(double h); void glxLockColorTo(DWORD rgb); +void glxFatLine(Vector a, Vector b, double width); void glxUnlockColor(void); void glxColorRGB(DWORD rgb); void glxColorRGBa(DWORD rgb, double a); diff --git a/style.cpp b/style.cpp index 21dd55b..26ed1f2 100644 --- a/style.cpp +++ b/style.cpp @@ -68,12 +68,14 @@ void Style::CreateDefaultStyle(hStyle h) { Style ns; ZERO(&ns); - ns.color = CnfThawDWORD(d->color, CnfColor(d->cnfPrefix)); - ns.width = CnfThawFloat((float)(d->width), CnfWidth(d->cnfPrefix)); - ns.widthHow = WIDTH_AS_PIXELS; - ns.visible = true; - ns.exportable = true; - ns.h = h; + ns.color = CnfThawDWORD(d->color, CnfColor(d->cnfPrefix)); + ns.width = CnfThawFloat((float)(d->width), CnfWidth(d->cnfPrefix)); + ns.widthAs = UNITS_AS_PIXELS; + ns.textHeight = DEFAULT_TEXT_HEIGHT; + ns.textHeightAs = UNITS_AS_PIXELS; + ns.visible = true; + ns.exportable = true; + ns.h = h; if(isDefaultStyle) { ns.name.strcpy(CnfPrefixToName(d->cnfPrefix)); } else { @@ -88,11 +90,13 @@ void Style::LoadFactoryDefaults(void) { for(d = &(Defaults[0]); d->h.v; d++) { Style *s = Get(d->h); - s->color = d->color; - s->width = d->width; - s->widthHow = WIDTH_AS_PIXELS; - s->visible = true; - s->exportable = true; + s->color = d->color; + s->width = d->width; + s->widthAs = UNITS_AS_PIXELS; + s->textHeight = DEFAULT_TEXT_HEIGHT; + s->textHeightAs = UNITS_AS_PIXELS; + s->visible = true; + s->exportable = true; s->name.strcpy(CnfPrefixToName(d->cnfPrefix)); } SS.backgroundColor = RGB(0, 0, 0); @@ -119,7 +123,8 @@ void Style::AssignSelectionToStyle(DWORD v) { SS.GW.GroupSelection(); SS.UndoRemember(); - for(int i = 0; i < SS.GW.gs.entities; i++) { + int i; + for(i = 0; i < SS.GW.gs.entities; i++) { hEntity he = SS.GW.gs.entity[i]; if(!he.isFromRequest()) { showError = true; @@ -131,6 +136,13 @@ void Style::AssignSelectionToStyle(DWORD v) { r->style.v = v; SS.later.generateAll = true; } + for(i = 0; i < SS.GW.gs.constraints; i++) { + hConstraint hc = SS.GW.gs.constraint[i]; + Constraint *c = SK.GetConstraint(hc); + if(c->type != Constraint::COMMENT) continue; + + c->disp.style.v = v; + } if(showError) { Error("Can't assign style to an entity that's derived from another " @@ -204,9 +216,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_AS_MM) { + if(s->widthAs == UNITS_AS_MM) { r = s->width * SS.GW.scale; - } else if(s->widthHow == WIDTH_AS_PIXELS) { + } else if(s->widthAs == UNITS_AS_PIXELS) { r = s->width; } // This returns a float because glLineWidth expects a float, avoid casts. @@ -221,6 +233,20 @@ double Style::WidthMm(int hs) { return widthpx / SS.GW.scale; } +//----------------------------------------------------------------------------- +// Return the associated text height, in pixels. +//----------------------------------------------------------------------------- +double Style::TextHeight(hStyle hs) { + Style *s = Get(hs); + if(s->textHeightAs == UNITS_AS_MM) { + return s->textHeight * SS.GW.scale; + } else if(s->textHeightAs == UNITS_AS_PIXELS) { + return s->textHeight; + } else { + return DEFAULT_TEXT_HEIGHT; + } +} + //----------------------------------------------------------------------------- // Should lines and curves from this style appear in the output file? Only // if it's both shown and exportable. @@ -346,18 +372,22 @@ void TextWindow::ScreenDeleteStyle(int link, DWORD v) { InvalidateGraphics(); } -void TextWindow::ScreenChangeStyleWidth(int link, DWORD v) { +void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, DWORD v) { hStyle hs = { v }; Style *s = Style::Get(hs); + double val = (link == 'w') ? s->width : s->textHeight; + int units = (link == 'w') ? s->widthAs : s->textHeightAs; + char str[300]; - if(s->widthHow == Style::WIDTH_AS_PIXELS) { - sprintf(str, "%.2f", s->width); + if(units == Style::UNITS_AS_PIXELS) { + sprintf(str, "%.2f", val); } else { - strcpy(str, SS.MmToString(s->width)); + strcpy(str, SS.MmToString(val)); } - ShowTextEditControl(16, 8, str); + ShowTextEditControl((link == 'w') ? 21 : 26, 13, str); SS.TW.edit.style = hs; - SS.TW.edit.meaning = EDIT_STYLE_WIDTH; + SS.TW.edit.meaning = (link == 'w') ? EDIT_STYLE_WIDTH : + EDIT_STYLE_TEXT_HEIGHT; } void TextWindow::ScreenChangeStyleColor(int link, DWORD v) { @@ -377,10 +407,24 @@ void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) { Style *s = Style::Get(hs); switch(link) { case 'w': - if(s->widthHow == Style::WIDTH_AS_PIXELS) { - s->widthHow = Style::WIDTH_AS_MM; + // Units for the width + if(s->widthAs == Style::UNITS_AS_PIXELS) { + s->widthAs = Style::UNITS_AS_MM; + s->width /= SS.GW.scale; } else { - s->widthHow = Style::WIDTH_AS_PIXELS; + s->widthAs = Style::UNITS_AS_PIXELS; + s->width *= SS.GW.scale; + } + break; + + case 'h': + // Units for the height + if(s->textHeightAs == Style::UNITS_AS_PIXELS) { + s->textHeightAs = Style::UNITS_AS_MM; + s->textHeight /= SS.GW.scale; + } else { + s->textHeightAs = Style::UNITS_AS_PIXELS; + s->textHeight *= SS.GW.scale; } break; @@ -391,6 +435,34 @@ void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) { case 'v': s->visible = !(s->visible); break; + + // Horizontal text alignment + case 'L': + s->textOrigin |= Style::ORIGIN_LEFT; + s->textOrigin &= ~Style::ORIGIN_RIGHT; + break; + case 'H': + s->textOrigin &= ~Style::ORIGIN_LEFT; + s->textOrigin &= ~Style::ORIGIN_RIGHT; + break; + case 'R': + s->textOrigin &= ~Style::ORIGIN_LEFT; + s->textOrigin |= Style::ORIGIN_RIGHT; + break; + + // Vertical text alignment + case 'B': + s->textOrigin |= Style::ORIGIN_BOT; + s->textOrigin &= ~Style::ORIGIN_TOP; + break; + case 'V': + s->textOrigin &= ~Style::ORIGIN_BOT; + s->textOrigin &= ~Style::ORIGIN_TOP; + break; + case 'T': + s->textOrigin &= ~Style::ORIGIN_BOT; + s->textOrigin |= Style::ORIGIN_TOP; + break; } InvalidateGraphics(); } @@ -398,17 +470,27 @@ void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) { bool TextWindow::EditControlDoneForStyles(char *str) { Style *s; switch(edit.meaning) { - case EDIT_STYLE_WIDTH: + case EDIT_STYLE_TEXT_HEIGHT: + 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; + double v; + int units = (edit.meaning == EDIT_STYLE_TEXT_HEIGHT) ? + s->textHeightAs : s->widthAs; + if(units == Style::UNITS_AS_MM) { + v = SS.StringToMm(str); + } else { + v = atof(str); + } + v = max(0, v); + if(edit.meaning == EDIT_STYLE_TEXT_HEIGHT) { + s->textHeight = v; + } else { + s->width = v; + } + return true; + } case EDIT_BACKGROUND_COLOR: case EDIT_STYLE_COLOR: { double r, g, b; @@ -462,31 +544,6 @@ void TextWindow::ShowStyleInfo(void) { 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, @@ -502,7 +559,98 @@ void TextWindow::ShowStyleInfo(void) { s->h.v, &ScreenChangeStyleYesNo, (!s->exportable ? "" : "no"), (!s->exportable ? "no" : "")); + } + char *unit = (SS.viewUnits == SolveSpace::UNIT_INCHES) ? "inches" : "mm"; + + // The line width, and its units + if(s->widthAs == Style::UNITS_AS_PIXELS) { + Printf(true, "%FtLINE WIDTH %E%@ %D%f%Lw%Fl[change]%E", + s->width, + s->h.v, &ScreenChangeStyleWidthOrTextHeight); + } else { + Printf(true, "%FtLINE WIDTH %E%s %D%f%Lw%Fl[change]%E", + SS.MmToString(s->width), + s->h.v, &ScreenChangeStyleWidthOrTextHeight); + } + + bool widthpx = (s->widthAs == Style::UNITS_AS_PIXELS); + if(s->h.v < Style::FIRST_CUSTOM) { + Printf(false,"%FtIN UNITS OF %Fspixels%E"); + } else { + Printf(false,"%FtIN UNITS OF " + "%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 : "")); + } + + // The text height, and its units + char *chng = (s->h.v < Style::FIRST_CUSTOM) ? "" : "[change]"; + if(s->textHeightAs == Style::UNITS_AS_PIXELS) { + Printf(true, "%FtTEXT HEIGHT %E%@ %D%f%Lt%Fl%s%E", + s->textHeight, + s->h.v, &ScreenChangeStyleWidthOrTextHeight, + chng); + } else { + Printf(true, "%FtTEXT HEIGHT %E%s %D%f%Lt%Fl%s%E", + SS.MmToString(s->textHeight), + s->h.v, &ScreenChangeStyleWidthOrTextHeight, + chng); + } + + bool textHeightpx = (s->textHeightAs == Style::UNITS_AS_PIXELS); + if(s->h.v < Style::FIRST_CUSTOM) { + Printf(false,"%FtIN UNITS OF %Fspixels%E"); + } else { + Printf(false,"%FtIN UNITS OF " + "%Fh%D%f%Lh%s%E%Fs%s%E / %Fh%D%f%Lh%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ( textHeightpx ? "" : "pixels"), + ( textHeightpx ? "pixels" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (!textHeightpx ? "" : unit), + (!textHeightpx ? unit : "")); + } + + if(s->h.v >= Style::FIRST_CUSTOM) { + bool neither; + + neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT)); + Printf(true, "%FtALIGN TEXT " + "%Fh%D%f%LL%s%E%Fs%s%E / " + "%Fh%D%f%LH%s%E%Fs%s%E / " + "%Fh%D%f%LR%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ((s->textOrigin & Style::ORIGIN_LEFT) ? "" : "left"), + ((s->textOrigin & Style::ORIGIN_LEFT) ? "left" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (neither ? "" : "center"), + (neither ? "center" : ""), + s->h.v, &ScreenChangeStyleYesNo, + ((s->textOrigin & Style::ORIGIN_RIGHT) ? "" : "right"), + ((s->textOrigin & Style::ORIGIN_RIGHT) ? "right" : "")); + + neither = !(s->textOrigin & (Style::ORIGIN_BOT | Style::ORIGIN_TOP)); + Printf(false, "%Ft " + "%Fh%D%f%LB%s%E%Fs%s%E / " + "%Fh%D%f%LV%s%E%Fs%s%E / " + "%Fh%D%f%LT%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ((s->textOrigin & Style::ORIGIN_BOT) ? "" : "bottom"), + ((s->textOrigin & Style::ORIGIN_BOT) ? "bottom" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (neither ? "" : "center"), + (neither ? "center" : ""), + s->h.v, &ScreenChangeStyleYesNo, + ((s->textOrigin & Style::ORIGIN_TOP) ? "" : "top"), + ((s->textOrigin & Style::ORIGIN_TOP) ? "top" : "")); + } + + if(s->h.v >= Style::FIRST_CUSTOM) { Printf(false, ""); Printf(false, "To assign lines or curves to this style,"); Printf(false, "select them on the drawing. Then commit"); diff --git a/ui.h b/ui.h index 1586348..b7df140 100644 --- a/ui.h +++ b/ui.h @@ -94,9 +94,10 @@ public: 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; + static const int EDIT_STYLE_TEXT_HEIGHT = 51; + static const int EDIT_STYLE_COLOR = 52; + static const int EDIT_STYLE_NAME = 53; + static const int EDIT_BACKGROUND_COLOR = 54; struct { int meaning; int i; @@ -189,7 +190,7 @@ public: 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 ScreenChangeStyleWidthOrTextHeight(int link, DWORD v); static void ScreenChangeStyleColor(int link, DWORD v); static void ScreenChangeBackgroundColor(int link, DWORD v); @@ -400,6 +401,7 @@ public: void Clear(void); bool IsEmpty(void); bool Equals(Selection *b); + bool IsStylable(void); }; Selection hover; static const int MAX_SELECTED = 32; @@ -425,6 +427,7 @@ public: int anyNormals; int vectors; int constraints; + int stylables; int n; } gs; void GroupSelection(void); @@ -439,6 +442,7 @@ public: static const int CMNU_GROUP_INFO = 0x105; static const int CMNU_REFERENCE_DIM = 0x106; static const int CMNU_OTHER_ANGLE = 0x107; + static const int CMNU_STYLE_INFO = 0x108; static const int CMNU_FIRST_STYLE = 0x40000000; void ContextMenuListStyles(void); diff --git a/wishlist.txt b/wishlist.txt index 32a8b47..c4d4e27 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,7 +1,5 @@ grid -better text -right-click menu wireframe export -----