UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

134 lines 5.17 kB
import { Accuracy1D } from '../../data/values/Accuracy1D'; import { AngleUnit } from '../../utils'; import { MergeShape } from './MergeShape'; /** * Merges two or more frames together based on a merge key. * * ## Usage * ```typescript * new FrameMergeNode(); * ``` * @rdf {@link http://purl.org/poso/HighLevelFusion} * @category Flow shape */ export class FrameMergeNode extends MergeShape { /** * Merge multiple data objects together * @param {DataObject[]} objects Data objects * @returns {DataObject} Merged data object */ mergeObjects(objects) { const baseObject = objects[0]; // Relative positions for (let i = 1; i < objects.length; i++) { objects[i].getRelativePositions().forEach(relativePos => { baseObject.addRelativePosition(relativePos); }); } // Weighted position merging const positions = objects.map(object => object.getPosition()).filter(position => position !== undefined); if (positions.length === 0) { return baseObject; } let newPosition = positions[0].clone(); for (let i = 1; i < positions.length; i++) { newPosition = this.mergePositions(newPosition, positions[i].clone()); } if (newPosition.accuracy) { newPosition.accuracy.value = 1 / newPosition.accuracy.value; } if (newPosition.linearVelocity) { newPosition.linearVelocity.setAccuracy(1 / newPosition.linearVelocity.accuracy.valueOf()); } baseObject.setPosition(newPosition); return baseObject; } mergePositions(positionA, positionB) { const newPosition = positionA; if (!positionB) { return newPosition; } // Accuracy of the two positions const posAccuracyA = positionA.accuracy || new Accuracy1D(1, positionA.unit); let posAccuracyB = positionB.accuracy || new Accuracy1D(1, positionB.unit); posAccuracyB = posAccuracyB.to(posAccuracyA.unit); // Apply position merging newPosition.fromVector(newPosition.toVector3().multiplyScalar(1 / posAccuracyA.valueOf()).add(positionB.toVector3(newPosition.unit).multiplyScalar(1 / posAccuracyB.valueOf()))); newPosition.fromVector(newPosition.toVector3().divideScalar(1 / posAccuracyA.valueOf() + 1 / posAccuracyB.valueOf())); newPosition.accuracy.value = 1 / (posAccuracyA.valueOf() + posAccuracyB.valueOf()); newPosition.linearVelocity = this._mergeVelocity(newPosition.linearVelocity, positionB.linearVelocity); newPosition.orientation = this._mergeOrientation(newPosition.orientation, positionB.orientation); // Average timestamp newPosition.timestamp = Math.round((positionA.timestamp * (1 / posAccuracyA.value) + positionB.timestamp * (1 / posAccuracyB.value)) / (1 / posAccuracyA.value + 1 / posAccuracyB.value)); return newPosition; } _mergeVelocity(velocityA, velocityB) { if (velocityB) { if (velocityA) { const lvAccuracyA = velocityA.accuracy.valueOf() || 1; const lvAccuracyB = velocityB.accuracy.valueOf() || 1; // Merge linear velocity velocityA.multiplyScalar(1 / lvAccuracyA).add(velocityB.multiplyScalar(1 / lvAccuracyB)); velocityA.divideScalar(1 / lvAccuracyA + 1 / lvAccuracyB); velocityA.setAccuracy(1 / (lvAccuracyA + lvAccuracyB)); } else { velocityA = velocityB; } } return velocityA; } _mergeOrientation(orientationA, orientationB) { if (orientationB) { if (orientationA) { const accuracyA = orientationA.accuracy || new Accuracy1D(1, AngleUnit.RADIAN); const accuracyB = orientationB.accuracy || new Accuracy1D(1, AngleUnit.RADIAN); const slerp = (1 / accuracyA.value + 1 / accuracyB.value) / accuracyB.value / 2; orientationA.slerp(orientationB, slerp); } else { orientationA = orientationB; } } return orientationA; } /** * Merge the data frames * @param {DataFrame[]} frames Data frames to merge * @returns {Promise<DataFrame>} Promise of merged data frame */ merge(frames) { const mergedFrame = frames[0]; const mergedObjects = new Map(); mergedFrame.getObjects().forEach(object => { if (mergedObjects.get(object.uid)) { mergedObjects.get(object.uid).push(object); } else { mergedObjects.set(object.uid, [object]); } }); for (let i = 1; i < frames.length; i++) { const frame = frames[i]; frame.getObjects().forEach(object => { if (!mergedFrame.hasObject(object)) { // Add object mergedFrame.addObject(object); mergedObjects.set(object.uid, [object]); } else { mergedObjects.get(object.uid).push(object); } }); // Merge properties Object.keys(frame).forEach(propertyName => { const value = mergedFrame[propertyName]; if (value === undefined || value === null) { mergedFrame[propertyName] = frame[propertyName]; } }); } // Merge objects using the merging function mergedObjects.forEach(values => { const mergedObject = this.mergeObjects(values); mergedFrame.addObject(mergedObject); }); return mergedFrame; } }