mudlet-map-renderer 0.0.24 → 0.1.0-konva

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,123 @@
1
+ import Area from "./Area";
2
+ import ExplorationArea from "./ExplorationArea";
3
+
4
+ interface Color {
5
+ rgb: number[];
6
+ rgbValue: string;
7
+ symbolColor: number[];
8
+ symbolColorValue: string,
9
+ }
10
+
11
+ const defaultColor: Color = {
12
+ rgb: [114, 1, 0],
13
+ rgbValue: 'rgb(114, 1, 0)',
14
+ symbolColor: [225, 225, 225],
15
+ symbolColorValue: 'rgb(225,225,225)'
16
+ }
17
+
18
+ function calculateLuminance(rgb: number[]) {
19
+ const rn = rgb[0] / 255;
20
+ const gn = rgb[1] / 255;
21
+ const bn = rgb[2] / 255;
22
+
23
+ const max = Math.max(rn, gn, bn);
24
+ const min = Math.min(rn, gn, bn);
25
+
26
+ return (max + min) / 2;
27
+ }
28
+
29
+ export default class MapReader {
30
+
31
+ private rooms: Record<number, MapData.Room> = {};
32
+ private areas: Record<number, Area> = {};
33
+ private areaSources: Record<number, MapData.Area> = {};
34
+ private visitedRooms?: Set<number>;
35
+ private explorationEnabled = false;
36
+ private colors: Record<number, Color> = {};
37
+
38
+ constructor(map: MapData.Map, envs: MapData.Env[]) {
39
+ map.forEach(area => {
40
+ area.rooms.forEach(room => {
41
+ room.y = -room.y;
42
+ this.rooms[room.id] = room;
43
+ })
44
+ const areaId = parseInt(area.areaId);
45
+ this.areas[areaId] = new Area(area);
46
+ this.areaSources[areaId] = area;
47
+ })
48
+ this.colors = envs.reduce((acc, c) => ({
49
+ ...acc,
50
+ [c.envId]: {
51
+ rgb: c.colors,
52
+ rgbValue: `rgb(${c.colors.join(',')}`,
53
+ symbolColor: calculateLuminance(c.colors) > 0.41 ? [25, 25, 25] : [225, 255, 255],
54
+ symbolColorValue: calculateLuminance(c.colors) > 0.41 ? 'rgb(25,25,25)' : 'rgb(225,255,255)'
55
+ }
56
+ }), {});
57
+ }
58
+
59
+ getArea(areaId: number) {
60
+ return this.areas[areaId];
61
+ }
62
+
63
+ getExplorationArea(areaId: number) {
64
+ const area = this.areas[areaId];
65
+ if (area instanceof ExplorationArea) {
66
+ return area;
67
+ }
68
+ return undefined;
69
+ }
70
+
71
+ getAreas() {
72
+ return Object.values(this.areas);
73
+ }
74
+
75
+ getRooms() {
76
+ return Object.values(this.rooms);
77
+ }
78
+
79
+ getRoom(roomId: number) {
80
+ return this.rooms[roomId];
81
+ }
82
+
83
+ decorateWithExploration(visitedRooms?: Iterable<number> | Set<number>) {
84
+ this.visitedRooms = visitedRooms instanceof Set ? visitedRooms : new Set(visitedRooms ?? []);
85
+ Object.entries(this.areaSources).forEach(([id, area]) => {
86
+ const numericId = parseInt(id, 10);
87
+ this.areas[numericId] = new ExplorationArea(area, this.visitedRooms!);
88
+ });
89
+ this.explorationEnabled = true;
90
+ return this.visitedRooms;
91
+ }
92
+
93
+ getVisitedRooms() {
94
+ return this.visitedRooms;
95
+ }
96
+
97
+ clearExplorationDecoration() {
98
+ Object.entries(this.areaSources).forEach(([id, area]) => {
99
+ const numericId = parseInt(id, 10);
100
+ this.areas[numericId] = new Area(area);
101
+ });
102
+ this.explorationEnabled = false;
103
+ }
104
+
105
+ isExplorationEnabled() {
106
+ return this.explorationEnabled;
107
+ }
108
+
109
+ getColorValue(envId: number): string {
110
+ return this.colors[envId]?.rgbValue ?? defaultColor.rgbValue;
111
+ }
112
+
113
+ getSymbolColor(envId: number, opacity?: number): string {
114
+ const color = this.colors[envId]?.symbolColor ?? defaultColor.symbolColor;
115
+ const normalizedOpacity = Math.min(Math.max(opacity ?? 1, 0), 1);
116
+ const value = color.join(',');
117
+ if (normalizedOpacity != 1) {
118
+ return `rgba(${value}, ${normalizedOpacity})`;
119
+ }
120
+ return `rgba(${value})`;
121
+ }
122
+
123
+ }
@@ -0,0 +1,42 @@
1
+ export default class Plane {
2
+
3
+ private readonly bounds: { minX: number, maxX: number, minY: number, maxY: number }
4
+ private readonly rooms: MapData.Room[] = [];
5
+ private readonly labels: MapData.Label[] = [];
6
+
7
+ constructor(rooms: MapData.Room[], labels: MapData.Label[]) {
8
+ this.rooms = rooms
9
+ this.bounds = this.createBounds();
10
+ this.labels = labels
11
+ }
12
+
13
+ getRooms() {
14
+ return this.rooms;
15
+ }
16
+
17
+ getLabels() {
18
+ return this.labels;
19
+ }
20
+
21
+ getBounds() {
22
+ return this.bounds;
23
+ }
24
+
25
+ private createBounds() {
26
+ return this.rooms.reduce(
27
+ (acc, r) => ({
28
+ minX: Math.min(acc.minX, r.x),
29
+ maxX: Math.max(acc.maxX, r.x),
30
+ minY: Math.min(acc.minY, r.y),
31
+ maxY: Math.max(acc.maxY, r.y),
32
+ }),
33
+ {
34
+ minX: Number.POSITIVE_INFINITY,
35
+ maxX: Number.NEGATIVE_INFINITY,
36
+ minY: Number.POSITIVE_INFINITY,
37
+ maxY: Number.NEGATIVE_INFINITY,
38
+ }
39
+ );
40
+ }
41
+
42
+ }
@@ -0,0 +1,90 @@
1
+ declare namespace MapData {
2
+
3
+ export type direction =
4
+ "north"
5
+ | "south"
6
+ | "east"
7
+ | "west"
8
+ | "northwest"
9
+ | "northeast"
10
+ | "southeast"
11
+ | "southwest"
12
+ | "up"
13
+ | "down"
14
+ | "in"
15
+ | "out"
16
+
17
+ export interface Color {
18
+ alpha: number;
19
+ r: number;
20
+ g: number;
21
+ b: number;
22
+ }
23
+
24
+ export interface Line {
25
+ points: Point[];
26
+ attributes: LineAttribute;
27
+ }
28
+
29
+ export interface Point {
30
+ x: number;
31
+ y: number;
32
+ }
33
+
34
+ export interface LineAttribute {
35
+ color: Color;
36
+ style: string;
37
+ arrow: boolean;
38
+ }
39
+
40
+ export interface Room {
41
+ id: number;
42
+ area: number;
43
+ x: number;
44
+ y: number;
45
+ z: number;
46
+ areaId: string;
47
+ weight: number;
48
+ roomChar: string;
49
+ name: string;
50
+ userData: Record<string, string>;
51
+ customLines: Record<string, Line>;
52
+ stubs: number[];
53
+ hash: string;
54
+ env: number;
55
+ exits: Record<direction, number>;
56
+ doors: Record<string, 1 | 2 | 3>;
57
+ specialExits: Record<string, number>;
58
+ exitLocks?: number[];
59
+ mSpecialExitLocks?: number[];
60
+ }
61
+
62
+ export interface Label {
63
+ labelId: number;
64
+ areaId: number;
65
+ pixMap: string;
66
+ X: number;
67
+ Y: number;
68
+ Z: number;
69
+ Width: number;
70
+ Height: number;
71
+ Text: string;
72
+ FgColor: Color;
73
+ BgColor: Color;
74
+ }
75
+
76
+ export interface Area {
77
+ areaName: string;
78
+ areaId: string;
79
+ rooms: Room[];
80
+ labels: Label[];
81
+ }
82
+
83
+ export type Map = Area[]
84
+
85
+ export interface Env {
86
+ envId: number;
87
+ colors: number[];
88
+ }
89
+
90
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2021",
4
+ "lib": ["ES2021", "DOM"],
5
+ "module": "ES2020",
6
+ "moduleResolution": "Bundler",
7
+ "declaration": true,
8
+ "declarationDir": "dist",
9
+ "outDir": "dist",
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "resolveJsonModule": true,
14
+ "sourceMap": true,
15
+ },
16
+ "include": ["src"]
17
+ }
package/vercel.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "buildCommand": "yarn demo:build",
3
+ "outputDirectory": "demo/dist",
4
+ "devCommand": "yarn demo:dev"
5
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { defineConfig } from 'vite';
2
+ import dts from 'vite-plugin-dts';
3
+
4
+ export default defineConfig(({ command }) => ({
5
+ root: command === 'serve' ? 'demo' : '.',
6
+ plugins: [
7
+ dts({
8
+ include: ['src'],
9
+ outDir: 'dist'
10
+ })
11
+ ],
12
+ build: {
13
+ lib: {
14
+ entry: 'src/index.ts',
15
+ name: 'mudlet-map-renderer',
16
+ fileName: (format) => (format === 'es' ? 'index.mjs' : 'index.cjs'),
17
+ formats: ['es', 'cjs']
18
+ },
19
+ rollupOptions: {
20
+ external: ['konva', 'canvas']
21
+ },
22
+ sourcemap: true,
23
+ emptyOutDir: true
24
+ },
25
+ server: {
26
+ open: true,
27
+ port: 5173
28
+ }
29
+ }));
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vite';
2
+ import path from 'node:path';
3
+
4
+ export default defineConfig({
5
+ root: 'demo',
6
+ resolve: {
7
+ alias: {
8
+ '@src': path.resolve(__dirname, 'src')
9
+ }
10
+ },
11
+ build: {
12
+ outDir: 'dist',
13
+ emptyOutDir: true
14
+ }
15
+ });
package/.prettierrc DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "tabWidth": 4,
3
- "useTabs": false,
4
- "printWidth": 160
5
- }
package/CHANGELOG.md DELETED
@@ -1,69 +0,0 @@
1
- # 0.0.24
2
- - respect room weights
3
- # 0.0.23
4
- - do not draw path fragments no on the level of displayed area
5
-
6
- # 0.0.22
7
- - handle not existing paths
8
-
9
- # 0.0.21
10
- - fixes in special links to other areas
11
- - add controls for paths
12
-
13
- # 0.0.20
14
-
15
- # 0.0.19
16
- - add path renders
17
-
18
- # 0.0.18
19
- - drag event will be fired actually on drag not only on release
20
-
21
- # 0.0.17
22
- - fix emboss
23
-
24
- # 0.0.16
25
- - add emoss setting
26
- - add drag event
27
-
28
- # 0.0.15
29
- - additional fix for custom exit lines exit
30
- # 0.0.14
31
- - add area change clicks on custom exit lines
32
- # 0.0.13
33
- - add line color (experimental)
34
-
35
- # 0.0.12
36
- - try to limit label font size to fit within label bounds
37
-
38
- # 0.0.11
39
- - another fix frame mode for background selection
40
-
41
- # 0.0.10
42
- - fix frame mode for background selection
43
-
44
- # 0.0.9
45
- - add transparent labels option
46
-
47
- # 0.0.8
48
- - remove wrong limits for drag
49
- # 0.0.7
50
- - fix renderer compliation error
51
- # 0.0.6
52
- - fix controls error when zooming out max
53
- - make JS code not use experimental features
54
- # 0.0.5
55
- - add limit function for areas
56
-
57
- # 0.0.4
58
- - fix exports
59
-
60
- # 0.0.3
61
- - add option to set font and background color
62
- - multiple fixes
63
-
64
- # 0.0.2
65
- - add option to set font for text labels
66
- - fix README example
67
-
68
- # 0.0.1
69
- - initial release
package/README.md DELETED
@@ -1,59 +0,0 @@
1
- # Mudlet Map Renderer
2
-
3
- Mudlet map rendering library. Can be used in Node.js and in browser.
4
-
5
- Until version `1.0.0` API is subject of change. Use with caution!
6
-
7
- ## TODO
8
-
9
- - [ ] Publish some examples (with data)
10
- - [ ] Convert to .ts
11
- - [ ] Document functions
12
- - [ ] Add test
13
- - [ ] Add lint
14
- - [ ] Align model with mudlet-map-binary-reader
15
-
16
-
17
- ## Very basic example
18
-
19
- ```js
20
- const fs = require("fs");
21
- const { MudletMapReader } = require("mudlet-map-binary-reader") //npm mudlet-map-binary-reader
22
- const { Renderer, MapReader } = require("mudlet-map-renderer")
23
-
24
- let map = MudletMapReader.read("./data/map.dat");
25
- MudletMapReader.export(map, "./data");
26
- let mapData = require("./data/mapExport.json");
27
- let mapColors = require("./data/colors.json");
28
-
29
- let reader = new MapReader(mapData, mapColors);
30
-
31
- const roomId = 1;
32
- const scale = 40;
33
- let area = reader.getAreaByRoomId(roomId);
34
- let settings = { scale: scale }
35
- let renderer = new Renderer(null, reader, area, reader.getColors(), settings);
36
- fs.writeFileSync("mapFull.svg", renderer.exportSvg());
37
- fs.writeFileSync("mapFragment.svg", renderer.exportSvg(roomId, 10));
38
- console.log("Map generated");
39
- ```
40
-
41
- ## Settings and their default values
42
- ```js
43
- class Settings {
44
- isRound = false;
45
- scale = 55;
46
- roomSize = 10;
47
- exitsSize = 2;
48
- borders = false;
49
- frameMode = false;
50
- areaName = true;
51
- showLabels = true;
52
- uniformLevelSize = false;
53
- fontFamily = 'sans-serif';
54
- mapBackground = "#000000";
55
- linesColor = '#FFFFFF';
56
- transparentLabels = false;
57
- emboss = false;
58
- }
59
- ```
package/exports.js DELETED
@@ -1,5 +0,0 @@
1
- module.exports = {
2
- Renderer: require("./map-fragment/draw/renderer").Renderer,
3
- Settings: require("./map-fragment/draw/renderer").Settings,
4
- MapReader: require("./map-fragment/reader/MapReader").MapReader,
5
- };
@@ -1,172 +0,0 @@
1
- const paper = require("paper");
2
- const { PathFinder } = require("../reader/PathFinder");
3
-
4
- let selectionStyle = function (item) {
5
- let style = {
6
- strokeColor: new paper.Color(180 / 255, 93 / 255, 60 / 255, 0.9),
7
- };
8
- if (item.closed) {
9
- style.fillColor = new paper.Color(
10
- new paper.Gradient([[item.fillColor, 0.38], new paper.Color(1, 1, 1)], false),
11
- item.bounds.topCenter,
12
- item.bounds.bottomCenter
13
- );
14
- }
15
- return style;
16
- };
17
-
18
- paper.Item.prototype.select = function (styleFunction) {
19
- this.mapSelected = !this.mapSelected;
20
- if (this.mapSelected && styleFunction !== undefined) {
21
- let style = styleFunction(this);
22
- this.orgStyle = {};
23
- for (const key in style) {
24
- this.orgStyle[key] = this[key];
25
- }
26
- this.style = style;
27
- } else {
28
- this.style = this.orgStyle;
29
- }
30
- };
31
-
32
- class Controls {
33
- constructor(renderer, reader, element, paperScope) {
34
- this.renderer = renderer;
35
- this.reader = reader;
36
- this.element = element;
37
- this.scope = paperScope;
38
- this.view = paperScope.view;
39
- this.element.onwheel = (event) => this.zoom(event);
40
- this.activateDrag();
41
- this.renderer.emitter.addEventListener("roomClick", (event) => this.selectRoom(event.detail));
42
- this.renderer.emitter.addEventListener("backgroundClick", () => this.deselectRoom());
43
- this.renderer.emitter.addEventListener("areaArrowClick", (event) => this.goToRoomArea(event.detail));
44
-
45
- let bounds = this.renderer.getBounds();
46
-
47
- this.view.center = bounds.center;
48
- this.view.zoom = Math.min(this.view.size.width / bounds.width, this.view.size.height / bounds.height);
49
- this.view.minZoom = this.view.zoom;
50
-
51
- this.pathFinder = new PathFinder(reader);
52
- }
53
-
54
- zoom(event) {
55
- event.preventDefault();
56
- let oldZoom = this.view.zoom;
57
- this.deltaZoom(event.deltaY > 0 ? 0.9 : 1.1);
58
- let viewPos = this.view.viewToProject(new paper.Point(event.offsetX, event.offsetY));
59
- let zoomScale = oldZoom / this.view.zoom;
60
- let centerAdjust = viewPos.subtract(this.view.center);
61
- let offset = viewPos.subtract(centerAdjust.multiply(zoomScale)).subtract(this.view.center);
62
- this.view.center = this.view.center.add(offset);
63
- }
64
-
65
- setZoom(value) {
66
- this.view.zoom = value;
67
- this.view.zoom = Math.min(Math.max(this.view.zoom, this.view.minZoom), 50);
68
- this.element.dispatchEvent(new CustomEvent("zoom", { detail: this.view }));
69
- }
70
-
71
- deltaZoom(delta) {
72
- this.setZoom(this.view.zoom * delta);
73
- }
74
-
75
- activateDrag() {
76
- let toolPan = new paper.Tool();
77
- toolPan.activate();
78
- toolPan.onMouseDrag = (event) => {
79
- this.toggleOptimizedDrag(true);
80
- this.element.style.cursor = "all-scroll";
81
- let delta = event.downPoint.subtract(event.point);
82
- this.view.translate(delta.negate());
83
- this.isDrag = true;
84
- this.element.dispatchEvent(new CustomEvent("drag", { detail: this.view }));
85
- };
86
- toolPan.onMouseDown = () => {
87
- this.isDrag = false;
88
- this.element.dispatchEvent(new CustomEvent("drag", { detail: this.view }));
89
- };
90
- toolPan.onMouseUp = () => {
91
- this.isDrag = false;
92
- this.element.style.cursor = "default";
93
- this.toggleOptimizedDrag(false);
94
- };
95
- }
96
-
97
- toggleOptimizedDrag(state) {
98
- if (!this.renderer.settings.optimizeDrag) {
99
- return;
100
- }
101
- if (state) {
102
- if (!this.isDrag) {
103
- this.renderer.linkLayer.visible = false;
104
- this.renderer.roomLayer.visible = false;
105
- this.renderer.rasterLayer.visible = true;
106
- }
107
- } else {
108
- this.renderer.linkLayer.visible = true;
109
- this.renderer.roomLayer.visible = true;
110
- this.renderer.rasterLayer.visible = false;
111
- }
112
- }
113
-
114
- selectRoom(room) {
115
- if (this.isDrag) {
116
- return false;
117
- }
118
- this.deselectRoom();
119
- this.renderer.renderPosition(room.id);
120
- room.render.select(selectionStyle);
121
- room.exitsRenders.forEach((render) => render.select(selectionStyle));
122
- this.selected = room;
123
-
124
- this.element.dispatchEvent(new CustomEvent("roomSelected", { detail: room }));
125
- }
126
-
127
- deselectRoom() {
128
- if (this.isDrag) {
129
- return false;
130
- }
131
- this.renderer.clearPosition();
132
- this.renderer.clearHighlight();
133
- if (this.selected !== undefined) {
134
- this.selected.render.select();
135
- this.selected.exitsRenders.forEach((render) => render.select());
136
- delete this.selected;
137
- this.element.dispatchEvent(new CustomEvent("roomDeselected"));
138
- }
139
- }
140
-
141
- centerRoom(id) {
142
- let room = this.renderer.area.getRoomById(id);
143
- if (room !== undefined) {
144
- this.centerOnItem(room.render);
145
- this.selectRoom(room);
146
- }
147
- }
148
-
149
- centerOnItem(item) {
150
- this.view.center = item.localToGlobal(item.position);
151
- }
152
-
153
- goToRoomArea(id) {
154
- let destRoom = this.reader.getRoomById(id);
155
- this.element.dispatchEvent(new CustomEvent("goToArea", { detail: destRoom }));
156
- }
157
-
158
- move(x, y) {
159
- this.view.scrollBy(new paper.Point(x * 50, y * 50));
160
- }
161
-
162
- renderPath(from, to, color) {
163
- let rooms = this.pathFinder.path(from, to)?.map(number => parseInt(number));
164
- if (rooms) {
165
- return this.renderer.renderPath(rooms, color);
166
- }
167
- }
168
- }
169
-
170
- module.exports = {
171
- Controls: Controls,
172
- };