Add hidden line and surface removal, and vector shaded surface
export. So I calculate lighting for each triangle in the mesh, make a BSP, and then traverse it in-order and output those as SVG or EPS. And I test edges against the mesh, removing those portions of the edge that overlap a triangle in front of them (using the kd-tree to accelerate). [git-p4: depot-paths = "//depot/solvespace/": change = 1931]solver
parent
ed9f448398
commit
1a845c3432
24
bsp.cpp
24
bsp.cpp
|
@ -390,6 +390,30 @@ SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SBsp3::GenerateInPaintOrder(SMesh *m) {
|
||||||
|
if(!this) return;
|
||||||
|
|
||||||
|
// Doesn't matter which branch we take if the normal has zero z
|
||||||
|
// component, so don't need a separate case for that.
|
||||||
|
if(n.z < 0) {
|
||||||
|
pos->GenerateInPaintOrder(m);
|
||||||
|
} else {
|
||||||
|
neg->GenerateInPaintOrder(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
SBsp3 *flip = this;
|
||||||
|
while(flip) {
|
||||||
|
m->AddTriangle(&(flip->tri));
|
||||||
|
flip = flip->more;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n.z < 0) {
|
||||||
|
neg->GenerateInPaintOrder(m);
|
||||||
|
} else {
|
||||||
|
pos->GenerateInPaintOrder(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SBsp3::DebugDraw(void) {
|
void SBsp3::DebugDraw(void) {
|
||||||
if(!this) return;
|
if(!this) return;
|
||||||
|
|
||||||
|
|
4
draw.cpp
4
draw.cpp
|
@ -992,7 +992,9 @@ void GraphicsWindow::Paint(int w, int h) {
|
||||||
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0);
|
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLfloat ambient[4] = { 0.4f, 0.4f, 0.4f, 1.0f };
|
GLfloat ambient[4] = { (float)SS.ambientIntensity,
|
||||||
|
(float)SS.ambientIntensity,
|
||||||
|
(float)SS.ambientIntensity, 1 };
|
||||||
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
|
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
|
||||||
|
|
||||||
glxUnlockColor();
|
glxUnlockColor();
|
||||||
|
|
2
dsc.h
2
dsc.h
|
@ -88,6 +88,8 @@ public:
|
||||||
static bool BoundingBoxIntersectsLine(Vector amax, Vector amin,
|
static bool BoundingBoxIntersectsLine(Vector amax, Vector amin,
|
||||||
Vector p0, Vector p1, bool segment);
|
Vector p0, Vector p1, bool segment);
|
||||||
bool OutsideAndNotOn(Vector maxv, Vector minv);
|
bool OutsideAndNotOn(Vector maxv, Vector minv);
|
||||||
|
Vector InPerspective(Vector u, Vector v, Vector n,
|
||||||
|
Vector origin, double cameraTan);
|
||||||
Point2d Project2d(Vector u, Vector v);
|
Point2d Project2d(Vector u, Vector v);
|
||||||
Point2d ProjectXy(void);
|
Point2d ProjectXy(void);
|
||||||
};
|
};
|
||||||
|
|
244
export.cpp
244
export.cpp
|
@ -2,9 +2,6 @@
|
||||||
#include <png.h>
|
#include <png.h>
|
||||||
|
|
||||||
void SolveSpace::ExportSectionTo(char *filename) {
|
void SolveSpace::ExportSectionTo(char *filename) {
|
||||||
SPolygon sp;
|
|
||||||
ZERO(&sp);
|
|
||||||
|
|
||||||
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
|
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
|
||||||
gn = gn.WithMagnitude(1);
|
gn = gn.WithMagnitude(1);
|
||||||
|
|
||||||
|
@ -89,17 +86,17 @@ void SolveSpace::ExportSectionTo(char *filename) {
|
||||||
SEdgeList el;
|
SEdgeList el;
|
||||||
ZERO(&el);
|
ZERO(&el);
|
||||||
root->MakeNakedEdgesInto(&el);
|
root->MakeNakedEdgesInto(&el);
|
||||||
// Assemble those edges into a polygon, and clear the edge list
|
|
||||||
el.AssemblePolygon(&sp, NULL);
|
|
||||||
el.Clear();
|
|
||||||
m.Clear();
|
m.Clear();
|
||||||
|
|
||||||
// And write the polygon.
|
// And write the edges.
|
||||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
||||||
if(out) {
|
if(out) {
|
||||||
ExportPolygon(&sp, u, v, n, origin, out);
|
// parallel projection (no perspective), and no mesh
|
||||||
|
ExportLinesAndMesh(&el, NULL,
|
||||||
|
u, v, n, origin, 0,
|
||||||
|
out);
|
||||||
}
|
}
|
||||||
sp.Clear();
|
el.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SolveSpace::ExportViewTo(char *filename) {
|
void SolveSpace::ExportViewTo(char *filename) {
|
||||||
|
@ -113,9 +110,10 @@ void SolveSpace::ExportViewTo(char *filename) {
|
||||||
e->GenerateEdges(&edges);
|
e->GenerateEdges(&edges);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPolygon sp;
|
SMesh *sm = NULL;
|
||||||
ZERO(&sp);
|
if(SS.GW.showShaded) {
|
||||||
edges.AssemblePolygon(&sp, NULL);
|
sm = &((SS.GetGroup(SS.GW.activeGroup))->runningMesh);
|
||||||
|
}
|
||||||
|
|
||||||
Vector u = SS.GW.projRight,
|
Vector u = SS.GW.projRight,
|
||||||
v = SS.GW.projUp,
|
v = SS.GW.projUp,
|
||||||
|
@ -124,45 +122,124 @@ void SolveSpace::ExportViewTo(char *filename) {
|
||||||
|
|
||||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
||||||
if(out) {
|
if(out) {
|
||||||
ExportPolygon(&sp, u, v, n, origin, out);
|
ExportLinesAndMesh(&edges, sm,
|
||||||
|
u, v, n, origin, SS.cameraTangent*SS.GW.scale,
|
||||||
|
out);
|
||||||
}
|
}
|
||||||
edges.Clear();
|
edges.Clear();
|
||||||
sp.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SolveSpace::ExportPolygon(SPolygon *sp,
|
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SMesh *sm,
|
||||||
Vector u, Vector v, Vector n, Vector origin,
|
Vector u, Vector v, Vector n,
|
||||||
|
Vector origin, double cameraTan,
|
||||||
VectorFileWriter *out)
|
VectorFileWriter *out)
|
||||||
{
|
{
|
||||||
int i, j;
|
double s = 1.0 / SS.exportScale;
|
||||||
|
|
||||||
// Project into the export plane; so when we're done, z doesn't matter,
|
// Project into the export plane; so when we're done, z doesn't matter,
|
||||||
// and x and y are what goes in the DXF.
|
// and x and y are what goes in the DXF.
|
||||||
for(i = 0; i < sp->l.n; i++) {
|
SEdge *e;
|
||||||
SContour *sc = &(sp->l.elem[i]);
|
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
|
||||||
for(j = 0; j < sc->l.n; j++) {
|
// project into the specified csys, and apply export scale
|
||||||
Vector *p = &(sc->l.elem[j].p);
|
(e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||||
*p = p->Minus(origin);
|
(e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||||
*p = p->DotInToCsys(u, v, n);
|
|
||||||
// and apply the export scale factor
|
|
||||||
double s = SS.exportScale;
|
|
||||||
*p = p->ScaledBy(1.0/s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If cutter radius compensation is requested, then perform it now.
|
// If cutter radius compensation is requested, then perform it now
|
||||||
if(fabs(SS.exportOffset) > LENGTH_EPS) {
|
if(fabs(SS.exportOffset) > LENGTH_EPS) {
|
||||||
|
// assemble those edges into a polygon, and clear the edge list
|
||||||
|
SPolygon sp;
|
||||||
|
ZERO(&sp);
|
||||||
|
sel->AssemblePolygon(&sp, NULL);
|
||||||
|
sel->Clear();
|
||||||
|
|
||||||
SPolygon compd;
|
SPolygon compd;
|
||||||
ZERO(&compd);
|
ZERO(&compd);
|
||||||
sp->normal = Vector::From(0, 0, -1);
|
sp.normal = Vector::From(0, 0, -1);
|
||||||
sp->FixContourDirections();
|
sp.FixContourDirections();
|
||||||
sp->OffsetInto(&compd, SS.exportOffset);
|
sp.OffsetInto(&compd, SS.exportOffset);
|
||||||
sp->Clear();
|
sp.Clear();
|
||||||
*sp = compd;
|
|
||||||
|
compd.MakeEdgesInto(sel);
|
||||||
|
compd.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now begin the entities, which are just line segments reproduced from
|
// Now the triangle mesh; project, then build a BSP to perform
|
||||||
// our piecewise linear curves.
|
// occlusion testing and generated the shaded surfaces.
|
||||||
out->OutputPolygon(sp);
|
SMesh smp;
|
||||||
|
ZERO(&smp);
|
||||||
|
if(sm) {
|
||||||
|
Vector l0 = (SS.lightDir[0]).WithMagnitude(1),
|
||||||
|
l1 = (SS.lightDir[1]).WithMagnitude(1);
|
||||||
|
STriangle *tr;
|
||||||
|
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
||||||
|
STriangle tt = *tr;
|
||||||
|
tt.a = (tt.a).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||||
|
tt.b = (tt.b).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||||
|
tt.c = (tt.c).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||||
|
|
||||||
|
// And calculate lighting for the triangle
|
||||||
|
Vector n = tt.Normal().WithMagnitude(1);
|
||||||
|
double lighting = SS.ambientIntensity +
|
||||||
|
max(0, (SS.lightIntensity[0])*(n.Dot(l0))) +
|
||||||
|
max(0, (SS.lightIntensity[1])*(n.Dot(l1)));
|
||||||
|
double r = min(1, REDf (tt.meta.color)*lighting),
|
||||||
|
g = min(1, GREENf(tt.meta.color)*lighting),
|
||||||
|
b = min(1, BLUEf (tt.meta.color)*lighting);
|
||||||
|
tt.meta.color = RGBf(r, g, b);
|
||||||
|
smp.AddTriangle(&tt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the BSP routines to generate the split triangles in paint order.
|
||||||
|
SBsp3 *bsp = SBsp3::FromMesh(&smp);
|
||||||
|
SMesh sms;
|
||||||
|
ZERO(&sms);
|
||||||
|
bsp->GenerateInPaintOrder(&sms);
|
||||||
|
// And cull the back-facing triangles
|
||||||
|
STriangle *tr;
|
||||||
|
sms.l.ClearTags();
|
||||||
|
for(tr = sms.l.First(); tr; tr = sms.l.NextAfter(tr)) {
|
||||||
|
Vector n = tr->Normal();
|
||||||
|
if(n.z < 0) {
|
||||||
|
tr->tag = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sms.l.RemoveTagged();
|
||||||
|
|
||||||
|
// And now we perform hidden line removal if requested
|
||||||
|
SEdgeList hlrd;
|
||||||
|
ZERO(&hlrd);
|
||||||
|
if(sm && !SS.GW.showHdnLines) {
|
||||||
|
SKdNode *root = SKdNode::From(&smp);
|
||||||
|
root->ClearTags();
|
||||||
|
int cnt = 1234;
|
||||||
|
|
||||||
|
SEdge *se;
|
||||||
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||||
|
SEdgeList out;
|
||||||
|
ZERO(&out);
|
||||||
|
// Split the original edge against the mesh
|
||||||
|
out.AddEdge(se->a, se->b);
|
||||||
|
root->OcclusionTestLine(*se, &out, cnt);
|
||||||
|
cnt++;
|
||||||
|
// And add the results to our output
|
||||||
|
SEdge *sen;
|
||||||
|
for(sen = out.l.First(); sen; sen = out.l.NextAfter(sen)) {
|
||||||
|
hlrd.AddEdge(sen->a, sen->b);
|
||||||
|
}
|
||||||
|
out.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
sel = &hlrd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now write the lines and triangles to the output file
|
||||||
|
out->Output(sel, &sms);
|
||||||
|
|
||||||
|
smp.Clear();
|
||||||
|
sms.Clear();
|
||||||
|
hlrd.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VectorFileWriter::StringEndsIn(char *str, char *ending) {
|
bool VectorFileWriter::StringEndsIn(char *str, char *ending) {
|
||||||
|
@ -207,28 +284,36 @@ VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VectorFileWriter::OutputPolygon(SPolygon *sp) {
|
void VectorFileWriter::Output(SEdgeList *sel, SMesh *sm) {
|
||||||
int i, j;
|
STriangle *tr;
|
||||||
|
SEdge *e;
|
||||||
|
|
||||||
// First calculate the bounding box.
|
// First calculate the bounding box.
|
||||||
ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
||||||
ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
|
ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
|
||||||
for(i = 0; i < sp->l.n; i++) {
|
if(sel) {
|
||||||
SContour *sc = &(sp->l.elem[i]);
|
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
|
||||||
for(j = 0; j < sc->l.n; j++) {
|
(e->a).MakeMaxMin(&ptMax, &ptMin);
|
||||||
(sc->l.elem[j].p).MakeMaxMin(&ptMax, &ptMin);
|
(e->b).MakeMaxMin(&ptMax, &ptMin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(sm) {
|
||||||
|
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
||||||
|
(tr->a).MakeMaxMin(&ptMax, &ptMin);
|
||||||
|
(tr->b).MakeMaxMin(&ptMax, &ptMin);
|
||||||
|
(tr->c).MakeMaxMin(&ptMax, &ptMin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StartFile();
|
StartFile();
|
||||||
for(i = 0; i < sp->l.n; i++) {
|
if(sm) {
|
||||||
SContour *sc = &(sp->l.elem[i]);
|
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
||||||
|
Triangle(tr);
|
||||||
for(j = 1; j < sc->l.n; j++) {
|
}
|
||||||
Vector p0 = sc->l.elem[j-1].p,
|
}
|
||||||
p1 = sc->l.elem[j].p;
|
if(sel) {
|
||||||
|
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
|
||||||
LineSegment(p0.x, p0.y, p1.x, p1.y);
|
LineSegment(e->a.x, e->a.y, e->b.x, e->b.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FinishAndCloseFile();
|
FinishAndCloseFile();
|
||||||
|
@ -303,6 +388,8 @@ void DxfFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
||||||
x0, y0, 0.0,
|
x0, y0, 0.0,
|
||||||
x1, y1, 0.0);
|
x1, y1, 0.0);
|
||||||
}
|
}
|
||||||
|
void DxfFileWriter::Triangle(STriangle *tr) {
|
||||||
|
}
|
||||||
|
|
||||||
void DxfFileWriter::FinishAndCloseFile(void) {
|
void DxfFileWriter::FinishAndCloseFile(void) {
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
|
@ -351,6 +438,37 @@ void EpsFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
||||||
MmToPoints(x0 - ptMin.x), MmToPoints(y0 - ptMin.y),
|
MmToPoints(x0 - ptMin.x), MmToPoints(y0 - ptMin.y),
|
||||||
MmToPoints(x1 - ptMin.x), MmToPoints(y1 - ptMin.y));
|
MmToPoints(x1 - ptMin.x), MmToPoints(y1 - ptMin.y));
|
||||||
}
|
}
|
||||||
|
void EpsFileWriter::Triangle(STriangle *tr) {
|
||||||
|
fprintf(f,
|
||||||
|
"%.3f %.3f %.3f setrgbcolor\r\n"
|
||||||
|
"newpath\r\n"
|
||||||
|
" %.3f %.3f moveto\r\n"
|
||||||
|
" %.3f %.3f lineto\r\n"
|
||||||
|
" %.3f %.3f lineto\r\n"
|
||||||
|
" closepath\r\n"
|
||||||
|
"fill\r\n",
|
||||||
|
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
|
||||||
|
MmToPoints(tr->a.x - ptMin.x), MmToPoints(tr->a.y - ptMin.y),
|
||||||
|
MmToPoints(tr->b.x - ptMin.x), MmToPoints(tr->b.y - ptMin.y),
|
||||||
|
MmToPoints(tr->c.x - ptMin.x), MmToPoints(tr->c.y - ptMin.y));
|
||||||
|
|
||||||
|
// same issue with cracks, stroke it to avoid them
|
||||||
|
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
|
||||||
|
fprintf(f,
|
||||||
|
"%.3f %.3f %.3f setrgbcolor\r\n"
|
||||||
|
"%.3f setlinewidth\r\n"
|
||||||
|
"newpath\r\n"
|
||||||
|
" %.3f %.3f moveto\r\n"
|
||||||
|
" %.3f %.3f lineto\r\n"
|
||||||
|
" %.3f %.3f lineto\r\n"
|
||||||
|
" closepath\r\n"
|
||||||
|
"stroke\r\n",
|
||||||
|
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
|
||||||
|
MmToPoints(sw),
|
||||||
|
MmToPoints(tr->a.x - ptMin.x), MmToPoints(tr->a.y - ptMin.y),
|
||||||
|
MmToPoints(tr->b.x - ptMin.x), MmToPoints(tr->b.y - ptMin.y),
|
||||||
|
MmToPoints(tr->c.x - ptMin.x), MmToPoints(tr->c.y - ptMin.y));
|
||||||
|
}
|
||||||
|
|
||||||
void EpsFileWriter::FinishAndCloseFile(void) {
|
void EpsFileWriter::FinishAndCloseFile(void) {
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
|
@ -379,11 +497,28 @@ void SvgFileWriter::StartFile(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
void SvgFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
||||||
|
// SVG uses a coordinate system with the origin at top left, +y down
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
"<polyline points='%.3f %.3f, %.3f %.3f' "
|
"<polyline points='%.3f,%.3f %.3f,%.3f' "
|
||||||
"stroke-width='1' stroke='black' style='fill: none;' />\r\n",
|
"stroke-width='1' stroke='black' style='fill: none;' />\r\n",
|
||||||
(x0 - ptMin.x), (y0 - ptMin.y),
|
(x0 - ptMin.x), (ptMax.y - y0),
|
||||||
(x1 - ptMin.x), (y1 - ptMin.y));
|
(x1 - ptMin.x), (ptMax.y - y1));
|
||||||
|
}
|
||||||
|
void SvgFileWriter::Triangle(STriangle *tr) {
|
||||||
|
// crispEdges turns of anti-aliasing, which tends to cause hairline
|
||||||
|
// cracks between triangles; but there still is some cracking, so
|
||||||
|
// specify a stroke width too, hope for around a pixel
|
||||||
|
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
|
||||||
|
fprintf(f,
|
||||||
|
"<polygon points='%.3f,%.3f %.3f,%.3f %.3f,%.3f' "
|
||||||
|
"stroke='#%02x%02x%02x' stroke-width='%.3f' "
|
||||||
|
"style='fill:#%02x%02x%02x' shape-rendering='crispEdges'/>\r\n",
|
||||||
|
(tr->a.x - ptMin.x), (ptMax.y - tr->a.y),
|
||||||
|
(tr->b.x - ptMin.x), (ptMax.y - tr->b.y),
|
||||||
|
(tr->c.x - ptMin.x), (ptMax.y - tr->c.y),
|
||||||
|
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color),
|
||||||
|
sw,
|
||||||
|
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgFileWriter::FinishAndCloseFile(void) {
|
void SvgFileWriter::FinishAndCloseFile(void) {
|
||||||
|
@ -407,6 +542,9 @@ void HpglFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
||||||
fprintf(f, "PU%d,%d;\r\n", (int)MmToHpglUnits(x0), (int)MmToHpglUnits(y0));
|
fprintf(f, "PU%d,%d;\r\n", (int)MmToHpglUnits(x0), (int)MmToHpglUnits(y0));
|
||||||
fprintf(f, "PD%d,%d;\r\n", (int)MmToHpglUnits(x1), (int)MmToHpglUnits(y1));
|
fprintf(f, "PD%d,%d;\r\n", (int)MmToHpglUnits(x1), (int)MmToHpglUnits(y1));
|
||||||
}
|
}
|
||||||
|
void HpglFileWriter::Triangle(STriangle *tr) {
|
||||||
|
// HPGL does not support filled triangles
|
||||||
|
}
|
||||||
|
|
||||||
void HpglFileWriter::FinishAndCloseFile(void) {
|
void HpglFileWriter::FinishAndCloseFile(void) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
134
mesh.cpp
134
mesh.cpp
|
@ -535,6 +535,140 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt, bool *inter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) {
|
||||||
|
SEdgeList seln;
|
||||||
|
ZERO(&seln);
|
||||||
|
|
||||||
|
Vector tn = tr->Normal().WithMagnitude(1);
|
||||||
|
double td = tn.Dot(tr->a);
|
||||||
|
|
||||||
|
// Consider front-facing triangles only
|
||||||
|
if(tn.z > LENGTH_EPS) {
|
||||||
|
// If the edge crosses our triangle's plane, then split into above
|
||||||
|
// and below parts.
|
||||||
|
SEdge *se;
|
||||||
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||||
|
double da = (se->a).Dot(tn) - td,
|
||||||
|
db = (se->b).Dot(tn) - td;
|
||||||
|
if((da < -LENGTH_EPS && db > LENGTH_EPS) ||
|
||||||
|
(db < -LENGTH_EPS && da > LENGTH_EPS))
|
||||||
|
{
|
||||||
|
Vector m = Vector::AtIntersectionOfPlaneAndLine(
|
||||||
|
tn, td,
|
||||||
|
se->a, se->b, NULL);
|
||||||
|
seln.AddEdge(m, se->b);
|
||||||
|
se->b = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
|
||||||
|
sel->AddEdge(se->a, se->b);
|
||||||
|
}
|
||||||
|
seln.Clear();
|
||||||
|
|
||||||
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||||
|
Vector pt = ((se->a).Plus(se->b)).ScaledBy(0.5);
|
||||||
|
double dt = pt.Dot(tn) - td;
|
||||||
|
if(pt.Dot(tn) - td > -LENGTH_EPS) {
|
||||||
|
// Edge is in front of or on our plane (remember, tn.z > 0)
|
||||||
|
// so it is exempt from further splitting
|
||||||
|
se->auxA = 1;
|
||||||
|
} else {
|
||||||
|
// Edge is behind our plane, needs further splitting
|
||||||
|
se->auxA = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Considering only the (x, y) coordinates, split the edge against our
|
||||||
|
// triangle.
|
||||||
|
Point2d a = (tr->a).ProjectXy(),
|
||||||
|
b = (tr->b).ProjectXy(),
|
||||||
|
c = (tr->c).ProjectXy();
|
||||||
|
|
||||||
|
Point2d n[3] = { (b.Minus(a)).Normal().WithMagnitude(1),
|
||||||
|
(c.Minus(b)).Normal().WithMagnitude(1),
|
||||||
|
(a.Minus(c)).Normal().WithMagnitude(1) };
|
||||||
|
|
||||||
|
double d[3] = { n[0].Dot(b),
|
||||||
|
n[1].Dot(c),
|
||||||
|
n[2].Dot(a) };
|
||||||
|
|
||||||
|
// Split all of the edges where they intersect the triangle edges
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 3; i++) {
|
||||||
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||||
|
if(se->auxA) continue;
|
||||||
|
|
||||||
|
Point2d ap = (se->a).ProjectXy(),
|
||||||
|
bp = (se->b).ProjectXy();
|
||||||
|
double da = n[i].Dot(ap) - d[i],
|
||||||
|
db = n[i].Dot(bp) - d[i];
|
||||||
|
if((da < -LENGTH_EPS && db > LENGTH_EPS) ||
|
||||||
|
(db < -LENGTH_EPS && da > LENGTH_EPS))
|
||||||
|
{
|
||||||
|
double dab = (db - da);
|
||||||
|
Vector spl = ((se->a).ScaledBy( db/dab)).Plus(
|
||||||
|
(se->b).ScaledBy(-da/dab));
|
||||||
|
seln.AddEdge(spl, se->b);
|
||||||
|
se->b = spl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
|
||||||
|
sel->AddEdge(se->a, se->b, 0);
|
||||||
|
}
|
||||||
|
seln.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||||
|
if(se->auxA) {
|
||||||
|
// Lies above or on the triangle plane, so triangle doesn't
|
||||||
|
// occlude it.
|
||||||
|
se->tag = 0;
|
||||||
|
} else {
|
||||||
|
// Test the segment to see if it lies outside the triangle
|
||||||
|
// (i.e., outside wrt at least one edge), and keep it only
|
||||||
|
// then.
|
||||||
|
Point2d pt = ((se->a).Plus(se->b).ScaledBy(0.5)).ProjectXy();
|
||||||
|
se->tag = 1;
|
||||||
|
for(i = 0; i < 3; i++) {
|
||||||
|
if(n[i].Dot(pt) - d[i] > -LENGTH_EPS) se->tag = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sel->l.RemoveTagged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) {
|
||||||
|
if(gt && lt) {
|
||||||
|
double ac = (orig.a).Element(which),
|
||||||
|
bc = (orig.b).Element(which);
|
||||||
|
// We can ignore triangles that are separated in x or y, but triangles
|
||||||
|
// that are separated in z may still contribute
|
||||||
|
if(ac < c + KDTREE_EPS ||
|
||||||
|
bc < c + KDTREE_EPS ||
|
||||||
|
which == 2)
|
||||||
|
{
|
||||||
|
lt->OcclusionTestLine(orig, sel, cnt);
|
||||||
|
}
|
||||||
|
if(ac > c - KDTREE_EPS ||
|
||||||
|
bc > c - KDTREE_EPS ||
|
||||||
|
which == 2)
|
||||||
|
{
|
||||||
|
gt->OcclusionTestLine(orig, sel, cnt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
STriangleLl *ll;
|
||||||
|
for(ll = tris; ll; ll = ll->next) {
|
||||||
|
STriangle *tr = ll->tri;
|
||||||
|
|
||||||
|
if(tr->tag == cnt) continue;
|
||||||
|
|
||||||
|
SplitLinesAgainstTriangle(sel, tr);
|
||||||
|
tr->tag = cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool *inter, bool *leaky) {
|
void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool *inter, bool *leaky) {
|
||||||
if(inter) *inter = false;
|
if(inter) *inter = false;
|
||||||
if(leaky) *leaky = false;
|
if(leaky) *leaky = false;
|
||||||
|
|
|
@ -159,6 +159,8 @@ public:
|
||||||
|
|
||||||
void InsertInPlane(bool pos2, STriangle *tr, SMesh *m);
|
void InsertInPlane(bool pos2, STriangle *tr, SMesh *m);
|
||||||
|
|
||||||
|
void GenerateInPaintOrder(SMesh *m);
|
||||||
|
|
||||||
void DebugDraw(void);
|
void DebugDraw(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -222,6 +224,9 @@ public:
|
||||||
|
|
||||||
void FindEdgeOn(Vector a, Vector b, int *n, int cnt, bool *inter);
|
void FindEdgeOn(Vector a, Vector b, int *n, int cnt, bool *inter);
|
||||||
void MakeNakedEdgesInto(SEdgeList *sel, bool *inter=NULL, bool *leaky=NULL);
|
void MakeNakedEdgesInto(SEdgeList *sel, bool *inter=NULL, bool *leaky=NULL);
|
||||||
|
|
||||||
|
void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt);
|
||||||
|
void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -30,6 +30,7 @@ void SolveSpace::Init(char *cmdLine) {
|
||||||
// Light intensities
|
// Light intensities
|
||||||
lightIntensity[0] = CnfThawFloat(1.0f, "LightIntensity_0");
|
lightIntensity[0] = CnfThawFloat(1.0f, "LightIntensity_0");
|
||||||
lightIntensity[1] = CnfThawFloat(0.5f, "LightIntensity_1");
|
lightIntensity[1] = CnfThawFloat(0.5f, "LightIntensity_1");
|
||||||
|
ambientIntensity = 0.3; // no setting for that yet
|
||||||
// Light positions
|
// Light positions
|
||||||
lightDir[0].x = CnfThawFloat(-1.0f, "LightDir_0_Right" );
|
lightDir[0].x = CnfThawFloat(-1.0f, "LightDir_0_Right" );
|
||||||
lightDir[0].y = CnfThawFloat( 1.0f, "LightDir_0_Up" );
|
lightDir[0].y = CnfThawFloat( 1.0f, "LightDir_0_Up" );
|
||||||
|
|
11
solvespace.h
11
solvespace.h
|
@ -346,15 +346,17 @@ public:
|
||||||
static bool StringEndsIn(char *str, char *ending);
|
static bool StringEndsIn(char *str, char *ending);
|
||||||
static VectorFileWriter *ForFile(char *file);
|
static VectorFileWriter *ForFile(char *file);
|
||||||
|
|
||||||
void OutputPolygon(SPolygon *sp);
|
void Output(SEdgeList *sel, SMesh *sm);
|
||||||
|
|
||||||
virtual void LineSegment(double x0, double y0, double x1, double y1) = 0;
|
virtual void LineSegment(double x0, double y0, double x1, double y1) = 0;
|
||||||
|
virtual void Triangle(STriangle *tr) = 0;
|
||||||
virtual void StartFile(void) = 0;
|
virtual void StartFile(void) = 0;
|
||||||
virtual void FinishAndCloseFile(void) = 0;
|
virtual void FinishAndCloseFile(void) = 0;
|
||||||
};
|
};
|
||||||
class DxfFileWriter : public VectorFileWriter {
|
class DxfFileWriter : public VectorFileWriter {
|
||||||
public:
|
public:
|
||||||
void LineSegment(double x0, double y0, double x1, double y1);
|
void LineSegment(double x0, double y0, double x1, double y1);
|
||||||
|
void Triangle(STriangle *tr);
|
||||||
void StartFile(void);
|
void StartFile(void);
|
||||||
void FinishAndCloseFile(void);
|
void FinishAndCloseFile(void);
|
||||||
};
|
};
|
||||||
|
@ -362,12 +364,14 @@ class EpsFileWriter : public VectorFileWriter {
|
||||||
public:
|
public:
|
||||||
static double MmToPoints(double mm);
|
static double MmToPoints(double mm);
|
||||||
void LineSegment(double x0, double y0, double x1, double y1);
|
void LineSegment(double x0, double y0, double x1, double y1);
|
||||||
|
void Triangle(STriangle *tr);
|
||||||
void StartFile(void);
|
void StartFile(void);
|
||||||
void FinishAndCloseFile(void);
|
void FinishAndCloseFile(void);
|
||||||
};
|
};
|
||||||
class SvgFileWriter : public VectorFileWriter {
|
class SvgFileWriter : public VectorFileWriter {
|
||||||
public:
|
public:
|
||||||
void LineSegment(double x0, double y0, double x1, double y1);
|
void LineSegment(double x0, double y0, double x1, double y1);
|
||||||
|
void Triangle(STriangle *tr);
|
||||||
void StartFile(void);
|
void StartFile(void);
|
||||||
void FinishAndCloseFile(void);
|
void FinishAndCloseFile(void);
|
||||||
};
|
};
|
||||||
|
@ -375,6 +379,7 @@ class HpglFileWriter : public VectorFileWriter {
|
||||||
public:
|
public:
|
||||||
static double MmToHpglUnits(double mm);
|
static double MmToHpglUnits(double mm);
|
||||||
void LineSegment(double x0, double y0, double x1, double y1);
|
void LineSegment(double x0, double y0, double x1, double y1);
|
||||||
|
void Triangle(STriangle *tr);
|
||||||
void StartFile(void);
|
void StartFile(void);
|
||||||
void FinishAndCloseFile(void);
|
void FinishAndCloseFile(void);
|
||||||
};
|
};
|
||||||
|
@ -430,6 +435,7 @@ public:
|
||||||
int modelColor[MODEL_COLORS];
|
int modelColor[MODEL_COLORS];
|
||||||
Vector lightDir[2];
|
Vector lightDir[2];
|
||||||
double lightIntensity[2];
|
double lightIntensity[2];
|
||||||
|
double ambientIntensity;
|
||||||
double chordTol;
|
double chordTol;
|
||||||
int maxSegments;
|
int maxSegments;
|
||||||
double cameraTangent;
|
double cameraTangent;
|
||||||
|
@ -494,8 +500,9 @@ public:
|
||||||
void ExportMeshTo(char *file);
|
void ExportMeshTo(char *file);
|
||||||
void ExportViewTo(char *file);
|
void ExportViewTo(char *file);
|
||||||
void ExportSectionTo(char *file);
|
void ExportSectionTo(char *file);
|
||||||
void ExportPolygon(SPolygon *sp,
|
void ExportLinesAndMesh(SEdgeList *sel, SMesh *sm,
|
||||||
Vector u, Vector v, Vector n, Vector origin,
|
Vector u, Vector v, Vector n, Vector origin,
|
||||||
|
double cameraTan,
|
||||||
VectorFileWriter *out);
|
VectorFileWriter *out);
|
||||||
|
|
||||||
static void MenuAnalyze(int id);
|
static void MenuAnalyze(int id);
|
||||||
|
|
10
ui.h
10
ui.h
|
@ -10,9 +10,13 @@ public:
|
||||||
#ifndef RGB
|
#ifndef RGB
|
||||||
#define RGB(r, g, b) ((r) | ((g) << 8) | ((b) << 16))
|
#define RGB(r, g, b) ((r) | ((g) << 8) | ((b) << 16))
|
||||||
#endif
|
#endif
|
||||||
#define REDf(v) ((((v) >> 0) & 0xff) / 255.0f)
|
#define RGBf(r, g, b) RGB((int)(255*(r)), (int)(255*(g)), (int)(255*(b)))
|
||||||
#define GREENf(v) ((((v) >> 8) & 0xff) / 255.0f)
|
#define RED(v) (((v) >> 0) & 0xff)
|
||||||
#define BLUEf(v) ((((v) >> 16) & 0xff) / 255.0f)
|
#define GREEN(v) (((v) >> 8) & 0xff)
|
||||||
|
#define BLUE(v) (((v) >> 16) & 0xff)
|
||||||
|
#define REDf(v) (RED (v) / 255.0f)
|
||||||
|
#define GREENf(v) (GREEN(v) / 255.0f)
|
||||||
|
#define BLUEf(v) (BLUE (v) / 255.0f)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char c;
|
char c;
|
||||||
int color;
|
int color;
|
||||||
|
|
13
util.cpp
13
util.cpp
|
@ -430,6 +430,19 @@ Vector Vector::ScaleOutOfCsys(Vector u, Vector v, Vector n) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector Vector::InPerspective(Vector u, Vector v, Vector n,
|
||||||
|
Vector origin, double cameraTan)
|
||||||
|
{
|
||||||
|
Vector r = this->Minus(origin);
|
||||||
|
r = r.DotInToCsys(u, v, n);
|
||||||
|
// yes, minus; we are assuming a csys where u cross v equals n, backwards
|
||||||
|
// from the display stuff
|
||||||
|
double w = (1 - r.z*cameraTan);
|
||||||
|
r = r.ScaledBy(1/w);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
double Vector::DistanceToLine(Vector p0, Vector dp) {
|
double Vector::DistanceToLine(Vector p0, Vector dp) {
|
||||||
double m = dp.Magnitude();
|
double m = dp.Magnitude();
|
||||||
return ((this->Minus(p0)).Cross(dp)).Magnitude() / m;
|
return ((this->Minus(p0)).Cross(dp)).Magnitude() / m;
|
||||||
|
|
Loading…
Reference in New Issue