Now I can add a constraint (a length), and it's displayed
on-screen, and I can drag the label. That's progress. Also implement a bunch of untested expression stuff, since I'll need that for the values of the dimensions, for example. [git-p4: depot-paths = "//depot/solvespace/": change = 1668]solver
parent
094e10204d
commit
22302dca7a
2
Makefile
2
Makefile
|
@ -17,6 +17,8 @@ SSOBJS = $(OBJDIR)\solvespace.obj \
|
||||||
$(OBJDIR)\sketch.obj \
|
$(OBJDIR)\sketch.obj \
|
||||||
$(OBJDIR)\glhelper.obj \
|
$(OBJDIR)\glhelper.obj \
|
||||||
$(OBJDIR)\expr.obj \
|
$(OBJDIR)\expr.obj \
|
||||||
|
$(OBJDIR)\constraint.obj \
|
||||||
|
$(OBJDIR)\drawconstraint.obj \
|
||||||
|
|
||||||
|
|
||||||
LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib
|
LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
hConstraint Constraint::AddConstraint(Constraint *c) {
|
||||||
|
SS.constraint.AddAndAssignId(c);
|
||||||
|
return c->h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Constraint::MenuConstrain(int id) {
|
||||||
|
Constraint c;
|
||||||
|
memset(&c, 0, sizeof(c));
|
||||||
|
c.group = SS.GW.activeGroup;
|
||||||
|
|
||||||
|
SS.GW.GroupSelection();
|
||||||
|
#define gs (SS.GW.gs)
|
||||||
|
|
||||||
|
switch(id) {
|
||||||
|
case GraphicsWindow::MNU_DISTANCE_DIA:
|
||||||
|
if(gs.points == 2 && gs.n == 2) {
|
||||||
|
c.type = PT_PT_DISTANCE;
|
||||||
|
c.ptA = gs.point[0];
|
||||||
|
c.ptB = gs.point[1];
|
||||||
|
} else if(gs.lineSegments == 1 && gs.n == 1) {
|
||||||
|
c.type = PT_PT_DISTANCE;
|
||||||
|
c.ptA = gs.entity[0].point(16);
|
||||||
|
c.ptB = gs.entity[0].point(16+3);
|
||||||
|
} else {
|
||||||
|
Error("Bad selection for distance / diameter constraint.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
c.disp.offset = Vector::MakeFrom(50, 50, 50);
|
||||||
|
AddConstraint(&c);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: oops();
|
||||||
|
}
|
||||||
|
|
||||||
|
SS.GW.ClearSelection();
|
||||||
|
InvalidateGraphics();
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
bool Constraint::HasLabel(void) {
|
||||||
|
switch(type) {
|
||||||
|
case PT_PT_DISTANCE:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Constraint::DrawOrGetDistance(void) {
|
||||||
|
// Unit vectors that describe our current view of the scene.
|
||||||
|
Vector gr = SS.GW.projRight;
|
||||||
|
Vector gu = SS.GW.projUp;
|
||||||
|
Vector gn = gr.Cross(gu);
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case PT_PT_DISTANCE: {
|
||||||
|
Vector ap = SS.GetPoint(ptA)->GetCoords();
|
||||||
|
Vector bp = SS.GetPoint(ptB)->GetCoords();
|
||||||
|
|
||||||
|
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
|
||||||
|
|
||||||
|
if(dogd.drawing) {
|
||||||
|
Vector ab = ap.Minus(bp);
|
||||||
|
Vector ar = ap.Minus(ref);
|
||||||
|
// Normal to a plan containing the line and the label origin.
|
||||||
|
Vector n = ab.Cross(ar);
|
||||||
|
Vector out = ab.Cross(n).WithMagnitude(1);
|
||||||
|
out = out.ScaledBy(-out.Dot(ar));
|
||||||
|
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glxVertex3v(ap);
|
||||||
|
glxVertex3v(ap.Plus(out));
|
||||||
|
glxVertex3v(bp);
|
||||||
|
glxVertex3v(bp.Plus(out));
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glxTranslatev(ref);
|
||||||
|
glxOntoCsys(gr, gu);
|
||||||
|
glxWriteText("ABCDEFG");
|
||||||
|
glPopMatrix();
|
||||||
|
} else {
|
||||||
|
Point2d o = SS.GW.ProjectPoint(ref);
|
||||||
|
dogd.dmin = o.DistanceTo(dogd.mp) - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: oops();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Constraint::Draw(void) {
|
||||||
|
dogd.drawing = true;
|
||||||
|
DrawOrGetDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
double Constraint::GetDistance(Point2d mp) {
|
||||||
|
dogd.drawing = false;
|
||||||
|
dogd.mp = mp;
|
||||||
|
dogd.dmin = 1e12;
|
||||||
|
|
||||||
|
DrawOrGetDistance();
|
||||||
|
|
||||||
|
return dogd.dmin;
|
||||||
|
}
|
||||||
|
|
10
dsc.h
10
dsc.h
|
@ -19,6 +19,7 @@ public:
|
||||||
Vector Normal(int which);
|
Vector Normal(int which);
|
||||||
Vector RotatedAbout(Vector axis, double theta);
|
Vector RotatedAbout(Vector axis, double theta);
|
||||||
double Magnitude(void);
|
double Magnitude(void);
|
||||||
|
Vector WithMagnitude(double s);
|
||||||
Vector ScaledBy(double s);
|
Vector ScaledBy(double s);
|
||||||
|
|
||||||
// Call a rotation matrix [ u' v' n' ]'; this returns the first and
|
// Call a rotation matrix [ u' v' n' ]'; this returns the first and
|
||||||
|
@ -103,6 +104,15 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Tag(H h, int tag) {
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < elems; i++) {
|
||||||
|
if(elem[i].t.h.v == h.v) {
|
||||||
|
elem[i].tag = tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RemoveTagged(void) {
|
void RemoveTagged(void) {
|
||||||
int src, dest;
|
int src, dest;
|
||||||
dest = 0;
|
dest = 0;
|
||||||
|
|
70
expr.cpp
70
expr.cpp
|
@ -44,3 +44,73 @@ double Expr::Eval(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expr *Expr::PartialWrt(hParam p) {
|
||||||
|
Expr *da, *db;
|
||||||
|
|
||||||
|
switch(op) {
|
||||||
|
case PARAM_PTR: oops();
|
||||||
|
case PARAM: return FromConstant(p.v == x.parh.v ? 1 : 0);
|
||||||
|
|
||||||
|
case CONSTANT: return FromConstant(0);
|
||||||
|
|
||||||
|
case PLUS: return (a->PartialWrt(p))->Plus(b->PartialWrt(p));
|
||||||
|
case MINUS: return (a->PartialWrt(p))->Minus(b->PartialWrt(p));
|
||||||
|
|
||||||
|
case TIMES:
|
||||||
|
da = a->PartialWrt(p);
|
||||||
|
db = b->PartialWrt(p);
|
||||||
|
return (a->Times(db))->Plus(b->Times(da));
|
||||||
|
|
||||||
|
case DIV:
|
||||||
|
da = a->PartialWrt(p);
|
||||||
|
db = b->PartialWrt(p);
|
||||||
|
return ((da->Times(b))->Minus(a->Times(db)))->Div(b->Square());
|
||||||
|
|
||||||
|
case SQRT:
|
||||||
|
return (FromConstant(0.5)->Div(a->Sqrt()))->Times(a->PartialWrt(p));
|
||||||
|
|
||||||
|
case SQUARE:
|
||||||
|
return (FromConstant(2.0)->Times(a))->Times(a->PartialWrt(p));
|
||||||
|
|
||||||
|
case NEGATE: return (a->PartialWrt(p))->Negate();
|
||||||
|
case SIN: return (a->Cos())->Times(a->PartialWrt(p));
|
||||||
|
case COS: return ((a->Sin())->Times(a->PartialWrt(p)))->Negate();
|
||||||
|
|
||||||
|
default: oops();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char StringBuffer[4096];
|
||||||
|
void Expr::App(char *s, ...) {
|
||||||
|
va_list f;
|
||||||
|
va_start(f, s);
|
||||||
|
vsprintf(StringBuffer+strlen(StringBuffer), s, f);
|
||||||
|
}
|
||||||
|
char *Expr::Print(void) {
|
||||||
|
StringBuffer[0] = '\0';
|
||||||
|
PrintW();
|
||||||
|
return StringBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Expr::PrintW(void) {
|
||||||
|
switch(op) {
|
||||||
|
case PARAM: App("(param %08x)", x.parh.v); break;
|
||||||
|
case PARAM_PTR: App("(paramp %08x)", x.parp->h.v); break;
|
||||||
|
|
||||||
|
case CONSTANT: App("%.3f", x.v);
|
||||||
|
|
||||||
|
case PLUS: App("(+ "); a->PrintW(); b->PrintW(); App(")"); break;
|
||||||
|
case MINUS: App("(- "); a->PrintW(); b->PrintW(); App(")"); break;
|
||||||
|
case TIMES: App("(* "); a->PrintW(); b->PrintW(); App(")"); break;
|
||||||
|
case DIV: App("(/ "); a->PrintW(); b->PrintW(); App(")"); break;
|
||||||
|
|
||||||
|
case NEGATE: App("(- "); a->PrintW(); App(")"); break;
|
||||||
|
case SQRT: App("(sqrt "); a->PrintW(); App(")"); break;
|
||||||
|
case SQUARE: App("(square "); a->PrintW(); App(")"); break;
|
||||||
|
case SIN: App("(sin "); a->PrintW(); App(")"); break;
|
||||||
|
case COS: App("(cos "); a->PrintW(); App(")"); break;
|
||||||
|
|
||||||
|
default: oops();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
5
expr.h
5
expr.h
|
@ -58,7 +58,10 @@ public:
|
||||||
double Eval(void);
|
double Eval(void);
|
||||||
|
|
||||||
void ParamsToPointers(void);
|
void ParamsToPointers(void);
|
||||||
void Print(void);
|
|
||||||
|
void App(char *str, ...);
|
||||||
|
char *Print(void);
|
||||||
|
void PrintW(void); // worker
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
119
graphicswin.cpp
119
graphicswin.cpp
|
@ -5,6 +5,7 @@
|
||||||
#define mView (&GraphicsWindow::MenuView)
|
#define mView (&GraphicsWindow::MenuView)
|
||||||
#define mEdit (&GraphicsWindow::MenuEdit)
|
#define mEdit (&GraphicsWindow::MenuEdit)
|
||||||
#define mReq (&GraphicsWindow::MenuRequest)
|
#define mReq (&GraphicsWindow::MenuRequest)
|
||||||
|
#define mCon (&Constraint::MenuConstrain)
|
||||||
#define S 0x100
|
#define S 0x100
|
||||||
const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||||
{ 0, "&File", 0, NULL },
|
{ 0, "&File", 0, NULL },
|
||||||
|
@ -19,6 +20,8 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||||
{ 1, "&Undo\tCtrl+Z", 0, NULL },
|
{ 1, "&Undo\tCtrl+Z", 0, NULL },
|
||||||
{ 1, "&Redo\tCtrl+Y", 0, NULL },
|
{ 1, "&Redo\tCtrl+Y", 0, NULL },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
|
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
|
||||||
|
{ 1, NULL, 0, NULL },
|
||||||
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
|
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
|
||||||
|
|
||||||
{ 0, "&View", 0, NULL },
|
{ 0, "&View", 0, NULL },
|
||||||
|
@ -27,7 +30,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||||
{ 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, "&Onto Plane / Coordinate System\tO", MNU_ORIENT_ONTO, 'O', mView },
|
{ 1, "&Onto Plane / Coordinate System\tO", MNU_ORIENT_ONTO, 'O', mView },
|
||||||
{ 1, "&Lock Orientation\tL", 0, 'L', mView },
|
{ 1, "&Lock Orientation\tL", MNU_LOCK_VIEW, 'L', mView },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
{ 1, "Dimensions in &Inches", 0, NULL },
|
{ 1, "Dimensions in &Inches", 0, NULL },
|
||||||
{ 1, "Dimensions in &Millimeters", 0, NULL },
|
{ 1, "Dimensions in &Millimeters", 0, NULL },
|
||||||
|
@ -54,7 +57,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||||
{ 1, "To&ggle Construction\tG", 0, 'G', NULL },
|
{ 1, "To&ggle Construction\tG", 0, 'G', NULL },
|
||||||
|
|
||||||
{ 0, "&Constrain", 0, NULL },
|
{ 0, "&Constrain", 0, NULL },
|
||||||
{ 1, "&Distance / Diameter\tShift+D", 0, 'D'|S, NULL },
|
{ 1, "&Distance / Diameter\tShift+D", MNU_DISTANCE_DIA, 'D'|S, mCon },
|
||||||
{ 1, "A&ngle\tShift+N", 0, 'N'|S, NULL },
|
{ 1, "A&ngle\tShift+N", 0, 'N'|S, NULL },
|
||||||
{ 1, "Other S&upplementary Angle\tShift+U", 0, 'U'|S, NULL },
|
{ 1, "Other S&upplementary Angle\tShift+U", 0, 'U'|S, NULL },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
|
@ -108,7 +111,7 @@ Point2d GraphicsWindow::ProjectPoint(Vector p) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::MenuView(MenuId id) {
|
void GraphicsWindow::MenuView(int id) {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
case MNU_ZOOM_IN:
|
case MNU_ZOOM_IN:
|
||||||
SS.GW.scale *= 1.2;
|
SS.GW.scale *= 1.2;
|
||||||
|
@ -121,6 +124,11 @@ void GraphicsWindow::MenuView(MenuId id) {
|
||||||
case MNU_ZOOM_TO_FIT:
|
case MNU_ZOOM_TO_FIT:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MNU_LOCK_VIEW:
|
||||||
|
SS.GW.viewLocked = !SS.GW.viewLocked;
|
||||||
|
CheckMenuById(MNU_LOCK_VIEW, SS.GW.viewLocked);
|
||||||
|
break;
|
||||||
|
|
||||||
case MNU_ORIENT_ONTO:
|
case MNU_ORIENT_ONTO:
|
||||||
SS.GW.GroupSelection();
|
SS.GW.GroupSelection();
|
||||||
if(SS.GW.gs.n == 1 && SS.GW.gs.csyss == 1) {
|
if(SS.GW.gs.n == 1 && SS.GW.gs.csyss == 1) {
|
||||||
|
@ -155,7 +163,7 @@ void GraphicsWindow::EnsureValidActiveGroup(void) {
|
||||||
activeGroup = SS.group.elem[i].t.h;
|
activeGroup = SS.group.elem[i].t.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::MenuEdit(MenuId id) {
|
void GraphicsWindow::MenuEdit(int id) {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
case MNU_UNSELECT_ALL:
|
case MNU_UNSELECT_ALL:
|
||||||
SS.GW.ClearSelection();
|
SS.GW.ClearSelection();
|
||||||
|
@ -164,11 +172,38 @@ void GraphicsWindow::MenuEdit(MenuId id) {
|
||||||
SS.TW.Show();
|
SS.TW.Show();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MNU_DELETE: {
|
||||||
|
int i;
|
||||||
|
SS.request.ClearTags();
|
||||||
|
for(i = 0; i < MAX_SELECTED; i++) {
|
||||||
|
Selection *s = &(SS.GW.selection[i]);
|
||||||
|
hRequest r;
|
||||||
|
r.v = 0;
|
||||||
|
if(s->point.v) {
|
||||||
|
Point *pt = SS.GetPoint(s->point);
|
||||||
|
Entity *e = SS.GetEntity(pt->entity());
|
||||||
|
if(e->type == Entity::DATUM_POINT) {
|
||||||
|
r = e->request();
|
||||||
|
}
|
||||||
|
} else if(s->entity.v) {
|
||||||
|
Entity *e = SS.GetEntity(s->entity);
|
||||||
|
r = e->request();
|
||||||
|
}
|
||||||
|
if(r.v) SS.request.Tag(r, 1);
|
||||||
|
}
|
||||||
|
SS.request.RemoveTagged();
|
||||||
|
|
||||||
|
SS.GenerateAll();
|
||||||
|
SS.GW.ClearSelection();
|
||||||
|
SS.GW.hover.Clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: oops();
|
default: oops();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::MenuRequest(MenuId id) {
|
void GraphicsWindow::MenuRequest(int id) {
|
||||||
char *s;
|
char *s;
|
||||||
switch(id) {
|
switch(id) {
|
||||||
case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
|
case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
|
||||||
|
@ -183,12 +218,16 @@ c:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::UpdateDraggedPoint(hPoint hp, double mx, double my) {
|
void GraphicsWindow::UpdateDraggedHPoint(hPoint hp, double mx, double my) {
|
||||||
Point *p = SS.point.FindById(hp);
|
Point *p = SS.point.FindById(hp);
|
||||||
Vector pos = p->GetCoords();
|
Vector pos = p->GetCoords();
|
||||||
pos = pos.Plus(projRight.ScaledBy((mx - orig.mouse.x)/scale));
|
UpdateDraggedPoint(&pos, mx, my);
|
||||||
pos = pos.Plus(projUp.ScaledBy((my - orig.mouse.y)/scale));
|
|
||||||
p->ForceTo(pos);
|
p->ForceTo(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsWindow::UpdateDraggedPoint(Vector *pos, double mx, double my) {
|
||||||
|
*pos = pos->Plus(projRight.ScaledBy((mx - orig.mouse.x)/scale));
|
||||||
|
*pos = pos->Plus(projUp.ScaledBy((my - orig.mouse.y)/scale));
|
||||||
|
|
||||||
orig.mouse.x = mx;
|
orig.mouse.x = mx;
|
||||||
orig.mouse.y = my;
|
orig.mouse.y = my;
|
||||||
|
@ -206,11 +245,12 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
double dx = (x - orig.mouse.x) / scale;
|
double dx = (x - orig.mouse.x) / scale;
|
||||||
double dy = (y - orig.mouse.y) / scale;
|
double dy = (y - orig.mouse.y) / scale;
|
||||||
|
|
||||||
if(shiftDown) {
|
// When the view is locked, permit only translation (pan).
|
||||||
|
if(shiftDown || viewLocked) {
|
||||||
offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x;
|
offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x;
|
||||||
offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y;
|
offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y;
|
||||||
offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z;
|
offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z;
|
||||||
} else if(ctrlDown) {
|
} else if(ctrlDown && !viewLocked) {
|
||||||
double theta = atan2(orig.mouse.y, orig.mouse.x);
|
double theta = atan2(orig.mouse.y, orig.mouse.x);
|
||||||
theta -= atan2(y, x);
|
theta -= atan2(y, x);
|
||||||
|
|
||||||
|
@ -219,7 +259,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
projUp = orig.projUp.RotatedAbout(normal, theta);
|
projUp = orig.projUp.RotatedAbout(normal, theta);
|
||||||
|
|
||||||
NormalizeProjectionVectors();
|
NormalizeProjectionVectors();
|
||||||
} else {
|
} else if(!viewLocked) {
|
||||||
double s = 0.3*(PI/180); // degrees per pixel
|
double s = 0.3*(PI/180); // degrees per pixel
|
||||||
projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx);
|
projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx);
|
||||||
projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy);
|
projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy);
|
||||||
|
@ -235,14 +275,21 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
|
|
||||||
InvalidateGraphics();
|
InvalidateGraphics();
|
||||||
} else if(leftDown) {
|
} else if(leftDown) {
|
||||||
// We are left-dragging. This is often used to drag points.
|
// We are left-dragging. This is often used to drag points, or
|
||||||
|
// constraint labels.
|
||||||
if(hover.point.v && !hover.point.isFromReferences()) {
|
if(hover.point.v && !hover.point.isFromReferences()) {
|
||||||
ClearSelection();
|
ClearSelection();
|
||||||
UpdateDraggedPoint(hover.point, x, y);
|
UpdateDraggedHPoint(hover.point, x, y);
|
||||||
|
} else if(hover.constraint.v &&
|
||||||
|
SS.GetConstraint(hover.constraint)->HasLabel())
|
||||||
|
{
|
||||||
|
ClearSelection();
|
||||||
|
Constraint *c = SS.constraint.FindById(hover.constraint);
|
||||||
|
UpdateDraggedPoint(&(c->disp.offset), x, y);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT) {
|
if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT) {
|
||||||
UpdateDraggedPoint(pendingPoint, x, y);
|
UpdateDraggedHPoint(pendingPoint, x, y);
|
||||||
} else {
|
} else {
|
||||||
// Do our usual hit testing, for the selection.
|
// Do our usual hit testing, for the selection.
|
||||||
Selection s;
|
Selection s;
|
||||||
|
@ -256,35 +303,37 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GraphicsWindow::Selection::Equals(Selection *b) {
|
bool GraphicsWindow::Selection::Equals(Selection *b) {
|
||||||
if(point.v != b->point.v) return false;
|
if(point.v != b->point.v) return false;
|
||||||
if(entity.v != b->entity.v) return false;
|
if(entity.v != b->entity.v) return false;
|
||||||
|
if(constraint.v != b->constraint.v) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool GraphicsWindow::Selection::IsEmpty(void) {
|
bool GraphicsWindow::Selection::IsEmpty(void) {
|
||||||
if(point.v) return false;
|
if(point.v) return false;
|
||||||
if(entity.v) return false;
|
if(entity.v) return false;
|
||||||
|
if(constraint.v) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void GraphicsWindow::Selection::Clear(void) {
|
void GraphicsWindow::Selection::Clear(void) {
|
||||||
point.v = entity.v = 0;
|
point.v = entity.v = constraint.v = 0;
|
||||||
}
|
}
|
||||||
void GraphicsWindow::Selection::Draw(void) {
|
void GraphicsWindow::Selection::Draw(void) {
|
||||||
if(point.v) SS.point. FindById(point )->Draw();
|
if(point.v) SS.point. FindById(point )->Draw();
|
||||||
if(entity.v) SS.entity.FindById(entity)->Draw();
|
if(entity.v) SS.entity. FindById(entity )->Draw();
|
||||||
|
if(constraint.v) SS.constraint.FindById(constraint)->Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) {
|
void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) {
|
||||||
int i;
|
int i;
|
||||||
double d, dmin = 1e12;
|
double d, dmin = 1e12;
|
||||||
|
|
||||||
dest->point.v = 0;
|
memset(dest, 0, sizeof(*dest));
|
||||||
dest->entity.v = 0;
|
|
||||||
|
|
||||||
// Do the points
|
// Do the points
|
||||||
for(i = 0; i < SS.entity.elems; i++) {
|
for(i = 0; i < SS.entity.elems; i++) {
|
||||||
d = SS.entity.elem[i].t.GetDistance(mp);
|
d = SS.entity.elem[i].t.GetDistance(mp);
|
||||||
if(d < 10 && d < dmin) {
|
if(d < 10 && d < dmin) {
|
||||||
dest->point.v = 0;
|
memset(dest, 0, sizeof(*dest));
|
||||||
dest->entity = SS.entity.elem[i].t.h;
|
dest->entity = SS.entity.elem[i].t.h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,10 +342,19 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) {
|
||||||
for(i = 0; i < SS.point.elems; i++) {
|
for(i = 0; i < SS.point.elems; i++) {
|
||||||
d = SS.point.elem[i].t.GetDistance(mp);
|
d = SS.point.elem[i].t.GetDistance(mp);
|
||||||
if(d < 10 && d < dmin) {
|
if(d < 10 && d < dmin) {
|
||||||
dest->entity.v = 0;
|
memset(dest, 0, sizeof(*dest));
|
||||||
dest->point = SS.point.elem[i].t.h;
|
dest->point = SS.point.elem[i].t.h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constraints
|
||||||
|
for(i = 0; i < SS.constraint.elems; i++) {
|
||||||
|
d = SS.constraint.elem[i].t.GetDistance(mp);
|
||||||
|
if(d < 10 && d < dmin) {
|
||||||
|
memset(dest, 0, sizeof(*dest));
|
||||||
|
dest->constraint = SS.constraint.elem[i].t.h;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::ClearSelection(void) {
|
void GraphicsWindow::ClearSelection(void) {
|
||||||
|
@ -308,7 +366,7 @@ void GraphicsWindow::ClearSelection(void) {
|
||||||
|
|
||||||
void GraphicsWindow::GroupSelection(void) {
|
void GraphicsWindow::GroupSelection(void) {
|
||||||
gs.points = gs.entities = 0;
|
gs.points = gs.entities = 0;
|
||||||
gs.csyss = 0;
|
gs.csyss = gs.lineSegments = 0;
|
||||||
gs.n = 0;
|
gs.n = 0;
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < MAX_SELECTED; i++) {
|
for(i = 0; i < MAX_SELECTED; i++) {
|
||||||
|
@ -322,8 +380,9 @@ void GraphicsWindow::GroupSelection(void) {
|
||||||
(gs.n)++;
|
(gs.n)++;
|
||||||
|
|
||||||
Entity *e = SS.entity.FindById(s->entity);
|
Entity *e = SS.entity.FindById(s->entity);
|
||||||
if(e->type == Entity::CSYS_2D) {
|
switch(e->type) {
|
||||||
(gs.csyss)++;
|
case Entity::CSYS_2D: (gs.csyss)++; break;
|
||||||
|
case Entity::LINE_SEGMENT: (gs.lineSegments)++; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,6 +555,10 @@ void GraphicsWindow::Paint(int w, int h) {
|
||||||
for(i = 0; i < SS.point.elems; i++) {
|
for(i = 0; i < SS.point.elems; i++) {
|
||||||
SS.point.elem[i].t.Draw();
|
SS.point.elem[i].t.Draw();
|
||||||
}
|
}
|
||||||
|
glColor3f(1.0f, 0, 1.0f);
|
||||||
|
for(i = 0; i < SS.constraint.elems; i++) {
|
||||||
|
SS.constraint.elem[i].t.Draw();
|
||||||
|
}
|
||||||
|
|
||||||
// Then redraw whatever the mouse is hovering over, highlighted. Have
|
// Then redraw whatever the mouse is hovering over, highlighted. Have
|
||||||
// to disable the depth test, so that we can overdraw.
|
// to disable the depth test, so that we can overdraw.
|
||||||
|
|
72
sketch.h
72
sketch.h
|
@ -12,6 +12,9 @@ class Entity;
|
||||||
class Param;
|
class Param;
|
||||||
class Point;
|
class Point;
|
||||||
|
|
||||||
|
class hEquation;
|
||||||
|
class Equation;
|
||||||
|
|
||||||
// All of the hWhatever handles are a 32-bit ID, that is used to represent
|
// All of the hWhatever handles are a 32-bit ID, that is used to represent
|
||||||
// some data structure in the sketch.
|
// some data structure in the sketch.
|
||||||
class hGroup {
|
class hGroup {
|
||||||
|
@ -79,9 +82,9 @@ public:
|
||||||
static const hRequest HREQUEST_REFERENCE_ZX;
|
static const hRequest HREQUEST_REFERENCE_ZX;
|
||||||
|
|
||||||
// Types of requests
|
// Types of requests
|
||||||
static const int CSYS_2D = 0;
|
static const int CSYS_2D = 10;
|
||||||
static const int DATUM_POINT = 1;
|
static const int DATUM_POINT = 11;
|
||||||
static const int LINE_SEGMENT = 10;
|
static const int LINE_SEGMENT = 20;
|
||||||
|
|
||||||
int type;
|
int type;
|
||||||
|
|
||||||
|
@ -173,6 +176,69 @@ public:
|
||||||
double GetDistance(Point2d mp);
|
double GetDistance(Point2d mp);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class hConstraint {
|
||||||
|
public:
|
||||||
|
DWORD v;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Constraint {
|
||||||
|
public:
|
||||||
|
static const int USER_EQUATION = 10;
|
||||||
|
static const int POINTS_COINCIDENT = 20;
|
||||||
|
static const int PT_PT_DISTANCE = 30;
|
||||||
|
static const int PT_LINE_DISTANCE = 31;
|
||||||
|
|
||||||
|
static const int HORIZONTAL = 40;
|
||||||
|
static const int VERTICAL = 41;
|
||||||
|
|
||||||
|
hConstraint h;
|
||||||
|
int type;
|
||||||
|
hGroup group;
|
||||||
|
|
||||||
|
// These are the parameters for the constraint.
|
||||||
|
Expr *exprA;
|
||||||
|
Expr *exprB;
|
||||||
|
hPoint ptA;
|
||||||
|
hPoint ptB;
|
||||||
|
hPoint ptC;
|
||||||
|
hEntity entityA;
|
||||||
|
hEntity entityB;
|
||||||
|
|
||||||
|
// These define how the constraint is drawn on-screen.
|
||||||
|
struct {
|
||||||
|
hEntity csys;
|
||||||
|
Vector offset;
|
||||||
|
Vector u, v;
|
||||||
|
} disp;
|
||||||
|
|
||||||
|
static hConstraint AddConstraint(Constraint *c);
|
||||||
|
static void MenuConstrain(int id);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool drawing;
|
||||||
|
Point2d mp;
|
||||||
|
double dmin;
|
||||||
|
} dogd; // state for drawing or getting distance (for hit testing)
|
||||||
|
double GetDistance(Point2d mp);
|
||||||
|
void Draw(void);
|
||||||
|
void DrawOrGetDistance(void);
|
||||||
|
|
||||||
|
bool HasLabel(void);
|
||||||
|
|
||||||
|
void Generate(IdList<Equation,hEquation> *l);
|
||||||
|
};
|
||||||
|
|
||||||
|
class hEquation {
|
||||||
|
public:
|
||||||
|
DWORD v;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Equation {
|
||||||
|
public:
|
||||||
|
hEquation h;
|
||||||
|
Expr *e;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
inline hEntity hRequest::entity(int i)
|
inline hEntity hRequest::entity(int i)
|
||||||
{ hEntity r; r.v = (v << 10) | i; return r; }
|
{ hEntity r; r.v = (v << 10) | i; return r; }
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
|
|
||||||
template IdList<Request,hRequest>;
|
|
||||||
template IdList<Entity,hEntity>;
|
|
||||||
template IdList<Point,hPoint>;
|
|
||||||
|
|
||||||
SolveSpace SS;
|
SolveSpace SS;
|
||||||
|
|
||||||
void SolveSpace::Init(void) {
|
void SolveSpace::Init(void) {
|
||||||
|
constraint.Clear();
|
||||||
request.Clear();
|
request.Clear();
|
||||||
|
group.Clear();
|
||||||
|
|
||||||
entity.Clear();
|
entity.Clear();
|
||||||
point.Clear();
|
point.Clear();
|
||||||
param.Clear();
|
param.Clear();
|
||||||
group.Clear();
|
|
||||||
|
|
||||||
// Our initial group, that contains the references.
|
// Our initial group, that contains the references.
|
||||||
Group g;
|
Group g;
|
||||||
|
|
44
solvespace.h
44
solvespace.h
|
@ -12,13 +12,6 @@
|
||||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Expr;
|
|
||||||
|
|
||||||
void dbp(char *str, ...);
|
|
||||||
void Error(char *str, ...);
|
|
||||||
Expr *AllocExpr(void);
|
|
||||||
void FreeAllExprs(void);
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -27,14 +20,23 @@ void FreeAllExprs(void);
|
||||||
#include <gl/gl.h>
|
#include <gl/gl.h>
|
||||||
#include <gl/glu.h>
|
#include <gl/glu.h>
|
||||||
|
|
||||||
|
class Expr;
|
||||||
|
|
||||||
|
// From the platform-specific code.
|
||||||
|
void CheckMenuById(int id, BOOL checked);
|
||||||
|
void InvalidateGraphics(void);
|
||||||
|
void InvalidateText(void);
|
||||||
|
void dbp(char *str, ...);
|
||||||
|
void Error(char *str, ...);
|
||||||
|
Expr *AllocExpr(void);
|
||||||
|
void FreeAllExprs(void);
|
||||||
|
|
||||||
|
|
||||||
#include "dsc.h"
|
#include "dsc.h"
|
||||||
#include "sketch.h"
|
#include "sketch.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "expr.h"
|
#include "expr.h"
|
||||||
|
|
||||||
// From the platform-specific code.
|
|
||||||
void InvalidateGraphics(void);
|
|
||||||
void InvalidateText(void);
|
|
||||||
|
|
||||||
// Utility functions that are provided in the platform-independent code.
|
// Utility functions that are provided in the platform-independent code.
|
||||||
void glxVertex3v(Vector u);
|
void glxVertex3v(Vector u);
|
||||||
|
@ -56,14 +58,22 @@ public:
|
||||||
TextWindow TW;
|
TextWindow TW;
|
||||||
GraphicsWindow GW;
|
GraphicsWindow GW;
|
||||||
|
|
||||||
IdList<Group,hGroup> group;
|
// These lists define the sketch, and are edited by the user.
|
||||||
IdList<Request,hRequest> request;
|
IdList<Group,hGroup> group;
|
||||||
IdList<Entity,hEntity> entity;
|
IdList<Request,hRequest> request;
|
||||||
IdList<Point,hPoint> point;
|
IdList<Constraint,hConstraint> constraint;
|
||||||
IdList<Param,hParam> param;
|
|
||||||
|
|
||||||
inline Entity *GetEntity(hEntity h) { return entity.FindById(h); }
|
// These lists are generated automatically when we solve the sketch.
|
||||||
inline Param *GetParam (hParam h) { return param. FindById(h); }
|
IdList<Entity,hEntity> entity;
|
||||||
|
IdList<Point,hPoint> point;
|
||||||
|
IdList<Param,hParam> param;
|
||||||
|
|
||||||
|
inline Constraint *GetConstraint(hConstraint h)
|
||||||
|
{ return constraint.FindById(h); }
|
||||||
|
inline Request *GetRequest(hRequest h) { return request.FindById(h); }
|
||||||
|
inline Entity *GetEntity (hEntity h) { return entity. FindById(h); }
|
||||||
|
inline Param *GetParam (hParam h) { return param. FindById(h); }
|
||||||
|
inline Point *GetPoint (hPoint h) { return point. FindById(h); }
|
||||||
|
|
||||||
hGroup activeGroup;
|
hGroup activeGroup;
|
||||||
|
|
||||||
|
|
19
ui.h
19
ui.h
|
@ -78,12 +78,17 @@ public:
|
||||||
MNU_ZOOM_OUT,
|
MNU_ZOOM_OUT,
|
||||||
MNU_ZOOM_TO_FIT,
|
MNU_ZOOM_TO_FIT,
|
||||||
MNU_ORIENT_ONTO,
|
MNU_ORIENT_ONTO,
|
||||||
|
MNU_LOCK_VIEW,
|
||||||
MNU_UNSELECT_ALL,
|
MNU_UNSELECT_ALL,
|
||||||
|
// Edit
|
||||||
|
MNU_DELETE,
|
||||||
// Request
|
// Request
|
||||||
MNU_DATUM_POINT,
|
MNU_DATUM_POINT,
|
||||||
MNU_LINE_SEGMENT,
|
MNU_LINE_SEGMENT,
|
||||||
|
// Constrain
|
||||||
|
MNU_DISTANCE_DIA,
|
||||||
} MenuId;
|
} MenuId;
|
||||||
typedef void MenuHandler(MenuId id);
|
typedef void MenuHandler(int id);
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int level; // 0 == on menu bar, 1 == one level down
|
int level; // 0 == on menu bar, 1 == one level down
|
||||||
char *label; // or NULL for a separator
|
char *label; // or NULL for a separator
|
||||||
|
@ -92,9 +97,9 @@ public:
|
||||||
MenuHandler *fn;
|
MenuHandler *fn;
|
||||||
} MenuEntry;
|
} MenuEntry;
|
||||||
static const MenuEntry menu[];
|
static const MenuEntry menu[];
|
||||||
static void MenuView(MenuId id);
|
static void MenuView(int id);
|
||||||
static void MenuEdit(MenuId id);
|
static void MenuEdit(int id);
|
||||||
static void MenuRequest(MenuId id);
|
static void MenuRequest(int id);
|
||||||
|
|
||||||
// The width and height (in pixels) of the window.
|
// The width and height (in pixels) of the window.
|
||||||
double width, height;
|
double width, height;
|
||||||
|
@ -111,6 +116,7 @@ public:
|
||||||
Vector projUp;
|
Vector projUp;
|
||||||
Point2d mouse;
|
Point2d mouse;
|
||||||
} orig;
|
} orig;
|
||||||
|
bool viewLocked;
|
||||||
|
|
||||||
void NormalizeProjectionVectors(void);
|
void NormalizeProjectionVectors(void);
|
||||||
Point2d ProjectPoint(Vector p);
|
Point2d ProjectPoint(Vector p);
|
||||||
|
@ -132,6 +138,7 @@ public:
|
||||||
public:
|
public:
|
||||||
hPoint point;
|
hPoint point;
|
||||||
hEntity entity;
|
hEntity entity;
|
||||||
|
hConstraint constraint;
|
||||||
|
|
||||||
void Draw(void);
|
void Draw(void);
|
||||||
|
|
||||||
|
@ -150,6 +157,7 @@ public:
|
||||||
int points;
|
int points;
|
||||||
int entities;
|
int entities;
|
||||||
int csyss;
|
int csyss;
|
||||||
|
int lineSegments;
|
||||||
int n;
|
int n;
|
||||||
} gs;
|
} gs;
|
||||||
void GroupSelection(void);
|
void GroupSelection(void);
|
||||||
|
@ -163,7 +171,8 @@ public:
|
||||||
static void ToggleBool(int link, DWORD v);
|
static void ToggleBool(int link, DWORD v);
|
||||||
static void ToggleAnyDatumShown(int link, DWORD v);
|
static void ToggleAnyDatumShown(int link, DWORD v);
|
||||||
|
|
||||||
void UpdateDraggedPoint(hPoint hp, double mx, double my);
|
void UpdateDraggedPoint(Vector *pos, double mx, double my);
|
||||||
|
void UpdateDraggedHPoint(hPoint hp, double mx, double my);
|
||||||
|
|
||||||
// These are called by the platform-specific code.
|
// These are called by the platform-specific code.
|
||||||
void Paint(int w, int h);
|
void Paint(int w, int h);
|
||||||
|
|
11
util.cpp
11
util.cpp
|
@ -120,7 +120,7 @@ Vector Vector::Normal(int which) {
|
||||||
oops();
|
oops();
|
||||||
}
|
}
|
||||||
|
|
||||||
n = n.ScaledBy(1/n.Magnitude());
|
n = n.WithMagnitude(1);
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -160,6 +160,15 @@ Vector Vector::ScaledBy(double v) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector Vector::WithMagnitude(double v) {
|
||||||
|
double m = Magnitude();
|
||||||
|
if(m < 0.001) {
|
||||||
|
return MakeFrom(v, 0, 0);
|
||||||
|
} else {
|
||||||
|
return ScaledBy(v/Magnitude());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Point2d Point2d::Plus(Point2d b) {
|
Point2d Point2d::Plus(Point2d b) {
|
||||||
Point2d r;
|
Point2d r;
|
||||||
r.x = x + b.x;
|
r.x = x + b.x;
|
||||||
|
|
|
@ -39,7 +39,6 @@ void dbp(char *str, ...)
|
||||||
va_start(f, str);
|
va_start(f, str);
|
||||||
vsprintf(buf, str, f);
|
vsprintf(buf, str, f);
|
||||||
OutputDebugString(buf);
|
OutputDebugString(buf);
|
||||||
OutputDebugString("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Error(char *str, ...)
|
void Error(char *str, ...)
|
||||||
|
@ -300,8 +299,8 @@ static BOOL ProcessKeyDown(WPARAM wParam)
|
||||||
c = wParam;
|
c = wParam;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= 0x100;
|
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= 0x100;
|
||||||
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= 0x200;
|
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= 0x200;
|
||||||
|
|
||||||
for(int i = 0; SS.GW.menu[i].level >= 0; i++) {
|
for(int i = 0; SS.GW.menu[i].level >= 0; i++) {
|
||||||
if(c == SS.GW.menu[i].accel) {
|
if(c == SS.GW.menu[i].accel) {
|
||||||
|
@ -443,6 +442,26 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckMenuById(int id, BOOL checked)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int subMenu = -1;
|
||||||
|
|
||||||
|
for(i = 0; SS.GW.menu[i].level >= 0; i++) {
|
||||||
|
if(SS.GW.menu[i].level == 0) subMenu++;
|
||||||
|
|
||||||
|
if(SS.GW.menu[i].id == id) {
|
||||||
|
if(subMenu < 0) oops();
|
||||||
|
if(subMenu >= (sizeof(SubMenus)/sizeof(SubMenus[0]))) oops();
|
||||||
|
|
||||||
|
CheckMenuItem(SubMenus[subMenu], id,
|
||||||
|
checked ? MF_CHECKED : MF_UNCHECKED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oops();
|
||||||
|
}
|
||||||
|
|
||||||
HMENU CreateGraphicsWindowMenus(void)
|
HMENU CreateGraphicsWindowMenus(void)
|
||||||
{
|
{
|
||||||
HMENU top = CreateMenu();
|
HMENU top = CreateMenu();
|
||||||
|
|
Loading…
Reference in New Issue