From 144eed8fd4da1f075ae79ebfd97c55ea09b651e2 Mon Sep 17 00:00:00 2001 From: randy1burrell Date: Tue, 11 Apr 2023 22:23:26 -0500 Subject: [PATCH] feat: Skip certain combination of layers This commit skips certain combinations of layers. --- README.md | 16 +++++ src/config.js | 4 ++ src/main.js | 163 +++++++++++++++++++++++++++++++------------------- 3 files changed, 120 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 98a393c2e..bb7eb36b3 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ const layerConfigurations = [ { name: "Eyeswear" }, { name: "Headwear" }, ], + skipLayerMatch: [ + ["Black", "Green"], + ["Middle", "Purple"], + ] }, ]; ``` @@ -84,6 +88,10 @@ const layerConfigurations = [ { name: "Eyeswear" }, { name: "Headwear" }, ], + skipLayerMatch: [ + ["Black", "Green"], + ["Middle", "Purple"], + ] }, { // Creates an additional 100 artworks @@ -97,6 +105,10 @@ const layerConfigurations = [ { name: "Headwear" }, { name: "AlienHeadwear" }, ], + skipLayerMatch: [ + ["Black", "Green"], + ["Middle", "Purple"], + ] }, ]; ``` @@ -141,6 +153,10 @@ const layerConfigurations = [ { name: "Bottom lid", options: { blend: MODE.overlay, opacity: 0.7 } }, { name: "Top lid" }, ], + skipLayerMatch: [ + ["Black", "Green"], + ["Middle", "Purple"], + ] }, ]; ``` diff --git a/src/config.js b/src/config.js index c46d867a0..c176244bc 100644 --- a/src/config.js +++ b/src/config.js @@ -34,6 +34,10 @@ const layerConfigurations = [ { name: "Bottom lid" }, { name: "Top lid" }, ], + skipLayerMatch: [ + ["Black", "Green"], + ["Middle", "Purple"], + ] }, ]; diff --git a/src/main.js b/src/main.js index e9c08dcf2..b4ff5cf49 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,6 @@ const basePath = process.cwd(); const { NETWORK } = require(`${basePath}/constants/network.js`); -const fs = require("fs"); +const fs = require('fs'); const sha1 = require(`${basePath}/node_modules/sha1`); const { createCanvas, loadImage } = require(`${basePath}/node_modules/canvas`); const buildDir = `${basePath}/build`; @@ -23,12 +23,12 @@ const { gif, } = require(`${basePath}/src/config.js`); const canvas = createCanvas(format.width, format.height); -const ctx = canvas.getContext("2d"); +const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = format.smoothing; var metadataList = []; var attributesList = []; var dnaList = new Set(); -const DNA_DELIMITER = "-"; +const DNA_DELIMITER = '-'; const HashlipsGiffer = require(`${basePath}/modules/HashlipsGiffer.js`); let hashlipsGiffer = null; @@ -45,10 +45,10 @@ const buildSetup = () => { } }; -const getRarityWeight = (_str) => { +const getRarityWeight = _str => { let nameWithoutExtension = _str.slice(0, -4); var nameWithoutWeight = Number( - nameWithoutExtension.split(rarityDelimiter).pop() + nameWithoutExtension.split(rarityDelimiter).pop(), ); if (isNaN(nameWithoutWeight)) { nameWithoutWeight = 1; @@ -56,24 +56,24 @@ const getRarityWeight = (_str) => { return nameWithoutWeight; }; -const cleanDna = (_str) => { +const cleanDna = _str => { const withoutOptions = removeQueryStrings(_str); - var dna = Number(withoutOptions.split(":").shift()); + var dna = Number(withoutOptions.split(':').shift()); return dna; }; -const cleanName = (_str) => { +const cleanName = _str => { let nameWithoutExtension = _str.slice(0, -4); var nameWithoutWeight = nameWithoutExtension.split(rarityDelimiter).shift(); return nameWithoutWeight; }; -const getElements = (path) => { +const getElements = path => { return fs .readdirSync(path) - .filter((item) => !/(^|\/)\.[^\/\.]/g.test(item)) + .filter(item => !/(^|\/)\.[^\/\.]/g.test(item)) .map((i, index) => { - if (i.includes("-")) { + if (i.includes('-')) { throw new Error(`layer name can not contain dashes, please fix: ${i}`); } return { @@ -86,34 +86,34 @@ const getElements = (path) => { }); }; -const layersSetup = (layersOrder) => { +const layersSetup = layersOrder => { const layers = layersOrder.map((layerObj, index) => ({ id: index, elements: getElements(`${layersDir}/${layerObj.name}/`), name: - layerObj.options?.["displayName"] != undefined - ? layerObj.options?.["displayName"] + layerObj.options?.['displayName'] != undefined + ? layerObj.options?.['displayName'] : layerObj.name, blend: - layerObj.options?.["blend"] != undefined - ? layerObj.options?.["blend"] - : "source-over", + layerObj.options?.['blend'] != undefined + ? layerObj.options?.['blend'] + : 'source-over', opacity: - layerObj.options?.["opacity"] != undefined - ? layerObj.options?.["opacity"] + layerObj.options?.['opacity'] != undefined + ? layerObj.options?.['opacity'] : 1, bypassDNA: - layerObj.options?.["bypassDNA"] !== undefined - ? layerObj.options?.["bypassDNA"] + layerObj.options?.['bypassDNA'] !== undefined + ? layerObj.options?.['bypassDNA'] : false, })); return layers; }; -const saveImage = (_editionCount) => { +const saveImage = _editionCount => { fs.writeFileSync( `${buildDir}/images/${_editionCount}.png`, - canvas.toBuffer("image/png") + canvas.toBuffer('image/png'), ); }; @@ -139,7 +139,7 @@ const addMetadata = (_dna, _edition) => { date: dateTime, ...extraMetadata, attributes: attributesList, - compiler: "HashLips Art Engine", + compiler: 'HashLips Art Engine', }; if (network == NETWORK.sol) { tempMetadata = { @@ -159,10 +159,10 @@ const addMetadata = (_dna, _edition) => { files: [ { uri: `${_edition}.png`, - type: "image/png", + type: 'image/png', }, ], - category: "image", + category: 'image', creators: solanaMetadata.creators, }, }; @@ -171,7 +171,7 @@ const addMetadata = (_dna, _edition) => { attributesList = []; }; -const addAttributes = (_element) => { +const addAttributes = _element => { let selectedElement = _element.layer.selectedElement; attributesList.push({ trait_type: _element.layer.name, @@ -179,14 +179,14 @@ const addAttributes = (_element) => { }); }; -const loadLayerImg = async (_layer) => { +const loadLayerImg = async _layer => { try { - return new Promise(async (resolve) => { + return new Promise(async resolve => { const image = await loadImage(`${_layer.selectedElement.path}`); resolve({ layer: _layer, loadedImage: image }); }); } catch (error) { - console.error("Error loading image:", error); + console.error('Error loading image:', error); } }; @@ -206,23 +206,23 @@ const drawElement = (_renderObject, _index, _layersLen) => { `${_renderObject.layer.name}${text.spacer}${_renderObject.layer.selectedElement.name}`, text.xGap, text.yGap * (_index + 1), - text.size + text.size, ) : ctx.drawImage( _renderObject.loadedImage, 0, 0, format.width, - format.height + format.height, ); addAttributes(_renderObject); }; -const constructLayerToDna = (_dna = "", _layers = []) => { +const constructLayerToDna = (_dna = '', _layers = []) => { let mappedDnaToLayers = _layers.map((layer, index) => { let selectedElement = layer.elements.find( - (e) => e.id == cleanDna(_dna.split(DNA_DELIMITER)[index]) + e => e.id == cleanDna(_dna.split(DNA_DELIMITER)[index]), ); return { name: layer.name, @@ -242,16 +242,16 @@ const constructLayerToDna = (_dna = "", _layers = []) => { * @param {String} _dna New DNA string * @returns new DNA string with any items that should be filtered, removed. */ -const filterDNAOptions = (_dna) => { +const filterDNAOptions = _dna => { const dnaItems = _dna.split(DNA_DELIMITER); - const filteredDNA = dnaItems.filter((element) => { + const filteredDNA = dnaItems.filter(element => { const query = /(\?.*$)/; const querystring = query.exec(element); if (!querystring) { return true; } - const options = querystring[1].split("&").reduce((r, setting) => { - const keyPairs = setting.split("="); + const options = querystring[1].split('&').reduce((r, setting) => { + const keyPairs = setting.split('='); return { ...r, [keyPairs[0]]: keyPairs[1] }; }, []); @@ -269,21 +269,21 @@ const filterDNAOptions = (_dna) => { * @param {String} _dna The entire newDNA string * @returns Cleaned DNA string without querystring parameters. */ -const removeQueryStrings = (_dna) => { +const removeQueryStrings = _dna => { const query = /(\?.*$)/; - return _dna.replace(query, ""); + return _dna.replace(query, ''); }; -const isDnaUnique = (_DnaList = new Set(), _dna = "") => { +const isDnaUnique = (_DnaList = new Set(), _dna = '') => { const _filteredDNA = filterDNAOptions(_dna); return !_DnaList.has(_filteredDNA); }; -const createDna = (_layers) => { +const createDna = _layers => { let randNum = []; - _layers.forEach((layer) => { + _layers.forEach(layer => { var totalWeight = 0; - layer.elements.forEach((element) => { + layer.elements.forEach(element => { totalWeight += element.weight; }); // number between 0 - totalWeight @@ -294,8 +294,8 @@ const createDna = (_layers) => { if (random < 0) { return randNum.push( `${layer.elements[i].id}:${layer.elements[i].filename}${ - layer.bypassDNA ? "?bypassDNA=true" : "" - }` + layer.bypassDNA ? '?bypassDNA=true' : '' + }`, ); } } @@ -303,20 +303,20 @@ const createDna = (_layers) => { return randNum.join(DNA_DELIMITER); }; -const writeMetaData = (_data) => { +const writeMetaData = _data => { fs.writeFileSync(`${buildDir}/json/_metadata.json`, _data); }; -const saveMetaDataSingleFile = (_editionCount) => { - let metadata = metadataList.find((meta) => meta.edition == _editionCount); +const saveMetaDataSingleFile = _editionCount => { + let metadata = metadataList.find(meta => meta.edition == _editionCount); debugLogs ? console.log( - `Writing metadata for ${_editionCount}: ${JSON.stringify(metadata)}` + `Writing metadata for ${_editionCount}: ${JSON.stringify(metadata)}`, ) : null; fs.writeFileSync( `${buildDir}/json/${_editionCount}.json`, - JSON.stringify(metadata, null, 2) + JSON.stringify(metadata, null, 2), ); }; @@ -350,11 +350,24 @@ const startCreating = async () => { abstractedIndexes = shuffle(abstractedIndexes); } debugLogs - ? console.log("Editions left to create: ", abstractedIndexes) + ? console.log('Editions left to create: ', abstractedIndexes) : null; + + // Figure out if layers have been selected that should not be combined + const skipOn = layerConfigurations[layerConfigIndex].skipLayerMatch; + if (skipOn && skipOn.length > 0) { + skipOn.forEach(pair => { + if (pair.length !== 2) { + throw new Error( + 'All arrays in the skipLayerMatch list must have 2 items', + ); + } + }); + } + while (layerConfigIndex < layerConfigurations.length) { const layers = layersSetup( - layerConfigurations[layerConfigIndex].layersOrder + layerConfigurations[layerConfigIndex].layersOrder, ); while ( editionCount <= layerConfigurations[layerConfigIndex].growEditionSizeTo @@ -363,13 +376,37 @@ const startCreating = async () => { if (isDnaUnique(dnaList, newDna)) { let results = constructLayerToDna(newDna, layers); let loadedElements = []; + let universaleSet = new Set(); - results.forEach((layer) => { + results.forEach(layer => { + universaleSet.add(layer.selectedElement.name); loadedElements.push(loadLayerImg(layer)); }); - await Promise.all(loadedElements).then((renderObjectArray) => { - debugLogs ? console.log("Clearing canvas") : null; + if (skipOn && skipOn.length > 0) { + let skip = false; + skipOn.forEach(item => { + if (skip) { + return; + } + if (universaleSet.has(item[0]) && universaleSet.has(item[1])) { + console.log( + `Matched on ${item[0]} and ${ + item[1] + }, skipping traits with DNA: ${sha1(newDna)}`, + ); + skip = true; + } + }); + + // Skip if layers have been combined selected should not be combined + if (skip) { + continue; + } + } + + await Promise.all(loadedElements).then(renderObjectArray => { + debugLogs ? console.log('Clearing canvas') : null; ctx.clearRect(0, 0, format.width, format.height); if (gif.export) { hashlipsGiffer = new HashlipsGiffer( @@ -378,7 +415,7 @@ const startCreating = async () => { `${buildDir}/gifs/${abstractedIndexes[0]}.gif`, gif.repeat, gif.quality, - gif.delay + gif.delay, ); hashlipsGiffer.start(); } @@ -389,7 +426,7 @@ const startCreating = async () => { drawElement( renderObject, index, - layerConfigurations[layerConfigIndex].layersOrder.length + layerConfigurations[layerConfigIndex].layersOrder.length, ); if (gif.export) { hashlipsGiffer.add(); @@ -399,26 +436,26 @@ const startCreating = async () => { hashlipsGiffer.stop(); } debugLogs - ? console.log("Editions left to create: ", abstractedIndexes) + ? console.log('Editions left to create: ', abstractedIndexes) : null; saveImage(abstractedIndexes[0]); addMetadata(newDna, abstractedIndexes[0]); saveMetaDataSingleFile(abstractedIndexes[0]); console.log( `Created edition: ${abstractedIndexes[0]}, with DNA: ${sha1( - newDna - )}` + newDna, + )}`, ); }); dnaList.add(filterDNAOptions(newDna)); editionCount++; abstractedIndexes.shift(); } else { - console.log("DNA exists!"); + console.log('DNA exists!'); failedCount++; if (failedCount >= uniqueDnaTorrance) { console.log( - `You need more layers or elements to grow your edition to ${layerConfigurations[layerConfigIndex].growEditionSizeTo} artworks!` + `You need more layers or elements to grow your edition to ${layerConfigurations[layerConfigIndex].growEditionSizeTo} artworks!`, ); process.exit(); }