提问者:小点点

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


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

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

我不想简单地找到圆心的中点,因为这没有考虑圆圈的大小。一个巨大的圆可能会被一个微小的圆所左右,这并不能构成一个真实的模拟。

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

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

哦,这里还有一个指向github上的repo的链接:Circle Simulator

这是我的第一个stackOverflow问题,所以如果我做了一些完全愚蠢的事情,请原谅我。谢谢大家!

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>
  

共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)。

匿名用户

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

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