Программное рисование части эллипса с помощью контура Безье или эллиптического пути — SVG и raphael.js

Я пытаюсь нарисовать кривую Безье, окружающую эллипс с заданным полем:

Путь Безье, окружающая эллипс

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

На данный момент я сделал эту функцию:

function bezierPathTopRounded(ellipse, margin) {
    var box = ellipse.paper.getBBox();

    var leftX = box.x - margin; 

    var rightX = box.x + margin + box.width;

    var y = box.y + box.height/2 - margin; 

    var p = "M "+ leftX  + ", "+ y
    + " C " //could be relative too
    + ( box.x - margin + (box.width/15)  )  + ", " + ( box.y + margin - (box.height/4.5)  ) + " "
    + ( box.x + margin + box.width - (box.width/15)  )  + ", " + ( box.y + margin - (box.height/4.5) ) + " "
    + rightX +", " + y;

    return p;   
}

Но я не могу понять, как рассчитать значения точек этого направления, чтобы они работали с любым эллипсом:

  • коробка.ширина/15
  • коробка.высота/4,5

В этом примере есть скрипка.

Я прочитал этот вопрос stackoverflow и попробовал то же самое на моем примере, но до сих пор не могу найти простое решение, оно остается случайным...

Изменить

Сейчас пробую с эллиптическим Arc, результат хуже, чем с Путем Безье:

Испытание эллиптической дугой

Есть функция, которую я использую. Если я удалю поле, оно будет точно соответствовать моему эллипсу... Наконец, вопрос в том, как я могу следовать эллипсу с полем?

function borderPath(ellipse, margin, flag) {
    var flag = flag == undefined ? 1 : 0;

    var box = ellipse.paper.getBBox();

    var leftX = box.x - margin;

    var rightX = box.x + margin + box.width;

    var y = box.y + box.height/2;
    y += (flag == 1) ? -margin : margin;

    var rx = box.width/2 + margin;
    var ry = box.height/2;

    var p = "M "+ leftX + ", "+ y
    + " A "
    + rx + " " + ry
    + " 0 0 "+ flag +" "
    + rightX +", " + y;

    return p;
}

См. обновленную скрипту здесь.

Очень извиняюсь за ужасные цвета, это для примера.


person soyuka    schedule 19.04.2013    source источник
comment
Почему Безье? Почему бы не нарисовать еще один частичный эллипс подходящего размера/положения?   -  person Beetroot-Beetroot    schedule 19.04.2013
comment
Вы имеете в виду эллиптическую дугу? Я пробовал это раньше, но это было невозможно из-за полей, это уже не идеальный эллипс. И мне нужно, чтобы это был путь, чтобы я мог нарисовать эту конкретную линию.   -  person soyuka    schedule 19.04.2013
comment
Да, я имею в виду эллиптическую дугу, хотя я понимаю вашу точку зрения, что концентрические эллипсы могут не давать желаемого эффекта. Сказав это, я думаю, что кривая Безье всегда будет только приближением. Как вы получили box.width/15 и box.height/4.5? Разве это не вопрос обобщения математики?   -  person Beetroot-Beetroot    schedule 19.04.2013
comment
Я много пробовал, прежде чем получить эти оба номера. Это может быть вопрос обобщения математики, не знаю... Я попробую еще раз с эллиптической дугой и дам вам знать.   -  person soyuka    schedule 19.04.2013
comment
Я обновил эллиптическую дугу, но результат все еще неправильный...   -  person soyuka    schedule 19.04.2013


Ответы (3)


Если вы хотите сделать это с кривыми Безье, вам придется решить, насколько «неправильным» вы хотите, чтобы это выглядело. Кривые Безье не могут представлять круглые (и, в более широком смысле, эллиптические) кривые, они могут быть только очень близкими, как и многоугольник, только с большей точностью, используя меньшее количество секций.

Я описываю и аппроксимацию окружности, и смещение кривой с помощью кривых Безье в моем учебнике по кривым Безье, http://pomax.github.io/bezierinfo/#circles_cubic и http://pomax.github.io/bezierinfo/#offsetting соответственно, но если вы кодируете это с нуля, в частности, смещение будет излишним, если оно вам нужно только для того, что вы описываете в своем примере.

