Skip to content

Commit

Permalink
Merge pull request #109 from HashLips/dev
Browse files Browse the repository at this point in the history
Updated
  • Loading branch information
HashLips authored Oct 15, 2021
2 parents c0e4b7d + ba395a0 commit 1e3fa1a
Showing 10 changed files with 203 additions and 66 deletions.
46 changes: 34 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -107,7 +107,11 @@ You can mix up the `layerConfigurations` order on how the images are saved by se

If you want to have logs to debug and see what is happening when you generate images you can set the variable `debugLogs` in the `config.js` file to true. It is false by default, so you will only see general logs.

If you want to play around with different blending modes, you can add a `blend: MODE.colorBurn` field to the layersOrder object. If you need a layers to have a different opacity then you can add the `opacity: 0.7` field to the layersOrder object as well. Both the `blend: MODE.colorBurn` and `opacity: 0.7` can be addes on the same layer if you want to.
If you want to play around with different blending modes, you can add a `blend: MODE.colorBurn` field to the layersOrder `options` object.

If you need a layers to have a different opacity then you can add the `opacity: 0.7` field to the layersOrder `options` object as well.

To use a different metadata attribute name you can add the `displayName: "Awesome Eye Color"` to the `options` object. All options are optional and can be addes on the same layer if you want to.

Here is an example on how you can play around with both filter fields:

@@ -118,11 +122,18 @@ const layerConfigurations = [
layersOrder: [
{ name: "Background" },
{ name: "Eyeball" },
{ name: "Eye color", blend: MODE.colorBurn },
{
name: "Eye color",
options: {
blend: MODE.destinationIn,
opcacity: 0.2,
displayName: "Awesome Eye Color",
},
},
{ name: "Iris" },
{ name: "Shine" },
{ name: "Bottom lid", blend: MODE.overlay, opacity: 0.7 },
{ name: "Top lid", opacity: 0.7 },
{ name: "Bottom lid", options: { blend: MODE.overlay, opacity: 0.7 } },
{ name: "Top lid" },
],
},
];
@@ -214,36 +225,47 @@ That's it, you're done.

## Utils

### Updating baseUri for IPFS
### Updating baseUri for IPFS and description

You might possibly want to update the baseUri after you have ran your collection. To update the baseUri simply run:
You might possibly want to update the baseUri and description after you have ran your collection. To update the baseUri and description simply run:

```sh
node utils/updateBaseUri.js
npm run update_info
```

### Generate a preview image

Create a preview image collage of your collection, run:

```sh
node utils/createPreviewCollage.js
npm run preview
```

### Re-generate the \_metadata.json file
### Generate pixelated images from collection

This util will only work if you have all the individual json files and want to re-generate the \_metadata.json file if you lost it, run:
In order to convert images into pixelated images you would need a list of images that you want to convert. So run the generator first.

Then simply run this command:

```sh
node utils/regenerateMetadata.js
npm run pixelate
```

All your images will be outputted in the `/build/pixel_images` directory.
If you want to change the ratio of the pixelation then you can update the ratio property on the `pixelFormat` object in the `src/config.js` file. The lower the number on the left, the more pixelated the image will be.

```js
const pixelFormat = {
ratio: 5 / 128,
};
```

### Printing rarity data (Experimental feature)

To see the percentages of each attribute across your collection, run:

```sh
node utils/rarityData.js
npm run rarity
```

The output will look something like this:
File renamed without changes.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -13,7 +13,11 @@
},
"scripts": {
"build": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
"generate": "node index.js",
"rarity": "node utils/rarity.js",
"preview": "node utils/preview.js",
"pixelate": "node utils/pixelate.js",
"update_info": "node utils/update_info.js"
},
"author": "Daniel Eugene Botha (HashLips)",
"license": "MIT",
25 changes: 23 additions & 2 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -3,14 +3,14 @@
const path = require("path");
const isLocal = typeof process.pkg === "undefined";
const basePath = isLocal ? process.cwd() : path.dirname(process.execPath);
const { MODE } = require(path.join(basePath, "src/blendMode.js"));
const { MODE } = require(path.join(basePath, "constants/blend_mode.js"));
const description =
"This is the description of your NFT project, remember to replace this";
const baseUri = "ipfs://NewUriToReplace";

