From ee78b0ea5c8244efa2da6848d6013fcb25383472 Mon Sep 17 00:00:00 2001 From: bruceballad Date: Sun, 24 Oct 2021 12:00:44 +0200 Subject: [PATCH 01/12] resetNameIndex fix [Fix] There is a small bug with resetNameIndex functionality that breaks the numbers when it is used after the second layer config set. Changing the "+=" to "=" at the line 556 seems to fix it. --- src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.js b/src/main.js index 34a7b2b20..aa6379ab2 100644 --- a/src/main.js +++ b/src/main.js @@ -553,7 +553,7 @@ const startCreating = async () => { if (layerConfigurations[layerConfigIndex].resetNameIndex) { _offset = layerConfigurations.reduce((acc, layer, index) => { if (index < layerConfigIndex) { - acc += layer.growEditionSizeTo; + acc = layer.growEditionSizeTo; return acc; } return acc; From 86fa0c0c6c1cdd63a828facf291093cf6f78bdbe Mon Sep 17 00:00:00 2001 From: bruceballad Date: Sun, 24 Oct 2021 12:18:29 +0200 Subject: [PATCH 02/12] Improvements - [Added] The ability to add a suffix after the number in the edition name. Now, if there is a suffix, 'resetNameIndex=true' option adds the sub-index counter number after the suffix. So, you can form names like; "PREFIX #10 - SUFFIX #2". (If there is no suffix set, resetNameIndex works as originally intended) - [Added] The ability to set custom descriptions for each layer config set. - [Added] The ability to prepend custom descriptions for each layer config set. So, you can form unique descriptions like; "Item #10 is an art piece from Collection X". - [Added] External_url config for OpenSea. - [Added] Some sample extraAttributes to be used as templates based on OpenSea metadata-standards. --- src/main.js | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/main.js b/src/main.js index aa6379ab2..3adb62d67 100644 --- a/src/main.js +++ b/src/main.js @@ -18,6 +18,7 @@ const { layersDir, format, baseUri, + baseExternalUrl, description, background, uniqueDnaTorrance, @@ -91,7 +92,7 @@ const getElements = (path, layer) => { const extension = /\.[0-9a-zA-Z]+$/; const sublayer = !extension.test(i); const weight = getRarityWeight(i); - + const blendMode = layer.blend != undefined ? layer.blend : "source-over"; const opacity = layer.opacity != undefined ? layer.opacity : 1; @@ -186,8 +187,15 @@ const drawBackground = () => { const addMetadata = (_dna, _edition, _prefixData) => { let dateTime = Date.now(); - const { _prefix, _offset, _imageHash } = _prefixData; - + const { + _descriptionOverwrite, + _prependNameInDescription, + _prefix, + _offset, + _suffix, + _imageHash + } = _prefixData; + const combinedAttrs = [...attributesList, ...extraAttributes()]; const cleanedAttrs = combinedAttrs.reduce((acc, current) => { const x = acc.find((item) => item.trait_type === current.trait_type); @@ -200,9 +208,18 @@ const addMetadata = (_dna, _edition, _prefixData) => { let tempMetadata = { dna: hash(_dna), - name: `${_prefix ? _prefix + " " : ""}#${_edition - _offset}`, - description: description, + + // New name builder. It can form names like; "PREFIX #10 - SUFFIX #2". - BB + name: `${_prefix ? _prefix + " " : ""}#${_suffix ? _edition : _edition - _offset}${_suffix ? " " + _suffix + (_offset>0 ? " #" + (_edition - _offset) : "") : ""}`, + + // new description builder, it can prepends the asset name, AND overwrite the description for different layerConfigs. Can form unique descriptions like; "Item #10 is an art piece from Collection X". - BB + description: `${_prependNameInDescription ? `${_prefix ? _prefix + " " : ""}#${_suffix ? _edition : _edition - _offset}${_suffix ? " " + _suffix + (_offset>0 ? " #" + (_edition - _offset) : "") : ""}` : ""}${_descriptionOverwrite ? _descriptionOverwrite : description}`, + + // Adds external_url if the baseExternalUrl in config is not empty, combines it with edition numbers. - BB + ...(baseExternalUrl !== "" && { external_url: `${baseExternalUrl}${_edition}` }), + image: `${baseUri}/${_edition}${outputJPEG ? ".jpg" : ".png"}`, + ...(hashImages === true && { imageHash: _imageHash }), edition: _edition, date: dateTime, @@ -542,11 +559,22 @@ const startCreating = async () => { }` ); const _imageHash = hash(savedFile); + + // if there's a descriptionOverwrite for the current layerConfig, then + // use that description in the assets - BB + const _descriptionOverwrite = layerConfigurations[layerConfigIndex].descriptionOverwrite + ? layerConfigurations[layerConfigIndex].descriptionOverwrite + : null; + + // prependNameInDescription - BB + const _prependNameInDescription = layerConfigurations[layerConfigIndex].prependNameInDescription; + // if there's a prefix for the current configIndex, then // start count back at 1 for the name, only. const _prefix = layerConfigurations[layerConfigIndex].namePrefix ? layerConfigurations[layerConfigIndex].namePrefix : null; + // if resetNameIndex is turned on, calculate the offset and send it // with the prefix let _offset = 0; @@ -559,9 +587,20 @@ const startCreating = async () => { return acc; }, 0); } + + // if there's a suffix for the current configIndex, then + // add it after the counter number + // if resetNameIndex is on too, the resetted counter will be added after the suffix + const _suffix = layerConfigurations[layerConfigIndex].nameSuffix + ? layerConfigurations[layerConfigIndex].nameSuffix + : null; + addMetadata(newDna, abstractedIndexes[0], { + _descriptionOverwrite, // BB + _prependNameInDescription, // BB _prefix, _offset, + _suffix, _imageHash, }); From 89b17cff7ec130d9ec25c451629687f106a9051b Mon Sep 17 00:00:00 2001 From: bruceballad Date: Sun, 24 Oct 2021 12:43:10 +0200 Subject: [PATCH 03/12] Improvements part-2 - [Added] The ability to add a suffix after the number in the edition name. Now, if there is a suffix, 'resetNameIndex=true' option adds the sub-index counter number after the suffix. So, you can form names like; "PREFIX #10 - SUFFIX #2". (If there is no suffix set, resetNameIndex works as originally intended) - [Added] The ability to set custom descriptions for each layer config set. - [Added] The ability to prepend custom descriptions for each layer config set. So, you can form unique descriptions like; "Item #10 is an art piece from Collection X". - [Added] External_url config for OpenSea. - [Added] Some sample extraAttributes to be used as templates based on OpenSea metadata-standards. --- src/config.js | 121 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 38 deletions(-) diff --git a/src/config.js b/src/config.js index 7ad09cd56..a1400637e 100644 --- a/src/config.js +++ b/src/config.js @@ -9,21 +9,46 @@ const buildDir = path.join(basePath, "/build"); const layersDir = path.join(basePath, "/layers"); const description = - "This is the description of your NFT project, remember to replace this"; + "Default Description"; const baseUri = "ipfs://NewUriToReplace"; +const baseExternalUrl = "https://test.com/collection?asset="; //Base url for extelnal link under the image on OpenSea. The edition number will be appended. Leave empty "" to remove. - BB + const outputJPEG = false; // if false, the generator outputs png's // if you use an empty/transparent file, set the name here. const emptyLayerName = "NONE"; -//IF you need a provenance hash, turn this on +//IF you need a provenance hash, turn this on const hashImages = true; const layerConfigurations = [ + { - growEditionSizeTo: 11, - // namePrefix: "Monkey", Use to add a name to Metadata `name:` + growEditionSizeTo: 10, + descriptionOverwrite: " with Unique Description For Layer Set A", // for layerSet spesific descriptions. Here, it is written in a way to suit the names that will be prepended. + prependNameInDescription: true, // add/prepend asset name to the description. - BB + namePrefix: "Monkey", + resetNameIndex: true, // this will start the count at #1 for this layerconfig + nameSuffix: "Set A", // add a suffix after the number. if resetNameIndex is on too, put the reseted counter after the suffix - BB + layersOrder: [ + { name: "Back Accessory" }, + { name: "Head" }, + { name: "Clothes" }, + { name: "Eyes" }, + { name: "Hair" }, + { name: "Head Accessory" }, + { name: "Shirt Accessories" }, + ], + }, + + { + growEditionSizeTo: 20, + descriptionOverwrite: " with Unique Description For Layer Set A", // for layerSet spesific descriptions. Here, it is written in a way to suit the names that will be prepended. + prependNameInDescription: true, // add/prepend asset name to the description. - BB + namePrefix: "Monkey", + resetNameIndex: true, // this will start the count at #1 for this layerconfig + nameSuffix: "Set B", // add a suffix after the number. if resetNameIndex is on too, put the reseted counter after the suffix - BB layersOrder: [ { name: "Back Accessory" }, { name: "Head" }, @@ -34,19 +59,10 @@ const layerConfigurations = [ { name: "Shirt Accessories" }, ], }, - // { - // growEditionSizeTo: 10, - // namePrefix: "Lion", - // resetNameIndex: true, // this will start the Lion count at #1 instead of #6 - // layersOrder: [ - // { name: "Background" }, - // { name: "Hats" }, - // { name: "Male Hair" }, - // ], - // }, ]; -/** + +/** * Incompatible items can be added to this object by a files cleanName * This works in layer order, meaning, you need to define the layer that comes * first as the Key, and the incompatible items that _may_ come after. @@ -59,7 +75,8 @@ const incompatible = { // White: ["rare-Pink-Pompadour"], }; -/** + +/** * Require combinations of files when constructing DNA, this bypasses the * randomization and weights. * @@ -68,8 +85,10 @@ const incompatible = { * the items in the array are "required" items that should be pulled from folders * further in the stack */ + const forcedCombinations = { - floral: ["MetallicShades", "Golden Sakura"], + // Force some layers if a certain layer is used. + //floral: ["MetallicShades", "Golden Sakura"], }; const shuffleLayerConfigurations = false; @@ -86,31 +105,56 @@ const background = { brightness: "80%", }; -const extraMetadata = {}; +const extraMetadata = { + // You can add extra info for your collection here. Such as the artist name. + "Artist": "A person", +}; const extraAttributes = () => [ + + //OpenSea Trait Types for rich data = https://docs.opensea.io/docs/metadata-standards + //These are just examples to show dynamic uses. Delete all these if they do not fiy in your project. + { + display_type: "boost_percentage", // Boost trait with lightning icon. Number is shown with a % sign. Circle fill by the percentage. + trait_type: "Health", + value: Math.floor(Math.random() * 100), + }, + { + display_type: "boost_number", // Boost trait with lightning icon. Number is shown with a + sign. Circle is filled. + trait_type: "Energy", + value: Math.floor(Math.random() * 100), + }, + { + display_type: "number", // Appears in the "Stats" area with a large number. "Out of X" value is taken from the collection. + trait_type: "Mana", + value: Math.floor(Math.random() * 100), + }, + { + //Integer value with no display_type set makes the trait appear in the "Rankings" area with a progress bar. Max value is taken from the collection. + trait_type: "Rank", + value: Math.floor(Math.random() * 100), + }, + + { + //display_type date makes a date section appear in the right column near "Rankings" and "Stats." + display_type: "date", + trait_type: "Birthday", + + // Give a random date between; + // Unix Timestamp 1609455600 (GMT Thu Dec 31 2020 23:00:00 GMT+0000) and + // Unix Timestamp 631148400 (GMT Sun Dec 31 1989 23:00:00 GMT+0000) + value: (Math.floor( Math.random() * (1609455634 - 631148434) ) + 631148434) + }, + + { + //String value with no display_type set makes the trait appear in the "Properties" area like layers. + trait_type: "Familly", + value: `Familly #${ Math.floor(Math.random() * (6 - 1 + 1) ) + 1 }`, // Math.floor(Math.random() * (max - min + 1) ) + min; // min max included + } + // Optionally, if you need to overwrite one of your layers attributes. // You can include the same name as the layer, here, and it will overwrite - // - // { - // trait_type: "Bottom lid", - // value: ` Bottom lid # ${Math.random() * 100}`, - // }, - // { - // display_type: "boost_number", - // trait_type: "Aqua Power", - // value: Math.random() * 100, - // }, - // { - // display_type: "boost_number", - // trait_type: "Health", - // value: Math.random() * 100, - // }, - // { - // display_type: "boost_number", - // trait_type: "Mana", - // value: Math.floor(Math.random() * 100), - // }, + ]; const rarityDelimiter = "#"; @@ -129,6 +173,7 @@ module.exports = { layersDir, format, baseUri, + baseExternalUrl, description, background, uniqueDnaTorrance, From 8a78b3b289955efcd96d9b3553467386c86adf58 Mon Sep 17 00:00:00 2001 From: bruceballad Date: Sun, 24 Oct 2021 13:01:10 +0200 Subject: [PATCH 04/12] New removeMetadataContent utility A new utility script to remove a top level data from metadata files. It is created by modifying removeTrait.js utility. This utility can be used for removing unwanted top level data from json files. You can remove a data you may not need in your project, such as "dna" or "external_url". Use by running the following command; ``` node utils/removeMetadataContent.js "Background" ``` or for additional logging, use with the `-d` flag ``` node utils/removeMetadataContent.js "Background" -d ``` --- utils/removeMetadataContent.js | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 utils/removeMetadataContent.js diff --git a/utils/removeMetadataContent.js b/utils/removeMetadataContent.js new file mode 100644 index 000000000..ee3617214 --- /dev/null +++ b/utils/removeMetadataContent.js @@ -0,0 +1,80 @@ +/* +--------------- +This utility can be used for removing unwanted data from json files such as "dna" or "external_url". +Created by modifying removeTrait.js utility. + +Use by running the following command; +``` +node utils/removeMetadataContent.js "Background" +``` + +or for additional logging, use with the `-d` flag +``` +node utils/removeMetadataContent.js "Background" -d +``` + +- BB +--------------- +*/ + + +"use strict"; + +const isLocal = typeof process.pkg === "undefined"; +const basePath = isLocal ? process.cwd() : path.dirname(process.execPath); +const fs = require("fs"); +const path = require("path"); +const { Command } = require("commander"); +const program = new Command(); + +const chalk = require("chalk"); +const jsonDir = `${basePath}/build/json`; +const metadataFilePath = `${basePath}/build/json/_metadata.json`; + +const getIndividualJsonFiles = () => { + return fs + .readdirSync(jsonDir) + .filter((item) => /^[0-9]{1,6}.json/g.test(item)); +}; + +program + .argument("") + .option("-d, --debug", "display some debugging") + .action((target, options, command) => { + const jsonFiles = getIndividualJsonFiles(); + options.debug + ? console.log( + `Found ${jsonFiles.length} json files in "${jsonDir}" to process` + ) + : null; + + console.log(chalk.greenBright.inverse(`Removing ${target}`)); + jsonFiles.forEach((filename) => { + // read the contents + options.debug ? console.log(`removing ${target} from ${filename}`) : null; + const contents = JSON.parse(fs.readFileSync(`${jsonDir}/${filename}`)); + + const hasTarget = contents.hasOwnProperty(target); + + if (!hasTarget) { + console.log(chalk.yellow(`"${target}" not found in ${filename}`)); + } + // remove the target from attributes + + delete contents[target] + + // write the file + fs.writeFileSync( + `${jsonDir}/${filename}`, + JSON.stringify(contents, null, 2) + ); + + options.debug + ? console.log( + hasTarget ? chalk.greenBright("Removed \n") : "…skipped \n" + ) + : null; + }); + }); + +program.parse(); From 17fbecf3fe7bdbf8c7f451c0af4b4ed6aed15e70 Mon Sep 17 00:00:00 2001 From: bruceballad Date: Fri, 29 Oct 2021 12:58:47 +0200 Subject: [PATCH 05/12] Delete 3shadow?blend=multiply&opacity=50.png This file name was preventing GitHub Desktop app from cloning the repo. I know this name was experimental. I could not try renaming it on the website too. So, I need to remove it for now. --- .../3shadow?blend=multiply&opacity=50.png | Bin 33178 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 layers/Clothes/Shirtthree#3/3shadow?blend=multiply&opacity=50.png diff --git a/layers/Clothes/Shirtthree#3/3shadow?blend=multiply&opacity=50.png b/layers/Clothes/Shirtthree#3/3shadow?blend=multiply&opacity=50.png deleted file mode 100644 index 88a1e2fea4a146fb2e56511d399d7c31439fb15d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33178 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 pk51v=ap} Date: Fri, 29 Oct 2021 15:53:18 +0200 Subject: [PATCH 06/12] Changes based on the requests from nftchef #config.js - The sample text of `const description =` property is returned to how it was previously. - `prependNameInDescription` property is removed as we now replace `{name}` key in the text for embeding names in the text anywhere we want, not only prepend. - The order of new properties added to the sample layer config groups are ordered for a better merge and easy reading. - Additional `extraAttributes` examples are removed. #main.js - String combining in asset name and description texts are now all by the proper use of template strings. - The functions that asset names and descriptions are built are moved to a better place to reduce repeating codes. - Instead of passing `_prefix`, `_offset`, and `_suffix`, separately and using these during metadata writing, they are now being passed to metadata builder as just `_name`. - The check and switch for `_descriptionOverwrite` is now done before the description is passed to metadata builder as `_description`. - `_prependNameInDescription` propery is removed. Now names get embeded in the text with RegExp replacing the `{name}` key before passing the `_description` text to metadata. #README.md - Explanations and examples for the new `suffix` and `descriptionOverwrite` properties are added. In the `# Name + Number prefix, suffix, and counter reset for configuration sets` section (the title is modified too). - A new section for `Remove Metadata Content` utility is added for directives on how to use this utility. #3shadow_blend=multiply&opacity=50.png - This file name was preventing GitHub Desktop app from cloning the repo to a local disk. It was only removed for the cloning step. Now it is being added back to match the source. --- README.md | 30 ++++++- .../3shadow_blend=multiply&opacity=50.png | Bin 0 -> 33178 bytes src/config.js | 77 ++++++------------ src/main.js | 43 +++++----- 4 files changed, 74 insertions(+), 76 deletions(-) create mode 100644 layers/Clothes/Shirtthree#3/3shadow_blend=multiply&opacity=50.png diff --git a/README.md b/README.md index dc516c2e4..62055d4a3 100644 --- a/README.md +++ b/README.md @@ -102,9 +102,9 @@ HAIR Where the containing folder will define the traits _rarity_ and in the event that it is selected as part of the randomization, BOTH nested images will be included in the final result, in alphabetical oder–hence the 1, 2, numbering. -# Name + Number prefix and reset for configuration sets +# Name + Number prefix, suffix, and counter reset for configuration sets -If you are using the generator with multiple `layerConfiguration` objects to generate different species/genders/types, it is possible to add a name prefix and a reset counter for the name, so the token names start at `1` for each type. +If you are using the generator with multiple `layerConfiguration` objects to generate different species/genders/types, it is possible to add a name prefix and a suffix, as well as a reset counter for the names, so the token names start at `1` for each type. for example, if you are creating multiple animals, and each animal should start with `Animal #1`, but the token numbers should increment as normal, you can use the following `namePrefix` and `resetNameIndex` properties to acheive this. @@ -112,6 +112,9 @@ for example, if you are creating multiple animals, and each animal should start growEditionSizeTo: 10, namePrefix: "Lion", resetNameIndex: true, // this will start the Lion count at #1 instead of #6 + nameSuffix: "Set-A", // add a suffix after the number. if resetNameIndex is on too, put the reseted counter after the suffix + descriptionOverwrite: "Unique Description For Layer Set-A which {name} is a member of.", // LayerConfig spesific descriptions. Use {name} to embed asset names. + layersOrder: [ { name: "Background" }, { name: "Eyeball" }, @@ -122,6 +125,14 @@ for example, if you are creating multiple animals, and each animal should start You may choose to omit the `resetNameIndex` or set it to false if you would instead like each layer set to use the token (\_edition) number in the name–it does this by default. + +If you want each config set to have a sub group name other than the prefix, you can add it with `nameSuffix` property. And form a name like `Animal #5 Monkey`. +If you use `resetNameIndex: true` propert together with `nameSuffix` property, you the reset counter will be added after the suffix name, and form a name like `Animal #5 - Monkey #3`. + +You can also set different descriptions for different layer configuration sets by using `descriptionOverwrite` property. Write a string that will overwrite the default description text only for the current layer set. + +If you want you can embed the generated asset names into the description texts by writing `{name}` in the string and make each description unique. This method works for the default description text too. You can form unique descriptions such as; `Unique Description For Layer Set-A which "Animal #5 - Monkey #3" is a member of.` + # Chance of "NONE" or skipping a trait If you would like any given layer or sublayer to have a chance at _rolling_ `NONE`, you can do this by adding a blank PNG to the folder, and giving it the name of `NONE` + the weight you would like to use for it's chance of being chosen–identical to any other layer. @@ -292,6 +303,21 @@ If you would like to print additional logging, use the `-d` flag node utils/removeTrait.js "Background" -d ``` +# Remove Metadata Content + +If you need to remove an unwanted top-level data from json files such as `dna` or `external_url` you can use the `removeMetadataContent` util command. +Note that, this utility is a bit different than `removeTrait` util, as you can delete a necessary line such as `name` or `image` too. So, use this with caution. + +Use this util by running a command like the following; +``` +node utils/removeMetadataContent.js "external_url" +``` + +If you would like to print additional logging, use the `-d` flag +``` +node utils/removeMetadataContent.js "external_url" -d +``` + # Randomly Insert Rare items - Replace Util If you would like to manually add 'hand drawn' or unique versions into the pool of generated items, this utility takes a source folder (of your new artwork) and inserts it into the `build` directory, assigning them to random id's. diff --git a/layers/Clothes/Shirtthree#3/3shadow_blend=multiply&opacity=50.png b/layers/Clothes/Shirtthree#3/3shadow_blend=multiply&opacity=50.png new file mode 100644 index 0000000000000000000000000000000000000000..88a1e2fea4a146fb2e56511d399d7c31439fb15d GIT binary patch literal 33178 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 pk51v=ap} [ - - //OpenSea Trait Types for rich data = https://docs.opensea.io/docs/metadata-standards - //These are just examples to show dynamic uses. Delete all these if they do not fiy in your project. - { - display_type: "boost_percentage", // Boost trait with lightning icon. Number is shown with a % sign. Circle fill by the percentage. - trait_type: "Health", - value: Math.floor(Math.random() * 100), - }, - { - display_type: "boost_number", // Boost trait with lightning icon. Number is shown with a + sign. Circle is filled. - trait_type: "Energy", - value: Math.floor(Math.random() * 100), - }, - { - display_type: "number", // Appears in the "Stats" area with a large number. "Out of X" value is taken from the collection. - trait_type: "Mana", - value: Math.floor(Math.random() * 100), - }, - { - //Integer value with no display_type set makes the trait appear in the "Rankings" area with a progress bar. Max value is taken from the collection. - trait_type: "Rank", - value: Math.floor(Math.random() * 100), - }, - - { - //display_type date makes a date section appear in the right column near "Rankings" and "Stats." - display_type: "date", - trait_type: "Birthday", - - // Give a random date between; - // Unix Timestamp 1609455600 (GMT Thu Dec 31 2020 23:00:00 GMT+0000) and - // Unix Timestamp 631148400 (GMT Sun Dec 31 1989 23:00:00 GMT+0000) - value: (Math.floor( Math.random() * (1609455634 - 631148434) ) + 631148434) - }, - - { - //String value with no display_type set makes the trait appear in the "Properties" area like layers. - trait_type: "Familly", - value: `Familly #${ Math.floor(Math.random() * (6 - 1 + 1) ) + 1 }`, // Math.floor(Math.random() * (max - min + 1) ) + min; // min max included - } - // Optionally, if you need to overwrite one of your layers attributes. // You can include the same name as the layer, here, and it will overwrite - + // + // { + // trait_type: "Bottom lid", + // value: ` Bottom lid # ${Math.random() * 100}`, + // }, + // { + // display_type: "boost_number", + // trait_type: "Aqua Power", + // value: Math.random() * 100, + // }, + // { + // display_type: "boost_number", + // trait_type: "Health", + // value: Math.random() * 100, + // }, + // { + // display_type: "boost_number", + // trait_type: "Mana", + // value: Math.floor(Math.random() * 100), + // }, ]; const rarityDelimiter = "#"; diff --git a/src/main.js b/src/main.js index 025d94cce..132964ca7 100644 --- a/src/main.js +++ b/src/main.js @@ -200,12 +200,9 @@ const drawBackground = () => { const addMetadata = (_dna, _edition, _prefixData) => { let dateTime = Date.now(); const { - _descriptionOverwrite, - _prependNameInDescription, - _prefix, - _offset, - _suffix, - _imageHash + _name, + _description, + _imageHash, } = _prefixData; const combinedAttrs = [...attributesList, ...extraAttributes()]; @@ -221,11 +218,9 @@ const addMetadata = (_dna, _edition, _prefixData) => { let tempMetadata = { dna: hash(_dna), - // New name builder. It can form names like; "PREFIX #10 - SUFFIX #2". - BB - name: `${_prefix ? _prefix + " " : ""}#${_suffix ? _edition : _edition - _offset}${_suffix ? " " + _suffix + (_offset>0 ? " #" + (_edition - _offset) : "") : ""}`, + name: _name, - // new description builder, it can prepends the asset name, AND overwrite the description for different layerConfigs. Can form unique descriptions like; "Item #10 is an art piece from Collection X". - BB - description: `${_prependNameInDescription ? `${_prefix ? _prefix + " " : ""}#${_suffix ? _edition : _edition - _offset}${_suffix ? " " + _suffix + (_offset>0 ? " #" + (_edition - _offset) : "") : ""}` : ""}${_descriptionOverwrite ? _descriptionOverwrite : description}`, + description: _description, // Adds external_url if the baseExternalUrl in config is not empty, combines it with edition numbers. - BB ...(baseExternalUrl !== "" && { external_url: `${baseExternalUrl}${_edition}` }), @@ -586,15 +581,6 @@ const startCreating = async () => { ); const _imageHash = hash(savedFile); - // if there's a descriptionOverwrite for the current layerConfig, then - // use that description in the assets - BB - const _descriptionOverwrite = layerConfigurations[layerConfigIndex].descriptionOverwrite - ? layerConfigurations[layerConfigIndex].descriptionOverwrite - : null; - - // prependNameInDescription - BB - const _prependNameInDescription = layerConfigurations[layerConfigIndex].prependNameInDescription; - // if there's a prefix for the current configIndex, then // start count back at 1 for the name, only. const _prefix = layerConfigurations[layerConfigIndex].namePrefix @@ -621,12 +607,21 @@ const startCreating = async () => { ? layerConfigurations[layerConfigIndex].nameSuffix : null; + + // New name builder. It can form names like; "PREFIX #10 - SUFFIX #2". - BB + //const _name = `${_prefix ? _prefix + " " : ""}#${_suffix ? _edition : _edition - _offset}${_suffix ? " " + _suffix + (_offset>0 ? " #" + (_edition - _offset) : "") : ""}`; + const _name = `${_prefix ? `${_prefix} ` : ``}#${_suffix ? abstractedIndexes[0] : abstractedIndexes[0] - _offset}${_suffix ? ` ${_suffix}${layerConfigurations[layerConfigIndex].resetNameIndex ? ` #${abstractedIndexes[0] - _offset}` : ``}` : ``}`; + console.log("_offset: "+_offset); + + // New description builder, it can embed the asset name, AND overwrite the description for different layerConfigs. + // Can form unique descriptions like; "Item #10 is an art piece from Collection X". + const _description = (layerConfigurations[layerConfigIndex].descriptionOverwrite + ? layerConfigurations[layerConfigIndex].descriptionOverwrite + : description).replace(/{name}/g, _name); + addMetadata(newDna, abstractedIndexes[0], { - _descriptionOverwrite, // BB - _prependNameInDescription, // BB - _prefix, - _offset, - _suffix, + _name, + _description, _imageHash, }); From c9d4fa99d898a4e47dda36a0f6e3606fc115ecba Mon Sep 17 00:00:00 2001 From: bruceballad Date: Thu, 4 Nov 2021 00:32:06 +0100 Subject: [PATCH 07/12] small comment clean up. Deleted a leftover comment line. --- src/main.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.js b/src/main.js index a084c890f..de3c18303 100644 --- a/src/main.js +++ b/src/main.js @@ -634,8 +634,7 @@ const startCreating = async () => { : null; - // New name builder. It can form names like; "PREFIX #10 - SUFFIX #2". - BB - //const _name = `${_prefix ? _prefix + " " : ""}#${_suffix ? _edition : _edition - _offset}${_suffix ? " " + _suffix + (_offset>0 ? " #" + (_edition - _offset) : "") : ""}`; + // New name builder. It can form names like; "PREFIX #10 - SUFFIX #2". const _name = `${_prefix ? `${_prefix} ` : ``}#${_suffix ? abstractedIndexes[0] : abstractedIndexes[0] - _offset}${_suffix ? ` ${_suffix}${layerConfigurations[layerConfigIndex].resetNameIndex ? ` #${abstractedIndexes[0] - _offset}` : ``}` : ``}`; console.log("_offset: "+_offset); From 70b9cd4b5d02f44d5f778bab85c7678605c32739 Mon Sep 17 00:00:00 2001 From: bruceballad Date: Tue, 16 Nov 2021 13:51:54 +0100 Subject: [PATCH 08/12] Fix for using createPreviewCollage.js with JPEG outputs Added a small check for the outputJPEG setting. Now it can pull images with .jpg extension to create preview collage too. --- utils/createPreviewCollage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/createPreviewCollage.js b/utils/createPreviewCollage.js index 724cda9a2..931f49e5e 100644 --- a/utils/createPreviewCollage.js +++ b/utils/createPreviewCollage.js @@ -8,7 +8,7 @@ const { createCanvas, loadImage } = require("canvas"); const buildDir = `${basePath}/build`; console.log(path.join(basePath, "/src/config.js")); -const { preview } = require(path.join(basePath, "/src/config.js")); +const { preview, outputJPEG } = require(path.join(basePath, "/src/config.js")); // read json data const rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`); @@ -37,7 +37,7 @@ const saveProjectPreviewImage = async (_data) => { // Don't want to rely on "edition" for assuming index for (let index = 0; index < _data.length; index++) { const nft = _data[index]; - await loadImage(`${buildDir}/images/${nft.edition}.png`).then((image) => { + await loadImage(`${buildDir}/images/${nft.edition}.${ outputJPEG ? `jpg` : `png` }`).then((image) => { previewCtx.drawImage( image, thumbWidth * (index % thumbPerRow), From a4c6c8ddfa7f9fb5c3c0df936e9ac2ba24ef883a Mon Sep 17 00:00:00 2001 From: bruceballad Date: Tue, 16 Nov 2021 14:37:26 +0100 Subject: [PATCH 09/12] fix _offset console message + edition reports with name Removed a leftover "_offset" console message while generating images. Added "name" info in the "Created edition: #" messages in console for better reports. This will allow following prefix, suffix. and offset number changes while generating images. --- src/main.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.js b/src/main.js index ccc71315e..b22ff5674 100644 --- a/src/main.js +++ b/src/main.js @@ -641,7 +641,6 @@ const startCreating = async () => { // New name builder. It can form names like; "PREFIX #10 - SUFFIX #2". const _name = `${_prefix ? `${_prefix} ` : ``}#${_suffix ? abstractedIndexes[0] : abstractedIndexes[0] - _offset}${_suffix ? ` ${_suffix}${layerConfigurations[layerConfigIndex].resetNameIndex ? ` #${abstractedIndexes[0] - _offset}` : ``}` : ``}`; - console.log("_offset: "+_offset); // New description builder, it can embed the asset name, AND overwrite the description for different layerConfigs. // Can form unique descriptions like; "Item #10 is an art piece from Collection X". @@ -657,7 +656,7 @@ const startCreating = async () => { saveMetaDataSingleFile(abstractedIndexes[0]); console.log( - `Created edition: ${abstractedIndexes[0]}, with DNA: ${hash( + `Created edition: ${abstractedIndexes[0]}, "${_name}" with DNA: ${hash( newDna )}` ); From a38859dc15e8762b08ac55ccaf2242f70f1597b7 Mon Sep 17 00:00:00 2001 From: bruceballad Date: Tue, 16 Nov 2021 21:25:24 +0100 Subject: [PATCH 10/12] Fix for using updateBaseUri.js with JPEG outputs The baseUri util was always making the file extension in URLs ".png". Added a small check for the outputJPEG setting. Now it can update baseUri and write ".jpg" or ".png" based on the config outputJPEG setting. --- utils/updateBaseUri.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/updateBaseUri.js b/utils/updateBaseUri.js index 5d37318be..747617e44 100644 --- a/utils/updateBaseUri.js +++ b/utils/updateBaseUri.js @@ -6,14 +6,14 @@ const basePath = isLocal ? process.cwd() : path.dirname(process.execPath); const fs = require("fs"); console.log(path.join(basePath, "/src/config.js")); -const { baseUri } = require(path.join(basePath, "/src/config.js")); +const { baseUri, outputJPEG } = require(path.join(basePath, "/src/config.js")); // read json data let rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`); let data = JSON.parse(rawdata); data.forEach((item) => { - item.image = `${baseUri}/${item.edition}.png`; + item.image = `${baseUri}/${item.edition}.${ outputJPEG ? `jpg` : `png` }`; fs.writeFileSync( `${basePath}/build/json/${item.edition}.json`, JSON.stringify(item, null, 2) From 064e4b74026dc2b17627229b2749ff81225c95c3 Mon Sep 17 00:00:00 2001 From: bruceballad Date: Sun, 21 Nov 2021 21:11:16 +0100 Subject: [PATCH 11/12] adds bypass DNA manual update reflecting the last commit made by @nftchef https://github.com/nftchef/hashlips_art_engine/commit/2e9d74e61ae2f60f1ce33558b59dcfc28056dd22 --- README.md | 15 ++++++++++++ src/config.js | 7 +++++- src/main.js | 63 ++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index afdfc7cc5..879b98676 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,21 @@ HAIR Where the containing folder will define the traits _rarity_ and in the event that it is selected as part of the randomization, BOTH nested images will be included in the final result, in alphabetical oder–hence the 1, 2, numbering. +### Options + +#### Exclude a layer from DNA + +If you want to have a layer _ignored_ in the DNA uniqueness check, you can set `bypassDNA: true` in the `options` object. This has the effect of making sure the rest of the traits are unique while not considering the `Background` Layers as traits, for example. The layers _are_ included in the final image. + +```js +layersOrder: [ + { name: "Background" }, + { name: "Background" , + options: { + bypassDNA: false; + } + }, +``` ### Sublayer Options diff --git a/src/config.js b/src/config.js index 5d03de35a..0ba8e6537 100644 --- a/src/config.js +++ b/src/config.js @@ -31,7 +31,12 @@ const layerConfigurations = [ nameSuffix: "Set A", // add a suffix after the number. if resetNameIndex is on too, put the reseted counter after the suffix - BB descriptionOverwrite: "{name} with Unique Description For Layer Set A", // LayerConfig spesific descriptions. Use {name} to embed asset names. layersOrder: [ - { name: "Back Accessory" }, + { + name: "Back Accessory", + options: { + bypassDNA: true, + }, + }, { name: "Head" }, { name: "Clothes" }, { name: "Eyes" }, diff --git a/src/main.js b/src/main.js index b22ff5674..a27271ca5 100644 --- a/src/main.js +++ b/src/main.js @@ -200,6 +200,10 @@ const layersSetup = (layersOrder) => { ...(layerObj.display_type !== undefined && { display_type: layerObj.display_type, }), + bypassDNA: + layerObj.options?.["bypassDNA"] !== undefined + ? layerObj.options?.["bypassDNA"] + : false, }; }); @@ -338,6 +342,45 @@ const constructLayerToDna = (_dna = [], _layers = []) => { return mappedDnaToLayers; }; +/** + * In some cases a DNA string may contain optional query parameters for options + * such as bypassing the DNA isUnique check, this function filters out those + * items without modifying the stored DNA. + * + * @param {String} _dna New DNA string + * @returns new DNA string with any items that should be filtered, removed. + */ + const filterDNAOptions = (_dna) => { + const filteredDNA = _dna.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("="); + return { ...r, [keyPairs[0]]: keyPairs[1] }; + }, []); + + return options.bypassDNA; + }); + + return filteredDNA; +}; + +/** + * Cleaning function for DNA strings. When DNA strings include an option, it + * is added to the filename with a ?setting=value query string. It needs to be + * removed to properly access the file name before Drawing. + * + * @param {String} _dna The entire newDNA string + * @returns Cleaned DNA string without querystring parameters. + */ +const removeQueryStrings = (_dna) => { + const query = /(\?.*$)/; + return _dna.replace(query, ""); +}; + const isDnaUnique = (_DnaList = [], _dna = []) => { let foundDna = _DnaList.find((i) => i.join("") === _dna.join("")); return foundDna == undefined ? true : false; @@ -360,7 +403,8 @@ function pickRandomElement( dnaSequence, parentId, incompatibleDNA, - forcedDNA + forcedDNA, + bypassDNA ) { let totalWeight = 0; // Does this layer include a forcedDNA item? ya? just return it. @@ -371,7 +415,7 @@ function pickRandomElement( debugLogs ? console.log(chalk.yellowBright(`Force picking ${forcedPick.name}/n`)) : null; - let dnaString = `${parentId}.${forcedPick.id}:${forcedPick.filename}`; + let dnaString = `${parentId}.${forcedPick.id}:${forcedPick.filename}${bypassDNA}`; return dnaSequence.push(dnaString); } @@ -401,7 +445,7 @@ function pickRandomElement( // If there is no weight, it's required, always include it // If directory has %, that is % chance to enter the dir if (element.weight == "required" && !element.sublayer) { - let dnaString = `${parentId}.${element.id}:${element.filename}`; + let dnaString = `${parentId}.${element.id}:${element.filename}${bypassDNA}`; dnaSequence.unshift(dnaString); return; } @@ -411,7 +455,8 @@ function pickRandomElement( dnaSequence, `${parentId}.${element.id}`, incompatibleDNA, - forcedDNA + forcedDNA, + bypassDNA ); } if (element.weight !== "required") { @@ -464,7 +509,8 @@ function pickRandomElement( dnaSequence, `${parentId}.${currentLayers[i].id}`, incompatibleDNA, - forcedDNA + forcedDNA, + bypassDNA ) ); } @@ -473,7 +519,7 @@ function pickRandomElement( if (currentLayers[i].name === emptyLayerName) { return dnaSequence; } - let dnaString = `${parentId}.${currentLayers[i].id}:${currentLayers[i].filename}`; + let dnaString = `${parentId}.${currentLayers[i].id}:${currentLayers[i].filename}${bypassDNA}`; return dnaSequence.push(dnaString); } } @@ -505,7 +551,8 @@ const createDna = (_layers) => { layerSequence, layer.id, incompatibleDNA, - forcedDNA + forcedDNA, + layer.bypassDNA ? "?bypassDNA=true" : "" ); const sortedLayers = sortLayers(layerSequence); dnaSequence = [...dnaSequence, [sortedLayers]]; @@ -661,7 +708,7 @@ const startCreating = async () => { )}` ); }); - dnaList.push(newDna); + dnaList.push(filterDNAOptions(newDna)); editionCount++; abstractedIndexes.shift(); } else { From 3d139dc9af2c56d038656c195b2556825ab18efa Mon Sep 17 00:00:00 2001 From: bruceballad Date: Sun, 2 Jan 2022 05:30:06 +0100 Subject: [PATCH 12/12] Small Update Just changed counter reset to 'true' for the second sample layerconfig set. --- .../3shadow_blend=multiply&opacity=50.png | Bin 33178 -> 0 bytes package-lock.json | 220 ++++++++++++++++-- package.json | 5 +- src/config.js | 2 +- 4 files changed, 205 insertions(+), 22 deletions(-) delete mode 100644 layers/Clothes/Shirtthree#3/3shadow_blend=multiply&opacity=50.png diff --git a/layers/Clothes/Shirtthree#3/3shadow_blend=multiply&opacity=50.png b/layers/Clothes/Shirtthree#3/3shadow_blend=multiply&opacity=50.png deleted file mode 100644 index 88a1e2fea4a146fb2e56511d399d7c31439fb15d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33178 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 pk51v=ap}= 0.0.1", - "crypt": ">= 0.0.1" - } - }, "signal-exit": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", @@ -434,6 +608,14 @@ "ansi-regex": "^2.0.0" } }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, "tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", diff --git a/package.json b/package.json index fc0952f3b..1a7e7bb3c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "build": "node index.js", "generate:solana": "node index.js && node utils/metaplex.js", "generate:cardano": "node index.js && node utils/cardano.js", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "preview": "node utils/createPreviewCollage.js" }, "author": "Daniel Eugene Botha (HashLips), NFTChef", "license": "MIT", @@ -29,4 +30,4 @@ "eslint-plugin-node": "^11.1.0", "keccak256": "^1.0.3" } -} \ No newline at end of file +} diff --git a/src/config.js b/src/config.js index 5e615f38d..f2cc24eea 100644 --- a/src/config.js +++ b/src/config.js @@ -49,7 +49,7 @@ const layerConfigurations = [ { growEditionSizeTo: 10, namePrefix: "Monkey", - resetNameIndex: false, // this will start the count at #1 for this layerconfig + resetNameIndex: true, // this will start the count at #1 for this layerconfig nameSuffix: "Set B", // add a suffix after the number. if resetNameIndex is on too, put the reseted counter after the suffix - BB descriptionOverwrite: "Description for {name} from Layer Set B", // LayerConfig spesific descriptions. Use {name} to embed asset names.