I want to to take any shape, create a smaller version of it, and then use a negative mask to create a new shape that is like an outline of the first object, but with a constant thickness border. To my understanding, this is generally not possible without distorting the smaller image slightly (e.g. changing it's aspect ratio). So I'm ok if the solution creates some distortion of the smaller image.
I also know how to create a negative mask:
ctx.globalCompositeOperation = 'source-out'
Don’t worry about giving me the code for drawing these shapes on the canvas. I have code that does that fine. The problem I’m finding is that the border thickness is not constant because I need to distort the smaller shape to make it so. At the moment I haven’t distorted it and don’t know how.
One approach I thought of might be to move along the outer edge of the large image, and for each point, draw a corresponding point some constant distance inwards, which will be on the edge of the smaller image. In effect tracing the smaller image as you go around the larger one. But I don’t know how to sure the angle is correct. Do I have to use some radius of curvature?
Whether it's a some kind of mathematical solution, algorithm or a pointer to a library that I can use I'm all ears!
Thanks in advance.
Here is one possibility using destination-out
to cut out a piece at a time in a temporary canvas to later combine them in the "main" canvas, it's a small code at the end ...
the image I'm starting with is a sunflower:
I thought that would be a good start because of all the odd shape edges
var s = 10 // thickness scale
var img = new Image;
img.onload = draw;
img.src = "https://i.imgur.com/eKKoWVT.png"
var canvas = document.getElementById('c')
var t_canvas = document.createElement('canvas');
t_canvas.width = canvas.width
t_canvas.height = canvas.height
var ctx = canvas.getContext('2d')
var t_ctx = t_canvas.getContext('2d')
function draw() {
for (i = 0; i < 8; i += Math.PI / 64) {
t_ctx.globalCompositeOperation = "source-over";
t_ctx.drawImage(img, 0, 0, 400, 400);
t_ctx.globalCompositeOperation = "destination-out";
t_ctx.drawImage(img, Math.sin(i) * s, Math.cos(i) * s, 400, 400);
ctx.drawImage(t_canvas, 0, 0, 400, 400);
}
}
<canvas id="c" width=400 height=400></canvas>
Here is a "slow motion" process showing how the inline drawing is extracted from the big image
var s = 50 // thickness scale
var i = 0
var img = new Image;
img.onload = draw
img.src = "https://i.imgur.com/eKKoWVT.png"
var canvas = document.getElementById('c')
var t_canvas = document.createElement('canvas');
t_canvas.width = canvas.width
t_canvas.height = canvas.height
var ctx = canvas.getContext('2d')
var t_ctx = t_canvas.getContext('2d')
function draw() {
t_ctx.globalCompositeOperation = "source-over";
t_ctx.drawImage(img, 0, 0, 400, 400);
t_ctx.globalCompositeOperation = "destination-out";
t_ctx.drawImage(img, Math.sin(i) * s, Math.cos(i) * s, 400, 400);
ctx.globalCompositeOperation = "xor";
ctx.drawImage(t_canvas, 0, 0, 400, 400);
ctx.moveTo(200 + Math.sin(i) * 9, 200 + Math.cos(i) * 9)
ctx.lineTo(200 + Math.sin(i) * s, 200 + Math.cos(i) * s)
ctx.stroke()
i += Math.PI / 64
if (i < 8)
setTimeout(draw, 100);
}
<canvas id="c" width=400 height=400></canvas>