p5-phone 1.6.0 → 1.6.2
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/README.md +3 -3
- package/dist/p5-phone.js +6 -3
- package/dist/p5-phone.min.js +2 -2
- package/examples/homepage/index.html +5 -5
- package/package.json +1 -1
- package/src/p5-phone.js +6 -3
- package/examples/workArea/01_camera-selector/README.md +0 -119
- package/examples/workArea/01_camera-selector/index.html +0 -28
- package/examples/workArea/01_camera-selector/sketch.js +0 -239
- package/examples/workArea/03_facemesh-nose/index.html +0 -34
- package/examples/workArea/03_facemesh-nose/sketch.js +0 -247
- package/examples/workArea/03_facemesh-nose-preload/index.html +0 -34
- package/examples/workArea/03_facemesh-nose-preload/sketch.js +0 -173
- package/examples/workArea/04_facemesh-FINAL/README.md +0 -85
- package/examples/workArea/04_facemesh-FINAL/index.html +0 -31
- package/examples/workArea/04_facemesh-FINAL/sketch.js +0 -240
- package/examples/workArea/04_facemesh-simplified_temp/README.md +0 -93
- package/examples/workArea/04_facemesh-simplified_temp/index.html +0 -31
- package/examples/workArea/04_facemesh-simplified_temp/sketch.js +0 -259
- package/examples/workArea/05_handpose/extra.js +0 -0
- package/examples/workArea/05_handpose/index.html +0 -31
- package/examples/workArea/05_handpose/sketch.js +0 -362
- package/examples/workArea/05_handpose-preload/index.html +0 -31
- package/examples/workArea/05_handpose-preload/sketch.js +0 -362
- package/examples/workArea/06_bodypose-FINAL/index.html +0 -31
- package/examples/workArea/06_bodypose-FINAL/sketch.js +0 -360
package/README.md
CHANGED
|
@@ -67,10 +67,10 @@ This library simplifies access to the following p5.js mobile sensor and audio co
|
|
|
67
67
|
|
|
68
68
|
```html
|
|
69
69
|
<!-- Minified version (recommended) -->
|
|
70
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.
|
|
70
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js"></script>
|
|
71
71
|
|
|
72
72
|
<!-- Development version (larger, with comments) -->
|
|
73
|
-
<!-- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.
|
|
73
|
+
<!-- <script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.js"></script> -->
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
### Basic Setup
|
|
@@ -98,7 +98,7 @@ This library simplifies access to the following p5.js mobile sensor and audio co
|
|
|
98
98
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
99
99
|
|
|
100
100
|
<!-- Load p5-phone library -->
|
|
101
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.
|
|
101
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js"></script>
|
|
102
102
|
|
|
103
103
|
</head>
|
|
104
104
|
<body>
|
package/dist/p5-phone.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.
|
|
2
|
+
* p5-phone v1.6.0
|
|
3
3
|
* Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease
|
|
4
4
|
* https://github.com/npuckett/p5-phone
|
|
5
5
|
*
|
|
@@ -1367,8 +1367,11 @@ class PhoneCamera {
|
|
|
1367
1367
|
|
|
1368
1368
|
const videoWidth = this._video.width;
|
|
1369
1369
|
const videoHeight = this._video.height;
|
|
1370
|
-
|
|
1371
|
-
|
|
1370
|
+
|
|
1371
|
+
// Get actual canvas dimensions (works on both desktop and mobile)
|
|
1372
|
+
// Use p5.js width/height globals if available, otherwise fall back to window dimensions
|
|
1373
|
+
const canvasWidth = (typeof width !== 'undefined') ? width : window.innerWidth;
|
|
1374
|
+
const canvasHeight = (typeof height !== 'undefined') ? height : window.innerHeight;
|
|
1372
1375
|
|
|
1373
1376
|
let drawWidth, drawHeight, drawX, drawY;
|
|
1374
1377
|
|
package/dist/p5-phone.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.
|
|
2
|
+
* p5-phone v1.6.0
|
|
3
3
|
* Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease
|
|
4
4
|
* https://github.com/npuckett/p5-phone
|
|
5
5
|
*
|
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
* Released under the MIT License
|
|
8
8
|
* https://opensource.org/licenses/MIT
|
|
9
9
|
*/
|
|
10
|
-
window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,window._debugErrorHandlersSet||(window._debugErrorHandlersSet=!0,window._earlyErrors=window._earlyErrors||[],window.addEventListener("error",function(e){const n=`${e.error?.message||e.message||"Unknown error"} (${e.filename?e.filename.split("/").pop():"unknown file"}:${e.lineno||"unknown line"})`;console.error("🚨 Error caught:",n),e.error?.stack&&console.error("Stack:",e.error.stack),window._earlyErrors.push({type:"error",message:"JavaScript Error: "+n,stack:e.error?.stack}),!1===window.SHOW_DEBUG||window._debugVisible||"function"==typeof showDebug&&showDebug(),window._debugVisible&&"function"==typeof debugError&&(debugError("JavaScript Error:",n),e.error?.stack&&debugError("Stack trace:",e.error.stack))}),window.addEventListener("unhandledrejection",function(e){const n=e.reason?.message||e.reason||"Unknown promise rejection";console.error("🚨 Promise rejection caught:",n),window._earlyErrors.push({type:"error",message:"Unhandled Promise Rejection: "+n}),window._debugVisible&&"function"==typeof debugError&&debugError("Unhandled Promise Rejection:",n)})),window.sensorsEnabled=!1,window.micEnabled=!1,window.soundEnabled=!1,window.gesturesLocked=!1,window.vibrationEnabled=!1;let _permissionsInitialized=!1,_micInstance=null;function lockGestures(){window.gesturesLocked||(console.log("🔒 Locking mobile gestures..."),_initializeGestureBlocking(),_initializeP5TouchOverrides(),window.gesturesLocked=!0,console.log("✅ Mobile gestures locked"))}function enableGyroButton(e="ENABLE MOTION SENSORS",n="Requesting motion sensors..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via button")})}function enableGyroTap(e="Tap screen to enable motion sensors"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via tap")})}function enableMicButton(e="ENABLE MICROPHONE",n="Requesting microphone access..."){_createPermissionButton(e,n,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via button")})}function enableMicTap(e="Tap screen to enable microphone"){_createTapToEnable(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via tap")})}function enableSoundButton(e="ENABLE SOUND",n="Enabling audio..."){_createPermissionButton(e,n,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via button")})}function enableSoundTap(e="Tap screen to enable sound"){_createTapToEnable(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via tap")})}function enableVibrationButton(e="ENABLE VIBRATION",n="Enabling vibration..."){_createPermissionButton(e,n,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via button")})}function enableVibrationTap(e="Tap screen to enable vibration"){_createTapToEnable(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via tap")})}function enableAllButton(e="ENABLE MOTION & MICROPHONE",n="Requesting permissions..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via button")})}function enableAllTap(e="Tap screen to enable motion sensors & microphone"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via tap")})}function vibrate(e){return window.vibrationEnabled?navigator.vibrate?navigator.vibrate(e):(console.warn("⚠️ Vibration API not supported on this device"),!1):(console.warn("⚠️ Vibration not enabled. Call enableVibrationTap() or enableVibrationButton() first."),!1)}function stopVibration(){navigator.vibrate&&navigator.vibrate(0)}async function _requestMotionPermissions(){try{if("undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission){const e=await DeviceOrientationEvent.requestPermission();if(console.log("Orientation permission:",e),"undefined"!=typeof DeviceMotionEvent&&"function"==typeof DeviceMotionEvent.requestPermission){const e=await DeviceMotionEvent.requestPermission();console.log("Motion permission:",e)}}window.sensorsEnabled=!0,_notifySketchReady()}catch(e){console.error("Motion sensor permission error:",e),_debugVisible&&debugError("Motion sensor permission error:",e),window.sensorsEnabled=!0,_notifySketchReady()}}async function _requestMicrophonePermissions(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),"undefined"!=typeof mic&&mic&&mic.start?(mic.start(),_micInstance=mic,window.micEnabled=!0):console.warn("No microphone object found. Create one with: mic = new p5.AudioIn();"),_notifySketchReady()}catch(e){console.error("Microphone permission error:",e),_debugVisible&&debugError("Microphone permission error:",e),_notifySketchReady()}}async function _requestSoundOutput(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.soundEnabled=!0,_notifySketchReady()}catch(e){console.error("Sound output error:",e),_debugVisible&&debugError("Sound output error:",e),window.soundEnabled=!0,_notifySketchReady()}}async function _requestVibrationPermission(){try{if(!navigator.vibrate)return console.warn("⚠️ Vibration API not supported on this device (likely iOS)"),_debugVisible&&debugWarn("Vibration API not supported on this device"),window.vibrationEnabled=!1,void _notifySketchReady();navigator.vibrate(1)?(window.vibrationEnabled=!0,console.log("✅ Vibration enabled")):(console.warn("⚠️ Vibration API available but vibration failed"),window.vibrationEnabled=!1),_notifySketchReady()}catch(e){console.error("Vibration permission error:",e),_debugVisible&&debugError("Vibration permission error:",e),window.vibrationEnabled=!1,_notifySketchReady()}}function _notifySketchReady(){"function"==typeof userSetupComplete&&userSetupComplete(),window.dispatchEvent(new CustomEvent("permissionsReady",{detail:{sensors:window.sensorsEnabled,microphone:window.micEnabled,sound:window.soundEnabled,vibration:window.vibrationEnabled,gestures:window.gesturesLocked}}))}function _createPermissionButton(e,n,t){_removeExistingUI();const o=document.createElement("button");o.id="permissionButton",o.textContent=e,o.style.cssText="\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 20px 40px;\n font-size: 18px;\n font-weight: bold;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n border-radius: 12px;\n cursor: pointer;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n box-shadow: 0 4px 15px rgba(0,0,0,0.2);\n transition: transform 0.2s ease;\n touch-action: manipulation;\n ";const i=document.createElement("div");i.id="permissionStatus",i.textContent=n,i.style.cssText="\n position: fixed;\n top: 60%;\n left: 50%;\n transform: translate(-50%, 0);\n color: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n z-index: 999998;\n display: none;\n ",o.addEventListener("mouseenter",()=>{o.style.transform="translate(-50%, -50%) scale(1.05)"}),o.addEventListener("mouseleave",()=>{o.style.transform="translate(-50%, -50%) scale(1)"});const r=async()=>{o.parentNode&&(o.style.display="none",i.style.display="block",await t(),i.style.display="none",_removeExistingUI())};o.addEventListener("click",r),o.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),o.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()}),document.body.appendChild(o),document.body.appendChild(i)}function _createTapToEnable(e,n){_removeExistingUI();const t=document.createElement("div");t.id="tapOverlay",t.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n cursor: pointer;\n touch-action: manipulation;\n ";const o=document.createElement("div");o.textContent=e,o.style.cssText="\n color: white;\n font-size: 24px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n padding: 40px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.1);\n backdrop-filter: blur(10px);\n ",t.appendChild(o);const i=async()=>{t.parentNode&&(o.textContent="Enabling...",await n(),t.parentNode&&document.body.removeChild(t))};t.addEventListener("click",i),t.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),i()}),t.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),i()}),document.body.appendChild(t)}function _removeExistingUI(){const e=document.getElementById("permissionButton"),n=document.getElementById("permissionStatus"),t=document.getElementById("tapOverlay");e&&e.remove(),n&&n.remove(),t&&t.remove()}function _initializeGestureBlocking(){window.history.pushState(null,"",window.location.href),window.onpopstate=function(){window.history.pushState(null,"",window.location.href)},window.addEventListener("beforeunload",function(e){e.preventDefault(),e.returnValue=""}),_initializeEdgeSwipePrevention(),_initializeOtherGesturePrevention()}function _initializeEdgeSwipePrevention(){let e=0,n=0;document.addEventListener("touchstart",function(t){t.touches&&t.touches.length>0&&(e=t.touches[0].clientX,n=t.touches[0].clientY,(e<20||e>window.innerWidth-20)&&t.preventDefault())},{passive:!1,capture:!0}),document.addEventListener("touchmove",function(t){if(!t.touches||0===t.touches.length)return;let o=t.touches[0].clientX,i=t.touches[0].clientY,r=o-e,a=i-n;(e<20&&r>0||e>window.innerWidth-20&&r<0)&&(t.preventDefault(),t.stopPropagation()),0===window.pageYOffset&&a>0&&t.preventDefault(),!t.target||"CANVAS"!==t.target.tagName||document.getElementById("tapOverlay")||document.getElementById("permissionButton")||t.preventDefault()},{passive:!1,capture:!0})}function _initializeOtherGesturePrevention(){document.addEventListener("gesturestart",function(e){e.preventDefault()}),document.addEventListener("gesturechange",function(e){e.preventDefault()}),document.addEventListener("gestureend",function(e){e.preventDefault()});let e=0;document.addEventListener("touchend",function(n){if(n.target&&("tapOverlay"===n.target.id||n.target.closest("#tapOverlay")||"permissionButton"===n.target.id||"permissionStatus"===n.target.id||n.target.closest("#permissionButton")||n.target.closest("#permissionStatus")))return;const t=Date.now();t-e<=300&&n.preventDefault(),e=t},!1),window.oncontextmenu=function(e){return e.preventDefault(),e.stopPropagation(),!1}}function _initializeP5TouchOverrides(){setTimeout(()=>{if(window._setupDone)_overrideP5Touch();else{const e=setInterval(()=>{window._setupDone&&(_overrideP5Touch(),clearInterval(e))},100)}},100)}function _overrideP5Touch(){const e=window.touchStarted||function(){},n=window.touchMoved||function(){},t=window.touchEnded||function(){},o=window.mousePressed||function(){},i=window.mouseDragged||function(){},r=window.mouseReleased||function(){};window.touchStarted=function(n){return e(n),!1},window.touchMoved=function(e){return n(e),!1},window.touchEnded=function(e){return t(e),!1},window.mousePressed=function(e){return o(e),!1},window.mouseDragged=function(e){return i(e),!1},window.mouseReleased=function(e){return r(e),!1}}document.addEventListener("DOMContentLoaded",function(){const e=document.getElementById("startButton"),n=document.getElementById("statusText");e&&n&&(console.warn("⚠️ Legacy HTML elements detected. Consider using the new API functions instead."),e.addEventListener("click",async()=>{e.classList.add("hidden"),n.classList.remove("hidden"),n.textContent="Requesting permissions...",await _requestMotionPermissions(),await _requestMicrophonePermissions(),n.classList.add("hidden")}),lockGestures())});let _debugPanel=null,_debugVisible=!1,_debugMessages=[];const MAX_DEBUG_MESSAGES=20;function showDebug(){_createDebugPanel(),_debugPanel.style.display="block",_debugVisible=!0,window._debugVisible=!0,_setupConsoleOverrides(),_displayEarlyErrors()}function hideDebug(){_debugPanel&&(_debugPanel.style.display="none",_debugVisible=!1)}function toggleDebug(){_debugVisible?hideDebug():showDebug()}function debug(...e){console.log(...e);_addDebugMessage(e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" "),"log")}function debugError(...e){(window._originalConsoleError||console.error).apply(console,e);_addDebugMessage(`❌ ERROR: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"error")}function debugWarn(...e){(window._originalConsoleWarn||console.warn).apply(console,e);_addDebugMessage(`⚠️ WARNING: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"warning")}function _addDebugMessage(e,n="log"){const t={text:`[${(new Date).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}] ${e}`,type:n};_debugMessages.push(t),_debugMessages.length>20&&_debugMessages.shift(),_debugPanel&&_updateDebugDisplay()}function _setupConsoleOverrides(){window._consoleOverrideSet||(window._consoleOverrideSet=!0,window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,console.error=function(...e){window._originalConsoleError.apply(console,e),_debugVisible&&debugError(...e)},console.warn=function(...e){window._originalConsoleWarn.apply(console,e),_debugVisible&&debugWarn(...e)})}function _displayEarlyErrors(){window._earlyErrors&&window._earlyErrors.length>0&&(debugError(`🚨 Found ${window._earlyErrors.length} early error(s):`),window._earlyErrors.forEach(e=>{debugError(e.message),e.stack&&debugError("Stack trace:",e.stack)}),window._earlyErrors=[])}function _createDebugPanel(){if(_debugPanel)return;_debugPanel=document.createElement("div"),_debugPanel.id="mobile-debug-panel",_debugPanel.innerHTML='\n <div id="mobile-debug-header">\n <span>Debug</span>\n <button id="mobile-debug-close">×</button>\n </div>\n <div id="mobile-debug-content"></div>\n ';const e=document.createElement("style");e.textContent="\n #mobile-debug-panel {\n position: fixed;\n top: 20px;\n right: 20px;\n width: 350px;\n max-width: calc(100vw - 40px);\n max-height: 400px;\n background: rgba(0, 0, 0, 0.9);\n color: #ffffff;\n font-family: 'Courier New', monospace;\n font-size: 12px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n z-index: 10000;\n display: none;\n }\n \n #mobile-debug-header {\n background: rgba(255, 255, 255, 0.1);\n padding: 8px 12px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-radius: 8px 8px 0 0;\n }\n \n #mobile-debug-header span {\n font-weight: bold;\n font-size: 13px;\n }\n \n #mobile-debug-close {\n background: none;\n border: none;\n color: #ffffff;\n font-size: 18px;\n cursor: pointer;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n #mobile-debug-close:hover {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n }\n \n #mobile-debug-content {\n padding: 12px;\n max-height: 340px;\n overflow-y: auto;\n word-wrap: break-word;\n line-height: 1.4;\n }\n \n .debug-message {\n margin-bottom: 4px;\n white-space: pre-wrap;\n }\n \n .debug-message.error {\n color: #ff6b6b;\n background: rgba(255, 107, 107, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ff6b6b;\n }\n \n .debug-message.warning {\n color: #ffd93d;\n background: rgba(255, 217, 61, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ffd93d;\n }\n \n .debug-timestamp {\n color: #888;\n font-size: 10px;\n }\n \n @media (max-width: 480px) {\n #mobile-debug-panel {\n width: calc(100vw - 20px);\n right: 10px;\n top: 10px;\n }\n }\n ",document.head.appendChild(e),document.body.appendChild(_debugPanel),document.getElementById("mobile-debug-close").onclick=hideDebug,_updateDebugDisplay()}function _updateDebugDisplay(){if(!_debugPanel)return;const e=document.getElementById("mobile-debug-content");e&&(e.innerHTML=_debugMessages.map(e=>"string"==typeof e?`<div class="debug-message">${e}</div>`:`<div class="debug-message ${e.type}">${e.text}</div>`).join(""),e.scrollTop=e.scrollHeight)}debug.clear=function(){_debugMessages=[],_debugPanel&&_updateDebugDisplay(),console.clear()},window.debug=debug,window.debugError=debugError,window.debugWarn=debugWarn,window.showDebug=showDebug,window.hideDebug=hideDebug,window.toggleDebug=toggleDebug,window.lockGestures=lockGestures,window.enableGyroTap=enableGyroTap,window.enableGyroButton=enableGyroButton,window.enableMicTap=enableMicTap,window.enableMicButton=enableMicButton,window.enableSoundTap=enableSoundTap,window.enableSoundButton=enableSoundButton,window.enableVibrationTap=enableVibrationTap,window.enableVibrationButton=enableVibrationButton,window.vibrate=vibrate,window.stopVibration=stopVibration,window.enableAllTap=enableAllTap,window.enableAllButton=enableAllButton;class PhoneCamera{constructor(e="user",n=!0,t="fitHeight"){this._active=e,this._mirror=n,this._mode=t,this._fixedWidth=640,this._fixedHeight=480,this._video=null,this._ready=!1,this._p5Instance=window,this._onReadyCallback=null,this._createCaptureRef=null,window._phoneCameras||(window._phoneCameras=[]),window._phoneCameras.push(this)}get ready(){return this._ready}get video(){return this._video}get videoElement(){return this._video?this._video.elt:null}get width(){if(!this._ready)return 0;return this.getDimensions().width}get height(){if(!this._ready)return 0;return this.getDimensions().height}get active(){return this._active}set active(e){"user"===e||"environment"===e?this._active!==e&&(this._active=e,this._ready&&this._switchCamera()):console.error('PhoneCamera: active must be "user" or "environment"')}get mirror(){return this._mirror}set mirror(e){this._mirror=!!e}get mode(){return this._mode}set mode(e){const n=["fitWidth","fitHeight","cover","contain","fixed"];n.includes(e)?this._mode=e:console.error("PhoneCamera: mode must be one of:",n.join(", "))}get fixedWidth(){return this._fixedWidth}set fixedWidth(e){this._fixedWidth=Math.max(1,e)}get fixedHeight(){return this._fixedHeight}set fixedHeight(e){this._fixedHeight=Math.max(1,e)}onReady(e){this._onReadyCallback=e,this._ready&&this._video&&this._video.elt&&this._video.elt.readyState>=2?e():this._video&&this._checkVideoReady()}_initializeCamera(){if(this._ready||this._video)return;const e={video:{facingMode:this._active},audio:!1};this._video=createCapture(e,()=>{this._ready=!0,this._video.hide(),console.log("✅ PhoneCamera ready"),this._checkVideoReady()}),this._video&&this._video.elt&&this._video.elt.addEventListener("loadeddata",()=>{this._ready=!0,this._checkVideoReady()})}_checkVideoReady(){if(this._video&&this._video.elt&&this._video.elt.readyState>=2){if(this._onReadyCallback){const e=this._onReadyCallback;this._onReadyCallback=null,e()}}else setTimeout(()=>this._checkVideoReady(),100)}_switchCamera(){if(!this._video)return;this._ready;this._ready=!1,this._video.remove();const e={video:{facingMode:this._active},audio:!1};this._video=createCapture(e,()=>{this._ready=!0,this._video.hide(),console.log(`✅ PhoneCamera switched to ${this._active} camera`)}),this._video&&this._video.elt&&this._video.elt.addEventListener("loadeddata",()=>{this._ready=!0})}remove(){this._video&&(this._video.remove(),this._video=null),this._ready=!1}getDimensions(){if(!this._ready||!this._video)return{x:0,y:0,width:0,height:0,scaleX:1,scaleY:1};const e=this._video.width,n=this._video.height,t=window.width||window.innerWidth,o=window.height||window.innerHeight;let i,r,a,s;if("fixed"===this._mode)i=this._fixedWidth,r=this._fixedHeight,a=(t-i)/2,s=(o-r)/2;else if("fitWidth"===this._mode)i=t,r=n/e*i,a=0,s=(o-r)/2;else if("fitHeight"===this._mode)r=o,i=e/n*r,a=(t-i)/2,s=0;else if("cover"===this._mode){const d=Math.max(t/e,o/n);i=e*d,r=n*d,a=(t-i)/2,s=(o-r)/2}else if("contain"===this._mode){const d=Math.min(t/e,o/n);i=e*d,r=n*d,a=(t-i)/2,s=(o-r)/2}return{x:a,y:s,width:i,height:r,scaleX:i/e,scaleY:r/n}}mapPoint(e,n){const t=this.getDimensions();let o=e*t.scaleX;const i=n*t.scaleY;this._mirror&&(o=t.width-o);return{x:o+Math.max(0,t.x),y:i+Math.max(0,t.y)}}mapKeypoint(e){if(!e||void 0===e.x||void 0===e.y)return console.warn("PhoneCamera.mapKeypoint: invalid keypoint",e),e;const n=this.mapPoint(e.x,e.y);return{...e,x:n.x,y:n.y}}mapKeypoints(e){return Array.isArray(e)?e.map(e=>this.mapKeypoint(e)):(console.warn("PhoneCamera.mapKeypoints: expected array, got",typeof e),e)}_draw(){if(!this._ready||!this._video)return;const e=this.getDimensions();push(),this._mirror?(translate(e.x+e.width,e.y),scale(-1,1),image(this._video,0,0,e.width,e.height)):image(this._video,e.x,e.y,e.width,e.height),pop()}}function createPhoneCamera(e="user",n=!0,t="fitHeight"){return new PhoneCamera(e,n,t)}function enableCameraButton(e="ENABLE CAMERA",n="Starting camera..."){_createPermissionButton(e,n,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via button")})}function enableCameraTap(e="Tap screen to enable camera"){navigator.permissions&&navigator.permissions.query?navigator.permissions.query({name:"camera"}).then(n=>{"granted"===n.state?(console.log("✅ Camera permission already granted - auto-starting"),_requestCameraPermission()):_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}).catch(()=>{_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}):_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}async function _requestCameraPermission(){try{if(void 0!==window._phoneCameras&&Array.isArray(window._phoneCameras))for(let e of window._phoneCameras)!e||e._ready||e._video||e._initializeCamera();"function"==typeof userCameraReady&&userCameraReady(),_notifySketchReady()}catch(e){console.error("Camera permission error:",e),_debugVisible&&debugError("Camera permission error:",e),_notifySketchReady()}}if(window.createPhoneCamera=createPhoneCamera,window.enableCameraButton=enableCameraButton,window.enableCameraTap=enableCameraTap,"undefined"!=typeof p5&&p5.prototype){const e=p5.prototype.image;p5.prototype.image=function(...n){if(n[0]instanceof PhoneCamera){const t=n[0];if(n.length>=3){if(!t.ready||!t.video)return;const o=n[1],i=n[2],r=n[3]||t.width,a=n[4]||t.height;this.push(),t.mirror?(this.translate(o+r,i),this.scale(-1,1),e.call(this,t.video,0,0,r,a)):e.call(this,t.video,o,i,r,a),this.pop()}else t._draw()}else e.apply(this,n)}}"undefined"!=typeof p5&&p5.prototype&&(p5.prototype.lockGestures=lockGestures,p5.prototype.enableGyroTap=enableGyroTap,p5.prototype.enableGyroButton=enableGyroButton,p5.prototype.enableMicTap=enableMicTap,p5.prototype.enableMicButton=enableMicButton,p5.prototype.enableSoundTap=enableSoundTap,p5.prototype.enableSoundButton=enableSoundButton,p5.prototype.enableVibrationTap=enableVibrationTap,p5.prototype.enableVibrationButton=enableVibrationButton,p5.prototype.vibrate=vibrate,p5.prototype.stopVibration=stopVibration,p5.prototype.enableAllTap=enableAllTap,p5.prototype.enableAllButton=enableAllButton,p5.prototype.createPhoneCamera=createPhoneCamera,p5.prototype.enableCameraButton=enableCameraButton,p5.prototype.enableCameraTap=enableCameraTap,p5.prototype.showDebug=showDebug,p5.prototype.hideDebug=hideDebug,p5.prototype.toggleDebug=toggleDebug,p5.prototype.debug=debug,p5.prototype.debugError=debugError,p5.prototype.debugWarn=debugWarn,console.log("✅ Mobile p5.js Permissions: p5.prototype functions registered"));
|
|
10
|
+
window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,window._debugErrorHandlersSet||(window._debugErrorHandlersSet=!0,window._earlyErrors=window._earlyErrors||[],window.addEventListener("error",function(e){const n=`${e.error?.message||e.message||"Unknown error"} (${e.filename?e.filename.split("/").pop():"unknown file"}:${e.lineno||"unknown line"})`;console.error("🚨 Error caught:",n),e.error?.stack&&console.error("Stack:",e.error.stack),window._earlyErrors.push({type:"error",message:"JavaScript Error: "+n,stack:e.error?.stack}),!1===window.SHOW_DEBUG||window._debugVisible||"function"==typeof showDebug&&showDebug(),window._debugVisible&&"function"==typeof debugError&&(debugError("JavaScript Error:",n),e.error?.stack&&debugError("Stack trace:",e.error.stack))}),window.addEventListener("unhandledrejection",function(e){const n=e.reason?.message||e.reason||"Unknown promise rejection";console.error("🚨 Promise rejection caught:",n),window._earlyErrors.push({type:"error",message:"Unhandled Promise Rejection: "+n}),window._debugVisible&&"function"==typeof debugError&&debugError("Unhandled Promise Rejection:",n)})),window.sensorsEnabled=!1,window.micEnabled=!1,window.soundEnabled=!1,window.gesturesLocked=!1,window.vibrationEnabled=!1;let _permissionsInitialized=!1,_micInstance=null;function lockGestures(){window.gesturesLocked||(console.log("🔒 Locking mobile gestures..."),_initializeGestureBlocking(),_initializeP5TouchOverrides(),window.gesturesLocked=!0,console.log("✅ Mobile gestures locked"))}function enableGyroButton(e="ENABLE MOTION SENSORS",n="Requesting motion sensors..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via button")})}function enableGyroTap(e="Tap screen to enable motion sensors"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),console.log("✅ Gyroscope enabled via tap")})}function enableMicButton(e="ENABLE MICROPHONE",n="Requesting microphone access..."){_createPermissionButton(e,n,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via button")})}function enableMicTap(e="Tap screen to enable microphone"){_createTapToEnable(e,async()=>{await _requestMicrophonePermissions(),console.log("✅ Microphone enabled via tap")})}function enableSoundButton(e="ENABLE SOUND",n="Enabling audio..."){_createPermissionButton(e,n,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via button")})}function enableSoundTap(e="Tap screen to enable sound"){_createTapToEnable(e,async()=>{await _requestSoundOutput(),console.log("✅ Sound output enabled via tap")})}function enableVibrationButton(e="ENABLE VIBRATION",n="Enabling vibration..."){_createPermissionButton(e,n,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via button")})}function enableVibrationTap(e="Tap screen to enable vibration"){_createTapToEnable(e,async()=>{await _requestVibrationPermission(),console.log("✅ Vibration enabled via tap")})}function enableAllButton(e="ENABLE MOTION & MICROPHONE",n="Requesting permissions..."){_createPermissionButton(e,n,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via button")})}function enableAllTap(e="Tap screen to enable motion sensors & microphone"){_createTapToEnable(e,async()=>{await _requestMotionPermissions(),await _requestMicrophonePermissions(),console.log("✅ Motion sensors and microphone enabled via tap")})}function vibrate(e){return window.vibrationEnabled?navigator.vibrate?navigator.vibrate(e):(console.warn("⚠️ Vibration API not supported on this device"),!1):(console.warn("⚠️ Vibration not enabled. Call enableVibrationTap() or enableVibrationButton() first."),!1)}function stopVibration(){navigator.vibrate&&navigator.vibrate(0)}async function _requestMotionPermissions(){try{if("undefined"!=typeof DeviceOrientationEvent&&"function"==typeof DeviceOrientationEvent.requestPermission){const e=await DeviceOrientationEvent.requestPermission();if(console.log("Orientation permission:",e),"undefined"!=typeof DeviceMotionEvent&&"function"==typeof DeviceMotionEvent.requestPermission){const e=await DeviceMotionEvent.requestPermission();console.log("Motion permission:",e)}}window.sensorsEnabled=!0,_notifySketchReady()}catch(e){console.error("Motion sensor permission error:",e),_debugVisible&&debugError("Motion sensor permission error:",e),window.sensorsEnabled=!0,_notifySketchReady()}}async function _requestMicrophonePermissions(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),"undefined"!=typeof mic&&mic&&mic.start?(mic.start(),_micInstance=mic,window.micEnabled=!0):console.warn("No microphone object found. Create one with: mic = new p5.AudioIn();"),_notifySketchReady()}catch(e){console.error("Microphone permission error:",e),_debugVisible&&debugError("Microphone permission error:",e),_notifySketchReady()}}async function _requestSoundOutput(){try{"undefined"!=typeof userStartAudio&&await userStartAudio(),window.soundEnabled=!0,_notifySketchReady()}catch(e){console.error("Sound output error:",e),_debugVisible&&debugError("Sound output error:",e),window.soundEnabled=!0,_notifySketchReady()}}async function _requestVibrationPermission(){try{if(!navigator.vibrate)return console.warn("⚠️ Vibration API not supported on this device (likely iOS)"),_debugVisible&&debugWarn("Vibration API not supported on this device"),window.vibrationEnabled=!1,void _notifySketchReady();navigator.vibrate(1)?(window.vibrationEnabled=!0,console.log("✅ Vibration enabled")):(console.warn("⚠️ Vibration API available but vibration failed"),window.vibrationEnabled=!1),_notifySketchReady()}catch(e){console.error("Vibration permission error:",e),_debugVisible&&debugError("Vibration permission error:",e),window.vibrationEnabled=!1,_notifySketchReady()}}function _notifySketchReady(){"function"==typeof userSetupComplete&&userSetupComplete(),window.dispatchEvent(new CustomEvent("permissionsReady",{detail:{sensors:window.sensorsEnabled,microphone:window.micEnabled,sound:window.soundEnabled,vibration:window.vibrationEnabled,gestures:window.gesturesLocked}}))}function _createPermissionButton(e,n,t){_removeExistingUI();const o=document.createElement("button");o.id="permissionButton",o.textContent=e,o.style.cssText="\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 20px 40px;\n font-size: 18px;\n font-weight: bold;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n border-radius: 12px;\n cursor: pointer;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n box-shadow: 0 4px 15px rgba(0,0,0,0.2);\n transition: transform 0.2s ease;\n touch-action: manipulation;\n ";const i=document.createElement("div");i.id="permissionStatus",i.textContent=n,i.style.cssText="\n position: fixed;\n top: 60%;\n left: 50%;\n transform: translate(-50%, 0);\n color: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n z-index: 999998;\n display: none;\n ",o.addEventListener("mouseenter",()=>{o.style.transform="translate(-50%, -50%) scale(1.05)"}),o.addEventListener("mouseleave",()=>{o.style.transform="translate(-50%, -50%) scale(1)"});const r=async()=>{o.parentNode&&(o.style.display="none",i.style.display="block",await t(),i.style.display="none",_removeExistingUI())};o.addEventListener("click",r),o.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),r()}),o.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),r()}),document.body.appendChild(o),document.body.appendChild(i)}function _createTapToEnable(e,n){_removeExistingUI();const t=document.createElement("div");t.id="tapOverlay",t.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n cursor: pointer;\n touch-action: manipulation;\n ";const o=document.createElement("div");o.textContent=e,o.style.cssText="\n color: white;\n font-size: 24px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n text-align: center;\n padding: 40px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.1);\n backdrop-filter: blur(10px);\n ",t.appendChild(o);const i=async()=>{t.parentNode&&(o.textContent="Enabling...",await n(),t.parentNode&&document.body.removeChild(t))};t.addEventListener("click",i),t.addEventListener("touchend",function(e){e.preventDefault(),e.stopPropagation(),i()}),t.addEventListener("pointerup",function(e){e.preventDefault(),e.stopPropagation(),i()}),document.body.appendChild(t)}function _removeExistingUI(){const e=document.getElementById("permissionButton"),n=document.getElementById("permissionStatus"),t=document.getElementById("tapOverlay");e&&e.remove(),n&&n.remove(),t&&t.remove()}function _initializeGestureBlocking(){window.history.pushState(null,"",window.location.href),window.onpopstate=function(){window.history.pushState(null,"",window.location.href)},window.addEventListener("beforeunload",function(e){e.preventDefault(),e.returnValue=""}),_initializeEdgeSwipePrevention(),_initializeOtherGesturePrevention()}function _initializeEdgeSwipePrevention(){let e=0,n=0;document.addEventListener("touchstart",function(t){t.touches&&t.touches.length>0&&(e=t.touches[0].clientX,n=t.touches[0].clientY,(e<20||e>window.innerWidth-20)&&t.preventDefault())},{passive:!1,capture:!0}),document.addEventListener("touchmove",function(t){if(!t.touches||0===t.touches.length)return;let o=t.touches[0].clientX,i=t.touches[0].clientY,r=o-e,a=i-n;(e<20&&r>0||e>window.innerWidth-20&&r<0)&&(t.preventDefault(),t.stopPropagation()),0===window.pageYOffset&&a>0&&t.preventDefault(),!t.target||"CANVAS"!==t.target.tagName||document.getElementById("tapOverlay")||document.getElementById("permissionButton")||t.preventDefault()},{passive:!1,capture:!0})}function _initializeOtherGesturePrevention(){document.addEventListener("gesturestart",function(e){e.preventDefault()}),document.addEventListener("gesturechange",function(e){e.preventDefault()}),document.addEventListener("gestureend",function(e){e.preventDefault()});let e=0;document.addEventListener("touchend",function(n){if(n.target&&("tapOverlay"===n.target.id||n.target.closest("#tapOverlay")||"permissionButton"===n.target.id||"permissionStatus"===n.target.id||n.target.closest("#permissionButton")||n.target.closest("#permissionStatus")))return;const t=Date.now();t-e<=300&&n.preventDefault(),e=t},!1),window.oncontextmenu=function(e){return e.preventDefault(),e.stopPropagation(),!1}}function _initializeP5TouchOverrides(){setTimeout(()=>{if(window._setupDone)_overrideP5Touch();else{const e=setInterval(()=>{window._setupDone&&(_overrideP5Touch(),clearInterval(e))},100)}},100)}function _overrideP5Touch(){const e=window.touchStarted||function(){},n=window.touchMoved||function(){},t=window.touchEnded||function(){},o=window.mousePressed||function(){},i=window.mouseDragged||function(){},r=window.mouseReleased||function(){};window.touchStarted=function(n){return e(n),!1},window.touchMoved=function(e){return n(e),!1},window.touchEnded=function(e){return t(e),!1},window.mousePressed=function(e){return o(e),!1},window.mouseDragged=function(e){return i(e),!1},window.mouseReleased=function(e){return r(e),!1}}document.addEventListener("DOMContentLoaded",function(){const e=document.getElementById("startButton"),n=document.getElementById("statusText");e&&n&&(console.warn("⚠️ Legacy HTML elements detected. Consider using the new API functions instead."),e.addEventListener("click",async()=>{e.classList.add("hidden"),n.classList.remove("hidden"),n.textContent="Requesting permissions...",await _requestMotionPermissions(),await _requestMicrophonePermissions(),n.classList.add("hidden")}),lockGestures())});let _debugPanel=null,_debugVisible=!1,_debugMessages=[];const MAX_DEBUG_MESSAGES=20;function showDebug(){_createDebugPanel(),_debugPanel.style.display="block",_debugVisible=!0,window._debugVisible=!0,_setupConsoleOverrides(),_displayEarlyErrors()}function hideDebug(){_debugPanel&&(_debugPanel.style.display="none",_debugVisible=!1)}function toggleDebug(){_debugVisible?hideDebug():showDebug()}function debug(...e){console.log(...e);_addDebugMessage(e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" "),"log")}function debugError(...e){(window._originalConsoleError||console.error).apply(console,e);_addDebugMessage(`❌ ERROR: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"error")}function debugWarn(...e){(window._originalConsoleWarn||console.warn).apply(console,e);_addDebugMessage(`⚠️ WARNING: ${e.map(e=>{if("object"==typeof e&&null!==e)try{return JSON.stringify(e,null,2)}catch(n){return String(e)}return String(e)}).join(" ")}`,"warning")}function _addDebugMessage(e,n="log"){const t={text:`[${(new Date).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}] ${e}`,type:n};_debugMessages.push(t),_debugMessages.length>20&&_debugMessages.shift(),_debugPanel&&_updateDebugDisplay()}function _setupConsoleOverrides(){window._consoleOverrideSet||(window._consoleOverrideSet=!0,window._originalConsoleError=console.error,window._originalConsoleWarn=console.warn,console.error=function(...e){window._originalConsoleError.apply(console,e),_debugVisible&&debugError(...e)},console.warn=function(...e){window._originalConsoleWarn.apply(console,e),_debugVisible&&debugWarn(...e)})}function _displayEarlyErrors(){window._earlyErrors&&window._earlyErrors.length>0&&(debugError(`🚨 Found ${window._earlyErrors.length} early error(s):`),window._earlyErrors.forEach(e=>{debugError(e.message),e.stack&&debugError("Stack trace:",e.stack)}),window._earlyErrors=[])}function _createDebugPanel(){if(_debugPanel)return;_debugPanel=document.createElement("div"),_debugPanel.id="mobile-debug-panel",_debugPanel.innerHTML='\n <div id="mobile-debug-header">\n <span>Debug</span>\n <button id="mobile-debug-close">×</button>\n </div>\n <div id="mobile-debug-content"></div>\n ';const e=document.createElement("style");e.textContent="\n #mobile-debug-panel {\n position: fixed;\n top: 20px;\n right: 20px;\n width: 350px;\n max-width: calc(100vw - 40px);\n max-height: 400px;\n background: rgba(0, 0, 0, 0.9);\n color: #ffffff;\n font-family: 'Courier New', monospace;\n font-size: 12px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n z-index: 10000;\n display: none;\n }\n \n #mobile-debug-header {\n background: rgba(255, 255, 255, 0.1);\n padding: 8px 12px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-radius: 8px 8px 0 0;\n }\n \n #mobile-debug-header span {\n font-weight: bold;\n font-size: 13px;\n }\n \n #mobile-debug-close {\n background: none;\n border: none;\n color: #ffffff;\n font-size: 18px;\n cursor: pointer;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n #mobile-debug-close:hover {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n }\n \n #mobile-debug-content {\n padding: 12px;\n max-height: 340px;\n overflow-y: auto;\n word-wrap: break-word;\n line-height: 1.4;\n }\n \n .debug-message {\n margin-bottom: 4px;\n white-space: pre-wrap;\n }\n \n .debug-message.error {\n color: #ff6b6b;\n background: rgba(255, 107, 107, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ff6b6b;\n }\n \n .debug-message.warning {\n color: #ffd93d;\n background: rgba(255, 217, 61, 0.1);\n padding: 4px;\n border-radius: 3px;\n border-left: 3px solid #ffd93d;\n }\n \n .debug-timestamp {\n color: #888;\n font-size: 10px;\n }\n \n @media (max-width: 480px) {\n #mobile-debug-panel {\n width: calc(100vw - 20px);\n right: 10px;\n top: 10px;\n }\n }\n ",document.head.appendChild(e),document.body.appendChild(_debugPanel),document.getElementById("mobile-debug-close").onclick=hideDebug,_updateDebugDisplay()}function _updateDebugDisplay(){if(!_debugPanel)return;const e=document.getElementById("mobile-debug-content");e&&(e.innerHTML=_debugMessages.map(e=>"string"==typeof e?`<div class="debug-message">${e}</div>`:`<div class="debug-message ${e.type}">${e.text}</div>`).join(""),e.scrollTop=e.scrollHeight)}debug.clear=function(){_debugMessages=[],_debugPanel&&_updateDebugDisplay(),console.clear()},window.debug=debug,window.debugError=debugError,window.debugWarn=debugWarn,window.showDebug=showDebug,window.hideDebug=hideDebug,window.toggleDebug=toggleDebug,window.lockGestures=lockGestures,window.enableGyroTap=enableGyroTap,window.enableGyroButton=enableGyroButton,window.enableMicTap=enableMicTap,window.enableMicButton=enableMicButton,window.enableSoundTap=enableSoundTap,window.enableSoundButton=enableSoundButton,window.enableVibrationTap=enableVibrationTap,window.enableVibrationButton=enableVibrationButton,window.vibrate=vibrate,window.stopVibration=stopVibration,window.enableAllTap=enableAllTap,window.enableAllButton=enableAllButton;class PhoneCamera{constructor(e="user",n=!0,t="fitHeight"){this._active=e,this._mirror=n,this._mode=t,this._fixedWidth=640,this._fixedHeight=480,this._video=null,this._ready=!1,this._p5Instance=window,this._onReadyCallback=null,this._createCaptureRef=null,window._phoneCameras||(window._phoneCameras=[]),window._phoneCameras.push(this)}get ready(){return this._ready}get video(){return this._video}get videoElement(){return this._video?this._video.elt:null}get width(){if(!this._ready)return 0;return this.getDimensions().width}get height(){if(!this._ready)return 0;return this.getDimensions().height}get active(){return this._active}set active(e){"user"===e||"environment"===e?this._active!==e&&(this._active=e,this._ready&&this._switchCamera()):console.error('PhoneCamera: active must be "user" or "environment"')}get mirror(){return this._mirror}set mirror(e){this._mirror=!!e}get mode(){return this._mode}set mode(e){const n=["fitWidth","fitHeight","cover","contain","fixed"];n.includes(e)?this._mode=e:console.error("PhoneCamera: mode must be one of:",n.join(", "))}get fixedWidth(){return this._fixedWidth}set fixedWidth(e){this._fixedWidth=Math.max(1,e)}get fixedHeight(){return this._fixedHeight}set fixedHeight(e){this._fixedHeight=Math.max(1,e)}onReady(e){this._onReadyCallback=e,this._ready&&this._video&&this._video.elt&&this._video.elt.readyState>=2?e():this._video&&this._checkVideoReady()}_initializeCamera(){if(this._ready||this._video)return;const e={video:{facingMode:this._active},audio:!1};this._video=createCapture(e,()=>{this._ready=!0,this._video.hide(),console.log("✅ PhoneCamera ready"),this._checkVideoReady()}),this._video&&this._video.elt&&this._video.elt.addEventListener("loadeddata",()=>{this._ready=!0,this._checkVideoReady()})}_checkVideoReady(){if(this._video&&this._video.elt&&this._video.elt.readyState>=2){if(this._onReadyCallback){const e=this._onReadyCallback;this._onReadyCallback=null,e()}}else setTimeout(()=>this._checkVideoReady(),100)}_switchCamera(){if(!this._video)return;this._ready;this._ready=!1,this._video.remove();const e={video:{facingMode:this._active},audio:!1};this._video=createCapture(e,()=>{this._ready=!0,this._video.hide(),console.log(`✅ PhoneCamera switched to ${this._active} camera`)}),this._video&&this._video.elt&&this._video.elt.addEventListener("loadeddata",()=>{this._ready=!0})}remove(){this._video&&(this._video.remove(),this._video=null),this._ready=!1}getDimensions(){if(!this._ready||!this._video)return{x:0,y:0,width:0,height:0,scaleX:1,scaleY:1};const e=this._video.width,n=this._video.height,t="undefined"!=typeof width?width:window.innerWidth,o="undefined"!=typeof height?height:window.innerHeight;let i,r,a,s;if("fixed"===this._mode)i=this._fixedWidth,r=this._fixedHeight,a=(t-i)/2,s=(o-r)/2;else if("fitWidth"===this._mode)i=t,r=n/e*i,a=0,s=(o-r)/2;else if("fitHeight"===this._mode)r=o,i=e/n*r,a=(t-i)/2,s=0;else if("cover"===this._mode){const d=Math.max(t/e,o/n);i=e*d,r=n*d,a=(t-i)/2,s=(o-r)/2}else if("contain"===this._mode){const d=Math.min(t/e,o/n);i=e*d,r=n*d,a=(t-i)/2,s=(o-r)/2}return{x:a,y:s,width:i,height:r,scaleX:i/e,scaleY:r/n}}mapPoint(e,n){const t=this.getDimensions();let o=e*t.scaleX;const i=n*t.scaleY;this._mirror&&(o=t.width-o);return{x:o+Math.max(0,t.x),y:i+Math.max(0,t.y)}}mapKeypoint(e){if(!e||void 0===e.x||void 0===e.y)return console.warn("PhoneCamera.mapKeypoint: invalid keypoint",e),e;const n=this.mapPoint(e.x,e.y);return{...e,x:n.x,y:n.y}}mapKeypoints(e){return Array.isArray(e)?e.map(e=>this.mapKeypoint(e)):(console.warn("PhoneCamera.mapKeypoints: expected array, got",typeof e),e)}_draw(){if(!this._ready||!this._video)return;const e=this.getDimensions();push(),this._mirror?(translate(e.x+e.width,e.y),scale(-1,1),image(this._video,0,0,e.width,e.height)):image(this._video,e.x,e.y,e.width,e.height),pop()}}function createPhoneCamera(e="user",n=!0,t="fitHeight"){return new PhoneCamera(e,n,t)}function enableCameraButton(e="ENABLE CAMERA",n="Starting camera..."){_createPermissionButton(e,n,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via button")})}function enableCameraTap(e="Tap screen to enable camera"){navigator.permissions&&navigator.permissions.query?navigator.permissions.query({name:"camera"}).then(n=>{"granted"===n.state?(console.log("✅ Camera permission already granted - auto-starting"),_requestCameraPermission()):_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}).catch(()=>{_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}):_createTapToEnable(e,async()=>{await _requestCameraPermission(),console.log("✅ Camera enabled via tap")})}async function _requestCameraPermission(){try{if(void 0!==window._phoneCameras&&Array.isArray(window._phoneCameras))for(let e of window._phoneCameras)!e||e._ready||e._video||e._initializeCamera();"function"==typeof userCameraReady&&userCameraReady(),_notifySketchReady()}catch(e){console.error("Camera permission error:",e),_debugVisible&&debugError("Camera permission error:",e),_notifySketchReady()}}if(window.createPhoneCamera=createPhoneCamera,window.enableCameraButton=enableCameraButton,window.enableCameraTap=enableCameraTap,"undefined"!=typeof p5&&p5.prototype){const e=p5.prototype.image;p5.prototype.image=function(...n){if(n[0]instanceof PhoneCamera){const t=n[0];if(n.length>=3){if(!t.ready||!t.video)return;const o=n[1],i=n[2],r=n[3]||t.width,a=n[4]||t.height;this.push(),t.mirror?(this.translate(o+r,i),this.scale(-1,1),e.call(this,t.video,0,0,r,a)):e.call(this,t.video,o,i,r,a),this.pop()}else t._draw()}else e.apply(this,n)}}"undefined"!=typeof p5&&p5.prototype&&(p5.prototype.lockGestures=lockGestures,p5.prototype.enableGyroTap=enableGyroTap,p5.prototype.enableGyroButton=enableGyroButton,p5.prototype.enableMicTap=enableMicTap,p5.prototype.enableMicButton=enableMicButton,p5.prototype.enableSoundTap=enableSoundTap,p5.prototype.enableSoundButton=enableSoundButton,p5.prototype.enableVibrationTap=enableVibrationTap,p5.prototype.enableVibrationButton=enableVibrationButton,p5.prototype.vibrate=vibrate,p5.prototype.stopVibration=stopVibration,p5.prototype.enableAllTap=enableAllTap,p5.prototype.enableAllButton=enableAllButton,p5.prototype.createPhoneCamera=createPhoneCamera,p5.prototype.enableCameraButton=enableCameraButton,p5.prototype.enableCameraTap=enableCameraTap,p5.prototype.showDebug=showDebug,p5.prototype.hideDebug=hideDebug,p5.prototype.toggleDebug=toggleDebug,p5.prototype.debug=debug,p5.prototype.debugError=debugError,p5.prototype.debugWarn=debugWarn,console.log("✅ Mobile p5.js Permissions: p5.prototype functions registered"));
|
|
@@ -535,14 +535,14 @@
|
|
|
535
535
|
|
|
536
536
|
<div class="code-box">
|
|
537
537
|
<h4>Minified version (recommended)</h4>
|
|
538
|
-
<pre><code><script src="https://cdn.jsdelivr.net/npm/p5-phone@1.
|
|
539
|
-
<button class="copy-button" onclick="copyCode(this, '<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.
|
|
538
|
+
<pre><code><script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js"></script></code></pre>
|
|
539
|
+
<button class="copy-button" onclick="copyCode(this, '<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js"></script>')">Copy</button>
|
|
540
540
|
</div>
|
|
541
541
|
|
|
542
542
|
<div class="code-box">
|
|
543
543
|
<h4>Development version (larger, with comments)</h4>
|
|
544
|
-
<pre><code><script src="https://cdn.jsdelivr.net/npm/p5-phone@1.
|
|
545
|
-
<button class="copy-button" onclick="copyCode(this, '<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.
|
|
544
|
+
<pre><code><script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.js"></script></code></pre>
|
|
545
|
+
<button class="copy-button" onclick="copyCode(this, '<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.js"></script>')">Copy</button>
|
|
546
546
|
</div>
|
|
547
547
|
</div>
|
|
548
548
|
|
|
@@ -608,7 +608,7 @@
|
|
|
608
608
|
</style>
|
|
609
609
|
|
|
610
610
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
611
|
-
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.
|
|
611
|
+
<script src="https://cdn.jsdelivr.net/npm/p5-phone@1.6.1/dist/p5-phone.min.js"></script>
|
|
612
612
|
</head>
|
|
613
613
|
<body>
|
|
614
614
|
<script src="sketch.js"></script>
|
package/package.json
CHANGED
package/src/p5-phone.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* p5-phone v1.
|
|
2
|
+
* p5-phone v1.6.0
|
|
3
3
|
* Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease
|
|
4
4
|
* https://github.com/npuckett/p5-phone
|
|
5
5
|
*
|
|
@@ -1367,8 +1367,11 @@ class PhoneCamera {
|
|
|
1367
1367
|
|
|
1368
1368
|
const videoWidth = this._video.width;
|
|
1369
1369
|
const videoHeight = this._video.height;
|
|
1370
|
-
|
|
1371
|
-
|
|
1370
|
+
|
|
1371
|
+
// Get actual canvas dimensions (works on both desktop and mobile)
|
|
1372
|
+
// Use p5.js width/height globals if available, otherwise fall back to window dimensions
|
|
1373
|
+
const canvasWidth = (typeof width !== 'undefined') ? width : window.innerWidth;
|
|
1374
|
+
const canvasHeight = (typeof height !== 'undefined') ? height : window.innerHeight;
|
|
1372
1375
|
|
|
1373
1376
|
let drawWidth, drawHeight, drawX, drawY;
|
|
1374
1377
|
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
# Camera Selector - PhoneCamera API Demo
|
|
2
|
-
|
|
3
|
-
This example demonstrates the full capabilities of the **PhoneCamera** class from p5-phone library.
|
|
4
|
-
|
|
5
|
-
## Features Demonstrated
|
|
6
|
-
|
|
7
|
-
### 1. Camera Switching
|
|
8
|
-
- Toggle between front (`'user'`) and back (`'environment'`) cameras
|
|
9
|
-
- Instant switching with `cam.active = 'environment'`
|
|
10
|
-
- No manual camera recreation needed
|
|
11
|
-
|
|
12
|
-
### 2. Display Modes
|
|
13
|
-
Cycle through all 5 display modes:
|
|
14
|
-
- **fitHeight** - Fill canvas height, maintain aspect ratio (default)
|
|
15
|
-
- **fitWidth** - Fill canvas width, maintain aspect ratio
|
|
16
|
-
- **cover** - Fill entire canvas, crop if needed
|
|
17
|
-
- **contain** - Fit entirely within canvas, letterbox if needed
|
|
18
|
-
- **fixed** - Use fixed dimensions (640x480 default)
|
|
19
|
-
|
|
20
|
-
### 3. Mirroring Control
|
|
21
|
-
- Toggle horizontal flip with `cam.mirror = true/false`
|
|
22
|
-
- Typically ON for front camera (natural selfie view)
|
|
23
|
-
- Typically OFF for back camera
|
|
24
|
-
|
|
25
|
-
### 4. Status Display
|
|
26
|
-
- Real-time display of current settings
|
|
27
|
-
- Shows video dimensions and display dimensions
|
|
28
|
-
- Demonstrates `cam.getDimensions()` for debugging
|
|
29
|
-
|
|
30
|
-
## Code Comparison
|
|
31
|
-
|
|
32
|
-
### Original Version (331 lines):
|
|
33
|
-
```javascript
|
|
34
|
-
// Manual camera management
|
|
35
|
-
function startCamera(facingMode) {
|
|
36
|
-
if (videoCapture) videoCapture.remove();
|
|
37
|
-
let constraints = { video: { facingMode }, audio: false, flipped: isFlipped };
|
|
38
|
-
videoCapture = createCapture(constraints);
|
|
39
|
-
videoCapture.hide();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Manual dimension calculations (35+ lines)
|
|
43
|
-
function drawVideoFeed() {
|
|
44
|
-
let videoWidth = videoCapture.width;
|
|
45
|
-
let videoHeight = videoCapture.height;
|
|
46
|
-
if (scaleMode === 'fitWidth') {
|
|
47
|
-
drawWidth = width;
|
|
48
|
-
drawHeight = (videoHeight / videoWidth) * width;
|
|
49
|
-
// ... more calculations
|
|
50
|
-
}
|
|
51
|
-
// ... more modes
|
|
52
|
-
image(videoCapture, drawX, drawY, drawWidth, drawHeight);
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### New Version (~220 lines):
|
|
57
|
-
```javascript
|
|
58
|
-
// Create once in setup
|
|
59
|
-
cam = createPhoneCamera('user', true, 'fitHeight');
|
|
60
|
-
|
|
61
|
-
// Switch cameras with property
|
|
62
|
-
cam.active = 'environment';
|
|
63
|
-
|
|
64
|
-
// Change modes with property
|
|
65
|
-
cam.mode = 'cover';
|
|
66
|
-
|
|
67
|
-
// Draw with one line
|
|
68
|
-
image(cam, 0, 0); // All positioning automatic!
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Key Benefits
|
|
72
|
-
|
|
73
|
-
✅ **33% Less Code** - From 331 to ~220 lines
|
|
74
|
-
✅ **No Manual Calculations** - All dimension math handled by library
|
|
75
|
-
✅ **Property-Based API** - Simple assignments instead of method calls
|
|
76
|
-
✅ **Automatic Mirroring** - Built into drawing, no manual transforms
|
|
77
|
-
✅ **5 Display Modes** - More options than original (added cover, contain)
|
|
78
|
-
✅ **Status Info** - Easy access to dimensions via `getDimensions()`
|
|
79
|
-
|
|
80
|
-
## Usage Tips
|
|
81
|
-
|
|
82
|
-
### Camera Switching
|
|
83
|
-
```javascript
|
|
84
|
-
// Instant switch
|
|
85
|
-
cam.active = cam.active === 'user' ? 'environment' : 'user';
|
|
86
|
-
|
|
87
|
-
// Typically adjust mirroring when switching
|
|
88
|
-
if (cam.active === 'environment') {
|
|
89
|
-
cam.mirror = false; // Back camera usually not mirrored
|
|
90
|
-
} else {
|
|
91
|
-
cam.mirror = true; // Front camera mirrored for selfie
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Display Mode Selection
|
|
96
|
-
- **Mobile Portrait**: Use `fitHeight` (default) - fills screen vertically
|
|
97
|
-
- **Mobile Landscape**: Use `fitWidth` - fills screen horizontally
|
|
98
|
-
- **Fill Screen**: Use `cover` - no letterboxing, may crop video
|
|
99
|
-
- **See Full Video**: Use `contain` - shows entire video, may letterbox
|
|
100
|
-
- **Fixed Size**: Use `fixed` with `cam.fixedWidth` and `cam.fixedHeight`
|
|
101
|
-
|
|
102
|
-
### Getting Dimension Info
|
|
103
|
-
```javascript
|
|
104
|
-
let dims = cam.getDimensions();
|
|
105
|
-
console.log(dims.x, dims.y); // Position on canvas
|
|
106
|
-
console.log(dims.width, dims.height); // Display size
|
|
107
|
-
console.log(dims.scaleX, dims.scaleY); // Scale factors for ML5
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Testing Instructions
|
|
111
|
-
|
|
112
|
-
1. Open on mobile device
|
|
113
|
-
2. Tap to enable camera
|
|
114
|
-
3. Use bottom buttons to:
|
|
115
|
-
- **Left**: Switch between front/back cameras
|
|
116
|
-
- **Middle**: Cycle through display modes
|
|
117
|
-
- **Right**: Toggle mirroring on/off
|
|
118
|
-
4. Observe status overlay showing current settings
|
|
119
|
-
5. Rotate device to test different orientations
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>01 - Camera Selector</title>
|
|
7
|
-
|
|
8
|
-
<!-- Basic CSS to remove browser defaults and align canvas -->
|
|
9
|
-
<style>
|
|
10
|
-
body {
|
|
11
|
-
margin: 0;
|
|
12
|
-
padding: 0;
|
|
13
|
-
overflow: hidden;
|
|
14
|
-
}
|
|
15
|
-
</style>
|
|
16
|
-
|
|
17
|
-
<!-- Load p5.js library -->
|
|
18
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.10/p5.min.js"></script>
|
|
19
|
-
|
|
20
|
-
<!-- Load p5-phone library with camera support -->
|
|
21
|
-
<script src="../../../src/p5-phone.js"></script>
|
|
22
|
-
|
|
23
|
-
</head>
|
|
24
|
-
<body>
|
|
25
|
-
<!-- Load the p5.js sketch -->
|
|
26
|
-
<script src="sketch.js"></script>
|
|
27
|
-
</body>
|
|
28
|
-
</html>
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
// ==============================================
|
|
2
|
-
// CAMERA SELECTOR - SIMPLIFIED WITH P5-PHONE
|
|
3
|
-
// ==============================================
|
|
4
|
-
// Demonstrates the PhoneCamera API from p5-phone library.
|
|
5
|
-
// Shows camera switching, display modes, and mirroring.
|
|
6
|
-
//
|
|
7
|
-
// KEY FEATURES:
|
|
8
|
-
// - Switch between front/back cameras
|
|
9
|
-
// - Cycle through display modes (fitHeight, fitWidth, cover, contain, fixed)
|
|
10
|
-
// - Toggle mirroring on/off
|
|
11
|
-
// - All camera logic handled by PhoneCamera class
|
|
12
|
-
// ==============================================
|
|
13
|
-
|
|
14
|
-
// ==============================================
|
|
15
|
-
// GLOBAL VARIABLES
|
|
16
|
-
// ==============================================
|
|
17
|
-
let cam; // PhoneCamera instance
|
|
18
|
-
let cameraButton; // Button to switch cameras
|
|
19
|
-
let scaleModeButton; // Button to change scale mode
|
|
20
|
-
let flipButton; // Button to toggle video flip
|
|
21
|
-
|
|
22
|
-
// ==============================================
|
|
23
|
-
// SETUP FUNCTION - Runs once when page loads
|
|
24
|
-
// ==============================================
|
|
25
|
-
function setup() {
|
|
26
|
-
createCanvas(windowWidth, windowHeight);
|
|
27
|
-
|
|
28
|
-
// Lock mobile gestures to prevent browser interference
|
|
29
|
-
lockGestures();
|
|
30
|
-
|
|
31
|
-
// Create PhoneCamera: front camera, mirrored, fit to height
|
|
32
|
-
cam = createPhoneCamera('user', true, 'fitHeight');
|
|
33
|
-
|
|
34
|
-
// Create UI buttons
|
|
35
|
-
createCameraButton();
|
|
36
|
-
createScaleModeButton();
|
|
37
|
-
createFlipButton();
|
|
38
|
-
|
|
39
|
-
// Enable camera with tap
|
|
40
|
-
enableCameraTap();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ==============================================
|
|
44
|
-
// CAMERA READY - Called when user enables camera
|
|
45
|
-
// ==============================================
|
|
46
|
-
function userCameraReady() {
|
|
47
|
-
// Initialize the camera (starts video capture)
|
|
48
|
-
cam._initializeCamera();
|
|
49
|
-
console.log('Camera started!');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ==============================================
|
|
53
|
-
// DRAW FUNCTION - Runs continuously
|
|
54
|
-
// ==============================================
|
|
55
|
-
function draw() {
|
|
56
|
-
background(40);
|
|
57
|
-
|
|
58
|
-
// Draw the camera feed if ready
|
|
59
|
-
if (cam.ready) {
|
|
60
|
-
image(cam, 0, 0); // PhoneCamera handles all positioning and mirroring!
|
|
61
|
-
} else {
|
|
62
|
-
// Show loading message
|
|
63
|
-
fill(255);
|
|
64
|
-
textAlign(CENTER, CENTER);
|
|
65
|
-
textSize(24);
|
|
66
|
-
text('Tap to start camera...', width/2, height/2);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Draw status overlay
|
|
70
|
-
drawStatus();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ==============================================
|
|
74
|
-
// CREATE CAMERA BUTTON
|
|
75
|
-
// ==============================================
|
|
76
|
-
function createCameraButton() {
|
|
77
|
-
cameraButton = createButton('Front Cam');
|
|
78
|
-
|
|
79
|
-
let buttonWidth = width / 3;
|
|
80
|
-
let buttonHeight = 60;
|
|
81
|
-
let buttonX = 0;
|
|
82
|
-
let buttonY = height - buttonHeight;
|
|
83
|
-
|
|
84
|
-
cameraButton.position(buttonX, buttonY);
|
|
85
|
-
cameraButton.size(buttonWidth, buttonHeight);
|
|
86
|
-
cameraButton.style('font-size', '14px');
|
|
87
|
-
cameraButton.style('background-color', '#4A90E2');
|
|
88
|
-
cameraButton.style('color', 'white');
|
|
89
|
-
cameraButton.style('border', 'none');
|
|
90
|
-
cameraButton.style('border-radius', '0');
|
|
91
|
-
cameraButton.style('cursor', 'pointer');
|
|
92
|
-
cameraButton.style('box-shadow', 'none');
|
|
93
|
-
cameraButton.style('z-index', '1000');
|
|
94
|
-
|
|
95
|
-
cameraButton.mousePressed(switchCamera);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ==============================================
|
|
99
|
-
// CREATE SCALE MODE BUTTON
|
|
100
|
-
// ==============================================
|
|
101
|
-
function createScaleModeButton() {
|
|
102
|
-
scaleModeButton = createButton('Fit Height');
|
|
103
|
-
|
|
104
|
-
let buttonWidth = width / 3;
|
|
105
|
-
let buttonHeight = 60;
|
|
106
|
-
let buttonX = width / 3;
|
|
107
|
-
let buttonY = height - buttonHeight;
|
|
108
|
-
|
|
109
|
-
scaleModeButton.position(buttonX, buttonY);
|
|
110
|
-
scaleModeButton.size(buttonWidth, buttonHeight);
|
|
111
|
-
scaleModeButton.style('font-size', '14px');
|
|
112
|
-
scaleModeButton.style('background-color', '#50C878');
|
|
113
|
-
scaleModeButton.style('color', 'white');
|
|
114
|
-
scaleModeButton.style('border', 'none');
|
|
115
|
-
scaleModeButton.style('border-radius', '0');
|
|
116
|
-
scaleModeButton.style('cursor', 'pointer');
|
|
117
|
-
scaleModeButton.style('box-shadow', 'none');
|
|
118
|
-
scaleModeButton.style('z-index', '1000');
|
|
119
|
-
|
|
120
|
-
scaleModeButton.mousePressed(cycleScaleMode);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ==============================================
|
|
124
|
-
// CREATE FLIP BUTTON
|
|
125
|
-
// ==============================================
|
|
126
|
-
function createFlipButton() {
|
|
127
|
-
flipButton = createButton('Mirror: ON');
|
|
128
|
-
|
|
129
|
-
let buttonWidth = width / 3;
|
|
130
|
-
let buttonHeight = 60;
|
|
131
|
-
let buttonX = (width / 3) * 2;
|
|
132
|
-
let buttonY = height - buttonHeight;
|
|
133
|
-
|
|
134
|
-
flipButton.position(buttonX, buttonY);
|
|
135
|
-
flipButton.size(buttonWidth, buttonHeight);
|
|
136
|
-
flipButton.style('font-size', '14px');
|
|
137
|
-
flipButton.style('background-color', '#FF6B6B');
|
|
138
|
-
flipButton.style('color', 'white');
|
|
139
|
-
flipButton.style('border', 'none');
|
|
140
|
-
flipButton.style('border-radius', '0');
|
|
141
|
-
flipButton.style('cursor', 'pointer');
|
|
142
|
-
flipButton.style('box-shadow', 'none');
|
|
143
|
-
flipButton.style('z-index', '1000');
|
|
144
|
-
|
|
145
|
-
flipButton.mousePressed(toggleFlip);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// ==============================================
|
|
149
|
-
// SWITCH CAMERA
|
|
150
|
-
// ==============================================
|
|
151
|
-
function switchCamera() {
|
|
152
|
-
if (!cam.ready) return;
|
|
153
|
-
|
|
154
|
-
// Toggle camera with property assignment
|
|
155
|
-
cam.active = cam.active === 'user' ? 'environment' : 'user';
|
|
156
|
-
|
|
157
|
-
// Update button text
|
|
158
|
-
cameraButton.html(cam.active === 'user' ? 'Front Cam' : 'Back Cam');
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// ==============================================
|
|
162
|
-
// CYCLE SCALE MODE
|
|
163
|
-
// ==============================================
|
|
164
|
-
function cycleScaleMode() {
|
|
165
|
-
if (!cam.ready) return;
|
|
166
|
-
|
|
167
|
-
// Cycle through: fitHeight -> fitWidth -> cover -> contain -> fixed -> fitHeight
|
|
168
|
-
const modes = ['fitHeight', 'fitWidth', 'cover', 'contain', 'fixed'];
|
|
169
|
-
const modeNames = ['Fit Height', 'Fit Width', 'Cover', 'Contain', 'Fixed'];
|
|
170
|
-
|
|
171
|
-
let currentIndex = modes.indexOf(cam.mode);
|
|
172
|
-
let nextIndex = (currentIndex + 1) % modes.length;
|
|
173
|
-
|
|
174
|
-
cam.mode = modes[nextIndex];
|
|
175
|
-
scaleModeButton.html(modeNames[nextIndex]);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// ==============================================
|
|
179
|
-
// TOGGLE FLIP
|
|
180
|
-
// ==============================================
|
|
181
|
-
function toggleFlip() {
|
|
182
|
-
if (!cam.ready) return;
|
|
183
|
-
|
|
184
|
-
// Toggle mirroring with property assignment
|
|
185
|
-
cam.mirror = !cam.mirror;
|
|
186
|
-
|
|
187
|
-
// Update button text
|
|
188
|
-
flipButton.html(cam.mirror ? 'Mirror: ON' : 'Mirror: OFF');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// ==============================================
|
|
192
|
-
// DRAW STATUS
|
|
193
|
-
// ==============================================
|
|
194
|
-
function drawStatus() {
|
|
195
|
-
if (!cam.ready) return;
|
|
196
|
-
|
|
197
|
-
// Draw status overlay at top
|
|
198
|
-
push();
|
|
199
|
-
fill(0, 0, 0, 150);
|
|
200
|
-
noStroke();
|
|
201
|
-
rect(0, 0, width, 80);
|
|
202
|
-
|
|
203
|
-
fill(255);
|
|
204
|
-
textAlign(CENTER, CENTER);
|
|
205
|
-
textSize(16);
|
|
206
|
-
text(`Camera: ${cam.active}`, width/2, 20);
|
|
207
|
-
text(`Mode: ${cam.mode} | Mirror: ${cam.mirror ? 'ON' : 'OFF'}`, width/2, 45);
|
|
208
|
-
|
|
209
|
-
// Show dimensions
|
|
210
|
-
let dims = cam.getDimensions();
|
|
211
|
-
textSize(12);
|
|
212
|
-
text(`Display: ${dims.width.toFixed(0)}x${dims.height.toFixed(0)} | Video: ${cam.video.width}x${cam.video.height}`, width/2, 65);
|
|
213
|
-
pop();
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ==============================================
|
|
217
|
-
// WINDOW RESIZE
|
|
218
|
-
// ==============================================
|
|
219
|
-
function windowResized() {
|
|
220
|
-
resizeCanvas(windowWidth, windowHeight);
|
|
221
|
-
|
|
222
|
-
// Recalculate button dimensions and positions
|
|
223
|
-
let buttonWidth = width / 3;
|
|
224
|
-
let buttonHeight = 60;
|
|
225
|
-
let buttonY = height - buttonHeight;
|
|
226
|
-
|
|
227
|
-
if (cameraButton) {
|
|
228
|
-
cameraButton.position(0, buttonY);
|
|
229
|
-
cameraButton.size(buttonWidth, buttonHeight);
|
|
230
|
-
}
|
|
231
|
-
if (scaleModeButton) {
|
|
232
|
-
scaleModeButton.position(buttonWidth, buttonY);
|
|
233
|
-
scaleModeButton.size(buttonWidth, buttonHeight);
|
|
234
|
-
}
|
|
235
|
-
if (flipButton) {
|
|
236
|
-
flipButton.position(buttonWidth * 2, buttonY);
|
|
237
|
-
flipButton.size(buttonWidth, buttonHeight);
|
|
238
|
-
}
|
|
239
|
-
}
|