html5 - Jigsaw Puzzle pices using Bezier Curve -


i trying make jigsaw pieces -

enter image description here enter image description here

what have tried till lineto -

outside: function (ctx, s, cx, cy) {         ctx.lineto(cx, cy)         ctx.lineto(cx+s*.3, cy)         ctx.lineto(cx+s*.5, cy+s*-.2)         ctx.lineto(cx+s*.7, cy)         ctx.lineto(cx+s, cy)     },     inside: function (ctx, s, cx, cy) {         ctx.lineto(cx, cy)         ctx.lineto(cx+s*.3, cy)         ctx.lineto(cx+s*.5, cy+s*+.2)         ctx.lineto(cx+s*.7, cy)         ctx.lineto(cx+s, cy)     }, 

fiddle link

efficient jigsaw design simple , works this:

the linked code shows how efficiently assemble 1 of jigsaw pieces reusing single side design.

the piece on right side of illustration traditional (or "japanese style") piece. means sides uniform in length , interlocking. japanese style pieces easiest design because single piece of design code , reused throughout puzzle.

ironically, while japanese style puzzles easiest code, more difficult user solve since many pieces physically fit without correctly solving puzzle.

how design japanese style jigsaw puzzle

  1. design 1 side (not more!) of jigsaw piece combining multiple cubic bezier curves.

  2. use transforms apply 1 jigsaw design top, right, bottom or left sides needed. (or code functions automatically manipulate original bezier control points apply 1 jigsaw design 4 sides). mirror original side design give pieces variety of "inny" , "outy" sides.

  3. assemble puzzle pieces mirroring design of each neighboring side:

    • give top-left piece (0,0) random right side (either inny or outy).
    • let's assume piece (0,0) assigned outy right side. next piece right (1,0) must inny left side.
    • now give piece (1,0) random right side (either inny or outy), , piece (2,0) must mirrored type of side. , on...
    • so in general, fill puzzle assigning random right sides pieces , mirroring assigned side on left side of next piece.
    • do same vertically. fill puzzle assigning random bottom sides pieces , mirroring assigned side on top side of next piece.

designing 2 example pieces you've illustrated

i assume linked code not code because shows how design piece on right of illustration(!).

// given center point of piece (cx,cy) , side length (s) // single side "outy" design below // use single design (with transforms/mirroring) make pieces ctx.lineto(cx + s * .34, cy); ctx.beziercurveto(cx + s * .5, cy, cx + s * .4, cy + s * -.15, cx + s * .4, cy + s * -.15); ctx.beziercurveto(cx + s * .3, cy + s * -.3, cx + s * .5, cy + s * -.3, cx + s * .5, cy + s * -.3); ctx.beziercurveto(cx + s * .7, cy + s * -.3, cx + s * .6, cy + s * -.15, cx + s * .6, cy + s * -.15); ctx.beziercurveto(cx + s * .5, cy, cx + s * .65, cy, cx + s * .65, cy); ctx.lineto(cx + s, cy); 

then can reuse 1 single set of bezier curves along transformations create entire puzzle. transformations==moving, rotating , mirroring 1 single design make side of puzzle piece.

the piece on left of illustration freeform style jigsaw puzzle. more complex because uses 3 different side designs. assume there additional side designs haven't shown because 3-sided design show not allow pieces interlock complete puzzle.

you have several options when creating freeform style jigsaw puzzle.

non-interlocking freeform style

in style, take image , draw lines cut non-uniform pieces can arranged form image. think of pizza that's been sliced randomly. can fit pieces reform pizza if pieces not interlock. mmmmm, pizza! :-)

interlocking freeform style

in style, design 2+ sides , create puzzle same way traditional style puzzle. create 1 design use left-right sides , second design use top-bottom sides. complexity 2 types of sides must fit meet. means side-type-1 must share mirrored pattern intersects side-type-2.

so design piece on left side of illustration, must decide if want interlocking-freeform or non-interlocking-freeform.

non-interlocking freeform easier. pull apart 3 types of sides , use them mirrored partners chop image.

for interlocking-freeform, more design work necessary on part. must create additional side designs interlock 3 designs you've created.

that's quick tour of jigsaw puzzles...good luck project!

[ additional details ]

for piece on right side of illustration, common "outside" looks "shoulders & head" silhouette.

the bezier-set create shoulders & head break down this:

  • a bezier "left shoulder"
  • a bezier "left neck"
  • a bezier "left head"
  • a bezier "right head"
  • a bezier "right neck"
  • a bezier "right shoulder"

a shoulder & head bezier set might this:

enter image description here

here's 1 specific example of control points create outside side "shoulders & head" shape:

var shouldersandheadcubicbeziercontrolpoints=[     {cx1:0,  cy1:0,  cx2:35,cy2:15, ex:37, ey:5},   // left shoulder     {cx1:37, cy1:5,  cx2:40,cy2:0,  ex:38, ey:-5},  // left neck     {cx1:38, cy1:-5, cx2:20,cy2:-20,ex:50, ey:-20}, // left head     {cx1:50, cy1:-20,cx2:80,cy2:-20,ex:62, ey:-5},  // right head     {cx1:62, cy1:-5, cx2:60,cy2:0,  ex:63, ey:5},   // right neck     {cx1:63, cy1:5,  cx2:65,cy2:15, ex:100,ey:0},   // right shoulder ]; 

once have "outside" set of curves, can use canvas's context transformations flip "outside" mirrored "inside". alternatively can manually reverse "outside" array of curve control points.

illustrations: top-tab , top-slot (top-slot top-tab mirrored)

enter image description here

example displaying top-tab , top-slot:

var canvas=document.getelementbyid("canvas");  var ctx=canvas.getcontext("2d");  var cw=canvas.width;  var ch=canvas.height;  function reoffset(){    var bb=canvas.getboundingclientrect();    offsetx=bb.left;    offsety=bb.top;          }  var offsetx,offsety;  reoffset();  window.onscroll=function(e){ reoffset(); }    ctx.linewidth=3;  var colors=['red','green','blue','gold','purple','cyan'];    var bset=makebeziers();    draw(bset,50,100);    var bsetmirrored=mirror(bset,1,-1,0,0);    draw(bsetmirrored,50,200);    function draw(bset,transx,transy){    ctx.translate(transx,transy);    ctx.scale(2,2);    for(var i=0;i<bset.length;i++){      var b=bset[i];      ctx.beginpath();      ctx.beziercurveto(b.cx1,b.cy1,b.cx2,b.cy2,b.ex,b.ey);      ctx.strokestyle=colors[i];      ctx.stroke();    }    ctx.settransform(1,0,0,1,0,0);  }      function makebeziers(){    return([      {cx1:0,  cy1:0,  cx2:35,cy2:15, ex:37, ey:5},   // left shoulder      {cx1:37, cy1:5,  cx2:40,cy2:0,  ex:38, ey:-5},  // left neck      {cx1:38, cy1:-5, cx2:20,cy2:-20,ex:50, ey:-20}, // left head      {cx1:50, cy1:-20,cx2:80,cy2:-20,ex:62, ey:-5},  // right head      {cx1:62, cy1:-5, cx2:60,cy2:0,  ex:63, ey:5},   // right neck      {cx1:63, cy1:5,  cx2:65,cy2:15, ex:100,ey:0},   // right shoulder    ]);      }        function mirror(b,signx,signy,x,y){      var a=[];           for(var i=0;i<b.length;i++){      var bb=b[i];      a.push({        cx1: bb.cx1*signx+x,        cy1: bb.cy1*signy+y,        cx2: bb.cx2*signx+x,        cy2: bb.cy2*signy+y,        ex:  bb.ex*signx+x,        ey:  bb.ey*signy+y      });    }    return(a);  }
body{ background-color: ivory; }  #canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>


Comments