LivingDead NFT Project

This commit is contained in:
2022-01-10 22:49:39 +01:00
commit da22559503
64 changed files with 3234 additions and 0 deletions

177
utils/generate_metadata.js Normal file
View File

@@ -0,0 +1,177 @@
const fs = require("fs");
const path = require("path");
const { createCanvas, loadImage } = require("canvas");
const basePath = process.cwd();
const buildDir = `${basePath}/build/json`;
const inputDir = `${basePath}/build/images`;
const {
format,
namePrefix,
description,
baseUri,
} = require(`${basePath}/src/config.js`);
const console = require("console");
const canvas = createCanvas(format.width, format.height);
const ctx = canvas.getContext("2d");
const metadataList = [];
const buildSetup = () => {
if (fs.existsSync(buildDir)) {
fs.rmdirSync(buildDir, { recursive: true });
}
fs.mkdirSync(buildDir);
};
const getImages = (_dir) => {
try {
return fs
.readdirSync(_dir)
.filter((item) => {
let extension = path.extname(`${_dir}${item}`);
if (extension == ".png" || extension == ".jpg") {
return item;
}
})
.map((i) => {
return {
filename: i,
path: `${_dir}/${i}`,
};
});
} catch {
return null;
}
};
const loadImgData = async (_imgObject) => {
return new Promise(async (resolve) => {
const image = await loadImage(`${_imgObject.path}`);
resolve({ imgObject: _imgObject, loadedImage: image });
});
};
const draw = (_imgObject) => {
let w = canvas.width;
let h = canvas.height;
ctx.drawImage(_imgObject.loadedImage, 0, 0, w, h);
};
const addRarity = () => {
let w = canvas.width;
let h = canvas.height;
let i = -4;
let count = 0;
let imgdata = ctx.getImageData(0, 0, w, h);
let rgb = imgdata.data;
let newRgb = { r: 0, g: 0, b: 0 };
const tolerance = 15;
const rareColorBase = "NOT a Hot Dog";
const rareColor = [
{ name: "Hot Dog", rgb: { r: 192, g: 158, b: 131 } },
{ name: "Hot Dog", rgb: { r: 128, g: 134, b: 90 } },
{ name: "Hot Dog", rgb: { r: 113, g: 65, b: 179 } },
{ name: "Hot Dog", rgb: { r: 162, g: 108, b: 67 } },
];
while ((i += 10 * 4) < rgb.length) {
++count;
newRgb.r += rgb[i];
newRgb.g += rgb[i + 1];
newRgb.b += rgb[i + 2];
}
newRgb.r = ~~(newRgb.r / count);
newRgb.g = ~~(newRgb.g / count);
newRgb.b = ~~(newRgb.b / count);
let rarity = rareColorBase;
rareColor.forEach((color) => {
if (isNeighborColor(newRgb, color.rgb, tolerance)) {
rarity = color.name;
}
});
console.log(newRgb);
console.log(rarity);
return [
{
trait_type: "average color",
value: `rgb(${newRgb.r},${newRgb.g},${newRgb.b})`,
},
{
trait_type: "What is this?",
value: rarity,
},
{
trait_type: "date",
value: randomIntFromInterval(1500, 1900),
},
];
};
randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
};
isNeighborColor = (color1, color2, tolerance) => {
return (
Math.abs(color1.r - color2.r) <= tolerance &&
Math.abs(color1.g - color2.g) <= tolerance &&
Math.abs(color1.b - color2.b) <= tolerance
);
};
const saveMetadata = (_loadedImageObject) => {
let shortName = _loadedImageObject.imgObject.filename.replace(
/\.[^/.]+$/,
""
);
let tempAttributes = [];
tempAttributes.push(addRarity());
let tempMetadata = {
name: `${namePrefix} #${shortName}`,
description: description,
image: `${baseUri}/${shortName}.png`,
edition: Number(shortName),
attributes: tempAttributes,
compiler: "HashLips Art Engine",
};
fs.writeFileSync(
`${buildDir}/${shortName}.json`,
JSON.stringify(tempMetadata, null, 2)
);
metadataList.push(tempMetadata);
};
const writeMetaData = (_data) => {
fs.writeFileSync(`${buildDir}/_metadata.json`, _data);
};
const startCreating = async () => {
const images = getImages(inputDir);
if (images == null) {
console.log("Please generate collection first.");
return;
}
let loadedImageObjects = [];
images.forEach((imgObject) => {
loadedImageObjects.push(loadImgData(imgObject));
});
await Promise.all(loadedImageObjects).then((loadedImageObjectArray) => {
loadedImageObjectArray.forEach((loadedImageObject) => {
draw(loadedImageObject);
saveMetadata(loadedImageObject);
console.log(
`Created metadata for image: ${loadedImageObject.imgObject.filename}`
);
});
});
writeMetaData(JSON.stringify(metadataList, null, 2));
};
buildSetup();
startCreating();

83
utils/pixelate.js Normal file
View File

