How can I number a point list in p5.js - javascript

is it possible to number a list of points in p5.js?
Right now I am using ml5.pj for face mesh detections, which outputs x and y coordinates for a set of 465 points.
I want to select a few. In order to do that, I need to know what are the corresponding indexes.
Any possible way to do this?
Not relevant, but on Grasshopper 3D, it is a component called "point list"
let facemesh;
let video;
let predictions = [];
function setup() {
createCanvas(640, 480);
video = createCapture(VIDEO);
video.size(width, height);
facemesh = ml5.facemesh(video, modelReady);
// This sets up an event that fills the global variable "predictions"
// with an array every time new predictions are made
facemesh.on("predict", results => {
predictions = results;
});
// Hide the video element, and just show the canvas
video.hide();
}
function modelReady() {
console.log("Model ready!");
}
function draw() {
// image(video, 0, 0, width, height);
background(255);
// We can call both functions to draw all keypoints
drawKeypoints();
}
// A function to draw ellipses over the detected keypoints
function drawKeypoints() {
for (let i = 0; i < predictions.length; i += 1) {
const keypoints = predictions[i].scaledMesh;
// Draw facial keypoints.
for (let j = 0; j < keypoints.length; j += 1) {
const [x, y] = keypoints[j];
fill(0, 255, 0);
ellipse(x, y, 3, 3);
}
}
}

Okay, if I understand you correctly you want to add labeling to each point. You could get sophisticated with this and have it on hover via tracking the cursor coordinates and using those as a key to access an object val. However, since you say you are not well rounded in programming -- I'm going to keep this super simple here...
We are just going to add text to where the point is and have it offset vertically by 5px. You can read more about text here in the p5.js documentation: https://p5js.org/reference/#/p5/text
Here's a link on template literals in js: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
// A function to draw ellipses over the detected keypoints
function drawKeypoints() {
for (let i = 0; i < predictions.length; i += 1) {
const keypoints = predictions[i].scaledMesh;
// Draw facial keypoints.
for (let j = 0; j < keypoints.length; j += 1) {
const [x, y] = keypoints[j];
fill(0, 255, 0);
ellipse(x, y, 3, 3);
text(`${i}-${j}`, x, y+5); // Draw Text with Index Labelling
}
}
}
Advanced: Showing the text on hover.
Create an Object to show the values based on x-y:i-j key:vals
Detect Mouse X, Y Coordinates
Display on Hover
const hoverCoords = {}
function draw() {
background(255);
drawKeypoints();
hoverCoords[`${mouseX}-${mouseY}`] && text(hoverCoords[`${mouseX}-${mouseY}`], x, y+5)
}
// A function to draw ellipses over the detected keypoints
function drawKeypoints() {
for (let i = 0; i < predictions.length; i += 1) {
const keypoints = predictions[i].scaledMesh;
// Draw facial keypoints.
for (let j = 0; j < keypoints.length; j += 1) {
const [x, y] = keypoints[j];
hoverCoords[`${x}-${y}`] = `${i}-${j}` // Create object key val
fill(0, 255, 0);
ellipse(x, y, 3, 3);
}
}
}
I haven't tested the above but you know should be the right approach using an object and setting coordinates as key vals and then being able to do a truthy match on that to display the i-j vals. Look into objects in javascript.

Related

No collision between circles and drawn lines in matter.js

