diff --git a/bsp.cpp b/bsp.cpp index 177ea29..adfeab4 100644 --- a/bsp.cpp +++ b/bsp.cpp @@ -3,6 +3,22 @@ SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); } SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); } +static int ByArea(const void *av, const void *bv) { + STriangle *a = (STriangle *)av; + STriangle *b = (STriangle *)bv; + + double fa = a->Normal().Magnitude(); + double fb = b->Normal().Magnitude(); + + if(fa > fb) { + return -1; + } else if(fa < fb) { + return 1; + } else { + return 0; + } +} + SBsp3 *SBsp3::FromMesh(SMesh *m) { SBsp3 *bsp3 = NULL; int i; @@ -12,13 +28,10 @@ SBsp3 *SBsp3::FromMesh(SMesh *m) { mc.AddTriangle(&(m->l.elem[i])); } - srand(0); // Let's be deterministic, at least! - int n = mc.l.n; - while(n > 1) { - int k = rand() % n; - n--; - SWAP(STriangle, mc.l.elem[k], mc.l.elem[n]); - } + // The larger-area triangles have more trustworthy normals. By inserting + // those first, we hope to improve the numerical accuracy of our + // split planes. + qsort(mc.l.elem, mc.l.n, sizeof(mc.l.elem[0]), ByArea); for(i = 0; i < mc.l.n; i++) { bsp3 = bsp3->Insert(&(mc.l.elem[i]), NULL); diff --git a/draw.cpp b/draw.cpp index 3eec33a..5b567e4 100644 --- a/draw.cpp +++ b/draw.cpp @@ -20,6 +20,10 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown) { if(GraphicsEditControlIsVisible()) return; + if(rightDown) { + middleDown = true; + shiftDown = true; + } Point2d mp = { x, y }; @@ -262,7 +266,7 @@ void GraphicsWindow::ClearPending(void) { memset(&pending, 0, sizeof(pending)); } -void GraphicsWindow::MouseMiddleDown(double x, double y) { +void GraphicsWindow::MouseMiddleOrRightDown(double x, double y) { if(GraphicsEditControlIsVisible()) return; orig.offset = offset; diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 1803f5c..a5f20cf 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -77,6 +77,7 @@ void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { glPopMatrix(); } else { double l = swidth/2 - sheight/2; + l = max(l, 5/SS.GW.scale); Point2d a = SS.GW.ProjectPoint(ref.Minus(gr.WithMagnitude(l))); Point2d b = SS.GW.ProjectPoint(ref.Plus (gr.WithMagnitude(l))); double d = dogd.mp.DistanceToLine(a, b.Minus(a), true); diff --git a/dsc.h b/dsc.h index 787ca39..cde82c6 100644 --- a/dsc.h +++ b/dsc.h @@ -46,7 +46,9 @@ public: static Vector AtIntersectionOfPlanes(Vector n1, double d1, Vector n2, double d2); + double Element(int i); bool Equals(Vector v); + bool EqualsExactly(Vector v); Vector Plus(Vector b); Vector Minus(Vector b); Vector Negated(void); @@ -58,8 +60,10 @@ public: Vector DotInToCsys(Vector u, Vector v, Vector n); Vector ScaleOutOfCsys(Vector u, Vector v, Vector n); double DistanceToLine(Vector p0, Vector dp); + bool OnLineSegment(Vector a, Vector b); Vector ClosestPointOnLine(Vector p0, Vector dp); double Magnitude(void); + double MagSquared(void); Vector WithMagnitude(double s); Vector ScaledBy(double s); Vector ProjectInto(hEntity wrkpl); diff --git a/glhelper.cpp b/glhelper.cpp index 86d84da..9c381b0 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -280,29 +280,20 @@ void glxDebugPolygon(SPolygon *p) } } -void glxDebugEdgeList(SEdgeList *el) +void glxDrawEdges(SEdgeList *el) { int i; - glLineWidth(2); - glPointSize(7); - glDisable(GL_DEPTH_TEST); + glLineWidth(1); + glxDepthRangeOffset(2); + glxColor3d(REDf(SS.edgeColor), GREENf(SS.edgeColor), BLUEf(SS.edgeColor)); + + glBegin(GL_LINES); for(i = 0; i < el->l.n; i++) { SEdge *se = &(el->l.elem[i]); - if(se->tag) continue; - Vector a = se->a, b = se->b; - - glxLockColorTo(0, 1, 1); - Vector d = (a.Minus(b)).WithMagnitude(-0); - glBegin(GL_LINES); - glxVertex3v(a.Plus(d)); - glxVertex3v(b.Minus(d)); - glEnd(); - glxLockColorTo(0, 0, 1); - glBegin(GL_POINTS); - glxVertex3v(a.Plus(d)); - glxVertex3v(b.Minus(d)); - glEnd(); + glxVertex3v(se->a); + glxVertex3v(se->b); } + glEnd(); } void glxDebugMesh(SMesh *m) diff --git a/groupmesh.cpp b/groupmesh.cpp index 88f0558..505b743 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -350,7 +350,7 @@ void Group::GenerateMesh(void) { if(type == TRANSLATE || type == ROTATE) { GenerateMeshForStepAndRepeat(); - return; + goto done; } if(type == EXTRUDE) { @@ -511,7 +511,7 @@ void Group::GenerateMesh(void) { // same as last time, no combining required. if(thisMesh.l.n == 0) { runningMesh.MakeFromCopy(PreviousGroupMesh()); - return; + goto done; } // So our group's mesh appears in thisMesh. Combine this with the previous @@ -536,6 +536,14 @@ void Group::GenerateMesh(void) { // The error is reported in the text window for the group. SS.later.showTW = true; } + +done: + emphEdges.Clear(); + if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) { + SKdNode *root = SKdNode::From(&runningMesh); + root->SnapToMesh(&runningMesh); + root->MakeEdgesToEmphasizeInto(&emphEdges); + } } SMesh *Group::PreviousGroupMesh(void) { @@ -574,9 +582,13 @@ void Group::Draw(void) { if(gs.faces > 0) ms1 = gs.face[0].v; if(gs.faces > 1) ms2 = gs.face[1].v; - glEnable(GL_LIGHTING); - if(SS.GW.showShaded) glxFillMesh(specColor, &runningMesh, mh, ms1, ms2); - glDisable(GL_LIGHTING); + if(SS.GW.showShaded) { + glEnable(GL_LIGHTING); + glxFillMesh(specColor, &runningMesh, mh, ms1, ms2); + glDisable(GL_LIGHTING); + + glxDrawEdges(&emphEdges); + } if(meshError.yes) { // Draw the error triangles in bright red stripes, with no Z buffering diff --git a/mesh.cpp b/mesh.cpp index b41e05a..3488dae 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -293,3 +293,338 @@ DWORD SMesh::FirstIntersectionWith(Point2d mp) { return face; } +#define KDTREE_EPS (20*LENGTH_EPS) // nice and sloppy + +STriangleLl *STriangleLl::Alloc(void) + { return (STriangleLl *)AllocTemporary(sizeof(STriangleLl)); } +SKdNode *SKdNode::Alloc(void) + { return (SKdNode *)AllocTemporary(sizeof(SKdNode)); } + +SKdNode *SKdNode::From(SMesh *m) { + int i; + STriangle *tra = (STriangle *)AllocTemporary((m->l.n) * sizeof(*tra)); + + for(i = 0; i < m->l.n; i++) { + tra[i] = m->l.elem[i]; + } + + srand(0); + int n = m->l.n; + while(n > 1) { + int k = rand() % n; + n--; + SWAP(STriangle, tra[k], tra[n]); + } + + STriangleLl *tll = NULL; + for(i = 0; i < m->l.n; i++) { + STriangleLl *tn = STriangleLl::Alloc(); + tn->tri = &(tra[i]); + tn->next = tll; + tll = tn; + } + + return SKdNode::From(tll, 0); +} + +SKdNode *SKdNode::From(STriangleLl *tll, int which) { + SKdNode *ret = Alloc(); + + if(!tll) goto leaf; + + int i; + int gtc[3] = { 0, 0, 0 }, ltc[3] = { 0, 0, 0 }, allc = 0; + double badness[3]; + double split[3]; + for(i = 0; i < 3; i++) { + int tcnt = 0; + STriangleLl *ll; + for(ll = tll; ll; ll = ll->next) { + STriangle *tr = ll->tri; + split[i] += (ll->tri->a).Element(i); + split[i] += (ll->tri->b).Element(i); + split[i] += (ll->tri->c).Element(i); + tcnt++; + } + split[i] /= (tcnt*3); + + for(ll = tll; ll; ll = ll->next) { + STriangle *tr = ll->tri; + + double a = (tr->a).Element(i), + b = (tr->b).Element(i), + c = (tr->c).Element(i); + + if(a < split[i] + KDTREE_EPS || + b < split[i] + KDTREE_EPS || + c < split[i] + KDTREE_EPS) + { + ltc[i]++; + } + if(a > split[i] - KDTREE_EPS || + b > split[i] - KDTREE_EPS || + c > split[i] - KDTREE_EPS) + { + gtc[i]++; + } + if(i == 0) allc++; + } + badness[i] = pow((double)ltc[i], 4) + pow((double)gtc[i], 4); + } + if(badness[0] < badness[1] && badness[0] < badness[2]) { + which = 0; + } else if(badness[1] < badness[2]) { + which = 1; + } else { + which = 2; + } + + if(allc < 10) goto leaf; + if(allc == gtc[which] || allc == ltc[which]) goto leaf; + + STriangleLl *ll; + STriangleLl *lgt = NULL, *llt = NULL; + for(ll = tll; ll; ll = ll->next) { + STriangle *tr = ll->tri; + + double a = (tr->a).Element(which), + b = (tr->b).Element(which), + c = (tr->c).Element(which); + + if(a < split[which] + KDTREE_EPS || + b < split[which] + KDTREE_EPS || + c < split[which] + KDTREE_EPS) + { + STriangleLl *n = STriangleLl::Alloc(); + *n = *ll; + n->next = llt; + llt = n; + } + if(a > split[which] - KDTREE_EPS || + b > split[which] - KDTREE_EPS || + c > split[which] - KDTREE_EPS) + { + STriangleLl *n = STriangleLl::Alloc(); + *n = *ll; + n->next = lgt; + lgt = n; + } + } + + ret->which = which; + ret->c = split[which]; + ret->gt = SKdNode::From(lgt, (which + 1) % 3); + ret->lt = SKdNode::From(llt, (which + 1) % 3); + return ret; + +leaf: +// dbp("leaf: allc=%d gtc=%d ltc=%d which=%d", allc, gtc[which], ltc[which], which); + ret->tris = tll; + return ret; +} + +void SKdNode::ClearTags(void) { + if(gt && lt) { + gt->ClearTags(); + lt->ClearTags(); + } else { + STriangleLl *ll; + for(ll = tris; ll; ll = ll->next) { + ll->tri->tag = 0; + } + } +} + +void SKdNode::AddTriangle(STriangle *tr) { + if(gt && lt) { + double ta = (tr->a).Element(which), + tb = (tr->b).Element(which), + tc = (tr->c).Element(which); + if(ta < c + KDTREE_EPS || + tb < c + KDTREE_EPS || + tc < c + KDTREE_EPS) + { + lt->AddTriangle(tr); + } + if(ta > c - KDTREE_EPS || + tb > c - KDTREE_EPS || + tc > c - KDTREE_EPS) + { + gt->AddTriangle(tr); + } + } else { + STriangleLl *tn = STriangleLl::Alloc(); + tn->tri = tr; + tn->next = tris; + tris = tn; + } +} + +void SKdNode::MakeMeshInto(SMesh *m) { + if(gt) gt->MakeMeshInto(m); + if(lt) lt->MakeMeshInto(m); + + STriangleLl *ll; + for(ll = tris; ll; ll = ll->next) { + if(ll->tri->tag) continue; + + m->AddTriangle(ll->tri); + ll->tri->tag = 1; + } +} + +void SKdNode::SnapToVertex(Vector v, SMesh *extras) { + if(gt && lt) { + double vc = v.Element(which); + if(vc < c + KDTREE_EPS) { + lt->SnapToVertex(v, extras); + } + if(vc > c - KDTREE_EPS) { + gt->SnapToVertex(v, extras); + } + // Nothing bad happens if the triangle to be split appears in both + // branches; the first call will split the triangle, so that the + // second call will do nothing, because the modified triangle will + // already contain v + } else { + STriangleLl *ll; + for(ll = tris; ll; ll = ll->next) { + STriangle *tr = ll->tri; + + // Do a cheap bbox test first + int k; + bool mightHit = true; + + for(k = 0; k < 3; k++) { + if((tr->a).Element(k) < v.Element(k) - KDTREE_EPS && + (tr->b).Element(k) < v.Element(k) - KDTREE_EPS && + (tr->c).Element(k) < v.Element(k) - KDTREE_EPS) + { + mightHit = false; + break; + } + if((tr->a).Element(k) > v.Element(k) + KDTREE_EPS && + (tr->b).Element(k) > v.Element(k) + KDTREE_EPS && + (tr->c).Element(k) > v.Element(k) + KDTREE_EPS) + { + mightHit = false; + break; + } + } + if(!mightHit) continue; + + if(tr->a.Equals(v)) { tr->a = v; continue; } + if(tr->b.Equals(v)) { tr->b = v; continue; } + if(tr->c.Equals(v)) { tr->c = v; continue; } + + if(v.OnLineSegment(tr->a, tr->b)) { + STriangle nt = STriangle::From(tr->meta, tr->a, v, tr->c); + extras->AddTriangle(&nt); + tr->a = v; + continue; + } + if(v.OnLineSegment(tr->b, tr->c)) { + STriangle nt = STriangle::From(tr->meta, tr->b, v, tr->a); + extras->AddTriangle(&nt); + tr->b = v; + continue; + } + if(v.OnLineSegment(tr->c, tr->a)) { + STriangle nt = STriangle::From(tr->meta, tr->c, v, tr->b); + extras->AddTriangle(&nt); + tr->c = v; + continue; + } + } + } +} + +void SKdNode::SnapToMesh(SMesh *m) { + int i, j, k; + for(i = 0; i < m->l.n; i++) { + STriangle *tr = &(m->l.elem[i]); + for(j = 0; j < 3; j++) { + Vector v = ((j == 0) ? tr->a : + ((j == 1) ? tr->b : + tr->c)); + + SMesh extra; + ZERO(&extra); + SnapToVertex(v, &extra); + + for(k = 0; k < extra.l.n; k++) { + STriangle *tra = (STriangle *)AllocTemporary(sizeof(*tra)); + *tra = extra.l.elem[k]; + AddTriangle(tra); + } + extra.Clear(); + } + } +} + +void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int *nOther, + STriMeta m, int cnt) +{ + if(gt && lt) { + double ac = a.Element(which), + bc = b.Element(which); + if(ac < c + KDTREE_EPS || + bc < c + KDTREE_EPS) + { + lt->FindEdgeOn(a, b, n, nOther, m, cnt); + } + if(ac > c - KDTREE_EPS || + bc > c - KDTREE_EPS) + { + gt->FindEdgeOn(a, b, n, nOther, m, cnt); + } + } else { + STriangleLl *ll; + for(ll = tris; ll; ll = ll->next) { + STriangle *tr = ll->tri; + + if(tr->tag == cnt) continue; + + if((a.EqualsExactly(tr->b) && b.EqualsExactly(tr->a)) || + (a.EqualsExactly(tr->c) && b.EqualsExactly(tr->b)) || + (a.EqualsExactly(tr->a) && b.EqualsExactly(tr->c))) + { + (*n)++; + if(tr->meta.face != m.face) (*nOther)++; + } + + tr->tag = cnt; + } + } +} + +void SKdNode::MakeEdgesToEmphasizeInto(SEdgeList *sel) { + SMesh m; + ZERO(&m); + ClearTags(); + MakeMeshInto(&m); + + int cnt = 1234; + int i, j; + for(i = 0; i < m.l.n; i++) { + STriangle *tr = &(m.l.elem[i]); + + for(j = 0; j < 3; j++) { + Vector a = (j == 0) ? tr->a : ((j == 1) ? tr->b : tr->c); + Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a); + + int n = 0, nOther = 0; + FindEdgeOn(a, b, &n, &nOther, tr->meta, cnt++); + if(n != 1) { + dbp("hanging edge: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)", + n, CO(a), CO(b)); + } + if(nOther > 0) { + sel->AddEdge(a, b); + } + } + } + + m.Clear(); +} + diff --git a/polygon.h b/polygon.h index 54b84aa..b9ac461 100644 --- a/polygon.h +++ b/polygon.h @@ -217,9 +217,11 @@ public: STriangle *tri; STriangleLl *next; + + static STriangleLl *Alloc(void); }; -class SKdTree { +class SKdNode { public: static const int BY_X = 0; static const int BY_Y = 1; @@ -227,10 +229,25 @@ public: int which; double c; - SKdTree *gt; - SKdTree *lt; + SKdNode *gt; + SKdNode *lt; STriangleLl *tris; + + static SKdNode *Alloc(void); + static SKdNode *From(SMesh *m); + static SKdNode *From(STriangleLl *tll, int which); + + void AddTriangle(STriangle *tr); + void MakeMeshInto(SMesh *m); + void ClearTags(void); + + void FindEdgeOn(Vector a, Vector b, int *n, int *nOther, + STriMeta m, int cnt); + void MakeEdgesToEmphasizeInto(SEdgeList *sel); + + void SnapToMesh(SMesh *m); + void SnapToVertex(Vector v, SMesh *extras); }; #endif diff --git a/sketch.h b/sketch.h index d550b8a..5401238 100644 --- a/sketch.h +++ b/sketch.h @@ -146,6 +146,7 @@ public: SMesh interferesAt; bool yes; } meshError; + SEdgeList emphEdges; static const int COMBINE_AS_UNION = 0; static const int COMBINE_AS_DIFFERENCE = 1; diff --git a/solvespace.cpp b/solvespace.cpp index 9848b01..8460be6 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -30,6 +30,8 @@ void SolveSpace::Init(char *cmdLine) { viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits"); // Camera tangent (determines perspective) cameraTangent = ((int)CnfThawDWORD(0, "CameraTangent"))/1e6; + // Color for edges (drawn as lines for emphasis) + edgeColor = CnfThawDWORD(RGB(0, 0, 0), "EdgeColor"); // Recent files menus for(i = 0; i < MAX_RECENT; i++) { char name[100]; @@ -82,6 +84,8 @@ void SolveSpace::Exit(void) { CnfFreezeDWORD((int)viewUnits, "ViewUnits"); // Camera tangent (determines perspective) CnfFreezeDWORD((int)(cameraTangent*1e6), "CameraTangent"); + // Color for edges (drawn as lines for emphasis) + CnfFreezeDWORD(edgeColor, "EdgeColor"); ExitNow(); } @@ -399,7 +403,8 @@ void SolveSpace::GenerateAll(int first, int last) { deleted.groups, deleted.groups == 1 ? "" : "s"); memset(&deleted, 0, sizeof(deleted)); } - + + FreeAllTemporary(); allConsistent = true; return; diff --git a/solvespace.h b/solvespace.h index ddeb869..7d4c2d9 100644 --- a/solvespace.h +++ b/solvespace.h @@ -126,7 +126,7 @@ void glxTesselatePolygon(GLUtesselator *gt, SPolygon *p); void glxFillPolygon(SPolygon *p); void glxFillMesh(int color, SMesh *m, DWORD h, DWORD s1, DWORD s2); void glxDebugPolygon(SPolygon *p); -void glxDebugEdgeList(SEdgeList *l); +void glxDrawEdges(SEdgeList *l); void glxDebugMesh(SMesh *m); void glxMarkPolygonNormal(SPolygon *p); void glxWriteText(char *str); @@ -348,6 +348,8 @@ public: double lightIntensity[2]; double meshTol; double cameraTangent; + DWORD edgeColor; + int CircleSides(double r); typedef enum { UNIT_MM = 0, diff --git a/textscreens.cpp b/textscreens.cpp index 6dcf836..eba0c15 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -553,6 +553,14 @@ void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) { ShowTextEditControl(43, 3, str); SS.TW.edit.meaning = EDIT_CAMERA_TANGENT; } +void TextWindow::ScreenChangeEdgeColor(int link, DWORD v) { + char str[1024]; + sprintf(str, "%.3f, %.3f, %.3f", + REDf(SS.edgeColor), GREENf(SS.edgeColor), BLUEf(SS.edgeColor)); + + ShowTextEditControl(49, 3, str); + SS.TW.edit.meaning = EDIT_EDGE_COLOR; +} void TextWindow::ShowConfiguration(void) { int i; Printf(true, "%Ft material color-(r, g, b)"); @@ -583,13 +591,19 @@ void TextWindow::ShowConfiguration(void) { Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E; now %d triangles", SS.meshTol, &ScreenChangeMeshTolerance, 0, - SS.group.elem[SS.group.n-1].runningMesh.l.n); + SS.GetGroup(SS.GW.activeGroup)->runningMesh.l.n); Printf(false, ""); Printf(false, "%Ft perspective factor (0 for isometric)%E"); Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E", SS.cameraTangent*1000, &ScreenChangeCameraTangent, 0); + + Printf(false, ""); + Printf(false, "%Ft edge color r,g,b (0,0,0 for no edges)%E"); + Printf(false, "%Ba %@, %@, %@ %Fl%Ll%f%D[change]%E", + REDf(SS.edgeColor), GREENf(SS.edgeColor), BLUEf(SS.edgeColor), + &ScreenChangeEdgeColor, 0); } //----------------------------------------------------------------------------- @@ -664,6 +678,16 @@ void TextWindow::EditControlDone(char *s) { InvalidateGraphics(); break; } + case EDIT_EDGE_COLOR: { + double r, g, b; + if(sscanf(s, "%lf, %lf, %lf", &r, &g, &b)==3) { + SS.edgeColor = RGB(r*255, g*255, b*255); + } else { + Error("Bad format: specify color as r, g, b"); + } + SS.GenerateAll(0, INT_MAX); + break; + } case EDIT_HELIX_TURNS: case EDIT_HELIX_PITCH: case EDIT_HELIX_DRADIUS: { diff --git a/ui.h b/ui.h index 0ee602e..1009a58 100644 --- a/ui.h +++ b/ui.h @@ -64,10 +64,11 @@ public: static const int EDIT_COLOR = 5; static const int EDIT_MESH_TOLERANCE = 6; static const int EDIT_CAMERA_TANGENT = 7; - static const int EDIT_HELIX_TURNS = 8; - static const int EDIT_HELIX_PITCH = 9; - static const int EDIT_HELIX_DRADIUS = 10; - static const int EDIT_TTF_TEXT = 11; + static const int EDIT_EDGE_COLOR = 8; + static const int EDIT_HELIX_TURNS = 20; + static const int EDIT_HELIX_PITCH = 21; + static const int EDIT_HELIX_DRADIUS = 22; + static const int EDIT_TTF_TEXT = 23; struct { int meaning; int i; @@ -129,6 +130,7 @@ public: static void ScreenChangeColor(int link, DWORD v); static void ScreenChangeMeshTolerance(int link, DWORD v); static void ScreenChangeCameraTangent(int link, DWORD v); + static void ScreenChangeEdgeColor(int link, DWORD v); void EditControlDone(char *s); }; @@ -345,7 +347,7 @@ public: void MouseLeftDown(double x, double y); void MouseLeftUp(double x, double y); void MouseLeftDoubleClick(double x, double y); - void MouseMiddleDown(double x, double y); + void MouseMiddleOrRightDown(double x, double y); void MouseScroll(double x, double y, int delta); void EditControlDone(char *s); }; diff --git a/undoredo.cpp b/undoredo.cpp index 806bb69..45b72d2 100644 --- a/undoredo.cpp +++ b/undoredo.cpp @@ -52,6 +52,7 @@ void SolveSpace::PushFromCurrentOnto(UndoStack *uk) { ZERO(&(dest.thisMesh)); ZERO(&(dest.runningMesh)); ZERO(&(dest.meshError)); + ZERO(&(dest.emphEdges)); ZERO(&(dest.remap)); src->remap.DeepCopyInto(&(dest.remap)); @@ -92,6 +93,7 @@ void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) { g->thisMesh.Clear(); g->runningMesh.Clear(); g->meshError.interferesAt.Clear(); + g->emphEdges.Clear(); g->remap.Clear(); g->impMesh.Clear(); g->impEntity.Clear(); diff --git a/util.cpp b/util.cpp index 0fcaa1c..cbfb294 100644 --- a/util.cpp +++ b/util.cpp @@ -275,8 +275,28 @@ Vector Vector::From(hParam x, hParam y, hParam z) { return v; } +double Vector::Element(int i) { + switch(i) { + case 0: return x; + case 1: return y; + case 2: return z; + default: oops(); + } +} + bool Vector::Equals(Vector v) { - return (this->Minus(v)).Magnitude() < LENGTH_EPS; + // Quick axis-aligned tests before going further + double dx = v.x - x; if(dx < -LENGTH_EPS || dx > LENGTH_EPS) return false; + double dy = v.y - y; if(dy < -LENGTH_EPS || dy > LENGTH_EPS) return false; + double dz = v.z - z; if(dz < -LENGTH_EPS || dz > LENGTH_EPS) return false; + + return (this->Minus(v)).MagSquared() < LENGTH_EPS*LENGTH_EPS; +} + +bool Vector::EqualsExactly(Vector v) { + return (x == v.x) && + (y == v.y) && + (z == v.z); } Vector Vector::Plus(Vector b) { @@ -405,6 +425,20 @@ double Vector::DistanceToLine(Vector p0, Vector dp) { return ((this->Minus(p0)).Cross(dp)).Magnitude() / m; } +bool Vector::OnLineSegment(Vector a, Vector b) { + Vector d = b.Minus(a); + + double m = d.MagSquared(); + double distsq = ((this->Minus(a)).Cross(d)).MagSquared() / m; + + if(distsq >= LENGTH_EPS*LENGTH_EPS) return false; + + double t = (this->Minus(a)).DivPivoting(d); + // On-endpoint must be tested for separately. + if(t < 0 || t > 1) return false; + return true; +} + Vector Vector::ClosestPointOnLine(Vector p0, Vector dp) { dp = dp.WithMagnitude(1); // this, p0, and (p0+dp) define a plane; the min distance is in @@ -419,6 +453,10 @@ Vector Vector::ClosestPointOnLine(Vector p0, Vector dp) { return this->Plus(n.WithMagnitude(d)); } +double Vector::MagSquared(void) { + return x*x + y*y + z*z; +} + double Vector::Magnitude(void) { return sqrt(x*x + y*y + z*z); } diff --git a/win32/w32main.cpp b/win32/w32main.cpp index 18ca6ed..6949268 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -124,19 +124,16 @@ void *MemRealloc(void *p, int n) { } p = HeapReAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, p, n); - vl(); if(!p) oops(); return p; } void *MemAlloc(int n) { void *p = HeapAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n); - vl(); if(!p) oops(); return p; } void MemFree(void *p) { HeapFree(Perm, HEAP_NO_SERIALIZE, p); - vl(); } void vl(void) { @@ -613,6 +610,7 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: { int x = LOWORD(lParam); int y = HIWORD(lParam); @@ -631,8 +629,8 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, SS.GW.MouseLeftUp(x, y); } else if(msg == WM_LBUTTONDBLCLK) { SS.GW.MouseLeftDoubleClick(x, y); - } else if(msg == WM_MBUTTONDOWN) { - SS.GW.MouseMiddleDown(x, y); + } else if(msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) { + SS.GW.MouseMiddleOrRightDown(x, y); } else if(msg == WM_MOUSEMOVE) { SS.GW.MouseMoved(x, y, !!(wParam & MK_LBUTTON), diff --git a/wishlist.txt b/wishlist.txt index 21508c2..4700ab9 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,5 +1,4 @@ -STL check for meshes, and T intersection removal STL export DXF export some kind of rounding / chamfer @@ -7,11 +6,12 @@ remove back button in browser? auto-generate circles and faces when lathing copy the section geometry to other end when sweeping cylindrical faces -draw explicit edges long term ----- incremental regen of entities? my own plane poly triangulation code +exact curved surfaces +