summaryrefslogtreecommitdiffstats
path: root/src/toys/ineaa.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/toys/ineaa.cpp')
-rw-r--r--src/toys/ineaa.cpp655
1 files changed, 655 insertions, 0 deletions
diff --git a/src/toys/ineaa.cpp b/src/toys/ineaa.cpp
new file mode 100644
index 0000000..75b9d41
--- /dev/null
+++ b/src/toys/ineaa.cpp
@@ -0,0 +1,655 @@
+#include <2geom/convex-hull.h>
+
+#include <toys/path-cairo.h>
+#include <toys/toy-framework-2.h>
+#include <2geom/d2.h>
+#include <2geom/geom.h>
+#include <2geom/numeric/linear_system.h>
+
+#include <aa.h>
+#include <complex>
+#include <algorithm>
+#include <optional>
+
+using std::vector;
+using namespace Geom;
+using namespace std;
+
+typedef std::complex<AAF> CAAF;
+
+struct PtLexCmp{
+ bool operator()(const Point &a, const Point &b) {
+ return (a[0] < b[0]) || ((a[0] == b[0]) and (a[1] < b[1]));
+ }
+};
+
+// draw ax + by + c = 0
+void draw_line_in_rect(cairo_t*cr, Rect &r, Point n, double c) {
+ std::optional<Geom::LineSegment> ls =
+ rect_line_intersect(r, Line::fromNormalDistance(n, c));
+
+ if(ls) {
+ cairo_move_to(cr, (*ls)[0]);
+ cairo_line_to(cr, (*ls)[1]);
+ cairo_stroke(cr);
+
+ }
+}
+
+void fill_line_in_rect(cairo_t*cr, Rect &r, Point n, double c) {
+ ConvexHull ch;
+
+ std::optional<Geom::LineSegment> ls =
+ rect_line_intersect(r, Line::fromNormalDistance(n, c));
+
+ if(ls) {
+ ch.boundary.push_back((*ls)[0]);
+ ch.boundary.push_back((*ls)[1]);
+ }
+ for(int i = 0; i < 4; i++) {
+ Point p = r.corner(i);
+ if(dot(n,p) < c) {
+ ch.boundary.push_back(p);
+ }
+ }
+ ch.graham();
+ cairo_convex_hull(cr, ch.boundary);
+}
+
+OptRect tighten(Rect &r, Point n, Interval lu) {
+ vector<Geom::Point> result;
+ Point resultp;
+ for(int i = 0; i < 4; i++) {
+ Point cnr = r.corner(i);
+ double z = dot(cnr, n);
+ if((z > lu[0]) and (z < lu[1]))
+ result.push_back(cnr);
+ }
+ for(int i = 0; i < 2; i++) {
+ double c = lu[i];
+
+ std::optional<Geom::LineSegment> ls =
+ rect_line_intersect(r, Line::fromNormalDistance(n, c));
+
+ if(ls) {
+ result.push_back((*ls)[0]);
+ result.push_back((*ls)[1]);
+ }
+ }
+ if(result.size() < 2)
+ return OptRect();
+ Rect nr(result[0], result[1]);
+ for(size_t i = 2; i < result.size(); i++) {
+ nr.expandTo(result[i]);
+ }
+ return intersect(nr, r);
+}
+
+AAF ls_sample_based(AAF x, vector<Point> pts) {
+ NL::Matrix m(pts.size(), 2);
+ NL::Vector v(pts.size());
+ NL::LinearSystem ls(m, v);
+
+ m.set_all(0);
+ v.set_all(0);
+ for (unsigned int k = 0; k < pts.size(); ++k)
+ {
+ m(k,0) += pts[k][0];
+ m(k,1) += 1;
+ //std::cout << pts[k] << " ";
+
+ v[k] += pts[k][1];
+ //v[1] += pts[k][1];
+ //v[2] += y2;
+ }
+
+ ls.SV_solve();
+
+ double A = ls.solution()[0];
+ double B = ls.solution()[1];
+ // Ax + B = y
+ Interval bnd(0,0);
+ for (unsigned int k = 0; k < pts.size(); ++k)
+ {
+ bnd.extendTo(A*pts[k][0]+B - pts[k][1]);
+ }
+ //std::cout << A << "," << B << std::endl;
+ return AAF(x, A, B, bnd.extent(),
+ x.special);
+}
+
+AAF md_sample_based(AAF x, vector<Point> pts) {
+ Geom::ConvexHull ch1(pts);
+ Point a, b, c;
+ ch1.narrowest_diameter(a, b, c);
+ Point db = c-b;
+ double A = db[1]/db[0];
+ Point aa = db*(dot(db, a-b)/dot(db,db))+b;
+ Point mid = (a+aa)/2;
+ double B = mid[1] - A*mid[0];
+ double dB = (a[1] - A*a[0]) - B;
+ // Ax + B = y
+ //std::cout << A << "," << B << std::endl;
+ return AAF(x, A, B, dB,
+ x.special);
+}
+
+AAF atan_sample_based(AAF x) {
+ interval ab(x);
+ const double a = ab.min(); // [a,b] is our interval
+ const double b = ab.max();
+
+ const double ea = atan(a);
+ const double eb = atan(b);
+ vector<Point> pts;
+ pts.push_back(Point(a,ea));
+ pts.push_back(Point(b,eb));
+ const double alpha = (eb-ea)/(b-a);
+ double xs = sqrt(1/alpha-1);
+ if((a < xs) and (xs < b))
+ pts.push_back(Point(xs,atan(xs)));
+ xs = -xs;
+ if((a < xs) and (xs < b))
+ pts.push_back(Point(xs,atan(xs)));
+
+ return md_sample_based(x, pts);
+}
+
+AAF log_sample_based(AAF x) {
+ interval ab(x);
+ const double a = ab.min(); // [a,b] is our interval
+ const double b = ab.max();
+ AAF_TYPE type;
+ if(a > 0)
+ type = AAF_TYPE_AFFINE;
+ else if(b < 0) { // no point in continuing
+ type = AAF_TYPE_NAN;
+ return AAF(type);
+ }
+ else if(a <= 0) { // undefined, can we do better?
+ type = (AAF_TYPE)(AAF_TYPE_AFFINE | AAF_TYPE_NAN);
+ return AAF(type);
+ // perhaps we should make a = 0+eps and try to continue?
+ }
+
+ const double ea = log(a);
+ const double eb = log(b);
+ vector<Point> pts;
+ pts.push_back(Point(a,ea));
+ pts.push_back(Point(b,eb));
+ const double alpha = (eb-ea)/(b-a);
+ // dlog(xs) = alpha
+ double xs = 1/(alpha);
+ if((a < xs) and (xs < b))
+ pts.push_back(Point(xs,log(xs)));
+
+ return md_sample_based(x, pts);
+}
+
+AAF exp_sample_based(AAF x) {
+ interval ab(x);
+ const double a = ab.min(); // [a,b] is our interval
+ const double b = ab.max();
+
+ const double ea = exp(a);
+ const double eb = exp(b);
+ vector<Point> pts;
+ pts.push_back(Point(a,ea));
+ pts.push_back(Point(b,eb));
+ const double alpha = (eb-ea)/(b-a);
+ // dexp(xs) = alpha
+ double xs = log(alpha);
+ if((a < xs) and (xs < b))
+ pts.push_back(Point(xs,exp(xs)));
+
+ return md_sample_based(x, pts);
+}
+
+double
+desy_lambert_W(double x) {
+ if (x <= 500.0) {
+ double lx1 = log(x + 1.0);
+ return 0.665 * (1 + 0.0195 * lx1) * lx1 + 0.04;
+ }
+ return log(x - 4.0) - (1.0 - 1.0/log(x)) * log(log(x));
+}
+
+double lambertW(double x, double prec = 1E-12, int maxiters = 100) {
+ double w = desy_lambert_W(x);
+ const double e = exp(1);
+ for(int i = 0; i < maxiters; i++) {
+ double we = w * pow(e,w);
+ double w1e = (w + 1) * pow(e,w);
+ if( prec > abs((x - we) / w1e))
+ return w;
+ w -= (we - x) / (w1e - (w+2) * (we-x) / (2*w+2));
+ }
+ //raise ValueError("W doesn't converge fast enough for abs(z) = %f" % abs(x))
+ return 0./0.;
+}
+
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_min.h>
+
+typedef struct{
+ double a, b;
+} param_W;
+
+double fn1 (double x, void * params)
+{
+ param_W *pw = (param_W*)params;
+ return (pw->a*x+pw->b) - lambertW(x);
+}
+
+double optimise(void * params, double a, double b) {
+ int status;
+ //param_W *pw = (param_W*)params;
+ int iter = 0, max_iter = 100;
+ double m = (a+b)/2;
+ gsl_function F;
+
+ F.function = &fn1;
+ F.params = params;
+
+ const gsl_min_fminimizer_type *T = gsl_min_fminimizer_brent;
+ gsl_min_fminimizer *s = gsl_min_fminimizer_alloc (T);
+ if(a+1e-10 >= b) return m;
+#if 0
+ cout << a << " " << b << " " << m << endl;
+ cout << "fn:" << fn1(a, params) << " " << fn1(b, params) << " " << fn1(m, params) << endl;
+ cout << "fn:" << (pw->a*a+pw->b) << " " << (pw->a*b+pw->b) << " " << (pw->a*m+pw->b) << endl;
+#endif
+ gsl_min_fminimizer_set (s, &F, m, a, b);
+ do
+ {
+ iter++;
+ status = gsl_min_fminimizer_iterate (s);
+
+ m = gsl_min_fminimizer_x_minimum (s);
+ a = gsl_min_fminimizer_x_lower (s);
+ b = gsl_min_fminimizer_x_upper (s);
+
+ status
+ = gsl_min_test_interval (a, b, 0.001, 0.0);
+
+ }
+ while (status == GSL_CONTINUE && iter < max_iter);
+
+ gsl_min_fminimizer_free (s);
+
+ return m;
+}
+
+
+AAF W_sample_based(AAF x) {
+ interval ab(x);
+ const double a = ab.min(); // [a,b] is our interval
+ const double b = ab.max();
+ const double e = exp(1);
+ AAF_TYPE type;
+ if(a >= -1./e)
+ type = AAF_TYPE_AFFINE;
+ else if(b < 0) { // no point in continuing
+ type = AAF_TYPE_NAN;
+ return AAF(type);
+ }
+ else if(a <= 0) { // undefined, can we do better?
+ type = (AAF_TYPE)(AAF_TYPE_AFFINE | AAF_TYPE_NAN);
+ return AAF(type);
+ // perhaps we should make a = 0+eps and try to continue?
+ }
+ const double ea = lambertW(a);
+ const double eb = lambertW(b);
+ vector<Point> pts;
+ pts.push_back(Point(a,ea));
+ pts.push_back(Point(b,eb));
+ const double alpha = (eb-ea)/(b-a);
+ // d(W(xs)) = alpha
+ // W =
+ param_W pw;
+ pw.a = alpha;
+ pw.b = ea - alpha*a;
+ if(a < b) {
+ double xs = optimise(&pw, a, b);
+ if((a < xs) and (xs < b))
+ pts.push_back(Point(xs,lambertW(xs)));
+ }
+ return md_sample_based(x, pts);
+}
+
+AAF pow_sample_based(AAF x, double p) {
+ interval ab(x);
+ const double a = ab.min(); // [a,b] is our interval
+ const double b = ab.max();
+ AAF_TYPE type;
+ if(floor(p) != p) {
+ if(a >= 0)
+ type = AAF_TYPE_AFFINE;
+ else if(b < 0) { // no point in continuing
+ type = AAF_TYPE_NAN;
+ return AAF(type);
+ }
+ else if(a <= 0) { // undefined, can we do better?
+ type = (AAF_TYPE)(AAF_TYPE_AFFINE | AAF_TYPE_NAN);
+ return AAF(type);
+ // perhaps we should make a = 0+eps and try to continue?
+ }
+ }
+ const double ea = pow(a, p);
+ const double eb = pow(b, p);
+ vector<Point> pts;
+ pts.push_back(Point(a,ea));
+ pts.push_back(Point(b,eb));
+ const double alpha = (eb-ea)/(b-a);
+ // d(xs^p) = alpha
+ // p xs^(p-1) = alpha
+ // xs = (alpha/p)^(1-p)
+ double xs = pow(alpha/p, 1./(p-1));
+ if((a < xs) and (xs < b))
+ pts.push_back(Point(xs,pow(xs, p)));
+ xs = -xs;
+ if((a < xs) and (xs < b))
+ pts.push_back(Point(xs,pow(xs, p)));
+
+ return md_sample_based(x, pts);
+}
+
+Point origin;
+AAF trial_eval(AAF x, AAF y) {
+ x = x-origin[0];
+ y = y-origin[1];
+ x = x/200;
+ y = y/200;
+ AAF x2 = pow_sample_based(x,2);
+ AAF y2 = pow_sample_based(y,2);
+ //return x2 + y2 - 1;
+ //return y - pow(x,3);
+ return y + W_sample_based(x);
+ //return y - pow_sample_based(x,2.5);
+ //return y - log_sample_based(x);
+ //return y - log(x);
+ //return -y + -exp_sample_based(x*log(x));
+ return -x + -exp_sample_based(y*log(y));
+ //return y - sqrt(sin(x));
+ //return sqrt(y)*x - sqrt(x) - y - 1;
+ //return y-1/x;
+ //return exp(x)-y;
+ //return sin(x)-y;
+ //return exp_sample_based(x)-y;
+ //return atan(x)-y;
+ //return atan_sample_based(x)-y;
+ //return atanh(x)-y;
+ //return x*y;
+ //return 4*x+3*y-1;
+ //return x*x + y*y - 1;
+ return pow_sample_based((x2 + y2), 2) - (x2-y2);
+ //return x*x-y;
+ //return (x*x*x-y*x)*sin(x) + (x-y*y)*cos(y)-0.5;
+}
+
+AAF xaxis(AAF x, AAF y) {
+ (void)x;
+ y = y-origin[1];
+ y = y/200;
+ return y;
+}
+
+AAF yaxis(AAF x, AAF y) {
+ (void)y;
+ x = x-origin[0];
+ x = x/200;
+ return x;
+}
+
+class ConvexTest: public Toy {
+public:
+ PointSetHandle test_window;
+ PointSetHandle samples;
+ PointHandle orig_handle;
+ ConvexTest () {
+ toggles.push_back(Toggle("Show trials", false));
+ handles.push_back(&test_window);
+ handles.push_back(&samples);
+ handles.push_back(&orig_handle);
+ orig_handle.pos = Point(00,300);
+ test_window.push_back(Point(100,100));
+ test_window.push_back(Point(200,200));
+ for(int i = 0; i < 0; i++) {
+ samples.push_back(Point(i*100, i*100+25));
+ }
+ }
+ int iters;
+ int splits[4];
+ bool show_splits;
+ std::vector<Toggle> toggles;
+ AAF (*eval)(AAF, AAF);
+ void recursive_implicit(Rect r, cairo_t*cr, double w) {
+ if(show_splits) {
+ cairo_save(cr);
+ cairo_set_line_width(cr, 0.3);
+ /*if(f.is_partial())
+ cairo_set_source_rgba(cr, 1, 0, 1, 0.25);
+ else*/
+ cairo_set_source_rgba(cr, 0, 1, 0, 0.25);
+ cairo_rectangle(cr, r);
+ cairo_stroke(cr);
+ cairo_restore(cr);
+ }
+ iters++;
+ AAF x(interval(r.left(), r.right()));
+ AAF y(interval(r.top(), r.bottom()));
+ //assert(x.rad() > 0);
+ //assert(y.rad() > 0);
+ AAF f = (*eval)(x, y);
+ double a = f.index_coeff(x.get_index(0))/x.index_coeff(x.get_index(0));
+ double b = f.index_coeff(y.get_index(0))/y.index_coeff(y.get_index(0));
+ AAF d = a*x + b*y - f;
+ interval ivl(d);
+ Point n(a,b);
+ OptRect out = tighten(r, n, Interval(ivl.min(), ivl.max()));
+ if(ivl.extent() < 0.5*L2(n)) {
+
+ cairo_save(cr);
+ cairo_set_source_rgba(cr, 0,1,0,0.125);
+ fill_line_in_rect(cr, r, n, ivl.middle());
+ cairo_fill(cr);
+ cairo_restore(cr);
+ draw_line_in_rect(cr, r, n, ivl.middle());
+ return;
+ }
+ if(f.strictly_neg()) {
+ cairo_save(cr);
+ cairo_set_source_rgba(cr, 0,1,0,0.125);
+ cairo_rectangle(cr, r);
+ cairo_fill(cr);
+ cairo_restore(cr);
+ return;
+ }
+ if(!f.is_partial() and f.is_indeterminate()) {
+ cairo_save(cr);
+ cairo_set_line_width(cr, 0.3);
+ if(f.is_infinite()) {
+ cairo_set_source_rgb(cr, 1, 0.5, 0.5);
+ } else if(f.is_nan()) {
+ cairo_set_source_rgb(cr, 1, 1, 0);
+ } else {
+ cairo_set_source_rgb(cr, 1, 0, 0);
+ }
+ cairo_rectangle(cr, r);
+ if(show_splits) {
+ cairo_stroke(cr);
+ } else {
+ cairo_fill(cr);
+ }
+ cairo_restore(cr);
+ return;
+ }
+
+ if((r.width() > w) or (r.height()>w)) {
+ if(f.strictly_neg() or f.straddles_zero()) {
+ // Three possibilities:
+ // 1) the trim operation buys us enough that we should just iterate
+ Point c = r.midpoint();
+ Rect oldr = r;
+ if(1 && out) {
+ r = *out;
+ for(int i = 0; i < 4; i++) {
+ Point p = oldr.corner(i);
+ if(dot(n,p) < ivl.middle()) {
+ r.expandTo(p);
+ }
+ }
+ }
+ if(1 && out && (r.area() < oldr.area()*0.25)) {
+ splits[0] ++;
+ recursive_implicit(r, cr, w);
+ // 2) one dimension is significantly smaller
+ } else if(1 && (r[1].extent() < oldr[1].extent()*0.5)) {
+ splits[1]++;
+ recursive_implicit(Rect(Interval(r.left(), r.right()),
+ Interval(r.top(), c[1])), cr,w);
+ recursive_implicit(Rect(Interval(r.left(), r.right()),
+ Interval(c[1], r.bottom())), cr,w);
+ } else if(1 && (r[0].extent() < oldr[0].extent()*0.5)) {
+ splits[2]++;
+ recursive_implicit(Rect(Interval(r.left(), c[0]),
+ Interval(r.top(), r.bottom())), cr,w);
+ recursive_implicit(Rect(Interval(c[0], r.right()),
+ Interval(r.top(), r.bottom())), cr,w);
+ // 3) to ensure progress we must do a four way split
+ } else {
+ splits[3]++;
+ recursive_implicit(Rect(Interval(r.left(), c[0]),
+ Interval(r.top(), c[1])), cr,w);
+ recursive_implicit(Rect(Interval(c[0], r.right()),
+ Interval(r.top(), c[1])), cr,w);
+ recursive_implicit(Rect(Interval(r.left(), c[0]),
+ Interval(c[1], r.bottom())), cr,w);
+ recursive_implicit(Rect(Interval(c[0], r.right()),
+ Interval(c[1], r.bottom())), cr,w);
+ }
+ }
+ } else {
+ }
+ }
+
+ void key_hit(GdkEventKey *e) override {
+ if(e->keyval == 'w') toggles[0].toggle(); else
+ if(e->keyval == 'a') toggles[1].toggle(); else
+ if(e->keyval == 'q') toggles[2].toggle(); else
+ if(e->keyval == 's') toggles[3].toggle();
+ redraw();
+ }
+ void mouse_pressed(GdkEventButton* e) override {
+ toggle_events(toggles, e);
+ Toy::mouse_pressed(e);
+ }
+ void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) override {
+ cairo_set_source_rgba (cr, 0., 0., 0, 1);
+ cairo_set_line_width (cr, 1);
+ origin = orig_handle.pos;
+ if(1) {
+ cairo_save(cr);
+ cairo_set_line_width(cr, 0.3);
+ cairo_set_source_rgb(cr, 0.5, 0.5, 1);
+ eval = xaxis;
+ //recursive_implicit(Rect(Interval(0,width), Interval(0, height)), cr, 3);
+ eval = yaxis;
+ //recursive_implicit(Rect(Interval(0,width), Interval(0, height)), cr, 3);
+ cairo_restore(cr);
+ iters = 0;
+ for(int & split : splits)
+ split = 0;
+ show_splits = toggles[0].on;
+ eval = trial_eval;
+ recursive_implicit(Rect(Interval(0,width), Interval(0, height)), cr, 3);
+ for(int split : splits)
+ *notify << split << " + ";
+ *notify << " = " << iters;
+ }
+ if(1) {
+ Rect r(test_window.pts[0], test_window.pts[1]);
+ AAF x(interval(r.left(), r.right()));
+ AAF y(interval(r.top(), r.bottom()));
+ //AAF f = md_sample_based(x, samples.pts)-y;
+ if(0) {
+ x = x-500;
+ y = y-300;
+ x = x/200;
+ y = y/200;
+ AAF f = atan_sample_based(x)-y;
+ cout << f << endl;
+ }
+ AAF f = (*eval)(x, y);
+ double a = f.index_coeff(x.get_index(0))/x.index_coeff(x.get_index(0));
+ double b = f.index_coeff(y.get_index(0))/y.index_coeff(y.get_index(0));
+ AAF d = a*x + b*y - f;
+ //cout << d << endl;
+ interval ivl(d);
+ Point n(a,b);
+ OptRect out = tighten(r, n, Interval(ivl.min(), ivl.max()));
+ if(out)
+ cairo_rectangle(cr, *out);
+ cairo_rectangle(cr, r);
+ draw_line_in_rect(cr, r, n, ivl.min());
+ if(f.strictly_neg()) {
+ cairo_save(cr);
+ cairo_set_source_rgba(cr, .5, 0.5, 0, 0.125);
+ cairo_fill(cr);
+ cairo_restore(cr);
+ } else
+ cairo_stroke(cr);
+ cairo_save(cr);
+ cairo_set_line_width(cr, 0.3);
+ cairo_set_source_rgb(cr, 0.5, 0.5, 0);
+ draw_line_in_rect(cr, r, n, ivl.middle());
+ cairo_stroke(cr);
+ cairo_restore(cr);
+ //fill_line_in_rect(cr, r, n, c);
+ draw_line_in_rect(cr, r, n, ivl.max());
+ cairo_stroke(cr);
+ }
+ if(0) {
+ Geom::ConvexHull gm(samples.pts);
+ cairo_convex_hull(cr, gm);
+ cairo_stroke(cr);
+ Point a, b, c;
+ gm.narrowest_diameter(a, b, c);
+ cairo_save(cr);
+ cairo_set_line_width(cr, 2);
+ cairo_set_source_rgba(cr, 1, 0, 0, 0.5);
+ cairo_move_to(cr, b);
+ cairo_line_to(cr, c);
+ cairo_move_to(cr, a);
+ cairo_line_to(cr, (c-b)*dot(a-b, c-b)/dot(c-b,c-b)+b);
+ cairo_stroke(cr);
+ //std::cout << a << ", " << b << ", " << c << ": " << dia << "\n";
+ cairo_restore(cr);
+ }
+ Toy::draw(cr, notify, width, height, save,timer_stream);
+ Point d(25,25);
+ toggles[0].bounds = Rect(Point(10, height-80)+d,
+ Point(10+120, height-80+d[1])+d);
+
+ draw_toggles(cr, toggles);
+ }
+
+};
+
+int main(int argc, char **argv) {
+ init(argc, argv, new ConvexTest());
+
+ return 0;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :