Add cubics, and tweak mouse handling code.

[git-p4: depot-paths = "//depot/solvespace/": change = 1689]
solver
Jonathan Westhues 2008-04-25 04:07:17 -08:00
parent c934737d9e
commit 5bc3738ec4
5 changed files with 110 additions and 52 deletions

View File

@ -321,6 +321,26 @@ void Entity::DrawOrGetDistance(int order) {
break; break;
} }
case CUBIC: {
Vector p0 = SS.GetEntity(assoc[0])->PointGetCoords();
Vector p1 = SS.GetEntity(assoc[1])->PointGetCoords();
Vector p2 = SS.GetEntity(assoc[2])->PointGetCoords();
Vector p3 = SS.GetEntity(assoc[3])->PointGetCoords();
int i, n = 20;
Vector prev = p0;
for(i = 1; i <= n; i++) {
double t = ((double)i)/n;
Vector p =
(p0.ScaledBy((1 - t)*(1 - t)*(1 - t))).Plus(
(p1.ScaledBy(3*t*(1 - t)*(1 - t))).Plus(
(p2.ScaledBy(3*t*t*(1 - t))).Plus(
(p3.ScaledBy(t*t*t)))));
LineDrawOrGetDistanceOrEdge(prev, p);
prev = p;
}
break;
}
default: default:
oops(); oops();
} }

View File

