Skip to content

Instantly share code, notes, and snippets.

@royshil
Last active October 7, 2019 12:21
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save royshil/8025583 to your computer and use it in GitHub Desktop.
Save royshil/8025583 to your computer and use it in GitHub Desktop.
Simple 2D NURBS renderer for OpenCV, reading a DXF file.

To build use something along the lines of

g++ NRUBSOpenCV.cpp -o NRUBSOpenCV -I/usr/local/include -I/usr/local/include/eigen3 -L/usr/local/lib -lopencv_core -lopencv_highgui -lopencv_imgproc -ldxflib
/*
* Simple 2D NURBS renderer for OpenCV, reading DXF files
*
* The MIT License (MIT)
*
* Copyright (c) 2013 Roy Shilkrot
*
* Updated: Nov 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <dxflib/dl_dxf.h>
#include <dxflib/dl_creationadapter.h>
#include <Eigen/Eigen>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <numeric>
#include <vector>
#include <iostream>
#include <iterator>
#include <functional>
using namespace std;
/**
* from: http://en.wikipedia.org/wiki/NURBS
* and: http://web.cs.wpi.edu/~matt/courses/cs563/talks/nurbs.html
*
* This is the main NURBS rendering implementation.
*/
struct MyNURBS {
typedef Eigen::Vector3f Vec3T;
int deg;
vector<Vec3T> P;
vector<float> k;
vector<float> w;
float f(const int i, const int n, const float u) const {
const float denom = k[i + n] - k[i];
if (denom == 0) {
return 0.0f;
}
return (u - k[i]) / denom;
}
float g(const int i, const int n, const float u) const {
const float denom = k[i + n + 1] - k[i];
if (denom == 0) {
return 0.0f;
}
return (k[i + n + 1] - u) / denom;
}
float N(const int i, const int n, const float u) const {
if (n == 0) {
const float Ninu = ((k[i] <= u && u <= k[i + 1])) ? 1 : 0;
return Ninu;
} else {
const float Nin1u = N(i, n - 1, u);
const float Ni1n1u = N(i + 1, n - 1, u);
const float a = (Nin1u != 0.0f) ? f(i, n, u) * Nin1u : 0.0f;
const float b = (Ni1n1u != 0.0f) ? g(i, n, u) * Ni1n1u : 0.0f;
const float Ninu = a + b;
return Ninu;
}
}
/**
* Evaluate the NURBS over a number of segments and return the curve points
*/
vector<Vec3T> evaluate(const int num_segments = 10) {
if (w.size() <= 0) {
w.resize(P.size(), 1.0); //uniform weights if they were not specified
}
vector<Vec3T> C;
const float step = (k.back() - k.front()) / static_cast<float>(num_segments);
for (float u = k.front(); u <= k.back(); u += step) {
Vec3T C_u = Vec3T::Zero();
vector<float> Nku;
for (int j = 0; j < P.size(); ++j) { //sum(j = 0, n){w_j * N_j,k(u)}
Nku.push_back(w[j] * N(j, deg, u));
}
const float denom = std::accumulate(Nku.begin(), Nku.end(), 0.0f);
for (int i = 0; i < P.size(); ++i) {
if (Nku[i] != 0.0f && denom != 0.0f) {
//R_i,k(u) = w_i * N_i,k(u) / sum(j = 0, n){w_j * N_j,k(u)}
float R_iku = Nku[i] / denom;
if (R_iku != 0.0f) {
C_u += P[i] * R_iku;
}
}
}
C.push_back(C_u);
}
return C;
}
void draw(cv::Mat& img, const cv::Scalar s = cv::Scalar(255), const int num_segments = 10) {
vector<Vec3T> C = evaluate(num_segments);
//draw curve
for (int i = 1; i < C.size(); ++i) {
cv::line(img, *(cv::Point2f*) &C[i], *(cv::Point2f*) &C[i - 1], s, 1);
}
//control points
for (int i = 0; i < P.size(); ++i) {
const Vec3T v = P[i];
cv::circle(img, *(cv::Point2f*) &(v), 2, cv::Scalar(0, 255, 255), 1);
}
}
};
class MyDXFFilter: public DL_CreationAdapter {
public:
virtual void addSpline(const DL_SplineData& s) {
cerr << "spline " << s.degree << " "
<< s.flags << " "
<< s.nControl << " "
<< s.nKnots << endl;
nurbs.push_back(MyNURBS());
nurbs.back().deg = s.degree;
}
virtual void addControlPoint(const DL_ControlPointData& cp) {
cerr << "ctrl pt " << cp.x << " " << cp.y << " " << cp.z << endl;
nurbs.back().P.push_back(Eigen::Vector3f(cp.x, cp.y, cp.z));
}
virtual void addKnot(const DL_KnotData& k) {
cerr << "knot " << k.k << endl;
nurbs.back().k.push_back(k.k);
}
vector<MyNURBS> nurbs;
private:
};
int main(int argc, char** argv) {
cv::Mat tmp;
tmp.create(512, 512, CV_8UC3);
if (argc < 2) {
cerr << "Please provide DXF filename to process." << endl;
return 1;
}
const string filename(argv[1]);
MyDXFFilter f;
DL_Dxf dxf;
if (!dxf.in(filename, &f)) {
cerr << filename << " could not be opened." << endl;
return 1;
} else {
cout << "loaded " << filename << endl;
}
for (int i = 0; i < f.nurbs.size(); ++i) {
f.nurbs[i].draw(tmp, cv::Scalar(255), 50);
}
cv::imshow("tmp", tmp);
cv::waitKey();
}
@Napoleon-BlownApart
Copy link

Napoleon-BlownApart commented Dec 29, 2017

Thanks for the sample code. I was able to build it in Xcode after installing dxflib and eigen3 via homebrew.
However, I cannot find a dxf file that will render anything visible. I imagine this may have something to do with DXF versions?
Would it be possible to add a sample image to this GIST?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment