diff options
Diffstat (limited to '')
-rw-r--r-- | src/toys/squiggles.cpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/toys/squiggles.cpp b/src/toys/squiggles.cpp new file mode 100644 index 0000000..eef33ad --- /dev/null +++ b/src/toys/squiggles.cpp @@ -0,0 +1,216 @@ +#include <2geom/piecewise.h> +#include <2geom/sbasis.h> +#include <2geom/bezier-to-sbasis.h> +#include <2geom/sbasis-math.h> +#include <2geom/sbasis-geometric.h> + +#include <toys/path-cairo.h> +#include <toys/toy-framework-2.h> + +#include <vector> + +#define NB_CTL_PTS 6 +#define K_SCALE .002 + +using namespace Geom; +using namespace std; + +void cairo_pw(cairo_t *cr, Piecewise<SBasis> p) { + for(unsigned i = 0; i < p.size(); i++) { + D2<SBasis> B; + B[0] = Linear(p.cuts[i], p.cuts[i+1]); + B[1] = Linear(150) + p[i]; + cairo_d2_sb(cr, B); + } +} + +void cairo_horiz(cairo_t *cr, double y, vector<double> p) { + for(double i : p) { + cairo_move_to(cr, i, y); + cairo_rel_line_to(cr, 0, 10); + } +} + +void cairo_vert(cairo_t *cr, double x, vector<double> p) { + for(double i : p) { + cairo_move_to(cr, x, i); + cairo_rel_line_to(cr, 10, 0); + } +} + +/* +Piecewise<SBasis> interpolate(std::vector<double> values, std::vector<double> times){ + assert ( values.size() == times.size() ); + if ( values.size() == 0 ) return Piecewise<SBasis>(); + if ( values.size() == 1 ) return Piecewise<SBasis>(values[0]);//what about time?? + + SBasis bump_in = Linear(0,1);//Enough for piecewise linear interpolation. + //bump_in.push_back(Linear(-1,1));//uncomment for C^1 interpolation + SBasis bump_out = Linear(1,0); + //bump_out.push_back(Linear(1,-1)); + + Piecewise<SBasis> result; + result.cuts.push_back(times[0]); + for (unsigned i = 0; i<values.size()-1; i++){ + result.push(bump_out*values[i]+bump_in*values[i+1],times[i+1]); + } + return result; +} +*/ + +//#include <toys/pwsbhandle.cpp // FIXME: This looks like it may give problems later, (including a .cpp file) + +class Squiggles: public Toy { + PointSetHandle hand; + unsigned current_ctl_pt; + Point current_pos; + Point current_dir; + std::vector<double> curvatures; + std::vector<double> times; + Piecewise<D2<SBasis> > curve; + double tot_length; + int mode; //0=set curvature, 1=set curv.+rotation, 2=translate, 3=slide time. + + void mouse_moved(GdkEventMotion* e) override{ + mode = 0; + if((e->state & (GDK_SHIFT_MASK)) && + (e->state & (GDK_CONTROL_MASK))) { + mode = 3; + }else if(e->state & (GDK_CONTROL_MASK)) { + mode = 1; + }else if(e->state & (GDK_SHIFT_MASK)) { + mode = 2; + } + Toy::mouse_moved(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); + + *notify << "Drag to set curvature,\n"; + *notify << "SHIFT-Drag to move curve,\n"; + *notify << "CTRL-Drag to rotate,\n"; + *notify << "SHIFT-CTRL-Drag to slide handles."; + //Get user input + if (mouse_down && selected) { + for(unsigned i = 0; i < handles.size(); i++) { + if(selected == handles[i]){ + current_ctl_pt = i; + break; + } + } + double time = times[current_ctl_pt]; + current_pos = curve.valueAt(time); + current_dir = derivative(curve).valueAt(time);//*This should be a unit vector!* + Point hdle = dynamic_cast<PointHandle*>(handles[current_ctl_pt])->pos; + if (mode == 0){ + curvatures[current_ctl_pt] = cross(hdle - current_pos,current_dir)*K_SCALE; + }else if (mode == 1){//Rotate + double sign = ( curvatures[current_ctl_pt]>=0 ? 1 : -1 ); + //curvatures[current_ctl_pt] = sign*L2(hdle - current_pos)*K_SCALE; + current_dir = -sign*unit_vector(rot90(hdle - current_pos)); + }else if (mode == 2){//Translate + Point old_pos = current_pos + curvatures[current_ctl_pt]/K_SCALE*rot90(current_dir); + current_pos += hdle - old_pos; + curve += hdle - old_pos; + }else if (mode == 3){//Slide time + Point old_pos = current_pos + curvatures[current_ctl_pt]/K_SCALE*rot90(current_dir); + double delta = dot(hdle - old_pos,current_dir); + double epsilon = 2; + if (current_ctl_pt>0 && times[current_ctl_pt]+delta < times[current_ctl_pt-1]+epsilon){ + delta = times[current_ctl_pt-1] + epsilon - times[current_ctl_pt]; + } + if (current_ctl_pt<times.size()-1 && times[current_ctl_pt]+delta > times[current_ctl_pt+1]-epsilon){ + delta = times[current_ctl_pt+1] - epsilon - times[current_ctl_pt]; + } + times[current_ctl_pt] += delta; + current_pos += delta*current_dir; + } + } + + //Compute new curve + + Piecewise<SBasis> curvature = interpolate( times, curvatures , 1); + Piecewise<SBasis> alpha = integral(curvature); + Piecewise<D2<SBasis> > v = sectionize(tan2(alpha)); + curve = integral(v)+Point(100,100); + + //transform to keep current point in place + double time = times[current_ctl_pt]; + Point new_pos = curve.valueAt(time); + Point new_dir = v.valueAt(time); + Affine mat1 = Affine( new_dir[X], new_dir[Y], -new_dir[Y], new_dir[X], new_pos[X], new_pos[Y]); + Affine mat2 = Affine(current_dir[X],current_dir[Y],-current_dir[Y],current_dir[X],current_pos[X],current_pos[Y]); + mat1 = mat1.inverse()*mat2; + curve = curve*mat1; + v = v*mat1.withoutTranslation(); + + //update handles + cairo_save(cr); + double dashes[2] = {3, 2}; + cairo_set_dash(cr, dashes, 2, 0); + cairo_set_line_width(cr, .5); + cairo_set_source_rgba (cr, 0., 0., 0.5, 1); + for(unsigned i = 0; i < NB_CTL_PTS; i++) { + Point m = curve.valueAt(times[i]); + dynamic_cast<PointHandle*>(handles[i])->pos = m + + curvatures[i]/K_SCALE*rot90(v.valueAt(times[i])); + draw_handle(cr, m); + cairo_move_to(cr, m); + cairo_line_to(cr, dynamic_cast<PointHandle*>(handles[i])->pos); + } + +#if 0 + D2<Piecewise<SBasis> > graphe; + graphe[X] = Piecewise<SBasis>(Linear(100,300)); + graphe[Y] = -curvature/K_SCALE+400; + graphe[X].setDomain(graphe[Y].domain()); + cairo_d2_pw_sb(cr, graphe); +#endif + + cairo_stroke(cr); + cairo_restore(cr); + + cairo_pw_d2_sb(cr, curve); + cairo_set_source_rgba (cr, 0., 0., 0, 1); + cairo_stroke(cr); + + Toy::draw(cr, notify, width, height, save,timer_stream); + } + + bool should_draw_numbers() override { return false; } + +public: + Squiggles () { + current_ctl_pt = 0; + current_dir = Point(1,0); + current_pos = Point(100,100); + tot_length = 300; + + curve = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(100.,300.),SBasis(100.,100.))); + for(unsigned i = 0; i < NB_CTL_PTS; i++) { + curvatures.push_back(0); + times.push_back(i*tot_length/(NB_CTL_PTS-1)); + PointHandle *pt_hdle = new PointHandle(Geom::Point(100+i*tot_length/(NB_CTL_PTS-1), 100.)); + handles.push_back(pt_hdle); + } + } +}; + +int main(int argc, char **argv) { + init(argc, argv, new Squiggles()); + 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 : |