I am having trouble creating a line in p5/matter.js. My sketch is available on the p5 editor. On mousePressed and mouseDragged, the code grabs the mouse position every ten moves and uses curveVertex to draw a line between the current and last point. All of these points are stored in an array. This draws on the canvas perfectly but cannot interact with other objects.
function mouseDragged(){
if (pointCount == 0) {
points.push({x: mouseX, y: mouseY});
pointCount += 1;
} else if (pointCount == 10) {
pointCount = 0;
} else {
pointCount += 1;
}
}
function mousePressed(){
points.push({x: mouseX, y: mouseY});
}
function mouseReleased(){
line = new Line(points);
console.log(points);
}
function draw() {
background("#efefef");
circles.push(new Circle(200, 50, random(5, 10)));
Engine.update(engine);
for (let i = 0; i < circles.length; i++) {
circles[i].show();
if (circles[i].isOffScreen()) {
circles[i].removeFromWorld();
circles.splice(i, 1);
i--;
}
}
// for (let i = 0; i < boundaries.length; i++) {
// boundaries[i].show();
// // console.log(boundaries[i].body.isStatic)
// }
if (points.length > 0) {
// Loop through creating line segments
beginShape();
noFill();
// Add the first point
stroke('black');
strokeWeight(5);
curveVertex(points[0].x,points[0].y)
curveVertex(points[0].x,points[0].y)
// Draw line
points.forEach(function(p){
curveVertex(p.x,p.y);
})
vertex(points[points.length-1].x,points[points.length-1].y) // Duplicate ending point
endShape()
}
// Draw points for visualization
stroke('#ff9900')
strokeWeight(10)
// points.push({x: x, y: y})
points.forEach(function(p){
point(p.x, p.y)
})
}
I tried creating a class and passed the points array to it, thinking that the matter.vertices function would take the points and make the needed body for the falling balls to bounce off. The code does not throw an error, but no collision occurs with the line. The example provided in matter.js document for the vertices function is unavailable and I have been unable to find any examples online. Hoping someone can point me in the right direction to get the created line to interact with the falling balls.
class Line {
constructor(vertices) {
let options = {
friction:0,
restitution: 0.95,
// angle: a,
isStatic: true
}
this.body = Matter.Body.create(options);
this.v = Matter.Vertices.create(vertices, this.body)
World.add(world, this.body);
}
}

Is it possible to hide the video in ml5.js but keep the hand pose points?

I am trying to hide the video (webcam in this case) but keep the detected points of the hand. I have tried different methods, but I am unsuccessful so far. I appreciate the help.
Main code (below) from ml5.js can be found and tested live here: https://editor.p5js.org/ml5/sketches/Handpose_Webcam
let handpose;
let video;
let predictions = [];
function setup() {
createCanvas(640, 480);
video = createCapture(VIDEO);
video.size(width, height);
handpose = ml5.handpose(video, modelReady);
// This sets up an event that fills the global variable "predictions"
// with an array every time new hand poses are detected
handpose.on("predict", results => {
predictions = results;
});
// Hide the video element, and just show the canvas
video.hide();
}
function modelReady() {
console.log("Model ready!");
}
function draw() {
image(video, 0, 0, width, height);
// We can call both functions to draw all keypoints and the skeletons
drawKeypoints();
}
// A function to draw ellipses over the detected keypoints
function drawKeypoints() {
for (let i = 0; i < predictions.length; i += 1) {
const prediction = predictions[i];
for (let j = 0; j < prediction.landmarks.length; j += 1) {
const keypoint = prediction.landmarks[j];
fill(0, 255, 0);
noStroke();
ellipse(keypoint[0], keypoint[1], 10, 10);
}
}
}
Yes, just stop drawing it to the canvas, and add a call to background to fill the canvas.
function draw() {
// image(video, 0, 0, width, height); // <- just delete this line
background(255);
// We can call both functions to draw all keypoints and the skeletons
drawKeypoints();
}
https://editor.p5js.org/Samathingamajig/sketches/BV_kqU0Ik

How do I create a random number that remains constant and doesn't change in Javascript?

I'm trying to have a few circles drawn on the screen that do not move after initialization. Right now it is constantly drawing them to the screen instead of keeping them there. Here's the code:
for (let i = 0; i < 1; i++) {
//location
const r = random(100, 900);
const r2 = random(900, 100);
//size
const rS = random(50, 250);
const rS2 = random(250, 50);
//draw the ellipse with parameters
ellipse(r, r2, rS, rS2);
}
(This is with the p5.js library)
It sounds like your code is in the draw() function, which is called multiple times a second. Since you call random() every single time, it creates new parameters every single time. Instead, you should assign parameters to a variable somewhere else (like in the setup function) and then use those in the draw function. Something like:
var ellipses = [];
function setup() {
createCanvas(640, 480);
for (let i = 0; i < 1; i++) {
ellipses.push({
r: random(100, 300),
r2: random(300, 100),
rS: random(50, 250),
rS2: random(250, 50)
});
}
}
function draw() {
clear();
//location
//draw the ellipse with parameters
ellipses.forEach(function (e) {
ellipse(e.r, e.r2, e.rS, e.rS2);
})
}
<script src="https://unpkg.com/p5#1.1.9/lib/p5.min.js"></script>

Multiple html canvas element with random graphics on same page

