Include custom styled entities in the same plane when exporting section.

pull/409/head
Bauke Conijn 2019-05-13 16:34:22 +02:00 committed by whitequark
parent c44a471649
commit 7d181f0d0f
8 changed files with 99 additions and 36 deletions

View File

@ -46,6 +46,8 @@ New export/import features:
exported. This format allows to easily hack on triangle mesh data created exported. This format allows to easily hack on triangle mesh data created
in SolveSpace, supports colour information and is more space efficient than in SolveSpace, supports colour information and is more space efficient than
most other formats. most other formats.
* Export 2d section: custom styled entities that lie in the same
plane as the exported section are included.
New rendering features: New rendering features:
* The "Show/hide hidden lines" button is now a tri-state button that allows * The "Show/hide hidden lines" button is now a tri-state button that allows

View File

@ -784,6 +784,47 @@ Vector EntityBase::EndpointFinish() const {
return SK.GetEntity(point[2])->PointGetNum(); return SK.GetEntity(point[2])->PointGetNum();
} else ssassert(false, "Unexpected entity type"); } else ssassert(false, "Unexpected entity type");
} }
static bool PointInPlane(hEntity h, Vector norm, double distance) {
Vector p = SK.GetEntity(h)->PointGetNum();
return (fabs(norm.Dot(p) - distance) < LENGTH_EPS);
}
bool EntityBase::IsInPlane(Vector norm, double distance) const {
switch(type) {
case Type::LINE_SEGMENT: {
return PointInPlane(point[0], norm, distance)
&& PointInPlane(point[1], norm, distance);
}
case Type::CUBIC:
case Type::CUBIC_PERIODIC: {
bool periodic = type == Type::CUBIC_PERIODIC;
int n = periodic ? 3 + extraPoints : extraPoints;
int i;
for (i=0; i<n; i++) {
if (!PointInPlane(point[i], norm, distance)) return false;
}
return true;
}
case Type::CIRCLE:
case Type::ARC_OF_CIRCLE: {
// If it is an (arc of) a circle, check whether the normals
// are parallel and the mid point is in the plane.
Vector n = Normal()->NormalN();
if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false;
return PointInPlane(point[0], norm, distance);
}
case Type::TTF_TEXT: {
Vector n = Normal()->NormalN();
if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false;
return PointInPlane(point[0], norm, distance)
&& PointInPlane(point[1], norm, distance);
}
default:
return false;
}
}
void EntityBase::RectGetPointsExprs(ExprVector *eb, ExprVector *ec) const { void EntityBase::RectGetPointsExprs(ExprVector *eb, ExprVector *ec) const {
ssassert(type == Type::TTF_TEXT || type == Type::IMAGE, ssassert(type == Type::TTF_TEXT || type == Type::IMAGE,

View File

@ -78,9 +78,8 @@ void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
g->runningMesh.MakeEdgesInPlaneInto(&el, n, d); g->runningMesh.MakeEdgesInPlaneInto(&el, n, d);
// If there's a shell, then grab the edges and possibly Beziers. // If there's a shell, then grab the edges and possibly Beziers.
g->runningShell.MakeSectionEdgesInto(n, d, bool export_as_pwl = SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS;
&el, g->runningShell.MakeSectionEdgesInto(n, d, &el, export_as_pwl ? NULL : &bl);
(SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS) ? NULL : &bl);
// All of these are solid model edges, so use the appropriate style. // All of these are solid model edges, so use the appropriate style.
SEdge *se; SEdge *se;
@ -92,8 +91,28 @@ void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
sb->auxA = Style::SOLID_EDGE; sb->auxA = Style::SOLID_EDGE;
} }
el.CullExtraneousEdges(); // Remove all overlapping edges/beziers to merge the areas they describe.
bl.CullIdenticalBeziers(); el.CullExtraneousEdges(/*both=*/true);
bl.CullIdenticalBeziers(/*both=*/true);
// Collect lines and beziers with custom style & export.
int i;
for(i = 0; i < SK.entity.n; i++) {
Entity *e = &(SK.entity.elem[i]);
if (!e->IsVisible()) continue;
if (e->style.v < Style::FIRST_CUSTOM) continue;
if (!Style::Exportable(e->style.v)) continue;
if (!e->IsInPlane(n,d)) continue;
if (export_as_pwl) {
e->GenerateEdges(&el);
} else {
e->GenerateBezierCurves(&bl);
}
}
// Only remove half of the overlapping edges/beziers to support TTF Stick Fonts.
el.CullExtraneousEdges(/*both=*/false);
bl.CullIdenticalBeziers(/*both=*/false);
// And write the edges. // And write the edges.
VectorFileWriter *out = VectorFileWriter::ForFile(filename); VectorFileWriter *out = VectorFileWriter::ForFile(filename);
@ -573,7 +592,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
// If possible, then we will assemble these output curves into loops. They // If possible, then we will assemble these output curves into loops. They
// will then get exported as closed paths. // will then get exported as closed paths.
SBezierLoopSetSet sblss = {}; SBezierLoopSetSet sblss = {};
SBezierList leftovers = {}; SBezierLoopSet leftovers = {};
SSurface srf = SSurface::FromPlane(Vector::From(0, 0, 0), SSurface srf = SSurface::FromPlane(Vector::From(0, 0, 0),
Vector::From(1, 0, 0), Vector::From(1, 0, 0),
Vector::From(0, 1, 0)); Vector::From(0, 1, 0));
@ -586,14 +605,11 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
&allClosed, &notClosedAt, &allClosed, &notClosedAt,
NULL, NULL, NULL, NULL,
&leftovers); &leftovers);
for(b = leftovers.l.First(); b; b = leftovers.l.NextAfter(b)) { sblss.l.Add(&leftovers);
sblss.AddOpenPath(b);
}
// Now write the lines and triangles to the output file // Now write the lines and triangles to the output file
out->OutputLinesAndMesh(&sblss, &sms); out->OutputLinesAndMesh(&sblss, &sms);
leftovers.Clear();
spxyz.Clear(); spxyz.Clear();
sblss.Clear(); sblss.Clear();
smp.Clear(); smp.Clear();

