xxxxxxxxxx
218
// this code is adapted from Pippin Barr's example
// Mirror video?
var mirror = true;
var videoElement = document.getElementById(`webcam`);
function onResults(results) {
if (results.multiHandLandmarks && mirror) {
results.multiHandLandmarks.forEach((h) => {
h.forEach((pt) => {
pt.x = pt.x * -1 + 1;
});
});
}
if (results.multiHandedness && mirror) {
results.multiHandedness.forEach((h) => {
h.label = h.label === "Left" ? "Right" : "Left";
});
}
handResults = results;
}
// Camera parameters
var camera = new Camera(videoElement, {
onFrame: async () => {
await hands.send({
image: videoElement,
});
},
facingMode: "environment",
width: 720,
height: 480,
});
// Hand tracking parameters
var handOptions = {
maxNumHands: 40, // change max number of detected hands here
modelComplexity: 1,
minDetectionConfidence: 0.5, // hand detection confidence threshold
minTrackingConfidence: 0.5,
};
var hands = new Hands({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`;
},
});
hands.setOptions(handOptions);
hands.onResults(onResults);
camera.start();
var handResults = undefined;
var webcam = undefined;
function displayHands(results) {
if (!results) return;
if (results.multiHandLandmarks) {
for (var l = 0; l < results.multiHandLandmarks.length; l++) {
var colorCode = color(`hsb(${(200 + 70 * l) % 360}, 100%, 100%)`);
h = results.multiHandLandmarks[l];
var minX = Math.min(h.map((p) => p.x)) * width;
var maxX = Math.max(h.map((p) => p.x)) * width;
var minY = Math.min(h.map((p) => p.y)) * height;
var maxY = Math.max(h.map((p) => p.y)) * height;
noFill();
stroke(colorCode);
strokeWeight(1);
rect(minX, minY, maxX - minX, maxY - minY);
for (var j = 0; j < 5; j++) {
for (var i = 1; i < 4; i++) {
var index = j * 4 + i;
var pointA = remapHandPoint(h[index]);
var pointB = remapHandPoint(h[index + 1]);
line(pointA.x, pointA.y, pointB.x, pointB.y);
}
}
line(
remapHandPoint(h[0]).x,
remapHandPoint(h[0]).y,
remapHandPoint(h[1]).x,
remapHandPoint(h[1]).y
);
beginShape();
vertex(remapHandPoint(h[0]).x, remapHandPoint(h[0]).y);
vertex(remapHandPoint(h[5]).x, remapHandPoint(h[5]).y);
vertex(remapHandPoint(h[9]).x, remapHandPoint(h[9]).y);
vertex(remapHandPoint(h[13]).x, remapHandPoint(h[13]).y);
vertex(remapHandPoint(h[17]).x, remapHandPoint(h[17]).y);
endShape(CLOSE);
noStroke();
fill(colorCode);
for (var i = 0; i < h.length; i++) {
var point = remapHandPoint(h[i]);
circle(point.x, point.y, 5);
}
text(results.multiHandedness[l].label, minX, minY);
}
}
}
// maps points to the width and height of the canvas as a p5.Vector object
function remapHandPoint(pt) {
if (pt.x && pt.y) {
return createVector(pt.x * width, pt.y * height);
}
return undefined;
}
function remapAllHandLandmarks(hr) {
if (hr) {
if (hr.multiHandLandmarks.length > 0) {
return hr.multiHandLandmarks.map((l) =>
l.map((h) => ({
pt: remapHandPoint(h),
x: h.x * width,
y: h.y * height,
z: h.z,
}))
);
}
return [];
}
return undefined;
}
/////////////////////////
//
// p5.js portion
//
/////////////////////////
let objects = [];
function setup() {
createCanvas(640, 360);
webcam = select("#webcam");
objects.push(new Draggable(100, 100, 100));
objects.push(new Draggable(200, 200, 100));
objects.push(new Draggable(300, 100, 100));
}
function draw() {
background(0);
var remappedHandLandmarks = remapAllHandLandmarks(handResults);
push();
if (mirror) {
translate(width, 0);
scale(-1, 1);
}
image(webcam, 0, 0, width, height);
pop();
displayHands(handResults);
for (let i = 0; i < objects.length; i++) {
objects[i].update(remappedHandLandmarks, 20, 0.5);
objects[i].display("rgba(255, 100, 100, 0.5)", "rgba(100, 255, 100, 0.5)");
}
}
class Draggable {
constructor(_x, _y, _size) {
this.position = createVector(_x, _y);
this.size = _size;
this.dragging = false;
}
update(handsData, pinch, speed) {
// remappedHandLandmarks, pinch threshold, speed [0, 1]
if (handsData) {
let dragged = false;
this.dragging = false;
handsData.forEach((h) => {
if (!dragged) {
const p1 = h[4]; // thumb
const p2 = h[8]; // index finger
if (p5.Vector.dist(p1.pt, p2.pt) < pinch) {
const d = p5.Vector.dist(p1.pt, this.position);
if (d < this.size * 0.5) {
dragged = true;
this.dragging = true;
let delta = p5.Vector.sub(p1.pt, this.position);
this.position.add(delta.mult(speed));
}
}
}
});
}
}
display(_fill, _fillDragged) {
noStroke();
fill(_fill);
if (this.dragging) {
fill(_fillDragged);
}
circle(this.position.x, this.position.y, this.size);
}
}