function maskedFiberTexture(mask, options = {}) {
let {
width = 800,
height = 600,
linewidth = 1,
coarseness = 10,
waviness = 0.2,
background = "white",
backgroundOpacity = 1,
color = "black",
colorVariation = 0,
threadLen = 10,
opacity = 0.2,
randomSeed = 0
} = options;
const canvas = DOM.canvas(width, height);
const ctx = canvas.getContext("2d");
const baseColor = d3.hsl(color);
const sep = coarseness;
const avgLen = sep * 2;
const rand = makeRandFunction(randomSeed);
const pds = new Poisson(
{
shape: [width, height],
minDistance: sep * 0.8,
maxDistance: sep / 0.8,
tries: 15
},
rand
);
background = d3.color(background);
background.opacity = backgroundOpacity;
ctx.fillStyle = background.formatRgb();
ctx.fillRect(0, 0, width, height);
ctx.lineWidth = linewidth;
const [minv, maxv] = [
Math.max(0, 1 - colorVariation),
Math.min(1.5, 1 + colorVariation)
];
for (let [x, y] of pds.fill()) {
let angle;
if (mask) {
if (mask.getPixel(x, y)[3] != 255) continue;
const [gx, gy] = mask.getGradient(x, y);
const g = Math.min(1, Math.hypot(gx, gy) / 10);
angle =
g * (Math.atan2(gy, gx) + Math.PI * (0.5 + ~~rand(2))) +
(1 - g) * rand(-Math.PI, Math.PI);
} else {
angle = rand(-Math.PI, Math.PI);
}
let [px, py] = [x, y];
ctx.beginPath();
let pts = [[x, y]];
for (let i = 0; i < threadLen * 3; i++) {
let wav = waviness;
let ang = angle;
for (let tries = 5; tries >= 0; tries--) {
const len = sep * 0.33 * rand(0.7, 1 / 0.7);
const ang = angle + Math.PI * 0.25 * wav * rand(-1, 1);
const p = [px + len * Math.cos(ang), py + len * Math.sin(ang)];
if (!mask || mask.getPixel(...p)[3] == 255) {
[px, py] = p;
angle = ang;
pts.push(p);
break;
}
wav *= 1.5;
}
}
let color = d3.hsl(
(baseColor.h + 180 * colorVariation * rand(-1, 1)) % 360,
Math.max(0, Math.min(1, baseColor.s + colorVariation * rand(-1, 1))),
Math.max(0, Math.min(1, baseColor.l + colorVariation * rand(-1, 1)))
);
color.opacity = opacity;
ctx.strokeStyle = color.formatRgb();
for (let p of makeCurve(pts)) ctx.lineTo(...p);
ctx.stroke();
}
return canvas;
}