/* use stacked sine waves (and possibly other things for sharp bits?) to make a nice shape for the moths' wings. possibly use an envelope over that */ // this initial function takes an input value (an angle) in the 0-1 range and spits out a radius // you could also remap this into x and y space for testing function getWaveValue(x, seed) { seedDRand(seed); var frequency0 = dRandomFloatIn(1, 5); var frequency1 = dRandomFloatIn(1, 3); if (dRandomInt(3) == 0) frequency1 = dRandomFloatIn(0, 32); var frequency2 = dRandomFloatIn(0, 1); var input = 2 * Math.PI * x; var masterAmplitude = 0.2; var amplitude0 = dRandomFloatIn(0.1, 0.25); var amplitude1 = dRandomFloatIn(0.1, 0.25); var result = 0.8 + masterAmplitude * (amplitude0 * Math.sin(frequency0 * input) + amplitude1 * Math.sin(frequency1 * input) + Math.sin(frequency2 * input)); // possibly add rounding at edges here var roundingSize = dRandomFloatIn(0.05, 0.15); if (dRandomInt(3) == 0) roundingSize = dRandomFloatIn(0.1, 0.5); if (x < roundingSize) result *= Math.sin(x / roundingSize * Math.PI * 0.5) if (x > 1 - roundingSize) result *= Math.sin((1 - x) / roundingSize * Math.PI * 0.5) return result; } // draws a single pair of wings in black on white // with the specified seed at the specified angle // can be composited with other pairs to make a single mask // which can then be applied to the wing texture function waveWingEdge(x, y, currentWingAngle, xOffset, currentWingArc, wingSeed, scale, sign) { var angle; var value; var startAngle = currentWingAngle; var radius = 0.4 * mainCanvas.width; var newX, newY; var strokeLength = 0.05 * radius; var pass1Offset; var pass2Offset = 0.05 * radius; context.fillStyle = "#ffffff"; context.fillRect(0, 0, mainCanvas.width, mainCanvas.height); if (true) { //for (sign = -1; sign <= 1; sign += 2) { seedDRand(wingSeed); context.save(); context.translate(400 + sign * xOffset, y); //context.rotate(sign * wingAngle); context.strokeStyle = "#ffffff"; context.lineCap = "round"; // fill wing shape if (true) { context.save(); context.strokeStyle = "black"; context.lineWidth = 12; context.lineJoin = "round"; context.fillStyle = "black"; context.moveTo(0, 0); context.beginPath(); context.globalAlpha = 1; for (i = 0; i <= 1.001; i += 0.01) { value = getWaveValue(i, wingSeed); angle = -i * currentWingArc + startAngle; newX = sign * value * (radius + 10) * Math.cos(angle); newY = -value * (radius + 10) * Math.sin(angle); pass1Offset = (Math.random() * 0.5 - 0.25) * 0.07 * radius; context.lineTo(newX, newY); } context.closePath(); context.fill(); context.restore(); //context.clip(); //context.stroke(); // tried using this to soften corners, but the wide edges break patterns } // feather edges context.save(); if (true) { for (i = 0; i <= 1.001; i += 0.005) { value = getWaveValue(i, wingSeed); if (value > 0.6) { //if (true) { strokeLength = 1.0 * radius; context.beginPath(); context.lineWidth = 3 + Math.random() * 3; context.globalAlpha = Math.random() * 0.5; angle = -i * currentWingArc + startAngle; newX = sign * value * radius * Math.cos(angle); newY = -value * radius * Math.sin(angle); pass1Offset = (Math.random() * 0.3 - 0.3) * 0.07 * radius; context.moveTo(newX + sign * pass1Offset * Math.cos(angle), newY - pass1Offset * Math.sin(angle)); context.lineTo(newX + sign * strokeLength * Math.cos(angle), newY - strokeLength * Math.sin(angle)); context.stroke(); // second, fully opaque line context.beginPath(); context.globalAlpha = 1; pass2Offset = (Math.random() * 0.5) * 0.07 * radius; context.moveTo(newX + sign * pass2Offset * Math.cos(angle), newY - pass2Offset * Math.sin(angle)); context.lineTo(newX + sign * strokeLength * Math.cos(angle), newY - strokeLength * Math.sin(angle)); context.stroke(); } } } // bridge center to soften wing shape if (false) { context.save(); context.strokeStyle = "#000000"; for (i = 0; i < 5; i ++) { context.lineWidth = 12 + 8 * i; context.globalAlpha = .2 + 0.1 * i; context.beginPath(); context.moveTo(sign * 32 * Math.cos(startAngle - currentWingArc), -32 * Math.sin(startAngle - currentWingArc)); context.lineTo(sign * 60 * Math.cos(startAngle - currentWingArc / 2), -60 * Math.sin(startAngle - currentWingArc / 2)); context.stroke(); } context.restore(); } context.restore(); context.restore(); } } // draws just the outline, for testing purposes function waveWingEdgeTest(x, y, currentWingAngle, xOffset, currentWingArc, wingSeed, scale) { var angle; var value; var startAngle = currentWingAngle; var radius = 0.4 * mainCanvas.width; var newX, newY; var strokeLength = 0.05 * radius; var pass1Offset; var pass2Offset = 0.05 * radius; for (sign = -1; sign <= 1; sign += 2) { seedDRand(wingSeed); context.save(); context.translate(400 + sign * xOffset, y); //context.rotate(sign * wingAngle); context.strokeStyle = "#ffffff"; // test angle context.save(); context.beginPath(); context.strokeStyle = "#000000"; context.lineWidth = 8; context.globalAlpha = 1; angle = startAngle; //context.rotate(sign * wingAngle); context.moveTo(0, 0); newX = sign * radius * Math.cos(wingAngle); newY = radius * -Math.sin(wingAngle); context.lineTo(newX, newY); context.stroke(); context.restore(); context.lineCap = "round"; // fill wing shape if (true) { context.save(); context.strokeStyle = "black"; context.lineWidth = 1; context.fillStyle = "black"; context.beginPath(); context.moveTo(0, 0); context.globalAlpha = 1; for (i = 0; i <= 1.001; i += 0.01) { value = getWaveValue(i, wingSeed); angle = -i * currentWingArc + startAngle; newX = sign * value * radius * Math.cos(angle); newY = -value * radius * Math.sin(angle); pass1Offset = (Math.random() * 0.5 - 0.25) * 0.07 * radius; context.lineTo(newX, newY); } context.closePath(); context.stroke(); context.restore(); } context.restore(); context.restore(); } }