Add horizontal and vertical constraints. Those have their own 2d/3d

issues, when the points are not all in the same coordinate system.
All painful, of course. Also add continuous line drawing, and
auto-constraining of line segments as I draw.

[git-p4: depot-paths = "//depot/solvespace/": change = 1683]
This commit is contained in:
Jonathan Westhues 2008-04-22 23:29:19 -08:00
parent 1bf7e3deaf
commit a8001adf33
8 changed files with 260 additions and 44 deletions

View File

@ -5,6 +5,16 @@ hConstraint Constraint::AddConstraint(Constraint *c) {
return c->h;
}
void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
Constraint c;
memset(&c, 0, sizeof(c));
c.group = SS.GW.activeGroup;
c.type = Constraint::POINTS_COINCIDENT;
c.ptA = ptA;
c.ptB = ptB;
SS.constraint.AddAndAssignId(&c);
}
void Constraint::MenuConstrain(int id) {
Constraint c;
memset(&c, 0, sizeof(c));
@ -62,6 +72,48 @@ void Constraint::MenuConstrain(int id) {
AddConstraint(&c);
break;
case GraphicsWindow::MNU_VERTICAL:
case GraphicsWindow::MNU_HORIZONTAL: {
hEntity ha, hb;
if(gs.lineSegments == 1 && gs.n == 1) {
c.entityA = gs.entity[0];
Entity *e = SS.GetEntity(c.entityA);
ha = e->assoc[0];
hb = e->assoc[1];
} else if(gs.points == 2 && gs.n == 2) {
ha = c.ptA = gs.point[0];
hb = c.ptB = gs.point[1];
} else {
Error("Bad selection for horizontal / vertical constraint.");
return;
}
Entity *ea = SS.GetEntity(ha);
Entity *eb = SS.GetEntity(hb);
if(ea->csys.v == Entity::NO_CSYS.v &&
eb->csys.v == Entity::NO_CSYS.v)
{
Error("Horizontal/vertical constraint applies only to "
"entities drawn in a 2d coordinate system.");
return;
}
if(eb->csys.v == SS.GW.activeCsys.v) {
// We are constraining two points in two different csyss; so
// we have two choices for the definitons of the coordinate
// directions. ptA's gets chosen, so make sure that's the
// active csys.
hEntity t = c.ptA;
c.ptA = c.ptB;
c.ptB = t;
}
if(id == GraphicsWindow::MNU_HORIZONTAL) {
c.type = HORIZONTAL;
} else {
c.type = VERTICAL;
}
AddConstraint(&c);
break;
}
case GraphicsWindow::MNU_SOLVE_NOW:
SS.Solve();
return;
@ -157,24 +209,27 @@ void Constraint::Generate(IdList<Equation,hEquation> *l) {
AddEq(l, eab.x, 0);
AddEq(l, eab.y, 1);
AddEq(l, eab.z, 2);
} else if(a->IsPointIn3d() && !b->IsPointIn3d()) {
// One point has 2 DOF, one has 3; write two eqs, on the
// projection of the 3 DOF point into the 2 DOF point plane.
ExprVector p3;
p3 = a->PointGetExprs();
Entity *csy = SS.GetEntity(b->csys);
ExprVector u, v;
csy->Csys2dGetBasisExprs(&u, &v);
AddEq(l, Expr::FromParam(b->param.h[0])->Minus(p3.Dot(u)), 0);
AddEq(l, Expr::FromParam(b->param.h[1])->Minus(p3.Dot(v)), 1);
} else if(a->csys.v == b->csys.v) {
} else if(!(a->IsPointIn3d() || b->IsPointIn3d()) &&
(a->csys.v == b->csys.v))
{
// Both in same csys, nice.
AddEq(l, Expr::FromParam(a->param.h[0])->Minus(
Expr::FromParam(b->param.h[0])), 0);
AddEq(l, Expr::FromParam(a->param.h[1])->Minus(
Expr::FromParam(b->param.h[1])), 1);
} else {
oops();
// Either two 2 DOF points in different planes, or one
// 3 DOF point and one 2 DOF point. Either way, write two
// equations on the projection of a into b's plane.
ExprVector p3;
p3 = a->PointGetExprs();
Entity *csy = SS.GetEntity(b->csys);
ExprVector offset = csy->Csys2dGetOffsetExprs();
p3 = p3.Minus(offset);
ExprVector u, v;
csy->Csys2dGetBasisExprs(&u, &v);
AddEq(l, Expr::FromParam(b->param.h[0])->Minus(p3.Dot(u)), 0);
AddEq(l, Expr::FromParam(b->param.h[1])->Minus(p3.Dot(v)), 1);
}
break;
}
@ -188,6 +243,41 @@ void Constraint::Generate(IdList<Equation,hEquation> *l) {
break;
}
case HORIZONTAL:
case VERTICAL: {
hEntity ha, hb;
if(entityA.v) {
Entity *e = SS.GetEntity(entityA);
ha = e->assoc[0];
hb = e->assoc[1];
} else {
ha = ptA;
hb = ptB;
}
Entity *a = SS.GetEntity(ha);
Entity *b = SS.GetEntity(hb);
if(a->csys.v == Entity::NO_CSYS.v) {
Entity *t = a;
a = b;
b = t;
}
if(a->csys.v == b->csys.v) {
int i = (type == HORIZONTAL) ? 1 : 0;
AddEq(l, Expr::FromParam(a->param.h[i])->Minus(
Expr::FromParam(b->param.h[i])), 0);
} else {
Entity *csy = SS.GetEntity(a->csys);
ExprVector u, v;
csy->Csys2dGetBasisExprs(&u, &v);
ExprVector norm = (type == HORIZONTAL) ? v : u;
ExprVector pa = a->PointGetExprs();
ExprVector pb = b->PointGetExprs();
AddEq(l, (pa.Minus(pb)).Dot(norm), 0);
}
break;
}
default: oops();
}
}

