@seatlayer/core 0.29.0 → 0.30.0
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/README.md +102 -22
- package/dist/chunk-FGS67GT3.js +1252 -0
- package/dist/chunk-FGS67GT3.js.map +1 -0
- package/dist/index.cjs +572 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +269 -909
- package/dist/index.js.map +1 -1
- package/dist/{types-B-tpUFqz.d.cts → types-CG2pEDoI.d.cts} +56 -0
- package/dist/{types-B-tpUFqz.d.ts → types-CG2pEDoI.d.ts} +56 -0
- package/dist/view3d/index.cjs +2315 -99
- package/dist/view3d/index.cjs.map +1 -1
- package/dist/view3d/index.d.cts +236 -27
- package/dist/view3d/index.d.ts +236 -27
- package/dist/view3d/index.js +1912 -98
- package/dist/view3d/index.js.map +1 -1
- package/package.json +5 -3
- package/dist/chunk-5MLADB2N.js +0 -53
- package/dist/chunk-5MLADB2N.js.map +0 -1
|
@@ -0,0 +1,1252 @@
|
|
|
1
|
+
// src/core/types.ts
|
|
2
|
+
var ACCESSIBILITY_TYPES = [
|
|
3
|
+
{ key: "wheelchair", label: "Wheelchair space", short: "Wheelchair", icon: "\u267F" },
|
|
4
|
+
{ key: "companion", label: "Companion seat", short: "Companion", icon: "\u{1F9D1}\u200D\u{1F91D}\u200D\u{1F9D1}" },
|
|
5
|
+
{ key: "semi-ambulatory", label: "Semi-ambulatory (limited mobility)", short: "Limited mobility", icon: "\u{1F9AF}" },
|
|
6
|
+
{ key: "hearing", label: "Assistive listening", short: "Hearing", icon: "\u{1F9BB}" },
|
|
7
|
+
{ key: "cart", label: "CART live-caption view", short: "CART captions", icon: "CC" },
|
|
8
|
+
{ key: "sign-language", label: "Sign-language view", short: "Sign language", icon: "\u{1F91F}" },
|
|
9
|
+
{ key: "plus-size", label: "Plus-size seat", short: "Plus-size", icon: "\u{1F4BA}" },
|
|
10
|
+
{ key: "lift-armrest", label: "Lift-up armrest", short: "Lift armrest", icon: "\u2195\uFE0F" }
|
|
11
|
+
];
|
|
12
|
+
var ACCESSIBILITY_LABEL = new Map(ACCESSIBILITY_TYPES.map((a) => [a.key, a]));
|
|
13
|
+
function accessibilityMeta(key) {
|
|
14
|
+
return ACCESSIBILITY_LABEL.get(key);
|
|
15
|
+
}
|
|
16
|
+
var ACCESSIBILITY_RING_COLOR = {
|
|
17
|
+
wheelchair: "#3b82f6",
|
|
18
|
+
companion: "#8b5cf6",
|
|
19
|
+
"semi-ambulatory": "#0ea5e9",
|
|
20
|
+
hearing: "#14b8a6",
|
|
21
|
+
cart: "#7c3aed",
|
|
22
|
+
"sign-language": "#f59e0b",
|
|
23
|
+
"plus-size": "#ec4899",
|
|
24
|
+
"lift-armrest": "#22c55e"
|
|
25
|
+
};
|
|
26
|
+
function accessibilityRingColor(types) {
|
|
27
|
+
const primary = types?.[0];
|
|
28
|
+
return primary && ACCESSIBILITY_RING_COLOR[primary] || "#3b82f6";
|
|
29
|
+
}
|
|
30
|
+
var LABEL_STYLE_MIN_SIZE = 8;
|
|
31
|
+
var LABEL_STYLE_MAX_SIZE = 24;
|
|
32
|
+
var SURROUNDINGS_SHAPE_ROLES = [
|
|
33
|
+
"reference-focal",
|
|
34
|
+
"bar",
|
|
35
|
+
"entrance",
|
|
36
|
+
"exit",
|
|
37
|
+
"restroom",
|
|
38
|
+
"screen",
|
|
39
|
+
"sound",
|
|
40
|
+
"concession",
|
|
41
|
+
"coat",
|
|
42
|
+
"wall"
|
|
43
|
+
];
|
|
44
|
+
var SURROUNDINGS_SHAPE_ROLE_SET = new Set(SURROUNDINGS_SHAPE_ROLES);
|
|
45
|
+
function layerOf(obj) {
|
|
46
|
+
switch (obj.type) {
|
|
47
|
+
case "row":
|
|
48
|
+
case "table":
|
|
49
|
+
case "gaArea":
|
|
50
|
+
case "booth":
|
|
51
|
+
case "section":
|
|
52
|
+
return "interactive";
|
|
53
|
+
// Raster décor follows its explicit authored z-layer. Absence retains the
|
|
54
|
+
// legacy Background default; bitmap content is never guessed semantically.
|
|
55
|
+
case "decorImage":
|
|
56
|
+
return obj.layer === "foreground" ? "foreground" : "background";
|
|
57
|
+
// Source-backed focal geometry and curated venue landmarks help an author
|
|
58
|
+
// orient around the sellable plan. A stage and an ordinary authored shape
|
|
59
|
+
// remain Background even when they carry an arbitrary/custom role.
|
|
60
|
+
case "shape":
|
|
61
|
+
return obj.role && SURROUNDINGS_SHAPE_ROLE_SET.has(obj.role) ? "surroundings" : "background";
|
|
62
|
+
// Free text — including semantic icon text — is background furniture.
|
|
63
|
+
case "text":
|
|
64
|
+
return "background";
|
|
65
|
+
default:
|
|
66
|
+
return "interactive";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
var CHART_STORAGE_KEY = "seatmap.chart";
|
|
70
|
+
|
|
71
|
+
// src/core/units.ts
|
|
72
|
+
var METRES_PER_CHART_UNIT = 0.55 / 24;
|
|
73
|
+
var CHART_UNITS_PER_METRE = 1 / METRES_PER_CHART_UNIT;
|
|
74
|
+
var LIFT_PER_STEP_WORLD = 58;
|
|
75
|
+
var TIER_HEIGHT_M = LIFT_PER_STEP_WORLD * METRES_PER_CHART_UNIT;
|
|
76
|
+
var SECTION_ELEVATION_TIER_MIN = 0;
|
|
77
|
+
var SECTION_ELEVATION_TIER_MAX = 3;
|
|
78
|
+
var SECTION_HEIGHT_MIN_M = 0;
|
|
79
|
+
var SECTION_HEIGHT_MAX_M = 120;
|
|
80
|
+
var SECTION_RAKE_MIN_DEG = 0;
|
|
81
|
+
var SECTION_RAKE_MAX_DEG = 45;
|
|
82
|
+
var LEGACY_SECTION_ELEVATION_TIER_MAX = 7;
|
|
83
|
+
var SEATED_EYE_HEIGHT_M = 1.2;
|
|
84
|
+
function finiteClamped(value, min, max, fallback) {
|
|
85
|
+
return Number.isFinite(value) ? Math.max(min, Math.min(max, value)) : fallback;
|
|
86
|
+
}
|
|
87
|
+
function sectionElevationTier(value) {
|
|
88
|
+
if (!Number.isFinite(value)) return SECTION_ELEVATION_TIER_MIN;
|
|
89
|
+
return Math.max(
|
|
90
|
+
SECTION_ELEVATION_TIER_MIN,
|
|
91
|
+
Math.min(SECTION_ELEVATION_TIER_MAX, Math.round(value))
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function compatibleAutomaticTier(value) {
|
|
95
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) return 0;
|
|
96
|
+
if (value <= LEGACY_SECTION_ELEVATION_TIER_MAX) return value;
|
|
97
|
+
return SECTION_ELEVATION_TIER_MAX;
|
|
98
|
+
}
|
|
99
|
+
function sectionGeometry(section, context = {}) {
|
|
100
|
+
const floorBaseHeightM = finiteClamped(
|
|
101
|
+
context.floorBaseHeightM,
|
|
102
|
+
SECTION_HEIGHT_MIN_M,
|
|
103
|
+
SECTION_HEIGHT_MAX_M,
|
|
104
|
+
0
|
|
105
|
+
);
|
|
106
|
+
const automaticHeight = Math.min(
|
|
107
|
+
SECTION_HEIGHT_MAX_M,
|
|
108
|
+
floorBaseHeightM + compatibleAutomaticTier(section.elevation) * TIER_HEIGHT_M
|
|
109
|
+
);
|
|
110
|
+
const height = section.height === void 0 ? automaticHeight : finiteClamped(section.height, SECTION_HEIGHT_MIN_M, SECTION_HEIGHT_MAX_M, automaticHeight);
|
|
111
|
+
const rake = finiteClamped(section.rake, SECTION_RAKE_MIN_DEG, SECTION_RAKE_MAX_DEG, 0);
|
|
112
|
+
return { height, rake };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/core/complexGeometry.ts
|
|
116
|
+
function cubicPoint(path, t) {
|
|
117
|
+
const u = 1 - t;
|
|
118
|
+
const a = u * u * u;
|
|
119
|
+
const b = 3 * u * u * t;
|
|
120
|
+
const c = 3 * u * t * t;
|
|
121
|
+
const d = t * t * t;
|
|
122
|
+
return {
|
|
123
|
+
x: a * path.start.x + b * path.control1.x + c * path.control2.x + d * path.end.x,
|
|
124
|
+
y: a * path.start.y + b * path.control1.y + c * path.control2.y + d * path.end.y
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function distributeAlongCubic(path, count, resolution = 192) {
|
|
128
|
+
if (!Number.isInteger(count) || count < 1) throw new Error("Path point count must be a positive integer");
|
|
129
|
+
if (count === 1) return [cubicPoint(path, 0.5)];
|
|
130
|
+
const samples = Array.from({ length: resolution + 1 }, (_, index) => cubicPoint(path, index / resolution));
|
|
131
|
+
const lengths = new Float64Array(samples.length);
|
|
132
|
+
for (let index = 1; index < samples.length; index += 1) {
|
|
133
|
+
const dx = samples[index].x - samples[index - 1].x;
|
|
134
|
+
const dy = samples[index].y - samples[index - 1].y;
|
|
135
|
+
lengths[index] = lengths[index - 1] + Math.hypot(dx, dy);
|
|
136
|
+
}
|
|
137
|
+
const total = lengths[lengths.length - 1];
|
|
138
|
+
if (total <= 1e-9) return Array.from({ length: count }, () => ({ ...path.start }));
|
|
139
|
+
const output = [];
|
|
140
|
+
let segment = 1;
|
|
141
|
+
for (let index = 0; index < count; index += 1) {
|
|
142
|
+
const target = total * index / (count - 1);
|
|
143
|
+
while (segment < lengths.length - 1 && lengths[segment] < target) segment += 1;
|
|
144
|
+
const before = lengths[segment - 1];
|
|
145
|
+
const after = lengths[segment];
|
|
146
|
+
const ratio = after === before ? 0 : (target - before) / (after - before);
|
|
147
|
+
output.push({
|
|
148
|
+
x: samples[segment - 1].x + (samples[segment].x - samples[segment - 1].x) * ratio,
|
|
149
|
+
y: samples[segment - 1].y + (samples[segment].y - samples[segment - 1].y) * ratio
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return output;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/core/sectionPath.ts
|
|
156
|
+
var TAU = Math.PI * 2;
|
|
157
|
+
function translateSectionOutlinePath(path, dx, dy) {
|
|
158
|
+
const translate = (point) => ({ x: point.x + dx, y: point.y + dy });
|
|
159
|
+
return transformSectionOutlinePath(path, translate);
|
|
160
|
+
}
|
|
161
|
+
function transformSectionOutlinePath(path, transform, radiusScale = 1, reflected = false) {
|
|
162
|
+
return {
|
|
163
|
+
...path,
|
|
164
|
+
start: transform(path.start),
|
|
165
|
+
segments: path.segments.map((segment) => segment.kind === "line" ? { ...segment, end: transform(segment.end) } : segment.kind === "arc" ? {
|
|
166
|
+
...segment,
|
|
167
|
+
center: transform(segment.center),
|
|
168
|
+
radius: segment.radius * Math.abs(radiusScale),
|
|
169
|
+
clockwise: reflected ? !segment.clockwise : segment.clockwise,
|
|
170
|
+
end: transform(segment.end)
|
|
171
|
+
} : {
|
|
172
|
+
...segment,
|
|
173
|
+
control1: transform(segment.control1),
|
|
174
|
+
control2: transform(segment.control2),
|
|
175
|
+
end: transform(segment.end)
|
|
176
|
+
})
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
var SAGITTA_TO_CONTROL = 4 / 3;
|
|
180
|
+
|
|
181
|
+
// src/core/labeling.ts
|
|
182
|
+
var FULL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
183
|
+
var LOWER_ALPHABET = "abcdefghijklmnopqrstuvwxyz";
|
|
184
|
+
function toBijectiveBase(index, alphabet) {
|
|
185
|
+
const base = alphabet.length;
|
|
186
|
+
let n = index + 1;
|
|
187
|
+
let out = "";
|
|
188
|
+
while (n > 0) {
|
|
189
|
+
n -= 1;
|
|
190
|
+
const rem = n % base;
|
|
191
|
+
out = alphabet[rem] + out;
|
|
192
|
+
n = Math.floor(n / base);
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
function toLetters(value, lower = false) {
|
|
197
|
+
const alphabet = lower ? LOWER_ALPHABET : FULL_ALPHABET;
|
|
198
|
+
return toBijectiveBase(Math.max(0, Math.floor(value) - 1), alphabet);
|
|
199
|
+
}
|
|
200
|
+
var ROMAN_TABLE = [
|
|
201
|
+
[1e3, "M"],
|
|
202
|
+
[900, "CM"],
|
|
203
|
+
[500, "D"],
|
|
204
|
+
[400, "CD"],
|
|
205
|
+
[100, "C"],
|
|
206
|
+
[90, "XC"],
|
|
207
|
+
[50, "L"],
|
|
208
|
+
[40, "XL"],
|
|
209
|
+
[10, "X"],
|
|
210
|
+
[9, "IX"],
|
|
211
|
+
[5, "V"],
|
|
212
|
+
[4, "IV"],
|
|
213
|
+
[1, "I"]
|
|
214
|
+
];
|
|
215
|
+
function toRoman(value) {
|
|
216
|
+
if (!Number.isFinite(value) || value <= 0) return String(value);
|
|
217
|
+
let remaining = Math.floor(value);
|
|
218
|
+
let out = "";
|
|
219
|
+
for (const [n, sym] of ROMAN_TABLE) {
|
|
220
|
+
while (remaining >= n) {
|
|
221
|
+
out += sym;
|
|
222
|
+
remaining -= n;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/core/venueStructure.ts
|
|
229
|
+
var BLOCK_LINK_RATIO = 1.8;
|
|
230
|
+
function probesOf(pts) {
|
|
231
|
+
const n = pts.length;
|
|
232
|
+
if (n <= 3) return [...pts];
|
|
233
|
+
return [pts[Math.floor(n * 0.25)], pts[Math.floor(n * 0.5)], pts[Math.floor(n * 0.75)]];
|
|
234
|
+
}
|
|
235
|
+
function distanceToPolyline(pts, x, y) {
|
|
236
|
+
if (!pts.length) return Infinity;
|
|
237
|
+
if (pts.length === 1) return Math.hypot(x - pts[0].x, y - pts[0].y);
|
|
238
|
+
let best = Infinity;
|
|
239
|
+
for (let i = 0; i + 1 < pts.length; i++) {
|
|
240
|
+
const a = pts[i], b = pts[i + 1];
|
|
241
|
+
const vx = b.x - a.x, vy = b.y - a.y;
|
|
242
|
+
const len2 = vx * vx + vy * vy;
|
|
243
|
+
let t = len2 > 1e-12 ? ((x - a.x) * vx + (y - a.y) * vy) / len2 : 0;
|
|
244
|
+
if (t < 0) t = 0;
|
|
245
|
+
else if (t > 1) t = 1;
|
|
246
|
+
const d = Math.hypot(x - (a.x + t * vx), y - (a.y + t * vy));
|
|
247
|
+
if (d < best) best = d;
|
|
248
|
+
}
|
|
249
|
+
return best;
|
|
250
|
+
}
|
|
251
|
+
function boundOf(pts) {
|
|
252
|
+
let cx = 0, cy = 0;
|
|
253
|
+
for (const p of pts) {
|
|
254
|
+
cx += p.x;
|
|
255
|
+
cy += p.y;
|
|
256
|
+
}
|
|
257
|
+
const n = pts.length || 1;
|
|
258
|
+
cx /= n;
|
|
259
|
+
cy /= n;
|
|
260
|
+
let r = 0;
|
|
261
|
+
for (const p of pts) {
|
|
262
|
+
const d = Math.hypot(p.x - cx, p.y - cy);
|
|
263
|
+
if (d > r) r = d;
|
|
264
|
+
}
|
|
265
|
+
return { cx, cy, r };
|
|
266
|
+
}
|
|
267
|
+
function boundGap(a, b) {
|
|
268
|
+
return Math.hypot(a.cx - b.cx, a.cy - b.cy) - a.r - b.r;
|
|
269
|
+
}
|
|
270
|
+
function rowGap(a, b) {
|
|
271
|
+
let best = Infinity;
|
|
272
|
+
for (const p of probesOf(a)) {
|
|
273
|
+
const d = distanceToPolyline(b, p.x, p.y);
|
|
274
|
+
if (d < best) best = d;
|
|
275
|
+
}
|
|
276
|
+
for (const p of probesOf(b)) {
|
|
277
|
+
const d = distanceToPolyline(a, p.x, p.y);
|
|
278
|
+
if (d < best) best = d;
|
|
279
|
+
}
|
|
280
|
+
return best;
|
|
281
|
+
}
|
|
282
|
+
function orderAlongRow(pts, indices) {
|
|
283
|
+
if (pts.length < 3) return { pts, seatIndices: indices };
|
|
284
|
+
let cx = 0, cy = 0;
|
|
285
|
+
for (const p of pts) {
|
|
286
|
+
cx += p.x;
|
|
287
|
+
cy += p.y;
|
|
288
|
+
}
|
|
289
|
+
cx /= pts.length;
|
|
290
|
+
cy /= pts.length;
|
|
291
|
+
let far = -1, ax = pts[0];
|
|
292
|
+
for (const p of pts) {
|
|
293
|
+
const d = Math.hypot(p.x - cx, p.y - cy);
|
|
294
|
+
if (d > far) {
|
|
295
|
+
far = d;
|
|
296
|
+
ax = p;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
let dx = ax.x - cx, dy = ax.y - cy;
|
|
300
|
+
const len = Math.hypot(dx, dy);
|
|
301
|
+
if (len > 1e-9) {
|
|
302
|
+
dx /= len;
|
|
303
|
+
dy /= len;
|
|
304
|
+
} else {
|
|
305
|
+
dx = 1;
|
|
306
|
+
dy = 0;
|
|
307
|
+
}
|
|
308
|
+
const order = pts.map((p, i) => ({ p, i, t: (p.x - cx) * dx + (p.y - cy) * dy })).sort((u, v) => u.t - v.t);
|
|
309
|
+
return { pts: order.map((o) => o.p), seatIndices: order.map((o) => indices[o.i]) };
|
|
310
|
+
}
|
|
311
|
+
function fitCircle(pts) {
|
|
312
|
+
const n = pts.length;
|
|
313
|
+
if (n < 3) return null;
|
|
314
|
+
let mx = 0, my = 0;
|
|
315
|
+
for (const p of pts) {
|
|
316
|
+
mx += p.x;
|
|
317
|
+
my += p.y;
|
|
318
|
+
}
|
|
319
|
+
mx /= n;
|
|
320
|
+
my /= n;
|
|
321
|
+
let suu = 0, suv = 0, svv = 0, suuu = 0, svvv = 0, suvv = 0, svuu = 0;
|
|
322
|
+
for (const p of pts) {
|
|
323
|
+
const u = p.x - mx, v = p.y - my;
|
|
324
|
+
suu += u * u;
|
|
325
|
+
svv += v * v;
|
|
326
|
+
suv += u * v;
|
|
327
|
+
suuu += u * u * u;
|
|
328
|
+
svvv += v * v * v;
|
|
329
|
+
suvv += u * v * v;
|
|
330
|
+
svuu += v * u * u;
|
|
331
|
+
}
|
|
332
|
+
const det = suu * svv - suv * suv;
|
|
333
|
+
if (Math.abs(det) < 1e-9) return null;
|
|
334
|
+
const b1 = (suuu + suvv) / 2;
|
|
335
|
+
const b2 = (svvv + svuu) / 2;
|
|
336
|
+
const uc = (b1 * svv - b2 * suv) / det;
|
|
337
|
+
const vc = (b2 * suu - b1 * suv) / det;
|
|
338
|
+
const cx = uc + mx, cy = vc + my;
|
|
339
|
+
if (!Number.isFinite(cx) || !Number.isFinite(cy)) return null;
|
|
340
|
+
return { cx, cy };
|
|
341
|
+
}
|
|
342
|
+
var CIRCLE_ROW_SPREAD_LIMIT = 0.15;
|
|
343
|
+
function fitBlockAxis(group) {
|
|
344
|
+
if (group.length < 2) return null;
|
|
345
|
+
const cents = group.map((r) => {
|
|
346
|
+
let x = 0, y = 0;
|
|
347
|
+
for (const p of r.pts) {
|
|
348
|
+
x += p.x;
|
|
349
|
+
y += p.y;
|
|
350
|
+
}
|
|
351
|
+
return { x: x / r.pts.length, y: y / r.pts.length };
|
|
352
|
+
});
|
|
353
|
+
let ox = 0, oy = 0;
|
|
354
|
+
for (const c of cents) {
|
|
355
|
+
ox += c.x;
|
|
356
|
+
oy += c.y;
|
|
357
|
+
}
|
|
358
|
+
ox /= cents.length;
|
|
359
|
+
oy /= cents.length;
|
|
360
|
+
let sxx = 0, sxy = 0, syy = 0;
|
|
361
|
+
for (const c of cents) {
|
|
362
|
+
const dx = c.x - ox, dy = c.y - oy;
|
|
363
|
+
sxx += dx * dx;
|
|
364
|
+
sxy += dx * dy;
|
|
365
|
+
syy += dy * dy;
|
|
366
|
+
}
|
|
367
|
+
const tr = sxx + syy;
|
|
368
|
+
const det = sxx * syy - sxy * sxy;
|
|
369
|
+
const lambda = tr / 2 + Math.sqrt(Math.max(0, tr * tr / 4 - det));
|
|
370
|
+
let ax, ay;
|
|
371
|
+
if (Math.abs(sxy) > 1e-12) {
|
|
372
|
+
ax = lambda - syy;
|
|
373
|
+
ay = sxy;
|
|
374
|
+
} else if (sxx >= syy) {
|
|
375
|
+
ax = 1;
|
|
376
|
+
ay = 0;
|
|
377
|
+
} else {
|
|
378
|
+
ax = 0;
|
|
379
|
+
ay = 1;
|
|
380
|
+
}
|
|
381
|
+
const len = Math.hypot(ax, ay);
|
|
382
|
+
if (!(len > 1e-12)) return null;
|
|
383
|
+
ax /= len;
|
|
384
|
+
ay /= len;
|
|
385
|
+
let lo = Infinity, hi = -Infinity;
|
|
386
|
+
const widths = [];
|
|
387
|
+
for (const r of group) {
|
|
388
|
+
let rlo = Infinity, rhi = -Infinity;
|
|
389
|
+
for (const p of r.pts) {
|
|
390
|
+
const t = p.x * ax + p.y * ay;
|
|
391
|
+
if (t < rlo) rlo = t;
|
|
392
|
+
if (t > rhi) rhi = t;
|
|
393
|
+
if (t < lo) lo = t;
|
|
394
|
+
if (t > hi) hi = t;
|
|
395
|
+
}
|
|
396
|
+
widths.push(rhi - rlo);
|
|
397
|
+
}
|
|
398
|
+
const range = hi - lo;
|
|
399
|
+
if (!(range > 1e-9)) return null;
|
|
400
|
+
widths.sort((a, b) => a - b);
|
|
401
|
+
const p90 = widths[Math.min(widths.length - 1, Math.floor(widths.length * 0.9))];
|
|
402
|
+
return p90 / range <= AXIS_ROW_WIDTH_LIMIT ? { x: ax, y: ay } : null;
|
|
403
|
+
}
|
|
404
|
+
var AXIS_ROW_WIDTH_LIMIT = 0.35;
|
|
405
|
+
function resolveSection(sectionId, seats, seatIndices, focal) {
|
|
406
|
+
const groups = /* @__PURE__ */ new Map();
|
|
407
|
+
for (const i of seatIndices) {
|
|
408
|
+
const s = seats[i];
|
|
409
|
+
const key = s.rowId || `__seat-${i}`;
|
|
410
|
+
let g = groups.get(key);
|
|
411
|
+
if (!g) {
|
|
412
|
+
g = { pts: [], idx: [] };
|
|
413
|
+
groups.set(key, g);
|
|
414
|
+
}
|
|
415
|
+
g.pts.push({ x: s.x, y: s.y });
|
|
416
|
+
g.idx.push(i);
|
|
417
|
+
}
|
|
418
|
+
if (!groups.size) return { sectionId, rows: [], blockCount: 0 };
|
|
419
|
+
const rows = [];
|
|
420
|
+
for (const [id, g] of groups) {
|
|
421
|
+
const ordered = orderAlongRow(g.pts, g.idx);
|
|
422
|
+
let sum = 0;
|
|
423
|
+
for (const p of ordered.pts) sum += Math.hypot(p.x - focal.x, p.y - focal.y);
|
|
424
|
+
rows.push({
|
|
425
|
+
id,
|
|
426
|
+
pts: ordered.pts,
|
|
427
|
+
seatIndices: ordered.seatIndices,
|
|
428
|
+
blockId: -1,
|
|
429
|
+
ordinal: 0,
|
|
430
|
+
blockDepth: 0,
|
|
431
|
+
focalDistance: sum / ordered.pts.length
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
const bounds = rows.map((r) => boundOf(r.pts));
|
|
435
|
+
const nearest = [];
|
|
436
|
+
for (let i = 0; i < rows.length; i++) {
|
|
437
|
+
let best = Infinity;
|
|
438
|
+
for (let j = 0; j < rows.length; j++) {
|
|
439
|
+
if (i === j) continue;
|
|
440
|
+
if (boundGap(bounds[i], bounds[j]) >= best) continue;
|
|
441
|
+
const d = rowGap(rows[i].pts, rows[j].pts);
|
|
442
|
+
if (d < best) best = d;
|
|
443
|
+
}
|
|
444
|
+
if (Number.isFinite(best)) nearest.push(best);
|
|
445
|
+
}
|
|
446
|
+
nearest.sort((a, b) => a - b);
|
|
447
|
+
const typicalGap = nearest.length ? nearest[Math.floor(nearest.length / 2)] : 1;
|
|
448
|
+
const linkDistance = Math.max(typicalGap * BLOCK_LINK_RATIO, 1e-6);
|
|
449
|
+
const adjacency = rows.map(() => []);
|
|
450
|
+
for (let i = 0; i < rows.length; i++) {
|
|
451
|
+
for (let j = i + 1; j < rows.length; j++) {
|
|
452
|
+
if (boundGap(bounds[i], bounds[j]) > linkDistance) continue;
|
|
453
|
+
if (rowGap(rows[i].pts, rows[j].pts) <= linkDistance) {
|
|
454
|
+
adjacency[i].push(j);
|
|
455
|
+
adjacency[j].push(i);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
let blockCount = 0;
|
|
460
|
+
for (let i = 0; i < rows.length; i++) {
|
|
461
|
+
if (rows[i].blockId !== -1) continue;
|
|
462
|
+
const id = blockCount++;
|
|
463
|
+
const stack = [i];
|
|
464
|
+
rows[i].blockId = id;
|
|
465
|
+
while (stack.length) {
|
|
466
|
+
const k = stack.pop();
|
|
467
|
+
for (const n of adjacency[k]) {
|
|
468
|
+
if (rows[n].blockId !== -1) continue;
|
|
469
|
+
rows[n].blockId = id;
|
|
470
|
+
stack.push(n);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const byBlock = /* @__PURE__ */ new Map();
|
|
475
|
+
for (const r of rows) {
|
|
476
|
+
const a = byBlock.get(r.blockId) ?? [];
|
|
477
|
+
a.push(r);
|
|
478
|
+
byBlock.set(r.blockId, a);
|
|
479
|
+
}
|
|
480
|
+
const centres = [];
|
|
481
|
+
for (const r of rows) {
|
|
482
|
+
if (r.pts.length < 4) continue;
|
|
483
|
+
const c = fitCircle(r.pts);
|
|
484
|
+
if (!c) continue;
|
|
485
|
+
if (!Number.isFinite(c.cx) || !Number.isFinite(c.cy)) continue;
|
|
486
|
+
centres.push({ x: c.cx, y: c.cy });
|
|
487
|
+
}
|
|
488
|
+
const centre = centres.length >= 2 ? (() => {
|
|
489
|
+
const xs = centres.map((c) => c.x).sort((a, b) => a - b);
|
|
490
|
+
const ys = centres.map((c) => c.y).sort((a, b) => a - b);
|
|
491
|
+
const mid = Math.floor(centres.length / 2);
|
|
492
|
+
return { cx: xs[mid], cy: ys[mid] };
|
|
493
|
+
})() : null;
|
|
494
|
+
if (centre) {
|
|
495
|
+
const radiusOf = (p) => Math.hypot(p.x - centre.cx, p.y - centre.cy);
|
|
496
|
+
let lo = Infinity, hi = -Infinity;
|
|
497
|
+
const spreads = [];
|
|
498
|
+
for (const r of rows) {
|
|
499
|
+
let rlo = Infinity, rhi = -Infinity;
|
|
500
|
+
for (const p of r.pts) {
|
|
501
|
+
const d = radiusOf(p);
|
|
502
|
+
if (d < rlo) rlo = d;
|
|
503
|
+
if (d > rhi) rhi = d;
|
|
504
|
+
}
|
|
505
|
+
spreads.push(rhi - rlo);
|
|
506
|
+
if (rlo < lo) lo = rlo;
|
|
507
|
+
if (rhi > hi) hi = rhi;
|
|
508
|
+
}
|
|
509
|
+
const range = hi - lo;
|
|
510
|
+
spreads.sort((a, b) => a - b);
|
|
511
|
+
const p90 = spreads[Math.min(spreads.length - 1, Math.floor(spreads.length * 0.9))];
|
|
512
|
+
if (range > 1e-9 && p90 / range <= CIRCLE_ROW_SPREAD_LIMIT) {
|
|
513
|
+
const withRadius = rows.map((r) => {
|
|
514
|
+
let sum = 0;
|
|
515
|
+
for (const p of r.pts) sum += radiusOf(p);
|
|
516
|
+
return { row: r, radius: sum / r.pts.length };
|
|
517
|
+
});
|
|
518
|
+
let minR = Infinity;
|
|
519
|
+
for (const w of withRadius) if (w.radius < minR) minR = w.radius;
|
|
520
|
+
withRadius.sort((a, b) => a.radius - b.radius);
|
|
521
|
+
let ordinal = -1, lastRadius = -Infinity;
|
|
522
|
+
const tolerance = range / Math.max(1, withRadius.length) * 0.5;
|
|
523
|
+
const ordered = [];
|
|
524
|
+
for (const w of withRadius) {
|
|
525
|
+
if (w.radius - lastRadius > tolerance) {
|
|
526
|
+
ordinal++;
|
|
527
|
+
lastRadius = w.radius;
|
|
528
|
+
}
|
|
529
|
+
w.row.ordinal = ordinal;
|
|
530
|
+
w.row.blockDepth = w.radius - minR;
|
|
531
|
+
ordered.push(w.row);
|
|
532
|
+
}
|
|
533
|
+
return { sectionId, rows: ordered, blockCount };
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
const out = [];
|
|
537
|
+
for (const [, group] of byBlock) {
|
|
538
|
+
const axis = fitBlockAxis(group);
|
|
539
|
+
if (axis) {
|
|
540
|
+
const key = (r) => {
|
|
541
|
+
let sum = 0;
|
|
542
|
+
for (const p of r.pts) sum += p.x * axis.x + p.y * axis.y;
|
|
543
|
+
return sum / r.pts.length;
|
|
544
|
+
};
|
|
545
|
+
group.sort((a, b) => key(a) - key(b));
|
|
546
|
+
let depth = 0;
|
|
547
|
+
for (let i = 0; i < group.length; i++) {
|
|
548
|
+
if (i > 0) depth += rowGap(group[i - 1].pts, group[i].pts);
|
|
549
|
+
group[i].ordinal = i;
|
|
550
|
+
group[i].blockDepth = depth;
|
|
551
|
+
out.push(group[i]);
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
let front = Infinity;
|
|
555
|
+
for (const r of group) if (r.focalDistance < front) front = r.focalDistance;
|
|
556
|
+
group.sort((a, b) => a.focalDistance - b.focalDistance);
|
|
557
|
+
for (let i = 0; i < group.length; i++) {
|
|
558
|
+
group[i].ordinal = i;
|
|
559
|
+
group[i].blockDepth = Math.max(0, group[i].focalDistance - front);
|
|
560
|
+
out.push(group[i]);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return { sectionId, rows: out, blockCount };
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/core/layout.ts
|
|
568
|
+
function overrideAccessibility(o) {
|
|
569
|
+
if (!o) return [];
|
|
570
|
+
if (o.accessibility && o.accessibility.length) {
|
|
571
|
+
return o.wheelchairSpaceType && !o.accessibility.includes("wheelchair") ? ["wheelchair", ...o.accessibility] : o.accessibility;
|
|
572
|
+
}
|
|
573
|
+
if (o.wheelchairSpaceType) return ["wheelchair"];
|
|
574
|
+
return o.accessible ? ["wheelchair"] : [];
|
|
575
|
+
}
|
|
576
|
+
var DEG = Math.PI / 180;
|
|
577
|
+
var SEAT_R = 9;
|
|
578
|
+
var TABLE_SEAT_OFFSET = 16;
|
|
579
|
+
function place(lx, ly, deg, origin) {
|
|
580
|
+
const a = deg * DEG;
|
|
581
|
+
const cos = Math.cos(a);
|
|
582
|
+
const sin = Math.sin(a);
|
|
583
|
+
return {
|
|
584
|
+
x: origin.x + lx * cos - ly * sin,
|
|
585
|
+
y: origin.y + lx * sin + ly * cos
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function rowSeatPositions(row) {
|
|
589
|
+
const { seatCount, seatSpacing, curve, rotation, origin } = row;
|
|
590
|
+
const out = [];
|
|
591
|
+
if (row.path) return distributeAlongCubic(row.path, seatCount);
|
|
592
|
+
if (seatCount <= 1) {
|
|
593
|
+
if (seatCount === 1) out.push({ x: origin.x, y: origin.y });
|
|
594
|
+
return out;
|
|
595
|
+
}
|
|
596
|
+
if (curve === 0) {
|
|
597
|
+
for (let i = 0; i < seatCount; i++) out.push(place(i * seatSpacing, 0, rotation, origin));
|
|
598
|
+
return out;
|
|
599
|
+
}
|
|
600
|
+
const arcStep = curve / (seatCount - 1) * DEG;
|
|
601
|
+
const radius = seatSpacing / (2 * Math.sin(Math.abs(arcStep) / 2));
|
|
602
|
+
for (let i = 0; i < seatCount; i++) {
|
|
603
|
+
const phi = i * arcStep;
|
|
604
|
+
const lx = radius * Math.sin(phi);
|
|
605
|
+
const ly = -radius + radius * Math.cos(phi);
|
|
606
|
+
out.push(place(lx, ly, rotation, origin));
|
|
607
|
+
}
|
|
608
|
+
return out;
|
|
609
|
+
}
|
|
610
|
+
function overrideMap(row) {
|
|
611
|
+
const m = /* @__PURE__ */ new Map();
|
|
612
|
+
if (row.overrides) for (const o of row.overrides) m.set(o.index, o);
|
|
613
|
+
return m;
|
|
614
|
+
}
|
|
615
|
+
function rowInventoryCount(row) {
|
|
616
|
+
const skipped = new Set((row.overrides ?? []).filter((override) => override.skip && Number.isInteger(override.index) && override.index >= 0 && override.index < row.seatCount).map((override) => override.index));
|
|
617
|
+
return Math.max(0, row.seatCount - skipped.size);
|
|
618
|
+
}
|
|
619
|
+
function centerRank(n) {
|
|
620
|
+
const rank = new Array(n);
|
|
621
|
+
Array.from({ length: n }, (_, i) => i).sort((a, b) => Math.abs(2 * a - (n - 1)) - Math.abs(2 * b - (n - 1)) || a - b).forEach((idx, k) => rank[idx] = k);
|
|
622
|
+
return rank;
|
|
623
|
+
}
|
|
624
|
+
function seatLabelPart(row, i) {
|
|
625
|
+
const rawStart = row.seatLabelStart ?? 1;
|
|
626
|
+
const dir = row.seatNumbering?.direction ?? "ltr";
|
|
627
|
+
const step = row.seatNumbering?.step ?? 1;
|
|
628
|
+
const scheme = row.seatNumbering?.scheme ?? "decimal";
|
|
629
|
+
const prefix = row.seatNumbering?.prefix ?? "";
|
|
630
|
+
const endAt = row.seatNumbering?.endAt;
|
|
631
|
+
const n = row.seatCount;
|
|
632
|
+
if (scheme === "updown" || scheme === "updown-descending") {
|
|
633
|
+
const half = Math.ceil(n / 2);
|
|
634
|
+
const core2 = scheme === "updown" ? i < half ? rawStart + 2 * i : rawStart - 1 + 2 * (n - i) : i < half ? rawStart + 2 * (half - 1 - i) : rawStart + 1 + 2 * (i - half);
|
|
635
|
+
return `${prefix}${core2}`;
|
|
636
|
+
}
|
|
637
|
+
const effStep = scheme === "odd" || scheme === "even" ? 2 : step;
|
|
638
|
+
const start = endAt != null && Number.isFinite(endAt) ? endAt - (n - 1) * effStep : rawStart;
|
|
639
|
+
const p = dir === "center" ? centerRank(n)[i] : dir === "rtl" ? n - 1 - i : i;
|
|
640
|
+
let core;
|
|
641
|
+
switch (scheme) {
|
|
642
|
+
case "odd": {
|
|
643
|
+
const firstOdd = start % 2 === 1 ? start : start + 1;
|
|
644
|
+
core = String(firstOdd + p * 2);
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
case "even": {
|
|
648
|
+
const firstEven = start % 2 === 0 ? start : start + 1;
|
|
649
|
+
core = String(firstEven + p * 2);
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
case "roman":
|
|
653
|
+
core = toRoman(start + p * step);
|
|
654
|
+
break;
|
|
655
|
+
case "letters-upper":
|
|
656
|
+
core = toLetters(start + p * step, false);
|
|
657
|
+
break;
|
|
658
|
+
case "letters-lower":
|
|
659
|
+
core = toLetters(start + p * step, true);
|
|
660
|
+
break;
|
|
661
|
+
case "decimal":
|
|
662
|
+
default:
|
|
663
|
+
core = String(start + p * step);
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
return `${prefix}${core}`;
|
|
667
|
+
}
|
|
668
|
+
function expandRowSlots(row) {
|
|
669
|
+
const ov = overrideMap(row);
|
|
670
|
+
return rowSeatPositions(row).map((p, i) => {
|
|
671
|
+
const o = ov.get(i);
|
|
672
|
+
const accessibility = overrideAccessibility(o);
|
|
673
|
+
const part = seatLabelPart(row, i);
|
|
674
|
+
const inventoryLabel = o?.label ?? `${row.label}-${part}`;
|
|
675
|
+
const displayPrefix = row.displayLabel ?? row.label;
|
|
676
|
+
const commercial = { ...row.commercial, ...o?.commercial };
|
|
677
|
+
return {
|
|
678
|
+
index: i,
|
|
679
|
+
x: p.x + (o?.dx ?? 0),
|
|
680
|
+
y: p.y + (o?.dy ?? 0),
|
|
681
|
+
label: inventoryLabel,
|
|
682
|
+
displayLabel: o?.displayLabel ?? `${displayPrefix}-${part}`,
|
|
683
|
+
categoryKey: o?.categoryKey ?? row.categoryKey,
|
|
684
|
+
skipped: !!o?.skip,
|
|
685
|
+
accessible: accessibility.length > 0,
|
|
686
|
+
accessibility,
|
|
687
|
+
wheelchairSpaceType: o?.wheelchairSpaceType,
|
|
688
|
+
commercial: Object.values(commercial).some((value) => value !== void 0 && value !== false && value !== "") ? commercial : void 0,
|
|
689
|
+
viewUrl: o?.viewFromSeatUrl ?? row.viewFromSeatUrl,
|
|
690
|
+
labelStyle: o?.labelStyle
|
|
691
|
+
};
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
function expandRow(row) {
|
|
695
|
+
const seats = [];
|
|
696
|
+
for (const slot of expandRowSlots(row)) {
|
|
697
|
+
if (slot.skipped) continue;
|
|
698
|
+
seats.push({
|
|
699
|
+
id: `${row.id}:${slot.index}`,
|
|
700
|
+
label: slot.label,
|
|
701
|
+
displayLabel: slot.displayLabel === slot.label ? void 0 : slot.displayLabel,
|
|
702
|
+
x: slot.x,
|
|
703
|
+
y: slot.y,
|
|
704
|
+
rowId: row.id,
|
|
705
|
+
categoryKey: slot.categoryKey,
|
|
706
|
+
accessible: slot.accessible || void 0,
|
|
707
|
+
accessibility: slot.accessibility.length ? slot.accessibility : void 0,
|
|
708
|
+
wheelchairSpaceType: slot.wheelchairSpaceType,
|
|
709
|
+
commercial: slot.commercial,
|
|
710
|
+
viewUrl: slot.viewUrl,
|
|
711
|
+
labelStyle: slot.labelStyle
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
return seats;
|
|
715
|
+
}
|
|
716
|
+
function tableSeatCountsBySide(t) {
|
|
717
|
+
if (t.seatCountsBySide) return { ...t.seatCountsBySide };
|
|
718
|
+
const enabled = t.sides && t.sides.length ? t.sides : ["top", "bottom"];
|
|
719
|
+
const order = ["top", "bottom", "left", "right"].filter((side) => enabled.includes(side));
|
|
720
|
+
const counts = { top: 0, bottom: 0, left: 0, right: 0 };
|
|
721
|
+
if (!order.length) return counts;
|
|
722
|
+
const n = Math.max(0, Math.round(t.seatCount));
|
|
723
|
+
for (let index = 0; index < n; index++) counts[order[index % order.length]] += 1;
|
|
724
|
+
return counts;
|
|
725
|
+
}
|
|
726
|
+
function expandTableSlots(t) {
|
|
727
|
+
const seats = [];
|
|
728
|
+
const n = Math.max(0, Math.round(t.seatCount));
|
|
729
|
+
if (n === 0) return seats;
|
|
730
|
+
const overrides = new Map((t.overrides ?? []).map((override) => [override.index, override]));
|
|
731
|
+
const mk = (index, x, y, side) => {
|
|
732
|
+
const override = overrides.get(index);
|
|
733
|
+
const accessibility = overrideAccessibility(override);
|
|
734
|
+
const label = override?.label ?? `${t.label}-${index + 1}`;
|
|
735
|
+
const displayPrefix = t.displayLabel ?? t.label;
|
|
736
|
+
return {
|
|
737
|
+
index,
|
|
738
|
+
label,
|
|
739
|
+
displayLabel: override?.displayLabel ?? `${displayPrefix}-${index + 1}`,
|
|
740
|
+
x: x + (override?.dx ?? 0),
|
|
741
|
+
y: y + (override?.dy ?? 0),
|
|
742
|
+
categoryKey: override?.categoryKey ?? t.categoryKey,
|
|
743
|
+
skipped: !!override?.skip,
|
|
744
|
+
accessible: accessibility.length > 0,
|
|
745
|
+
accessibility,
|
|
746
|
+
wheelchairSpaceType: override?.wheelchairSpaceType,
|
|
747
|
+
commercial: override?.commercial,
|
|
748
|
+
viewUrl: override?.viewFromSeatUrl,
|
|
749
|
+
labelStyle: override?.labelStyle,
|
|
750
|
+
...side ? { side } : {}
|
|
751
|
+
};
|
|
752
|
+
};
|
|
753
|
+
if (t.shape === "round") {
|
|
754
|
+
const R = (t.radius ?? 40) + TABLE_SEAT_OFFSET;
|
|
755
|
+
const base = t.rotation * DEG;
|
|
756
|
+
const arc = Math.max(0, Math.min(360, t.seatArc ?? 360));
|
|
757
|
+
if (arc >= 360 || n === 1) {
|
|
758
|
+
for (let i = 0; i < n; i++) {
|
|
759
|
+
const a = base + i / n * 2 * Math.PI;
|
|
760
|
+
seats.push(mk(i, t.center.x + R * Math.cos(a), t.center.y + R * Math.sin(a)));
|
|
761
|
+
}
|
|
762
|
+
return seats;
|
|
763
|
+
}
|
|
764
|
+
const arcRad = arc * DEG;
|
|
765
|
+
const halfGap = (2 * Math.PI - arcRad) / 2;
|
|
766
|
+
const start = base + halfGap;
|
|
767
|
+
for (let i = 0; i < n; i++) {
|
|
768
|
+
const a = start + i / (n - 1) * arcRad;
|
|
769
|
+
seats.push(mk(i, t.center.x + R * Math.cos(a), t.center.y + R * Math.sin(a)));
|
|
770
|
+
}
|
|
771
|
+
return seats;
|
|
772
|
+
}
|
|
773
|
+
const w = t.width ?? 80;
|
|
774
|
+
const h = t.height ?? 50;
|
|
775
|
+
const counts = tableSeatCountsBySide(t);
|
|
776
|
+
const order = ["top", "bottom", "left", "right"];
|
|
777
|
+
let idx = 0;
|
|
778
|
+
for (const side of order) {
|
|
779
|
+
const count = counts[side];
|
|
780
|
+
for (let j = 0; j < count; j++) {
|
|
781
|
+
let localX;
|
|
782
|
+
let localY;
|
|
783
|
+
if (side === "top") {
|
|
784
|
+
localX = -w / 2 + (j + 0.5) * w / count;
|
|
785
|
+
localY = -h / 2 - TABLE_SEAT_OFFSET;
|
|
786
|
+
} else if (side === "bottom") {
|
|
787
|
+
localX = -w / 2 + (j + 0.5) * w / count;
|
|
788
|
+
localY = h / 2 + TABLE_SEAT_OFFSET;
|
|
789
|
+
} else if (side === "left") {
|
|
790
|
+
localX = -w / 2 - TABLE_SEAT_OFFSET;
|
|
791
|
+
localY = -h / 2 + (j + 0.5) * h / count;
|
|
792
|
+
} else {
|
|
793
|
+
localX = w / 2 + TABLE_SEAT_OFFSET;
|
|
794
|
+
localY = -h / 2 + (j + 0.5) * h / count;
|
|
795
|
+
}
|
|
796
|
+
const point = place(localX, localY, t.rotation, t.center);
|
|
797
|
+
seats.push(mk(idx++, point.x, point.y, side));
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return seats;
|
|
801
|
+
}
|
|
802
|
+
function tableInventoryCount(t) {
|
|
803
|
+
if (t.bookAsWhole || t.variableOccupancy) return Math.max(0, Math.round(t.seatCount));
|
|
804
|
+
return expandTableSlots(t).filter((slot) => !slot.skipped).length;
|
|
805
|
+
}
|
|
806
|
+
function expandTable(t) {
|
|
807
|
+
return expandTableSlots(t).filter((slot) => !slot.skipped).map((slot) => ({
|
|
808
|
+
id: `${t.id}:${slot.index}`,
|
|
809
|
+
label: slot.label,
|
|
810
|
+
...slot.displayLabel !== slot.label ? { displayLabel: slot.displayLabel } : {},
|
|
811
|
+
x: slot.x,
|
|
812
|
+
y: slot.y,
|
|
813
|
+
rowId: t.id,
|
|
814
|
+
categoryKey: slot.categoryKey,
|
|
815
|
+
...slot.accessible ? { accessible: true } : {},
|
|
816
|
+
...slot.accessibility.length ? { accessibility: slot.accessibility } : {},
|
|
817
|
+
...slot.wheelchairSpaceType ? { wheelchairSpaceType: slot.wheelchairSpaceType } : {},
|
|
818
|
+
...slot.commercial ? { commercial: slot.commercial } : {},
|
|
819
|
+
...slot.viewUrl ? { viewUrl: slot.viewUrl } : {},
|
|
820
|
+
...slot.labelStyle ? { labelStyle: slot.labelStyle } : {}
|
|
821
|
+
}));
|
|
822
|
+
}
|
|
823
|
+
function expandBooth(b) {
|
|
824
|
+
return [
|
|
825
|
+
{
|
|
826
|
+
id: `${b.id}:0`,
|
|
827
|
+
label: b.label,
|
|
828
|
+
...b.displayLabel && b.displayLabel !== b.label ? { displayLabel: b.displayLabel } : {},
|
|
829
|
+
x: b.center.x,
|
|
830
|
+
y: b.center.y,
|
|
831
|
+
rowId: b.id,
|
|
832
|
+
categoryKey: b.categoryKey,
|
|
833
|
+
kind: "booth"
|
|
834
|
+
}
|
|
835
|
+
];
|
|
836
|
+
}
|
|
837
|
+
function pointInPolygon(p, poly) {
|
|
838
|
+
let inside = false;
|
|
839
|
+
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
840
|
+
const xi = poly[i].x;
|
|
841
|
+
const yi = poly[i].y;
|
|
842
|
+
const xj = poly[j].x;
|
|
843
|
+
const yj = poly[j].y;
|
|
844
|
+
const hit = yi > p.y !== yj > p.y && p.x < (xj - xi) * (p.y - yi) / (yj - yi) + xi;
|
|
845
|
+
if (hit) inside = !inside;
|
|
846
|
+
}
|
|
847
|
+
return inside;
|
|
848
|
+
}
|
|
849
|
+
function pointOnPolygonBoundary(p, poly) {
|
|
850
|
+
return poly.some((start, index) => {
|
|
851
|
+
const end = poly[(index + 1) % poly.length];
|
|
852
|
+
const cross = (p.y - start.y) * (end.x - start.x) - (p.x - start.x) * (end.y - start.y);
|
|
853
|
+
if (Math.abs(cross) > 1e-7) return false;
|
|
854
|
+
const dot = (p.x - start.x) * (end.x - start.x) + (p.y - start.y) * (end.y - start.y);
|
|
855
|
+
const lengthSquared = (end.x - start.x) ** 2 + (end.y - start.y) ** 2;
|
|
856
|
+
return dot >= -1e-7 && dot <= lengthSquared + 1e-7;
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
function pointInPolygonWithHoles(p, outer, holes) {
|
|
860
|
+
return pointInPolygon(p, outer) && !(holes ?? []).some((hole) => pointInPolygon(p, hole) || pointOnPolygonBoundary(p, hole));
|
|
861
|
+
}
|
|
862
|
+
function polygonLabelPoint(outer, holes) {
|
|
863
|
+
if (!outer.length) return { x: 0, y: 0 };
|
|
864
|
+
const xs = outer.map((point) => point.x);
|
|
865
|
+
const ys = outer.map((point) => point.y);
|
|
866
|
+
const bounds = { minX: Math.min(...xs), maxX: Math.max(...xs), minY: Math.min(...ys), maxY: Math.max(...ys) };
|
|
867
|
+
const centroid = polygonCentroid(outer);
|
|
868
|
+
if (pointInPolygonWithHoles(centroid, outer, holes)) return centroid;
|
|
869
|
+
let best = outer[0];
|
|
870
|
+
let bestScore = -Infinity;
|
|
871
|
+
const rings = [outer, ...holes ?? []];
|
|
872
|
+
for (let row = 1; row < 24; row += 1) {
|
|
873
|
+
for (let column = 1; column < 24; column += 1) {
|
|
874
|
+
const point = {
|
|
875
|
+
x: bounds.minX + (bounds.maxX - bounds.minX) * column / 24,
|
|
876
|
+
y: bounds.minY + (bounds.maxY - bounds.minY) * row / 24
|
|
877
|
+
};
|
|
878
|
+
if (!pointInPolygonWithHoles(point, outer, holes)) continue;
|
|
879
|
+
const score = Math.min(...rings.flatMap((ring) => ring.map((start, index) => {
|
|
880
|
+
const end = ring[(index + 1) % ring.length];
|
|
881
|
+
const dx = end.x - start.x;
|
|
882
|
+
const dy = end.y - start.y;
|
|
883
|
+
const denominator = dx * dx + dy * dy;
|
|
884
|
+
const projection = denominator ? ((point.x - start.x) * dx + (point.y - start.y) * dy) / denominator : 0;
|
|
885
|
+
const t = Math.max(0, Math.min(1, projection));
|
|
886
|
+
return Math.hypot(point.x - (start.x + t * dx), point.y - (start.y + t * dy));
|
|
887
|
+
})));
|
|
888
|
+
if (score > bestScore) {
|
|
889
|
+
best = point;
|
|
890
|
+
bestScore = score;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return best;
|
|
895
|
+
}
|
|
896
|
+
function polygonCentroid(pts) {
|
|
897
|
+
if (!pts.length) return { x: 0, y: 0 };
|
|
898
|
+
let x = 0;
|
|
899
|
+
let y = 0;
|
|
900
|
+
for (const p of pts) {
|
|
901
|
+
x += p.x;
|
|
902
|
+
y += p.y;
|
|
903
|
+
}
|
|
904
|
+
return { x: x / pts.length, y: y / pts.length };
|
|
905
|
+
}
|
|
906
|
+
function objectCenter(o) {
|
|
907
|
+
switch (o.type) {
|
|
908
|
+
case "row": {
|
|
909
|
+
const seats = expandRow(o);
|
|
910
|
+
if (!seats.length) return { x: o.origin.x, y: o.origin.y };
|
|
911
|
+
let x = 0;
|
|
912
|
+
let y = 0;
|
|
913
|
+
for (const s of seats) {
|
|
914
|
+
x += s.x;
|
|
915
|
+
y += s.y;
|
|
916
|
+
}
|
|
917
|
+
return { x: x / seats.length, y: y / seats.length };
|
|
918
|
+
}
|
|
919
|
+
case "table":
|
|
920
|
+
case "booth":
|
|
921
|
+
return { x: o.center.x, y: o.center.y };
|
|
922
|
+
case "gaArea":
|
|
923
|
+
return polygonCentroid(o.points);
|
|
924
|
+
case "section":
|
|
925
|
+
return polygonCentroid(o.outline);
|
|
926
|
+
case "text":
|
|
927
|
+
return { x: o.position.x, y: o.position.y };
|
|
928
|
+
case "shape":
|
|
929
|
+
if (o.points && o.points.length) return polygonCentroid(o.points);
|
|
930
|
+
if (o.x != null && o.y != null && o.width != null && o.height != null) {
|
|
931
|
+
return { x: o.x + o.width / 2, y: o.y + o.height / 2 };
|
|
932
|
+
}
|
|
933
|
+
return { x: o.x ?? 0, y: o.y ?? 0 };
|
|
934
|
+
case "decorImage":
|
|
935
|
+
return { x: o.x + o.width / 2, y: o.y + o.height / 2 };
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
function samePoints(left, right) {
|
|
939
|
+
return left.length === right.length && left.every((point, index) => point.x === right[index].x && point.y === right[index].y);
|
|
940
|
+
}
|
|
941
|
+
function sameGASurfaceAsSection(object, section) {
|
|
942
|
+
if (object.type !== "gaArea" || !samePoints(object.points, section.outline)) return false;
|
|
943
|
+
const objectHoles = object.holes ?? [];
|
|
944
|
+
const sectionHoles = section.holes ?? [];
|
|
945
|
+
return objectHoles.length === sectionHoles.length && objectHoles.every((hole, index) => samePoints(hole, sectionHoles[index]));
|
|
946
|
+
}
|
|
947
|
+
function owningSectionForObject(objects, object) {
|
|
948
|
+
const sections = objects.filter((candidate) => candidate.type === "section");
|
|
949
|
+
const referencedLogicalId = "referenceInventorySource" in object ? object.referenceInventorySource?.logicalSectionId : void 0;
|
|
950
|
+
const center = objectCenter(object);
|
|
951
|
+
const referencedOwner = referencedLogicalId ? sections.find((section) => (section.logicalSectionId ?? section.id) === referencedLogicalId && (sameGASurfaceAsSection(object, section) || pointInPolygonWithHoles(center, section.outline, section.holes))) : void 0;
|
|
952
|
+
return referencedOwner ?? sections.find((section) => pointInPolygonWithHoles(center, section.outline, section.holes));
|
|
953
|
+
}
|
|
954
|
+
function floorsOf(doc) {
|
|
955
|
+
if (doc.floors && doc.floors.length) return doc.floors;
|
|
956
|
+
return [{
|
|
957
|
+
id: "floor-0",
|
|
958
|
+
name: "Main",
|
|
959
|
+
objects: doc.objects,
|
|
960
|
+
focalPoint: doc.focalPoint,
|
|
961
|
+
referenceImage: doc.referenceImage,
|
|
962
|
+
backgroundImage: doc.backgroundImage
|
|
963
|
+
}];
|
|
964
|
+
}
|
|
965
|
+
function floorObjects(doc, floorId) {
|
|
966
|
+
const floors = floorsOf(doc);
|
|
967
|
+
return (floorId ? floors.find((f) => f.id === floorId) : floors[0])?.objects ?? [];
|
|
968
|
+
}
|
|
969
|
+
function allObjects(doc) {
|
|
970
|
+
return doc.floors && doc.floors.length ? doc.floors.flatMap((f) => f.objects) : doc.objects;
|
|
971
|
+
}
|
|
972
|
+
function translateObject(o, dx, dy) {
|
|
973
|
+
const p = (pt) => ({ x: pt.x + dx, y: pt.y + dy });
|
|
974
|
+
const pts = (a) => a.map(p);
|
|
975
|
+
switch (o.type) {
|
|
976
|
+
case "row":
|
|
977
|
+
return { ...o, origin: p(o.origin) };
|
|
978
|
+
case "table":
|
|
979
|
+
case "booth":
|
|
980
|
+
return { ...o, center: p(o.center) };
|
|
981
|
+
case "gaArea":
|
|
982
|
+
return { ...o, points: pts(o.points), ...o.holes ? { holes: o.holes.map(pts) } : {} };
|
|
983
|
+
case "section":
|
|
984
|
+
return {
|
|
985
|
+
...o,
|
|
986
|
+
outline: pts(o.outline),
|
|
987
|
+
...o.outlinePath ? { outlinePath: translateSectionOutlinePath(o.outlinePath, dx, dy) } : {},
|
|
988
|
+
...o.holes ? { holes: o.holes.map(pts) } : {}
|
|
989
|
+
};
|
|
990
|
+
case "text":
|
|
991
|
+
return { ...o, position: p(o.position) };
|
|
992
|
+
case "shape":
|
|
993
|
+
return {
|
|
994
|
+
...o,
|
|
995
|
+
...o.points ? { points: pts(o.points) } : {},
|
|
996
|
+
...o.x != null ? { x: o.x + dx } : {},
|
|
997
|
+
...o.y != null ? { y: o.y + dy } : {}
|
|
998
|
+
};
|
|
999
|
+
case "decorImage":
|
|
1000
|
+
return { ...o, x: o.x + dx, y: o.y + dy };
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
function stackFloors(doc, spread = 900) {
|
|
1004
|
+
if (!doc.floors || doc.floors.length < 2) return doc;
|
|
1005
|
+
const objects = [];
|
|
1006
|
+
doc.floors.forEach((f, i) => {
|
|
1007
|
+
const dy = -i * spread;
|
|
1008
|
+
for (const o of f.objects) objects.push(dy === 0 ? o : translateObject(o, 0, dy));
|
|
1009
|
+
});
|
|
1010
|
+
return { ...doc, objects, floors: void 0 };
|
|
1011
|
+
}
|
|
1012
|
+
function expandFloorObjects(objects, zones, fallbackFocal, viewFallback) {
|
|
1013
|
+
const out = [];
|
|
1014
|
+
const segmented = /* @__PURE__ */ new Map();
|
|
1015
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1016
|
+
for (const object of objects) {
|
|
1017
|
+
if (object.type !== "row" || !object.segmentedRow) continue;
|
|
1018
|
+
const list = grouped.get(object.segmentedRow.groupId) ?? [];
|
|
1019
|
+
list.push(object);
|
|
1020
|
+
grouped.set(object.segmentedRow.groupId, list);
|
|
1021
|
+
}
|
|
1022
|
+
for (const [groupId, members] of grouped) {
|
|
1023
|
+
const ordered = members.slice().sort((left, right) => left.segmentedRow.componentIndex - right.segmentedRow.componentIndex);
|
|
1024
|
+
const expectedCount = ordered[0]?.segmentedRow?.componentCount ?? 0;
|
|
1025
|
+
const first = ordered[0]?.segmentedRow;
|
|
1026
|
+
if (!first) continue;
|
|
1027
|
+
const valid = expectedCount >= 2 && ordered.length === expectedCount && first?.boundaryBefore === "start" && ordered.every((row, index) => row.segmentedRow?.kind === "segmented-row-v1" && row.segmentedRow.groupId === groupId && row.segmentedRow.componentCount === expectedCount && row.segmentedRow.componentIndex === index && (index === 0 ? row.segmentedRow.boundaryBefore === "start" : row.segmentedRow.boundaryBefore !== "start") && row.segmentedRow.displayLabel === first.displayLabel);
|
|
1028
|
+
if (!valid) continue;
|
|
1029
|
+
const totalSeats = ordered.reduce((sum, row) => sum + row.seatCount, 0);
|
|
1030
|
+
let adjacencyOffset = 0;
|
|
1031
|
+
let displayOffset = 0;
|
|
1032
|
+
for (const row of ordered) {
|
|
1033
|
+
if (row.segmentedRow.boundaryBefore === "break") adjacencyOffset += 1;
|
|
1034
|
+
segmented.set(row.id, {
|
|
1035
|
+
groupId,
|
|
1036
|
+
adjacencyOffset,
|
|
1037
|
+
displayOffset,
|
|
1038
|
+
displayLabel: first.displayLabel,
|
|
1039
|
+
totalSeats,
|
|
1040
|
+
canonical: ordered[0],
|
|
1041
|
+
viewFromSeatUrl: first.viewFromSeatUrl
|
|
1042
|
+
});
|
|
1043
|
+
adjacencyOffset += row.seatCount;
|
|
1044
|
+
displayOffset += row.seatCount;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
for (const obj of objects) {
|
|
1048
|
+
let seats = [];
|
|
1049
|
+
if (obj.type === "row") seats = expandRow(obj);
|
|
1050
|
+
else if (obj.type === "table") seats = expandTable(obj);
|
|
1051
|
+
else if (obj.type === "booth") seats = expandBooth(obj);
|
|
1052
|
+
if (!seats.length) continue;
|
|
1053
|
+
if (obj.type === "row") {
|
|
1054
|
+
const logical = segmented.get(obj.id);
|
|
1055
|
+
if (logical) {
|
|
1056
|
+
const overrides = new Map((obj.overrides ?? []).map((override) => [override.index, override]));
|
|
1057
|
+
for (const seat of seats) {
|
|
1058
|
+
const physicalIndex = Number(seat.id.slice(seat.id.lastIndexOf(":") + 1));
|
|
1059
|
+
if (!Number.isInteger(physicalIndex)) continue;
|
|
1060
|
+
const displayOrdinal = logical.displayOffset + physicalIndex;
|
|
1061
|
+
seat.logicalRowId = logical.groupId;
|
|
1062
|
+
seat.logicalSeatIndex = logical.adjacencyOffset + physicalIndex;
|
|
1063
|
+
if (!overrides.get(physicalIndex)?.displayLabel) {
|
|
1064
|
+
const numberingRow = {
|
|
1065
|
+
...logical.canonical,
|
|
1066
|
+
seatCount: logical.totalSeats,
|
|
1067
|
+
label: logical.displayLabel,
|
|
1068
|
+
displayLabel: logical.displayLabel
|
|
1069
|
+
};
|
|
1070
|
+
seat.displayLabel = `${logical.displayLabel}-${seatLabelPart(numberingRow, displayOrdinal)}`;
|
|
1071
|
+
}
|
|
1072
|
+
seat.viewUrl ??= logical.viewFromSeatUrl;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
const owner = owningSectionForObject(objects, obj);
|
|
1077
|
+
const inheritedView = owner?.viewFromSeatUrl;
|
|
1078
|
+
const zone = owner?.zone ? zones?.find((candidate) => candidate.id === owner.zone) : void 0;
|
|
1079
|
+
const resolvedFocal = zone?.focalPoint ?? fallbackFocal;
|
|
1080
|
+
for (const seat of seats) {
|
|
1081
|
+
if (inheritedView) seat.viewUrl ??= inheritedView;
|
|
1082
|
+
if (viewFallback) seat.viewUrl ??= viewFallback;
|
|
1083
|
+
if (owner) seat.sectionId = owner.logicalSectionId ?? owner.id;
|
|
1084
|
+
if (owner?.zone) seat.zoneId = owner.zone;
|
|
1085
|
+
if (resolvedFocal) seat.focalPoint = { ...resolvedFocal };
|
|
1086
|
+
}
|
|
1087
|
+
out.push(...seats);
|
|
1088
|
+
}
|
|
1089
|
+
return out;
|
|
1090
|
+
}
|
|
1091
|
+
function expandChart(doc, options = {}) {
|
|
1092
|
+
if (doc.floors?.length) {
|
|
1093
|
+
const out2 = [];
|
|
1094
|
+
for (const floor of doc.floors) {
|
|
1095
|
+
const floorFocal = floor.focalPoint ?? doc.focalPoint;
|
|
1096
|
+
const floorView = floor.viewFromSeatUrl ?? doc.viewFromSeatUrl;
|
|
1097
|
+
const seats = expandFloorObjects(floor.objects, doc.zones, floorFocal, floorView);
|
|
1098
|
+
assignEyeHeights(floor.objects, floor.focalPoint ?? doc.focalPoint, floor.baseHeightM ?? 0, seats);
|
|
1099
|
+
out2.push(...seats);
|
|
1100
|
+
}
|
|
1101
|
+
return out2;
|
|
1102
|
+
}
|
|
1103
|
+
const out = expandFloorObjects(doc.objects, doc.zones, doc.focalPoint, doc.viewFromSeatUrl);
|
|
1104
|
+
assignEyeHeights(doc.objects, doc.focalPoint, options.floorBaseHeightM ?? 0, out);
|
|
1105
|
+
return out;
|
|
1106
|
+
}
|
|
1107
|
+
function assignEyeHeights(objects, focal, floorBaseHeightM, seats) {
|
|
1108
|
+
const sections = objects.filter((o) => o.type === "section");
|
|
1109
|
+
const hasGeometry = floorBaseHeightM > 0 || sections.some((s) => s.height !== void 0 || s.rake !== void 0 || (s.elevation ?? 0) > 0);
|
|
1110
|
+
if (!sections.length || !hasGeometry || !focal) {
|
|
1111
|
+
for (const seat of seats) seat.eyeHeightM = floorBaseHeightM + SEATED_EYE_HEIGHT_M;
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
const owner = new Array(seats.length);
|
|
1115
|
+
const geo = /* @__PURE__ */ new Map();
|
|
1116
|
+
const seatsBySection = /* @__PURE__ */ new Map();
|
|
1117
|
+
for (let i = 0; i < seats.length; i++) {
|
|
1118
|
+
const seat = seats[i];
|
|
1119
|
+
const sec = sections.find((s) => pointInPolygonWithHoles({ x: seat.x, y: seat.y }, s.outline, s.holes)) ?? null;
|
|
1120
|
+
owner[i] = sec;
|
|
1121
|
+
if (!sec) continue;
|
|
1122
|
+
if (!geo.has(sec.id)) geo.set(sec.id, sectionGeometry(sec, { floorBaseHeightM }));
|
|
1123
|
+
const list = seatsBySection.get(sec.id);
|
|
1124
|
+
if (list) list.push(i);
|
|
1125
|
+
else seatsBySection.set(sec.id, [i]);
|
|
1126
|
+
}
|
|
1127
|
+
const seatLevel = new Array(seats.length).fill(void 0);
|
|
1128
|
+
for (const [secId, indices] of seatsBySection) {
|
|
1129
|
+
const g = geo.get(secId);
|
|
1130
|
+
if (!g) continue;
|
|
1131
|
+
const rakeTan = g.rake > 0 ? Math.tan(g.rake * Math.PI / 180) : 0;
|
|
1132
|
+
const structure = resolveSection(secId, seats, indices, focal);
|
|
1133
|
+
for (const row of structure.rows) {
|
|
1134
|
+
const riseM = row.blockDepth * METRES_PER_CHART_UNIT * rakeTan;
|
|
1135
|
+
const level = g.height + riseM;
|
|
1136
|
+
for (const si of row.seatIndices) seatLevel[si] = level;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
for (let i = 0; i < seats.length; i++) {
|
|
1140
|
+
const seat = seats[i];
|
|
1141
|
+
const sec = owner[i];
|
|
1142
|
+
if (!sec) {
|
|
1143
|
+
seat.eyeHeightM = floorBaseHeightM + SEATED_EYE_HEIGHT_M;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
const g = geo.get(sec.id);
|
|
1147
|
+
seat.eyeHeightM = (seatLevel[i] ?? g.height) + SEATED_EYE_HEIGHT_M;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
var PAD = 40;
|
|
1151
|
+
function chartBounds(doc) {
|
|
1152
|
+
let minX = Infinity;
|
|
1153
|
+
let minY = Infinity;
|
|
1154
|
+
let maxX = -Infinity;
|
|
1155
|
+
let maxY = -Infinity;
|
|
1156
|
+
const acc = (x, y) => {
|
|
1157
|
+
if (x < minX) minX = x;
|
|
1158
|
+
if (y < minY) minY = y;
|
|
1159
|
+
if (x > maxX) maxX = x;
|
|
1160
|
+
if (y > maxY) maxY = y;
|
|
1161
|
+
};
|
|
1162
|
+
for (const s of expandChart(doc)) acc(s.x, s.y);
|
|
1163
|
+
for (const obj of allObjects(doc)) {
|
|
1164
|
+
if (obj.type === "gaArea") {
|
|
1165
|
+
for (const p of obj.points) acc(p.x, p.y);
|
|
1166
|
+
} else if (obj.type === "section") {
|
|
1167
|
+
for (const p of obj.outline) acc(p.x, p.y);
|
|
1168
|
+
} else if (obj.type === "decorImage") {
|
|
1169
|
+
acc(obj.x, obj.y);
|
|
1170
|
+
acc(obj.x + obj.width, obj.y + obj.height);
|
|
1171
|
+
} else if (obj.type === "shape") {
|
|
1172
|
+
if (obj.points && obj.points.length) {
|
|
1173
|
+
for (const p of obj.points) acc(p.x, p.y);
|
|
1174
|
+
} else if (obj.x != null && obj.y != null && obj.width != null && obj.height != null) {
|
|
1175
|
+
acc(obj.x, obj.y);
|
|
1176
|
+
acc(obj.x + obj.width, obj.y + obj.height);
|
|
1177
|
+
}
|
|
1178
|
+
} else if (obj.type === "table") {
|
|
1179
|
+
const off = TABLE_SEAT_OFFSET + SEAT_R;
|
|
1180
|
+
const ext = obj.shape === "round" ? (obj.radius ?? 40) + off : Math.max((obj.width ?? 80) / 2, (obj.height ?? 50) / 2) + off;
|
|
1181
|
+
acc(obj.center.x - ext, obj.center.y - ext);
|
|
1182
|
+
acc(obj.center.x + ext, obj.center.y + ext);
|
|
1183
|
+
} else if (obj.type === "booth") {
|
|
1184
|
+
const ext = Math.max(obj.width, obj.height) / 2;
|
|
1185
|
+
acc(obj.center.x - ext, obj.center.y - ext);
|
|
1186
|
+
acc(obj.center.x + ext, obj.center.y + ext);
|
|
1187
|
+
} else if (obj.type === "text") {
|
|
1188
|
+
const w = obj.fontSize * obj.text.length * 0.6;
|
|
1189
|
+
acc(obj.position.x, obj.position.y);
|
|
1190
|
+
acc(obj.position.x + w, obj.position.y + obj.fontSize);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
for (const image of [doc.referenceImage, doc.backgroundImage]) {
|
|
1194
|
+
if (!image) continue;
|
|
1195
|
+
const { center, width } = image;
|
|
1196
|
+
const bh = width * 3 / 4;
|
|
1197
|
+
acc(center.x - width / 2, center.y - bh / 2);
|
|
1198
|
+
acc(center.x + width / 2, center.y + bh / 2);
|
|
1199
|
+
}
|
|
1200
|
+
if (!isFinite(minX)) {
|
|
1201
|
+
const f = doc.focalPoint ?? { x: 0, y: 0 };
|
|
1202
|
+
return { x: f.x - 200, y: f.y - 200, width: 400, height: 400 };
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
1205
|
+
x: minX - PAD,
|
|
1206
|
+
y: minY - PAD,
|
|
1207
|
+
width: maxX - minX + PAD * 2,
|
|
1208
|
+
height: maxY - minY + PAD * 2
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
export {
|
|
1213
|
+
ACCESSIBILITY_TYPES,
|
|
1214
|
+
accessibilityMeta,
|
|
1215
|
+
ACCESSIBILITY_RING_COLOR,
|
|
1216
|
+
accessibilityRingColor,
|
|
1217
|
+
LABEL_STYLE_MIN_SIZE,
|
|
1218
|
+
LABEL_STYLE_MAX_SIZE,
|
|
1219
|
+
SURROUNDINGS_SHAPE_ROLES,
|
|
1220
|
+
layerOf,
|
|
1221
|
+
CHART_STORAGE_KEY,
|
|
1222
|
+
METRES_PER_CHART_UNIT,
|
|
1223
|
+
CHART_UNITS_PER_METRE,
|
|
1224
|
+
TIER_HEIGHT_M,
|
|
1225
|
+
SEATED_EYE_HEIGHT_M,
|
|
1226
|
+
sectionElevationTier,
|
|
1227
|
+
sectionGeometry,
|
|
1228
|
+
distanceToPolyline,
|
|
1229
|
+
resolveSection,
|
|
1230
|
+
rowSeatPositions,
|
|
1231
|
+
rowInventoryCount,
|
|
1232
|
+
seatLabelPart,
|
|
1233
|
+
expandRowSlots,
|
|
1234
|
+
expandRow,
|
|
1235
|
+
tableSeatCountsBySide,
|
|
1236
|
+
expandTableSlots,
|
|
1237
|
+
tableInventoryCount,
|
|
1238
|
+
expandTable,
|
|
1239
|
+
expandBooth,
|
|
1240
|
+
pointInPolygon,
|
|
1241
|
+
pointInPolygonWithHoles,
|
|
1242
|
+
polygonLabelPoint,
|
|
1243
|
+
objectCenter,
|
|
1244
|
+
owningSectionForObject,
|
|
1245
|
+
floorsOf,
|
|
1246
|
+
floorObjects,
|
|
1247
|
+
allObjects,
|
|
1248
|
+
stackFloors,
|
|
1249
|
+
expandChart,
|
|
1250
|
+
chartBounds
|
|
1251
|
+
};
|
|
1252
|
+
//# sourceMappingURL=chunk-FGS67GT3.js.map
|