@terraware/web-components 4.2.26-rc.0 → 4.3.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/_vendor/react-multi-carousel/index.js +13 -0
- package/_vendor/react-multi-carousel/lib/Arrows.js +13 -0
- package/_vendor/react-multi-carousel/lib/Carousel.js +19 -0
- package/_vendor/react-multi-carousel/lib/CarouselItems.js +14 -0
- package/_vendor/react-multi-carousel/lib/Dots.js +16 -0
- package/_vendor/react-multi-carousel/lib/index.js +13 -0
- package/_vendor/react-multi-carousel/lib/types.js +13 -0
- package/_vendor/react-multi-carousel/lib/utils/clones.js +14 -0
- package/_vendor/react-multi-carousel/lib/utils/common.js +13 -0
- package/_vendor/react-multi-carousel/lib/utils/dots.js +14 -0
- package/_vendor/react-multi-carousel/lib/utils/elementWidth.js +12 -0
- package/_vendor/react-multi-carousel/lib/utils/index.js +20 -0
- package/_vendor/react-multi-carousel/lib/utils/mouseOrTouchMove.js +14 -0
- package/_vendor/react-multi-carousel/lib/utils/next.js +13 -0
- package/_vendor/react-multi-carousel/lib/utils/previous.js +14 -0
- package/_vendor/react-multi-carousel/lib/utils/throttle.js +12 -0
- package/_vendor/react-multi-carousel/lib/utils/throwError.js +14 -0
- package/_virtual/Arrows.js +3 -0
- package/_virtual/Carousel.js +3 -0
- package/_virtual/CarouselItems.js +3 -0
- package/_virtual/Dots.js +3 -0
- package/_virtual/_commonjsHelpers.js +5 -0
- package/_virtual/clones.js +3 -0
- package/_virtual/common.js +3 -0
- package/_virtual/dots2.js +3 -0
- package/_virtual/elementWidth.js +3 -0
- package/_virtual/index.js +7 -0
- package/_virtual/index2.js +3 -0
- package/_virtual/index3.js +3 -0
- package/_virtual/mouseOrTouchMove.js +3 -0
- package/_virtual/next.js +3 -0
- package/_virtual/previous.js +3 -0
- package/_virtual/throttle.js +3 -0
- package/_virtual/throwError.js +3 -0
- package/_virtual/types.js +3 -0
- package/components/AntSwitch/index.js +6 -4
- package/components/Autocomplete/Autocomplete.js +19 -16
- package/components/Badge/index.js +8 -6
- package/components/BusySpinner/index.js +11 -8
- package/components/Button/Button.js +12 -9
- package/components/Checkbox.js +11 -8
- package/components/Confirm/index.js +14 -17
- package/components/DatePicker/DatePicker.js +19 -23
- package/components/DialogBox/DialogBox.js +25 -20
- package/components/Divisor.js +11 -8
- package/components/Dropdown.js +18 -15
- package/components/EditableTable/index.js +17 -14
- package/components/ErrorBox/ErrorBox.js +18 -15
- package/components/FileChooser/index.js +20 -15
- package/components/FormBottomBar/index.js +15 -12
- package/components/Icon/Icon.js +9 -6
- package/components/Icon/icons/index.js +252 -251
- package/components/IconTooltip/index.js +14 -9
- package/components/Map/MapBox.js +35 -32
- package/components/Map/MapContainer.js +10 -6
- package/components/Map/MapDateSliderControl.js +14 -12
- package/components/Map/MapDrawer.js +16 -12
- package/components/Map/MapLegend.js +32 -28
- package/components/Map/MapViewStyleControl.js +11 -9
- package/components/Map/ReactMapboxWrapper.js +1 -1
- package/components/Map/index.js +13 -11
- package/components/Map/types.js +5 -3
- package/components/Map/useMaintainLayerOrder.js +5 -2
- package/components/Markdown/index.js +6 -4
- package/components/Message/Message.js +25 -20
- package/components/MultiSelect/index.js +26 -23
- package/components/Navbar/NavFooter.js +8 -5
- package/components/Navbar/NavItem.js +14 -11
- package/components/Navbar/NavSection.js +10 -7
- package/components/Navbar/Navbar.js +16 -11
- package/components/Navbar/SubNavbar.js +8 -5
- package/components/Note.js +10 -7
- package/components/OverlayModal/OverlayModal.js +13 -11
- package/components/PageForm/index.js +15 -12
- package/components/PhotoChooser/index.js +30 -25
- package/components/PhotosCarousel/index.js +15 -18
- package/components/Pill/index.js +14 -11
- package/components/PillList/index.js +10 -7
- package/components/PlacementWrapper/index.js +8 -6
- package/components/PopoverMenu/Popover.js +12 -9
- package/components/PopoverMenu/index.js +14 -11
- package/components/PopoverMultiSelect/index.js +14 -11
- package/components/ProgressCircle/ProgressCircle.js +12 -9
- package/components/RadioButton.js +9 -6
- package/components/Select/Select.js +9 -6
- package/components/Select/SelectT.js +30 -26
- package/components/Separator/index.js +7 -5
- package/components/Size.js +1 -1
- package/components/Slider/index.js +9 -6
- package/components/SummaryBox.js +13 -10
- package/components/Tabs/index.js +22 -18
- package/components/TextTruncated/index.js +13 -20
- package/components/Textfield/Textfield.js +34 -28
- package/components/Textfield/TruncatedTextArea.js +12 -10
- package/components/TimelineSlider/TimelineMarker.js +8 -6
- package/components/TimelineSlider/TimelineRail.js +6 -4
- package/components/TimelineSlider/index.js +14 -12
- package/components/Tooltip/Tooltip.js +12 -7
- package/components/ViewPhotosDialog/index.js +14 -11
- package/components/VirtualWalkthrough/Annotation.js +10 -8
- package/components/VirtualWalkthrough/AnnotationEditPane.js +23 -21
- package/components/VirtualWalkthrough/AnnotationPanel.js +16 -14
- package/components/VirtualWalkthrough/Application.js +9 -7
- package/components/VirtualWalkthrough/AutoRotator.js +5 -3
- package/components/VirtualWalkthrough/BlockingSpinner.js +10 -7
- package/components/VirtualWalkthrough/BoundaryRing.js +8 -6
- package/components/VirtualWalkthrough/BoundaryWall.js +8 -6
- package/components/VirtualWalkthrough/CameraInfo.js +15 -13
- package/components/VirtualWalkthrough/ControlsInfoPane.js +59 -57
- package/components/VirtualWalkthrough/GradientSky.js +8 -6
- package/components/VirtualWalkthrough/SplatControls.js +37 -35
- package/components/VirtualWalkthrough/SplatCrop.js +6 -4
- package/components/VirtualWalkthrough/SplatFadeCrop.js +7 -5
- package/components/VirtualWalkthrough/SplatModel.js +17 -14
- package/components/VirtualWalkthrough/SplatRevealRain.js +6 -4
- package/components/VirtualWalkthrough/TfAnnotationManager.js +8 -5
- package/components/VirtualWalkthrough/TfXrNavigation.js +7 -5
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +47 -45
- package/components/VirtualWalkthrough/VrAnnotationPanel.js +9 -7
- package/components/VirtualWalkthrough/XrAnnotationInteraction.js +9 -7
- package/components/VirtualWalkthrough/XrExitButton.js +9 -7
- package/components/VirtualWalkthrough/XrGazeDwell.js +9 -7
- package/components/VirtualWalkthrough/XrPointerRay.js +9 -7
- package/components/VirtualWalkthrough/boundary-ring.js +7 -5
- package/components/VirtualWalkthrough/boundary-wall.js +8 -6
- package/components/VirtualWalkthrough/groundPlane.js +6 -3
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +5 -2
- package/components/VirtualWalkthrough/splatFormat.js +6 -4
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +4 -2
- package/components/VirtualWalkthrough/vr-annotation-panel.js +8 -5
- package/components/VirtualWalkthrough/walkthrough-camera.js +6 -4
- package/components/VirtualWalkthrough/xr-annotation-candidates.js +8 -6
- package/components/VirtualWalkthrough/xr-annotation-interaction.js +9 -6
- package/components/VirtualWalkthrough/xr-annotation-targeting.js +5 -2
- package/components/VirtualWalkthrough/xr-button-arming.js +4 -2
- package/components/VirtualWalkthrough/xr-button-hold.js +5 -3
- package/components/VirtualWalkthrough/xr-exit-button.js +10 -8
- package/components/VirtualWalkthrough/xr-face-buttons.js +6 -4
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +5 -3
- package/components/VirtualWalkthrough/xr-gaze-dwell.js +9 -7
- package/components/VirtualWalkthrough/xr-interactive-ui.js +10 -8
- package/components/VirtualWalkthrough/xr-pointer-ray.js +5 -3
- package/components/VirtualWalkthrough/xr-progress-pie.js +4 -2
- package/components/VirtualWalkthrough/xr-ray-quad.js +5 -2
- package/components/VirtualWalkthrough/xr-scene-bounds.js +5 -3
- package/components/VirtualWalkthrough/xr-teleport-gesture.js +4 -2
- package/components/svg/BlobbyGrayIconImage.js +13 -11
- package/components/svg/BlobbyGrayIconImport.js +13 -11
- package/components/svg/BlobbyGrayIconUploadToTheCloud.js +13 -11
- package/components/svg/BlobbyIconCloudChecked.js +41 -39
- package/components/svg/BlobbyIconExportOrUploadPhoto.js +41 -39
- package/components/svg/BlobbyIconFolder.js +41 -39
- package/components/svg/BlobbyIconGraphReport.js +41 -39
- package/components/svg/BlobbyIconHappy.js +41 -39
- package/components/svg/BlobbyIconHeartMonitor.js +41 -39
- package/components/svg/BlobbyIconLeaf.js +41 -39
- package/components/svg/BlobbyIconLibrary.js +41 -39
- package/components/svg/BlobbyIconNursery.js +41 -39
- package/components/svg/BlobbyIconOrganization.js +41 -39
- package/components/svg/BlobbyIconParchment.js +41 -39
- package/components/svg/BlobbyIconPeople.js +41 -39
- package/components/svg/BlobbyIconSeedBank.js +41 -39
- package/components/svg/BlobbyIconSeedling.js +41 -39
- package/components/svg/BlobbyIconSeeds.js +41 -39
- package/components/svg/BlobbyIconSite.js +42 -40
- package/components/svg/BlobbyIconSparkles.js +41 -39
- package/components/svg/BlobbyIconWrench.js +41 -39
- package/components/svg/Bug.js +8 -6
- package/components/svg/Calendar.js +8 -6
- package/components/svg/CaretDown.js +8 -6
- package/components/svg/CaretLeft.js +8 -6
- package/components/svg/CaretRight.js +8 -6
- package/components/svg/CaretUp.js +8 -6
- package/components/svg/ChevronDown.js +8 -6
- package/components/svg/ChevronUp.js +8 -6
- package/components/svg/Close.js +8 -6
- package/components/svg/Critical.js +8 -6
- package/components/svg/Edit.js +41 -39
- package/components/svg/Error.js +8 -6
- package/components/svg/Filter.js +8 -6
- package/components/svg/Folder.js +8 -6
- package/components/svg/Help.js +8 -6
- package/components/svg/Home.js +8 -6
- package/components/svg/IconAdd.js +8 -6
- package/components/svg/IconArrowRight.js +8 -6
- package/components/svg/IconBusinessNetwork.js +8 -6
- package/components/svg/IconCalendar.js +8 -6
- package/components/svg/IconCancel.js +8 -6
- package/components/svg/IconChargingBattery.js +8 -6
- package/components/svg/IconChecklist.js +8 -6
- package/components/svg/IconCheckmark.js +8 -6
- package/components/svg/IconCoinInHand.js +8 -6
- package/components/svg/IconColumns.js +8 -6
- package/components/svg/IconComment.js +8 -6
- package/components/svg/IconDashboard.js +8 -6
- package/components/svg/IconDataMigration.js +8 -6
- package/components/svg/IconDocument.js +8 -6
- package/components/svg/IconDownloadFromTheCloud.js +8 -6
- package/components/svg/IconEdit.js +8 -6
- package/components/svg/IconExpand.js +8 -6
- package/components/svg/IconExport.js +8 -6
- package/components/svg/IconExternalLink.js +8 -6
- package/components/svg/IconEye.js +8 -6
- package/components/svg/IconEyeOff.js +8 -6
- package/components/svg/IconFile.js +8 -6
- package/components/svg/IconFolder.js +8 -6
- package/components/svg/IconFullScreen.js +8 -6
- package/components/svg/IconFutures.js +8 -6
- package/components/svg/IconGraphReport.js +8 -6
- package/components/svg/IconHeartMonitor.js +8 -6
- package/components/svg/IconHelp.js +8 -6
- package/components/svg/IconHistory.js +8 -6
- package/components/svg/IconImport.js +8 -6
- package/components/svg/IconIndex.js +8 -6
- package/components/svg/IconLayers.js +8 -6
- package/components/svg/IconLibrary.js +8 -6
- package/components/svg/IconList.js +8 -6
- package/components/svg/IconLivePlant.js +8 -6
- package/components/svg/IconLocations.js +8 -6
- package/components/svg/IconMail.js +8 -6
- package/components/svg/IconManager.js +8 -6
- package/components/svg/IconMarker.js +8 -6
- package/components/svg/IconMenu.js +8 -6
- package/components/svg/IconMenuHorizontal.js +8 -6
- package/components/svg/IconModule.js +8 -6
- package/components/svg/IconMyLocation.js +8 -6
- package/components/svg/IconNote.js +8 -6
- package/components/svg/IconNursery.js +8 -6
- package/components/svg/IconOrg.js +8 -6
- package/components/svg/IconParchment.js +8 -6
- package/components/svg/IconPhoto.js +8 -6
- package/components/svg/IconPlantsFilled.js +8 -6
- package/components/svg/IconRedo.js +8 -6
- package/components/svg/IconRestorationSite.js +8 -6
- package/components/svg/IconSeedBank.js +8 -6
- package/components/svg/IconSeedling.js +8 -6
- package/components/svg/IconSettings.js +8 -6
- package/components/svg/IconSlice.js +8 -6
- package/components/svg/IconSubmit.js +8 -6
- package/components/svg/IconSubtract.js +8 -6
- package/components/svg/IconSynced.js +8 -6
- package/components/svg/IconTrashCan.js +8 -6
- package/components/svg/IconTreasureMap.js +8 -6
- package/components/svg/IconUnavailable.js +8 -6
- package/components/svg/IconUndo.js +8 -6
- package/components/svg/IconVariable.js +8 -6
- package/components/svg/IconVideo.js +9 -7
- package/components/svg/IconWifi.js +8 -6
- package/components/svg/Info.js +8 -6
- package/components/svg/Key.js +8 -6
- package/components/svg/Leaf.js +8 -6
- package/components/svg/Lock.js +8 -6
- package/components/svg/Logo.js +16 -14
- package/components/svg/MenuVertical.js +8 -6
- package/components/svg/Notification.js +8 -6
- package/components/svg/Person.js +8 -6
- package/components/svg/Plus.js +8 -6
- package/components/svg/RestorationSite.js +8 -6
- package/components/svg/Search.js +8 -6
- package/components/svg/Seeds.js +8 -6
- package/components/svg/Site.js +8 -6
- package/components/svg/Sparkles.js +8 -6
- package/components/svg/Species.js +8 -6
- package/components/svg/Species2.js +41 -39
- package/components/svg/Spinner.js +16 -14
- package/components/svg/Star.js +8 -6
- package/components/svg/Success.js +8 -6
- package/components/svg/SuccessFilled.js +8 -6
- package/components/svg/TerrawareLogoDesktop.js +17 -15
- package/components/svg/TerrawareLogoMobile.js +9 -7
- package/components/svg/Touchscreen.js +8 -6
- package/components/svg/UploadCloud.js +41 -39
- package/components/svg/Warning.js +8 -6
- package/components/svg/WelcomeClipboard.js +32 -30
- package/components/svg/index.js +127 -127
- package/components/table/EnhancedTableToolbar.js +18 -15
- package/components/table/EnhancedTableToolbarV2.js +8 -6
- package/components/table/TableCellRenderer.js +37 -32
- package/components/table/TableHeader.js +17 -14
- package/components/table/TableHeaderItem.js +18 -15
- package/components/table/density.js +5 -3
- package/components/table/index.js +40 -37
- package/components/table/sort.js +6 -4
- package/components/table/types.js +1 -1
- package/components/types/index.js +1 -1
- package/hooks/useBoolean.js +4 -2
- package/hooks/useCameraPosition.js +5 -2
- package/hooks/useDevicePerformance.js +6 -3
- package/hooks/useXr.js +6 -4
- package/index.js +55 -58
- package/package.json +6 -2
- package/style-dictionary-dist/TerrawareTheme.js +2 -1
- package/theme.js +5 -2
- package/utils/color.js +5 -2
- package/utils/date.js +7 -6
- package/utils/index.js +4 -5
- package/utils/preventDefaultEvent.js +2 -1
- package/utils/text.js +3 -2
- package/utils/useDeviceInfo.js +2 -1
- package/virtualWalkthrough.js +23 -31
- package/components/Button/button.test.js +0 -23
- package/components/MultiSelect/MultiSelect.test.js +0 -73
- package/components/VirtualWalkthrough/TfXrNavigation.test.js +0 -110
- package/components/VirtualWalkthrough/boundary-ring.test.js +0 -109
- package/components/VirtualWalkthrough/boundary-wall.test.js +0 -203
- package/components/VirtualWalkthrough/groundPlane.test.js +0 -45
- package/components/VirtualWalkthrough/splatFormat.test.js +0 -51
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.test.js +0 -26
- package/components/VirtualWalkthrough/xr-annotation-targeting.test.js +0 -66
- package/components/VirtualWalkthrough/xr-button-arming.test.js +0 -35
- package/components/VirtualWalkthrough/xr-button-hold.test.js +0 -57
- package/components/VirtualWalkthrough/xr-exit-button.test.js +0 -23
- package/components/VirtualWalkthrough/xr-face-buttons.test.js +0 -82
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.test.js +0 -61
- package/components/VirtualWalkthrough/xr-interactive-ui.test.js +0 -165
- package/components/VirtualWalkthrough/xr-progress-pie.test.js +0 -79
- package/components/VirtualWalkthrough/xr-ray-quad.test.js +0 -37
- package/components/VirtualWalkthrough/xr-scene-bounds.test.js +0 -70
- package/components/VirtualWalkthrough/xr-teleport-gesture.test.js +0 -90
- package/components/table/sort.test.js +0 -35
- package/utils/date.test.js +0 -84
- package/utils/text.test.js +0 -21
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { detectSplatFormat, splatLoaderFilename } from './splatFormat';
|
|
2
|
-
describe('detectSplatFormat', () => {
|
|
3
|
-
it('detects a bundled .sog archive', () => {
|
|
4
|
-
expect(detectSplatFormat('https://example.com/scenes/7154.sog')).toBe('sog');
|
|
5
|
-
});
|
|
6
|
-
it('detects an unbundled sog meta.json', () => {
|
|
7
|
-
expect(detectSplatFormat('https://example.com/scenes/7154/meta.json')).toBe('sogUnbundled');
|
|
8
|
-
});
|
|
9
|
-
it('detects a streamed LOD sog lod-meta.json', () => {
|
|
10
|
-
expect(detectSplatFormat('https://example.com/scenes/7154/lod-meta.json')).toBe('sogStreamed');
|
|
11
|
-
});
|
|
12
|
-
it('detects a .ply model', () => {
|
|
13
|
-
expect(detectSplatFormat('https://example.com/scenes/7154.ply')).toBe('ply');
|
|
14
|
-
});
|
|
15
|
-
it('ignores query strings on signed urls', () => {
|
|
16
|
-
expect(detectSplatFormat('https://example.com/7154/meta.json?rlkey=abc123&raw=1')).toBe('sogUnbundled');
|
|
17
|
-
});
|
|
18
|
-
it('ignores url fragments', () => {
|
|
19
|
-
expect(detectSplatFormat('https://example.com/7154/lod-meta.json#frag')).toBe('sogStreamed');
|
|
20
|
-
});
|
|
21
|
-
it('is case insensitive', () => {
|
|
22
|
-
expect(detectSplatFormat('https://example.com/7154/Meta.JSON')).toBe('sogUnbundled');
|
|
23
|
-
expect(detectSplatFormat('https://example.com/7154.SOG')).toBe('sog');
|
|
24
|
-
});
|
|
25
|
-
it('handles a bare filename with no directory', () => {
|
|
26
|
-
expect(detectSplatFormat('meta.json')).toBe('sogUnbundled');
|
|
27
|
-
});
|
|
28
|
-
it('does not mistake lod-meta.json for the unbundled format', () => {
|
|
29
|
-
expect(detectSplatFormat('https://example.com/lod-meta.json')).not.toBe('sogUnbundled');
|
|
30
|
-
});
|
|
31
|
-
it('does not treat an arbitrary .json file as a sog manifest', () => {
|
|
32
|
-
expect(detectSplatFormat('https://example.com/scene.json')).toBe('sog');
|
|
33
|
-
});
|
|
34
|
-
it('falls back to the bundled sog format for extensionless urls', () => {
|
|
35
|
-
expect(detectSplatFormat('https://example.com/assets/abc123?raw=1')).toBe('sog');
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
describe('splatLoaderFilename', () => {
|
|
39
|
-
it('maps the bundled format to a .sog filename so the loader picks the sog bundle parser', () => {
|
|
40
|
-
expect(splatLoaderFilename('sog')).toBe('model.sog');
|
|
41
|
-
});
|
|
42
|
-
it('maps the unbundled format to meta.json so the loader picks the plain sog parser', () => {
|
|
43
|
-
expect(splatLoaderFilename('sogUnbundled')).toBe('meta.json');
|
|
44
|
-
});
|
|
45
|
-
it('maps the streamed format to the exact basename the octree parser matches on', () => {
|
|
46
|
-
expect(splatLoaderFilename('sogStreamed')).toBe('lod-meta.json');
|
|
47
|
-
});
|
|
48
|
-
it('maps ply to a .ply filename', () => {
|
|
49
|
-
expect(splatLoaderFilename('ply')).toBe('model.ply');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { wrapText } from './vr-annotation-panel-layout';
|
|
2
|
-
|
|
3
|
-
// Each character is 1 unit wide; a space counts as a character within a line.
|
|
4
|
-
const measure = s => s.length;
|
|
5
|
-
describe('wrapText', () => {
|
|
6
|
-
it('returns an empty array for empty or whitespace-only input', () => {
|
|
7
|
-
expect(wrapText('', 10, measure)).toEqual([]);
|
|
8
|
-
expect(wrapText(' ', 10, measure)).toEqual([]);
|
|
9
|
-
});
|
|
10
|
-
it('keeps text on one line when it fits', () => {
|
|
11
|
-
expect(wrapText('hello world', 11, measure)).toEqual(['hello world']);
|
|
12
|
-
});
|
|
13
|
-
it('wraps to a new line when the next word would exceed the width', () => {
|
|
14
|
-
// 'hello world' is 11 wide; width 10 forces a break before 'world'.
|
|
15
|
-
expect(wrapText('hello world', 10, measure)).toEqual(['hello', 'world']);
|
|
16
|
-
});
|
|
17
|
-
it('packs as many words as fit per line greedily', () => {
|
|
18
|
-
expect(wrapText('a bb cc d', 5, measure)).toEqual(['a bb', 'cc d']);
|
|
19
|
-
});
|
|
20
|
-
it('puts an over-long word on its own line rather than dropping it', () => {
|
|
21
|
-
expect(wrapText('tiny enormouslylongword end', 8, measure)).toEqual(['tiny', 'enormouslylongword', 'end']);
|
|
22
|
-
});
|
|
23
|
-
it('collapses runs of whitespace', () => {
|
|
24
|
-
expect(wrapText('a b', 10, measure)).toEqual(['a b']);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { Vec3 } from 'playcanvas';
|
|
2
|
-
import { nearestAnnotationHit } from './xr-annotation-targeting';
|
|
3
|
-
describe('nearestAnnotationHit', () => {
|
|
4
|
-
const origin = new Vec3(0, 0, 5);
|
|
5
|
-
const forward = new Vec3(0, 0, -1);
|
|
6
|
-
it('returns null when there are no candidates', () => {
|
|
7
|
-
expect(nearestAnnotationHit(origin, forward, [])).toBeNull();
|
|
8
|
-
});
|
|
9
|
-
it('returns null when the ray misses every candidate', () => {
|
|
10
|
-
const candidates = [{
|
|
11
|
-
position: new Vec3(0, 5, 0),
|
|
12
|
-
radius: 1
|
|
13
|
-
}];
|
|
14
|
-
expect(nearestAnnotationHit(origin, forward, candidates)).toBeNull();
|
|
15
|
-
});
|
|
16
|
-
it('returns the index of a single hit candidate', () => {
|
|
17
|
-
const candidates = [{
|
|
18
|
-
position: new Vec3(0, 0, 0),
|
|
19
|
-
radius: 1
|
|
20
|
-
}];
|
|
21
|
-
expect(nearestAnnotationHit(origin, forward, candidates)).toBe(0);
|
|
22
|
-
});
|
|
23
|
-
it('returns the nearest candidate when several are hit, regardless of order', () => {
|
|
24
|
-
const far = {
|
|
25
|
-
position: new Vec3(0, 0, -5),
|
|
26
|
-
radius: 1
|
|
27
|
-
};
|
|
28
|
-
const near = {
|
|
29
|
-
position: new Vec3(0, 0, 0),
|
|
30
|
-
radius: 1
|
|
31
|
-
};
|
|
32
|
-
expect(nearestAnnotationHit(origin, forward, [far, near])).toBe(1);
|
|
33
|
-
});
|
|
34
|
-
it('treats a ray starting inside a sphere as the nearest hit', () => {
|
|
35
|
-
const inside = {
|
|
36
|
-
position: new Vec3(0, 0, 5),
|
|
37
|
-
radius: 1
|
|
38
|
-
};
|
|
39
|
-
const ahead = {
|
|
40
|
-
position: new Vec3(0, 0, 0),
|
|
41
|
-
radius: 1
|
|
42
|
-
};
|
|
43
|
-
expect(nearestAnnotationHit(origin, forward, [ahead, inside])).toBe(1);
|
|
44
|
-
});
|
|
45
|
-
it('normalizes a non-unit direction', () => {
|
|
46
|
-
const candidates = [{
|
|
47
|
-
position: new Vec3(0, 0, 0),
|
|
48
|
-
radius: 1
|
|
49
|
-
}];
|
|
50
|
-
expect(nearestAnnotationHit(origin, new Vec3(0, 0, -4), candidates)).toBe(0);
|
|
51
|
-
});
|
|
52
|
-
it('returns null when the only candidate is behind the ray origin', () => {
|
|
53
|
-
const candidates = [{
|
|
54
|
-
position: new Vec3(0, 0, 0),
|
|
55
|
-
radius: 1
|
|
56
|
-
}];
|
|
57
|
-
expect(nearestAnnotationHit(origin, new Vec3(0, 0, 1), candidates)).toBeNull();
|
|
58
|
-
});
|
|
59
|
-
it('registers a tangent graze as a hit', () => {
|
|
60
|
-
const candidates = [{
|
|
61
|
-
position: new Vec3(0, 0, 0),
|
|
62
|
-
radius: 1
|
|
63
|
-
}];
|
|
64
|
-
expect(nearestAnnotationHit(new Vec3(0, 1, 5), new Vec3(0, 0, -1), candidates)).toBe(0);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { ButtonArmingLatch } from './xr-button-arming';
|
|
2
|
-
const source = () => ({});
|
|
3
|
-
describe('ButtonArmingLatch', () => {
|
|
4
|
-
it('never counts a source pressed from the very first poll, while it stays down', () => {
|
|
5
|
-
const latch = new ButtonArmingLatch();
|
|
6
|
-
const src = source();
|
|
7
|
-
expect(latch.trackPress(src, true)).toBe(false);
|
|
8
|
-
expect(latch.trackPress(src, true)).toBe(false);
|
|
9
|
-
expect(latch.trackPress(src, true)).toBe(false);
|
|
10
|
-
});
|
|
11
|
-
it('counts a press that follows a release', () => {
|
|
12
|
-
const latch = new ButtonArmingLatch();
|
|
13
|
-
const src = source();
|
|
14
|
-
latch.trackPress(src, true);
|
|
15
|
-
latch.trackPress(src, false);
|
|
16
|
-
expect(latch.trackPress(src, true)).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
it('tracks each input source independently, so one held from the first poll does not block another', () => {
|
|
19
|
-
const latch = new ButtonArmingLatch();
|
|
20
|
-
const held = source();
|
|
21
|
-
const other = source();
|
|
22
|
-
latch.trackPress(held, true);
|
|
23
|
-
latch.trackPress(other, false);
|
|
24
|
-
expect(latch.trackPress(held, true)).toBe(false);
|
|
25
|
-
expect(latch.trackPress(other, true)).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
it('stays armed across later press/release cycles', () => {
|
|
28
|
-
const latch = new ButtonArmingLatch();
|
|
29
|
-
const src = source();
|
|
30
|
-
latch.trackPress(src, false);
|
|
31
|
-
expect(latch.trackPress(src, true)).toBe(true);
|
|
32
|
-
latch.trackPress(src, false);
|
|
33
|
-
expect(latch.trackPress(src, true)).toBe(true);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { INITIAL_HOLD_STATE, advanceHold } from './xr-button-hold';
|
|
2
|
-
const THRESHOLD = 1.25;
|
|
3
|
-
|
|
4
|
-
/** Arms the timer the way a real release does, so a press can start accumulating. */
|
|
5
|
-
const released = () => advanceHold(INITIAL_HOLD_STATE, false, 0.1, THRESHOLD).state;
|
|
6
|
-
describe('advanceHold', () => {
|
|
7
|
-
it('ignores a button that is already held when tracking starts', () => {
|
|
8
|
-
const result = advanceHold(INITIAL_HOLD_STATE, true, 2, THRESHOLD);
|
|
9
|
-
expect(result.progress).toBe(0);
|
|
10
|
-
expect(result.justFired).toBe(false);
|
|
11
|
-
});
|
|
12
|
-
it('stays unarmed for as long as a pre-held button is down', () => {
|
|
13
|
-
let state = INITIAL_HOLD_STATE;
|
|
14
|
-
for (let frame = 0; frame < 5; frame += 1) {
|
|
15
|
-
state = advanceHold(state, true, 1, THRESHOLD).state;
|
|
16
|
-
}
|
|
17
|
-
expect(advanceHold(state, true, 1, THRESHOLD).justFired).toBe(false);
|
|
18
|
-
});
|
|
19
|
-
it('arms on release so the next press accumulates', () => {
|
|
20
|
-
expect(advanceHold(released(), true, 0.5, THRESHOLD).progress).toBeCloseTo(0.4);
|
|
21
|
-
});
|
|
22
|
-
it('accumulates progress across frames', () => {
|
|
23
|
-
let state = released();
|
|
24
|
-
let progress = 0;
|
|
25
|
-
for (let frame = 0; frame < 3; frame += 1) {
|
|
26
|
-
const result = advanceHold(state, true, 0.25, THRESHOLD);
|
|
27
|
-
state = result.state;
|
|
28
|
-
progress = result.progress;
|
|
29
|
-
}
|
|
30
|
-
expect(progress).toBeCloseTo(0.6);
|
|
31
|
-
});
|
|
32
|
-
it('fires once when the hold reaches the threshold', () => {
|
|
33
|
-
const partial = advanceHold(released(), true, 1, THRESHOLD).state;
|
|
34
|
-
const firing = advanceHold(partial, true, 1, THRESHOLD);
|
|
35
|
-
expect(firing.justFired).toBe(true);
|
|
36
|
-
expect(firing.progress).toBe(1);
|
|
37
|
-
expect(advanceHold(firing.state, true, 1, THRESHOLD).justFired).toBe(false);
|
|
38
|
-
});
|
|
39
|
-
it('clamps progress to the threshold', () => {
|
|
40
|
-
const result = advanceHold(released(), true, 99, THRESHOLD);
|
|
41
|
-
expect(result.progress).toBe(1);
|
|
42
|
-
expect(result.justFired).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
it('resets to zero on release', () => {
|
|
45
|
-
const partial = advanceHold(released(), true, 1, THRESHOLD).state;
|
|
46
|
-
const result = advanceHold(partial, false, 0.1, THRESHOLD);
|
|
47
|
-
expect(result.progress).toBe(0);
|
|
48
|
-
expect(result.state.elapsed).toBe(0);
|
|
49
|
-
});
|
|
50
|
-
it('fires again only after a fresh press', () => {
|
|
51
|
-
const fired = advanceHold(advanceHold(released(), true, 1, THRESHOLD).state, true, 1, THRESHOLD).state;
|
|
52
|
-
const afterRelease = advanceHold(fired, false, 0.1, THRESHOLD).state;
|
|
53
|
-
const rePressed = advanceHold(afterRelease, true, 1, THRESHOLD);
|
|
54
|
-
expect(rePressed.justFired).toBe(false);
|
|
55
|
-
expect(advanceHold(rePressed.state, true, 1, THRESHOLD).justFired).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Vec3 } from 'playcanvas';
|
|
2
|
-
import { raySphereIntersect } from './xr-exit-button';
|
|
3
|
-
describe('raySphereIntersect', () => {
|
|
4
|
-
const CENTER = new Vec3(0, 0, 0);
|
|
5
|
-
it('hits a sphere straight ahead of the ray', () => {
|
|
6
|
-
expect(raySphereIntersect(new Vec3(0, 0, 5), new Vec3(0, 0, -1), CENTER, 1)).toBe(true);
|
|
7
|
-
});
|
|
8
|
-
it('misses a sphere off to the side', () => {
|
|
9
|
-
expect(raySphereIntersect(new Vec3(0, 5, 5), new Vec3(0, 0, -1), CENTER, 1)).toBe(false);
|
|
10
|
-
});
|
|
11
|
-
it('misses when the sphere is behind the ray origin', () => {
|
|
12
|
-
expect(raySphereIntersect(new Vec3(0, 0, 5), new Vec3(0, 0, 1), CENTER, 1)).toBe(false);
|
|
13
|
-
});
|
|
14
|
-
it('hits when the ray origin is inside the sphere', () => {
|
|
15
|
-
expect(raySphereIntersect(new Vec3(0, 0, 0), new Vec3(1, 0, 0), CENTER, 1)).toBe(true);
|
|
16
|
-
});
|
|
17
|
-
it('normalizes a non-unit direction', () => {
|
|
18
|
-
expect(raySphereIntersect(new Vec3(0, 0, 5), new Vec3(0, 0, -3), CENTER, 1)).toBe(true);
|
|
19
|
-
});
|
|
20
|
-
it('grazes a tangent sphere', () => {
|
|
21
|
-
expect(raySphereIntersect(new Vec3(0, 1, 5), new Vec3(0, 0, -1), CENTER, 1)).toBe(true);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { FaceButtonPressTracker, faceButtonPressed, secondaryFaceButtonPressed } from './xr-face-buttons';
|
|
2
|
-
|
|
3
|
-
/** Fake input source whose gamepad reports `pressedIndices` as held. */
|
|
4
|
-
const source = (pressedIndices, buttonCount = 6) => ({
|
|
5
|
-
gamepad: {
|
|
6
|
-
buttons: Array.from({
|
|
7
|
-
length: buttonCount
|
|
8
|
-
}, (_, index) => ({
|
|
9
|
-
pressed: pressedIndices.includes(index)
|
|
10
|
-
}))
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
const handTrackedSource = () => ({});
|
|
14
|
-
describe('faceButtonPressed', () => {
|
|
15
|
-
it('is false for an input source with no gamepad', () => {
|
|
16
|
-
expect(faceButtonPressed(handTrackedSource())).toBe(false);
|
|
17
|
-
});
|
|
18
|
-
it('is false when the gamepad has fewer buttons than the face buttons', () => {
|
|
19
|
-
expect(faceButtonPressed(source([0], 2))).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
it('is true when A/X is pressed', () => {
|
|
22
|
-
expect(faceButtonPressed(source([4]))).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
it('is true when B/Y is pressed', () => {
|
|
25
|
-
expect(faceButtonPressed(source([5]))).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
it('is false when only the trigger and grip are pressed', () => {
|
|
28
|
-
expect(faceButtonPressed(source([0, 1]))).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
it('is false when nothing is pressed', () => {
|
|
31
|
-
expect(faceButtonPressed(source([]))).toBe(false);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
describe('secondaryFaceButtonPressed', () => {
|
|
35
|
-
it('is true when B/Y is pressed', () => {
|
|
36
|
-
expect(secondaryFaceButtonPressed(source([5]))).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
it('is false when only A/X is pressed', () => {
|
|
39
|
-
expect(secondaryFaceButtonPressed(source([4]))).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
it('is false for an input source with no gamepad', () => {
|
|
42
|
-
expect(secondaryFaceButtonPressed(handTrackedSource())).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
it('is false when the gamepad has fewer buttons than the face buttons', () => {
|
|
45
|
-
expect(secondaryFaceButtonPressed(source([0], 2))).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe('FaceButtonPressTracker', () => {
|
|
49
|
-
it('fires once for a held press and re-arms after release', () => {
|
|
50
|
-
const tracker = new FaceButtonPressTracker();
|
|
51
|
-
// One persistent source whose button state mutates, as a real XrInputSource does across frames.
|
|
52
|
-
const buttons = Array.from({
|
|
53
|
-
length: 6
|
|
54
|
-
}, () => ({
|
|
55
|
-
pressed: false
|
|
56
|
-
}));
|
|
57
|
-
const controller = {
|
|
58
|
-
gamepad: {
|
|
59
|
-
buttons
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
const holdFaceButton = pressed => {
|
|
63
|
-
buttons[4].pressed = pressed;
|
|
64
|
-
};
|
|
65
|
-
expect(tracker.justPressed(controller)).toBe(false);
|
|
66
|
-
holdFaceButton(true);
|
|
67
|
-
expect(tracker.justPressed(controller)).toBe(true);
|
|
68
|
-
expect(tracker.justPressed(controller)).toBe(false);
|
|
69
|
-
holdFaceButton(false);
|
|
70
|
-
expect(tracker.justPressed(controller)).toBe(false);
|
|
71
|
-
holdFaceButton(true);
|
|
72
|
-
expect(tracker.justPressed(controller)).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
it('tracks each input source independently', () => {
|
|
75
|
-
const tracker = new FaceButtonPressTracker();
|
|
76
|
-
const left = source([4]);
|
|
77
|
-
const right = source([]);
|
|
78
|
-
expect(tracker.justPressed(left)).toBe(true);
|
|
79
|
-
expect(tracker.justPressed(right)).toBe(false);
|
|
80
|
-
expect(tracker.justPressed(left)).toBe(false);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { INITIAL_DWELL_STATE, advanceDwell } from './xr-gaze-dwell-state';
|
|
2
|
-
const THRESHOLD = 1.5;
|
|
3
|
-
describe('advanceDwell', () => {
|
|
4
|
-
it('stays reset when there is no gaze target', () => {
|
|
5
|
-
const r = advanceDwell({
|
|
6
|
-
targetIndex: 2,
|
|
7
|
-
elapsed: 1.0
|
|
8
|
-
}, null, 0.1, THRESHOLD);
|
|
9
|
-
expect(r.state).toEqual(INITIAL_DWELL_STATE);
|
|
10
|
-
expect(r.progress).toBe(0);
|
|
11
|
-
expect(r.justOpened).toBe(false);
|
|
12
|
-
});
|
|
13
|
-
it('resets to zero when the target changes', () => {
|
|
14
|
-
const r = advanceDwell({
|
|
15
|
-
targetIndex: 0,
|
|
16
|
-
elapsed: 1.0
|
|
17
|
-
}, 1, 0.1, THRESHOLD);
|
|
18
|
-
expect(r.state).toEqual({
|
|
19
|
-
targetIndex: 1,
|
|
20
|
-
elapsed: 0
|
|
21
|
-
});
|
|
22
|
-
expect(r.progress).toBe(0);
|
|
23
|
-
expect(r.justOpened).toBe(false);
|
|
24
|
-
});
|
|
25
|
-
it('accumulates dwell time while the same target is held', () => {
|
|
26
|
-
const r = advanceDwell({
|
|
27
|
-
targetIndex: 3,
|
|
28
|
-
elapsed: 0.5
|
|
29
|
-
}, 3, 0.25, THRESHOLD);
|
|
30
|
-
expect(r.state).toEqual({
|
|
31
|
-
targetIndex: 3,
|
|
32
|
-
elapsed: 0.75
|
|
33
|
-
});
|
|
34
|
-
expect(r.progress).toBeCloseTo(0.5, 5);
|
|
35
|
-
expect(r.justOpened).toBe(false);
|
|
36
|
-
});
|
|
37
|
-
it('fires justOpened once when crossing the threshold and clamps elapsed', () => {
|
|
38
|
-
const r = advanceDwell({
|
|
39
|
-
targetIndex: 3,
|
|
40
|
-
elapsed: 1.4
|
|
41
|
-
}, 3, 0.5, THRESHOLD);
|
|
42
|
-
expect(r.justOpened).toBe(true);
|
|
43
|
-
expect(r.progress).toBe(1);
|
|
44
|
-
expect(r.state).toEqual({
|
|
45
|
-
targetIndex: 3,
|
|
46
|
-
elapsed: THRESHOLD
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
it('does not re-fire while the target stays held past the threshold', () => {
|
|
50
|
-
const r = advanceDwell({
|
|
51
|
-
targetIndex: 3,
|
|
52
|
-
elapsed: THRESHOLD
|
|
53
|
-
}, 3, 0.5, THRESHOLD);
|
|
54
|
-
expect(r.justOpened).toBe(false);
|
|
55
|
-
expect(r.progress).toBe(1);
|
|
56
|
-
expect(r.state).toEqual({
|
|
57
|
-
targetIndex: 3,
|
|
58
|
-
elapsed: THRESHOLD
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { Vec3 } from 'playcanvas';
|
|
2
|
-
import { VrAnnotationPanel } from './vr-annotation-panel';
|
|
3
|
-
import { XrExitButton } from './xr-exit-button';
|
|
4
|
-
import { rayHitsAnnotationHotspot, rayHitsAnnotationPanel, rayHitsExitButton, rayHitsInteractiveUi } from './xr-interactive-ui';
|
|
5
|
-
|
|
6
|
-
// Jest cannot parse the PlayCanvas ESM scripts that xr-annotation-candidates imports.
|
|
7
|
-
jest.mock('playcanvas/scripts/esm/annotations.mjs', () => ({
|
|
8
|
-
Annotation: {
|
|
9
|
-
scriptName: 'annotation'
|
|
10
|
-
}
|
|
11
|
-
}));
|
|
12
|
-
const ORIGIN = new Vec3(0, 0, 5);
|
|
13
|
-
const FORWARD = new Vec3(0, 0, -1);
|
|
14
|
-
|
|
15
|
-
/** Fake annotation hotspot entity: unit quad at `position`, scale 1, with the stock open callback. */
|
|
16
|
-
const hotspot = position => ({
|
|
17
|
-
getPosition: () => position,
|
|
18
|
-
getWorldTransform: () => ({
|
|
19
|
-
getScale: out => out.set(1, 1, 1)
|
|
20
|
-
}),
|
|
21
|
-
script: {
|
|
22
|
-
get: () => ({
|
|
23
|
-
onVrOpenCallback: () => undefined
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
/**
|
|
28
|
-
* Fake app whose `findByName` serves only the entities the helpers look up. Each script stub returns
|
|
29
|
-
* its fake only for the matching `scriptName`, so a lookup by the wrong name misses - the way a real
|
|
30
|
-
* `Script#get` would - instead of silently succeeding.
|
|
31
|
-
*/
|
|
32
|
-
const fakeApp = ({
|
|
33
|
-
hotspots,
|
|
34
|
-
panelHit,
|
|
35
|
-
panelHasScript = true,
|
|
36
|
-
exitHit
|
|
37
|
-
}) => ({
|
|
38
|
-
root: {
|
|
39
|
-
findByName: name => {
|
|
40
|
-
if (name === 'annotations-root') {
|
|
41
|
-
return hotspots ? {
|
|
42
|
-
children: hotspots
|
|
43
|
-
} : null;
|
|
44
|
-
}
|
|
45
|
-
if (name === 'vr-annotation-panel') {
|
|
46
|
-
if (panelHit === undefined) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
script: {
|
|
51
|
-
get: scriptName => scriptName === VrAnnotationPanel.scriptName ? panelHasScript ? {
|
|
52
|
-
rayHitsPanel: () => panelHit
|
|
53
|
-
} : {} : undefined
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
if (name === 'xr-exit-button') {
|
|
58
|
-
return exitHit === undefined ? null : {
|
|
59
|
-
script: {
|
|
60
|
-
get: scriptName => scriptName === XrExitButton.scriptName ? {
|
|
61
|
-
rayHitsButton: () => exitHit
|
|
62
|
-
} : undefined
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
describe('rayHitsAnnotationHotspot', () => {
|
|
71
|
-
it('is true when the ray enters a hotspot sphere', () => {
|
|
72
|
-
const app = fakeApp({
|
|
73
|
-
hotspots: [hotspot(new Vec3(0, 0, 0))]
|
|
74
|
-
});
|
|
75
|
-
expect(rayHitsAnnotationHotspot(app, ORIGIN, FORWARD)).toBe(true);
|
|
76
|
-
});
|
|
77
|
-
it('is false when the ray misses every hotspot', () => {
|
|
78
|
-
const app = fakeApp({
|
|
79
|
-
hotspots: [hotspot(new Vec3(0, 8, 0))]
|
|
80
|
-
});
|
|
81
|
-
expect(rayHitsAnnotationHotspot(app, ORIGIN, FORWARD)).toBe(false);
|
|
82
|
-
});
|
|
83
|
-
it('is false when there is no annotations root', () => {
|
|
84
|
-
expect(rayHitsAnnotationHotspot(fakeApp({}), ORIGIN, FORWARD)).toBe(false);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Pins the derived radius (HOTSPOT_HALF_EXTENT 0.5 * scale 1 * HIT_RADIUS_PAD 2.5 = 1.25) against
|
|
88
|
-
// drift: a hotspot at (0,0,0) or (0,8,0) would pass the two tests above for nearly any radius.
|
|
89
|
-
it('is true just inside the derived hit radius', () => {
|
|
90
|
-
const app = fakeApp({
|
|
91
|
-
hotspots: [hotspot(new Vec3(0, 1.2, 0))]
|
|
92
|
-
});
|
|
93
|
-
expect(rayHitsAnnotationHotspot(app, ORIGIN, FORWARD)).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
it('is false just outside the derived hit radius', () => {
|
|
96
|
-
const app = fakeApp({
|
|
97
|
-
hotspots: [hotspot(new Vec3(0, 1.3, 0))]
|
|
98
|
-
});
|
|
99
|
-
expect(rayHitsAnnotationHotspot(app, ORIGIN, FORWARD)).toBe(false);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
describe('rayHitsAnnotationPanel', () => {
|
|
103
|
-
it('delegates to the panel script', () => {
|
|
104
|
-
expect(rayHitsAnnotationPanel(fakeApp({
|
|
105
|
-
panelHit: true
|
|
106
|
-
}), ORIGIN, FORWARD)).toBe(true);
|
|
107
|
-
expect(rayHitsAnnotationPanel(fakeApp({
|
|
108
|
-
panelHit: false
|
|
109
|
-
}), ORIGIN, FORWARD)).toBe(false);
|
|
110
|
-
});
|
|
111
|
-
it('is false when no panel is mounted', () => {
|
|
112
|
-
expect(rayHitsAnnotationPanel(fakeApp({}), ORIGIN, FORWARD)).toBe(false);
|
|
113
|
-
});
|
|
114
|
-
it('is false when the panel entity has no hit-test method', () => {
|
|
115
|
-
const app = fakeApp({
|
|
116
|
-
panelHit: true,
|
|
117
|
-
panelHasScript: false
|
|
118
|
-
});
|
|
119
|
-
expect(rayHitsAnnotationPanel(app, ORIGIN, FORWARD)).toBe(false);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
describe('rayHitsExitButton', () => {
|
|
123
|
-
it('delegates to the exit button script', () => {
|
|
124
|
-
expect(rayHitsExitButton(fakeApp({
|
|
125
|
-
exitHit: true
|
|
126
|
-
}), ORIGIN, FORWARD)).toBe(true);
|
|
127
|
-
expect(rayHitsExitButton(fakeApp({
|
|
128
|
-
exitHit: false
|
|
129
|
-
}), ORIGIN, FORWARD)).toBe(false);
|
|
130
|
-
});
|
|
131
|
-
it('is false when no exit button is mounted', () => {
|
|
132
|
-
expect(rayHitsExitButton(fakeApp({}), ORIGIN, FORWARD)).toBe(false);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
describe('rayHitsInteractiveUi', () => {
|
|
136
|
-
it('is false when the ray hits nothing interactive', () => {
|
|
137
|
-
const app = fakeApp({
|
|
138
|
-
hotspots: [hotspot(new Vec3(0, 8, 0))],
|
|
139
|
-
panelHit: false,
|
|
140
|
-
exitHit: false
|
|
141
|
-
});
|
|
142
|
-
expect(rayHitsInteractiveUi(app, ORIGIN, FORWARD)).toBe(false);
|
|
143
|
-
});
|
|
144
|
-
it('is true for a hotspot hit', () => {
|
|
145
|
-
const app = fakeApp({
|
|
146
|
-
hotspots: [hotspot(new Vec3(0, 0, 0))],
|
|
147
|
-
panelHit: false,
|
|
148
|
-
exitHit: false
|
|
149
|
-
});
|
|
150
|
-
expect(rayHitsInteractiveUi(app, ORIGIN, FORWARD)).toBe(true);
|
|
151
|
-
});
|
|
152
|
-
it('is true for a panel hit', () => {
|
|
153
|
-
expect(rayHitsInteractiveUi(fakeApp({
|
|
154
|
-
panelHit: true
|
|
155
|
-
}), ORIGIN, FORWARD)).toBe(true);
|
|
156
|
-
});
|
|
157
|
-
it('is true for an exit button hit', () => {
|
|
158
|
-
expect(rayHitsInteractiveUi(fakeApp({
|
|
159
|
-
exitHit: true
|
|
160
|
-
}), ORIGIN, FORWARD)).toBe(true);
|
|
161
|
-
});
|
|
162
|
-
it('is false in an empty scene', () => {
|
|
163
|
-
expect(rayHitsInteractiveUi(fakeApp({}), ORIGIN, FORWARD)).toBe(false);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { drawProgressPie } from './xr-progress-pie';
|
|
2
|
-
const TRACK_FILL = 'rgba(255, 255, 255, 0.18)';
|
|
3
|
-
const WEDGE_FILL = 'rgba(64, 200, 255, 0.85)';
|
|
4
|
-
|
|
5
|
-
/** Minimal 2D-context stand-in that records the arcs drawn and the fill colour used for each. */
|
|
6
|
-
const stubContext = () => {
|
|
7
|
-
const arcs = [];
|
|
8
|
-
const fills = [];
|
|
9
|
-
const ctx = {
|
|
10
|
-
fillStyle: '',
|
|
11
|
-
save: () => undefined,
|
|
12
|
-
restore: () => undefined,
|
|
13
|
-
beginPath: () => undefined,
|
|
14
|
-
moveTo: () => undefined,
|
|
15
|
-
closePath: () => undefined,
|
|
16
|
-
arc: (_x, _y, radius, start, end) => {
|
|
17
|
-
arcs.push({
|
|
18
|
-
radius,
|
|
19
|
-
start,
|
|
20
|
-
end
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
fill: () => {
|
|
24
|
-
fills.push(ctx.fillStyle);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
return {
|
|
28
|
-
ctx: ctx,
|
|
29
|
-
arcs,
|
|
30
|
-
fills
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
describe('drawProgressPie', () => {
|
|
34
|
-
it('draws only the full-circle track at zero progress', () => {
|
|
35
|
-
const {
|
|
36
|
-
ctx,
|
|
37
|
-
arcs,
|
|
38
|
-
fills
|
|
39
|
-
} = stubContext();
|
|
40
|
-
drawProgressPie(ctx, 128, 0);
|
|
41
|
-
expect(arcs).toHaveLength(1);
|
|
42
|
-
expect(arcs[0].end - arcs[0].start).toBeCloseTo(Math.PI * 2);
|
|
43
|
-
expect(fills).toEqual([TRACK_FILL]);
|
|
44
|
-
});
|
|
45
|
-
it('sweeps a quarter turn clockwise from the top at quarter progress', () => {
|
|
46
|
-
const {
|
|
47
|
-
ctx,
|
|
48
|
-
arcs
|
|
49
|
-
} = stubContext();
|
|
50
|
-
drawProgressPie(ctx, 128, 0.25);
|
|
51
|
-
expect(arcs).toHaveLength(2);
|
|
52
|
-
expect(arcs[1].start).toBeCloseTo(-Math.PI / 2);
|
|
53
|
-
expect(arcs[1].end).toBeCloseTo(0);
|
|
54
|
-
});
|
|
55
|
-
it('sweeps a full turn at full progress', () => {
|
|
56
|
-
const {
|
|
57
|
-
ctx,
|
|
58
|
-
arcs
|
|
59
|
-
} = stubContext();
|
|
60
|
-
drawProgressPie(ctx, 128, 1);
|
|
61
|
-
expect(arcs[1].end - arcs[1].start).toBeCloseTo(Math.PI * 2);
|
|
62
|
-
});
|
|
63
|
-
it('fills the wedge over the track', () => {
|
|
64
|
-
const {
|
|
65
|
-
ctx,
|
|
66
|
-
fills
|
|
67
|
-
} = stubContext();
|
|
68
|
-
drawProgressPie(ctx, 128, 0.5);
|
|
69
|
-
expect(fills).toEqual([TRACK_FILL, WEDGE_FILL]);
|
|
70
|
-
});
|
|
71
|
-
it('scales the radius to the drawn size', () => {
|
|
72
|
-
const {
|
|
73
|
-
ctx,
|
|
74
|
-
arcs
|
|
75
|
-
} = stubContext();
|
|
76
|
-
drawProgressPie(ctx, 256, 0);
|
|
77
|
-
expect(arcs[0].radius).toBeCloseTo(128 * 0.92);
|
|
78
|
-
});
|
|
79
|
-
});
|