@marble.fun/track-catalog 3.0.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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -0
  3. package/assets.json +346 -0
  4. package/package.json +32 -0
  5. package/release.json +135 -0
  6. package/src/audio/game-audio-preference.ts +9 -0
  7. package/src/authoritative.ts +6 -0
  8. package/src/catalog.ts +1 -0
  9. package/src/index.ts +14 -0
  10. package/src/legacy-stock-catalog.ts +130 -0
  11. package/src/pages/lobby/lobby-types.ts +140 -0
  12. package/src/pages/lobby/marble-factory.ts +931 -0
  13. package/src/pages/play/race/egypt-elevator-blender.ts +159 -0
  14. package/src/pages/play/race/egypt-portal-frame.ts +382 -0
  15. package/src/pages/play/race/finish-detection.ts +1 -0
  16. package/src/pages/play/race/japanese-sakura-castle-support-blender.ts +510 -0
  17. package/src/pages/play/race/japanese-sakura-kawara-roof.ts +201 -0
  18. package/src/pages/play/race/japanese-sakura-portal-frame.ts +325 -0
  19. package/src/pages/play/race/jungle-portal-frame.ts +308 -0
  20. package/src/pages/play/race/jungle-temple-lift.ts +366 -0
  21. package/src/pages/play/race/marble-entity.ts +257 -0
  22. package/src/pages/play/race/marble-material.ts +20 -0
  23. package/src/pages/play/race/marble-race-engine.ts +2652 -0
  24. package/src/pages/play/race/path-pattern-layout.ts +142 -0
  25. package/src/pages/play/race/peg-obstacle-visual.ts +171 -0
  26. package/src/pages/play/race/physics-debug.ts +1 -0
  27. package/src/pages/play/race/physics-dry-run.ts +90 -0
  28. package/src/pages/play/race/portal-derived-elevator-skins.ts +729 -0
  29. package/src/pages/play/race/portal-pair-color.ts +20 -0
  30. package/src/pages/play/race/portal-proportions.ts +25 -0
  31. package/src/pages/play/race/portal-visual.ts +1108 -0
  32. package/src/pages/play/race/race-camera-director.ts +586 -0
  33. package/src/pages/play/race/race-camera-obstruction.ts +37 -0
  34. package/src/pages/play/race/race-camera-tour-planner.ts +274 -0
  35. package/src/pages/play/race/race-camera.ts +206 -0
  36. package/src/pages/play/race/race-completion.ts +5 -0
  37. package/src/pages/play/race/race-configuration.ts +57 -0
  38. package/src/pages/play/race/race-marble-focus-renderer.ts +134 -0
  39. package/src/pages/play/race/race-physics-profile.ts +7 -0
  40. package/src/pages/play/race/race-physics.ts +16 -0
  41. package/src/pages/play/race/race-playback.ts +41 -0
  42. package/src/pages/play/race/race-rolling-audio.ts +616 -0
  43. package/src/pages/play/race/race-simulation-renderer.ts +221 -0
  44. package/src/pages/play/race/race-terminal-checker-strip.ts +226 -0
  45. package/src/pages/play/race/race-transform-interpolation.ts +50 -0
  46. package/src/pages/play/race/race-types.ts +12 -0
  47. package/src/pages/play/race/rebound-visual.ts +70 -0
  48. package/src/pages/play/race/seeded-random.ts +23 -0
  49. package/src/pages/play/race/space-station-portal-frame.ts +275 -0
  50. package/src/pages/play/race/track-appearance-canopy.ts +946 -0
  51. package/src/pages/play/race/track-appearance-floor-runner.ts +206 -0
  52. package/src/pages/play/race/track-appearance-jungle-flower-hedge.ts +483 -0
  53. package/src/pages/play/race/track-appearance-rope-wall.ts +443 -0
  54. package/src/pages/play/race/track-appearance-sakura-bridge-fence.ts +305 -0
  55. package/src/pages/play/race/track-appearance-sakura-castle-wall.ts +1031 -0
  56. package/src/pages/play/race/track-appearance-space-station-wall.ts +497 -0
  57. package/src/pages/play/race/track-appearance-stone-block-shell.ts +517 -0
  58. package/src/pages/play/race/track-appearance-support.ts +983 -0
  59. package/src/pages/play/race/track-appearance-vampire-carved-wall.ts +545 -0
  60. package/src/pages/play/race/track-appearance-wall-terminal.ts +167 -0
  61. package/src/pages/play/race/track-course.ts +271 -0
  62. package/src/pages/play/race/track-definition.ts +443 -0
  63. package/src/pages/play/race/track-fence-architecture.ts +33 -0
  64. package/src/pages/play/race/track-floor-material-rules.ts +51 -0
  65. package/src/pages/play/race/track-mechanisms.ts +1350 -0
  66. package/src/pages/play/race/track-physics-surface.ts +6 -0
  67. package/src/pages/play/race/track-route-network.ts +9 -0
  68. package/src/pages/play/race/track-shadow-batching.ts +760 -0
  69. package/src/pages/play/race/track-surface-basis.ts +1 -0
  70. package/src/pages/play/race/track-surface-topology.ts +326 -0
  71. package/src/pages/play/race/track-world.ts +10181 -0
  72. package/src/pages/play/race/under-track-support-architecture.ts +122 -0
  73. package/src/pages/play/race/vortex-profile.ts +5 -0
  74. package/src/pages/play/race/vortex-water-effect.ts +863 -0
  75. package/src/pages/play/race/wild-west-mine-lift.ts +281 -0
  76. package/src/pages/play/race/wild-west-vortex-housing.ts +572 -0
  77. package/src/pages/track-lab/atlas-vortex-layout.ts +1 -0
  78. package/src/pages/track-lab/cone-field-layout.ts +1 -0
  79. package/src/pages/track-lab/conveyor-layout.ts +1 -0
  80. package/src/pages/track-lab/engine/canonical-track-assembly.ts +64 -0
  81. package/src/pages/track-lab/engine/editor-object-geometry.ts +170 -0
  82. package/src/pages/track-lab/engine/track-lab-connection-state.ts +123 -0
  83. package/src/pages/track-lab/engine/track-piece-visual.ts +1420 -0
  84. package/src/pages/track-lab/flipper-bank-layout.ts +1 -0
  85. package/src/pages/track-lab/flipper-component.ts +127 -0
  86. package/src/pages/track-lab/rising-wall-layout.ts +1 -0
  87. package/src/pages/track-lab/simple-obstacle-layout.ts +1 -0
  88. package/src/pages/track-lab/track-piece-layout.ts +28 -0
  89. package/src/pages/track-lab/track-runtime-adapter.ts +1152 -0
  90. package/src/three/geometry-attribute-aliases.ts +24 -0
  91. package/src/three/geometry-detail.ts +36 -0
  92. package/src/three/homepage-marble-geometry.ts +15 -0
  93. package/src/three/japanese-sakura-appearance.ts +252 -0
  94. package/src/three/jungle-flower-hedge-appearance.ts +7 -0
  95. package/src/three/jungle-temple-appearance.ts +5 -0
  96. package/src/three/low-quality-scene-renderer.ts +132 -0
  97. package/src/three/marble-sphere-geometry.ts +13 -0
  98. package/src/three/mobile-rendering-budget.ts +11 -0
  99. package/src/three/painted-valley-appearance.ts +270 -0
  100. package/src/three/railroad-appearance.ts +71 -0
  101. package/src/three/renderer-resize.ts +32 -0
  102. package/src/three/rope-wall-appearance.ts +91 -0
  103. package/src/three/rugged-track-appearance.ts +2 -0
  104. package/src/three/scene-lighting.ts +183 -0
  105. package/src/three/scene-shader-preparation.ts +64 -0
  106. package/src/three/scene-theme-controller.ts +461 -0
  107. package/src/three/shadow-shader-preparation.ts +162 -0
  108. package/src/three/shared-obstacle-metal.ts +44 -0
  109. package/src/three/soft-additive-particles.ts +57 -0
  110. package/src/three/space-station-appearance.ts +213 -0
  111. package/src/three/static-track-batched-mesh.ts +132 -0
  112. package/src/three/stone-track-shell-appearance.ts +12 -0
  113. package/src/three/theme-ambience-controller.ts +226 -0
  114. package/src/three/theme-asset-cache.ts +120 -0
  115. package/src/three/theme-audio-preference.ts +43 -0
  116. package/src/three/theme-catalog.ts +1457 -0
  117. package/src/three/theme-material-assets.ts +258 -0
  118. package/src/three/theme-material-ktx2-manifest.ts +190 -0
  119. package/src/three/theme-material-registry.ts +1952 -0
  120. package/src/three/theme-world-factory.ts +590 -0
  121. package/src/three/track-connector-batch.ts +125 -0
  122. package/src/three/track-material-continuity.ts +442 -0
  123. package/src/three/track-theme.ts +535 -0
  124. package/src/three/vampire-castle-appearance.ts +258 -0
  125. package/src/three/visual-frame-pacing.ts +19 -0
  126. package/src/three/visual-geometry.ts +120 -0
  127. package/src/three/visual-quality.ts +83 -0
  128. package/src/three/water-appearance.ts +29 -0
  129. package/src/three/wild-west-sand-appearance.ts +2 -0
