2008-04-14 10:28:32 +00:00
|
|
|
#include "solvespace.h"
|
|
|
|
|
2008-05-26 09:56:50 +00:00
|
|
|
const hConstraint Constraint::NO_CONSTRAINT = { 0 };
|
|
|
|
|
2008-04-27 09:03:01 +00:00
|
|
|
char *Constraint::DescriptionString(void) {
|
|
|
|
static char ret[1024];
|
2008-06-01 00:26:41 +00:00
|
|
|
|
|
|
|
char *s;
|
|
|
|
switch(type) {
|
|
|
|
case POINTS_COINCIDENT: s = "pts-coincident"; break;
|
|
|
|
case PT_PT_DISTANCE: s = "pt-pt-distance"; break;
|
|
|
|
case PT_LINE_DISTANCE: s = "pt-line-distance"; break;
|
|
|
|
case PT_PLANE_DISTANCE: s = "pt-plane-distance"; break;
|
2008-06-06 08:46:55 +00:00
|
|
|
case PT_FACE_DISTANCE: s = "pt-face-distance"; break;
|
2008-06-01 00:26:41 +00:00
|
|
|
case PT_IN_PLANE: s = "pt-in-plane"; break;
|
|
|
|
case PT_ON_LINE: s = "pt-on-line"; break;
|
|
|
|
case PT_ON_FACE: s = "pt-on-face"; break;
|
|
|
|
case EQUAL_LENGTH_LINES:s = "eq-length"; break;
|
2008-06-14 11:16:14 +00:00
|
|
|
case EQ_LEN_PT_LINE_D: s = "eq-length-and-pt-ln-dist"; break;
|
|
|
|
case EQ_PT_LN_DISTANCES:s = "eq-pt-line-distances"; break;
|
2008-06-01 00:26:41 +00:00
|
|
|
case LENGTH_RATIO: s = "length-ratio"; break;
|
|
|
|
case SYMMETRIC: s = "symmetric"; break;
|
|
|
|
case SYMMETRIC_HORIZ: s = "symmetric-h"; break;
|
|
|
|
case SYMMETRIC_VERT: s = "symmetric-v"; break;
|
2008-07-02 06:59:49 +00:00
|
|
|
case SYMMETRIC_LINE: s = "symmetric-line"; break;
|
2008-06-01 00:26:41 +00:00
|
|
|
case AT_MIDPOINT: s = "at-midpoint"; break;
|
|
|
|
case HORIZONTAL: s = "horizontal"; break;
|
|
|
|
case VERTICAL: s = "vertical"; break;
|
|
|
|
case DIAMETER: s = "diameter"; break;
|
|
|
|
case PT_ON_CIRCLE: s = "pt-on-circle"; break;
|
|
|
|
case SAME_ORIENTATION: s = "same-orientation"; break;
|
|
|
|
case ANGLE: s = "angle"; break;
|
|
|
|
case PARALLEL: s = "parallel"; break;
|
2008-07-13 12:44:05 +00:00
|
|
|
case ARC_LINE_TANGENT: s = "arc-line-tangent"; break;
|
|
|
|
case CUBIC_LINE_TANGENT:s = "cubic-line-tangent"; break;
|
2008-07-02 09:21:29 +00:00
|
|
|
case PERPENDICULAR: s = "perpendicular"; break;
|
2008-06-01 00:26:41 +00:00
|
|
|
case EQUAL_RADIUS: s = "eq-radius"; break;
|
2008-07-20 12:24:43 +00:00
|
|
|
case EQUAL_ANGLE: s = "eq-angle"; break;
|
2008-06-12 08:58:58 +00:00
|
|
|
case COMMENT: s = "comment"; break;
|
2008-06-01 00:26:41 +00:00
|
|
|
default: s = "???"; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(ret, "c%03x-%s", h.v, s);
|
2008-04-27 09:03:01 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-09-06 21:36:31 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Delete all constraints with the specified type, entityA, ptA. We use this
|
|
|
|
// when auto-removing constraints that would become redundant.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void Constraint::DeleteAllConstraintsFor(int type, hEntity entityA, hEntity ptA)
|
|
|
|
{
|
|
|
|
SS.constraint.ClearTags();
|
|
|
|
for(int i = 0; i < SS.constraint.n; i++) {
|
|
|
|
Constraint *ct = &(SS.constraint.elem[i]);
|
|
|
|
if(ct->type != type) continue;
|
|
|
|
|
|
|
|
if(ct->entityA.v != entityA.v) continue;
|
|
|
|
if(ct->ptA.v != ptA.v) continue;
|
|
|
|
ct->tag = 1;
|
|
|
|
}
|
|
|
|
SS.constraint.RemoveTagged();
|
|
|
|
// And no need to do anything special, since nothing
|
|
|
|
// ever depends on a constraint. But do clear the
|
|
|
|
// hover, in case the just-deleted constraint was
|
|
|
|
// hovered.
|
|
|
|
SS.GW.hover.Clear();
|
|
|
|
}
|
|
|
|
|
2008-05-17 23:48:58 +00:00
|
|
|
void Constraint::AddConstraint(Constraint *c) {
|
2008-06-04 10:22:30 +00:00
|
|
|
AddConstraint(c, true);
|
|
|
|
}
|
|
|
|
void Constraint::AddConstraint(Constraint *c, bool rememberForUndo) {
|
|
|
|
if(rememberForUndo) SS.UndoRemember();
|
|
|
|
|
2008-04-14 10:28:32 +00:00
|
|
|
SS.constraint.AddAndAssignId(c);
|
2008-04-27 05:00:12 +00:00
|
|
|
|
2008-06-02 11:43:27 +00:00
|
|
|
SS.MarkGroupDirty(c->group);
|
2008-06-03 18:28:41 +00:00
|
|
|
SS.later.generateAll = true;
|
2008-04-14 10:28:32 +00:00
|
|
|
}
|
|
|
|
|
2008-07-14 02:45:11 +00:00
|
|
|
void Constraint::Constrain(int type, hEntity ptA, hEntity ptB,
|
|
|
|
hEntity entityA, hEntity entityB,
|
|
|
|
bool other)
|
2008-05-08 07:30:30 +00:00
|
|
|
{
|
2008-04-23 07:29:19 +00:00
|
|
|
Constraint c;
|
|
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
c.group = SS.GW.activeGroup;
|
2008-05-27 02:22:20 +00:00
|
|
|
c.workplane = SS.GW.ActiveWorkplane();
|
2008-05-08 07:30:30 +00:00
|
|
|
c.type = type;
|
2008-04-23 07:29:19 +00:00
|
|
|
c.ptA = ptA;
|
|
|
|
c.ptB = ptB;
|
2008-05-08 07:30:30 +00:00
|
|
|
c.entityA = entityA;
|
2008-07-14 02:45:11 +00:00
|
|
|
c.entityB = entityB;
|
|
|
|
c.other = other;
|
2008-06-04 10:22:30 +00:00
|
|
|
AddConstraint(&c, false);
|
2008-04-23 07:29:19 +00:00
|
|
|
}
|
2008-07-14 02:45:11 +00:00
|
|
|
void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA){
|
|
|
|
Constrain(type, ptA, ptB, entityA, Entity::NO_ENTITY, false);
|
|
|
|
}
|
2008-05-08 07:30:30 +00:00
|
|
|
void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
|
2008-07-14 02:45:11 +00:00
|
|
|
Constrain(POINTS_COINCIDENT, ptA, ptB,
|
|
|
|
Entity::NO_ENTITY, Entity::NO_ENTITY, false);
|
2008-05-07 04:17:29 +00:00
|
|
|
}
|
|
|
|
|
2008-04-14 10:28:32 +00:00
|
|
|
void Constraint::MenuConstrain(int id) {
|
|
|
|
Constraint c;
|
2008-07-02 06:59:49 +00:00
|
|
|
ZERO(&c);
|
2008-04-14 10:28:32 +00:00
|
|
|
c.group = SS.GW.activeGroup;
|
2008-05-27 02:22:20 +00:00
|
|
|
c.workplane = SS.GW.ActiveWorkplane();
|
2008-04-14 10:28:32 +00:00
|
|
|
|
|
|
|
SS.GW.GroupSelection();
|
|
|
|
#define gs (SS.GW.gs)
|
|
|
|
|
|
|
|
switch(id) {
|
2008-04-27 05:00:12 +00:00
|
|
|
case GraphicsWindow::MNU_DISTANCE_DIA: {
|
2008-04-14 10:28:32 +00:00
|
|
|
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;
|
2008-04-19 11:09:47 +00:00
|
|
|
Entity *e = SS.GetEntity(gs.entity[0]);
|
2008-04-27 03:26:27 +00:00
|
|
|
c.ptA = e->point[0];
|
|
|
|
c.ptB = e->point[1];
|
2008-05-08 07:30:30 +00:00
|
|
|
} else if(gs.workplanes == 1 && gs.points == 1 && gs.n == 2) {
|
|
|
|
c.type = PT_PLANE_DISTANCE;
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
} else if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
|
|
|
|
c.type = PT_LINE_DISTANCE;
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityA = gs.entity[0];
|
2008-06-06 08:46:55 +00:00
|
|
|
} else if(gs.faces == 1 && gs.points == 1 && gs.n == 2) {
|
|
|
|
c.type = PT_FACE_DISTANCE;
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityA = gs.face[0];
|
2008-05-07 08:19:37 +00:00
|
|
|
} else if(gs.circlesOrArcs == 1 && gs.n == 1) {
|
|
|
|
c.type = DIAMETER;
|
|
|
|
c.entityA = gs.entity[0];
|
2008-04-14 10:28:32 +00:00
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for distance / diameter constraint. This "
|
|
|
|
"constraint can apply to:\r\n\r\n"
|
|
|
|
" * two points (distance between points)\r\n"
|
|
|
|
" * a line segment (length)\r\n"
|
|
|
|
" * a workplane and a point (minimum distance)\r\n"
|
|
|
|
" * a line segment and a point (minimum distance)\r\n"
|
|
|
|
" * a plane face and a point (minimum distance)\r\n"
|
|
|
|
" * a circle or an arc (diameter)\r\n");
|
2008-04-14 10:28:32 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-05-07 08:19:37 +00:00
|
|
|
if(c.type == PT_PT_DISTANCE) {
|
|
|
|
Vector n = SS.GW.projRight.Cross(SS.GW.projUp);
|
|
|
|
Vector a = SS.GetEntity(c.ptA)->PointGetNum();
|
|
|
|
Vector b = SS.GetEntity(c.ptB)->PointGetNum();
|
2008-05-11 10:40:37 +00:00
|
|
|
c.disp.offset = n.Cross(a.Minus(b));
|
|
|
|
c.disp.offset = (c.disp.offset).WithMagnitude(50/SS.GW.scale);
|
2008-05-07 08:19:37 +00:00
|
|
|
} else {
|
2008-06-01 08:45:11 +00:00
|
|
|
c.disp.offset = Vector::From(0, 0, 0);
|
2008-05-07 08:19:37 +00:00
|
|
|
}
|
2008-04-27 05:00:12 +00:00
|
|
|
|
2008-06-14 08:43:38 +00:00
|
|
|
c.valA = 0;
|
2008-04-22 10:53:42 +00:00
|
|
|
c.ModifyToSatisfy();
|
2008-04-14 10:28:32 +00:00
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
2008-04-27 05:00:12 +00:00
|
|
|
}
|
2008-04-14 10:28:32 +00:00
|
|
|
|
2008-04-21 08:16:38 +00:00
|
|
|
case GraphicsWindow::MNU_ON_ENTITY:
|
|
|
|
if(gs.points == 2 && gs.n == 2) {
|
|
|
|
c.type = POINTS_COINCIDENT;
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.ptB = gs.point[1];
|
2008-05-05 11:17:00 +00:00
|
|
|
} else if(gs.points == 1 && gs.workplanes == 1 && gs.n == 2) {
|
2008-04-21 08:16:38 +00:00
|
|
|
c.type = PT_IN_PLANE;
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityA = gs.entity[0];
|
2008-04-28 09:40:02 +00:00
|
|
|
} else if(gs.points == 1 && gs.lineSegments == 1 && gs.n == 2) {
|
|
|
|
c.type = PT_ON_LINE;
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityA = gs.entity[0];
|
2008-05-08 07:30:30 +00:00
|
|
|
} else if(gs.points == 1 && gs.circlesOrArcs == 1 && gs.n == 2) {
|
|
|
|
c.type = PT_ON_CIRCLE;
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityA = gs.entity[0];
|
2008-06-02 03:31:37 +00:00
|
|
|
} else if(gs.points == 1 && gs.faces == 1 && gs.n == 2) {
|
|
|
|
c.type = PT_ON_FACE;
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityA = gs.face[0];
|
2008-04-21 08:16:38 +00:00
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for on point / curve / plane constraint. "
|
|
|
|
"This constraint can apply to:\r\n\r\n"
|
|
|
|
" * two points (points coincident)\r\n"
|
|
|
|
" * a point and a workplane (point in plane)\r\n"
|
|
|
|
" * a point and a line segment (point on line)\r\n"
|
|
|
|
" * a point and a circle or arc (point on curve)\r\n"
|
|
|
|
" * a point and a plane face (point on face)\r\n");
|
2008-04-21 08:16:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
|
2008-04-22 05:00:49 +00:00
|
|
|
case GraphicsWindow::MNU_EQUAL:
|
|
|
|
if(gs.lineSegments == 2 && gs.n == 2) {
|
|
|
|
c.type = EQUAL_LENGTH_LINES;
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.entityB = gs.entity[1];
|
2008-06-14 11:16:14 +00:00
|
|
|
} else if(gs.lineSegments == 2 && gs.points == 2 && gs.n == 4) {
|
|
|
|
c.type = EQ_PT_LN_DISTANCES;
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityB = gs.entity[1];
|
|
|
|
c.ptB = gs.point[1];
|
|
|
|
} else if(gs.lineSegments == 1 && gs.points == 2 && gs.n == 3) {
|
|
|
|
// The same line segment for the distances, but different
|
|
|
|
// points.
|
|
|
|
c.type = EQ_PT_LN_DISTANCES;
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.entityB = gs.entity[0];
|
|
|
|
c.ptB = gs.point[1];
|
|
|
|
} else if(gs.lineSegments == 2 && gs.points == 1 && gs.n == 3) {
|
|
|
|
c.type = EQ_LEN_PT_LINE_D;
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.entityB = gs.entity[1];
|
|
|
|
c.ptA = gs.point[0];
|
2008-07-20 12:24:43 +00:00
|
|
|
} else if(gs.vectors == 4 && gs.n == 4) {
|
|
|
|
c.type = EQUAL_ANGLE;
|
|
|
|
c.entityA = gs.vector[0];
|
|
|
|
c.entityB = gs.vector[1];
|
|
|
|
c.entityC = gs.vector[2];
|
|
|
|
c.entityD = gs.vector[3];
|
|
|
|
} else if(gs.vectors == 3 && gs.n == 3) {
|
|
|
|
c.type = EQUAL_ANGLE;
|
|
|
|
c.entityA = gs.vector[0];
|
|
|
|
c.entityB = gs.vector[1];
|
|
|
|
c.entityC = gs.vector[1];
|
|
|
|
c.entityD = gs.vector[2];
|
2008-05-12 10:01:44 +00:00
|
|
|
} else if(gs.circlesOrArcs == 2 && gs.n == 2) {
|
|
|
|
c.type = EQUAL_RADIUS;
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.entityB = gs.entity[1];
|
2008-04-22 05:00:49 +00:00
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for equal length / radius constraint. "
|
|
|
|
"This constraint can apply to:\r\n\r\n"
|
|
|
|
" * two line segments (equal length)\r\n"
|
|
|
|
" * two line segments and two points "
|
|
|
|
"(equal point-line distances)\r\n"
|
|
|
|
" * a line segment and two points "
|
|
|
|
"(equal point-line distances)\r\n"
|
|
|
|
" * a line segment, and a point and line segment "
|
|
|
|
"(point-line distance equals length)\r\n"
|
2008-07-20 12:24:43 +00:00
|
|
|
" * four line segments or normals "
|
|
|
|
"(equal angle between A,B and C,D)\r\n"
|
|
|
|
" * three line segments or normals "
|
|
|
|
"(equal angle between A,B and B,C)\r\n"
|
2008-07-14 04:29:43 +00:00
|
|
|
" * two circles or arcs (equal radius)\r\n");
|
2008-04-22 05:00:49 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-02-26 12:48:31 +00:00
|
|
|
if(c.type == EQUAL_ANGLE) {
|
|
|
|
// Infer the nearest supplementary angle from the sketch.
|
|
|
|
Vector a1 = SS.GetEntity(c.entityA)->VectorGetNum(),
|
|
|
|
b1 = SS.GetEntity(c.entityB)->VectorGetNum(),
|
|
|
|
a2 = SS.GetEntity(c.entityC)->VectorGetNum(),
|
|
|
|
b2 = SS.GetEntity(c.entityD)->VectorGetNum();
|
|
|
|
double d1 = a1.Dot(b1), d2 = a2.Dot(b2);
|
|
|
|
|
|
|
|
if(d1*d2 < 0) {
|
|
|
|
c.other = true;
|
|
|
|
}
|
|
|
|
}
|
2008-04-22 05:00:49 +00:00
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
|
2008-05-11 10:40:37 +00:00
|
|
|
case GraphicsWindow::MNU_RATIO:
|
|
|
|
if(gs.lineSegments == 2 && gs.n == 2) {
|
|
|
|
c.type = LENGTH_RATIO;
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.entityB = gs.entity[1];
|
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for length ratio constraint. This "
|
|
|
|
"constraint can apply to:\r\n\r\n"
|
|
|
|
" * two line segments\r\n");
|
2008-05-11 10:40:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-06-14 08:43:38 +00:00
|
|
|
c.valA = 0;
|
2008-05-11 10:40:37 +00:00
|
|
|
c.ModifyToSatisfy();
|
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
|
2008-04-30 08:14:32 +00:00
|
|
|
case GraphicsWindow::MNU_AT_MIDPOINT:
|
|
|
|
if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
|
|
|
|
c.type = AT_MIDPOINT;
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.ptA = gs.point[0];
|
2008-09-06 21:36:31 +00:00
|
|
|
|
|
|
|
// If a point is at-midpoint, then no reason to also constrain
|
|
|
|
// it on-line; so auto-remove that.
|
|
|
|
DeleteAllConstraintsFor(PT_ON_LINE, c.entityA, c.ptA);
|
2008-05-05 11:17:00 +00:00
|
|
|
} else if(gs.lineSegments == 1 && gs.workplanes == 1 && gs.n == 2) {
|
2008-05-01 06:53:50 +00:00
|
|
|
c.type = AT_MIDPOINT;
|
2008-05-05 11:17:00 +00:00
|
|
|
int i = SS.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
|
2008-05-01 06:53:50 +00:00
|
|
|
c.entityA = gs.entity[i];
|
|
|
|
c.entityB = gs.entity[1-i];
|
2008-04-30 08:14:32 +00:00
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for at midpoint constraint. This "
|
|
|
|
"constraint can apply to:\r\n\r\n"
|
|
|
|
" * a line segment and a point "
|
|
|
|
"(point at midpoint)\r\n"
|
|
|
|
" * a line segment and a workplane "
|
|
|
|
"(line's midpoint on plane)\r\n");
|
2008-04-30 08:14:32 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GraphicsWindow::MNU_SYMMETRIC:
|
2008-05-13 10:38:21 +00:00
|
|
|
if(gs.points == 2 &&
|
|
|
|
((gs.workplanes == 1 && gs.n == 3) ||
|
|
|
|
(gs.n == 2)))
|
|
|
|
{
|
2008-04-30 08:14:32 +00:00
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.ptB = gs.point[1];
|
2008-05-13 10:38:21 +00:00
|
|
|
} else if(gs.lineSegments == 1 &&
|
|
|
|
((gs.workplanes == 1 && gs.n == 2) ||
|
|
|
|
(gs.n == 1)))
|
|
|
|
{
|
2008-05-05 11:17:00 +00:00
|
|
|
int i = SS.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
|
2008-04-30 08:14:32 +00:00
|
|
|
Entity *line = SS.GetEntity(gs.entity[i]);
|
|
|
|
c.entityA = gs.entity[1-i];
|
|
|
|
c.ptA = line->point[0];
|
|
|
|
c.ptB = line->point[1];
|
2008-07-02 06:59:49 +00:00
|
|
|
} else if(SS.GW.LockedInWorkplane()
|
|
|
|
&& gs.lineSegments == 2 && gs.n == 2)
|
|
|
|
{
|
|
|
|
Entity *l0 = SS.GetEntity(gs.entity[0]),
|
|
|
|
*l1 = SS.GetEntity(gs.entity[1]);
|
|
|
|
|
|
|
|
if((l1->group.v != SS.GW.activeGroup.v) ||
|
|
|
|
(l1->construction && !(l0->construction)))
|
|
|
|
{
|
|
|
|
SWAP(Entity *, l0, l1);
|
|
|
|
}
|
|
|
|
c.ptA = l1->point[0];
|
|
|
|
c.ptB = l1->point[1];
|
|
|
|
c.entityA = l0->h;
|
|
|
|
c.type = SYMMETRIC_LINE;
|
|
|
|
} else if(SS.GW.LockedInWorkplane()
|
|
|
|
&& gs.lineSegments == 1 && gs.points == 2 && gs.n == 3)
|
|
|
|
{
|
|
|
|
c.ptA = gs.point[0];
|
|
|
|
c.ptB = gs.point[1];
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
c.type = SYMMETRIC_LINE;
|
2008-04-30 08:14:32 +00:00
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for symmetric constraint. This constraint "
|
|
|
|
"can apply to:\r\n\r\n"
|
|
|
|
" * two points or a line segment "
|
|
|
|
"(symmetric about workplane's coordinate axis)\r\n"
|
|
|
|
" * line segment, and two points or a line segment "
|
|
|
|
"(symmetric about line segment)\r\n"
|
|
|
|
" * workplane, and two points or a line segment "
|
|
|
|
"(symmetric about workplane)\r\n");
|
2008-04-30 08:14:32 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-07-02 06:59:49 +00:00
|
|
|
if(c.type != 0) {
|
|
|
|
// Already done, symmetry about a line segment in a workplane
|
|
|
|
} else if(c.entityA.v == Entity::NO_ENTITY.v) {
|
|
|
|
// Horizontal / vertical symmetry, implicit symmetry plane
|
|
|
|
// normal to the workplane
|
2008-05-13 10:38:21 +00:00
|
|
|
if(c.workplane.v == Entity::FREE_IN_3D.v) {
|
|
|
|
Error("Must be locked in to workplane when constraining "
|
|
|
|
"symmetric without an explicit symmetry plane.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Vector pa = SS.GetEntity(c.ptA)->PointGetNum();
|
|
|
|
Vector pb = SS.GetEntity(c.ptB)->PointGetNum();
|
|
|
|
Vector dp = pa.Minus(pb);
|
|
|
|
Entity *norm = SS.GetEntity(c.workplane)->Normal();;
|
|
|
|
Vector u = norm->NormalU(), v = norm->NormalV();
|
|
|
|
if(fabs(dp.Dot(u)) > fabs(dp.Dot(v))) {
|
|
|
|
c.type = SYMMETRIC_HORIZ;
|
|
|
|
} else {
|
|
|
|
c.type = SYMMETRIC_VERT;
|
|
|
|
}
|
2008-06-11 04:22:52 +00:00
|
|
|
if(gs.lineSegments == 1) {
|
|
|
|
// If this line segment is already constrained horiz or
|
|
|
|
// vert, then auto-remove that redundant constraint.
|
2008-09-06 21:36:31 +00:00
|
|
|
DeleteAllConstraintsFor(HORIZONTAL, (gs.entity[0]),
|
|
|
|
Entity::NO_ENTITY);
|
|
|
|
DeleteAllConstraintsFor(VERTICAL, (gs.entity[0]),
|
|
|
|
Entity::NO_ENTITY);
|
|
|
|
|
2008-06-11 04:22:52 +00:00
|
|
|
}
|
2008-05-13 10:38:21 +00:00
|
|
|
} else {
|
|
|
|
// Symmetry with a symmetry plane specified explicitly.
|
|
|
|
c.type = SYMMETRIC;
|
|
|
|
}
|
2008-04-30 08:14:32 +00:00
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
|
2008-04-23 07:29:19 +00:00
|
|
|
case GraphicsWindow::MNU_VERTICAL:
|
|
|
|
case GraphicsWindow::MNU_HORIZONTAL: {
|
|
|
|
hEntity ha, hb;
|
2008-04-27 05:00:12 +00:00
|
|
|
if(c.workplane.v == Entity::FREE_IN_3D.v) {
|
|
|
|
Error("Select workplane before constraining horiz/vert.");
|
|
|
|
return;
|
|
|
|
}
|
2008-04-23 07:29:19 +00:00
|
|
|
if(gs.lineSegments == 1 && gs.n == 1) {
|
|
|
|
c.entityA = gs.entity[0];
|
|
|
|
Entity *e = SS.GetEntity(c.entityA);
|
2008-04-27 03:26:27 +00:00
|
|
|
ha = e->point[0];
|
|
|
|
hb = e->point[1];
|
2008-04-23 07:29:19 +00:00
|
|
|
} else if(gs.points == 2 && gs.n == 2) {
|
|
|
|
ha = c.ptA = gs.point[0];
|
|
|
|
hb = c.ptB = gs.point[1];
|
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for horizontal / vertical constraint. "
|
|
|
|
"This constraint can apply to:\r\n\r\n"
|
|
|
|
" * two points\r\n"
|
|
|
|
" * a line segment\r\n");
|
2008-04-23 07:29:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(id == GraphicsWindow::MNU_HORIZONTAL) {
|
|
|
|
c.type = HORIZONTAL;
|
|
|
|
} else {
|
|
|
|
c.type = VERTICAL;
|
|
|
|
}
|
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-06-18 09:18:51 +00:00
|
|
|
case GraphicsWindow::MNU_ORIENTED_SAME: {
|
2008-05-09 05:33:23 +00:00
|
|
|
if(gs.anyNormals == 2 && gs.n == 2) {
|
|
|
|
c.type = SAME_ORIENTATION;
|
|
|
|
c.entityA = gs.anyNormal[0];
|
|
|
|
c.entityB = gs.anyNormal[1];
|
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for same orientation constraint. This "
|
|
|
|
"constraint can apply to:\r\n\r\n"
|
|
|
|
" * two normals\r\n");
|
2008-05-09 05:33:23 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-06-18 09:18:51 +00:00
|
|
|
SS.UndoRemember();
|
|
|
|
|
|
|
|
Entity *nfree = SS.GetEntity(c.entityA);
|
|
|
|
Entity *nref = SS.GetEntity(c.entityB);
|
|
|
|
if(nref->group.v == SS.GW.activeGroup.v) {
|
|
|
|
SWAP(Entity *, nref, nfree);
|
|
|
|
}
|
|
|
|
if(nfree->group.v == SS.GW.activeGroup.v &&
|
|
|
|
nref ->group.v != SS.GW.activeGroup.v)
|
|
|
|
{
|
|
|
|
// nfree is free, and nref is locked (since it came from a
|
|
|
|
// previous group); so let's force nfree aligned to nref,
|
|
|
|
// and make convergence easy
|
|
|
|
Vector ru = nref ->NormalU(), rv = nref ->NormalV();
|
|
|
|
Vector fu = nfree->NormalU(), fv = nfree->NormalV();
|
|
|
|
|
|
|
|
if(fabs(fu.Dot(ru)) < fabs(fu.Dot(rv))) {
|
|
|
|
// There might be an odd*90 degree rotation about the
|
|
|
|
// normal vector; allow that, since the numerical
|
|
|
|
// constraint does
|
|
|
|
SWAP(Vector, ru, rv);
|
|
|
|
}
|
|
|
|
fu = fu.Dot(ru) > 0 ? ru : ru.ScaledBy(-1);
|
|
|
|
fv = fv.Dot(rv) > 0 ? rv : rv.ScaledBy(-1);
|
|
|
|
|
|
|
|
nfree->NormalForceTo(Quaternion::From(fu, fv));
|
|
|
|
}
|
|
|
|
AddConstraint(&c, false);
|
2008-05-09 05:33:23 +00:00
|
|
|
break;
|
2008-06-18 09:18:51 +00:00
|
|
|
}
|
2008-05-09 05:33:23 +00:00
|
|
|
|
2008-05-17 11:15:14 +00:00
|
|
|
case GraphicsWindow::MNU_OTHER_ANGLE:
|
|
|
|
if(gs.constraints == 1 && gs.n == 0) {
|
|
|
|
Constraint *c = SS.GetConstraint(gs.constraint[0]);
|
|
|
|
if(c->type == ANGLE) {
|
2008-07-20 12:24:43 +00:00
|
|
|
SS.UndoRemember();
|
2008-07-13 12:44:05 +00:00
|
|
|
c->other = !(c->other);
|
2008-05-17 11:15:14 +00:00
|
|
|
c->ModifyToSatisfy();
|
|
|
|
break;
|
|
|
|
}
|
2008-07-20 12:24:43 +00:00
|
|
|
if(c->type == EQUAL_ANGLE) {
|
|
|
|
SS.UndoRemember();
|
|
|
|
c->other = !(c->other);
|
|
|
|
SS.MarkGroupDirty(c->group);
|
|
|
|
SS.later.generateAll = true;
|
|
|
|
break;
|
|
|
|
}
|
2008-05-17 11:15:14 +00:00
|
|
|
}
|
|
|
|
Error("Must select an angle constraint.");
|
2008-06-11 04:22:52 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
case GraphicsWindow::MNU_REFERENCE:
|
|
|
|
if(gs.constraints == 1 && gs.n == 0) {
|
|
|
|
Constraint *c = SS.GetConstraint(gs.constraint[0]);
|
2008-06-12 08:58:58 +00:00
|
|
|
if(c->HasLabel() && c->type != COMMENT) {
|
2008-06-11 04:22:52 +00:00
|
|
|
(c->reference) = !(c->reference);
|
|
|
|
SS.GetGroup(c->group)->clean = false;
|
|
|
|
SS.GenerateAll();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Error("Must select a constraint with associated label.");
|
|
|
|
return;
|
2008-05-17 11:15:14 +00:00
|
|
|
|
2008-02-26 12:48:31 +00:00
|
|
|
case GraphicsWindow::MNU_ANGLE: {
|
2008-05-17 11:15:14 +00:00
|
|
|
if(gs.vectors == 2 && gs.n == 2) {
|
|
|
|
c.type = ANGLE;
|
|
|
|
c.entityA = gs.vector[0];
|
|
|
|
c.entityB = gs.vector[1];
|
2008-06-14 08:43:38 +00:00
|
|
|
c.valA = 0;
|
2008-05-17 11:15:14 +00:00
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for angle constraint. This constraint "
|
|
|
|
"can apply to:\r\n\r\n"
|
|
|
|
" * two line segments\r\n"
|
|
|
|
" * a line segment and a normal\r\n"
|
|
|
|
" * two normals\r\n");
|
2008-05-17 11:15:14 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-02-26 12:48:31 +00:00
|
|
|
|
|
|
|
Entity *ea = SS.GetEntity(c.entityA),
|
|
|
|
*eb = SS.GetEntity(c.entityB);
|
|
|
|
if(ea->type == Entity::LINE_SEGMENT &&
|
|
|
|
eb->type == Entity::LINE_SEGMENT)
|
|
|
|
{
|
|
|
|
Vector a0 = SS.GetEntity(ea->point[0])->PointGetNum(),
|
|
|
|
a1 = SS.GetEntity(ea->point[1])->PointGetNum(),
|
|
|
|
b0 = SS.GetEntity(eb->point[0])->PointGetNum(),
|
|
|
|
b1 = SS.GetEntity(eb->point[1])->PointGetNum();
|
|
|
|
if(a0.Equals(b0) || a1.Equals(b1)) {
|
|
|
|
// okay, vectors should be drawn in same sense
|
|
|
|
} else if(a0.Equals(b1) || a1.Equals(b0)) {
|
|
|
|
// vectors are in opposite sense
|
|
|
|
c.other = true;
|
|
|
|
} else {
|
|
|
|
// no shared point; not clear which intersection to draw
|
|
|
|
}
|
|
|
|
}
|
2008-05-17 11:15:14 +00:00
|
|
|
c.ModifyToSatisfy();
|
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
2008-02-26 12:48:31 +00:00
|
|
|
}
|
2008-05-17 11:15:14 +00:00
|
|
|
|
2008-05-09 05:33:23 +00:00
|
|
|
case GraphicsWindow::MNU_PARALLEL:
|
|
|
|
if(gs.vectors == 2 && gs.n == 2) {
|
|
|
|
c.type = PARALLEL;
|
|
|
|
c.entityA = gs.vector[0];
|
|
|
|
c.entityB = gs.vector[1];
|
2008-07-13 12:44:05 +00:00
|
|
|
} else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
|
|
|
|
Entity *line = SS.GetEntity(gs.entity[0]);
|
|
|
|
Entity *arc = SS.GetEntity(gs.entity[1]);
|
|
|
|
if(line->type == Entity::ARC_OF_CIRCLE) {
|
|
|
|
SWAP(Entity *, line, arc);
|
|
|
|
}
|
|
|
|
Vector l0 = SS.GetEntity(line->point[0])->PointGetNum(),
|
|
|
|
l1 = SS.GetEntity(line->point[1])->PointGetNum();
|
|
|
|
Vector a1 = SS.GetEntity(arc->point[1])->PointGetNum(),
|
|
|
|
a2 = SS.GetEntity(arc->point[2])->PointGetNum();
|
|
|
|
|
|
|
|
if(l0.Equals(a1) || l1.Equals(a1)) {
|
|
|
|
c.other = false;
|
|
|
|
} else if(l0.Equals(a2) || l1.Equals(a2)) {
|
|
|
|
c.other = true;
|
|
|
|
} else {
|
|
|
|
Error("The tangent arc and line segment must share an "
|
|
|
|
"endpoint. Constrain them with Constrain -> "
|
|
|
|
"On Point before constraining tangent.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
c.type = ARC_LINE_TANGENT;
|
|
|
|
c.entityA = arc->h;
|
|
|
|
c.entityB = line->h;
|
|
|
|
} else if(gs.lineSegments == 1 && gs.cubics == 1 && gs.n == 2) {
|
|
|
|
Entity *line = SS.GetEntity(gs.entity[0]);
|
|
|
|
Entity *cubic = SS.GetEntity(gs.entity[1]);
|
|
|
|
if(line->type == Entity::CUBIC) {
|
|
|
|
SWAP(Entity *, line, cubic);
|
|
|
|
}
|
|
|
|
Vector l0 = SS.GetEntity(line->point[0])->PointGetNum(),
|
|
|
|
l1 = SS.GetEntity(line->point[1])->PointGetNum();
|
|
|
|
Vector a0 = SS.GetEntity(cubic->point[0])->PointGetNum(),
|
|
|
|
a3 = SS.GetEntity(cubic->point[3])->PointGetNum();
|
|
|
|
|
|
|
|
if(l0.Equals(a0) || l1.Equals(a0)) {
|
|
|
|
c.other = false;
|
|
|
|
} else if(l0.Equals(a3) || l1.Equals(a3)) {
|
|
|
|
c.other = true;
|
|
|
|
} else {
|
|
|
|
Error("The tangent cubic and line segment must share an "
|
|
|
|
"endpoint. Constrain them with Constrain -> "
|
|
|
|
"On Point before constraining tangent.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
c.type = CUBIC_LINE_TANGENT;
|
|
|
|
c.entityA = cubic->h;
|
|
|
|
c.entityB = line->h;
|
2008-05-09 05:33:23 +00:00
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for parallel / tangent constraint. This "
|
|
|
|
"constraint can apply to:\r\n\r\n"
|
|
|
|
" * two line segments (parallel)\r\n"
|
|
|
|
" * a line segment and a normal (parallel)\r\n"
|
|
|
|
" * two normals (parallel)\r\n"
|
|
|
|
" * a line segment and an arc, that share an endpoint "
|
|
|
|
"(tangent)\r\n"
|
|
|
|
" * a line segment and a cubic bezier, that share an "
|
|
|
|
"endpoint (tangent)\r\n");
|
2008-05-09 05:33:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
|
2008-07-02 09:21:29 +00:00
|
|
|
case GraphicsWindow::MNU_PERPENDICULAR:
|
|
|
|
if(gs.vectors == 2 && gs.n == 2) {
|
|
|
|
c.type = PERPENDICULAR;
|
|
|
|
c.entityA = gs.vector[0];
|
|
|
|
c.entityB = gs.vector[1];
|
|
|
|
} else {
|
2008-07-14 04:29:43 +00:00
|
|
|
Error("Bad selection for perpendicular constraint. This "
|
|
|
|
"constraint can apply to:\r\n\r\n"
|
|
|
|
" * two line segments\r\n"
|
|
|
|
" * a line segment and a normal\r\n"
|
|
|
|
" * two normals\r\n");
|
2008-07-02 09:21:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
|
2008-06-12 08:58:58 +00:00
|
|
|
case GraphicsWindow::MNU_COMMENT:
|
|
|
|
c.type = COMMENT;
|
|
|
|
c.comment.strcpy("NEW COMMENT -- DOUBLE-CLICK TO EDIT");
|
2008-06-14 08:43:38 +00:00
|
|
|
c.disp.offset = SS.GW.offset.ScaledBy(-1);
|
2008-06-12 08:58:58 +00:00
|
|
|
AddConstraint(&c);
|
|
|
|
break;
|
|
|
|
|
2008-04-14 10:28:32 +00:00
|
|
|
default: oops();
|
|
|
|
}
|
|
|
|
|
|
|
|
SS.GW.ClearSelection();
|
|
|
|
InvalidateGraphics();
|
|
|
|
}
|
2008-04-20 11:35:10 +00:00
|
|
|
|