Add double-sided extrudes, and user interface for that. Also put

the coordinate system (x, y, z normal vectors) in the bottom left
corner of the screen at all times, and hide group-created
workplanes except when that group is active, and activate that
workplane when the group is activated.

[git-p4: depot-paths = "//depot/solvespace/": change = 1726]
solver
Jonathan Westhues 2008-05-17 00:02:39 -08:00
parent 3cdbbb83b1
commit 749e2a0149
9 changed files with 182 additions and 83 deletions

View File

@ -9,7 +9,7 @@ char *Constraint::DescriptionString(void) {
hConstraint Constraint::AddConstraint(Constraint *c) {
SS.constraint.AddAndAssignId(c);
SS.GenerateAll(SS.GW.solving == GraphicsWindow::SOLVE_ALWAYS);
SS.GW.GeneratePerSolving();
return c->h;
}

View File

@ -332,7 +332,7 @@ void Entity::PointForceTo(Vector p) {
}
case POINT_N_TRANS: {
Vector trans = p.Minus(numPoint);
Vector trans = (p.Minus(numPoint)).ScaledBy(1.0/timesApplied);
SS.GetParam(param[0])->val = trans.x;
SS.GetParam(param[1])->val = trans.y;
SS.GetParam(param[2])->val = trans.z;
@ -379,9 +379,9 @@ Vector Entity::PointGetNum(void) {
case POINT_N_TRANS: {
p = numPoint;
p.x += SS.GetParam(param[0])->val;
p.y += SS.GetParam(param[1])->val;
p.z += SS.GetParam(param[2])->val;
p.x += timesApplied * SS.GetParam(param[0])->val;
p.y += timesApplied * SS.GetParam(param[1])->val;
p.z += timesApplied * SS.GetParam(param[2])->val;
break;
}
@ -432,7 +432,7 @@ ExprVector Entity::PointGetExprs(void) {
trans.x = Expr::FromParam(param[0]);
trans.y = Expr::FromParam(param[1]);
trans.z = Expr::FromParam(param[2]);
r = orig.Plus(trans);
r = orig.Plus(trans.ScaledBy(Expr::FromConstant(timesApplied)));
break;
}
case POINT_N_ROT_TRANS: {
@ -619,27 +619,47 @@ void Entity::DrawOrGetDistance(int order) {
if(order >= 0 && order != 2) break;
if(!SS.GW.showNormals) break;
hRequest hr = h.request();
double f = 0.5;
if(hr.v == Request::HREQUEST_REFERENCE_XY.v) {
glxColor3d(0, 0, f);
} else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) {
glxColor3d(f, 0, 0);
} else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) {
glxColor3d(0, f, 0);
} else {
glxColor3d(0, 0.4, 0.4);
}
Quaternion q = NormalGetNum();
Vector tail = SS.GetEntity(point[0])->PointGetNum();
Vector v = (q.RotationN()).WithMagnitude(50/SS.GW.scale);
Vector tip = tail.Plus(v);
LineDrawOrGetDistance(tail, tip);
int i;
for(i = 0; i < 2; i++) {
hRequest hr = h.request();
double f = (i == 0 ? 0.4 : 1);
if(hr.v == Request::HREQUEST_REFERENCE_XY.v) {
glxColor3d(0, 0, f);
} else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) {
glxColor3d(f, 0, 0);
} else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) {
glxColor3d(0, f, 0);
} else {
glxColor3d(0, 0.4, 0.4);
if(i > 0) break;
}
v = v.WithMagnitude(12/SS.GW.scale);
Vector axis = q.RotationV();
LineDrawOrGetDistance(tip, tip.Minus(v.RotatedAbout(axis, 0.6)));
LineDrawOrGetDistance(tip, tip.Minus(v.RotatedAbout(axis, -0.6)));
Quaternion q = NormalGetNum();
Vector tail;
if(i == 0) {
tail = SS.GetEntity(point[0])->PointGetNum();
} else {
// Draw an extra copy of the x, y, and z axes, that's
// always in the corner of the view and at the front.
// So those are always available, perhaps useful.
double s = SS.GW.scale;
double h = 60 - SS.GW.height/2;
double w = 60 - SS.GW.width/2;
Vector gn = SS.GW.projRight.Cross(SS.GW.projUp);
tail = SS.GW.projRight.ScaledBy(w/s).Plus(
SS.GW.projUp. ScaledBy(h/s)).Plus(
gn.ScaledBy(-4*w/s)).Minus(SS.GW.offset);
}
Vector v = (q.RotationN()).WithMagnitude(50/SS.GW.scale);
Vector tip = tail.Plus(v);
LineDrawOrGetDistance(tail, tip);
v = v.WithMagnitude(12/SS.GW.scale);
Vector axis = q.RotationV();
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis, 0.6)));
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis,-0.6)));
}
break;
}
@ -652,6 +672,12 @@ void Entity::DrawOrGetDistance(int order) {
if(order >= 0 && order != 0) break;
if(!SS.GW.showWorkplanes) break;
if((!h.isFromRequest()) && (h.group().v != SS.GW.activeGroup.v)) {
// Workplanes that are automatically created by an in-wrkpl
// drawing group appear only when that group is active.
break;
}
Vector p;
p = SS.GetEntity(point[0])->PointGetNum();

View File

@ -50,7 +50,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'g', "Group.name", 'N', &(SS.sv.g.name) },
{ 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) },
{ 'g', "Group.opB.v", 'x', &(SS.sv.g.opB.v) },
{ 'g', "Group.wrkpl.type", 'd', &(SS.sv.g.wrkpl.type) },
{ 'g', "Group.subtype", 'd', &(SS.sv.g.subtype) },
{ 'g', "Group.wrkpl.q.w", 'f', &(SS.sv.g.wrkpl.q.w) },
{ 'g', "Group.wrkpl.q.vx", 'f', &(SS.sv.g.wrkpl.q.vx) },
{ 'g', "Group.wrkpl.q.vy", 'f', &(SS.sv.g.wrkpl.q.vy) },