@@ -0,0 +1,170 @@
1
+ import * as THREE from 'three'
2
+
3
+ import { isStaticTrackColorBatchSource } from '../../play/race/track-shadow-batching'
4
+
5
+ const objectBounds = new THREE.Box3()
6
+
7
+ export function computeEditorBounds(root: THREE.Object3D): THREE.Box3 {
8
+ root.updateWorldMatrix(true, true)
9
+ const bounds = new THREE.Box3()
10
+ const canonicalOverride = editorBoundsOverride(root)
11
+ if (canonicalOverride) {
12
+ bounds.min.fromArray(canonicalOverride.min)
13
+ bounds.max.fromArray(canonicalOverride.max)
14
+ return bounds.applyMatrix4(root.matrixWorld)
15
+ }
16
+
17
+ root.traverse(object => {
18
+ if (!isEditorGeometry(object)) return
19
+ expandByGeometryObject(bounds, object)
20
+ })
21
+
22
+ return bounds
23
+ }
24
+
25
+ export function collectEditorSelectables(root: THREE.Object3D): THREE.Object3D[] {
26
+ const selectables: THREE.Object3D[] = []
27
+ root.traverse(object => {
28
+ if (object instanceof THREE.Mesh && !hasEditorExclusion(object, 'decorative')) {
29
+ selectables.push(object)
30
+ }
31
+ })
32
+ return selectables
33
+ }
34
+
35
+ /** Builds a swept canonical selection cage when a surfaced piece provides one. */
36
+ export function createCanonicalEditorOutlineGeometry(
37
+ root: THREE.Object3D,
38
+ relativeTo: THREE.Object3D,
39
+ padding: number,
40
+ ): THREE.BufferGeometry | undefined {
41
+ // Composite mechanisms can contain canonical lane proxies without those
42
+ // proxies describing the mechanism's complete footprint. In that case the
43
+ // caller must use the full canonical editor bounds instead of mistaking a
44
+ // nested entry or outlet lane for the whole selection outline.
45
+ if (root.userData.editorOutlineMode === 'bounds') return undefined
46
+
47
+ root.updateWorldMatrix(true, true)
48
+ relativeTo.updateWorldMatrix(true, true)
49
+ const inverseRelative = relativeTo.matrixWorld.clone().invert()
50
+ const positions: number[] = []
51
+ const right = new THREE.Vector3()
52
+ const up = new THREE.Vector3()
53
+ const transform = new THREE.Matrix4()
54
+ const appendSegment = (left: THREE.Vector3, rightPoint: THREE.Vector3): void => {
55
+ positions.push(left.x, left.y, left.z, rightPoint.x, rightPoint.y, rightPoint.z)
56
+ }
57
+
58
+ root.traverse(object => {
59
+ const rawSections = object.userData.editorBoundsSections as unknown
60
+ if (!Array.isArray(rawSections) || rawSections.length < 2) return
61
+ const sections = rawSections.flatMap(rawSection => {
62
+ if (!Array.isArray(rawSection) || rawSection.length !== 4) return []
63
+ const section = rawSection.map(rawPoint => {
64
+ if (!Array.isArray(rawPoint) || rawPoint.length !== 3 || !rawPoint.every(Number.isFinite)) return undefined
65
+ return new THREE.Vector3().fromArray(rawPoint)
66
+ })
67
+ if (section.some(candidate => candidate === undefined)) return []
68
+ const prepared = section as THREE.Vector3[]
69
+ right.copy(prepared[1]!).sub(prepared[0]!).normalize()
70
+ up.copy(prepared[2]!).sub(prepared[0]!).normalize()
71
+ prepared[0]!.addScaledVector(right, -padding).addScaledVector(up, -padding)
72
+ prepared[1]!.addScaledVector(right, padding).addScaledVector(up, -padding)
73
+ prepared[2]!.addScaledVector(right, -padding).addScaledVector(up, padding)
74
+ prepared[3]!.addScaledVector(right, padding).addScaledVector(up, padding)
75
+ return [prepared]
76
+ })
77
+ if (sections.length < 2) return
78
+ transform.copy(inverseRelative).multiply(object.matrixWorld)
79
+ sections.forEach(section => section.forEach(candidate => candidate.applyMatrix4(transform)))
80
+ // A swept cage needs every section to keep its four longitudinal edges on
81
+ // a curve or grade, but drawing a complete rectangle at every tessellation
82
+ // sample turns a long piece into a stack of wire boxes. Only the two real
83
+ // terminals own cross-sections; interior samples bend the four rails.
84
+ for (const section of [sections[0]!, sections.at(-1)!]) {
85
+ appendSegment(section[0]!, section[1]!)
86
+ appendSegment(section[2]!, section[3]!)
87
+ appendSegment(section[0]!, section[2]!)
88
+ appendSegment(section[1]!, section[3]!)
89
+ }
90
+ for (let index = 0; index < sections.length - 1; index += 1) {
91
+ for (let corner = 0; corner < 4; corner += 1) {
92
+ appendSegment(sections[index]![corner]!, sections[index + 1]![corner]!)
93
+ }
94
+ }
95
+ })
96
+ if (positions.length === 0) return undefined
97
+ const geometry = new THREE.BufferGeometry()
98
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3))
99
+ geometry.userData.terminalCrossSectionCount = 2
100
+ geometry.userData.intermediateCrossSectionCount = 0
101
+ geometry.computeBoundingBox()
102
+ return geometry
103
+ }
104
+
105
+ export function isEditorObjectVisible(object: THREE.Object3D): boolean {
106
+ for (let current: THREE.Object3D | null = object; current; current = current.parent) {
107
+ // Static color batching hides the rendered source mesh but deliberately
108
+ // retains it as the exact per-piece ray-picking surface.
109
+ if (!current.visible && !(current === object && isStaticTrackColorBatchSource(current))) return false
110
+ }
111
+ return true
112
+ }
113
+
114
+ function isEditorGeometry(object: THREE.Object3D): object is THREE.Mesh | THREE.Line | THREE.Points {
115
+ const visualOnly = hasEditorExclusion(object, 'visualOnly')
116
+ return (
117
+ !hasEditorExclusion(object, 'decorative') &&
118
+ !hasEditorExclusion(object, 'excludeFromEditorBounds') &&
119
+ (!visualOnly || editorBoundsOverride(object) !== undefined) &&
120
+ (object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points)
121
+ )
122
+ }
123
+
124
+ function hasEditorExclusion(
125
+ object: THREE.Object3D,
126
+ key: 'decorative' | 'excludeFromEditorBounds' | 'visualOnly',
127
+ ): boolean {
128
+ for (let current: THREE.Object3D | null = object; current; current = current.parent) {
129
+ if (current.userData[key] === true) return true
130
+ }
131
+ return false
132
+ }
133
+
134
+ function expandByGeometryObject(bounds: THREE.Box3, object: THREE.Mesh | THREE.Line | THREE.Points): void {
135
+ const override = editorBoundsOverride(object)
136
+ if (override) {
137
+ objectBounds.min.fromArray(override.min)
138
+ objectBounds.max.fromArray(override.max)
139
+ bounds.union(objectBounds.applyMatrix4(object.matrixWorld))
140
+ return
141
+ }
142
+ if (object instanceof THREE.InstancedMesh) {
143
+ if (object.boundingBox === null) object.computeBoundingBox()
144
+ if (object.boundingBox) bounds.union(objectBounds.copy(object.boundingBox).applyMatrix4(object.matrixWorld))
145
+ return
146
+ }
147
+
148
+ if (object.geometry.boundingBox === null) object.geometry.computeBoundingBox()
149
+ if (object.geometry.boundingBox) {
150
+ bounds.union(objectBounds.copy(object.geometry.boundingBox).applyMatrix4(object.matrixWorld))
151
+ }
152
+ }
153
+
154
+ function editorBoundsOverride(
155
+ object: THREE.Object3D,
156
+ ): { min: readonly [number, number, number]; max: readonly [number, number, number] } | undefined {
157
+ const override = object.userData.editorBoundsOverride as
158
+ { min?: readonly number[]; max?: readonly number[] } | undefined
159
+ if (
160
+ override?.min?.length !== 3 ||
161
+ override.max?.length !== 3 ||
162
+ ![...override.min, ...override.max].every(Number.isFinite)
163
+ ) {
164
+ return undefined
165
+ }
166
+ return {
167
+ min: override.min as readonly [number, number, number],
168
+ max: override.max as readonly [number, number, number],
169
+ }
170
+ }
@@ -0,0 +1,123 @@
1
+ import {
2
+ connectorWorldDirection,
3
+ connectorWorldPosition,
4
+ initialTrackCatalog,
5
+ resolveTemplateConnectors,
6
+ rotateTrackVector,
7
+ type TrackDocument,
8
+ } from '@marble.fun/physics-engine/browser'
9
+
10
+ import { fluidSurfaceStyleFromId } from '../../../three/water-appearance'
11
+ import { underTrackSupportStylesConnect } from '../../play/race/under-track-support-architecture'
12
+ import { createSharedRailConnectorFrame, type RailConnectorFrame } from '../track-piece-layout'
13
+
14
+ export interface TrackLabVisualConnectionState {
15
+ connectedConnectorIdsByPiece: Map<string, Set<string>>
16
+ continuousFluidConnectorIdsByPiece: Map<string, Set<string>>
17
+ continuousSupportConnectorIdsByPiece: Map<string, Set<string>>
18
+ connectorFramesByPiece: Map<string, Map<string, RailConnectorFrame>>
19
+ }
20
+
21
+ function supportsCanonicalRailPorts(materialSlots: readonly string[]): boolean {
22
+ // Match Test Run's surfaced-piece contract. The previous editor-only
23
+ // runtime-kind allowlist excluded cones, elevators, starts, finishes,
24
+ // portals, and vortices, so Material Debug showed gaps that runtime tests
25
+ // could not reproduce.
26
+ return materialSlots.includes('lane.surface')
27
+ }
28
+
29
+ export function visualConnectionState(document: TrackDocument): TrackLabVisualConnectionState {
30
+ const connectedConnectorIdsByPiece = new Map<string, Set<string>>()
31
+ const continuousFluidConnectorIdsByPiece = new Map<string, Set<string>>()
32
+ const continuousSupportConnectorIdsByPiece = new Map<string, Set<string>>()
33
+ const connectorFramesByPiece = new Map<string, Map<string, RailConnectorFrame>>()
34
+ const piecesById = new Map(document.pieces.map(piece => [piece.id, piece]))
35
+ for (const connection of document.connections) {
36
+ for (const endpoint of [connection.from, connection.to]) {
37
+ const connectorIds = connectedConnectorIdsByPiece.get(endpoint.pieceId) ?? new Set<string>()
38
+ connectorIds.add(endpoint.connectorId)
39
+ connectedConnectorIdsByPiece.set(endpoint.pieceId, connectorIds)
40
+ }
41
+ const fromPiece = piecesById.get(connection.from.pieceId)
42
+ const toPiece = piecesById.get(connection.to.pieceId)
43
+ if (!fromPiece || !toPiece) continue
44
+ const fromTemplate = initialTrackCatalog.require(fromPiece)
45
+ const toTemplate = initialTrackCatalog.require(toPiece)
46
+ const fromConnector = resolveTemplateConnectors(fromTemplate, fromPiece.parameters).find(
47
+ connector => connector.id === connection.from.connectorId,
48
+ )
49
+ const toConnector = resolveTemplateConnectors(toTemplate, toPiece.parameters).find(
50
+ connector => connector.id === connection.to.connectorId,
51
+ )
52
+ const fromFluidStyle = fluidSurfaceStyleFromId(
53
+ fromTemplate.runtimeKind === 'vortex' && fromConnector?.kind.endsWith('input')
54
+ ? fromPiece.appearance?.vortexWaterId
55
+ : fromPiece.appearance?.waterOverlayId,
56
+ )
57
+ const toFluidStyle = fluidSurfaceStyleFromId(
58
+ toTemplate.runtimeKind === 'vortex' && toConnector?.kind.endsWith('input')
59
+ ? toPiece.appearance?.vortexWaterId
60
+ : toPiece.appearance?.waterOverlayId,
61
+ )
62
+ if (fromFluidStyle && fromFluidStyle === toFluidStyle) {
63
+ for (const endpoint of [connection.from, connection.to]) {
64
+ const connectorIds = continuousFluidConnectorIdsByPiece.get(endpoint.pieceId) ?? new Set<string>()
65
+ connectorIds.add(endpoint.connectorId)
66
+ continuousFluidConnectorIdsByPiece.set(endpoint.pieceId, connectorIds)
67
+ }
68
+ }
69
+ if (
70
+ underTrackSupportStylesConnect(fromPiece.appearance?.supportMaterialId, toPiece.appearance?.supportMaterialId)
71
+ ) {
72
+ for (const endpoint of [connection.from, connection.to]) {
73
+ const connectorIds = continuousSupportConnectorIdsByPiece.get(endpoint.pieceId) ?? new Set<string>()
74
+ connectorIds.add(endpoint.connectorId)
75
+ continuousSupportConnectorIdsByPiece.set(endpoint.pieceId, connectorIds)
76
+ }
77
+ }
78
+ if (!fromConnector || !toConnector) continue
79
+ if (fromConnector.troughProfile !== 'concave' || toConnector.troughProfile !== 'concave') continue
80
+ const fromSupportsCanonicalPorts = supportsCanonicalRailPorts(fromTemplate.materialSlots)
81
+ const toSupportsCanonicalPorts = supportsCanonicalRailPorts(toTemplate.materialSlots)
82
+ if (!fromSupportsCanonicalPorts && !toSupportsCanonicalPorts) continue
83
+ const worldFrame = createSharedRailConnectorFrame(
84
+ fromConnector,
85
+ toConnector,
86
+ connectorWorldDirection(fromPiece, fromConnector),
87
+ )
88
+ const fromPosition = connectorWorldPosition(fromPiece, fromConnector)
89
+ const toPosition = connectorWorldPosition(toPiece, toConnector)
90
+ const sharedWorldPosition: [number, number, number] = [
91
+ (fromPosition[0] + toPosition[0]) / 2,
92
+ (fromPosition[1] + toPosition[1]) / 2,
93
+ (fromPosition[2] + toPosition[2]) / 2,
94
+ ]
95
+ for (const [piece, connectorId, supportsCanonicalPorts] of [
96
+ [fromPiece, fromConnector.id, fromSupportsCanonicalPorts],
97
+ [toPiece, toConnector.id, toSupportsCanonicalPorts],
98
+ ] as const) {
99
+ if (!supportsCanonicalPorts) continue
100
+ const frames = connectorFramesByPiece.get(piece.id) ?? new Map<string, RailConnectorFrame>()
101
+ const inverseYaw = ((4 - piece.transform.yawSteps) % 4) as 0 | 1 | 2 | 3
102
+ frames.set(connectorId, {
103
+ ...worldFrame,
104
+ position: rotateTrackVector(
105
+ [
106
+ sharedWorldPosition[0] - piece.transform.position[0],
107
+ sharedWorldPosition[1] - piece.transform.position[1],
108
+ sharedWorldPosition[2] - piece.transform.position[2],
109
+ ],
110
+ inverseYaw,
111
+ ),
112
+ tangent: rotateTrackVector(worldFrame.tangent, inverseYaw),
113
+ })
114
+ connectorFramesByPiece.set(piece.id, frames)
115
+ }
116
+ }
117
+ return {
118
+ connectedConnectorIdsByPiece,
119
+ continuousFluidConnectorIdsByPiece,
120
+ continuousSupportConnectorIdsByPiece,
121
+ connectorFramesByPiece,
122
+ }
123
+ }