From f87152e8c036eabc0b8a956fea3eed1604a4687d Mon Sep 17 00:00:00 2001 From: EvilSpirit Date: Wed, 6 Jan 2016 18:40:17 +0600 Subject: [PATCH] DXF: export constraints with labels as DXF constraints, not pwl. Specifically, the following constraint types: * pt-pt-distance * pt-line-distance * diameter * angle * comment --- src/entity.cpp | 16 ++++ src/export.cpp | 62 +++++++++---- src/exportvector.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++- src/sketch.h | 1 + src/solvespace.h | 26 ++++-- 5 files changed, 292 insertions(+), 30 deletions(-) diff --git a/src/entity.cpp b/src/entity.cpp index 2d44771..7f4a3a7 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -76,6 +76,22 @@ Vector EntityBase::VectorGetRefPoint(void) { } } +Vector EntityBase::VectorGetStartPoint(void) { + switch(type) { + case LINE_SEGMENT: + return SK.GetEntity(point[1])->PointGetNum(); + + case NORMAL_IN_3D: + case NORMAL_IN_2D: + case NORMAL_N_COPY: + case NORMAL_N_ROT: + case NORMAL_N_ROT_AA: + return SK.GetEntity(point[0])->PointGetNum(); + + default: oops(); + } +} + bool EntityBase::IsCircle(void) { return (type == CIRCLE) || (type == ARC_OF_CIRCLE); } diff --git a/src/export.cpp b/src/export.cpp index 94a09f2..6bfb4b5 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -112,6 +112,9 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wir SEdgeList edges = {}; SBezierList beziers = {}; + VectorFileWriter *out = VectorFileWriter::ForFile(filename); + if(!out) return; + SS.exportMode = true; GenerateAll(GENERATE_ALL); @@ -153,31 +156,41 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wir } if(SS.GW.showConstraints) { - Constraint *c; - for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) { - c->GetEdges(&edges); + if(!out->OutputConstraints(&SK.constraint)) { + // The output format cannot represent constraints directly, + // so convert them to edges. + Constraint *c; + for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) { + c->GetEdges(&edges); + } } } if(wireframe) { - VectorFileWriter *out = VectorFileWriter::ForFile(filename); - if(out) { - ExportWireframeCurves(&edges, &beziers, out); - } + Vector u = Vector::From(1.0, 0.0, 0.0), + v = Vector::From(0.0, 1.0, 0.0), + n = Vector::From(0.0, 0.0, 1.0), + origin = Vector::From(0.0, 0.0, 0.0); + double cameraTan = 0.0, + scale = 1.0; + + out->SetModelviewProjection(u, v, n, origin, cameraTan, scale); + + ExportWireframeCurves(&edges, &beziers, out); } else { Vector u = SS.GW.projRight, v = SS.GW.projUp, n = u.Cross(v), origin = SS.GW.offset.ScaledBy(-1); - VectorFileWriter *out = VectorFileWriter::ForFile(filename); - if(out) { - ExportLinesAndMesh(&edges, &beziers, sm, - u, v, n, origin, SS.CameraTangent()*SS.GW.scale, - out); - } + out->SetModelviewProjection(u, v, n, origin, + SS.CameraTangent()*SS.GW.scale, SS.exportScale); - if(out && !out->HasCanvasSize()) { + ExportLinesAndMesh(&edges, &beziers, sm, + u, v, n, origin, SS.CameraTangent()*SS.GW.scale, + out); + + if(!out->HasCanvasSize()) { // These file formats don't have a canvas size, so they just // get exported in the raw coordinate system. So indicate what // that was on-screen. @@ -215,7 +228,7 @@ void SolveSpaceUI::ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl, sblss.AddOpenPath(sb); } - out->Output(&sblss, NULL); + out->OutputLinesAndMesh(&sblss, NULL); sblss.Clear(); } @@ -381,7 +394,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s } // Now write the lines and triangles to the output file - out->Output(&sblss, &sms); + out->OutputLinesAndMesh(&sblss, &sms); leftovers.Clear(); spxyz.Clear(); @@ -441,7 +454,22 @@ VectorFileWriter *VectorFileWriter::ForFile(const std::string &filename) { return ret; } -void VectorFileWriter::Output(SBezierLoopSetSet *sblss, SMesh *sm) { +void VectorFileWriter::SetModelviewProjection(const Vector &u, const Vector &v, const Vector &n, + const Vector &origin, double cameraTan, + double scale) { + this->u = u; + this->v = v; + this->n = n; + this->origin = origin; + this->cameraTan = cameraTan; + this->scale = scale; +} + +Vector VectorFileWriter::Transform(Vector &pos) const { + return pos.InPerspective(u, v, n, origin, cameraTan).ScaledBy(1.0 / scale); +} + +void VectorFileWriter::OutputLinesAndMesh(SBezierLoopSetSet *sblss, SMesh *sm) { STriangle *tr; SBezier *b; diff --git a/src/exportvector.cpp b/src/exportvector.cpp index bee7909..76d30a1 100644 --- a/src/exportvector.cpp +++ b/src/exportvector.cpp @@ -6,7 +6,7 @@ #include #include "solvespace.h" -void VectorFileWriter::Dummy(void) { +VectorFileWriter::~VectorFileWriter() { // This out-of-line virtual method definition quells the following warning // from Clang++: "'VectorFileWriter' has no out-of-line virtual method // definitions; its vtable will be emitted in every translation unit @@ -22,10 +22,25 @@ class DxfWriteInterface : public DRW_Interface { int currentColor; double currentWidth; + static DRW_Coord toCoord(const Vector &v) { + return DRW_Coord(v.x, v.y, v.z); + } + + Vector xfrm(Vector v) { + return writer->Transform(v); + } + public: DxfWriteInterface(DxfFileWriter *w, dxfRW *dxfrw) : writer(w), dxf(dxfrw) {} + virtual void writeTextstyles() { + DRW_Textstyle ts; + ts.name = "unicode"; + ts.font = "unicode"; + dxf->writeTextstyle(&ts); + } + virtual void writeEntities() { for(DxfFileWriter::BezierPath &path : writer->paths) { currentColor = path.color; @@ -34,6 +49,110 @@ public: writeBezier(sb); } } + + if(writer->constraint) { + Constraint *c; + for(c = writer->constraint->First(); c; c = writer->constraint->NextAfter(c)) { + switch(c->type) { + case Constraint::PT_PT_DISTANCE: { + Vector ap = SK.GetEntity(c->ptA)->PointGetNum(); + Vector bp = SK.GetEntity(c->ptB)->PointGetNum(); + Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(c->disp.offset); + writeAlignedDimension(xfrm(ap), xfrm(bp), xfrm(ref), + xfrm(ref), c->Label()); + break; + } + + case Constraint::PT_LINE_DISTANCE: { + Vector pt = SK.GetEntity(c->ptA)->PointGetNum(); + Entity *line = SK.GetEntity(c->entityA); + Vector lA = SK.GetEntity(line->point[0])->PointGetNum(); + Vector lB = SK.GetEntity(line->point[1])->PointGetNum(); + Vector dl = lB.Minus(lA); + + Vector closest = pt.ClosestPointOnLine(lA, dl); + + if(pt.Equals(closest)) break; + + Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(c->disp.offset); + Vector refClosest = ref.ClosestPointOnLine(lA, dl); + + double ddl = dl.Dot(dl); + if(fabs(ddl) > LENGTH_EPS * LENGTH_EPS) { + double t = refClosest.Minus(lA).Dot(dl) / ddl; + if(t < 0.0) { + refClosest = lA; + } else if(t > 1.0) { + refClosest = lB; + } + } + + Vector xdl = xfrm(lB).Minus(xfrm(lA)); + writeLinearDimension(xfrm(pt), xfrm(refClosest), xfrm(ref), + xfrm(ref), c->Label(), + atan2(xdl.y, xdl.x) / PI * 180.0 + 90.0, 0.0); + break; + } + + case Constraint::DIAMETER: { + Entity *circle = SK.GetEntity(c->entityA); + Vector center = SK.GetEntity(circle->point[0])->PointGetNum(); + Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum(); + Vector n = q.RotationN().WithMagnitude(1); + double r = circle->CircleGetRadiusNum(); + + Vector ref = center.Plus(c->disp.offset); + // Force the label into the same plane as the circle. + ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center))); + + Vector rad = ref.Minus(center).WithMagnitude(r); + if(/*isRadius*/c->other) { + writeRadialDimension( + xfrm(center), xfrm(center.Plus(rad)), + xfrm(ref), c->Label()); + } else { + writeDiametricDimension( + xfrm(center.Minus(rad)), xfrm(center.Plus(rad)), + xfrm(ref), c->Label()); + } + break; + } + + case Constraint::ANGLE: { + Entity *a = SK.GetEntity(c->entityA); + Entity *b = SK.GetEntity(c->entityB); + + Vector a0 = a->VectorGetStartPoint(); + Vector b0 = b->VectorGetStartPoint(); + Vector da = a->VectorGetNum(); + Vector db = b->VectorGetNum(); + if(/*otherAngle*/c->other) { + a0 = a0.Plus(da); + da = da.ScaledBy(-1); + } + + bool skew = false; + Vector ref = c->disp.offset; + Vector pi = Vector::AtIntersectionOfLines(a0, a0.Plus(da), b0, b0.Plus(db), + &skew); + if(!skew) ref = pi.Plus(c->disp.offset); + + writeAngularDimension( + xfrm(a0), xfrm(a0.Plus(da)), xfrm(b0), xfrm(b0.Plus(db)), xfrm(ref), + xfrm(ref), c->Label()); + break; + } + + case Constraint::COMMENT: { + Style *st = Style::Get(c->disp.style); + writeText(xfrm(c->disp.offset), c->Label(), + Style::TextHeight(c->disp.style) / SS.GW.scale, + st->textAngle, st->textOrigin); + break; + } + } + } + } } void assignEntityDefaults(DRW_Entity *entity) { @@ -44,8 +163,8 @@ public: void writeLine(const Vector &p0, const Vector &p1) { DRW_Line line; assignEntityDefaults(&line); - line.basePoint = DRW_Coord(p0.x, p0.y, 0.0); - line.secPoint = DRW_Coord(p1.x, p1.y, 0.0); + line.basePoint = toCoord(p0); + line.secPoint = toCoord(p1); dxf->writeLine(&line); } @@ -53,7 +172,7 @@ public: DRW_Arc arc; assignEntityDefaults(&arc); arc.radious = r; - arc.basePoint = DRW_Coord(c.x, c.y, 0.0); + arc.basePoint = toCoord(c); arc.staangle = sa; arc.endangle = ea; dxf->writeArc(&arc); @@ -154,8 +273,95 @@ public: polyline.vertlist.push_back(vertex); } } + + void writeAlignedDimension(Vector def1, Vector def2, Vector dimp, + Vector textp, const std::string &text) { + DRW_DimAligned dim; + dim.setDef1Point(toCoord(def1)); + dim.setDef2Point(toCoord(def2)); + dim.setDimPoint(toCoord(dimp)); + dim.setTextPoint(toCoord(textp)); + dim.setText(text); + dxf->writeDimension(&dim); + } + + void writeLinearDimension(Vector def1, Vector def2, Vector dimp, + Vector textp, const std::string &text, + double angle, double oblique) { + DRW_DimLinear dim; + dim.setDef1Point(toCoord(def1)); + dim.setDef2Point(toCoord(def2)); + dim.setDimPoint(toCoord(dimp)); + dim.setTextPoint(toCoord(textp)); + dim.setText(text); + dim.setAngle(angle); + dim.setOblique(oblique); + dxf->writeDimension(&dim); + } + + void writeRadialDimension(Vector center, Vector radius, + Vector textp, const std::string &text) { + DRW_DimRadial dim; + dim.setCenterPoint(toCoord(center)); + dim.setDiameterPoint(toCoord(radius)); + dim.setTextPoint(toCoord(textp)); + dim.setText(text); + dxf->writeDimension(&dim); + } + + void writeDiametricDimension(Vector def1, Vector def2, + Vector textp, const std::string &text) { + DRW_DimDiametric dim; + dim.setDiameter1Point(toCoord(def1)); + dim.setDiameter2Point(toCoord(def2)); + dim.setTextPoint(toCoord(textp)); + dim.setText(text); + dxf->writeDimension(&dim); + } + + void writeAngularDimension(Vector fl1, Vector fl2, Vector sl1, Vector sl2, Vector dimp, + Vector textp, const std::string &text) { + DRW_DimAngular dim; + dim.setFirstLine1(toCoord(fl1)); + dim.setFirstLine2(toCoord(fl2)); + dim.setSecondLine1(toCoord(sl1)); + dim.setSecondLine2(toCoord(sl2)); + dim.setDimPoint(toCoord(dimp)); + dim.setTextPoint(toCoord(textp)); + dim.setText(text); + dxf->writeDimension(&dim); + } + + void writeText(Vector textp, const std::string &text, + double height, double angle, int origin) { + DRW_Text txt; + txt.style = "unicode"; + txt.basePoint = toCoord(textp); + txt.secPoint = txt.basePoint; + txt.text = text; + txt.height = height; + txt.angle = angle; + txt.alignH = DRW_Text::HCenter; + if(origin & Style::ORIGIN_LEFT) { + txt.alignH = DRW_Text::HLeft; + } else if(origin & Style::ORIGIN_RIGHT) { + txt.alignH = DRW_Text::HRight; + } + txt.alignV = DRW_Text::VMiddle; + if(origin & Style::ORIGIN_TOP) { + txt.alignV = DRW_Text::VTop; + } else if(origin & Style::ORIGIN_BOT) { + txt.alignV = DRW_Text::VBaseLine; + } + dxf->writeText(&txt); + } }; +bool DxfFileWriter::OutputConstraints(IdList *constraint) { + this->constraint = constraint; + return true; +} + void DxfFileWriter::StartFile(void) { paths.clear(); } @@ -183,8 +389,9 @@ void DxfFileWriter::Bezier(SBezier *sb) { void DxfFileWriter::FinishAndCloseFile(void) { dxfRW dxf(filename.c_str()); DxfWriteInterface interface(this, &dxf); - dxf.write(&interface, DRW::AC1018, false); + dxf.write(&interface, DRW::AC1021, false); paths.clear(); + constraint = NULL; } //----------------------------------------------------------------------------- diff --git a/src/sketch.h b/src/sketch.h index 23abe5a..a6b5178 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -383,6 +383,7 @@ public: ExprVector VectorGetExprs(void); Vector VectorGetNum(void); Vector VectorGetRefPoint(void); + Vector VectorGetStartPoint(void); // For distances bool IsDistance(void); diff --git a/src/solvespace.h b/src/solvespace.h index e34c9d9..1350e16 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -490,24 +490,33 @@ public: }; class VectorFileWriter { +protected: + Vector u, v, n, origin; + double cameraTan, scale; + public: FILE *f; std::string filename; Vector ptMin, ptMax; - // This quells the Clang++ warning "'VectorFileWriter' has virtual - // functions but non-virtual destructor" - virtual ~VectorFileWriter() {} - static double MmToPts(double mm); static VectorFileWriter *ForFile(const std::string &filename); - void Output(SBezierLoopSetSet *sblss, SMesh *sm); + ~VectorFileWriter(); + + void SetModelviewProjection(const Vector &u, const Vector &v, const Vector &n, + const Vector &origin, double cameraTan, double scale); + Vector Transform(Vector &pos) const; + + void OutputLinesAndMesh(SBezierLoopSetSet *sblss, SMesh *sm); void BezierAsPwl(SBezier *sb); void BezierAsNonrationalCubic(SBezier *sb, int depth=0); + virtual bool OutputConstraints(IdList *constraint) + { return false; } + virtual void StartPath( RgbaColor strokeRgb, double lineWidth, bool filled, RgbaColor fillRgb) = 0; virtual void FinishPath(RgbaColor strokeRgb, double lineWidth, @@ -517,8 +526,6 @@ public: virtual void StartFile(void) = 0; virtual void FinishAndCloseFile(void) = 0; virtual bool HasCanvasSize(void) = 0; - - virtual void Dummy(void); }; class DxfFileWriter : public VectorFileWriter { public: @@ -528,7 +535,10 @@ public: double width; }; - std::vector paths; + std::vector paths; + IdList *constraint; + + bool OutputConstraints(IdList *constraint); void StartPath( RgbaColor strokeRgb, double lineWidth, bool filled, RgbaColor fillRgb);