How to detect collisions between a moving point and the stroke of an arbitrary boundary shape? - p5.js

I want to draw a circle(for example) and create a couple of points inside that circle. The points are moving randomly, but when they hit the circle's stroke, they should either stop or go inverse or react in some way (it's not that important). I know it would be easy to do with rect-like shapes, but I want to draw inside custom shapes like stars or flower.

To find out if point inside the circle or not you just need to calculate distance from center of circle to point itself. If that distance less then radius - point inside circle.
Fun example:
let c, r;
let points = [];
function setup()
{
createCanvas(200, 200);
r = 50;
c = createVector(100, 100);
let pos = createVector(95, 110);
let v = p5.Vector.random2D();
points.push({p: pos, v: v});
}
function draw()
{
background(0);
points.forEach(point => {
point.p.add(point.v);
if(point.p.dist(c) > r) {
let n = point.p.copy().sub(c).normalize();
let d_n = point.v.dot(n);
point.v = p5.Vector.sub(n.mult(2).mult(d_n), point.v).mult(-1);
//pv.mult(-1);
}
circle(point.p.x,point.p.y,5);
});
noFill();
stroke(255);
circle(c.x,c.y, r*2);
}
function mouseClicked() {
let p = createVector(mouseX, mouseY);
if (p.dist(c) < r) {
points.push({
p: p,
v: p5.Vector.random2D(),
});
}
// prevent default
return false;
}
<script src="https://github.com/processing/p5.js/releases/download/v1.4.0/p5.min.js"></script>
click inside circle
Boring example:
let c, radius;
function setup()
{
createCanvas(200, 200);
c = createVector(100, 100);
radius = 50;
}
function draw()
{
background(0);
let point = createVector(mouseX, mouseY);
let distance = c.dist(point);
if (distance > radius) {
fill("red");
} else {
fill("green");
};
circle(c.x, c.y, radius * 2);
// just rendering text :)
stroke(255);
line(point.x,point.y, c.x,c.y);
stroke(0);
fill(200)
push()
translate(p5.Vector.add(c, p5.Vector.sub(point, c).div(2)));
text(distance.toFixed(2),0,0)
pop()
text("Move your mouse",20,10);
}
<script src="https://github.com/processing/p5.js/releases/download/v1.4.0/p5.min.js"></script>

Related

How can add interaction and animation to shapes drawn in Processing?

