Как рассчитать контрольные точки для гладкого пути с учетом набора точек? - PullRequest
14 голосов
/ 06 ноября 2011

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

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

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

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

     [path addQuadCurveToPoint:nextPoint controlPoint:WTF];
}

Ответы [ 2 ]

12 голосов
/ 10 ноября 2011

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

Путь Безье на 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.Промыть и повторить через массив точек.

4 голосов
/ 10 ноября 2011

Я не уверен, насколько это поможет, но мне пришлось сделать нечто подобное, чтобы реализовать изогнутый путь для заметок, которым нужно следовать в этом приложении (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);
}

Удачи!

...