rushabhcodes/ESP32-E-Reader

A compact ESP32-C3 e-reader PCB integrating LiPo charging/protection, USB-C, 3.3 V regulation, e-paper display bias generation, microSD storage, and tactile controls.

Version
1.0.85
License
unset
Stars
3

routing-identity.ts

import type { SimpleRouteJson } from "tscircuit";

/** Stable physical identities, independent of schematic-generated database IDs. */
export function routingIdentity(input: SimpleRouteJson) {
	const identities = new Map<string, string>();
	const idShapes = new Map<string, Set<string>>();
	const addShape = (id: string, shape: string) => {
		const shapes = idShapes.get(id) ?? new Set<string>();
		shapes.add(shape);
		idShapes.set(id, shapes);
	};
	const shapes = input.obstacles.map((obstacle) => {
		const { connectedTo, circuitJsonMetadata, obstacleId, ...shape } = obstacle;
		return JSON.stringify(sortedObject(shape));
	});
	const shapeNumbers = new Map(
		[...new Set(shapes)].sort().map((shape, index) => [shape, index]),
	);
	for (const [index, obstacle] of input.obstacles.entries()) {
		const { circuitJsonMetadata, obstacleId } = obstacle;
		const identity = String(shapeNumbers.get(shapes[index]));
		const metadata = circuitJsonMetadata as Record<string, string> | undefined;
		for (const [key, id] of Object.entries(metadata ?? {})) {
			if (key.endsWith("_id")) addShape(id, `${key}:${identity}`);
		}
		if (obstacleId) addShape(obstacleId, `obstacle:${identity}`);
	}
	for (const [id, shapes] of idShapes) {
		identities.set(id, JSON.stringify([...shapes].sort()));
	}
	const normalize = (value: unknown): unknown => {
		if (typeof value === "string") return identities.get(value) ?? value;
		if (Array.isArray(value)) return value.map(normalize);
		if (value && typeof value === "object") {
			return Object.fromEntries(
				Object.entries(value)
					.sort(([a], [b]) => a.localeCompare(b))
					.map(([key, item]) => {
						if (key === "connectedTo" || key === "offBoardConnectsTo") {
							// These IDs are an extra alias for the full connectivity set below.
							// Their numbers also count schematic objects and are not stable.
							const ids = (item as string[]).filter(
								(id) => !/^connectivity_net\d+$/.test(id),
							);
							return [key, ids.map(normalize).sort()];
						}
						if (key === "pointsToConnect" || key === "layers") {
							return [
								key,
								(item as unknown[]).map(normalize).sort(compareJson),
							];
						}
						return [key, normalize(item)];
					}),
			);
		}
		return value;
	};
	const { obstacles, connections, ...settings } = input;
	return {
		identities,
		signature: JSON.stringify({
			settings: normalize(settings),
			obstacles: obstacles.map(normalize).sort(compareJson),
			connections: connections.map(normalize).sort(compareJson),
		}),
	};
}

function compareJson(a: unknown, b: unknown) {
	return JSON.stringify(a).localeCompare(JSON.stringify(b));
}

function sortedObject(value: unknown): unknown {
	if (Array.isArray(value)) return value.map(sortedObject).sort(compareJson);
	if (!value || typeof value !== "object") return value;
	return Object.fromEntries(
		Object.entries(value)
			.sort(([a], [b]) => a.localeCompare(b))
			.map(([key, item]) => [key, sortedObject(item)]),
	);
}