I'm trying to code a canvas full of shapes(houses) and animate them in processing.
Here's an example of shape:
void house(int x, int y) {
pushMatrix();
translate(x, y);
fill(0, 200, 0);
triangle(15, 0, 0, 15, 30, 15);
rect(0, 15, 30, 30);
rect(12, 30, 10, 15);
popMatrix();
}
By animation I mean moving them in random directions.
I would also like to add basic interaction: when hovering over a house it's colour would change.
At the moment I've managed to render a canvas full of houses:
void setup() {
size(500, 500);
background(#74F5E9);
for (int i = 30; i < 500; i = i + 100) {
for (int j = 30; j < 500; j = j + 100) {
house(i, j);
}
}
}
void house(int x, int y) {
pushMatrix();
translate(x, y);
fill(0, 200, 0);
triangle(15, 0, 0, 15, 30, 15);
rect(0, 15, 30, 30);
rect(12, 30, 10, 15);
popMatrix();
}
Without seeing source code: your attempted sketch it's very hard to tell.
They can be animated in many ways and it's unclear what you mean. For example, is that the position/rotation/scale of each square, is it the corners/vertices of each square, both ?
You might have a clear idea in your mind, but the current form of the question is ambiguous. We also don't know you're comfort level with various notions such as classes/objects/PVector/PShape/etc. If you were to 'story board' this animation what would it look like ? Breaking the problem down and explaining it in a way that anyone can understand might actually help you figure out a solution on your own as well.
Processing has plenty of examples. Here are a few I find relevant based on what my understanding is of your problem.
You can have a look at the Objects and Create Shapes examples:
File > Examples > Basics > Objects > Objects: Demonstrates grouping drawing/animation (easing, damping). You can tweak this example draw a single square and once you're happy with the look/motion you can animate multiple using an array or ArrayList
File > Examples > Topics > Create Shapes > PolygonPShapeOOP3: Great example using PShape to animate objects.
File > Examples > Topics > Create Shapes > WigglePShape: This example demonstrates how to access and modify the vertices of a PShape
For reference I'm simply copy/pasting the examples mentioned above here as well:
Objects
/**
* Objects
* by hbarragan.
*
* Move the cursor across the image to change the speed and positions
* of the geometry. The class MRect defines a group of lines.
*/
MRect r1, r2, r3, r4;
void setup()
{
size(640, 360);
fill(255, 204);
noStroke();
r1 = new MRect(1, 134.0, 0.532, 0.1*height, 10.0, 60.0);
r2 = new MRect(2, 44.0, 0.166, 0.3*height, 5.0, 50.0);
r3 = new MRect(2, 58.0, 0.332, 0.4*height, 10.0, 35.0);
r4 = new MRect(1, 120.0, 0.0498, 0.9*height, 15.0, 60.0);
}
void draw()
{
background(0);
r1.display();
r2.display();
r3.display();
r4.display();
r1.move(mouseX-(width/2), mouseY+(height*0.1), 30);
r2.move((mouseX+(width*0.05))%width, mouseY+(height*0.025), 20);
r3.move(mouseX/4, mouseY-(height*0.025), 40);
r4.move(mouseX-(width/2), (height-mouseY), 50);
}
class MRect
{
int w; // single bar width
float xpos; // rect xposition
float h; // rect height
float ypos ; // rect yposition
float d; // single bar distance
float t; // number of bars
MRect(int iw, float ixp, float ih, float iyp, float id, float it) {
w = iw;
xpos = ixp;
h = ih;
ypos = iyp;
d = id;
t = it;
}
void move (float posX, float posY, float damping) {
float dif = ypos - posY;
if (abs(dif) > 1) {
ypos -= dif/damping;
}
dif = xpos - posX;
if (abs(dif) > 1) {
xpos -= dif/damping;
}
}
void display() {
for (int i=0; i<t; i++) {
rect(xpos+(i*(d+w)), ypos, w, height*h);
}
}
}
PolygonPShapeOOP3:
/**
* PolygonPShapeOOP.
*
* Wrapping a PShape inside a custom class
* and demonstrating how we can have a multiple objects each
* using the same PShape.
*/
// A list of objects
ArrayList<Polygon> polygons;
// Three possible shapes
PShape[] shapes = new PShape[3];
void setup() {
size(640, 360, P2D);
shapes[0] = createShape(ELLIPSE,0,0,100,100);
shapes[0].setFill(color(255, 127));
shapes[0].setStroke(false);
shapes[1] = createShape(RECT,0,0,100,100);
shapes[1].setFill(color(255, 127));
shapes[1].setStroke(false);
shapes[2] = createShape();
shapes[2].beginShape();
shapes[2].fill(0, 127);
shapes[2].noStroke();
shapes[2].vertex(0, -50);
shapes[2].vertex(14, -20);
shapes[2].vertex(47, -15);
shapes[2].vertex(23, 7);
shapes[2].vertex(29, 40);
shapes[2].vertex(0, 25);
shapes[2].vertex(-29, 40);
shapes[2].vertex(-23, 7);
shapes[2].vertex(-47, -15);
shapes[2].vertex(-14, -20);
shapes[2].endShape(CLOSE);
// Make an ArrayList
polygons = new ArrayList<Polygon>();
for (int i = 0; i < 25; i++) {
int selection = int(random(shapes.length)); // Pick a random index
Polygon p = new Polygon(shapes[selection]); // Use corresponding PShape to create Polygon
polygons.add(p);
}
}
void draw() {
background(102);
// Display and move them all
for (Polygon poly : polygons) {
poly.display();
poly.move();
}
}
// A class to describe a Polygon (with a PShape)
class Polygon {
// The PShape object
PShape s;
// The location where we will draw the shape
float x, y;
// Variable for simple motion
float speed;
Polygon(PShape s_) {
x = random(width);
y = random(-500, -100);
s = s_;
speed = random(2, 6);
}
// Simple motion
void move() {
y+=speed;
if (y > height+100) {
y = -100;
}
}
// Draw the object
void display() {
pushMatrix();
translate(x, y);
shape(s);
popMatrix();
}
}
WigglePShape:
/**
* WigglePShape.
*
* How to move the individual vertices of a PShape
*/
// A "Wiggler" object
Wiggler w;
void setup() {
size(640, 360, P2D);
w = new Wiggler();
}
void draw() {
background(255);
w.display();
w.wiggle();
}
// An object that wraps the PShape
class Wiggler {
// The PShape to be "wiggled"
PShape s;
// Its location
float x, y;
// For 2D Perlin noise
float yoff = 0;
// We are using an ArrayList to keep a duplicate copy
// of vertices original locations.
ArrayList<PVector> original;
Wiggler() {
x = width/2;
y = height/2;
// The "original" locations of the vertices make up a circle
original = new ArrayList<PVector>();
for (float a = 0; a < radians(370); a += 0.2) {
PVector v = PVector.fromAngle(a);
v.mult(100);
original.add(new PVector());
original.add(v);
}
// Now make the PShape with those vertices
s = createShape();
s.beginShape(TRIANGLE_STRIP);
s.fill(80, 139, 255);
s.noStroke();
for (PVector v : original) {
s.vertex(v.x, v.y);
}
s.endShape(CLOSE);
}
void wiggle() {
float xoff = 0;
// Apply an offset to each vertex
for (int i = 1; i < s.getVertexCount(); i++) {
// Calculate a new vertex location based on noise around "original" location
PVector pos = original.get(i);
float a = TWO_PI*noise(xoff,yoff);
PVector r = PVector.fromAngle(a);
r.mult(4);
r.add(pos);
// Set the location of each vertex to the new one
s.setVertex(i, r.x, r.y);
// increment perlin noise x value
xoff+= 0.5;
}
// Increment perlin noise y value
yoff += 0.02;
}
void display() {
pushMatrix();
translate(x, y);
shape(s);
popMatrix();
}
}
Update
Based on your comments here's an version of your sketch modified so the color of the hovered house changes:
// store house bounding box dimensions for mouse hover check
int houseWidth = 30;
// 30 px rect height + 15 px triangle height
int houseHeight = 45;
void setup() {
size(500, 500);
}
void draw(){
background(#74F5E9);
for (int i = 30; i < 500; i = i + 100) {
for (int j = 30; j < 500; j = j + 100) {
// check if the cursor is (roughly) over a house
// and render with a different color
if(overHouse(i, j)){
house(i, j, color(0, 0, 200));
}else{
house(i, j, color(0, 200, 0));
}
}
}
}
void house(int x, int y, color fillColor) {
pushMatrix();
translate(x, y);
fill(fillColor);
triangle(15, 0, 0, 15, 30, 15);
rect(0, 15, 30, 30);
rect(12, 30, 10, 15);
popMatrix();
}
// from Processing RollOver example
// https://processing.org/examples/rollover.html
boolean overRect(int x, int y, int width, int height) {
if (mouseX >= x && mouseX <= x+width &&
mouseY >= y && mouseY <= y+height) {
return true;
} else {
return false;
}
}
// check if the mouse is within the bounding box of a house
boolean overHouse(int x, int y){
// offset half the house width since the pivot is at the tip of the house
// the horizontal center
return overRect(x - (houseWidth / 2), y, houseWidth, houseHeight);
}
The code is commented, but here are the main takeaways:
the house() function has been changed so you can specify a color
the overRect() function has been copied from the Rollover example
the overHouse() function uses overRect(), but adds a horizontal offset to take into account the house is drawn from the middle top point (the house tip is the shape's pivot point)
Regarding animation, Processing has tons of examples:
https://processing.org/examples/sinewave.html
https://processing.org/examples/additivewave.html
https://processing.org/examples/noise1d.html
https://processing.org/examples/noisewave.html
https://processing.org/examples/arrayobjects.html
and well as the Motion / Simulate / Vectors sections:
Let's start take sine motion as an example.
The sin() function takes an angle (in radians by default) and returns a value between -1.0 and 1.0
Since you're already calculating positions for each house within a 2D grid, you can offset each position using sin() to animate it. The nice thing about it is cyclical: no matter what angle you provide you always get values between -1.0 and 1.0. This would save you the trouble of needing to store the current x, y positions of each house in arrays so you can increment them in a different directions.
Here's a modified version of the above sketch that uses sin() to animate:
// store house bounding box dimensions for mouse hover check
int houseWidth = 30;
// 30 px rect height + 15 px triangle height
int houseHeight = 45;
void setup() {
size(500, 500);
}
void draw(){
background(#74F5E9);
for (int i = 30; i < 500; i = i + 100) {
for (int j = 30; j < 500; j = j + 100) {
// how fast should each module move around a circle (angle increment)
// try changing i with j, adding i + j or trying other mathematical expressions
// also try changing 0.05 to other values
float phase = (i + frameCount) * 0.05;
// try changing amplitude to other values
float amplitude = 30.0;
// map the sin() result from it's range to a pixel range (-30px to 30px for example)
float xOffset = map(sin(phase), -1.0, 1.0, -amplitude, amplitude);
// offset each original grid horizontal position (i) by the mapped sin() result
float x = i + xOffset;
// check if the cursor is (roughly) over a house
// and render with a different color
if(overHouse(i, j)){
house(x, j, color(0, 0, 200));
}else{
house(x, j, color(0, 200, 0));
}
}
}
}
void house(float x, float y, color fillColor) {
pushMatrix();
translate(x, y);
fill(fillColor);
triangle(15, 0, 0, 15, 30, 15);
rect(0, 15, 30, 30);
rect(12, 30, 10, 15);
popMatrix();
}
// from Processing RollOver example
// https://processing.org/examples/rollover.html
boolean overRect(int x, int y, int width, int height) {
if (mouseX >= x && mouseX <= x+width &&
mouseY >= y && mouseY <= y+height) {
return true;
} else {
return false;
}
}
// check if the mouse is within the bounding box of a house
boolean overHouse(int x, int y){
// offset half the house width since the pivot is at the tip of the house
// the horizontal center
return overRect(x - (houseWidth / 2), y, houseWidth, houseHeight);
}
Read through the comments and try to tweak the code to get a better understanding of how it works and have fun coming up with different animations.
The main changes are:
modifying the house() function to use float x,y positions (instead of int): this is to avoid converting float to int when using sin(), map() and get smoother motions (instead of motion that "snaps" to whole pixels)
Mapped sine to positions which can be used to animate
Wrapping the 3 instructions that calculate the x offset into a reusable function would allow you do further experiment. What if you used a similar technique the y position of each house ? What about both x and y ?
Go through the code step by step. Try to understand it, change it, break it, fix it and make new sketches reusing code.

Processing Picking up ellipse from screen

I have a project that creates an array list of balls (ellipses). When I press my mouse down (left button) and hold it an ellipse follows my mouse. When I let go the ellipse is placed on the screen where my mouse was.
I wanna be able to right click and hold over an ellipse (any random one) and have it following my mouse again like previous. and again if I let go of the mouse button, it should be placed back on the screen where my mouse is currently positioned.
I am struggling to understand how I can find the x y position of the ellipse that is already on the screen and remove the ellipse from the list and have it follow my mouse again.
Any suggestion let me know-
Here is my main class
ArrayList<Ball> ballList = new ArrayList<Ball>();boolean touching;
void setup() {
size(600, 600);
}
void draw() {
background(150);
// if the mouse button is held down, set the ball's coordinates to the mouse coordinates
if (ballList.size() > 0 && mousePressed && mouseButton==LEFT) {
ballList.get(ballList.size() - 1).xPos = mouseX; // 'ballList.get(ballList.size() - 1)' is the java way to get the last item added to an arrayList
ballList.get(ballList.size() - 1).yPos = mouseY;
}
for (Ball b : ballList) {
b.drawBall();
}
}
// this method will trigger once every time the user press a mouse button
void mousePressed() {
if (mouseButton==LEFT) {
ballList.add(new Ball(mouseX, mouseY));
}
}
here is my ball class
class Ball {
float xPos, yPos;
Ball(float xPos, float yPos) {
this.xPos= xPos;
this. yPos= yPos;
}
void drawBall() {
ellipse(xPos, yPos, 50, 50);
println("X" + xPos + " Y:"+ yPos);
}
void moveBall(){
}
}
You can check if a ball is under the cursor by checking if the distance(dist()) between the ball's position and the mouse's position:
if(dist(ball.x, ball.y, mouseX, mouseY) < ball.radius){
println("mouse over ball");
}
Currently you're hardcoding the ball diameter (50), but you could easily add a radius property:
class Ball {
float xPos, yPos;
float diameter = 50;
float radius = diameter * 0.5;
Ball(float xPos, float yPos) {
this.xPos= xPos;
this. yPos= yPos;
}
void drawBall() {
ellipse(xPos, yPos, diameter, diameter);
}
}
The condition can be wrapped in a for loop to do the same check on every ball and into a function which either returns the first ball matching the condition or null (in case there is no ball under the cursor):
Ball getBall(float x, float y){
// for each ball
for(Ball ball : ballList){
// check if the x,y coordinates are inside any of the existing balls
if(dist(ball.xPos, ball.yPos, x, y) < ball.radius){
// return the 1st match
return ball;
}
}
// return null if nothing was found
return null;
}
You're already using classes and functions, but just in case the above syntax looks unfamiliar:
the Ball type at the begining of the function replaces void and it means the function must return an object of type Ball (as opposed to void)
the return keyword both exits the function but also returns the reference to the ball (if found, null otherwise)
To discern whether there's a selected ball or not (when switching between left and right click) you could use a Ball variable which initially is null, but gets assigned either then left click is pressed or right click is pressed and there's a mouse coordinates fall within the ball's position/radius.
Here's a modified version of your code using the above:
ArrayList<Ball> ballList = new ArrayList<Ball>();
// reference to selection
Ball selectedBall;
void setup() {
size(600, 600);
}
void draw() {
background(150);
// render all balls
for (Ball ball : ballList) {
ball.drawBall();
}
}
// this method will trigger once every time the user press a mouse button
void mousePressed() {
if (mouseButton == LEFT) {
// reset the selection to the newest ball
selectedBall = new Ball(mouseX, mouseY);
// append it to the list
ballList.add(selectedBall);
println("added new ball and updated selection", selectedBall);
}
if (mouseButton == RIGHT) {
// check if a ball is under the cursor, if so select it
selectedBall = getBall(mouseX, mouseY);
println("right click selection", selectedBall);
}
}
void mouseDragged() {
// update dragged ball coordinates if there is a previous selection
if (selectedBall != null) {
selectedBall.xPos = mouseX;
selectedBall.yPos = mouseY;
println("dagging selected ball", selectedBall);
}
}
void mouseReleased() {
// clear selection
selectedBall = null;
println("selection cleared");
}
Ball getBall(float x, float y) {
// for each ball
for (Ball ball : ballList) {
// check if the x,y coordinates are inside any of the existing balls
if ( dist(ball.xPos, ball.yPos, x, y) < ball.radius ) {
// return the 1st match (exits loop and function immediately)
return ball;
}
}
// return null if nothing was found
return null;
}
class Ball {
float xPos, yPos;
float diameter = 50;
float radius = diameter * 0.5;
Ball(float xPos, float yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBall() {
ellipse(xPos, yPos, diameter, diameter);
}
// pretty print info
String toString(){
return "[Ball x=" + xPos + " y="+ yPos + "]";
}
}
I've removed a few unused variables, added toString() (so it displays info nicely using println()) and sprinkled a few optional println() statements so it's easier to see what's going as you test the code.
Final notes:
currently if you left click multiple times you can add mulitple overlapping balls. You can tweak the implementation to update the check if there's a ball there first and if so only add a new ball if there aren't any existing balls at that locations already
looping though every ball and checking distance (which uses sqrt()) can get computationally expensive for a large number of balls. At this stage code readability is more important, but in case you code develops into something a lot more complex you could used squared distance instead of dist() and use other optimisation techniques.
Update
Here's a tweaked version of the above sketch which only adds a new ball if there isn't one already at the mouse location (allowing mouse left only dragging):
ArrayList<Ball> ballList = new ArrayList<Ball>();
// reference to selection
Ball selectedBall;
void setup() {
size(600, 600);
}
void draw() {
background(150);
// render all balls
for (Ball ball : ballList) {
ball.drawBall();
}
}
// this method will trigger once every time the user press a mouse button
void mousePressed() {
// update selection
selectedBall = getBall(mouseX, mouseY);
// if there isn't a ball already, add one:
if (selectedBall == null) {
ballList.add(new Ball(mouseX, mouseY));
}
}
void mouseDragged() {
// update dragged ball coordinates if there is a previous selection
if (selectedBall != null) {
selectedBall.xPos = mouseX;
selectedBall.yPos = mouseY;
}
}
void mouseReleased() {
// clear selection
selectedBall = null;
}
Ball getBall(float x, float y) {
// for each ball
for (Ball ball : ballList) {
// check if the x,y coordinates are inside any of the existing balls
if ( dist(ball.xPos, ball.yPos, x, y) < ball.radius ) {
// return the 1st match (exits loop and function immediately)
return ball;
}
}
// return null if nothing was found
return null;
}
class Ball {
float xPos, yPos;
float diameter = 50;
float radius = diameter * 0.5;
Ball(float xPos, float yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBall() {
ellipse(xPos, yPos, diameter, diameter);
}
// pretty print info
String toString(){
return "[Ball x=" + xPos + " y="+ yPos + "]";
}
}
If you want to pick the newly added ball immediately you can off course both add the new ball and update the selection:
ArrayList<Ball> ballList = new ArrayList<Ball>();
// reference to selection
Ball selectedBall;
void setup() {
size(600, 600);
}
void draw() {
background(150);
// render all balls
for (Ball ball : ballList) {
ball.drawBall();
}
}
// this method will trigger once every time the user press a mouse button
void mousePressed() {
// update selection
selectedBall = getBall(mouseX, mouseY);
// if there isn't a ball already, add one:
if (selectedBall == null) {
selectedBall = new Ball(mouseX, mouseY);
ballList.add(selectedBall);
}
}
void mouseDragged() {
// update dragged ball coordinates if there is a previous selection
if (selectedBall != null) {
selectedBall.xPos = mouseX;
selectedBall.yPos = mouseY;
}
}
void mouseReleased() {
// clear selection
selectedBall = null;
}
Ball getBall(float x, float y) {
// for each ball
for (Ball ball : ballList) {
// check if the x,y coordinates are inside any of the existing balls
if ( dist(ball.xPos, ball.yPos, x, y) < ball.radius ) {
// return the 1st match (exits loop and function immediately)
return ball;
}
}
// return null if nothing was found
return null;
}
class Ball {
float xPos, yPos;
float diameter = 50;
float radius = diameter * 0.5;
Ball(float xPos, float yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBall() {
ellipse(xPos, yPos, diameter, diameter);
}
// pretty print info
String toString(){
return "[Ball x=" + xPos + " y="+ yPos + "]";
}
}

How to generate random position object

I'm trying to generate 15 circles on a 1500 - 1000 space, every cirlce with a diferent position. I know how to generate 1 random circle but that's all. How should I do it?
It sounds like you're looking for a for loop:
for(int i = 0; i < 15; i++){
// draw a random circle here
}
Shameless self-promotion: here is a tutorial on for loops in Processing.
Basically, you need to create a Circle class and ArrayList which contains all your circles.
Then using a for you add in that list 15 circles passing to constructor random coordinates and a fixed width and height you decide.
class Circle {
float x, y, size;
public Circle(float x, float y, float size) {
this.x = x;
this.y = y;
this.size = size;
}
public void update() {
ellipse(x, y, size, size);
}
}
Declare globally your ArrayList. Now, in setup() you instantiate your ArrayList and populate it with randomly generated coordinates
ArrayList<Circle> circlesList; // This needs to be declared globally
float circleSize = 64; // Circles size in pixels
void setup() {
size(1500, 1000);
circlesList = new ArrayList<Circle>();
// Populating the ArrayList with circles
for (int i = 0; i < 15; i++) {
float randomx = random(0, 1500); // Random generated X
float randomy = random(0, 1000); // Random generated Y
Circle newCircle = new Circle(randomx, randomy, circleSize);
circlesList.add(newCircle);
}
}
Now in draw() function, using a foreach loop, you'll draw every single circle inside that ArrayList
void draw() {
background(255); // Background color
fill(255, 0, 0); // Circle fill color
for (Circle c : circlesList) {
c.update();
}
}
Please note that this way your circles may overlap or be a little bit outside of the screen. Ask anything if the code is unclear, don't just copy-paste it.
Hope this helped :)

Flocking sketch: How to change triangles (boids) appearance to png image

I was wondering if there was a way to change the appearance of the "particles" to an image that I've created. I found this sketch in the processing library and I've been trying to figure out how can I upload birds that I've created into the sketch.
Would anyone care to help me figure out if it's even possible?
Thank you!
//https://processing.org/examples/flocking.html
Flock flock;
void setup() {
size(640, 360);
flock = new Flock();
// Add an initial set of boids into the system
for (int i = 0; i < 150; i++) {
flock.addBoid(new Boid(width/2,height/2));
}
}
void draw() {
background(50);
flock.run();
}
// Add a new boid into the System
void mousePressed() {
flock.addBoid(new Boid(mouseX,mouseY));
}
// The Flock (a list of Boid objects)
class Flock {
ArrayList<Boid> boids; // An ArrayList for all the boids
Flock() {
boids = new ArrayList<Boid>(); // Initialize the ArrayList
}
void run() {
for (Boid b : boids) {
b.run(boids); // Passing the entire list of boids to each boid individually
}
}
void addBoid(Boid b) {
boids.add(b);
}
}
// The Boid class
class Boid {
PVector location;
PVector velocity;
PVector acceleration;
float r;
float maxforce; // Maximum steering force
float maxspeed; // Maximum speed
Boid(float x, float y) {
acceleration = new PVector(0, 0);
// This is a new PVector method not yet implemented in JS
// velocity = PVector.random2D();
// Leaving the code temporarily this way so that this example runs in JS
float angle = random(TWO_PI);
velocity = new PVector(cos(angle), sin(angle));
location = new PVector(x, y);
r = 2.0;
maxspeed = 2;
maxforce = 0.03;
}
void run(ArrayList<Boid> boids) {
flock(boids);
update();
borders();
render();
}
void applyForce(PVector force) {
// We could add mass here if we want A = F / M
acceleration.add(force);
}
// We accumulate a new acceleration each time based on three rules
void flock(ArrayList<Boid> boids) {
PVector sep = separate(boids); // Separation
PVector ali = align(boids); // Alignment
PVector coh = cohesion(boids); // Cohesion
// Arbitrarily weight these forces
sep.mult(1.5);
ali.mult(1.0);
coh.mult(1.0);
// Add the force vectors to acceleration
applyForce(sep);
applyForce(ali);
applyForce(coh);
}
// Method to update location
void update() {
// Update velocity
velocity.add(acceleration);
// Limit speed
velocity.limit(maxspeed);
location.add(velocity);
// Reset accelertion to 0 each cycle
acceleration.mult(0);
}
// A method that calculates and applies a steering force towards a target
// STEER = DESIRED MINUS VELOCITY
PVector seek(PVector target) {
PVector desired = PVector.sub(target, location); // A vector pointing from the location to the target
// Scale to maximum speed
desired.normalize();
desired.mult(maxspeed);
// Above two lines of code below could be condensed with new PVector setMag() method
// Not using this method until Processing.js catches up
// desired.setMag(maxspeed);
// Steering = Desired minus Velocity
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxforce); // Limit to maximum steering force
return steer;
}
void render() {
// Draw a triangle rotated in the direction of velocity
float theta = velocity.heading2D() + radians(90);
// heading2D() above is now heading() but leaving old syntax until Processing.js catches up
fill(200, 100);
stroke(255);
pushMatrix();
translate(location.x, location.y);
rotate(theta);
beginShape(TRIANGLES);
vertex(0, -r*2);
vertex(-r, r*2);
vertex(r, r*2);
endShape();
popMatrix();
}
// Wraparound
void borders() {
if (location.x < -r) location.x = width+r;
if (location.y < -r) location.y = height+r;
if (location.x > width+r) location.x = -r;
if (location.y > height+r) location.y = -r;
}
// Separation
// Method checks for nearby boids and steers away
PVector separate (ArrayList<Boid> boids) {
float desiredseparation = 25.0f;
PVector steer = new PVector(0, 0, 0);
int count = 0;
// For every boid in the system, check if it's too close
for (Boid other : boids) {
float d = PVector.dist(location, other.location);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < desiredseparation)) {
// Calculate vector pointing away from neighbor
PVector diff = PVector.sub(location, other.location);
diff.normalize();
diff.div(d); // Weight by distance
steer.add(diff);
count++; // Keep track of how many
}
}
// Average -- divide by how many
if (count > 0) {
steer.div((float)count);
}
// As long as the vector is greater than 0
if (steer.mag() > 0) {
// First two lines of code below could be condensed with new PVector setMag() method
// Not using this method until Processing.js catches up
// steer.setMag(maxspeed);
// Implement Reynolds: Steering = Desired - Velocity
steer.normalize();
steer.mult(maxspeed);
steer.sub(velocity);
steer.limit(maxforce);
}
return steer;
}
// Alignment
// For every nearby boid in the system, calculate the average velocity
PVector align (ArrayList<Boid> boids) {
float neighbordist = 50;
PVector sum = new PVector(0, 0);
int count = 0;
for (Boid other : boids) {
float d = PVector.dist(location, other.location);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.velocity);
count++;
}
}
if (count > 0) {
sum.div((float)count);
// First two lines of code below could be condensed with new PVector setMag() method
// Not using this method until Processing.js catches up
// sum.setMag(maxspeed);
// Implement Reynolds: Steering = Desired - Velocity
sum.normalize();
sum.mult(maxspeed);
PVector steer = PVector.sub(sum, velocity);
steer.limit(maxforce);
return steer;
}
else {
return new PVector(0, 0);
}
}
// Cohesion
// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
PVector cohesion (ArrayList<Boid> boids) {
float neighbordist = 50;
PVector sum = new PVector(0, 0); // Start with empty vector to accumulate all locations
int count = 0;
for (Boid other : boids) {
float d = PVector.dist(location, other.location);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.location); // Add location
count++;
}
}
if (count > 0) {
sum.div(count);
return seek(sum); // Steer towards the location
}
else {
return new PVector(0, 0);
}
}
}
Take a look at the render() function in the Boid class:
void render() {
// Draw a triangle rotated in the direction of velocity
float theta = velocity.heading2D() + radians(90);
// heading2D() above is now heading() but leaving old syntax until Processing.js catches up
fill(200, 100);
stroke(255);
pushMatrix();
translate(location.x, location.y);
rotate(theta);
beginShape(TRIANGLES);
vertex(0, -r*2);
vertex(-r, r*2);
vertex(r, r*2);
endShape();
popMatrix();
}
This is the code that draws a single Boid. You could modify this code to draw an image instead. One way might be to just texture the existing triangle. You'd do this using the texture() function:
texture(yourImageHere);
vertex(0, -r*2);
vertex(-r, r*2);
vertex(r, r*2);
Or you could change the rendering to use a rectangle to show your image. Keep in mind that if you change the shape of the Boids, you might also need to change some of the values used in the flocking behavior to accommodate the new shapes.