View File

@ -33,7 +33,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
Vector gu = SS.GW.projUp;
Vector gn = gr.Cross(gu);
glxColor(1, 0.3, 1);
glxColor(1, 0.2, 1);
switch(type) {
case PT_PT_DISTANCE: {
Vector ap = SS.GetEntity(ptA)->PointGetCoords();
@ -125,6 +125,55 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
break;
}
case HORIZONTAL:
case VERTICAL:
if(entityA.v) {
Entity *e = SS.GetEntity(entityA);
Vector a = SS.GetEntity(e->assoc[0])->PointGetCoords();
Vector b = SS.GetEntity(e->assoc[1])->PointGetCoords();
Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5));
if(dogd.drawing) {
glPushMatrix();
glxTranslatev(m);
glxOntoCsys(gr, gu);
glxWriteText(type == HORIZONTAL ? "H" : "V");
glPopMatrix();
} else {
Point2d ref = SS.GW.ProjectPoint(m);
dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10);
}
} else {
Vector a = SS.GetEntity(ptA)->PointGetCoords();
Vector b = SS.GetEntity(ptB)->PointGetCoords();
Entity *csy = SS.GetEntity(SS.GetEntity(ptA)->csys);
Vector cn = csy->Csys2dGetNormalVector();
int i;
for(i = 0; i < 2; i++) {
Vector o = (i == 0) ? a : b;
Vector d = (i == 0) ? a.Minus(b) : b.Minus(a);
Vector dp = cn.Cross(d);
d = d.WithMagnitude(14/SS.GW.scale);
Vector c = o.Minus(d);
LineDrawOrGetDistance(o, c);
d = d.WithMagnitude(3/SS.GW.scale);
dp = dp.WithMagnitude(2/SS.GW.scale);
if(dogd.drawing) {
glBegin(GL_QUADS);
glxVertex3v((c.Plus(d)).Plus(dp));
glxVertex3v((c.Minus(d)).Plus(dp));
glxVertex3v((c.Minus(d)).Minus(dp));
glxVertex3v((c.Plus(d)).Minus(dp));
glEnd();
} else {
Point2d ref = SS.GW.ProjectPoint(c);
dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-6);
}
}
}
break;
default: oops();
}
}

View File

