var ARjs = ARjs || {}
var THREEx = THREEx || {}
ARjs.MarkersAreaLearning = THREEx.ArMultiMakersLearning = function(arToolkitContext, subMarkersControls){
var _this = this
this._arToolkitContext = arToolkitContext
this.subMarkersControls = subMarkersControls
this.enabled = true
arToolkitContext.addEventListener('sourceProcessed', function(){
_this._onSourceProcessed()
})
}
* What to do when a image source is fully processed
*/
ARjs.MarkersAreaLearning.prototype._onSourceProcessed = function(){
var originQuaternion = this.subMarkersControls[0].object3d.quaternion
if( this.enabled === false ) return
var visibleMarkerControls = this.subMarkersControls.filter(function(markerControls){
return markerControls.object3d.visible === true
})
var count = Object.keys(visibleMarkerControls).length
var positionDelta = new THREE.Vector3()
var quaternionDelta = new THREE.Quaternion()
var scaleDelta = new THREE.Vector3()
var tmpMatrix = new THREE.Matrix4()
for(var i = 0; i < count; i++){
var markerControls1 = visibleMarkerControls[i]
for(var j = 0; j < count; j++){
var markerControls2 = visibleMarkerControls[j]
if( i === j ) continue
if( markerControls1.object3d.userData.seenCouples === undefined ){
markerControls1.object3d.userData.seenCouples = {}
}
var seenCouples = markerControls1.object3d.userData.seenCouples
if( seenCouples[markerControls2.id] === undefined ){
seenCouples[markerControls2.id] = {
count : 0,
position : {
sum: new THREE.Vector3(0,0,0),
average: new THREE.Vector3(0,0,0),
},
quaternion : {
sum: new THREE.Quaternion(0,0,0,0),
average: new THREE.Quaternion(0,0,0,0),
},
scale : {
sum: new THREE.Vector3(0,0,0),
average: new THREE.Vector3(0,0,0),
},
}
}
tmpMatrix.getInverse(markerControls1.object3d.matrix)
tmpMatrix.multiply(markerControls2.object3d.matrix)
tmpMatrix.decompose(positionDelta, quaternionDelta, scaleDelta)
var stats = seenCouples[markerControls2.id]
stats.count++
THREEx.ArMultiMarkerControls.averageVector3(stats.position.sum, positionDelta, stats.count, stats.position.average)
THREEx.ArMultiMarkerControls.averageQuaternion(stats.quaternion.sum, quaternionDelta, originQuaternion, stats.count, stats.quaternion.average)
THREEx.ArMultiMarkerControls.averageVector3(stats.scale.sum, scaleDelta, stats.count, stats.scale.average)
}
}
}
ARjs.MarkersAreaLearning.prototype.computeResult = function(){
var _this = this
var originSubControls = this.subMarkersControls[0]
this.deleteResult()
originSubControls.object3d.userData.result = {
averageMatrix : new THREE.Matrix4(),
confidenceFactor: 1,
}
* ALGO in pseudo code
*
* - Set confidenceFactor of origin sub markers as 1
*
* Start Looping
* - For a given sub marker, skip it if it already has a result.
* - if no result, check all seen couple and find n ones which has a progress of 1 or more.
* - So the other seen sub markers, got a valid transformation matrix.
* - So take local averages position/orientation/scale, compose a transformation matrix.
* - aka transformation matrix from parent matrix * transf matrix pos/orientation/scale
* - Multiple it by the other seen marker matrix.
* - Loop on the array until one pass could not compute any new sub marker
*/
do{
var resultChanged = false
this.subMarkersControls.forEach(function(subMarkerControls){
var result = subMarkerControls.object3d.userData.result
var isLearned = (result !== undefined && result.confidenceFactor >= 1) ? true : false
if( isLearned === true ) return
var otherSubControlsID = _this._getLearnedCoupleStats(subMarkerControls)
if( otherSubControlsID === null ){
return
}
var otherSubControls = _this._getSubMarkerControlsByID(otherSubControlsID)
var seenCoupleStats = subMarkerControls.object3d.userData.seenCouples[otherSubControlsID]
var averageMatrix = new THREE.Matrix4()
averageMatrix.compose(seenCoupleStats.position.average, seenCoupleStats.quaternion.average, seenCoupleStats.scale.average)
var otherAverageMatrix = otherSubControls.object3d.userData.result.averageMatrix
var matrix = new THREE.Matrix4().getInverse(otherAverageMatrix).multiply(averageMatrix)
matrix = new THREE.Matrix4().getInverse(matrix)
console.assert( subMarkerControls.object3d.userData.result === undefined )
subMarkerControls.object3d.userData.result = {
averageMatrix: matrix,
confidenceFactor: 1
}
resultChanged = true
})
}while(resultChanged === true)
}
* get a _this.subMarkersControls id based on markerControls.id
*/
ARjs.MarkersAreaLearning.prototype._getLearnedCoupleStats = function(subMarkerControls){
if( subMarkerControls.object3d.userData.seenCouples === undefined ) return null
var seenCouples = subMarkerControls.object3d.userData.seenCouples
var coupleControlsIDs = Object.keys(seenCouples).map(Number)
for(var i = 0; i < coupleControlsIDs.length; i++){
var otherSubControlsID = coupleControlsIDs[i]
var otherSubControls = this._getSubMarkerControlsByID(otherSubControlsID)
var result = otherSubControls.object3d.userData.result
var isLearned = (result !== undefined && result.confidenceFactor >= 1) ? true : false
if( isLearned === false ) continue
return otherSubControlsID
}
return null
}
* get a _this.subMarkersControls based on markerControls.id
*/
ARjs.MarkersAreaLearning.prototype._getSubMarkerControlsByID = function(controlsID){
for(var i = 0; i < this.subMarkersControls.length; i++){
var subMarkerControls = this.subMarkersControls[i]
if( subMarkerControls.id === controlsID ){
return subMarkerControls
}
}
return null
}
ARjs.MarkersAreaLearning.prototype.toJSON = function(){
this.computeResult()
var data = {
meta : {
createdBy : "Area Learning - AR.js "+THREEx.ArToolkitContext.REVISION,
createdAt : new Date().toJSON(),
},
trackingBackend: this._arToolkitContext.parameters.trackingBackend,
subMarkersControls : [],
}
var originSubControls = this.subMarkersControls[0]
var originMatrixInverse = new THREE.Matrix4().getInverse(originSubControls.object3d.matrix)
this.subMarkersControls.forEach(function(subMarkerControls, index){
if( subMarkerControls.object3d.userData.result === undefined ) return
var poseMatrix = subMarkerControls.object3d.userData.result.averageMatrix
console.assert(poseMatrix instanceof THREE.Matrix4)
var info = {
parameters : {
},
poseMatrix : poseMatrix.toArray(),
}
if( subMarkerControls.parameters.type === 'pattern' ){
info.parameters.type = subMarkerControls.parameters.type
info.parameters.patternUrl = subMarkerControls.parameters.patternUrl
}else if( subMarkerControls.parameters.type === 'barcode' ){
info.parameters.type = subMarkerControls.parameters.type
info.parameters.barcodeValue = subMarkerControls.parameters.barcodeValue
}else console.assert(false)
data.subMarkersControls.push(info)
})
var strJSON = JSON.stringify(data, null, '\t');
var humanReadable = false
if( humanReadable === true ){
var tmp = JSON.parse(strJSON)
tmp.subMarkersControls.forEach(function(markerControls){
markerControls.poseMatrix = markerControls.poseMatrix.map(function(value){
var roundingFactor = 100
return Math.round(value*roundingFactor)/roundingFactor
})
})
strJSON = JSON.stringify(tmp, null, '\t');
}
return strJSON;
}
* reset all collected statistics
*/
ARjs.MarkersAreaLearning.prototype.resetStats = function(){
this.deleteResult()
this.subMarkersControls.forEach(function(markerControls){
delete markerControls.object3d.userData.seenCouples
})
}
* reset all collected statistics
*/
ARjs.MarkersAreaLearning.prototype.deleteResult = function(){
this.subMarkersControls.forEach(function(markerControls){
delete markerControls.object3d.userData.result
})
}