View File

@ -327,10 +327,13 @@ bool SEdgeList::ContainsEdge(const SEdge *set) const {
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Remove unnecessary edges: if two are anti-parallel then remove both, and if // Remove unnecessary edges:
// two are parallel then remove one. // - if two are anti-parallel then
// if both=true, remove both
// else remove only one.
// - if two are parallel then remove one.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SEdgeList::CullExtraneousEdges() { void SEdgeList::CullExtraneousEdges(bool both) {
l.ClearTags(); l.ClearTags();
int i, j; int i, j;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
@ -342,8 +345,9 @@ void SEdgeList::CullExtraneousEdges() {
set->tag = 1; set->tag = 1;
} }
if((set->a).Equals(se->b) && (set->b).Equals(se->a)) { if((set->a).Equals(se->b) && (set->b).Equals(se->a)) {
// Two anti-parallel edges exist; so keep neither. // Two anti-parallel edges exist; if both=true, keep neither,
se->tag = 1; // otherwise keep only one.
if (both) se->tag = 1;
set->tag = 1; set->tag = 1;
} }
} }

View File

@ -58,7 +58,7 @@ public:
Vector *pi=NULL, SPointList *spl=NULL) const; Vector *pi=NULL, SPointList *spl=NULL) const;
bool ContainsEdgeFrom(const SEdgeList *sel) const; bool ContainsEdgeFrom(const SEdgeList *sel) const;
bool ContainsEdge(const SEdge *se) const; bool ContainsEdge(const SEdge *se) const;
void CullExtraneousEdges(); void CullExtraneousEdges(bool both=true);
void MergeCollinearSegments(Vector a, Vector b); void MergeCollinearSegments(Vector a, Vector b);
}; };

View File

