<html><head>
<style>
canvas { display: flex; margin: 0 auto; }
</style>
</head><body>
<canvas width="800" height="800"></canvas>
<script>
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
function draw() {
ctx.fillStyle = "#fff";
drawCircle(circlePos.x, circlePos.y, circleR);
drawSquare(squarePos.x, squarePos.y, squareW, squareH);
}
function drawCircle(xCenter, yCenter, radius) {
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI);
ctx.fill();
}
function drawSquare(x, y, w, h) {
ctx.fillStyle = "#f0f";
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.fill();
}
function pointToSegmentDistance(point, segBeg, segEnd) {
var A = point.x - segBeg.x;
var B = point.y - segBeg.y;
var C = segEnd.x - segBeg.x;
var D = segEnd.y - segBeg.y;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = -1;
if (len_sq != 0)
param = dot / len_sq;
let intersectPoint;
if (param < 0) {
intersectPoint = segBeg;
}
else if (param > 1) {
intersectPoint = segEnd;
}
else {
intersectPoint = { x: segBeg.x + param * C, y:segBeg.y + param * D };
}
var dx = point.x - intersectPoint.x;
var dy = point.y - intersectPoint.y;
return { intersect: intersectPoint, distance: Math.sqrt(dx * dx + dy * dy) };
}
function pointInPolygon( point, polygon ) {
let vertices = polygon.vertex;
let odd = false;
for (let i = 0, j = polygon.length - 1; i < polygon.length; i++) {
if (((polygon[i].y > point.y) !== (polygon[j].y > point.y))
&& (point.x < ((polygon[j].x - polygon[i].x) * (point.y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x))) {
odd = !odd;
}
j = i;
}
return odd;
}
function getCircleRectangleDisplacement( rX, rY, rW, rH, cX, cY, cR ) {
let rect = [
{ x: rX, y:rY },
{ x: rX + rW, y:rY },
{ x: rX + rW, y:rY + rH },
{ x: rX, y:rY + rH }
];
let boundingPolygon = [
{ x: rX, y: rY },
{ x: rX, y: rY - cR },
{ x: rX + rW, y: rY - cR },
{ x: rX + rW, y: rY },
{ x: rX + rW + cR, y: rY },
{ x: rX + rW + cR, y: rY + rH },
{ x: rX + rW, y: rY + rH },
{ x: rX + rW, y: rY + rH + cR },
{ x: rX, y: rY + rH + cR },
{ x: rX, y: rY + rH },
{ x: rX - cR, y: rY + rH },
{ x: rX - cR, y: rY }
];
ctx.setLineDash([2,2]);ctx.beginPath();ctx.moveTo(boundingPolygon[0].x,boundingPolygon[0].y);for (let p of boundingPolygon) {ctx.lineTo(p.x,p.y);} ctx.lineTo(boundingPolygon[0].x,boundingPolygon[0].y);ctx.stroke();
circleCenter = { x: cX, y: cY };
if ( pointInPolygon( circleCenter, boundingPolygon ) ) {
let newCircleCenter;
let minDistance = Number.MAX_VALUE;
for ( let i = 1; i < boundingPolygon.length; i += 3 ) {
let pts = pointToSegmentDistance( circleCenter, boundingPolygon[ i ], boundingPolygon[ i + 1 ] );
if ( pts.distance < minDistance ) {
newCircleCenter = pts.intersect;
minDistance = pts.distance;
}
}
circleCenter = newCircleCenter;
} else {
let newCircleCenter;
let minDistance = Number.MAX_VALUE;
for ( let i = 0; i < boundingPolygon.length; i += 3 ) {
let d = Math.sqrt( ( circleCenter.x - boundingPolygon[ i ].x ) ** 2 + ( circleCenter.y - boundingPolygon[ i ].y ) ** 2 );
if ( d < cR && d < minDistance ) {
newCircleCenter = {
x: boundingPolygon[ i ].x + ( circleCenter.x - boundingPolygon[ i ].x ) * cR / d,
y: boundingPolygon[ i ].y + ( circleCenter.y - boundingPolygon[ i ].y ) * cR / d
}
minDistance = d;
}
}
if ( newCircleCenter ) {
circleCenter = newCircleCenter;
}
}
return circleCenter;
}
function displace() {
ctx.fillStyle = "#b2c7ef";
ctx.fillRect(0, 0, 800, 800);
circlePos.x += 1;
circlePos.y += 1;
circlePos = getCircleRectangleDisplacement(squarePos.x, squarePos.y, squareW, squareH, circlePos.x, circlePos.y, circleR);
draw();
if ( maxIterations < iterations++ ) {
clearInterval( timer );
}
}
let circlePos = { x: 280, y: 40 };
circlePos={ x: 240, y: 110 };
let squarePos = { x: 240, y: 110 };
let circleR = 50;
let squareW = 100;
let squareH = 100;
let iterations = 0;
let maxIterations = 200;
let timer = setInterval(displace, 50);
</script>
</body></html>