UNPKG

@google/model-viewer

Version:

Easily display interactive 3D models on the web and in AR!

182 lines 7.92 kB
/* @license * Copyright 2019 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an 'AS IS' BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Vector3 } from 'three'; import { ModelViewerElement } from '../../model-viewer'; import { $needsRender, $scene } from '../../model-viewer-base'; import { Hotspot } from '../../three-components/Hotspot.js'; import { timePasses, waitForEvent } from '../../utilities'; import { assetPath, rafPasses } from '../helpers'; const expect = chai.expect; const sceneContainsHotspot = (scene, element) => { const { children } = scene.target; for (let i = 0, l = children.length; i < l; i++) { const hotspot = children[i]; if (hotspot instanceof Hotspot && hotspot.element.children[0].children[0] .name === element.slot) { // expect it has been changed from default expect(hotspot.position).to.not.eql(new Vector3()); expect(hotspot.normal).to.not.eql(new Vector3(0, 1, 0)); return true; } } return false; }; const closeToVector3 = (a, b) => { const delta = 0.001; expect(a.x).to.be.closeTo(b.x, delta); expect(a.y).to.be.closeTo(b.y, delta); expect(a.z).to.be.closeTo(b.z, delta); }; const withinRange = (a, start, finish) => { expect(a.u).to.be.within(start, finish); expect(a.v).to.be.within(start, finish); }; suite('Annotation', () => { let element; let scene; setup(async () => { element = new ModelViewerElement(); document.body.insertBefore(element, document.body.firstChild); scene = element[$scene]; element.src = assetPath('models/cube.gltf'); await waitForEvent(element, 'poster-dismissed'); }); teardown(() => { if (element.parentNode != null) { element.parentNode.removeChild(element); } }); suite('a model-viewer element with a hotspot', () => { let hotspot; let numSlots; setup(async () => { hotspot = document.createElement('div'); hotspot.setAttribute('slot', 'hotspot-1'); hotspot.setAttribute('data-position', '1m 1m 1m'); hotspot.setAttribute('data-normal', '0m 0m -1m'); element.appendChild(hotspot); await timePasses(); numSlots = scene.target.children.length; }); teardown(() => { if (hotspot.parentElement === element) { element.removeChild(hotspot); } }); test('creates a corresponding slot', () => { expect(sceneContainsHotspot(scene, hotspot)).to.be.true; }); suite('adding a second hotspot with the same name', () => { let hotspot2; setup(async () => { hotspot2 = document.createElement('div'); hotspot2.setAttribute('slot', 'hotspot-1'); hotspot2.setAttribute('data-position', '0m 1m 2m'); hotspot2.setAttribute('data-normal', '1m 0m 0m'); element.appendChild(hotspot2); await timePasses(); }); teardown(() => { if (hotspot2.parentElement === element) { element.removeChild(hotspot2); } }); test('does not change the slot', () => { expect(scene.target.children.length).to.be.equal(numSlots); }); test('does not change the data', () => { const { position, normal } = scene.target.children[numSlots - 1]; expect(position).to.be.deep.equal(new Vector3(1, 1, 1)); expect(normal).to.be.deep.equal(new Vector3(0, 0, -1)); }); test('updateHotspot does change the data', () => { element.updateHotspot({ name: 'hotspot-1', position: '0m 1m 2m', normal: '1m 0m 0m' }); const { position, normal } = scene.target.children[numSlots - 1]; expect(position).to.be.deep.equal(new Vector3(0, 1, 2)); expect(normal).to.be.deep.equal(new Vector3(1, 0, 0)); }); test('and removing it does not remove the slot', async () => { element.removeChild(hotspot); await timePasses(); expect(scene.target.children.length).to.be.equal(numSlots); }); test('but removing both does remove the slot', async () => { element.removeChild(hotspot); element.removeChild(hotspot2); await timePasses(); expect(scene.target.children.length).to.be.equal(numSlots - 1); }); suite('with a camera', () => { let wrapper; setup(async () => { // This is to wait for the hotspots to be added to their slots, as // this triggers their visibility to "show". Otherwise, sometimes the // following hide() call will happen first, then when the camera // moves, we never get a hotspot-visibility event because they were // already visible. await rafPasses(); wrapper = scene.target.children[numSlots - 1].element; }); test('the hotspot is hidden', async () => { expect(wrapper.classList.contains('hide')).to.be.true; }); test('the hotspot is visible after turning', async () => { element[$scene].yaw = Math.PI; element[$scene].updateMatrixWorld(); element[$needsRender](); await waitForEvent(hotspot2, 'hotspot-visibility'); expect(!!wrapper.classList.contains('hide')).to.be.false; }); }); }); }); suite('a model-viewer element with a loaded cube', () => { let width = 0; let height = 0; setup(async () => { width = 200; height = 300; element.setAttribute('style', `width: ${width}px; height: ${height}px`); element.cameraOrbit = '0deg 90deg 2m'; element.jumpCameraToGoal(); await rafPasses(); }); test('gets expected hit result', async () => { await rafPasses(); const hitResult = element.positionAndNormalFromPoint(width / 2, height / 2); expect(hitResult).to.be.ok; const { position, normal, uv } = hitResult; closeToVector3(position, new Vector3(0, 0, 0.5)); closeToVector3(normal, new Vector3(0, 0, 1)); if (uv != null) { withinRange(uv, 0, 1); } }); test('gets expected hit result when turned', async () => { element.resetTurntableRotation(-Math.PI / 2); await rafPasses(); const hitResult = element.positionAndNormalFromPoint(width / 2, height / 2); expect(hitResult).to.be.ok; const { position, normal, uv } = hitResult; closeToVector3(position, new Vector3(0.5, 0, 0)); closeToVector3(normal, new Vector3(1, 0, 0)); if (uv != null) { withinRange(uv, 0, 1); } }); }); }); //# sourceMappingURL=annotation-spec.js.map