Add beginnings of exact curve export. We take the trim curves in
our specified section plane; we then split them according to the start and endpoints of each STrimBy, using de Castejau's algorithm. These sections get projected (possibly in perspective, which I do correctly) into 2d and exported. Except, for now they just get pwl'd in the export files. That's the fallback, since it works for any file format. But that's the place to add special cases for circles etc., or to export them exactly. DXF supports the latter, but very painfully since I would need to write a later-versioned file, which requires thousands of lines of baggage. I'll probably stick with arcs. [git-p4: depot-paths = "//depot/solvespace/": change = 1936]solver
parent
22afc5ea15
commit
775653a75d
16
dsc.h
16
dsc.h
|
@ -6,6 +6,7 @@ typedef unsigned long DWORD;
|
|||
typedef unsigned char BYTE;
|
||||
|
||||
class Vector;
|
||||
class Vector4;
|
||||
class Point2d;
|
||||
class hEntity;
|
||||
class hParam;
|
||||
|
@ -92,6 +93,21 @@ public:
|
|||
Vector origin, double cameraTan);
|
||||
Point2d Project2d(Vector u, Vector v);
|
||||
Point2d ProjectXy(void);
|
||||
Vector4 Project4d(void);
|
||||
};
|
||||
|
||||
class Vector4 {
|
||||
public:
|
||||
double w, x, y, z;
|
||||
|
||||
static Vector4 From(double w, double x, double y, double z);
|
||||
static Vector4 From(double w, Vector v3);
|
||||
static Vector4 Blend(Vector4 a, Vector4 b, double t);
|
||||
|
||||
Vector4 Plus(Vector4 b);
|
||||
Vector4 Minus(Vector4 b);
|
||||
Vector4 ScaledBy(double s);
|
||||
Vector PerspectiveProject(void);
|
||||
};
|
||||
|
||||
class Point2d {
|
||||
|
|
122
export.cpp
122
export.cpp
|
@ -62,59 +62,53 @@ void SolveSpace::ExportSectionTo(char *filename) {
|
|||
n = n.WithMagnitude(1);
|
||||
d = origin.Dot(n);
|
||||
|
||||
SMesh m;
|
||||
ZERO(&m);
|
||||
m.MakeFromCopy(&(g->runningMesh));
|
||||
|
||||
// Delete all triangles in the mesh that do not lie in our export plane.
|
||||
m.l.ClearTags();
|
||||
int i;
|
||||
for(i = 0; i < m.l.n; i++) {
|
||||
STriangle *tr = &(m.l.elem[i]);
|
||||
|
||||
if((fabs(n.Dot(tr->a) - d) >= LENGTH_EPS) ||
|
||||
(fabs(n.Dot(tr->b) - d) >= LENGTH_EPS) ||
|
||||
(fabs(n.Dot(tr->c) - d) >= LENGTH_EPS))
|
||||
{
|
||||
tr->tag = 1;
|
||||
}
|
||||
}
|
||||
m.l.RemoveTagged();
|
||||
|
||||
// Select the naked edges in our resulting open mesh.
|
||||
SKdNode *root = SKdNode::From(&m);
|
||||
SEdgeList el;
|
||||
ZERO(&el);
|
||||
root->MakeNakedEdgesInto(&el);
|
||||
m.Clear();
|
||||
SBezierList bl;
|
||||
ZERO(&bl);
|
||||
|
||||
g->runningShell.MakeSectionEdgesInto(n, d,
|
||||
&el, SS.exportPwlCurves ? NULL : &bl);
|
||||
|
||||
el.CullExtraneousEdges();
|
||||
|
||||
// And write the edges.
|
||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
||||
if(out) {
|
||||
// parallel projection (no perspective), and no mesh
|
||||
ExportLinesAndMesh(&el, NULL,
|
||||
ExportLinesAndMesh(&el, &bl, NULL,
|
||||
u, v, n, origin, 0,
|
||||
out);
|
||||
}
|
||||
el.Clear();
|
||||
bl.Clear();
|
||||
}
|
||||
|
||||
void SolveSpace::ExportViewTo(char *filename) {
|
||||
int i;
|
||||
SEdgeList edges;
|
||||
ZERO(&edges);
|
||||
for(i = 0; i < SS.entity.n; i++) {
|
||||
Entity *e = &(SS.entity.elem[i]);
|
||||
if(!e->IsVisible()) continue;
|
||||
|
||||
e->GenerateEdges(&edges);
|
||||
}
|
||||
SBezierList beziers;
|
||||
ZERO(&beziers);
|
||||
|
||||
SMesh *sm = NULL;
|
||||
if(SS.GW.showShaded) {
|
||||
sm = &((SS.GetGroup(SS.GW.activeGroup))->runningMesh);
|
||||
}
|
||||
|
||||
for(i = 0; i < SS.entity.n; i++) {
|
||||
Entity *e = &(SS.entity.elem[i]);
|
||||
if(!e->IsVisible()) continue;
|
||||
|
||||
if(SS.exportPwlCurves || (sm && !SS.GW.showHdnLines)) {
|
||||
// We will be doing hidden line removal, which we can't do on
|
||||
// exact curves; so we need things broken down to pwls.
|
||||
e->GenerateEdges(&edges);
|
||||
} else {
|
||||
e->GenerateBezierCurves(&beziers);
|
||||
}
|
||||
}
|
||||
|
||||
if(SS.GW.showEdges) {
|
||||
SEdgeList *selr = &((SS.GetGroup(SS.GW.activeGroup))->runningEdges);
|
||||
SEdge *se;
|
||||
|
@ -130,14 +124,15 @@ void SolveSpace::ExportViewTo(char *filename) {
|
|||
|
||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
||||
if(out) {
|
||||
ExportLinesAndMesh(&edges, sm,
|
||||
ExportLinesAndMesh(&edges, &beziers, sm,
|
||||
u, v, n, origin, SS.cameraTangent*SS.GW.scale,
|
||||
out);
|
||||
}
|
||||
edges.Clear();
|
||||
beziers.Clear();
|
||||
}
|
||||
|
||||
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SMesh *sm,
|
||||
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
|
||||
Vector u, Vector v, Vector n,
|
||||
Vector origin, double cameraTan,
|
||||
VectorFileWriter *out)
|
||||
|
@ -153,6 +148,17 @@ void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SMesh *sm,
|
|||
(e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||
}
|
||||
|
||||
SBezier *b;
|
||||
if(sbl) {
|
||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
||||
*b = b->InPerspective(u, v, n, origin, cameraTan);
|
||||
int i;
|
||||
for(i = 0; i <= b->deg; i++) {
|
||||
b->ctrl[i] = (b->ctrl[i]).ScaledBy(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If cutter radius compensation is requested, then perform it now
|
||||
if(fabs(SS.exportOffset) > LENGTH_EPS) {
|
||||
// assemble those edges into a polygon, and clear the edge list
|
||||
|
@ -252,7 +258,7 @@ void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SMesh *sm,
|
|||
}
|
||||
|
||||
// Now write the lines and triangles to the output file
|
||||
out->Output(sel, &sms);
|
||||
out->Output(sel, sbl, &sms);
|
||||
|
||||
smp.Clear();
|
||||
sms.Clear();
|
||||
|
@ -301,9 +307,10 @@ VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void VectorFileWriter::Output(SEdgeList *sel, SMesh *sm) {
|
||||
void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
|
||||
STriangle *tr;
|
||||
SEdge *e;
|
||||
SBezier *b;
|
||||
|
||||
// First calculate the bounding box.
|
||||
ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
||||
|
@ -321,6 +328,14 @@ void VectorFileWriter::Output(SEdgeList *sel, SMesh *sm) {
|
|||
(tr->c).MakeMaxMin(&ptMax, &ptMin);
|
||||
}
|
||||
}
|
||||
if(sbl) {
|
||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
||||
int i;
|
||||
for(i = 0; i <= b->deg; i++) {
|
||||
(b->ctrl[i]).MakeMaxMin(&ptMax, &ptMin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StartFile();
|
||||
if(sm && SS.exportShadedTriangles) {
|
||||
|
@ -333,9 +348,27 @@ void VectorFileWriter::Output(SEdgeList *sel, SMesh *sm) {
|
|||
LineSegment(e->a.x, e->a.y, e->b.x, e->b.y);
|
||||
}
|
||||
}
|
||||
if(sbl) {
|
||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
||||
Bezier(b);
|
||||
}
|
||||
}
|
||||
FinishAndCloseFile();
|
||||
}
|
||||
|
||||
void VectorFileWriter::BezierAsPwl(SBezier *sb) {
|
||||
List<Vector> lv;
|
||||
ZERO(&lv);
|
||||
sb->MakePwlInto(&lv);
|
||||
int i;
|
||||
for(i = 1; i < lv.n; i++) {
|
||||
LineSegment(lv.elem[i-1].x, lv.elem[i-1].y,
|
||||
lv.elem[i ].x, lv.elem[i ].y);
|
||||
}
|
||||
lv.Clear();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Routines for DXF export
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -405,9 +438,14 @@ void DxfFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
|||
x0, y0, 0.0,
|
||||
x1, y1, 0.0);
|
||||
}
|
||||
|
||||
void DxfFileWriter::Triangle(STriangle *tr) {
|
||||
}
|
||||
|
||||
void DxfFileWriter::Bezier(SBezier *sb) {
|
||||
BezierAsPwl(sb);
|
||||
}
|
||||
|
||||
void DxfFileWriter::FinishAndCloseFile(void) {
|
||||
fprintf(f,
|
||||
" 0\r\n"
|
||||
|
@ -455,6 +493,7 @@ void EpsFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
|||
MmToPoints(x0 - ptMin.x), MmToPoints(y0 - ptMin.y),
|
||||
MmToPoints(x1 - ptMin.x), MmToPoints(y1 - ptMin.y));
|
||||
}
|
||||
|
||||
void EpsFileWriter::Triangle(STriangle *tr) {
|
||||
fprintf(f,
|
||||
"%.3f %.3f %.3f setrgbcolor\r\n"
|
||||
|
@ -487,6 +526,10 @@ void EpsFileWriter::Triangle(STriangle *tr) {
|
|||
MmToPoints(tr->c.x - ptMin.x), MmToPoints(tr->c.y - ptMin.y));
|
||||
}
|
||||
|
||||
void EpsFileWriter::Bezier(SBezier *sb) {
|
||||
BezierAsPwl(sb);
|
||||
}
|
||||
|
||||
void EpsFileWriter::FinishAndCloseFile(void) {
|
||||
fprintf(f,
|
||||
"\r\n"
|
||||
|
@ -521,6 +564,7 @@ void SvgFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
|||
(x0 - ptMin.x), (ptMax.y - y0),
|
||||
(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
|
||||
|
@ -538,6 +582,10 @@ void SvgFileWriter::Triangle(STriangle *tr) {
|
|||
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color));
|
||||
}
|
||||
|
||||
void SvgFileWriter::Bezier(SBezier *sb) {
|
||||
BezierAsPwl(sb);
|
||||
}
|
||||
|
||||
void SvgFileWriter::FinishAndCloseFile(void) {
|
||||
fprintf(f, "\r\n</svg>\r\n");
|
||||
fclose(f);
|
||||
|
@ -559,8 +607,12 @@ 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, "PD%d,%d;\r\n", (int)MmToHpglUnits(x1), (int)MmToHpglUnits(y1));
|
||||
}
|
||||
|
||||
void HpglFileWriter::Triangle(STriangle *tr) {
|
||||
// HPGL does not support filled triangles
|
||||
}
|
||||
|
||||
void HpglFileWriter::Bezier(SBezier *sb) {
|
||||
BezierAsPwl(sb);
|
||||
}
|
||||
|
||||
void HpglFileWriter::FinishAndCloseFile(void) {
|
||||
|
|
|
@ -270,9 +270,25 @@ void GraphicsWindow::LoopOverPoints(
|
|||
int i, j;
|
||||
for(i = 0; i < SS.entity.n; i++) {
|
||||
Entity *e = &(SS.entity.elem[i]);
|
||||
if(!e->IsPoint()) continue;
|
||||
if(!e->IsVisible()) continue;
|
||||
if(e->IsPoint()) {
|
||||
HandlePointForZoomToFit(e->PointGetNum(), pmax, pmin, wmin, div);
|
||||
} else if(e->type == Entity::CIRCLE) {
|
||||
// Lots of entities can extend outside the bbox of their points,
|
||||
// but circles are particularly bad. We want to get things halfway
|
||||
// reasonable without the mesh, because a zoom to fit is used to
|
||||
// set the zoom level to set the chord tol.
|
||||
double r = e->CircleGetRadiusNum();
|
||||
Vector c = SS.GetEntity(e->point[0])->PointGetNum();
|
||||
Quaternion q = SS.GetEntity(e->normal)->NormalGetNum();
|
||||
for(j = 0; j < 4; j++) {
|
||||
Vector p = (j == 0) ? (c.Plus(q.RotationU().ScaledBy( r))) :
|
||||
(j == 1) ? (c.Plus(q.RotationU().ScaledBy(-r))) :
|
||||
(j == 2) ? (c.Plus(q.RotationV().ScaledBy( r))) :
|
||||
(c.Plus(q.RotationV().ScaledBy(-r)));
|
||||
HandlePointForZoomToFit(p, pmax, pmin, wmin, div);
|
||||
}
|
||||
}
|
||||
}
|
||||
Group *g = SS.GetGroup(activeGroup);
|
||||
for(i = 0; i < g->runningMesh.l.n; i++) {
|
||||
|
|
|
@ -56,8 +56,8 @@ void SolveSpace::Init(char *cmdLine) {
|
|||
drawBackFaces = CnfThawDWORD(1, "DrawBackFaces");
|
||||
// Export shaded triangles in a 2d view
|
||||
exportShadedTriangles = CnfThawDWORD(1, "ExportShadedTriangles");
|
||||
// Export exact curves (instead of pwl) when possible
|
||||
exportExactCurves = CnfThawDWORD(0, "ExportExactCurves");
|
||||
// Export pwl curves (instead of exact) always
|
||||
exportPwlCurves = CnfThawDWORD(0, "ExportPwlCurves");
|
||||
// Show toolbar in the graphics window
|
||||
showToolbar = CnfThawDWORD(1, "ShowToolbar");
|
||||
// Recent files menus
|
||||
|
@ -124,8 +124,8 @@ void SolveSpace::Exit(void) {
|
|||
CnfFreezeDWORD(drawBackFaces, "DrawBackFaces");
|
||||
// Export shaded triangles in a 2d view
|
||||
CnfFreezeDWORD(exportShadedTriangles, "ExportShadedTriangles");
|
||||
// Export exact curves (instead of pwl) when possible
|
||||
CnfFreezeDWORD(exportExactCurves, "ExportExactCurves");
|
||||
// Export pwl curves (instead of exact) always
|
||||
CnfFreezeDWORD(exportPwlCurves, "ExportPwlCurves");
|
||||
// Show toolbar in the graphics window
|
||||
CnfFreezeDWORD(showToolbar, "ShowToolbar");
|
||||
|
||||
|
|
14
solvespace.h
14
solvespace.h
|
@ -329,8 +329,6 @@ public:
|
|||
Vector TransformIntPoint(int x, int y);
|
||||
void LineSegment(int x0, int y0, int x1, int y1);
|
||||
void Bezier(int x0, int y0, int x1, int y1, int x2, int y2);
|
||||
void BezierPwl(double ta, double tb, Vector p0, Vector p1, Vector p2);
|
||||
Vector BezierEval(double t, Vector p0, Vector p1, Vector p2);
|
||||
};
|
||||
|
||||
class TtfFontList {
|
||||
|
@ -352,8 +350,10 @@ public:
|
|||
static bool StringEndsIn(char *str, char *ending);
|
||||
static VectorFileWriter *ForFile(char *file);
|
||||
|
||||
void Output(SEdgeList *sel, SMesh *sm);
|
||||
void Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm);
|
||||
void BezierAsPwl(SBezier *sb);
|
||||
|
||||
virtual void Bezier(SBezier *sb) = 0;
|
||||
virtual void LineSegment(double x0, double y0, double x1, double y1) = 0;
|
||||
virtual void Triangle(STriangle *tr) = 0;
|
||||
virtual void StartFile(void) = 0;
|
||||
|
@ -363,6 +363,7 @@ class DxfFileWriter : public VectorFileWriter {
|
|||
public:
|
||||
void LineSegment(double x0, double y0, double x1, double y1);
|
||||
void Triangle(STriangle *tr);
|
||||
void Bezier(SBezier *sb);
|
||||
void StartFile(void);
|
||||
void FinishAndCloseFile(void);
|
||||
};
|
||||
|
@ -371,6 +372,7 @@ public:
|
|||
static double MmToPoints(double mm);
|
||||
void LineSegment(double x0, double y0, double x1, double y1);
|
||||
void Triangle(STriangle *tr);
|
||||
void Bezier(SBezier *sb);
|
||||
void StartFile(void);
|
||||
void FinishAndCloseFile(void);
|
||||
};
|
||||
|
@ -378,6 +380,7 @@ class SvgFileWriter : public VectorFileWriter {
|
|||
public:
|
||||
void LineSegment(double x0, double y0, double x1, double y1);
|
||||
void Triangle(STriangle *tr);
|
||||
void Bezier(SBezier *sb);
|
||||
void StartFile(void);
|
||||
void FinishAndCloseFile(void);
|
||||
};
|
||||
|
@ -386,6 +389,7 @@ public:
|
|||
static double MmToHpglUnits(double mm);
|
||||
void LineSegment(double x0, double y0, double x1, double y1);
|
||||
void Triangle(STriangle *tr);
|
||||
void Bezier(SBezier *sb);
|
||||
void StartFile(void);
|
||||
void FinishAndCloseFile(void);
|
||||
};
|
||||
|
@ -451,7 +455,7 @@ public:
|
|||
int drawBackFaces;
|
||||
int showToolbar;
|
||||
int exportShadedTriangles;
|
||||
int exportExactCurves;
|
||||
int exportPwlCurves;
|
||||
|
||||
int CircleSides(double r);
|
||||
typedef enum {
|
||||
|
@ -508,7 +512,7 @@ public:
|
|||
void ExportMeshTo(char *file);
|
||||
void ExportViewTo(char *file);
|
||||
void ExportSectionTo(char *file);
|
||||
void ExportLinesAndMesh(SEdgeList *sel, SMesh *sm,
|
||||
void ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
|
||||
Vector u, Vector v, Vector n, Vector origin,
|
||||
double cameraTan,
|
||||
VectorFileWriter *out);
|
||||
|
|
|
@ -4,39 +4,63 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
#include "../solvespace.h"
|
||||
|
||||
SBezier SBezier::From(Vector p0, Vector p1) {
|
||||
SBezier SBezier::From(Vector4 p0, Vector4 p1) {
|
||||
SBezier ret;
|
||||
ZERO(&ret);
|
||||
ret.deg = 1;
|
||||
ret.weight[0] = ret.weight[1] = 1;
|
||||
ret.ctrl[0] = p0;
|
||||
ret.ctrl[1] = p1;
|
||||
ret.weight[0] = p0.w;
|
||||
ret.ctrl [0] = p0.PerspectiveProject();
|
||||
ret.weight[1] = p1.w;
|
||||
ret.ctrl [1] = p1.PerspectiveProject();
|
||||
return ret;
|
||||
}
|
||||
|
||||
SBezier SBezier::From(Vector p0, Vector p1, Vector p2) {
|
||||
SBezier SBezier::From(Vector4 p0, Vector4 p1, Vector4 p2) {
|
||||
SBezier ret;
|
||||
ZERO(&ret);
|
||||
ret.deg = 2;
|
||||
ret.weight[0] = ret.weight[1] = ret.weight[2] = 1;
|
||||
ret.ctrl[0] = p0;
|
||||
ret.ctrl[1] = p1;
|
||||
ret.ctrl[2] = p2;
|
||||
ret.weight[0] = p0.w;
|
||||
ret.ctrl [0] = p0.PerspectiveProject();
|
||||
ret.weight[1] = p1.w;
|
||||
ret.ctrl [1] = p1.PerspectiveProject();
|
||||
ret.weight[2] = p2.w;
|
||||
ret.ctrl [2] = p2.PerspectiveProject();
|
||||
return ret;
|
||||
}
|
||||
|
||||
SBezier SBezier::From(Vector p0, Vector p1, Vector p2, Vector p3) {
|
||||
SBezier SBezier::From(Vector4 p0, Vector4 p1, Vector4 p2, Vector4 p3) {
|
||||
SBezier ret;
|
||||
ZERO(&ret);
|
||||
ret.deg = 3;
|
||||
ret.weight[0] = ret.weight[1] = ret.weight[2] = ret.weight[3] = 1;
|
||||
ret.ctrl[0] = p0;
|
||||
ret.ctrl[1] = p1;
|
||||
ret.ctrl[2] = p2;
|
||||
ret.ctrl[3] = p3;
|
||||
ret.weight[0] = p0.w;
|
||||
ret.ctrl [0] = p0.PerspectiveProject();
|
||||
ret.weight[1] = p1.w;
|
||||
ret.ctrl [1] = p1.PerspectiveProject();
|
||||
ret.weight[2] = p2.w;
|
||||
ret.ctrl [2] = p2.PerspectiveProject();
|
||||
ret.weight[3] = p3.w;
|
||||
ret.ctrl [3] = p3.PerspectiveProject();
|
||||
return ret;
|
||||
}
|
||||
|
||||
SBezier SBezier::From(Vector p0, Vector p1) {
|
||||
return SBezier::From(p0.Project4d(),
|
||||
p1.Project4d());
|
||||
}
|
||||
|
||||
SBezier SBezier::From(Vector p0, Vector p1, Vector p2) {
|
||||
return SBezier::From(p0.Project4d(),
|
||||
p1.Project4d(),
|
||||
p2.Project4d());
|
||||
}
|
||||
|
||||
SBezier SBezier::From(Vector p0, Vector p1, Vector p2, Vector p3) {
|
||||
return SBezier::From(p0.Project4d(),
|
||||
p1.Project4d(),
|
||||
p2.Project4d(),
|
||||
p3.Project4d());
|
||||
}
|
||||
|
||||
Vector SBezier::Start(void) {
|
||||
return ctrl[0];
|
||||
}
|
||||
|
@ -73,6 +97,34 @@ SBezier SBezier::TransformedBy(Vector t, Quaternion q) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Apply a perspective transformation to a rational Bezier curve, calculating
|
||||
// the new weights as required.
|
||||
//-----------------------------------------------------------------------------
|
||||
SBezier SBezier::InPerspective(Vector u, Vector v, Vector n,
|
||||
Vector origin, double cameraTan)
|
||||
{
|
||||
Quaternion q = Quaternion::From(u, v);
|
||||
q = q.Inverse();
|
||||
// we want Q*(p - o) = Q*p - Q*o
|
||||
SBezier ret = this->TransformedBy(q.Rotate(origin).ScaledBy(-1), q);
|
||||
int i;
|
||||
for(i = 0; i <= deg; i++) {
|
||||
Vector4 ct = Vector4::From(ret.weight[i], ret.ctrl[i]);
|
||||
// so the desired curve, before perspective, is
|
||||
// (x/w, y/w, z/w)
|
||||
// and after perspective is
|
||||
// ((x/w)/(1 - (z/w)*cameraTan, ...
|
||||
// = (x/(w - z*cameraTan), ...
|
||||
// so we want to let w' = w - z*cameraTan
|
||||
ct.w = ct.w - ct.z*cameraTan;
|
||||
|
||||
ret.ctrl[i] = ct.PerspectiveProject();
|
||||
ret.weight[i] = ct.w;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool SBezier::Equals(SBezier *b) {
|
||||
// We just test of identical degree and control points, even though two
|
||||
// curves could still be coincident (even sharing endpoints).
|
||||
|
|
|
@ -107,6 +107,98 @@ Vector SBezier::PointAt(double t) {
|
|||
return pt;
|
||||
}
|
||||
|
||||
Vector SBezier::TangentAt(double t) {
|
||||
Vector pt = Vector::From(0, 0, 0), pt_p = Vector::From(0, 0, 0);
|
||||
double d = 0, d_p = 0;
|
||||
|
||||
int i;
|
||||
for(i = 0; i <= deg; i++) {
|
||||
double B = Bernstein(i, deg, t),
|
||||
Bp = BernsteinDerivative(i, deg, t);
|
||||
|
||||
pt = pt.Plus(ctrl[i].ScaledBy(B*weight[i]));
|
||||
d += weight[i]*B;
|
||||
|
||||
pt_p = pt_p.Plus(ctrl[i].ScaledBy(Bp*weight[i]));
|
||||
d_p += weight[i]*Bp;
|
||||
}
|
||||
|
||||
// quotient rule; f(t) = n(t)/d(t), so f' = (n'*d - n*d')/(d^2)
|
||||
Vector ret;
|
||||
ret = (pt_p.ScaledBy(d)).Minus(pt.ScaledBy(d_p));
|
||||
ret = ret.ScaledBy(1.0/(d*d));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SBezier::ClosestPointTo(Vector p, double *t) {
|
||||
int i;
|
||||
double minDist = VERY_POSITIVE;
|
||||
*t = 0;
|
||||
double res = (deg <= 2) ? 7.0 : 20.0;
|
||||
for(i = 0; i < (int)res; i++) {
|
||||
double tryt = (i/res);
|
||||
|
||||
Vector tryp = PointAt(tryt);
|
||||
double d = (tryp.Minus(p)).Magnitude();
|
||||
if(d < minDist) {
|
||||
*t = tryt;
|
||||
minDist = d;
|
||||
}
|
||||
}
|
||||
|
||||
Vector p0;
|
||||
for(i = 0; i < 15; i++) {
|
||||
p0 = PointAt(*t);
|
||||
if(p0.Equals(p, RATPOLY_EPS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector dp = TangentAt(*t);
|
||||
Vector pc = p.ClosestPointOnLine(p0, dp);
|
||||
*t += (pc.Minus(p0)).DivPivoting(dp);
|
||||
}
|
||||
dbp("didn't converge (closest point on bezier curve)");
|
||||
}
|
||||
|
||||
void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) {
|
||||
Vector4 ct[4];
|
||||
int i;
|
||||
for(i = 0; i <= deg; i++) {
|
||||
ct[i] = Vector4::From(weight[i], ctrl[i]);
|
||||
}
|
||||
|
||||
switch(deg) {
|
||||
case 1: {
|
||||
Vector4 cts = Vector4::Blend(ct[0], ct[1], t);
|
||||
*bef = SBezier::From(ct[0], cts);
|
||||
*aft = SBezier::From(cts, ct[1]);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
Vector4 ct01 = Vector4::Blend(ct[0], ct[1], t),
|
||||
ct12 = Vector4::Blend(ct[1], ct[2], t),
|
||||
cts = Vector4::Blend(ct01, ct12, t);
|
||||
|
||||
*bef = SBezier::From(ct[0], ct01, cts);
|
||||
*aft = SBezier::From(cts, ct12, ct[2]);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
Vector4 ct01 = Vector4::Blend(ct[0], ct[1], t),
|
||||
ct12 = Vector4::Blend(ct[1], ct[2], t),
|
||||
ct23 = Vector4::Blend(ct[2], ct[3], t),
|
||||
ct01_12 = Vector4::Blend(ct01, ct12, t),
|
||||
ct12_23 = Vector4::Blend(ct12, ct23, t),
|
||||
cts = Vector4::Blend(ct01_12, ct12_23, t);
|
||||
|
||||
*bef = SBezier::From(ct[0], ct01, ct01_12, cts);
|
||||
*aft = SBezier::From(cts, ct12_23, ct23, ct[3]);
|
||||
break;
|
||||
}
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
void SBezier::MakePwlInto(List<Vector> *l) {
|
||||
l->Add(&(ctrl[0]));
|
||||
MakePwlWorker(l, 0.0, 1.0);
|
||||
|
@ -180,7 +272,7 @@ void SSurface::TangentsAt(double u, double v, Vector *tu, Vector *tv) {
|
|||
den_v += weight[i][j]*Bi*Bjp;
|
||||
}
|
||||
}
|
||||
// Quotient rule; f(t) = n(t)/d(t), so f' = (n'*d - n*d')/(d^2)
|
||||
// quotient rule; f(t) = n(t)/d(t), so f' = (n'*d - n*d')/(d^2)
|
||||
*tu = ((num_u.ScaledBy(den)).Minus(num.ScaledBy(den_u)));
|
||||
*tu = tu->ScaledBy(1.0/(den*den));
|
||||
|
||||
|
|
|
@ -174,21 +174,13 @@ bool SSurface::LineEntirelyOutsideBbox(Vector a, Vector b, bool segment) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv,
|
||||
SShell *useCurvesFrom)
|
||||
//-----------------------------------------------------------------------------
|
||||
// Generate the piecewise linear approximation of the trim stb, which applies
|
||||
// to the curve sc.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SSurface::MakeTrimEdgesInto(SEdgeList *sel, bool asUv,
|
||||
SCurve *sc, STrimBy *stb)
|
||||
{
|
||||
STrimBy *stb;
|
||||
for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) {
|
||||
SCurve *sc = shell->curve.FindById(stb->curve);
|
||||
|
||||
// We have the option to use the curves from another shell; this
|
||||
// is relevant when generating the coincident edges while doing the
|
||||
// Booleans, since the curves from the output shell will be split
|
||||
// against any intersecting surfaces (and the originals aren't).
|
||||
if(useCurvesFrom) {
|
||||
sc = useCurvesFrom->curve.FindById(sc->newH);
|
||||
}
|
||||
|
||||
Vector prev, prevuv, ptuv;
|
||||
bool inCurve = false, empty = true;
|
||||
double u = 0, v = 0;
|
||||
|
@ -227,6 +219,68 @@ void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv,
|
|||
if(inCurve) dbp("trim was unterminated");
|
||||
if(empty) dbp("trim was empty");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Generate all of our trim curves, in piecewise linear form. We can do
|
||||
// so in either uv or xyz coordinates. And if requested, then we can use
|
||||
// the split curves from useCurvesFrom instead of the curves in our own
|
||||
// shell.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv,
|
||||
SShell *useCurvesFrom)
|
||||
{
|
||||
STrimBy *stb;
|
||||
for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) {
|
||||
SCurve *sc = shell->curve.FindById(stb->curve);
|
||||
|
||||
// We have the option to use the curves from another shell; this
|
||||
// is relevant when generating the coincident edges while doing the
|
||||
// Booleans, since the curves from the output shell will be split
|
||||
// against any intersecting surfaces (and the originals aren't).
|
||||
if(useCurvesFrom) {
|
||||
sc = useCurvesFrom->curve.FindById(sc->newH);
|
||||
}
|
||||
|
||||
MakeTrimEdgesInto(sel, asUv, sc, stb);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Report our trim curves. If a trim curve is exact and sbl is not null, then
|
||||
// add its exact form to sbl. Otherwise, add its piecewise linearization to
|
||||
// sel.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SSurface::MakeSectionEdgesInto(SShell *shell,
|
||||
SEdgeList *sel, SBezierList *sbl)
|
||||
{
|
||||
STrimBy *stb;
|
||||
for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) {
|
||||
SCurve *sc = shell->curve.FindById(stb->curve);
|
||||
SBezier *sb = &(sc->exact);
|
||||
|
||||
if(sbl && sc->isExact && sb->deg != 1) {
|
||||
double ts, tf;
|
||||
if(stb->backwards) {
|
||||
sb->ClosestPointTo(stb->start, &tf);
|
||||
sb->ClosestPointTo(stb->finish, &ts);
|
||||
} else {
|
||||
sb->ClosestPointTo(stb->start, &ts);
|
||||
sb->ClosestPointTo(stb->finish, &tf);
|
||||
}
|
||||
SBezier junk_bef, keep_aft;
|
||||
sb->SplitAt(ts, &junk_bef, &keep_aft);
|
||||
// In the kept piece, the range that used to go from ts to 1
|
||||
// now goes from 0 to 1; so rescale tf appropriately.
|
||||
tf = (tf - ts)/(1 - ts);
|
||||
|
||||
SBezier keep_bef, junk_aft;
|
||||
keep_aft.SplitAt(tf, &keep_bef, &junk_aft);
|
||||
|
||||
sbl->l.Add(&keep_bef);
|
||||
} else {
|
||||
MakeTrimEdgesInto(sel, false, sc, stb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
|
||||
|
@ -439,6 +493,17 @@ void SShell::MakeEdgesInto(SEdgeList *sel) {
|
|||
}
|
||||
}
|
||||
|
||||
void SShell::MakeSectionEdgesInto(Vector n, double d,
|
||||
SEdgeList *sel, SBezierList *sbl)
|
||||
{
|
||||
SSurface *s;
|
||||
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
||||
if(s->CoincidentWithPlane(n, d)) {
|
||||
s->MakeSectionEdgesInto(this, sel, sbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SShell::TriangulateInto(SMesh *sm) {
|
||||
SSurface *s;
|
||||
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
||||
|
|
|
@ -50,7 +50,8 @@ public:
|
|||
};
|
||||
|
||||
// Stuff for rational polynomial curves, of degree one to three. These are
|
||||
// our inputs.
|
||||
// our inputs, and are also calculated for certain exact surface-surface
|
||||
// intersections.
|
||||
class SBezier {
|
||||
public:
|
||||
int tag;
|
||||
|
@ -59,6 +60,10 @@ public:
|
|||
double weight[4];
|
||||
|
||||
Vector PointAt(double t);
|
||||
Vector TangentAt(double t);
|
||||
void ClosestPointTo(Vector p, double *t);
|
||||
void SplitAt(double t, SBezier *bef, SBezier *aft);
|
||||
|
||||
Vector Start(void);
|
||||
Vector Finish(void);
|
||||
bool Equals(SBezier *b);
|
||||
|
@ -69,10 +74,15 @@ public:
|
|||
void Reverse(void);
|
||||
|
||||
SBezier TransformedBy(Vector t, Quaternion q);
|
||||
SBezier InPerspective(Vector u, Vector v, Vector n,
|
||||
Vector origin, double cameraTan);
|
||||
|
||||
static SBezier From(Vector p0, Vector p1, Vector p2, Vector p3);
|
||||
static SBezier From(Vector p0, Vector p1, Vector p2);
|
||||
static SBezier From(Vector p0, Vector p1);
|
||||
static SBezier From(Vector4 p0, Vector4 p1, Vector4 p2, Vector4 p3);
|
||||
static SBezier From(Vector4 p0, Vector4 p1, Vector4 p2);
|
||||
static SBezier From(Vector4 p0, Vector4 p1);
|
||||
};
|
||||
|
||||
class SBezierList {
|
||||
|
@ -233,8 +243,10 @@ public:
|
|||
Vector *start, Vector *finish);
|
||||
|
||||
void TriangulateInto(SShell *shell, SMesh *sm);
|
||||
void MakeTrimEdgesInto(SEdgeList *sel, bool asUv, SCurve *sc, STrimBy *stb);
|
||||
void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv,
|
||||
SShell *useCurvesFrom=NULL);
|
||||
void MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *sbl);
|
||||
void MakeClassifyingBsp(SShell *shell);
|
||||
double ChordToleranceForEdge(Vector a, Vector b);
|
||||
|
||||
|
@ -282,6 +294,8 @@ public:
|
|||
|
||||
void TriangulateInto(SMesh *sm);
|
||||
void MakeEdgesInto(SEdgeList *sel);
|
||||
void MakeSectionEdgesInto(Vector n, double d,
|
||||
SEdgeList *sel, SBezierList *sbl);
|
||||
void Clear(void);
|
||||
};
|
||||
|
||||
|
|
|
@ -610,8 +610,8 @@ void TextWindow::ScreenChangeShadedTriangles(int link, DWORD v) {
|
|||
SS.exportShadedTriangles = !SS.exportShadedTriangles;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
void TextWindow::ScreenChangeExactCurves(int link, DWORD v) {
|
||||
SS.exportExactCurves = !SS.exportExactCurves;
|
||||
void TextWindow::ScreenChangePwlCurves(int link, DWORD v) {
|
||||
SS.exportPwlCurves = !SS.exportPwlCurves;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
void TextWindow::ShowConfiguration(void) {
|
||||
|
@ -681,14 +681,14 @@ void TextWindow::ShowConfiguration(void) {
|
|||
&ScreenChangeShadedTriangles,
|
||||
(!SS.exportShadedTriangles ? "" : "no"),
|
||||
(!SS.exportShadedTriangles ? "no" : ""));
|
||||
Printf(false, "%Ft export exact curves in DXF: "
|
||||
Printf(false, "%Ft curves as piecewise linear: "
|
||||
"%Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E",
|
||||
&ScreenChangeExactCurves,
|
||||
(SS.exportExactCurves ? "" : "yes"),
|
||||
(SS.exportExactCurves ? "yes" : ""),
|
||||
&ScreenChangeExactCurves,
|
||||
(!SS.exportExactCurves ? "" : "no"),
|
||||
(!SS.exportExactCurves ? "no" : ""));
|
||||
&ScreenChangePwlCurves,
|
||||
(SS.exportPwlCurves ? "" : "yes"),
|
||||
(SS.exportPwlCurves ? "yes" : ""),
|
||||
&ScreenChangePwlCurves,
|
||||
(!SS.exportPwlCurves ? "" : "no"),
|
||||
(!SS.exportPwlCurves ? "no" : ""));
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, "%Ft draw back faces: "
|
||||
|
|
2
ui.h
2
ui.h
|
@ -145,7 +145,7 @@ public:
|
|||
static void ScreenGoToWebsite(int link, DWORD v);
|
||||
|
||||
static void ScreenChangeBackFaces(int link, DWORD v);
|
||||
static void ScreenChangeExactCurves(int link, DWORD v);
|
||||
static void ScreenChangePwlCurves(int link, DWORD v);
|
||||
static void ScreenChangeShadedTriangles(int link, DWORD v);
|
||||
|
||||
static void ScreenStepDimSteps(int link, DWORD v);
|
||||
|
|
37
util.cpp
37
util.cpp
|
@ -537,6 +537,10 @@ Point2d Vector::ProjectXy(void) {
|
|||
return p;
|
||||
}
|
||||
|
||||
Vector4 Vector::Project4d(void) {
|
||||
return Vector4::From(1, x, y, z);
|
||||
}
|
||||
|
||||
double Vector::DivPivoting(Vector delta) {
|
||||
double mx = fabs(delta.x), my = fabs(delta.y), mz = fabs(delta.z);
|
||||
|
||||
|
@ -731,6 +735,39 @@ Vector Vector::AtIntersectionOfPlanes(Vector na, double da,
|
|||
return Vector::From(detx/det, dety/det, detz/det);
|
||||
}
|
||||
|
||||
Vector4 Vector4::From(double w, double x, double y, double z) {
|
||||
Vector4 ret;
|
||||
ret.w = w;
|
||||
ret.x = x;
|
||||
ret.y = y;
|
||||
ret.z = z;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector4 Vector4::From(double w, Vector v) {
|
||||
return Vector4::From(w, w*v.x, w*v.y, w*v.z);
|
||||
}
|
||||
|
||||
Vector4 Vector4::Blend(Vector4 a, Vector4 b, double t) {
|
||||
return (a.ScaledBy(1 - t)).Plus(b.ScaledBy(t));
|
||||
}
|
||||
|
||||
Vector4 Vector4::Plus(Vector4 b) {
|
||||
return Vector4::From(w + b.w, x + b.x, y + b.y, z + b.z);
|
||||
}
|
||||
|
||||
Vector4 Vector4::Minus(Vector4 b) {
|
||||
return Vector4::From(w - b.w, x - b.x, y - b.y, z - b.z);
|
||||
}
|
||||
|
||||
Vector4 Vector4::ScaledBy(double s) {
|
||||
return Vector4::From(w*s, x*s, y*s, z*s);
|
||||
}
|
||||
|
||||
Vector Vector4::PerspectiveProject(void) {
|
||||
return Vector::From(x / w, y / w, z / w);
|
||||
}
|
||||
|
||||
Point2d Point2d::From(double x, double y) {
|
||||
Point2d r;
|
||||
r.x = x; r.y = y;
|
||||
|
|
Loading…
Reference in New Issue