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)); } SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); } 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 *SBsp3::FromMesh(SMesh *m) {
SBsp3 *bsp3 = NULL; SBsp3 *bsp3 = NULL;
int i; int i;
@ -12,13 +28,10 @@ SBsp3 *SBsp3::FromMesh(SMesh *m) {
mc.AddTriangle(&(m->l.elem[i])); mc.AddTriangle(&(m->l.elem[i]));
} }
srand(0); // Let's be deterministic, at least! // The larger-area triangles have more trustworthy normals. By inserting
int n = mc.l.n; // those first, we hope to improve the numerical accuracy of our
while(n > 1) { // split planes.
int k = rand() % n; qsort(mc.l.elem, mc.l.n, sizeof(mc.l.elem[0]), ByArea);
n--;
SWAP(STriangle, mc.l.elem[k], mc.l.elem[n]);
}
for(i = 0; i < mc.l.n; i++) { for(i = 0; i < mc.l.n; i++) {
bsp3 = bsp3->Insert(&(mc.l.elem[i]), NULL); 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) bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown)
{ {
if(GraphicsEditControlIsVisible()) return; if(GraphicsEditControlIsVisible()) return;
if(rightDown) {
middleDown = true;
shiftDown = true;
}
Point2d mp = { x, y }; Point2d mp = { x, y };
@ -262,7 +266,7 @@ void GraphicsWindow::ClearPending(void) {
memset(&pending, 0, sizeof(pending)); memset(&pending, 0, sizeof(pending));
} }
void GraphicsWindow::MouseMiddleDown(double x, double y) { void GraphicsWindow::MouseMiddleOrRightDown(double x, double y) {
if(GraphicsEditControlIsVisible()) return; if(GraphicsEditControlIsVisible()) return;
orig.offset = offset; orig.offset = offset;

View File

@ -77,6 +77,7 @@ void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) {
glPopMatrix(); glPopMatrix();
} else { } else {
double l = swidth/2 - sheight/2; double l = swidth/2 - sheight/2;
l = max(l, 5/SS.GW.scale);
Point2d a = SS.GW.ProjectPoint(ref.Minus(gr.WithMagnitude(l))); Point2d a = SS.GW.ProjectPoint(ref.Minus(gr.WithMagnitude(l)));
Point2d b = SS.GW.ProjectPoint(ref.Plus (gr.WithMagnitude(l))); Point2d b = SS.GW.ProjectPoint(ref.Plus (gr.WithMagnitude(l)));
double d = dogd.mp.DistanceToLine(a, b.Minus(a), true); 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, static Vector AtIntersectionOfPlanes(Vector n1, double d1,
Vector n2, double d2); Vector n2, double d2);
double Element(int i);
bool Equals(Vector v); bool Equals(Vector v);
bool EqualsExactly(Vector v);
Vector Plus(Vector b); Vector Plus(Vector b);
Vector Minus(Vector b); Vector Minus(Vector b);
Vector Negated(void); Vector Negated(void);
@ -58,8 +60,10 @@ public:
Vector DotInToCsys(Vector u, Vector v, Vector n); Vector DotInToCsys(Vector u, Vector v, Vector n);
Vector ScaleOutOfCsys(Vector u, Vector v, Vector n); Vector ScaleOutOfCsys(Vector u, Vector v, Vector n);
double DistanceToLine(Vector p0, Vector dp); double DistanceToLine(Vector p0, Vector dp);
bool OnLineSegment(Vector a, Vector b);
Vector ClosestPointOnLine(Vector p0, Vector dp); Vector ClosestPointOnLine(Vector p0, Vector dp);
double Magnitude(void); double Magnitude(void);
double MagSquared(void);
Vector WithMagnitude(double s); Vector WithMagnitude(double s);
Vector ScaledBy(double s); Vector ScaledBy(double s);
Vector ProjectInto(hEntity wrkpl); Vector ProjectInto(hEntity wrkpl);

View File

@ -280,29 +280,20 @@ void glxDebugPolygon(SPolygon *p)
} }
} }
void glxDebugEdgeList(SEdgeList *el) void glxDrawEdges(SEdgeList *el)
{ {
int i; int i;
glLineWidth(2); glLineWidth(1);
glPointSize(7); glxDepthRangeOffset(2);
glDisable(GL_DEPTH_TEST); glxColor3d(REDf(SS.edgeColor), GREENf(SS.edgeColor), BLUEf(SS.edgeColor));
glBegin(GL_LINES);
for(i = 0; i < el->l.n; i++) { for(i = 0; i < el->l.n; i++) {
SEdge *se = &(el->l.elem[i]); SEdge *se = &(el->l.elem[i]);
if(se->tag) continue; glxVertex3v(se->a);
Vector a = se->a, b = se->b; glxVertex3v(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();
} }
glEnd();
} }
void glxDebugMesh(SMesh *m) void glxDebugMesh(SMesh *m)

View File

@ -350,7 +350,7 @@ void Group::GenerateMesh(void) {
if(type == TRANSLATE || type == ROTATE) { if(type == TRANSLATE || type == ROTATE) {
GenerateMeshForStepAndRepeat(); GenerateMeshForStepAndRepeat();
return; goto done;
} }
if(type == EXTRUDE) { if(type == EXTRUDE) {
@ -511,7 +511,7 @@ void Group::GenerateMesh(void) {
// same as last time, no combining required. // same as last time, no combining required.
if(thisMesh.l.n == 0) { if(thisMesh.l.n == 0) {
runningMesh.MakeFromCopy(PreviousGroupMesh()); runningMesh.MakeFromCopy(PreviousGroupMesh());
return; goto done;
} }
// So our group's mesh appears in thisMesh. Combine this with the previous // 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. // The error is reported in the text window for the group.
SS.later.showTW = true; 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) { SMesh *Group::PreviousGroupMesh(void) {
@ -574,10 +582,14 @@ void Group::Draw(void) {
if(gs.faces > 0) ms1 = gs.face[0].v; if(gs.faces > 0) ms1 = gs.face[0].v;
if(gs.faces > 1) ms2 = gs.face[1].v; if(gs.faces > 1) ms2 = gs.face[1].v;
if(SS.GW.showShaded) {
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
if(SS.GW.showShaded) glxFillMesh(specColor, &runningMesh, mh, ms1, ms2); glxFillMesh(specColor, &runningMesh, mh, ms1, ms2);
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
glxDrawEdges(&emphEdges);
}
if(meshError.yes) { if(meshError.yes) {
// Draw the error triangles in bright red stripes, with no Z buffering // Draw the error triangles in bright red stripes, with no Z buffering
GLubyte mask[32*32/8]; GLubyte mask[32*32/8];

335
mesh.cpp
View File

@ -293,3 +293,338 @@ DWORD SMesh::FirstIntersectionWith(Point2d mp) {
return face; 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; STriangle *tri;
STriangleLl *next; STriangleLl *next;
static STriangleLl *Alloc(void);
}; };
class SKdTree { class SKdNode {
public: public:
static const int BY_X = 0; static const int BY_X = 0;
static const int BY_Y = 1; static const int BY_Y = 1;
@ -227,10 +229,25 @@ public:
int which; int which;
double c; double c;
SKdTree *gt; SKdNode *gt;
SKdTree *lt; SKdNode *lt;
STriangleLl *tris; 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 #endif

View File

@ -146,6 +146,7 @@ public:
SMesh interferesAt; SMesh interferesAt;
bool yes; bool yes;
} meshError; } meshError;
SEdgeList emphEdges;
static const int COMBINE_AS_UNION = 0; static const int COMBINE_AS_UNION = 0;
static const int COMBINE_AS_DIFFERENCE = 1; 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"); viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits");
// Camera tangent (determines perspective) // Camera tangent (determines perspective)
cameraTangent = ((int)CnfThawDWORD(0, "CameraTangent"))/1e6; cameraTangent = ((int)CnfThawDWORD(0, "CameraTangent"))/1e6;
// Color for edges (drawn as lines for emphasis)
edgeColor = CnfThawDWORD(RGB(0, 0, 0), "EdgeColor");
// Recent files menus // Recent files menus
for(i = 0; i < MAX_RECENT; i++) { for(i = 0; i < MAX_RECENT; i++) {
char name[100]; char name[100];
@ -82,6 +84,8 @@ void SolveSpace::Exit(void) {
CnfFreezeDWORD((int)viewUnits, "ViewUnits"); CnfFreezeDWORD((int)viewUnits, "ViewUnits");
// Camera tangent (determines perspective) // Camera tangent (determines perspective)
CnfFreezeDWORD((int)(cameraTangent*1e6), "CameraTangent"); CnfFreezeDWORD((int)(cameraTangent*1e6), "CameraTangent");
// Color for edges (drawn as lines for emphasis)
CnfFreezeDWORD(edgeColor, "EdgeColor");
ExitNow(); ExitNow();
} }
@ -400,6 +404,7 @@ void SolveSpace::GenerateAll(int first, int last) {
memset(&deleted, 0, sizeof(deleted)); memset(&deleted, 0, sizeof(deleted));
} }
FreeAllTemporary();
allConsistent = true; allConsistent = true;
return; return;

View File

@ -126,7 +126,7 @@ void glxTesselatePolygon(GLUtesselator *gt, SPolygon *p);
void glxFillPolygon(SPolygon *p); void glxFillPolygon(SPolygon *p);
void glxFillMesh(int color, SMesh *m, DWORD h, DWORD s1, DWORD s2); void glxFillMesh(int color, SMesh *m, DWORD h, DWORD s1, DWORD s2);
void glxDebugPolygon(SPolygon *p); void glxDebugPolygon(SPolygon *p);
void glxDebugEdgeList(SEdgeList *l); void glxDrawEdges(SEdgeList *l);
void glxDebugMesh(SMesh *m); void glxDebugMesh(SMesh *m);
void glxMarkPolygonNormal(SPolygon *p); void glxMarkPolygonNormal(SPolygon *p);
void glxWriteText(char *str); void glxWriteText(char *str);
@ -348,6 +348,8 @@ public:
double lightIntensity[2]; double lightIntensity[2];
double meshTol; double meshTol;
double cameraTangent; double cameraTangent;
DWORD edgeColor;
int CircleSides(double r); int CircleSides(double r);
typedef enum { typedef enum {
UNIT_MM = 0, UNIT_MM = 0,

View File

@ -553,6 +553,14 @@ void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) {
ShowTextEditControl(43, 3, str); ShowTextEditControl(43, 3, str);
SS.TW.edit.meaning = EDIT_CAMERA_TANGENT; 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) { void TextWindow::ShowConfiguration(void) {
int i; int i;
Printf(true, "%Ft material color-(r, g, b)"); 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", Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E; now %d triangles",
SS.meshTol, SS.meshTol,
&ScreenChangeMeshTolerance, 0, &ScreenChangeMeshTolerance, 0,
SS.group.elem[SS.group.n-1].runningMesh.l.n); SS.GetGroup(SS.GW.activeGroup)->runningMesh.l.n);
Printf(false, ""); Printf(false, "");
Printf(false, "%Ft perspective factor (0 for isometric)%E"); Printf(false, "%Ft perspective factor (0 for isometric)%E");
Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E", Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E",
SS.cameraTangent*1000, SS.cameraTangent*1000,
&ScreenChangeCameraTangent, 0); &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(); InvalidateGraphics();
break; 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_TURNS:
case EDIT_HELIX_PITCH: case EDIT_HELIX_PITCH:
case EDIT_HELIX_DRADIUS: { case EDIT_HELIX_DRADIUS: {

12
ui.h
View File

@ -64,10 +64,11 @@ public:
static const int EDIT_COLOR = 5; static const int EDIT_COLOR = 5;
static const int EDIT_MESH_TOLERANCE = 6; static const int EDIT_MESH_TOLERANCE = 6;
static const int EDIT_CAMERA_TANGENT = 7; static const int EDIT_CAMERA_TANGENT = 7;
static const int EDIT_HELIX_TURNS = 8; static const int EDIT_EDGE_COLOR = 8;
static const int EDIT_HELIX_PITCH = 9; static const int EDIT_HELIX_TURNS = 20;
static const int EDIT_HELIX_DRADIUS = 10; static const int EDIT_HELIX_PITCH = 21;
static const int EDIT_TTF_TEXT = 11; static const int EDIT_HELIX_DRADIUS = 22;
static const int EDIT_TTF_TEXT = 23;
struct { struct {
int meaning; int meaning;
int i; int i;
@ -129,6 +130,7 @@ public:
static void ScreenChangeColor(int link, DWORD v); static void ScreenChangeColor(int link, DWORD v);
static void ScreenChangeMeshTolerance(int link, DWORD v); static void ScreenChangeMeshTolerance(int link, DWORD v);
static void ScreenChangeCameraTangent(int link, DWORD v); static void ScreenChangeCameraTangent(int link, DWORD v);
static void ScreenChangeEdgeColor(int link, DWORD v);
void EditControlDone(char *s); void EditControlDone(char *s);
}; };
@ -345,7 +347,7 @@ public:
void MouseLeftDown(double x, double y); void MouseLeftDown(double x, double y);
void MouseLeftUp(double x, double y); void MouseLeftUp(double x, double y);
void MouseLeftDoubleClick(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 MouseScroll(double x, double y, int delta);
void EditControlDone(char *s); void EditControlDone(char *s);
}; };

View File

@ -52,6 +52,7 @@ void SolveSpace::PushFromCurrentOnto(UndoStack *uk) {
ZERO(&(dest.thisMesh)); ZERO(&(dest.thisMesh));
ZERO(&(dest.runningMesh)); ZERO(&(dest.runningMesh));
ZERO(&(dest.meshError)); ZERO(&(dest.meshError));
ZERO(&(dest.emphEdges));
ZERO(&(dest.remap)); ZERO(&(dest.remap));
src->remap.DeepCopyInto(&(dest.remap)); src->remap.DeepCopyInto(&(dest.remap));
@ -92,6 +93,7 @@ void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) {
g->thisMesh.Clear(); g->thisMesh.Clear();
g->runningMesh.Clear(); g->runningMesh.Clear();
g->meshError.interferesAt.Clear(); g->meshError.interferesAt.Clear();
g->emphEdges.Clear();
g->remap.Clear(); g->remap.Clear();
g->impMesh.Clear(); g->impMesh.Clear();
g->impEntity.Clear(); g->impEntity.Clear();

View File

@ -275,8 +275,28 @@ Vector Vector::From(hParam x, hParam y, hParam z) {
return v; 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) { 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) { Vector Vector::Plus(Vector b) {
@ -405,6 +425,20 @@ double Vector::DistanceToLine(Vector p0, Vector dp) {
return ((this->Minus(p0)).Cross(dp)).Magnitude() / m; 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) { Vector Vector::ClosestPointOnLine(Vector p0, Vector dp) {
dp = dp.WithMagnitude(1); dp = dp.WithMagnitude(1);
// this, p0, and (p0+dp) define a plane; the min distance is in // 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)); return this->Plus(n.WithMagnitude(d));
} }
double Vector::MagSquared(void) {
return x*x + y*y + z*z;
}
double Vector::Magnitude(void) { double Vector::Magnitude(void) {
return sqrt(x*x + y*y + z*z); 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); p = HeapReAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, p, n);
vl();
if(!p) oops(); if(!p) oops();
return p; return p;
} }
void *MemAlloc(int n) { void *MemAlloc(int n) {
void *p = HeapAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n); void *p = HeapAlloc(Perm, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
vl();
if(!p) oops(); if(!p) oops();
return p; return p;
} }
void MemFree(void *p) { void MemFree(void *p) {
HeapFree(Perm, HEAP_NO_SERIALIZE, p); HeapFree(Perm, HEAP_NO_SERIALIZE, p);
vl();
} }
void vl(void) { void vl(void) {
@ -613,6 +610,7 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
case WM_LBUTTONUP: case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK: case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN: { case WM_MBUTTONDOWN: {
int x = LOWORD(lParam); int x = LOWORD(lParam);
int y = HIWORD(lParam); int y = HIWORD(lParam);
@ -631,8 +629,8 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
SS.GW.MouseLeftUp(x, y); SS.GW.MouseLeftUp(x, y);
} else if(msg == WM_LBUTTONDBLCLK) { } else if(msg == WM_LBUTTONDBLCLK) {
SS.GW.MouseLeftDoubleClick(x, y); SS.GW.MouseLeftDoubleClick(x, y);
} else if(msg == WM_MBUTTONDOWN) { } else if(msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
SS.GW.MouseMiddleDown(x, y); SS.GW.MouseMiddleOrRightDown(x, y);
} else if(msg == WM_MOUSEMOVE) { } else if(msg == WM_MOUSEMOVE) {
SS.GW.MouseMoved(x, y, SS.GW.MouseMoved(x, y,
!!(wParam & MK_LBUTTON), !!(wParam & MK_LBUTTON),

View File

@ -1,5 +1,4 @@
STL check for meshes, and T intersection removal
STL export STL export
DXF export DXF export
some kind of rounding / chamfer some kind of rounding / chamfer
@ -7,11 +6,12 @@ remove back button in browser?
auto-generate circles and faces when lathing auto-generate circles and faces when lathing
copy the section geometry to other end when sweeping copy the section geometry to other end when sweeping
cylindrical faces cylindrical faces
draw explicit edges
long term long term
----- -----
incremental regen of entities? incremental regen of entities?
my own plane poly triangulation code my own plane poly triangulation code
exact curved surfaces