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]
This commit is contained in:
Jonathan Westhues 2009-09-24 07:52:48 -08:00
parent 9416faca88
commit 4634961054
13 changed files with 436 additions and 169 deletions

View File

@ -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();

View File

@ -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)++;
}
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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 ")) {

View File

@ -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;

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -181,6 +181,7 @@ typedef IdList<Param,hParam> 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);

260
style.cpp
View File

@ -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");

12
ui.h
View File

@ -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);

View File

@ -1,7 +1,5 @@
grid
better text
right-click menu
wireframe export
-----