{"mappings":"AAAA;IACE,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAOD;IACE,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAA;CACV;AAoBD;IACE,QAAQ,EAAE,KAAK,CAAA;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AU7BD;gBASc,EAAE,EAAE,qBAAqB;IAgDxB,iBAAiB,CAAC,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC;IA6B1F,oBAAoB,CAAC,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IA0BtH,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,GAAE,MAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IA6CrI,OAAO;CAWrB","sources":["src/v2/src/v2/types.ts","src/v2/src/v2/gpu/utils.ts","src/v2/src/v2/gpu/Program.ts","src/v2/src/v2/gpu/ComputeSimilarities.ts","src/v2/src/v2/gpu/AverageSimilarities.ts","src/v2/src/v2/gpu/FindHighestSimilarities.ts","src/v2/src/v2/gpu/FindHighestSimilarity.ts","src/v2/src/v2/gpu/DownloadTexture.ts","src/v2/src/v2/gpu/CropImage.ts","src/v2/src/v2/gpu/index.ts","src/v2/src/v2/index.ts","src/v2/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,"import * as gpu from './gpu/index'\nimport { imageDataToTexture, chunk } from './gpu/utils'\nimport { Chunk, Dimensions, Match, Point, WaldoImageData, WaldoTexture } from './types'\n\ntype _Match = Match\ntype _Point = Point\ntype _WaldoImageData = WaldoImageData\nexport { _Match as Match, _Point as Point, _WaldoImageData as WaldoImageData }\n\nexport class Waldo {\n  private gl: WebGLRenderingContext\n  private computeSimilarities: gpu.ComputeSimilarities\n  private averageSimilarities: gpu.AverageSimilarities\n  private findHighestSimilarities: gpu.FindHighestSimilarities\n  private findHighestSimilarity: gpu.FindHighestSimilarity\n  private downloadTexture: gpu.DownloadTexture\n  private _glLocked: boolean = false\n\n  constructor(gl: WebGLRenderingContext) {\n    this.gl = gl\n    this.gl.getExtension('OES_texture_float')\n    \n    this.computeSimilarities = new gpu.ComputeSimilarities(this.gl)\n    this.averageSimilarities = new gpu.AverageSimilarities(this.gl)\n    this.findHighestSimilarities = new gpu.FindHighestSimilarities(this.gl)\n    this.findHighestSimilarity = new gpu.FindHighestSimilarity(this.gl)\n\n    this.downloadTexture = new gpu.DownloadTexture(this.gl)\n  }\n\n  private lockGl(): Promise<boolean> {\n    // Wait for unlock, then lock\n    const poll = (resolve) => {\n      if (!this._glLocked) {\n        this._glLocked = true\n        resolve(this._glLocked)\n      } else {\n        // console.warn('GL is locked, retrying in 10ms')\n        setTimeout(() => { poll(resolve) }, 10)\n      }\n    }\n    return new Promise(poll)\n  }\n\n  private unlockGl() {\n    this._glLocked = false\n  }\n\n  private highestSimilarities(imageData: WaldoImageData, templateData: WaldoImageData): Chunk[] {\n    // Create textures\n    const image = imageDataToTexture(this.gl, imageData)\n    const template = imageDataToTexture(this.gl, templateData)\n\n    // Split image into processing chunks that don't exceed the texture size limitation\n    const chunks = chunk(image.dimensions, template.dimensions, this.gl.MAX_TEXTURE_SIZE)\n\n    // Do processing for every chunk\n    chunks.forEach(chunk => {\n      chunk.computedSimilarities = this.computeSimilarities.run(image, template, chunk.region)\n      chunk.averagedSimilarities = this.averageSimilarities.run(chunk.computedSimilarities, template.dimensions)\n      chunk.highestSimilarities = this.findHighestSimilarities.run(chunk.averagedSimilarities)\n    })\n\n    return chunks\n  }\n\n  public async highestSimilarity(imageData: WaldoImageData, templateData: WaldoImageData): Promise<Match> {\n    await this.lockGl()\n\n    const chunks: Chunk[] = this.highestSimilarities(imageData, templateData)\n\n    let highestSimilarityValue = 0\n    let highestSimilarityLocation: Point = { x: 0, y: 0 }\n\n    chunks.forEach(chunk => {\n      chunk.highestSimilarity = this.findHighestSimilarity.run(chunk.highestSimilarities as WaldoTexture)\n      const { data } = this.downloadTexture.run(chunk.highestSimilarity)\n\n      if (data[0] >= highestSimilarityValue) {\n        highestSimilarityValue = data[0]\n        highestSimilarityLocation = {\n          x: Math.floor(data[1] + chunk.region.origin.x),\n          y: Math.floor(data[2] + chunk.region.origin.y)\n        }\n      }\n    })\n\n    this.unlockGl()\n\n    return {\n      location: highestSimilarityLocation,\n      similarity: highestSimilarityValue\n    }\n  }\n\n  public async filteredSimilarities(imageData: WaldoImageData, templateData: WaldoImageData, minSimilarity: number): Promise<Match[]> {\n    await this.lockGl()\n\n    const chunks: Chunk[] = this.highestSimilarities(imageData, templateData)\n    const matches: Match[] = []\n\n    chunks.forEach(chunk => {\n      const { data } = this.downloadTexture.run(chunk.highestSimilarities as WaldoTexture)\n\n      for (let i = 0; i < data.length; i = i+4) {\n        if (data[i+0] >= minSimilarity) {\n          matches.push({\n            similarity: data[i+0],\n            location: {\n              x: Math.floor(data[i+1] + chunk.region.origin.x),\n              y: Math.floor(i/4       + chunk.region.origin.y)\n            }\n          })\n        }\n      }\n    })\n\n    this.unlockGl()\n    return matches\n  }\n\n  public async verifyMatch(imageData: WaldoImageData, templateData: WaldoImageData, match: Match, similarityMargin: number = 0.05): Promise<boolean> {\n    await this.lockGl()\n\n    const image = imageDataToTexture(this.gl, imageData)\n    const template = imageDataToTexture(this.gl, templateData)\n\n    const chunk: Chunk = {\n      region: {\n        origin: {\n          x: match.location.x,\n          y: match.location.y\n        },\n        dimensions: { w: 1, h: 1 }\n      }\n    }\n\n    chunk.computedSimilarities = this.computeSimilarities.run(image, template, chunk.region)\n    chunk.averagedSimilarities = this.averageSimilarities.run(chunk.computedSimilarities, template.dimensions)\n    const { data } = this.downloadTexture.run(chunk.averagedSimilarities)\n\n    this.unlockGl()\n  \n    /*\n      Check if provided match location is possible!\n\n      Texture lookup at coordinates that are outside of the actual\n      texture size return the value of the closest coordinate.\n      (Texture doesn't wrap arround but is clamped to the edge)\n\n      Without this check this function could also verify a match that is located outside of the image!\n      (As long as the to be verified similarity value is the same as the acutal similarity value on the edge of the image)\n    */\n    const validDimensions: Dimensions = {\n      w: image.dimensions.w - template.dimensions.w + 1,\n      h: image.dimensions.h - template.dimensions.h + 1\n    }\n\n    if (match.location.x > validDimensions.w || match.location.y > validDimensions.h) {\n      return false\n    }\n\n    // Verify match if difference between recalculated similarity and specified similarity is within 5% (default)\n    return (Math.abs(data[0] - match.similarity) < similarityMargin)\n  }\n\n  public async destroy() {\n    this.computeSimilarities.destroy()\n    this.averageSimilarities.destroy()\n    this.findHighestSimilarities.destroy()\n    this.findHighestSimilarity.destroy()\n    this.downloadTexture.destroy()\n    this.gl.getExtension('WEBGL_lose_context')?.loseContext()\n    if (this.gl.canvas !== undefined) {\n      this.gl.canvas.remove()\n    }\n  }\n}"],"names":[],"version":3,"file":"types.d.ts.map"}