diff --git a/constraint.cpp b/constraint.cpp index c706d05..a01c7ba 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -5,6 +5,16 @@ hConstraint Constraint::AddConstraint(Constraint *c) { return c->h; } +void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) { + Constraint c; + memset(&c, 0, sizeof(c)); + c.group = SS.GW.activeGroup; + c.type = Constraint::POINTS_COINCIDENT; + c.ptA = ptA; + c.ptB = ptB; + SS.constraint.AddAndAssignId(&c); +} + void Constraint::MenuConstrain(int id) { Constraint c; memset(&c, 0, sizeof(c)); @@ -62,6 +72,48 @@ void Constraint::MenuConstrain(int id) { AddConstraint(&c); break; + case GraphicsWindow::MNU_VERTICAL: + case GraphicsWindow::MNU_HORIZONTAL: { + hEntity ha, hb; + if(gs.lineSegments == 1 && gs.n == 1) { + c.entityA = gs.entity[0]; + Entity *e = SS.GetEntity(c.entityA); + ha = e->assoc[0]; + hb = e->assoc[1]; + } else if(gs.points == 2 && gs.n == 2) { + ha = c.ptA = gs.point[0]; + hb = c.ptB = gs.point[1]; + } else { + Error("Bad selection for horizontal / vertical constraint."); + return; + } + Entity *ea = SS.GetEntity(ha); + Entity *eb = SS.GetEntity(hb); + if(ea->csys.v == Entity::NO_CSYS.v && + eb->csys.v == Entity::NO_CSYS.v) + { + Error("Horizontal/vertical constraint applies only to " + "entities drawn in a 2d coordinate system."); + return; + } + if(eb->csys.v == SS.GW.activeCsys.v) { + // We are constraining two points in two different csyss; so + // we have two choices for the definitons of the coordinate + // directions. ptA's gets chosen, so make sure that's the + // active csys. + hEntity t = c.ptA; + c.ptA = c.ptB; + c.ptB = t; + } + if(id == GraphicsWindow::MNU_HORIZONTAL) { + c.type = HORIZONTAL; + } else { + c.type = VERTICAL; + } + AddConstraint(&c); + break; + } + case GraphicsWindow::MNU_SOLVE_NOW: SS.Solve(); return; @@ -157,24 +209,27 @@ void Constraint::Generate(IdList *l) { AddEq(l, eab.x, 0); AddEq(l, eab.y, 1); AddEq(l, eab.z, 2); - } else if(a->IsPointIn3d() && !b->IsPointIn3d()) { - // One point has 2 DOF, one has 3; write two eqs, on the - // projection of the 3 DOF point into the 2 DOF point plane. - ExprVector p3; - p3 = a->PointGetExprs(); - Entity *csy = SS.GetEntity(b->csys); - ExprVector u, v; - csy->Csys2dGetBasisExprs(&u, &v); - AddEq(l, Expr::FromParam(b->param.h[0])->Minus(p3.Dot(u)), 0); - AddEq(l, Expr::FromParam(b->param.h[1])->Minus(p3.Dot(v)), 1); - } else if(a->csys.v == b->csys.v) { + } else if(!(a->IsPointIn3d() || b->IsPointIn3d()) && + (a->csys.v == b->csys.v)) + { // Both in same csys, nice. AddEq(l, Expr::FromParam(a->param.h[0])->Minus( Expr::FromParam(b->param.h[0])), 0); AddEq(l, Expr::FromParam(a->param.h[1])->Minus( Expr::FromParam(b->param.h[1])), 1); } else { - oops(); + // Either two 2 DOF points in different planes, or one + // 3 DOF point and one 2 DOF point. Either way, write two + // equations on the projection of a into b's plane. + ExprVector p3; + p3 = a->PointGetExprs(); + Entity *csy = SS.GetEntity(b->csys); + ExprVector offset = csy->Csys2dGetOffsetExprs(); + p3 = p3.Minus(offset); + ExprVector u, v; + csy->Csys2dGetBasisExprs(&u, &v); + AddEq(l, Expr::FromParam(b->param.h[0])->Minus(p3.Dot(u)), 0); + AddEq(l, Expr::FromParam(b->param.h[1])->Minus(p3.Dot(v)), 1); } break; } @@ -188,6 +243,41 @@ void Constraint::Generate(IdList *l) { break; } + case HORIZONTAL: + case VERTICAL: { + hEntity ha, hb; + if(entityA.v) { + Entity *e = SS.GetEntity(entityA); + ha = e->assoc[0]; + hb = e->assoc[1]; + } else { + ha = ptA; + hb = ptB; + } + Entity *a = SS.GetEntity(ha); + Entity *b = SS.GetEntity(hb); + if(a->csys.v == Entity::NO_CSYS.v) { + Entity *t = a; + a = b; + b = t; + } + + if(a->csys.v == b->csys.v) { + int i = (type == HORIZONTAL) ? 1 : 0; + AddEq(l, Expr::FromParam(a->param.h[i])->Minus( + Expr::FromParam(b->param.h[i])), 0); + } else { + Entity *csy = SS.GetEntity(a->csys); + ExprVector u, v; + csy->Csys2dGetBasisExprs(&u, &v); + ExprVector norm = (type == HORIZONTAL) ? v : u; + ExprVector pa = a->PointGetExprs(); + ExprVector pb = b->PointGetExprs(); + AddEq(l, (pa.Minus(pb)).Dot(norm), 0); + } + break; + } + default: oops(); } } diff --git a/drawconstraint.cpp b/drawconstraint.cpp index bb6c064..e86702d 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -33,7 +33,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { Vector gu = SS.GW.projUp; Vector gn = gr.Cross(gu); - glxColor(1, 0.3, 1); + glxColor(1, 0.2, 1); switch(type) { case PT_PT_DISTANCE: { Vector ap = SS.GetEntity(ptA)->PointGetCoords(); @@ -125,6 +125,55 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { break; } + case HORIZONTAL: + case VERTICAL: + if(entityA.v) { + Entity *e = SS.GetEntity(entityA); + Vector a = SS.GetEntity(e->assoc[0])->PointGetCoords(); + Vector b = SS.GetEntity(e->assoc[1])->PointGetCoords(); + Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5)); + + if(dogd.drawing) { + glPushMatrix(); + glxTranslatev(m); + glxOntoCsys(gr, gu); + glxWriteText(type == HORIZONTAL ? "H" : "V"); + glPopMatrix(); + } else { + Point2d ref = SS.GW.ProjectPoint(m); + dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10); + } + } else { + Vector a = SS.GetEntity(ptA)->PointGetCoords(); + Vector b = SS.GetEntity(ptB)->PointGetCoords(); + Entity *csy = SS.GetEntity(SS.GetEntity(ptA)->csys); + Vector cn = csy->Csys2dGetNormalVector(); + + int i; + for(i = 0; i < 2; i++) { + Vector o = (i == 0) ? a : b; + Vector d = (i == 0) ? a.Minus(b) : b.Minus(a); + Vector dp = cn.Cross(d); + d = d.WithMagnitude(14/SS.GW.scale); + Vector c = o.Minus(d); + LineDrawOrGetDistance(o, c); + d = d.WithMagnitude(3/SS.GW.scale); + dp = dp.WithMagnitude(2/SS.GW.scale); + if(dogd.drawing) { + glBegin(GL_QUADS); + glxVertex3v((c.Plus(d)).Plus(dp)); + glxVertex3v((c.Minus(d)).Plus(dp)); + glxVertex3v((c.Minus(d)).Minus(dp)); + glxVertex3v((c.Plus(d)).Minus(dp)); + glEnd(); + } else { + Point2d ref = SS.GW.ProjectPoint(c); + dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-6); + } + } + } + break; + default: oops(); } } diff --git a/entity.cpp b/entity.cpp index d1c9faa..6e1dbc0 100644 --- a/entity.cpp +++ b/entity.cpp @@ -16,6 +16,12 @@ void Entity::Csys2dGetBasisVectors(Vector *u, Vector *v) { *v = quat.RotationV(); } +Vector Entity::Csys2dGetNormalVector(void) { + Vector u, v; + Csys2dGetBasisVectors(&u, &v); + return u.Cross(v); +} + void Entity::Csys2dGetBasisExprs(ExprVector *u, ExprVector *v) { Expr *a = Expr::FromParam(param.h[0]); Expr *b = Expr::FromParam(param.h[1]); @@ -47,6 +53,10 @@ void Entity::Csys2dGetBasisExprs(ExprVector *u, ExprVector *v) { v->z = (v->z)->Plus(two->Times(c->Times(d))); } +ExprVector Entity::Csys2dGetOffsetExprs(void) { + return SS.GetEntity(assoc[0])->PointGetExprs(); +} + bool Entity::HasPlane(void) { switch(type) { case CSYS_2D: diff --git a/graphicswin.cpp b/graphicswin.cpp index 05cc836..4633ef8 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -73,8 +73,8 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "A&ngle\tShift+N", 0, 'N'|S, NULL }, { 1, "Other S&upplementary Angle\tShift+U", 0, 'U'|S, NULL }, { 1, NULL, 0, NULL }, -{ 1, "&Horizontal\tShift+H", 0, 'H'|S, NULL }, -{ 1, "&Vertical\tShift+V", 0, 'V'|S, NULL }, +{ 1, "&Horizontal\tShift+H", MNU_HORIZONTAL, 'H'|S, mCon }, +{ 1, "&Vertical\tShift+V", MNU_VERTICAL, 'V'|S, mCon }, { 1, NULL, 0, NULL }, { 1, "&On Point / Curve / Plane\tShift+O", MNU_ON_ENTITY, 'O'|S, mCon }, { 1, "E&qual Length / Radius\tShift+Q", MNU_EQUAL, 'Q'|S, mCon }, @@ -264,6 +264,8 @@ void GraphicsWindow::MenuEdit(int id) { SS.GW.ClearSelection(); SS.GW.pendingOperation = 0; SS.GW.pendingDescription = NULL; + SS.GW.pendingPoint.v = 0; + SS.GW.pendingConstraint.v = 0; SS.TW.ScreenNavigation('h', 0); SS.TW.Show(); break; @@ -360,7 +362,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, double dy = (y - orig.mouse.y) / scale; // When the view is locked, permit only translation (pan). - if(shiftDown || viewLocked) { + if(!(shiftDown || ctrlDown) || viewLocked) { offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x; offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y; offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z; @@ -404,7 +406,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, // Start dragging this point. ClearSelection(); pendingPoint = hover.entity; - pendingOperation = PENDING_OPERATION_DRAGGING_POINT; + pendingOperation = DRAGGING_POINT; } } else if(hover.constraint.v && SS.GetConstraint(hover.constraint)->HasLabel()) @@ -412,29 +414,28 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, if(dm > dmt) { ClearSelection(); pendingConstraint = hover.constraint; - pendingOperation = PENDING_OPERATION_DRAGGING_CONSTRAINT; + pendingOperation = DRAGGING_CONSTRAINT; } } - } else if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT || - pendingOperation == PENDING_OPERATION_DRAGGING_NEW_POINT) + } else if(pendingOperation == DRAGGING_POINT || + pendingOperation == DRAGGING_NEW_POINT || + pendingOperation == DRAGGING_NEW_LINE_POINT) { UpdateDraggedEntity(pendingPoint, x, y); - } else if(pendingOperation == PENDING_OPERATION_DRAGGING_CONSTRAINT) { + } else if(pendingOperation == DRAGGING_CONSTRAINT) { Constraint *c = SS.constraint.FindById(pendingConstraint); UpdateDraggedPoint(&(c->disp.offset), x, y); } } else { // No buttons pressed. - if(pendingOperation == PENDING_OPERATION_DRAGGING_NEW_POINT) { + if(pendingOperation == DRAGGING_NEW_POINT || + pendingOperation == DRAGGING_NEW_LINE_POINT) + { UpdateDraggedEntity(pendingPoint, x, y); + HitTestMakeSelection(mp); } else { // Do our usual hit testing, for the selection. - Selection s; - HitTestMakeSelection(mp, &s); - if(!s.Equals(&hover)) { - hover = s; - InvalidateGraphics(); - } + HitTestMakeSelection(mp); } } } @@ -457,18 +458,22 @@ void GraphicsWindow::Selection::Draw(void) { if(constraint.v) SS.GetConstraint(constraint)->Draw(); } -void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { +void GraphicsWindow::HitTestMakeSelection(Point2d mp) { int i; double d, dmin = 1e12; - - memset(dest, 0, sizeof(*dest)); + Selection s; + memset(&s, 0, sizeof(s)); // Do the entities for(i = 0; i < SS.entity.n; i++) { - d = SS.entity.elem[i].GetDistance(mp); + Entity *e = &(SS.entity.elem[i]); + // Don't hover whatever's being dragged. + if(e->h.request().v == pendingPoint.request().v) continue; + + d = e->GetDistance(mp); if(d < 10 && d < dmin) { - memset(dest, 0, sizeof(*dest)); - dest->entity = SS.entity.elem[i].h; + memset(&s, 0, sizeof(s)); + s.entity = e->h; dmin = d; } } @@ -477,11 +482,16 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) { for(i = 0; i < SS.constraint.n; i++) { d = SS.constraint.elem[i].GetDistance(mp); if(d < 10 && d < dmin) { - memset(dest, 0, sizeof(*dest)); - dest->constraint = SS.constraint.elem[i].h; + memset(&s, 0, sizeof(s)); + s.constraint = SS.constraint.elem[i].h; dmin = d; } } + + if(!s.Equals(&hover)) { + hover = s; + InvalidateGraphics(); + } } void GraphicsWindow::ClearSelection(void) { @@ -565,17 +575,41 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { hr = AddRequest(Request::LINE_SEGMENT); SS.GetEntity(hr.entity(1))->PointForceTo(v); - pendingOperation = PENDING_OPERATION_DRAGGING_NEW_POINT; + pendingOperation = DRAGGING_NEW_LINE_POINT; pendingPoint = hr.entity(2); pendingDescription = "click to place next point of line"; SS.GetEntity(pendingPoint)->PointForceTo(v); break; - case PENDING_OPERATION_DRAGGING_NEW_POINT: + case DRAGGING_NEW_POINT: // The MouseMoved event has already dragged it under the cursor. pendingOperation = 0; + pendingPoint.v = 0; break; + case DRAGGING_NEW_LINE_POINT: { + if(hover.entity.v && SS.GetEntity(hover.entity)->IsPoint()) { + Constraint::ConstrainCoincident(pendingPoint, hover.entity); + pendingOperation = 0; + pendingPoint.v = 0; + break; + } + // Create a new line segment, so that we continue drawing. + hRequest hr = AddRequest(Request::LINE_SEGMENT); + SS.GetEntity(hr.entity(1))->PointForceTo(v); + + // Constrain the line segments to share an endpoint + Constraint::ConstrainCoincident(pendingPoint, hr.entity(1)); + + // And drag an endpoint of the new line segment + pendingOperation = DRAGGING_NEW_LINE_POINT; + pendingPoint = hr.entity(2); + pendingDescription = "click to place next point of next line"; + SS.GetEntity(pendingPoint)->PointForceTo(v); + + break; + } + case 0: default: { pendingOperation = 0; @@ -609,8 +643,8 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { void GraphicsWindow::MouseLeftUp(double mx, double my) { switch(pendingOperation) { - case PENDING_OPERATION_DRAGGING_POINT: - case PENDING_OPERATION_DRAGGING_CONSTRAINT: + case DRAGGING_POINT: + case DRAGGING_CONSTRAINT: pendingOperation = 0; pendingPoint.v = 0; pendingConstraint.v = 0; diff --git a/polygon.h b/polygon.h new file mode 100644 index 0000000..95ea594 --- /dev/null +++ b/polygon.h @@ -0,0 +1,21 @@ + +#ifndef __POLYGON_H +#define __POLYGON_H + +class SPolyhedron { +public: +}; + +class SPolygon { +public: +}; + +class SContour { +public: +}; + +class SEdgeList { +public: +}; + +#endif diff --git a/sketch.h b/sketch.h index 061573e..baa6da6 100644 --- a/sketch.h +++ b/sketch.h @@ -58,9 +58,13 @@ public: int solveOrder; bool solved; + bool visible; + NameStr name; char *DescriptionString(void); + + SPolygon GetPolygon(void); }; @@ -140,7 +144,9 @@ public: // Applies only for a CSYS_2D type void Csys2dGetBasisVectors(Vector *u, Vector *v); + Vector Csys2dGetNormalVector(void); void Csys2dGetBasisExprs(ExprVector *u, ExprVector *v); + ExprVector Csys2dGetOffsetExprs(void); bool IsPoint(void); bool IsPointIn3d(void); @@ -260,6 +266,8 @@ public: void ModifyToSatisfy(void); void AddEq(IdList *l, Expr *expr, int index); static Expr *Distance(hEntity pa, hEntity pb); + + static void ConstrainCoincident(hEntity ptA, hEntity ptB); }; class hEquation { diff --git a/solvespace.h b/solvespace.h index 2fd9b6d..4eb8985 100644 --- a/solvespace.h +++ b/solvespace.h @@ -56,6 +56,7 @@ void MemFree(void *p); #include "dsc.h" +#include "polygon.h" #include "sketch.h" #include "ui.h" #include "expr.h" diff --git a/ui.h b/ui.h index 0cc0d70..02fc1f8 100644 --- a/ui.h +++ b/ui.h @@ -101,6 +101,8 @@ public: MNU_DISTANCE_DIA, MNU_EQUAL, MNU_ON_ENTITY, + MNU_HORIZONTAL, + MNU_VERTICAL, MNU_SOLVE_NOW, } MenuId; typedef void MenuHandler(int id); @@ -148,9 +150,10 @@ public: // Operations that must be completed by doing something with the mouse // are noted here. - static const int PENDING_OPERATION_DRAGGING_POINT = 0x0f000000; - static const int PENDING_OPERATION_DRAGGING_NEW_POINT = 0x0f000001; - static const int PENDING_OPERATION_DRAGGING_CONSTRAINT = 0x0f000002; + static const int DRAGGING_POINT = 0x0f000000; + static const int DRAGGING_NEW_POINT = 0x0f000001; + static const int DRAGGING_NEW_LINE_POINT = 0x0f000002; + static const int DRAGGING_CONSTRAINT = 0x0f000003; hEntity pendingPoint; hConstraint pendingConstraint; int pendingOperation; @@ -175,7 +178,7 @@ public: Selection hover; static const int MAX_SELECTED = 32; Selection selection[MAX_SELECTED]; - void HitTestMakeSelection(Point2d mp, Selection *dest); + void HitTestMakeSelection(Point2d mp); void ClearSelection(void); struct { hEntity point[MAX_SELECTED];