@srsergio/taptapp-ar 1.0.26 → 1.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compiler/simple-ar.d.ts +46 -22
- package/dist/compiler/simple-ar.js +70 -43
- package/package.json +1 -1
- package/src/compiler/simple-ar.js +75 -46
|
@@ -15,32 +15,49 @@
|
|
|
15
15
|
* await ar.start();
|
|
16
16
|
*/
|
|
17
17
|
export class SimpleAR {
|
|
18
|
+
/**
|
|
19
|
+
* @param {Object} options
|
|
20
|
+
* @param {HTMLElement} options.container
|
|
21
|
+
* @param {string|string[]} options.targetSrc
|
|
22
|
+
* @param {HTMLElement} options.overlay
|
|
23
|
+
* @param {number} [options.scale=1.0]
|
|
24
|
+
* @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onFound]
|
|
25
|
+
* @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onLost]
|
|
26
|
+
* @param {((data: {targetIndex: number, worldMatrix: number[]}) => void) | null} [options.onUpdate]
|
|
27
|
+
* @param {Object} [options.cameraConfig]
|
|
28
|
+
*/
|
|
18
29
|
constructor({ container, targetSrc, overlay, scale, onFound, onLost, onUpdate, cameraConfig, }: {
|
|
19
|
-
container:
|
|
20
|
-
targetSrc:
|
|
21
|
-
overlay:
|
|
30
|
+
container: HTMLElement;
|
|
31
|
+
targetSrc: string | string[];
|
|
32
|
+
overlay: HTMLElement;
|
|
22
33
|
scale?: number | undefined;
|
|
23
|
-
onFound?:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
onFound?: ((data: {
|
|
35
|
+
targetIndex: number;
|
|
36
|
+
}) => void | Promise<void>) | null | undefined;
|
|
37
|
+
onLost?: ((data: {
|
|
38
|
+
targetIndex: number;
|
|
39
|
+
}) => void | Promise<void>) | null | undefined;
|
|
40
|
+
onUpdate?: ((data: {
|
|
41
|
+
targetIndex: number;
|
|
42
|
+
worldMatrix: number[];
|
|
43
|
+
}) => void) | null | undefined;
|
|
44
|
+
cameraConfig?: Object | undefined;
|
|
31
45
|
});
|
|
32
|
-
container:
|
|
33
|
-
targetSrc:
|
|
34
|
-
overlay:
|
|
46
|
+
container: HTMLElement;
|
|
47
|
+
targetSrc: string | string[];
|
|
48
|
+
overlay: HTMLElement;
|
|
35
49
|
scaleMultiplier: number;
|
|
36
|
-
onFound:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
onFound: ((data: {
|
|
51
|
+
targetIndex: number;
|
|
52
|
+
}) => void | Promise<void>) | null;
|
|
53
|
+
onLost: ((data: {
|
|
54
|
+
targetIndex: number;
|
|
55
|
+
}) => void | Promise<void>) | null;
|
|
56
|
+
onUpdateCallback: ((data: {
|
|
57
|
+
targetIndex: number;
|
|
58
|
+
worldMatrix: number[];
|
|
59
|
+
}) => void) | null;
|
|
60
|
+
cameraConfig: Object;
|
|
44
61
|
video: HTMLVideoElement | null;
|
|
45
62
|
controller: Controller | null;
|
|
46
63
|
isTracking: boolean;
|
|
@@ -59,5 +76,12 @@ export class SimpleAR {
|
|
|
59
76
|
_initController(): void;
|
|
60
77
|
_handleUpdate(data: any): void;
|
|
61
78
|
_positionOverlay(mVT: any, targetIndex: any): void;
|
|
79
|
+
/**
|
|
80
|
+
* Projects a 3D marker-space point all the way to 2D screen CSS pixels
|
|
81
|
+
*/
|
|
82
|
+
_projectToScreen(x: any, y: any, z: any, mVT: any, proj: any, videoW: any, videoH: any, containerRect: any, needsRotation: any): {
|
|
83
|
+
sx: number;
|
|
84
|
+
sy: number;
|
|
85
|
+
};
|
|
62
86
|
}
|
|
63
87
|
import { Controller } from "./controller.js";
|
|
@@ -16,6 +16,17 @@ import { Controller } from "./controller.js";
|
|
|
16
16
|
* await ar.start();
|
|
17
17
|
*/
|
|
18
18
|
class SimpleAR {
|
|
19
|
+
/**
|
|
20
|
+
* @param {Object} options
|
|
21
|
+
* @param {HTMLElement} options.container
|
|
22
|
+
* @param {string|string[]} options.targetSrc
|
|
23
|
+
* @param {HTMLElement} options.overlay
|
|
24
|
+
* @param {number} [options.scale=1.0]
|
|
25
|
+
* @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onFound]
|
|
26
|
+
* @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onLost]
|
|
27
|
+
* @param {((data: {targetIndex: number, worldMatrix: number[]}) => void) | null} [options.onUpdate]
|
|
28
|
+
* @param {Object} [options.cameraConfig]
|
|
29
|
+
*/
|
|
19
30
|
constructor({ container, targetSrc, overlay, scale = 1.0, // Multiplicador de escala personalizado
|
|
20
31
|
onFound = null, onLost = null, onUpdate = null, cameraConfig = { facingMode: 'environment', width: 1280, height: 720 }, }) {
|
|
21
32
|
this.container = container;
|
|
@@ -45,6 +56,7 @@ class SimpleAR {
|
|
|
45
56
|
const targets = Array.isArray(this.targetSrc) ? this.targetSrc : [this.targetSrc];
|
|
46
57
|
const result = await this.controller.addImageTargets(targets);
|
|
47
58
|
this.markerDimensions = result.dimensions; // [ [w1, h1], [w2, h2], ... ]
|
|
59
|
+
console.log("Targets loaded. Dimensions:", this.markerDimensions);
|
|
48
60
|
this.controller.processVideo(this.video);
|
|
49
61
|
return this;
|
|
50
62
|
}
|
|
@@ -133,54 +145,38 @@ class SimpleAR {
|
|
|
133
145
|
const containerRect = this.container.getBoundingClientRect();
|
|
134
146
|
const videoW = this.video.videoWidth;
|
|
135
147
|
const videoH = this.video.videoHeight;
|
|
148
|
+
// 1. Determine orientation needs
|
|
136
149
|
const isPortrait = containerRect.height > containerRect.width;
|
|
137
150
|
const isVideoLandscape = videoW > videoH;
|
|
138
151
|
const needsRotation = isPortrait && isVideoLandscape;
|
|
139
|
-
//
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
//
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
//
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
rotation = Math.atan2(mVT[1][0], mVT[0][0]) - Math.PI / 2;
|
|
152
|
+
// 2. Get intrinsic projection from controller
|
|
153
|
+
const proj = this.controller.projectionTransform;
|
|
154
|
+
// 3. Project 3 points to determine position, scale, and rotation
|
|
155
|
+
// Points in Marker Space: Center, Right-Edge, and Down-Edge
|
|
156
|
+
const pMid = this._projectToScreen(markerW / 2, markerH / 2, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
157
|
+
const pRight = this._projectToScreen(markerW / 2 + 100, markerH / 2, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
158
|
+
// 4. Calculate Screen Position
|
|
159
|
+
const screenX = pMid.sx;
|
|
160
|
+
const screenY = pMid.sy;
|
|
161
|
+
// 5. Calculate Rotation and Scale from the projected X-axis vector
|
|
162
|
+
const dx = pRight.sx - pMid.sx;
|
|
163
|
+
const dy = pRight.sy - pMid.sy;
|
|
164
|
+
const rotation = Math.atan2(dy, dx);
|
|
165
|
+
const pixelDistance100 = Math.sqrt(dx * dx + dy * dy);
|
|
166
|
+
// Since we projected 100 units, the scale for the whole markerW is:
|
|
167
|
+
const finalScale = (pixelDistance100 / 100) * this.scaleMultiplier;
|
|
168
|
+
// DEBUG LOGS
|
|
169
|
+
if (window.AR_DEBUG) {
|
|
170
|
+
console.log('--- AR POSITION DEBUG (Point Projection) ---');
|
|
171
|
+
console.log('Container:', containerRect.width.toFixed(0), 'x', containerRect.height.toFixed(0));
|
|
172
|
+
console.log('Video:', videoW, 'x', videoH, 'needsRotation:', needsRotation);
|
|
173
|
+
console.log('Screen Pos:', screenX.toFixed(1), screenY.toFixed(1));
|
|
174
|
+
console.log('Rotated Angle:', (rotation * 180 / Math.PI).toFixed(1), 'deg');
|
|
175
|
+
console.log('Final Scale:', finalScale.toFixed(4));
|
|
164
176
|
}
|
|
165
|
-
else {
|
|
166
|
-
// Normal mapping:
|
|
167
|
-
// Buffer X -> Screen X
|
|
168
|
-
// Buffer Y -> Screen Y
|
|
169
|
-
const bufferOffsetX = (tx * f / tz);
|
|
170
|
-
const bufferOffsetY = (ty * f / tz);
|
|
171
|
-
screenX = offsetX + (displayW / 2) + (bufferOffsetX * perspectiveScale);
|
|
172
|
-
screenY = offsetY + (displayH / 2) + (bufferOffsetY * perspectiveScale);
|
|
173
|
-
rotation = Math.atan2(mVT[1][0], mVT[0][0]);
|
|
174
|
-
}
|
|
175
|
-
// Final Scale calculation:
|
|
176
|
-
// f/tz converts world units to buffer pixels
|
|
177
|
-
// perspectiveScale converts buffer pixels to screen pixels
|
|
178
|
-
// matrixScale handles the target's relative orientation in 2D
|
|
179
|
-
const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
|
|
180
|
-
const finalScale = (f / tz) * perspectiveScale * matrixScale * this.scaleMultiplier;
|
|
181
177
|
// Apply
|
|
182
178
|
this.overlay.style.width = `${markerW}px`;
|
|
183
|
-
this.overlay.style.height = 'auto';
|
|
179
|
+
this.overlay.style.height = 'auto'; // Maintain aspect ratio of the overlay asset
|
|
184
180
|
this.overlay.style.position = 'absolute';
|
|
185
181
|
this.overlay.style.transformOrigin = 'center center';
|
|
186
182
|
this.overlay.style.left = '0';
|
|
@@ -188,9 +184,40 @@ class SimpleAR {
|
|
|
188
184
|
this.overlay.style.transform = `
|
|
189
185
|
translate(${screenX}px, ${screenY}px)
|
|
190
186
|
translate(-50%, -50%)
|
|
191
|
-
scale(${finalScale})
|
|
192
187
|
rotate(${rotation}rad)
|
|
188
|
+
scale(${finalScale})
|
|
193
189
|
`;
|
|
194
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Projects a 3D marker-space point all the way to 2D screen CSS pixels
|
|
193
|
+
*/
|
|
194
|
+
_projectToScreen(x, y, z, mVT, proj, videoW, videoH, containerRect, needsRotation) {
|
|
195
|
+
// Marker -> Camera Space
|
|
196
|
+
const tx = mVT[0][0] * x + mVT[0][1] * y + mVT[0][2] * z + mVT[0][3];
|
|
197
|
+
const ty = mVT[1][0] * x + mVT[1][1] * y + mVT[1][2] * z + mVT[1][3];
|
|
198
|
+
const tz = mVT[2][0] * x + mVT[2][1] * y + mVT[2][2] * z + mVT[2][3];
|
|
199
|
+
// Camera -> Buffer Pixels (e.g. 1280x720)
|
|
200
|
+
const bx = (proj[0][0] * tx / tz) + proj[0][2];
|
|
201
|
+
const by = (proj[1][1] * ty / tz) + proj[1][2];
|
|
202
|
+
// Buffer -> Screen CSS Pixels
|
|
203
|
+
const vW = needsRotation ? videoH : videoW;
|
|
204
|
+
const vH = needsRotation ? videoW : videoH;
|
|
205
|
+
const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
|
|
206
|
+
const displayW = vW * perspectiveScale;
|
|
207
|
+
const displayH = vH * perspectiveScale;
|
|
208
|
+
const offsetX = (containerRect.width - displayW) / 2;
|
|
209
|
+
const offsetY = (containerRect.height - displayH) / 2;
|
|
210
|
+
let sx, sy;
|
|
211
|
+
if (needsRotation) {
|
|
212
|
+
// Mapping: Camera +X (Right) -> Screen +Y (Down), Camera +Y (Down) -> Screen -X (Left)
|
|
213
|
+
sx = offsetX + (displayW / 2) - (by - proj[1][2]) * perspectiveScale;
|
|
214
|
+
sy = offsetY + (displayH / 2) + (bx - proj[0][2]) * perspectiveScale;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
sx = offsetX + (displayW / 2) + (bx - proj[0][2]) * perspectiveScale;
|
|
218
|
+
sy = offsetY + (displayH / 2) + (by - proj[1][2]) * perspectiveScale;
|
|
219
|
+
}
|
|
220
|
+
return { sx, sy };
|
|
221
|
+
}
|
|
195
222
|
}
|
|
196
223
|
export { SimpleAR };
|
package/package.json
CHANGED
|
@@ -17,6 +17,17 @@ import { Controller } from "./controller.js";
|
|
|
17
17
|
* await ar.start();
|
|
18
18
|
*/
|
|
19
19
|
class SimpleAR {
|
|
20
|
+
/**
|
|
21
|
+
* @param {Object} options
|
|
22
|
+
* @param {HTMLElement} options.container
|
|
23
|
+
* @param {string|string[]} options.targetSrc
|
|
24
|
+
* @param {HTMLElement} options.overlay
|
|
25
|
+
* @param {number} [options.scale=1.0]
|
|
26
|
+
* @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onFound]
|
|
27
|
+
* @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onLost]
|
|
28
|
+
* @param {((data: {targetIndex: number, worldMatrix: number[]}) => void) | null} [options.onUpdate]
|
|
29
|
+
* @param {Object} [options.cameraConfig]
|
|
30
|
+
*/
|
|
20
31
|
constructor({
|
|
21
32
|
container,
|
|
22
33
|
targetSrc,
|
|
@@ -59,6 +70,7 @@ class SimpleAR {
|
|
|
59
70
|
const targets = Array.isArray(this.targetSrc) ? this.targetSrc : [this.targetSrc];
|
|
60
71
|
const result = await this.controller.addImageTargets(targets);
|
|
61
72
|
this.markerDimensions = result.dimensions; // [ [w1, h1], [w2, h2], ... ]
|
|
73
|
+
console.log("Targets loaded. Dimensions:", this.markerDimensions);
|
|
62
74
|
|
|
63
75
|
this.controller.processVideo(this.video);
|
|
64
76
|
|
|
@@ -155,68 +167,49 @@ class SimpleAR {
|
|
|
155
167
|
|
|
156
168
|
const [markerW, markerH] = this.markerDimensions[targetIndex];
|
|
157
169
|
const containerRect = this.container.getBoundingClientRect();
|
|
158
|
-
|
|
159
170
|
const videoW = this.video.videoWidth;
|
|
160
171
|
const videoH = this.video.videoHeight;
|
|
161
172
|
|
|
173
|
+
// 1. Determine orientation needs
|
|
162
174
|
const isPortrait = containerRect.height > containerRect.width;
|
|
163
175
|
const isVideoLandscape = videoW > videoH;
|
|
164
176
|
const needsRotation = isPortrait && isVideoLandscape;
|
|
165
177
|
|
|
166
|
-
//
|
|
167
|
-
const
|
|
168
|
-
const vH = needsRotation ? videoW : videoH;
|
|
178
|
+
// 2. Get intrinsic projection from controller
|
|
179
|
+
const proj = this.controller.projectionTransform;
|
|
169
180
|
|
|
170
|
-
//
|
|
171
|
-
|
|
181
|
+
// 3. Project 3 points to determine position, scale, and rotation
|
|
182
|
+
// Points in Marker Space: Center, Right-Edge, and Down-Edge
|
|
183
|
+
const pMid = this._projectToScreen(markerW / 2, markerH / 2, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
184
|
+
const pRight = this._projectToScreen(markerW / 2 + 100, markerH / 2, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
172
185
|
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
const offsetY = (containerRect.height - displayH) / 2;
|
|
186
|
+
// 4. Calculate Screen Position
|
|
187
|
+
const screenX = pMid.sx;
|
|
188
|
+
const screenY = pMid.sy;
|
|
177
189
|
|
|
178
|
-
//
|
|
179
|
-
const
|
|
190
|
+
// 5. Calculate Rotation and Scale from the projected X-axis vector
|
|
191
|
+
const dx = pRight.sx - pMid.sx;
|
|
192
|
+
const dy = pRight.sy - pMid.sy;
|
|
180
193
|
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
|
|
184
|
-
const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
|
|
194
|
+
const rotation = Math.atan2(dy, dx);
|
|
195
|
+
const pixelDistance100 = Math.sqrt(dx * dx + dy * dy);
|
|
185
196
|
|
|
186
|
-
|
|
197
|
+
// Since we projected 100 units, the scale for the whole markerW is:
|
|
198
|
+
const finalScale = (pixelDistance100 / 100) * this.scaleMultiplier;
|
|
187
199
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
screenY = offsetY + (displayH / 2) + (bufferOffsetX * perspectiveScale);
|
|
197
|
-
rotation = Math.atan2(mVT[1][0], mVT[0][0]) - Math.PI / 2;
|
|
198
|
-
} else {
|
|
199
|
-
// Normal mapping:
|
|
200
|
-
// Buffer X -> Screen X
|
|
201
|
-
// Buffer Y -> Screen Y
|
|
202
|
-
const bufferOffsetX = (tx * f / tz);
|
|
203
|
-
const bufferOffsetY = (ty * f / tz);
|
|
204
|
-
|
|
205
|
-
screenX = offsetX + (displayW / 2) + (bufferOffsetX * perspectiveScale);
|
|
206
|
-
screenY = offsetY + (displayH / 2) + (bufferOffsetY * perspectiveScale);
|
|
207
|
-
rotation = Math.atan2(mVT[1][0], mVT[0][0]);
|
|
200
|
+
// DEBUG LOGS
|
|
201
|
+
if (window.AR_DEBUG) {
|
|
202
|
+
console.log('--- AR POSITION DEBUG (Point Projection) ---');
|
|
203
|
+
console.log('Container:', containerRect.width.toFixed(0), 'x', containerRect.height.toFixed(0));
|
|
204
|
+
console.log('Video:', videoW, 'x', videoH, 'needsRotation:', needsRotation);
|
|
205
|
+
console.log('Screen Pos:', screenX.toFixed(1), screenY.toFixed(1));
|
|
206
|
+
console.log('Rotated Angle:', (rotation * 180 / Math.PI).toFixed(1), 'deg');
|
|
207
|
+
console.log('Final Scale:', finalScale.toFixed(4));
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
// Final Scale calculation:
|
|
211
|
-
// f/tz converts world units to buffer pixels
|
|
212
|
-
// perspectiveScale converts buffer pixels to screen pixels
|
|
213
|
-
// matrixScale handles the target's relative orientation in 2D
|
|
214
|
-
const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
|
|
215
|
-
const finalScale = (f / tz) * perspectiveScale * matrixScale * this.scaleMultiplier;
|
|
216
|
-
|
|
217
210
|
// Apply
|
|
218
211
|
this.overlay.style.width = `${markerW}px`;
|
|
219
|
-
this.overlay.style.height = 'auto';
|
|
212
|
+
this.overlay.style.height = 'auto'; // Maintain aspect ratio of the overlay asset
|
|
220
213
|
this.overlay.style.position = 'absolute';
|
|
221
214
|
this.overlay.style.transformOrigin = 'center center';
|
|
222
215
|
this.overlay.style.left = '0';
|
|
@@ -224,10 +217,46 @@ class SimpleAR {
|
|
|
224
217
|
this.overlay.style.transform = `
|
|
225
218
|
translate(${screenX}px, ${screenY}px)
|
|
226
219
|
translate(-50%, -50%)
|
|
227
|
-
scale(${finalScale})
|
|
228
220
|
rotate(${rotation}rad)
|
|
221
|
+
scale(${finalScale})
|
|
229
222
|
`;
|
|
230
223
|
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Projects a 3D marker-space point all the way to 2D screen CSS pixels
|
|
227
|
+
*/
|
|
228
|
+
_projectToScreen(x, y, z, mVT, proj, videoW, videoH, containerRect, needsRotation) {
|
|
229
|
+
// Marker -> Camera Space
|
|
230
|
+
const tx = mVT[0][0] * x + mVT[0][1] * y + mVT[0][2] * z + mVT[0][3];
|
|
231
|
+
const ty = mVT[1][0] * x + mVT[1][1] * y + mVT[1][2] * z + mVT[1][3];
|
|
232
|
+
const tz = mVT[2][0] * x + mVT[2][1] * y + mVT[2][2] * z + mVT[2][3];
|
|
233
|
+
|
|
234
|
+
// Camera -> Buffer Pixels (e.g. 1280x720)
|
|
235
|
+
const bx = (proj[0][0] * tx / tz) + proj[0][2];
|
|
236
|
+
const by = (proj[1][1] * ty / tz) + proj[1][2];
|
|
237
|
+
|
|
238
|
+
// Buffer -> Screen CSS Pixels
|
|
239
|
+
const vW = needsRotation ? videoH : videoW;
|
|
240
|
+
const vH = needsRotation ? videoW : videoH;
|
|
241
|
+
const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
|
|
242
|
+
|
|
243
|
+
const displayW = vW * perspectiveScale;
|
|
244
|
+
const displayH = vH * perspectiveScale;
|
|
245
|
+
const offsetX = (containerRect.width - displayW) / 2;
|
|
246
|
+
const offsetY = (containerRect.height - displayH) / 2;
|
|
247
|
+
|
|
248
|
+
let sx, sy;
|
|
249
|
+
if (needsRotation) {
|
|
250
|
+
// Mapping: Camera +X (Right) -> Screen +Y (Down), Camera +Y (Down) -> Screen -X (Left)
|
|
251
|
+
sx = offsetX + (displayW / 2) - (by - proj[1][2]) * perspectiveScale;
|
|
252
|
+
sy = offsetY + (displayH / 2) + (bx - proj[0][2]) * perspectiveScale;
|
|
253
|
+
} else {
|
|
254
|
+
sx = offsetX + (displayW / 2) + (bx - proj[0][2]) * perspectiveScale;
|
|
255
|
+
sy = offsetY + (displayH / 2) + (by - proj[1][2]) * perspectiveScale;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return { sx, sy };
|
|
259
|
+
}
|
|
231
260
|
}
|
|
232
261
|
|
|
233
262
|
export { SimpleAR };
|