Trying to create multiple canvas element on a single html page and then draw different graphics on each of them . But the issue is , the same graphics is drawn on all of them without any randomization
canvas_container_div = document.getElementById('canvas_container_div');
let animation_frame_id;
let canvas_array = [];
let context_array = [];
let number_of_canvas = 5;
//creating canvas and storing it in an array
for(var i = 0;i < number_of_canvas ; i++){
var canvas1 = document.createElement('canvas');
canvas1.style.width = '200px';
canvas1.style.height = '200px';
canvas1.style.margin = '10px';
canvas1.style.border = '1px solid white';
canvas_array.push(canvas1);
}
//displaying all canvas inside the div element
for(var i = 0;i < canvas_array.length ; i++){
canvas_container_div.appendChild(canvas_array[i]);
}
//getting all the contex for all the canvas
for(var i = 0;i < canvas_array.length ; i++){
context_array.push(canvas_array[i].getContext('2d'));
}
//random values generating
let hue = Math.random()*360;
//or other radom parameters
//updating each graphics
function update(ctx){
ctx.fillStyle = 'hsl('+hue+',100%,50%)';
}
function render(){
//getting all the context
for(var i = 0;i < context_array.length ; i++){
//clearing bg for perticular canvas
context_array[i].clearRect(0,0,canvas_array[i].width,canvas_array[i].height);
//passing perticular canvas context to update method
update(context_array[i]);
//drawing with pertucular context
context_array[i].beginPath();
context_array[i].arc(canvas_array[i].width/2,canvas_array[i].height/2,40,0,Math.PI *2);
context_array[i].closePath();
context_array[i].fill();
}
animation_frame_id = requestAnimationFrame(render);
}
render();
Wanted to have different color for all the circle drawn on different canvas , but all the circles are of same color . cannot randomize
You are defining your hue outside of your renders for loop, so each time it runs the loop and calls on the hue, it is getting the same color for the fill style. You could create a function that randomizes the hue inside a for loop to to create an array of colors that is the same length as your canvas array.
Then in your for loop within the render function call on the array and its index to make each individual circle a different color fill style.
NOTE: This only randomizes the colors, it does not check if a color already exists, so you may want additional code to check if a number is already in the array before pushing the value into the array. Furthermore, you will notice some hues randomize within a certain number close enough to each other that they actually look like they are the same, you could also include code within the setHue() function that checks to see if the the numbers are within a certain restraint of each other, this would likely be a .include() or even a conditional that checks the difference between the hue array and the current value within the loop.
let hue = [];
function setHue() {
for (let h = 0; h < context_array.length; h++) {
hue.push(Math.trunc(Math.random() * 360));
}
}
setHue();
canvas_container_div = document.getElementById('canvas_container_div');
let animation_frame_id;
let canvas_array = [];
let context_array = [];
let number_of_canvas = 5;
//creating canvas and storing it in an array
for (var i = 0; i < number_of_canvas; i++) {
var canvas1 = document.createElement('canvas');
canvas1.style.width = '200px';
canvas1.style.height = '200px';
canvas1.style.margin = '10px';
canvas1.style.border = '1px solid white';
canvas_array.push(canvas1);
}
//displaying all canvas inside the div element
for (var i = 0; i < canvas_array.length; i++) {
canvas_container_div.appendChild(canvas_array[i]);
}
//getting all the contex for all the canvas
for (var i = 0; i < canvas_array.length; i++) {
context_array.push(canvas_array[i].getContext('2d'));
}
//Randomize your hue value and make an array to hold the value
//Then in your for loop within the render function call on the
//array and its index to make each individual circle a different color fill style
let hue = [];
function setHue() {
for (let h = 0; h < context_array.length; h++) {
let color = Math.trunc(Math.random() * 360);
hue.push(color);
}
console.log(hue);
}
setHue();
function render() {
//getting all the context
for (var i = 0; i < context_array.length; i++) {
//clearing bg for perticular canvas
context_array[i].clearRect(0, 0, canvas_array[i].width, canvas_array[i].height);
//drawing with particular context
context_array[i].beginPath();
context_array[i].arc(canvas_array[i].width / 2, canvas_array[i].height / 2, 40, 0, Math.PI * 2);
context_array[i].closePath();
context_array[i].fill();
context_array[i].fillStyle = 'hsl(' + hue[i] + ',100%,50%)';
}
animation_frame_id = requestAnimationFrame(render);
}
render();
<div id="canvas_container_div"></div>

