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);
}
}
Related
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.
I am learning user input through mouse in p5.js and i want to create a square by 4 line method on 4 different clicks , 1 click for each line and the last click completing the square.
below code is for 2 lines but the are both running at the same time and i cannot understand the if command to separately run them .
function setup() {
createCanvas(400, 400);
background(220);
}
function draw() {
}
function mousePressed()
{
line(width/20,height/40,mouseX,mouseY);
line(pmouseX,pmouseY,mouseX,mouseY);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>
You've to store the points to a list. If the list contains 4 elements and the mouse is clicked again, clear the list:
var pts = [];
function mousePressed()
{
if (pts.length == 4) {
pts = [];
}
pts.push([mouseX, mouseY])
}
Do all the drawing continuously in draw(). Clear the background. Draw the liens between the points ins a loop. If the number of point is 4, the draw a line from the last point to the 1st point.
Additionally you can draw a "rubber" line from the last point to the current mouse position, if there is at least 1 point in the list:
function draw() {
background(220);
// draw the lines between the points
for (var i=0; i < pts.length-1; ++i) {
line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1]);
}
var close = pts.length == 4;
if (close) {
// draw line from 1st point to at point
line(pts[pts.length-1][0], pts[pts.length-1][1], pts[0][0], pts[0][1]);
}
else if (pts.length > 0) {
// draw a rubber line from last point to the mouse
line(pts[pts.length-1][0], pts[pts.length-1][1], mouseX,mouseY);
}
}
See the example
function setup() {
createCanvas(400, 400);
}
var pts = [];
function mousePressed()
{
if (pts.length == 4) {
pts = [];
}
pts.push([mouseX, mouseY])
}
function draw() {
background(220);
// draw the lines between the points
for (var i=0; i < pts.length-1; ++i) {
line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1]);
}
var close = pts.length == 4;
if (close) {
// draw line from 1st point to at point
line(pts[pts.length-1][0], pts[pts.length-1][1], pts[0][0], pts[0][1]);
}
else if (pts.length > 0) {
// draw a rubber line from last point to the mouse
line(pts[pts.length-1][0], pts[pts.length-1][1], mouseX,mouseY);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>
So I am trying to make a simple space game. You will have a ship that moves left and right, and Asteroids will be generated above the top of the canvas at random X position and size and they will move down towards the ship.
How can I create Asteroid objects in seperate positions? Like having more than one existing in the canvas at once, without creating them as totally seperate objects with seperate variables?
This sets the variables I would like the asteroid to be created on.
var asteroids = {
size: Math.floor((Math.random() * 40) + 15),
startY: 100,
startX: Math.floor((Math.random() * canvas.width-200) + 200),
speed: 1
}
This is what I used to draw the asteroid. (It makes a hexagon shape with random size at a random x coordinate)
function drawasteroid() {
this.x = asteroids.startX;
this.y = 100;
this.size = asteroids.size;
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
ctx.moveTo(this.x,this.y-this.size*0.5);
ctx.lineTo(this.x+this.size*0.9,this.y);
ctx.lineTo(this.x+this.size*0.9,this.y+this.size*1);
ctx.lineTo(this.x,this.y+this.size*1.5);
ctx.lineTo(this.x-this.size*0.9,this.y+this.size*1);
ctx.lineTo(this.x-this.size*0.9,this.y);
ctx.fill();
}
I included ALL of my code in this snippet. Upon running it, you will see that I currently have a ship that moves and the asteroid is drawn at a random size and random x coordinate. I just need to know about how to go about making the asteroid move down while creating other new asteroids that will also move down.
Thank You for all your help! I am new to javascript.
// JavaScript Document
////// Variables //////
var canvas = {width:300, height:500, fps:30};
var score = 0;
var player = {
x:canvas.width/2,
y:canvas.height-100,
defaultSpeed: 5,
speed: 10
};
var asteroids = {
size: Math.floor((Math.random() * 40) + 15),
startY: 100,
startX: Math.floor((Math.random() * canvas.width-200) + 200),
speed: 1
}
var left = false;
var right = false;
////// Arrow keys //////
function onkeydown(e) {
if(e.keyCode === 37) {
left = true;
}
if(e.keyCode === 39) {
right = true;
}
}
function onkeyup(e) {
if (e.keyCode === 37) {
left = false;
}
if(e.keyCode === 39) {
right = false;
}
}
////// other functions //////
//function to clear canvas
function clearCanvas() {
ctx.clearRect(0,0,canvas.width,canvas.height);
}
// draw the score in the upper left corner
function drawscore(score) {
var score = 0;
ctx.fillStyle = "#FFFFFF";
ctx.fillText(score,50,50);
}
// Draw Player ship.
function ship(x,y) {
var x = player.x;
var y = player.y;
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
ctx.moveTo(x,y);
ctx.lineTo(x+15,y+50);
ctx.lineTo(x-15,y+50);
ctx.fill();
}
// move player ship.
function moveShip() {
document.onkeydown = onkeydown;
document.onkeyup = onkeyup;
if (left === true && player.x > 50) {
player.x -= player.speed;
}
if (right === true && player.x < canvas.width - 50) {
player.x += player.speed;
}
}
// Draw Asteroid
function drawasteroid() {
this.x = asteroids.startX;
this.y = 100;
this.size = asteroids.size;
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
ctx.moveTo(this.x,this.y-this.size*0.5);
ctx.lineTo(this.x+this.size*0.9,this.y);
ctx.lineTo(this.x+this.size*0.9,this.y+this.size*1);
ctx.lineTo(this.x,this.y+this.size*1.5);
ctx.lineTo(this.x-this.size*0.9,this.y+this.size*1);
ctx.lineTo(this.x-this.size*0.9,this.y);
ctx.fill();
}
// move Asteroid
function moveAsteroid() {
//don't know how I should go about this.
}
// update
setInterval (update, 1000/canvas.fps);
function update() {
// test collisions and key inputs
moveShip();
// redraw the next frame of the animation
clearCanvas();
drawasteroid();
drawscore();
ship();
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>My Game</title>
<script src="game-functions.js"></script>
<!--
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
-->
</head>
<body>
<canvas id="ctx" width="300" height="500" style="border: thin solid black; background-color: black;"></canvas>
<br>
<script>
////// Canvas setup //////
var ctx = document.getElementById("ctx").getContext("2d");
</script>
</body>
</html>
You want to make the creation of asteroids dynamic...so why not set up a setInterval that gets called at random intervals as below. You don't need a separate declaration for each Asteroids object you create. You can just declare a temporary one in a setInterval function. This will instantiate multiple different objects with the same declaration. Of course you need to store each object somewhere which is precisely what the array is for.
You also have to make sure that asteroids get removed from the array whenever the moveAsteroid function is called if they are off of the canvas. The setInterval function below should be called on window load and exists alongside your main rendering setInterval function.
You are also going to have to change your moveAsteroid function a bit to be able to point to a specific Asteroids object from the array. You can do this by adding the Asteroids object as a parameter of the function or by making the function a property of the Asteroids class and using this. I did the latter in the example below.
var astArray = [];
var manageAsteroidFrequency = 2000;
var Asteroids {
X: //something
Y://something
speed:1
move: function() {
this.X -= speed;
}
}
var mainRenderingFunction = setInterval( function() {
for (var i = astArray.length-1 ; i > -1; i --){
if(astArray[i].Y < 0){
astArray.splice(i, 1)
}else{
astArray[i].move;
}
}
}, 40);
var manageAsteroids = setInterval( function () {
if (astArray.length < 4){
var tmpAst = new Asteroids();
astArray.push(tmpAst);
}
manageAsteroidFrequency = Math.floor(Math.random()*10000);
}, manageAsteroidFrequency);
I've got this code. What I want the code to do is to make the ball move and when the ball goes over a grey spot (holes) it goes back to the starting point. I've done that by creating a random place for the grey holes. I simply need to find a way to define the position of these holes even though they are randomized.
var startPoint = new Path.Circle(new Point(40, 40), 40);
startPoint.fillColor = "green";
//finishPoint
var finishPoint = new Path.Circle(new Point(1300, 600), 40);
finishPoint.fillColor = "red";
var ball = new Path.Circle(new Point(40, 40), 20);
ball.fillColor = "black";
//holes
var path = new Path(new Point(20, 20), new Point(20, 23));
path.style = {
strokeColor: 'grey',
strokeWidth: 70,
strokeCap: 'round'
};
var holes = new Symbol(path);
for (var i = 0; i < 10; i++) {
var placement = view.size * Point.random();
var placed = holes.place(placement);
}
var vector = new Point(0, 0);
function onFrame(event) {
ball.position += vector / 100;
}
var moves = new Point(100, 1);
function onKeyDown(event) {
if (event.key === "s") {
vector.y += 10;
}
if (event.key === "d") {
vector.x += 10;
}
if (event.key === "a") {
vector.x -= 10;
}
if (event.key === "w") {
vector.y -= 10;
}
var ballPlace = ball.position;
if (ballPlace.isClose(finishPoint.position, 40) == true) {
var text = new PointText(view.center);
text.content = 'Congratulations';
text.style = {
fontFamily: 'Courier New',
fontWeight: 'bold',
fontSize: 100,
fillColor: 'gold',
justification: 'center'
};
ball.remove();
}
if(ballPlace.isClose(placement.position, 40) == true) {
ball = new Point(40, 40);
}
};
and I want the ball to go back to Point(40, 40) when it goes over a grey hole (var holes) but I can't get it to work. Any idea how to fix this?
You want to test the ball's position against the holes to see if the ball goes back to the starting position. The simplest way I can think of to do this is to create a group of the holes then test the position of the ball against that group. In the following code the ball's position is simulated via the onMouseMove function and the holes are flashed red to indicate when the ball would be returned to the the starting position.
var holes = [];
var hole;
for (var i = 0; i < 10; i++) {
hole = new Path.Circle(view.size * Point.random(), 10);
hole.fillColor = 'grey';
holes.push(hole);
}
holes = new Group(holes);
onMouseMove = function(e) {
if (holes.hitTest(e.point)) {
holes.fillColor = 'red';
} else {
holes.fillColor = 'grey';
}
Here's an implementation: sketch. It should be straightforward to replaced onMouseMove with onFrame, move the ball as you currently do, and then test to see if it falls into a hole.
In order to test if the ball is over a hole you can remove on the onMouseMove function and replace it with:
onFrame = function(e) {
ball.position += vector / 100;
if (holes.hitTest(ball.position)) {
// move the ball wherever you want to move it, position text,
// etc. you might have to loop through the array to find which
// hole was hit.
}
}
#Luke Park is right about using an array.
Trial each new point, by ensuring it is a distance from all other existing points. Example below (not scaled to view.size).
p = Point.random();
while ( isTooClose(p, points) ) {
p = Point.random();
}
It's possible for this to loop infinitely, but if you're populating the area sparsely, there should be no problem.
isTooClose tests each point in array p, where distance = sqrt(dxdx + dydy). If you have many points, you can optimise by avoiding sqrt(), by testing whether the raw dx and dy values are smaller than the test radius.
You can also use a similar function on each frame, to test for collision.
I want to create a mobile web page where a shape appears on the screen, the user can only traces over the outline of the shape with his/her finger and then a new shape will appear. This library has a few good examples of what I am looking to do, just with more shapes. I have already found a couple of good examples for drawing on the canvas on a touch device here and here. The thing I don't know is how to constrain the line so you are only drawing on the path with a single continuous line. Is there something built in that will let me specify the only path you can draw, or do I have to write that logic by hand?
We can split the issue into two parts :
1) knowing if the user is on the path.
2) knowing if the user went on all path parts.
For 1), we can use the isPointInPath context2D method to know if the mouse/touch point (x,y) is on the curve. The constraint here is that you must build a closed surface, meaning a surface drawn by a fill(), not one built with a stroke(). So in case you are stroking thick lines, you have to do some little math to build the corresponding figures out of moveTo+lineTo+fill.
For 2) : build a list of 'check-points' for your shape. You might have, for instance 8 control points for a circle. Then decide of a distance at which the user will 'activate' the check point. Now the algorithm is, in pseudo-code:
mouseDown => check()
mouseMove => if mouse is down, check()
checkPointList = [ [ 10, 40, false ] , [ centerX, centerY, isChecked], ... ] ;
checked = 0;
function check() {
clear screen
draw the path
if (mouse down and mouse point on path) {
for ( checkPoint in CheckPointList) {
if (checkPoint near enough of mouse) {
checkPoint[3]=true;
checked++;
}
}
if (checked == checkPointList.length) ==>>> User DID draw all the shape.
} else
clear the flags of the checkPointList;
checked=0;
}
I did a moooost simple demo here, which quites work.
The control points are shown in red when disactivated, green when activated :
http://jsbin.com/wekaxiguwiyo/1/edit?js,output
// boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
function draw() {
ctx.clearRect(0,0,300,300);
drawShape();
drawCP();
}
// Shape
function drawShape() {
ctx.beginPath();
ctx.moveTo(30,5);
ctx.lineTo(80,5);
ctx.lineTo(80, 300);
ctx.lineTo(30,300);
ctx.closePath();
ctx.lineWidth= 16;
ctx.fillStyle='#000';
ctx.fill();
}
// Control points
var points = [ [50, 50, false], [50,120, false], [50, 190, false],[50,260, false ] ];
var pointsCount = 0;
function drawCP() {
for (var i=0; i<points.length; i++) {
var p = points[i];
ctx.fillStyle=p[2]?'#0F0':'#F00';
ctx.fillRect(p[0]-1, p[1]-1, 2, 2);
}
}
function resetCP() {
for (var i=0; i<points.length; i++) {
points[i][2]=false;
}
pointsCount=0;
}
function testCP(x,y) {
var d=30;
d=sq(d);
for (var i=0; i<points.length; i++) {
if (sq(points[i][0]-x)+sq(points[i][1]-y)<d) {
if (!points[i][2]) pointsCount++;
points[i][2]=true
};
}
}
function sq(x) { return x*x; }
//
draw();
// most simple event handling
addEventListener('mousemove', mouseMove);
var r = cv.getBoundingClientRect();
function mouseMove(e) {
var x = e.pageX-r.left;
var y = e.pageY-r.top;
draw();
ctx.fillStyle='#000';
if (ctx.isPointInPath(x,y)) {
ctx.fillStyle='#F00';
testCP(x,y);
} else {
resetCP();
}
ctx.fillRect(x-3,y-3,6,6);
var pathDrawn = (pointsCount == points.length);
if (pathDrawn) ctx.fillText('Shape drawn!!', 150, 150);
}