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
in SolveSpace, supports colour information and is more space efficient than
most other formats.
* Export 2d section: custom styled entities that lie in the same
plane as the exported section are included.
New rendering features:
* 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();
} 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 {
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);
// If there's a shell, then grab the edges and possibly Beziers.
g->runningShell.MakeSectionEdgesInto(n, d,
&el,
(SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS) ? NULL : &bl);
bool export_as_pwl = SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS;
g->runningShell.MakeSectionEdgesInto(n, d, &el, export_as_pwl ? NULL : &bl);
// All of these are solid model edges, so use the appropriate style.
SEdge *se;
@ -92,8 +91,28 @@ void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
sb->auxA = Style::SOLID_EDGE;
}
el.CullExtraneousEdges();
bl.CullIdenticalBeziers();
// Remove all overlapping edges/beziers to merge the areas they describe.
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.
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
// will then get exported as closed paths.
SBezierLoopSetSet sblss = {};
SBezierList leftovers = {};
SBezierLoopSet leftovers = {};
SSurface srf = SSurface::FromPlane(Vector::From(0, 0, 0),
Vector::From(1, 0, 0),
Vector::From(0, 1, 0));
@ -586,14 +605,11 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
&allClosed, &notClosedAt,
NULL, NULL,
&leftovers);
for(b = leftovers.l.First(); b; b = leftovers.l.NextAfter(b)) {
sblss.AddOpenPath(b);
}
sblss.l.Add(&leftovers);
// Now write the lines and triangles to the output file
out->OutputLinesAndMesh(&sblss, &sms);
leftovers.Clear();
spxyz.Clear();
sblss.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
// two are parallel then remove one.
// Remove unnecessary edges:
// - 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();
int i, j;
for(i = 0; i < l.n; i++) {
@ -342,8 +345,9 @@ void SEdgeList::CullExtraneousEdges() {
set->tag = 1;
}
if((set->a).Equals(se->b) && (set->b).Equals(se->a)) {
// Two anti-parallel edges exist; so keep neither.
se->tag = 1;
// Two anti-parallel edges exist; if both=true, keep neither,
// otherwise keep only one.
if (both) se->tag = 1;
set->tag = 1;
}
}

View File

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

View File

@ -185,7 +185,7 @@ public:
SPolygon polyLoops;
SBezierLoopSetSet bezierLoops;
SBezierList bezierOpens;
SBezierLoopSet bezierOpens;
struct {
PolyError how;
@ -483,6 +483,7 @@ public:
bool HasEndpoints() const;
Vector EndpointStart() const;
Vector EndpointFinish() const;
bool IsInPlane(Vector norm, double distance) 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
// 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;
l.ClearTags();
@ -247,7 +248,7 @@ void SBezierList::CullIdenticalBeziers() {
if(bj->Equals(bi) ||
bj->Equals(&bir))
{
bi->tag = 1;
if (both) bi->tag = 1;
bj->tag = 1;
}
}
@ -491,7 +492,7 @@ bool SBezierLoop::IsClosed() const {
SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
double chordTol,
bool *allClosed, SEdge *errorAt,
SBezierList *openContours)
SBezierLoopSet *openContours)
{
SBezierLoopSet ret = {};
@ -504,12 +505,10 @@ SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
// Record open loops in a separate list, if requested.
*allClosed = false;
if(openContours) {
SBezier *sb;
for(sb = loop.l.First(); sb; sb = loop.l.NextAfter(sb)) {
openContours->l.Add(sb);
}
}
openContours->l.Add(&loop);
} else {
loop.Clear();
}
} else {
ret.l.Add(&loop);
poly->AddEmptyContour();
@ -576,7 +575,7 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
double chordTol,
bool *allClosed, SEdge *notClosedAt,
bool *allCoplanar, Vector *notCoplanarAt,
SBezierList *openContours)
SBezierLoopSet *openContours)
{
SSurface srfPlane;
if(!srfuv) {
@ -589,7 +588,9 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
if(openContours) {
SBezier *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;
@ -705,12 +706,10 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
if(loop->tag == USED_LOOP) continue;
if(openContours) {
SBezier *sb;
for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) {
openContours->l.Add(sb);
}
}
openContours->l.Add(loop);
} else {
loop->Clear();
}
// but don't free the used loops, since we shallow-copied them to
// ourself
}

View File

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