The constraint solver now compiles as a library, and I have a

little test app that links against it. I still need to polish a few
things, but this is more or less as it should be.

[git-p4: depot-paths = "//depot/solvespace/": change = 1944]
solver
Jonathan Westhues 2009-04-19 23:30:09 -08:00
parent 11e4c7f5d9
commit 4d742a5777
20 changed files with 1208 additions and 295 deletions

View File

@ -10,6 +10,7 @@ OBJDIR = obj
FREEZE = $(OBJDIR)\freeze.obj FREEZE = $(OBJDIR)\freeze.obj
W32OBJS = $(OBJDIR)\w32main.obj \ W32OBJS = $(OBJDIR)\w32main.obj \
$(OBJDIR)\w32util.obj \
SSOBJS = $(OBJDIR)\solvespace.obj \ SSOBJS = $(OBJDIR)\solvespace.obj \
$(OBJDIR)\textwin.obj \ $(OBJDIR)\textwin.obj \

View File

@ -387,7 +387,7 @@ void Constraint::MenuConstrain(int id) {
Vector pa = SK.GetEntity(c.ptA)->PointGetNum(); Vector pa = SK.GetEntity(c.ptA)->PointGetNum();
Vector pb = SK.GetEntity(c.ptB)->PointGetNum(); Vector pb = SK.GetEntity(c.ptB)->PointGetNum();
Vector dp = pa.Minus(pb); Vector dp = pa.Minus(pb);
Entity *norm = SK.GetEntity(c.workplane)->Normal();; EntityBase *norm = SK.GetEntity(c.workplane)->Normal();;
Vector u = norm->NormalU(), v = norm->NormalV(); Vector u = norm->NormalU(), v = norm->NormalV();
if(fabs(dp.Dot(u)) > fabs(dp.Dot(v))) { if(fabs(dp.Dot(u)) > fabs(dp.Dot(v))) {
c.type = SYMMETRIC_HORIZ; c.type = SYMMETRIC_HORIZ;

View File

@ -33,13 +33,13 @@ Expr *ConstraintBase::VectorsParallel(int eq, ExprVector a, ExprVector b) {
Expr *ConstraintBase::PointLineDistance(hEntity wrkpl, hEntity hpt, hEntity hln) Expr *ConstraintBase::PointLineDistance(hEntity wrkpl, hEntity hpt, hEntity hln)
{ {
Entity *ln = SK.GetEntity(hln); EntityBase *ln = SK.GetEntity(hln);
Entity *a = SK.GetEntity(ln->point[0]); EntityBase *a = SK.GetEntity(ln->point[0]);
Entity *b = SK.GetEntity(ln->point[1]); EntityBase *b = SK.GetEntity(ln->point[1]);
Entity *p = SK.GetEntity(hpt); EntityBase *p = SK.GetEntity(hpt);
if(wrkpl.v == Entity::FREE_IN_3D.v) { if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
ExprVector ep = p->PointGetExprs(); ExprVector ep = p->PointGetExprs();
ExprVector ea = a->PointGetExprs(); ExprVector ea = a->PointGetExprs();
@ -76,11 +76,11 @@ Expr *ConstraintBase::PointPlaneDistance(ExprVector p, hEntity hpl) {
} }
Expr *ConstraintBase::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) { Expr *ConstraintBase::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) {
Entity *pa = SK.GetEntity(hpa); EntityBase *pa = SK.GetEntity(hpa);
Entity *pb = SK.GetEntity(hpb); EntityBase *pb = SK.GetEntity(hpb);
if(!(pa->IsPoint() && pb->IsPoint())) oops(); if(!(pa->IsPoint() && pb->IsPoint())) oops();
if(wrkpl.v == Entity::FREE_IN_3D.v) { if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
// This is true distance // This is true distance
ExprVector ea, eb, eab; ExprVector ea, eb, eab;
ea = pa->PointGetExprs(); ea = pa->PointGetExprs();
@ -109,11 +109,11 @@ Expr *ConstraintBase::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) {
Expr *ConstraintBase::DirectionCosine(hEntity wrkpl, Expr *ConstraintBase::DirectionCosine(hEntity wrkpl,
ExprVector ae, ExprVector be) ExprVector ae, ExprVector be)
{ {
if(wrkpl.v == Entity::FREE_IN_3D.v) { if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
Expr *mags = (ae.Magnitude())->Times(be.Magnitude()); Expr *mags = (ae.Magnitude())->Times(be.Magnitude());
return (ae.Dot(be))->Div(mags); return (ae.Dot(be))->Div(mags);
} else { } else {
Entity *w = SK.GetEntity(wrkpl); EntityBase *w = SK.GetEntity(wrkpl);
ExprVector u = w->Normal()->NormalExprsU(); ExprVector u = w->Normal()->NormalExprsU();
ExprVector v = w->Normal()->NormalExprsV(); ExprVector v = w->Normal()->NormalExprsV();
Expr *ua = u.Dot(ae); Expr *ua = u.Dot(ae);
@ -130,7 +130,7 @@ Expr *ConstraintBase::DirectionCosine(hEntity wrkpl,
ExprVector ConstraintBase::PointInThreeSpace(hEntity workplane, ExprVector ConstraintBase::PointInThreeSpace(hEntity workplane,
Expr *u, Expr *v) Expr *u, Expr *v)
{ {
Entity *w = SK.GetEntity(workplane); EntityBase *w = SK.GetEntity(workplane);
ExprVector ub = w->Normal()->NormalExprsU(); ExprVector ub = w->Normal()->NormalExprsU();
ExprVector vb = w->Normal()->NormalExprsV(); ExprVector vb = w->Normal()->NormalExprsV();
@ -144,7 +144,7 @@ void ConstraintBase::ModifyToSatisfy(void) {
Vector a = SK.GetEntity(entityA)->VectorGetNum(); Vector a = SK.GetEntity(entityA)->VectorGetNum();
Vector b = SK.GetEntity(entityB)->VectorGetNum(); Vector b = SK.GetEntity(entityB)->VectorGetNum();
if(other) a = a.ScaledBy(-1); if(other) a = a.ScaledBy(-1);
if(workplane.v != Entity::FREE_IN_3D.v) { if(workplane.v != EntityBase::FREE_IN_3D.v) {
a = a.ProjectVectorInto(workplane); a = a.ProjectVectorInto(workplane);
b = b.ProjectVectorInto(workplane); b = b.ProjectVectorInto(workplane);
} }
@ -202,7 +202,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
case PT_FACE_DISTANCE: { case PT_FACE_DISTANCE: {
ExprVector pt = SK.GetEntity(ptA)->PointGetExprs(); ExprVector pt = SK.GetEntity(ptA)->PointGetExprs();
Entity *f = SK.GetEntity(entityA); EntityBase *f = SK.GetEntity(entityA);
ExprVector p0 = f->FaceGetPointExprs(); ExprVector p0 = f->FaceGetPointExprs();
ExprVector n = f->FaceGetNormalExprs(); ExprVector n = f->FaceGetNormalExprs();
AddEq(l, (pt.Minus(p0)).Dot(n)->Minus(exA), 0); AddEq(l, (pt.Minus(p0)).Dot(n)->Minus(exA), 0);
@ -210,8 +210,8 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case EQUAL_LENGTH_LINES: { case EQUAL_LENGTH_LINES: {
Entity *a = SK.GetEntity(entityA); EntityBase *a = SK.GetEntity(entityA);
Entity *b = SK.GetEntity(entityB); EntityBase *b = SK.GetEntity(entityB);
AddEq(l, Distance(workplane, a->point[0], a->point[1])->Minus( AddEq(l, Distance(workplane, a->point[0], a->point[1])->Minus(
Distance(workplane, b->point[0], b->point[1])), 0); Distance(workplane, b->point[0], b->point[1])), 0);
break; break;
@ -220,7 +220,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
// These work on distance squared, since the pt-line distances are // These work on distance squared, since the pt-line distances are
// signed, and we want the absolute value. // signed, and we want the absolute value.
case EQ_LEN_PT_LINE_D: { case EQ_LEN_PT_LINE_D: {
Entity *forLen = SK.GetEntity(entityA); EntityBase *forLen = SK.GetEntity(entityA);
Expr *d1 = Distance(workplane, forLen->point[0], forLen->point[1]); Expr *d1 = Distance(workplane, forLen->point[0], forLen->point[1]);
Expr *d2 = PointLineDistance(workplane, ptA, entityB); Expr *d2 = PointLineDistance(workplane, ptA, entityB);
AddEq(l, (d1->Square())->Minus(d2->Square()), 0); AddEq(l, (d1->Square())->Minus(d2->Square()), 0);
@ -234,8 +234,8 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case LENGTH_RATIO: { case LENGTH_RATIO: {
Entity *a = SK.GetEntity(entityA); EntityBase *a = SK.GetEntity(entityA);
Entity *b = SK.GetEntity(entityB); EntityBase *b = SK.GetEntity(entityB);
Expr *la = Distance(workplane, a->point[0], a->point[1]); Expr *la = Distance(workplane, a->point[0], a->point[1]);
Expr *lb = Distance(workplane, b->point[0], b->point[1]); Expr *lb = Distance(workplane, b->point[0], b->point[1]);
AddEq(l, (la->Div(lb))->Minus(exA), 0); AddEq(l, (la->Div(lb))->Minus(exA), 0);
@ -243,23 +243,23 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case DIAMETER: { case DIAMETER: {
Entity *circle = SK.GetEntity(entityA); EntityBase *circle = SK.GetEntity(entityA);
Expr *r = circle->CircleGetRadiusExpr(); Expr *r = circle->CircleGetRadiusExpr();
AddEq(l, (r->Times(Expr::From(2)))->Minus(exA), 0); AddEq(l, (r->Times(Expr::From(2)))->Minus(exA), 0);
break; break;
} }
case EQUAL_RADIUS: { case EQUAL_RADIUS: {
Entity *c1 = SK.GetEntity(entityA); EntityBase *c1 = SK.GetEntity(entityA);
Entity *c2 = SK.GetEntity(entityB); EntityBase *c2 = SK.GetEntity(entityB);
AddEq(l, (c1->CircleGetRadiusExpr())->Minus( AddEq(l, (c1->CircleGetRadiusExpr())->Minus(
c2->CircleGetRadiusExpr()), 0); c2->CircleGetRadiusExpr()), 0);
break; break;
} }
case EQUAL_LINE_ARC_LEN: { case EQUAL_LINE_ARC_LEN: {
Entity *line = SK.GetEntity(entityA), EntityBase *line = SK.GetEntity(entityA),
*arc = SK.GetEntity(entityB); *arc = SK.GetEntity(entityB);
// Get the line length // Get the line length
ExprVector l0 = SK.GetEntity(line->point[0])->PointGetExprs(), ExprVector l0 = SK.GetEntity(line->point[0])->PointGetExprs(),
@ -267,9 +267,9 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
Expr *ll = (l1.Minus(l0)).Magnitude(); Expr *ll = (l1.Minus(l0)).Magnitude();
// And get the arc radius, and the cosine of its angle // And get the arc radius, and the cosine of its angle
Entity *ao = SK.GetEntity(arc->point[0]), EntityBase *ao = SK.GetEntity(arc->point[0]),
*as = SK.GetEntity(arc->point[1]), *as = SK.GetEntity(arc->point[1]),
*af = SK.GetEntity(arc->point[2]); *af = SK.GetEntity(arc->point[2]);
ExprVector aos = (as->PointGetExprs()).Minus(ao->PointGetExprs()), ExprVector aos = (as->PointGetExprs()).Minus(ao->PointGetExprs()),
aof = (af->PointGetExprs()).Minus(ao->PointGetExprs()); aof = (af->PointGetExprs()).Minus(ao->PointGetExprs());
@ -301,9 +301,9 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case POINTS_COINCIDENT: { case POINTS_COINCIDENT: {
Entity *a = SK.GetEntity(ptA); EntityBase *a = SK.GetEntity(ptA);
Entity *b = SK.GetEntity(ptB); EntityBase *b = SK.GetEntity(ptB);
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane.v == EntityBase::FREE_IN_3D.v) {
ExprVector pa = a->PointGetExprs(); ExprVector pa = a->PointGetExprs();
ExprVector pb = b->PointGetExprs(); ExprVector pb = b->PointGetExprs();
AddEq(l, pa.x->Minus(pb.x), 0); AddEq(l, pa.x->Minus(pb.x), 0);
@ -329,7 +329,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
case PT_ON_FACE: { case PT_ON_FACE: {
// a plane, n dot (p - p0) = 0 // a plane, n dot (p - p0) = 0
ExprVector p = SK.GetEntity(ptA)->PointGetExprs(); ExprVector p = SK.GetEntity(ptA)->PointGetExprs();
Entity *f = SK.GetEntity(entityA); EntityBase *f = SK.GetEntity(entityA);
ExprVector p0 = f->FaceGetPointExprs(); ExprVector p0 = f->FaceGetPointExprs();
ExprVector n = f->FaceGetNormalExprs(); ExprVector n = f->FaceGetNormalExprs();
AddEq(l, (p.Minus(p0)).Dot(n), 0); AddEq(l, (p.Minus(p0)).Dot(n), 0);
@ -337,11 +337,11 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case PT_ON_LINE: case PT_ON_LINE:
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane.v == EntityBase::FREE_IN_3D.v) {
Entity *ln = SK.GetEntity(entityA); EntityBase *ln = SK.GetEntity(entityA);
Entity *a = SK.GetEntity(ln->point[0]); EntityBase *a = SK.GetEntity(ln->point[0]);
Entity *b = SK.GetEntity(ln->point[1]); EntityBase *b = SK.GetEntity(ln->point[1]);
Entity *p = SK.GetEntity(ptA); EntityBase *p = SK.GetEntity(ptA);
ExprVector ep = p->PointGetExprs(); ExprVector ep = p->PointGetExprs();
ExprVector ea = a->PointGetExprs(); ExprVector ea = a->PointGetExprs();
@ -370,10 +370,10 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
case PT_ON_CIRCLE: { case PT_ON_CIRCLE: {
// This actually constrains the point to lie on the cylinder. // This actually constrains the point to lie on the cylinder.
Entity *circle = SK.GetEntity(entityA); EntityBase *circle = SK.GetEntity(entityA);
ExprVector center = SK.GetEntity(circle->point[0])->PointGetExprs(); ExprVector center = SK.GetEntity(circle->point[0])->PointGetExprs();
ExprVector pt = SK.GetEntity(ptA)->PointGetExprs(); ExprVector pt = SK.GetEntity(ptA)->PointGetExprs();
Entity *normal = SK.GetEntity(circle->normal); EntityBase *normal = SK.GetEntity(circle->normal);
ExprVector u = normal->NormalExprsU(), ExprVector u = normal->NormalExprsU(),
v = normal->NormalExprsV(); v = normal->NormalExprsV();
@ -388,8 +388,8 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case AT_MIDPOINT: case AT_MIDPOINT:
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane.v == EntityBase::FREE_IN_3D.v) {
Entity *ln = SK.GetEntity(entityA); EntityBase *ln = SK.GetEntity(entityA);
ExprVector a = SK.GetEntity(ln->point[0])->PointGetExprs(); ExprVector a = SK.GetEntity(ln->point[0])->PointGetExprs();
ExprVector b = SK.GetEntity(ln->point[1])->PointGetExprs(); ExprVector b = SK.GetEntity(ln->point[1])->PointGetExprs();
ExprVector m = (a.Plus(b)).ScaledBy(Expr::From(0.5)); ExprVector m = (a.Plus(b)).ScaledBy(Expr::From(0.5));
@ -403,9 +403,9 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
AddEq(l, PointPlaneDistance(m, entityB), 0); AddEq(l, PointPlaneDistance(m, entityB), 0);
} }
} else { } else {
Entity *ln = SK.GetEntity(entityA); EntityBase *ln = SK.GetEntity(entityA);
Entity *a = SK.GetEntity(ln->point[0]); EntityBase *a = SK.GetEntity(ln->point[0]);
Entity *b = SK.GetEntity(ln->point[1]); EntityBase *b = SK.GetEntity(ln->point[1]);
Expr *au, *av, *bu, *bv; Expr *au, *av, *bu, *bv;
a->PointGetExprsInWorkplane(workplane, &au, &av); a->PointGetExprsInWorkplane(workplane, &au, &av);
@ -414,7 +414,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
Expr *mv = Expr::From(0.5)->Times(av->Plus(bv)); Expr *mv = Expr::From(0.5)->Times(av->Plus(bv));
if(ptA.v) { if(ptA.v) {
Entity *p = SK.GetEntity(ptA); EntityBase *p = SK.GetEntity(ptA);
Expr *pu, *pv; Expr *pu, *pv;
p->PointGetExprsInWorkplane(workplane, &pu, &pv); p->PointGetExprsInWorkplane(workplane, &pu, &pv);
AddEq(l, pu->Minus(mu), 0); AddEq(l, pu->Minus(mu), 0);
@ -427,10 +427,10 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
break; break;
case SYMMETRIC: case SYMMETRIC:
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane.v == EntityBase::FREE_IN_3D.v) {
Entity *plane = SK.GetEntity(entityA); EntityBase *plane = SK.GetEntity(entityA);
Entity *ea = SK.GetEntity(ptA); EntityBase *ea = SK.GetEntity(ptA);
Entity *eb = SK.GetEntity(ptB); EntityBase *eb = SK.GetEntity(ptB);
ExprVector a = ea->PointGetExprs(); ExprVector a = ea->PointGetExprs();
ExprVector b = eb->PointGetExprs(); ExprVector b = eb->PointGetExprs();
@ -447,9 +447,9 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
AddEq(l, au->Minus(bu), 1); AddEq(l, au->Minus(bu), 1);
AddEq(l, av->Minus(bv), 2); AddEq(l, av->Minus(bv), 2);
} else { } else {
Entity *plane = SK.GetEntity(entityA); EntityBase *plane = SK.GetEntity(entityA);
Entity *a = SK.GetEntity(ptA); EntityBase *a = SK.GetEntity(ptA);
Entity *b = SK.GetEntity(ptB); EntityBase *b = SK.GetEntity(ptB);
Expr *au, *av, *bu, *bv; Expr *au, *av, *bu, *bv;
a->PointGetExprsInWorkplane(workplane, &au, &av); a->PointGetExprsInWorkplane(workplane, &au, &av);
@ -464,7 +464,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
// to the symmetry pane's normal (i.e., that lies in the // to the symmetry pane's normal (i.e., that lies in the
// plane of symmetry). The line connecting the points is // plane of symmetry). The line connecting the points is
// perpendicular to that constructed vector. // perpendicular to that constructed vector.
Entity *w = SK.GetEntity(workplane); EntityBase *w = SK.GetEntity(workplane);
ExprVector u = w->Normal()->NormalExprsU(); ExprVector u = w->Normal()->NormalExprsU();
ExprVector v = w->Normal()->NormalExprsV(); ExprVector v = w->Normal()->NormalExprsV();
@ -479,8 +479,8 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
case SYMMETRIC_HORIZ: case SYMMETRIC_HORIZ:
case SYMMETRIC_VERT: { case SYMMETRIC_VERT: {
Entity *a = SK.GetEntity(ptA); EntityBase *a = SK.GetEntity(ptA);
Entity *b = SK.GetEntity(ptB); EntityBase *b = SK.GetEntity(ptB);
Expr *au, *av, *bu, *bv; Expr *au, *av, *bu, *bv;
a->PointGetExprsInWorkplane(workplane, &au, &av); a->PointGetExprsInWorkplane(workplane, &au, &av);
@ -497,16 +497,16 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case SYMMETRIC_LINE: { case SYMMETRIC_LINE: {
Entity *pa = SK.GetEntity(ptA); EntityBase *pa = SK.GetEntity(ptA);
Entity *pb = SK.GetEntity(ptB); EntityBase *pb = SK.GetEntity(ptB);
Expr *pau, *pav, *pbu, *pbv; Expr *pau, *pav, *pbu, *pbv;
pa->PointGetExprsInWorkplane(workplane, &pau, &pav); pa->PointGetExprsInWorkplane(workplane, &pau, &pav);
pb->PointGetExprsInWorkplane(workplane, &pbu, &pbv); pb->PointGetExprsInWorkplane(workplane, &pbu, &pbv);
Entity *ln = SK.GetEntity(entityA); EntityBase *ln = SK.GetEntity(entityA);
Entity *la = SK.GetEntity(ln->point[0]); EntityBase *la = SK.GetEntity(ln->point[0]);
Entity *lb = SK.GetEntity(ln->point[1]); EntityBase *lb = SK.GetEntity(ln->point[1]);
Expr *lau, *lav, *lbu, *lbv; Expr *lau, *lav, *lbu, *lbv;
la->PointGetExprsInWorkplane(workplane, &lau, &lav); la->PointGetExprsInWorkplane(workplane, &lau, &lav);
lb->PointGetExprsInWorkplane(workplane, &lbu, &lbv); lb->PointGetExprsInWorkplane(workplane, &lbu, &lbv);
@ -533,15 +533,15 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
case VERTICAL: { case VERTICAL: {
hEntity ha, hb; hEntity ha, hb;
if(entityA.v) { if(entityA.v) {
Entity *e = SK.GetEntity(entityA); EntityBase *e = SK.GetEntity(entityA);
ha = e->point[0]; ha = e->point[0];
hb = e->point[1]; hb = e->point[1];
} else { } else {
ha = ptA; ha = ptA;
hb = ptB; hb = ptB;
} }
Entity *a = SK.GetEntity(ha); EntityBase *a = SK.GetEntity(ha);
Entity *b = SK.GetEntity(hb); EntityBase *b = SK.GetEntity(hb);
Expr *au, *av, *bu, *bv; Expr *au, *av, *bu, *bv;
a->PointGetExprsInWorkplane(workplane, &au, &av); a->PointGetExprsInWorkplane(workplane, &au, &av);
@ -552,10 +552,10 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case SAME_ORIENTATION: { case SAME_ORIENTATION: {
Entity *a = SK.GetEntity(entityA); EntityBase *a = SK.GetEntity(entityA);
Entity *b = SK.GetEntity(entityB); EntityBase *b = SK.GetEntity(entityB);
if(b->group.v != group.v) { if(b->group.v != group.v) {
SWAP(Entity *, a, b); SWAP(EntityBase *, a, b);
} }
ExprVector au = a->NormalExprsU(), ExprVector au = a->NormalExprsU(),
@ -581,8 +581,8 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
case PERPENDICULAR: case PERPENDICULAR:
case ANGLE: { case ANGLE: {
Entity *a = SK.GetEntity(entityA); EntityBase *a = SK.GetEntity(entityA);
Entity *b = SK.GetEntity(entityB); EntityBase *b = SK.GetEntity(entityB);
ExprVector ae = a->VectorGetExprs(); ExprVector ae = a->VectorGetExprs();
ExprVector be = b->VectorGetExprs(); ExprVector be = b->VectorGetExprs();
if(other) ae = ae.ScaledBy(Expr::From(-1)); if(other) ae = ae.ScaledBy(Expr::From(-1));
@ -602,10 +602,10 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case EQUAL_ANGLE: { case EQUAL_ANGLE: {
Entity *a = SK.GetEntity(entityA); EntityBase *a = SK.GetEntity(entityA);
Entity *b = SK.GetEntity(entityB); EntityBase *b = SK.GetEntity(entityB);
Entity *c = SK.GetEntity(entityC); EntityBase *c = SK.GetEntity(entityC);
Entity *d = SK.GetEntity(entityD); EntityBase *d = SK.GetEntity(entityD);
ExprVector ae = a->VectorGetExprs(); ExprVector ae = a->VectorGetExprs();
ExprVector be = b->VectorGetExprs(); ExprVector be = b->VectorGetExprs();
ExprVector ce = c->VectorGetExprs(); ExprVector ce = c->VectorGetExprs();
@ -621,8 +621,8 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case ARC_LINE_TANGENT: { case ARC_LINE_TANGENT: {
Entity *arc = SK.GetEntity(entityA); EntityBase *arc = SK.GetEntity(entityA);
Entity *line = SK.GetEntity(entityB); EntityBase *line = SK.GetEntity(entityB);
ExprVector ac = SK.GetEntity(arc->point[0])->PointGetExprs(); ExprVector ac = SK.GetEntity(arc->point[0])->PointGetExprs();
ExprVector ap = ExprVector ap =
@ -636,8 +636,8 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case CUBIC_LINE_TANGENT: { case CUBIC_LINE_TANGENT: {
Entity *cubic = SK.GetEntity(entityA); EntityBase *cubic = SK.GetEntity(entityA);
Entity *line = SK.GetEntity(entityB); EntityBase *line = SK.GetEntity(entityB);
ExprVector endpoint = ExprVector endpoint =
SK.GetEntity(cubic->point[other ? 3 : 0])->PointGetExprs(); SK.GetEntity(cubic->point[other ? 3 : 0])->PointGetExprs();
@ -648,11 +648,11 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
ExprVector b = line->VectorGetExprs(); ExprVector b = line->VectorGetExprs();
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane.v == EntityBase::FREE_IN_3D.v) {
AddEq(l, VectorsParallel(0, a, b), 0); AddEq(l, VectorsParallel(0, a, b), 0);
AddEq(l, VectorsParallel(1, a, b), 1); AddEq(l, VectorsParallel(1, a, b), 1);
} else { } else {
Entity *w = SK.GetEntity(workplane); EntityBase *w = SK.GetEntity(workplane);
ExprVector wn = w->Normal()->NormalExprsN(); ExprVector wn = w->Normal()->NormalExprsN();
AddEq(l, (a.Cross(b)).Dot(wn), 0); AddEq(l, (a.Cross(b)).Dot(wn), 0);
} }
@ -660,18 +660,18 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
} }
case PARALLEL: { case PARALLEL: {
Entity *ea = SK.GetEntity(entityA), *eb = SK.GetEntity(entityB); EntityBase *ea = SK.GetEntity(entityA), *eb = SK.GetEntity(entityB);
if(eb->group.v != group.v) { if(eb->group.v != group.v) {
SWAP(Entity *, ea, eb); SWAP(EntityBase *, ea, eb);
} }
ExprVector a = ea->VectorGetExprs(); ExprVector a = ea->VectorGetExprs();
ExprVector b = eb->VectorGetExprs(); ExprVector b = eb->VectorGetExprs();
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane.v == EntityBase::FREE_IN_3D.v) {
AddEq(l, VectorsParallel(0, a, b), 0); AddEq(l, VectorsParallel(0, a, b), 0);
AddEq(l, VectorsParallel(1, a, b), 1); AddEq(l, VectorsParallel(1, a, b), 1);
} else { } else {
Entity *w = SK.GetEntity(workplane); EntityBase *w = SK.GetEntity(workplane);
ExprVector wn = w->Normal()->NormalExprsN(); ExprVector wn = w->Normal()->NormalExprsN();
AddEq(l, (a.Cross(b)).Dot(wn), 0); AddEq(l, (a.Cross(b)).Dot(wn), 0);
} }

View File

@ -354,7 +354,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
r = n.Normal(0); r = n.Normal(0);
d = n.Normal(1); d = n.Normal(1);
} else if(type == PT_IN_PLANE) { } else if(type == PT_IN_PLANE) {
Entity *n = SK.GetEntity(entityA)->Normal(); EntityBase *n = SK.GetEntity(entityA)->Normal();
r = n->NormalU(); r = n->NormalU();
d = n->NormalV(); d = n->NormalV();
} else { } else {
@ -434,7 +434,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
rn = gn; rn = gn;
ru = gu; ru = gu;
} else { } else {
Entity *normal = SK.GetEntity(workplane)->Normal(); EntityBase *normal = SK.GetEntity(workplane)->Normal();
rn = normal->NormalN(); rn = normal->NormalN();
ru = normal->NormalV(); // ru meaning r_up, not u/v ru = normal->NormalV(); // ru meaning r_up, not u/v
} }
@ -485,7 +485,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
v = gu; v = gu;
n = gn; n = gn;
} else { } else {
Entity *wn = SK.GetEntity(workplane)->Normal(); EntityBase *wn = SK.GetEntity(workplane)->Normal();
u = wn->NormalU(); u = wn->NormalU();
v = wn->NormalV(); v = wn->NormalV();
n = wn->NormalN(); n = wn->NormalN();

View File

@ -173,6 +173,30 @@ bool Entity::IsVisible(void) {
return true; return true;
} }
void Entity::CalculateNumerical(bool forExport) {
if(IsPoint()) actPoint = PointGetNum();
if(IsNormal()) actNormal = NormalGetNum();
if(type == DISTANCE || type == DISTANCE_N_COPY) {
actDistance = DistanceGetNum();
}
if(IsFace()) {
actPoint = FaceGetPointNum();
Vector n = FaceGetNormalNum();
actNormal = Quaternion::From(0, n.x, n.y, n.z);
}
if(forExport) {
// Visibility in copied import entities follows source file
actVisible = IsVisible();
} else {
// Copied entities within a file are always visible
actVisible = true;
}
}
bool Entity::PointIsFromReferences(void) {
return h.request().IsFromReferences();
}
void Entity::GenerateBezierCurves(SBezierList *sbl) { void Entity::GenerateBezierCurves(SBezierList *sbl) {
SBezier sb; SBezier sb;

1
dsc.h
View File

@ -233,6 +233,7 @@ public:
} else if(hm.v < t->h.v) { } else if(hm.v < t->h.v) {
first = mid + 1; first = mid + 1;
} else { } else {
dbp("can't insert in list; is handle %d not unique?", t->h.v);
oops(); oops();
} }
} }

View File

@ -162,7 +162,7 @@ void EntityBase::DistanceForceTo(double v) {
} else oops(); } else oops();
} }
Entity *EntityBase::Normal(void) { EntityBase *EntityBase::Normal(void) {
return SK.GetEntity(normal); return SK.GetEntity(normal);
} }
@ -202,8 +202,8 @@ Quaternion EntityBase::NormalGetNum(void) {
break; break;
case NORMAL_IN_2D: { case NORMAL_IN_2D: {
Entity *wrkpl = SK.GetEntity(workplane); EntityBase *wrkpl = SK.GetEntity(workplane);
Entity *norm = SK.GetEntity(wrkpl->normal); EntityBase *norm = SK.GetEntity(wrkpl->normal);
q = norm->NormalGetNum(); q = norm->NormalGetNum();
break; break;
} }
@ -286,8 +286,8 @@ ExprQuaternion EntityBase::NormalGetExprs(void) {
break; break;
case NORMAL_IN_2D: { case NORMAL_IN_2D: {
Entity *wrkpl = SK.GetEntity(workplane); EntityBase *wrkpl = SK.GetEntity(workplane);
Entity *norm = SK.GetEntity(wrkpl->normal); EntityBase *norm = SK.GetEntity(wrkpl->normal);
q = norm->NormalGetExprs(); q = norm->NormalGetExprs();
break; break;
} }
@ -315,10 +315,6 @@ ExprQuaternion EntityBase::NormalGetExprs(void) {
return q; return q;
} }
bool EntityBase::PointIsFromReferences(void) {
return h.request().IsFromReferences();
}
void EntityBase::PointForceTo(Vector p) { void EntityBase::PointForceTo(Vector p) {
switch(type) { switch(type) {
case POINT_IN_3D: case POINT_IN_3D:
@ -328,7 +324,7 @@ void EntityBase::PointForceTo(Vector p) {
break; break;
case POINT_IN_2D: { case POINT_IN_2D: {
Entity *c = SK.GetEntity(workplane); EntityBase *c = SK.GetEntity(workplane);
p = p.Minus(c->WorkplaneGetOffset()); p = p.Minus(c->WorkplaneGetOffset());
SK.GetParam(param[0])->val = p.Dot(c->Normal()->NormalU()); SK.GetParam(param[0])->val = p.Dot(c->Normal()->NormalU());
SK.GetParam(param[1])->val = p.Dot(c->Normal()->NormalV()); SK.GetParam(param[1])->val = p.Dot(c->Normal()->NormalV());
@ -390,7 +386,7 @@ Vector EntityBase::PointGetNum(void) {
break; break;
case POINT_IN_2D: { case POINT_IN_2D: {
Entity *c = SK.GetEntity(workplane); EntityBase *c = SK.GetEntity(workplane);
Vector u = c->Normal()->NormalU(); Vector u = c->Normal()->NormalU();
Vector v = c->Normal()->NormalV(); Vector v = c->Normal()->NormalV();
p = u.ScaledBy(SK.GetParam(param[0])->val); p = u.ScaledBy(SK.GetParam(param[0])->val);
@ -439,7 +435,7 @@ ExprVector EntityBase::PointGetExprs(void) {
break; break;
case POINT_IN_2D: { case POINT_IN_2D: {
Entity *c = SK.GetEntity(workplane); EntityBase *c = SK.GetEntity(workplane);
ExprVector u = c->Normal()->NormalExprsU(); ExprVector u = c->Normal()->NormalExprsU();
ExprVector v = c->Normal()->NormalExprsV(); ExprVector v = c->Normal()->NormalExprsV();
r = c->WorkplaneGetOffsetExprs(); r = c->WorkplaneGetOffsetExprs();
@ -488,7 +484,7 @@ void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) {
*v = Expr::From(param[1]); *v = Expr::From(param[1]);
} else { } else {
// Get the offset and basis vectors for this weird exotic csys. // Get the offset and basis vectors for this weird exotic csys.
Entity *w = SK.GetEntity(wrkpl); EntityBase *w = SK.GetEntity(wrkpl);
ExprVector wp = w->WorkplaneGetOffsetExprs(); ExprVector wp = w->WorkplaneGetOffsetExprs();
ExprVector wu = w->Normal()->NormalExprsU(); ExprVector wu = w->Normal()->NormalExprsU();
ExprVector wv = w->Normal()->NormalExprsV(); ExprVector wv = w->Normal()->NormalExprsV();
@ -696,23 +692,3 @@ void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) {
} }
} }
void Entity::CalculateNumerical(bool forExport) {
if(IsPoint()) actPoint = PointGetNum();
if(IsNormal()) actNormal = NormalGetNum();
if(type == DISTANCE || type == DISTANCE_N_COPY) {
actDistance = DistanceGetNum();
}
if(IsFace()) {
actPoint = FaceGetPointNum();
Vector n = FaceGetNormalNum();
actNormal = Quaternion::From(0, n.x, n.y, n.z);
}
if(forExport) {
// Visibility in copied import entities follows source file
actVisible = IsVisible();
} else {
// Copied entities within a file are always visible
actVisible = true;
}
}

403
exposed/DOC.txt Normal file
View File

@ -0,0 +1,403 @@
A sketch in SolveSpace consists of three basic elements: parameters,
entities, and constraints.
A parameter (Slvs_Param) is a single real number, represented internally
by a double-precision floating point variable. The parameters are unknown
variables that the solver modifies in order to satisfy the constraints.
An entity (Slvs_Entity) is a geometric thing, like a point or a line
segment or a circle. Entities are defined in terms of parameters,
and in terms of other entities. For example, a point in three-space
is represented by three parameters, corresponding to its x, y, and z
coordinates in our base coordinate frame. A line segment is represented
by two point entities, corresponding to its endpoints.
A constraint (Slvs_Constraint) is a geometric property of an entity,
or a relationship among multiple entities. For example, a point-point
distance constraint will set the distance between two point entities.
Paramters, entities, and constraints are typically referenced by their
handles (Slvs_hParam, Slvs_hEntity, Slvs_hConstraint). These handles are
32-bit integer values starting from 1. Each object has a unique handle
within its type (but it's acceptable, for example to have a constraint
with an Slvs_hConstraint of 7, and also to have an entity with an
Slvs_hEntity of 7). The use of handles instead of pointers helps to
avoid memory corruption.
Entities and constraints are assigned into groups. A group is a set of
entities and constraints that is solved simultaneously. In a parametric
CAD system, a single group would typically correspond to a single sketch.
Constraints within a group may refer to entities outside that group,
but only the entities within that group will be modified by the solver.
(Consider point A in group 1, and point B in group 2. We have a constraint
in group 2 that makes the points coincident. When we solve group 2, the
solver is allowed to move point B to place it on top of point A. It is
not allowed to move point A to put it on top of point B, because point
A is outside the group being solved.)
This corresponds to the typical structure of a parametric CAD system. In a
later sketch, we may constrain our entities against existing geometry from
earlier sketches. The constraints will move the entities in our current
sketch, but will not change the geometry from the earlier sketches.
To use the solver, we first define a set of parameters, entities,
and constraints. We provide an initial guess for each parameter; this
will improve convergence, and also determine which solution gets chosen
when (finitely many) multiple solutions exist. Typically, these initial
guesses are provided by the initial configuration in which the user drew
the entities before constraining them.
We then run the solver for a given group. The entities within that group
are modified in an attempt to satisfy the constraints.
After running the solver, there are three possible outcomes:
* All constraints were satisfied to within our numerical
tolerance (i.e., success).
* The solver can prove that two constraints are inconsistent (for
example, if a line with nonzero length is constrained both
horizontal and vertical). In that case, a list of inconsistent
constraints is generated.
* The solver cannot prove that two constraints are inconsistent, but
it cannot find a solution. In that case, the list of unsatisfied
constraints is generated.
SLVS_E_POINT_IN_3D
A point in 3d. Defined by three parameters:
param[0] the point's x coordinate
param[1] y
param[1] z
SLVS_E_POINT_IN_2D
A point within a workplane. Defined by the workplane
wrkpl
and by two parameters
param[0] the point's u coordinate
param[1] v
within the coordinate system of the workplane. For example, if the
workplane is the zx plane, then u = z and v = x. If the workplane is
parallel to the zx plane, but translated so that the workplane's
origin is (3, 4, 5), then u = z - 5 and v = x - 3.
SLVS_E_NORMAL_IN_3D
A normal. In SolveSpace, "normals" represent a 3x3 rotation matrix
from our base coordinate system to a new frame. Defined by the
unit quaternion
param[0] w
param[1] x
param[2] y
param[3] z
where the quaternion is given by w + x*i + y*j + z*k.
It is useful to think of this quaternion as representing a plane
through the origin. This plane has three associated vectors: basis
vectors U, V that lie within the plane, and normal N that is
perpendicular to it. This means that
[ U V N ]'
defines a 3x3 rotation matrix. So U, V, and N all have unit length,
and are orthogonal so that
U cross V = N
V cross N = U
N cross U = V
Convenience functions (Slvs_Quaternion*) are provided to convert
between this representation as vectors U, V, N and the unit
quaternion.
A unit quaternion has only 3 degrees of freedom, but is specified in
terms of 4 parameters. An extra constraint is therefore generated
implicitly, that
w^2 + x^2 + y^2 + z^2 = 1
SLVS_E_NORMAL_IN_2D
A normal within a workplane. This is identical to the workplane's
normal, so it is simply defined by
wrkpl
This entity type is used, for example, to define a circle that lies
within a workplane. The circle's normal is the same as the workplane's
normal, so we can use an SLVS_E_NORMAL_IN_2D to copy the workplane's
normal.
SLVS_E_DISTANCE
A distance. This entity is used to define the radius of a circle, by
a single parameter
param[0] r
SLVS_E_WORKPLANE
An oriented plane, somewhere in 3d. This entity therefore has 6
degrees of freedom: three translational, and three rotational. It is
specified in terms of its origin
point[0] origin
and a normal
normal
The normal describes three vectors U, V, N, as discussed in the
documentation for SLVS_E_NORMAL_IN_3D. The plane is therefore given
by the equation
p = origin + s*U + t*V
for any scalar s and t.
SLVS_E_LINE_SEGMENT
A line segment between two endpoints
point[0]
point[1]
SLVS_E_CUBIC
A nonrational cubic Bezier segment
point[0] starting point P0
point[1] control point P1
point[2] control point P2
point[3] ending point P3
The curve then has equation
p(t) = P0*(1 - t)^3 + 3*P1*(1 - t)^2*t + 3*P2*(1 - t)*t^2 + P3*t^3
as t goes from 0 to 1.
SLVS_E_CIRCLE
A complete circle. The circle lies within a plane with normal
normal
The circle is centered at
point[0]
The circle's radius is
distance
SLVS_E_ARC_OF_CIRCLE
An arc of a circle. An arc must always lie within a workplane; it
cannot be free in 3d. So it is specified with a workplane
wrkpl
It is then defined by three points
point[0] center of the circle
point[1] begining of the arc
point[2] end of the arc
The arc runs counter-clockwise from its beginning to its end (with
the workplane's normal pointing owards the viewer). If the begining
and end of the arc are coincident, then the arc is considered to
represent a full circle.
This representation has an extra degree of freedom. An extra
constraint is therefore generated implicitly, so that
distance(center, beginning) = distance(center, end)
Many constraints can apply either in 3d, or in a workplane. This is
determined by the wrkpl member of the constraint. If that member is set
to SLVS_FREE_IN_3D, then the constraint applies in 3d. If that member
is set equal to a workplane, the the constraint applies projected into
that workplane. (For example, a constraint on the distance between two
points actually applies to the projected distance).
Constraints that may be used in 3d or projected into a workplane are
marked with a single star (*). Constraints that must always be used with
a workplane are marked with a double star (**). Constraints that ignore
the wrkpl member are marked with no star.
SLVS_C_PT_PT_DISTANCE*
The distance between points ptA and ptB is equal to valA.
SLVS_C_POINTS_COINCIDENT*
Points ptA and ptB are coincident (i.e., exactly on top of each
other).
SLVS_C_PT_PLANE_DISTANCE
The distance from point ptA to workplane entityA is equal to
valA. This is a signed distance; positive versus negative valA
correspond to a point that is above vs. below the plane.
SLVS_C_PT_LINE_DISTANCE*
The distance from point ptA to line segment entityA is equal to valA.
If the constraint is projected, then valA is a signed distance;
positive versus negative valA correspond to a point that is above
vs. below the line.
If the constraint applies in 3d, then valA must always be positive.
SLVS_C_PT_IN_PLANE
The point ptA lies in plane entityA.
SLVS_C_PT_ON_LINE*
The point ptA lies on the line entityA.
Note that this constraint removes one degree of freedom when projected
in to the plane, but two degrees of freedom in 3d.
SLVS_C_EQUAL_LENGTH_LINES*
The lines entityA and entityB have equal length.
SLVS_C_LENGTH_RATIO*
The length of line entityA divided by the length of line entityB is
equal to valA.
SLVS_C_EQ_LEN_PT_LINE_D*
The length of the line entityA is equal to the distance from point
ptA to line entityB.
SLVS_C_EQ_PT_LN_DISTANCES*
The distance from the line entityA to the point ptA is equal to the
distance from the line entityB to the point ptB.
SLVS_C_EQUAL_ANGLE*
The angle between lines entityA and entityB is equal to the angle
between lines entityC and entityD.
If other is true, then the angles are supplementary (i.e., theta1 =
180 - theta2) instead of equal.
SLVS_C_EQUAL_LINE_ARC_LEN*
The length of the line entityA is equal to the length of the circular
arc entityB.
SLVS_C_SYMMETRIC*
The points ptA and ptB are symmetric about the plane entityA. This
means that they are on opposite sides of the plane and at equal
distances from the plane, and that the line connecting ptA and ptB
is normal to the plane.
SLVS_C_SYMMETRIC_HORIZ
SLVS_C_SYMMETRIC_VERT**
The points ptA and ptB are symmetric about the horizontal or vertical
axis of the specified workplane.
SLVS_C_SYMMETRIC_LINE*
The points ptA and ptB are symmetric about the line entityA.
SLVS_C_AT_MIDPOINT*
The point ptA lies at the midpoint of the line entityA.
SLVS_C_HORIZONTAL
SLVS_C_VERTICAL**
The line connecting points ptA and ptB is horizontal or vertical.
SLVS_C_DIAMETER
The diameter of circle or arc entityA is equal to valA.
SLVS_C_PT_ON_CIRCLE
The point ptA lies on the right cylinder obtained by extruding circle
entityA normal to its plane.
SLVS_C_SAME_ORIENTATION
The normals entityA and entityB describe identical rotations. This
constraint therefore restricts three degrees of freedom.
SLVS_C_ANGLE*
The angle between lines entityA and entityB is equal to valA, where
valA is specified in degrees. This constraint equation is written
in the form
(A dot B)/(|A||B|) = cos(valA)
where A and B are vectors in the directions of lines A and B. This
equation does not specify the angle unambiguously; for example,
note that val A = +/- 90 degrees will produce the same equation.
If other is true, then the constraint is instead that
(A dot B)/(|A||B|) = -cos(valA)
SLVS_C_PERPENDICULAR*
Identical to SLVS_C_ANGLE with valA = 90 degrees.
SLVS_C_PARALLEL*
Lines entityA and entityB are parallel.
Note that this constraint removes one degree of freedom when projected
in to the plane, but two degrees of freedom in 3d.
SLVS_C_ARC_LINE_TANGENT**
The arc entityA is tangent to the line entityB. If other is false,
then the arc is tangent at its beginning (point[1]). If other is true,
then the arc is tangent at its end (point[2]).
SLVS_C_CUBIC_LINE_TANGENT*
The cubic entityA is tangent to the line entityB. If other is false,
then the cubic is tangent at its beginning (point[0]). If other is
true, then the arc is tangent at its end (point[3]).
SLVS_C_EQUAL_RADIUS
The circles or arcs entityA and entityB have equal radius.

49
exposed/Makefile Normal file
View File

@ -0,0 +1,49 @@
DEFINES = /D_WIN32_WINNT=0x500 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN32_LEAN_AND_MEAN /DWIN32 /DLIBRARY
# Use the multi-threaded static libc because libpng and zlib do; not sure if anything bad
# happens if those mix, but don't want to risk it.
CFLAGS = /W3 /nologo -MT -Iextlib -I..\..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /I.. /Zi /EHs
HEADERS = ..\solvespace.h ..\dsc.h ..\sketch.h ..\expr.h slvs.h
OBJDIR = obj
SSOBJS = $(OBJDIR)\util.obj \
$(OBJDIR)\entity.obj \
$(OBJDIR)\expr.obj \
$(OBJDIR)\constrainteq.obj \
$(OBJDIR)\system.obj \
W32OBJS = $(OBJDIR)\w32util.obj \
LIBOBJS = $(OBJDIR)\lib.obj \
LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib
all: $(OBJDIR)/ctest.exe
@cp $(OBJDIR)/ctest.exe .
ctest.exe
clean:
rm -f obj/*
$(OBJDIR)/slvs.lib: $(SSOBJS) $(LIBOBJS) $(W32OBJS)
@lib /OUT:$(OBJDIR)/slvs.lib $(SSOBJS) $(LIBOBJS) $(W32OBJS)
@echo slvs.lib
$(OBJDIR)/ctest.exe: example.c $(OBJDIR)/slvs.lib
@$(CC) $(CFLAGS) -Fe$(OBJDIR)/ctest.exe example.c $(OBJDIR)/slvs.lib $(LIBS)
@echo ctest.exe
$(SSOBJS): ..\$(@B).cpp $(HEADERS)
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\$(@B).cpp
$(W32OBJS): ..\win32\$(@B).cpp $(HEADERS)
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\win32\$(@B).cpp
$(LIBOBJS): $(@B).cpp $(HEADERS)
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp

190
exposed/example.c Normal file
View File

@ -0,0 +1,190 @@
#include <windows.h>
#include <stdio.h>
#include "slvs.h"
Slvs_System sys;
void *CheckMalloc(size_t n)
{
void *r = malloc(n);
if(!r) {
printf("out of memory!\n");
exit(-1);
}
return r;
}
//-----------------------------------------------------------------------------
// An example of a constraint in 3d. We create
//-----------------------------------------------------------------------------
void Example3d(void)
{
// This will contain a single group, which will arbitrarily number 1.
int g = 1;
// A point, initially at (x y z) = (10 10 10)
sys.param[sys.params++] = Slvs_MakeParam(1, g, 10.0);
sys.param[sys.params++] = Slvs_MakeParam(2, g, 10.0);
sys.param[sys.params++] = Slvs_MakeParam(3, g, 10.0);
sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
// and a second point at (20 20 20)
sys.param[sys.params++] = Slvs_MakeParam(4, g, 20.0);
sys.param[sys.params++] = Slvs_MakeParam(5, g, 20.0);
sys.param[sys.params++] = Slvs_MakeParam(6, g, 20.0);
sys.entity[sys.entities++] = Slvs_MakePoint3d(102, g, 4, 5, 6);
// and a line segment connecting them.
sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g,
SLVS_FREE_IN_3D, 101, 102);
// The distance between the points should be 30.0 units.
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
1, g,
SLVS_C_PT_PT_DISTANCE,
SLVS_FREE_IN_3D,
30.0,
101, 102, 0, 0);
// Let's tell the solver to keep the second point as close to constant
// as possible, instead moving the first point.
sys.dragged[0] = 4;
sys.dragged[1] = 5;
sys.dragged[2] = 6;
// Now that we have written our system, we solve.
Slvs_Solve(&sys, g);
if(sys.result == SLVS_RESULT_OKAY) {
printf("okay; now at (%.3f %.3f %.3f)\n"
" (%.3f %.3f %.3f)\n",
sys.param[0].val, sys.param[1].val, sys.param[2].val,
sys.param[3].val, sys.param[4].val, sys.param[5].val);
printf("%d DOF\n", sys.dof);
} else {
printf("solve failed");
}
}
//-----------------------------------------------------------------------------
// An example of a constraint in 2d. In an earlier group, we have created a
// workplane. Then in our group to be solved, we create a line segment, which
// we dimension to be horizontal and 2.0 units long.
//-----------------------------------------------------------------------------
void Example2d(void)
{
int g;
double qw, qx, qy, qz;
g = 1;
// First, we create our workplane. Its origin corresponds to the origin
// of our base frame (x y z) = (0 0 0)
sys.param[sys.params++] = Slvs_MakeParam(1, g, 0.0);
sys.param[sys.params++] = Slvs_MakeParam(2, g, 0.0);
sys.param[sys.params++] = Slvs_MakeParam(3, g, 0.0);
sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
// and it is parallel to the xy plane, so it has basis vectors (1 0 0)
// and (0 1 0).
Slvs_MakeQuaternion(1, 0, 0,
0, 1, 0, &qw, &qx, &qy, &qz);
sys.param[sys.params++] = Slvs_MakeParam(4, g, qw);
sys.param[sys.params++] = Slvs_MakeParam(5, g, qx);
sys.param[sys.params++] = Slvs_MakeParam(6, g, qy);
sys.param[sys.params++] = Slvs_MakeParam(7, g, qz);
sys.entity[sys.entities++] = Slvs_MakeNormal3d(102, g, 3, 4, 5, 6);
sys.entity[sys.entities++] = Slvs_MakeWorkplane(200, g, 101, 102);
// Now create a second group. We'll solve group 2, while leaving group 1
// constant; so the workplane that we've created will be locked down,
// and the solver can't move it.
g = 2;
// These points are represented by their coordinates (u v) within the
// workplane, so they need only two parameters each.
sys.param[sys.params++] = Slvs_MakeParam(11, g, 10.0);
sys.param[sys.params++] = Slvs_MakeParam(12, g, 20.0);
sys.entity[sys.entities++] = Slvs_MakePoint2d(301, g, 200, 11, 12);
sys.param[sys.params++] = Slvs_MakeParam(13, g, 20.0);
sys.param[sys.params++] = Slvs_MakeParam(14, g, 10.0);
sys.entity[sys.entities++] = Slvs_MakePoint2d(302, g, 200, 13, 14);
// And we create a line segment with those endpoints.
sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g,
200, 301, 302);
// The length of our line segment is 30.0 units.
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
1, g,
SLVS_C_PT_PT_DISTANCE,
200,
30.0,
301, 302, 0, 0);
// And the distance from our line segment to the origin is 10.0 units.
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
2, g,
SLVS_C_PT_LINE_DISTANCE,
200,
10.0,
101, 0, 400, 0);
// And the line segment is vertical.
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
3, g,
SLVS_C_VERTICAL,
200,
0.0,
0, 0, 400, 0);
// And the distance from one endpoint to the origin is 15.0 units.
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
4, g,
SLVS_C_PT_PT_DISTANCE,
200,
15.0,
301, 101, 0, 0);
// And the distance from one endpoint to the origin is 15.0 units.
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
5, g,
SLVS_C_PT_PT_DISTANCE,
200,
18.0,
302, 101, 0, 0);
// And solve.
Slvs_Solve(&sys, g);
if(sys.result == SLVS_RESULT_OKAY) {
printf("okay; now at (%.3f %.3f)\n"
" (%.3f %.3f)\n",
sys.param[7].val, sys.param[8].val,
sys.param[9].val, sys.param[10].val);
printf("%d DOF\n", sys.dof);
} else {
int i;
printf("solve failed: problematic constraints are:");
for(i = 0; i < sys.faileds; i++) {
printf(" %d", sys.failed[i]);
}
printf("\n");
if(sys.result == SLVS_RESULT_INCONSISTENT) {
printf("system inconsistent\n");
} else {
printf("system nonconvergent\n");
}
}
}
int main(void)
{
memset(&sys, 0, sizeof(sys));
sys.param = CheckMalloc(50*sizeof(sys.param[0]));
sys.entity = CheckMalloc(50*sizeof(sys.entity[0]));
sys.constraint = CheckMalloc(50*sizeof(sys.constraint[0]));
sys.failed = CheckMalloc(50*sizeof(sys.failed[0]));
sys.faileds = 50;
// Example3d();
Example2d();
}

234
exposed/lib.cpp Normal file
View File

@ -0,0 +1,234 @@
#include "solvespace.h"
#include "slvs.h"
Sketch SK;
System SYS;
int IsInit = 0;
void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
// Nothing to do for now.
}
extern "C" {
void Slvs_QuaternionU(double qw, double qx, double qy, double qz,
double *x, double *y, double *z)
{
Quaternion q = Quaternion::From(qw, qx, qy, qz);
Vector v = q.RotationU();
*x = v.x;
*y = v.y;
*z = v.z;
}
void Slvs_QuaternionV(double qw, double qx, double qy, double qz,
double *x, double *y, double *z)
{
Quaternion q = Quaternion::From(qw, qx, qy, qz);
Vector v = q.RotationV();
*x = v.x;
*y = v.y;
*z = v.z;
}
void Slvs_QuaternionN(double qw, double qx, double qy, double qz,
double *x, double *y, double *z)
{
Quaternion q = Quaternion::From(qw, qx, qy, qz);
Vector v = q.RotationN();
*x = v.x;
*y = v.y;
*z = v.z;
}
void Slvs_MakeQuaternion(double ux, double uy, double uz,
double vx, double vy, double vz,
double *qw, double *qx, double *qy, double *qz)
{
Vector u = Vector::From(ux, uy, uz),
v = Vector::From(vx, vy, vz);
Quaternion q = Quaternion::From(u, v);
*qw = q.w;
*qx = q.vx;
*qy = q.vy;
*qz = q.vz;
}
void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg)
{
if(!IsInit) {
InitHeaps();
IsInit = 1;
}
int i;
for(i = 0; i < ssys->params; i++) {
Slvs_Param *sp = &(ssys->param[i]);
Param p;
ZERO(&p);
p.h.v = sp->h;
p.val = sp->val;
SK.param.Add(&p);
if(sp->group == shg) {
SYS.param.Add(&p);
}
}
for(i = 0; i < ssys->entities; i++) {
Slvs_Entity *se = &(ssys->entity[i]);
EntityBase e;
ZERO(&e);
switch(se->type) {
case SLVS_E_POINT_IN_3D: e.type = Entity::POINT_IN_3D; break;
case SLVS_E_POINT_IN_2D: e.type = Entity::POINT_IN_2D; break;
case SLVS_E_NORMAL_IN_3D: e.type = Entity::NORMAL_IN_3D; break;
case SLVS_E_NORMAL_IN_2D: e.type = Entity::NORMAL_IN_2D; break;
case SLVS_E_DISTANCE: e.type = Entity::DISTANCE; break;
case SLVS_E_WORKPLANE: e.type = Entity::WORKPLANE; break;
case SLVS_E_LINE_SEGMENT: e.type = Entity::LINE_SEGMENT; break;
case SLVS_E_CUBIC: e.type = Entity::CUBIC; break;
case SLVS_E_CIRCLE: e.type = Entity::CIRCLE; break;
case SLVS_E_ARC_OF_CIRCLE: e.type = Entity::ARC_OF_CIRCLE; break;
default: dbp("bad entity type %d", se->type); return;
}
e.h.v = se->h;
e.group.v = se->group;
e.workplane.v = se->wrkpl;
e.point[0].v = se->point[0];
e.point[1].v = se->point[1];
e.point[2].v = se->point[2];
e.point[3].v = se->point[3];
e.normal.v = se->normal;
e.distance.v = se->distance;
e.param[0].v = se->param[0];
e.param[1].v = se->param[1];
e.param[2].v = se->param[2];
e.param[3].v = se->param[3];
SK.entity.Add(&e);
}
for(i = 0; i < ssys->constraints; i++) {
Slvs_Constraint *sc = &(ssys->constraint[i]);
ConstraintBase c;
ZERO(&c);
int t;
switch(sc->type) {
case SLVS_C_POINTS_COINCIDENT: t = Constraint::POINTS_COINCIDENT; break;
case SLVS_C_PT_PT_DISTANCE: t = Constraint::PT_PT_DISTANCE; break;
case SLVS_C_PT_PLANE_DISTANCE: t = Constraint::PT_PLANE_DISTANCE; break;
case SLVS_C_PT_LINE_DISTANCE: t = Constraint::PT_LINE_DISTANCE; break;
case SLVS_C_PT_FACE_DISTANCE: t = Constraint::PT_FACE_DISTANCE; break;
case SLVS_C_PT_IN_PLANE: t = Constraint::PT_IN_PLANE; break;
case SLVS_C_PT_ON_LINE: t = Constraint::PT_ON_LINE; break;
case SLVS_C_PT_ON_FACE: t = Constraint::PT_ON_FACE; break;
case SLVS_C_EQUAL_LENGTH_LINES: t = Constraint::EQUAL_LENGTH_LINES; break;
case SLVS_C_LENGTH_RATIO: t = Constraint::LENGTH_RATIO; break;
case SLVS_C_EQ_LEN_PT_LINE_D: t = Constraint::EQ_LEN_PT_LINE_D; break;
case SLVS_C_EQ_PT_LN_DISTANCES: t = Constraint::EQ_PT_LN_DISTANCES; break;
case SLVS_C_EQUAL_ANGLE: t = Constraint::EQUAL_ANGLE; break;
case SLVS_C_EQUAL_LINE_ARC_LEN: t = Constraint::EQUAL_LINE_ARC_LEN; break;
case SLVS_C_SYMMETRIC: t = Constraint::SYMMETRIC; break;
case SLVS_C_SYMMETRIC_HORIZ: t = Constraint::SYMMETRIC_HORIZ; break;
case SLVS_C_SYMMETRIC_VERT: t = Constraint::SYMMETRIC_VERT; break;
case SLVS_C_SYMMETRIC_LINE: t = Constraint::SYMMETRIC_LINE; break;
case SLVS_C_AT_MIDPOINT: t = Constraint::AT_MIDPOINT; break;
case SLVS_C_HORIZONTAL: t = Constraint::HORIZONTAL; break;
case SLVS_C_VERTICAL: t = Constraint::VERTICAL; break;
case SLVS_C_DIAMETER: t = Constraint::DIAMETER; break;
case SLVS_C_PT_ON_CIRCLE: t = Constraint::PT_ON_CIRCLE; break;
case SLVS_C_SAME_ORIENTATION: t = Constraint::SAME_ORIENTATION; break;
case SLVS_C_ANGLE: t = Constraint::ANGLE; break;
case SLVS_C_PARALLEL: t = Constraint::PARALLEL; break;
case SLVS_C_PERPENDICULAR: t = Constraint::PERPENDICULAR; break;
case SLVS_C_ARC_LINE_TANGENT: t = Constraint::ARC_LINE_TANGENT; break;
case SLVS_C_CUBIC_LINE_TANGENT: t = Constraint::CUBIC_LINE_TANGENT; break;
case SLVS_C_EQUAL_RADIUS: t = Constraint::EQUAL_RADIUS; break;
default: dbp("bad constraint type %d", sc->type); return;
}
c.type = t;
c.h.v = sc->h;
c.group.v = sc->group;
c.workplane.v = sc->wrkpl;
c.valA = sc->valA;
c.ptA.v = sc->ptA;
c.ptB.v = sc->ptB;
c.entityA.v = sc->entityA;
c.entityB.v = sc->entityB;
c.entityC.v = sc->entityC;
c.entityD.v = sc->entityD;
c.other = (sc->other) ? true : false;
SK.constraint.Add(&c);
}
if(System::MAX_DRAGGED < 4) oops();
for(i = 0; i < System::MAX_DRAGGED; i++) {
SYS.dragged[i].v = 0;
}
SYS.dragged[0].v = ssys->dragged[0];
SYS.dragged[1].v = ssys->dragged[1];
SYS.dragged[2].v = ssys->dragged[2];
SYS.dragged[3].v = ssys->dragged[3];
Group g;
ZERO(&g);
g.h.v = shg;
List<hConstraint> bad;
ZERO(&bad);
// Now we're finally ready to solve!
int how = SYS.Solve(&g, &(ssys->dof), &bad, false);
switch(how) {
case System::SOLVED_OKAY:
ssys->result = SLVS_RESULT_OKAY;
break;
case System::DIDNT_CONVERGE:
ssys->result = SLVS_RESULT_DIDNT_CONVERGE;
break;
case System::SINGULAR_JACOBIAN:
ssys->result = SLVS_RESULT_INCONSISTENT;
break;
case System::TOO_MANY_UNKNOWNS:
ssys->result = SLVS_RESULT_TOO_MANY_UNKNOWNS;
break;
default: oops();
}
// Write the new parameter values back to our caller.
for(i = 0; i < ssys->params; i++) {
Slvs_Param *sp = &(ssys->param[i]);
hParam hp = { sp->h };
sp->val = SK.GetParam(hp)->val;
}
if(ssys->failed) {
// Copy over any the list of problematic constraints.
for(i = 0; i < ssys->faileds && i < bad.n; i++) {
ssys->failed[i] = bad.elem[i].v;
}
ssys->faileds = bad.n;
}
bad.Clear();
SYS.param.Clear();
SK.param.Clear();
SK.entity.Clear();
SK.constraint.Clear();
}
}

0
exposed/obj/t Normal file
View File

View File

@ -141,7 +141,7 @@ void SolveSpace::GenerateAll(void) {
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.group.n; i++) {
Group *g = &(SK.group.elem[i]); Group *g = &(SK.group.elem[i]);
g->order = i; g->order = i;
if((!g->clean) || (g->solved.how != Group::SOLVED_OKAY)) { if((!g->clean) || (g->solved.how != System::SOLVED_OKAY)) {
firstDirty = min(firstDirty, i); firstDirty = min(firstDirty, i);
} }
if(g->h.v == SS.GW.activeGroup.v) { if(g->h.v == SS.GW.activeGroup.v) {
@ -204,7 +204,7 @@ void SolveSpace::GenerateAll(int first, int last, bool andFindFree) {
if(g->h.v == Group::HGROUP_REFERENCES.v) { if(g->h.v == Group::HGROUP_REFERENCES.v) {
ForceReferences(); ForceReferences();
g->solved.how = Group::SOLVED_OKAY; g->solved.how = System::SOLVED_OKAY;
g->clean = true; g->clean = true;
} else { } else {
if(i >= first && i <= last) { if(i >= first && i <= last) {
@ -330,6 +330,60 @@ void SolveSpace::ForceReferences(void) {
} }
} }
void SolveSpace::MarkDraggedParams(void) {
int i;
for(i = 0; i < System::MAX_DRAGGED; i++) {
sys.dragged[i] = Param::NO_PARAM;
}
if(SS.GW.pending.point.v) {
// The pending point could be one in a group that has not yet
// been processed, in which case the lookup will fail; but
// that's not an error.
Entity *pt = SK.entity.FindByIdNoOops(SS.GW.pending.point);
if(pt) {
switch(pt->type) {
case Entity::POINT_N_TRANS:
case Entity::POINT_IN_3D:
sys.dragged[0] = pt->param[0];
sys.dragged[1] = pt->param[1];
sys.dragged[2] = pt->param[2];
break;
case Entity::POINT_IN_2D:
sys.dragged[0] = pt->param[0];
sys.dragged[1] = pt->param[1];
break;
}
}
}
if(SS.GW.pending.circle.v) {
Entity *circ = SK.entity.FindByIdNoOops(SS.GW.pending.circle);
if(circ) {
Entity *dist = SK.GetEntity(circ->distance);
switch(dist->type) {
case Entity::DISTANCE:
sys.dragged[0] = dist->param[0];
break;
}
}
}
if(SS.GW.pending.normal.v) {
Entity *norm = SK.entity.FindByIdNoOops(SS.GW.pending.normal);
if(norm) {
switch(norm->type) {
case Entity::NORMAL_IN_3D:
sys.dragged[0] = norm->param[0];
sys.dragged[1] = norm->param[1];
sys.dragged[2] = norm->param[2];
sys.dragged[3] = norm->param[3];
break;
// other types are locked, so not draggable
}
}
}
}
void SolveSpace::SolveGroup(hGroup hg, bool andFindFree) { void SolveSpace::SolveGroup(hGroup hg, bool andFindFree) {
int i; int i;
// Clear out the system to be solved. // Clear out the system to be solved.
@ -353,7 +407,16 @@ void SolveSpace::SolveGroup(hGroup hg, bool andFindFree) {
p->val = SK.GetParam(p->h)->val; p->val = SK.GetParam(p->h)->val;
} }
sys.Solve(g, andFindFree); MarkDraggedParams();
g->solved.remove.Clear();
int how = sys.Solve(g, &(g->solved.dof),
&(g->solved.remove), andFindFree);
if((how != System::SOLVED_OKAY) ||
(how == System::SOLVED_OKAY && g->solved.how != System::SOLVED_OKAY))
{
TextWindow::ReportHowGroupSolved(g->h);
}
g->solved.how = how;
FreeAllTemporary(); FreeAllTemporary();
} }
@ -361,7 +424,7 @@ bool SolveSpace::AllGroupsOkay(void) {
int i; int i;
bool allOk = true; bool allOk = true;
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.group.n; i++) {
if(SK.group.elem[i].solved.how != Group::SOLVED_OKAY) { if(SK.group.elem[i].solved.how != System::SOLVED_OKAY) {
allOk = false; allOk = false;
} }
} }

View File

@ -104,9 +104,6 @@ public:
double valC; double valC;
DWORD color; DWORD color;
static const int SOLVED_OKAY = 0;
static const int DIDNT_CONVERGE = 10;
static const int SINGULAR_JACOBIAN = 11;
struct { struct {
int how; int how;
int dof; int dof;
@ -336,7 +333,7 @@ public:
void WorkplaneGetPlaneExprs(ExprVector *n, Expr **d); void WorkplaneGetPlaneExprs(ExprVector *n, Expr **d);
ExprVector WorkplaneGetOffsetExprs(void); ExprVector WorkplaneGetOffsetExprs(void);
Vector WorkplaneGetOffset(void); Vector WorkplaneGetOffset(void);
Entity *Normal(void); EntityBase *Normal(void);
bool IsFace(void); bool IsFace(void);
ExprVector FaceGetNormalExprs(void); ExprVector FaceGetNormalExprs(void);
@ -350,7 +347,6 @@ public:
ExprVector PointGetExprs(void); ExprVector PointGetExprs(void);
void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v); void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v);
void PointForceTo(Vector v); void PointForceTo(Vector v);
bool PointIsFromReferences(void);
// These apply only the POINT_N_ROT_TRANS, which has an assoc rotation // These apply only the POINT_N_ROT_TRANS, which has an assoc rotation
Quaternion PointGetQuaternion(void); Quaternion PointGetQuaternion(void);
void PointForceQuaternionTo(Quaternion q); void PointForceQuaternionTo(Quaternion q);
@ -401,6 +397,7 @@ public:
void DrawOrGetDistance(void); void DrawOrGetDistance(void);
bool IsVisible(void); bool IsVisible(void);
bool PointIsFromReferences(void);
void GenerateBezierCurves(SBezierList *sbl); void GenerateBezierCurves(SBezierList *sbl);
void GenerateEdges(SEdgeList *el, bool includingConstruction=false); void GenerateEdges(SEdgeList *el, bool includingConstruction=false);

View File

@ -143,6 +143,7 @@ void FreeAllTemporary(void);
void *MemRealloc(void *p, int n); void *MemRealloc(void *p, int n);
void *MemAlloc(int n); void *MemAlloc(int n);
void MemFree(void *p); void MemFree(void *p);
void InitHeaps(void);
void vl(void); // debug function to validate heaps void vl(void); // debug function to validate heaps
// End of platform-specific functions // End of platform-specific functions
@ -202,12 +203,17 @@ void MakePathAbsolute(char *base, char *path);
class System { class System {
public: public:
#define MAX_UNKNOWNS 1000 static const int MAX_UNKNOWNS = 1000;
static const int MAX_DRAGGED = 4;
EntityList entity; EntityList entity;
ParamList param; ParamList param;
IdList<Equation,hEquation> eq; IdList<Equation,hEquation> eq;
// A list of parameters that are being dragged; these are the ones that
// we should put as close as possible to their initial positions.
hParam dragged[MAX_DRAGGED];
// In general, the tag indicates the subsys that a variable/equation // In general, the tag indicates the subsys that a variable/equation
// has been assigned to; these are exceptions for variables: // has been assigned to; these are exceptions for variables:
static const int VAR_SUBSTITUTED = 10000; static const int VAR_SUBSTITUTED = 10000;
@ -253,14 +259,19 @@ public:
bool WriteJacobian(int tag); bool WriteJacobian(int tag);
void EvalJacobian(void); void EvalJacobian(void);
void WriteEquationsExceptFor(hConstraint hc, hGroup hg); void WriteEquationsExceptFor(hConstraint hc, Group *g);
void FindWhichToRemoveToFixJacobian(Group *g); void FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad);
void SolveBySubstitution(void); void SolveBySubstitution(void);
static bool IsDragged(hParam p); bool IsDragged(hParam p);
bool NewtonSolve(int tag); bool NewtonSolve(int tag);
void Solve(Group *g, bool andFindFree);
static const int SOLVED_OKAY = 0;
static const int DIDNT_CONVERGE = 10;
static const int SINGULAR_JACOBIAN = 11;
static const int TOO_MANY_UNKNOWNS = 20;
int Solve(Group *g, int *dof, List<hConstraint> *bad, bool andFindFree);
}; };
class TtfFont { class TtfFont {
@ -410,20 +421,27 @@ public:
void FinishAndCloseFile(void); void FinishAndCloseFile(void);
}; };
#ifdef LIBRARY
# define ENTITY EntityBase
# define CONSTRAINT ConstraintBase
#else
# define ENTITY Entity
# define CONSTRAINT Constraint
#endif
class Sketch { class Sketch {
public: public:
// These are user-editable, and define the sketch. // These are user-editable, and define the sketch.
IdList<Group,hGroup> group; IdList<Group,hGroup> group;
IdList<Constraint,hConstraint> constraint; IdList<CONSTRAINT,hConstraint> constraint;
IdList<Request,hRequest> request; IdList<Request,hRequest> request;
// These are generated from the above. // These are generated from the above.
IdList<Entity,hEntity> entity; IdList<ENTITY,hEntity> entity;
IdList<Param,hParam> param; IdList<Param,hParam> param;
inline Constraint *GetConstraint(hConstraint h) inline CONSTRAINT *GetConstraint(hConstraint h)
{ return constraint.FindById(h); } { return constraint.FindById(h); }
inline Entity *GetEntity (hEntity h) { return entity. FindById(h); } inline ENTITY *GetEntity (hEntity h) { return entity. FindById(h); }
inline Param *GetParam (hParam h) { return param. FindById(h); } inline Param *GetParam (hParam h) { return param. FindById(h); }
inline Request *GetRequest(hRequest h) { return request.FindById(h); } inline Request *GetRequest(hRequest h) { return request.FindById(h); }
inline Group *GetGroup (hGroup h) { return group. FindById(h); } inline Group *GetGroup (hGroup h) { return group. FindById(h); }
@ -565,6 +583,7 @@ public:
void GenerateAll(void); void GenerateAll(void);
void GenerateAll(int first, int last, bool andFindFree=false); void GenerateAll(int first, int last, bool andFindFree=false);
void SolveGroup(hGroup hg, bool andFindFree); void SolveGroup(hGroup hg, bool andFindFree);
void MarkDraggedParams(void);
void ForceReferences(void); void ForceReferences(void);
bool AllGroupsOkay(void); bool AllGroupsOkay(void);

View File

@ -61,51 +61,9 @@ void System::EvalJacobian(void) {
} }
bool System::IsDragged(hParam p) { bool System::IsDragged(hParam p) {
if(SS.GW.pending.point.v) { int i;
// The pending point could be one in a group that has not yet for(i = 0; i < MAX_DRAGGED; i++) {
// been processed, in which case the lookup will fail; but if(p.v == dragged[i].v) return true;
// that's not an error.
Entity *pt = SK.entity.FindByIdNoOops(SS.GW.pending.point);
if(pt) {
switch(pt->type) {
case Entity::POINT_N_TRANS:
case Entity::POINT_IN_3D:
if(p.v == (pt->param[0]).v) return true;
if(p.v == (pt->param[1]).v) return true;
if(p.v == (pt->param[2]).v) return true;
break;
case Entity::POINT_IN_2D:
if(p.v == (pt->param[0]).v) return true;
if(p.v == (pt->param[1]).v) return true;
break;
}
}
}
if(SS.GW.pending.circle.v) {
Entity *circ = SK.entity.FindByIdNoOops(SS.GW.pending.circle);
if(circ) {
Entity *dist = SK.GetEntity(circ->distance);
switch(dist->type) {
case Entity::DISTANCE:
if(p.v == (dist->param[0].v)) return true;
break;
}
}
}
if(SS.GW.pending.normal.v) {
Entity *norm = SK.entity.FindByIdNoOops(SS.GW.pending.normal);
if(norm) {
switch(norm->type) {
case Entity::NORMAL_IN_3D:
if(p.v == (norm->param[0].v)) return true;
if(p.v == (norm->param[1].v)) return true;
if(p.v == (norm->param[2].v)) return true;
if(p.v == (norm->param[3].v)) return true;
break;
// other types are locked, so not draggable
}
}
} }
return false; return false;
} }
@ -346,34 +304,33 @@ bool System::NewtonSolve(int tag) {
return converged; return converged;
} }
void System::WriteEquationsExceptFor(hConstraint hc, hGroup hg) { void System::WriteEquationsExceptFor(hConstraint hc, Group *g) {
int i; int i;
// Generate all the equations from constraints in this group // Generate all the equations from constraints in this group
for(i = 0; i < SK.constraint.n; i++) { for(i = 0; i < SK.constraint.n; i++) {
Constraint *c = &(SK.constraint.elem[i]); ConstraintBase *c = &(SK.constraint.elem[i]);
if(c->group.v != hg.v) continue; if(c->group.v != g->h.v) continue;
if(c->h.v == hc.v) continue; if(c->h.v == hc.v) continue;
c->Generate(&eq); c->Generate(&eq);
} }
// And the equations from entities // And the equations from entities
for(i = 0; i < SK.entity.n; i++) { for(i = 0; i < SK.entity.n; i++) {
Entity *e = &(SK.entity.elem[i]); EntityBase *e = &(SK.entity.elem[i]);
if(e->group.v != hg.v) continue; if(e->group.v != g->h.v) continue;
e->GenerateEquations(&eq); e->GenerateEquations(&eq);
} }
// And from the groups themselves // And from the groups themselves
(SK.GetGroup(hg))->GenerateEquations(&eq); g->GenerateEquations(&eq);
} }
void System::FindWhichToRemoveToFixJacobian(Group *g) { void System::FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad) {
int a, i; int a, i;
(g->solved.remove).Clear();
for(a = 0; a < 2; a++) { for(a = 0; a < 2; a++) {
for(i = 0; i < SK.constraint.n; i++) { for(i = 0; i < SK.constraint.n; i++) {
Constraint *c = &(SK.constraint.elem[i]); ConstraintBase *c = &(SK.constraint.elem[i]);
if(c->group.v != g->h.v) continue; if(c->group.v != g->h.v) continue;
if((c->type == Constraint::POINTS_COINCIDENT && a == 0) || if((c->type == Constraint::POINTS_COINCIDENT && a == 0) ||
(c->type != Constraint::POINTS_COINCIDENT && a == 1)) (c->type != Constraint::POINTS_COINCIDENT && a == 1))
@ -386,7 +343,7 @@ void System::FindWhichToRemoveToFixJacobian(Group *g) {
param.ClearTags(); param.ClearTags();
eq.Clear(); eq.Clear();
WriteEquationsExceptFor(c->h, g->h); WriteEquationsExceptFor(c->h, g);
eq.ClearTags(); eq.ClearTags();
// It's a major speedup to solve the easy ones by substitution here, // It's a major speedup to solve the easy ones by substitution here,
@ -399,16 +356,16 @@ void System::FindWhichToRemoveToFixJacobian(Group *g) {
int rank = CalculateRank(); int rank = CalculateRank();
if(rank == mat.m) { if(rank == mat.m) {
// We fixed it by removing this constraint // We fixed it by removing this constraint
(g->solved.remove).Add(&(c->h)); bad->Add(&(c->h));
} }
} }
} }
} }
void System::Solve(Group *g, bool andFindFree) { int System::Solve(Group *g, int *dof, List<hConstraint> *bad,
g->solved.remove.Clear(); bool andFindFree)
{
WriteEquationsExceptFor(Constraint::NO_CONSTRAINT, g->h); WriteEquationsExceptFor(Constraint::NO_CONSTRAINT, g);
int i, j = 0; int i, j = 0;
/* /*
@ -456,25 +413,20 @@ void System::Solve(Group *g, bool andFindFree) {
// Now write the Jacobian for what's left, and do a rank test; that // Now write the Jacobian for what's left, and do a rank test; that
// tells us if the system is inconsistently constrained. // tells us if the system is inconsistently constrained.
if(!WriteJacobian(0)) { if(!WriteJacobian(0)) {
g->solved.how = Group::TOO_MANY_UNKNOWNS; return System::TOO_MANY_UNKNOWNS;
TextWindow::ReportHowGroupSolved(g->h);
return;
} }
EvalJacobian(); EvalJacobian();
int rank = CalculateRank(); int rank = CalculateRank();
if(rank != mat.m) { if(rank != mat.m) {
FindWhichToRemoveToFixJacobian(g); FindWhichToRemoveToFixJacobian(g, bad);
g->solved.how = Group::SINGULAR_JACOBIAN; return System::SINGULAR_JACOBIAN;
g->solved.dof = 0;
TextWindow::ReportHowGroupSolved(g->h);
return;
} }
// This is not the full Jacobian, but any substitutions or single-eq // This is not the full Jacobian, but any substitutions or single-eq
// solves removed one equation and one unknown, therefore no effect // solves removed one equation and one unknown, therefore no effect
// on the number of DOF. // on the number of DOF.
g->solved.dof = mat.n - mat.m; if(dof) *dof = mat.n - mat.m;
// And do the leftovers as one big system // And do the leftovers as one big system
if(!NewtonSolve(0)) { if(!NewtonSolve(0)) {
@ -517,15 +469,9 @@ void System::Solve(Group *g, bool andFindFree) {
pp->known = true; pp->known = true;
pp->free = p->free; pp->free = p->free;
} }
if(g->solved.how != Group::SOLVED_OKAY) { return System::SOLVED_OKAY;
g->solved.how = Group::SOLVED_OKAY;
TextWindow::ReportHowGroupSolved(g->h);
}
return;
didnt_converge: didnt_converge:
g->solved.how = Group::DIDNT_CONVERGE;
(g->solved.remove).Clear();
SK.constraint.ClearTags(); SK.constraint.ClearTags();
for(i = 0; i < eq.n; i++) { for(i = 0; i < eq.n; i++) {
if(fabs(mat.B.num[i]) > CONVERGE_TOLERANCE || isnan(mat.B.num[i])) { if(fabs(mat.B.num[i]) > CONVERGE_TOLERANCE || isnan(mat.B.num[i])) {
@ -533,17 +479,17 @@ didnt_converge:
if(!mat.eq[i].isFromConstraint()) continue; if(!mat.eq[i].isFromConstraint()) continue;
hConstraint hc = mat.eq[i].constraint(); hConstraint hc = mat.eq[i].constraint();
Constraint *c = SK.constraint.FindByIdNoOops(hc); ConstraintBase *c = SK.constraint.FindByIdNoOops(hc);
if(!c) continue; if(!c) continue;
// Don't double-show constraints that generated multiple // Don't double-show constraints that generated multiple
// unsatisfied equations // unsatisfied equations
if(!c->tag) { if(!c->tag) {
(g->solved.remove).Add(&(c->h)); bad->Add(&(c->h));
c->tag = 1; c->tag = 1;
} }
} }
} }
TextWindow::ReportHowGroupSolved(g->h); return System::DIDNT_CONVERGE;
} }

View File

@ -112,7 +112,7 @@ void TextWindow::ShowListOfGroups(void) {
char *s = g->DescriptionString(); char *s = g->DescriptionString();
bool active = (g->h.v == SS.GW.activeGroup.v); bool active = (g->h.v == SS.GW.activeGroup.v);
bool shown = g->visible; bool shown = g->visible;
bool ok = (g->solved.how == Group::SOLVED_OKAY); bool ok = (g->solved.how == System::SOLVED_OKAY);
bool ref = (g->h.v == Group::HGROUP_REFERENCES.v); bool ref = (g->h.v == Group::HGROUP_REFERENCES.v);
Printf(false, "%Bp%Fd " Printf(false, "%Bp%Fd "
"%Fp%D%f%s%Ll%s%E%s " "%Fp%D%f%s%Ll%s%E%s "
@ -498,7 +498,7 @@ void TextWindow::ShowGroupInfo(void) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void TextWindow::ShowGroupSolveInfo(void) { void TextWindow::ShowGroupSolveInfo(void) {
Group *g = SK.group.FindById(shown.group); Group *g = SK.group.FindById(shown.group);
if(g->solved.how == Group::SOLVED_OKAY) { if(g->solved.how == System::SOLVED_OKAY) {
// Go back to the default group info screen // Go back to the default group info screen
shown.screen = SCREEN_GROUP_INFO; shown.screen = SCREEN_GROUP_INFO;
Show(); Show();
@ -507,17 +507,17 @@ void TextWindow::ShowGroupSolveInfo(void) {
Printf(true, "%FtGROUP %E%s", g->DescriptionString()); Printf(true, "%FtGROUP %E%s", g->DescriptionString());
switch(g->solved.how) { switch(g->solved.how) {
case Group::DIDNT_CONVERGE: case System::DIDNT_CONVERGE:
Printf(true, "%FxSOLVE FAILED!%Fd no convergence"); Printf(true, "%FxSOLVE FAILED!%Fd no convergence");
Printf(true, "the following constraints are unsatisfied"); Printf(true, "the following constraints are unsatisfied");
break; break;
case Group::SINGULAR_JACOBIAN: case System::SINGULAR_JACOBIAN:
Printf(true, "%FxSOLVE FAILED!%Fd inconsistent system"); Printf(true, "%FxSOLVE FAILED!%Fd inconsistent system");
Printf(true, "remove any one of these to fix it"); Printf(true, "remove any one of these to fix it");
break; break;
case Group::TOO_MANY_UNKNOWNS: case System::TOO_MANY_UNKNOWNS:
Printf(true, "Too many unknowns in a single group!"); Printf(true, "Too many unknowns in a single group!");
return; return;
} }

View File

@ -504,7 +504,7 @@ Vector Vector::WithMagnitude(double v) {
} }
Vector Vector::ProjectVectorInto(hEntity wrkpl) { Vector Vector::ProjectVectorInto(hEntity wrkpl) {
Entity *w = SK.GetEntity(wrkpl); EntityBase *w = SK.GetEntity(wrkpl);
Vector u = w->Normal()->NormalU(); Vector u = w->Normal()->NormalU();
Vector v = w->Normal()->NormalV(); Vector v = w->Normal()->NormalV();
@ -515,7 +515,7 @@ Vector Vector::ProjectVectorInto(hEntity wrkpl) {
} }
Vector Vector::ProjectInto(hEntity wrkpl) { Vector Vector::ProjectInto(hEntity wrkpl) {
Entity *w = SK.GetEntity(wrkpl); EntityBase *w = SK.GetEntity(wrkpl);
Vector p0 = w->WorkplaneGetOffset(); Vector p0 = w->WorkplaneGetOffset();
Vector f = this->Minus(p0); Vector f = this->Minus(p0);

View File

@ -49,18 +49,6 @@ int ClientIsSmallerBy;
HFONT FixedFont, LinkFont; HFONT FixedFont, LinkFont;
void dbp(char *str, ...)
{
va_list f;
static char buf[1024*50];
va_start(f, str);
_vsnprintf(buf, sizeof(buf), str, f);
va_end(f);
OutputDebugString(buf);
}
static void DoMessageBox(char *str, va_list f, BOOL error) static void DoMessageBox(char *str, va_list f, BOOL error)
{ {
char buf[1024*50]; char buf[1024*50];
@ -159,56 +147,6 @@ void SetWindowTitle(char *str) {
SetWindowText(GraphicsWnd, str); SetWindowText(GraphicsWnd, str);
} }
//-----------------------------------------------------------------------------
// A separate heap, on which we allocate expressions. Maybe a bit faster,
// since no fragmentation issues whatsoever, and it also makes it possible
// to be sloppy with our memory management, and just free everything at once
// at the end.
//-----------------------------------------------------------------------------
static HANDLE Temp;
void *AllocTemporary(int n)
{
void *v = HeapAlloc(Temp, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
if(!v) oops();
return v;
}
void FreeTemporary(void *p) {
HeapFree(Temp, HEAP_NO_SERIALIZE, p);
}
void FreeAllTemporary(void)
{
if(Temp) HeapDestroy(Temp);
Temp = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
// This is a good place to validate, because it gets called fairly
// often.
vl();
}
static HANDLE Perm;
void *MemRealloc(void *p, int n) {
if(!p) {
return MemAlloc(n);
}
p = HeapReAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, p, n);
if(!p) oops();
return p;
}
void *MemAlloc(int n) {
void *p = HeapAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
if(!p) oops();
return p;
}
void MemFree(void *p) {
HeapFree(Perm, HEAP_NO_SERIALIZE, p);
}
void vl(void) {
if(!HeapValidate(Temp, HEAP_NO_SERIALIZE, NULL)) oops();
if(!HeapValidate(Perm, HEAP_NO_SERIALIZE, NULL)) oops();
}
static void PaintTextWnd(HDC hdc) static void PaintTextWnd(HDC hdc)
{ {
int i; int i;
@ -892,13 +830,6 @@ int SaveFileYesNoCancel(void)
return r; return r;
} }
void GetAbsoluteFilename(char *file)
{
char absoluteFile[MAX_PATH];
GetFullPathName(file, sizeof(absoluteFile), absoluteFile, NULL);
strcpy(file, absoluteFile);
}
void LoadAllFontFiles(void) void LoadAllFontFiles(void)
{ {
WIN32_FIND_DATA wfd; WIN32_FIND_DATA wfd;
@ -1114,10 +1045,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
ThawWindowPos(TextWnd); ThawWindowPos(TextWnd);
ThawWindowPos(GraphicsWnd); ThawWindowPos(GraphicsWnd);
// Create the heap used for long-lived stuff (that gets freed piecewise). // Create the heaps for all dynamic memory (AllocTemporary, MemAlloc)
Perm = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0); InitHeaps();
// Create the heap that we use to store Exprs and other temp stuff.
FreeAllTemporary();
// A filename may have been specified on the command line; if so, then // A filename may have been specified on the command line; if so, then
// strip any quotation marks, and make it absolute. // strip any quotation marks, and make it absolute.

81
win32/w32util.cpp Normal file
View File

@ -0,0 +1,81 @@
#include <windows.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include "solvespace.h"
static HANDLE PermHeap, TempHeap;
void dbp(char *str, ...)
{
va_list f;
static char buf[1024*50];
va_start(f, str);
_vsnprintf(buf, sizeof(buf), str, f);
va_end(f);
OutputDebugString(buf);
}
void GetAbsoluteFilename(char *file)
{
char absoluteFile[MAX_PATH];
GetFullPathName(file, sizeof(absoluteFile), absoluteFile, NULL);
strcpy(file, absoluteFile);
}
//-----------------------------------------------------------------------------
// A separate heap, on which we allocate expressions. Maybe a bit faster,
// since no fragmentation issues whatsoever, and it also makes it possible
// to be sloppy with our memory management, and just free everything at once
// at the end.
//-----------------------------------------------------------------------------
void *AllocTemporary(int n)
{
void *v = HeapAlloc(TempHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
if(!v) oops();
return v;
}
void FreeTemporary(void *p) {
HeapFree(TempHeap, HEAP_NO_SERIALIZE, p);
}
void FreeAllTemporary(void)
{
if(TempHeap) HeapDestroy(TempHeap);
TempHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
// This is a good place to validate, because it gets called fairly
// often.
vl();
}
void *MemRealloc(void *p, int n) {
if(!p) {
return MemAlloc(n);
}
p = HeapReAlloc(PermHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, p, n);
if(!p) oops();
return p;
}
void *MemAlloc(int n) {
void *p = HeapAlloc(PermHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
if(!p) oops();
return p;
}
void MemFree(void *p) {
HeapFree(PermHeap, HEAP_NO_SERIALIZE, p);
}
void vl(void) {
if(!HeapValidate(TempHeap, HEAP_NO_SERIALIZE, NULL)) oops();
if(!HeapValidate(PermHeap, HEAP_NO_SERIALIZE, NULL)) oops();
}
void InitHeaps(void) {
// Create the heap used for long-lived stuff (that gets freed piecewise).
PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
// Create the heap that we use to store Exprs and other temp stuff.
FreeAllTemporary();
}