Javascript 如何在矩形外最小地置换圆?
这似乎应该很简单,但我找不到任何明确的答案。假设我有一个圆形和矩形。如果圆位于矩形之外,则应保持其当前位置。但是,如果它位于矩形内部,则应将其位移最小,使其仅位于矩形外部 我在下面创建了一个完整的演示,演示了我当前正在进行的工作。我最初的想法是将圆夹紧到最近的边缘,但这似乎无法正常工作。我想可能有一个解决方案涉及到分离轴定理,但我不确定这是否适用于这里,或者对于这类事情来说,这是不是太过分了Javascript 如何在矩形外最小地置换圆?,javascript,algorithm,math,collision-detection,game-physics,Javascript,Algorithm,Math,Collision Detection,Game Physics,这似乎应该很简单,但我找不到任何明确的答案。假设我有一个圆形和矩形。如果圆位于矩形之外,则应保持其当前位置。但是,如果它位于矩形内部,则应将其位移最小,使其仅位于矩形外部 我在下面创建了一个完整的演示,演示了我当前正在进行的工作。我最初的想法是将圆夹紧到最近的边缘,但这似乎无法正常工作。我想可能有一个解决方案涉及到分离轴定理,但我不确定这是否适用于这里,或者对于这类事情来说,这是不是太过分了 let canvas=document.querySelector(“canvas”); 设ctx=c
let canvas=document.querySelector(“canvas”);
设ctx=canvas.getContext(“2d”);
函数绘图(){
ctx.fillStyle=“#b2c7ef”;
ctx.fillRect(0,0,800,800);
ctx.fillStyle=“#fff”;
画圈(圆圈x,圆圈y,圆圈);
drawSquare(平方位置x、平方位置y、平方W、平方H);
}
函数绘图圆(X中心、Y中心、半径){
ctx.beginPath();
ctx.弧(xCenter,cCenter,半径,0,2*Math.PI);
ctx.fill();
}
函数drawSquare(x,y,w,h){
ctx.beginPath();
ctx.rect(x,y,w,h);
ctx.stroke();
}
功能钳位(值、最小值、最大值){
返回Math.min(Math.max(value,min),max);
}
功能getCircleRectangleDisplacement(rX、rY、rW、rH、cX、cY、cR){
让最接近的X=钳位(cX、rX、rX+rW);
let nearestY=夹具(cY,rY,rY+rH);
设newX=nearestX-cR/2;
设newY=nearestY-cR/2;
返回{x:newX,y:newY};
}
函数置换(){
circlePos=获取CircleRectangle位移(squarePos.x,squarePos.y,squareW,squareH,circlePos.x,circlePos.y,circleR);
draw();
}
设circlePos={x:280,y:70};
设squarePos={x:240,y:110};
设圈数=50;
设平方w=100;
设平方H=100;
draw();
设置超时(置换,500)代码>
canvas{display:flex;margin:0 auto;}
看看这里,核心是calc()函数,它是Java,不是JavaScript,但我认为您可以轻松地翻译它
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class CircleOutside extends JComponent {
protected Rectangle2D rect;
protected Point2D originalCenter;
protected double radius;
protected Point2D movedCenter;
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2=(Graphics2D) g;
g2.draw(rect);
g.setColor(Color.red);
g2.draw(new Ellipse2D.Double(originalCenter.getX()-radius, originalCenter.getY()-radius, 2*radius, 2*radius));
g.setColor(Color.green);
g2.draw(new Ellipse2D.Double(movedCenter.getX()-radius, movedCenter.getY()-radius, 2*radius, 2*radius));
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
originalCenter=e.getPoint();
calc();
repaint();
}
});
}
public void calc() {
movedCenter=originalCenter;
//Circle center distance from edges greater than radius, do not move
if (originalCenter.getY()+radius<=rect.getY()) {
return;
}
if (originalCenter.getY()-radius>=rect.getY()+rect.getHeight()) {
return;
}
if (originalCenter.getX()+radius<=rect.getX()) {
return;
}
if (originalCenter.getX()-radius>=rect.getX()+rect.getWidth()) {
return;
}
double moveX=0;
double moveY=0;
boolean movingY=false;
boolean movingX=false;
//Center projects into rectangle's width, move up or down
if (originalCenter.getX()>=rect.getX()&&originalCenter.getX()<=rect.getX()+rect.getWidth()) {
System.out.println("X in width");
double moveUp=rect.getY()-originalCenter.getY()-radius;
double moveDown=rect.getY()+rect.getHeight()-originalCenter.getY()+radius;
if (Math.abs(moveUp)<=Math.abs(moveDown)) {
moveY=moveUp;
} else {
moveY=moveDown;
}
System.out.println("UP "+moveUp+" DOWN "+moveDown);
movingY=true;
}
//Center projects into rectangle's height, move left or right
if (originalCenter.getY()>=rect.getY()&&originalCenter.getY()<=rect.getY()+rect.getHeight()) {
double moveLeft=rect.getX()-originalCenter.getX()-radius;
double moveRight=rect.getX()+rect.getWidth()-originalCenter.getX()+radius;
if (Math.abs(moveLeft)<=Math.abs(moveRight)) {
moveX=moveLeft;
} else {
moveX=moveRight;
}
movingX=true;
}
//If circle can be moved both on X or Y, choose the lower distance
if (movingX&&movingY) {
if (Math.abs(moveY)<Math.abs(moveX)) {
moveX=0;
} else {
moveY=0;
}
}
//Note that the following cases are mutually excluding with the previous ones
//Center is in the arc [90-180] centered in upper left corner with same radius as circle, calculate distance from corner and adjust both axis
if (originalCenter.getX()<rect.getX()&&originalCenter.getY()<rect.getY()) {
double dist=originalCenter.distance(rect.getX(),rect.getY());
if (dist<radius) {
double factor=(radius-dist)/dist;
moveX=factor*(originalCenter.getX()-rect.getX());
moveY=factor*(originalCenter.getY()-rect.getY());
}
}
//Center is in the arc [0-90] centered in upper right corner with same radius as circle, calculate distance from corner and adjust both axis
if (originalCenter.getX()>rect.getX()+rect.getWidth()&&originalCenter.getY()<rect.getY()) {
double dist=originalCenter.distance(rect.getX()+rect.getWidth(),rect.getY());
if (dist<radius) {
double factor=(radius-dist)/dist;
moveX=factor*(originalCenter.getX()-rect.getX()-rect.getWidth());
moveY=factor*(originalCenter.getY()-rect.getY());
}
}
//Center is in the arc [270-360] centered in lower right corner with same radius as circle, calculate distance from corner and adjust both axis
if (originalCenter.getX()>rect.getX()+rect.getWidth()&&originalCenter.getY()>rect.getY()+rect.getHeight()) {
double dist=originalCenter.distance(rect.getX()+rect.getWidth(),rect.getY()+rect.getHeight());
if (dist<radius) {
double factor=(radius-dist)/dist;
moveX=factor*(originalCenter.getX()-rect.getX()-rect.getWidth());
moveY=factor*(originalCenter.getY()-rect.getY()-rect.getHeight());
}
}
//Center is in the arc [180-270] centered in lower left corner with same radius as circle, calculate distance from corner and adjust both axis
if (originalCenter.getX()<rect.getX()&&originalCenter.getY()>rect.getY()+rect.getHeight()) {
double dist=originalCenter.distance(rect.getX(),rect.getY()+rect.getHeight());
if (dist<radius) {
double factor=(radius-dist)/dist;
moveX=factor*(originalCenter.getX()-rect.getX());
moveY=factor*(originalCenter.getY()-rect.getY()-rect.getHeight());
}
}
movedCenter=new Point2D.Double(originalCenter.getX()+moveX,originalCenter.getY()+moveY);
}
public static void main(String[] args) {
Rectangle2D rect=new Rectangle2D.Double(240, 110, 100, 100);
Point2D center=new Point2D.Double(280, 70);
double radius=50;
CircleOutside o=new CircleOutside();
o.rect=rect;
o.originalCenter=center;
o.radius=radius;
o.calc();
o.setPreferredSize(new Dimension(800,600));
JFrame frame=new JFrame("Test circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(o);
frame.pack();
frame.setVisible(true);
}
}
封装测试;
导入java.awt.Color;
导入java.awt.Dimension;
导入java.awt.Graphics;
导入java.awt.Graphics2D;
导入java.awt.event.MouseAdapter;
导入java.awt.event.MouseEvent;
导入java.awt.geom.Ellipse2D;
导入java.awt.geom.Point2D;
导入java.awt.geom.Rectangle2D;
导入javax.swing.JComponent;
导入javax.swing.JFrame;
公共类CircleOutside扩展JComponent{
保护矩形2d-rect;
受保护点2D原始中心;
保护双半径;
受保护的点2D移动中心;
@凌驾
受保护组件(图形g){
超级组件(g);
图形2d g2=(图形2d)g;
g2.绘制(rect);
g、 setColor(Color.red);
g2.绘制(新的椭圆E2D.Double(originalCenter.getX()-半径,originalCenter.getY()-半径,2*半径,2*半径));
g、 setColor(Color.green);
g2.绘制(新的椭圆E2D.Double(movedCenter.getX()-radius,movedCenter.getY()-radius,2*半径,2*半径));
addMouseListener(新的MouseAdapter(){
@凌驾
公共无效mouseClicked(MouseEvent e){
originalCenter=e.getPoint();
计算();
重新油漆();
}
});
}
公共无效计算(){
移动中心=原始中心;
//圆中心距边的距离大于半径,请勿移动
如果(originalCenter.getY()+radius=rect.getY()+rect.getHeight()){
返回;
}
if(originalCenter.getX()+radius=rect.getX()+rect.getWidth()){
返回;
}
双动x=0;
双动=0;
布尔movingY=false;
布尔movingX=false;
//居中投影到矩形的宽度,向上或向下移动
如果(originalCenter.getX()>=rect.getX()&&originalCenter.getX()对代码进行了以下修改:
- 增加了一个函数,除了计算线段上相应的垂直点外,还计算点到线段的距离
- 添加了确定点位于多边形内部还是外部的函数
- 修改函数
GetCirclerRectangleDisplacement
以执行以下操作:
- 创建一个边界多边形,将矩形的边延伸半径的长度。然后,如果圆心位于该边界多边形内,则需要将其移动到四(4)个边界多边形中的一个扩展边。函数
pointInPolygon
确定圆心是否在边界多边形中,如果在边界多边形中,则使用pointToSegmentDistance
查找四(4)条扩展边之一上最近的点,该点现在代表新的圆心
- 否则,如果圆心位于边界多边形之外,则该函数将检查圆心是否小于四个顶点之一的半径长度,如果小于,则将圆心移离顶点,使距离现在为半径
画布{display:flex;边距:0 auto;}
让canvas=document.querySelector(“canvas”);
设ctx=canvas.getContext(“2d”);
函数绘图(){
ctx.fillStyle=“#fff”;
画圈(圆圈x,圆圈y,圆圈);
drawSquare(平方位置x、平方位置y、平方W、平方H);
}
函数绘图圆(X中心、Y中心、半径){
ctx.fillStyle=“#fff”;
ctx.beginPath();
ctx.弧(xCenter,cCenter,半径,0,2*Math.PI);
ctx.fill();
}
函数drawSquare(x,y,w,h){
ctx.fillStyle=“#f0f”;
ctx.beginPath();
ctx.rect(x,y,w,h);
ctx.fill();
}
//来源和改编自https://stackoverflow.com/a/6853926/7696162
功能点到分段距离(点、分段、分段){
var A=点x-点x;
var B=点y-分段y;