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
parent
d4d5ab578d
commit
cad77c9c47
27
bsp.cpp
27
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);
|
||||
|
|
6
draw.cpp
6
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;
|
||||
|
|
|
@ -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
4
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);
|
||||
|
|
27
glhelper.cpp
27
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)
|
||||
|
|
|
@ -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
335
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();
|
||||
}
|
||||
|
||||
|
|
23
polygon.h
23
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
|
||||
|
|
1
sketch.h
1
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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
12
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);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
40
util.cpp
40
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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue