diff --git a/index.html b/index.html index 31831da..f67e3be 100644 --- a/index.html +++ b/index.html @@ -38,7 +38,7 @@ src="https://cdn.glitch.com/e8742440-f85d-495a-8f52-6f066f464880%2FAMB_Suburbs_Afternoon_Woods_Spring_Small_ST_MKH8050-30shortened_amplified.mp3?v=1599596031022" crossorigin="anonymous"> + set-model-from-state snap="offset: 0.125; snap: 0.25"> + + - - { // don't spawn if class specified in objects property but it is not matched intersected element @@ -22,8 +24,8 @@ AFRAME.registerComponent('intersection-spawn', { // Create element. const spawnEl = document.createElement('a-entity'); - // Snap intersection point to grid and offset from center. - spawnEl.setAttribute('position', evt.detail.intersection.point); + // // Snap intersection point to grid and offset from center. + // spawnEl.setAttribute('position', evt.detail.intersection.point); // Set components and properties. Object.keys(data).forEach(name => { @@ -31,8 +33,18 @@ AFRAME.registerComponent('intersection-spawn', { AFRAME.utils.entity.setComponentProperty(spawnEl, name, data[name]); // is setAttribute a new version of "set component property"? }); - // Append to scene. - el.sceneEl.appendChild(spawnEl); // check for another entity in identical position before spawning; change its color instead + const _worldToLocal = (originalPosition, targetEl) => { + // snap the intersection location to the gridlines + const helperVector = this.helperVector; + helperVector.copy(originalPosition); + targetEl.object3D.worldToLocal(helperVector); + return helperVector; + }; + + const localPos = _worldToLocal(evt.detail.intersection.point, this.targetEl); // convert world intersection position to local position + spawnEl.setAttribute('position', localPos) + this.targetEl.appendChild(spawnEl); + }); } }); \ No newline at end of file diff --git a/src/app/snap.js b/src/app/snap.js index 3650a8f..b9fa272 100644 --- a/src/app/snap.js +++ b/src/app/snap.js @@ -1,33 +1,84 @@ /** * Snap entity to the closest interval specified by `snap`. * Offset entity by `offset`. + * If localId provided, then use the entity returned as the local reference space */ AFRAME.registerComponent('snap', { dependencies: ['position'], schema: { - offset: {type: 'vec3'}, - snap: {type: 'vec3'} + offset: {type: 'number'}, + snap: {type: 'number'}, + localId: {type: 'selector'} }, init: function () { var self = this; + this.helperVector = new THREE.Vector3(); this.originalPos = this.el.getAttribute('position'); this.el.addEventListener('componentchanged', function (evt) { if (evt.detail.name === 'position') { self.update() }; }); + this.localEl = null; + if (!!this.data.anchorId) { + this.localEl = document.querySelector(this.data.localId) + } }, update: function () { + // this element should be a child of the parent anchor container + // then use snapper as usual const data = this.data; - const pos = AFRAME.utils.clone(this.originalPos); - pos.x = Math.floor(pos.x / data.snap.x) * data.snap.x + data.offset.x; - pos.y = Math.floor(pos.y / data.snap.y) * data.snap.y + data.offset.y; - pos.z = Math.floor(pos.z / data.snap.z) * data.snap.z + data.offset.z; + // const pos = AFRAME.utils.clone(this.originalPos); + // pos.x = Math.floor(pos.x / data.snap) * data.snap + data.offset.x; + // pos.y = Math.floor(pos.y / data.snap) * data.snap + data.offset.y; + // pos.z = Math.floor(pos.z / data.snap) * data.snap + data.offset.z; + + // this.el.setAttribute('position', pos); + + const _snapper = (originalPosition, offset, snap) => { + // snap the intersection location to the gridlines + const helperVector = this.helperVector; + helperVector.copy(originalPosition); + + helperVector.x = Math.floor(helperVector.x / snap) * snap + offset; + helperVector.y = Math.floor(helperVector.y / snap) * snap + offset; + helperVector.z = Math.floor(helperVector.z / snap) * snap + offset; + return helperVector; + }; + + const _worldToLocal = (originalPosition) => { + // snap the intersection location to the gridlines + const helperVector = this.helperVector; + helperVector.copy(originalPosition); + this.el.object3D.worldToLocal(helperVector); + return helperVector; + }; + + const _convertLocalToWorld = (localPosition) => { + // Use Three.js's localToWorld method to convert the position + const worldPosition = new THREE.Vector3(); + worldPosition.copy(localPosition); + this.el.object3D.localToWorld(worldPosition); + + return worldPosition; + } + + let snapPos; + if (this.localEl && this.localEl.object3D) { + // this.el.object3D.quaternion.copy(this.anchorEl.object3D.quaternion); copy from + const localPos = _worldToLocal(this.localEl.object3D.position); // convert world object from which to localize (anchor) to local position of this object + const localSnapPos = _snapper(localPos, this.data.offset, this.data.snap); // use snapping logic which assumes local intersection + snapPos = _convertLocalToWorld(localSnapPos); // world snap position + } else { + snapPos = _snapper(this.el.object3D.position, this.data.offset, this.data.snap); + } + + this.el.object3D.position.copy(snapPos); + - this.el.setAttribute('position', pos); } }); \ No newline at end of file