Расчет позиций трех соприкасающихся кругов по их радиусу
В конечном итоге это проблема геометрии. Я использую Raphaeljs, чтобы нарисовать 3 круга для каждого элемента в серии данных. Каждый круг представляет количество элементов в категории.
Я бы хотел, чтобы круги касались, но не перекрывали друг друга, и я бы хотел, чтобы весь набор находился в центре его родительского элемента div.
Схема здесь: http://chriscanipe.com/images/circles.jpg
Зная только радиус каждого круга, а также ширину и высоту родительского элемента div, как мне будет рассчитывать координаты xy для центра каждого круга? Чем больше я думаю об этом, тем больше я пытаюсь нарисовать треугольник, где каждый угол является центром круга по оси x,y.
3 ответа
У вас есть ряд ограничений, которые вам нужно будет ограничить или выбрать здесь, помимо вашего первоначального вопроса. На вашем примере изображения, как вы решили повернуть треугольник, образованный центрами? Что вы называете "центром" треугольника?
Один из способов подойти к этому может быть:
Предположим, что первая окружность в начале координат (0,0). Предположим, что вторая окружность находится в точке, расположенной непосредственно над ней (0,r1+r2). Вычислите третью точку - это пересечение двух окружностей. Один центрируется в начале координат и имеет радиус r1+r3, а другой - в центре (0,r1+r2) и имеет радиус r2+r3.
Теперь у вас есть эти три точки, вы можете рассчитать "центр"
Затем вы можете нарисовать свои круги на основе этого центра и размеров вашего div.
Это называется проблемой кругов Аполлония. Вы можете найти решения, используя ссылку.
Я работал над итеративными обобщениями этого. Если две кривые A и B касаются друг друга, преобразуйте третью кривую C так, чтобы она касалась двух других. Пользователь предоставляет некоторые подсказки в виде выбора точек на двух фиксированных кривых. Алгоритм работает следующим образом:
- Пусть p и q - выбранные точки, спроецированные на A и B.
- Преобразуйте C так, чтобы он соответствовал p и q.
- Поскольку мы не ожидаем, что C будет касаться A и B в точках p и q, нам нужно найти новый выбор p и q.
- Вычислить колеблющиеся круги (или линии, где кривизна равна 0) в точках p и q для кривых, которые встречаются в этих точках.
- Вычислить аккорды пересечения между кругами (и линиями).
- Возьмите p и q в качестве проекций средних точек хорд на A и B и повторяйте от 2, пока хорды пересечения не станут достаточно маленькими.
Конечно, есть много тонких деталей, но любой, кто способен реализовать этот алгоритм, должен уметь их решать.
Скоро я добавлю иллюстрацию, но у меня нет доступа к Mathematica в данный момент.
Вот моя реализация моего решения этой проблемы. Он включает в себя множество геометрических и триггерных тождеств, но не вызывает триггерные функции.
HTML >>>
<!DOCTYPE HTML>
<html>
<head>
<title>
Tangent Circles in a Box
</title>
<link rel="stylesheet" href="CSS/tangent_circles.css" type="text/css" />
</head>
<body>
<div id="main_container">
<div id="inner_container">
<img class="circle" id="left_circle" src="http://www.clker.com/cliparts/Z/Z/S/Y/S/w/red-circle-cross-transparent-background-md.png" alt="left_circle" />
<img class="circle" id="right_circle" src="http://www.clker.com/cliparts/Z/Z/S/Y/S/w/red-circle-cross-transparent-background-md.png" alt="right_circle" />
<img class="circle" id="third_circle" src="http://www.clker.com/cliparts/Z/Z/S/Y/S/w/red-circle-cross-transparent-background-md.png" alt="third_circle" />
</div>
</div>
<div id="userControls">
<form id="userControlsForm">
Circle One <input id="circleOneInput" type="text" placeholder="Enter Numeric Value" value=""><br>
Circle Two <input id="circleTwoInput" type="text" placeholder="Enter Numeric Value" value=""><br>
Circle Three <input id="circleThreeInput" type="text" placeholder="Enter Numeric Value" value="">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"></script>
<script src="JS/tangent_circles.js"></script>
</form>
</div>
</body>
</html>
HTML <<<
CSS >>>
body
{
background-color: white;
}
#main_container
{
background-color: #cccccc;
width: 800px;
height: 800px;
margin: auto;
border: solid #cccccc 1px;
-o-border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
#inner_container
{
width: 100%;
height: 100%;
position: relative;
}
.circle
{
position: absolute;
text-align: center;
font-family: fantasy;
}
#left_circle
{
top: 0px;
left: 0px;
width: 300px;
height: 300px;
}
#right_circle
{
top: 0px;
left: 0px;
width: 300px;
height: 300px;
}
#third_circle
{
top: 0px;
left: 0px;
width: 300px;
height: 300px;
}
#userControls
{
padding: 30px;
width: 400px;
height: 200px;
margin: auto;
margin-top: 30px;
background-color: #dddddd;
}
#circleOneInput
{
margin-bottom: 10px;
}
#circleTwoInput
{
margin-bottom: 10px;
}
#userControls
{
position: absolute;
left : 20px;
top : 50px;
width : 200px;
}
CSS <<<
JS >>>
$(function() {
function changeCircles(){
$("#inputWarning").remove();
var radius1 = parseInt( $("#circleOneInput").val(), 10);
var radius2 = parseInt( $("#circleTwoInput").val(), 10);
var radius3 = parseInt( $("#circleThreeInput").val(), 10);
console.log( 'radius1 = ' + radius1 + ', ' +
'radius2 = ' + radius2 + ', ' +
'radius3 = ' + radius3
);
if ( isNaN( radius1 ) || isNaN( radius2 ) || isNaN( radius3 ))
{
$("#userControlsForm").after('<span id="inputWarning" style="color: red;">Only Numbers Please</span>');
}
else
{
// normalize circle sizes
if (radius1 < 10)
{
radius1 = 10;
}
if (radius2 < 10)
{
radius2 = 10;
}
if (radius3 < 10)
{
radius3 = 10;
}
if (radius1 > 150)
{
radius1 = 150;
}
if (radius2 > 150)
{
radius2 = 150;
}
if (radius3 > 150)
{
radius3 = 150;
}
// do the actual circle changing
// 1) calculate
// 2) animate
// calculate sides of triangle
var a = radius2 + radius3;
var b = radius1 + radius3;
var c = radius2 + radius1;
// get dimensions of containing div
var container_width = $("#inner_container").width();
var container_height = $("#inner_container").height();
var center_x = container_width / 2.0;
var center_y = container_height / 2.0;
// calculate cosine and sine of angle inside circle b
var cos_beta = ((a * a) + (c * c) - (b * b))/(2 * a * c);
var sin_beta = Math.sqrt( 1 - cos_beta * cos_beta );
// calculate coordinates of circles a and b
var Ax = 0;
var Ay = 0;
var Bx = radius1 + radius2;
var By = 0;
// calculate cosine and sine of angle between triangle and horizontal
var cos_phi = (Bx - Ax)/c;
var sin_phi = Math.sqrt( 1 - cos_phi * cos_phi );
// calculate the cosine and sine of the sum of both angles
var cos_phiNbeta = cos_phi * cos_beta - sin_beta * sin_phi;
var sin_phiNbeta = cos_phi * sin_beta + sin_phi * cos_beta;
// calculate coordinates of circle c
var Cx = Bx - cos_phiNbeta * a;
var Cy = By + sin_phiNbeta * a;
// find centroid
var centroid_x = (Ax + Bx + Cx) / 3.0;
var centroid_y = (Ay + By + Cy) / 3.0;
// get coordinate adjustment
var adjust_x = center_x - centroid_x;
var adjust_y = center_y - centroid_y;
// convert coordinates to div position values
var A_left = Ax + adjust_x - radius1;
var A_top = Ay + adjust_y - radius1;
var B_left = Bx + adjust_x - radius2;
var B_top = By + adjust_y - radius2;
var C_left = Cx + adjust_x - radius3;
var C_top = Cy + adjust_y - radius3;
// calculate div dimensions
var A_width = 2 * radius1;
var A_height = 2 * radius1;
var B_width = 2 * radius2;
var B_height = 2 * radius2;
var C_width = 2 * radius3;
var C_height = 2 * radius3;
// the following needs Jquery
var circle_a = $("#left_circle");
var circle_b = $("#right_circle");
var circle_c = $("#third_circle");
circle_a.animate( {
'top' : A_top + 'px',
'left' : A_left + 'px',
'width' : A_width + 'px',
'height': A_height + 'px'
}, 500 );
circle_b.animate( {
'top' : B_top + 'px',
'left' : B_left + 'px',
'width' : B_width + 'px',
'height': B_height + 'px'
}, 500 );
circle_c.animate( {
'top' : C_top + 'px',
'left' : C_left + 'px',
'width' : C_width + 'px',
'height': C_height + 'px'
}, 500 );
}
}
$("#circleOneInput").keyup(changeCircles);
$("#circleTwoInput").keyup(changeCircles);
$("#circleThreeInput").keyup(changeCircles);
}); // end ready
JS <<<