Add reference dimensions, add user-programmable lighting, and add a

configuration screen where all this can be specified.

[git-p4: depot-paths = "//depot/solvespace/": change = 1783]
This commit is contained in:
Jonathan Westhues 2008-06-10 20:22:52 -08:00
parent f2645029b8
commit d471872830
16 changed files with 424 additions and 101 deletions

View File

@ -233,6 +233,22 @@ void Constraint::MenuConstrain(int id) {
} else {
c.type = SYMMETRIC_VERT;
}
if(gs.lineSegments == 1) {
// If this line segment is already constrained horiz or
// vert, then auto-remove that redundant constraint.
SS.constraint.ClearTags();
for(int i = 0; i < SS.constraint.n; i++) {
Constraint *ct = &(SS.constraint.elem[i]);
if(ct->type != HORIZONTAL && ct->type != VERTICAL) {
continue;
}
if(ct->entityA.v != (gs.entity[0]).v) continue;
ct->tag = 1;
}
SS.constraint.RemoveTagged();
// And no need to do anything special, since nothing
// ever depends on a constraint.
}
} else {
// Symmetry with a symmetry plane specified explicitly.
c.type = SYMMETRIC;
@ -290,7 +306,20 @@ void Constraint::MenuConstrain(int id) {
}
}
Error("Must select an angle constraint.");
return;
case GraphicsWindow::MNU_REFERENCE:
if(gs.constraints == 1 && gs.n == 0) {
Constraint *c = SS.GetConstraint(gs.constraint[0]);
if(c->HasLabel()) {
(c->reference) = !(c->reference);
SS.GetGroup(c->group)->clean = false;
SS.GenerateAll();
break;
}
}
Error("Must select a constraint with associated label.");
return;
case GraphicsWindow::MNU_ANGLE:
if(gs.vectors == 2 && gs.n == 2) {
@ -456,9 +485,9 @@ void Constraint::ModifyToSatisfy(void) {
// that means no extra work.
IdList<Equation,hEquation> l;
// An uninit IdList could lead us to free some random address, bad.
memset(&l, 0, sizeof(l));
Generate(&l);
ZERO(&l);
// Generate the equations even if this is a reference dimension
GenerateReal(&l);
if(l.n != 1) oops();
// These equations are written in the form f(...) - d = 0, where
@ -480,6 +509,11 @@ void Constraint::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) {
}
void Constraint::Generate(IdList<Equation,hEquation> *l) {
if(!reference) {
GenerateReal(l);
}
}
void Constraint::GenerateReal(IdList<Equation,hEquation> *l) {
Expr *exA = NULL;
if(exprA) exA = exprA->DeepCopy();

View File

@ -759,12 +759,12 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
}
}
Vector GraphicsWindow::VectorFromProjs(double right, double up, double fwd) {
Vector GraphicsWindow::VectorFromProjs(Vector rightUpForward) {
Vector n = projRight.Cross(projUp);
Vector r = offset.ScaledBy(-1);
r = r.Plus(projRight.ScaledBy(right));
r = r.Plus(projUp.ScaledBy(up));
r = r.Plus(n.ScaledBy(fwd));
Vector r = (projRight.ScaledBy(rightUpForward.x));
r = r.Plus(projUp.ScaledBy(rightUpForward.y));
r = r.Plus(n.ScaledBy(rightUpForward.z));
return r;
}
@ -810,21 +810,25 @@ void GraphicsWindow::Paint(int w, int h) {
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Let's use two lights
// Let's use two lights, at the user-specified locations
GLfloat f;
glEnable(GL_LIGHT0);
GLfloat li0[] = { 0.8f, 0.8f, 0.8f, 1.0f };
f = (GLfloat)SS.lightIntensity[0];
GLfloat li0[] = { f, f, f, 1.0f };
glLightfv(GL_LIGHT0, GL_DIFFUSE, li0);
glLightfv(GL_LIGHT0, GL_SPECULAR, li0);
glEnable(GL_LIGHT1);
GLfloat li1[] = { 0.4f, 0.4f, 0.4f, 1.0f };
f = (GLfloat)SS.lightIntensity[1];
GLfloat li1[] = { f, f, f, 1.0f };
glLightfv(GL_LIGHT1, GL_DIFFUSE, li1);
glLightfv(GL_LIGHT1, GL_SPECULAR, li1);
Vector lp;
lp = VectorFromProjs(-0.49*w/scale, 0.49*h/scale, 0);
lp = VectorFromProjs(SS.lightPos[0]);
GLfloat lp0[4] = { (GLfloat)lp.x, (GLfloat)lp.y, (GLfloat)lp.z, 0 };
glLightfv(GL_LIGHT0, GL_POSITION, lp0);
lp = VectorFromProjs(0.49*w/scale, 0.10*h/scale, 0);
lp = VectorFromProjs(SS.lightPos[1]);
GLfloat lp1[4] = { (GLfloat)lp.x, (GLfloat)lp.y, (GLfloat)lp.z, 0 };
glLightfv(GL_LIGHT1, GL_POSITION, lp1);

View File

@ -40,8 +40,24 @@ double Constraint::EllipticalInterpolation(double rx, double ry, double theta) {
return v;
}
char *Constraint::Label(void) {
static char Ret[1024];
if(type == ANGLE) {
sprintf(Ret, "%.2f", exprA->Eval());
} else if(type == LENGTH_RATIO) {
sprintf(Ret, "%.3f:1", exprA->Eval());
} else {
// exprA has units of distance
strcpy(Ret, SS.GW.ToString(exprA->Eval()));
}
if(reference) {
strcat(Ret, " REF");
}
return Ret;
}
void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) {
char *s = exprA->Print();
char *s = Label();
if(labelPos) {
// labelPos is from the top left corner (for the text box used to
// edit things), but ref is from the center.
@ -188,7 +204,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
double theta = atan2(disp.offset.Dot(gu), disp.offset.Dot(gr));
double adj = EllipticalInterpolation(
glxStrWidth(exprA->Print())/2, glxStrHeight()/2, theta);
glxStrWidth(Label())/2, glxStrHeight()/2, theta);
Vector mark = ref.Minus(center);
mark = mark.WithMagnitude(mark.Magnitude()-r);
@ -333,7 +349,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
double tl = atan2(rm.Dot(gu), rm.Dot(gr));
double adj = EllipticalInterpolation(
glxStrWidth(exprA->Print())/2, glxStrHeight()/2, tl);
glxStrWidth(Label())/2, glxStrHeight()/2, tl);
ref = ref.Plus(rm.WithMagnitude(adj + 3/SS.GW.scale));
} else {
// The lines are skew; no wonderful way to illustrate that.

View File

@ -4,7 +4,9 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) {
if(dogd.drawing) {
// glPolygonOffset works only on polys, not lines, so do it myself
Vector adj = SS.GW.projRight.Cross(SS.GW.projUp);
adj = adj.ScaledBy(5/SS.GW.scale);
// Draw lines from active group in front of those from previous
int delta = (group.v == SS.GW.activeGroup.v) ? 10 : 5;
adj = adj.ScaledBy(delta/SS.GW.scale);
glBegin(GL_LINES);
glxVertex3v(a.Plus(adj));
glxVertex3v(b.Plus(adj));
@ -14,6 +16,8 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) {
Point2d bp = SS.GW.ProjectPoint(b);
double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true);
// A little bit easier to select in the active group
if(group.v == SS.GW.activeGroup.v) d -= 1;
dogd.dmin = min(dogd.dmin, d);
}
dogd.refp = (a.Plus(b)).ScaledBy(0.5);
@ -164,9 +168,7 @@ void Entity::DrawOrGetDistance(void) {
glPolygonOffset(0, 0);
} else {
Point2d pp = SS.GW.ProjectPoint(v);
// Make a free point slightly easier to select, so that with
// coincident points, we select the free one.
dogd.dmin = pp.DistanceTo(dogd.mp) - 4;
dogd.dmin = pp.DistanceTo(dogd.mp) - 6;
}
break;
}
@ -292,7 +294,7 @@ void Entity::DrawOrGetDistance(void) {
Vector p1 = SS.GetEntity(point[1])->PointGetNum();
Vector p2 = SS.GetEntity(point[2])->PointGetNum();
Vector p3 = SS.GetEntity(point[3])->PointGetNum();
int i, n = 20;
int i, n = (int)(15/sqrt(SS.meshTol));
Vector prev = p0;
for(i = 1; i <= n; i++) {
double t = ((double)i)/n;
@ -307,7 +309,6 @@ void Entity::DrawOrGetDistance(void) {
break;
}
#define CIRCLE_SIDES(r) (7 + (int)(sqrt(r*SS.GW.scale)))
case ARC_OF_CIRCLE: {
Vector c = SS.GetEntity(point[0])->PointGetNum();
Vector pa = SS.GetEntity(point[1])->PointGetNum();
@ -321,7 +322,7 @@ void Entity::DrawOrGetDistance(void) {
double thetaa, thetab, dtheta;
ArcGetAngles(&thetaa, &thetab, &dtheta);
int i, n = 3 + (int)(CIRCLE_SIDES(ra)*dtheta/(2*PI));
int i, n = 3 + (int)(SS.CircleSides(ra)*dtheta/(2*PI));
Vector prev = pa;
for(i = 1; i <= n; i++) {
double theta = thetaa + (dtheta*i)/n;
@ -340,7 +341,7 @@ void Entity::DrawOrGetDistance(void) {
Vector center = SS.GetEntity(point[0])->PointGetNum();
Vector u = q.RotationU(), v = q.RotationV();
int i, c = CIRCLE_SIDES(r);
int i, c = SS.CircleSides(r);
Vector prev = u.ScaledBy(r).Plus(center);
for(i = 1; i <= c; i++) {
double phi = (2*PI*i)/c;

View File

@ -140,6 +140,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'c', "Constraint.entityA.v", 'x', &(SS.sv.c.entityA.v) },
{ 'c', "Constraint.entityB.v", 'x', &(SS.sv.c.entityB.v) },
{ 'c', "Constraint.otherAngle", 'b', &(SS.sv.c.otherAngle) },
{ 'c', "Constraint.reference", 'b', &(SS.sv.c.reference) },
{ 'c', "Constraint.disp.offset.x", 'f', &(SS.sv.c.disp.offset.x) },
{ 'c', "Constraint.disp.offset.y", 'f', &(SS.sv.c.disp.offset.y) },
{ 'c', "Constraint.disp.offset.z", 'f', &(SS.sv.c.disp.offset.z) },

View File

@ -175,9 +175,7 @@ void glxFillMesh(int specColor, SMesh *m, DWORD h, DWORD s1, DWORD s2)
color = specColor;
}
if(color != prevColor) {
GLfloat mpf[] = { ((color >> 0) & 0xff) / 255.0f,
((color >> 8) & 0xff) / 255.0f,
((color >> 16) & 0xff) / 255.0f, 1.0 };
GLfloat mpf[] = { REDf(color), GREENf(color), BLUEf(color), 1.0 };
glEnd();
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf);
prevColor = color;

View File

@ -65,7 +65,6 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "&Cubic Segment\t3", MNU_CUBIC, '3', mReq },
{ 1, NULL, 0, NULL },
{ 1, "Sym&bolic Variable\tB", 0, 'B', mReq },
{ 1, "&Import From File...\tI", 0, 'I', mReq },
{ 1, NULL, 0, NULL },
{ 1, "To&ggle Construction\tG", MNU_CONSTRUCTION, 'G', mReq },
@ -73,6 +72,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "&Distance / Diameter\tShift+D", MNU_DISTANCE_DIA, 'D'|S, mCon },
{ 1, "A&ngle\tShift+N", MNU_ANGLE, 'N'|S, mCon },
{ 1, "Other S&upplementary Angle\tShift+U", MNU_OTHER_ANGLE, 'U'|S, mCon },
{ 1, "Toggle &Reference Dim\tShift+R", MNU_REFERENCE, 'R'|S, mCon },
{ 1, NULL, 0, NULL },
{ 1, "&Horizontal\tShift+H", MNU_HORIZONTAL, 'H'|S, mCon },
{ 1, "&Vertical\tShift+V", MNU_VERTICAL, 'V'|S, mCon },
@ -83,7 +83,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "At &Midpoint\tShift+M", MNU_AT_MIDPOINT, 'M'|S, mCon },
{ 1, "S&ymmetric\tShift+Y", MNU_SYMMETRIC, 'Y'|S, mCon },
{ 1, "Para&llel\tShift+L", MNU_PARALLEL, 'L'|S, mCon },
{ 1, "Same O&rientation\tShift+R", MNU_ORIENTED_SAME, 'R'|S, mCon },
{ 1, "Same Orient&ation\tShift+A", MNU_ORIENTED_SAME, 'A'|S, mCon },
{ 1, NULL, 0, NULL },
{ 1, "Sym&bolic Equation\tShift+B", 0, 'B'|S, NULL },
{ 1, NULL, 0, NULL },
@ -187,6 +187,59 @@ void GraphicsWindow::AnimateOntoWorkplane(void) {
InvalidateGraphics();
}
void GraphicsWindow::HandlePointForZoomToFit(Vector p,
Point2d *pmax, Point2d *pmin)
{
Point2d p2 = ProjectPoint(p);
pmax->x = max(pmax->x, p2.x);
pmax->y = max(pmax->y, p2.y);
pmin->x = min(pmin->x, p2.x);
pmin->y = min(pmin->y, p2.y);
}
void GraphicsWindow::ZoomToFit(void) {
int i, j;
Point2d pmax = { -1e12, -1e12 }, pmin = { 1e12, 1e12 };
HandlePointForZoomToFit(Vector::From(0, 0, 0), &pmax, &pmin);
for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]);
if(!e->IsPoint()) continue;
if(!e->IsVisible()) continue;
HandlePointForZoomToFit(e->PointGetNum(), &pmax, &pmin);
}
Group *g = SS.GetGroup(activeGroup);
for(i = 0; i < g->mesh.l.n; i++) {
STriangle *tr = &(g->mesh.l.elem[i]);
HandlePointForZoomToFit(tr->a, &pmax, &pmin);
HandlePointForZoomToFit(tr->b, &pmax, &pmin);
HandlePointForZoomToFit(tr->c, &pmax, &pmin);
}
for(i = 0; i < g->poly.l.n; i++) {
SContour *sc = &(g->poly.l.elem[i]);
for(j = 0; j < sc->l.n; j++) {
HandlePointForZoomToFit(sc->l.elem[j].p, &pmax, &pmin);
}
}
pmax = pmax.ScaledBy(1/scale);
pmin = pmin.ScaledBy(1/scale);
double xm = (pmax.x + pmin.x)/2, ym = (pmax.y + pmin.y)/2;
double dx = pmax.x - pmin.x, dy = pmax.y - pmin.y;
offset = offset.Plus(projRight.ScaledBy(-xm)).Plus(
projUp. ScaledBy(-ym));
if(dx == 0 && dy == 0) {
scale = 5;
} else {
double scalex = 1e12, scaley = 1e12;
if(dx != 0) scalex = 0.9*width /dx;
if(dy != 0) scaley = 0.9*height/dy;
scale = min(100, min(scalex, scaley));
}
}
void GraphicsWindow::MenuView(int id) {
switch(id) {
case MNU_ZOOM_IN:
@ -198,6 +251,7 @@ void GraphicsWindow::MenuView(int id) {
break;
case MNU_ZOOM_TO_FIT:
SS.GW.ZoomToFit();
break;
case MNU_SHOW_TEXT_WND:

View File

@ -123,7 +123,17 @@ void Group::GenerateMesh(void) {
Vector axis = SS.GetEntity(predef.entityB)->VectorGetNum();
axis = axis.WithMagnitude(1);
int n = 20;
// Calculate the max radius, to determine fineness of mesh
double r, rmax = 0;
for(i = 0; i < edges.l.n; i++) {
SEdge *edge = &(edges.l.elem[i]);
r = (edge->a).DistanceToLine(orig, axis);
rmax = max(r, rmax);
r = (edge->b).DistanceToLine(orig, axis);
rmax = max(r, rmax);
}
int n = SS.CircleSides(rmax);
for(a = 0; a <= n; a++) {
double thetai = (2*PI*WRAP(a-1, n))/n, thetaf = (2*PI*a)/n;
for(i = 0; i < edges.l.n; i++) {
@ -139,18 +149,23 @@ void Group::GenerateMesh(void) {
Vector ab = (edge->b).Minus(edge->a);
Vector out = ((src->poly).normal).Cross(ab);
// This is a vector, not a point, so no origin for rotation
out = out.RotatedAbout(axis, thetai);
// The line sweeps out a quad, so two triangles
STriangle quad1 = STriangle::From(meta, ai, bi, af),
quad2 = STriangle::From(meta, af, bi, bf);
// Could be only one of the triangles has area; be sure
// to use that one for normal checking, then.
Vector n1 = quad1.Normal(), n2 = quad2.Normal();
Vector n = (n1.Magnitude() > n2.Magnitude()) ? n1 : n2;
if(n.Dot(out) < 0) {
quad1.FlipNormal();
quad2.FlipNormal();
}
// If one of the endpoints lies on the axis of rotation,
// then the quad is just a single triangle
// One or both of the endpoints might lie on the axis of
// rotation, in which case its triangle is zero-area.
if(da >= LENGTH_EPS) outm.AddTriangle(&quad1);
if(db >= LENGTH_EPS) outm.AddTriangle(&quad2);
}

View File

@ -220,7 +220,6 @@ void SMesh::MakeFromUnion(SMesh *a, SMesh *b) {
}
void SMesh::MakeFromDifference(SMesh *a, SMesh *b) {
SDWORD in = GetMilliseconds();
SBsp3 *bspa = SBsp3::FromMesh(a);
SBsp3 *bspb = SBsp3::FromMesh(b);
@ -231,8 +230,6 @@ void SMesh::MakeFromDifference(SMesh *a, SMesh *b) {
flipNormal = false;
keepCoplanar = false;
AddAgainstBsp(a, bspb);
dbp("dt = %d", GetMilliseconds() - in);
dbp("tris = %d", l.n);
}
bool SMesh::MakeFromInterferenceCheck(SMesh *srca, SMesh *srcb, SMesh *error) {

View File

@ -461,6 +461,7 @@ public:
void LineDrawOrGetDistance(Vector a, Vector b);
void DrawOrGetDistance(Vector *labelPos);
double EllipticalInterpolation(double rx, double ry, double theta);
char *Label(void);
void DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu);
void DoProjectedPoint(Vector *p);
@ -472,6 +473,7 @@ public:
bool HasLabel(void);
void Generate(IdList<Equation,hEquation> *l);
void GenerateReal(IdList<Equation,hEquation> *l);
// Some helpers when generating symbolic constraint equations
void ModifyToSatisfy(void);
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);

View File

@ -3,9 +3,41 @@
SolveSpace SS;
void SolveSpace::Init(char *cmdLine) {
int i;
// Default list of colors for the model material
modelColor[0] = CnfThawDWORD(RGB(150, 150, 150), "ModelColor_0");
modelColor[1] = CnfThawDWORD(RGB(100, 100, 100), "ModelColor_1");
modelColor[2] = CnfThawDWORD(RGB( 30, 30, 30), "ModelColor_2");
modelColor[3] = CnfThawDWORD(RGB(150, 0, 0), "ModelColor_3");
modelColor[4] = CnfThawDWORD(RGB( 0, 100, 0), "ModelColor_4");
modelColor[5] = CnfThawDWORD(RGB( 0, 80, 80), "ModelColor_5");
modelColor[6] = CnfThawDWORD(RGB( 0, 0, 130), "ModelColor_6");
modelColor[7] = CnfThawDWORD(RGB( 80, 0, 80), "ModelColor_7");
// Light intensities
lightIntensity[0] = ((int)CnfThawDWORD( 700, "LightIntensity_0"))/1000.0;
lightIntensity[1] = ((int)CnfThawDWORD( 400, "LightIntensity_1"))/1000.0;
// Light positions
lightPos[0].x = ((int)CnfThawDWORD(-500, "LightPos_0_Right" ))/1000.0;
lightPos[0].y = ((int)CnfThawDWORD( 500, "LightPos_0_Up" ))/1000.0;
lightPos[0].z = ((int)CnfThawDWORD( 0, "LightPos_0_Forward" ))/1000.0;
lightPos[1].x = ((int)CnfThawDWORD( 500, "LightPos_1_Right" ))/1000.0;
lightPos[1].y = ((int)CnfThawDWORD( 0, "LightPos_1_Up" ))/1000.0;
lightPos[1].z = ((int)CnfThawDWORD( 0, "LightPos_1_Forward" ))/1000.0;
// Mesh tolerance
meshTol = ((int)CnfThawDWORD(1000, "MeshTolerance"))/1000.0;
// Recent files menus
for(i = 0; i < MAX_RECENT; i++) {
char name[100];
sprintf(name, "RecentFile_%d", i);
strcpy(RecentFile[i], "");
CnfThawString(RecentFile[i], MAX_PATH, name);
}
RefreshRecentMenus();
// Start with either an empty file, or the file specified on the
// command line.
NewFile();
AfterNewFile();
if(strlen(cmdLine) != 0) {
if(LoadFromFile(cmdLine)) {
strcpy(saveFile, cmdLine);
@ -13,16 +45,49 @@ void SolveSpace::Init(char *cmdLine) {
NewFile();
}
}
AfterNewFile();
}
void SolveSpace::Exit(void) {
int i;
char name[100];
// Recent files
for(i = 0; i < MAX_RECENT; i++) {
sprintf(name, "RecentFile_%d", i);
CnfFreezeString(RecentFile[i], name);
}
// Model colors
for(i = 0; i < MODEL_COLORS; i++) {
sprintf(name, "ModelColor_%d", i);
CnfFreezeDWORD(modelColor[i], name);
}
// Light intensities
CnfFreezeDWORD((int)(lightIntensity[0]*1000), "LightIntensity_0");
CnfFreezeDWORD((int)(lightIntensity[1]*1000), "LightIntensity_1");
// Light positions
CnfFreezeDWORD((int)(lightPos[0].x*1000), "LightPos_0_Right");
CnfFreezeDWORD((int)(lightPos[0].y*1000), "LightPos_0_Up");
CnfFreezeDWORD((int)(lightPos[0].z*1000), "LightPos_0_Forward");
CnfFreezeDWORD((int)(lightPos[1].x*1000), "LightPos_1_Right");
CnfFreezeDWORD((int)(lightPos[1].y*1000), "LightPos_1_Up");
CnfFreezeDWORD((int)(lightPos[1].z*1000), "LightPos_1_Forward");
// Mesh tolerance
CnfFreezeDWORD((int)(meshTol*1000), "MeshTolerance");
ExitNow();
}
void SolveSpace::DoLater(void) {
if(later.generateAll) GenerateAll();
if(later.showTW) TW.Show();
ZERO(&later);
}
int SolveSpace::CircleSides(double r) {
int s = 7 + (int)(sqrt(r*SS.GW.scale/meshTol));
return min(s, 40);
}
void SolveSpace::AfterNewFile(void) {
ReloadAllImported();
GenerateAll(-1, -1);
@ -249,6 +314,14 @@ void SolveSpace::GenerateAll(int first, int last) {
}
}
// And update any reference dimensions with their new values
for(i = 0; i < constraint.n; i++) {
Constraint *c = &(constraint.elem[i]);
if(c->reference) {
c->ModifyToSatisfy();
}
}
prev.Clear();
InvalidateGraphics();
@ -459,7 +532,7 @@ void SolveSpace::MenuFile(int id) {
case GraphicsWindow::MNU_EXIT:
if(!SS.OkayToStartNewFile()) break;
ExitNow();
SS.Exit();
break;
default: oops();

View File

@ -80,6 +80,10 @@ void dbp(char *str, ...);
void Error(char *str, ...);
void ExitNow(void);
void CnfFreezeString(char *str, char *name);
void CnfFreezeDWORD(DWORD v, char *name);
void CnfThawString(char *str, int maxLen, char *name);
DWORD CnfThawDWORD(DWORD v, char *name);
void *AllocTemporary(int n);
void FreeAllTemporary(void);
@ -242,10 +246,21 @@ public:
void UndoClearState(UndoState *ut);
void UndoClearStack(UndoStack *uk);
// Little bits of extra configuration state
static const int MODEL_COLORS = 8;
int modelColor[MODEL_COLORS];
Vector lightPos[2];
double lightIntensity[2];
double meshTol;
void Init(char *cmdLine);
void Exit(void);
int CircleSides(double r);
// File load/save routines, including the additional files that get
// loaded when we have import groups.
FILE *fh;
void Init(char *cmdLine);
void AfterNewFile(void);
static void RemoveFromRecentList(char *file);
static void AddToRecentList(char *file);

View File

@ -31,16 +31,6 @@ void TextWindow::ClearSuper(void) {
shown = &(showns[shownIndex]);
ClearScreen();
Show();
// Default list of colors for the model material
modelColor[0] = RGB(150, 150, 150);
modelColor[1] = RGB(100, 100, 100);
modelColor[2] = RGB( 30, 30, 30);
modelColor[3] = RGB(150, 0, 0);
modelColor[4] = RGB( 0, 100, 0);
modelColor[5] = RGB( 0, 80, 80);
modelColor[6] = RGB( 0, 0, 130);
modelColor[7] = RGB( 80, 0, 80);
}
void TextWindow::ClearScreen(void) {
@ -97,6 +87,16 @@ void TextWindow::Printf(bool halfLine, char *fmt, ...) {
sprintf(buf, "%08x", v);
break;
}
case '@': {
double v = va_arg(vl, double);
sprintf(buf, "%.2f", v);
break;
}
case '2': {
double v = va_arg(vl, double);
sprintf(buf, "%s%.2f", v < 0 ? "" : " ", v);
break;
}
case '3': {
double v = va_arg(vl, double);
sprintf(buf, "%s%.3f", v < 0 ? "" : " ", v);
@ -201,10 +201,12 @@ void TextWindow::Show(void) {
if(SS.GW.pending.description) {
// A pending operation (that must be completed with the mouse in
// the graphics window) will preempt our usual display.
HideTextEditControl();
ShowHeader(false);
Printf(false, "");
Printf(false, "%s", SS.GW.pending.description);
} else if(gs.n > 0) {
HideTextEditControl();
ShowHeader(false);
DescribeSelection();
} else {
@ -216,6 +218,7 @@ void TextWindow::Show(void) {
case SCREEN_LIST_OF_GROUPS: ShowListOfGroups(); break;
case SCREEN_GROUP_INFO: ShowGroupInfo(); break;
case SCREEN_GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break;
case SCREEN_CONFIGURATION: ShowConfiguration(); break;
}
}
InvalidateText();
@ -437,15 +440,12 @@ void TextWindow::ScreenToggleGroupShown(int link, DWORD v) {
}
void TextWindow::ScreenShowGroupsSpecial(int link, DWORD v) {
int i;
bool before = true;
for(i = 0; i < SS.group.n; i++) {
Group *g = &(SS.group.elem[i]);
if(g->h.v == SS.GW.activeGroup.v) {
before = false;
} else if(before && link == 's') {
if(link == 's') {
g->visible = true;
} else if(!before && link == 'h') {
} else {
g->visible = false;
}
}
@ -471,6 +471,9 @@ void TextWindow::ScreenHowGroupSolved(int link, DWORD v) {
SS.TW.OneScreenForwardTo(SCREEN_GROUP_SOLVE_INFO);
SS.TW.shown->group.v = v;
}
void TextWindow::ScreenShowConfiguration(int link, DWORD v) {
SS.TW.OneScreenForwardTo(SCREEN_CONFIGURATION);
}
void TextWindow::ShowListOfGroups(void) {
Printf(true, "%Ftactv show ok group-name%E");
int i;
@ -508,10 +511,11 @@ void TextWindow::ShowListOfGroups(void) {
if(active) afterActive = true;
}
Printf(true, " %Fl%Ls%fshow all groups before active%E",
&(TextWindow::ScreenShowGroupsSpecial));
Printf(false, " %Fl%Lh%fhide all groups after active%E",
Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E",
&(TextWindow::ScreenShowGroupsSpecial),
&(TextWindow::ScreenShowGroupsSpecial));
Printf(false, " %Fl%Ls%fconfiguration%E",
&(TextWindow::ScreenShowConfiguration));
}
@ -569,8 +573,8 @@ void TextWindow::ScreenColor(int link, DWORD v) {
SS.UndoRemember();
Group *g = SS.GetGroup(SS.TW.shown->group);
if(v < 0 || v >= MODEL_COLORS) return;
g->color = SS.TW.modelColor[v];
if(v < 0 || v >= SS.MODEL_COLORS) return;
g->color = SS.modelColor[v];
SS.MarkGroupDirty(g->h);
SS.GenerateAll();
SS.GW.ClearSuper();
@ -610,7 +614,7 @@ void TextWindow::ShowGroupInfo(void) {
Printf(true, "%FtGROUP %E%s", g->DescriptionString());
} else {
Printf(true, "%FtGROUP %E%s "
"(%Fl%Ll%D%frename%E / %Fl%Ll%D%fdel%E)",
"[%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]",
g->DescriptionString(),
g->h.v, &TextWindow::ScreenChangeGroupName,
g->h.v, &TextWindow::ScreenDeleteGroup);
@ -646,7 +650,7 @@ void TextWindow::ShowGroupInfo(void) {
if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) {
int times = (int)(g->exprA->Eval());
Printf(true, "%Ft%s%E %d time%s %Fl%Ll%D%f(change)%E",
Printf(true, "%Ft%s%E %d time%s %Fl%Ll%D%f[change]%E",
s2, times, times == 1 ? "" : "s",
g->h.v, &TextWindow::ScreenChangeExprA);
}
@ -681,14 +685,14 @@ void TextWindow::ShowGroupInfo(void) {
if(g->type == Group::EXTRUDE || g->type == Group::LATHE) {
#define TWOX(v) v v
Printf(true, "%FtM_COLOR%E " TWOX(TWOX(TWOX("%Bp%D%f%Ln %Bd%E "))),
0x80000000 | modelColor[0], 0, &TextWindow::ScreenColor,
0x80000000 | modelColor[1], 1, &TextWindow::ScreenColor,
0x80000000 | modelColor[2], 2, &TextWindow::ScreenColor,
0x80000000 | modelColor[3], 3, &TextWindow::ScreenColor,
0x80000000 | modelColor[4], 4, &TextWindow::ScreenColor,
0x80000000 | modelColor[5], 5, &TextWindow::ScreenColor,
0x80000000 | modelColor[6], 6, &TextWindow::ScreenColor,
0x80000000 | modelColor[7], 7, &TextWindow::ScreenColor);
0x80000000 | SS.modelColor[0], 0, &TextWindow::ScreenColor,
0x80000000 | SS.modelColor[1], 1, &TextWindow::ScreenColor,
0x80000000 | SS.modelColor[2], 2, &TextWindow::ScreenColor,
0x80000000 | SS.modelColor[3], 3, &TextWindow::ScreenColor,
0x80000000 | SS.modelColor[4], 4, &TextWindow::ScreenColor,
0x80000000 | SS.modelColor[5], 5, &TextWindow::ScreenColor,
0x80000000 | SS.modelColor[6], 6, &TextWindow::ScreenColor,
0x80000000 | SS.modelColor[7], 7, &TextWindow::ScreenColor);
}
Printf(true, "%Ftrequests in group");
@ -727,13 +731,15 @@ void TextWindow::ShowGroupInfo(void) {
void TextWindow::ShowGroupSolveInfo(void) {
Group *g = SS.group.FindById(shown->group);
if(g->solved.how == Group::SOLVED_OKAY) {
// Go back to the default group info screen
shown->screen = SCREEN_GROUP_INFO;
Show();
return;
}
Printf(true, "%FtGROUP %E%s", g->DescriptionString());
switch(g->solved.how) {
case Group::SOLVED_OKAY:
Printf(true, " %Fsgroup solved okay%E");
break;
case Group::DIDNT_CONVERGE:
Printf(true, " %FxSOLVE FAILED!%Fd no convergence");
break;
@ -757,6 +763,69 @@ void TextWindow::ShowGroupSolveInfo(void) {
}
}
void TextWindow::ScreenChangeLightPosition(int link, DWORD v) {
char str[1024];
sprintf(str, "%.2f, %.2f, %.2f", CO(SS.lightPos[v]));
ShowTextEditControl(29+2*v, 8, str);
SS.TW.edit.meaning = EDIT_LIGHT_POSITION;
SS.TW.edit.i = v;
}
void TextWindow::ScreenChangeLightIntensity(int link, DWORD v) {
char str[1024];
sprintf(str, "%.2f", SS.lightIntensity[v]);
ShowTextEditControl(29+2*v, 30, str);
SS.TW.edit.meaning = EDIT_LIGHT_INTENSITY;
SS.TW.edit.i = v;
}
void TextWindow::ScreenChangeColor(int link, DWORD v) {
char str[1024];
sprintf(str, "%.2f, %.2f, %.2f",
REDf(SS.modelColor[v]),
GREENf(SS.modelColor[v]),
BLUEf(SS.modelColor[v]));
ShowTextEditControl(9+2*v, 12, str);
SS.TW.edit.meaning = EDIT_COLOR;
SS.TW.edit.i = v;
}
void TextWindow::ScreenChangeMeshTolerance(int link, DWORD v) {
char str[1024];
sprintf(str, "%.2f", SS.meshTol);
ShowTextEditControl(37, 3, str);
SS.TW.edit.meaning = EDIT_MESH_TOLERANCE;
}
void TextWindow::ShowConfiguration(void) {
int i;
Printf(true, "%Ft material color-(r, g, b)");
for(i = 0; i < SS.MODEL_COLORS; i++) {
Printf(false, "%Bp #%d: %Bp %Bp (%@, %@, %@) %f%D%Ll%Fl[change]%E",
(i & 1) ? 'd' : 'a',
i, 0x80000000 | SS.modelColor[i],
(i & 1) ? 'd' : 'a',
REDf(SS.modelColor[i]),
GREENf(SS.modelColor[i]),
BLUEf(SS.modelColor[i]),
&ScreenChangeColor, i);
}
Printf(false, "");
Printf(false, "%Ft light position intensity");
for(i = 0; i < 2; i++) {
Printf(false, "%Bp #%d (%2,%2,%2)%Fl%D%f%Ll[c]%E "
"%2 %Fl%D%f%Ll[c]%E",
(i & 1) ? 'd' : 'a', i,
CO(SS.lightPos[i]), i, &ScreenChangeLightPosition,
SS.lightIntensity[i], i, &ScreenChangeLightIntensity);
}
Printf(false, "");
Printf(false, "%Ft mesh tolerance (smaller is finer)%E");
Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E; now %d triangles",
SS.meshTol,
&ScreenChangeMeshTolerance, 0,
SS.group.elem[SS.group.n-1].mesh.l.n);
}
void TextWindow::EditControlDone(char *s) {
switch(edit.meaning) {
case EDIT_TIMES_REPEATED: {
@ -770,7 +839,6 @@ void TextWindow::EditControlDone(char *s) {
SS.MarkGroupDirty(g->h);
SS.later.generateAll = true;
SS.later.showTW = true;
} else {
Error("Not a valid number or expression: '%s'", s);
}
@ -792,11 +860,39 @@ void TextWindow::EditControlDone(char *s) {
Group *g = SS.GetGroup(edit.group);
g->name.strcpy(s);
}
SS.later.showTW = true;
SS.unsaved = true;
break;
}
case EDIT_LIGHT_INTENSITY:
SS.lightIntensity[edit.i] = min(1, max(0, atof(s)));
InvalidateGraphics();
break;
case EDIT_LIGHT_POSITION: {
double x, y, z;
if(sscanf(s, "%lf, %lf, %lf", &x, &y, &z)==3) {
SS.lightPos[edit.i] = Vector::From(x, y, z);
} else {
Error("Bad format: specify coordinates as x, y, z");
}
InvalidateGraphics();
break;
}
case EDIT_COLOR: {
double r, g, b;
if(sscanf(s, "%lf, %lf, %lf", &r, &g, &b)==3) {
SS.modelColor[edit.i] = RGB(r*255, g*255, b*255);
} else {
Error("Bad format: specify color as r, g, b");
}
break;
}
case EDIT_MESH_TOLERANCE: {
SS.meshTol = min(10, max(0.1, atof(s)));
SS.GenerateAll(0, INT_MAX);
break;
}
}
SS.later.showTW = true;
HideTextEditControl();
edit.meaning = EDIT_NOTHING;
}

25
ui.h
View File

@ -10,6 +10,9 @@ public:
#ifndef RGB
#define RGB(r, g, b) ((r) | ((g) << 8) | ((b) << 16))
#endif
#define REDf(v) ((((v) >> 0) & 0xff) / 255.0f)
#define GREENf(v) ((((v) >> 8) & 0xff) / 255.0f)
#define BLUEf(v) ((((v) >> 16) & 0xff) / 255.0f)
typedef struct {
char c;
int color;
@ -17,9 +20,6 @@ public:
static const Color fgColors[];
static const Color bgColors[];
static const int MODEL_COLORS = 8;
int modelColor[MODEL_COLORS];
BYTE text[MAX_ROWS][MAX_COLS];
typedef void LinkFunction(int link, DWORD v);
static const int NOT_A_LINK = 0;
@ -45,6 +45,7 @@ public:
static const int SCREEN_LIST_OF_GROUPS = 0;
static const int SCREEN_GROUP_INFO = 1;
static const int SCREEN_GROUP_SOLVE_INFO = 2;
static const int SCREEN_CONFIGURATION = 3;
typedef struct {
int screen;
hGroup group;
@ -58,8 +59,13 @@ public:
static const int EDIT_NOTHING = 0;
static const int EDIT_TIMES_REPEATED = 1;
static const int EDIT_GROUP_NAME = 2;
static const int EDIT_LIGHT_POSITION = 3;
static const int EDIT_LIGHT_INTENSITY = 4;
static const int EDIT_COLOR = 5;
static const int EDIT_MESH_TOLERANCE = 6;
struct {
int meaning;
int i;
hGroup group;
} edit;
@ -73,6 +79,7 @@ public:
void ShowListOfGroups(void);
void ShowGroupInfo(void);
void ShowGroupSolveInfo(void);
void ShowConfiguration(void);
// Special screen, based on selection
void DescribeSelection(void);
@ -90,18 +97,23 @@ public:
static void ScreenHoverRequest(int link, DWORD v);
static void ScreenSelectRequest(int link, DWORD v);
static void ScreenSelectConstraint(int link, DWORD v);
static void ScreenUnselectAll(int link, DWORD v);
static void ScreenChangeOneOrTwoSides(int link, DWORD v);
static void ScreenChangeMeshCombine(int link, DWORD v);
static void ScreenColor(int link, DWORD v);
static void ScreenUnselectAll(int link, DWORD v);
static void ScreenShowConfiguration(int link, DWORD v);
static void ScreenNavigation(int link, DWORD v);
// These ones do stuff with the edit control
static void ScreenChangeExprA(int link, DWORD v);
static void ScreenChangeGroupName(int link, DWORD v);
static void ScreenChangeLightPosition(int link, DWORD v);
static void ScreenChangeLightIntensity(int link, DWORD v);
static void ScreenChangeColor(int link, DWORD v);
static void ScreenChangeMeshTolerance(int link, DWORD v);
void EditControlDone(char *s);
};
@ -155,6 +167,7 @@ public:
MNU_DISTANCE_DIA,
MNU_ANGLE,
MNU_OTHER_ANGLE,
MNU_REFERENCE,
MNU_EQUAL,
MNU_RATIO,
MNU_ON_ENTITY,
@ -203,7 +216,9 @@ public:
void NormalizeProjectionVectors(void);
Point2d ProjectPoint(Vector p);
void AnimateOntoWorkplane(void);
Vector VectorFromProjs(double right, double up, double forward);
Vector VectorFromProjs(Vector rightUpForward);
void HandlePointForZoomToFit(Vector p, Point2d *pmax, Point2d *pmin);
void ZoomToFit(void);
typedef enum {
UNIT_MM = 0,

View File

@ -71,6 +71,23 @@ void ExitNow(void) {
PostQuitMessage(0);
}
//-----------------------------------------------------------------------------
// Helpers so that we can read/write registry keys from the platform-
// independent code.
//-----------------------------------------------------------------------------
void CnfFreezeString(char *str, char *name)
{ FreezeStringF(str, FREEZE_SUBKEY, name); }
void CnfFreezeDWORD(DWORD v, char *name)
{ FreezeDWORDF(v, FREEZE_SUBKEY, name); }
void CnfThawString(char *str, int maxLen, char *name)
{ ThawStringF(str, maxLen, FREEZE_SUBKEY, name); }
DWORD CnfThawDWORD(DWORD v, char *name)
{ return ThawDWORDF(v, FREEZE_SUBKEY, name); }
//-----------------------------------------------------------------------------
// A separate heap, on which we allocate expressions. Maybe a bit faster,
// since no fragmentation issues whatsoever, and it also makes it possible
@ -877,13 +894,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
InitCommonControls();
int i;
for(i = 0; i < MAX_RECENT; i++) {
char name[100];
sprintf(name, "RecentFile_%d", i);
ThawStringF(RecentFile[i], MAX_PATH, FREEZE_SUBKEY, name);
}
// A monospaced font
FixedFont = CreateFont(TEXT_HEIGHT-4, TEXT_WIDTH, 0, 0, FW_REGULAR, FALSE,
FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
@ -936,11 +946,5 @@ done:
// Write everything back to the registry
FreezeWindowPos(TextWnd);
FreezeWindowPos(GraphicsWnd);
for(i = 0; i < MAX_RECENT; i++) {
char name[100];
sprintf(name, "RecentFile_%d", i);
FreezeStringF(RecentFile[i], FREEZE_SUBKEY, name);
}
return 0;
}

View File

@ -1,17 +1,15 @@
STL check for meshes, and T intersection removal
STL export
better triangle combining (Simplify()) for meshes
DXF export
compress file format (binary?)
partitioned subsystems in the solver
arbitrary color specification
specify tolerance for meshing
specify light positions (all on one configuration screen)
TTF font text
display with proper formatting/units
more measurements
reference dimensions (just to look at, no equations)
some kind of rounding
some kind of rounding / chamfer
auto-constrain translate in then-active workplane
remove back button in browser?