From 7622534b2ad87328f3cc26fb3fb7570100e0ea4a Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Wed, 9 Jul 2008 21:26:08 -0800 Subject: [PATCH] Make the piecewise linear stuff for cubics and quadratics adaptive, based on a chord tolerance. And rewrite the pwl circles to work against a chord tolerance too (which they really were doing before, but in funny units). Also make "assemble" combine type do a union after interference checking; was previously just copying, which meant that coplanar faces could break subsequent operations. And make right-clicking effectively toggle shift key, instead of forcing it on; so you can pan or rotate with either right or middle button. [git-p4: depot-paths = "//depot/solvespace/": change = 1829] --- draw.cpp | 2 +- drawentity.cpp | 50 ++++++++++++++++++++++++++++++++++++------------- mesh.cpp | 18 ++++++++++-------- sketch.h | 4 ++++ solvespace.cpp | 26 +++++++++++++++++++------ solvespace.h | 4 +++- textscreens.cpp | 16 ++++++++-------- ttf.cpp | 39 +++++++++++++++++++++++++------------- ui.h | 4 ++-- wishlist.txt | 1 - 10 files changed, 111 insertions(+), 53 deletions(-) diff --git a/draw.cpp b/draw.cpp index 5b567e4..e7ac5bd 100644 --- a/draw.cpp +++ b/draw.cpp @@ -22,7 +22,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, if(GraphicsEditControlIsVisible()) return; if(rightDown) { middleDown = true; - shiftDown = true; + shiftDown = !shiftDown; } Point2d mp = { x, y }; diff --git a/drawentity.cpp b/drawentity.cpp index 929c2c7..bf09d6a 100644 --- a/drawentity.cpp +++ b/drawentity.cpp @@ -3,7 +3,7 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) { if(dogd.drawing) { // Draw lines from active group in front of those from previous - glxDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 2); + glxDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3); glBegin(GL_LINES); glxVertex3v(a); glxVertex3v(b); @@ -132,6 +132,41 @@ bool Entity::IsVisible(void) { return true; } +Vector Entity::BezierEval(double t, Vector p0, Vector p1, Vector p2, Vector p3) +{ + return (p0.ScaledBy((1 - t)*(1 - t)*(1 - t))).Plus( + (p1.ScaledBy(3*t*(1 - t)*(1 - t))).Plus( + (p2.ScaledBy(3*t*t*(1 - t))).Plus( + (p3.ScaledBy(t*t*t))))); +} + +void Entity::BezierPwl(double ta, double tb, + Vector p0, Vector p1, Vector p2, Vector p3) +{ + Vector pa = BezierEval(ta, p0, p1, p2, p3); + Vector pb = BezierEval(tb, p0, p1, p2, p3); + + double tm1 = (2*ta + tb) / 3; + double tm2 = (ta + 2*tb) / 3; + + Vector pm1 = BezierEval(tm1, p0, p1, p2, p3); + Vector pm2 = BezierEval(tm2, p0, p1, p2, p3); + + double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)), + pm2.DistanceToLine(pa, pb.Minus(pa))); + + double tol = 0.5*SS.chordTol/SS.GW.scale; + + if((tb - ta) < 0.05 || d < tol) { + LineDrawOrGetDistanceOrEdge(pa, pb); + } else { + double tm = (ta + tb) / 2; + BezierPwl(ta, tm, p0, p1, p2, p3); + BezierPwl(tm, tb, p0, p1, p2, p3); + } +} + + void Entity::DrawOrGetDistance(void) { // If an entity is invisible, then it doesn't get shown, and it doesn't // contribute a distance for the selection, but it still generates edges. @@ -301,18 +336,7 @@ void Entity::DrawOrGetDistance(void) { Vector p1 = SS.GetEntity(point[1])->PointGetNum(); Vector p2 = SS.GetEntity(point[2])->PointGetNum(); Vector p3 = SS.GetEntity(point[3])->PointGetNum(); - int i, n = (int)(15/sqrt(SS.meshTol)); - Vector prev = p0; - for(i = 1; i <= n; i++) { - double t = ((double)i)/n; - Vector p = - (p0.ScaledBy((1 - t)*(1 - t)*(1 - t))).Plus( - (p1.ScaledBy(3*t*(1 - t)*(1 - t))).Plus( - (p2.ScaledBy(3*t*t*(1 - t))).Plus( - (p3.ScaledBy(t*t*t))))); - LineDrawOrGetDistanceOrEdge(prev, p); - prev = p; - } + BezierPwl(0, 1, p0, p1, p2, p3); break; } diff --git a/mesh.cpp b/mesh.cpp index 47fcc4a..1ed1967 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -244,15 +244,17 @@ bool SMesh::MakeFromInterferenceCheck(SMesh *srca, SMesh *srcb, SMesh *error) { // Now we have a list of all the triangles (or fragments thereof) from // A that lie inside B, or vice versa. That's the interference, and // we report it so that it can be flagged. + + // For the actual output, take the union. + flipNormal = false; + keepCoplanar = false; + AddAgainstBsp(srcb, bspa); + + flipNormal = false; + keepCoplanar = true; + AddAgainstBsp(srca, bspb); - // But as far as the actual model, we just copy everything over. - int i; - for(i = 0; i < srca->l.n; i++) { - AddTriangle(&(srca->l.elem[i])); - } - for(i = 0; i < srcb->l.n; i++) { - AddTriangle(&(srcb->l.elem[i])); - } + // And we're successful if the intersection was empty. return (error->l.n == 0); } diff --git a/sketch.h b/sketch.h index 5401238..5c1ebb9 100644 --- a/sketch.h +++ b/sketch.h @@ -393,6 +393,10 @@ public: void LineDrawOrGetDistanceOrEdge(Vector a, Vector b); void DrawOrGetDistance(void); + void BezierPwl(double ta, double tb, + Vector p0, Vector p1, Vector p2, Vector p3); + Vector BezierEval(double t, Vector p0, Vector p1, Vector p2, Vector p3); + static void DrawAll(void); void Draw(void); double GetDistance(Point2d mp); diff --git a/solvespace.cpp b/solvespace.cpp index 05ce00f..2c1d685 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -23,8 +23,8 @@ void SolveSpace::Init(char *cmdLine) { lightDir[1].x = CnfThawFloat( 1.0f, "LightDir_1_Right" ); lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up" ); lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward" ); - // Mesh tolerance - meshTol = CnfThawFloat(1.0f, "MeshTolerance"); + // Chord tolerance + chordTol = CnfThawFloat(2.0f, "ChordTolerance"); // View units viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits"); // Camera tangent (determines perspective) @@ -79,8 +79,8 @@ void SolveSpace::Exit(void) { CnfFreezeFloat((float)lightDir[1].x, "LightDir_1_Right"); CnfFreezeFloat((float)lightDir[1].y, "LightDir_1_Up"); CnfFreezeFloat((float)lightDir[1].z, "LightDir_1_Forward"); - // Mesh tolerance - CnfFreezeFloat((float)meshTol, "MeshTolerance"); + // Chord tolerance + CnfFreezeFloat((float)chordTol, "ChordTolerance"); // Display/entry units CnfFreezeDWORD((DWORD)viewUnits, "ViewUnits"); // Camera tangent (determines perspective) @@ -100,8 +100,22 @@ void SolveSpace::DoLater(void) { } int SolveSpace::CircleSides(double r) { - int s = 7 + (int)(sqrt(r*SS.GW.scale/meshTol)); - return min(s, 40); + // Let the pwl segment be symmetric about the x axis; then the curve + // goes out to r, and if there's n segments, then the endpoints are + // at +/- (2pi/n)/2 = +/- pi/n. So the chord goes to x = r cos pi/n, + // from x = r, so it's + // tol = r - r cos pi/n + // tol = r(1 - cos pi/n) + // tol ~ r(1 - (1 - (pi/n)^2/2)) (Taylor expansion) + // tol = r((pi/n)^2/2) + // 2*tol/r = (pi/n)^2 + // sqrt(2*tol/r) = pi/n + // n = pi/sqrt(2*tol/r); + + double tol = chordTol/GW.scale; + int n = 3 + (int)(PI/sqrt(2*tol/r)); + + return max(7, min(n, 40)); } char *SolveSpace::MmToString(double v) { diff --git a/solvespace.h b/solvespace.h index c8156ca..ed66457 100644 --- a/solvespace.h +++ b/solvespace.h @@ -290,6 +290,8 @@ public: Vector TransformIntPoint(int x, int y); void LineSegment(int x0, int y0, int x1, int y1); void Bezier(int x0, int y0, int x1, int y1, int x2, int y2); + void BezierPwl(double ta, double tb, Vector p0, Vector p1, Vector p2); + Vector BezierEval(double t, Vector p0, Vector p1, Vector p2); }; class TtfFontList { @@ -354,7 +356,7 @@ public: int modelColor[MODEL_COLORS]; Vector lightDir[2]; double lightIntensity[2]; - double meshTol; + double chordTol; double cameraTangent; DWORD edgeColor; float exportScale; diff --git a/textscreens.cpp b/textscreens.cpp index e7318da..ac06d16 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -541,11 +541,11 @@ void TextWindow::ScreenChangeColor(int link, DWORD v) { SS.TW.edit.meaning = EDIT_COLOR; SS.TW.edit.i = v; } -void TextWindow::ScreenChangeMeshTolerance(int link, DWORD v) { +void TextWindow::ScreenChangeChordTolerance(int link, DWORD v) { char str[1024]; - sprintf(str, "%.2f", SS.meshTol); + sprintf(str, "%.2f", SS.chordTol); ShowTextEditControl(37, 3, str); - SS.TW.edit.meaning = EDIT_MESH_TOLERANCE; + SS.TW.edit.meaning = EDIT_CHORD_TOLERANCE; } void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) { char str[1024]; @@ -594,10 +594,10 @@ void TextWindow::ShowConfiguration(void) { } Printf(false, ""); - Printf(false, "%Ft mesh tolerance (smaller is finer)%E"); + Printf(false, "%Ft chord tolerance (in screen pixels)%E"); Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E; now %d triangles", - SS.meshTol, - &ScreenChangeMeshTolerance, 0, + SS.chordTol, + &ScreenChangeChordTolerance, 0, SS.GetGroup(SS.GW.activeGroup)->runningMesh.l.n); Printf(false, ""); @@ -702,8 +702,8 @@ void TextWindow::EditControlDone(char *s) { } break; } - case EDIT_MESH_TOLERANCE: { - SS.meshTol = min(10, max(0.1, atof(s))); + case EDIT_CHORD_TOLERANCE: { + SS.chordTol = min(10, max(0.1, atof(s))); SS.GenerateAll(0, INT_MAX); break; } diff --git a/ttf.cpp b/ttf.cpp index 98260c9..2f8c1b6 100644 --- a/ttf.cpp +++ b/ttf.cpp @@ -690,23 +690,36 @@ void TtfFont::LineSegment(int x0, int y0, int x1, int y1) { TransformIntPoint(x1, y1)); } -void TtfFont::Bezier(int x0, int y0, int x1, int y1, int x2, int y2) { - Entity *e = SS.GetEntity(entity); +Vector TtfFont::BezierEval(double t, Vector p0, Vector p1, Vector p2) { + return (p0.ScaledBy((1 - t)*(1 - t))).Plus( + (p1.ScaledBy(2*t*(1 - t))).Plus( + (p2.ScaledBy(t*t)))); +} +void TtfFont::BezierPwl(double ta, double tb, Vector p0, Vector p1, Vector p2) { + Vector pa = BezierEval(ta, p0, p1, p2); + Vector pb = BezierEval(tb, p0, p1, p2); + + double tm = (ta + tb) / 2; + + Vector pm = BezierEval(tm, p0, p1, p2); + + double tol = 0.5*SS.chordTol/SS.GW.scale; + + if((tb - ta) < 0.05 || pm.DistanceToLine(pa, pb.Minus(pa)) < tol) { + Entity *e = SS.GetEntity(entity); + e->LineDrawOrGetDistanceOrEdge(pa, pb); + } else { + BezierPwl(ta, tm, p0, p1, p2); + BezierPwl(tm, tb, p0, p1, p2); + } +} + +void TtfFont::Bezier(int x0, int y0, int x1, int y1, int x2, int y2) { Vector p0 = TransformIntPoint(x0, y0), p1 = TransformIntPoint(x1, y1), p2 = TransformIntPoint(x2, y2); - int i, n = max(2, (int)(4/sqrt(SS.meshTol))); - Vector prev = p0; - for(i = 1; i <= n; i++) { - double t = ((double)i)/n; - Vector p = - (p0.ScaledBy((1 - t)*(1 - t))).Plus( - (p1.ScaledBy(2*t*(1 - t))).Plus( - (p2.ScaledBy(t*t)))); - e->LineDrawOrGetDistanceOrEdge(prev, p); - prev = p; - } + BezierPwl(0, 1, p0, p1, p2); } diff --git a/ui.h b/ui.h index 7546411..422cb26 100644 --- a/ui.h +++ b/ui.h @@ -62,7 +62,7 @@ public: static const int EDIT_LIGHT_DIRECTION = 3; static const int EDIT_LIGHT_INTENSITY = 4; static const int EDIT_COLOR = 5; - static const int EDIT_MESH_TOLERANCE = 6; + static const int EDIT_CHORD_TOLERANCE = 6; static const int EDIT_CAMERA_TANGENT = 7; static const int EDIT_EDGE_COLOR = 8; static const int EDIT_EXPORT_SCALE = 9; @@ -129,7 +129,7 @@ public: static void ScreenChangeLightDirection(int link, DWORD v); static void ScreenChangeLightIntensity(int link, DWORD v); static void ScreenChangeColor(int link, DWORD v); - static void ScreenChangeMeshTolerance(int link, DWORD v); + static void ScreenChangeChordTolerance(int link, DWORD v); static void ScreenChangeCameraTangent(int link, DWORD v); static void ScreenChangeEdgeColor(int link, DWORD v); static void ScreenChangeExportScale(int link, DWORD v); diff --git a/wishlist.txt b/wishlist.txt index fe2847e..bb4ce18 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,5 +1,4 @@ -adaptive pwl for polynomial curves some kind of rounding / chamfer remove back button in browser? auto-generate circles and faces when lathing