合并两个不同大小的相交圆并找到加权半径中心的坐标

Merging two different sized intersecting circles and finding the coordinates of a radius-weighted center

我正在为圈子开发一个有趣的小模拟环境。我找不到准确的方法来组合两个圆并找到它们的中心坐标。

我设置了一个 html canvas,然后在平面上生成随机坐标以及随机大小的半径。在每一代之后,我检查每个圆与其他圆之间的交点。当圆圈相交时,我希望它们合并 - 用合并的表面积制作一个圆圈。找到新中心的坐标是我的问题。

我不想简单地找到中心的中点,因为这不会影响圆圈的大小。一个巨大的圆圈可能会被一个微小的圆圈摇晃,这不适合进行逼真的模拟。

我想到了一个我认为不好的解决方案:将中点公式产生的距离变化乘以两个圆半径的比率,得到所得三角形的角度,使用三角函数得到x 和 y 的差异,然后将其添加到大圆的中心并结束。

真的不知道这样做是否正确,所以我想问问比我聪明的人。

哦还有 link github 上的回购: Circle Simulator

这是我的第一个 Whosebug 问题,如果我做了一些完全愚蠢的事情,请多多包涵。谢谢大家!

var dataForm = document.getElementById('dataForm');
var type = document.getElementById('type');
var dataMinRad = document.getElementById('dataMinRad');
var dataMaxRad = document.getElementById('dataMaxRad');
var phaseInterval = document.getElementById('phaseInterval');

//form on submit
const onDataSubmit = (e) => {
    if (e) e.preventDefault();
    
    //updates min and max radius
    minRadius = parseInt(dataMinRad.value);
    maxRadius = parseInt(dataMaxRad.value);

    //clears canvas
    c.clearRect(0, 0, canvas.width, canvas.height);

    //clears circles
    circles = [];

    //clears any previous interval
    clearInterval(phase);
    
    let generator = eval(type.value), data;

    //every one second this code is repeated
    phase = setInterval(() => {
        //gets the circle data from whatever generator is selected
        data = generator();

        //adds the new circle and draws it on the canvas if the data is good
        if (data) {
            circles.push(new Circle(data.x, data.y, data.rad));
            circles[circles.length - 1].draw();
        }
    }, parseInt(phaseInterval.value));
}

dataForm.addEventListener('submit', onDataSubmit);

    </script>
    <script>

//initializes global elements
var stage = document.getElementById('stage');
var canvas = document.getElementById('myCanvas');
var c = canvas.getContext('2d');

//sets width and height of canvas to that of the stage
canvas.setAttribute('width', stage.clientWidth);
canvas.setAttribute('height', stage.clientHeight);

class Circle {
    constructor (x, y, rad) {
        this.x = x;
        this.y = y;
        this.rad = rad;
    }
    draw() {
        c.fillStyle = 'black';
        c.beginPath();
        c.arc(this.x, this.y, this.rad, 0, 2 * Math.PI, true);
        c.stroke();
    }
}

//variables
var circles = [];
var maxRadius = 100;
var minRadius = 1;
var phase;

const random = () => {
    //random coords and radius
    let x, y, rad;

    do {
        [x, y, rad] = [Math.round(Math.random() * canvas.width), Math.round(Math.random() * canvas.height), Math.ceil(Math.random() * (maxRadius - minRadius)) + minRadius];
    } while ((() => {
        for (let i in circles) {
            if (Math.sqrt(Math.pow(x - circles[i].x, 2) + Math.pow(y - circles[i].y, 2)) < rad + circles[i].rad) {
                return true;
            }
        }

        return false;
    })()) //end while

    return { x: x, y: y, rad: rad};
}

const order = () => {
    //gets some random coords and sets the radius to max
    let [x, y, rad] = [Math.round(Math.random() * canvas.width), Math.round(Math.random() * canvas.height), maxRadius];

    //decreases the radius while the resulting circle still intercects any other circle
    while (rad >= minRadius && (() => {
        for (let i in circles) {
            if (Math.sqrt(Math.pow(x - circles[i].x, 2) + Math.pow(y - circles[i].y, 2)) < rad + circles[i].rad) {
                return true;
            }
        }

        return false;
    })()) {
        rad--;
    }

    //only sends the radii that are greater than the minimum radius
    if (rad >= minRadius) return { x: x, y: y, rad: rad};
}

