Some graphics tweaks, to the order in which stuff gets drawn, to

determine what goes in front (e.g. put a drawn line in front of the
reference plane, even if the z order would want the opposite), and
some tweaks to the mouse behaviour, and a function to modify
constraints like dimensions so that the are initially satisfied.

[git-p4: depot-paths = "//depot/solvespace/": change = 1681]
This commit is contained in:
Jonathan Westhues 2008-04-22 02:53:42 -08:00
parent 1f77024771
commit fa71238def
7 changed files with 158 additions and 57 deletions

View File

@ -29,7 +29,8 @@ void Constraint::MenuConstrain(int id) {
return; return;
} }
c.disp.offset = Vector::MakeFrom(50, 50, 50); c.disp.offset = Vector::MakeFrom(50, 50, 50);
c.exprA = Expr::FromString("300")->DeepCopyKeep(); c.exprA = Expr::FromString("0")->DeepCopyKeep();
c.ModifyToSatisfy();
AddConstraint(&c); AddConstraint(&c);
break; break;
@ -103,6 +104,24 @@ Expr *Constraint::Distance(hEntity hpa, hEntity hpb) {
return (dx2->Plus(dy2->Plus(dz2)))->Sqrt(); return (dx2->Plus(dy2->Plus(dz2)))->Sqrt();
} }
void Constraint::ModifyToSatisfy(void) {
IdList<Equation,hEquation> l;
// An uninit IdList could lead us to free some random address, bad.
memset(&l, 0, sizeof(l));
Generate(&l);
if(l.n != 1) oops();
// These equations are written in the form f(...) - d = 0, where
// d is the value of the exprA.
double v = (l.elem[0].e)->Eval();
double nd = exprA->Eval() + v;
Expr::FreeKeep(&exprA);
exprA = Expr::FromConstant(nd)->DeepCopyKeep();
l.Clear();
}
void Constraint::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) { void Constraint::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) {
Equation eq; Equation eq;
eq.e = expr; eq.e = expr;

View File

@ -67,27 +67,40 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
} }
case POINTS_COINCIDENT: { case POINTS_COINCIDENT: {
// It's impossible to select this constraint on the drawing; if(!dogd.drawing) {
// have to do it from the text window. for(int i = 0; i < 2; i++) {
if(!dogd.drawing) break; Vector p = SS.GetEntity(i == 0 ? ptA : ptB)->
double s = 2; PointGetCoords();
Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); Point2d pp = SS.GW.ProjectPoint(p);
Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); // The point is selected within a radius of 7, from the
for(int i = 0; i < 2; i++) { // same center; so if the point is visible, then this
Vector p = SS.GetEntity(i == 0 ? ptA : ptB)->PointGetCoords(); // constraint cannot be selected. But that's okay.
glxColor(0.4, 0, 0.4); dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 3);
glBegin(GL_QUADS); }
glxVertex3v(p.Plus (r).Plus (d)); break;
glxVertex3v(p.Plus (r).Minus(d)); }
glxVertex3v(p.Minus(r).Minus(d));
glxVertex3v(p.Minus(r).Plus (d)); for(int a = 0; a < 2; a++) {
glEnd(); Vector r = SS.GW.projRight.ScaledBy((a+1)/SS.GW.scale);
Vector d = SS.GW.projUp.ScaledBy((2-a)/SS.GW.scale);
for(int i = 0; i < 2; i++) {
Vector p = SS.GetEntity(i == 0 ? ptA : ptB)->
PointGetCoords();
glxColor(0.4, 0, 0.4);
glBegin(GL_QUADS);
glxVertex3v(p.Plus (r).Plus (d));
glxVertex3v(p.Plus (r).Minus(d));
glxVertex3v(p.Minus(r).Minus(d));
glxVertex3v(p.Minus(r).Plus (d));
glEnd();
}
} }
break; break;
} }
case PT_IN_PLANE: { case PT_IN_PLANE: {
double s = 6; double s = 5;
Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale);
Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale);
Vector p = SS.GetEntity(ptA)->PointGetCoords(); Vector p = SS.GetEntity(ptA)->PointGetCoords();

View File

