summaryrefslogtreecommitdiffstats
path: root/src/toys/scribble.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/toys/scribble.cpp')
-rw-r--r--src/toys/scribble.cpp366
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 :
+
+