//the position changes must be weighted somehow
const agar = () => {
    //some looping control variables
    let i = 0, j = 1, noChange = true;

    //loops through the circles array in every circle until the noChange variable is false
    while (i < circles.length && noChange) {
        while (j < circles.length && noChange) {
            //checks if each circle is inside each other circle
            if (Math.sqrt(Math.pow(circles[i].x - circles[j].x, 2) + Math.pow(circles[i].y - circles[j].y, 2)) < circles[i].rad + circles[j].rad) {
                //copies the two circles
                let tempCircles = [circles[i], circles[j]];

                //splices the item closest to the end of the array first so that the position of the other doesn't shift after the splice
                if (i > j) {
                    circles.splice(i, 1);
                    circles.splice(j, 1);
                } else {
                    circles.splice(j, 1);
                    circles.splice(i, 1);
                }

                //radius of the two circles' surface area combined
                let rad = Math.sqrt(tempCircles[0].rad * tempCircles[0].rad + tempCircles[1].rad * tempCircles[1].rad);

                /*
                // method 1: the midpoint of the centers //

                let x = (tempCircles[0].x + tempCircles[1].x) / 2;
                
                let y = (tempCircles[0].y + tempCircles[1].y) / 2;

                */

                // method 2: the radius ratio weighted //

                let bigCircle, smallCircle;

                if (tempCircles[0].rad > tempCircles[1].rad) {
                    bigCircle = tempCircles[0];
                    smallCircle = tempCircles[1];
                } else {
                    bigCircle = tempCircles[1];
                    smallCircle = tempCircles[0];
                }

                //get the distance between the two circles
                let dist = Math.sqrt(Math.pow(bigCircle.x - smallCircle.x, 2) + Math.pow(bigCircle.y - smallCircle.y, 2));

                //gets the ratio of the two circles radius size
                let radRatio = smallCircle.rad / bigCircle.rad;

                //the adjusted hypot for the ratio
                dist = dist * radRatio;
                
                //the angle
                let theta = Math.atan2(smallCircle.y - bigCircle.y, smallCircle.x - bigCircle.x); // all hail atan2!

                //the new center coords
                let x = bigCircle.x + dist * Math.cos(theta);
                let y = bigCircle.y + dist * Math.sin(theta);

                circles.push(new Circle(x, y, rad));

                //change happened so the variable should be false
                noChange = false;

                /*
                
                -find the middle of the point
                -weigh it in the direction of teh biggest circle

                radius as the magnitude and [angle of the triangle created when the centers are connected] as the direction for both radii.

                find the point on each circle closest to the center of the other circle

                find those two points midpoint

                find the distance from that point to each of the centers

                those two distances are the magnitude of two new vectors with the same angels as before

                add those two vectors

                is there really not a freaking easier way?
                
                */

                /*
                try this:

                -get the distance between the centers.
                -multiply that by the ratio
                -get the angle
                -use that angle and that hypot to find the x and y
                -add the x and y to the bigger circles centerr

                */
            }
            j++;
        }
        i++;
        j = i + 1;
    }
    
    //if there was no change
    if (noChange) {
        //random coords and radius size
        let x = Math.round(Math.random() * canvas.width),
            y = Math.round(Math.random() * canvas.height),
            rad = Math.ceil(Math.random() * (maxRadius - minRadius)) + minRadius;

        //adds the random circle to the array
        circles.push(new Circle(x, y, rad));
    }

    //clears canvas
    c.clearRect(0, 0, canvas.width, canvas.height);

    //redraws ALL circles
    for (let i in circles) {
        circles[i].draw();
    }
}

onDataSubmit();
* {
  margin: 0;
  box-sizing: border-box;
}

#wrapper {
  width: 100%;
  max-width: 1280px;
  margin: auto;
  margin-right: 0;
  display: flex;
  flex-flow: row nowrap;
}

#dataContainer {
  height: 100%;
  width: 20%;
  padding: 5px;
}

#dataContainer>* {
  padding: 15px;
}

#dataForm {
  max-width: 200px;
  display: grid;
}

#dataForm>* {
  margin-top: 5px;
  width: 100%;
}

.center {
  margin: auto;
}

#stage {
  margin: 5px;
  width: 80%;
  height: 97vh;
}
  <div id='wrapper'>
    <!-- form containter -->
    <div id='dataContainer'>
      <h3>Data</h3>

      <form id='dataForm' method='post'>
        <label for='type'>Type:</label>
        <select id='type' name='type'>
          <option value='random' selected>Random</option>
          <option value='order'>Order</option>
          <option value='agar'>Agario</option>
        </select>

        <label for='min'>Min-Radius:</label>
        <input id='dataMinRad' name='min' type='number' value='1' min='0'>

        <label for='max'>Max-Radius:</label>
        <input id='dataMaxRad' name='max' type='number' value='100'>

        <label for='interval'>Phase Interval:</label>
        <input id='phaseInterval' name='interval' type='number' value='1' min='1'>

        <button type='submit' id='dataSubmit' class='center'>Load</submit>
            </form>
        </div>

        <!-- canvas container-->
        <div id='stage'>
            <canvas id='myCanvas'></canvas>
        </div>
    </div>
  

如果你能得到圆心坐标和半径,你就可以画这个新的圆了 像这样:

关于提取一个根√你可以用这个:

var xxxx = Math.pow(your target here,2);

更新我的回答:

所以问题是给定两个重叠的圆找到一个新的圆代表合并的圆并且面积等于原始圆的总和

对于这个新圆的中心,一种选择是找到两个原始圆的质心。如果你有两个质量 m1、m2 和位置 (x1,y1)、(x2,y2) 那么整个系统的质心将是

m1/(m1+m2) (x1,y1) + m2/(m1+m2) (x2,y2)

在这种情况下,质心将为

r1^2/(r1^2+r2^2) (x1,y1) + r2^2/(r1^2+r2^2) (x2,y2)

对于半径为 r1、r2 的圆,质量将与 pi r1^2 + pi r2^2 成正比。因此,圆的半径将为 sqrt(r1^2+r2^2).