Add routines to remove T intersections from the mesh, which are

introduced by the bsp routines. It's usually, though not always,
possible to generate a watertight mesh. The occasions where it's
not look ugly, floating point issues, no quick fix.

And use those to generate a list of edges where two different faces
meet, which I can emphasize for cosmetic reasons (and some UI to
specify whether to do that, and with what color).

And make the right mouse button rotate the model, since that was
previously doing nothing.

[git-p4: depot-paths = "//depot/solvespace/": change = 1821]
solver
Jonathan Westhues 2008-07-05 23:56:24 -08:00
parent d4d5ab578d
commit cad77c9c47
17 changed files with 499 additions and 50 deletions

27
bsp.cpp
View File

@ -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);

View File

@ -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;

View File

@ -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);

4
dsc.h
View File

@ -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);

View File

@ -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)

View File

@ -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

335
mesh.cpp
View File

@ -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();
}

View File

@ -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

View File

@ -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;

View File

@ -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();
}
@ -400,6 +404,7 @@ void SolveSpace::GenerateAll(int first, int last) {
memset(&deleted, 0, sizeof(deleted));
}
FreeAllTemporary();
allConsistent = true;
return;

View File

@ -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,

View File

@ -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: {

12
ui.h
View File

@ -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);
};

View File

@ -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();

View File

@ -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);
}

View File

@ -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),

View File

@ -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