const layerConfigurations = [
{
growEditionSizeTo: 10,
growEditionSizeTo: 5,
layersOrder: [
{ name: "Background" },
{ name: "Eyeball" },
@@ -32,9 +32,28 @@ const format = {
height: 512,
};

const text = {
only: false,
color: "#ffffff",
size: 20,
xGap: 40,
yGap: 40,
align: "left",
baseline: "top",
weight: "regular",
family: "Courier",
spacer: " => ",
};

const pixelFormat = {
ratio: 2 / 128,
};

const background = {
generate: true,
brightness: "80%",
static: false,
default: "#000000",
};

const extraMetadata = {};
@@ -62,4 +81,6 @@ module.exports = {
shuffleLayerConfigurations,
debugLogs,
extraMetadata,
pixelFormat,
text,
};
64 changes: 49 additions & 15 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@ const { createCanvas, loadImage } = require(path.join(
));
const buildDir = path.join(basePath, "/build");
const layersDir = path.join(basePath, "/layers");
console.log(path.join(basePath, "/src/config.js"));
const {
format,
baseUri,
@@ -23,13 +22,14 @@ const {
shuffleLayerConfigurations,
debugLogs,
extraMetadata,
text,
} = require(path.join(basePath, "/src/config.js"));
const canvas = createCanvas(format.width, format.height);
const ctx = canvas.getContext("2d");
var metadataList = [];
var attributesList = [];
var dnaList = new Set();
const DNA_DELIMITER = '-';
const DNA_DELIMITER = "-";

const buildSetup = () => {
if (fs.existsSync(buildDir)) {
@@ -80,11 +80,19 @@ const getElements = (path) => {
const layersSetup = (layersOrder) => {
const layers = layersOrder.map((layerObj, index) => ({
id: index,
name: layerObj.name,
elements: getElements(`${layersDir}/${layerObj.name}/`),
blendMode:
layerObj["blend"] != undefined ? layerObj["blend"] : "source-over",
opacity: layerObj["opacity"] != undefined ? layerObj["opacity"] : 1,
name:
layerObj.options?.["displayName"] != undefined
? layerObj.options?.["displayName"]
: layerObj.name,
blend:
layerObj.options?.["blend"] != undefined
? layerObj.options?.["blend"]
: "source-over",
opacity:
layerObj.options?.["opacity"] != undefined
? layerObj.options?.["opacity"]
: 1,
}));
return layers;
};
@@ -103,7 +111,7 @@ const genColor = () => {
};

const drawBackground = () => {
ctx.fillStyle = genColor();
ctx.fillStyle = background.static ? background.default : genColor();
ctx.fillRect(0, 0, format.width, format.height);
};

@@ -139,29 +147,51 @@ const loadLayerImg = async (_layer) => {
});
};

const drawElement = (_renderObject) => {
const addText = (_sig, x, y, size) => {
ctx.fillStyle = text.color;
ctx.font = `${text.weight} ${size}pt ${text.family}`;
ctx.textBaseline = text.baseline;
ctx.textAlign = text.align;
ctx.fillText(_sig, x, y);
};

const drawElement = (_renderObject, _index, _layersLen) => {
ctx.globalAlpha = _renderObject.layer.opacity;
ctx.globalCompositeOperation = _renderObject.layer.blendMode;
ctx.drawImage(_renderObject.loadedImage, 0, 0, format.width, format.height);
ctx.globalCompositeOperation = _renderObject.layer.blend;
text.only
? addText(
`${_renderObject.layer.name}${text.spacer}${_renderObject.layer.selectedElement.name}`,
text.xGap,
text.yGap * (_index + 1),
text.size
)
: ctx.drawImage(
_renderObject.loadedImage,
0,
0,
format.width,
format.height
);

addAttributes(_renderObject);
};

const constructLayerToDna = (_dna = '', _layers = []) => {
const constructLayerToDna = (_dna = "", _layers = []) => {
let mappedDnaToLayers = _layers.map((layer, index) => {
let selectedElement = layer.elements.find(
(e) => e.id == cleanDna(_dna.split(DNA_DELIMITER)[index])
);
return {
name: layer.name,
blendMode: layer.blendMode,
blend: layer.blend,
opacity: layer.opacity,
selectedElement: selectedElement,
};
});
return mappedDnaToLayers;
};

const isDnaUnique = (_DnaList = new Set(), _dna = '') => {
const isDnaUnique = (_DnaList = new Set(), _dna = "") => {
return !_DnaList.has(_dna);
};

@@ -258,8 +288,12 @@ const startCreating = async () => {
if (background.generate) {
drawBackground();
}
renderObjectArray.forEach((renderObject) => {
drawElement(renderObject);
renderObjectArray.forEach((renderObject, index) => {
drawElement(
renderObject,
index,
layerConfigurations[layerConfigIndex].layersOrder.length
);
});
debugLogs
? console.log("Editions left to create: ", abstractedIndexes)
84 changes: 84 additions & 0 deletions utils/pixelate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const fs = require("fs");
const path = require("path");
const { createCanvas, loadImage } = require("canvas");
const isLocal = typeof process.pkg === "undefined";
const basePath = isLocal ? process.cwd() : path.dirname(process.execPath);
const buildDir = `${basePath}/build/pixel_images`;
const inputDir = `${basePath}/build/images`;
const { format, pixelFormat } = require(path.join(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();
File renamed without changes.
Loading

0 comments on commit 1e3fa1a

Please sign in to comment.