Add text angle for styles. Add ability to quickly change between

perspective and parallel projections. Add a snap grid, for points
and for text comments. Draw text comments in the plane of their
workplane if they have one, otherwise always facing forward.

And fix a few nasty bugs: the possibility of an extremely long
animation onto a workplane, accidental use of the wrong style line
width for constraints, misplaced text box in style screen for
default styles, other little stuff.

[git-p4: depot-paths = "//depot/solvespace/": change = 2037]
solver
Jonathan Westhues 2009-09-29 03:35:19 -08:00
parent 9f78ee3c33
commit db565438e3
14 changed files with 321 additions and 60 deletions

View File

@ -1148,6 +1148,7 @@ void GraphicsWindow::GroupSelection(void) {
Constraint *c = SK.GetConstraint(s->constraint); Constraint *c = SK.GetConstraint(s->constraint);
if(c->type == Constraint::COMMENT) { if(c->type == Constraint::COMMENT) {
(gs.stylables)++; (gs.stylables)++;
(gs.comments)++;
} }
} }
} }
@ -1226,7 +1227,7 @@ void GraphicsWindow::Paint(int w, int h) {
double mat[16]; double mat[16];
// Last thing before display is to apply the perspective // Last thing before display is to apply the perspective
double clp = SS.cameraTangent*scale; double clp = SS.CameraTangent()*scale;
MakeMatrix(mat, 1, 0, 0, 0, MakeMatrix(mat, 1, 0, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0,
@ -1323,6 +1324,79 @@ void GraphicsWindow::Paint(int w, int h) {
glxUnlockColor(); glxUnlockColor();
if(showSnapGrid && LockedInWorkplane()) {
hEntity he = ActiveWorkplane();
EntityBase *wrkpl = SK.GetEntity(he),
*norm = wrkpl->Normal();
Vector wu, wv, wn, wp;
wp = SK.GetEntity(wrkpl->point[0])->PointGetNum();
wu = norm->NormalU();
wv = norm->NormalV();
wn = norm->NormalN();
double g = SS.gridSpacing;
double umin = VERY_POSITIVE, umax = VERY_NEGATIVE,
vmin = VERY_POSITIVE, vmax = VERY_NEGATIVE;
int a;
for(a = 0; a < 4; a++) {
// Ideally, we would just do +/- half the width and height; but
// allow some extra slop for rounding.
Vector horiz = projRight.ScaledBy((0.6*width)/scale + 2*g),
vert = projUp. ScaledBy((0.6*height)/scale + 2*g);
if(a == 2 || a == 3) horiz = horiz.ScaledBy(-1);
if(a == 1 || a == 3) vert = vert. ScaledBy(-1);
Vector tp = horiz.Plus(vert).Minus(offset);
// Project the point into our grid plane, normal to the screen
// (not to the grid plane). If the plane is on edge then this is
// impossible so don't try to draw the grid.
bool parallel;
Vector tpp = Vector::AtIntersectionOfPlaneAndLine(
wn, wn.Dot(wp),
tp, tp.Plus(n),
&parallel);
if(parallel) goto nogrid;
tpp = tpp.Minus(wp);
double uu = tpp.Dot(wu),
vv = tpp.Dot(wv);
umin = min(uu, umin);
umax = max(uu, umax);
vmin = min(vv, vmin);
vmax = max(vv, vmax);
}
int i, j, i0, i1, j0, j1;
i0 = (int)(umin / g);
i1 = (int)(umax / g);
j0 = (int)(vmin / g);
j1 = (int)(vmax / g);
if(i0 > i1 || i1 - i0 > 400) goto nogrid;
if(j0 > j1 || j1 - j0 > 400) goto nogrid;
glLineWidth(1);
glxColorRGBa(Style::Color(Style::DATUM), 0.3);
glBegin(GL_LINES);
for(i = i0 + 1; i < i1; i++) {
glxVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j0*g)));
glxVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j1*g)));
}
for(j = j0 + 1; j < j1; j++) {
glxVertex3v(wp.Plus(wu.ScaledBy(i0*g)).Plus(wv.ScaledBy(j*g)));
glxVertex3v(wp.Plus(wu.ScaledBy(i1*g)).Plus(wv.ScaledBy(j*g)));
}
glEnd();
// Clear the depth buffer, so that the grid is at the very back of
// the Z order.
glClear(GL_DEPTH_BUFFER_BIT);
nogrid:;
}
// Draw the active group; this fills the polygons in a drawing group, and // Draw the active group; this fills the polygons in a drawing group, and
// draws the solid mesh. // draws the solid mesh.
(SK.GetGroup(activeGroup))->Draw(); (SK.GetGroup(activeGroup))->Draw();

View File

