memoscan
← all memos

Memo 0x6123d590…6dff6a on Ethereum

console.log("Scratch v5.4 © Matto"); console.log(`TOKEN ENTROPY: ${tokenData.hash}`); const tokenId = tokenData.tokenId; const attributes = {}; let seed = makeSeed(tokenData.hash); let nonce = 0; let showcncPreview = false; let showSignature = true; const allRadii = [0]; const brushCounts = [0,0,0]; const ringQTY = selectByProbs([5,4,3,2,1], RR()); const brushTypeList = []; const m = (R(0, 200) / 100) - 1; const sizeStrings = ["Small", "Medium", "Large"]; const sizeColors = ["red", "green", "blue"]; let focusMode, orbitMode, pointMode, metal, finish, bkg, metalColor; let proxLim = 1000 / 5; RR() < .80 ? metal = "Al" : metal = "Cu"; metal == "Cu" ? metalColor = "#b87333" : metalColor = "grey"; RR() < .80 ? finish = "Brushed" : finish = "Polished"; let dark = selectByProbs([false, true], RR()); if (!tokenData.plot) { dark ? bkg = 32 : bkg = 224; } for (i = 0; i < ringQTY; i++) { brushTypeList[i] = selectByProbs([0,1,2], RR()); brushCounts[brushTypeList[i]]++; } RR() > .75 ? focusMode = true : focusMode = false; RR() > .50 ? pointMode = true : pointMode = false; RR() > .50 && pointMode == true ? orbitMode = true : orbitMode = false; addAttribute("FOCUS", focusMode); addAttribute("Point Mode", pointMode); addAttribute("Orbits", orbitMode); addAttribute("Ring Count", ringQTY.toString()); addAttribute("Small Disc Rings", brushCounts[0].toString()); addAttribute("Medium Disc Rings", brushCounts[1].toString()); addAttribute("Large Disc Rings", brushCounts[2].toString()); addAttribute("Metal", metal); addAttribute("Finish", finish); addAttribute("Dark", dark); console.log("Attributes:"); console.log(attributes); const svgStart = `<?xml version="1.0" encoding="utf-8"?><svg viewBox="0 0 1000 1000" style="background-color:rgb(${bkg},${bkg},${bkg})" xmlns="http://www.w3.org/2000/svg">`; let svg = ""; let sig = ""; let cncPreview = ""; const cncSVG = []; for (let i=0; i < 4; i++) { cncSVG[i] = ""; if (i != 3) { cncSVG[i] += `<desc>Sand the ${sizeColors[i]} paths with a disk that is ${i + 1}/24th the artwork size.</desc><g id="padsize-${i + 1}" style="stroke:${sizeColors[i]}; stroke-width: 1px; stroke-opacity:1; fill-opacity:0">`; } else { cncSVG[i] += `<desc>Carve the black paths with an end mill. For a 24 x 24 inch plate, use a 1/16-inch bit.</desc>`; } } function setup() { noCanvas(); noLoop(); } function draw() { let bkg = `<g id="background" style="stroke:${metalColor}; stroke-width: ${1000/1000}px; stroke-opacity:1; fill-opacity:0">`; let bMod; finish == "Brushed" ? bMod = 0 : bMod = R(100, 200); for (let b = -2000; b < 2000; b += (10/3) + Math.sin(b) * (10/4) + bMod) { const x0 = Math.max(0, -b / m); const x1 = Math.min(1000, (1000 - b) / m); const y0 = b + x0 * m; const y1 = b + x1 * m; const clipped = getClippedLine(x0, y0, x1, y1); if (clipped) { bkg += lineWithGaps(clipped.x0,clipped.y0,clipped.x1,clipped.y1); } } svg = svgStart + bkg + "</g>"; for (let p = 0; p < ringQTY; p++) { let plotString = ""; let cncString = ""; const brushType = brushTypeList[p]; let radius, newStrings; const padding = 100 + (brushType + 1) * (1000 / 48); if (p == 0 && focusMode) { radius = (brushType + 1) * (2000 / 24); allRadii.push(radius); newStrings = addPoint(500, 500, brushType); } else { radius = randRad(padding); newStrings = addCircle(500, 500, radius, brushType); } plotString += newStrings.plot; cncString += newStrings.cnc; cncSVG[brushType] += newStrings.cnc; svg += `<g id="ring-${p}" style="stroke:${metalColor}; stroke-width: 1px; stroke-opacity:1; fill-opacity:0"><desc>Ring Radius: ${radius}, Disc Size: ${sizeStrings[brushType]}</desc>` + plotString + "</g>"; cncPreview += `<g id="cnc-${p}" style="stroke:purple; stroke-width: 2.6px; stroke-opacity:1; fill-opacity:0">` + cncString + "</g>"; } sig += `<polyline points="924,956 920,956 920,860 940,872 960,860 960,956 956,956" />`; sig += `<polyline points="928,902 940,872 952,902" stroke-linejoin="bevel" />`; sig += `<line x1="934" y1="888" x2="946" y2="888" />`; sig += `<line x1="920" y1="902" x2="960" y2="902" />`; sig += `<line x1="932" y1="902" x2="932" y2="928" />`; sig += `<line x1="948" y1="902" x2="948" y2="928" />`; sig += `<circle cx="940" cy="940" r="15" />`; let sigCNCStart = `<g id="cnc-signature" style="stroke:black; stroke-width: 2.6px; stroke-opacity:1; fill-opacity:0">`; cncPreview += sigCNCStart + sig + "</g>"; cncSVG[3] += sigCNCStart + sig; sig = `<g id="signature" style="stroke:${metalColor}; stroke-width: 2.6px; stroke-opacity:1; fill-opacity:0"><desc>Suggested Pen Thickness: 2.6x</desc>` + sig + "</g>"; for (let i = 0; i < 4; i++) { cncSVG[i] += "</g>"; } document.body.insertAdjacentHTML('beforeend', svg + sig + "</svg>"); console.log("CNC SVG:"); console.log(compilecncSVG()); } function addAttribute(trait_type, value) { attributes[trait_type] = value; } function makeSeed(input) { let s = 1; for (let i = 0; i < (input.length - 2) / 2; i++) { let v = parseInt(input.slice(2 + i * 2, 4 + i * 2), 16); s = ((s * v) % 999999999999) + 1 } return s; } function R(min, max) { const range = max - min + 1; const v = (seed % range) + min; newSeed(); return Math.floor(v); } function RR() { return R(1,100)/100; } function newSeed() { nonce++; seed = ((seed * 35932678341237) + nonce) % 999999999999 + 1 } function keyPressed() { if (tokenData.plot != true) { const k = key.toUpperCase(); if (k === "S") { saveStrings([svg + sig + "</svg>"], `SCRATCH-${tokenId}`, "svg"); } else if (k === "C") { showcncPreview = !showcncPreview; updateSVG(); } else if (k === "H") { showSignature = !showSignature; updateSVG() } else if (k === "E") { saveStrings([compilecncSVG()], `SCRATCH-${tokenId}-CNC`, "svg"); } } return false; } function updateSVG() { let comp = svg; if (showSignature) { comp += sig; } if (showcncPreview) { comp += cncPreview; } document.body.innerHTML = ''; document.body.insertAdjacentHTML('beforeend', comp + "</svg>"); } function randRad(padding) { const radius = R(allRadii[0], (500) - padding); let tooClose = false; for (let i = 0; i < allRadii.length; i++) { if (Math.abs(radius - allRadii[i]) < proxLim) { tooClose = true; proxLim -= 1; break; } } if (!tooClose) { allRadii.push(radius); proxLim = 1000 / 5; return radius; } else { return randRad(padding); } } function selectByProbs(list, randomValue) { const totalWeight = list.reduce(function(acc, item, index) { return acc + (1 / (index + 2)); }, 0); const threshold = totalWeight * randomValue; let currentSum = 0; for (let i = 0; i < list.length; i++) { currentSum += 1 / (i + 2); if (currentSum >= threshold) { return list[i]; } } return list[list.length - 1]; } function compilecncSVG() { let list = svgStart; list += `<desc>The grey border is a visual guide. Please scale the vectors appropriately.</desc><rect x="0" y="0" width="1000" height="1000" style="stroke:grey; stroke-width: 1px; stroke-opacity:1; fill-opacity:0" />` for (let i = 0; i < 4; i++) { if (cncSVG[i].length > 180) { list += cncSVG[i]; } } return list + "</svg>"; } function addPoint(x,y, brushType) { let plotString = ""; const cncString = `<circle cx="${x}" cy="${y}" r="4" />`; for (let i = 0; i < R(5,7); i++) { plotString += concentrics(x, y, (brushType + 1) * (1000 / 24), 1); } return {plot: plotString, cnc: cncString}; } function getY(x0, y0, x1, y1, x) { const m = (y1 - y0) / (x1 - x0); const b = y0 - m * x0; const y = m * x + b; return y; } function getX(x0, y0, x1, y1, y) { const m = (y1 - y0) / (x1 - x0); const b = y0 - m * x0; const x = (y - b) / m; return x; } function lineWithGaps(x0, y0, x1, y1) { let xT, yT; if(x0 > x1) { xT = x0; yT = y0; x0 = x1; y0 = y1; x1 = xT; y1 = yT; } let xS = x0; let yS = y0; let yE, xE; let str = ""; let lfg = true; while (lfg) { let dS; let dnm = [[60,4,48,12],[80,12,40,8]]; finish == "Brushed" ? dS = 0 : dS = 1; let lSeg = R((1000 / dnm[dS][0]), (1000 / dnm[dS][1])); let gSeg = R((1000 / dnm[dS][2]), (1000 / dnm[dS][3])); xS + lSeg > x1 ? xE = x1 : xE = xS + lSeg; yE = getY(x0, y0, x1, y1, xE); str += `<line x1="${xS}" y1="${yS}" x2="${xE}" y2="${yE}" />` xE + gSeg > x1 ? lfg = false : xS = xE + gSeg; xS = xE + gSeg; yS = getY(x0, y0, x1, y1, xS); } return str; } function addCircle(x,y, cr, brushType) { const r = (brushType + 1) * (1000 / 24); const orbitLocs = R(2,6); let locs; const pad = (1000 / 15) + (brushType + 1) * (1000 / 48); let plotString = ""; let cncString = ""; if (pointMode) { locs = R(2,20); } else { locs = Math.floor((2 * PI * cr) / 10); cncString = `<circle cx="${x}" cy="${y}" r="${cr}" />`; } const angleStep = 2 * PI / locs; for (let i = 0; i < locs; i++) { const angle = i * angleStep; const xC = x + cr * Math.cos(angle); const yC = y + cr * Math.sin(angle); if (pointMode) { const newStrings = addPoint(xC, yC, brushType); plotString += newStrings.plot; cncString += newStrings.cnc; if (orbitMode && cr > 200) { const closestEdgeDist = Math.min(Math.min(Math.abs(1000 - xC), xC), Math.min(Math.abs(1000 - yC), yC)); if (closestEdgeDist > pad + 2 * r) { const newStringsOrbit = addOrbit(xC, yC, 2 * r, brushType, orbitLocs); plotString += newStringsOrbit.plot; cncString += newStringsOrbit.cnc; } } } else { plotString += concentrics(xC, yC, r, 0); } } return {plot: plotString, cnc: cncString}; } function addOrbit(x, y, cr, brushType, locs) { const r = (brushType + 1) * (1000 / 24); let plotString = ""; let cncString = ""; const angleStep = 2 * PI / locs; for (let i = 0; i < locs; i++) { const angle = i * angleStep; const xC = x + cr * Math.cos(angle); const yC = y + cr * Math.sin(angle); const newStrings = addPoint(xC, yC, brushType); plotString += newStrings.plot; cncString += newStrings.cnc; } return {plot: plotString, cnc: cncString}; } function concentrics(x,y,r,f) { r /= 2; let arcs = ""; if (f == 1) { x = x + R(-4, 4); y = y + R(-4, 4); } for (let i = 10; i < r; i += R(r/25, r/8) ) { arcs += randomArcSVG(x,y,i); } return arcs; } function randomArcSVG(x, y, radius) { const getRandomAngle = () => R(0,628) / 100; const startAngle = getRandomAngle(); const endAngle = getRandomAngle(); let sweepAngle = endAngle - startAngle; if (sweepAngle < 0) { sweepAngle += 2 * Math.PI; } const startX = x + radius * Math.cos(startAngle); const startY = y + radius * Math.sin(startAngle); const endX = x + radius * Math.cos(endAngle); const endY = y + radius * Math.sin(endAngle); const largeArcFlag = sweepAngle > Math.PI ? 1 : 0; const sweepFlag = 1; const d = `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${endX} ${endY}`; return `<path d="${d}" />`; } function getClippedLine(x0, y0, x1, y1) { const xmin = R((100 / 24),(1000 / 48)); const ymin = R((100 / 24),(1000 / 48)); const xmax = 1000-R((100 / 24),(1000 / 48)); const ymax = 1000-R((100 / 24),(1000 / 48)); const inside = (x, y) => x >= xmin && x <= xmax && y >= ymin && y <= ymax; if (inside(x0, y0) && inside(x1, y1)) { return { x0, y0, x1, y1 }; } const m = (y1 - y0) / (x1 - x0); const b = y0 - m * x0; const xLeft = xmin; const yLeft = m * xLeft + b; const xRight = xmax; const yRight = m * xRight + b; const yTop = ymin; const xTop = (yTop - b) / m; const yBottom = ymax; const xBottom = (yBottom - b) / m; const intersections = [ { x: xLeft, y: yLeft }, { x: xRight, y: yRight }, { x: xTop, y: yTop }, { x: xBottom, y: yBottom } ]; const validIntersections = intersections.filter(p => inside(p.x, p.y)); if (validIntersections.length >= 2) { return { x0: validIntersections[0].x, y0: validIntersections[0].y, x1: validIntersections[1].x, y1: validIntersections[1].y }; } return null; }