Skip to main content

latest

An implementation of the Wave Function Collapse simple tiled model

This package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers
This package works with Cloudflare Workers
This package works with Node.js
This package works with Deno
This package works with Bun
This package works with Browsers
JSR Score
100%
Published
11 months ago (0.1.1)

wfc-tiles

This is a fork of https://github.com/Arkyris/blazinwfc, an implementation of the Wave Function Collapse simple tiled model. It can be used to generate tilemaps procedurally.

Usage

Basic

	import { WFC } from "@daemon/wfc-tiles";
	
	const wfc = new WFC({
		options: {
			saveInterval: 0.02,
			defaultWeight: 10
		},
		tiles: [
			{ edges: ["floor", "floor", "floor", "floor"], type: "t_floor", weight: 30 },
			{ edges: ["floor", "wall", "floor", "wall"], type: "t_wall", weight: 3 },
			{ edges: ["wall", "floor", "wall", "floor"], type: "t_wall", weight: 3 },
		],
		compareEdge(a: string, b: string) {
			return a === b;
		},
	});

	wfc.collapse(20);

Advanced

An advanced example with 33 tiles:

	function symmetries(tile: TileDefinition<number, number>): TileDefinition<number, number>[] {
		const symmetries: TileDefinition<number, number>[] = []; 
		for (let i = 0; i < 4; i++) {
			const rotate = (d: Direction) => (d + i) % 4;
			const exceptions = tile.exceptions
				? directions.map(d => tile.exceptions![rotate(d)]) as any
				: undefined;
			symmetries.push({
				edges: directions.map(d => tile.edges[rotate(d)]) as any,
				type: tile.type,
				weight: tile.weight,
				exceptions,
			});
		}
		return symmetries;
	}

	enum Tile {
		Floor, Wall, Wall2, Wall3, Room, Break, Connect
	};
	enum Edge {
		Floor, Wall, Wall2, Room, Break, Wall3 = 201
	};
	const tiles: TileDefinition<number, number>[] = [
		{ edges: [Edge.Floor, Edge.Floor, Edge.Floor, Edge.Floor], type: Tile.Floor, weight: 30 },
		{ edges: [Edge.Floor, Edge.Wall, Edge.Floor, Edge.Wall], type: Tile.Wall, weight: 3 },
		{ edges: [Edge.Wall, Edge.Floor, Edge.Wall, Edge.Floor], type: Tile.Wall, weight: 3 },
		...symmetries({ edges: [Edge.Wall, Edge.Floor, Edge.Floor, Edge.Wall], type: Tile.Wall, weight: 3 }),
		...symmetries({ edges: [Edge.Floor, Edge.Wall, Edge.Wall, Edge.Wall], type: Tile.Wall, weight: 3 }),
		...symmetries({ edges: [Edge.Floor, Edge.Wall2, Edge.Floor, Edge.Wall], type: Tile.Wall2, weight: 3 }),
		{ edges: [Edge.Wall2, Edge.Floor, Edge.Wall2, Edge.Floor], type: Tile.Wall2, weight: 1 },
		{ edges: [Edge.Floor, Edge.Wall2, Edge.Floor, Edge.Wall2], type: Tile.Wall2, weight: 10 },
		{ edges: [Edge.Floor, Edge.Wall, Edge.Floor, Edge.Wall], type: Tile.Break, weight: 1, exceptions: { [Direction.Left]: [Tile.Break], [Direction.Right]: [Tile.Break] } },
		...symmetries({ edges: [Edge.Wall3, Edge.Floor, Edge.Floor, -Edge.Wall3], type: Tile.Wall3, weight: 3, exceptions: { [Direction.Right]: [Tile.Wall3], [Direction.Down]: [Tile.Wall3] } }),
		...symmetries({ edges: [Edge.Room, Edge.Wall3, Edge.Floor, -Edge.Wall3], type: Tile.Wall3, weight: 3, exceptions: { [Direction.Down]: [Tile.Wall3] } }),
		{ edges: [Edge.Room, Edge.Room, Edge.Room, Edge.Room], type: Tile.Room, weight: 5 },
		...symmetries({ edges: [-Edge.Wall3, Edge.Room, Edge.Room, Edge.Wall3], type: Tile.Wall3, weight: 1 }),
		{ edges: [Edge.Wall3, Edge.Wall2, -Edge.Wall3, Edge.Room], type: Tile.Connect, weight: 1 },
		{ edges: [-Edge.Wall3, Edge.Room, Edge.Wall3, Edge.Wall2], type: Tile.Connect, weight: 1 },
	];

	const wfc = new WFC({
		options: {
			saveInterval: 0.02,
			defaultWeight: 10
		},
		tiles,
		compareEdge(a: number, b: number) {
			// edges over 200 (eg. Edge.Wall3) only match if they're the mirror
			// version of the other edge
			if (b > 200 || b < -200) return a === -b;
			return a === b;
		},
	});

	const output = wfc.collapse(50);

In this example, the Tile and Edge types are enums for fast comparisons, but they are generic, so you can use whatever type you want to model your tile interactions with. The symmetries function is a utility function that generates four tile definitions, each rotated 90°. You can define each tile explicitly instead, or use more complex definition generation logic, for example based on metadata attached to your tileset images.

Documentation

You can read the original readme most of still applies, or see the type documentations.

Add Package

deno add jsr:@daemon/wfc-tiles

Import symbol

import * as wfc_tiles from "@daemon/wfc-tiles";

---- OR ----

Import directly with a jsr specifier

import * as wfc_tiles from "jsr:@daemon/wfc-tiles";

Add Package

npx jsr add @daemon/wfc-tiles

Import symbol

import * as wfc_tiles from "@daemon/wfc-tiles";

Add Package

yarn dlx jsr add @daemon/wfc-tiles

Import symbol

import * as wfc_tiles from "@daemon/wfc-tiles";

Add Package

pnpm dlx jsr add @daemon/wfc-tiles

Import symbol

import * as wfc_tiles from "@daemon/wfc-tiles";

Add Package

bunx jsr add @daemon/wfc-tiles

Import symbol

import * as wfc_tiles from "@daemon/wfc-tiles";