thank you. what the best way to provide you with in info?
// utilise Moralis
const Moralis = require("moralis/node");
// canvas for image compile
const { createCanvas, loadImage } = require("canvas");
// import config
const {
layers,
width,
height,
description,
baseImageUri,
editionSize,
startEditionFrom,
rarityWeights,
} = require("./input/config.js");
const console = require("console");
const fs = require("fs");
const request = require("request");
const { default: axios } = require("axios");
const canvas = createCanvas(width, height);
const ctx = canvas.getContext("2d");
Moralis.start({ serverUrl, appId, masterKey });
// adds a signature to the top left corner of the canvas for pre-production
const signImage = (_sig) => {
ctx.fillStyle = "#000000";
ctx.font = "bold 30pt Helvetica";
ctx.textBaseline = "top";
ctx.textAlign = "left";
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%)`;
return pastel;
};
const drawBackground = () => {
ctx.fillStyle = genColor();
ctx.fillRect(0, 0, width, height);
};
// add metadata for individual nft edition
const generateMetadata = (_dna, _edition, _attributesList, _path) => {
let dateTime = Date.now();
let tempMetadata = {
dna: _dna.join(""),
name: `#${_edition}`,
description: description,
image: _path || baseImageUri,
edition: _edition,
date: dateTime,
attributes: _attributesList,
};
return tempMetadata;
};
// prepare attributes for the given element to be used as metadata
const getAttributeForElement = (_element) => {
let selectedElement = _element.layer.selectedElement;
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}`);
resolve({ layer: _layer, loadedImage: image });
});
};
const drawElement = (_element) => {
ctx.drawImage(
_element.loadedImage,
_element.layer.position.x,
_element.layer.position.y,
_element.layer.size.width,
_element.layer.size.height
);
};
// 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.find(
(element) => element.id === _dna[index]
);
return {
location: layer.location,
position: layer.position,
size: layer.size,
selectedElement: { ...selectedElement, rarity: _rarity },
};
});
return mappedDnaToLayers;
};
// 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;
};
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.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;
};
// 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 = [];
// image data collection
const imageDataArray = [];
let image_CID = "";
let meta_CID = "";
let ipfsArray = [];
// array of promises so that only if finished, will next promise be initiated
let promiseArray = [];
const saveToServer = async (_meta_hash, _image_hash) => {
for (let i = 1; i < editionSize + 1; i++) {
let id = i.toString();
let paddedHex = (
"0000000000000000000000000000000000000000000000000000000000000000" + id
).slice(-64);
let url = `https://ipfs.moralis.io:2053/ipfs/${_meta_hash}/metadata/${paddedHex}.json`;
let options = { json: true };
request(url, options, (error, res, body) => {
if (error) {
return console.log(error);
}
if (!error && res.statusCode == 200) {
// Save file reference to Moralis
const FileDatabase = new Moralis.Object("Metadata");
FileDatabase.set("edition", body.edition);
FileDatabase.set("name", body.name);
FileDatabase.set("dna", body.dna);
FileDatabase.set("image", body.image);
FileDatabase.set("attributes", body.attributes);
FileDatabase.set("meta_hash", _meta_hash);
FileDatabase.set("image_hash", _image_hash);
FileDatabase.save();
}
});
}
};
// upload metadata
const uploadMetadata = async (_cid) => {
ipfsArray = [];
promiseArray = [];
for (let i = 1; i < editionSize + 1; i++) {
let id = i.toString();
let paddedHex = (
"0000000000000000000000000000000000000000000000000000000000000000" + id
).slice(-64);
let filename = i.toString() + ".json";
let filetype = "base64";
imageDataArray[
i
].filePath = `https://ipfs.moralis.io:2053/ipfs/${_cid}/images/${paddedHex}.png`;
//imageDataArray[i].image_file = res.data[i].content;
// do something else here after firstFunction completes
let nftMetadata = generateMetadata(
imageDataArray[i].newDna,
imageDataArray[i].editionCount,
imageDataArray[i].attributesList,
imageDataArray[i].filePath
);
metadataList.push(nftMetadata);
const metaFile = new Moralis.File(filename, {
base64: Buffer.from(
JSON.stringify(metadataList.find((meta) => meta.edition == i))
).toString("base64"),
});
// save locally as file
fs.writeFileSync(
`./output/${filename}`,
JSON.stringify(metadataList.find((meta) => meta.edition == i))
);
promiseArray.push(
new Promise((res, rej) => {
fs.readFile(`./output/${id}.json`, (err, data) => {
if (err) rej();
ipfsArray.push({
path: `metadata/${paddedHex}.json`,
content: data.toString("base64"),
});
res();
});
})
);
}
Promise.all(promiseArray).then(() => {
axios
.post(api_url, ipfsArray, {
headers: {
"X-API-Key": xAPIKey,
"content-type": "application/json",
accept: "application/json",
},
})
.then((res) => {
meta_CID = res.data[0].path.split("/")[4];
console.log("META FILE PATHS:", res.data);
saveToServer(meta_CID, image_CID);
})
.catch((err) => {
console.log(err);
});
});
};
// Create generative art by using the canvas api
const startCreating = async () => {
console.log("##################");
console.log("# Generative Art #");
console.log("# - Generating your NFT collection");
console.log("##################");
console.log();
// 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 <= editionSize) {
console.log("-----------------");
console.log("Mutating %d of %d", editionCount, editionSize);
// upload to ipfs
const saveFile = async () => {
// get rarity from to config to create NFT as
let rarity = getRarity(editionCount);
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);
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));
});
let attributesList = [];
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
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(`#${1}`);
// write the image to the output directory
});
dnaListByRarity[rarity].push(newDna);
const base64ImgData = canvas.toBuffer();
const base64 = base64ImgData.toString("base64");
let filename = editionCount.toString() + ".png";
let filetype = "image/png";
// save locally as file
fs.writeFileSync(`./output/${filename}`, canvas.toBuffer(filetype));
console.log(
"Mutant " + editionCount.toString() + " a resident of Moralis"
);
imageDataArray[editionCount] = {
editionCount: editionCount,
newDna: newDna,
attributesList: attributesList,
};
};
const handleFinal = async () => {
// write image files
const imageData = await saveFile();
};
await handleFinal();
// iterate
editionCount++;
}
ipfsArray = [];
promiseArray = [];
for (let i = 1; i < editionCount; i++) {
let id = i.toString();
let paddedHex = (
"0000000000000000000000000000000000000000000000000000000000000000" + id
).slice(-64);
promiseArray.push(
new Promise((res, rej) => {
fs.readFile(`./output/${id}.png`, (err, data) => {
if (err) rej();
ipfsArray.push({
path: `images/${paddedHex}.png`,
content: data.toString("base64"),
});
res();
});
})
);
}
Promise.all(promiseArray).then(() => {
axios
.post(api_url, ipfsArray, {
headers: {
"X-API-Key": xAPIKey,
"content-type": "application/json",
accept: "application/json",
},
})
.then((res) => {
console.log("IMAGE FILE PATHS:", res.data);
image_CID = res.data[0].path.split("/")[4];
console.log("IMAGE CID:", image_CID);
// pass folder CID to meta data
uploadMetadata(image_CID);
})
.catch((err) => {
console.log(err);
});
});
writeMetaData(JSON.stringify(metadataList));
console.log("#########################################");
console.log("Welcome to Pixle");
console.log("#########################################");
console.log();
};
// Initiate code
startCreating();
I think that this error could happen usually if you don’t have the images in the right place for the script to read them
interesting ok how do i tell
you could try with fewer images, like 3 images maybe, I think that there is a constant in code that specifies the number of images
interesting would there be way to change that constant. because my specific project needs to generate a lot of people so having more combinations would be nice
i changes it so there is only a few pngs in each and still same error and even just put 1 png in and nothing
you could add more lines with console.log to identify where it stops
usually there was a problem with not finding a specific image or file on disk from where it tries to read it
ok i did now it getting caught on this line again thank you for your help still learning.
C:\Users\brans\Documents\2d game\Pixel people\index.js:145
Math.random() * layer.elementIdsForRarity[_rarity].length
i get that its can read it because its undefined but i don’t see how its not
here is where it is in the code
[quote="nosnarb1, post:6, topic:5895"]
// 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;
};
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.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;
};
[/quote]
you can see here how to paste formatted code on forum: READ BEFORE POSTING - How to post code in the forum
did that help? i tried to edit it
first code looks better now
what does the error say? what variables is undefined?
##################
Generative Art
- Generating your NFT collection
##################
Mutating 0 of 100
- rarity: undefined
C:\Users\brans\Documents\2d game\Pixel people\index.js:145
Math.random() * layer.elementIdsForRarity[_rarity].length
^
TypeError: Cannot read properties of undefined (reading ‘length’)
at C:\Users\brans\Documents\2d game\Pixel people\index.js:145:58
at Array.forEach ()
at createDna (C:\Users\brans\Documents\2d game\Pixel people\index.js:143:11)
at saveFile (C:\Users\brans\Documents\2d game\Pixel people\index.js:328:20)
at handleFinal (C:\Users\brans\Documents\2d game\Pixel people\index.js:390:31)
at startCreating (C:\Users\brans\Documents\2d game\Pixel people\index.js:393:11)
at Object. (C:\Users\brans\Documents\2d game\Pixel people\index.js:453:1)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
at Object.Module._extensions…js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:17:47
this would mean that this is undefined
right so then how do i define it i guess i thought i did here
const getRandomRarity = (_rarityOptions) => {
let randomPercent = Math.random() * 100;
let percentCount = 0
that seems to be an array, dat doesn’t have data for a particular _rarity