Подтвердить что ты не робот

Осложняющая векторная математика в JavaScript

У меня есть забавная проблема dev/math, которую я не могу решить.

См. иллюстрацию ниже.

End result

У меня есть два круга точек; один маленький и один большой.

Я хочу:

  • Нарисуйте линию из любой точки во внешнем круге в любую заданную точку в внутреннем круге (сделано)
  • Линии должны быть дугами и не должны пересекать границы внутренних кругов или границы внешнего круга. (Мне нужна ваша помощь!)

скрипка!

Я создал jsFiddle, написанный с RaphaelJS, где я создаю точки и рисую линии между ними. См. Здесь http://jsfiddle.net/KATT/xZVnx/9/.

В основном в функции drawLine мне нужна ваша помощь, чтобы рисовать приятную дугу, а не прямую линию.

Я добавил некоторые помощники для работы с векторами, se MathHelpers.Vector.

Пожалуйста, продолжайте разворачивать и попробуйте реализовать решение, в котором линии согнуты вокруг. Также очень ценятся решения, использующие Béziers, которые действительно выглядят хорошо.

И да, я думаю, что векторная геометрия лучше всего подходит для вычислений.

Очень, очень, очень благодарен за любую помощь. Я потратил много времени, пытаясь его решить, но моих ржавых навыков математики в средней школе недостаточно.

4b9b3361

Ответ 1

Один из вариантов - использовать эллиптические дуги. Это не выглядит слишком потрясающе, но удовлетворяет ограничениям, и я думаю, что его можно улучшить, осторожным выбором радиуса круга (здесь я использовал фиксированный).

var angleDiff = MathHelpers.getAngleInCircle(innerCircleIndex, numberOfDotsInInnerCircle) - MathHelpers.getAngleInCircle(outerCircleIndex, numberOfDotsInOuterCircle);
while (angleDiff > Math.PI) {
    angleDiff -= 2 * Math.PI;
}
while (angleDiff < -Math.PI) {
    angleDiff += 2 * Math.PI;
}

from = addOffsetAndRound(from);
to = addOffsetAndRound(to);
r = (0.5 * innerCircleRadius + 0.5 * outerCircleRadius);

var pathStr = "";
pathStr += "M" + [from.x, from.y].join(' '); // MOVE to
pathStr += "A" + [r, r, 0, 0, angleDiff >= 0 ? 1 : 0, to.x, to.y].join(' '); // Draw line to
var path = paper.path(pathStr);

P.S. Конечно, следует иметь в виду, что настоящая спираль не является эллиптической дугой.

Ответ 2

Начните просто рисовать кусочно спираль.

Радиус спирали переходит от начального угла к торцу, а радиус спирали переходит от радиуса внутреннего круга к радиусу внешнего круга.

Чтобы дать вам представление о том, что я имею в виду, выберите несколько частей (n)

var n = 20, // The number of lines in the spiral
    rStep = (outerRadius - innerRadius)/n, // the radius increase between points
    aStep = (outerAngle - innerAngle)/n,  // the angle change between points
    points = [];

// compute the cartesian coordinates (x, y) from polar coordinates (r, a)
function cartesian(r, a) {
    return [
        r * Math.cos(a),
        r * Math.sin(a)
    ];
}

for (i = 0; i <= n; i += 1) {
   points.push(cartesian(innerRadius + (i * rStep), innerAngle + (i * aStep));
}

Я продемонстрировал основную кусочную спираль, используя полярные координаты:

http://jsfiddle.net/xmDx8/

Попробуйте изменить n, чтобы увидеть, как создаются фрагменты.

drawLine(innerCircleIndex, outerCircleIndex, 1); // This is what you did

drawLine(innerCircleIndex, outerCircleIndex, 100); // 100 lines in the example

drawLine(innerCircleIndex, outerCircleIndex, n); // Choose n to see how it grows

Ответ 3

Если вы действительно хотели избежать математики, вы могли бы приблизиться к спирали, используя серию простых сегментов линии, не получая никакой информации, кроме координат двух соединяемых точек, и их соответствующих углов относительно начала координат или (очевидно, можно сделать вывод из другого). Учитывая (ix, iy, i_angle), описывающие координаты и относительный угол внутренней точки и (ox, oy, o_angle), описывающие координаты и относительный угол внешней точки,

        var generatePath = function( ix, iy, i_angle, ox, oy, o_angle )
        {
            var path = [ "M", ox, oy ];
            var slices = 100;

            var cur_angle = o_angle;
            var cur_r = cr_outer;
            var delta_r = ( cr_inner - cr_outer ) / slices;
            var delta_angle = ( i_angle - o_angle );
            if ( delta_angle > Math.PI )
                delta_angle -= Math.PI * 2;
            else if ( delta_angle < Math.PI * -1 )
                delta_angle += Math.PI * 2;
            delta_angle = delta_angle / slices;

            for ( var i = 0; i < slices; i++ )
            {
                cur_angle += delta_angle;
                cur_r += delta_r;
                path.push( "L", cx + Math.cos( cur_angle ) * cur_r, cy + Math.sin( cur_angle ) * cur_r );
            }
            return path;
        }

Логика глупо-простая, хорошо выглядит даже при высоких разрешениях и, возможно, более эффективна, чем "настоящий" сегмент дуги или серия квадратичной кривой Безье. Количество срезов на сегмент, вероятно, можно было бы настроить на основе длины и размера, чтобы сбалансировать производительность по сравнению с эстетикой.

У меня есть эта функция (с расчетами контрольных точек), поставленная здесь.

Ответ 4

Попытка подхода без фантастических математик:

  • найдите точку выхода и внутреннюю точку, расположенную на полпути между точками для подключения.
  • найдите середину сегмента, образованного этими полутонами.

Нарисуйте свою кривую, проходящую по этой точке.