From c8951c10cdcfbe699c3863c68af7b27f6abfe17d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Jan 2021 18:44:36 +0100 Subject: [PATCH] Generalized spline interpolation of DXF reader and provide a script binding (RBA::Utils). --- src/db/db/db.pro | 3 + src/db/db/dbUtils.cc | 260 ++++++++++++++++++++++++ src/db/db/dbUtils.h | 84 ++++++++ src/db/db/gsiDeclDbUtils.cc | 102 ++++++++++ src/db/unit_tests/dbRegionUtilsTests.cc | 38 +--- src/db/unit_tests/dbUtilsTests.cc | 143 +++++++++++++ src/db/unit_tests/unit_tests.pro | 1 + src/rba/unit_tests/rbaTests.cc | 1 + src/tl/tl/tlStringEx.h | 95 +++++++++ testdata/ruby/dbUtilsTests.rb | 79 +++++++ 10 files changed, 769 insertions(+), 37 deletions(-) create mode 100644 src/db/db/dbUtils.cc create mode 100644 src/db/db/dbUtils.h create mode 100644 src/db/db/gsiDeclDbUtils.cc create mode 100644 src/db/unit_tests/dbUtilsTests.cc create mode 100644 src/tl/tl/tlStringEx.h create mode 100644 testdata/ruby/dbUtilsTests.rb diff --git a/src/db/db/db.pro b/src/db/db/db.pro index e38ea7c5d..16f9df099 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -82,6 +82,7 @@ SOURCES = \ dbTilingProcessor.cc \ dbTrans.cc \ dbUserObject.cc \ + dbUtils.cc \ dbVector.cc \ dbWriter.cc \ dbWriterTools.cc \ @@ -122,6 +123,7 @@ SOURCES = \ gsiDeclDbText.cc \ gsiDeclDbTilingProcessor.cc \ gsiDeclDbTrans.cc \ + gsiDeclDbUtils.cc \ gsiDeclDbVector.cc \ gsiDeclDbLayoutDiff.cc \ gsiDeclDbGlyphs.cc \ @@ -288,6 +290,7 @@ HEADERS = \ dbTrans.h \ dbTypes.h \ dbUserObject.h \ + dbUtils.h \ dbVector.h \ dbWriter.h \ dbWriterTools.h \ diff --git a/src/db/db/dbUtils.cc b/src/db/db/dbUtils.cc new file mode 100644 index 000000000..db5ef75c5 --- /dev/null +++ b/src/db/db/dbUtils.cc @@ -0,0 +1,260 @@ +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbUtils.h" +#include "dbVector.h" +#include "tlException.h" + +namespace db +{ + +/* + +Rational B-Splines (NURBS) vs. non-rational B-Splines: + https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline + +De Boor algorithm for NURBS + https://github.com/caadxyz/DeBoorAlgorithmNurbs + +*/ + +static db::DPoint +b_spline_point (double x, const std::vector > &control_points, int p, const std::vector &t) +{ + int k = (int) (std::lower_bound (t.begin (), t.end (), x + 1e-6) - t.begin ()); + if (k <= p) { + return control_points.front ().first; + } else if (k > (int) control_points.size ()) { + return control_points.back ().first; + } + --k; + + std::vector d; + std::vector dw; + d.reserve(p + 1); + for (int j = 0; j <= p; ++j) { + double w = control_points[j + k - p].second; + d.push_back (control_points[j + k - p].first * w); + dw.push_back (w); + } + + for (int r = 1; r <= p; ++r) { + for (int j = p; j >= r; --j) { + double alpha = (x - t[j + k - p]) / (t[j + 1 + k - r] - t[j + k - p]); + d[j] = d[j] * alpha + (d[j - 1] - d[j - 1] * alpha); + dw[j] = dw[j] * alpha + dw[j - 1] * (1.0 - alpha); + } + } + + return d[p] * (1.0 / dw[p]); +} + +/** + * @brief Inserts new points into a sequence of points to refine the curve + * + * The idea is bisection of the segments until the desired degree of accuracy has been reached. + * + * @param control_points The control points + * @param curve_points The list of curve points which is going to be extended + * @param current_curve_point The iterator pointing to the current curve point + * @param t_start The t (curve parameter) value of the current curve point + * @param dt The current t interval + * @param degree The degree of the spline + * @param knots The knots + * @param sin_da The relative accuracy value implied by the circle resolution + * @param accu The desired absolute accuracy value + * + * New points are going to be inserted after current_curve_point and current_curve_point + 1 to achieve the + * required curvature. + */ +static void +spline_interpolate (std::list &curve_points, + typename std::list::iterator current_curve_point, + double t_start, + double dt, + const std::vector > &control_points, + int degree, + const std::vector &knots, + double sin_da, + double accu) +{ + std::list::iterator pm = current_curve_point; + ++pm; + std::list::iterator pe = pm; + ++pe; + + db::DPoint s1 = b_spline_point (t_start + 0.5 * dt, control_points, degree, knots); + db::DPoint s2 = b_spline_point (t_start + 1.5 * dt, control_points, degree, knots); + + db::DVector p1 (s1, *current_curve_point); + db::DVector p2 (*pm, s1); + double pl1 = p1.length(), pl2 = p2.length(); + + if (curve_points.size () < control_points.size () - degree - 1) { + + curve_points.insert (pm, s1); + spline_interpolate (curve_points, current_curve_point, t_start, dt * 0.5, control_points, degree, knots, sin_da, accu); + + curve_points.insert (pe, s2); + spline_interpolate (curve_points, pm, t_start + dt, dt * 0.5, control_points, degree, knots, sin_da, accu); + + } else { + + db::DVector q1 (s2, *pm); + db::DVector q2 (*pe, s2); + double ql1 = q1.length(), ql2 = q2.length(); + + db::DVector p (*pm, *current_curve_point); + db::DVector q (*pe, *pm); + double pl = p.length (), ql = q.length (); + + if (fabs (db::vprod (p, q)) > pl * ql * sin_da || + fabs (db::vprod (p1, p2)) > pl1 * pl2 * sin_da || + fabs (db::vprod (q1, q2)) > ql1 * ql2 * sin_da) { + + // angle between the segments is bigger than 2PI/n -> circle resolution is + // too small. Or: the angle between the new segments that we would introduce + // is also bigger (hence, the original segments may have a small angle but the + // curve "wiggles" when we increase the resolution) + + if (fabs (db::vprod (p1, p)) > pl * accu) { + + // In addition, the estimated accuracy is not good enough on the first segment: bisect this + // segment. + + curve_points.insert (pm, s1); + spline_interpolate (curve_points, current_curve_point, t_start, dt * 0.5, control_points, degree, knots, sin_da, accu); + + } + + if (fabs (db::vprod (q1, q)) > ql * accu) { + + // In addition, the estimated accuracy is not good enough on the first segment: bisect this + // segment. + + curve_points.insert (pe, s2); + spline_interpolate (curve_points, pm, t_start + dt, dt * 0.5, control_points, degree, knots, sin_da, accu); + + } + + } + + } +} + +static +std::list +do_spline_interpolation (const std::vector > &control_points, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) +{ + // TODO: this is quite inefficient + if (int (knots.size()) != int (control_points.size() + degree + 1)) { + throw tl::Exception (tl::to_string (tr ("Spline interpolation failed: mismatch between number of knots and points (#knots must be #points+degree+1)"))); + } + + if (int(knots.size ()) <= degree || control_points.empty () || degree <= 1) { + return std::list (); + } + + double t0 = knots [degree]; + double tn = knots [knots.size () - degree - 1]; + + std::list new_points; + new_points.push_back (control_points.front ().first); + + double dt = 0.5 * (tn - t0); + + for (double t = t0 + dt; t < tn + 1e-6; t += dt) { + db::DPoint s = b_spline_point (t, control_points, degree, knots); + new_points.push_back (s); + } + + spline_interpolate (new_points, new_points.begin (), t0, dt, control_points, degree, knots, relative_accuracy, absolute_accuracy); + + return new_points; +} + +template +std::list

+spline_interpolation (const std::vector > &control_points, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) +{ + std::vector > cp; + cp.reserve (control_points.size ()); + + for (size_t i = 0; i < control_points.size (); ++i) { + cp.push_back (std::make_pair (db::DPoint (control_points [i].first), control_points [i].second)); + } + + std::list result = do_spline_interpolation (cp, degree, knots, relative_accuracy, absolute_accuracy); + + std::list

ret; + for (std::list::const_iterator i = result.begin (); i != result.end (); ++i) { + ret.push_back (P (*i)); + } + return ret; +} + +template <> +std::list +spline_interpolation (const std::vector > &control_points, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) +{ + return do_spline_interpolation (control_points, degree, knots, relative_accuracy, absolute_accuracy); +} + +template +std::list

+spline_interpolation (const std::vector

&control_points, const std::vector &weights, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) +{ + std::vector > cp; + cp.reserve (control_points.size ()); + + for (size_t i = 0; i < control_points.size (); ++i) { + if (i >= weights.size ()) { + cp.push_back (std::make_pair (control_points [i], 1.0)); + } else { + cp.push_back (std::make_pair (control_points [i], weights [i])); + } + } + + return spline_interpolation (cp, degree, knots, relative_accuracy, absolute_accuracy); +} + +template +std::list

+spline_interpolation (const std::vector

&control_points, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) +{ + std::vector > cp; + cp.reserve (control_points.size ()); + + for (size_t i = 0; i < control_points.size (); ++i) { + cp.push_back (std::make_pair (control_points [i], 1.0)); + } + + return spline_interpolation (cp, degree, knots, relative_accuracy, absolute_accuracy); +} + +template std::list spline_interpolation (const std::vector &, int, const std::vector &, double, double); +template std::list spline_interpolation (const std::vector &, const std::vector &, int, const std::vector &, double, double); +template std::list spline_interpolation (const std::vector > &, int, const std::vector &, double, double); + +template std::list spline_interpolation (const std::vector &, int, const std::vector &, double, double); +template std::list spline_interpolation (const std::vector &, const std::vector &, int, const std::vector &, double, double); + +} diff --git a/src/db/db/dbUtils.h b/src/db/db/dbUtils.h new file mode 100644 index 000000000..30b6b4d03 --- /dev/null +++ b/src/db/db/dbUtils.h @@ -0,0 +1,84 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbUtils +#define HDR_dbUtils + +#include "dbCommon.h" +#include "dbPoint.h" + +#include +#include + +namespace db +{ + +/** + * @brief Provides a Spline curve with adjustable accuracy + * + * This function computes the Spline curve for a given set of control points (point, weight), degree and knots. + * + * The knot vector needs to be padded and it's size must fulfill the condition: + * + * @code + * knots.size == control_points.size + degree + 1 + * @/code + * + * The accuracy parameters allow tuing the resolution of the curve to target a specific approximation quality. + * "relative_accuracy" gives the accuracy relative to the local curvature radius, "absolute" accuracy gives the + * absolute accuracy. "accuracy" is the allowed deviation of polygon approximation from the ideal curve. + * The computed curve should meet at least one of the accurcay criteria. Setting both limits to a very small + * value will result in long run times and a large number of points returned. + * + * This function supports both rational splines (NURBS) and non-rational splines. The latter use weights of + * 1.0 for each point. + * + * The return value is a list of points forming a path which approximates the spline curve. + */ +template +DB_PUBLIC std::list