@ -31,8 +31,10 @@ void Constraint::LineDrawOrGetDistance(Vector a, Vector b) {
if(dogd.sel) { if(dogd.sel) {
dogd.sel->AddEdge(a, b, hs.v); dogd.sel->AddEdge(a, b, hs.v);
} else { } else {
if(hs.v && Style::Width(disp.style) >= 3.0) { // The only constraints with styles should be comments, so don't
glxFatLine(a, b, Style::Width(disp.style) / SS.GW.scale); // check otherwise, save looking up the styles constantly.
if(type == COMMENT && Style::Width(hs) >= 3.0) {
glxFatLine(a, b, Style::Width(hs) / SS.GW.scale);
} else { } else {
glBegin(GL_LINE_STRIP); glBegin(GL_LINE_STRIP);
glxVertex3v(a); glxVertex3v(a);
@ -95,10 +97,18 @@ void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) {
sheight = glxStrHeight(th); sheight = glxStrHeight(th);
// By default, the reference is from the center; but the style could // By default, the reference is from the center; but the style could
// specify otherwise if one is present. // specify otherwise if one is present, and it could also specify a
// rotation.
if(type == COMMENT && disp.style.v) { if(type == COMMENT && disp.style.v) {
Style *s = Style::Get(disp.style); Style *st = Style::Get(disp.style);
int o = s->textOrigin; // rotation first
double rads = st->textAngle*PI/180;
double c = cos(rads), s = sin(rads);
Vector pr = gr, pu = gu;
gr = pr.ScaledBy( c).Plus(pu.ScaledBy(s));
gu = pr.ScaledBy(-s).Plus(pu.ScaledBy(c));
// then origin
int o = st->textOrigin;
if(o & Style::ORIGIN_LEFT) ref = ref.Plus(gr.WithMagnitude(swidth/2)); 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_RIGHT) ref = ref.Minus(gr.WithMagnitude(swidth/2));
if(o & Style::ORIGIN_BOT) ref = ref.Plus(gu.WithMagnitude(sheight/2)); if(o & Style::ORIGIN_BOT) ref = ref.Plus(gu.WithMagnitude(sheight/2));
@ -378,8 +388,15 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
// If the group is hidden, then the constraints are hidden and not // If the group is hidden, then the constraints are hidden and not
// able to be selected. // able to be selected.
if(!(g->visible)) return; if(!(g->visible)) return;
// And likewise if the group is not the active group. // And likewise if the group is not the active group; except for comments
if(g->h.v != SS.GW.activeGroup.v) return; // with an assigned style.
if(g->h.v != SS.GW.activeGroup.v && !(type == COMMENT && disp.style.v)) {
return;
}
if(disp.style.v) {
Style *s = Style::Get(disp.style);
if(!s->visible) return;
}
// Unit vectors that describe our current view of the scene. One pixel // Unit vectors that describe our current view of the scene. One pixel
// long, not one actual unit. // long, not one actual unit.
@ -917,13 +934,23 @@ s:
} }
break; break;
case COMMENT: case COMMENT: {
if(disp.style.v) { if(disp.style.v) {
glLineWidth(Style::Width(disp.style)); glLineWidth(Style::Width(disp.style));
glxColorRGB(Style::Color(disp.style)); glxColorRGB(Style::Color(disp.style));
} }
DoLabel(disp.offset, labelPos, gr, gu); Vector u, v;
if(workplane.v == Entity::FREE_IN_3D.v) {
u = gr;
v = gu;
} else {
EntityBase *norm = SK.GetEntity(workplane)->Normal();
u = norm->NormalU();
v = norm->NormalV();
}
DoLabel(disp.offset, labelPos, u, v);
break; break;
}
default: oops(); default: oops();
} }

View File

@ -166,7 +166,7 @@ void SolveSpace::ExportViewTo(char *filename) {
VectorFileWriter *out = VectorFileWriter::ForFile(filename); VectorFileWriter *out = VectorFileWriter::ForFile(filename);
if(out) { if(out) {
ExportLinesAndMesh(&edges, &beziers, sm, ExportLinesAndMesh(&edges, &beziers, sm,
u, v, n, origin, SS.cameraTangent*SS.GW.scale, u, v, n, origin, SS.CameraTangent()*SS.GW.scale,
out); out);
} }
edges.Clear(); edges.Clear();

View File

@ -157,6 +157,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 's', "Style.widthAs", 'd', &(SS.sv.s.widthAs) }, { 's', "Style.widthAs", 'd', &(SS.sv.s.widthAs) },
{ 's', "Style.textHeight", 'f', &(SS.sv.s.textHeight) }, { 's', "Style.textHeight", 'f', &(SS.sv.s.textHeight) },
{ 's', "Style.textHeightAs", 'd', &(SS.sv.s.textHeightAs) }, { 's', "Style.textHeightAs", 'd', &(SS.sv.s.textHeightAs) },
{ 's', "Style.textAngle", 'f', &(SS.sv.s.textAngle) },
{ 's', "Style.textOrigin", 'x', &(SS.sv.s.textOrigin) }, { 's', "Style.textOrigin", 'x', &(SS.sv.s.textOrigin) },
{ 's', "Style.color", 'x', &(SS.sv.s.color) }, { 's', "Style.color", 'x', &(SS.sv.s.color) },
{ 's', "Style.visible", 'b', &(SS.sv.s.visible) }, { 's', "Style.visible", 'b', &(SS.sv.s.visible) },

View File

@ -32,9 +32,9 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "&Redo\tCtrl+Y", MNU_REDO, 'Y'|C, mEdit }, { 1, "&Redo\tCtrl+Y", MNU_REDO, 'Y'|C, mEdit },
{ 1, "Re&generate All\tSpace", MNU_REGEN_ALL, ' ', mEdit }, { 1, "Re&generate All\tSpace", MNU_REGEN_ALL, ' ', mEdit },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
{ 1, "Snap Selection to &Grid\t.", MNU_SNAP_TO_GRID, '.', mEdit },
{ 1, "Rotate Imported &90°\t9", MNU_ROTATE_90, '9', mEdit }, { 1, "Rotate Imported &90°\t9", MNU_ROTATE_90, '9', mEdit },
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit }, { 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit }, { 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
@ -43,6 +43,9 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "Zoom &Out\t-", MNU_ZOOM_OUT, '-', mView }, { 1, "Zoom &Out\t-", MNU_ZOOM_OUT, '-', mView },
{ 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView }, { 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
{ 1, "Show Snap &Grid\t>", MNU_SHOW_GRID, '.'|S, mView },
{ 1, "Force &Parallel Projection\t`", MNU_PARALLEL_PROJ, '`', mView },
{ 1, NULL, 0, NULL },
{ 1, "Nearest &Ortho View\tF2", MNU_NEAREST_ORTHO, F(2), mView }, { 1, "Nearest &Ortho View\tF2", MNU_NEAREST_ORTHO, F(2), mView },
{ 1, "Nearest &Isometric View\tF3", MNU_NEAREST_ISO, F(3), mView }, { 1, "Nearest &Isometric View\tF3", MNU_NEAREST_ISO, F(3), mView },
{ 1, "&Center View At Point\tF4", MNU_CENTER_VIEW, F(4), mView }, { 1, "&Center View At Point\tF4", MNU_CENTER_VIEW, F(4), mView },
@ -154,6 +157,7 @@ void GraphicsWindow::Init(void) {
showTextWindow = true; showTextWindow = true;
ShowTextWindow(showTextWindow); ShowTextWindow(showTextWindow);
showSnapGrid = false;
context.active = false; context.active = false;
// Do this last, so that all the menus get updated correctly. // Do this last, so that all the menus get updated correctly.
@ -199,7 +203,7 @@ Vector GraphicsWindow::ProjectPoint4(Vector p, double *w) {
r.y = p.Dot(projUp); r.y = p.Dot(projUp);
r.z = p.Dot(projUp.Cross(projRight)); r.z = p.Dot(projUp.Cross(projRight));
*w = 1 + r.z*SS.cameraTangent*scale; *w = 1 + r.z*SS.CameraTangent()*scale;
return r; return r;
} }
@ -230,6 +234,10 @@ void GraphicsWindow::AnimateOnto(Quaternion quatf, Vector offsetf) {
// Animate transition, unless it's a tiny move. // Animate transition, unless it's a tiny move.
SDWORD dt = (mp < 0.01 && mo < 10) ? (-20) : SDWORD dt = (mp < 0.01 && mo < 10) ? (-20) :
(SDWORD)(100 + 1000*mp + 0.4*mo); (SDWORD)(100 + 1000*mp + 0.4*mo);
// Don't ever animate for longer than 2000 ms; we can get absurdly
// long translations (as measured in pixels) if the user zooms out, moves,
// and then zooms in again.
if(dt > 2000) dt = 2000;
SDWORD tn, t0 = GetMilliseconds(); SDWORD tn, t0 = GetMilliseconds();
double s = 0; double s = 0;
Quaternion dq = quatf.Times(quat0.Inverse()); Quaternion dq = quatf.Times(quat0.Inverse());
@ -347,7 +355,7 @@ void GraphicsWindow::ZoomToFit(bool includingInvisibles) {
// Adjust the scale so that no points are behind the camera // Adjust the scale so that no points are behind the camera
if(wmin < 0.1) { if(wmin < 0.1) {
double k = SS.cameraTangent; double k = SS.CameraTangent();
// w = 1+k*scale*z // w = 1+k*scale*z
double zmin = (wmin - 1)/(k*scale); double zmin = (wmin - 1)/(k*scale);
// 0.1 = 1 + k*scale*zmin // 0.1 = 1 + k*scale*zmin
@ -370,6 +378,29 @@ void GraphicsWindow::MenuView(int id) {
SS.GW.ZoomToFit(false); SS.GW.ZoomToFit(false);
break; break;
case MNU_SHOW_GRID:
SS.GW.showSnapGrid = !SS.GW.showSnapGrid;
if(SS.GW.showSnapGrid && !SS.GW.LockedInWorkplane()) {
Message("No workplane is active, so the grid will not "
"appear.");
}
SS.GW.EnsureValidActives();
InvalidateGraphics();
break;
case MNU_PARALLEL_PROJ:
SS.forceParallelProj = !SS.forceParallelProj;
if(SS.cameraTangent < 1e-6) {
Error("The perspective factor is set to zero, so the view will "
"always be a parallel projection.\r\n\r\n"
"For a perspective projection, modify the camera tangent "
"in the configuration screen. A value around 0.3 is "
"typical.");
}
SS.GW.EnsureValidActives();
InvalidateGraphics();
break;
case MNU_NEAREST_ORTHO: case MNU_NEAREST_ORTHO:
case MNU_NEAREST_ISO: { case MNU_NEAREST_ISO: {
static const Vector ortho[3] = { static const Vector ortho[3] = {
@ -448,7 +479,7 @@ void GraphicsWindow::MenuView(int id) {
case MNU_SHOW_TOOLBAR: case MNU_SHOW_TOOLBAR:
SS.showToolbar = !SS.showToolbar; SS.showToolbar = !SS.showToolbar;
SS.GW.EnsureValidActives(); SS.GW.EnsureValidActives();
PaintGraphics(); InvalidateGraphics();
break; break;
case MNU_UNITS_MM: case MNU_UNITS_MM:
@ -532,6 +563,8 @@ void GraphicsWindow::EnsureValidActives(void) {
CheckMenuById(MNU_SHOW_TEXT_WND, SS.GW.showTextWindow); CheckMenuById(MNU_SHOW_TEXT_WND, SS.GW.showTextWindow);
CheckMenuById(MNU_SHOW_TOOLBAR, SS.showToolbar); CheckMenuById(MNU_SHOW_TOOLBAR, SS.showToolbar);
CheckMenuById(MNU_PARALLEL_PROJ, SS.forceParallelProj);
CheckMenuById(MNU_SHOW_GRID, SS.GW.showSnapGrid);
if(change) SS.later.showTW = true; if(change) SS.later.showTW = true;
} }
@ -583,6 +616,24 @@ void GraphicsWindow::DeleteTaggedRequests(void) {
SS.later.showTW = true; SS.later.showTW = true;
} }
Vector GraphicsWindow::SnapToGrid(Vector p) {
if(!LockedInWorkplane()) return p;
EntityBase *wrkpl = SK.GetEntity(ActiveWorkplane()),
*norm = wrkpl->Normal();
Vector wo = SK.GetEntity(wrkpl->point[0])->PointGetNum(),
wu = norm->NormalU(),
wv = norm->NormalV(),
wn = norm->NormalN();
Vector pp = (p.Minus(wo)).DotInToCsys(wu, wv, wn);
pp.x = floor((pp.x / SS.gridSpacing) + 0.5)*SS.gridSpacing;
pp.y = floor((pp.y / SS.gridSpacing) + 0.5)*SS.gridSpacing;
pp.z = 0;
return pp.ScaleOutOfCsys(wu, wv, wn).Plus(wo);
}
void GraphicsWindow::MenuEdit(int id) { void GraphicsWindow::MenuEdit(int id) {
switch(id) { switch(id) {
case MNU_UNSELECT_ALL: case MNU_UNSELECT_ALL:
@ -590,7 +641,10 @@ void GraphicsWindow::MenuEdit(int id) {
// If there's nothing selected to de-select, and no operation // If there's nothing selected to de-select, and no operation
// to cancel, then perhaps they want to return to the home // to cancel, then perhaps they want to return to the home
// screen in the text window. // screen in the text window.
if(SS.GW.gs.n == 0 && SS.GW.pending.operation == 0) { if(SS.GW.gs.n == 0 &&
SS.GW.gs.constraints == 0 &&
SS.GW.pending.operation == 0)
{
if(!(TextEditControlIsVisible() || if(!(TextEditControlIsVisible() ||
GraphicsEditControlIsVisible())) GraphicsEditControlIsVisible()))
{ {
@ -667,6 +721,46 @@ void GraphicsWindow::MenuEdit(int id) {
break; break;
} }
case MNU_SNAP_TO_GRID: {
if(!SS.GW.LockedInWorkplane()) {
Error("No workplane is active. Select a workplane to define "
"the plane for the snap grid.");
break;
}
SS.GW.GroupSelection();
if(SS.GW.gs.n != SS.GW.gs.points ||
SS.GW.gs.constraints != SS.GW.gs.comments ||
(SS.GW.gs.n == 0 && SS.GW.gs.constraints == 0))
{
Error("Can't snap these items to grid; select only points or "
"text comments. To snap a line, select its endpoints.");
break;
}
SS.UndoRemember();
int i;
for(i = 0; i < SS.GW.gs.points; i++) {
hEntity hp = SS.GW.gs.point[i];
Entity *ep = SK.GetEntity(hp);
Vector p = ep->PointGetNum();
ep->PointForceTo(SS.GW.SnapToGrid(p));
// Regenerate, with this point marked as dragged so that it
// gets placed as close as possible to our snap
SS.GW.pending.point = hp;
SS.MarkGroupDirty(ep->group);
SS.GenerateAll();
SS.GW.pending.point = Entity::NO_ENTITY;
}
for(i = 0; i < SS.GW.gs.constraints; i++) {
Constraint *c = SK.GetConstraint(SS.GW.gs.constraint[i]);
c->disp.offset = SS.GW.SnapToGrid(c->disp.offset);
}
SS.GW.ClearSelection();
InvalidateGraphics();
break;
}
case MNU_UNDO: case MNU_UNDO:
SS.UndoUndo(); SS.UndoUndo();
break; break;
@ -715,6 +809,7 @@ void GraphicsWindow::MenuRequest(int id) {
SS.GW.SetWorkplaneFreeIn3d(); SS.GW.SetWorkplaneFreeIn3d();
SS.GW.EnsureValidActives(); SS.GW.EnsureValidActives();
SS.later.showTW = true; SS.later.showTW = true;
InvalidateGraphics();
break; break;
case MNU_ARC: { case MNU_ARC: {

View File

@ -635,6 +635,7 @@ public:
static const int ORIGIN_BOT = 0x04; static const int ORIGIN_BOT = 0x04;
static const int ORIGIN_TOP = 0x08; static const int ORIGIN_TOP = 0x08;
int textOrigin; int textOrigin;
double textAngle;
DWORD color; DWORD color;
bool visible; bool visible;
bool exportable; bool exportable;

View File

@ -64,6 +64,8 @@ void SolveSpace::Init(char *cmdLine) {
viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits"); viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits");
// Camera tangent (determines perspective) // Camera tangent (determines perspective)
cameraTangent = CnfThawFloat(0.0f, "CameraTangent"); cameraTangent = CnfThawFloat(0.0f, "CameraTangent");
// Grid spacing
gridSpacing = CnfThawFloat(5.0f, "GridSpacing");
// Export scale factor // Export scale factor
exportScale = CnfThawFloat(1.0f, "ExportScale"); exportScale = CnfThawFloat(1.0f, "ExportScale");
// Export offset (cutter radius comp) // Export offset (cutter radius comp)
@ -150,6 +152,8 @@ void SolveSpace::Exit(void) {
CnfFreezeDWORD((DWORD)viewUnits, "ViewUnits"); CnfFreezeDWORD((DWORD)viewUnits, "ViewUnits");
// Camera tangent (determines perspective) // Camera tangent (determines perspective)
CnfFreezeFloat((float)cameraTangent, "CameraTangent"); CnfFreezeFloat((float)cameraTangent, "CameraTangent");
// Grid spacing
CnfFreezeFloat(gridSpacing, "GridSpacing");
// Export scale (a float, stored as a DWORD) // Export scale (a float, stored as a DWORD)
CnfFreezeFloat(exportScale, "ExportScale"); CnfFreezeFloat(exportScale, "ExportScale");
// Export offset (cutter radius comp) // Export offset (cutter radius comp)
@ -191,25 +195,6 @@ void SolveSpace::DoLater(void) {
ZERO(&later); ZERO(&later);
} }
int SolveSpace::CircleSides(double r) {
// Let the pwl segment be symmetric about the x axis; then the curve
// goes out to r, and if there's n segments, then the endpoints are
// at +/- (2pi/n)/2 = +/- pi/n. So the chord goes to x = r cos pi/n,
// from x = r, so it's
// tol = r - r cos pi/n
// tol = r(1 - cos pi/n)
// tol ~ r(1 - (1 - (pi/n)^2/2)) (Taylor expansion)
// tol = r((pi/n)^2/2)
// 2*tol/r = (pi/n)^2
// sqrt(2*tol/r) = pi/n
// n = pi/sqrt(2*tol/r);
double tol = chordTol/GW.scale;
int n = 3 + (int)(PI/sqrt(2*tol/r));
return max(7, min(n, maxSegments));
}
char *SolveSpace::MmToString(double v) { char *SolveSpace::MmToString(double v) {
static int WhichBuf; static int WhichBuf;
static char Bufs[8][128]; static char Bufs[8][128];
@ -243,6 +228,13 @@ double SolveSpace::ChordTolMm(void) {
return SS.chordTol / SS.GW.scale; return SS.chordTol / SS.GW.scale;
} }
double SolveSpace::CameraTangent(void) {
if(forceParallelProj) {
return 0;
} else {
return cameraTangent;
}
}
void SolveSpace::AfterNewFile(void) { void SolveSpace::AfterNewFile(void) {
// Clear out the traced point, which is no longer valid // Clear out the traced point, which is no longer valid

View File

@ -547,6 +547,7 @@ public:
double chordTol; double chordTol;
int maxSegments; int maxSegments;
double cameraTangent; double cameraTangent;
float gridSpacing;
float exportScale; float exportScale;
float exportOffset; float exportOffset;
int fixExportColors; int fixExportColors;
@ -569,7 +570,6 @@ public:
float dy; float dy;
} exportCanvas; } exportCanvas;
int CircleSides(double r);
typedef enum { typedef enum {
UNIT_MM = 0, UNIT_MM = 0,
UNIT_INCHES, UNIT_INCHES,
@ -579,6 +579,8 @@ public:
double ExprToMm(Expr *e); double ExprToMm(Expr *e);
double StringToMm(char *s); double StringToMm(char *s);
double ChordTolMm(void); double ChordTolMm(void);
bool forceParallelProj;
double CameraTangent(void);
// The platform-dependent code calls this before entering the msg loop // The platform-dependent code calls this before entering the msg loop
void Init(char *cmdLine); void Init(char *cmdLine);

View File

@ -73,6 +73,8 @@ void Style::CreateDefaultStyle(hStyle h) {
ns.widthAs = UNITS_AS_PIXELS; ns.widthAs = UNITS_AS_PIXELS;
ns.textHeight = DEFAULT_TEXT_HEIGHT; ns.textHeight = DEFAULT_TEXT_HEIGHT;
ns.textHeightAs = UNITS_AS_PIXELS; ns.textHeightAs = UNITS_AS_PIXELS;
ns.textOrigin = 0;
ns.textAngle = 0;
ns.visible = true; ns.visible = true;
ns.exportable = true; ns.exportable = true;
ns.h = h; ns.h = h;
@ -95,6 +97,8 @@ void Style::LoadFactoryDefaults(void) {
s->widthAs = UNITS_AS_PIXELS; s->widthAs = UNITS_AS_PIXELS;
s->textHeight = DEFAULT_TEXT_HEIGHT; s->textHeight = DEFAULT_TEXT_HEIGHT;
s->textHeightAs = UNITS_AS_PIXELS; s->textHeightAs = UNITS_AS_PIXELS;
s->textOrigin = 0;
s->textAngle = 0;
s->visible = true; s->visible = true;
s->exportable = true; s->exportable = true;
s->name.strcpy(CnfPrefixToName(d->cnfPrefix)); s->name.strcpy(CnfPrefixToName(d->cnfPrefix));
@ -375,8 +379,8 @@ void TextWindow::ScreenDeleteStyle(int link, DWORD v) {
void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, DWORD v) { void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, DWORD v) {
hStyle hs = { v }; hStyle hs = { v };
Style *s = Style::Get(hs); Style *s = Style::Get(hs);
double val = (link == 'w') ? s->width : s->textHeight; double val = (link == 't') ? s->textHeight : s->width;
int units = (link == 'w') ? s->widthAs : s->textHeightAs; int units = (link == 't') ? s->textHeightAs : s->widthAs;
char str[300]; char str[300];
if(units == Style::UNITS_AS_PIXELS) { if(units == Style::UNITS_AS_PIXELS) {
@ -384,10 +388,28 @@ void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, DWORD v) {
} else { } else {
strcpy(str, SS.MmToString(val)); strcpy(str, SS.MmToString(val));
} }
ShowTextEditControl((link == 'w') ? 21 : 26, 13, str); int row = 0;
if(link == 'w') {
row = 16; // width for a default style
} else if(link == 'W') {
row = 21; // width for a custom style
} else if(link == 't') {
row = 27; // text height (for custom styles only)
}
ShowTextEditControl(row, 13, str);
SS.TW.edit.style = hs; SS.TW.edit.style = hs;
SS.TW.edit.meaning = (link == 'w') ? EDIT_STYLE_WIDTH : SS.TW.edit.meaning = (link == 't') ? EDIT_STYLE_TEXT_HEIGHT :
EDIT_STYLE_TEXT_HEIGHT; EDIT_STYLE_WIDTH;
}
void TextWindow::ScreenChangeStyleTextAngle(int link, DWORD v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
char str[300];
sprintf(str, "%.2f", s->textAngle);
ShowTextEditControl(32, 13, str);
SS.TW.edit.style = hs;
SS.TW.edit.meaning = EDIT_STYLE_TEXT_ANGLE;
} }
void TextWindow::ScreenChangeStyleColor(int link, DWORD v) { void TextWindow::ScreenChangeStyleColor(int link, DWORD v) {
@ -491,6 +513,12 @@ bool TextWindow::EditControlDoneForStyles(char *str) {
} }
return true; return true;
} }
case EDIT_STYLE_TEXT_ANGLE:
SS.UndoRemember();
s = Style::Get(edit.style);
s->textAngle = WRAP_SYMMETRIC(atof(str), 360);
return true;
case EDIT_BACKGROUND_COLOR: case EDIT_BACKGROUND_COLOR:
case EDIT_STYLE_COLOR: { case EDIT_STYLE_COLOR: {
double r, g, b; double r, g, b;
@ -565,13 +593,15 @@ void TextWindow::ShowStyleInfo(void) {
// The line width, and its units // The line width, and its units
if(s->widthAs == Style::UNITS_AS_PIXELS) { if(s->widthAs == Style::UNITS_AS_PIXELS) {
Printf(true, "%FtLINE WIDTH %E%@ %D%f%Lw%Fl[change]%E", Printf(true, "%FtLINE WIDTH %E%@ %D%f%Lp%Fl[change]%E",
s->width, s->width,
s->h.v, &ScreenChangeStyleWidthOrTextHeight); s->h.v, &ScreenChangeStyleWidthOrTextHeight,
(s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
} else { } else {
Printf(true, "%FtLINE WIDTH %E%s %D%f%Lw%Fl[change]%E", Printf(true, "%FtLINE WIDTH %E%s %D%f%Lp%Fl[change]%E",
SS.MmToString(s->width), SS.MmToString(s->width),
s->h.v, &ScreenChangeStyleWidthOrTextHeight); s->h.v, &ScreenChangeStyleWidthOrTextHeight,
(s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
} }
bool widthpx = (s->widthAs == Style::UNITS_AS_PIXELS); bool widthpx = (s->widthAs == Style::UNITS_AS_PIXELS);
@ -589,14 +619,15 @@ void TextWindow::ShowStyleInfo(void) {
} }
// The text height, and its units // The text height, and its units
Printf(false, "");
char *chng = (s->h.v < Style::FIRST_CUSTOM) ? "" : "[change]"; char *chng = (s->h.v < Style::FIRST_CUSTOM) ? "" : "[change]";
if(s->textHeightAs == Style::UNITS_AS_PIXELS) { if(s->textHeightAs == Style::UNITS_AS_PIXELS) {
Printf(true, "%FtTEXT HEIGHT %E%@ %D%f%Lt%Fl%s%E", Printf(false, "%FtTEXT HEIGHT %E%@ %D%f%Lt%Fl%s%E",
s->textHeight, s->textHeight,
s->h.v, &ScreenChangeStyleWidthOrTextHeight, s->h.v, &ScreenChangeStyleWidthOrTextHeight,
chng); chng);
} else { } else {
Printf(true, "%FtTEXT HEIGHT %E%s %D%f%Lt%Fl%s%E", Printf(false, "%FtTEXT HEIGHT %E%s %D%f%Lt%Fl%s%E",
SS.MmToString(s->textHeight), SS.MmToString(s->textHeight),
s->h.v, &ScreenChangeStyleWidthOrTextHeight, s->h.v, &ScreenChangeStyleWidthOrTextHeight,
chng); chng);
@ -619,6 +650,10 @@ void TextWindow::ShowStyleInfo(void) {
if(s->h.v >= Style::FIRST_CUSTOM) { if(s->h.v >= Style::FIRST_CUSTOM) {
bool neither; bool neither;
Printf(true, "%FtTEXT ANGLE %E%@ %D%f%Ll%Fl[change]%E",
s->textAngle,
s->h.v, &ScreenChangeStyleTextAngle);
neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT)); neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT));
Printf(true, "%FtALIGN TEXT " Printf(true, "%FtALIGN TEXT "
"%Fh%D%f%LL%s%E%Fs%s%E / " "%Fh%D%f%LL%s%E%Fs%s%E / "

View File

@ -273,7 +273,7 @@ void TextWindow::ScreenChangeHelixParameter(int link, DWORD v) {
sprintf(str, "%.3f", g->valA); sprintf(str, "%.3f", g->valA);
SS.TW.edit.meaning = EDIT_HELIX_TURNS; SS.TW.edit.meaning = EDIT_HELIX_TURNS;
r = 12; r = 12;
} else if(link == 'p') { } else if(link == 'i') {
strcpy(str, SS.MmToString(g->valB)); strcpy(str, SS.MmToString(g->valB));
SS.TW.edit.meaning = EDIT_HELIX_PITCH; SS.TW.edit.meaning = EDIT_HELIX_PITCH;
r = 14; r = 14;
@ -383,7 +383,7 @@ void TextWindow::ShowGroupInfo(void) {
(!rh ? "" : "left-hand"), (!rh ? "left-hand" : "")); (!rh ? "" : "left-hand"), (!rh ? "left-hand" : ""));
Printf(false, "%FtTHROUGH%E %@ turns %Fl%Lt%D%f[change]%E", Printf(false, "%FtTHROUGH%E %@ turns %Fl%Lt%D%f[change]%E",
g->valA, g->h.v, &ScreenChangeHelixParameter); g->valA, g->h.v, &ScreenChangeHelixParameter);
Printf(false, "%FtPITCH%E %s axially per turn %Fl%Lp%D%f[change]%E", Printf(false, "%FtPITCH%E %s axially per turn %Fl%Li%D%f[change]%E",
SS.MmToString(g->valB), g->h.v, &ScreenChangeHelixParameter); SS.MmToString(g->valB), g->h.v, &ScreenChangeHelixParameter);
Printf(false, "%FtdRADIUS%E %s radially per turn %Fl%Lr%D%f[change]%E", Printf(false, "%FtdRADIUS%E %s radially per turn %Fl%Lr%D%f[change]%E",
SS.MmToString(g->valC), g->h.v, &ScreenChangeHelixParameter); SS.MmToString(g->valC), g->h.v, &ScreenChangeHelixParameter);
@ -625,15 +625,19 @@ void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) {
ShowTextEditControl(47, 3, str); ShowTextEditControl(47, 3, str);
SS.TW.edit.meaning = EDIT_CAMERA_TANGENT; SS.TW.edit.meaning = EDIT_CAMERA_TANGENT;
} }
void TextWindow::ScreenChangeGridSpacing(int link, DWORD v) {
ShowTextEditControl(51, 3, SS.MmToString(SS.gridSpacing));
SS.TW.edit.meaning = EDIT_GRID_SPACING;
}
void TextWindow::ScreenChangeExportScale(int link, DWORD v) { void TextWindow::ScreenChangeExportScale(int link, DWORD v) {
char str[1024]; char str[1024];
sprintf(str, "%.3f", (double)SS.exportScale); sprintf(str, "%.3f", (double)SS.exportScale);
ShowTextEditControl(53, 3, str); ShowTextEditControl(57, 3, str);
SS.TW.edit.meaning = EDIT_EXPORT_SCALE; SS.TW.edit.meaning = EDIT_EXPORT_SCALE;
} }
void TextWindow::ScreenChangeExportOffset(int link, DWORD v) { void TextWindow::ScreenChangeExportOffset(int link, DWORD v) {
ShowTextEditControl(57, 3, SS.MmToString(SS.exportOffset)); ShowTextEditControl(61, 3, SS.MmToString(SS.exportOffset));
SS.TW.edit.meaning = EDIT_EXPORT_OFFSET; SS.TW.edit.meaning = EDIT_EXPORT_OFFSET;
} }
void TextWindow::ScreenChangeFixExportColors(int link, DWORD v) { void TextWindow::ScreenChangeFixExportColors(int link, DWORD v) {
@ -670,7 +674,7 @@ void TextWindow::ScreenChangeCanvasSize(int link, DWORD v) {
default: return; default: return;
} }
int row = 71, col; int row = 75, col;
if(v < 10) { if(v < 10) {
row += v*2; row += v*2;
col = 11; col = 11;
@ -723,6 +727,10 @@ void TextWindow::ShowConfiguration(void) {
Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E", Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E",
SS.cameraTangent*1000, SS.cameraTangent*1000,
&ScreenChangeCameraTangent, 0); &ScreenChangeCameraTangent, 0);
Printf(false, "%Ft snap grid spacing%E");
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
SS.MmToString(SS.gridSpacing),
&ScreenChangeGridSpacing, 0);
Printf(false, ""); Printf(false, "");
Printf(false, "%Ft export scale factor (1.0=mm, 25.4=inch)"); Printf(false, "%Ft export scale factor (1.0=mm, 25.4=inch)");
@ -984,6 +992,15 @@ void TextWindow::EditControlDone(char *s) {
} }
case EDIT_CAMERA_TANGENT: { case EDIT_CAMERA_TANGENT: {
SS.cameraTangent = (min(2, max(0, atof(s))))/1000.0; SS.cameraTangent = (min(2, max(0, atof(s))))/1000.0;
if(SS.forceParallelProj) {
Message("The perspective factor will have no effect until you "
"disable View -> Force Parallel Projection.");
}
InvalidateGraphics();
break;
}
case EDIT_GRID_SPACING: {
SS.gridSpacing = (float)min(1e4, max(1e-3, SS.StringToMm(s)));
InvalidateGraphics(); InvalidateGraphics();
break; break;
} }

View File

@ -144,7 +144,11 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) {
case 'L': case 'L':
if(fmt[1] == '\0') goto done; if(fmt[1] == '\0') goto done;
fmt++; fmt++;
if(*fmt == 'p') {
link = va_arg(vl, int);
} else {
link = *fmt; link = *fmt;
}
break; break;
case 'f': case 'f':

23
ui.h
View File

@ -80,9 +80,10 @@ public:
static const int EDIT_CHORD_TOLERANCE = 13; static const int EDIT_CHORD_TOLERANCE = 13;
static const int EDIT_MAX_SEGMENTS = 14; static const int EDIT_MAX_SEGMENTS = 14;
static const int EDIT_CAMERA_TANGENT = 15; static const int EDIT_CAMERA_TANGENT = 15;
static const int EDIT_EXPORT_SCALE = 16; static const int EDIT_GRID_SPACING = 16;
static const int EDIT_EXPORT_OFFSET = 17; static const int EDIT_EXPORT_SCALE = 17;
static const int EDIT_CANVAS_SIZE = 18; static const int EDIT_EXPORT_OFFSET = 18;
static const int EDIT_CANVAS_SIZE = 19;
// For the helical sweep // For the helical sweep
static const int EDIT_HELIX_TURNS = 20; static const int EDIT_HELIX_TURNS = 20;
static const int EDIT_HELIX_PITCH = 21; static const int EDIT_HELIX_PITCH = 21;
@ -95,9 +96,10 @@ public:
// For the styles stuff // For the styles stuff
static const int EDIT_STYLE_WIDTH = 50; static const int EDIT_STYLE_WIDTH = 50;
static const int EDIT_STYLE_TEXT_HEIGHT = 51; static const int EDIT_STYLE_TEXT_HEIGHT = 51;
static const int EDIT_STYLE_COLOR = 52; static const int EDIT_STYLE_TEXT_ANGLE = 52;
static const int EDIT_STYLE_NAME = 53; static const int EDIT_STYLE_COLOR = 53;
static const int EDIT_BACKGROUND_COLOR = 54; static const int EDIT_STYLE_NAME = 54;
static const int EDIT_BACKGROUND_COLOR = 55;
struct { struct {
int meaning; int meaning;
int i; int i;
@ -187,10 +189,12 @@ public:
static void ScreenChangeChordTolerance(int link, DWORD v); static void ScreenChangeChordTolerance(int link, DWORD v);
static void ScreenChangeMaxSegments(int link, DWORD v); static void ScreenChangeMaxSegments(int link, DWORD v);
static void ScreenChangeCameraTangent(int link, DWORD v); static void ScreenChangeCameraTangent(int link, DWORD v);
static void ScreenChangeGridSpacing(int link, DWORD v);
static void ScreenChangeExportScale(int link, DWORD v); static void ScreenChangeExportScale(int link, DWORD v);
static void ScreenChangeExportOffset(int link, DWORD v); static void ScreenChangeExportOffset(int link, DWORD v);
static void ScreenChangeStyleName(int link, DWORD v); static void ScreenChangeStyleName(int link, DWORD v);
static void ScreenChangeStyleWidthOrTextHeight(int link, DWORD v); static void ScreenChangeStyleWidthOrTextHeight(int link, DWORD v);
static void ScreenChangeStyleTextAngle(int link, DWORD v);
static void ScreenChangeStyleColor(int link, DWORD v); static void ScreenChangeStyleColor(int link, DWORD v);
static void ScreenChangeBackgroundColor(int link, DWORD v); static void ScreenChangeBackgroundColor(int link, DWORD v);
@ -220,6 +224,8 @@ public:
MNU_ZOOM_IN, MNU_ZOOM_IN,
MNU_ZOOM_OUT, MNU_ZOOM_OUT,
MNU_ZOOM_TO_FIT, MNU_ZOOM_TO_FIT,
MNU_SHOW_GRID,
MNU_PARALLEL_PROJ,
MNU_NEAREST_ORTHO, MNU_NEAREST_ORTHO,
MNU_NEAREST_ISO, MNU_NEAREST_ISO,
MNU_CENTER_VIEW, MNU_CENTER_VIEW,
@ -231,6 +237,7 @@ public:
MNU_UNDO, MNU_UNDO,
MNU_REDO, MNU_REDO,
MNU_DELETE, MNU_DELETE,
MNU_SNAP_TO_GRID,
MNU_ROTATE_90, MNU_ROTATE_90,
MNU_UNSELECT_ALL, MNU_UNSELECT_ALL,
MNU_REGEN_ALL, MNU_REGEN_ALL,
@ -373,6 +380,7 @@ public:
// The constraint that is being edited with the on-screen textbox. // The constraint that is being edited with the on-screen textbox.
hConstraint constraintBeingEdited; hConstraint constraintBeingEdited;
Vector SnapToGrid(Vector p);
bool ConstrainPointByHovered(hEntity pt); bool ConstrainPointByHovered(hEntity pt);
void DeleteTaggedRequests(void); void DeleteTaggedRequests(void);
hRequest AddRequest(int type, bool rememberForUndo); hRequest AddRequest(int type, bool rememberForUndo);
@ -428,6 +436,7 @@ public:
int vectors; int vectors;
int constraints; int constraints;
int stylables; int stylables;
int comments;
int n; int n;
} gs; } gs;
void GroupSelection(void); void GroupSelection(void);
@ -469,6 +478,8 @@ public:
bool showHdnLines; bool showHdnLines;
static void ToggleBool(int link, DWORD v); static void ToggleBool(int link, DWORD v);
bool showSnapGrid;
void UpdateDraggedNum(Vector *pos, double mx, double my); void UpdateDraggedNum(Vector *pos, double mx, double my);
void UpdateDraggedPoint(hEntity hp, double mx, double my); void UpdateDraggedPoint(hEntity hp, double mx, double my);

View File

@ -539,9 +539,11 @@ static BOOL ProcessKeyDown(WPARAM wParam)
case VK_OEM_MINUS: c = '-'; break; case VK_OEM_MINUS: c = '-'; break;
case VK_ESCAPE: c = 27; break; case VK_ESCAPE: c = 27; break;
case VK_OEM_1: c = ';'; break; case VK_OEM_1: c = ';'; break;
case VK_OEM_3: c = '`'; break;
case VK_OEM_4: c = '['; break; case VK_OEM_4: c = '['; break;
case VK_OEM_6: c = ']'; break; case VK_OEM_6: c = ']'; break;
case VK_OEM_5: c = '\\'; break; case VK_OEM_5: c = '\\'; break;
case VK_OEM_PERIOD: c = '.'; break;
case VK_SPACE: c = ' '; break; case VK_SPACE: c = ' '; break;
case VK_DELETE: c = 127; break; case VK_DELETE: c = 127; break;
case VK_TAB: c = '\t'; break; case VK_TAB: c = '\t'; break;

View File

@ -1,5 +1,5 @@
grid split draw.cpp
multi-drag multi-drag
----- -----