Animating SVG polygons

I wrote a code to drawing polygons:
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', window.innerHeight);
document.querySelector('#bg').appendChild(svg);
for(var x = 0; x < polygons.length; x++){
var polygon = document.createElementNS(svg.namespaceURI, 'polygon');
polygon.setAttribute('points', polygons[0 + x]);
polygon.setAttribute('fill', polygons[0 + x][1]);
svg.appendChild(polygon);
}
My full code with polygon points:
http://codepen.io/anon/pen/WrqrbB
I would like to animate this polygons similar to this animation:
http://codepen.io/zessx/pen/ZGBMXZ
How to animate my polygons?
You can
call an animation function to manipulate your coordinate values as desired,
convert them to a string, e.g. using .join(),
send the resulting string back to the polygon as its points attribute value, redrawing the shape (as you were already doing when you initially created your shapes), and
have the animation function, when it is finished, call itself again at a reasonable built-in time-delay using requestAnimationFrame.
The following snippet gives a basic idea of what can be done.
(Note that I've redefined the array polygons in my example so that it is different from what you had, but that was done for the sake of simplicity in this example.)
var svg = document.getElementsByTagName("svg")[0];
var polygons = [], numSteps = 100, stepNum = 0;
var coords = [
[40, 20, 80, 20, 80, 60, 40, 60],
[140, 20, 180, 20, 160, 50]
];
for (var x = 0; x < coords.length; x++) {
polygons[x] = document.createElementNS(svg.namespaceURI, 'polygon');
polygons[x].setAttribute('points', coords[x].join());
svg.appendChild(polygons[x]);
}
function anim() {
for (var x = 0; x < coords.length; x++) {
coords[x] = coords[x].map(function(coord) {
return coord + 4 * (Math.random() - 0.5);
});
polygons[x].setAttribute('points', coords[x].join());
stepNum += 1;
}
if (stepNum < numSteps) requestAnimationFrame(anim);
}
anim();
<svg></svg>
UPDATE The above snippet shows generally how to animate a polygon. In your case, however, there is a further issue. On your codepen demo, it is clear that you have hard-coded the point coordinates for each polygon separately. Thus, if you want to move one point, you're going to have to update coordinates in at least 2 if not more places, for every polygon that touches that point.
A better approach would be to create a separate array of all points and then define each polygon with respect to that array. (This is similar to how things are sometimes done in 3D graphics, e.g. WebGL.) The following code snippet demonstrates this approach.
var svg = document.getElementsByTagName("svg")[0];
var polyElems = [], numSteps = 100, stepNum = 0;
var pts = [[120,20], [160,20], [200,20], [240,20], [100,50], [140,50], [180,50], [220,50], [260,50], [120,80], [160,80], [200,80], [240,80]];
var polyPts = [[0,1,5], [1,2,6], [2,3,7], [0,4,5], [1,5,6], [2,6,7], [3,7,8], [4,5,9], [5,6,10], [6,7,11], [7,8,12], [5,9,10], [6,10,11], [7,11,12]];
for (var x = 0; x < polyPts.length; x++) {
polyElems[x] = document.createElementNS(svg.namespaceURI, 'polygon');
polyElems[x].setAttribute('fill', '#'+Math.floor(Math.random()*16777215).toString(16));
// random hex color routine from http://www.paulirish.com/2009/random-hex-color-code-snippets/
drawPolygon(x);
}
function anim() {
pts = pts.map(function(pt) {
return pt.map(function(coord) {
return coord + 3 * (Math.random() - 0.5); // move each point
});
});
for (var x = 0; x < polyPts.length; x++) {drawPolygon(x);}
stepNum += 1;
if (stepNum < numSteps) requestAnimationFrame(anim); // redo anim'n until all anim'n steps done
}
anim(); // start the animation
function drawPolygon(x) {
var ptNums = polyPts[x];
var currCoords = [pts[ptNums[0]], pts[ptNums[1]], pts[ptNums[2]]].join();
// creates a string of coordinates; note that [[1,2],[3,4],[5,6]].join() yields "1,2,3,4,5,6"
polyElems[x].setAttribute('points', currCoords);
svg.appendChild(polyElems[x]);
}
<svg></svg>

Categories