+spline_interpolation (const std::vector > &control_points, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy); + +/** + * @brief A convencience version of the previous function + * + * This version takes separate vectors for point and weights for the control points. + */ +template +DB_PUBLIC std::list

+spline_interpolation (const std::vector

&control_points, const std::vector &weights, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy); + +/** + * @brief A convencience version of the previous function + * + * This version provides non-rational splines and does not take a weight vector. + */ +template +DB_PUBLIC std::list

+spline_interpolation (const std::vector

&control_points, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy); + +} + +#endif + + diff --git a/src/db/db/gsiDeclDbUtils.cc b/src/db/db/gsiDeclDbUtils.cc new file mode 100644 index 000000000..97b2fa359 --- /dev/null +++ b/src/db/db/gsiDeclDbUtils.cc @@ -0,0 +1,102 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "gsiDecl.h" +#include "dbUtils.h" + +namespace db +{ + +/** + * @brief A dummy class providing the namespace. + */ +struct UtilsDummy +{ + static std::list spi1 (const std::vector &control_points, const std::vector &weight, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) + { + return db::spline_interpolation (control_points, weight, degree, knots, relative_accuracy, absolute_accuracy); + } + + static std::list spi2 (const std::vector &control_points, const std::vector &weight, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) + { + return db::spline_interpolation (control_points, weight, degree, knots, relative_accuracy, absolute_accuracy); + } + + static std::list spi3 (const std::vector &control_points, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) + { + return db::spline_interpolation (control_points, degree, knots, relative_accuracy, absolute_accuracy); + } + + static std::list spi4 (const std::vector &control_points, int degree, const std::vector &knots, double relative_accuracy, double absolute_accuracy) + { + return db::spline_interpolation (control_points, degree, knots, relative_accuracy, absolute_accuracy); + } +}; + +} + +namespace gsi +{ + + Class decl_dbUtils ("db", "Utils", + gsi::method ("spline_interpolation", &db::UtilsDummy::spi1, gsi::arg ("control_points"), gsi::arg ("weights"), gsi::arg ("degree"), gsi::arg ("knots"), gsi::arg ("relative_accuracy"), gsi::arg ("absolute_accuracy"), + "@brief This function computes the Spline curve for a given set of control points (point, weight), degree and knots.\n" + "\n" + "The knot vector needs to be padded and it's size must fulfill the condition:\n" + "\n" + "@code\n" + " knots.size == control_points.size + degree + 1\n" + "@/code\n" + "\n" + "The accuracy parameters allow tuing the resolution of the curve to target a specific approximation quality.\n" + "\"relative_accuracy\" gives the accuracy relative to the local curvature radius, \"absolute\" accuracy gives the\n" + "absolute accuracy. \"accuracy\" is the allowed deviation of polygon approximation from the ideal curve.\n" + "The computed curve should meet at least one of the accurcay criteria. Setting both limits to a very small\n" + "value will result in long run times and a large number of points returned.\n" + "\n" + "This function supports both rational splines (NURBS) and non-rational splines. The latter use weights of\n" + "1.0 for each point.\n" + "\n" + "The return value is a list of points forming a path which approximates the spline curve.\n" + ) + + gsi::method ("spline_interpolation", &db::UtilsDummy::spi2, gsi::arg ("control_points"), gsi::arg ("weights"), gsi::arg ("degree"), gsi::arg ("knots"), gsi::arg ("relative_accuracy"), gsi::arg ("absolute_accuracy"), + "@brief This function computes the Spline curve for a given set of control points (point, weight), degree and knots.\n" + "\n" + "This is the version for integer-coordinate points." + ) + + gsi::method ("spline_interpolation", &db::UtilsDummy::spi3, gsi::arg ("control_points"), gsi::arg ("degree"), gsi::arg ("knots"), gsi::arg ("relative_accuracy"), gsi::arg ("absolute_accuracy"), + "@brief This function computes the Spline curve for a given set of control points (point, weight), degree and knots.\n" + "\n" + "This is the version for non-rational splines. It lacks the weight vector." + ) + + gsi::method ("spline_interpolation", &db::UtilsDummy::spi4, gsi::arg ("control_points"), gsi::arg ("degree"), gsi::arg ("knots"), gsi::arg ("relative_accuracy"), gsi::arg ("absolute_accuracy"), + "@brief This function computes the Spline curve for a given set of control points (point, weight), degree and knots.\n" + "\n" + "This is the version for integer-coordinate points for non-rational splines." + ), + "@brief This namespace provides a collection of utility functions\n" + "\n" + "This class has been introduced in version 0.27." + ); + +} diff --git a/src/db/unit_tests/dbRegionUtilsTests.cc b/src/db/unit_tests/dbRegionUtilsTests.cc index 5e161b272..c27a41e3d 100644 --- a/src/db/unit_tests/dbRegionUtilsTests.cc +++ b/src/db/unit_tests/dbRegionUtilsTests.cc @@ -23,46 +23,10 @@ #include "tlUnitTest.h" -#include "tlString.h" +#include "tlStringEx.h" #include "dbRegionUtils.h" -namespace tl -{ - -template -std::string to_string (Iter b, Iter e) -{ - std::string res; - for (Iter i = b; i != e; ++i) { - if (i != b) { - res += ","; - } - res += tl::to_string (*i); - } - return res; -} - -template -std::string to_string (const std::vector &v) -{ - return to_string (v.begin (), v.end ()); -} - -template -std::string to_string (const std::list &v) -{ - return to_string (v.begin (), v.end ()); -} - -template -std::string to_string (const std::set &v) -{ - return to_string (v.begin (), v.end ()); -} - -} - TEST(1_SimpleLShape) { std::set ep; diff --git a/src/db/unit_tests/dbUtilsTests.cc b/src/db/unit_tests/dbUtilsTests.cc new file mode 100644 index 000000000..8392775c6 --- /dev/null +++ b/src/db/unit_tests/dbUtilsTests.cc @@ -0,0 +1,143 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbUtils.h" +#include "tlUnitTest.h" +#include "tlStringEx.h" + +#include + +TEST(1) +{ + std::list pts; + + std::vector > cp; + cp.push_back (std::make_pair (db::DPoint (-1, 0), 1)); + cp.push_back (std::make_pair (db::DPoint (-1, 1), sqrt (0.5))); + cp.push_back (std::make_pair (db::DPoint (0, 1), 1)); + + std::vector k; + k.push_back (0.0); + k.push_back (0.0); + k.push_back (0.0); + k.push_back (1.0); + k.push_back (1.0); + k.push_back (1.0); + + pts = db::spline_interpolation (cp, 2, k, 0.01, 0.01); + + EXPECT_EQ (tl::to_string (pts), + "-1,0," + "-0.983305368417,0.181963052412," + "-0.929788301062,0.368094709562," + "-0.836995511219,0.547209753385," + "-0.707106781187,0.707106781187," + "-0.547209753385,0.836995511219," + "-0.368094709562,0.929788301062," + "-0.181963052412,0.983305368417," + "0,1" + ); + + for (std::list::iterator i = pts.begin (); i != pts.end (); ++i) { + EXPECT_EQ (fabs (i->double_distance () - 1.0) < 1e-10, true); + } + + pts = db::spline_interpolation (cp, 2, k, 0.001, 0.001); + + EXPECT_EQ (pts.size (), size_t (33)); + + for (std::list::iterator i = pts.begin (); i != pts.end (); ++i) { + EXPECT_EQ (fabs (i->double_distance () - 1.0) < 1e-10, true); + } +} + +TEST(2) +{ + std::list pts; + + std::vector cp; + cp.push_back (db::DPoint (-1, 0)); + cp.push_back (db::DPoint (-1, 1)); + cp.push_back (db::DPoint (0, 1)); + + std::vector w; + w.push_back (1); + w.push_back (sqrt (0.5)); + w.push_back (1); + + std::vector k; + k.push_back (0.0); + k.push_back (0.0); + k.push_back (0.0); + k.push_back (1.0); + k.push_back (1.0); + k.push_back (1.0); + + pts = db::spline_interpolation (cp, w, 2, k, 0.01, 0.01); + + EXPECT_EQ (tl::to_string (pts), + "-1,0," + "-0.983305368417,0.181963052412," + "-0.929788301062,0.368094709562," + "-0.836995511219,0.547209753385," + "-0.707106781187,0.707106781187," + "-0.547209753385,0.836995511219," + "-0.368094709562,0.929788301062," + "-0.181963052412,0.983305368417," + "0,1" + ); +} + +TEST(3) +{ + std::list pts; + + std::vector cp; + cp.push_back (db::DPoint (-1, 0)); + cp.push_back (db::DPoint (-1, 1)); + cp.push_back (db::DPoint (0, 1)); + + std::vector k; + k.push_back (0.0); + k.push_back (0.0); + k.push_back (0.0); + k.push_back (1.0); + k.push_back (1.0); + k.push_back (1.0); + + // non-rational spline + pts = db::spline_interpolation (cp, 2, k, 0.01, 0.01); + + EXPECT_EQ (tl::to_string (pts), + "-1,0," + "-0.984375,0.234375," + "-0.9375,0.4375," + "-0.859375,0.609375," + "-0.75,0.75," + "-0.609375,0.859375," + "-0.4375,0.9375," + "-0.234375,0.984375," + "0,1" + ); +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 5ff79a744..ee48d7549 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ dbCompoundOperationTests.cc \ dbRegionUtilsTests.cc \ + dbUtilsTests.cc \ dbWriterTools.cc \ dbLoadLayoutOptionsTests.cc \ dbSaveLayoutOptionsTests.cc \ diff --git a/src/rba/unit_tests/rbaTests.cc b/src/rba/unit_tests/rbaTests.cc index c9b83a6f9..9ac96cbf9 100644 --- a/src/rba/unit_tests/rbaTests.cc +++ b/src/rba/unit_tests/rbaTests.cc @@ -132,6 +132,7 @@ RUBYTEST (dbTextsTest, "dbTextsTest.rb") RUBYTEST (dbTilingProcessorTest, "dbTilingProcessorTest.rb") RUBYTEST (dbTransTest, "dbTransTest.rb") RUBYTEST (dbVectorTest, "dbVectorTest.rb") +RUBYTEST (dbUtilsTests, "dbUtilsTests.rb") RUBYTEST (edtTest, "edtTest.rb") RUBYTEST (extNetTracer, "extNetTracer.rb") RUBYTEST (imgObject, "imgObject.rb") diff --git a/src/tl/tl/tlStringEx.h b/src/tl/tl/tlStringEx.h new file mode 100644 index 000000000..8c325ddfb --- /dev/null +++ b/src/tl/tl/tlStringEx.h @@ -0,0 +1,95 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_tlStringEx +#define HDR_tlStringEx + +#include "tlString.h" + +#include +#include +#include +#include +#include +#include + +namespace tl +{ + +template +std::string to_string (Iter b, Iter e) +{ + std::string res; + for (Iter i = b; i != e; ++i) { + if (i != b) { + res += ","; + } + res += tl::to_string (*i); + } + return res; +} + +template +std::string to_string (const std::pair &p) +{ + return to_string (p.first) + "," + to_string (p.second); +} + +template +std::string to_string (const std::vector &v) +{ + return to_string (v.begin (), v.end ()); +} + +template +std::string to_string (const std::list &v) +{ + return to_string (v.begin (), v.end ()); +} + +template +std::string to_string (const std::set &v) +{ + return to_string (v.begin (), v.end ()); +} + +template +std::string to_string (const std::map &v) +{ + return to_string (v.begin (), v.end ()); +} + +template +std::string to_string (const std::unordered_set &v) +{ + return to_string (v.begin (), v.end ()); +} + +template +std::string to_string (const std::unordered_map &v) +{ + return to_string (v.begin (), v.end ()); +} + +} + +#endif diff --git a/testdata/ruby/dbUtilsTests.rb b/testdata/ruby/dbUtilsTests.rb new file mode 100644 index 000000000..4ad4838cf --- /dev/null +++ b/testdata/ruby/dbUtilsTests.rb @@ -0,0 +1,79 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2021 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBUtils_TestClass < TestBase + + def test_1 + + pts = [ RBA::DPoint::new(-1, 0), RBA::DPoint::new(-1, 1), RBA::DPoint::new(0, 1) ] + weights = [ 1, Math::sqrt(0.5), 1 ] + k = [ 0, 0, 0, 1, 1, 1 ] + + pts = RBA::Utils::spline_interpolation(pts, weights, 2, k, 0.01, 0.01) + + assert_equal(pts.collect(&:to_s).join(","), "-1,0,-0.983305368417,0.181963052412,-0.929788301062,0.368094709562,-0.836995511219,0.547209753385,-0.707106781187,0.707106781187,-0.547209753385,0.836995511219,-0.368094709562,0.929788301062,-0.181963052412,0.983305368417,0,1") + assert_equal(pts.collect(&:abs).collect { |d| "%.12g" % d }.join(","), "1,1,1,1,1,1,1,1,1") + + end + + def test_2 + + pts = [ RBA::Point::new(-1000, 0), RBA::Point::new(-1000, 1000), RBA::Point::new(0, 1000) ] + weights = [ 1, Math::sqrt(0.5), 1] + k = [ 0, 0, 0, 1, 1, 1 ] + + pts = RBA::Utils::spline_interpolation(pts, weights, 2, k, 0.01, 10) + + assert_equal(pts.collect(&:to_s).join(","), "-1000,0,-983,182,-930,368,-837,547,-707,707,-547,837,-368,930,-182,983,0,1000") + assert_equal(pts.collect(&:abs).collect { |d| "%.0f" % d }.join(","), "1000,1000,1000,1000,1000,1000,1000,1000,1000") + + end + + def test_3 + + pts = [ RBA::DPoint::new(-1, 0), RBA::DPoint::new(-1, 1), RBA::DPoint::new(0, 1) ] + k = [ 0, 0, 0, 1, 1, 1 ] + + pts = RBA::Utils::spline_interpolation(pts, 2, k, 0.01, 0.01) + + assert_equal(pts.collect(&:to_s).join(","), "-1,0,-0.984375,0.234375,-0.9375,0.4375,-0.859375,0.609375,-0.75,0.75,-0.609375,0.859375,-0.4375,0.9375,-0.234375,0.984375,0,1") + + end + + def test_4 + + pts = [ RBA::Point::new(-1000, 0), RBA::Point::new(-1000, 1000), RBA::Point::new(0, 1000) ] + k = [ 0, 0, 0, 1, 1, 1 ] + + pts = RBA::Utils::spline_interpolation(pts, 2, k, 0.01, 10) + + assert_equal(pts.collect(&:to_s).join(","), "-1000,0,-984,234,-938,438,-859,609,-750,750,-609,859,-438,938,-234,984,0,1000") + assert_equal(pts.collect(&:abs).collect { |d| "%.0f" % d }.join(","), "1000,1011,1035,1053,1061,1053,1035,1011,1000") + + end + +end + +load("test_epilogue.rb")