Pomax / bezierjs

A nodejs and client-side library for (cubic) Bezier curve work
MIT License
1.73k stars 233 forks source link

I can't combine one of your examples with my canvas draw #77

Closed josever90 closed 7 years ago

josever90 commented 7 years ago

Hi, I have a canvas draw that has curves and I want to know the size of it like one of your examples. example

How can I combine your example with my canvas draw?

This is my javascript code:

<script type="text/javascript">
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

c_size = 650;

ctx.canvas.width  = c_size;
ctx.canvas.height = c_size;

ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.moveTo(535,105);
ctx.quadraticCurveTo(585,44,620,115);
ctx.quadraticCurveTo(628,155,643,155);
ctx.quadraticCurveTo(628,195,643,360);
ctx.lineTo(550,368);
ctx.lineTo(538,302);
ctx.lineTo(552,285);
ctx.quadraticCurveTo(528,195,535,105);
ctx.stroke();
</script>
<canvas id='canvas' width='650' height='650' style="border: 1px solid #000">
Canvas not supported
</canvas>
Pomax commented 7 years ago

Think about this out loud: you're not drawing "a bezier curve", you are drawing an entire path consisting of many segments. Bezier.js only works with individual curves, so if you want to use Bezier.js, you're going to have to tally up the lengths of your segments, measuring their length as you draw each individual segment.

So to do that right, separate your data from your drawing, so you can work with it:

const mypath = [
  [535,105, 585,44, 620,115],
  [620,115, 628,155, 643,155],
  [643,155, 628,195, 643,360],
  [643,360, 550,368],
  [550,368, 538,302],
  [538,302, 552,285],
  [552,285, 528,195,535,105]
]

let totalLength = 0;

ctx.beginPath();

let start = mypath [0];
ctx.moveTo(start[0], start[1]);

mypath.forEach( (path,pos) => {
  // this part draws your path segment
  let relativeCoords = path.slice(2);
  if (path.length===2) { ctx.lineTo(...relativeCoords); }
  if (path.length===6) { ctx.quadraticCurveTo(...relativeCoords); }
  if (path.length===8) { ctx.curveTo(...relativeCoords); }

  // and this part calculates your segment length
  let b = new Bezier(...path);
  totalLength += b.length();
  console.log(`path length after adding segment ${pos} is ${totalLength}`);
});
ctx.stroke();

However, this is silly: the browser already has an API for measuring total path length built in, which not everyone knows about... SVG already has this built in =)

So you can already do this length checking without needing bezier.js, by creating a path and then calling path.getTotalLength():

const mypath = ... // same thing as above

// but now let's use an API we already get for free:
let path = document.createElementNS('http://www.w3.org/2000/svg', 'path);
let d = ""

ctx.beginPath();
let start = mypaths[0];

// Canvas2D initial moveto:
ctx.moveTo(start[0], start[1]);

// SVG pathing initial "moveto"
d += `M ${start[0]} ${start[1]}";

mypath.forEach( (path,pos) => {
  let relativeCoords = path.slice(2);
  if (path.length===2) { ctx.lineTo(...relativeCoords); d += " L "; }
  if (path.length===6) { ctx.quadraticCurveTo(...relativeCoords); d += " Q "; }
  if (path.length===8) { ctx.curveTo(...relativeCoords); d += " C "; }

  // and now we just ask the browser for the path lenght so far:
  d += relativeCoords.join(' ');
  path.setAttribute('d', d);
  console.log(`path length after adding segment ${pos} is ${ path.getTotalLength() }`);
});
ctx.stroke();