Add exact export of arcs for EPS, DXF, SVG, and of nonrational
polynomial curves for SVG. [git-p4: depot-paths = "//depot/solvespace/": change = 1937]solver
parent
775653a75d
commit
b5c8aade21
135
export.cpp
135
export.cpp
|
@ -71,6 +71,7 @@ void SolveSpace::ExportSectionTo(char *filename) {
|
||||||
&el, SS.exportPwlCurves ? NULL : &bl);
|
&el, SS.exportPwlCurves ? NULL : &bl);
|
||||||
|
|
||||||
el.CullExtraneousEdges();
|
el.CullExtraneousEdges();
|
||||||
|
bl.CullIdenticalBeziers();
|
||||||
|
|
||||||
// And write the edges.
|
// And write the edges.
|
||||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
||||||
|
@ -95,6 +96,9 @@ void SolveSpace::ExportViewTo(char *filename) {
|
||||||
if(SS.GW.showShaded) {
|
if(SS.GW.showShaded) {
|
||||||
sm = &((SS.GetGroup(SS.GW.activeGroup))->runningMesh);
|
sm = &((SS.GetGroup(SS.GW.activeGroup))->runningMesh);
|
||||||
}
|
}
|
||||||
|
if(sm->l.n == 0) {
|
||||||
|
sm = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for(i = 0; i < SS.entity.n; i++) {
|
for(i = 0; i < SS.entity.n; i++) {
|
||||||
Entity *e = &(SS.entity.elem[i]);
|
Entity *e = &(SS.entity.elem[i]);
|
||||||
|
@ -368,7 +372,6 @@ void VectorFileWriter::BezierAsPwl(SBezier *sb) {
|
||||||
lv.Clear();
|
lv.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Routines for DXF export
|
// Routines for DXF export
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -386,6 +389,18 @@ void DxfFileWriter::StartFile(void) {
|
||||||
" 1\r\n"
|
" 1\r\n"
|
||||||
"AC1006\r\n"
|
"AC1006\r\n"
|
||||||
" 9\r\n"
|
" 9\r\n"
|
||||||
|
"$ANGDIR\r\n"
|
||||||
|
" 70\r\n"
|
||||||
|
"0\r\n"
|
||||||
|
" 9\r\n"
|
||||||
|
"$AUNITS\r\n"
|
||||||
|
" 70\r\n"
|
||||||
|
"0\r\n"
|
||||||
|
" 9\r\n"
|
||||||
|
"$AUPREC\r\n"
|
||||||
|
" 70\r\n"
|
||||||
|
"0\r\n"
|
||||||
|
" 9\r\n"
|
||||||
"$INSBASE\r\n"
|
"$INSBASE\r\n"
|
||||||
" 10\r\n"
|
" 10\r\n"
|
||||||
"0.0\r\n"
|
"0.0\r\n"
|
||||||
|
@ -443,7 +458,40 @@ void DxfFileWriter::Triangle(STriangle *tr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DxfFileWriter::Bezier(SBezier *sb) {
|
void DxfFileWriter::Bezier(SBezier *sb) {
|
||||||
BezierAsPwl(sb);
|
Vector c, n = Vector::From(0, 0, 1);
|
||||||
|
double r;
|
||||||
|
if(sb->IsCircle(n, &c, &r)) {
|
||||||
|
double theta0 = atan2(sb->ctrl[0].y - c.y, sb->ctrl[0].x - c.x),
|
||||||
|
theta1 = atan2(sb->ctrl[2].y - c.y, sb->ctrl[2].x - c.x),
|
||||||
|
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
||||||
|
if(dtheta < 0) {
|
||||||
|
SWAP(double, theta0, theta1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f,
|
||||||
|
" 0\r\n"
|
||||||
|
"ARC\r\n"
|
||||||
|
" 8\r\n" // Layer code
|
||||||
|
"%d\r\n"
|
||||||
|
" 10\r\n" // x
|
||||||
|
"%.6f\r\n"
|
||||||
|
" 20\r\n" // y
|
||||||
|
"%.6f\r\n"
|
||||||
|
" 30\r\n" // z
|
||||||
|
"%.6f\r\n"
|
||||||
|
" 40\r\n" // radius
|
||||||
|
"%.6f\r\n"
|
||||||
|
" 50\r\n" // start angle
|
||||||
|
"%.6f\r\n"
|
||||||
|
" 51\r\n" // end angle
|
||||||
|
"%.6f\r\n",
|
||||||
|
0,
|
||||||
|
c.x, c.y, 0.0,
|
||||||
|
r,
|
||||||
|
theta0*180/PI, theta1*180/PI);
|
||||||
|
} else {
|
||||||
|
BezierAsPwl(sb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DxfFileWriter::FinishAndCloseFile(void) {
|
void DxfFileWriter::FinishAndCloseFile(void) {
|
||||||
|
@ -527,7 +575,31 @@ void EpsFileWriter::Triangle(STriangle *tr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpsFileWriter::Bezier(SBezier *sb) {
|
void EpsFileWriter::Bezier(SBezier *sb) {
|
||||||
BezierAsPwl(sb);
|
Vector c, n = Vector::From(0, 0, 1);
|
||||||
|
double r;
|
||||||
|
if(sb->IsCircle(n, &c, &r)) {
|
||||||
|
Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2];
|
||||||
|
double theta0 = atan2(p0.y - c.y, p0.x - c.x),
|
||||||
|
theta1 = atan2(p1.y - c.y, p1.x - c.x),
|
||||||
|
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
||||||
|
if(dtheta < 0) {
|
||||||
|
SWAP(double, theta0, theta1);
|
||||||
|
SWAP(Vector, p0, p1);
|
||||||
|
}
|
||||||
|
fprintf(f,
|
||||||
|
"newpath\r\n"
|
||||||
|
" %.3f %.3f moveto\r\n"
|
||||||
|
" %.3f %.3f %.3f %.3f %.3f arc\r\n"
|
||||||
|
" 1 setlinewidth\r\n"
|
||||||
|
" 0 setgray\r\n"
|
||||||
|
"stroke\r\n",
|
||||||
|
MmToPoints(p0.x - ptMin.x), MmToPoints(p0.y - ptMin.y),
|
||||||
|
MmToPoints(c.x - ptMin.x), MmToPoints(c.y - ptMin.y),
|
||||||
|
MmToPoints(r),
|
||||||
|
theta0*180/PI, theta1*180/PI);
|
||||||
|
} else {
|
||||||
|
BezierAsPwl(sb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpsFileWriter::FinishAndCloseFile(void) {
|
void EpsFileWriter::FinishAndCloseFile(void) {
|
||||||
|
@ -541,6 +613,10 @@ void EpsFileWriter::FinishAndCloseFile(void) {
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Routines for SVG output
|
// Routines for SVG output
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const char *SvgFileWriter::SVG_STYLE =
|
||||||
|
"stroke-width='1' stroke='black' style='fill: none;'";
|
||||||
|
|
||||||
void SvgFileWriter::StartFile(void) {
|
void SvgFileWriter::StartFile(void) {
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" "
|
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" "
|
||||||
|
@ -559,10 +635,10 @@ 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
|
// 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' %s />\r\n",
|
||||||
"stroke-width='1' stroke='black' style='fill: none;' />\r\n",
|
|
||||||
(x0 - ptMin.x), (ptMax.y - y0),
|
(x0 - ptMin.x), (ptMax.y - y0),
|
||||||
(x1 - ptMin.x), (ptMax.y - y1));
|
(x1 - ptMin.x), (ptMax.y - y1),
|
||||||
|
SVG_STYLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgFileWriter::Triangle(STriangle *tr) {
|
void SvgFileWriter::Triangle(STriangle *tr) {
|
||||||
|
@ -583,7 +659,52 @@ void SvgFileWriter::Triangle(STriangle *tr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgFileWriter::Bezier(SBezier *sb) {
|
void SvgFileWriter::Bezier(SBezier *sb) {
|
||||||
BezierAsPwl(sb);
|
Vector c, n = Vector::From(0, 0, 1);
|
||||||
|
double r;
|
||||||
|
if(sb->IsCircle(n, &c, &r)) {
|
||||||
|
Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2];
|
||||||
|
double theta0 = atan2(p0.y - c.y, p0.x - c.x),
|
||||||
|
theta1 = atan2(p1.y - c.y, p1.x - c.x),
|
||||||
|
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
||||||
|
// The arc must be less than 180 degrees, or else it couldn't have
|
||||||
|
// been represented as a single rational Bezier. And arrange it
|
||||||
|
// to run counter-clockwise, which corresponds to clockwise in
|
||||||
|
// SVG's mirrored coordinate system.
|
||||||
|
if(dtheta < 0) {
|
||||||
|
SWAP(Vector, p0, p1);
|
||||||
|
}
|
||||||
|
fprintf(f,
|
||||||
|
"<path d='M%.3f,%.3f "
|
||||||
|
"A%.3f,%.3f 0 0,0 %.3f,%.3f' %s />\r\n",
|
||||||
|
p0.x - ptMin.x, ptMax.y - p0.y,
|
||||||
|
r, r,
|
||||||
|
p1.x - ptMin.x, ptMax.y - p1.y,
|
||||||
|
SVG_STYLE);
|
||||||
|
} else if(!sb->IsRational()) {
|
||||||
|
if(sb->deg == 1) {
|
||||||
|
LineSegment(sb->ctrl[0].x, sb->ctrl[0].y,
|
||||||
|
sb->ctrl[1].x, sb->ctrl[1].y);
|
||||||
|
} else if(sb->deg == 2) {
|
||||||
|
fprintf(f,
|
||||||
|
"<path d='M%.3f,%.3f "
|
||||||
|
"Q%.3f,%.3f %.3f,%.3f' %s />\r\n",
|
||||||
|
sb->ctrl[0].x - ptMin.x, ptMax.y - sb->ctrl[0].y,
|
||||||
|
sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y,
|
||||||
|
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y,
|
||||||
|
SVG_STYLE);
|
||||||
|
} else if(sb->deg == 3) {
|
||||||
|
fprintf(f,
|
||||||
|
"<path d='M%.3f,%.3f "
|
||||||
|
"C%.3f,%.3f %.3f,%.3f %.3f,%.3f' %s />\r\n",
|
||||||
|
sb->ctrl[0].x - ptMin.x, ptMax.y - sb->ctrl[0].y,
|
||||||
|
sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y,
|
||||||
|
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y,
|
||||||
|
sb->ctrl[3].x - ptMin.x, ptMax.y - sb->ctrl[3].y,
|
||||||
|
SVG_STYLE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BezierAsPwl(sb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgFileWriter::FinishAndCloseFile(void) {
|
void SvgFileWriter::FinishAndCloseFile(void) {
|
||||||
|
|
|
@ -378,6 +378,7 @@ public:
|
||||||
};
|
};
|
||||||
class SvgFileWriter : public VectorFileWriter {
|
class SvgFileWriter : public VectorFileWriter {
|
||||||
public:
|
public:
|
||||||
|
static const char *SVG_STYLE;
|
||||||
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 Triangle(STriangle *tr);
|
||||||
void Bezier(SBezier *sb);
|
void Bezier(SBezier *sb);
|
||||||
|
|
|
@ -97,6 +97,59 @@ SBezier SBezier::TransformedBy(Vector t, Quaternion q) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Is this Bezier exactly the arc of a circle, projected along the specified
|
||||||
|
// axis? If yes, return that circle's center and radius.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool SBezier::IsCircle(Vector axis, Vector *center, double *r) {
|
||||||
|
if(deg != 2) return false;
|
||||||
|
|
||||||
|
Vector t0 = (ctrl[0]).Minus(ctrl[1]),
|
||||||
|
t2 = (ctrl[2]).Minus(ctrl[1]),
|
||||||
|
r0 = axis.Cross(t0),
|
||||||
|
r2 = axis.Cross(t2);
|
||||||
|
|
||||||
|
*center = Vector::AtIntersectionOfLines(ctrl[0], (ctrl[0]).Plus(r0),
|
||||||
|
ctrl[2], (ctrl[2]).Plus(r2),
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
|
||||||
|
double rd0 = center->Minus(ctrl[0]).Magnitude(),
|
||||||
|
rd2 = center->Minus(ctrl[2]).Magnitude();
|
||||||
|
if(fabs(rd0 - rd2) > LENGTH_EPS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*r = rd0;
|
||||||
|
|
||||||
|
Vector u = r0.WithMagnitude(1),
|
||||||
|
v = (axis.Cross(u)).WithMagnitude(1);
|
||||||
|
Point2d c2 = center->Project2d(u, v),
|
||||||
|
pa2 = (ctrl[0]).Project2d(u, v).Minus(c2),
|
||||||
|
pb2 = (ctrl[2]).Project2d(u, v).Minus(c2);
|
||||||
|
|
||||||
|
double thetaa = atan2(pa2.y, pa2.x), // in fact always zero due to csys
|
||||||
|
thetab = atan2(pb2.y, pb2.x),
|
||||||
|
dtheta = WRAP_NOT_0(thetab - thetaa, 2*PI);
|
||||||
|
if(dtheta > PI) {
|
||||||
|
// Not possible with a second order Bezier arc; so we must have
|
||||||
|
// the points backwards.
|
||||||
|
dtheta = 2*PI - dtheta;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fabs(weight[1] - cos(dtheta/2)) > LENGTH_EPS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SBezier::IsRational(void) {
|
||||||
|
int i;
|
||||||
|
for(i = 0; i <= deg; i++) {
|
||||||
|
if(fabs(weight[i] - 1) > LENGTH_EPS) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Apply a perspective transformation to a rational Bezier curve, calculating
|
// Apply a perspective transformation to a rational Bezier curve, calculating
|
||||||
// the new weights as required.
|
// the new weights as required.
|
||||||
|
@ -141,6 +194,32 @@ void SBezierList::Clear(void) {
|
||||||
l.Clear();
|
l.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// If our list contains multiple identical Beziers (in either forward or
|
||||||
|
// reverse order), then cull them.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void SBezierList::CullIdenticalBeziers(void) {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
l.ClearTags();
|
||||||
|
for(i = 0; i < l.n; i++) {
|
||||||
|
SBezier *bi = &(l.elem[i]), bir;
|
||||||
|
bir = *bi;
|
||||||
|
bir.Reverse();
|
||||||
|
|
||||||
|
for(j = i + 1; j < l.n; j++) {
|
||||||
|
SBezier *bj = &(l.elem[j]);
|
||||||
|
if(bj->Equals(bi) ||
|
||||||
|
bj->Equals(&bir))
|
||||||
|
{
|
||||||
|
bi->tag = 1;
|
||||||
|
bj->tag = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.RemoveTagged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
|
SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
|
||||||
bool *allClosed, SEdge *errorAt)
|
bool *allClosed, SEdge *errorAt)
|
||||||
|
|
|
@ -50,51 +50,15 @@ bool SSurface::IsExtrusion(SBezier *of, Vector *alongp) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SSurface::IsCylinder(Vector *center, Vector *axis, double *r,
|
bool SSurface::IsCylinder(Vector *axis, Vector *center, double *r,
|
||||||
Vector *start, Vector *finish)
|
Vector *start, Vector *finish)
|
||||||
{
|
{
|
||||||
SBezier sb;
|
SBezier sb;
|
||||||
if(!IsExtrusion(&sb, axis)) return false;
|
if(!IsExtrusion(&sb, axis)) return false;
|
||||||
if(sb.deg != 2) return false;
|
if(!sb.IsCircle(*axis, center, r)) return false;
|
||||||
|
|
||||||
Vector t0 = (sb.ctrl[0]).Minus(sb.ctrl[1]),
|
*start = sb.ctrl[0];
|
||||||
t2 = (sb.ctrl[2]).Minus(sb.ctrl[1]),
|
|
||||||
r0 = axis->Cross(t0),
|
|
||||||
r2 = axis->Cross(t2);
|
|
||||||
|
|
||||||
*center = Vector::AtIntersectionOfLines(sb.ctrl[0], (sb.ctrl[0]).Plus(r0),
|
|
||||||
sb.ctrl[2], (sb.ctrl[2]).Plus(r2),
|
|
||||||
NULL, NULL, NULL);
|
|
||||||
|
|
||||||
double rd0 = center->Minus(sb.ctrl[0]).Magnitude(),
|
|
||||||
rd2 = center->Minus(sb.ctrl[2]).Magnitude();
|
|
||||||
if(fabs(rd0 - rd2) > LENGTH_EPS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*r = rd0;
|
|
||||||
|
|
||||||
Vector u = r0.WithMagnitude(1),
|
|
||||||
v = (axis->Cross(u)).WithMagnitude(1);
|
|
||||||
Point2d c2 = center->Project2d(u, v),
|
|
||||||
pa2 = (sb.ctrl[0]).Project2d(u, v).Minus(c2),
|
|
||||||
pb2 = (sb.ctrl[2]).Project2d(u, v).Minus(c2);
|
|
||||||
|
|
||||||
double thetaa = atan2(pa2.y, pa2.x), // in fact always zero due to csys
|
|
||||||
thetab = atan2(pb2.y, pb2.x),
|
|
||||||
dtheta = WRAP_NOT_0(thetab - thetaa, 2*PI);
|
|
||||||
if(dtheta > PI) {
|
|
||||||
// Not possible with a second order Bezier arc; so we must have
|
|
||||||
// the points backwards.
|
|
||||||
dtheta = 2*PI - dtheta;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fabs(sb.weight[1] - cos(dtheta/2)) > LENGTH_EPS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*start = sb.ctrl[0];
|
|
||||||
*finish = sb.ctrl[2];
|
*finish = sb.ctrl[2];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,9 @@ public:
|
||||||
void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);
|
void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);
|
||||||
void Reverse(void);
|
void Reverse(void);
|
||||||
|
|
||||||
|
bool IsCircle(Vector axis, Vector *center, double *r);
|
||||||
|
bool IsRational(void);
|
||||||
|
|
||||||
SBezier TransformedBy(Vector t, Quaternion q);
|
SBezier TransformedBy(Vector t, Quaternion q);
|
||||||
SBezier InPerspective(Vector u, Vector v, Vector n,
|
SBezier InPerspective(Vector u, Vector v, Vector n,
|
||||||
Vector origin, double cameraTan);
|
Vector origin, double cameraTan);
|
||||||
|
@ -90,6 +93,7 @@ public:
|
||||||
List<SBezier> l;
|
List<SBezier> l;
|
||||||
|
|
||||||
void Clear(void);
|
void Clear(void);
|
||||||
|
void CullIdenticalBeziers(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SBezierLoop {
|
class SBezierLoop {
|
||||||
|
@ -239,8 +243,8 @@ public:
|
||||||
bool CoincidentWithPlane(Vector n, double d);
|
bool CoincidentWithPlane(Vector n, double d);
|
||||||
bool CoincidentWith(SSurface *ss, bool sameNormal);
|
bool CoincidentWith(SSurface *ss, bool sameNormal);
|
||||||
bool IsExtrusion(SBezier *of, Vector *along);
|
bool IsExtrusion(SBezier *of, Vector *along);
|
||||||
bool IsCylinder(Vector *center, Vector *axis, double *r,
|
bool IsCylinder(Vector *axis, Vector *center, double *r,
|
||||||
Vector *start, Vector *finish);
|
Vector *start, Vector *finish);
|
||||||
|
|
||||||
void TriangulateInto(SShell *shell, SMesh *sm);
|
void TriangulateInto(SShell *shell, SMesh *sm);
|
||||||
void MakeTrimEdgesInto(SEdgeList *sel, bool asUv, SCurve *sc, STrimBy *stb);
|
void MakeTrimEdgesInto(SEdgeList *sel, bool asUv, SCurve *sc, STrimBy *stb);
|
||||||
|
|
|
@ -469,7 +469,7 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b,
|
||||||
ClosestPointTo(p, &(inter.p.x), &(inter.p.y));
|
ClosestPointTo(p, &(inter.p.x), &(inter.p.y));
|
||||||
inters.Add(&inter);
|
inters.Add(&inter);
|
||||||
}
|
}
|
||||||
} else if(IsCylinder(¢er, &axis, &radius, &start, &finish)) {
|
} else if(IsCylinder(&axis, ¢er, &radius, &start, &finish)) {
|
||||||
// This one can be solved in closed form too.
|
// This one can be solved in closed form too.
|
||||||
Vector ab = b.Minus(a);
|
Vector ab = b.Minus(a);
|
||||||
if(axis.Cross(ab).Magnitude() < LENGTH_EPS) {
|
if(axis.Cross(ab).Magnitude() < LENGTH_EPS) {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
|
||||||
marching algorithm for surface intersection
|
marching algorithm for surface intersection
|
||||||
surfaces of revolution (lathed)
|
surfaces of revolution (lathed)
|
||||||
cylinder-line special cases
|
|
||||||
boundary avoidance when casting ray for point-in-shell
|
boundary avoidance when casting ray for point-in-shell
|
||||||
tangent intersections
|
tangent intersections
|
||||||
short pwl edge avoidance
|
short pwl edge avoidance
|
||||||
exact curve export (at least for dxf)
|
direct PDF export
|
||||||
assembly
|
assembly
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
Loading…
Reference in New Issue