#include #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 #include #include #include #include #include #include #include <2geom/bezier-to-sbasis.h> #include <2geom/ord.h> #include <2geom/conicsec.h> using namespace Geom; using namespace std; // File: convert.h #include #include class BadConversion : public std::runtime_error { public: BadConversion(const std::string& s) : std::runtime_error(s) { } }; template 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 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 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 static T det(T a, T b, T c, T d) { return a*d - b*c; } template static T det(T M[2][2]) { return M[0][0]*M[1][1] - M[1][0]*M[0][1]; } template 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 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 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 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 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 orq = oxo.toCurve(rh.pos); if(orq) { RatQuad rq = *orq; draw_hull(cr, rq); vector 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 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 :