@ -191,9 +191,9 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) {
} }
} }
void Entity::Draw(void) { void Entity::Draw(int order) {
dogd.drawing = true; dogd.drawing = true;
DrawOrGetDistance(); DrawOrGetDistance(order);
} }
double Entity::GetDistance(Point2d mp) { double Entity::GetDistance(Point2d mp) {
@ -201,17 +201,18 @@ double Entity::GetDistance(Point2d mp) {
dogd.mp = mp; dogd.mp = mp;
dogd.dmin = 1e12; dogd.dmin = 1e12;
DrawOrGetDistance(); DrawOrGetDistance(-1);
return dogd.dmin; return dogd.dmin;
} }
void Entity::DrawOrGetDistance(void) { void Entity::DrawOrGetDistance(int order) {
glxColor(1, 1, 1); glxColor(1, 1, 1);
switch(type) { switch(type) {
case POINT_IN_3D: case POINT_IN_3D:
case POINT_IN_2D: { case POINT_IN_2D: {
if(order >= 0 && order != 2) break;
if(!SS.GW.showPoints) break; if(!SS.GW.showPoints) break;
Entity *isfor = SS.GetEntity(h.request().entity(0)); Entity *isfor = SS.GetEntity(h.request().entity(0));
@ -220,25 +221,28 @@ void Entity::DrawOrGetDistance(void) {
Vector v = PointGetCoords(); Vector v = PointGetCoords();
if(dogd.drawing) { if(dogd.drawing) {
double s = 4; double s = 3;
Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale); Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale);
Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale); Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale);
glxColor(0, 0.8, 0); glxColor(0, 0.8, 0);
glDisable(GL_LINE_SMOOTH);
glBegin(GL_QUADS); glBegin(GL_QUADS);
glxVertex3v(v.Plus (r).Plus (d)); glxVertex3v(v.Plus (r).Plus (d));
glxVertex3v(v.Plus (r).Minus(d)); glxVertex3v(v.Plus (r).Minus(d));
glxVertex3v(v.Minus(r).Minus(d)); glxVertex3v(v.Minus(r).Minus(d));
glxVertex3v(v.Minus(r).Plus (d)); glxVertex3v(v.Minus(r).Plus (d));
glEnd(); glEnd();
glEnable(GL_LINE_SMOOTH);
} else { } else {
Point2d pp = SS.GW.ProjectPoint(v); Point2d pp = SS.GW.ProjectPoint(v);
dogd.dmin = pp.DistanceTo(dogd.mp) - 8; dogd.dmin = pp.DistanceTo(dogd.mp) - 7;
} }
break; break;
} }
case CSYS_2D: { case CSYS_2D: {
if(order >= 0 && order != 0) break;
if(!SS.GW.show2dCsyss) break; if(!SS.GW.show2dCsyss) break;
Vector p; Vector p;
@ -274,6 +278,7 @@ void Entity::DrawOrGetDistance(void) {
} }
case LINE_SEGMENT: { case LINE_SEGMENT: {
if(order >= 0 && order != 1) break;
Vector a = SS.GetEntity(assoc[0])->PointGetCoords(); Vector a = SS.GetEntity(assoc[0])->PointGetCoords();
Vector b = SS.GetEntity(assoc[1])->PointGetCoords(); Vector b = SS.GetEntity(assoc[1])->PointGetCoords();
LineDrawOrGetDistance(a, b); LineDrawOrGetDistance(a, b);

View File

@ -37,6 +37,17 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "Dimensions in &Inches", 0, NULL }, { 1, "Dimensions in &Inches", 0, NULL },
{ 1, "Dimensions in &Millimeters", 0, NULL }, { 1, "Dimensions in &Millimeters", 0, NULL },
{ 0, "&Group", 0, 0, NULL },
{ 1, "New &Drawing Group", 0, 0, NULL },
{ 1, NULL, 0, NULL },
{ 1, "New Step and Repeat &Translating", 0, 0, NULL },
{ 1, "New Step and Repeat &Rotating", 0, 0, NULL },
{ 1, NULL, 0, 0, NULL },
{ 1, "New Extrusion", 0, 0, NULL },
{ 1, NULL, 0, 0, NULL },
{ 1, "New Boolean Difference", 0, 0, NULL },
{ 1, "New Boolean Union", 0, 0, NULL },
{ 0, "&Request", 0, NULL }, { 0, "&Request", 0, NULL },
{ 1, "Dra&w in 2d Coordinate System\tW", MNU_SEL_CSYS, 'W', mReq }, { 1, "Dra&w in 2d Coordinate System\tW", MNU_SEL_CSYS, 'W', mReq },
{ 1, "Draw Anywhere in 3d\tQ", MNU_NO_CSYS, 'Q', mReq }, { 1, "Draw Anywhere in 3d\tQ", MNU_NO_CSYS, 'Q', mReq },
@ -47,15 +58,11 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "2d Coordinate S&ystem\tY", 0, 'Y', mReq }, { 1, "2d Coordinate S&ystem\tY", 0, 'Y', mReq },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
{ 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq }, { 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq },
{ 1, "&Rectangle\tR", MNU_RECTANGLE, 'R', mReq },
{ 1, "&Circle\tC", 0, 'C', mReq }, { 1, "&Circle\tC", 0, 'C', mReq },
{ 1, "&Arc of a Circle\tA", 0, 'A', mReq }, { 1, "&Arc of a Circle\tA", 0, 'A', mReq },
{ 1, "&Cubic Segment\t3", 0, '3', mReq }, { 1, "&Cubic Segment\t3", 0, '3', mReq },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
{ 1, "Boolean &Union\tU", 0, 'U', mReq },
{ 1, "Boolean &Difference\tD", 0, 'D', mReq },
{ 1, "Step and Repeat &Translate\tT", 0, 'T', mReq },
{ 1, "Step and Repeat &Rotate\tR", 0, 'R', mReq },
{ 1, NULL, 0, NULL },
{ 1, "Sym&bolic Variable\tB", 0, 'B', mReq }, { 1, "Sym&bolic Variable\tB", 0, 'B', mReq },
{ 1, "&Import From File...\tI", 0, 'I', mReq }, { 1, "&Import From File...\tI", 0, 'I', mReq },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
@ -223,7 +230,9 @@ void GraphicsWindow::EnsureValidActives(void) {
} }
if(change) SS.TW.Show(); if(change) SS.TW.Show();
EnableMenuById(MNU_NO_CSYS, (activeCsys.v != Entity::NO_CSYS.v)); bool in3d = (activeCsys.v == Entity::NO_CSYS.v);
CheckMenuById(MNU_NO_CSYS, in3d);
CheckMenuById(MNU_SEL_CSYS, !in3d);
} }
void GraphicsWindow::MenuEdit(int id) { void GraphicsWindow::MenuEdit(int id) {
@ -359,21 +368,41 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
} else if(leftDown) { } else if(leftDown) {
// We are left-dragging. This is often used to drag points, or // We are left-dragging. This is often used to drag points, or
// constraint labels. // constraint labels.
if(hover.entity.v && double dm = orig.mouse.DistanceTo(mp);
SS.GetEntity(hover.entity)->IsPoint() && // Don't start a drag until we've moved some threshold distance from
!SS.GetEntity(hover.entity)->PointIsFromReferences()) // the mouse-down point, to avoid accidental drags.
double dmt = 3;
if(pendingOperation == 0) {
if(hover.entity.v &&
SS.GetEntity(hover.entity)->IsPoint() &&
!SS.GetEntity(hover.entity)->PointIsFromReferences())
{
if(dm > dmt) {
// Start dragging this point.
ClearSelection();
pendingPoint = hover.entity;
pendingOperation = PENDING_OPERATION_DRAGGING_POINT;
}
} else if(hover.constraint.v &&
SS.GetConstraint(hover.constraint)->HasLabel())
{
if(dm > dmt) {
ClearSelection();
pendingConstraint = hover.constraint;
pendingOperation = PENDING_OPERATION_DRAGGING_CONSTRAINT;
}
}
} else if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT ||
pendingOperation == PENDING_OPERATION_DRAGGING_NEW_POINT)
{ {
ClearSelection(); UpdateDraggedEntity(pendingPoint, x, y);
UpdateDraggedEntity(hover.entity, x, y); } else if(pendingOperation == PENDING_OPERATION_DRAGGING_CONSTRAINT) {
} else if(hover.constraint.v && Constraint *c = SS.constraint.FindById(pendingConstraint);
SS.GetConstraint(hover.constraint)->HasLabel())
{
ClearSelection();
Constraint *c = SS.constraint.FindById(hover.constraint);
UpdateDraggedPoint(&(c->disp.offset), x, y); UpdateDraggedPoint(&(c->disp.offset), x, y);
} }
} else { } else {
if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT) { // No buttons pressed.
if(pendingOperation == PENDING_OPERATION_DRAGGING_NEW_POINT) {
UpdateDraggedEntity(pendingPoint, x, y); UpdateDraggedEntity(pendingPoint, x, y);
} else { } else {
// Do our usual hit testing, for the selection. // Do our usual hit testing, for the selection.
@ -401,7 +430,7 @@ void GraphicsWindow::Selection::Clear(void) {
entity.v = constraint.v = 0; entity.v = constraint.v = 0;
} }
void GraphicsWindow::Selection::Draw(void) { void GraphicsWindow::Selection::Draw(void) {
if(entity.v) SS.GetEntity (entity )->Draw(); if(entity.v) SS.GetEntity (entity )->Draw(-1);
if(constraint.v) SS.GetConstraint(constraint)->Draw(); if(constraint.v) SS.GetConstraint(constraint)->Draw();
} }
@ -499,21 +528,27 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
hRequest hr; hRequest hr;
switch(pendingOperation) { switch(pendingOperation) {
case MNU_DATUM_POINT: case MNU_DATUM_POINT:
ClearSelection(); hover.Clear();
hr = AddRequest(Request::DATUM_POINT); hr = AddRequest(Request::DATUM_POINT);
SS.GetEntity(hr.entity(0))->PointForceTo(v); SS.GetEntity(hr.entity(0))->PointForceTo(v);
pendingOperation = 0; pendingOperation = 0;
break; break;
case MNU_LINE_SEGMENT: case MNU_LINE_SEGMENT:
ClearSelection(); hover.Clear();
hr = AddRequest(Request::LINE_SEGMENT); hr = AddRequest(Request::LINE_SEGMENT);
SS.GetEntity(hr.entity(1))->PointForceTo(v); SS.GetEntity(hr.entity(1))->PointForceTo(v);
pendingOperation = PENDING_OPERATION_DRAGGING_POINT;
pendingOperation = PENDING_OPERATION_DRAGGING_NEW_POINT;
pendingPoint = hr.entity(2); pendingPoint = hr.entity(2);
pendingDescription = "click to place next point of line"; pendingDescription = "click to place next point of line";
SS.GetEntity(pendingPoint)->PointForceTo(v); SS.GetEntity(pendingPoint)->PointForceTo(v);
break; break;
case PENDING_OPERATION_DRAGGING_POINT: case PENDING_OPERATION_DRAGGING_NEW_POINT:
// The MouseMoved event has already dragged it under the cursor. // The MouseMoved event has already dragged it under the cursor.
pendingOperation = 0; pendingOperation = 0;
break; break;
@ -549,6 +584,20 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
InvalidateGraphics(); InvalidateGraphics();
} }
void GraphicsWindow::MouseLeftUp(double mx, double my) {
switch(pendingOperation) {
case PENDING_OPERATION_DRAGGING_POINT:
case PENDING_OPERATION_DRAGGING_CONSTRAINT:
pendingOperation = 0;
pendingPoint.v = 0;
pendingConstraint.v = 0;
break;
default:
break; // do nothing
}
}
void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
if(GraphicsEditControlIsVisible()) return; if(GraphicsEditControlIsVisible()) return;
@ -649,16 +698,22 @@ void GraphicsWindow::Paint(int w, int h) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int i; int i, a;
// First, draw the entire scene. // First, draw the entire scene. We don't necessarily want to draw
// things with normal z-buffering behaviour; e.g. we always want to
// draw a line segment in front of a reference. So we have three draw
// levels, and only the first gets normal depth testing.
glxUnlockColor(); glxUnlockColor();
for(i = 0; i < SS.entity.n; i++) { for(a = 0; a <= 2; a++) {
SS.entity.elem[i].Draw(); // Three levels: 0 least prominent (e.g. a reference csys), 1 is
// middle (e.g. line segment), 2 is always in front (e.g. point).
if(a == 1) glDisable(GL_DEPTH_TEST);
for(i = 0; i < SS.entity.n; i++) {
SS.entity.elem[i].Draw(a);
}
} }
// Want the constraints to get drawn in front, so disable depth test.
glDisable(GL_DEPTH_TEST);
// Draw the constraints // Draw the constraints
for(i = 0; i < SS.constraint.n; i++) { for(i = 0; i < SS.constraint.n; i++) {
SS.constraint.elem[i].Draw(); SS.constraint.elem[i].Draw();

View File

@ -164,8 +164,8 @@ public:
double dmin; double dmin;
} dogd; } dogd;
void LineDrawOrGetDistance(Vector a, Vector b); void LineDrawOrGetDistance(Vector a, Vector b);
void DrawOrGetDistance(void); void DrawOrGetDistance(int order);
void Draw(void); void Draw(int order);
double GetDistance(Point2d mp); double GetDistance(Point2d mp);
char *DescriptionString(void); char *DescriptionString(void);
@ -257,6 +257,7 @@ public:
void Generate(IdList<Equation,hEquation> *l); void Generate(IdList<Equation,hEquation> *l);
// Some helpers when generating symbolic constraint equations // Some helpers when generating symbolic constraint equations
void ModifyToSatisfy(void);
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index); void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
static Expr *Distance(hEntity pa, hEntity pb); static Expr *Distance(hEntity pa, hEntity pb);
}; };

