p5-phone 1.4.4
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/LICENSE +21 -0
- package/README.md +509 -0
- package/dist/p5-phone.js +1062 -0
- package/dist/p5-phone.min.js +10 -0
- package/examples/Phone Sensor Examples/microphone/01_mic_level/index.html +19 -0
- package/examples/Phone Sensor Examples/microphone/01_mic_level/sketch.js +117 -0
- package/examples/Phone Sensor Examples/movement/01_orientation_basic/index.html +28 -0
- package/examples/Phone Sensor Examples/movement/01_orientation_basic/sketch.js +123 -0
- package/examples/Phone Sensor Examples/movement/02_rotational_velocity/index.html +28 -0
- package/examples/Phone Sensor Examples/movement/02_rotational_velocity/sketch.js +144 -0
- package/examples/Phone Sensor Examples/movement/03_acceleration/index.html +28 -0
- package/examples/Phone Sensor Examples/movement/03_acceleration/sketch.js +87 -0
- package/examples/Phone Sensor Examples/sound/01_dual_audio/index.html +31 -0
- package/examples/Phone Sensor Examples/sound/01_dual_audio/sketch.js +225 -0
- package/examples/Phone Sensor Examples/sound/01_dual_audio/tracks/audio1.mp3 +0 -0
- package/examples/Phone Sensor Examples/sound/01_dual_audio/tracks/audio2.mp3 +0 -0
- package/examples/Phone Sensor Examples/sound/02_volume_touches/index.html +31 -0
- package/examples/Phone Sensor Examples/sound/02_volume_touches/sketch.js +269 -0
- package/examples/Phone Sensor Examples/sound/02_volume_touches/tracks/audio1.mp3 +0 -0
- package/examples/Phone Sensor Examples/touch/01_touch_basic/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/01_touch_basic/sketch.js +94 -0
- package/examples/Phone Sensor Examples/touch/02_touch_zones/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/02_touch_zones/sketch.js +220 -0
- package/examples/Phone Sensor Examples/touch/03_touch_count/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/03_touch_count/sketch.js +93 -0
- package/examples/Phone Sensor Examples/touch/04_touch_distance/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/04_touch_distance/sketch.js +120 -0
- package/examples/Phone Sensor Examples/touch/05_touch_angle/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/05_touch_angle/sketch.js +117 -0
- package/examples/Phone Sensor Examples - Minimal/microphone/01_mic_level/index.html +19 -0
- package/examples/Phone Sensor Examples - Minimal/microphone/01_mic_level/sketch.js +72 -0
- package/examples/Phone Sensor Examples - Minimal/movement/01_orientation_basic/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/movement/01_orientation_basic/sketch.js +51 -0
- package/examples/Phone Sensor Examples - Minimal/movement/02_rotational_velocity/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/movement/02_rotational_velocity/sketch.js +70 -0
- package/examples/Phone Sensor Examples - Minimal/movement/03_acceleration/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/movement/03_acceleration/sketch.js +83 -0
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/index.html +19 -0
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/sketch.js +96 -0
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/tracks/audio1.mp3 +0 -0
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/index.html +19 -0
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/sketch.js +118 -0
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/tracks/audio1.mp3 +0 -0
- package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/sketch.js +58 -0
- package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/sketch.js +78 -0
- package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/sketch.js +64 -0
- package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/sketch.js +69 -0
- package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/sketch.js +85 -0
- package/examples/Phone and Gif/collision/README.md +31 -0
- Gif/collision/gifs/spaceSuit2.png +0 -0
- package/examples/Phone and Gif/collision/index.html +19 -0
- package/examples/Phone and Gif/collision/sketch.js +226 -0
- Gif/fetch/gifs/corgiswimflip.gif +0 -0
- package/examples/Phone and Gif/fetch/index.html +18 -0
- package/examples/Phone and Gif/fetch/sketch.js +139 -0
- Gif/fly/gifs/comparison.gif +0 -0
- package/examples/Phone and Gif/fly/index.html +18 -0
- package/examples/Phone and Gif/fly/sketch.js +103 -0
- Gif/roll/gifs/how-penciles-are-made.gif +0 -0
- package/examples/Phone and Gif/roll/index.html +18 -0
- package/examples/Phone and Gif/roll/sketch.js +121 -0
- package/examples/UXcompare/button-vs-movement/index.html +45 -0
- package/examples/UXcompare/button-vs-movement/sketch.js +355 -0
- package/examples/UXcompare/button-vs-orientation/index.html +25 -0
- package/examples/UXcompare/button-vs-orientation/sketch.js +317 -0
- package/examples/UXcompare/button-vs-shake/index.html +45 -0
- package/examples/UXcompare/button-vs-shake/sketch.js +320 -0
- package/examples/UXcompare/gyroscope-demo/index.html +78 -0
- package/examples/UXcompare/gyroscope-demo/sketch.js +166 -0
- package/examples/UXcompare/index.html +419 -0
- package/examples/UXcompare/microphone-demo/index.html +79 -0
- package/examples/UXcompare/microphone-demo/sketch.js +210 -0
- package/examples/UXcompare/slider-vs-angle/index.html +25 -0
- package/examples/UXcompare/slider-vs-angle/sketch.js +429 -0
- package/examples/UXcompare/slider-vs-distance/index.html +25 -0
- package/examples/UXcompare/slider-vs-distance/sketch.js +401 -0
- package/examples/UXcompare/slider-vs-microphone/index.html +26 -0
- package/examples/UXcompare/slider-vs-microphone/sketch.js +336 -0
- package/examples/UXcompare/slider-vs-touches/index.html +25 -0
- package/examples/UXcompare/slider-vs-touches/sketch.js +376 -0
- package/examples/UXcompare/sliders-vs-acceleration/index.html +25 -0
- package/examples/UXcompare/sliders-vs-acceleration/sketch.js +361 -0
- package/examples/UXcompare/sliders-vs-rotation/index.html +25 -0
- package/examples/UXcompare/sliders-vs-rotation/sketch.js +375 -0
- package/examples/blankTemplate/index.html +31 -0
- package/examples/blankTemplate/sketch.js +55 -0
- package/examples/homepage/index.html +506 -0
- package/package.json +73 -0
- package/src/p5-phone.js +1062 -0
- package/src/permissionMic.js +240 -0
- package/src/permissionsGesture.js +213 -0
- package/src/permissionsGyro.js +246 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// Sound Visualizer
|
|
2
|
+
// Demonstrates microphone input for audio-reactive visuals
|
|
3
|
+
|
|
4
|
+
let mic;
|
|
5
|
+
let particles = [];
|
|
6
|
+
let levelHistory = [];
|
|
7
|
+
let maxHistoryLength = 100;
|
|
8
|
+
|
|
9
|
+
function setup() {
|
|
10
|
+
createCanvas(windowWidth, windowHeight);
|
|
11
|
+
|
|
12
|
+
// Create microphone input
|
|
13
|
+
mic = new p5.AudioIn();
|
|
14
|
+
|
|
15
|
+
// Initialize particles
|
|
16
|
+
for (let i = 0; i < 50; i++) {
|
|
17
|
+
particles.push(createParticle());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
colorMode(HSB, 360, 100, 100, 1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function draw() {
|
|
24
|
+
background(0, 0, 10, 0.1);
|
|
25
|
+
|
|
26
|
+
if (window.micEnabled) {
|
|
27
|
+
drawVisualizer();
|
|
28
|
+
} else {
|
|
29
|
+
showWaitingScreen();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function drawVisualizer() {
|
|
34
|
+
let level = mic.getLevel();
|
|
35
|
+
|
|
36
|
+
// Store level history for waveform
|
|
37
|
+
levelHistory.push(level);
|
|
38
|
+
if (levelHistory.length > maxHistoryLength) {
|
|
39
|
+
levelHistory.shift();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Central circle that responds to audio
|
|
43
|
+
let centerSize = map(level, 0, 1, 50, 300);
|
|
44
|
+
let hue = (millis() * 0.1) % 360;
|
|
45
|
+
|
|
46
|
+
// Draw central responsive circle
|
|
47
|
+
push();
|
|
48
|
+
translate(width/2, height/2);
|
|
49
|
+
|
|
50
|
+
// Outer glow
|
|
51
|
+
for (let i = 0; i < 5; i++) {
|
|
52
|
+
let alpha = map(i, 0, 4, 0.3, 0.05);
|
|
53
|
+
fill(hue, 80, 100, alpha);
|
|
54
|
+
noStroke();
|
|
55
|
+
circle(0, 0, centerSize + i * 20);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Core circle
|
|
59
|
+
fill(hue, 100, 100, 0.8);
|
|
60
|
+
circle(0, 0, centerSize);
|
|
61
|
+
|
|
62
|
+
// Inner details
|
|
63
|
+
fill(hue + 180, 80, 100, 0.5);
|
|
64
|
+
circle(0, 0, centerSize * 0.6);
|
|
65
|
+
|
|
66
|
+
pop();
|
|
67
|
+
|
|
68
|
+
// Update and draw particles
|
|
69
|
+
for (let particle of particles) {
|
|
70
|
+
updateParticle(particle, level);
|
|
71
|
+
drawParticle(particle);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Draw waveform
|
|
75
|
+
drawWaveform();
|
|
76
|
+
|
|
77
|
+
// Draw level meter
|
|
78
|
+
drawLevelMeter(level);
|
|
79
|
+
|
|
80
|
+
// Instructions
|
|
81
|
+
fill(0, 0, 100, 0.7);
|
|
82
|
+
textAlign(CENTER, BOTTOM);
|
|
83
|
+
textSize(16);
|
|
84
|
+
text("Make some noise!", width/2, height - 20);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function createParticle() {
|
|
88
|
+
return {
|
|
89
|
+
x: random(width),
|
|
90
|
+
y: random(height),
|
|
91
|
+
vx: random(-2, 2),
|
|
92
|
+
vy: random(-2, 2),
|
|
93
|
+
size: random(5, 15),
|
|
94
|
+
hue: random(360),
|
|
95
|
+
life: 1.0
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function updateParticle(particle, audioLevel) {
|
|
100
|
+
// Move particle
|
|
101
|
+
particle.x += particle.vx;
|
|
102
|
+
particle.y += particle.vy;
|
|
103
|
+
|
|
104
|
+
// Audio influence
|
|
105
|
+
let audioForce = audioLevel * 10;
|
|
106
|
+
let centerX = width / 2;
|
|
107
|
+
let centerY = height / 2;
|
|
108
|
+
|
|
109
|
+
// Pull towards center when loud
|
|
110
|
+
if (audioLevel > 0.1) {
|
|
111
|
+
let dx = centerX - particle.x;
|
|
112
|
+
let dy = centerY - particle.y;
|
|
113
|
+
let distance = sqrt(dx * dx + dy * dy);
|
|
114
|
+
|
|
115
|
+
if (distance > 0) {
|
|
116
|
+
particle.vx += (dx / distance) * audioForce * 0.02;
|
|
117
|
+
particle.vy += (dy / distance) * audioForce * 0.02;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Bounce off edges
|
|
122
|
+
if (particle.x < 0 || particle.x > width) {
|
|
123
|
+
particle.vx *= -0.8;
|
|
124
|
+
particle.x = constrain(particle.x, 0, width);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (particle.y < 0 || particle.y > height) {
|
|
128
|
+
particle.vy *= -0.8;
|
|
129
|
+
particle.y = constrain(particle.y, 0, height);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Apply friction
|
|
133
|
+
particle.vx *= 0.98;
|
|
134
|
+
particle.vy *= 0.98;
|
|
135
|
+
|
|
136
|
+
// Update size based on audio
|
|
137
|
+
particle.targetSize = map(audioLevel, 0, 1, 5, 25);
|
|
138
|
+
particle.size = lerp(particle.size, particle.targetSize, 0.1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function drawParticle(particle) {
|
|
142
|
+
push();
|
|
143
|
+
translate(particle.x, particle.y);
|
|
144
|
+
|
|
145
|
+
let alpha = map(particle.size, 5, 25, 0.3, 0.8);
|
|
146
|
+
fill(particle.hue, 80, 100, alpha);
|
|
147
|
+
noStroke();
|
|
148
|
+
|
|
149
|
+
circle(0, 0, particle.size);
|
|
150
|
+
|
|
151
|
+
// Inner highlight
|
|
152
|
+
fill(particle.hue, 40, 100, alpha * 0.5);
|
|
153
|
+
circle(0, 0, particle.size * 0.5);
|
|
154
|
+
|
|
155
|
+
pop();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function drawWaveform() {
|
|
159
|
+
if (levelHistory.length < 2) return;
|
|
160
|
+
|
|
161
|
+
stroke(0, 0, 100, 0.6);
|
|
162
|
+
strokeWeight(2);
|
|
163
|
+
noFill();
|
|
164
|
+
|
|
165
|
+
beginShape();
|
|
166
|
+
for (let i = 0; i < levelHistory.length; i++) {
|
|
167
|
+
let x = map(i, 0, levelHistory.length - 1, 0, width);
|
|
168
|
+
let y = map(levelHistory[i], 0, 1, height - 50, height - 150);
|
|
169
|
+
vertex(x, y);
|
|
170
|
+
}
|
|
171
|
+
endShape();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function drawLevelMeter(level) {
|
|
175
|
+
// Level meter on the side
|
|
176
|
+
let meterHeight = 200;
|
|
177
|
+
let meterWidth = 20;
|
|
178
|
+
let meterX = width - 40;
|
|
179
|
+
let meterY = height/2 - meterHeight/2;
|
|
180
|
+
|
|
181
|
+
// Background
|
|
182
|
+
fill(0, 0, 0, 0.3);
|
|
183
|
+
rect(meterX, meterY, meterWidth, meterHeight);
|
|
184
|
+
|
|
185
|
+
// Level bar
|
|
186
|
+
let levelHeight = map(level, 0, 1, 0, meterHeight);
|
|
187
|
+
let levelHue = map(level, 0, 1, 120, 0); // Green to red
|
|
188
|
+
|
|
189
|
+
fill(levelHue, 80, 100, 0.8);
|
|
190
|
+
rect(meterX, meterY + meterHeight - levelHeight, meterWidth, levelHeight);
|
|
191
|
+
|
|
192
|
+
// Level text
|
|
193
|
+
fill(0, 0, 100);
|
|
194
|
+
textAlign(CENTER, CENTER);
|
|
195
|
+
textSize(12);
|
|
196
|
+
text("MIC", meterX + meterWidth/2, meterY - 20);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function showWaitingScreen() {
|
|
200
|
+
fill(0, 0, 100, 0.7);
|
|
201
|
+
textAlign(CENTER, CENTER);
|
|
202
|
+
textSize(24);
|
|
203
|
+
text("Waiting for microphone...", width/2, height/2);
|
|
204
|
+
textSize(16);
|
|
205
|
+
text("Allow microphone access to continue", width/2, height/2 + 40);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function windowResized() {
|
|
209
|
+
resizeCanvas(windowWidth, windowHeight);
|
|
210
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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, user-scalable=no">
|
|
6
|
+
<title>Slider vs Touch Angle - Mobile p5 Permissions</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
canvas {
|
|
16
|
+
display: block;
|
|
17
|
+
}
|
|
18
|
+
</style>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/mobile-p5-permissions@1.4.4/dist/p5.mobile-permissions.min.js"></script>
|
|
23
|
+
<script src="sketch.js"></script>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
// Slider vs Touch Angle Comparison
|
|
2
|
+
// Shows traditional float slider UI vs two-touch angle measurement
|
|
3
|
+
|
|
4
|
+
// Color scheme - using proper p5.js colors
|
|
5
|
+
let backgroundColor, sliderColor, touchColor;
|
|
6
|
+
|
|
7
|
+
// Slider properties
|
|
8
|
+
let sliderValue = 0.5; // Current slider value (0.0-1.0)
|
|
9
|
+
let sliderMin = 0.0;
|
|
10
|
+
let sliderMax = 1.0;
|
|
11
|
+
let sliderX, sliderY;
|
|
12
|
+
let sliderWidth = 300;
|
|
13
|
+
let sliderHeight = 40;
|
|
14
|
+
let isDraggingSlider = false;
|
|
15
|
+
|
|
16
|
+
// Touch angle measurement
|
|
17
|
+
let touchAngle = 0.0; // Angle in degrees
|
|
18
|
+
let normalizedAngle = 0.0; // Normalized to 0.0-1.0 (0-360 degrees)
|
|
19
|
+
|
|
20
|
+
// Layout properties
|
|
21
|
+
let topSectionHeight;
|
|
22
|
+
let bottomSectionHeight;
|
|
23
|
+
let dividerY;
|
|
24
|
+
let currentOrientation = 'portrait';
|
|
25
|
+
|
|
26
|
+
function setup() {
|
|
27
|
+
createCanvas(windowWidth, windowHeight);
|
|
28
|
+
|
|
29
|
+
// Initialize colors properly
|
|
30
|
+
backgroundColor = color(44, 62, 80); // Dark blue-gray
|
|
31
|
+
sliderColor = color(52, 152, 219); // Blue
|
|
32
|
+
touchColor = color(231, 76, 60); // Red
|
|
33
|
+
|
|
34
|
+
// Only call lockGestures if it exists
|
|
35
|
+
if (typeof lockGestures === 'function') {
|
|
36
|
+
lockGestures();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Calculate layout
|
|
40
|
+
updateLayout();
|
|
41
|
+
|
|
42
|
+
textAlign(CENTER, CENTER);
|
|
43
|
+
noStroke();
|
|
44
|
+
|
|
45
|
+
console.log('Slider vs Angle ready!');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function draw() {
|
|
49
|
+
background(backgroundColor);
|
|
50
|
+
|
|
51
|
+
// Update orientation
|
|
52
|
+
currentOrientation = (width > height) ? 'landscape' : 'portrait';
|
|
53
|
+
|
|
54
|
+
// Calculate touch angle for bottom section
|
|
55
|
+
calculateTouchAngle();
|
|
56
|
+
|
|
57
|
+
// Draw divider line
|
|
58
|
+
stroke(255, 255, 255, 100);
|
|
59
|
+
strokeWeight(2);
|
|
60
|
+
line(0, dividerY, width, dividerY);
|
|
61
|
+
noStroke();
|
|
62
|
+
|
|
63
|
+
// Draw top section - Traditional Float Slider UI
|
|
64
|
+
drawTopSection();
|
|
65
|
+
|
|
66
|
+
// Draw bottom section - Touch Angle Interaction
|
|
67
|
+
drawBottomSection();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function drawTopSection() {
|
|
71
|
+
// Section title
|
|
72
|
+
fill(255, 255, 255);
|
|
73
|
+
textSize(24);
|
|
74
|
+
if (currentOrientation === 'landscape') {
|
|
75
|
+
textAlign(LEFT, CENTER);
|
|
76
|
+
text('TRADITIONAL UI', 30, 40);
|
|
77
|
+
} else {
|
|
78
|
+
textAlign(CENTER, CENTER);
|
|
79
|
+
text('TRADITIONAL UI', width / 2, 40);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Draw slider
|
|
83
|
+
drawSlider();
|
|
84
|
+
|
|
85
|
+
// Draw value bar visualization
|
|
86
|
+
drawValueBar(sliderValue, topSectionHeight / 2 + 80, sliderColor);
|
|
87
|
+
|
|
88
|
+
// Draw slider value text
|
|
89
|
+
fill(255, 255, 255);
|
|
90
|
+
textSize(20);
|
|
91
|
+
let angleFromSlider = sliderValue * 360;
|
|
92
|
+
let valueText = 'Slider angle: ' + angleFromSlider.toFixed(0) + '°';
|
|
93
|
+
if (currentOrientation === 'landscape') {
|
|
94
|
+
textAlign(LEFT, CENTER);
|
|
95
|
+
text(valueText, 30, topSectionHeight - 40);
|
|
96
|
+
} else {
|
|
97
|
+
textAlign(CENTER, CENTER);
|
|
98
|
+
text(valueText, width / 2, topSectionHeight - 40);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function drawBottomSection() {
|
|
103
|
+
// Section title
|
|
104
|
+
fill(255, 255, 255);
|
|
105
|
+
textSize(24);
|
|
106
|
+
if (currentOrientation === 'landscape') {
|
|
107
|
+
textAlign(LEFT, CENTER);
|
|
108
|
+
text('MOBILE INTERACTION', 30, dividerY + 40);
|
|
109
|
+
} else {
|
|
110
|
+
textAlign(CENTER, CENTER);
|
|
111
|
+
text('MOBILE INTERACTION', width / 2, dividerY + 40);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Instructions
|
|
115
|
+
fill(255, 255, 255, 200);
|
|
116
|
+
textSize(18);
|
|
117
|
+
let instructionText = getTouchInstructions();
|
|
118
|
+
if (currentOrientation === 'landscape') {
|
|
119
|
+
textAlign(LEFT, CENTER);
|
|
120
|
+
text(instructionText, 30, dividerY + 80);
|
|
121
|
+
} else {
|
|
122
|
+
textAlign(CENTER, CENTER);
|
|
123
|
+
text(instructionText, width / 2, dividerY + 80);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Draw value bar for angle
|
|
127
|
+
drawValueBar(normalizedAngle, dividerY + (bottomSectionHeight / 2) + 40, touchColor);
|
|
128
|
+
|
|
129
|
+
// Draw touch angle visualization
|
|
130
|
+
drawTouchAngleVisualization();
|
|
131
|
+
|
|
132
|
+
// Draw angle value text
|
|
133
|
+
fill(255, 255, 255);
|
|
134
|
+
textSize(20);
|
|
135
|
+
let angleText = 'Touch angle: ' + touchAngle.toFixed(0) + '°';
|
|
136
|
+
if (currentOrientation === 'landscape') {
|
|
137
|
+
textAlign(LEFT, CENTER);
|
|
138
|
+
text(angleText, 30, height - 40);
|
|
139
|
+
} else {
|
|
140
|
+
textAlign(CENTER, CENTER);
|
|
141
|
+
text(angleText, width / 2, height - 40);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function getTouchInstructions() {
|
|
146
|
+
let bottomTouches = getBottomTouches();
|
|
147
|
+
if (bottomTouches.length === 0) {
|
|
148
|
+
return 'Place two fingers to measure angle!';
|
|
149
|
+
} else if (bottomTouches.length === 1) {
|
|
150
|
+
return 'Add a second finger!';
|
|
151
|
+
} else if (bottomTouches.length === 2) {
|
|
152
|
+
return 'Rotate fingers to change angle!';
|
|
153
|
+
} else {
|
|
154
|
+
return 'Too many fingers - use only 2!';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function drawSlider() {
|
|
159
|
+
// Position slider in center of top section
|
|
160
|
+
sliderY = topSectionHeight / 2 - sliderHeight / 2;
|
|
161
|
+
|
|
162
|
+
if (currentOrientation === 'landscape') {
|
|
163
|
+
sliderX = width / 2 - sliderWidth / 2 + 50; // Offset right to avoid left text
|
|
164
|
+
} else {
|
|
165
|
+
sliderX = width / 2 - sliderWidth / 2;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Slider track (background)
|
|
169
|
+
fill(255, 255, 255, 100);
|
|
170
|
+
rect(sliderX, sliderY, sliderWidth, sliderHeight, sliderHeight / 2);
|
|
171
|
+
|
|
172
|
+
// Calculate slider handle position
|
|
173
|
+
let handleX = map(sliderValue, sliderMin, sliderMax, sliderX + 20, sliderX + sliderWidth - 20);
|
|
174
|
+
|
|
175
|
+
// Slider fill (shows progress)
|
|
176
|
+
fill(sliderColor);
|
|
177
|
+
rect(sliderX, sliderY, handleX - sliderX + 20, sliderHeight, sliderHeight / 2);
|
|
178
|
+
|
|
179
|
+
// Slider handle shadow
|
|
180
|
+
fill(0, 0, 0, 50);
|
|
181
|
+
circle(handleX + 2, sliderY + sliderHeight / 2 + 2, 35);
|
|
182
|
+
|
|
183
|
+
// Slider handle
|
|
184
|
+
fill(255, 255, 255);
|
|
185
|
+
if (isDraggingSlider) {
|
|
186
|
+
fill(255, 255, 0); // Yellow when dragging
|
|
187
|
+
}
|
|
188
|
+
circle(handleX, sliderY + sliderHeight / 2, 35);
|
|
189
|
+
|
|
190
|
+
// Handle indicator
|
|
191
|
+
fill(sliderColor);
|
|
192
|
+
circle(handleX, sliderY + sliderHeight / 2, 15);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function drawValueBar(value, yPos, barColor) {
|
|
196
|
+
// Draw a horizontal bar representing the value (0.0-1.0)
|
|
197
|
+
let barWidth = min(300, width - 120);
|
|
198
|
+
let barHeight = 30;
|
|
199
|
+
let barX = width / 2 - barWidth / 2;
|
|
200
|
+
|
|
201
|
+
if (currentOrientation === 'landscape') {
|
|
202
|
+
barX = width / 2 - barWidth / 2 + 25; // Slight offset for landscape
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Background bar
|
|
206
|
+
fill(255, 255, 255, 100);
|
|
207
|
+
rect(barX, yPos - barHeight / 2, barWidth, barHeight, barHeight / 2);
|
|
208
|
+
|
|
209
|
+
// Value fill
|
|
210
|
+
let fillWidth = value * barWidth;
|
|
211
|
+
fill(barColor);
|
|
212
|
+
rect(barX, yPos - barHeight / 2, fillWidth, barHeight, barHeight / 2);
|
|
213
|
+
|
|
214
|
+
// Value text on bar (showing degrees)
|
|
215
|
+
fill(255, 255, 255);
|
|
216
|
+
textAlign(CENTER, CENTER);
|
|
217
|
+
textSize(16);
|
|
218
|
+
text((value * 360).toFixed(0) + '°', barX + barWidth / 2, yPos);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function drawTouchAngleVisualization() {
|
|
222
|
+
let bottomTouches = getBottomTouches();
|
|
223
|
+
|
|
224
|
+
if (bottomTouches.length >= 2) {
|
|
225
|
+
// Draw angle visualization between first two touches
|
|
226
|
+
let touch1 = bottomTouches[0];
|
|
227
|
+
let touch2 = bottomTouches[1];
|
|
228
|
+
|
|
229
|
+
// Connection line
|
|
230
|
+
stroke(touchColor);
|
|
231
|
+
strokeWeight(3);
|
|
232
|
+
line(touch1.x, touch1.y, touch2.x, touch2.y);
|
|
233
|
+
|
|
234
|
+
// Calculate midpoint for angle arc
|
|
235
|
+
let midX = (touch1.x + touch2.x) / 2;
|
|
236
|
+
let midY = (touch1.y + touch2.y) / 2;
|
|
237
|
+
|
|
238
|
+
// Draw angle arc
|
|
239
|
+
stroke(255, 255, 0); // Yellow for angle indicator
|
|
240
|
+
strokeWeight(2);
|
|
241
|
+
noFill();
|
|
242
|
+
let arcRadius = 60;
|
|
243
|
+
|
|
244
|
+
// Calculate angle from horizontal (right direction = 0°)
|
|
245
|
+
let angleRad = atan2(touch2.y - touch1.y, touch2.x - touch1.x);
|
|
246
|
+
|
|
247
|
+
// Draw reference line (horizontal)
|
|
248
|
+
stroke(255, 255, 255, 100);
|
|
249
|
+
strokeWeight(1);
|
|
250
|
+
line(midX - 40, midY, midX + 40, midY);
|
|
251
|
+
|
|
252
|
+
// Draw angle arc from horizontal to the line
|
|
253
|
+
stroke(255, 255, 0);
|
|
254
|
+
strokeWeight(2);
|
|
255
|
+
if (angleRad >= 0) {
|
|
256
|
+
arc(midX, midY, arcRadius, arcRadius, 0, angleRad);
|
|
257
|
+
} else {
|
|
258
|
+
arc(midX, midY, arcRadius, arcRadius, angleRad, 0);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Touch points
|
|
262
|
+
fill(touchColor);
|
|
263
|
+
noStroke();
|
|
264
|
+
circle(touch1.x, touch1.y, 40);
|
|
265
|
+
circle(touch2.x, touch2.y, 40);
|
|
266
|
+
|
|
267
|
+
// Touch numbers
|
|
268
|
+
fill(255, 255, 255);
|
|
269
|
+
textAlign(CENTER, CENTER);
|
|
270
|
+
textSize(18);
|
|
271
|
+
text('1', touch1.x, touch1.y);
|
|
272
|
+
text('2', touch2.x, touch2.y);
|
|
273
|
+
|
|
274
|
+
// Angle text near midpoint
|
|
275
|
+
fill(0, 0, 0, 150);
|
|
276
|
+
let angleText = Math.round(touchAngle) + '°';
|
|
277
|
+
textSize(14);
|
|
278
|
+
let textW = textWidth(angleText) + 10;
|
|
279
|
+
rect(midX - textW / 2, midY - 35, textW, 20, 5);
|
|
280
|
+
|
|
281
|
+
// Angle text
|
|
282
|
+
fill(255, 255, 0);
|
|
283
|
+
text(angleText, midX, midY - 25);
|
|
284
|
+
|
|
285
|
+
} else {
|
|
286
|
+
// Draw all touches in bottom section
|
|
287
|
+
for (let i = 0; i < bottomTouches.length; i++) {
|
|
288
|
+
let touch = bottomTouches[i];
|
|
289
|
+
fill(touchColor);
|
|
290
|
+
noStroke();
|
|
291
|
+
circle(touch.x, touch.y, 40);
|
|
292
|
+
|
|
293
|
+
fill(255, 255, 255);
|
|
294
|
+
textAlign(CENTER, CENTER);
|
|
295
|
+
textSize(18);
|
|
296
|
+
text(i + 1, touch.x, touch.y);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function getBottomTouches() {
|
|
302
|
+
let bottomTouches = [];
|
|
303
|
+
if (touches && touches.length > 0) {
|
|
304
|
+
for (let touch of touches) {
|
|
305
|
+
if (touch && typeof touch.x === 'number' && typeof touch.y === 'number' && touch.y > dividerY) {
|
|
306
|
+
bottomTouches.push(touch);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return bottomTouches;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function calculateTouchAngle() {
|
|
314
|
+
let bottomTouches = getBottomTouches();
|
|
315
|
+
|
|
316
|
+
if (bottomTouches.length >= 2) {
|
|
317
|
+
let touch1 = bottomTouches[0];
|
|
318
|
+
let touch2 = bottomTouches[1];
|
|
319
|
+
|
|
320
|
+
// Calculate angle in radians, then convert to degrees
|
|
321
|
+
let angleRad = atan2(touch2.y - touch1.y, touch2.x - touch1.x);
|
|
322
|
+
touchAngle = degrees(angleRad);
|
|
323
|
+
|
|
324
|
+
// Normalize to 0-360 degrees
|
|
325
|
+
if (touchAngle < 0) {
|
|
326
|
+
touchAngle += 360;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Normalize to 0.0-1.0 range
|
|
330
|
+
normalizedAngle = touchAngle / 360.0;
|
|
331
|
+
} else {
|
|
332
|
+
touchAngle = 0.0;
|
|
333
|
+
normalizedAngle = 0.0;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function isSliderPressed(x, y) {
|
|
338
|
+
return x >= sliderX &&
|
|
339
|
+
x <= sliderX + sliderWidth &&
|
|
340
|
+
y >= sliderY - 20 &&
|
|
341
|
+
y <= sliderY + sliderHeight + 20 &&
|
|
342
|
+
y < dividerY;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function updateSliderValueFromTouch(x) {
|
|
346
|
+
let newValue = map(x, sliderX, sliderX + sliderWidth, sliderMin, sliderMax);
|
|
347
|
+
newValue = constrain(newValue, sliderMin, sliderMax);
|
|
348
|
+
sliderValue = newValue;
|
|
349
|
+
|
|
350
|
+
console.log('Slider angle:', (sliderValue * 360).toFixed(1) + '°');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Handle touch interaction for mobile
|
|
354
|
+
function touchStarted() {
|
|
355
|
+
// Handle slider interaction only for touches in top section
|
|
356
|
+
if (touches && touches.length >= 1) {
|
|
357
|
+
for (let touch of touches) {
|
|
358
|
+
if (touch && touch.y < dividerY && isSliderPressed(touch.x, touch.y)) {
|
|
359
|
+
isDraggingSlider = true;
|
|
360
|
+
updateSliderValueFromTouch(touch.x);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return false; // Always prevent default
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function touchMoved() {
|
|
369
|
+
// Update slider if dragging and touch is in top section
|
|
370
|
+
if (isDraggingSlider && touches && touches.length >= 1) {
|
|
371
|
+
for (let touch of touches) {
|
|
372
|
+
if (touch && touch.y < dividerY) {
|
|
373
|
+
updateSliderValueFromTouch(touch.x);
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return false; // Always prevent default
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function touchEnded() {
|
|
382
|
+
// Check if any touches remain in slider area
|
|
383
|
+
if (isDraggingSlider) {
|
|
384
|
+
let sliderTouchExists = false;
|
|
385
|
+
if (touches && touches.length > 0) {
|
|
386
|
+
for (let touch of touches) {
|
|
387
|
+
if (touch && touch.y < dividerY && isSliderPressed(touch.x, touch.y)) {
|
|
388
|
+
sliderTouchExists = true;
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (!sliderTouchExists) {
|
|
394
|
+
isDraggingSlider = false;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return false; // Always prevent default
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Handle mouse interaction for desktop testing
|
|
401
|
+
function mousePressed() {
|
|
402
|
+
if (isSliderPressed(mouseX, mouseY)) {
|
|
403
|
+
isDraggingSlider = true;
|
|
404
|
+
updateSliderValueFromTouch(mouseX);
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function mouseDragged() {
|
|
410
|
+
if (isDraggingSlider) {
|
|
411
|
+
updateSliderValueFromTouch(mouseX);
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function mouseReleased() {
|
|
417
|
+
isDraggingSlider = false;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function updateLayout() {
|
|
421
|
+
topSectionHeight = height * 0.5;
|
|
422
|
+
bottomSectionHeight = height * 0.5;
|
|
423
|
+
dividerY = height * 0.5;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function windowResized() {
|
|
427
|
+
resizeCanvas(windowWidth, windowHeight);
|
|
428
|
+
updateLayout();
|
|
429
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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, user-scalable=no">
|
|
6
|
+
<title>Slider vs Touch Distance - Mobile p5 Permissions</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
canvas {
|
|
16
|
+
display: block;
|
|
17
|
+
}
|
|
18
|
+
</style>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/mobile-p5-permissions@1.4.4/dist/p5.mobile-permissions.min.js"></script>
|
|
23
|
+
<script src="sketch.js"></script>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|