p5(s => {
let p1, p2;
s.preload = () => {
p1 = s.loadImage(sourceImg ? sourceImg : oriImg);
}
let yStep = 160, yMult = 6, xStep = 3, xSmooth = 128, imageScaleUp, r = 0, a = 0,strokeWidth,startX, startY, z, b, oldB, maxB, minB, invert, connectEnds, blur;
let done = false;
let scaleFactor, xOffset, deltaPhase, deltaX, deltaAmp, minPhaseIncrement, maxPhaseIncrement, oddRow, finalRow, reverseRow, lastX, scaledYStep, yc;
s.setup = () => {
init();
scaleFactor = 1 / imageScaleUp;
xOffset = 0;
minPhaseIncrement = 10 * Math.PI * 2 / (p2.width / xStep);
maxPhaseIncrement = Math.PI * 2 * xStep / strokeWidth;
oddRow = false, finalRow = false, scaledYStep = p2.height / yStep;
yc = 0
if (connectEnds) s.beginShape();
s.textAlign(s.CENTER);
s.textSize(16);
}
s.draw = () => {
s.stroke(0);
s.noFill();
s.strokeWeight(strokeWidth * scaleFactor);
if (!done) {
if (connectEnds) s.text('Working...', s.width/2, s.height/2);
if (!connectEnds) s.beginShape();
oddRow = !oddRow;
if (yc + scaledYStep >= p2.height) finalRow = true;
reverseRow = (connectEnds && !oddRow);
a = 0;
if (reverseRow) {
if (!connectEnds || yc === 0) {
// extra initial point on first line or beginning of each lines if ends not connected
s.curveVertex(xOffset + scaleFactor * (p2.width + 0.1 * xStep), scaleFactor * yc);
}
s.curveVertex(xOffset + scaleFactor * (p2.width), scaleFactor * yc);
} else {
if (!connectEnds || yc === 0) {
// extra initial point on first line or beginning of each lines if ends not connected
s.curveVertex(xOffset - scaleFactor * (0.1 * xStep), scaleFactor * yc);
}
s.curveVertex(xOffset, scaleFactor * yc);
}
// now step along width of the image
let phase = 0, lastPhase = 0, lastAmp = 0, finalStep = false;
let x = 1, lastX = 1, xPoints = [], yPoints=[];
while(!finalStep){
// move from right to left
x += xStep;
if (x + xStep >= p2.width) finalStep = true;
b = s.brightness(p2.get(x,yc));
b = s.max(minB, b);
z = s.max(maxB - b, 0);
r = z / yStep * yMult;
//apply the min phase increment to avoid gaps
let df = z/xSmooth;
if (df < minPhaseIncrement) df = minPhaseIncrement;
//max phase increment, cap the freqency as at certain level, higer frequency doesnt make visual difference anymore
if (df > maxPhaseIncrement) df = maxPhaseIncrement;
phase += df;
deltaX = x - lastX;
deltaAmp = r - lastAmp;
deltaPhase = phase - lastPhase;
if (!finalStep) {
if (deltaPhase > Math.PI / 2) {
// linearly interpolate phase and amplitude since last vertex added
let verticeCount = Math.floor(deltaPhase / (Math.PI / 2));
let ip = (verticeCount * Math.PI / 2) / deltaPhase;
let dt = deltaX * ip;
let xPerVertex = dt / verticeCount;
let ampPerVertex = (ip * deltaAmp) / verticeCount;
for (let i = 0; i < verticeCount; ++i) {
lastX = lastX + xPerVertex;
lastPhase = lastPhase + Math.PI / 2;
lastAmp = lastAmp + ampPerVertex;
xPoints.push(xOffset + scaleFactor * lastX);
yPoints.push(scaleFactor * (yc + Math.sin(lastPhase) * lastAmp))
}
}
}
}
if (reverseRow) {
xPoints.reverse();
yPoints.reverse();
}
for (let i = 0; i < xPoints.length; ++i) {
s.curveVertex(xPoints[i], yPoints[i]);
}
// add a final point to make the visual consistent
if (reverseRow) {
s.curveVertex(xOffset, yc * scaleFactor);
if (!connectEnds || finalRow) {
s.curveVertex(xOffset - scaleFactor* (0.1 * xStep), yc * scaleFactor);
}
} else {
s.curveVertex(xOffset + scaleFactor * p2.width, scaleFactor * yc);
if (!connectEnds || finalRow) {
s.curveVertex(xOffset + scaleFactor* (p2.width + 0.1 * xStep), yc * scaleFactor);
}
}
if (connectEnds && !finalRow){
// add end connectors
if (reverseRow) {
s.curveVertex(xOffset - scaleFactor * (0.1 * xStep + scaledYStep / 3), (yc + scaledYStep/2) * scaleFactor);
} else {
s.curveVertex(xOffset + scaleFactor * (p2.width + 0.1 * xStep + scaledYStep / 3),(yc + scaledYStep/2) * scaleFactor);
}
}
if (!connectEnds) s.endShape();
yc += scaledYStep;
if (yc >= p2.height) done = true;
} else {
if (connectEnds) {
s.background(255);
s.endShape();
}
s.noLoop();
}
}
function init(){
// set parameters
yStep = options.lineNo;
yMult = options.squiggleStrength;
xStep = 31 - options.detail;
invert = options.inverted;
connectEnds = options.connectEnds;
strokeWidth = options.lineW;
maxB = options.white;
minB = options.black;
xSmooth = 257 - options.freq;
imageScaleUp = options.scale;
blur = options.blur;
startX = 0, startY = 0;
// process source image
p1.filter(s.GRAY); // converted to gray scale
if (invert) p1.filter(s.INVERT);
if (blur > 0) p1.filter(s.BLUR, blur);
// create another image for buffer
p2 = s.createImage(p1.width * imageScaleUp, p1.height * imageScaleUp);
p2.copy(p1, 0, 0, p1.width, p1.height, 0, 0, p2.width, p2.height);
s.createCanvas(p1.width, p1.height);
}
})