How to rotate a line in a circle (radar like) in Processing while also plotting points?

I am trying to rotate a line around in a circle that represents the direction a sensor is facing, while also plotting distance measurements. So I can't use background() in the draw function to clear the screen, because it erases the plotting of the distance readings. I've tried pggraphics and a few others ways, but can't seem to find a way to do it.
This is what I have right now:
void setup() {
background(255,255,255);
size(540, 540);
}
void draw() {
translate(width/2, height/2);
ellipse(0,0,100,100);
newX = x*cos(theta)- y*sin(theta);
newY = x*sin(theta)+ y*cos(theta);
theta = theta + PI/100;
//pushMatrix();
fill(255, 255);
line(0, 0, newX, newY);
rotate(theta);
//popMatrix();
}
I am new to Processing, and coding in general, but can anyone point me in the right direction on how to do this? Thanks
This is what it outputs: http://imgur.com/I825mjE
You can use background(). You just need to redraw the readings on each frame. You could store the readings in an ArrayList, which allows you to add new readings, change them and remove them.
An example:
ArrayList<PVector> readings;
int readingsCount = 15;
void setup() {
size(540, 540);
// create collection of random readings
readings = new ArrayList<PVector>();
for(float angle = 0; angle < TWO_PI; angle += TWO_PI/ readingsCount) {
float distance = random(100, 200);
// the new reading has an angle...
PVector newReading = PVector.fromAngle(angle);
// ... and a distance
newReading.mult(distance);
// Add the new reading to the collection
readings.add(newReading);
}
}
void draw() {
background(255);
// Put (0, 0) in the middle of the screen
translate(width/2, height/2);
float radius = 250;
noFill();
ellipse(0, 0, 2*radius, 2*radius);
// draw spinning line
float angle = frameCount * 0.1;
line(0, 0, radius * cos(angle), radius * sin(angle));
// draw readings
for(PVector p : readings) {
ellipse(p.x, p.y, 20, 20);
}
}

Resources