position holds the center of a circle with a diameter variable. enemy.position is the top left corner of a square which is an enemy, size is the width and height of the enemy. I am using pVector and line() from the processing.core.pApplet library in java.
What I’m trying to do is draw a line from the circle to an enemy. The line should look like it started from the centre of the enemy towards pos (centre of the circle) but stops on the surface of the circle. From what I can tell, the code I have left below works but only in cases where the enemy is to the right of the player, otherwise, the line’s position on the surface of the circle is inverted to the other side,
PVector riseRun = PVector.sub(position, enemy.position); float angle = atan(riseRun.y/riseRun.x); line(position.x + (cos(angle) * diameter/2), position.y + (sin(angle) * diameter/2), enemy.position.x + (enemy.size/2), enemy.position.y + enemy.size/2);
Advertisement
Answer
You can find the side of the rectangle by finding the point on the rectangle that lies on the straight line given by the center of the circle and the center of the rectangle.
The point on the rectangle and the circle can be computed by the minimum relation of the offset between the center points and the size of the rectangle.
In the following algorithm, the rectangle is defined by the center point (rectCenter
) and the size (size
) and the circle is defined by the center point (circleCenter
) and the radius (radius
):
PVector[] intersectRectangleCircle(PVector rectCenter, PVector size, PVector circleCenter, float radius) { PVector offset = PVector.sub(circleCenter, rectCenter); if (offset.x == 0 && offset.y == 0) return null; float ratio; if (offset.x == 0) ratio = size.y / abs(offset.y); else if (offset.y == 0) ratio = size.x / abs(offset.x); else ratio = min(size.x / abs(offset.x), size.y / abs(offset.y)); ratio *= 0.5; PVector p1 = PVector.add(rectCenter, PVector.mult(offset, ratio)); offset.normalize(); PVector p2 = PVector.sub(circleCenter, offset.mult(radius)); return new PVector[]{p1, p2}; }
Minimal example:
void setup() { size(500, 500); } PVector[] intersectRectangleCircle(PVector rectCenter, PVector size, PVector circleCenter, float radius) { PVector offset = PVector.sub(circleCenter, rectCenter); if (offset.x == 0 && offset.y == 0) return null; float ratio; if (offset.x == 0) ratio = size.y / abs(offset.y); else if (offset.y == 0) ratio = size.x / abs(offset.x); else ratio = min(size.x / abs(offset.x), size.y / abs(offset.y)); ratio *= 0.5; PVector p1 = PVector.add(rectCenter, PVector.mult(offset, ratio)); offset.normalize(); PVector p2 = PVector.sub(circleCenter, offset.mult(radius)); return new PVector[]{p1, p2}; } void draw() { background(160); float sizeX = 150; float sizeY = 100; float rectX = width/2-sizeX/2; float rectY = height/2-sizeY/2; float radius = 50; float centerX = mouseX; float centerY = mouseY; PVector[] outerPoints = intersectRectangleCircle( new PVector(rectX+sizeX/2, rectY+sizeY/2), new PVector(sizeX, sizeY), new PVector(centerX, centerY), radius ); noFill(); strokeWeight(3); stroke(0, 128, 0); circle(centerX, centerY, radius*2); stroke(0, 0, 128); rect(rectX, rectY, sizeX, sizeY); if (outerPoints != null) { stroke(128, 0, 0); strokeWeight(1); line(rectX+sizeX/2, rectY+sizeY/2, centerX, centerY); stroke(255, 0, 0); strokeWeight(3); line(outerPoints[0].x, outerPoints[0].y, outerPoints[1].x, outerPoints[1].y); fill(128, 0, 0); circle(outerPoints[0].x, outerPoints[0].y, 8); circle(outerPoints[1].x, outerPoints[1].y, 8); } }