@@ -0,0 +1,83 @@
const fs = require("fs");
const path = require("path");
const { createCanvas, loadImage } = require("canvas");
const basePath = process.cwd();
const buildDir = `${basePath}/build/pixel_images`;
const inputDir = `${basePath}/build/images`;
const { format, pixelFormat } = require(`${basePath}/src/config.js`);
const console = require("console");
const canvas = createCanvas(format.width, format.height);
const ctx = canvas.getContext("2d");
const buildSetup = () => {
if (fs.existsSync(buildDir)) {
fs.rmdirSync(buildDir, { recursive: true });
}
fs.mkdirSync(buildDir);
};
const getImages = (_dir) => {
try {
return fs
.readdirSync(_dir)
.filter((item) => {
let extension = path.extname(`${_dir}${item}`);
if (extension == ".png" || extension == ".jpg") {
return item;
}
})
.map((i) => {
return {
filename: i,
path: `${_dir}/${i}`,
};
});
} catch {
return null;
}
};
const loadImgData = async (_imgObject) => {
return new Promise(async (resolve) => {
const image = await loadImage(`${_imgObject.path}`);
resolve({ imgObject: _imgObject, loadedImage: image });
});
};
const draw = (_imgObject) => {
let size = pixelFormat.ratio;
let w = canvas.width * size;
let h = canvas.height * size;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(_imgObject.loadedImage, 0, 0, w, h);
ctx.drawImage(canvas, 0, 0, w, h, 0, 0, canvas.width, canvas.height);
};
const saveImage = (_loadedImageObject) => {
fs.writeFileSync(
`${buildDir}/${_loadedImageObject.imgObject.filename}`,
canvas.toBuffer("image/png")
);
};
const startCreating = async () => {
const images = getImages(inputDir);
if (images == null) {
console.log("Please generate collection first.");
return;
}
let loadedImageObjects = [];
images.forEach((imgObject) => {
loadedImageObjects.push(loadImgData(imgObject));
});
await Promise.all(loadedImageObjects).then((loadedImageObjectArray) => {
loadedImageObjectArray.forEach((loadedImageObject) => {
draw(loadedImageObject);
saveImage(loadedImageObject);
console.log(`Pixelated image: ${loadedImageObject.imgObject.filename}`);
});
});
};
buildSetup();
startCreating();

51
utils/preview.js Normal file
View File

