diff options
Diffstat (limited to 'src/toys/scribble.cpp')
-rw-r--r-- | src/toys/scribble.cpp | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/src/toys/scribble.cpp b/src/toys/scribble.cpp new file mode 100644 index 0000000..afaff65 --- /dev/null +++ b/src/toys/scribble.cpp @@ -0,0 +1,366 @@ +#include <2geom/d2.h> +#include <2geom/sbasis.h> +#include <2geom/sbasis-2d.h> +#include <2geom/sbasis-geometric.h> +#include <2geom/sbasis-math.h> +#include <2geom/bezier-to-sbasis.h> +#include <2geom/sbasis-to-bezier.h> +#include <2geom/path-intersection.h> +#include <2geom/bezier-curve.h> +#include <2geom/transforms.h> +#include <2geom/angle.h> + +#include <toys/path-cairo.h> +#include <toys/toy-framework-2.h> +#include <sstream> + +using std::vector; +using namespace Geom; +using namespace std; + +// TODO: +// use path2 +// replace Ray stuff with path2 line segments. + +//----------------------------------------------- + +void draw_segment(cairo_t* cr, Point const& p1, Point const& p2) +{ + cairo_move_to(cr, p1); + cairo_line_to(cr, p2); +} + +void draw_segment(cairo_t* cr, Point const& p1, double angle, double length) +{ + Point p2; + p2[X] = length * std::cos(angle); + p2[Y] = length * std::sin(angle); + p2 += p1; + draw_segment(cr, p1, p2); +} + +void draw_segment(cairo_t* cr, LineSegment const& ls) +{ + draw_segment(cr, ls[0], ls[1]); +} + +int num = 30; +vector<double> c1_bot; +vector<double> c1_top; +vector<double> c2_bot; +vector<double> c2_top; +vector<double> c3_bot; +vector<double> c3_top; +vector<double> c4_bot; +vector<double> c4_top; + +CubicBezier create_bezier(Point const &anchor, double angle /* in degrees */, + double length, double dx1, double dx2, cairo_t *cr = NULL) { + Point A = anchor; + Point dir = Point(1.0, 0) * Rotate(-angle) * length; + Point B = anchor + dir; + + Point C = A - Point(1.0, 0) * dx1; + Point D = B + Point(1.0, 0) * dx1; + Point E = A + Point(1.0, 0) * dx2; + Point F = B - Point(1.0, 0) * dx2; + + if (cr) { + draw_cross(cr, A); + draw_cross(cr, B); + draw_cross(cr, C); + draw_cross(cr, D); + draw_cross(cr, E); + draw_cross(cr, F); + } + + return CubicBezier(C, E, F, D); +} + +/* + * Draws a single "scribble segment" (we use many of these to cover the whole curve). + * + * Let I1, I2 be two adjacent intervals (bounded by the points A1, A2, A3) on the lower and J1, J2 + * two adjacent intervals (bounded by B1, B2, B3) on the upper parallel. Then we specify: + * + * - The point in I1 where the scribble line starts (given by a value in [0,1]) + * - The point in J2 where the scribble line ends (given by a value in [0,1]) + * - A point in I2 (1st intermediate point of the Bezier curve) + * - A point in J1 (2nd intermediate point of the Bezier curve) + * + */ +CubicBezier +create_bezier_again(Point const &anchor1, Point const &anchor2, Point const &dir1, Point const &dir2, + double /*c1*/, double /*c2*/, double c3, double c4, double mu, cairo_t *cr = NULL) { + Point A = anchor1;// - dir * c1; + Point B = anchor1 + dir1 * (c3 + mu); + Point C = anchor2 - dir2 * (c4 + mu); + Point D = anchor2;// + dir * c2; + + if (cr) { + draw_cross(cr, A); + //draw_cross(cr, B); + //draw_cross(cr, C); + //draw_cross(cr, D); + } + + return CubicBezier(A, B, C, D); +} + +CubicBezier +create_bezier_along_curve(Piecewise<D2<SBasis> > const &curve1, + Piecewise<D2<SBasis> > const &curve2, + double segdist, + Coord const t1, Coord const t2, Point const &n, + double c1, double c2, double /*c3*/, double /*c4*/, double /*mu*/, cairo_t *cr = NULL) { + cout << "create_bezier_along_curve -- start" << endl; + /* + Point A = curve1.valueAt(t1 - c1); + Point B = curve1.valueAt(t1) + n * (c3 + mu); + Point C = curve2.valueAt(t2) - n * (c4 + mu); + Point D = curve2.valueAt(t2 + c2); + */ + Point A = curve1.valueAt(t1 - c1 * segdist); + Point B = curve1.valueAt(t1) + n * 0.1; + Point C = curve2.valueAt(t2) - n * 0.1; + Point D = curve2.valueAt(t2 + c2 * segdist); + + if (cr) { + draw_cross(cr, A); + //draw_cross(cr, B); + //draw_cross(cr, C); + //draw_cross(cr, D); + } + + cout << "create_bezier_along_curve -- end" << endl; + return CubicBezier(A, B, C, D); +} + +class OffsetTester: public Toy { + PointSetHandle psh; + PointSetHandle psh_rand; + + void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) override { + double w = 600; + double slider_top = w/4.; + double slider_bot = w*3./4.; + double slider_margin = 40; + double slider_middle = (slider_top + slider_bot) / 2; + + if(psh.pts.empty()) { + psh.pts.emplace_back(200,300); + psh.pts.emplace_back(350,250); + psh.pts.emplace_back(500,280); + psh.pts.emplace_back(700,300); + + psh.pts.emplace_back(400,300); + psh.pts.emplace_back(550,250); + psh.pts.emplace_back(700,280); + psh.pts.emplace_back(900,300); + + psh.pts.emplace_back(900,500); + psh.pts.emplace_back(700,480); + psh.pts.emplace_back(550,450); + psh.pts.emplace_back(400,500); + + psh_rand.pts.emplace_back(slider_margin,slider_bot); + psh_rand.pts.emplace_back(slider_margin,slider_top); + psh_rand.pts.emplace_back(slider_margin,slider_top); + psh_rand.pts.emplace_back(slider_margin,slider_top); + psh_rand.pts.emplace_back(slider_margin,slider_top); + psh_rand.pts.emplace_back(slider_margin,slider_bot); + psh_rand.pts.emplace_back(slider_margin,slider_middle); + } + + psh_rand.pts[0][X] = slider_margin; + if (psh_rand.pts[0][Y]<slider_top) psh_rand.pts[0][Y] = slider_top; + if (psh_rand.pts[0][Y]>slider_bot) psh_rand.pts[0][Y] = slider_bot; + psh_rand.pts[1][X] = slider_margin + 15; + if (psh_rand.pts[1][Y]<slider_top) psh_rand.pts[1][Y] = slider_top; + if (psh_rand.pts[1][Y]>slider_bot) psh_rand.pts[1][Y] = slider_bot; + psh_rand.pts[2][X] = slider_margin + 30; + if (psh_rand.pts[2][Y]<slider_top) psh_rand.pts[2][Y] = slider_top; + if (psh_rand.pts[2][Y]>slider_bot) psh_rand.pts[2][Y] = slider_bot; + psh_rand.pts[3][X] = slider_margin + 45; + if (psh_rand.pts[3][Y]<slider_top) psh_rand.pts[3][Y] = slider_top; + if (psh_rand.pts[3][Y]>slider_bot) psh_rand.pts[3][Y] = slider_bot; + psh_rand.pts[4][X] = slider_margin + 60; + if (psh_rand.pts[4][Y]<slider_top) psh_rand.pts[4][Y] = slider_top; + if (psh_rand.pts[4][Y]>slider_bot) psh_rand.pts[4][Y] = slider_bot; + psh_rand.pts[5][X] = slider_margin + 75; + if (psh_rand.pts[5][Y]<slider_top) psh_rand.pts[5][Y] = slider_top; + if (psh_rand.pts[5][Y]>slider_bot) psh_rand.pts[5][Y] = slider_bot; + psh_rand.pts[6][X] = slider_margin + 90; + if (psh_rand.pts[6][Y]<slider_top) psh_rand.pts[6][Y] = slider_top; + if (psh_rand.pts[6][Y]>slider_bot) psh_rand.pts[6][Y] = slider_bot; + + *notify << "Sliders:" << endl << endl << endl << endl; + *notify << "0 - segment distance" << endl; + *notify << "1 - start anchor randomization" << endl; + *notify << "2 - end anchor randomization" << endl; + *notify << "3 - start rounding randomization" << endl; + *notify << "4 - end rounding randomization" << endl; + *notify << "5 - start/end rounding increase randomization" << endl; + *notify << "6 - additional offset of the upper anchors (to modify the segment angle)" << endl; + + for(unsigned i = 0; i < psh_rand.size(); ++i) { + cairo_move_to(cr,Geom::Point(slider_margin + 15.0 * i, slider_bot)); + cairo_line_to(cr,Geom::Point(slider_margin + 15.0 * i, slider_top)); + } + cairo_set_line_width(cr,.5); + cairo_set_source_rgba (cr, 0., 0.3, 0., 1.); + cairo_stroke(cr); + + cairo_set_line_width (cr, 2); + cairo_set_source_rgba (cr, 0., 0., 0.8, 1); + + // Draw the curve and its offsets + D2<SBasis> B = psh.asBezier(); + cairo_d2_sb(cr, B); + cairo_stroke(cr); + + Coord offset = 30; + Piecewise<D2<SBasis> > n = rot90(unitVector(derivative(B))); + Piecewise<D2<SBasis> > offset_curve1 = Piecewise<D2<SBasis> >(B)+n*offset; + Piecewise<D2<SBasis> > offset_curve2 = Piecewise<D2<SBasis> >(B)-n*offset; + PathVector offset_path1 = path_from_piecewise(offset_curve1, 0.1); + PathVector offset_path2 = path_from_piecewise(offset_curve2, 0.1); + Piecewise<D2<SBasis> > tangent1 = unitVector(derivative(offset_curve1)); + Piecewise<D2<SBasis> > tangent2 = unitVector(derivative(offset_curve2)); + cairo_set_line_width (cr, 1); + cairo_path(cr, offset_path1); + cairo_path(cr, offset_path2); + cairo_stroke(cr); + + cairo_set_source_rgba (cr, 0., 0.5, 0., 1); + + double lambda1 = 1.0 - (psh_rand.pts[1][Y] - slider_top) * 2.0/w; + double lambda2 = 1.0 - (psh_rand.pts[2][Y] - slider_top) * 2.0/w; + double lambda3 = 1.0 - (psh_rand.pts[3][Y] - slider_top) * 2.0/w; + double lambda4 = 1.0 - (psh_rand.pts[4][Y] - slider_top) * 2.0/w; + double mu = 1.0 - (psh_rand.pts[5][Y] - slider_top) * 2.0/w; + //Point dir = Point(1,0) * (slider_bot - psh_rand.pts[0][Y]) / 2.5; + double off = 0.5 - (psh_rand.pts[6][Y] - slider_top) * 2.0/w; + + double segdist = (slider_bot - psh_rand.pts[0][Y]) / (slider_bot - slider_top) * 0.1; + if (segdist < 0.01) { + segdist = 0.01; + } + + vector<Point> pts_bot; + vector<Point> pts_top; + vector<Point> dirs_bot; + vector<Point> dirs_top; + int counter = 0; + for(double i = 0.0; i < 1.0; i += segdist) { + draw_cross(cr, offset_curve1.valueAt(i)); + pts_bot.push_back(offset_curve1.valueAt(i + segdist * c1_bot[counter] * lambda1)); + pts_top.push_back(offset_curve2.valueAt(i + segdist * (c2_top[counter] * lambda2 + 1/2.0) + off)); + dirs_bot.push_back(tangent1.valueAt(i) * 20); + dirs_top.push_back(tangent2.valueAt(i) * 20); + ++counter; + } + + for(int i = 0; i < num; ++i) { + cout << "c1_bot[" << i << "]: " << c1_bot[i] << endl; + } + + for (int i = 0; i < num-1; ++i) { + Path path1; + //cout << "dirs_bot[" << i << "]: " << dirs_bot[i] << endl; + cout << "c3_bot[" << i << "]: " << c3_bot[i] << endl; + CubicBezier bc = create_bezier_again(pts_bot[i], pts_top[i], + dirs_bot[i], dirs_top[i], + 0, 0, c3_bot[i] * lambda3, c4_top[i] * lambda4, mu, cr); + //c1_bot[i] * lambda1, + //c2_top[i] * lambda2, + //c3_bot[i] * lambda3, + //c4_top[i] * lambda4, mu, cr); + + path1.append(bc); + cairo_path(cr, path1); + + Path path2; + bc = create_bezier_again(pts_top[i], pts_bot[i+1], + dirs_top[i], dirs_bot[i+1], + 0, 0, c4_bot[i] * lambda4, c3_top[i] * lambda3, mu, cr); + /* + bc = create_bezier_again(pts_top[i+1], pts_bot[i], dir, + 1.0 - c2_top[i] * lambda2, + 1.0 - c1_bot[i+1] * lambda1, + c3_top[i] * lambda3, + c4_bot[i] * lambda4, mu, cr); + */ + path2.append(bc); + cairo_path(cr, path2); + } + + cairo_stroke(cr); + + Toy::draw(cr, notify, width, height, save,timer_stream); + } + +public: + OffsetTester() { + handles.push_back(&psh); + handles.push_back(&psh_rand); + /* + psh.pts.clear(); + Point A(100,300); + //Point B(100,200); + //Point C(200,330); + Point D(200,100); + psh.push_back(A); + //psh.push_back(B); + //psh.push_back(C); + psh.push_back(D); + psh.push_back(Geom::Point(slider_margin,slider_bot)); + */ + + for (int i = 0; i < num; ++i) { + c1_bot.push_back(uniform() - 0.5); + c1_top.push_back(uniform() - 0.5); + //c1_bot.push_back(1.0); + //c1_top.push_back(1.0); + c2_bot.push_back(uniform() - 0.5); + c2_top.push_back(uniform() - 0.5); + + c3_bot.push_back(uniform()); + c3_top.push_back(uniform()); + c4_bot.push_back(uniform()); + c4_top.push_back(uniform()); + } + + /* + for (int i = 0; i < num; ++i) { + c1_bot[i] = c1_bot[i] / 10.0; + c2_bot[i] = c2_bot[i] / 10.0; + c3_bot[i] = c3_bot[i] / 10.0; + c4_bot[i] = c4_bot[i] / 10.0; + + c1_top[i] = c1_top[i] / 10.0; + c2_top[i] = c2_top[i] / 10.0; + c3_top[i] = c3_top[i] / 10.0; + c4_top[i] = c4_top[i] / 10.0; + } + */ + } +}; + +int main(int argc, char **argv) { + init(argc, argv, new OffsetTester); + 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:encoding = utf-8:textwidth = 99 : + + |