diff --git a/src/dxf/Entity.h b/src/dxf/Entity.h index 9bf56bda..34e6c0d3 100644 --- a/src/dxf/Entity.h +++ b/src/dxf/Entity.h @@ -40,7 +40,7 @@ namespace DXF { virtual ~Entity() {} virtual type_t getType() const = 0; - virtual void addVertex(const cb::Vector3D &v) {THROW("Cannot add vertex");} + virtual void addVertex(const cb::Vector3D &v, double weight = 0.0) {THROW("Cannot add vertex");} virtual void addKnot(double k) {THROW("Cannot add knot");} }; } diff --git a/src/dxf/PolyLine.h b/src/dxf/PolyLine.h index 53659f6d..efcb450d 100644 --- a/src/dxf/PolyLine.h +++ b/src/dxf/PolyLine.h @@ -28,15 +28,27 @@ namespace DXF { class PolyLine : public Entity { + /* Polyline flag (bit-coded; default = 0): + * 1 = This is a closed polyline (or a polygon mesh closed in the M direction) + * 2 = Curve-fit vertices have been added + * 4 = Spline-fit vertices have been added + * 8 = This is a 3D polyline + * 16 = This is a 3D polygon mesh + * 32 = The polygon mesh is closed in the N direction + * 64 = The polyline is a polyface mesh + * 128 = The linetype pattern is generated continuously around the vertices of this polyline + */ + unsigned flags; std::vector vertices; public: - PolyLine() {} + PolyLine(unsigned flags) : flags(flags) {} const std::vector &getVertices() const {return vertices;} // From Entity - void addVertex(const cb::Vector3D &v) {vertices.push_back(v);} + void addVertex(const cb::Vector3D &v, double weight = 0.0) {vertices.push_back(v);} + unsigned getFlags() const {return flags;} type_t getType() const {return DXF_POLYLINE;} }; } diff --git a/src/dxf/Reader.cpp b/src/dxf/Reader.cpp index 7fd03916..471a9ee2 100644 --- a/src/dxf/Reader.cpp +++ b/src/dxf/Reader.cpp @@ -85,7 +85,7 @@ void Reader::addCircle(const DL_CircleData &circle) { void Reader::addPolyline(const DL_PolylineData &polyline) { if (!entity.isNull()) THROW("DXF Already in DXF entity"); - addEntity(entity = new PolyLine); + addEntity(entity = new PolyLine(polyline.flags)); } @@ -97,12 +97,12 @@ void Reader::addVertex(const DL_VertexData &vertex) { void Reader::addSpline(const DL_SplineData &spline) { if (!entity.isNull()) THROW("DXF Already in DXF entity"); - addEntity(entity = new Spline(spline.degree)); + addEntity(entity = new Spline(spline.degree, spline.flags)); } void Reader::addControlPoint(const DL_ControlPointData &ctrlPt) { - entity->addVertex(cb::Vector3D(ctrlPt.x, ctrlPt.y, ctrlPt.z)); + entity->addVertex(cb::Vector3D(ctrlPt.x, ctrlPt.y, ctrlPt.z), ctrlPt.w); } diff --git a/src/dxf/Spline.h b/src/dxf/Spline.h index 5eb80d7c..e749ab7d 100644 --- a/src/dxf/Spline.h +++ b/src/dxf/Spline.h @@ -28,19 +28,30 @@ namespace DXF { class Spline : public Entity { unsigned degree; + /* Spline flag (bit coded): + * 1 = Closed spline + * 2 = Periodic spline + * 4 = Rational spline + * 8 = Planar + * 16 = Linear (planar bit is also set) + */ + unsigned flags; std::vector ctrlPts; + std::vector weights; std::vector knots; public: - Spline(unsigned degree) : degree(degree) {} + Spline(unsigned degree, unsigned flags) : degree(degree), flags(flags) {} unsigned getDegree() const {return degree;} + unsigned getFlags() const {return flags;} const std::vector &getControlPoints() const {return ctrlPts;} + const std::vector &getWeights() const {return weights;} const std::vector &getKnots() const {return knots;} // From Entity void addKnot(double k) {knots.push_back(k);} - void addVertex(const cb::Vector3D &v) {ctrlPts.push_back(v);} + void addVertex(const cb::Vector3D &v, double weight = 1.0) {ctrlPts.push_back(v); weights.push_back(weight);} type_t getType() const {return DXF_SPLINE;} }; } diff --git a/src/tplang/DXFModule.cpp b/src/tplang/DXFModule.cpp index da59d15a..52ef5b4a 100644 --- a/src/tplang/DXFModule.cpp +++ b/src/tplang/DXFModule.cpp @@ -110,6 +110,8 @@ void DXFModule::openCB(const js::Value &args, js::Sink &sink) { case DXF::Entity::DXF_POLYLINE: { const DXF::PolyLine &polyLine = dynamic_cast(entity); + /* TODO: expose other flags. */ + sink.insertBoolean("isClosed", (polyLine.getFlags()&(1<<0))!=0 ); const vector &vertices = polyLine.getVertices(); sink.insertList("vertices"); @@ -130,9 +132,15 @@ void DXFModule::openCB(const js::Value &args, js::Sink &sink) { const DXF::Spline &spline = dynamic_cast(entity); sink.insert("degree", spline.getDegree()); + sink.insertBoolean("isClosed", (spline.getFlags()&(1<<0))!=0 ); + sink.insertBoolean("isPeriodical", (spline.getFlags()&(1<<1))!=0 ); + sink.insertBoolean("isRational", (spline.getFlags()&(1<<2))!=0 ); + sink.insertBoolean("isPlanar", (spline.getFlags()&(1<<3))!=0 ); + sink.insertBoolean("isLinear", (spline.getFlags()&(1<<4))!=0 ); // Control points const vector &ctrlPts = spline.getControlPoints(); + const std::vector &weights = spline.getWeights(); sink.insertList("ctrlPts"); for (unsigned k = 0; k < ctrlPts.size(); k++) { @@ -140,6 +148,7 @@ void DXFModule::openCB(const js::Value &args, js::Sink &sink) { sink.insert("x", ctrlPts[k].x()); sink.insert("y", ctrlPts[k].y()); sink.insert("z", ctrlPts[k].z()); + sink.insert("w", weights[k]); sink.insert("type", DXF::Entity::DXF_POINT); sink.endDict(); } diff --git a/tpl_lib/dxf/dxf.tpl b/tpl_lib/dxf/dxf.tpl index 7bc8125c..e8480d1f 100644 --- a/tpl_lib/dxf/dxf.tpl +++ b/tpl_lib/dxf/dxf.tpl @@ -68,7 +68,7 @@ function dtoa(n, width) { function str(x) { - return (typeof x == 'string') ? x : JSON.stringify(x); + return (typeof x == 'string') ? x : JSON.stringify(x, null, ' '); } @@ -149,50 +149,161 @@ function bounds_center(bounds) { } -function quad_bezier(p, t) { - var c = [sqr(1 - t), 2 * (1 - t) * t, sqr(t)]; +function get_segment_index(knots, t, degree, is_closed) { + // find the spline segment for the t value provided - return { - x: c[0] * p[0].x + c[1] * p[1].x + c[2] * p[2].x, - y: c[0] * p[0].y + c[1] * p[1].y + c[2] * p[2].y + /* TODO: can this be unified? */ + if(is_closed) { + for(var segment = 0; segmentt) return segment-1; + } + } else { + for(var segment = degree; segment= knots[segment] && t <= knots[segment+1]) { + return segment; + } + } } + + throw new Error('segment not found'); } -function quad_bezier_length(p) { - var l = 0; - var v = p[0]; +function nurbs_build_verts(spl) { + var degree = spl.degree; + var npts = spl.ctrlPts.length; - for (var i = 1; i <= 100; i++) { - var u = quad_bezier(p, 0.01 * i); - l += distance2D(v, u); - v = u; + if(degree < 1) throw new Error('degree must be at least 1 (linear)'); + if(degree > (npts-1)) throw new Error('degree must be less than or equal to point count - 1'); + + // convert spl.ctrlPts to homogeneous coordinates + var verts = []; + if(spl.isClosed) { + for(var i=0; i knots[knots.length-1]) { + throw new Error('t out of bounds'); } + + if(spl.isClosed) { + var min = knots[degree+1]; + var max = knots[knots.length-degree-1]; + t = min + (max-min) * t; + } else { + var min = knots[degree]; + var max = knots[knots.length-degree]; + t = min + (max-min) * t; + } + + var segment = get_segment_index(knots, t, degree, spl.isClosed); + + /* Only deep copy the vertices that we are going to modify */ + var verts = []; + for(var j=segment; j>segment-degree-1; j--) { + var i = (npts+j)%npts; + verts[i] = []; + verts[i][0] = vertices[i][0]; + verts[i][1] = vertices[i][1]; + verts[i][2] = vertices[i][2]; + verts[i][3] = vertices[i][3]; + } + + for(var level=1; level<=degree+1; level++) { + // build level of the pyramid + for(var i=segment; i>segment-degree-1+level; i--) { + var klen = knots.length; + var vai_1 = knots[(i+klen)%klen]; + var vaip_r = knots[(i+degree+1-level+klen)%klen]; + var alpha = (t - vai_1) / (vaip_r - vai_1); + + // interpolate each component + var components = 4; // x, y, z, w + for(var j=0; j