View File

@ -267,6 +267,10 @@ void GraphicsWindow::EnsureValidActives(void) {
if(change) SS.TW.Show();
}
void GraphicsWindow::GeneratePerSolving(void) {
SS.GenerateAll(solving == SOLVE_ALWAYS);
}
void GraphicsWindow::MenuEdit(int id) {
switch(id) {
case MNU_UNSELECT_ALL:
@ -302,7 +306,7 @@ void GraphicsWindow::MenuEdit(int id) {
SS.GW.hover.Clear();
// And regenerate to get rid of what it generates, plus anything
// that references it (since the regen code checks for that).
SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS);
SS.GW.GeneratePerSolving();
SS.GW.EnsureValidActives();
SS.TW.Show();
break;
@ -365,7 +369,7 @@ c:
r->construction = !(r->construction);
}
SS.GW.ClearSelection();
SS.GenerateAll(SS.GW.solving == GraphicsWindow::SOLVE_ALWAYS);
SS.GW.GeneratePerSolving();
break;
}
@ -607,7 +611,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
default: oops();
}
SS.GenerateAll(solving == SOLVE_ALWAYS);
SS.GW.GeneratePerSolving();
havePainted = false;
}
@ -958,7 +962,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
break;
}
}
SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS);
SS.GW.GeneratePerSolving();
SS.TW.Show();
InvalidateGraphics();
@ -998,7 +1002,7 @@ void GraphicsWindow::EditControlDone(char *s) {
Expr::FreeKeep(&(c->exprA));
c->exprA = e->DeepCopyKeep();
HideGraphicsEditControl();
SS.GenerateAll(solving == SOLVE_ALWAYS);
SS.GW.GeneratePerSolving();
} else {
Error("Not a valid number or expression: '%s'", s);
}
@ -1030,7 +1034,7 @@ void GraphicsWindow::ToggleBool(int link, DWORD v) {
bool *vb = (bool *)v;
*vb = !*vb;
SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS);
SS.GW.GeneratePerSolving();
InvalidateGraphics();
SS.TW.Show();
}
@ -1041,7 +1045,7 @@ void GraphicsWindow::ToggleAnyDatumShown(int link, DWORD v) {
SS.GW.showNormals = t;
SS.GW.showPoints = t;
SS.GenerateAll(SS.GW.solving == SOLVE_ALWAYS);
SS.GW.GeneratePerSolving();
InvalidateGraphics();
SS.TW.Show();
}
@ -1090,6 +1094,9 @@ void GraphicsWindow::Paint(int w, int h) {
glEnable(GL_DEPTH_TEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glEnable(GL_NORMALIZE);
// At the same depth, we want later lines drawn over earlier.
glDepthFunc(GL_LEQUAL);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

View File

@ -37,7 +37,7 @@ void Group::MenuGroup(int id) {
g.type = DRAWING_WORKPLANE;
g.name.strcpy("draw-in-plane");
if(gs.points == 1 && gs.n == 1) {
g.wrkpl.type = WORKPLANE_BY_POINT_ORTHO;
g.subtype = WORKPLANE_BY_POINT_ORTHO;
Vector u = SS.GW.projRight, v = SS.GW.projUp;
u = u.ClosestOrtho();
@ -47,7 +47,7 @@ void Group::MenuGroup(int id) {
g.wrkpl.q = Quaternion::MakeFrom(u, v);
g.wrkpl.origin = gs.point[0];
} else if(gs.points == 1 && gs.lineSegments == 2 && gs.n == 3) {
g.wrkpl.type = WORKPLANE_BY_LINE_SEGMENTS;
g.subtype = WORKPLANE_BY_LINE_SEGMENTS;
g.wrkpl.origin = gs.point[0];
g.wrkpl.entityB = gs.entity[0];
@ -75,6 +75,7 @@ void Group::MenuGroup(int id) {
g.type = EXTRUDE;
g.opA = SS.GW.activeGroup;
g.wrkpl.entityB = SS.GW.activeWorkplane;
g.subtype = EXTRUDE_TWO_SIDED;
g.name.strcpy("extrude");
break;
@ -122,7 +123,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
case DRAWING_WORKPLANE: {
Quaternion q;
if(wrkpl.type == WORKPLANE_BY_LINE_SEGMENTS) {
if(subtype == WORKPLANE_BY_LINE_SEGMENTS) {
Vector u = SS.GetEntity(wrkpl.entityB)->VectorGetNum();
Vector v = SS.GetEntity(wrkpl.entityC)->VectorGetNum();
u = u.WithMagnitude(1);
@ -133,7 +134,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
if(wrkpl.negateU) u = u.ScaledBy(-1);
if(wrkpl.negateV) v = v.ScaledBy(-1);
q = Quaternion::MakeFrom(u, v);
} else if(wrkpl.type == WORKPLANE_BY_POINT_ORTHO) {
} else if(subtype == WORKPLANE_BY_POINT_ORTHO) {
// Already given, numerically.
q = wrkpl.q;
} else oops();
@ -170,14 +171,28 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
AddParam(param, h.param(0), gn.x);
AddParam(param, h.param(1), gn.y);
AddParam(param, h.param(2), gn.z);
int ai, af;
if(subtype == EXTRUDE_ONE_SIDED) {
ai = 0; af = 1;
} else if(subtype == EXTRUDE_TWO_SIDED) {
ai = -1; af = 1;
} else oops();
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue;
CopyEntity(e->h, 0,
hEntity he = e->h; e = NULL;
// As soon as I call CopyEntity, e may become invalid! That
// adds entities, which may cause a realloc.
CopyEntity(he, ai,
h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
true, true);
true);
CopyEntity(he, af,
h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
true);
MakeExtrusionLines(he, ai, af);
}
break;
@ -199,7 +214,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
CopyEntity(e->h, 0,
h.param(0), h.param(1), h.param(2),
h.param(3), h.param(4), h.param(5), h.param(6),
false, false);
false);
}
break;
@ -255,9 +270,24 @@ hEntity Group::Remap(hEntity in, int copyNumber) {
return h.entity(em.h.v);
}
void Group::MakeExtrusionLines(hEntity in, int ai, int af) {
Entity *ep = SS.GetEntity(in);
if(!(ep->IsPoint())) return;
Entity en;
memset(&en, 0, sizeof(en));
en.point[0] = Remap(ep->h, ai);
en.point[1] = Remap(ep->h, af);
en.group = h;
en.h = Remap(ep->h, 10);
en.type = Entity::LINE_SEGMENT;
// And then this line segment gets added
SS.entity.Add(&en);
}
void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz,
bool transOnly, bool isExtrusion)
bool transOnly)
{
Entity *ep = SS.GetEntity(in);
@ -307,7 +337,6 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
en.param[0] = dx;
en.param[1] = dy;
en.param[2] = dz;
en.numPoint = ep->PointGetNum();
} else {
en.type = Entity::POINT_N_ROT_TRANS;
en.param[0] = dx;
@ -317,25 +346,9 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
en.param[4] = qvx;
en.param[5] = qvy;
en.param[6] = qvz;
en.numPoint = ep->PointGetNum();
}
if(isExtrusion) {
if(a != 0) oops();
SS.entity.Add(&en);
// Any operation on these lists may break existing pointers!
ep = SS.GetEntity(in);
hEntity np = en.h;
memset(&en, 0, sizeof(en));
en.point[0] = ep->h;
en.point[1] = np;
en.group = h;
en.h = Remap(ep->h, 1);
en.type = Entity::LINE_SEGMENT;
// And then this line segment gets added
}
en.numPoint = ep->PointGetNum();
en.timesApplied = a;
break;
case Entity::NORMAL_N_COPY:
@ -344,16 +357,16 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
case Entity::NORMAL_IN_2D:
if(transOnly) {
en.type = Entity::NORMAL_N_COPY;
en.numNormal = ep->NormalGetNum();
} else {
en.type = Entity::NORMAL_N_ROT;
en.numNormal = ep->NormalGetNum();
en.param[0] = qw;
en.param[1] = qvx;
en.param[2] = qvy;
en.param[3] = qvz;
}
en.numNormal = ep->NormalGetNum();
en.point[0] = Remap(ep->point[0], a);
en.timesApplied = a;
break;
case Entity::DISTANCE_N_COPY:
@ -400,12 +413,24 @@ void Group::MakePolygons(void) {
translate.x = SS.GetParam(h.param(0))->val;
translate.y = SS.GetParam(h.param(1))->val;
translate.z = SS.GetParam(h.param(2))->val;
Vector t0, dt;
if(subtype == EXTRUDE_ONE_SIDED) {
t0 = Vector::MakeFrom(0, 0, 0); dt = translate;
} else {
t0 = translate.ScaledBy(-1); dt = translate.ScaledBy(2);
}
// Get the source polygon to extrude, and break it down to edges
edges.l.Clear();
Group *src = SS.GetGroup(opA);
if(src->faces.n != 1) return;
(src->faces.elem[0]).MakeEdgesInto(&edges);
for(i = 0; i < edges.l.n; i++) {
SEdge *edge = &(edges.l.elem[i]);
edge->a = (edge->a).Plus(t0);
edge->b = (edge->b).Plus(t0);
}
SPolygon poly;
SEdge error;
@ -419,7 +444,6 @@ void Group::MakePolygons(void) {
}
poly.normal = n;
poly.FixContourDirections();
poly.FixContourDirections();
faces.Add(&poly);
// Regenerate the edges, with the contour directions fixed up.
@ -434,14 +458,14 @@ void Group::MakePolygons(void) {
poly.AddEmptyContour();
poly.AddPoint(edge->a);
poly.AddPoint(edge->b);
poly.AddPoint((edge->b).Plus(translate));
poly.AddPoint((edge->a).Plus(translate));
poly.AddPoint((edge->b).Plus(dt));
poly.AddPoint((edge->a).Plus(dt));
poly.AddPoint(edge->a);
poly.normal = ((edge->a).Minus(edge->b).Cross(n)).WithMagnitude(1);
faces.Add(&poly);
edge->a = (edge->a).Plus(translate);
edge->b = (edge->b).Plus(translate);
edge->a = (edge->a).Plus(dt);
edge->b = (edge->b).Plus(dt);
}
// The top

View File

@ -92,8 +92,11 @@ public:
static const int WORKPLANE_BY_POINT_ORTHO = 6000;
static const int WORKPLANE_BY_LINE_SEGMENTS = 6001;
static const int EXTRUDE_ONE_SIDED = 7000;
static const int EXTRUDE_TWO_SIDED = 7001;
int subtype;
struct {
int type;
Quaternion q;
hEntity origin;
hEntity entityB;
@ -121,9 +124,10 @@ public:
// mapping list.
IdList<EntityMap,EntityId> remap;
hEntity Remap(hEntity in, int copyNumber);
void MakeExtrusionLines(hEntity in, int ai, int af);
void CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz,
bool transOnly, bool isExtrusion);
bool transOnly);
void GenerateEquations(IdList<Equation,hEquation> *l);

View File

@ -210,7 +210,6 @@ void TextWindow::ScreenNavigation(int link, DWORD v) {
SS.TW.OneScreenForwardTo(-1);
break;
}
SS.TW.Show();
}
void TextWindow::ShowHeader(void) {
ClearScreen();
@ -262,25 +261,37 @@ hs(SS.GW.showHdnLines), (DWORD)(&SS.GW.showHdnLines), &(SS.GW.ToggleBool)
void TextWindow::ScreenSelectGroup(int link, DWORD v) {
SS.TW.OneScreenForwardTo(SCREEN_GROUP_INFO);
SS.TW.shown->group.v = v;
SS.TW.Show();
}
void TextWindow::ScreenToggleGroupShown(int link, DWORD v) {
hGroup hg = { v };
Group *g = SS.GetGroup(hg);
g->visible = !(g->visible);
}
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]);
InvalidateGraphics();
SS.TW.Show();
if(g->h.v == SS.GW.activeGroup.v) {
before = false;
} else if(before && link == 's') {
g->visible = true;
} else if(!before && link == 'h') {
g->visible = false;
}
}
}
void TextWindow::ScreenActivateGroup(int link, DWORD v) {
hGroup hg = { v };
Group *g = SS.GetGroup(hg);
g->visible = true;
SS.GW.activeGroup.v = v;
InvalidateGraphics();
SS.TW.Show();
if(g->type == Group::DRAWING_WORKPLANE) {
// If we're activating an in-workplane drawing, then activate that
// workplane too.
SS.GW.activeWorkplane = g->h.entity(0);
}
}
void TextWindow::ShowListOfGroups(void) {
Printf(true, "%Ftactive show group-name%E");
@ -309,20 +320,30 @@ void TextWindow::ShowListOfGroups(void) {
// Link to a screen that gives more details on the group
g->h.v, (&TextWindow::ScreenSelectGroup), s);
}
Printf(true, " %Fl%Ls%fshow all groups before active%E",
&(TextWindow::ScreenShowGroupsSpecial));
Printf(false, " %Fl%Lh%fhide all groups after active%E",
&(TextWindow::ScreenShowGroupsSpecial));
}
void TextWindow::ScreenSelectConstraint(int link, DWORD v) {
SS.TW.OneScreenForwardTo(SCREEN_CONSTRAINT_INFO);
SS.TW.shown->constraint.v = v;
SS.TW.Show();
}
void TextWindow::ScreenSelectRequest(int link, DWORD v) {
SS.TW.OneScreenForwardTo(SCREEN_REQUEST_INFO);
SS.TW.shown->request.v = v;
SS.TW.Show();
}
void TextWindow::ScreenChangeExtrudeSides(int link, DWORD v) {
Group *g = SS.GetGroup(SS.TW.shown->group);
if(g->subtype == Group::EXTRUDE_ONE_SIDED) {
g->subtype = Group::EXTRUDE_TWO_SIDED;
} else {
g->subtype = Group::EXTRUDE_ONE_SIDED;
}
SS.GW.GeneratePerSolving();
}
void TextWindow::ShowGroupInfo(void) {
Group *g = SS.group.FindById(shown->group);
@ -334,7 +355,18 @@ void TextWindow::ShowGroupInfo(void) {
} else {
s = "";
}
Printf(true, "%Ft%sgroup %E%s", s, g->DescriptionString());
Printf(true, "%Ft%sGROUP %E%s", s, g->DescriptionString());
if(g->type == Group::EXTRUDE) {
bool one = (g->subtype == Group::EXTRUDE_ONE_SIDED);
Printf(true, "%FtEXTRUDE%E one-sided: %Fh%f%Ll%s%E%Fs%s%E",
&TextWindow::ScreenChangeExtrudeSides,
(one ? "" : "no"), (one ? "yes" : ""));
Printf(false, " two-sided: %Fh%f%Ll%s%E%Fs%s%E",
&TextWindow::ScreenChangeExtrudeSides,
(!one ? "" : "no"), (!one ? "yes" : ""));
}
Printf(true, "%Ftrequests in group");
int i, a = 0;

4
ui.h
View File

@ -69,9 +69,12 @@ public:
static void ScreenSelectGroup(int link, DWORD v);
static void ScreenActivateGroup(int link, DWORD v);
static void ScreenToggleGroupShown(int link, DWORD v);
static void ScreenShowGroupsSpecial(int link, DWORD v);
static void ScreenSelectRequest(int link, DWORD v);
static void ScreenSelectConstraint(int link, DWORD v);
static void ScreenChangeExtrudeSides(int link, DWORD v);
static void ScreenNavigation(int link, DWORD v);
};
@ -254,6 +257,7 @@ public:
static const int DONT_SOLVE = 0;
static const int SOLVE_ALWAYS = 1;
int solving;
void GeneratePerSolving(void);
void UpdateDraggedNum(Vector *pos, double mx, double my);
void UpdateDraggedPoint(hEntity hp, double mx, double my);

View File

@ -318,6 +318,8 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
SS.TW.meta[r][c].link,
SS.TW.meta[r][c].data
);
SS.TW.Show();
InvalidateGraphics();
}
}
break;