I am practicing working with vectors. In this sketch I draw a line connecting centers of two ellipses. How can I shorten the line so that it touches the perimeter of each ellipse (not the center)?
PVector v1, v2;
void setup(){
noLoop();
v1 = new PVector(40, 20);
v2 = new PVector(25, 50);
}
void draw(){
ellipse(v1.x, v1.y, 12, 12);
ellipse(v2.x, v2.y, 12, 12);
line(v1.x, v1.y, v2.x, v2.y);
}
First you'd need to calculate the points where the line would cross the edges of the circles. Fortunately, this is quite easy: (Note that I don't know Processing, so treat this as psuedocode)
direction = atan2(v2.y-v1.y,v2.x-v1.x)
x1 = v1.x+cos(direction)*radius
y1 = v1.y+sin(direction)*radius
x2 = v2.x-cos(direction)*radius
y2 = v2.y-sin(direction)*radius
Then just draw the line (x1,y1,x2,y2)
Related
I have drawn curves that denote the customer country and the country where he is headed for a trip in a map.
But I could not add a gradient so that the lines would denote the said information and gives this random color between two colors at random. Here's what I tried.
int steps = 10;
noFill();
//stroke(#5A38FA, 50);
strokeWeight(1);
for(int i=0; i<steps; i++) {
strokeWeight(1);
noFill();
stroke(lerpColor(#31B5E8, #F0E62E, (float)i/steps));
bezier(locationX, locationY, locationX+random(15, 50), locationY+random(13,50), customerLocationX+random(15, 30), customerLocationY+random(15, 70), customerLocationX, customerLocationY);
}
You can decompose a bezier curve into points using the bezierPoint()method and then draw straight line segments between successive points, specifying the colour for each individual line segment (meanwhile gradually lerping the colour of course).
I've produced a method which can do that in the code example below.
Additionally, with the method, you can specify the curve's magnitude (curve) and the direction of the curve (dir); the method calculates the bezier control point using the point on a line perpendicular to the midpoint between the start point (head) and end point (tail).
void setup() {
size(500, 500);
smooth(4);
noLoop();
redraw();
strokeWeight(5);
noFill();
}
void draw() {
background(35);
drawCurve(new PVector(50, 50), new PVector(456, 490), #31B5E8, #F0E62E, 50, -1);
drawCurve(new PVector(150, 75), new PVector(340, 410), #B9FF00, #FF00C5, 150, 1);
drawCurve(new PVector(200, 480), new PVector(480, 30), #007CFF, #89CA7F, 100, 1);
}
void drawCurve(PVector head, PVector tail, color headCol, color tailCol, float curve, int curveDir) {
final float theta2 = angleBetween(tail, head);
final PVector midPoint = new PVector((head.x + tail.x) / 2,
(head.y + tail.y) / 2);
final PVector bezierCPoint = new PVector(midPoint.x + (sin(-theta2) * (curve * 2 * curveDir)),
midPoint.y + (cos(-theta2) * (curve * 2 * curveDir)));
PVector point = head.copy();
for (float t=0; t<=1; t+=0.025) {
float x1 = bezierPoint(head.x, bezierCPoint.x, bezierCPoint.x, tail.x, t);
float y1 = bezierPoint(head.y, bezierCPoint.y, bezierCPoint.y, tail.y, t);
PVector pointB = new PVector(x1, y1);
stroke(lerpColor(headCol, tailCol, t));
line(point.x, point.y, pointB.x, pointB.y);
point = pointB.copy();
}
}
static float angleBetween(PVector tail, PVector head) {
float a = PApplet.atan2(tail.y - head.y, tail.x - head.x);
if (a < 0) {
a += PConstants.TWO_PI;
}
return a;
}
Result:
I want to achieve a slow fade in size on every collapse into itself. In other words, when the circle is at its biggest, the ellipses will be at the largest in size and conversely the opposite for the retraction. So far I am trying to achieve this affect by remapping the cSize from the distance of the center point, but somewhere along the way something is going wrong. At the moment I am getting a slow transition from small to large in ellipse size, but the inner ellipses are noticeably larger. I want an equal distribution of size amongst all ellipses in relation to center point distance.
I've simplified the code down to 4 ellipses rather than an array of rows of ellipses in order to hopefully simplify this example. This is done in the for (int x = -50; x <= 50; x+=100).
I've seen one or two examples that slightly does what I want, but is more or less static. This example is kind of similar because the ellipse size gets smaller or larger in relation to the mouse position
Distance2D
Here is an additional diagram of the grid of ellipses I am trying to create, In addition, I am trying to scale that "square grid" of ellipses by a center point.
Multiple ellipses + Scale by center
Any pointers?
float cSize;
float shrinkOrGrow;
void setup() {
size(640, 640);
noStroke();
smooth();
fill(255);
}
void draw() {
background(#202020);
translate(width/2, height/2);
if (cSize > 10) {
shrinkOrGrow = 0;
} else if (cSize < 1 ) {
shrinkOrGrow = 1;
}
if (shrinkOrGrow == 1) {
cSize += .1;
} else if (shrinkOrGrow == 0) {
cSize -= .1;
}
for (int x = -50; x <= 50; x+=100) {
for (int y = -50; y <= 50; y+=100) {
float d = dist(x, y, 0, 0);
float fromCenter = map(cSize, 0, d, 1, 10);
pushMatrix();
translate(x, y);
rotate(radians(d + frameCount));
ellipse(x, y, fromCenter, fromCenter);
popMatrix();
}
}
}
The values you're passing into the map() function don't make a lot of sense to me:
float fromCenter = map(cSize, 0, d, 1, 100);
The cSize variable bounces from 1 to 10 independent of anything else. The d variable is the distance of each ellipse to the center of the circle, but that's going to be static for each one since you're using the rotate() function to "move" the circle, which never actually moves. That's based only on the frameCount variable, which you never use to calculate the size of your ellipses.
In other words, the position of the ellipses and their size are completely unrelated in your code.
You need to refactor your code so that the size is based on the distance. I see two main options for doing this:
Option 1: Right now you're moving the circles on screen using the translate() and rotate() functions. You could think of this as the camera moving, not the ellipses moving. So if you want to base the size of the ellipse on its distance from some point, you have to get the distance of the transformed point, not the original point.
Luckily, Processing gives you the screenX() and screenY() functions for figuring out where a point will be after you transform it.
Here's an example of how you might use it:
for (int x = -50; x <= 50; x+=100) {
for (int y = -50; y <= 50; y+=100) {
pushMatrix();
//transform the point
//in other words, move the camera
translate(x, y);
rotate(radians(frameCount));
//get the position of the transformed point on the screen
float screenX = screenX(x, y);
float screenY = screenY(x, y);
//get the distance of that position from the center
float distanceFromCenter = dist(screenX, screenY, width/2, height/2);
//use that distance to create a diameter
float diameter = 141 - distanceFromCenter;
//draw the ellipse using that diameter
ellipse(x, y, diameter, diameter);
popMatrix();
}
}
Option 2: Stop using translate() and rotate(), and use the positions of the ellipses directly.
You might create a class that encapsulates everything you need to move and draw an ellipse. Then just create instances of that class and iterate over them. You'd need some basic trig to figure out the positions, but you could then use them directly.
Here's a little example of doing it that way:
ArrayList<RotatingEllipse> ellipses = new ArrayList<RotatingEllipse>();
void setup() {
size(500, 500);
ellipses.add(new RotatingEllipse(width*.25, height*.25));
ellipses.add(new RotatingEllipse(width*.75, height*.25));
ellipses.add(new RotatingEllipse(width*.75, height*.75));
ellipses.add(new RotatingEllipse(width*.25, height*.75));
}
void draw() {
background(0);
for (RotatingEllipse e : ellipses) {
e.stepAndDraw();
}
}
void mouseClicked() {
ellipses.add(new RotatingEllipse(mouseX, mouseY));
}
void mouseDragged() {
ellipses.add(new RotatingEllipse(mouseX, mouseY));
}
class RotatingEllipse {
float rotateAroundX;
float rotateAroundY;
float distanceFromRotatingPoint;
float angle;
public RotatingEllipse(float startX, float startY) {
rotateAroundX = (width/2 + startX)/2;
rotateAroundY = (height/2 + startY)/2;
distanceFromRotatingPoint = dist(startX, startY, rotateAroundX, rotateAroundY);
angle = atan2(startY-height/2, startX-width/2);
}
public void stepAndDraw() {
angle += PI/64;
float x = rotateAroundX + cos(angle)*distanceFromRotatingPoint;
float y = rotateAroundY + sin(angle)*distanceFromRotatingPoint;
float distance = dist(x, y, width/2, height/2);
float diameter = 50*(500-distance)/500;
ellipse(x, y, diameter, diameter);
}
}
Try clicking or dragging in this example. User interaction makes more sense to me using this approach, but which option you choose really depends on what fits inside your head the best.
I am trying to rotate a rectangle without using the rotate function, but instead using a matrix..I know how to rotate a line using a matrix but all my attempts to rotate a rectangle have failed.
I dont think this is use full but heres is my code that rotates the line.
float[][] rotation;
float[] position;
float theta = 180;
float pointX;
float pointY;
void setup() {
frameRate(60);
size(600, 600);
pointX = 0;
pointY = 0;
rotation = new float[2][2];
position = new float[8];
}
void draw() {
background(200);
theta = mouseX;
position[0] = mouseY;
position[1] = mouseY;
position[2] = -mouseY;
position[3] = mouseY;
rotation[0][0] = cos(radians(theta));
rotation[0][1] = -sin(radians(theta));
rotation[1][0] = sin(radians(theta));
rotation[1][1] = cos(radians(theta));
float newpos[] = new float[8];
newpos[0] += position[0] * rotation[0][0];
newpos[1] += position[1] * rotation[0][1];
translate(width/2, height/2);
line(0, 0, pointX+newpos[0], pointY+newpos[1]);
line(0, 0, pointX+newpos[0] * -1, pointY+newpos[1] * -1);
}
Although the lines behaves properly it is by chance... You have a crucial part of the calculation of the new x and y of the point not as it should have been. As you can find in wikipedia, you need to calculate the sin and cos in the matrix as you properly did, but when creating the new point you don't exactly do this:
Start by having a look at pushMatrix()/popMatrix() and coordinate spaces.
Have a look at Daniel Shiffman's tutorial as well, it's pretty well explained.
If you need to get lower level than this, have a look at the PMatrix2D class.
Notice there is a rotate() function. After you rotate, you can either simply apply
the matrix(using applyMatrix()) but you might as well be using push/pop matrix calls.
Another option is multiply vectors(rectangle corners) to the rotation matrix and draw the result/transformed points.
The code below draws a spiral using objects from a string array. Everything is fine, except that I would like the text objects to be drawn at a roughly 45 degree angle at each instance (based on the current x, y coordinates in the code below) rather than being drawn horizontally (when the text is horizontally drawn, it naturally overlaps with other text at concentrated points along the top & bottom of the curve). I researched some methods, but I'm still very new to all of this, and potential solutions have all evaded me.
String example = "";
String[] wordSet = split(example, " ");
float x, y;
float angle = 0;
float radiusSpiralLine = 10;
size (800, 800);
translate(width/2, height/2);
background(#ffffff);
smooth();
fill(0);
for (int i = 0; i < wordSet.length; i++) {
angle += .05;
radiusSpiralLine += .5;
x = cos(angle) * radiusSpiralLine;
y = sin(angle) * radiusSpiralLine;
textSize(9);
text(wordSet[i], x, y);
}
Here is tutorial to very similar problem. In basic you need to store projection matrix by pushMatrix() then translate and rotate according to position of letter on curve and then restore matrix by popMatrix(). I don't know how exactly do you want to rotate you text but just fold round your text() function like this maybe it will help you:
pushMatrix();
translate(x, y);
rotate(angle);
text(wordSet[i], 0, 0);
popMatrix();
First, you should start getting in the habit of wrapping code in the setup() and draw() functions. Since you're drawing a static image you don't need the draw() function, but I think it's good practice to have those two.
Now, what you are doing now is simply translating the words by a very small amount. Do the math:
x = cos(angle) * radiusSpiralLine; //cos(.05)*.5 = .499
y = sin(angle) * radiusSpiralLine; //sin(.05)*.5 = .024
That means they move less than a pixel, and they're not rotating at all.
What you need is your good ol' friend, the rotate() function.
Let's re-write code:
String example = "These are a bunch of words going around!";
String[] wordSet = split(example, " ");
float x, y;
float angle = 0;
void setup() {
size (800, 800);
background(#ffffff);
smooth();
fill(0);
pushMatrix();
translate(width/2, height/2); //Translate when you need to translate, not before
for (int i = 0; i < wordSet.length; i++) {
angle = PI/5; //Our good friends, radians
textSize(20); //What is this, text for ants? Change to 20
rotate(angle);
text(wordSet[i], 20, 0);
}
popMatrix();
}
void draw() {
}
First notice, the setup() and draw(). I like them there. It looks nicer, I think.
A couple of important things to note.
The effects of rotate() and translate() are on the canvas are cumulative.
We could have had the same effect in different ways:
for (int i = 0; i < wordSet.length; i++) {
angle = PI/5;
textSize(20);
rotate(angle); //always rotating by PI/5 ON TOP of previous rotation
text(wordSet[i], 20, 0);
}
//Everything from now on will still be rotated, we don't want that!
Slightly better, but not there yet:
for (int i = 0; i < wordSet.length; i++) {
angle += PI/5; //constantly increasing the angle
textSize(20);
pushMatrix(); //push a new canvas on top of everything
rotate(angle); //rotate by angle (which increases every loop)
text(wordSet[i], 20, 0);
popMatrix(); //pop the rotated canvas out, go back to original canvas
} //Things won't be rotated, but they'll still be translated, since translate() is outside of pushMatrix and popMatrix
Hope this helps.
I have a sketch where I am using several images. One of the images is being translated and rotated.
After PopMatrix how do I know the new position (X.Y) of image that has been moved around ?
You have to use modelX() and modelY(). See the example below, which draws ellipses instead of images:
void setup(){
size(600, 600, P2D);
ellipseMode(CENTER);
pushMatrix();
translate(width/2, height/2);
rotate(1.23);
int x1 = 100, y1 = 100;
ellipse(x1, y1, 10, 10);
// store translated / rotated coordinates
float x2 = modelX(x1, y1, 0);
float y2 = modelY(x1, y1, 0);
popMatrix();
// draw red dot with stored coordinates
stroke(255, 0, 0);
ellipse(x2, y2, 2, 2);
}