diff options
Diffstat (limited to '')
-rw-r--r-- | src/toys/intersect-data.cpp | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/src/toys/intersect-data.cpp b/src/toys/intersect-data.cpp new file mode 100644 index 0000000..d262684 --- /dev/null +++ b/src/toys/intersect-data.cpp @@ -0,0 +1,436 @@ +#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 <cstdlib> +#include <cstdio> +#include <set> +#include <vector> +#include <algorithm> + +#include <toys/path-cairo.h> +#include <toys/toy-framework-2.h> +#include <2geom/bezier-to-sbasis.h> +#include <2geom/ord.h> + +#include "topology.cpp" + + +static double exp_rescale(double x){ return pow(10, x);} +std::string exp_formatter(double x){ return default_formatter(exp_rescale(x));} + + +class IntersectDataTester: public Toy { + unsigned nb_paths; + unsigned nb_curves_per_path; + unsigned degree; + double tol; + + PathVector cmd_line_paths; + + std::vector<PointSetHandle> paths_handles; + std::vector<Slider> sliders; + int nb_steps; + + Topology topo; + + //TODO conversions to path should be owned by the relevant classes. + Path edgeToPath(Topology::OrientedEdge o_edge){ + Topology::Edge e = topo.edges[o_edge.edge]; + D2<SBasis> p = topo.input_paths[e.path][e.curve].toSBasis(); + Interval dom = e.portion; + p = portion(p, dom); + if ( o_edge.reversed ){ + p = compose( p, Linear(1.,0.) ); + } + Path ret; + ret.setStitching(true); + Point center; + unsigned c_idx = topo.source(o_edge, true); + if ( c_idx == NULL_IDX ){ + ret.append(p); + }else{ + center = topo.vertices[c_idx].bounds.midpoint(); + ret = Path(center); + ret.append(p); + } + c_idx = topo.target(o_edge, true); + if ( c_idx == NULL_IDX ){ + return ret; + }else{ + center = topo.vertices[c_idx].bounds.midpoint(); + if ( center != p.at1() ) ret.appendNew<LineSegment>(center); + return ret; + } + } + + Path boundaryToPath(Topology::Boundary b){ + Point pt; + Path bndary; + bndary.setStitching(true); + + if (b.size()==0){ return Path(); } + + Topology::OrientedEdge o_edge = b.front(); + unsigned first_v = topo.source(o_edge, true); + if ( first_v != NULL_IDX ){ + pt = topo.vertices[first_v].bounds.midpoint(); + bndary = Path(pt); + } + + for (unsigned i = 0; i < b.size(); i++){ + bndary.append( edgeToPath(b[i])); + } + bndary.close(); + return bndary; + } + + //TODO:this should return a path vector, but we glue the components for easy drawing in the toy. + Path areaToPath(unsigned a){ + Path bndary; + bndary.setStitching(true); + if ( topo.areas[a].boundary.size()==0 ){//this is the unbounded component... + OptRect bbox = bounds_fast( topo.input_paths ); + if (!bbox ){return Path();}//??? + bbox->expandBy(50); + bndary = Path(bbox->corner(0)); + bndary.appendNew<LineSegment>(bbox->corner(1)); + bndary.appendNew<LineSegment>(bbox->corner(2)); + bndary.appendNew<LineSegment>(bbox->corner(3)); + bndary.appendNew<LineSegment>(bbox->corner(0)); + }else{ + bndary = boundaryToPath(topo.areas[a].boundary); + } + for (auto & inner_boundarie : topo.areas[a].inner_boundaries){ + bndary.append( boundaryToPath(inner_boundarie)); + bndary.appendNew<LineSegment>( bndary.initialPoint() ); + } + bndary.close(); + return bndary; + } + void drawAreas( cairo_t *cr, Topology const &topo, bool fill=true ){ + //don't draw the first one... + for (unsigned a=0; a<topo.areas.size(); a++){ + drawArea(cr, topo, a, fill); + } + } + void drawArea( cairo_t *cr, Topology const &topo, unsigned a, bool fill=true ){ + if (a>=topo.areas.size()) return; + Path bndary = areaToPath(a); + cairo_path(cr, bndary); + double r,g,b; + + int winding = 0; + for (int k : topo.areas[a].windings){ + winding += k; + } + + //convertHSVtoRGB(0, 1., .5 + winding/10, r,g,b); + //convertHSVtoRGB(360*a/topo.areas.size(), 1., .5, r,g,b); + convertHSVtoRGB(180+30*winding, 1., .5, r,g,b); + cairo_set_source_rgba (cr, r, g, b, 1); + //cairo_set_source_rgba (cr, 1., 0., 1., .3); + + if (fill){ + cairo_fill(cr); + }else{ + cairo_set_line_width (cr, 5); + cairo_stroke(cr); + } + } + + void highlightRay( cairo_t *cr, Topology &topo, unsigned b, unsigned r ){ + if (b>=topo.vertices.size()) return; + if (r>=topo.vertices[b].boundary.size()) return; + Rect box = topo.vertices[b].bounds; + //box.expandBy(2); + cairo_rectangle(cr, box); + cairo_set_source_rgba (cr, 1., 0., 0, 1.0); + cairo_set_line_width (cr, 1); + cairo_fill(cr); + unsigned eidx = topo.vertices[b].boundary[r].edge; + Topology::Edge e = topo.edges[eidx]; + D2<SBasis> p = topo.input_paths[e.path][e.curve].toSBasis(); + Interval dom = e.portion; + if (topo.vertices[b].boundary[r].reversed){ + //dom[0] += e.portion.extent()*2./3; + cairo_set_source_rgba (cr, 0., 1., 0., 1.0); + }else{ + //dom[1] -= e.portion.extent()*2./3; + cairo_set_source_rgba (cr, 0., 0., 1., 1.0); + } + p = portion(p, dom); + cairo_d2_sb(cr, p); + cairo_set_source_rgba (cr, 1., 0., 0, 1.0); + cairo_set_line_width (cr, 5); + cairo_stroke(cr); + } + + void drawEdge( cairo_t *cr, Topology const &topo, unsigned eidx ){ + if (eidx>=topo.edges.size()) return; + Topology::Edge e = topo.edges[eidx]; + D2<SBasis> p = topo.input_paths[e.path][e.curve].toSBasis(); + Interval dom = e.portion; + p = portion(p, dom); + cairo_d2_sb(cr, p); + if (e.start == NULL_IDX || e.end == NULL_IDX ) + cairo_set_source_rgba (cr, 0., 1., 0, 1.0); + else + cairo_set_source_rgba (cr, 0., 0., 0, 1.0); + cairo_set_line_width (cr, 1); + cairo_stroke(cr); + } + void drawEdges( cairo_t *cr, Topology const &topo ){ + for (unsigned e=0; e<topo.edges.size(); e++){ + drawEdge(cr, topo, e); + } + } + void drawKnownEdges( cairo_t *cr, Topology const &topo ){ + for (unsigned v=0; v<topo.vertices.size(); v++){ + for (unsigned e=0; e<topo.vertices[v].boundary.size(); e++){ + drawEdge(cr, topo, topo.vertices[v].boundary[e].edge); + } + } + } + + + void drawBox( cairo_t *cr, Topology const &topo, unsigned b ){ + if (b>=topo.vertices.size()) return; + Rect box = topo.vertices[b].bounds; + //box.expandBy(5); + cairo_rectangle(cr, box); + cairo_set_source_rgba (cr, 1., 0., 0, .5); + cairo_set_line_width (cr, 1); + cairo_stroke(cr); + cairo_rectangle(cr, box); + cairo_set_source_rgba (cr, 1., 0., 0, .2); + cairo_fill(cr); + +// //std::cout<<"\nintersection boundary:\n"; +// for (unsigned i = 0; i < topo.vertices[b].boundary.size(); i++){ +// unsigned eidx = topo.vertices[b].boundary[i].edge; +// Topology::Edge e = topo.edges[eidx]; +// D2<SBasis> p = topo.input_paths[e.path][e.curve].toSBasis(); +// Interval dom = e.portion; +// if (topo.vertices[b].boundary[i].reversed){ +// dom[0] += e.portion.extent()*2./3; +// cairo_set_source_rgba (cr, 0., 1., .5, 1); +// }else{ +// dom[1] -= e.portion.extent()*2./3; +// cairo_set_source_rgba (cr, 0., .5, 1., 1); +// } +// p = portion(p, dom); +// cairo_d2_sb(cr, p); +// cairo_set_line_width (cr, 2); +// cairo_stroke(cr); +// } + } + void drawBoxes( cairo_t *cr, Topology const &topo ){ + for (unsigned b=0; b<topo.vertices.size(); b++){ + drawBox(cr, topo, b); + } + } + + void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) override { + *notify<<"line command args: svgd file or (nb paths, nb curves/path, degree of curves).\n"; + cairo_set_source_rgba (cr, 0., 0., 0, 1); + cairo_set_line_width (cr, 1); + + PathVector paths; + if (!cmd_line_paths.empty()){ + paths = cmd_line_paths; + for (unsigned i = 0; i < paths.size(); i++){ + paths[i] *= Translate( paths_handles[i].pts[0] - paths[i].initialPoint() ); + } + }else{ + for (unsigned i = 0; i < nb_paths; i++){ + paths_handles[i].pts.back()=paths_handles[i].pts.front(); + paths.push_back(Path(paths_handles[i].pts[0])); + for (unsigned j = 0; j+degree < paths_handles[i].size(); j+=degree){ + D2<SBasis> c = handles_to_sbasis(paths_handles[i].pts.begin()+j, degree); + if ( j + degree == paths_handles[i].size()-1 ){ + c[X].at(0)[1] = paths_handles[i].pts.front()[X]; + c[Y].at(0)[1] = paths_handles[i].pts.front()[Y]; + } + paths[i].append(c); + } + paths[i].close(); + } + } + *notify<<"Use '<' and '>' keys to move backward/forward in the sweep: (currently doing "<<nb_steps<<" steps)\n"; + *notify<<"nb_steps: "<<nb_steps<<"\n"; + + +#if 0 + cairo_path(cr, paths); + cairo_set_source_rgba (cr, 0., 0., 0, 1); + cairo_set_line_width (cr, 1); + cairo_stroke(cr); +#endif + + tol = exp_rescale( sliders[3].value() ); + topo = Topology(paths, cr, tol, nb_steps ); + +#if 1 + unsigned v = (unsigned)(sliders[0].value()*(double(topo.vertices.size()))); + unsigned r = (unsigned)(sliders[1].value()*(double(topo.vertices[v].boundary.size()))); + unsigned a = (unsigned)(sliders[2].value()*(double(topo.areas.size()))); + if( v == topo.vertices.size() ) v--; + if( r == topo.vertices[v].boundary.size()) r--; + if( a == topo.areas.size()) a--; + drawAreas(cr, topo); + drawKnownEdges(cr, topo); + //drawArea(cr, topo, a, false); + //highlightRay(cr, topo, v, r ); + //*notify<<"highlighted edge: "<< topo.vertices[v].boundary[r].edge<<"\n"; + + //drawBox(cr,topo, unsigned(sliders[0].value())); + drawBoxes(cr,topo); +#endif + Toy::draw(cr, notify, width, height, save, timer_stream); + } + + + void initSliders(){ + sliders.emplace_back(0.0, 1, 0, 0.0, "intersection chooser"); + sliders.emplace_back(0.0, 1, 0, 0.0, "ray chooser"); + sliders.emplace_back(0.0, 1, 0, 0.0, "area chooser"); + sliders.emplace_back(-5.0, 2, 0, 0.0, "tolerance chooser"); + + handles.push_back(&(sliders[0])); + handles.push_back(&(sliders[1])); + handles.push_back(&(sliders[2])); + handles.push_back(&(sliders[3])); + + sliders[0].geometry(Point(50, 20), 250); + sliders[1].geometry(Point(50, 50), 250); + sliders[2].geometry(Point(50, 80), 250); + sliders[3].geometry(Point(50, 110), 250); + sliders[3].formatter( &exp_formatter ); + + } + + public: + IntersectDataTester(PathVector input_paths){ + cmd_line_paths = input_paths; + //nb_paths=0; nb_curves_per_path = 0; degree = 0;//meaningless + paths_handles = std::vector<PointSetHandle>( cmd_line_paths.size(), PointSetHandle() ); + for(unsigned i = 0; i < cmd_line_paths.size(); i++){ + //TODO: use path iterators to deal with closed/open paths!!! + //cmd_line_paths[i].close(); + if ( cmd_line_paths[i].closed() ){ + cmd_line_paths[i].appendNew<LineSegment>(cmd_line_paths[i].initialPoint() ); + } + Point p = cmd_line_paths[i].initialPoint(); + paths_handles.emplace_back(); + paths_handles[i].push_back(p); + handles.push_back( &paths_handles[i] ); + } + initSliders(); + } + + IntersectDataTester(unsigned paths, unsigned curves_in_path, unsigned degree) : + nb_paths(paths), nb_curves_per_path(curves_in_path), degree(degree) { + + paths_handles = std::vector<PointSetHandle>( nb_paths, PointSetHandle() ); + for(unsigned i = 0; i < nb_paths; i++){ + for(unsigned j = 0; j < (nb_curves_per_path*degree)+1; j++){ + paths_handles[i].push_back(uniform()*400, 100+ uniform()*300); + } + handles.push_back(&paths_handles[i]); + } + initSliders(); + } + + IntersectDataTester(){ + nb_paths=3; nb_curves_per_path = 5; degree = 1; + + paths_handles.emplace_back(); + paths_handles[0].push_back(100,100); + paths_handles[0].push_back(100,200); + paths_handles[0].push_back(300,200); + paths_handles[0].push_back(300,100); + paths_handles[0].push_back(100,100); + + paths_handles.emplace_back(); + paths_handles[1].push_back(120,190); + paths_handles[1].push_back(200,210); + paths_handles[1].push_back(280,190); + paths_handles[1].push_back(200,300); + paths_handles[1].push_back(120,190); + + paths_handles.emplace_back(); + paths_handles[2].push_back(180,150); + paths_handles[2].push_back(200,140); + paths_handles[2].push_back(220,150); + paths_handles[2].push_back(300,160); + paths_handles[2].push_back(180,150); + + handles.push_back(&paths_handles[0]); + handles.push_back(&paths_handles[1]); + handles.push_back(&paths_handles[2]); + + initSliders(); + } + + + void first_time(int /*argc*/, char** /*argv*/) override { + nb_steps = -1; + } + + void key_hit(GdkEventKey *e) override + { + char choice = std::toupper(e->keyval); + switch ( choice ) + { + case '>': + nb_steps++; + break; + case '<': + if ( nb_steps > -1 ) nb_steps--; + break; + } + redraw(); + } + +}; + +int main(int argc, char **argv) { + if(argc == 2){ + const char *path_name = argv[1]; + PathVector cmd_line_paths = read_svgd(path_name); //* Scale(3); + OptRect bounds = bounds_exact(cmd_line_paths); + if (bounds) { + cmd_line_paths *= Translate(Point(10,10) - bounds->min()); + } + init(argc, argv, new IntersectDataTester(cmd_line_paths)); + }else{ + unsigned nb_paths=3, nb_curves_per_path = 5, degree = 1; + if(argc > 3) + sscanf(argv[3], "%d", °ree); + if(argc > 2) + sscanf(argv[2], "%d", &nb_curves_per_path); + if(argc > 1){ + sscanf(argv[1], "%d", &nb_paths); + init(argc, argv, new IntersectDataTester( nb_paths, nb_curves_per_path, degree ) ); + }else{ + init(argc, argv, new IntersectDataTester()); + } + } + 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 : |