@ -16,6 +16,12 @@ void Entity::Csys2dGetBasisVectors(Vector *u, Vector *v) {
*v = quat.RotationV();
}
Vector Entity::Csys2dGetNormalVector(void) {
Vector u, v;
Csys2dGetBasisVectors(&u, &v);
return u.Cross(v);
}
void Entity::Csys2dGetBasisExprs(ExprVector *u, ExprVector *v) {
Expr *a = Expr::FromParam(param.h[0]);
Expr *b = Expr::FromParam(param.h[1]);
@ -47,6 +53,10 @@ void Entity::Csys2dGetBasisExprs(ExprVector *u, ExprVector *v) {
v->z = (v->z)->Plus(two->Times(c->Times(d)));
}
ExprVector Entity::Csys2dGetOffsetExprs(void) {
return SS.GetEntity(assoc[0])->PointGetExprs();
}
bool Entity::HasPlane(void) {
switch(type) {
case CSYS_2D:

View File

@ -73,8 +73,8 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "A&ngle\tShift+N", 0, 'N'|S, NULL },
{ 1, "Other S&upplementary Angle\tShift+U", 0, 'U'|S, NULL },
{ 1, NULL, 0, NULL },
{ 1, "&Horizontal\tShift+H", 0, 'H'|S, NULL },
{ 1, "&Vertical\tShift+V", 0, 'V'|S, NULL },
{ 1, "&Horizontal\tShift+H", MNU_HORIZONTAL, 'H'|S, mCon },
{ 1, "&Vertical\tShift+V", MNU_VERTICAL, 'V'|S, mCon },
{ 1, NULL, 0, NULL },
{ 1, "&On Point / Curve / Plane\tShift+O", MNU_ON_ENTITY, 'O'|S, mCon },
{ 1, "E&qual Length / Radius\tShift+Q", MNU_EQUAL, 'Q'|S, mCon },
@ -264,6 +264,8 @@ void GraphicsWindow::MenuEdit(int id) {
SS.GW.ClearSelection();
SS.GW.pendingOperation = 0;
SS.GW.pendingDescription = NULL;
SS.GW.pendingPoint.v = 0;
SS.GW.pendingConstraint.v = 0;
SS.TW.ScreenNavigation('h', 0);
SS.TW.Show();
break;
@ -360,7 +362,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
double dy = (y - orig.mouse.y) / scale;
// When the view is locked, permit only translation (pan).
if(shiftDown || viewLocked) {
if(!(shiftDown || ctrlDown) || viewLocked) {
offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x;
offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y;
offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z;
@ -404,7 +406,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
// Start dragging this point.
ClearSelection();
pendingPoint = hover.entity;
pendingOperation = PENDING_OPERATION_DRAGGING_POINT;
pendingOperation = DRAGGING_POINT;
}
} else if(hover.constraint.v &&
SS.GetConstraint(hover.constraint)->HasLabel())
@ -412,29 +414,28 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
if(dm > dmt) {
ClearSelection();
pendingConstraint = hover.constraint;
pendingOperation = PENDING_OPERATION_DRAGGING_CONSTRAINT;
pendingOperation = DRAGGING_CONSTRAINT;
}
}
} else if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT ||
pendingOperation == PENDING_OPERATION_DRAGGING_NEW_POINT)
} else if(pendingOperation == DRAGGING_POINT ||
pendingOperation == DRAGGING_NEW_POINT ||
pendingOperation == DRAGGING_NEW_LINE_POINT)
{
UpdateDraggedEntity(pendingPoint, x, y);
} else if(pendingOperation == PENDING_OPERATION_DRAGGING_CONSTRAINT) {
} else if(pendingOperation == DRAGGING_CONSTRAINT) {
Constraint *c = SS.constraint.FindById(pendingConstraint);
UpdateDraggedPoint(&(c->disp.offset), x, y);
}
} else {
// No buttons pressed.
if(pendingOperation == PENDING_OPERATION_DRAGGING_NEW_POINT) {
if(pendingOperation == DRAGGING_NEW_POINT ||
pendingOperation == DRAGGING_NEW_LINE_POINT)
{
UpdateDraggedEntity(pendingPoint, x, y);
HitTestMakeSelection(mp);
} else {
// Do our usual hit testing, for the selection.
Selection s;
HitTestMakeSelection(mp, &s);
if(!s.Equals(&hover)) {
hover = s;
InvalidateGraphics();
}
HitTestMakeSelection(mp);
}
}
}
@ -457,18 +458,22 @@ void GraphicsWindow::Selection::Draw(void) {
if(constraint.v) SS.GetConstraint(constraint)->Draw();
}
void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) {
void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
int i;
double d, dmin = 1e12;
memset(dest, 0, sizeof(*dest));
Selection s;
memset(&s, 0, sizeof(s));
// Do the entities
for(i = 0; i < SS.entity.n; i++) {
d = SS.entity.elem[i].GetDistance(mp);
Entity *e = &(SS.entity.elem[i]);
// Don't hover whatever's being dragged.
if(e->h.request().v == pendingPoint.request().v) continue;
d = e->GetDistance(mp);
if(d < 10 && d < dmin) {
memset(dest, 0, sizeof(*dest));
dest->entity = SS.entity.elem[i].h;
memset(&s, 0, sizeof(s));
s.entity = e->h;
dmin = d;
}
}
@ -477,11 +482,16 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) {
for(i = 0; i < SS.constraint.n; i++) {
d = SS.constraint.elem[i].GetDistance(mp);
if(d < 10 && d < dmin) {
memset(dest, 0, sizeof(*dest));
dest->constraint = SS.constraint.elem[i].h;
memset(&s, 0, sizeof(s));
s.constraint = SS.constraint.elem[i].h;
dmin = d;
}
}
if(!s.Equals(&hover)) {
hover = s;
InvalidateGraphics();
}
}
void GraphicsWindow::ClearSelection(void) {
@ -565,17 +575,41 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
hr = AddRequest(Request::LINE_SEGMENT);
SS.GetEntity(hr.entity(1))->PointForceTo(v);
pendingOperation = PENDING_OPERATION_DRAGGING_NEW_POINT;
pendingOperation = DRAGGING_NEW_LINE_POINT;
pendingPoint = hr.entity(2);
pendingDescription = "click to place next point of line";
SS.GetEntity(pendingPoint)->PointForceTo(v);
break;
case PENDING_OPERATION_DRAGGING_NEW_POINT:
case DRAGGING_NEW_POINT:
// The MouseMoved event has already dragged it under the cursor.
pendingOperation = 0;
pendingPoint.v = 0;
break;
case DRAGGING_NEW_LINE_POINT: {
if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) {
Constraint::ConstrainCoincident(pendingPoint, hover.entity);
pendingOperation = 0;
pendingPoint.v = 0;
break;
}
// Create a new line segment, so that we continue drawing.
hRequest hr = AddRequest(Request::LINE_SEGMENT);
SS.GetEntity(hr.entity(1))->PointForceTo(v);
// Constrain the line segments to share an endpoint
Constraint::ConstrainCoincident(pendingPoint, hr.entity(1));
// And drag an endpoint of the new line segment
pendingOperation = DRAGGING_NEW_LINE_POINT;
pendingPoint = hr.entity(2);
pendingDescription = "click to place next point of next line";
SS.GetEntity(pendingPoint)->PointForceTo(v);
break;
}
case 0:
default: {
pendingOperation = 0;
@ -609,8 +643,8 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
void GraphicsWindow::MouseLeftUp(double mx, double my) {
switch(pendingOperation) {
case PENDING_OPERATION_DRAGGING_POINT:
case PENDING_OPERATION_DRAGGING_CONSTRAINT:
case DRAGGING_POINT:
case DRAGGING_CONSTRAINT:
pendingOperation = 0;
pendingPoint.v = 0;
pendingConstraint.v = 0;

21
polygon.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef __POLYGON_H
#define __POLYGON_H
class SPolyhedron {
public:
};
class SPolygon {
public:
};
class SContour {
public:
};
class SEdgeList {
public:
};
#endif

View File

@ -58,9 +58,13 @@ public:
int solveOrder;
bool solved;
bool visible;
NameStr name;
char *DescriptionString(void);
SPolygon GetPolygon(void);
};
@ -140,7 +144,9 @@ public:
// Applies only for a CSYS_2D type
void Csys2dGetBasisVectors(Vector *u, Vector *v);
Vector Csys2dGetNormalVector(void);
void Csys2dGetBasisExprs(ExprVector *u, ExprVector *v);
ExprVector Csys2dGetOffsetExprs(void);
bool IsPoint(void);
bool IsPointIn3d(void);
@ -260,6 +266,8 @@ public:
void ModifyToSatisfy(void);
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
static Expr *Distance(hEntity pa, hEntity pb);
static void ConstrainCoincident(hEntity ptA, hEntity ptB);
};
class hEquation {

View File

@ -56,6 +56,7 @@ void MemFree(void *p);
#include "dsc.h"
#include "polygon.h"
#include "sketch.h"
#include "ui.h"
#include "expr.h"

11
ui.h
View File

@ -101,6 +101,8 @@ public:
MNU_DISTANCE_DIA,
MNU_EQUAL,
MNU_ON_ENTITY,
MNU_HORIZONTAL,
MNU_VERTICAL,
MNU_SOLVE_NOW,
} MenuId;
typedef void MenuHandler(int id);
@ -148,9 +150,10 @@ public:
// Operations that must be completed by doing something with the mouse
// are noted here.
static const int PENDING_OPERATION_DRAGGING_POINT = 0x0f000000;
static const int PENDING_OPERATION_DRAGGING_NEW_POINT = 0x0f000001;
static const int PENDING_OPERATION_DRAGGING_CONSTRAINT = 0x0f000002;
static const int DRAGGING_POINT = 0x0f000000;
static const int DRAGGING_NEW_POINT = 0x0f000001;
static const int DRAGGING_NEW_LINE_POINT = 0x0f000002;
static const int DRAGGING_CONSTRAINT = 0x0f000003;
hEntity pendingPoint;
hConstraint pendingConstraint;
int pendingOperation;
@ -175,7 +178,7 @@ public:
Selection hover;
static const int MAX_SELECTED = 32;
Selection selection[MAX_SELECTED];
void HitTestMakeSelection(Point2d mp, Selection *dest);
void HitTestMakeSelection(Point2d mp);
void ClearSelection(void);
struct {
hEntity point[MAX_SELECTED];