xxxxxxxxxx
254
// 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: 1280,
height: 720,
});
// Hand tracking parameters
var handOptions = {
maxNumHands: 4, // 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;
}
// HandButton Object
class HandButton {
constructor(_name, _x, _y, _d, _time, _trigger) {
this.name = _name;
this.x = _x;
this.y = _y;
this.d = _d;
this.time = _time;
this.timestamp = 0;
this.active = false;
this.triggered = false;
this.trigger = _trigger;
}
update(results) {
var check = false;
if (results) {
if (results.multiHandLandmarks) {
for (var hand of results.multiHandLandmarks) {
for (var i = 0; i < hand.length; i++) {
var p = remapHandPoint(hand[i]);
var tx = p.x;
var ty = p.y;
if (dist(this.x, this.y, tx, ty) < this.d / 2) {
check = true;
if (!this.active) {
this.active = true;
this.timestamp = millis();
} else {
if (millis() - this.timestamp > this.time && !this.triggered) {
this.triggered = true;
return true;
}
}
}
}
}
}
}
if (!check) {
this.active = false;
this.triggered = false;
}
}
reset() {
this.active = false;
this.triggered = false;
}
display(_textColor, _fill, _stroke, _weight) {
fill(_fill);
noStroke();
circle(this.x, this.y, this.d);
stroke(_stroke);
strokeWeight(_weight);
noFill();
var ratio = 0;
if (this.active) {
ratio = (millis() - this.timestamp) / this.time;
ratio = constrain(ratio, 0, 1);
}
arc(this.x, this.y, this.d, this.d, 0, TWO_PI * ratio);
noStroke();
fill(_textColor);
textAlign(CENTER, CENTER);
text(this.name, this.x, this.y);
}
}
/////////////////////////
//
// p5.js portion
//
/////////////////////////
var button = new HandButton("hit me", 200, 200, 100, 1000);
function setup() {
createCanvas(1000, 600);
webcam = select("#webcam");
}
function draw() {
background(0);
push();
if (mirror) {
translate(width, 0);
scale(-1, 1);
}
image(webcam, 0, 0, width, height);
pop();
var remappedHandLandmarks = remapAllHandLandmarks(handResults);
displayHands(handResults);
if (button.update(handResults)) {
console.log("button triggered");
button.x = random(200, width - 200);
button.y = random(200, height - 200);
button.reset();
}
button.display("white", "gray", "lightgray", 10);
}