@terraware/web-components 4.2.12 → 4.2.14-rc.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/components/AntSwitch/index.js +8 -14
- package/components/Autocomplete/Autocomplete.js +67 -70
- package/components/Badge/index.js +16 -20
- package/components/BusySpinner/index.js +30 -35
- package/components/Button/Button.js +23 -27
- package/components/Button/button.test.js +8 -10
- package/components/Checkbox.js +10 -19
- package/components/Confirm/index.js +44 -51
- package/components/DatePicker/DatePicker.js +116 -117
- package/components/DialogBox/DialogBox.js +76 -66
- package/components/Divisor.js +15 -20
- package/components/Dropdown.js +54 -61
- package/components/EditableTable/index.js +129 -135
- package/components/ErrorBox/ErrorBox.js +37 -36
- package/components/FileChooser/index.js +73 -77
- package/components/FormBottomBar/index.js +63 -69
- package/components/Icon/Icon.js +14 -21
- package/components/Icon/icons/index.d.ts +1 -1
- package/components/Icon/icons/index.d.ts.map +1 -1
- package/components/Icon/icons/index.js +251 -255
- package/components/IconTooltip/index.js +29 -34
- package/components/Map/MapBox.js +199 -220
- package/components/Map/MapContainer.js +14 -18
- package/components/Map/MapDateSliderControl.js +44 -46
- package/components/Map/MapDrawer.js +32 -33
- package/components/Map/MapLegend.js +96 -95
- package/components/Map/MapViewStyleControl.js +38 -48
- package/components/Map/ReactMapboxWrapper.js +2 -9
- package/components/Map/index.js +47 -55
- package/components/Map/types.js +2 -8
- package/components/Map/useMaintainLayerOrder.js +4 -11
- package/components/Markdown/index.js +14 -21
- package/components/Message/Message.js +59 -51
- package/components/MultiSelect/MultiSelect.test.js +22 -24
- package/components/MultiSelect/index.js +73 -69
- package/components/Navbar/NavFooter.js +8 -13
- package/components/Navbar/NavItem.js +43 -37
- package/components/Navbar/NavSection.js +13 -17
- package/components/Navbar/Navbar.js +35 -38
- package/components/Navbar/SubNavbar.js +8 -13
- package/components/Note.js +14 -19
- package/components/OverlayModal/OverlayModal.js +36 -37
- package/components/PageForm/index.js +30 -35
- package/components/PhotoChooser/index.js +93 -98
- package/components/Pill/index.js +29 -31
- package/components/PillList/index.js +16 -22
- package/components/PlacementWrapper/index.js +20 -25
- package/components/PopoverMenu/Popover.js +33 -37
- package/components/PopoverMenu/index.js +41 -46
- package/components/PopoverMultiSelect/index.js +18 -21
- package/components/ProgressCircle/ProgressCircle.js +23 -26
- package/components/RadioButton.js +7 -14
- package/components/Select/Select.js +8 -13
- package/components/Select/SelectT.js +110 -108
- package/components/Separator/index.js +8 -14
- package/components/Size.js +1 -5
- package/components/Slider/index.js +7 -13
- package/components/SummaryBox.js +36 -39
- package/components/Tabs/index.js +75 -77
- package/components/TextTruncated/index.js +64 -65
- package/components/Textfield/Textfield.js +100 -95
- package/components/Textfield/TruncatedTextArea.js +44 -46
- package/components/TimelineSlider/TimelineMarker.js +24 -28
- package/components/TimelineSlider/TimelineRail.js +5 -11
- package/components/TimelineSlider/index.js +47 -51
- package/components/Tooltip/Tooltip.js +21 -26
- package/components/ViewPhotosDialog/index.js +58 -62
- package/components/VirtualWalkthrough/Annotation.d.ts +41 -0
- package/components/VirtualWalkthrough/Annotation.d.ts.map +1 -0
- package/components/VirtualWalkthrough/Annotation.js +197 -0
- package/components/VirtualWalkthrough/AnnotationEditPane.d.ts +20 -0
- package/components/VirtualWalkthrough/AnnotationEditPane.d.ts.map +1 -0
- package/components/VirtualWalkthrough/AnnotationEditPane.js +136 -0
- package/components/VirtualWalkthrough/AnnotationPanel.d.ts +13 -0
- package/components/VirtualWalkthrough/AnnotationPanel.d.ts.map +1 -0
- package/components/VirtualWalkthrough/AnnotationPanel.js +196 -0
- package/components/VirtualWalkthrough/Application.d.ts +9 -0
- package/components/VirtualWalkthrough/Application.d.ts.map +1 -0
- package/components/VirtualWalkthrough/Application.js +32 -0
- package/components/VirtualWalkthrough/AutoRotator.d.ts +109 -0
- package/components/VirtualWalkthrough/AutoRotator.d.ts.map +1 -0
- package/components/VirtualWalkthrough/AutoRotator.js +217 -0
- package/components/VirtualWalkthrough/BlockingSpinner.d.ts +3 -0
- package/components/VirtualWalkthrough/BlockingSpinner.d.ts.map +1 -0
- package/components/VirtualWalkthrough/BlockingSpinner.js +27 -0
- package/components/VirtualWalkthrough/BoundaryRing.d.ts +9 -0
- package/components/VirtualWalkthrough/BoundaryRing.d.ts.map +1 -0
- package/components/VirtualWalkthrough/BoundaryRing.js +33 -0
- package/components/VirtualWalkthrough/CameraInfo.d.ts +16 -0
- package/components/VirtualWalkthrough/CameraInfo.d.ts.map +1 -0
- package/components/VirtualWalkthrough/CameraInfo.js +88 -0
- package/components/VirtualWalkthrough/ControlsInfoPane.d.ts +37 -0
- package/components/VirtualWalkthrough/ControlsInfoPane.d.ts.map +1 -0
- package/components/VirtualWalkthrough/ControlsInfoPane.js +196 -0
- package/components/VirtualWalkthrough/GradientSky.d.ts +9 -0
- package/components/VirtualWalkthrough/GradientSky.d.ts.map +1 -0
- package/components/VirtualWalkthrough/GradientSky.js +73 -0
- package/components/VirtualWalkthrough/SplatControls.d.ts +50 -0
- package/components/VirtualWalkthrough/SplatControls.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatControls.js +252 -0
- package/components/VirtualWalkthrough/SplatCrop.d.ts +8 -0
- package/components/VirtualWalkthrough/SplatCrop.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatCrop.js +18 -0
- package/components/VirtualWalkthrough/SplatFadeCrop.d.ts +8 -0
- package/components/VirtualWalkthrough/SplatFadeCrop.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatFadeCrop.js +18 -0
- package/components/VirtualWalkthrough/SplatModel.d.ts +15 -0
- package/components/VirtualWalkthrough/SplatModel.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatModel.js +60 -0
- package/components/VirtualWalkthrough/SplatRevealRain.d.ts +18 -0
- package/components/VirtualWalkthrough/SplatRevealRain.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatRevealRain.js +38 -0
- package/components/VirtualWalkthrough/TfAnnotationManager.d.ts +110 -0
- package/components/VirtualWalkthrough/TfAnnotationManager.d.ts.map +1 -0
- package/components/VirtualWalkthrough/TfAnnotationManager.js +368 -0
- package/components/VirtualWalkthrough/TfXrNavigation.d.ts +15 -0
- package/components/VirtualWalkthrough/TfXrNavigation.d.ts.map +1 -0
- package/components/VirtualWalkthrough/TfXrNavigation.js +24 -0
- package/components/VirtualWalkthrough/annotation-styles.css +5 -0
- package/components/VirtualWalkthrough/application-styles.css +4 -0
- package/components/VirtualWalkthrough/boundary-ring.d.ts +47 -0
- package/components/VirtualWalkthrough/boundary-ring.d.ts.map +1 -0
- package/components/VirtualWalkthrough/boundary-ring.js +125 -0
- package/components/VirtualWalkthrough/boundary-ring.test.js +109 -0
- package/components/VirtualWalkthrough/groundPlane.d.ts +14 -0
- package/components/VirtualWalkthrough/groundPlane.d.ts.map +1 -0
- package/components/VirtualWalkthrough/groundPlane.js +47 -0
- package/components/VirtualWalkthrough/groundPlane.test.js +45 -0
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.d.ts +19 -0
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.d.ts.map +1 -0
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +119 -0
- package/components/VirtualWalkthrough/walkthrough-camera.d.ts +77 -0
- package/components/VirtualWalkthrough/walkthrough-camera.d.ts.map +1 -0
- package/components/VirtualWalkthrough/walkthrough-camera.js +320 -0
- package/components/svg/BlobbyGrayIconImage.js +36 -41
- package/components/svg/BlobbyGrayIconImport.js +36 -41
- package/components/svg/BlobbyGrayIconUploadToTheCloud.js +36 -41
- package/components/svg/BlobbyIconCloudChecked.js +161 -158
- package/components/svg/BlobbyIconExportOrUploadPhoto.js +161 -158
- package/components/svg/BlobbyIconFolder.js +161 -158
- package/components/svg/BlobbyIconGraphReport.js +161 -158
- package/components/svg/BlobbyIconHappy.js +161 -158
- package/components/svg/BlobbyIconHeartMonitor.js +161 -158
- package/components/svg/BlobbyIconLeaf.js +161 -158
- package/components/svg/BlobbyIconLibrary.js +161 -158
- package/components/svg/BlobbyIconNursery.js +163 -160
- package/components/svg/BlobbyIconOrganization.js +161 -158
- package/components/svg/BlobbyIconParchment.js +161 -158
- package/components/svg/BlobbyIconPeople.js +161 -158
- package/components/svg/BlobbyIconSeedBank.js +161 -158
- package/components/svg/BlobbyIconSeedling.js +161 -158
- package/components/svg/BlobbyIconSeeds.js +161 -158
- package/components/svg/BlobbyIconSite.js +164 -161
- package/components/svg/BlobbyIconSparkles.js +161 -158
- package/components/svg/BlobbyIconWrench.js +161 -158
- package/components/svg/Bug.js +18 -27
- package/components/svg/Calendar.js +18 -27
- package/components/svg/CaretDown.js +18 -27
- package/components/svg/CaretLeft.js +18 -27
- package/components/svg/CaretRight.js +18 -27
- package/components/svg/CaretUp.js +18 -27
- package/components/svg/ChevronDown.js +18 -27
- package/components/svg/ChevronUp.js +18 -27
- package/components/svg/Close.js +18 -27
- package/components/svg/Critical.js +18 -27
- package/components/svg/Edit.js +161 -158
- package/components/svg/Error.js +18 -27
- package/components/svg/Filter.js +18 -27
- package/components/svg/Folder.js +18 -27
- package/components/svg/Help.js +18 -27
- package/components/svg/Home.js +18 -27
- package/components/svg/IconAdd.js +18 -27
- package/components/svg/IconArrowRight.js +19 -28
- package/components/svg/IconBusinessNetwork.js +18 -27
- package/components/svg/IconCalendar.d.ts +1 -1
- package/components/svg/IconCalendar.d.ts.map +1 -1
- package/components/svg/IconCalendar.js +18 -27
- package/components/svg/IconCancel.js +18 -27
- package/components/svg/IconChargingBattery.js +18 -27
- package/components/svg/IconChecklist.js +19 -28
- package/components/svg/IconCheckmark.js +18 -27
- package/components/svg/IconCoinInHand.js +18 -27
- package/components/svg/IconColumns.js +18 -27
- package/components/svg/IconComment.js +18 -27
- package/components/svg/IconDashboard.js +18 -27
- package/components/svg/IconDataMigration.js +18 -27
- package/components/svg/IconDocument.d.ts +8 -0
- package/components/svg/IconDocument.d.ts.map +1 -0
- package/components/svg/IconDocument.js +21 -0
- package/components/svg/IconDownloadFromTheCloud.js +19 -28
- package/components/svg/IconEdit.js +19 -28
- package/components/svg/IconExpand.js +19 -28
- package/components/svg/IconExport.js +18 -27
- package/components/svg/IconExternalLink.js +18 -27
- package/components/svg/IconEye.js +18 -27
- package/components/svg/IconEyeOff.js +18 -27
- package/components/svg/IconFile.js +18 -27
- package/components/svg/IconFolder.js +18 -27
- package/components/svg/IconFullScreen.js +18 -27
- package/components/svg/IconFutures.js +18 -27
- package/components/svg/IconGraphReport.js +18 -27
- package/components/svg/IconHeartMonitor.js +18 -27
- package/components/svg/IconHelp.js +19 -28
- package/components/svg/IconHistory.js +18 -27
- package/components/svg/IconImport.js +18 -27
- package/components/svg/IconIndex.js +18 -27
- package/components/svg/IconLayers.js +18 -27
- package/components/svg/IconLibrary.js +18 -27
- package/components/svg/IconList.js +18 -27
- package/components/svg/IconLivePlant.js +18 -27
- package/components/svg/IconLocations.js +18 -27
- package/components/svg/IconMail.js +18 -27
- package/components/svg/IconManager.js +18 -27
- package/components/svg/IconMarker.js +18 -27
- package/components/svg/IconMenu.js +18 -27
- package/components/svg/IconMenuHorizontal.js +18 -27
- package/components/svg/IconModule.js +18 -27
- package/components/svg/IconMyLocation.js +18 -27
- package/components/svg/IconNote.js +19 -28
- package/components/svg/IconNursery.js +18 -27
- package/components/svg/IconOrg.js +18 -27
- package/components/svg/IconParchment.js +18 -27
- package/components/svg/IconPhoto.js +18 -27
- package/components/svg/IconPlantsFilled.js +18 -27
- package/components/svg/IconRedo.js +18 -27
- package/components/svg/IconRestorationSite.js +18 -27
- package/components/svg/IconSeedBank.js +18 -27
- package/components/svg/IconSeedling.js +18 -27
- package/components/svg/IconSettings.js +18 -27
- package/components/svg/IconSlice.js +18 -27
- package/components/svg/IconSubmit.js +18 -27
- package/components/svg/IconSubtract.js +19 -28
- package/components/svg/IconSynced.js +18 -27
- package/components/svg/IconTrashCan.js +18 -27
- package/components/svg/IconTreasureMap.js +18 -27
- package/components/svg/IconUnavailable.js +18 -27
- package/components/svg/IconUndo.js +18 -27
- package/components/svg/IconVariable.js +18 -27
- package/components/svg/IconVideo.js +19 -28
- package/components/svg/IconWifi.js +18 -27
- package/components/svg/Info.js +18 -27
- package/components/svg/Key.js +18 -27
- package/components/svg/Leaf.js +18 -27
- package/components/svg/Lock.js +18 -27
- package/components/svg/Logo.js +40 -45
- package/components/svg/MenuVertical.js +18 -27
- package/components/svg/Notification.js +18 -27
- package/components/svg/Person.js +18 -27
- package/components/svg/Plus.js +18 -27
- package/components/svg/RestorationSite.js +18 -27
- package/components/svg/Search.js +18 -27
- package/components/svg/Seeds.js +18 -27
- package/components/svg/Site.js +18 -27
- package/components/svg/Sparkles.js +18 -27
- package/components/svg/Species.js +18 -27
- package/components/svg/Species2.js +161 -158
- package/components/svg/Spinner.js +27 -36
- package/components/svg/Star.js +18 -27
- package/components/svg/Success.js +18 -27
- package/components/svg/SuccessFilled.js +18 -27
- package/components/svg/TerrawareLogoDesktop.js +44 -49
- package/components/svg/TerrawareLogoMobile.js +20 -29
- package/components/svg/Touchscreen.js +18 -27
- package/components/svg/UploadCloud.js +161 -158
- package/components/svg/Warning.js +18 -27
- package/components/svg/WelcomeClipboard.js +130 -135
- package/components/svg/index.d.ts +1 -0
- package/components/svg/index.d.ts.map +1 -1
- package/components/svg/index.js +127 -888
- package/components/table/EnhancedTableToolbar.js +45 -47
- package/components/table/EnhancedTableToolbarV2.js +18 -26
- package/components/table/TableCellRenderer.js +137 -143
- package/components/table/TableHeader.js +45 -47
- package/components/table/TableHeaderItem.js +65 -64
- package/components/table/density.js +3 -11
- package/components/table/index.js +225 -218
- package/components/table/sort.js +8 -18
- package/components/table/sort.test.js +6 -8
- package/components/table/types.js +1 -5
- package/components/types/index.js +1 -5
- package/hooks/useBoolean.d.ts +4 -0
- package/hooks/useBoolean.d.ts.map +1 -0
- package/hooks/useBoolean.js +12 -0
- package/hooks/useCameraPosition.d.ts +8 -0
- package/hooks/useCameraPosition.d.ts.map +1 -0
- package/hooks/useCameraPosition.js +33 -0
- package/index.js +57 -398
- package/license-report.html +1 -1
- package/package.json +10 -3
- package/style-dictionary-dist/TerrawareTheme.js +1 -7
- package/theme.js +7 -14
- package/utils/color.d.ts +2 -0
- package/utils/color.d.ts.map +1 -0
- package/utils/color.js +14 -0
- package/utils/date.js +13 -24
- package/utils/date.test.js +40 -43
- package/utils/index.js +5 -53
- package/utils/preventDefaultEvent.js +1 -7
- package/utils/text.js +2 -9
- package/utils/text.test.js +9 -11
- package/utils/useDeviceInfo.js +10 -16
- package/virtualWalkthrough.d.ts +31 -0
- package/virtualWalkthrough.d.ts.map +1 -0
- package/virtualWalkthrough.js +28 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
import { boundaryRingMesh } from './boundary-ring';
|
|
3
|
+
const FLAT = [new Vec3(0, 0, 0), new Vec3(1, 0, 0), new Vec3(0, 0, 1)];
|
|
4
|
+
const toVertices = positions => {
|
|
5
|
+
const out = [];
|
|
6
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
7
|
+
out.push([positions[i], positions[i + 1], positions[i + 2]]);
|
|
8
|
+
}
|
|
9
|
+
return out;
|
|
10
|
+
};
|
|
11
|
+
describe('boundaryRingMesh', () => {
|
|
12
|
+
it('produces four vertices and six indices per dash', () => {
|
|
13
|
+
const geom = boundaryRingMesh({
|
|
14
|
+
center: new Vec3(0, 0, 0),
|
|
15
|
+
radius: 5,
|
|
16
|
+
width: 0.1,
|
|
17
|
+
groundPlane: FLAT,
|
|
18
|
+
dashCount: 16
|
|
19
|
+
});
|
|
20
|
+
expect(geom.positions).toHaveLength(16 * 4 * 3);
|
|
21
|
+
expect(geom.indices).toHaveLength(16 * 6);
|
|
22
|
+
});
|
|
23
|
+
it('places every vertex at the inner or outer radius from center in XZ', () => {
|
|
24
|
+
const center = new Vec3(2, 0, -3);
|
|
25
|
+
const radius = 4;
|
|
26
|
+
const width = 0.2;
|
|
27
|
+
const inner = radius - width / 2;
|
|
28
|
+
const outer = radius + width / 2;
|
|
29
|
+
const geom = boundaryRingMesh({
|
|
30
|
+
center,
|
|
31
|
+
radius,
|
|
32
|
+
width,
|
|
33
|
+
groundPlane: FLAT,
|
|
34
|
+
dashCount: 32
|
|
35
|
+
});
|
|
36
|
+
for (const [x,, z] of toVertices(geom.positions)) {
|
|
37
|
+
const d = Math.sqrt((x - center.x) ** 2 + (z - center.z) ** 2);
|
|
38
|
+
const atInner = Math.abs(d - inner) < 1e-6;
|
|
39
|
+
const atOuter = Math.abs(d - outer) < 1e-6;
|
|
40
|
+
expect(atInner || atOuter).toBe(true);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
it('lifts every vertex onto a tilted ground plane (y = z)', () => {
|
|
44
|
+
const tilted = [new Vec3(0, 0, 0), new Vec3(1, 0, 0), new Vec3(0, 1, 1)];
|
|
45
|
+
const geom = boundaryRingMesh({
|
|
46
|
+
center: new Vec3(0, 0, 0),
|
|
47
|
+
radius: 3,
|
|
48
|
+
width: 0.1,
|
|
49
|
+
groundPlane: tilted,
|
|
50
|
+
dashCount: 8
|
|
51
|
+
});
|
|
52
|
+
for (const [, y, z] of toVertices(geom.positions)) {
|
|
53
|
+
expect(y).toBeCloseTo(z);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
it('references only in-range vertices from the index buffer', () => {
|
|
57
|
+
const geom = boundaryRingMesh({
|
|
58
|
+
center: new Vec3(0, 0, 0),
|
|
59
|
+
radius: 5,
|
|
60
|
+
width: 0.1,
|
|
61
|
+
groundPlane: FLAT,
|
|
62
|
+
dashCount: 8
|
|
63
|
+
});
|
|
64
|
+
const vertexCount = geom.positions.length / 3;
|
|
65
|
+
for (const idx of geom.indices) {
|
|
66
|
+
expect(idx).toBeGreaterThanOrEqual(0);
|
|
67
|
+
expect(idx).toBeLessThan(vertexCount);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
it('returns empty geometry for a non-positive radius', () => {
|
|
71
|
+
const geom = boundaryRingMesh({
|
|
72
|
+
center: new Vec3(0, 0, 0),
|
|
73
|
+
radius: 0,
|
|
74
|
+
width: 0.1,
|
|
75
|
+
groundPlane: FLAT
|
|
76
|
+
});
|
|
77
|
+
expect(geom.positions).toHaveLength(0);
|
|
78
|
+
expect(geom.indices).toHaveLength(0);
|
|
79
|
+
});
|
|
80
|
+
it('returns empty geometry for a non-positive width', () => {
|
|
81
|
+
const geom = boundaryRingMesh({
|
|
82
|
+
center: new Vec3(0, 0, 0),
|
|
83
|
+
radius: 5,
|
|
84
|
+
width: 0,
|
|
85
|
+
groundPlane: FLAT
|
|
86
|
+
});
|
|
87
|
+
expect(geom.positions).toHaveLength(0);
|
|
88
|
+
});
|
|
89
|
+
it('returns empty geometry for a non-positive dashCount', () => {
|
|
90
|
+
const geom = boundaryRingMesh({
|
|
91
|
+
center: new Vec3(0, 0, 0),
|
|
92
|
+
radius: 5,
|
|
93
|
+
width: 0.1,
|
|
94
|
+
groundPlane: FLAT,
|
|
95
|
+
dashCount: 0
|
|
96
|
+
});
|
|
97
|
+
expect(geom.positions).toHaveLength(0);
|
|
98
|
+
});
|
|
99
|
+
it('returns empty geometry for an invalid ground plane', () => {
|
|
100
|
+
const collinear = [new Vec3(0, 0, 0), new Vec3(1, 0, 0), new Vec3(2, 0, 0)];
|
|
101
|
+
const geom = boundaryRingMesh({
|
|
102
|
+
center: new Vec3(0, 0, 0),
|
|
103
|
+
radius: 5,
|
|
104
|
+
width: 0.1,
|
|
105
|
+
groundPlane: collinear
|
|
106
|
+
});
|
|
107
|
+
expect(geom.positions).toHaveLength(0);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
export type GroundPlane = {
|
|
3
|
+
normal: Vec3;
|
|
4
|
+
point: Vec3;
|
|
5
|
+
};
|
|
6
|
+
/** Compute Y on the plane defined by `normal` passing through `point` at world XZ coords (x, z). */
|
|
7
|
+
export declare const yOnPlane: (x: number, z: number, normal: Vec3, point: Vec3, fallback: number) => number;
|
|
8
|
+
/**
|
|
9
|
+
* Derive a ground plane from 3 world-space points.
|
|
10
|
+
* Returns a normalized normal (flipped so normal.y >= 0) and a point on the plane,
|
|
11
|
+
* or null when fewer than 3 points are supplied or they are degenerate/collinear.
|
|
12
|
+
*/
|
|
13
|
+
export declare const computeGroundPlane: (points: Vec3[]) => GroundPlane | null;
|
|
14
|
+
//# sourceMappingURL=groundPlane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groundPlane.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/groundPlane.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,CAAC;AAExD,oGAAoG;AACpG,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,QAAQ,IAAI,EAAE,OAAO,IAAI,EAAE,UAAU,MAAM,KAAG,MAM5F,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,IAAI,EAAE,KAAG,WAAW,GAAG,IA8BjE,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
/** Compute Y on the plane defined by `normal` passing through `point` at world XZ coords (x, z). */
|
|
3
|
+
export const yOnPlane = (x, z, normal, point, fallback) => {
|
|
4
|
+
if (Math.abs(normal.y) < 1e-6) {
|
|
5
|
+
return fallback;
|
|
6
|
+
}
|
|
7
|
+
return point.y - (normal.x * (x - point.x) + normal.z * (z - point.z)) / normal.y;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Derive a ground plane from 3 world-space points.
|
|
12
|
+
* Returns a normalized normal (flipped so normal.y >= 0) and a point on the plane,
|
|
13
|
+
* or null when fewer than 3 points are supplied or they are degenerate/collinear.
|
|
14
|
+
*/
|
|
15
|
+
export const computeGroundPlane = points => {
|
|
16
|
+
if (points.length < 3) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const p0 = points[0];
|
|
20
|
+
const p1 = points[1];
|
|
21
|
+
const p2 = points[2];
|
|
22
|
+
const v1x = p1.x - p0.x;
|
|
23
|
+
const v1y = p1.y - p0.y;
|
|
24
|
+
const v1z = p1.z - p0.z;
|
|
25
|
+
const v2x = p2.x - p0.x;
|
|
26
|
+
const v2y = p2.y - p0.y;
|
|
27
|
+
const v2z = p2.z - p0.z;
|
|
28
|
+
let nx = v1y * v2z - v1z * v2y;
|
|
29
|
+
let ny = v1z * v2x - v1x * v2z;
|
|
30
|
+
let nz = v1x * v2y - v1y * v2x;
|
|
31
|
+
const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
32
|
+
if (len < 1e-10) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
nx /= len;
|
|
36
|
+
ny /= len;
|
|
37
|
+
nz /= len;
|
|
38
|
+
if (ny < 0) {
|
|
39
|
+
nx = -nx;
|
|
40
|
+
ny = -ny;
|
|
41
|
+
nz = -nz;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
normal: new Vec3(nx, ny, nz),
|
|
45
|
+
point: new Vec3(p0.x, p0.y, p0.z)
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
import { computeGroundPlane, yOnPlane } from './groundPlane';
|
|
3
|
+
describe('computeGroundPlane', () => {
|
|
4
|
+
it('returns an up normal for a flat horizontal plane', () => {
|
|
5
|
+
const plane = computeGroundPlane([new Vec3(0, 5, 0), new Vec3(1, 5, 0), new Vec3(0, 5, 1)]);
|
|
6
|
+
expect(plane).not.toBeNull();
|
|
7
|
+
expect(plane.normal.x).toBeCloseTo(0);
|
|
8
|
+
expect(plane.normal.y).toBeCloseTo(1);
|
|
9
|
+
expect(plane.normal.z).toBeCloseTo(0);
|
|
10
|
+
expect(plane.point.y).toBeCloseTo(5);
|
|
11
|
+
});
|
|
12
|
+
it('flips a downward-facing normal so normal.y >= 0', () => {
|
|
13
|
+
const plane = computeGroundPlane([new Vec3(0, 0, 0), new Vec3(0, 0, 1), new Vec3(1, 0, 0)]);
|
|
14
|
+
expect(plane).not.toBeNull();
|
|
15
|
+
expect(plane.normal.y).toBeCloseTo(1);
|
|
16
|
+
});
|
|
17
|
+
it('computes a unit normal for a plane tilted 45deg about X (y = z)', () => {
|
|
18
|
+
const plane = computeGroundPlane([new Vec3(0, 0, 0), new Vec3(1, 0, 0), new Vec3(0, 1, 1)]);
|
|
19
|
+
expect(plane).not.toBeNull();
|
|
20
|
+
const n = plane.normal;
|
|
21
|
+
expect(Math.sqrt(n.x * n.x + n.y * n.y + n.z * n.z)).toBeCloseTo(1);
|
|
22
|
+
expect(n.x).toBeCloseTo(0);
|
|
23
|
+
expect(n.y).toBeCloseTo(Math.SQRT1_2);
|
|
24
|
+
expect(n.z).toBeCloseTo(-Math.SQRT1_2);
|
|
25
|
+
});
|
|
26
|
+
it('returns null for fewer than 3 points', () => {
|
|
27
|
+
expect(computeGroundPlane([new Vec3(0, 0, 0), new Vec3(1, 0, 0)])).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
it('returns null for collinear points', () => {
|
|
30
|
+
expect(computeGroundPlane([new Vec3(0, 0, 0), new Vec3(1, 0, 0), new Vec3(2, 0, 0)])).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('yOnPlane', () => {
|
|
34
|
+
it('returns the constant height on a flat plane', () => {
|
|
35
|
+
expect(yOnPlane(3, 7, new Vec3(0, 1, 0), new Vec3(0, 5, 0), -1)).toBeCloseTo(5);
|
|
36
|
+
});
|
|
37
|
+
it('interpolates height on a plane where y = z', () => {
|
|
38
|
+
const n = new Vec3(0, Math.SQRT1_2, -Math.SQRT1_2);
|
|
39
|
+
expect(yOnPlane(0, 2, n, new Vec3(0, 0, 0), 0)).toBeCloseTo(2);
|
|
40
|
+
expect(yOnPlane(10, -3, n, new Vec3(0, 0, 0), 0)).toBeCloseTo(-3);
|
|
41
|
+
});
|
|
42
|
+
it('returns the fallback when the normal is (near) horizontal', () => {
|
|
43
|
+
expect(yOnPlane(1, 1, new Vec3(1, 0, 0), new Vec3(0, 9, 0), 42)).toBe(42);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
import { GsplatShaderEffect } from 'playcanvas/scripts/esm/gsplat/gsplat-shader-effect.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Fade crop shader effect for gaussian splats.
|
|
5
|
+
* Applies a smooth fade to splats outside the AABB based on distance.
|
|
6
|
+
* Splats at the AABB edge are fully visible, and fade to invisible at fadeDistance away.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GsplatFadeCropShaderEffect extends GsplatShaderEffect {
|
|
9
|
+
static scriptName: string;
|
|
10
|
+
_aabbMinArray: number[];
|
|
11
|
+
_aabbMaxArray: number[];
|
|
12
|
+
aabbMin: Vec3;
|
|
13
|
+
aabbMax: Vec3;
|
|
14
|
+
fadeDistance: number;
|
|
15
|
+
getShaderGLSL(): string;
|
|
16
|
+
getShaderWGSL(): string;
|
|
17
|
+
updateEffect(): void;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=gsplat-fade-crop-effect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gsplat-fade-crop-effect.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/gsplat-fade-crop-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AA0F5F;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,kBAAkB;IAChE,MAAM,CAAC,UAAU,SAAgC;IAEjD,aAAa,WAAa;IAC1B,aAAa,WAAa;IAE1B,OAAO,OAA8B;IACrC,OAAO,OAA2B;IAClC,YAAY,SAAO;IAEnB,aAAa;IAIb,aAAa;IAIb,YAAY;CAab"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
import { GsplatShaderEffect } from 'playcanvas/scripts/esm/gsplat/gsplat-shader-effect.mjs';
|
|
3
|
+
const shaderGLSL = /* glsl */`
|
|
4
|
+
uniform vec3 uAabbMin;
|
|
5
|
+
uniform vec3 uAabbMax;
|
|
6
|
+
uniform float uFadeDistance;
|
|
7
|
+
|
|
8
|
+
void modifySplatCenter(inout vec3 center) {
|
|
9
|
+
// No modifications needed
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
void modifySplatRotationScale(vec3 originalCenter, vec3 modifiedCenter, inout vec4 rotation, inout vec3 scale) {
|
|
13
|
+
// Check if splat is inside AABB
|
|
14
|
+
bool insideAABB = all(greaterThanEqual(modifiedCenter, uAabbMin)) && all(lessThanEqual(modifiedCenter, uAabbMax));
|
|
15
|
+
|
|
16
|
+
if (insideAABB) {
|
|
17
|
+
// Inside the box, no fade needed
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Calculate distance to AABB for splats outside
|
|
22
|
+
vec3 distToMin = uAabbMin - modifiedCenter;
|
|
23
|
+
vec3 distToMax = modifiedCenter - uAabbMax;
|
|
24
|
+
|
|
25
|
+
// Get the maximum positive distance on each axis (how far outside we are)
|
|
26
|
+
vec3 outsideDistance = max(distToMin, distToMax);
|
|
27
|
+
outsideDistance = max(outsideDistance, vec3(0.0));
|
|
28
|
+
|
|
29
|
+
// Use the maximum distance across all axes
|
|
30
|
+
float maxOutsideDistance = max(max(outsideDistance.x, outsideDistance.y), outsideDistance.z);
|
|
31
|
+
|
|
32
|
+
// Calculate fade factor: 1.0 at edge, 0.0 at fadeDistance away
|
|
33
|
+
float fadeFactor = 1.0 - clamp(maxOutsideDistance / uFadeDistance, 0.0, 1.0);
|
|
34
|
+
|
|
35
|
+
// Apply smooth fade using smoothstep for nicer transition
|
|
36
|
+
fadeFactor = smoothstep(0.0, 1.0, fadeFactor);
|
|
37
|
+
|
|
38
|
+
// Scale the splat based on fade factor
|
|
39
|
+
scale *= fadeFactor;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void modifySplatColor(vec3 center, inout vec4 color) {
|
|
43
|
+
// No color modification needed
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
const shaderWGSL = /* wgsl */`
|
|
47
|
+
uniform uAabbMin: vec3f;
|
|
48
|
+
uniform uAabbMax: vec3f;
|
|
49
|
+
uniform uFadeDistance: f32;
|
|
50
|
+
|
|
51
|
+
fn modifySplatCenter(center: ptr<function, vec3f>) {
|
|
52
|
+
// No modifications needed
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fn modifySplatRotationScale(originalCenter: vec3f, modifiedCenter: vec3f, rotation: ptr<function, vec4f>, scale: ptr<function, vec3f>) {
|
|
56
|
+
// Check if splat is inside AABB
|
|
57
|
+
let insideAABB = all(modifiedCenter >= uniform.uAabbMin) && all(modifiedCenter <= uniform.uAabbMax);
|
|
58
|
+
|
|
59
|
+
if (insideAABB) {
|
|
60
|
+
// Inside the box, no fade needed
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Calculate distance to AABB for splats outside
|
|
65
|
+
let distToMin = uniform.uAabbMin - modifiedCenter;
|
|
66
|
+
let distToMax = modifiedCenter - uniform.uAabbMax;
|
|
67
|
+
|
|
68
|
+
// Get the maximum positive distance on each axis (how far outside we are)
|
|
69
|
+
var outsideDistance = max(distToMin, distToMax);
|
|
70
|
+
outsideDistance = max(outsideDistance, vec3f(0.0));
|
|
71
|
+
|
|
72
|
+
// Use the maximum distance across all axes
|
|
73
|
+
let maxOutsideDistance = max(max(outsideDistance.x, outsideDistance.y), outsideDistance.z);
|
|
74
|
+
|
|
75
|
+
// Calculate fade factor: 1.0 at edge, 0.0 at fadeDistance away
|
|
76
|
+
var fadeFactor = 1.0 - clamp(maxOutsideDistance / uniform.uFadeDistance, 0.0, 1.0);
|
|
77
|
+
|
|
78
|
+
// Apply smooth fade using smoothstep for nicer transition
|
|
79
|
+
fadeFactor = smoothstep(0.0, 1.0, fadeFactor);
|
|
80
|
+
|
|
81
|
+
// Scale the splat based on fade factor
|
|
82
|
+
*scale = (*scale) * fadeFactor;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fn modifySplatColor(center: vec3f, color: ptr<function, vec4f>) {
|
|
86
|
+
// No color modification needed
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Fade crop shader effect for gaussian splats.
|
|
92
|
+
* Applies a smooth fade to splats outside the AABB based on distance.
|
|
93
|
+
* Splats at the AABB edge are fully visible, and fade to invisible at fadeDistance away.
|
|
94
|
+
*/
|
|
95
|
+
export class GsplatFadeCropShaderEffect extends GsplatShaderEffect {
|
|
96
|
+
static scriptName = 'gsplatFadeCropShaderEffect';
|
|
97
|
+
_aabbMinArray = [0, 0, 0];
|
|
98
|
+
_aabbMaxArray = [0, 0, 0];
|
|
99
|
+
aabbMin = new Vec3(-0.5, -0.5, -0.5);
|
|
100
|
+
aabbMax = new Vec3(0.5, 0.5, 0.5);
|
|
101
|
+
fadeDistance = 0.5;
|
|
102
|
+
getShaderGLSL() {
|
|
103
|
+
return shaderGLSL;
|
|
104
|
+
}
|
|
105
|
+
getShaderWGSL() {
|
|
106
|
+
return shaderWGSL;
|
|
107
|
+
}
|
|
108
|
+
updateEffect() {
|
|
109
|
+
this._aabbMinArray[0] = this.aabbMin.x;
|
|
110
|
+
this._aabbMinArray[1] = this.aabbMin.y;
|
|
111
|
+
this._aabbMinArray[2] = this.aabbMin.z;
|
|
112
|
+
this.setUniform('uAabbMin', this._aabbMinArray);
|
|
113
|
+
this._aabbMaxArray[0] = this.aabbMax.x;
|
|
114
|
+
this._aabbMaxArray[1] = this.aabbMax.y;
|
|
115
|
+
this._aabbMaxArray[2] = this.aabbMax.z;
|
|
116
|
+
this.setUniform('uAabbMax', this._aabbMaxArray);
|
|
117
|
+
this.setUniform('uFadeDistance', this.fadeDistance);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Script, Vec3 } from 'playcanvas';
|
|
2
|
+
export declare class WalkthroughCamera extends Script {
|
|
3
|
+
static scriptName: string;
|
|
4
|
+
/** @attribute */
|
|
5
|
+
moveSpeed: number;
|
|
6
|
+
/** @attribute */
|
|
7
|
+
moveFastSpeed: number;
|
|
8
|
+
/** @attribute */
|
|
9
|
+
moveSlowSpeed: number;
|
|
10
|
+
/** @attribute */
|
|
11
|
+
lookSensitivity: number;
|
|
12
|
+
/** @attribute */
|
|
13
|
+
rotateDamping: number;
|
|
14
|
+
/** @attribute */
|
|
15
|
+
pitchMin: number;
|
|
16
|
+
/** @attribute */
|
|
17
|
+
pitchMax: number;
|
|
18
|
+
/** @attribute */
|
|
19
|
+
scrollSpeed: number;
|
|
20
|
+
/** @attribute */
|
|
21
|
+
boundsCenter: Vec3;
|
|
22
|
+
/** @attribute */
|
|
23
|
+
boundsRadius: number;
|
|
24
|
+
/** @attribute */
|
|
25
|
+
freeFly: boolean;
|
|
26
|
+
/** @attribute */
|
|
27
|
+
enableFly: boolean;
|
|
28
|
+
/** @attribute */
|
|
29
|
+
averageCameraHeight: number;
|
|
30
|
+
/**
|
|
31
|
+
* Three world-space points defining the ground plane.
|
|
32
|
+
* When provided, the camera Y position is derived from this plane during non-freeFly movement.
|
|
33
|
+
*
|
|
34
|
+
* Not a React prop — must be set directly on the script instance via useEffect. The @playcanvas/react
|
|
35
|
+
* Script component is wrapped in memo() whose shallowEquals comparator returns early on the first prop
|
|
36
|
+
* with a .equals() method (Vec3), so any prop listed after boundsCenter is never compared and the
|
|
37
|
+
* component won't re-render when it changes. Set via:
|
|
38
|
+
* app.root.findByName('camera')?.script?.walkthroughCamera.groundPlane = points
|
|
39
|
+
* Should be updated if shallowEquals is fixed (see https://github.com/playcanvas/react/pull/298)
|
|
40
|
+
*/
|
|
41
|
+
groundPlane: Vec3[];
|
|
42
|
+
private _pitch;
|
|
43
|
+
private _yaw;
|
|
44
|
+
private _targetPitch;
|
|
45
|
+
private _targetYaw;
|
|
46
|
+
private _isDragging;
|
|
47
|
+
private _lastX;
|
|
48
|
+
private _lastY;
|
|
49
|
+
private _scrollDelta;
|
|
50
|
+
private _keys;
|
|
51
|
+
private _removeListeners;
|
|
52
|
+
private _planeNormal;
|
|
53
|
+
private _planePoint;
|
|
54
|
+
private _hasGroundPlane;
|
|
55
|
+
private _syncGroundPlane;
|
|
56
|
+
initialize(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Position the camera at `position` looking toward `focus`.
|
|
59
|
+
* Compatible with the useCameraPosition hook's reset() call.
|
|
60
|
+
*/
|
|
61
|
+
reset(focus: Vec3, position: Vec3): void;
|
|
62
|
+
get currentYaw(): number;
|
|
63
|
+
get currentPitch(): number;
|
|
64
|
+
/**
|
|
65
|
+
* Orbit the camera by `yawDelta` degrees around `boundsCenter`.
|
|
66
|
+
* Called by AutoRotator
|
|
67
|
+
*/
|
|
68
|
+
orbitStep(yawDelta: number): void;
|
|
69
|
+
/**
|
|
70
|
+
* Returns the point directly in front of the camera.
|
|
71
|
+
* Compatible with the useCameraPosition hook's focusPoint read.
|
|
72
|
+
*/
|
|
73
|
+
get focusPoint(): Vec3;
|
|
74
|
+
update(dt: number): void;
|
|
75
|
+
destroy(): void;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=walkthrough-camera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walkthrough-camera.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/walkthrough-camera.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAAE,IAAI,EAAQ,MAAM,YAAY,CAAC;AA8BtD,qBAAa,iBAAkB,SAAQ,MAAM;IAC3C,MAAM,CAAC,UAAU,SAAuB;IAExC,iBAAiB;IACjB,SAAS,SAAO;IAEhB,iBAAiB;IACjB,aAAa,SAAO;IAEpB,iBAAiB;IACjB,aAAa,SAAQ;IAErB,iBAAiB;IACjB,eAAe,SAAO;IAEtB,iBAAiB;IACjB,aAAa,SAAQ;IAErB,iBAAiB;IACjB,QAAQ,SAAO;IAEf,iBAAiB;IACjB,QAAQ,SAAM;IAEd,iBAAiB;IACjB,WAAW,SAAS;IAEpB,iBAAiB;IACjB,YAAY,OAAqB;IAEjC,iBAAiB;IACjB,YAAY,SAAM;IAElB,iBAAiB;IACjB,OAAO,UAAS;IAEhB,iBAAiB;IACjB,SAAS,UAAQ;IAEjB,iBAAiB;IACjB,mBAAmB,SAAK;IAExB;;;;;;;;;;OAUG;IACH,WAAW,EAAE,IAAI,EAAE,CAAM;IAGzB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,IAAI,CAAK;IAGjB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,gBAAgB,CAAsB;IAG9C,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,gBAAgB;IAaxB,UAAU;IAkFV;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI;IAsBjC,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM;IAsB1B;;;OAGG;IACH,IAAI,UAAU,IAAI,IAAI,CAMrB;IAED,MAAM,CAAC,EAAE,EAAE,MAAM;IA4FjB,OAAO;CAIR"}
|