From 44c36800c000eec926af13f2d7595c500f7d8c98 Mon Sep 17 00:00:00 2001 From: Shouqyan Date: Tue, 31 Aug 2021 00:13:55 +0200 Subject: [PATCH 01/13] adding readme, logs, some improvements --- README.md | 34 ++++++++++ index.js | 163 ++++++++++++++++++++++++++++++++---------------- input/config.js | 2 - 3 files changed, 143 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index d39f2c87..8286def9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ # generative-art-opensource Create generative art by using the canvas api and node js, feel free to contribute to this repo with new ideas. + +# Project Setup +- install `node.js` on your local system (https://nodejs.org/en/) +- clone the repository to your local system `https://github.com/HashLips/generative-art-node.git` +- run `yarn add all` to install dependencies + +# How to use +## Run the code +1. Run `node index.js ` +2. Open the `./output` folder to find your generated images to use as NFTs + +## Adjust the provided configuration and resources +### Configuration file +The file `./input/config.js` contains the following properties that can be adjusted to your preference in order to change the behavior of the NFT generation procedure: +- width: - of your image in pixels. Default: `1000px` +- height: - of your image in pixels. Default: `1000px` +- dir: - where image parts are stored. Default: `./input` +- description: - of your generated NFT +- baseImageUri: - URL base to access your NFTs from. This will be used by platforms to find your image resource. This expects the image to be accessible by it's id like `${baseImageUri}/${id}`. +- startEditionFrom: - number (int) to start naming NFTs from. +- editionSize: - number (int) to end edition at. +- rarityWeights: - allows to provide rarity categories and how many of each type to include in an edition. +- layers: list of layers that should be used to render the image. See next section for detail. + +### Image layers +The image layers are different parts that make up a full image by overlaying on top of each other. E.g. in this example we start with the eyeball and layer features like the eye lids or iris on top to create the completed and unique eye, which we can then use as part of our NFT collection. +To ensure uniqueness, we want to add various features and multiple options for each of them in order to allow enough permutations for the amount of unique images we require. + +To start, copy the layers/features and their images in a flat hierarchy at a directory of your choice (by default we expect them in `./input/`). The features should contain options for each rarity that is provided via the config file. + +After adding the layers, adjust them accordingly in the `config.js` by providing the directory path, positioning and sizes. + +# Development suggestions +- Preferably use VSCode with the prettifier plugin for a consistent coding style (or equivalent js formatting rules) \ No newline at end of file diff --git a/index.js b/index.js index 76b601b4..43822517 100644 --- a/index.js +++ b/index.js @@ -8,16 +8,13 @@ const { baseImageUri, editionSize, startEditionFrom, - endEditionAt, rarityWeights, } = require("./input/config.js"); const console = require("console"); const canvas = createCanvas(width, height); const ctx = canvas.getContext("2d"); -var metadataList = []; -var attributesList = []; -var dnaList = []; +// saves the generated image to the output folder, using the edition count as the name const saveImage = (_editionCount) => { fs.writeFileSync( `./output/${_editionCount}.png`, @@ -25,6 +22,7 @@ const saveImage = (_editionCount) => { ); }; +// adds a signature to the top left corner of the canvas const signImage = (_sig) => { ctx.fillStyle = "#000000"; ctx.font = "bold 30pt Courier"; @@ -33,6 +31,7 @@ const signImage = (_sig) => { ctx.fillText(_sig, 40, 40); }; +// generate a random color hue const genColor = () => { let hue = Math.floor(Math.random() * 360); let pastel = `hsl(${hue}, 100%, 85%)`; @@ -44,7 +43,8 @@ const drawBackground = () => { ctx.fillRect(0, 0, width, height); }; -const addMetadata = (_dna, _edition) => { +// add metadata for individual nft edition +const generateMetadata = (_dna, _edition, _attributesList) => { let dateTime = Date.now(); let tempMetadata = { dna: _dna.join(""), @@ -53,20 +53,23 @@ const addMetadata = (_dna, _edition) => { image: `${baseImageUri}/${_edition}`, edition: _edition, date: dateTime, - attributes: attributesList, + attributes: _attributesList, }; - metadataList.push(tempMetadata); - attributesList = []; + return tempMetadata; }; -const addAttributes = (_element) => { +// prepare attributes for the given element to be used as metadata +const getAttributeForElement = (_element) => { let selectedElement = _element.layer.selectedElement; - attributesList.push({ + let attribute = { name: selectedElement.name, rarity: selectedElement.rarity, - }); + }; + return attribute; }; +// loads an image from the layer path +// returns the image in a format usable by canvas const loadLayerImg = async (_layer) => { return new Promise(async (resolve) => { const image = await loadImage(`${_layer.selectedElement.path}`); @@ -82,9 +85,11 @@ const drawElement = (_element) => { _element.layer.size.width, _element.layer.size.height ); - addAttributes(_element); }; +// check the configured layer to find information required for rendering the layer +// this maps the layer information to the generated dna and prepares it for +// drawing on a canvas const constructLayerToDna = (_dna = [], _layers = [], _rarity) => { let mappedDnaToLayers = _layers.map((layer, index) => { let selectedElement = layer.elements[_rarity][_dna[index]]; @@ -99,24 +104,15 @@ const constructLayerToDna = (_dna = [], _layers = [], _rarity) => { return mappedDnaToLayers; }; -const getRarity = (_editionCount) => { - let rarity = ""; - rarityWeights.forEach((rarityWeight) => { - if ( - _editionCount >= rarityWeight.from && - _editionCount <= rarityWeight.to - ) { - rarity = rarityWeight.value; - } - }); - return rarity; -}; - +// check if the given dna is contained within the given dnaList +// return true if it is, indicating that this dna is already in use and should be recalculated const isDnaUnique = (_DnaList = [], _dna = []) => { let foundDna = _DnaList.find((i) => i.join("") === _dna.join("")); return foundDna == undefined ? true : false; }; +// create a dna based on the available layers for the given rarity +// use a random part for each layer const createDna = (_layers, _rarity) => { let randNum = []; _layers.forEach((layer) => { @@ -126,48 +122,107 @@ const createDna = (_layers, _rarity) => { return randNum; }; +// holds which rarity should be used for which image in edition +let rarityForEdition; +// get the rarity for the image by edition number that should be generated +const getRarity = (_editionCount) => { + if (!rarityForEdition) { + // prepare array to iterate over + rarityForEdition = []; + rarityWeights.forEach((rarityWeight) => { + for (let i = rarityWeight.from; i <= rarityWeight.to; i++) { + rarityForEdition.push(rarityWeight.value); + } + }); + } + return rarityForEdition[editionSize - _editionCount]; +}; + const writeMetaData = (_data) => { fs.writeFileSync("./output/_metadata.json", _data); }; +// holds which dna has already been used during generation +let dnaListByRarity = {}; +// holds metadata for all NFTs +let metadataList = []; +// Create generative art by using the canvas api const startCreating = async () => { + console.log('##################'); + console.log('# Generative Art'); + console.log('# - Create your NFT collection'); + console.log('##################'); + + console.log(); + console.log('start creating NFTs.') + + // clear meta data from previous run writeMetaData(""); + + // prepare dnaList object + rarityWeights.forEach((rarityWeight) => { + dnaListByRarity[rarityWeight.value] = []; + }); + + // create NFTs from startEditionFrom to editionSize let editionCount = startEditionFrom; - while (editionCount <= endEditionAt) { - console.log(editionCount); + while (editionCount <= editionSize) { + console.log('-----------------') + console.log('creating NFT %d of %d', editionCount, editionSize); + // get rarity from to config to create NFT as let rarity = getRarity(editionCount); - console.log(rarity); + console.log('- rarity: ' + rarity); + // calculate the NFT dna by getting a random part for each layer/feature + // based on the ones available for the given rarity to use during generation let newDna = createDna(layers, rarity); - console.log(dnaList); - - if (isDnaUnique(dnaList, newDna)) { - let results = constructLayerToDna(newDna, layers, rarity); - let loadedElements = []; //promise array - - results.forEach((layer) => { - loadedElements.push(loadLayerImg(layer)); - }); - - await Promise.all(loadedElements).then((elementArray) => { - ctx.clearRect(0, 0, width, height); - drawBackground(); - elementArray.forEach((element) => { - drawElement(element); - }); - signImage(`#${editionCount}`); - saveImage(editionCount); - addMetadata(newDna, editionCount); - console.log(`Created edition: ${editionCount} with DNA: ${newDna}`); - }); - dnaList.push(newDna); - editionCount++; - } else { - console.log("DNA exists!"); + while (!isDnaUnique(dnaListByRarity[rarity], newDna)) { + // recalculate dna as this has been used before. + console.log('found duplicate DNA ' + newDna.join('-') + ', recalculate...'); + newDna = createDna(layers, rarity); } + console.log('- dna: ' + newDna.join('-')); + + // propagate information about required layer contained within config into a mapping object + // = prepare for drawing + let results = constructLayerToDna(newDna, layers, rarity); + let loadedElements = []; + + // load all images to be used by canvas + results.forEach((layer) => { + loadedElements.push(loadLayerImg(layer)); + }); + + // elements are loaded asynchronously + // -> await for all to be available before drawing the image + await Promise.all(loadedElements).then((elementArray) => { + // create empty image + ctx.clearRect(0, 0, width, height); + // draw a random background color + drawBackground(); + // store information about each layer to add it as meta information + let attributesList = []; + // draw each layer + elementArray.forEach((element) => { + drawElement(element); + attributesList.push(getAttributeForElement(element)); + }); + // add an image signature as the edition count to the top left of the image + signImage(`#${editionCount}`); + // write the image to the output directory + saveImage(editionCount); + let nftMetadata = generateMetadata(newDna, editionCount, attributesList); + metadataList.push(nftMetadata) + console.log('- metadata: ' + JSON.stringify(nftMetadata)); + console.log('- edition ' + editionCount + ' created.'); + console.log(); + }); + dnaListByRarity[rarity].push(newDna); + editionCount++; } writeMetaData(JSON.stringify(metadataList)); }; -startCreating(); +// Initiate code +startCreating(); \ No newline at end of file diff --git a/input/config.js b/input/config.js index 3dfa5bce..b1486bd8 100644 --- a/input/config.js +++ b/input/config.js @@ -5,7 +5,6 @@ const dir = __dirname; const description = "This is an NFT made by the coolest generative code."; const baseImageUri = "https://hashlips/nft"; const startEditionFrom = 1; -const endEditionAt = 10; const editionSize = 10; const rarityWeights = [ { @@ -107,6 +106,5 @@ module.exports = { baseImageUri, editionSize, startEditionFrom, - endEditionAt, rarityWeights, }; From 1f37dda669698ffe66c413ae0ce3580632dfaa1a Mon Sep 17 00:00:00 2001 From: Shouqyan Date: Tue, 31 Aug 2021 00:14:40 +0200 Subject: [PATCH 02/13] minor adjustment to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8286def9..7329a64f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Create generative art by using the canvas api and node js, feel free to contribu # How to use ## Run the code -1. Run `node index.js ` +1. Run `node index.js` 2. Open the `./output` folder to find your generated images to use as NFTs ## Adjust the provided configuration and resources From b041556a7b1a5285a5473bf75b36c334528a8e48 Mon Sep 17 00:00:00 2001 From: Shouqyan Date: Tue, 31 Aug 2021 00:21:32 +0200 Subject: [PATCH 03/13] wrong repo url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7329a64f..62d3742f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Create generative art by using the canvas api and node js, feel free to contribu # Project Setup - install `node.js` on your local system (https://nodejs.org/en/) -- clone the repository to your local system `https://github.com/HashLips/generative-art-node.git` +- clone the repository to your local system `https://github.com/cranqcore/generative-art-opensource` - run `yarn add all` to install dependencies # How to use From 475981841008cc08c3c1aaa0e85cf27b4ddee42b Mon Sep 17 00:00:00 2001 From: Shouqyan Date: Tue, 31 Aug 2021 00:22:26 +0200 Subject: [PATCH 04/13] wrong repo url [2] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62d3742f..0f760961 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Create generative art by using the canvas api and node js, feel free to contribu # Project Setup - install `node.js` on your local system (https://nodejs.org/en/) -- clone the repository to your local system `https://github.com/cranqcore/generative-art-opensource` +- clone the repository to your local system `git@github.com:HashLips/generative-art-opensource.git` - run `yarn add all` to install dependencies # How to use From b95526e6bdf79480b54855d82cd95ac2d65e40eb Mon Sep 17 00:00:00 2001 From: Shouqyan Date: Tue, 31 Aug 2021 18:22:39 +0200 Subject: [PATCH 05/13] Add some more details on readme --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0f760961..6a464d60 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Create generative art by using the canvas api and node js, feel free to contribu # How to use ## Run the code 1. Run `node index.js` -2. Open the `./output` folder to find your generated images to use as NFTs +2. Open the `./output` folder to find your generated images to use as NFTs, as well as the metadata to use for NFT marketplaces. ## Adjust the provided configuration and resources ### Configuration file @@ -17,20 +17,20 @@ The file `./input/config.js` contains the following properties that can be adjus - width: - of your image in pixels. Default: `1000px` - height: - of your image in pixels. Default: `1000px` - dir: - where image parts are stored. Default: `./input` -- description: - of your generated NFT +- description: - of your generated NFT. Default: `This is an NFT made by the coolest generative code.` - baseImageUri: - URL base to access your NFTs from. This will be used by platforms to find your image resource. This expects the image to be accessible by it's id like `${baseImageUri}/${id}`. -- startEditionFrom: - number (int) to start naming NFTs from. -- editionSize: - number (int) to end edition at. -- rarityWeights: - allows to provide rarity categories and how many of each type to include in an edition. +- startEditionFrom: - number (int) to start naming NFTs from. Default: `1` +- editionSize: - number (int) to end edition at. Default: `10` +- rarityWeights: - allows to provide rarity categories and how many of each type to include in an edition. Default: `1 super_rare, 4 rare, 5 original` - layers: list of layers that should be used to render the image. See next section for detail. ### Image layers -The image layers are different parts that make up a full image by overlaying on top of each other. E.g. in this example we start with the eyeball and layer features like the eye lids or iris on top to create the completed and unique eye, which we can then use as part of our NFT collection. +The image layers are different parts that make up a full image by overlaying on top of each other. E.g. in the example input content of this repository we start with the eyeball and layer features like the eye lids or iris on top to create the completed and unique eye, which we can then use as part of our NFT collection. To ensure uniqueness, we want to add various features and multiple options for each of them in order to allow enough permutations for the amount of unique images we require. To start, copy the layers/features and their images in a flat hierarchy at a directory of your choice (by default we expect them in `./input/`). The features should contain options for each rarity that is provided via the config file. -After adding the layers, adjust them accordingly in the `config.js` by providing the directory path, positioning and sizes. +After adding the `layers`, adjust them accordingly in the `config.js` by providing the directory path, positioning and sizes. # Development suggestions - Preferably use VSCode with the prettifier plugin for a consistent coding style (or equivalent js formatting rules) \ No newline at end of file From d1c7c4e10c1231d31526775647e9473839580a6b Mon Sep 17 00:00:00 2001 From: Shouqyan Date: Wed, 1 Sep 2021 23:28:25 +0200 Subject: [PATCH 06/13] #12 allow defining specific percentages for rarity/layer combinations for more expressiveness in NFT combination --- README.md | 4 + index.js | 29 ++++++- input/config.js | 218 ++++++++++++++++++++++++++++++------------------ 3 files changed, 167 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 6a464d60..2d09c003 100644 --- a/README.md +++ b/README.md @@ -32,5 +32,9 @@ To start, copy the layers/features and their images in a flat hierarchy at a dir After adding the `layers`, adjust them accordingly in the `config.js` by providing the directory path, positioning and sizes. +### Allowing different rarities for certain rarity/layer combinations +It is possible to provide a percentage at which e.g. a rare item would contain a rare vs. common part in a given layer. This can be done via the `addRarityPercentForLayer` that can be found in the `config.js` as well. +This allows for more fine grained control over how much randomness there should be during the generation process, and allows a combination of common and rare parts. + # Development suggestions - Preferably use VSCode with the prettifier plugin for a consistent coding style (or equivalent js formatting rules) \ No newline at end of file diff --git a/index.js b/index.js index 43822517..f45476a2 100644 --- a/index.js +++ b/index.js @@ -92,7 +92,7 @@ const drawElement = (_element) => { // drawing on a canvas const constructLayerToDna = (_dna = [], _layers = [], _rarity) => { let mappedDnaToLayers = _layers.map((layer, index) => { - let selectedElement = layer.elements[_rarity][_dna[index]]; + let selectedElement = layer.elements.find(element => element.id === _dna[index]); return { location: layer.location, position: layer.position, @@ -100,7 +100,6 @@ const constructLayerToDna = (_dna = [], _layers = [], _rarity) => { selectedElement: selectedElement, }; }); - return mappedDnaToLayers; }; @@ -111,13 +110,35 @@ const isDnaUnique = (_DnaList = [], _dna = []) => { return foundDna == undefined ? true : false; }; +const getRandomRarity = (_rarityOptions) => { + let randomPercent = Math.random() * 100; + let percentCount = 0; + + for (let i = 0; i <= _rarityOptions.length; i++) { + percentCount += _rarityOptions[i].percent; + if (percentCount >= randomPercent) { + console.log(`use random rarity ${_rarityOptions[i].id}`) + return _rarityOptions[i].id; + } + } + return _rarityOptions[0].id; +} + // create a dna based on the available layers for the given rarity // use a random part for each layer const createDna = (_layers, _rarity) => { let randNum = []; + let _rarityWeight = rarityWeights.find(rw => rw.value === _rarity); _layers.forEach((layer) => { - let num = Math.floor(Math.random() * layer.elements[_rarity].length); - randNum.push(num); + let num = Math.floor(Math.random() * layer.elementIdsForRarity[_rarity].length); + if (_rarityWeight && _rarityWeight.layerPercent[layer.id]) { + // if there is a layerPercent defined, we want to identify which dna to actually use here (instead of only picking from the same rarity) + let _rarityForLayer = getRandomRarity(_rarityWeight.layerPercent[layer.id]); + num = Math.floor(Math.random() * layer.elementIdsForRarity[_rarityForLayer].length); + randNum.push(layer.elementIdsForRarity[_rarityForLayer][num]); + } else { + randNum.push(layer.elementIdsForRarity[_rarity][num]); + } }); return randNum; }; diff --git a/input/config.js b/input/config.js index b1486bd8..a9d659a9 100644 --- a/input/config.js +++ b/input/config.js @@ -1,103 +1,161 @@ +/************************************************************** + * UTILITY FUNCTIONS + * - scroll to BEGIN CONFIG to provide the config values + *************************************************************/ const fs = require("fs"); -const width = 1000; -const height = 1000; const dir = __dirname; -const description = "This is an NFT made by the coolest generative code."; -const baseImageUri = "https://hashlips/nft"; -const startEditionFrom = 1; -const editionSize = 10; -const rarityWeights = [ - { - value: "super_rare", - from: 1, - to: 1, - }, - { - value: "rare", - from: 2, - to: 5, - }, - { - value: "original", - from: 5, - to: editionSize, - }, -]; +// adds a rarity to the configuration. This is expected to correspond with a directory containing the rarity for each defined layer +// @param _id - id of the rarity +// @param _from - number in the edition to start this rarity from +// @param _to - number in the edition to generate this rarity to +// @return a rarity object used to dynamically generate the NFTs +const addRarity = (_id, _from, _to) => { + const _rarityWeight = { + value: _id, + from: _from, + to: _to, + layerPercent: {} + }; + return _rarityWeight; +}; + +// get the name without last 4 characters -> slice .png from the name const cleanName = (_str) => { let name = _str.slice(0, -4); return name; }; -const getElements = (path) => { +// reads the filenames of a given folder and returns it with its name and path +const getElements = (_path, _elementCount) => { return fs - .readdirSync(path) + .readdirSync(_path) .filter((item) => !/(^|\/)\.[^\/\.]/g.test(item)) .map((i) => { return { + id: _elementCount, name: cleanName(i), - path: `${path}/${i}`, + path: `${_path}/${i}` }; }); }; +// adds a layer to the configuration. The layer will hold information on all the defined parts and +// where they should be rendered in the image +// @param _id - id of the layer +// @param _position - on which x/y value to render this part +// @param _size - of the image +// @return a layer object used to dynamically generate the NFTs +const addLayer = (_id, _position, _size) => { + if (!_id) { + console.log('error adding layer, parameters id required'); + return null; + } + if (!_position) { + _position = { x: 0, y: 0 }; + } + if (!_size) { + _size = { width: width, height: height } + } + // add two different dimension for elements: + // - all elements with their path information + // - only the ids mapped to their rarity + let elements = []; + let elementCount = 0; + let elementIdsForRarity = {}; + rarityWeights.forEach((rarityWeight) => { + let elementsForRarity = getElements(`${dir}/${_id}/${rarityWeight.value}`); + + elementIdsForRarity[rarityWeight.value] = []; + elementsForRarity.forEach((_elementForRarity) => { + _elementForRarity.id = `${editionDnaPrefix}${elementCount}`; + elements.push(_elementForRarity); + elementIdsForRarity[rarityWeight.value].push(_elementForRarity.id); + elementCount++; + }) + elements[rarityWeight.value] = elementsForRarity; + }); + + let elementsForLayer = { + id: _id, + position: _position, + size: _size, + elements, + elementIdsForRarity + }; + return elementsForLayer; +}; + +// adds layer-specific percentages to use one vs another rarity +// @param _rarityId - the id of the rarity to specifiy +// @param _layerId - the id of the layer to specifiy +// @param _percentages - an object defining the rarities and the percentage with which a given rarity for this layer should be used +const addRarityPercentForLayer = (_rarityId, _layerId, _percentages) => { + let _rarityFound = false; + rarityWeights.forEach((_rarityWeight) => { + if (_rarityWeight.value === _rarityId) { + let _percentArray = []; + for (let percentType in _percentages) { + _percentArray.push({ + id: percentType, + percent: _percentages[percentType] + }) + } + _rarityWeight.layerPercent[_layerId] = _percentArray; + _rarityFound = true; + } + }); + if (!_rarityFound) { + console.log(`rarity ${_rarityId} not found, failed to add percentage information`); + } +} + +/************************************************************** + * BEGIN CONFIG + *************************************************************/ + +// image width in pixels +const width = 1000; +// image height in pixels +const height = 1000; +// description for NFT in metadata file +const description = "This is an NFT made by the coolest generative code."; +// base url to use in metadata file +// the id of the nft will be added to this url, in the example e.g. https://hashlips/nft/1 for NFT with id 1 +const baseImageUri = "https://hashlips/nft"; +// id for edition to start from +const startEditionFrom = 1; +// amount of NFTs to generate in edition +const editionSize = 10; +// prefix to add to edition dna ids (to distinguish dna counts from different generation processes for the same collection) +const editionDnaPrefix = 0 + +// create required weights +// for each weight, call 'addRarity' with the id and from which to which element this rarity should be applied +let rarityWeights = [ + addRarity('super_rare', 1, 1), + addRarity('rare', 2, 5), + addRarity('original', 5, 10) +]; + +// create required layers +// for each layer, call 'addLayer' with the id and optionally the positioning and size +// the id would be the name of the folder in your input directory, e.g. 'ball' for ./input/ball const layers = [ - { - elements: { - original: getElements(`${dir}/ball/original`), - rare: getElements(`${dir}/ball/rare`), - super_rare: getElements(`${dir}/ball/super_rare`), - }, - position: { x: 0, y: 0 }, - size: { width: width, height: height }, - }, - { - elements: { - original: getElements(`${dir}/eye color/original`), - rare: getElements(`${dir}/eye color/rare`), - super_rare: getElements(`${dir}/eye color/super_rare`), - }, - position: { x: 0, y: 0 }, - size: { width: width, height: height }, - }, - { - elements: { - original: getElements(`${dir}/iris/original`), - rare: getElements(`${dir}/iris/rare`), - super_rare: getElements(`${dir}/iris/super_rare`), - }, - position: { x: 0, y: 0 }, - size: { width: width, height: height }, - }, - { - elements: { - original: getElements(`${dir}/shine/original`), - rare: getElements(`${dir}/shine/rare`), - super_rare: getElements(`${dir}/shine/super_rare`), - }, - position: { x: 0, y: 0 }, - size: { width: width, height: height }, - }, - { - elements: { - original: getElements(`${dir}/bottom lid/original`), - rare: getElements(`${dir}/bottom lid/rare`), - super_rare: getElements(`${dir}/bottom lid/super_rare`), - }, - position: { x: 0, y: 0 }, - size: { width: width, height: height }, - }, - { - elements: { - original: getElements(`${dir}/top lid/original`), - rare: getElements(`${dir}/top lid/rare`), - super_rare: getElements(`${dir}/top lid/super_rare`), - }, - position: { x: 0, y: 0 }, - size: { width: width, height: height }, - }, + addLayer('ball'), + addLayer('eye color'), + addLayer('iris'), + addLayer('shine'), + addLayer('bottom lid'), + addLayer('top lid') ]; +// provide any specific percentages that are required for a given layer and rarity level +// all provided options are used based on their percentage values to decide which layer to select from +addRarityPercentForLayer('super_rare', 'ball', { 'super_rare': 33, 'rare': 33, 'original': 33 }); +addRarityPercentForLayer('super_rare', 'eye color', { 'super_rare': 50, 'rare': 25, 'original': 25 }); +addRarityPercentForLayer('original', 'eye color', { 'super_rare': 50, 'rare': 25, 'original': 25 }); + module.exports = { layers, width, From 156723fa7e10be2cd9f4d901c4024f8fa4262f0e Mon Sep 17 00:00:00 2001 From: Shouqyan Date: Wed, 1 Sep 2021 23:39:13 +0200 Subject: [PATCH 07/13] adding editionDnaPrefix to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2d09c003..15b85a92 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ The file `./input/config.js` contains the following properties that can be adjus - baseImageUri: - URL base to access your NFTs from. This will be used by platforms to find your image resource. This expects the image to be accessible by it's id like `${baseImageUri}/${id}`. - startEditionFrom: - number (int) to start naming NFTs from. Default: `1` - editionSize: - number (int) to end edition at. Default: `10` +- editionDnaPrefix: - value (number or string) that indicates which dna from an edition is used there. I.e. dna `0` from to independent batches in the same edition may differ, and can be differentiated using this. Default: `0` - rarityWeights: - allows to provide rarity categories and how many of each type to include in an edition. Default: `1 super_rare, 4 rare, 5 original` - layers: list of layers that should be used to render the image. See next section for detail. From 54e9efceeefde34a357481a6337ebbf291614919 Mon Sep 17 00:00:00 2001 From: Shouqyan Date: Thu, 2 Sep 2021 11:12:03 +0200 Subject: [PATCH 08/13] some explanation on addLayer functionality --- README.md | 1 + input/config.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 15b85a92..193b62c5 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ To ensure uniqueness, we want to add various features and multiple options for e To start, copy the layers/features and their images in a flat hierarchy at a directory of your choice (by default we expect them in `./input/`). The features should contain options for each rarity that is provided via the config file. After adding the `layers`, adjust them accordingly in the `config.js` by providing the directory path, positioning and sizes. +Use the existing `addLayers` calls as guidance for how to add layers. This can either only use the name of the layer and will use default positioning (x=0, y=0) and sizes (width=configured width, height=configure height), or positioning and sizes can be provided for more flexibility. ### Allowing different rarities for certain rarity/layer combinations It is possible to provide a percentage at which e.g. a rare item would contain a rare vs. common part in a given layer. This can be done via the `addRarityPercentForLayer` that can be found in the `config.js` as well. diff --git a/input/config.js b/input/config.js index a9d659a9..aad063a7 100644 --- a/input/config.js +++ b/input/config.js @@ -142,7 +142,7 @@ let rarityWeights = [ // for each layer, call 'addLayer' with the id and optionally the positioning and size // the id would be the name of the folder in your input directory, e.g. 'ball' for ./input/ball const layers = [ - addLayer('ball'), + addLayer('ball', { x: 0, y: 0 }, { width: width, height: height }), addLayer('eye color'), addLayer('iris'), addLayer('shine'), From 7a568a4745378a2e131756e02e4205d575e9e257 Mon Sep 17 00:00:00 2001 From: Iqbal Fasri Date: Thu, 9 Sep 2021 15:34:46 +0700 Subject: [PATCH 09/13] fix rarity attribute undefined --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index f45476a2..679e1270 100644 --- a/index.js +++ b/index.js @@ -97,7 +97,7 @@ const constructLayerToDna = (_dna = [], _layers = [], _rarity) => { location: layer.location, position: layer.position, size: layer.size, - selectedElement: selectedElement, + selectedElement: {...selectedElement, rarity: _rarity }, }; }); return mappedDnaToLayers; From 22479ec50896973dd091c8445aeab54234b51be2 Mon Sep 17 00:00:00 2001 From: HashLips <88177839+HashLips@users.noreply.github.com> Date: Sun, 12 Sep 2021 18:36:05 +0200 Subject: [PATCH 10/13] Updated readme --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 193b62c5..3e4c69d3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,17 @@ +# Welcome to HashLips + +All the code in these repos was created and explained by HashLips on the main YouTube channel. + +To find out more please visit: + +[HashLips YouTube](https://www.youtube.com/channel/UC1LV4_VQGBJHTJjEWUmy8nA) + +[HashLips Telegram](https://t.me/hashlipsnft) + +[HashLips Twitter](https://twitter.com/hashlipsnft) + +[HashLips Website](https://hashlips.online/HashLips) + # generative-art-opensource Create generative art by using the canvas api and node js, feel free to contribute to this repo with new ideas. @@ -39,4 +53,4 @@ It is possible to provide a percentage at which e.g. a rare item would contain a This allows for more fine grained control over how much randomness there should be during the generation process, and allows a combination of common and rare parts. # Development suggestions -- Preferably use VSCode with the prettifier plugin for a consistent coding style (or equivalent js formatting rules) \ No newline at end of file +- Preferably use VSCode with the prettifier plugin for a consistent coding style (or equivalent js formatting rules) From 8e5ac1bb1e880d759b262afffeacc7fdd6bec5af Mon Sep 17 00:00:00 2001 From: HashLips <88177839+HashLips@users.noreply.github.com> Date: Mon, 13 Sep 2021 22:13:11 +0200 Subject: [PATCH 11/13] Updated readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3e4c69d3..c54ad945 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ All the code in these repos was created and explained by HashLips on the main YouTube channel. +Important: There is a new repo for this code. +[https://github.com/HashLips/hashlips_art_engine](https://github.com/HashLips/hashlips_art_engine) + To find out more please visit: [HashLips YouTube](https://www.youtube.com/channel/UC1LV4_VQGBJHTJjEWUmy8nA) From be11bee801f4a9f459b19de93bee8ecdae7e303b Mon Sep 17 00:00:00 2001 From: HashLips <88177839+HashLips@users.noreply.github.com> Date: Thu, 16 Sep 2021 12:43:49 +0200 Subject: [PATCH 12/13] Updated readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c54ad945..894fa89b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Create generative art by using the canvas api and node js, feel free to contribu # Project Setup - install `node.js` on your local system (https://nodejs.org/en/) - clone the repository to your local system `git@github.com:HashLips/generative-art-opensource.git` -- run `yarn add all` to install dependencies +- run `yarn install` to install dependencies # How to use ## Run the code From ed8a37d5c4277c5011dd21152f5f4e1761060894 Mon Sep 17 00:00:00 2001 From: HashLips <88177839+HashLips@users.noreply.github.com> Date: Tue, 21 Sep 2021 00:12:35 +0200 Subject: [PATCH 13/13] Added social media --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 894fa89b..a82e1289 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,21 @@ -# Welcome to HashLips - -All the code in these repos was created and explained by HashLips on the main YouTube channel. +# Welcome to HashLips 👄 Important: There is a new repo for this code. [https://github.com/HashLips/hashlips_art_engine](https://github.com/HashLips/hashlips_art_engine) +All the code in these repos was created and explained by HashLips on the main YouTube channel. + To find out more please visit: -[HashLips YouTube](https://www.youtube.com/channel/UC1LV4_VQGBJHTJjEWUmy8nA) +[đŸ“ē YouTube](https://www.youtube.com/channel/UC1LV4_VQGBJHTJjEWUmy8nA) + +[👄 Discord](https://discord.com/invite/qh6MWhMJDN) -[HashLips Telegram](https://t.me/hashlipsnft) +[đŸ’Ŧ Telegram](https://t.me/hashlipsnft) -[HashLips Twitter](https://twitter.com/hashlipsnft) +[đŸĻ Twitter](https://twitter.com/hashlipsnft) -[HashLips Website](https://hashlips.online/HashLips) +[â„šī¸ Website](https://hashlips.online/HashLips) # generative-art-opensource Create generative art by using the canvas api and node js, feel free to contribute to this repo with new ideas.