@ -58,7 +58,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "&Rectangle\tR", MNU_RECTANGLE, 'R', 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", MNU_CUBIC, '3', mReq },
{ 1, NULL, 0, NULL }, { 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 },
@ -320,6 +320,7 @@ void GraphicsWindow::MenuRequest(int 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;
case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c; case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c;
case MNU_CUBIC: s = "click first point of cubic segment"; goto c;
c: c:
SS.GW.pendingOperation = id; SS.GW.pendingOperation = id;
SS.GW.pendingDescription = s; SS.GW.pendingDescription = s;
@ -388,53 +389,55 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
orig.mouse.y = y; orig.mouse.y = y;
InvalidateGraphics(); InvalidateGraphics();
} else if(leftDown) { return;
// We are left-dragging. This is often used to drag points, or }
// constraint labels.
double dm = orig.mouse.DistanceTo(mp); // Enforce a bit of static friction before we start dragging.
// Don't start a drag until we've moved some threshold distance from double dm = orig.mouse.DistanceTo(mp);
// the mouse-down point, to avoid accidental drags. if(leftDown && dm > 3 && pendingOperation == 0) {
double dmt = 3; if(hover.entity.v &&
if(pendingOperation == 0) { SS.GetEntity(hover.entity)->IsPoint() &&
if(hover.entity.v && !SS.GetEntity(hover.entity)->PointIsFromReferences())
SS.GetEntity(hover.entity)->IsPoint() &&
!SS.GetEntity(hover.entity)->PointIsFromReferences())
{
if(dm > dmt) {
// Start dragging this point.
ClearSelection();
pendingPoint = hover.entity;
pendingOperation = DRAGGING_POINT;
}
} else if(hover.constraint.v &&
SS.GetConstraint(hover.constraint)->HasLabel())
{
if(dm > dmt) {
ClearSelection();
pendingConstraint = hover.constraint;
pendingOperation = DRAGGING_CONSTRAINT;
}
}
} else if(pendingOperation == DRAGGING_POINT ||
pendingOperation == DRAGGING_NEW_POINT ||
pendingOperation == DRAGGING_NEW_LINE_POINT)
{ {
UpdateDraggedEntity(pendingPoint, x, y); // Start dragging this point.
} else if(pendingOperation == DRAGGING_CONSTRAINT) { ClearSelection();
Constraint *c = SS.constraint.FindById(pendingConstraint); pendingPoint = hover.entity;
UpdateDraggedPoint(&(c->disp.offset), x, y); pendingOperation = DRAGGING_POINT;
} } else if(hover.constraint.v &&
} else { SS.GetConstraint(hover.constraint)->HasLabel())
// No buttons pressed.
if(pendingOperation == DRAGGING_NEW_POINT ||
pendingOperation == DRAGGING_NEW_LINE_POINT)
{ {
UpdateDraggedEntity(pendingPoint, x, y); ClearSelection();
HitTestMakeSelection(mp); pendingConstraint = hover.constraint;
} else { pendingOperation = DRAGGING_CONSTRAINT;
// Do our usual hit testing, for the selection.
HitTestMakeSelection(mp);
} }
} else if(leftDown && pendingOperation == DRAGGING_CONSTRAINT) {
Constraint *c = SS.constraint.FindById(pendingConstraint);
UpdateDraggedPoint(&(c->disp.offset), x, y);
} else if(leftDown && pendingOperation == DRAGGING_POINT) {
UpdateDraggedEntity(pendingPoint, x, y);
HitTestMakeSelection(mp);
}
// No buttons pressed.
if(pendingOperation == DRAGGING_NEW_POINT ||
pendingOperation == DRAGGING_NEW_LINE_POINT)
{
UpdateDraggedEntity(pendingPoint, x, y);
HitTestMakeSelection(mp);
} else if(pendingOperation == DRAGGING_NEW_CUBIC_POINT) {
UpdateDraggedEntity(pendingPoint, x, y);
HitTestMakeSelection(mp);
hRequest hr = pendingPoint.request();
Vector p0 = SS.GetEntity(hr.entity(1))->PointGetCoords();
Vector p3 = SS.GetEntity(hr.entity(4))->PointGetCoords();
Vector p1 = p0.ScaledBy(2.0/3).Plus(p3.ScaledBy(1.0/3));
SS.GetEntity(hr.entity(2))->PointForceTo(p1);
Vector p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3));
SS.GetEntity(hr.entity(3))->PointForceTo(p2);
} else if(!leftDown) {
// Do our usual hit testing, for the selection.
HitTestMakeSelection(mp);
} }
} }
@ -556,6 +559,10 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
v = v.Plus(projRight.ScaledBy(mx/scale)); v = v.Plus(projRight.ScaledBy(mx/scale));
v = v.Plus(projUp.ScaledBy(my/scale)); v = v.Plus(projUp.ScaledBy(my/scale));
#define MAYBE_PLACE(p) \
if(hover.entity.v && SS.GetEntity((p))->IsPoint()) { \
Constraint::ConstrainCoincident(hover.entity, (p)); \
}
hRequest hr; hRequest hr;
switch(pendingOperation) { switch(pendingOperation) {
case MNU_DATUM_POINT: case MNU_DATUM_POINT:
@ -570,9 +577,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
case MNU_LINE_SEGMENT: case MNU_LINE_SEGMENT:
hr = AddRequest(Request::LINE_SEGMENT); hr = AddRequest(Request::LINE_SEGMENT);
SS.GetEntity(hr.entity(1))->PointForceTo(v); SS.GetEntity(hr.entity(1))->PointForceTo(v);
if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { MAYBE_PLACE(hr.entity(1));
Constraint::ConstrainCoincident(hover.entity, hr.entity(1));
}
ClearSelection(); hover.Clear(); ClearSelection(); hover.Clear();
@ -582,12 +587,35 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
SS.GetEntity(pendingPoint)->PointForceTo(v); SS.GetEntity(pendingPoint)->PointForceTo(v);
break; break;
case MNU_CUBIC:
hr = AddRequest(Request::CUBIC);
SS.GetEntity(hr.entity(1))->PointForceTo(v);
SS.GetEntity(hr.entity(2))->PointForceTo(v);
SS.GetEntity(hr.entity(3))->PointForceTo(v);
SS.GetEntity(hr.entity(4))->PointForceTo(v);
MAYBE_PLACE(hr.entity(1));
ClearSelection(); hover.Clear();
pendingOperation = DRAGGING_NEW_CUBIC_POINT;
pendingPoint = hr.entity(4);
pendingDescription = "click to place next point of cubic";
break;
case DRAGGING_NEW_POINT: case 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;
pendingPoint.v = 0; pendingPoint.v = 0;
break; break;
case DRAGGING_NEW_CUBIC_POINT:
if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) {
Constraint::ConstrainCoincident(pendingPoint, hover.entity);
}
pendingOperation = 0;
pendingPoint.v = 0;
break;
case DRAGGING_NEW_LINE_POINT: { case DRAGGING_NEW_LINE_POINT: {
if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) {
Constraint::ConstrainCoincident(pendingPoint, hover.entity); Constraint::ConstrainCoincident(pendingPoint, hover.entity);
@ -614,6 +642,9 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
case 0: case 0:
default: { default: {
pendingOperation = 0; pendingOperation = 0;
pendingPoint.v = 0;
pendingConstraint.v = 0;
pendingDescription = NULL;
if(hover.IsEmpty()) break; if(hover.IsEmpty()) break;

View File

@ -80,6 +80,9 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
case Request::LINE_SEGMENT: case Request::LINE_SEGMENT:
et = Entity::LINE_SEGMENT; points = 2; params = 0; goto c; et = Entity::LINE_SEGMENT; points = 2; params = 0; goto c;
case Request::CUBIC:
et = Entity::CUBIC; points = 4; params = 0; goto c;
c: { c: {
// Generate the entity that's specific to this request. // Generate the entity that's specific to this request.
e.type = et; e.type = et;

View File

@ -106,6 +106,7 @@ public:
static const int CSYS_2D = 100; static const int CSYS_2D = 100;
static const int DATUM_POINT = 101; static const int DATUM_POINT = 101;
static const int LINE_SEGMENT = 200; static const int LINE_SEGMENT = 200;
static const int CUBIC = 300;
int type; int type;
@ -138,6 +139,7 @@ public:
static const int POINT_IN_3D = 2000; static const int POINT_IN_3D = 2000;
static const int POINT_IN_2D = 2001; static const int POINT_IN_2D = 2001;
static const int LINE_SEGMENT = 3000; static const int LINE_SEGMENT = 3000;
static const int CUBIC = 4000;
int type; int type;
bool symbolic; bool symbolic;

10
ui.h
View File

@ -98,6 +98,7 @@ public:
MNU_DATUM_POINT, MNU_DATUM_POINT,
MNU_LINE_SEGMENT, MNU_LINE_SEGMENT,
MNU_RECTANGLE, MNU_RECTANGLE,
MNU_CUBIC,
// Constrain // Constrain
MNU_DISTANCE_DIA, MNU_DISTANCE_DIA,
MNU_EQUAL, MNU_EQUAL,
@ -151,10 +152,11 @@ 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 DRAGGING_POINT = 0x0f000000; static const int DRAGGING_POINT = 0x0f000000;
static const int DRAGGING_NEW_POINT = 0x0f000001; static const int DRAGGING_NEW_POINT = 0x0f000001;
static const int DRAGGING_NEW_LINE_POINT = 0x0f000002; static const int DRAGGING_NEW_LINE_POINT = 0x0f000002;
static const int DRAGGING_CONSTRAINT = 0x0f000003; static const int DRAGGING_NEW_CUBIC_POINT = 0x0f000003;
static const int DRAGGING_CONSTRAINT = 0x0f000004;
hEntity pendingPoint; hEntity pendingPoint;
hConstraint pendingConstraint; hConstraint pendingConstraint;
int pendingOperation; int pendingOperation;