Вместо этого я бы рекомендовал запустить Inkscape или Illustrator, включить наложение сетки и нарисовать кривую Безье вокруг вашего эллипса. Сделайте так, чтобы это выглядело хорошо, затем проверьте, что такое координаты, и используйте это как достаточную надежную информацию для реализации в вашей программе холста. Вам, вероятно, не нужна математически жесткая корректность, пока люди не говорят «это выглядит неправильно», у вас все должно быть в порядке.

person Mike 'Pomax' Kamermans    schedule 19.04.2013
comment
Хорошие статьи у вас получились! Я согласен с тем, что они не могут быть совершенно правильными, но на моем примере это выглядит не так уж и неправильно, не так ли? Я не могу сделать их с помощью Inkscape и т. д., потому что хочу анимировать их в соответствии с эллипсом. Мое решение до сих пор имеет две разные функции для пути (начало, конец). Теперь, если бы я хотел иметь идеальные кривые, должен ли я использовать эллиптические дуги? — что тоже будет не правильно, потому что обрезано по краю - person soyuka; 19.04.2013
comment
Использование эллиптической дуги, вероятно, будет достаточно для вас, если вы сместите оба радиуса на одинаковую величину. Итак, если ваш эллипс (x,y,r1,r2) и вы хотите смещение более половины эллипса, скажем, 10px, вы должны использовать arc(x,y,r1+10,r2+10, pi,2 *pi), причем последние два обозначают начальный и конечный угол дуги. Проблема здесь в том, что если вы используете Canvas2D, единственная доступная в данный момент дуга — это дуга окружности. Команда эллиптической дуги (эллипс), наконец, находится в обновленном черновике (я один из тех, кто просил ее, очень рад, что она была добавлена ​​сейчас), но пока не поддерживается. - person Mike 'Pomax' Kamermans; 19.04.2013
comment
Я использую Raphael (svg без холста, поэтому путь A - rx ry x-axis-rotation large-arc-flag sweep-flag x y), но я попробую найти способ с этой информацией, спасибо. - person soyuka; 19.04.2013
comment
Здесь требуется нарисовать тонкую линию на внешнем краю поля постоянной ширины вокруг эллипса. Если это так, то поле может быть рассчитано как геометрическое место постоянной проекции нормали на эллипс - уравнение здесь. Я сомневаюсь, что такое геометрическое место является эллипсом; для эллипса с любым эксцентриситетом он будет стремиться к окружности по мере увеличения ширины поля. - person Beetroot-Beetroot; 20.04.2013
comment
Если вы можете заставить его работать, то результат должен быть хорошим, но потребуется много работы, чтобы написать уравнение, вычислить проекцию и построить геометрическое место. Поскольку уравнение является декартовым, а не полярным, вам, возможно, придется обрабатывать верхнюю/нижнюю и левую/правую дуги эллипса отдельно, иначе итеративные приращения будут стремиться к бесконечно малому. - person Beetroot-Beetroot; 20.04.2013

Мне удалось сделать эллиптическую дугу в соответствии с эллипсом и его границей. Затем я просто скрываю ненужную часть прямоугольником.

Вот функция:

function borderPath(ellipse, flag) {
    var flag = flag == undefined ? 1 : flag;

    var box = ellipse.paper.getBBox();

    var leftX = box.x;

    var rightX = box.x + box.width;

    var y = box.y + box.height/2;

    var rx = box.width/2;
    var ry = box.height/2;

    var p = "M "+ leftX + ", "+ y
    + " A "
    + rx + " " + ry
    + " 0 0 "+ flag +" "
    + rightX +", " + y;

    return p;
}
person soyuka    schedule 26.04.2013

Использование кривых Безье для рисования эллиптического пути может вызвать у вас головную боль. Как вы сказали в комментарии, вы используете дугу пути, которая хорошо работает с RaphaelJS.

Документацию обо всех ожидаемых значениях, особенно о флагах, можно найти по адресу http://www.svgbasics.com/arcs.html .

person lib3d    schedule 20.04.2013