/ JavaFX / Hand drawing effect with JavaFX

Hand drawing effect with JavaFX

Hendrik on 2014/11/13 - 20:46 in JavaFX

Some days ago Thierry Wasylczenko blogged about 12 of his favorite JS libs. On of the libs is js-sequence-diagrams that I did know. By using the library you can draw sequence diagrams in a browser. I really like the hand drawn theme of the tool that draw all the diagrams like they are sketched with a pen. Here is an example of the theme:
js-sequence-diagrams
After having a look at the source code of js-sequence-diagrams I found the methods that render all the hand drawn lines. Cubic curves are used here and the control points of the curves are calculated by using random values. These two control points define the bend of the curve as you can see in the following picture:
cubic curve
Once I’ve seen this I wanted to try if I can do the same with JavaFX :)
Thankfully JavaFX contains support for cubic curves. The Path shape can contain curves that can be defined by the CubicCurveTo class. By using this class you can create lines and shapes that look like hand drawn onces by using the same algorithm as in js-sequence-diagrams. I created a simple test class that draws some arrows, lines and rectangles by using this algorithm. Here are some results:
sketch1
sketch2
sketch3
sketch4

As you can see in the picture the shapes look different in each of them. This is caused by the random values that are part of the algorithm. If you want to try this here is my one class gist that contains all the code:

5 POST COMMENT

Send Us A Message Here

Your email address will not be published. Required fields are marked *

5 Comments
  • 2014/11/14

    Thanks, I’ll definitely use that :-) I’m curious though. Is there a reason why you didn’t use the inbuilt functions and instead used the core math (sqrt, cos, sin, etc)? Because e. g. instead of

    double wobble = Math.sqrt((endPoint.getX() - startPoint.getX()) * (endPoint.getX() - startPoint.getX()) + (endPoint.getY() - startPoint.getY()) * (endPoint.getY() - startPoint.getY())) / 25;

    you could as well have used

    double wobble = startPoint.distance(endPoint) / 25;

    Lofi
    Reply
    • 2014/11/14

      I tried with less math 😀


      public Shape createHandDrawnArrow(double x1, double y1, double x2, double y2, double strokeWidth, Color color) {

      Point2D startPoint = new Point2D(x1, y1);
      Point2D endPoint = new Point2D(x2, y2);

      double arrowLength = strokeWidth * 5;

      // create curve
      CubicCurve line = createHandDrawnLine( startPoint, endPoint, strokeWidth, color);

      // create arrow
      CubicCurve arrowLeft = createHandDrawnLine( new Point2D( 0,0), new Point2D(5, arrowLength), strokeWidth, color);
      CubicCurve arrowRight = createHandDrawnLine(new Point2D( 0,0), new Point2D(-5, arrowLength), strokeWidth, color);
      Shape arrow = Shape.union(arrowLeft, arrowRight);

      // get angle at end point
      Point2D tan = new Point2D( line.getEndX() - line.getControlX2(), line.getEndY()-line.getControlY2());
      double angle = Math.atan2( tan.getY(), tan.getX());

      // set rotation for arrow
      Rotate rz = new Rotate();
      rz.setAxis(Rotate.Z_AXIS); // pivot point
      rz.setAngle(Math.toDegrees(angle) + 90); // rotate works in deg instead of rad; arrow origin is top => apply additional offset of 90 deg

      // move arrow from 0/0 to end point
      arrow.setTranslateX(endPoint.getX());
      arrow.setTranslateY(endPoint.getY());
      arrow.getTransforms().add(rz);

      return Shape.union(line, arrow);

      }

      public CubicCurve createHandDrawnLine(Point2D startPoint, Point2D endPoint, double strokeWidth, Color color) {

      double wobble = startPoint.distance(endPoint) / 25;

      double r1 = Math.random();
      double r2 = Math.random();

      double xfactor = Math.random() > 0.5 ? wobble : -wobble;
      double yfactor = Math.random() > 0.5 ? wobble : -wobble;

      Point2D control1 = new Point2D((endPoint.getX() - startPoint.getX()) * r1 + startPoint.getX() + xfactor, (endPoint.getY() - startPoint.getY()) * r1 + startPoint.getY() + yfactor);
      Point2D control2 = new Point2D((endPoint.getX() - startPoint.getX()) * r2 + startPoint.getX() - xfactor, (endPoint.getY() - startPoint.getY()) * r2 + startPoint.getY() - yfactor);

      CubicCurve curve = new CubicCurve( startPoint.getX(), startPoint.getY(), control1.getX(), control1.getY(), control2.getX(), control2.getY(), endPoint.getX(), endPoint.getY());
      curve.setStrokeLineCap(StrokeLineCap.ROUND);
      curve.setStroke(color);
      curve.setStrokeWidth(strokeWidth + (strokeWidth * (Math.random() - 0.5) / 8.0));
      curve.setStrokeType(StrokeType.CENTERED);
      curve.setFill(null);
      return curve;
      }

      Lofi
      Reply
    • 2014/11/15

      Oh, cool :) I don’t saw this method

      admin
      Reply
  • Pingback:JavaFX links of the week, November 17 // JavaFX News, Demos and Insight // FX Experience

  • 2014/11/17

    thanks, been looking for this sketch-effect since last year! Eureka!

    francis valero
    Reply