diff options
Diffstat (limited to 'src/toys/conic-5.cpp')
-rw-r--r-- | src/toys/conic-5.cpp | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/src/toys/conic-5.cpp b/src/toys/conic-5.cpp new file mode 100644 index 0000000..51140c1 --- /dev/null +++ b/src/toys/conic-5.cpp @@ -0,0 +1,356 @@ +#include <iostream> +#include <2geom/path.h> +#include <2geom/svg-path-parser.h> +#include <2geom/path-intersection.h> +#include <2geom/basic-intersection.h> +#include <2geom/pathvector.h> +#include <2geom/exception.h> +#include <2geom/sbasis-geometric.h> +#include <2geom/path-intersection.h> +#include <2geom/nearest-time.h> +#include <2geom/line.h> +#include <2geom/bezier-to-sbasis.h> +#include <2geom/sbasis-to-bezier.h> + +#include <cstdlib> +#include <map> +#include <vector> +#include <algorithm> +#include <optional> + +#include <toys/path-cairo.h> +#include <toys/toy-framework-2.h> +#include <2geom/bezier-to-sbasis.h> +#include <2geom/ord.h> + + + +#include <2geom/conicsec.h> + +using namespace Geom; +using namespace std; + + +// File: convert.h +#include <sstream> +#include <stdexcept> + +class BadConversion : public std::runtime_error { +public: + BadConversion(const std::string& s) + : std::runtime_error(s) + { } +}; + +template <typename T> +inline std::string stringify(T x) +{ + std::ostringstream o; + if (!(o << x)) + throw BadConversion("stringify(T)"); + return o.str(); +} + +void draw_hull(cairo_t*cr, RatQuad rq) { + cairo_move_to(cr, rq.P[0]); + cairo_line_to(cr, rq.P[1]); + cairo_line_to(cr, rq.P[2]); + cairo_stroke(cr); +} + + + +void draw(cairo_t* cr, xAx C, Rect bnd) { + if(bnd[1].extent() < 5) return; + vector<double> prev_rts; + double py = bnd[Y].min(); + for(int i = 0; i < 100; i++) { + double t = i/100.; + double y = bnd[Y].valueAt(t); + vector<double> rts = C.roots(Point(1, 0), Point(0, y)); + int top = 0; + for(unsigned j = 0; j < rts.size(); j++) { + if(bnd[0].contains(rts[j])) { + rts[top] = rts[j]; + top++; + } + } + rts.erase(rts.begin()+top, rts.end()); + + if(rts.size() == prev_rts.size()) { + for(unsigned j = 0; j < rts.size(); j++) { + cairo_move_to(cr, prev_rts[j], py); + cairo_line_to(cr, rts[j], y); + cairo_stroke(cr); + } + } else { + draw(cr, C, Rect(bnd[X], Interval(py, y))); + } + prev_rts = rts; + py = y; + } +} + +template <typename T> +static T det(T a, T b, T c, T d) { + return a*d - b*c; +} + +template <typename T> +static T det(T M[2][2]) { + return M[0][0]*M[1][1] - M[1][0]*M[0][1]; +} + +template <typename T> +static T det3(T M[3][3]) { + return ( M[0][0] * det(M[1][1], M[1][2], + M[2][1], M[2][2]) + -M[1][0] * det(M[0][1], M[0][2], + M[2][1], M[2][2]) + +M[2][0] * det(M[0][1], M[0][2], + M[1][1], M[1][2])); +} + +double xAx_descr(xAx const & C) { + double mC[3][3] = {{C.c[0], (C.c[1])/2, (C.c[3])/2}, + {(C.c[1])/2, C.c[2], (C.c[4])/2}, + {(C.c[3])/2, (C.c[4])/2, C.c[5]}}; + + return det3(mC); +} + +void draw_ratquad(cairo_t*cr, RatQuad rq, double tol=1e-1) { + CubicBezier cb = rq.toCubic(); + // I tried using the nearest point to 0.5 for the error, but the improvement was negligible + if(L2(cb.pointAt(0.5) - rq.pointAt(0.5)) > tol) { + RatQuad a, b; + rq.split(a, b); + draw_ratquad(cr, a, tol); + draw_ratquad(cr, b, tol); + } else { + cairo_curve(cr, cb); + //draw_cross(cr, cb.pointAt(0)); + //draw_cross(cr, cb.pointAt(1)); + } +} + + +class Conic5: public Toy { + PointSetHandle path_handles; + PointHandle oncurve; + PointSetHandle cutting_plane; + std::vector<Slider> sliders; + RectHandle rh; + + 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); + + if(0) { + Path path; + path = Path(path_handles.pts[0]); + D2<SBasis> c = handles_to_sbasis(path_handles.pts.begin(), 2); + path.append(c); + + cairo_save(cr); + cairo_path(cr, path); + cairo_set_source_rgba (cr, 0., 1., 0, 0.3); + cairo_set_line_width (cr, 3); + cairo_stroke(cr); + cairo_restore(cr); + + //double w = exp(sliders[0].value()); + } + Point A = path_handles.pts[0]; + Point B = path_handles.pts[1]; + Point C = path_handles.pts[2]; + + if(1) { + QuadraticBezier qb(A, B, C); + //double abt = qb.nearestTime(oncurve.pos); + //oncurve.pos = qb.pointAt(abt); + + RatQuad rq = RatQuad::fromPointsTangents(A, B-A, oncurve.pos, C, B -C); //( A, B, C, w); + + cairo_save(cr); + cairo_set_source_rgba (cr, 0., 0., 0, 1); + cairo_set_line_width (cr, 1); + draw_ratquad(cr, rq); + //cairo_d2_sb(cr, rq.hermite()); + cairo_stroke(cr); + cairo_restore(cr); + } + + if(0) { + RatQuad rq = RatQuad::circularArc(A, B, C); + + cairo_save(cr); + cairo_set_source_rgba (cr, 0., 0., 0, 1); + cairo_set_line_width (cr, 1); + RatQuad a, b; + rq.split(a,b); + cairo_curve(cr, a.toCubic()); + cairo_curve(cr, b.toCubic()); + cairo_stroke(cr); + cairo_restore(cr); + } + + Rect screen_rect(Interval(10, width-10), Interval(10, height-10)); + Line cutLine(cutting_plane.pts[0], cutting_plane.pts[1]); + //double dist; + //Point norm = cutLine.normalAndDist(dist); + + const unsigned N = 3; + xAx sources[N] = { + xAx::fromPoint(A)*(exp(-sliders[0].value())), + xAx::fromPoint(B)*(exp(-sliders[1].value())), + xAx::fromPoint(C)*(exp(-sliders[2].value())) + //xAx::fromLine(Line(A, oncurve.pos)) + }; + for(unsigned i = 0; i < N; i++) { + //*notify << sources[i] << "\n"; + } + for(unsigned i = 0; i < N; i++) { + for(unsigned j = i+1; j < N; j++) { + xAx Q = sources[i]-sources[j]; + *notify << Q << " is a " << Q.categorise() << "\n"; + } + } + { + cairo_save(cr); + cairo_set_source_rgba(cr, 0, 0, 1, 0.5); + + ::draw(cr, (sources[0]-sources[1]), screen_rect); + ::draw(cr, (sources[0]-sources[2]), screen_rect); + ::draw(cr, (sources[1]-sources[2]), screen_rect); + cairo_restore(cr); + } + { + string os; + for(unsigned i = 0; i < N; i++) { + for(unsigned j = i+1; j < N; j++) { + xAx Q = sources[i]-sources[j]; + Interval iQ = Q.extrema(rh.pos); + if(iQ.contains(0)) { + os += stringify(iQ) + "\n"; + + Q.toCurve(rh.pos); + vector<Point> crs = Q.crossings(rh.pos); + for(auto & ei : crs) { + draw_cross(cr, ei); + } + + } + } + } + + draw_text(cr, rh.pos.midpoint(), + os); + } + if(1) { + xAx oxo=sources[0] - sources[2]; + Timer tm; + + tm.ask_for_timeslice(); + tm.start(); + + std::vector<Point> intrs = intersect(oxo, sources[0] - sources[1]); + Timer::Time als_time = tm.lap(); + *notify << "intersect time = " << als_time << std::endl; + for(auto & intr : intrs) { + cairo_save(cr); + cairo_set_source_rgb(cr, 1, 0,0); + draw_cross(cr, intr); + cairo_stroke(cr); + cairo_restore(cr); + } + + std::optional<RatQuad> orq = oxo.toCurve(rh.pos); + if(orq) { + RatQuad rq = *orq; + draw_hull(cr, rq); + vector<SBasis> hrq = rq.homogeneous(); + SBasis vertex_poly = (sources[0] - sources[1]).evaluate_at(hrq[0], hrq[1], hrq[2]); + //*notify << "\n0: " << hrq[0]; + //*notify << "\n1: " << hrq[1]; + //*notify << "\n2: " << hrq[2]; + vector<double> rts = roots(vertex_poly); + //*notify << "\nvertex poly:" << vertex_poly << '\n'; + for(unsigned i = 0; i < rts.size(); i++) { + draw_circ(cr, Point(rq.pointAt(rts[i]))); + *notify << "\nrq" << i << ":" << rts[i]; + } + + cairo_save(cr); + cairo_set_source_rgb(cr, 1, 0, 0); + RatQuad a, b; + rq.split(a,b); + cairo_curve(cr, a.toCubic()); + cairo_curve(cr, b.toCubic()); + cairo_stroke(cr); + cairo_restore(cr); + } + } + if(0) { + RatQuad a, b; + //rq.split(a,b); + //cairo_move_to(cr, rq.toCubic().pointAt(0.5)); + cairo_line_to(cr, a.P[2]); + cairo_stroke(cr); + + cairo_curve(cr, a.toCubic()); + cairo_curve(cr, b.toCubic()); + + } + cairo_stroke(cr); + + //*notify << "w = " << w << "; lambda = " << rq.lambda() << "\n"; + Toy::draw(cr, notify, width, height, save, timer_stream); + } + +public: + Conic5() { + handles.push_back(&path_handles); + handles.push_back(&rh); + rh.pos = Rect(Point(100,100), Point(200,200)); + rh.show_center_handle = true; + handles.push_back(&oncurve); + for(int j = 0; j < 3; j++){ + path_handles.push_back(uniform()*400, 100+ uniform()*300); + } + oncurve.pos = ((path_handles.pts[0]+path_handles.pts[1]+path_handles.pts[2])/3); + handles.push_back(&cutting_plane); + for(int j = 0; j < 2; j++){ + cutting_plane.push_back(uniform()*400, 100+ uniform()*300); + } + sliders.emplace_back(0.0, 5.0, 0, 0.0, "a"); + sliders.emplace_back(0.0, 5.0, 0, 0.0, "b"); + sliders.emplace_back(0.0, 5.0, 0, 0.0, "c"); + handles.push_back(&(sliders[0])); + handles.push_back(&(sliders[1])); + handles.push_back(&(sliders[2])); + sliders[0].geometry(Point(50, 20), 250); + sliders[1].geometry(Point(50, 50), 250); + sliders[2].geometry(Point(50, 80), 250); + } + + void first_time(int /*argc*/, char**/* argv*/) override { + + } +}; + +int main(int argc, char **argv) { + init(argc, argv, new Conic5()); + 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=4:softtabstop=4:fileencoding=utf-8:textwidth=99 : |