13
ui.h
View File

@ -94,6 +94,7 @@ public:
MNU_NO_CSYS, MNU_NO_CSYS,
MNU_DATUM_POINT, MNU_DATUM_POINT,
MNU_LINE_SEGMENT, MNU_LINE_SEGMENT,
MNU_RECTANGLE,
// Constrain // Constrain
MNU_DISTANCE_DIA, MNU_DISTANCE_DIA,
MNU_EQUAL, MNU_EQUAL,
@ -139,10 +140,13 @@ public:
// Operations that must be completed by doing something with the mouse // Operations that must be completed by doing something with the mouse
// are noted here. // are noted here.
static const int PENDING_OPERATION_DRAGGING_POINT = 0x0f000000; static const int PENDING_OPERATION_DRAGGING_POINT = 0x0f000000;
hEntity pendingPoint; static const int PENDING_OPERATION_DRAGGING_NEW_POINT = 0x0f000001;
int pendingOperation; static const int PENDING_OPERATION_DRAGGING_CONSTRAINT = 0x0f000002;
char *pendingDescription; hEntity pendingPoint;
hConstraint pendingConstraint;
int pendingOperation;
char *pendingDescription;
hRequest AddRequest(int type); hRequest AddRequest(int type);
// The constraint that is being edited with the on-screen textbox. // The constraint that is being edited with the on-screen textbox.
@ -194,6 +198,7 @@ public:
void MouseMoved(double x, double y, bool leftDown, bool middleDown, void MouseMoved(double x, double y, bool leftDown, bool middleDown,
bool rightDown, bool shiftDown, bool ctrlDown); bool rightDown, bool shiftDown, bool ctrlDown);
void MouseLeftDown(double x, double y); void MouseLeftDown(double x, double y);
void MouseLeftUp(double x, double y);
void MouseLeftDoubleClick(double x, double y); void MouseLeftDoubleClick(double x, double y);
void MouseMiddleDown(double x, double y); void MouseMiddleDown(double x, double y);
void MouseScroll(double x, double y, int delta); void MouseScroll(double x, double y, int delta);

View File

@ -13,8 +13,8 @@
#include "freeze.h" #include "freeze.h"
#define MIN_COLS 42 #define MIN_COLS 42
#define TEXT_HEIGHT 19 #define TEXT_HEIGHT 18
#define TEXT_WIDTH 10 #define TEXT_WIDTH 9
HINSTANCE Instance; HINSTANCE Instance;
@ -446,6 +446,7 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
case WM_MOUSEMOVE: case WM_MOUSEMOVE:
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK: case WM_LBUTTONDBLCLK:
case WM_MBUTTONDOWN: { case WM_MBUTTONDOWN: {
int x = LOWORD(lParam); int x = LOWORD(lParam);
@ -461,6 +462,8 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
if(msg == WM_LBUTTONDOWN) { if(msg == WM_LBUTTONDOWN) {
SS.GW.MouseLeftDown(x, y); SS.GW.MouseLeftDown(x, y);
} else if(msg == WM_LBUTTONUP) {
SS.GW.MouseLeftUp(x, y);
} else if(msg == WM_LBUTTONDBLCLK) { } else if(msg == WM_LBUTTONDBLCLK) {
SS.GW.MouseLeftDoubleClick(x, y); SS.GW.MouseLeftDoubleClick(x, y);
} else if(msg == WM_MBUTTONDOWN) { } else if(msg == WM_MBUTTONDOWN) {