@@ -0,0 +1,51 @@
const basePath = process.cwd();
const fs = require("fs");
const { createCanvas, loadImage } = require("canvas");
const buildDir = `${basePath}/build`;
const { preview } = require(`${basePath}/src/config.js`);
// read json data
const rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`);
const metadataList = JSON.parse(rawdata);
const saveProjectPreviewImage = async (_data) => {
// Extract from preview config
const { thumbWidth, thumbPerRow, imageRatio, imageName } = preview;
// Calculate height on the fly
const thumbHeight = thumbWidth * imageRatio;
// Prepare canvas
const previewCanvasWidth = thumbWidth * thumbPerRow;
const previewCanvasHeight =
thumbHeight * Math.ceil(_data.length / thumbPerRow);
// Shout from the mountain tops
console.log(
`Preparing a ${previewCanvasWidth}x${previewCanvasHeight} project preview with ${_data.length} thumbnails.`
);
// Initiate the canvas now that we have calculated everything
const previewPath = `${buildDir}/${imageName}`;
const previewCanvas = createCanvas(previewCanvasWidth, previewCanvasHeight);
const previewCtx = previewCanvas.getContext("2d");
// Iterate all NFTs and insert thumbnail into preview image
// 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) => {
previewCtx.drawImage(
image,
thumbWidth * (index % thumbPerRow),
thumbHeight * Math.trunc(index / thumbPerRow),
thumbWidth,
thumbHeight
);
});
}
// Write Project Preview to file
fs.writeFileSync(previewPath, previewCanvas.toBuffer("image/png"));
console.log(`Project preview image located at: ${previewPath}`);
};
saveProjectPreviewImage(metadataList);

91
utils/preview_gif.js Normal file
View File

@@ -0,0 +1,91 @@
const basePath = process.cwd();
const fs = require("fs");
const { createCanvas, loadImage } = require("canvas");
const buildDir = `${basePath}/build`;
const imageDir = `${buildDir}/images`;
const { format, preview_gif } = require(`${basePath}/src/config.js`);
const canvas = createCanvas(format.width, format.height);
const ctx = canvas.getContext("2d");
const HashlipsGiffer = require(`${basePath}/modules/HashlipsGiffer.js`);
let hashlipsGiffer = null;
const loadImg = async (_img) => {
return new Promise(async (resolve) => {
const loadedImage = await loadImage(`${_img}`);
resolve({ loadedImage: loadedImage });
});
};
// read image paths
const imageList = [];
const rawdata = fs.readdirSync(imageDir).forEach((file) => {
imageList.push(loadImg(`${imageDir}/${file}`));
});
const saveProjectPreviewGIF = async (_data) => {
// Extract from preview config
const { numberOfImages, order, repeat, quality, delay, imageName } =
preview_gif;
// Extract from format config
const { width, height } = format;
// Prepare canvas
const previewCanvasWidth = width;
const previewCanvasHeight = height;
if (_data.length < numberOfImages) {
console.log(
`You do not have enough images to create a gif with ${numberOfImages} images.`
);
} else {
// Shout from the mountain tops
console.log(
`Preparing a ${previewCanvasWidth}x${previewCanvasHeight} project preview with ${_data.length} images.`
);
const previewPath = `${buildDir}/${imageName}`;
ctx.clearRect(0, 0, width, height);
hashlipsGiffer = new HashlipsGiffer(
canvas,
ctx,
`${previewPath}`,
repeat,
quality,
delay
);
hashlipsGiffer.start();
await Promise.all(_data).then((renderObjectArray) => {
// Determin the order of the Images before creating the gif
if (order == "ASC") {
// Do nothing
} else if (order == "DESC") {
renderObjectArray.reverse();
} else if (order == "MIXED") {
renderObjectArray = renderObjectArray.sort(() => Math.random() - 0.5);
}
// Reduce the size of the array of Images to the desired amount
if (parseInt(numberOfImages) > 0) {
renderObjectArray = renderObjectArray.slice(0, numberOfImages);
}
renderObjectArray.forEach((renderObject, index) => {
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(
renderObject.loadedImage,
0,
0,
previewCanvasWidth,
previewCanvasHeight
);
hashlipsGiffer.add();
});
});
hashlipsGiffer.stop();
}
};
saveProjectPreviewGIF(imageList);

82
utils/rarity.js Normal file
View File

@@ -0,0 +1,82 @@
const basePath = process.cwd();
const fs = require("fs");
const layersDir = `${basePath}/layers`;
const { layerConfigurations } = require(`${basePath}/src/config.js`);
const { getElements } = require("../src/main.js");
// read json data
let rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`);
let data = JSON.parse(rawdata);
let editionSize = data.length;
let rarityData = [];
// intialize layers to chart
layerConfigurations.forEach((config) => {
let layers = config.layersOrder;
layers.forEach((layer) => {
// get elements for each layer
let elementsForLayer = [];
let elements = getElements(`${layersDir}/${layer.name}/`);
elements.forEach((element) => {
// just get name and weight for each element
let rarityDataElement = {
trait: element.name,
weight: element.weight.toFixed(0),
occurrence: 0, // initialize at 0
};
elementsForLayer.push(rarityDataElement);
});
let layerName =
layer.options?.["displayName"] != undefined
? layer.options?.["displayName"]
: layer.name;
// don't include duplicate layers
if (!rarityData.includes(layer.name)) {
// add elements for each layer to chart
rarityData[layerName] = elementsForLayer;
}
});
});
// fill up rarity chart with occurrences from metadata
data.forEach((element) => {
let attributes = element.attributes;
attributes.forEach((attribute) => {
let traitType = attribute.trait_type;
let value = attribute.value;
let rarityDataTraits = rarityData[traitType];
rarityDataTraits.forEach((rarityDataTrait) => {
if (rarityDataTrait.trait == value) {
// keep track of occurrences
rarityDataTrait.occurrence++;
}
});
});
});
// convert occurrences to occurence string
for (var layer in rarityData) {
for (var attribute in rarityData[layer]) {
// get chance
let chance =
((rarityData[layer][attribute].occurrence / editionSize) * 100).toFixed(2);
// show two decimal places in percent
rarityData[layer][attribute].occurrence =
`${rarityData[layer][attribute].occurrence} in ${editionSize} editions (${chance} %)`;
}
}
// print out rarity data
for (var layer in rarityData) {
console.log(`Trait type: ${layer}`);
for (var trait in rarityData[layer]) {
console.log(rarityData[layer][trait]);
}
console.log();
}

50
utils/update_info.js Normal file
View File

@@ -0,0 +1,50 @@
const basePath = process.cwd();
const { NETWORK } = require(`${basePath}/constants/network.js`);
const fs = require("fs");
const {
baseUri,
description,
namePrefix,
network,
solanaMetadata,
} = require(`${basePath}/src/config.js`);
// read json data
let rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`);
let data = JSON.parse(rawdata);
data.forEach((item) => {
if (network == NETWORK.sol) {
item.name = `${namePrefix} #${item.edition}`;
item.description = description;
item.creators = solanaMetadata.creators;
} else {
item.name = `${namePrefix} #${item.edition}`;
item.description = description;
item.image = `${baseUri}/${item.edition}.png`;
}
fs.writeFileSync(
`${basePath}/build/json/${item.edition}.json`,
JSON.stringify(item, null, 2)
);
});
fs.writeFileSync(
`${basePath}/build/json/_metadata.json`,
JSON.stringify(data, null, 2)
);
if (network == NETWORK.sol) {
console.log(`Updated description for images to ===> ${description}`);
console.log(`Updated name prefix for images to ===> ${namePrefix}`);
console.log(
`Updated creators for images to ===> ${JSON.stringify(
solanaMetadata.creators
)}`
);
} else {
console.log(`Updated baseUri for images to ===> ${baseUri}`);
console.log(`Updated description for images to ===> ${description}`);
console.log(`Updated name prefix for images to ===> ${namePrefix}`);
}