Skip to content

Commit

Permalink
chore(graph-layers): Restore pack-images script (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy authored Apr 15, 2024
1 parent f885d3f commit 7849819
Show file tree
Hide file tree
Showing 12 changed files with 765 additions and 615 deletions.
123 changes: 123 additions & 0 deletions modules/graph-layers/scripts/pack-marker-images.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* Generate texture atlas from images
* ```
* # default input/output
* npx tsx ./pack-marker-images.ts
*
* # custom input/output directories
* npx tsx ./pack-marker-images.ts [inputDir] [outputDir]
* ```
*/

import {readFile, writeFile, readdir} from 'node:fs/promises';
import {dirname, join, resolve} from 'node:path';
import { fileURLToPath } from 'node:url';
import ndarray, {NdArray} from 'ndarray';
import {getPixels, savePixels} from 'ndarray-pixels';
import pack from 'bin-pack';
import Datauri from 'datauri';

const __dirname = dirname(fileURLToPath(import.meta.url));
const packageRoot = join(__dirname, '..');

const inputDir = process.argv[2] || 'src/layers/common-layers/marker-layer/markers/';
const outputDir = process.argv[3] || 'src/layers/common-layers/marker-layer/';

const INPUT_DIR = resolve(packageRoot, inputDir);
const OUTPUT_IMAGE = resolve(packageRoot, outputDir, 'marker-atlas.png');
const OUTPUT_MAPPING = resolve(packageRoot, outputDir, `marker-mapping.ts`);
const OUTPUT_DATA_URL = resolve(packageRoot, outputDir, 'atlas-data-url.ts');
const OUTPUT_LIST = resolve(packageRoot, outputDir, 'marker-list.ts');
const IMAGE_PATTERN = /\.(png|jpg|jpeg|gif|bmp|tiff)$/i;

// Get all images in the input path
const fileNames = (await readdir(INPUT_DIR)).filter((name) => IMAGE_PATTERN.test(name));

Promise.all(fileNames.map((name: string) => readImage(resolve(INPUT_DIR, name)))).then(async (images) => {
// Images are loaded
const nodes = images.map((pixels: NdArray, index: number) => ({
name: fileNames[index],
pixels,
width: pixels.shape[0],
height: pixels.shape[1]
}));

// Bin pack
const result = pack(nodes);
// console.log(result.items.length + ' items packed.');

// Convert to texture atlas
const outputJSON = {};
const outputImage = createImage(result.width, result.height);
result.items.forEach((item) => {
outputJSON[item.item.name.replace(IMAGE_PATTERN, '')] = {
x: item.x,
y: item.y,
width: item.width,
height: item.height,
mask: true
};
copyPixels(item.item.pixels, outputImage, item.x, item.y);
});

// Write to disk
await writeMapping(OUTPUT_MAPPING, outputJSON);
await writeImage(OUTPUT_IMAGE, outputImage, () => writeDataURL(OUTPUT_IMAGE, OUTPUT_DATA_URL));
await writeList(OUTPUT_LIST, outputJSON);
});

/* Utils */

function copyPixels(fromImage: NdArray, toImage: NdArray, x: number, y: number): void {
const width = fromImage.shape[0];
const height = fromImage.shape[1];
const channels = fromImage.shape[2];

for (let i = 0; i < width; i++) {
for (let j = 0; j < height; j++) {
for (let k = 0; k < channels; k++) {
const value = fromImage.get(i, j, k);
toImage.set(i + x, j + y, k, value);
}
}
}
}

async function writeMapping(filePath: string, content: unknown): Promise<void> {
await exportJSFile(filePath, 'MarkerMapping', JSON.stringify(content, null, 2));
}

function createImage(width: number, height: number): NdArray<Uint8ClampedArray> {
return ndarray(new Uint8ClampedArray(width * height * 4), [width, height, 4]);
}

async function writeDataURL(imagePath: string, outputFilePath: string): Promise<void> {
const content = {dataURL: await Datauri(imagePath)};
await exportJSFile(outputFilePath, 'AtlasDataURL', JSON.stringify(content, null, 2));
}

async function writeList(filePath: string, content: object): Promise<void> {
const markers = Object.keys(content);
const markerMap = markers.reduce((res, marker) => {
res[marker] = marker;
return res;
}, {});
const contentStr = JSON.stringify(markerMap, null, 2);
await exportJSFile(filePath, 'MarkerList', contentStr);
}

async function exportJSFile(filePath: string, exportName: string, contentStr: string): Promise<void> {
await writeFile(
filePath,
`/* eslint-disable */\nexport const ${exportName} = ${contentStr};\n/* eslint-enable */\n`
);
}

async function readImage(filePath: string): Promise<NdArray> {
return readFile(filePath).then((buffer) => getPixels(buffer, 'image/png'));
}

async function writeImage(filePath: string, pixelArr: NdArray<Uint8ClampedArray>, createDataURL: () => void): Promise<void> {
await writeFile(filePath, await savePixels(pixelArr, 'image/png'));
createDataURL();
}

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @noflow
import {CompositeLayer} from '@deck.gl/core';
import {IconLayer} from '@deck.gl/layers';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,64 @@
/* eslint-disable */
export const MarkerList = {
'location-marker-filled': 'location-marker-filled',
'bell-filled': 'bell-filled',
'bookmark-filled': 'bookmark-filled',
bookmark: 'bookmark',
'cd-filled': 'cd-filled',
cd: 'cd',
checkmark: 'checkmark',
'circle-check-filled': 'circle-check-filled',
'circle-check': 'circle-check',
'circle-filled': 'circle-filled',
'circle-i-filled': 'circle-i-filled',
'circle-i': 'circle-i',
'circle-minus-filled': 'circle-minus-filled',
'circle-minus': 'circle-minus',
'circle-plus-filled': 'circle-plus-filled',
'circle-plus': 'circle-plus',
'circle-questionmark-filled': 'circle-questionmark-filled',
'circle-questionmark': 'circle-questionmark',
'circle-slash-filled': 'circle-slash-filled',
'circle-slash': 'circle-slash',
'circle-x-filled': 'circle-x-filled',
'circle-x': 'circle-x',
circle: 'circle',
'diamond-filled': 'diamond-filled',
diamond: 'diamond',
'flag-filled': 'flag-filled',
flag: 'flag',
gear: 'gear',
'heart-filled': 'heart-filled',
heart: 'heart',
bell: 'bell',
'location-marker': 'location-marker',
'octagonal-star-filled': 'octagonal-star-filled',
'octagonal-star': 'octagonal-star',
'person-filled': 'person-filled',
person: 'person',
'pin-filled': 'pin-filled',
pin: 'pin',
'plus-small': 'plus-small',
plus: 'plus',
'rectangle-filled': 'rectangle-filled',
rectangle: 'rectangle',
'star-filled': 'star-filled',
star: 'star',
'tag-filled': 'tag-filled',
tag: 'tag',
'thumb-down-filled': 'thumb-down-filled',
'thumb-down': 'thumb-down',
'thumb-up': 'thumb-up',
'thumb_up-filled': 'thumb_up-filled',
'triangle-down-filled': 'triangle-down-filled',
'triangle-down': 'triangle-down',
'triangle-left-filled': 'triangle-left-filled',
'triangle-left': 'triangle-left',
'triangle-right-filled': 'triangle-right-filled',
'triangle-right': 'triangle-right',
'triangle-up-filled': 'triangle-up-filled',
'triangle-up': 'triangle-up',
'x-small': 'x-small',
x: 'x'
"bell-filled": "bell-filled",
"bell": "bell",
"bookmark-filled": "bookmark-filled",
"bookmark": "bookmark",
"cd-filled": "cd-filled",
"cd": "cd",
"checkmark": "checkmark",
"circle-check-filled": "circle-check-filled",
"circle-check": "circle-check",
"circle-filled": "circle-filled",
"circle-i-filled": "circle-i-filled",
"circle-i": "circle-i",
"circle-minus-filled": "circle-minus-filled",
"circle-minus": "circle-minus",
"circle-plus-filled": "circle-plus-filled",
"circle-plus": "circle-plus",
"circle-questionmark-filled": "circle-questionmark-filled",
"circle-questionmark": "circle-questionmark",
"circle-slash-filled": "circle-slash-filled",
"circle-slash": "circle-slash",
"circle-x-filled": "circle-x-filled",
"circle-x": "circle-x",
"circle": "circle",
"diamond-filled": "diamond-filled",
"diamond": "diamond",
"flag-filled": "flag-filled",
"flag": "flag",
"gear": "gear",
"heart-filled": "heart-filled",
"heart": "heart",
"location-marker-filled": "location-marker-filled",
"location-marker": "location-marker",
"octagonal-star-filled": "octagonal-star-filled",
"octagonal-star": "octagonal-star",
"person-filled": "person-filled",
"person": "person",
"pin-filled": "pin-filled",
"pin": "pin",
"plus-small": "plus-small",
"plus": "plus",
"rectangle-filled": "rectangle-filled",
"rectangle": "rectangle",
"star-filled": "star-filled",
"star": "star",
"tag-filled": "tag-filled",
"tag": "tag",
"thumb-down-filled": "thumb-down-filled",
"thumb-down": "thumb-down",
"thumb-up": "thumb-up",
"thumb_up-filled": "thumb_up-filled",
"triangle-down-filled": "triangle-down-filled",
"triangle-down": "triangle-down",
"triangle-left-filled": "triangle-left-filled",
"triangle-left": "triangle-left",
"triangle-right-filled": "triangle-right-filled",
"triangle-right": "triangle-right",
"triangle-up-filled": "triangle-up-filled",
"triangle-up": "triangle-up",
"x-small": "x-small",
"x": "x"
};
/* eslint-enable */
Loading

0 comments on commit 7849819

Please sign in to comment.