1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  | /** 
 |   * The algoritm is learnt from 
 |   * https://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/ 
 |   * And we made some optimization for matrix inversion. 
 |   * Other similar approaches: 
 |   * "cv::getPerspectiveTransform", "Direct Linear Transformation". 
 |   */ 
 |    
 |  const LN2 = Math.log(2); 
 |    
 |  function determinant( 
 |      rows: number[][], 
 |      rank: number, 
 |      rowStart: number, 
 |      rowMask: number, 
 |      colMask: number, 
 |      detCache: {[key: string]: number} 
 |  ) { 
 |      const cacheKey = rowMask + '-' + colMask; 
 |      const fullRank = rows.length; 
 |    
 |      if (detCache.hasOwnProperty(cacheKey)) { 
 |          return detCache[cacheKey]; 
 |      } 
 |    
 |      if (rank === 1) { 
 |          // In this case the colMask must be like: `11101111`. We can find the place of `0`. 
 |          const colStart = Math.round(Math.log(((1 << fullRank) - 1) & ~colMask) / LN2); 
 |          return rows[rowStart][colStart]; 
 |      } 
 |    
 |      const subRowMask = rowMask | (1 << rowStart); 
 |      let subRowStart = rowStart + 1; 
 |      while (rowMask & (1 << subRowStart)) { 
 |          subRowStart++; 
 |      } 
 |    
 |      let sum = 0; 
 |      for (let j = 0, colLocalIdx = 0; j < fullRank; j++) { 
 |          const colTag = 1 << j; 
 |          if (!(colTag & colMask)) { 
 |              sum += (colLocalIdx % 2 ? -1 : 1) * rows[rowStart][j] 
 |                  // det(subMatrix(0, j)) 
 |                  * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache); 
 |              colLocalIdx++; 
 |          } 
 |      } 
 |    
 |      detCache[cacheKey] = sum; 
 |    
 |      return sum; 
 |  } 
 |    
 |  /** 
 |   * Usage: 
 |   * ```js 
 |   * const transformer = buildTransformer( 
 |   *     [10, 44, 100, 44, 100, 300, 10, 300], 
 |   *     [50, 54, 130, 14, 140, 330, 14, 220] 
 |   * ); 
 |   * const out = []; 
 |   * transformer && transformer([11, 33], out); 
 |   * ``` 
 |   * 
 |   * Notice: `buildTransformer` may take more than 10ms in some Android device. 
 |   * 
 |   * @param src source four points, [x0, y0, x1, y1, x2, y2, x3, y3] 
 |   * @param dest destination four points, [x0, y0, x1, y1, x2, y2, x3, y3] 
 |   * @return transformer If fail, return null/undefined. 
 |   */ 
 |  export function buildTransformer(src: number[], dest: number[]) { 
 |      const mA = [ 
 |          [src[0], src[1], 1, 0, 0, 0, -dest[0] * src[0], -dest[0] * src[1]], 
 |          [0, 0, 0, src[0], src[1], 1, -dest[1] * src[0], -dest[1] * src[1]], 
 |          [src[2], src[3], 1, 0, 0, 0, -dest[2] * src[2], -dest[2] * src[3]], 
 |          [0, 0, 0, src[2], src[3], 1, -dest[3] * src[2], -dest[3] * src[3]], 
 |          [src[4], src[5], 1, 0, 0, 0, -dest[4] * src[4], -dest[4] * src[5]], 
 |          [0, 0, 0, src[4], src[5], 1, -dest[5] * src[4], -dest[5] * src[5]], 
 |          [src[6], src[7], 1, 0, 0, 0, -dest[6] * src[6], -dest[6] * src[7]], 
 |          [0, 0, 0, src[6], src[7], 1, -dest[7] * src[6], -dest[7] * src[7]] 
 |      ]; 
 |    
 |      const detCache = {}; 
 |      const det = determinant(mA, 8, 0, 0, 0, detCache); 
 |      if (det === 0) { 
 |          // can not make transformer when and only when 
 |          // any three of the markers are collinear. 
 |          return; 
 |      } 
 |    
 |      // `invert(mA) * dest`, that is, `adj(mA) / det * dest`. 
 |      const vh: number[] = []; 
 |      for (let i = 0; i < 8; i++) { 
 |          for (let j = 0; j < 8; j++) { 
 |              vh[j] == null && (vh[j] = 0); 
 |              vh[j] += ((i + j) % 2 ? -1 : 1) 
 |                  // det(subMatrix(i, j)) 
 |                  * determinant(mA, 7, i === 0 ? 1 : 0, 1 << i, 1 << j, detCache) 
 |                  / det * dest[i]; 
 |          } 
 |      } 
 |    
 |      return function (out: number[], srcPointX: number, srcPointY: number) { 
 |          const pk = srcPointX * vh[6] + srcPointY * vh[7] + 1; 
 |          out[0] = (srcPointX * vh[0] + srcPointY * vh[1] + vh[2]) / pk; 
 |          out[1] = (srcPointX * vh[3] + srcPointY * vh[4] + vh[5]) / pk; 
 |      }; 
 |  } 
 |  
  |