@ -185,7 +185,7 @@ public:
SPolygon polyLoops; SPolygon polyLoops;
SBezierLoopSetSet bezierLoops; SBezierLoopSetSet bezierLoops;
SBezierList bezierOpens; SBezierLoopSet bezierOpens;
struct { struct {
PolyError how; PolyError how;
@ -483,6 +483,7 @@ public:
bool HasEndpoints() const; bool HasEndpoints() const;
Vector EndpointStart() const; Vector EndpointStart() const;
Vector EndpointFinish() const; Vector EndpointFinish() const;
bool IsInPlane(Vector norm, double distance) const;
void RectGetPointsExprs(ExprVector *eap, ExprVector *ebp) const; void RectGetPointsExprs(ExprVector *eap, ExprVector *ebp) const;

View File

@ -231,9 +231,10 @@ void SBezierList::ScaleSelfBy(double s) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// If our list contains multiple identical Beziers (in either forward or // If our list contains multiple identical Beziers (in either forward or
// reverse order), then cull them. // reverse order), then cull them. If both is true, both beziers are removed.
// Otherwise only one of them is removed.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SBezierList::CullIdenticalBeziers() { void SBezierList::CullIdenticalBeziers(bool both) {
int i, j; int i, j;
l.ClearTags(); l.ClearTags();
@ -247,7 +248,7 @@ void SBezierList::CullIdenticalBeziers() {
if(bj->Equals(bi) || if(bj->Equals(bi) ||
bj->Equals(&bir)) bj->Equals(&bir))
{ {
bi->tag = 1; if (both) bi->tag = 1;
bj->tag = 1; bj->tag = 1;
} }
} }
@ -491,7 +492,7 @@ bool SBezierLoop::IsClosed() const {
SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly, SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
double chordTol, double chordTol,
bool *allClosed, SEdge *errorAt, bool *allClosed, SEdge *errorAt,
SBezierList *openContours) SBezierLoopSet *openContours)
{ {
SBezierLoopSet ret = {}; SBezierLoopSet ret = {};
@ -504,12 +505,10 @@ SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
// Record open loops in a separate list, if requested. // Record open loops in a separate list, if requested.
*allClosed = false; *allClosed = false;
if(openContours) { if(openContours) {
SBezier *sb; openContours->l.Add(&loop);
for(sb = loop.l.First(); sb; sb = loop.l.NextAfter(sb)) { } else {
openContours->l.Add(sb);
}
}
loop.Clear(); loop.Clear();
}
} else { } else {
ret.l.Add(&loop); ret.l.Add(&loop);
poly->AddEmptyContour(); poly->AddEmptyContour();
@ -576,7 +575,7 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
double chordTol, double chordTol,
bool *allClosed, SEdge *notClosedAt, bool *allClosed, SEdge *notClosedAt,
bool *allCoplanar, Vector *notCoplanarAt, bool *allCoplanar, Vector *notCoplanarAt,
SBezierList *openContours) SBezierLoopSet *openContours)
{ {
SSurface srfPlane; SSurface srfPlane;
if(!srfuv) { if(!srfuv) {
@ -589,7 +588,9 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
if(openContours) { if(openContours) {
SBezier *sb; SBezier *sb;
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
openContours->l.Add(sb); SBezierLoop sbl={};
sbl.l.Add(sb);
openContours->l.Add(&sbl);
} }
} }
return; return;
@ -705,12 +706,10 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
if(loop->tag == USED_LOOP) continue; if(loop->tag == USED_LOOP) continue;
if(openContours) { if(openContours) {
SBezier *sb; openContours->l.Add(loop);
for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) { } else {
openContours->l.Add(sb);
}
}
loop->Clear(); loop->Clear();
}
// but don't free the used loops, since we shallow-copied them to // but don't free the used loops, since we shallow-copied them to
// ourself // ourself
} }

View File

@ -125,7 +125,7 @@ public:
void Clear(); void Clear();
void ScaleSelfBy(double s); void ScaleSelfBy(double s);
void CullIdenticalBeziers(); void CullIdenticalBeziers(bool both=true);
void AllIntersectionsWith(SBezierList *sblb, SPointList *spl) const; void AllIntersectionsWith(SBezierList *sblb, SPointList *spl) const;
bool GetPlaneContainingBeziers(Vector *p, Vector *u, Vector *v, bool GetPlaneContainingBeziers(Vector *p, Vector *u, Vector *v,
Vector *notCoplanarAt) const; Vector *notCoplanarAt) const;
@ -156,7 +156,7 @@ public:
static SBezierLoopSet From(SBezierList *spcl, SPolygon *poly, static SBezierLoopSet From(SBezierList *spcl, SPolygon *poly,
double chordTol, double chordTol,
bool *allClosed, SEdge *errorAt, bool *allClosed, SEdge *errorAt,
SBezierList *openContours); SBezierLoopSet *openContours);
void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax) const; void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax) const;
double SignedArea(); double SignedArea();
@ -172,7 +172,7 @@ public:
double chordTol, double chordTol,
bool *allClosed, SEdge *notClosedAt, bool *allClosed, SEdge *notClosedAt,
bool *allCoplanar, Vector *notCoplanarAt, bool *allCoplanar, Vector *notCoplanarAt,
SBezierList *openContours); SBezierLoopSet *openContours);
void AddOpenPath(SBezier *sb); void AddOpenPath(SBezier *sb);
void Clear(); void Clear();
}; };