|
| 1 | +/************************************************************** |
| 2 | + * UTILITY FUNCTIONS |
| 3 | + * - scroll to BEGIN CONFIG to provide the config values |
| 4 | + *************************************************************/ |
1 | 5 | const fs = require("fs");
|
2 |
| -const width = 1000; |
3 |
| -const height = 1000; |
4 | 6 | const dir = __dirname;
|
5 |
| -const description = "This is an NFT made by the coolest generative code."; |
6 |
| -const baseImageUri = "https://hashlips/nft"; |
7 |
| -const startEditionFrom = 1; |
8 |
| -const editionSize = 10; |
9 |
| -const rarityWeights = [ |
10 |
| - { |
11 |
| - value: "super_rare", |
12 |
| - from: 1, |
13 |
| - to: 1, |
14 |
| - }, |
15 |
| - { |
16 |
| - value: "rare", |
17 |
| - from: 2, |
18 |
| - to: 5, |
19 |
| - }, |
20 |
| - { |
21 |
| - value: "original", |
22 |
| - from: 5, |
23 |
| - to: editionSize, |
24 |
| - }, |
25 |
| -]; |
26 | 7 |
|
| 8 | +// adds a rarity to the configuration. This is expected to correspond with a directory containing the rarity for each defined layer |
| 9 | +// @param _id - id of the rarity |
| 10 | +// @param _from - number in the edition to start this rarity from |
| 11 | +// @param _to - number in the edition to generate this rarity to |
| 12 | +// @return a rarity object used to dynamically generate the NFTs |
| 13 | +const addRarity = (_id, _from, _to) => { |
| 14 | + const _rarityWeight = { |
| 15 | + value: _id, |
| 16 | + from: _from, |
| 17 | + to: _to, |
| 18 | + layerPercent: {} |
| 19 | + }; |
| 20 | + return _rarityWeight; |
| 21 | +}; |
| 22 | + |
| 23 | +// get the name without last 4 characters -> slice .png from the name |
27 | 24 | const cleanName = (_str) => {
|
28 | 25 | let name = _str.slice(0, -4);
|
29 | 26 | return name;
|
30 | 27 | };
|
31 | 28 |
|
32 |
| -const getElements = (path) => { |
| 29 | +// reads the filenames of a given folder and returns it with its name and path |
| 30 | +const getElements = (_path, _elementCount) => { |
33 | 31 | return fs
|
34 |
| - .readdirSync(path) |
| 32 | + .readdirSync(_path) |
35 | 33 | .filter((item) => !/(^|\/)\.[^\/\.]/g.test(item))
|
36 | 34 | .map((i) => {
|
37 | 35 | return {
|
| 36 | + id: _elementCount, |
38 | 37 | name: cleanName(i),
|
39 |
| - path: `${path}/${i}`, |
| 38 | + path: `${_path}/${i}` |
40 | 39 | };
|
41 | 40 | });
|
42 | 41 | };
|
43 | 42 |
|
| 43 | +// adds a layer to the configuration. The layer will hold information on all the defined parts and |
| 44 | +// where they should be rendered in the image |
| 45 | +// @param _id - id of the layer |
| 46 | +// @param _position - on which x/y value to render this part |
| 47 | +// @param _size - of the image |
| 48 | +// @return a layer object used to dynamically generate the NFTs |
| 49 | +const addLayer = (_id, _position, _size) => { |
| 50 | + if (!_id) { |
| 51 | + console.log('error adding layer, parameters id required'); |
| 52 | + return null; |
| 53 | + } |
| 54 | + if (!_position) { |
| 55 | + _position = { x: 0, y: 0 }; |
| 56 | + } |
| 57 | + if (!_size) { |
| 58 | + _size = { width: width, height: height } |
| 59 | + } |
| 60 | + // add two different dimension for elements: |
| 61 | + // - all elements with their path information |
| 62 | + // - only the ids mapped to their rarity |
| 63 | + let elements = []; |
| 64 | + let elementCount = 0; |
| 65 | + let elementIdsForRarity = {}; |
| 66 | + rarityWeights.forEach((rarityWeight) => { |
| 67 | + let elementsForRarity = getElements(`${dir}/${_id}/${rarityWeight.value}`); |
| 68 | + |
| 69 | + elementIdsForRarity[rarityWeight.value] = []; |
| 70 | + elementsForRarity.forEach((_elementForRarity) => { |
| 71 | + _elementForRarity.id = `${editionDnaPrefix}${elementCount}`; |
| 72 | + elements.push(_elementForRarity); |
| 73 | + elementIdsForRarity[rarityWeight.value].push(_elementForRarity.id); |
| 74 | + elementCount++; |
| 75 | + }) |
| 76 | + elements[rarityWeight.value] = elementsForRarity; |
| 77 | + }); |
| 78 | + |
| 79 | + let elementsForLayer = { |
| 80 | + id: _id, |
| 81 | + position: _position, |
| 82 | + size: _size, |
| 83 | + elements, |
| 84 | + elementIdsForRarity |
| 85 | + }; |
| 86 | + return elementsForLayer; |
| 87 | +}; |
| 88 | + |
| 89 | +// adds layer-specific percentages to use one vs another rarity |
| 90 | +// @param _rarityId - the id of the rarity to specifiy |
| 91 | +// @param _layerId - the id of the layer to specifiy |
| 92 | +// @param _percentages - an object defining the rarities and the percentage with which a given rarity for this layer should be used |
| 93 | +const addRarityPercentForLayer = (_rarityId, _layerId, _percentages) => { |
| 94 | + let _rarityFound = false; |
| 95 | + rarityWeights.forEach((_rarityWeight) => { |
| 96 | + if (_rarityWeight.value === _rarityId) { |
| 97 | + let _percentArray = []; |
| 98 | + for (let percentType in _percentages) { |
| 99 | + _percentArray.push({ |
| 100 | + id: percentType, |
| 101 | + percent: _percentages[percentType] |
| 102 | + }) |
| 103 | + } |
| 104 | + _rarityWeight.layerPercent[_layerId] = _percentArray; |
| 105 | + _rarityFound = true; |
| 106 | + } |
| 107 | + }); |
| 108 | + if (!_rarityFound) { |
| 109 | + console.log(`rarity ${_rarityId} not found, failed to add percentage information`); |
| 110 | + } |
| 111 | +} |
| 112 | + |
| 113 | +/************************************************************** |
| 114 | + * BEGIN CONFIG |
| 115 | + *************************************************************/ |
| 116 | + |
| 117 | +// image width in pixels |
| 118 | +const width = 1000; |
| 119 | +// image height in pixels |
| 120 | +const height = 1000; |
| 121 | +// description for NFT in metadata file |
| 122 | +const description = "This is an NFT made by the coolest generative code."; |
| 123 | +// base url to use in metadata file |
| 124 | +// 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 |
| 125 | +const baseImageUri = "https://hashlips/nft"; |
| 126 | +// id for edition to start from |
| 127 | +const startEditionFrom = 1; |
| 128 | +// amount of NFTs to generate in edition |
| 129 | +const editionSize = 10; |
| 130 | +// prefix to add to edition dna ids (to distinguish dna counts from different generation processes for the same collection) |
| 131 | +const editionDnaPrefix = 0 |
| 132 | + |
| 133 | +// create required weights |
| 134 | +// for each weight, call 'addRarity' with the id and from which to which element this rarity should be applied |
| 135 | +let rarityWeights = [ |
| 136 | + addRarity('super_rare', 1, 1), |
| 137 | + addRarity('rare', 2, 5), |
| 138 | + addRarity('original', 5, 10) |
| 139 | +]; |
| 140 | + |
| 141 | +// create required layers |
| 142 | +// for each layer, call 'addLayer' with the id and optionally the positioning and size |
| 143 | +// the id would be the name of the folder in your input directory, e.g. 'ball' for ./input/ball |
44 | 144 | const layers = [
|
45 |
| - { |
46 |
| - elements: { |
47 |
| - original: getElements(`${dir}/ball/original`), |
48 |
| - rare: getElements(`${dir}/ball/rare`), |
49 |
| - super_rare: getElements(`${dir}/ball/super_rare`), |
50 |
| - }, |
51 |
| - position: { x: 0, y: 0 }, |
52 |
| - size: { width: width, height: height }, |
53 |
| - }, |
54 |
| - { |
55 |
| - elements: { |
56 |
| - original: getElements(`${dir}/eye color/original`), |
57 |
| - rare: getElements(`${dir}/eye color/rare`), |
58 |
| - super_rare: getElements(`${dir}/eye color/super_rare`), |
59 |
| - }, |
60 |
| - position: { x: 0, y: 0 }, |
61 |
| - size: { width: width, height: height }, |
62 |
| - }, |
63 |
| - { |
64 |
| - elements: { |
65 |
| - original: getElements(`${dir}/iris/original`), |
66 |
| - rare: getElements(`${dir}/iris/rare`), |
67 |
| - super_rare: getElements(`${dir}/iris/super_rare`), |
68 |
| - }, |
69 |
| - position: { x: 0, y: 0 }, |
70 |
| - size: { width: width, height: height }, |
71 |
| - }, |
72 |
| - { |
73 |
| - elements: { |
74 |
| - original: getElements(`${dir}/shine/original`), |
75 |
| - rare: getElements(`${dir}/shine/rare`), |
76 |
| - super_rare: getElements(`${dir}/shine/super_rare`), |
77 |
| - }, |
78 |
| - position: { x: 0, y: 0 }, |
79 |
| - size: { width: width, height: height }, |
80 |
| - }, |
81 |
| - { |
82 |
| - elements: { |
83 |
| - original: getElements(`${dir}/bottom lid/original`), |
84 |
| - rare: getElements(`${dir}/bottom lid/rare`), |
85 |
| - super_rare: getElements(`${dir}/bottom lid/super_rare`), |
86 |
| - }, |
87 |
| - position: { x: 0, y: 0 }, |
88 |
| - size: { width: width, height: height }, |
89 |
| - }, |
90 |
| - { |
91 |
| - elements: { |
92 |
| - original: getElements(`${dir}/top lid/original`), |
93 |
| - rare: getElements(`${dir}/top lid/rare`), |
94 |
| - super_rare: getElements(`${dir}/top lid/super_rare`), |
95 |
| - }, |
96 |
| - position: { x: 0, y: 0 }, |
97 |
| - size: { width: width, height: height }, |
98 |
| - }, |
| 145 | + addLayer('ball'), |
| 146 | + addLayer('eye color'), |
| 147 | + addLayer('iris'), |
| 148 | + addLayer('shine'), |
| 149 | + addLayer('bottom lid'), |
| 150 | + addLayer('top lid') |
99 | 151 | ];
|
100 | 152 |
|
| 153 | +// provide any specific percentages that are required for a given layer and rarity level |
| 154 | +// all provided options are used based on their percentage values to decide which layer to select from |
| 155 | +addRarityPercentForLayer('super_rare', 'ball', { 'super_rare': 33, 'rare': 33, 'original': 33 }); |
| 156 | +addRarityPercentForLayer('super_rare', 'eye color', { 'super_rare': 50, 'rare': 25, 'original': 25 }); |
| 157 | +addRarityPercentForLayer('original', 'eye color', { 'super_rare': 50, 'rare': 25, 'original': 25 }); |
| 158 | + |
101 | 159 | module.exports = {
|
102 | 160 | layers,
|
103 | 161 | width,
|
|
0 commit comments