Как вычислить контрольные точки для гладкого пути с учетом множества точек?

Я использую UIBezierPath, но этот вопрос касается контрольных точек для путей, а не чертежа. Учитывая набор точек, я могу отобразить путь. Тем не менее, мне не удалось выяснить, как вычислить контрольные точки, чтобы иметь гладкую линию, как в редакторе кривых фотографий (Как реализовать редактор Photoshop Curves в UIKit).

Самый близкий ответ, который я видел, здесь: как я могу отслеживать движение пальца при касании для рисования гладких кривых?

Однако я все еще не могу понять правильный расчет. Подводя итог в коде:

for (int i = 0; i< points; i++) 
{
     ...

     [path addQuadCurveToPoint:nextPoint controlPoint:WTF];
}

Ответы

Ответ 1

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

Путь безье на ios (и os x) под ним в основном представляет собой список команд и точек рисования. например:

[path moveTo:CGMakePoint(1,1)];
[path curveToPoint:(10,10) controPoint1:(3,7) controlPoint2:(4,1)];
[path curveToPoint:(10,10) controPoint1:(15,17) controlPoint2:(21,11)];
[path closePath];

Результаты в:

moveto (1,1)      
curveto (10,10) (3,7) (4,1) 
curveto (20,0) (15,17) (21,11)    
closepath 

Контрольные точки на пути безье управляют направлением и скоростью кривой из точки. Первая контрольная точка (cp) управляет направлением и скоростью кривой, выходящей из предыдущей точки, а второй cp управляет тем же самым для точки, в которой вы изогнуты. Для квадратичной кривой (то, что вы используете с помощью addQuadCurveToPoint: controlPoint:), обе эти точки одинаковы, как вы можете видеть в документах для метода здесь.

Получение гладкой кривой вдоль множества точек предполагает, что cp1 и cp2 коллинеарны друг другу и эта линия параллельна точкам на обоих концах этого сегмента.

Annotated curve

Это выглядит примерно так:

[path moveTo:2];
[path curveTo:3 controlPoint1:cp1 controlPoint2:cp2];

cp1 и cp2 могут быть вычислены путем выбора некоторой постоянной длины строки и выполнения некоторой геометрии (я забыл все мои уравнения линии прямо сейчас, но они легко googleable)

Переход к использованию # → # для обозначения сегмента и # → # (cp #), чтобы назначить контрольную точку для этого вызова курсового сегмента.

Следующий вопрос - сделать кривую гладкой из 2- > 3 сегмента, идущего в 3- > 4 сегмент. На этом этапе вашего кода вы должны иметь контрольную точку, рассчитанную для 2- > 3 (cp2). Учитывая вашу постоянную длину линии от ранее (это будет контролировать то, насколько резкой кривой вы получаете), вы можете вычислить cp1 для 3- > 4, получив точку коллинеар с 2- > 3 (cp2) и точкой 3 на диаграмме. Затем вычислите 3- > 4 (cp2), который колинирует с 3 > 4 (cp1) и параллелен линии, которая имеет форму 3 и 4. Промойте и повторите через ваш массив точек.

Ответ 2

Я не уверен, насколько это поможет, но мне пришлось сделать что-то подобное, чтобы реализовать изогнутый путь для заметок в этом приложении (www.app.net/hereboy). По сути, это путь с тремя кривыми.

Для этого я создал 4 точки на кривую, начальную точку, конечную точку и две контрольные точки с отметкой 25% и отметкой 75%.

Вот код, который я написал для этого:

//create points along the keypath for curve.
CGMutablePathRef curvedPath = CGPathCreateMutable();
const int TOTAL_POINTS = 3;
int horizontalWiggle = 15;

int stepChangeX = (endPoint.x - viewOrigin.x) / TOTAL_POINTS;
int stepChangeY = (endPoint.y - viewOrigin.y) / TOTAL_POINTS;

for(int i = 0; i < TOTAL_POINTS; i++) {
    int startX = (int)(viewOrigin.x + i * stepChangeX);
    int startY = (int)(viewOrigin.y + i * stepChangeY);

    int endX = (int)(viewOrigin.x + (i+1) * stepChangeX);
    int endY = (int)(viewOrigin.y + (i+1) * stepChangeY);

    int cpX1 = (int)(viewOrigin.x + (i+0.25) * stepChangeX);
    if((i+1)%2) {
        cpX1 -= horizontalWiggle;
    } else {
        cpX1 += horizontalWiggle;
    }
    int cpY1 = (int)(viewOrigin.y + (i+0.25) * stepChangeY);

    int cpX2 = (int)(viewOrigin.x + (i+0.75) * stepChangeX);
    if((i+1)%2) {
        cpX2 -= horizontalWiggle;
    } else {
        cpX2 += horizontalWiggle;
    }
    int cpY2 = (int)(viewOrigin.y + (i+0.75) * stepChangeY);

    CGPathMoveToPoint(curvedPath, NULL, startX, startY);
    CGPathAddCurveToPoint(curvedPath, NULL, cpX1, cpY1, cpX2, cpY2, endX, endY);
}

Удачи!