@terrafirmagreg-team/recipe 0.1.0 → 1.2.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/package.json +16 -16
- package/src/components/button-copy.tsx +1 -0
- package/src/components/cards/card/index.tsx +5 -3
- package/src/components/cards/card/scene.tsx +4 -2
- package/src/components/cards/card/strip.tsx +4 -2
- package/src/components/cards/card/text.tsx +2 -1
- package/src/components/cards/card/tips.tsx +1 -0
- package/src/components/cards/default-item.tsx +6 -2
- package/src/components/cards/glare-filter.tsx +1 -0
- package/src/components/cards/paged-card.tsx +1 -0
- package/src/components/cards/slot-hitbox.tsx +4 -2
- package/src/components/cards/strip-card/buttons.tsx +7 -2
- package/src/components/cards/strip-card/index.tsx +3 -1
- package/src/components/cards/strip-card/member-cell.tsx +4 -2
- package/src/components/cards/strip-card/sprite.tsx +1 -0
- package/src/components/embed-card.tsx +44 -0
- package/src/components/mdx/recipe.tsx +34 -16
- package/src/components/tool-list.tsx +1 -0
- package/src/{lib/tree.ts → components/tree.tsx} +20 -12
- package/src/components/views/alloy/index.tsx +14 -7
- package/src/components/views/alloy/list.tsx +3 -2
- package/src/components/views/alloy/mix/index.tsx +1 -0
- package/src/components/views/alloy/mix/ratio.tsx +7 -6
- package/src/components/views/alloy/mix/rows.tsx +6 -7
- package/src/components/views/alloy/mix/status.tsx +8 -7
- package/src/components/views/alloy/mix/total.tsx +13 -12
- package/src/components/views/alloy/range-bar.tsx +2 -1
- package/src/components/views/alloy/stack-bar.tsx +4 -2
- package/src/components/views/browser/axis-switcher.tsx +13 -12
- package/src/components/views/browser/card-grid.tsx +2 -1
- package/src/components/views/browser/favorite-button.tsx +2 -1
- package/src/components/views/browser/frame.tsx +3 -2
- package/src/components/views/browser/index.tsx +20 -2
- package/src/components/views/browser/item-panel/index.tsx +11 -9
- package/src/components/views/browser/item-panel/skeleton.tsx +1 -0
- package/src/components/views/browser/item-panel-grid/cell.tsx +5 -3
- package/src/components/views/browser/item-panel-grid/favorite-cell.tsx +4 -2
- package/src/components/views/browser/item-panel-grid/index.tsx +4 -2
- package/src/components/views/browser/item-view/index.tsx +7 -12
- package/src/components/views/browser/item-view/tags.tsx +7 -5
- package/src/components/views/browser/item-view-header.tsx +10 -9
- package/src/components/views/browser/pane-tabs.tsx +1 -0
- package/src/components/views/browser/place-header.tsx +4 -3
- package/src/components/views/browser/query-controls.tsx +3 -1
- package/src/components/views/browser/recipe-item-header.tsx +5 -4
- package/src/components/views/browser/settings.tsx +4 -3
- package/src/components/views/browser/toolbar.tsx +3 -2
- package/src/components/views/browser/type-tabs.tsx +5 -3
- package/src/components/views/browser/type-view.tsx +7 -12
- package/src/components/views/browser/workstation-rail.tsx +4 -3
- package/src/components/views/calc/calculator/actions.tsx +4 -14
- package/src/components/views/calc/calculator/help.tsx +69 -28
- package/src/components/views/calc/calculator/index.tsx +34 -82
- package/src/components/views/calc/dot-overlay.tsx +1 -0
- package/src/components/views/calc/edge.tsx +134 -32
- package/src/components/views/calc/flow/index.tsx +92 -54
- package/src/components/views/calc/flow/picker-dialogs.tsx +84 -6
- package/src/components/views/calc/flow/toolbar.tsx +9 -21
- package/src/components/views/calc/flow/view-menu.tsx +16 -120
- package/src/components/views/calc/member-picker.tsx +8 -6
- package/src/components/views/calc/node-card/actions.tsx +28 -106
- package/src/components/views/calc/node-card/badges.tsx +10 -28
- package/src/components/views/calc/node-card/chips.tsx +8 -40
- package/src/components/views/calc/node-card/far.tsx +13 -65
- package/src/components/views/calc/node-card/full.tsx +133 -83
- package/src/components/views/calc/node-card/index.tsx +1 -0
- package/src/components/views/calc/node-card/menu.tsx +93 -22
- package/src/components/views/calc/node-card/port-cells.tsx +186 -0
- package/src/components/views/calc/node-card/port-rails.tsx +251 -0
- package/src/components/views/calc/node-card/ports.tsx +86 -0
- package/src/components/views/calc/recipe-picker/body.tsx +17 -14
- package/src/components/views/calc/recipe-picker/footer.tsx +5 -3
- package/src/components/views/calc/recipe-picker/index.tsx +5 -0
- package/src/components/views/calc/sidebar.tsx +19 -10
- package/src/components/views/calc/trees/icon-picker.tsx +3 -2
- package/src/components/views/calc/trees/index.tsx +5 -4
- package/src/components/views/calc/trees/label.tsx +6 -4
- package/src/components/views/calc/trees/rows.tsx +6 -5
- package/src/components/views/forge/gui.tsx +2 -1
- package/src/components/views/forge/index.tsx +14 -7
- package/src/components/views/forge/list.tsx +3 -2
- package/src/components/views/forge/workbench/controls.tsx +13 -6
- package/src/components/views/forge/workbench/index.tsx +3 -2
- package/src/components/views/forge/workbench/panel.tsx +1 -0
- package/src/components/views/forge/workbench/ruler.tsx +5 -4
- package/src/components/views/forge/workbench/status.tsx +6 -5
- package/src/components/views/forge/workbench/tape.tsx +5 -4
- package/src/hooks/alloy/use-fluid-colors.ts +12 -9
- package/src/hooks/alloy/use-mix-state.ts +3 -2
- package/src/hooks/calc/flow/use-engage.ts +129 -0
- package/src/hooks/calc/flow/use-handlers.ts +192 -0
- package/src/hooks/calc/flow/use-layout.ts +105 -0
- package/src/hooks/calc/flow/{mount.ts → use-mount.ts} +1 -0
- package/src/hooks/calc/flow/{nodes.ts → use-nodes.ts} +46 -37
- package/src/hooks/calc/flow/use-pickers.ts +57 -0
- package/src/hooks/calc/flow/{viewport.ts → use-viewport.ts} +13 -26
- package/src/hooks/calc/flow/{window.ts → use-window.ts} +1 -0
- package/src/hooks/calc/use-actions.ts +208 -229
- package/src/hooks/calc/use-amounts.ts +23 -60
- package/src/hooks/calc/use-data.ts +106 -192
- package/src/hooks/calc/use-defaults.ts +4 -2
- package/src/hooks/calc/use-far-mode.ts +1 -0
- package/src/hooks/calc/use-focus.ts +8 -7
- package/src/hooks/calc/use-maps.ts +1 -0
- package/src/hooks/calc/use-reset.ts +1 -0
- package/src/hooks/calc/use-resolver.ts +32 -196
- package/src/hooks/calc/use-session.ts +1 -0
- package/src/hooks/calc/use-share.ts +5 -2
- package/src/hooks/calc/use-storage.ts +5 -4
- package/src/hooks/calc/use-tier.ts +1 -0
- package/src/hooks/calc/use-view-mode.ts +1 -0
- package/src/hooks/calc/use-worker.ts +56 -121
- package/src/hooks/forge/use-anvil-gui.ts +4 -2
- package/src/hooks/forge/use-forge-state.ts +1 -0
- package/src/hooks/forge/use-ruler.ts +1 -0
- package/src/hooks/use-add-to-calc.ts +23 -19
- package/src/hooks/use-categories-by-id.ts +18 -0
- package/src/hooks/use-favorite-hotkey.ts +1 -0
- package/src/hooks/use-favorites.ts +1 -0
- package/src/hooks/use-group-outlines.ts +1 -0
- package/src/hooks/use-hide-unused.ts +2 -1
- package/src/hooks/use-index.ts +6 -2
- package/src/hooks/use-item-feeds.ts +3 -1
- package/src/hooks/use-item-pane.ts +6 -4
- package/src/hooks/use-item-press.ts +27 -23
- package/src/hooks/use-item-tag-groups.ts +4 -2
- package/src/hooks/use-item-tooltips.ts +3 -1
- package/src/hooks/use-left-sidebar-tree.ts +11 -3
- package/src/hooks/use-mod-names.ts +4 -2
- package/src/hooks/use-mount-limit.ts +1 -0
- package/src/hooks/use-panel-cells.ts +3 -1
- package/src/hooks/use-panel-page.ts +9 -6
- package/src/hooks/use-panel-tabs.ts +2 -1
- package/src/hooks/use-query.ts +8 -6
- package/src/hooks/use-session.ts +6 -2
- package/src/hooks/use-sidebar-group.ts +3 -7
- package/src/hooks/use-tag-members.ts +3 -1
- package/src/hooks/use-tool-recipes.ts +1 -0
- package/src/hooks/use-type-filter.ts +1 -0
- package/src/lib/alloy/mix.ts +1 -0
- package/src/lib/alloy/parse.ts +1 -0
- package/src/lib/calc/defaults.ts +6 -4
- package/src/lib/calc/flow.ts +143 -107
- package/src/lib/calc/focus.ts +1 -0
- package/src/lib/calc/meta.ts +3 -1
- package/src/lib/calc/pack.ts +47 -154
- package/src/lib/calc/ports.ts +43 -0
- package/src/lib/calc/resolve.ts +6 -38
- package/src/lib/calc/route/buckets.ts +1 -0
- package/src/lib/calc/route/geometry.ts +13 -24
- package/src/lib/calc/route/index.ts +1 -0
- package/src/lib/calc/route/polyline.ts +3 -3
- package/src/lib/calc/route/router.ts +181 -57
- package/src/lib/calc/scheme.ts +226 -0
- package/src/lib/calc/storage/entry.ts +27 -19
- package/src/lib/calc/storage/index.ts +2 -0
- package/src/lib/calc/storage/keys.ts +4 -8
- package/src/lib/calc/storage/store.ts +11 -36
- package/src/lib/calc/tree/index.ts +2 -16
- package/src/lib/calc/tree/materials.ts +1 -0
- package/src/lib/calc/tree/recipe.ts +1 -0
- package/src/lib/calc/tree/share.ts +86 -149
- package/src/lib/calc/worker/api.ts +3 -11
- package/src/lib/calc/worker/fetch.ts +1 -0
- package/src/lib/calc/worker/flush.ts +7 -20
- package/src/lib/calc/worker/index.ts +1 -1
- package/src/lib/calc/worker/protocol.ts +16 -13
- package/src/lib/calc/worker/resolve.ts +32 -84
- package/src/lib/calc/worker/state.ts +5 -6
- package/src/{components/cards/card/model.ts → lib/card-model.ts} +1 -0
- package/src/lib/client/recipe-categories.ts +11 -93
- package/src/lib/client/recipe.ts +13 -11
- package/src/lib/client-index/catalog.ts +33 -27
- package/src/lib/client-index/chrome.ts +30 -30
- package/src/lib/client-index/index.ts +1 -0
- package/src/lib/client-index/registry.ts +70 -59
- package/src/lib/client-index/rows.ts +63 -57
- package/src/lib/client-index/tooltips.ts +28 -34
- package/src/lib/config.ts +2 -1
- package/src/lib/data/category/dto.ts +4 -3
- package/src/lib/data/category/load.ts +9 -8
- package/src/lib/data/category/parse.ts +4 -3
- package/src/lib/data/dto.ts +4 -3
- package/src/lib/data/grid.ts +2 -1
- package/src/lib/data/parse/entry.ts +3 -2
- package/src/lib/data/parse/index.ts +1 -0
- package/src/lib/data/parse/layout.ts +5 -2
- package/src/lib/data/parse/slots.ts +3 -2
- package/src/lib/data/parse/widgets.ts +3 -2
- package/src/lib/data/scene.ts +1 -1
- package/src/lib/data/slot-io.ts +1 -0
- package/src/lib/dump.ts +4 -3
- package/src/lib/embed.ts +22 -0
- package/src/lib/favorite-hover.ts +1 -0
- package/src/lib/fluid-outputs.ts +10 -12
- package/src/lib/forge/actions.ts +2 -1
- package/src/lib/forge/data.ts +2 -1
- package/src/lib/forge/solve.ts +1 -0
- package/src/lib/group.ts +1 -0
- package/src/lib/index-store.ts +15 -9
- package/src/lib/links.ts +15 -9
- package/src/lib/lookup.ts +10 -8
- package/src/lib/panel-outline.ts +1 -0
- package/src/lib/panel.ts +3 -2
- package/src/lib/preview.ts +12 -8
- package/src/lib/recipe-slots.ts +1 -0
- package/src/lib/search.ts +1 -0
- package/src/lib/sidebar.ts +1 -0
- package/src/lib/used-items.ts +1 -0
- package/src/locales/en_us.json +170 -226
- package/src/locales/ru_ru.json +170 -226
- package/src/locales.ts +1 -0
- package/src/types/alloy.ts +1 -0
- package/src/{hooks/calc/flow/props.ts → types/calc-flow-props.ts} +18 -14
- package/src/types/calc.ts +41 -44
- package/src/types/forge.ts +1 -0
- package/src/types/index.ts +4 -2
- package/styles/calc.css +308 -47
- package/src/components/cards/fit-card.tsx +0 -26
- package/src/components/views/calc/target.tsx +0 -271
- package/src/hooks/calc/flow/engage.ts +0 -89
- package/src/hooks/calc/flow/handlers.ts +0 -115
- package/src/hooks/calc/flow/layout.ts +0 -149
- package/src/hooks/calc/flow/pickers.ts +0 -50
- package/src/hooks/calc/use-calc-fill.ts +0 -91
- package/src/hooks/calc/use-density.ts +0 -28
- package/src/hooks/calc/use-derived.ts +0 -258
- package/src/hooks/calc/use-direction.ts +0 -23
- package/src/hooks/calc/use-edge-style.ts +0 -40
- package/src/hooks/calc/use-fill.ts +0 -213
- package/src/lib/alloy/alloy.test.ts +0 -186
- package/src/lib/calc/tree/build.ts +0 -196
- package/src/lib/calc/tree/context.ts +0 -42
- package/src/lib/calc/tree/goal.ts +0 -64
- package/src/lib/calc/tree/ideal.ts +0 -36
- package/src/lib/calc/tree/pick.ts +0 -68
- package/src/lib/calc/tree/tree.test.ts +0 -169
- package/src/lib/calc/worker/fill.ts +0 -123
- package/src/lib/client-index/cache.ts +0 -13
- package/src/lib/forge/forge.test.ts +0 -123
package/src/lib/calc/resolve.ts
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
2
|
import type { Recipe } from '@terrafirmagreg-team/recipe/lib/data/dto';
|
|
3
3
|
import { loadClientRecipe } from '@terrafirmagreg-team/recipe/lib/client/recipe';
|
|
4
|
-
import { resolveClientTag } from '@terrafirmagreg-team/
|
|
4
|
+
import { resolveClientTag } from '@terrafirmagreg-team/dump/lib/dump/client/tags';
|
|
5
5
|
import { recipesForOutput } from '@terrafirmagreg-team/recipe/lib/lookup';
|
|
6
|
-
import {
|
|
7
|
-
calcAutoPick,
|
|
8
|
-
recipeDivisor,
|
|
9
|
-
toCalcRecipe,
|
|
10
|
-
type AutoPickResult,
|
|
11
|
-
type BuildContext,
|
|
12
|
-
} from './tree';
|
|
6
|
+
import { recipeDivisor, toCalcRecipe } from './tree';
|
|
13
7
|
import type { RecipeCompactIndex, RecipeCompactRow } from '@terrafirmagreg-team/recipe/types';
|
|
14
8
|
import type {
|
|
15
9
|
CalcKind,
|
|
16
|
-
CalcNode,
|
|
17
10
|
CalcRecipe,
|
|
18
11
|
CalcRecipeSummary,
|
|
19
12
|
TagResolution,
|
|
@@ -141,26 +134,6 @@ export function createSummaries(
|
|
|
141
134
|
};
|
|
142
135
|
}
|
|
143
136
|
|
|
144
|
-
export type PickContext = Pick<
|
|
145
|
-
BuildContext,
|
|
146
|
-
'recipeMap' | 'getRecipesForItem' | 'recipesResolved' | 'deadIds'
|
|
147
|
-
>;
|
|
148
|
-
|
|
149
|
-
export function pickAutoRecipe(
|
|
150
|
-
node: CalcNode,
|
|
151
|
-
selections: Record<string, string>,
|
|
152
|
-
ancestors: ReadonlySet<string>,
|
|
153
|
-
ctx: PickContext,
|
|
154
|
-
): AutoPickResult {
|
|
155
|
-
const chosen = node.path
|
|
156
|
-
? (selections[node.path] ?? selections[node.materialId])
|
|
157
|
-
: selections[node.materialId];
|
|
158
|
-
if (chosen && node.availableRecipes.some((row) => row.recipeId === chosen)) {
|
|
159
|
-
return { pick: chosen, wantRecipes: [], wantOut: [] };
|
|
160
|
-
}
|
|
161
|
-
return calcAutoPick(node.availableRecipes, node.materialId, ancestors, ctx);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
137
|
export type Wanted = {
|
|
165
138
|
recipes: Set<string>;
|
|
166
139
|
outs: Set<string>;
|
|
@@ -175,7 +148,7 @@ export type FetchedSlices = {
|
|
|
175
148
|
};
|
|
176
149
|
|
|
177
150
|
export async function fetchSlices(
|
|
178
|
-
dataset:
|
|
151
|
+
dataset: DatasetRef,
|
|
179
152
|
catalog: RecipeCompactIndex,
|
|
180
153
|
wanted: Wanted,
|
|
181
154
|
): Promise<FetchedSlices> {
|
|
@@ -208,13 +181,7 @@ export async function fetchSlices(
|
|
|
208
181
|
dead.add(id);
|
|
209
182
|
continue;
|
|
210
183
|
}
|
|
211
|
-
|
|
212
|
-
(row) => row.id === body.category,
|
|
213
|
-
);
|
|
214
|
-
const hydrated = category?.layout
|
|
215
|
-
? hydrateRecipe(body, category.layout)
|
|
216
|
-
: body;
|
|
217
|
-
recipes.set(id, { calc: toCalcRecipe(hydrated), body: hydrated });
|
|
184
|
+
recipes.set(id, { calc: toCalcRecipe(body), body });
|
|
218
185
|
}
|
|
219
186
|
const outs = new Map(outRows);
|
|
220
187
|
const tags = new Map<string, TagResolution>(
|
|
@@ -228,3 +195,4 @@ export async function fetchSlices(
|
|
|
228
195
|
);
|
|
229
196
|
return { recipes, outs, tags, dead };
|
|
230
197
|
}
|
|
198
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Node } from '@terrafirmagreg-team/ui/lib/canvas/xyflow';
|
|
2
2
|
import type { FlowPoint } from '@terrafirmagreg-team/ui/types/canvas';
|
|
3
|
-
import
|
|
3
|
+
import { portAnchorPoint } from '@terrafirmagreg-team/recipe/lib/calc/ports';
|
|
4
4
|
|
|
5
5
|
export const CELL = 8;
|
|
6
6
|
|
|
@@ -9,6 +9,8 @@ export const PAD = 6;
|
|
|
9
9
|
export type RouteNode = Pick<Node, 'id' | 'position'> & {
|
|
10
10
|
width?: number;
|
|
11
11
|
height?: number;
|
|
12
|
+
inputs?: number;
|
|
13
|
+
outputs?: number;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export type Rect = { x: number; y: number; w: number; h: number };
|
|
@@ -20,26 +22,17 @@ export const toRect = (node: RouteNode): Rect => ({
|
|
|
20
22
|
h: node.height ?? 0,
|
|
21
23
|
});
|
|
22
24
|
|
|
23
|
-
export const sameRect = (a: Rect, b: Rect) =>
|
|
24
|
-
a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
|
|
25
|
-
|
|
26
25
|
export function portAnchor(
|
|
27
26
|
node: RouteNode,
|
|
28
27
|
side: 'src' | 'tgt',
|
|
29
|
-
|
|
28
|
+
index = 0,
|
|
30
29
|
): FlowPoint {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
x: node.position.x,
|
|
41
|
-
y: node.position.y + (side === 'src' ? -h / 2 : h / 2),
|
|
42
|
-
};
|
|
30
|
+
return portAnchorPoint(
|
|
31
|
+
node,
|
|
32
|
+
side,
|
|
33
|
+
index,
|
|
34
|
+
side === 'tgt' ? (node.inputs ?? 1) : (node.outputs ?? 1),
|
|
35
|
+
);
|
|
43
36
|
}
|
|
44
37
|
|
|
45
38
|
export const cellKey = (x: number, y: number) => (y + 32768) * 65536 + (x + 32768);
|
|
@@ -51,12 +44,8 @@ export function zRoute(
|
|
|
51
44
|
b: FlowPoint,
|
|
52
45
|
sa: FlowPoint,
|
|
53
46
|
sb: FlowPoint,
|
|
54
|
-
direction: CalcDirection,
|
|
55
47
|
): FlowPoint[] {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return [a, sa, { x: mid, y: sa.y }, { x: mid, y: sb.y }, sb, b];
|
|
59
|
-
}
|
|
60
|
-
const mid = (sa.y + sb.y) / 2;
|
|
61
|
-
return [a, sa, { x: sa.x, y: mid }, { x: sb.x, y: mid }, sb, b];
|
|
48
|
+
const mid = (sa.x + sb.x) / 2;
|
|
49
|
+
return [a, sa, { x: mid, y: sa.y }, { x: mid, y: sb.y }, sb, b];
|
|
62
50
|
}
|
|
51
|
+
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { FlowPoint } from '@terrafirmagreg-team/ui/types/canvas';
|
|
2
|
-
import type { CalcDirection } from '@terrafirmagreg-team/recipe/lib/calc/flow';
|
|
3
2
|
import { CELL } from './geometry';
|
|
4
3
|
|
|
5
4
|
export function simplifyCells(
|
|
@@ -68,8 +67,8 @@ export function cellsToPolyline(
|
|
|
68
67
|
return px;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
export function orthoPoints(points: FlowPoint[]
|
|
72
|
-
let prevAxis: 'h' | 'v' =
|
|
70
|
+
export function orthoPoints(points: FlowPoint[]): FlowPoint[] {
|
|
71
|
+
let prevAxis: 'h' | 'v' = 'h';
|
|
73
72
|
const out: FlowPoint[] = [points[0]!];
|
|
74
73
|
for (let i = 1; i < points.length; i += 1) {
|
|
75
74
|
const prev = out[out.length - 1]!;
|
|
@@ -101,3 +100,4 @@ export function orthoPoints(points: FlowPoint[], direction: CalcDirection): Flow
|
|
|
101
100
|
if (out.length > 1) clean.push(out[out.length - 1]!);
|
|
102
101
|
return clean;
|
|
103
102
|
}
|
|
103
|
+
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import PF from 'pathfinding';
|
|
2
2
|
import type { Edge } from '@terrafirmagreg-team/ui/lib/canvas/xyflow';
|
|
3
3
|
import type { FlowPoint } from '@terrafirmagreg-team/ui/types/canvas';
|
|
4
|
-
import type { CalcDirection } from '@terrafirmagreg-team/recipe/lib/calc/flow';
|
|
5
4
|
import { createRectBuckets } from './buckets';
|
|
6
5
|
import {
|
|
7
6
|
CELL,
|
|
@@ -10,7 +9,6 @@ import {
|
|
|
10
9
|
keyX,
|
|
11
10
|
keyY,
|
|
12
11
|
portAnchor,
|
|
13
|
-
sameRect,
|
|
14
12
|
toRect,
|
|
15
13
|
zRoute,
|
|
16
14
|
type Rect,
|
|
@@ -20,30 +18,144 @@ import { cellsToPolyline, orthoPoints, simplifyCells } from './polyline';
|
|
|
20
18
|
|
|
21
19
|
const WINDOW = 160;
|
|
22
20
|
|
|
23
|
-
const MAX_CELLS =
|
|
21
|
+
const MAX_CELLS = 200_000;
|
|
24
22
|
|
|
25
|
-
const STUB =
|
|
23
|
+
const STUB = 40;
|
|
24
|
+
|
|
25
|
+
function stubLength(
|
|
26
|
+
from: FlowPoint,
|
|
27
|
+
dir: 1 | -1,
|
|
28
|
+
skip: Rect[],
|
|
29
|
+
clear: (x0: number, y0: number, x1: number, y1: number) => Iterable<Rect>,
|
|
30
|
+
): number {
|
|
31
|
+
let max = STUB;
|
|
32
|
+
const end = from.x + dir * STUB;
|
|
33
|
+
const x0 = Math.min(from.x, end) - 1;
|
|
34
|
+
const x1 = Math.max(from.x, end) + 1;
|
|
35
|
+
for (const rect of clear(x0, from.y - 1, x1, from.y + 1)) {
|
|
36
|
+
if (skip.includes(rect)) continue;
|
|
37
|
+
if (from.y < rect.y - PAD || from.y > rect.y + rect.h + PAD) continue;
|
|
38
|
+
const edge = dir === 1 ? rect.x - PAD : rect.x + rect.w + PAD;
|
|
39
|
+
const room = (edge - from.x) * dir;
|
|
40
|
+
if (room < max) max = Math.max(0, room);
|
|
41
|
+
}
|
|
42
|
+
return max;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function growWindow(
|
|
46
|
+
bounds: { x0: number; y0: number; x1: number; y1: number },
|
|
47
|
+
query: (x0: number, y0: number, x1: number, y1: number) => Iterable<Rect>,
|
|
48
|
+
pad: number,
|
|
49
|
+
): void {
|
|
50
|
+
for (let i = 0; i < 4; i += 1) {
|
|
51
|
+
let grew = false;
|
|
52
|
+
for (const rect of query(bounds.x0, bounds.y0, bounds.x1, bounds.y1)) {
|
|
53
|
+
const rx0 = rect.x - pad;
|
|
54
|
+
const ry0 = rect.y - pad;
|
|
55
|
+
const rx1 = rect.x + rect.w + pad;
|
|
56
|
+
const ry1 = rect.y + rect.h + pad;
|
|
57
|
+
if (rx0 < bounds.x0) { bounds.x0 = rx0; grew = true; }
|
|
58
|
+
if (ry0 < bounds.y0) { bounds.y0 = ry0; grew = true; }
|
|
59
|
+
if (rx1 > bounds.x1) { bounds.x1 = rx1; grew = true; }
|
|
60
|
+
if (ry1 > bounds.y1) { bounds.y1 = ry1; grew = true; }
|
|
61
|
+
}
|
|
62
|
+
if (!grew) break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function detourRoute(
|
|
67
|
+
a: FlowPoint,
|
|
68
|
+
b: FlowPoint,
|
|
69
|
+
sa: FlowPoint,
|
|
70
|
+
sb: FlowPoint,
|
|
71
|
+
skip: Rect[],
|
|
72
|
+
query: (x0: number, y0: number, x1: number, y1: number) => Iterable<Rect>,
|
|
73
|
+
): FlowPoint[] {
|
|
74
|
+
const bounds = {
|
|
75
|
+
x0: Math.min(sa.x, sb.x),
|
|
76
|
+
y0: Math.min(sa.y, sb.y),
|
|
77
|
+
x1: Math.max(sa.x, sb.x),
|
|
78
|
+
y1: Math.max(sa.y, sb.y),
|
|
79
|
+
};
|
|
80
|
+
growWindow(bounds, query, PAD);
|
|
81
|
+
const laneAt = (off: number): FlowPoint[][] => [
|
|
82
|
+
[sa, { x: bounds.x1 + off, y: sa.y }, { x: bounds.x1 + off, y: sb.y }, sb],
|
|
83
|
+
[sa, { x: bounds.x0 - off, y: sa.y }, { x: bounds.x0 - off, y: sb.y }, sb],
|
|
84
|
+
[sa, { x: sa.x, y: bounds.y0 - off }, { x: sb.x, y: bounds.y0 - off }, sb],
|
|
85
|
+
[sa, { x: sa.x, y: bounds.y1 + off }, { x: sb.x, y: bounds.y1 + off }, sb],
|
|
86
|
+
];
|
|
87
|
+
const clearLane = (lane: FlowPoint[]): number | null => {
|
|
88
|
+
let len = 0;
|
|
89
|
+
for (let i = 0; i + 1 < lane.length; i += 1) {
|
|
90
|
+
const p = lane[i]!;
|
|
91
|
+
const q = lane[i + 1]!;
|
|
92
|
+
len += Math.abs(q.x - p.x) + Math.abs(q.y - p.y);
|
|
93
|
+
const sx0 = Math.min(p.x, q.x) - 1;
|
|
94
|
+
const sy0 = Math.min(p.y, q.y) - 1;
|
|
95
|
+
const sx1 = Math.max(p.x, q.x) + 1;
|
|
96
|
+
const sy1 = Math.max(p.y, q.y) + 1;
|
|
97
|
+
for (const rect of query(sx0, sy0, sx1, sy1)) {
|
|
98
|
+
if (skip.includes(rect)) continue;
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return len;
|
|
103
|
+
};
|
|
104
|
+
for (let ring = 0; ring < 6; ring += 1) {
|
|
105
|
+
const off = CELL * (ring === 0 ? 1 : 2 ** (ring + 1));
|
|
106
|
+
let best: FlowPoint[] | null = null;
|
|
107
|
+
let bestLen = Infinity;
|
|
108
|
+
for (const lane of laneAt(off)) {
|
|
109
|
+
const len = clearLane(lane);
|
|
110
|
+
if (len !== null && len < bestLen) {
|
|
111
|
+
bestLen = len;
|
|
112
|
+
best = lane;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (best) return best;
|
|
116
|
+
growWindow(bounds, query, PAD);
|
|
117
|
+
}
|
|
118
|
+
return zRoute(a, b, sa, sb).slice(1, -1);
|
|
119
|
+
}
|
|
26
120
|
|
|
27
121
|
type Committed = { keys: number[]; x0: number; y0: number; x1: number; y1: number };
|
|
28
122
|
|
|
29
|
-
type
|
|
123
|
+
type RouteEdge = Pick<Edge, 'id' | 'source' | 'target'> & {
|
|
124
|
+
sourceHandle?: string | null;
|
|
125
|
+
targetHandle?: string | null;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
type EdgeState = {
|
|
129
|
+
points: FlowPoint[];
|
|
130
|
+
a: FlowPoint;
|
|
131
|
+
b: FlowPoint;
|
|
132
|
+
committed?: Committed;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const samePoint = (a: FlowPoint, b: FlowPoint) => a.x === b.x && a.y === b.y;
|
|
136
|
+
|
|
137
|
+
const targetInput = (edge: RouteEdge) => {
|
|
138
|
+
const handle = edge.targetHandle;
|
|
139
|
+
const n = handle?.startsWith('in-') ? Number(handle.slice(3)) : 0;
|
|
140
|
+
return Number.isFinite(n) ? n : 0;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const sourceOut = (edge: RouteEdge) => {
|
|
144
|
+
const n = /^out-(\d+)$/.exec(edge.sourceHandle ?? '')?.[1];
|
|
145
|
+
return n != null && Number.isFinite(Number(n)) ? Number(n) : 0;
|
|
146
|
+
};
|
|
30
147
|
|
|
31
148
|
export function createCircuitRouter(): (
|
|
32
149
|
nodes: RouteNode[],
|
|
33
|
-
edges:
|
|
34
|
-
direction: CalcDirection,
|
|
150
|
+
edges: RouteEdge[],
|
|
35
151
|
) => Map<string, FlowPoint[]> {
|
|
36
|
-
let prevDir: CalcDirection | null = null;
|
|
37
|
-
let prevRects = new Map<string, Rect>();
|
|
38
152
|
let prevEdges = new Map<string, EdgeState>();
|
|
39
153
|
|
|
40
|
-
return (nodes, edges
|
|
154
|
+
return (nodes, edges) => {
|
|
41
155
|
const out = new Map<string, FlowPoint[]>();
|
|
42
156
|
const nextEdges = new Map<string, EdgeState>();
|
|
43
157
|
if (!edges.length) {
|
|
44
|
-
prevDir = direction;
|
|
45
158
|
prevEdges = nextEdges;
|
|
46
|
-
prevRects = new Map(nodes.map((n) => [n.id, toRect(n)]));
|
|
47
159
|
return out;
|
|
48
160
|
}
|
|
49
161
|
|
|
@@ -51,11 +163,10 @@ export function createCircuitRouter(): (
|
|
|
51
163
|
const rects = new Map(nodes.map((node) => [node.id, toRect(node)]));
|
|
52
164
|
const rectsInWindow = createRectBuckets(rects.values());
|
|
53
165
|
|
|
54
|
-
const rebuild = prevDir !== direction;
|
|
55
166
|
const committed: Committed[] = [];
|
|
56
167
|
const used = new Set<number>();
|
|
57
168
|
const pending: {
|
|
58
|
-
edge:
|
|
169
|
+
edge: RouteEdge;
|
|
59
170
|
s: RouteNode;
|
|
60
171
|
t: RouteNode;
|
|
61
172
|
a: FlowPoint;
|
|
@@ -66,20 +177,11 @@ export function createCircuitRouter(): (
|
|
|
66
177
|
for (const edge of edges) {
|
|
67
178
|
const s = byId.get(edge.source);
|
|
68
179
|
const t = byId.get(edge.target);
|
|
69
|
-
|
|
70
|
-
const
|
|
180
|
+
if (!s || !t) continue;
|
|
181
|
+
const a = portAnchor(s, 'src', sourceOut(edge));
|
|
182
|
+
const b = portAnchor(t, 'tgt', targetInput(edge));
|
|
71
183
|
const prev = prevEdges.get(edge.id);
|
|
72
|
-
if (
|
|
73
|
-
!rebuild &&
|
|
74
|
-
prev &&
|
|
75
|
-
sRect &&
|
|
76
|
-
tRect &&
|
|
77
|
-
prevRects.get(edge.source) &&
|
|
78
|
-
prevRects.get(edge.target) &&
|
|
79
|
-
sameRect(prevRects.get(edge.source)!, sRect) &&
|
|
80
|
-
sameRect(prevRects.get(edge.target)!, tRect)
|
|
81
|
-
) {
|
|
82
|
-
|
|
184
|
+
if (prev && samePoint(prev.a, a) && samePoint(prev.b, b)) {
|
|
83
185
|
out.set(edge.id, prev.points);
|
|
84
186
|
nextEdges.set(edge.id, prev);
|
|
85
187
|
if (prev.committed) {
|
|
@@ -88,9 +190,6 @@ export function createCircuitRouter(): (
|
|
|
88
190
|
}
|
|
89
191
|
continue;
|
|
90
192
|
}
|
|
91
|
-
if (!s || !t || !sRect || !tRect) continue;
|
|
92
|
-
const a = portAnchor(s, 'src', direction);
|
|
93
|
-
const b = portAnchor(t, 'tgt', direction);
|
|
94
193
|
pending.push({
|
|
95
194
|
edge,
|
|
96
195
|
s,
|
|
@@ -109,28 +208,34 @@ export function createCircuitRouter(): (
|
|
|
109
208
|
|
|
110
209
|
for (const job of pending) {
|
|
111
210
|
const { a, b } = job;
|
|
112
|
-
const horizontal = direction === 'horizontal';
|
|
113
|
-
|
|
114
|
-
const gap = horizontal ? a.x - b.x : a.y - b.y;
|
|
115
|
-
if (gap > 0 && (horizontal ? a.y === b.y : a.x === b.x)) {
|
|
116
211
|
|
|
212
|
+
const gap = b.x - a.x;
|
|
213
|
+
if (gap > 0 && a.y === b.y) {
|
|
117
214
|
const points = [a, b];
|
|
118
215
|
out.set(job.edge.id, points);
|
|
119
|
-
nextEdges.set(job.edge.id, { points });
|
|
216
|
+
nextEdges.set(job.edge.id, { points, a, b });
|
|
120
217
|
continue;
|
|
121
218
|
}
|
|
122
219
|
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const sb =
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
220
|
+
const sa = {
|
|
221
|
+
x: a.x + stubLength(a, 1, [rects.get(job.s.id)!], rectsInWindow),
|
|
222
|
+
y: a.y,
|
|
223
|
+
};
|
|
224
|
+
const sb = {
|
|
225
|
+
x: b.x - stubLength(b, -1, [rects.get(job.t.id)!], rectsInWindow),
|
|
226
|
+
y: b.y,
|
|
227
|
+
};
|
|
228
|
+
const bounds = {
|
|
229
|
+
x0: Math.min(sa.x, sb.x) - WINDOW,
|
|
230
|
+
y0: Math.min(sa.y, sb.y) - WINDOW,
|
|
231
|
+
x1: Math.max(sa.x, sb.x) + WINDOW,
|
|
232
|
+
y1: Math.max(sa.y, sb.y) + WINDOW,
|
|
233
|
+
};
|
|
234
|
+
growWindow(bounds, rectsInWindow, PAD + CELL);
|
|
235
|
+
const wx0 = bounds.x0;
|
|
236
|
+
const wy0 = bounds.y0;
|
|
237
|
+
const wx1 = bounds.x1;
|
|
238
|
+
const wy1 = bounds.y1;
|
|
134
239
|
const ox = Math.floor(wx0 / CELL);
|
|
135
240
|
const oy = Math.floor(wy0 / CELL);
|
|
136
241
|
const gw = Math.ceil(wx1 / CELL) - ox + 1;
|
|
@@ -165,10 +270,11 @@ export function createCircuitRouter(): (
|
|
|
165
270
|
x: Math.min(gw - 1, Math.max(0, Math.round(sb.x / CELL) - ox)),
|
|
166
271
|
y: Math.min(gh - 1, Math.max(0, Math.round(sb.y / CELL) - oy)),
|
|
167
272
|
};
|
|
168
|
-
|
|
273
|
+
let best: number[][] | null = null;
|
|
274
|
+
let bestGrid: InstanceType<typeof PF.Grid> | null = null;
|
|
275
|
+
for (let pass = 0; pass < 2; pass += 1) {
|
|
169
276
|
const grid = base.clone();
|
|
170
277
|
if (pass === 0) {
|
|
171
|
-
|
|
172
278
|
for (const c of committed) {
|
|
173
279
|
if (
|
|
174
280
|
c.x1 < ox ||
|
|
@@ -196,17 +302,36 @@ export function createCircuitRouter(): (
|
|
|
196
302
|
tCell.y,
|
|
197
303
|
grid,
|
|
198
304
|
);
|
|
199
|
-
if (path.length)
|
|
200
|
-
|
|
201
|
-
|
|
305
|
+
if (!path.length) continue;
|
|
306
|
+
const mapped = path.map(([cx, cy]) => [cx + ox, cy + oy]);
|
|
307
|
+
if (pass === 1) {
|
|
308
|
+
if (best && best.length <= mapped.length * 1.34) break;
|
|
309
|
+
best = mapped;
|
|
310
|
+
bestGrid = grid;
|
|
311
|
+
break;
|
|
202
312
|
}
|
|
313
|
+
best = mapped;
|
|
314
|
+
bestGrid = grid;
|
|
203
315
|
}
|
|
316
|
+
cells = best;
|
|
317
|
+
passGrid = bestGrid;
|
|
204
318
|
}
|
|
205
319
|
|
|
206
320
|
if (!cells || !passGrid) {
|
|
207
|
-
const points = orthoPoints(
|
|
321
|
+
const points = orthoPoints([
|
|
322
|
+
a,
|
|
323
|
+
...detourRoute(
|
|
324
|
+
a,
|
|
325
|
+
b,
|
|
326
|
+
sa,
|
|
327
|
+
sb,
|
|
328
|
+
[rects.get(job.s.id)!, rects.get(job.t.id)!],
|
|
329
|
+
rectsInWindow,
|
|
330
|
+
),
|
|
331
|
+
b,
|
|
332
|
+
]);
|
|
208
333
|
out.set(job.edge.id, points);
|
|
209
|
-
nextEdges.set(job.edge.id, { points });
|
|
334
|
+
nextEdges.set(job.edge.id, { points, a, b });
|
|
210
335
|
continue;
|
|
211
336
|
}
|
|
212
337
|
|
|
@@ -221,7 +346,7 @@ export function createCircuitRouter(): (
|
|
|
221
346
|
g.isWalkableAt(x - ox, y - oy),
|
|
222
347
|
);
|
|
223
348
|
const polyline = cellsToPolyline(simple, sa, sb);
|
|
224
|
-
const points = orthoPoints([a, sa, ...polyline, sb, b]
|
|
349
|
+
const points = orthoPoints([a, sa, ...polyline, sb, b]);
|
|
225
350
|
out.set(job.edge.id, points);
|
|
226
351
|
|
|
227
352
|
const keys: number[] = [];
|
|
@@ -259,12 +384,11 @@ export function createCircuitRouter(): (
|
|
|
259
384
|
y1: cy1 === -Infinity ? 0 : cy1,
|
|
260
385
|
};
|
|
261
386
|
committed.push(entry);
|
|
262
|
-
nextEdges.set(job.edge.id, { points, committed: entry });
|
|
387
|
+
nextEdges.set(job.edge.id, { points, a: job.a, b: job.b, committed: entry });
|
|
263
388
|
}
|
|
264
389
|
|
|
265
|
-
prevDir = direction;
|
|
266
|
-
prevRects = rects;
|
|
267
390
|
prevEdges = nextEdges;
|
|
268
391
|
return out;
|
|
269
392
|
};
|
|
270
393
|
}
|
|
394
|
+
|