squishjs 0.6.41 → 0.7.1
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.
- package/.github/workflows/node.js.yml +1 -1
- package/LICENSE.md +21 -0
- package/README.md +116 -42
- package/index.js +11 -1
- package/oldsvgexport.js +89 -0
- package/package.json +1 -1
- package/render-tester.js +120 -0
- package/src/BaseNode.js +101 -0
- package/src/Game.js +15 -3
- package/src/GameNode.js +82 -79
- package/src/InternalGameNode.js +4 -3
- package/src/Squisher.js +224 -0
- package/src/ViewableGame.js +60 -0
- package/src/node-types.js +15 -0
- package/src/squish.js +90 -349
- package/src/squishHelpers/asset.js +63 -0
- package/src/squishHelpers/border.js +16 -0
- package/src/squishHelpers/color.js +16 -0
- package/src/squishHelpers/coordinates2d.js +98 -0
- package/src/squishHelpers/effect.js +55 -0
- package/src/squishHelpers/fill.js +16 -0
- package/src/squishHelpers/handleClick.js +16 -0
- package/src/squishHelpers/id.js +16 -0
- package/src/squishHelpers/input.js +22 -0
- package/src/squishHelpers/playerIds.js +12 -0
- package/src/squishHelpers/pos.js +23 -0
- package/src/squishHelpers/size.js +23 -0
- package/src/squishHelpers/subType.js +16 -0
- package/src/squishHelpers/text.js +154 -0
- package/src/subtype-mappings.js +10 -0
- package/src/subtypes.js +7 -0
- package/src/terrain/defs.ts +16 -0
- package/src/terrain/index.ts +156 -0
- package/src/terrain/terrainFunctions.ts +142 -0
- package/src/util/geometry.js +45 -0
- package/src/util/index.ts +6 -0
- package/src/util/listenable.ts +20 -0
- package/src/util/shapes.ts +18 -0
- package/src/util/views.js +108 -0
- package/test/Layers.test.js +63 -0
- package/test/Scale.test.js +235 -0
- package/test/Squisher.test.js +49 -0
- package/test/StateUpdates.js +10 -0
- package/test/Terrain.test.js +0 -1
- package/test/main.test.js +55 -35
- package/test/utils.js +91 -0
- package/testapp.html +26 -0
- package/testapp.js +988 -0
- package/tsconfig.json +22 -0
- package/license.txt +0 -674
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { pointDef, boardInfo } from './defs';
|
|
2
|
+
|
|
3
|
+
const placeTerrain = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
|
|
4
|
+
if (x < 0 || x >= boardWidth) {
|
|
5
|
+
return;
|
|
6
|
+
} else if (y < 0 || y >= boardHeight) {
|
|
7
|
+
return;
|
|
8
|
+
} else if ( x === keyPoint.x && y === keyPoint.y) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (!board[x][y].filled) {
|
|
13
|
+
if (board[x][y].north) {
|
|
14
|
+
board[x][y - 1].south = false;
|
|
15
|
+
}
|
|
16
|
+
if (board[x][y].south) {
|
|
17
|
+
board[x][y + 1].north = false;
|
|
18
|
+
}
|
|
19
|
+
if (board[x][y].east) {
|
|
20
|
+
board[x + 1][y].west = false;
|
|
21
|
+
}
|
|
22
|
+
if (board[x][y].west) {
|
|
23
|
+
board[x - 1][y].east = false;
|
|
24
|
+
}
|
|
25
|
+
board[x][y].filled = true;
|
|
26
|
+
board[x][y].north = false;
|
|
27
|
+
board[x][y].south = false;
|
|
28
|
+
board[x][y].east = false;
|
|
29
|
+
board[x][y].west = false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const square = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
|
|
34
|
+
const option = Math.floor(Math.random() * 4);
|
|
35
|
+
placeTerrain(board, { x, y }, boardWidth, boardHeight, keyPoint);
|
|
36
|
+
if (option === 0) { // start point is top left
|
|
37
|
+
placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
|
|
38
|
+
placeTerrain(board, { x, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
39
|
+
placeTerrain(board, { x: x + 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
40
|
+
} else if (option === 1) { // start point is top right
|
|
41
|
+
placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
|
|
42
|
+
placeTerrain(board, { x, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
43
|
+
placeTerrain(board, { x: x - 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
44
|
+
} else if (option === 2) { // start point is bottom left
|
|
45
|
+
placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
46
|
+
placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
|
|
47
|
+
placeTerrain(board, { x: x + 1, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
48
|
+
} else { // start point is bottom right
|
|
49
|
+
placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
50
|
+
placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
|
|
51
|
+
placeTerrain(board, { x: x - 1, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const line = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
|
|
56
|
+
const option = Math.floor(Math.random() * 4);
|
|
57
|
+
placeTerrain(board, { x, y }, boardWidth, boardHeight, keyPoint);
|
|
58
|
+
if (option === 0) { // start point is top
|
|
59
|
+
placeTerrain(board, { x, y: y + 1}, boardWidth, boardHeight, keyPoint);
|
|
60
|
+
placeTerrain(board, { x, y: y + 2 }, boardWidth, boardHeight, keyPoint);
|
|
61
|
+
placeTerrain(board, { x, y: y + 3 }, boardWidth, boardHeight, keyPoint);
|
|
62
|
+
} else if (option === 1) { // start point is left
|
|
63
|
+
placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
|
|
64
|
+
placeTerrain(board, { x: x + 2, y }, boardWidth, boardHeight, keyPoint);
|
|
65
|
+
placeTerrain(board, { x: x + 3, y }, boardWidth, boardHeight, keyPoint);
|
|
66
|
+
} else if (option === 2) { // start point is right
|
|
67
|
+
placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
|
|
68
|
+
placeTerrain(board, { x: x - 2, y }, boardWidth, boardHeight, keyPoint);
|
|
69
|
+
placeTerrain(board, { x: x - 3, y }, boardWidth, boardHeight, keyPoint);
|
|
70
|
+
} else { // start point is bottom
|
|
71
|
+
placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
72
|
+
placeTerrain(board, { x, y: y - 2 }, boardWidth, boardHeight, keyPoint);
|
|
73
|
+
placeTerrain(board, { x, y: y - 3 }, boardWidth, boardHeight, keyPoint);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const bentLine = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
|
|
78
|
+
const option = Math.floor(Math.random() * 4);
|
|
79
|
+
placeTerrain(board, { x, y }, boardWidth, boardHeight, keyPoint);
|
|
80
|
+
if (option === 0) { // start point is top
|
|
81
|
+
placeTerrain(board, { x, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
82
|
+
placeTerrain(board, { x, y: y + 2 }, boardWidth, boardHeight, keyPoint);
|
|
83
|
+
placeTerrain(board, { x: x + 1, y: y + 2 }, boardWidth, boardHeight, keyPoint);
|
|
84
|
+
} else if (option === 1) { // start point is left
|
|
85
|
+
placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
|
|
86
|
+
placeTerrain(board, { x: x + 2, y }, boardWidth, boardHeight, keyPoint);
|
|
87
|
+
placeTerrain(board, { x: x + 2, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
88
|
+
} else if (option === 2) { // start point is right
|
|
89
|
+
placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
|
|
90
|
+
placeTerrain(board, { x: x - 2, y }, boardWidth, boardHeight, keyPoint);
|
|
91
|
+
placeTerrain(board, { x: x - 2, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
92
|
+
} else { // start point is bottom
|
|
93
|
+
placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
94
|
+
placeTerrain(board, { x, y: y - 2 }, boardWidth, boardHeight, keyPoint);
|
|
95
|
+
placeTerrain(board, { x: x - 1, y: y - 2 }, boardWidth, boardHeight, keyPoint);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const zShape = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
|
|
100
|
+
const option = Math.floor(Math.random() * 4);
|
|
101
|
+
placeTerrain(board, { x, y }, boardWidth, boardHeight, keyPoint);
|
|
102
|
+
if (option === 0) { // start point is top
|
|
103
|
+
placeTerrain(board, { x, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
104
|
+
placeTerrain(board, { x: x - 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
105
|
+
placeTerrain(board, { x: x - 1, y: y + 2 }, boardWidth, boardHeight, keyPoint);
|
|
106
|
+
} else if (option === 1) { // start point is left
|
|
107
|
+
placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
|
|
108
|
+
placeTerrain(board, { x: x + 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
109
|
+
placeTerrain(board, { x: x + 2, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
110
|
+
} else if (option === 2) { // start point is right
|
|
111
|
+
placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
|
|
112
|
+
placeTerrain(board, { x: x - 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
113
|
+
placeTerrain(board, { x: x - 2, y: y + 1 }, boardWidth, boardHeight, keyPoint);
|
|
114
|
+
} else { // start point is bottom
|
|
115
|
+
placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
116
|
+
placeTerrain(board, { x: x - 1, y: y - 1 }, boardWidth, boardHeight, keyPoint);
|
|
117
|
+
placeTerrain(board, { x: x - 1, y: y - 2 }, boardWidth, boardHeight, keyPoint);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const terrainFunctions = [
|
|
122
|
+
square,
|
|
123
|
+
line,
|
|
124
|
+
bentLine,
|
|
125
|
+
zShape
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
export const getRandomTerrain = () => {
|
|
129
|
+
return terrainFunctions[Math.floor(Math.random() * terrainFunctions.length)];
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const getRandomStartPoint = (boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
|
|
133
|
+
const startPoint = {
|
|
134
|
+
x: Math.floor(Math.random() * boardWidth),
|
|
135
|
+
y: Math.floor(Math.random() * boardHeight)
|
|
136
|
+
};
|
|
137
|
+
while (startPoint.x === keyPoint.x && startPoint.y === keyPoint.y) {
|
|
138
|
+
startPoint.x = Math.floor(Math.random() * boardWidth);
|
|
139
|
+
startPoint.y = Math.floor(Math.random() * boardHeight);
|
|
140
|
+
}
|
|
141
|
+
return startPoint;
|
|
142
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const collisionHelper = (node, nodeToCheck, filter, collisions = []) => {
|
|
2
|
+
// assume rectangles for now
|
|
3
|
+
|
|
4
|
+
if (!filter || (filter(node) && node.node.id !== nodeToCheck.node.id)) {
|
|
5
|
+
// todo: clean this up its gross
|
|
6
|
+
// text does not have coordinates, so make a fake vertices array from the starting point of the text
|
|
7
|
+
const nodeText = node.node.text;
|
|
8
|
+
const vertices = node.node.coordinates2d || [
|
|
9
|
+
[nodeText.x, nodeText.y],
|
|
10
|
+
[nodeText.x, nodeText.y],
|
|
11
|
+
[nodeText.x, nodeText.y],
|
|
12
|
+
[nodeText.x, nodeText.y],
|
|
13
|
+
[nodeText.x, nodeText.y]
|
|
14
|
+
];
|
|
15
|
+
const verticesToCheck = nodeToCheck.node.coordinates2d;
|
|
16
|
+
|
|
17
|
+
const node1LeftX = vertices[0][0];
|
|
18
|
+
const node1RightX = vertices[1][0];
|
|
19
|
+
const node2LeftX = verticesToCheck[0][0];
|
|
20
|
+
const node2RightX = verticesToCheck[1][0];
|
|
21
|
+
|
|
22
|
+
const node1TopY = vertices[0][1];
|
|
23
|
+
const node1BottomY = vertices[2][1];
|
|
24
|
+
const node2TopY = verticesToCheck[0][1];
|
|
25
|
+
const node2BottomY = verticesToCheck[2][1];
|
|
26
|
+
|
|
27
|
+
const oneToTheLeft = node2RightX < node1LeftX || node1RightX < node2LeftX;
|
|
28
|
+
const oneBelow = node1TopY > node2BottomY || node2TopY > node1BottomY;
|
|
29
|
+
if (!(oneToTheLeft || oneBelow)) {
|
|
30
|
+
collisions.push(node);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const child in node.node.children) {
|
|
35
|
+
collisionHelper(node.node.children[child], nodeToCheck, filter, collisions);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return collisions;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const checkCollisions = (root, node, filter = null) => {
|
|
42
|
+
return collisionHelper(root, node, filter);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
module.exports = { checkCollisions };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { gameNodeDef, internalGameNodeDef } from '../sharedDefs';
|
|
2
|
+
export default (obj: internalGameNodeDef, onChange: () => void) => {
|
|
3
|
+
const handler = {
|
|
4
|
+
get(target: Object, property: PropertyKey, receiver: any) {
|
|
5
|
+
return Reflect.get(target, property, receiver);
|
|
6
|
+
},
|
|
7
|
+
defineProperty(target: Object, property: PropertyKey, descriptor: PropertyDescriptor) {
|
|
8
|
+
const change = Reflect.defineProperty(target, property, descriptor);
|
|
9
|
+
onChange && onChange();
|
|
10
|
+
return change;
|
|
11
|
+
},
|
|
12
|
+
deleteProperty(target: Object, property: PropertyKey) {
|
|
13
|
+
const change = Reflect.deleteProperty(target, property);
|
|
14
|
+
onChange && onChange();
|
|
15
|
+
return change;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return new Proxy(obj, handler);
|
|
20
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const rectangle = (startX: number, startY: number, width: number, height: number) => {
|
|
2
|
+
return [
|
|
3
|
+
[startX, startY],
|
|
4
|
+
[startX + width, startY],
|
|
5
|
+
[startX + width, startY + height],
|
|
6
|
+
[startX, startY + height],
|
|
7
|
+
[startX, startY],
|
|
8
|
+
];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const triangle = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) => {
|
|
12
|
+
return [
|
|
13
|
+
[x1, y1],
|
|
14
|
+
[x2, y2],
|
|
15
|
+
[x3, y3],
|
|
16
|
+
[x1, y1]
|
|
17
|
+
];
|
|
18
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const { GameNode } = require('../GameNode');
|
|
2
|
+
const Shapes = require('../Shapes');
|
|
3
|
+
const ShapeUtils = require('./shapes');
|
|
4
|
+
const GeometryUtils = require('./geometry');
|
|
5
|
+
const Colors = require('../Colors');
|
|
6
|
+
|
|
7
|
+
const getView = (plane, view, playerIds, translation = {}) => {
|
|
8
|
+
|
|
9
|
+
const wouldBeCollisions = GeometryUtils.checkCollisions(plane, {node: {coordinates2d: ShapeUtils.rectangle(view.x, view.y, view.w, view.h)}}, (node) => {
|
|
10
|
+
return node.node.id !== plane.node.id;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const convertedRoot = new GameNode.Shape({
|
|
14
|
+
shapeType: Shapes.POLYGON,
|
|
15
|
+
coordinates2d: ShapeUtils.rectangle(0, 0, 0, 0),
|
|
16
|
+
fill: Colors.COLORS.BLACK
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const convertedNodes = [];
|
|
20
|
+
|
|
21
|
+
if (wouldBeCollisions.length > 0) {
|
|
22
|
+
wouldBeCollisions.forEach(node => {
|
|
23
|
+
let shouldInclude = true;
|
|
24
|
+
|
|
25
|
+
const translatedCoords = [];
|
|
26
|
+
|
|
27
|
+
// same hack as geometry utils
|
|
28
|
+
const vertices = node.node.coordinates2d || [
|
|
29
|
+
[node.node.text.x, node.node.text.y],
|
|
30
|
+
[node.node.text.x, node.node.text.y],
|
|
31
|
+
[node.node.text.x, node.node.text.y],
|
|
32
|
+
[node.node.text.x, node.node.text.y],
|
|
33
|
+
[node.node.text.x, node.node.text.y]
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
for (let coorPairIndex in vertices) {
|
|
37
|
+
const coordPair = vertices[coorPairIndex];
|
|
38
|
+
const x = coordPair[0];
|
|
39
|
+
const y = coordPair[1];
|
|
40
|
+
let translatedX = Math.max(Math.min(x - view.x, 100), 0);
|
|
41
|
+
let translatedY = Math.max(Math.min(y - view.y, 100), 0);
|
|
42
|
+
|
|
43
|
+
const shouldTranslate = translation.filter ? translation.filter(node) : true;
|
|
44
|
+
|
|
45
|
+
if (shouldTranslate) {
|
|
46
|
+
if (translation.x) {
|
|
47
|
+
translatedX += translation.x;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (translation.y) {
|
|
51
|
+
translatedY += translation.y;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (translatedX < 0 || translatedX > 100) {
|
|
55
|
+
shouldInclude = false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (translatedY < 0 || translatedY > 100) {
|
|
59
|
+
shouldInclude = false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const xScale = 1//100 / (view.w || 100);
|
|
64
|
+
const yScale = 1//100 / (view.h || 100);
|
|
65
|
+
|
|
66
|
+
translatedX = xScale * translatedX;
|
|
67
|
+
translatedY = yScale * translatedY;
|
|
68
|
+
|
|
69
|
+
translatedCoords.push([translatedX, translatedY]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (shouldInclude) {
|
|
73
|
+
const copied = node.clone({handleClick: node.node.handleClick === null || node.node.handleClick === undefined ? null : node.node.handleClick});
|
|
74
|
+
|
|
75
|
+
if (translatedCoords && translatedCoords.length) {
|
|
76
|
+
if (copied.node.text) {
|
|
77
|
+
copied.node.text.x = translatedCoords[0][0];
|
|
78
|
+
copied.node.text.y = translatedCoords[0][1];
|
|
79
|
+
}
|
|
80
|
+
if (translatedCoords.length) {
|
|
81
|
+
copied.node.coordinates2d = translatedCoords;
|
|
82
|
+
}
|
|
83
|
+
if (copied.node.asset) {
|
|
84
|
+
const firstPoint = copied.node.coordinates2d[0];
|
|
85
|
+
const secondPoint = copied.node.coordinates2d[1];
|
|
86
|
+
const thirdPoint = copied.node.coordinates2d[2];
|
|
87
|
+
const width = secondPoint[0] - firstPoint[0];
|
|
88
|
+
const height = thirdPoint[1] - secondPoint[1];
|
|
89
|
+
Object.values(copied.node.asset)[0].pos.x = firstPoint[0];
|
|
90
|
+
Object.values(copied.node.asset)[0].pos.y = firstPoint[1];
|
|
91
|
+
Object.values(copied.node.asset)[0].size.x = width;
|
|
92
|
+
Object.values(copied.node.asset)[0].size.y = height;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
copied.node.playerIds = playerIds || [];
|
|
96
|
+
convertedNodes.push(copied);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
convertedNodes.forEach(c => convertedRoot.addChild(c));
|
|
102
|
+
|
|
103
|
+
return convertedRoot;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
getView
|
|
108
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { COLORS } = require("../src/Colors");
|
|
2
|
+
const assert = require("assert");
|
|
3
|
+
const Squisher = require('../src/Squisher');
|
|
4
|
+
const { squish, unsquish } = require('../src/squish');
|
|
5
|
+
const { FakeGame, verifyArrayEquality, rectNode, polygonNode, circleNode } = require('./utils');
|
|
6
|
+
|
|
7
|
+
test("two layers on a base - square over square", () => {
|
|
8
|
+
const base = rectNode({ x: 0, y: 0, width: 100, height: 100, fill: COLORS.RED });
|
|
9
|
+
const layerOne = rectNode({ x: 40, y: 40, width: 20, height: 20, fill: COLORS.BLUE });
|
|
10
|
+
const layerTwo = rectNode({ x: 45, y: 45, width: 10, height: 10, fill: COLORS.GREEN });
|
|
11
|
+
|
|
12
|
+
const game = new FakeGame([
|
|
13
|
+
{
|
|
14
|
+
root: base
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
root: layerOne
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
root: layerTwo
|
|
21
|
+
}
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const squisher = new Squisher({ game });
|
|
25
|
+
|
|
26
|
+
assert(squisher.state.length === 3);
|
|
27
|
+
verifyArrayEquality(squisher.state[0], squish(base));
|
|
28
|
+
verifyArrayEquality(squisher.state[1], squish(layerOne));
|
|
29
|
+
verifyArrayEquality(squisher.state[2], squish(layerTwo));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("test layer with child added", () => {
|
|
33
|
+
const base = rectNode({ x: 0, y: 0, width: 100, height: 100, fill: COLORS.RED });
|
|
34
|
+
const layer = rectNode({ x: 40, y: 40, width: 20, height: 20, fill: COLORS.BLUE });
|
|
35
|
+
|
|
36
|
+
const game = new FakeGame([
|
|
37
|
+
{
|
|
38
|
+
root: base
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
root: layer
|
|
42
|
+
}
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const squisher = new Squisher({ game });
|
|
46
|
+
|
|
47
|
+
const initialState = Array.from(squisher.state);
|
|
48
|
+
assert(initialState.length === 2);
|
|
49
|
+
verifyArrayEquality(initialState[0], squish(base));
|
|
50
|
+
verifyArrayEquality(initialState[1], squish(layer));
|
|
51
|
+
|
|
52
|
+
const child = rectNode({ x: 50, y: 50, height: 25, width: 25, fill: COLORS.WHITE });
|
|
53
|
+
|
|
54
|
+
// adding a child should trigger an update in the squisher and result in a new state
|
|
55
|
+
layer.addChild(child);
|
|
56
|
+
|
|
57
|
+
const expectedLayerValue = [squish(layer), squish(child)].flat();
|
|
58
|
+
const stateWithChild = Array.from(squisher.state);
|
|
59
|
+
assert(stateWithChild.length === 3);
|
|
60
|
+
verifyArrayEquality(stateWithChild[0], squish(base));
|
|
61
|
+
verifyArrayEquality(stateWithChild[1], squish(layer));
|
|
62
|
+
verifyArrayEquality(stateWithChild[2], squish(child));
|
|
63
|
+
});
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
const { COLORS } = require("../src/Colors");
|
|
2
|
+
const assert = require("assert");
|
|
3
|
+
const Squisher = require('../src/Squisher');
|
|
4
|
+
const { unsquish } = require('../src/squish');
|
|
5
|
+
|
|
6
|
+
const { FakeGame, verifyArrayEquality, rectNode, textNode, polygonNode, circleNode, lineNode } = require('./utils');
|
|
7
|
+
|
|
8
|
+
test("90% scale rectangles", () => {
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Red root taking up entirety of container.
|
|
12
|
+
* Four child nodes, each of a different color taking up a quarter of the container.
|
|
13
|
+
* If displayed on a screen, red would not be visible since children cover 100% of the overall space
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const root = rectNode({ x: 0, y: 0, width: 100, height: 100, fill: COLORS.RED });
|
|
17
|
+
const topLeftCorner = rectNode({ x: 0, y: 0, width: 50, height: 50, fill: COLORS.BLUE });
|
|
18
|
+
const topRightCorner = rectNode({ x: 50, y: 0, width: 50, height: 50, fill: COLORS.GREEN });
|
|
19
|
+
const bottomRightCorner = rectNode({ x: 50, y: 50, width: 50, height: 50, fill: COLORS.BLACK });
|
|
20
|
+
const bottomLeftCorner = rectNode({ x: 0, y: 50, width: 50, height: 50, fill: COLORS.PURPLE});
|
|
21
|
+
|
|
22
|
+
root.addChildren(topLeftCorner, topRightCorner, bottomRightCorner, bottomLeftCorner);
|
|
23
|
+
|
|
24
|
+
const game = new FakeGame([
|
|
25
|
+
{
|
|
26
|
+
root,
|
|
27
|
+
scale: {
|
|
28
|
+
x: .9,
|
|
29
|
+
y: .9
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const squisher = new Squisher({ game });
|
|
35
|
+
|
|
36
|
+
const nodesToRender = squisher.squish(game.getLayers());
|
|
37
|
+
|
|
38
|
+
assert(nodesToRender.length == 5);
|
|
39
|
+
|
|
40
|
+
const rootUnsquished = unsquish(nodesToRender[0]);
|
|
41
|
+
const topLeftUnsquished = unsquish(nodesToRender[1]);
|
|
42
|
+
const topRightUnsquished = unsquish(nodesToRender[2]);
|
|
43
|
+
const bottomRightUnsquished = unsquish(nodesToRender[3]);
|
|
44
|
+
const bottomLeftUnsquished = unsquish(nodesToRender[4]);
|
|
45
|
+
|
|
46
|
+
const rootCoords = rootUnsquished.node.coordinates2d;
|
|
47
|
+
const topLeftCoords = topLeftUnsquished.node.coordinates2d;
|
|
48
|
+
const topRightCoords = topRightUnsquished.node.coordinates2d;
|
|
49
|
+
const bottomRightCoords = bottomRightUnsquished.node.coordinates2d;
|
|
50
|
+
const bottomLeftCoords = bottomLeftUnsquished.node.coordinates2d;
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* Scaled 90% should mean centered with 5% margins
|
|
55
|
+
* Draw path starts at the top left, should match:
|
|
56
|
+
* (5, 5), (95, 5), (95, 95), (5, 95), (5, 5)
|
|
57
|
+
*/
|
|
58
|
+
verifyArrayEquality(rootCoords, [
|
|
59
|
+
[5, 5],
|
|
60
|
+
[95, 5],
|
|
61
|
+
[95, 95],
|
|
62
|
+
[5, 95],
|
|
63
|
+
[5, 5]
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* width is 45, since it was 50% of the container, now scaled down to 90% (0.9 * .5 == 0.45)
|
|
68
|
+
* same as height
|
|
69
|
+
*/
|
|
70
|
+
verifyArrayEquality(topLeftCoords, [
|
|
71
|
+
[5, 5],
|
|
72
|
+
[50, 5],
|
|
73
|
+
[50, 50],
|
|
74
|
+
[5, 50],
|
|
75
|
+
[5, 5]
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
verifyArrayEquality(topRightCoords, [
|
|
79
|
+
[50, 5],
|
|
80
|
+
[95, 5],
|
|
81
|
+
[95, 50],
|
|
82
|
+
[50, 50],
|
|
83
|
+
[50, 5]
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
verifyArrayEquality(bottomRightCoords, [
|
|
87
|
+
[50, 50],
|
|
88
|
+
[95, 50],
|
|
89
|
+
[95, 95],
|
|
90
|
+
[50, 95],
|
|
91
|
+
[50, 50]
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
verifyArrayEquality(bottomLeftCoords, [
|
|
95
|
+
[5, 50],
|
|
96
|
+
[50, 50],
|
|
97
|
+
[50, 95],
|
|
98
|
+
[5, 95],
|
|
99
|
+
[5, 50]
|
|
100
|
+
]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("50% scale triangle", () => {
|
|
104
|
+
|
|
105
|
+
const root = polygonNode({coordinates: [20, 20, 30, 30, 10, 30, 20, 20], fill: COLORS.GREEN });
|
|
106
|
+
|
|
107
|
+
const game = new FakeGame([
|
|
108
|
+
{
|
|
109
|
+
root,
|
|
110
|
+
scale: {
|
|
111
|
+
x: .5,
|
|
112
|
+
y: .5
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
const squisher = new Squisher({ game });
|
|
118
|
+
|
|
119
|
+
const nodesToRender = squisher.squish(game.getLayers());
|
|
120
|
+
|
|
121
|
+
assert(nodesToRender.length == 1);
|
|
122
|
+
|
|
123
|
+
const rootUnsquished = unsquish(nodesToRender[0]);
|
|
124
|
+
|
|
125
|
+
const rootCoords = rootUnsquished.node.coordinates2d;
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* 50% scale means we're expecting half of the width & height, plus a margin of 25% on each edge of the container
|
|
129
|
+
* (to account for the full 100% of the container)
|
|
130
|
+
*/
|
|
131
|
+
verifyArrayEquality(rootCoords, [
|
|
132
|
+
[35, 35],
|
|
133
|
+
[40, 40],
|
|
134
|
+
[30, 40],
|
|
135
|
+
[35, 35]
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("75% scale circle", () => {
|
|
141
|
+
|
|
142
|
+
const root = circleNode({center: {x: 50, y: 50 }, radius: 10, fill: COLORS.PINK });
|
|
143
|
+
|
|
144
|
+
const game = new FakeGame([
|
|
145
|
+
{
|
|
146
|
+
root,
|
|
147
|
+
scale: {
|
|
148
|
+
x: .75,
|
|
149
|
+
y: .75
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
]);
|
|
153
|
+
|
|
154
|
+
const squisher = new Squisher({ game });
|
|
155
|
+
const nodesToRender = squisher.squish(game.getLayers());
|
|
156
|
+
|
|
157
|
+
assert(nodesToRender.length == 1);
|
|
158
|
+
|
|
159
|
+
const rootUnsquished = unsquish(nodesToRender[0]);
|
|
160
|
+
|
|
161
|
+
const rootCoords = rootUnsquished.node.coordinates2d;
|
|
162
|
+
|
|
163
|
+
/*
|
|
164
|
+
* 50% scale means we're expecting half of the width & height, plus a margin of 25% on each edge of the container
|
|
165
|
+
* (to account for the full 100% of the container)
|
|
166
|
+
*/
|
|
167
|
+
verifyArrayEquality(rootCoords, [
|
|
168
|
+
50, 50, 7.5
|
|
169
|
+
]);
|
|
170
|
+
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('ayy lmao', () => {
|
|
174
|
+
|
|
175
|
+
const root = rectNode({ x: 0, y: 0, width: 100, height: 100, fill: COLORS.RED });
|
|
176
|
+
const topLeftCorner = rectNode({ x: 0, y: 0, width: 50, height: 50, fill: COLORS.BLUE });
|
|
177
|
+
const topRightCorner = rectNode({ x: 50, y: 0, width: 50, height: 50, fill: COLORS.GREEN });
|
|
178
|
+
const bottomRightCorner = rectNode({ x: 50, y: 50, width: 50, height: 50, fill: COLORS.BLACK });
|
|
179
|
+
const bottomLeftCorner = rectNode({ x: 0, y: 50, width: 50, height: 50, fill: COLORS.PURPLE});
|
|
180
|
+
|
|
181
|
+
const testTextNode = textNode({ text: 'ayy lmao', x: 75, y: 75, color: COLORS.WHITE, align: 'center' });
|
|
182
|
+
bottomRightCorner.addChild(testTextNode);
|
|
183
|
+
|
|
184
|
+
root.addChildren(topLeftCorner, topRightCorner, bottomRightCorner, bottomLeftCorner);
|
|
185
|
+
|
|
186
|
+
const game = new FakeGame([
|
|
187
|
+
{
|
|
188
|
+
root,
|
|
189
|
+
scale: {
|
|
190
|
+
x: 1,
|
|
191
|
+
y: 1
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]);
|
|
195
|
+
|
|
196
|
+
const squisher = new Squisher({ game });
|
|
197
|
+
|
|
198
|
+
const squishedLayers = squisher.state;
|
|
199
|
+
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("a line", () => {
|
|
203
|
+
|
|
204
|
+
const root = lineNode({coords: [[20, 20], [80, 80]], color: COLORS.ORANGE});
|
|
205
|
+
|
|
206
|
+
const game = new FakeGame([
|
|
207
|
+
{
|
|
208
|
+
root,
|
|
209
|
+
scale: {
|
|
210
|
+
x: 1,
|
|
211
|
+
y: 1
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
const squisher = new Squisher({ game });
|
|
217
|
+
const nodesToRender = squisher.squish(game.getLayers());
|
|
218
|
+
|
|
219
|
+
assert(nodesToRender.length == 1);
|
|
220
|
+
|
|
221
|
+
const rootUnsquished = unsquish(nodesToRender[0]);
|
|
222
|
+
|
|
223
|
+
const rootCoords = rootUnsquished.node.coordinates2d;
|
|
224
|
+
|
|
225
|
+
/*
|
|
226
|
+
* 50% scale means we're expecting half of the width & height, plus a margin of 25% on each edge of the container
|
|
227
|
+
* (to account for the full 100% of the container)
|
|
228
|
+
*/
|
|
229
|
+
verifyArrayEquality(rootCoords, [
|
|
230
|
+
[20, 20], [80, 80]
|
|
231
|
+
]);
|
|
232
|
+
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { COLORS } = require("../src/Colors");
|
|
2
|
+
const assert = require("assert");
|
|
3
|
+
const Squisher = require('../src/Squisher');
|
|
4
|
+
const { unsquish } = require('../src/squish');
|
|
5
|
+
|
|
6
|
+
const { FakeGame, verifyArrayEquality, rectNode, textNode, polygonNode, circleNode, lineNode } = require('./utils');
|
|
7
|
+
|
|
8
|
+
test("squisher listener", () => {
|
|
9
|
+
|
|
10
|
+
const root = lineNode({coords: [[20, 20], [80, 80]], color: COLORS.ORANGE});
|
|
11
|
+
|
|
12
|
+
const game = new FakeGame([
|
|
13
|
+
{
|
|
14
|
+
root,
|
|
15
|
+
scale: {
|
|
16
|
+
x: 1,
|
|
17
|
+
y: 1
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const squisher = new Squisher({ game });
|
|
23
|
+
|
|
24
|
+
let timesInvoked = 0;
|
|
25
|
+
|
|
26
|
+
squisher.addListener((newSquishedState) => {
|
|
27
|
+
if (timesInvoked === 0) {
|
|
28
|
+
assert(newSquishedState.length == 1);
|
|
29
|
+
const updatedRoot = unsquish(newSquishedState[0]);
|
|
30
|
+
verifyArrayEquality(updatedRoot.node.color, COLORS.BLACK);
|
|
31
|
+
} else if (timesInvoked === 1) {
|
|
32
|
+
assert(newSquishedState.length == 1);
|
|
33
|
+
const updatedRoot = unsquish(newSquishedState[0]);
|
|
34
|
+
verifyArrayEquality(updatedRoot.node.color, COLORS.GREEN);
|
|
35
|
+
}
|
|
36
|
+
timesInvoked++;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
assert(timesInvoked === 0);
|
|
40
|
+
|
|
41
|
+
root.node.color = COLORS.BLACK;
|
|
42
|
+
|
|
43
|
+
assert(timesInvoked === 1);
|
|
44
|
+
|
|
45
|
+
root.node.color = COLORS.GREEN;
|
|
46
|
+
assert(timesInvoked === 2);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
|