Округление начала и конца контура - PullRequest
2 голосов
/ 17 июля 2010

Я использую следующий алгоритм для генерации контуров многоугольника:

void OGLSHAPE::GenerateLinePoly(std::vector<DOUBLEPOINT> &input, int width)
{
    OutlineVec.clear();
    if(input.size() < 2)
    {
        return;
    }


    if(connected)
    {
        input.push_back(input[0]);
        input.push_back(input[1]);
    }


    float w = width / 2.0f;

    //glBegin(GL_TRIANGLES);
    for( size_t i = 0; i < input.size()-1; ++i )
    {
        POINTFLOAT cur;
        cur.x = input[i].point[0];
        cur.y = input[i].point[1];


        POINTFLOAT nxt;


        nxt.x = input[i+1].point[0];
        nxt.y = input[i+1].point[1];

        POINTFLOAT b;
        b.x = nxt.x - cur.x;
        b.y = nxt.y - cur.y;

        b = normalize(b);



        POINTFLOAT b_perp;
        b_perp.x = -b.y;
        b_perp.y = b.x;


        POINTFLOAT p0;
        POINTFLOAT p1;
        POINTFLOAT p2;
        POINTFLOAT p3;

        p0.x = cur.x + b_perp.x * w;
        p0.y = cur.y + b_perp.y * w;

        p1.x = cur.x - b_perp.x * w;
        p1.y = cur.y - b_perp.y * w;

        p2.x = nxt.x + b_perp.x * w;
        p2.y = nxt.y + b_perp.y * w;

        p3.x = nxt.x - b_perp.x * w;
        p3.y = nxt.y - b_perp.y * w;

        OutlineVec.push_back(p0.x);
        OutlineVec.push_back(p0.y);
        OutlineVec.push_back(p1.x);
        OutlineVec.push_back(p1.y);
        OutlineVec.push_back(p2.x);
        OutlineVec.push_back(p2.y);

        OutlineVec.push_back(p2.x);
        OutlineVec.push_back(p2.y);
        OutlineVec.push_back(p1.x);
        OutlineVec.push_back(p1.y);
        OutlineVec.push_back(p3.x);
        OutlineVec.push_back(p3.y);



        // only do joins when we have a prv
        if( i == 0 ) continue;


        POINTFLOAT prv;
        prv.x = input[i-1].point[0];
        prv.y = input[i-1].point[1];

        POINTFLOAT a;
        a.x = prv.x - cur.x;
        a.y = prv.y - cur.y;

        a = normalize(a);

        POINTFLOAT a_perp;
        a_perp.x = a.y;
        a_perp.y = -a.x;

        float det = a.x * b.y  - b.x * a.y;
        if( det > 0 )
        {
            a_perp.x = -a_perp.x;
            a_perp.y = -a_perp.y;

            b_perp.x = -b_perp.x;
            b_perp.y = -b_perp.y;
        }

        // TODO: do inner miter calculation

        // flip around normals and calculate round join points
        a_perp.x = -a_perp.x;
        a_perp.y = -a_perp.y;

        b_perp.x = -b_perp.x;
        b_perp.y = -b_perp.y;

        size_t num_pts = 4;

        std::vector< POINTFLOAT> round( 1 + num_pts + 1 );
        POINTFLOAT nc;
        nc.x = cur.x + (a_perp.x * w);
        nc.y = cur.y + (a_perp.y * w);

        round.front() = nc;

        nc.x = cur.x + (b_perp.x * w);
        nc.y = cur.y + (b_perp.y * w);

        round.back() = nc;

        for( size_t j = 1; j < num_pts+1; ++j )
        {
            float t = (float)j/(float)(num_pts+1);
            if( det > 0 )
         {
             POINTFLOAT nin;
             nin = slerp2d( b_perp, a_perp, 1.0f-t );
             nin.x *= w;
             nin.y *= w;

             nin.x += cur.x;
             nin.y += cur.y;

             round[j] = nin;
         }
            else
         {
             POINTFLOAT nin;
             nin = slerp2d( a_perp, b_perp, t );
             nin.x *= w;
             nin.y *= w;

             nin.x += cur.x;
             nin.y += cur.y;

             round[j] = nin;
         }
        }

        for( size_t j = 0; j < round.size()-1; ++j )
        {

            OutlineVec.push_back(cur.x);
            OutlineVec.push_back(cur.y);


            if( det > 0 )
         {
             OutlineVec.push_back(round[j + 1].x);
             OutlineVec.push_back(round[j + 1].y);
             OutlineVec.push_back(round[j].x);
             OutlineVec.push_back(round[j].y);
         }
            else
         {

             OutlineVec.push_back(round[j].x);
             OutlineVec.push_back(round[j].y);

             OutlineVec.push_back(round[j + 1].x);
             OutlineVec.push_back(round[j + 1].y);
         }
        }
    }

}

POINTFLOAT multiply(const POINTFLOAT &a, float b)
{
    POINTFLOAT result;
    result.x = a.x * b;
    result.y = a.y * b;
    return result;
}

POINTFLOAT normalize(const POINTFLOAT &a)
{
    return multiply(a, 1.0f/sqrt(a.x*a.x+a.y*a.y));
}


POINTFLOAT slerp2d( const POINTFLOAT &v0, 
                   const POINTFLOAT &v1, float t )
{
    float dot = (v0.x * v1.x + v0.y * v1.y);

    if( dot < -1.0f ) dot = -1.0f;
    if( dot > 1.0f ) dot = 1.0f;

    float theta_0 = acos( dot );
    float theta = theta_0 * t;

    POINTFLOAT v2;
    v2.x = -v0.y;
    v2.y = v0.x;

    POINTFLOAT result;
    result.x = v0.x * cos(theta) + v2.x * sin(theta);
    result.y = v0.y * cos(theta) + v2.y * sin(theta);

    return result;
}

Я заметил, что приложения векторного рисования позволяют округлять начало и конец сегмента.Как я могу изменить алгоритм генерации моей строки, чтобы округлить начало и конец несвязанного сегмента?

См. Ниже пример того, что я имею в виду:

альтернативный текст http://img39.imageshack.us/img39/6029/capss.png

Спасибо

Ответы [ 3 ]

1 голос
/ 06 августа 2010

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

Пока концы не встречаются, используйте slerp2d (-b, b_perp, t); и slerp2d (-b, -b_perp, t); для начала (порядок терминов может нуждаться в замене) с помощью slerp2d (b, b_perp, t); и slerp2d (b, -b_perp, t); на конец.

Вы можете избежать повторного вычисления round.back (), потому что это все еще P0 (или P1 в зависимости от определителя), а round.front () - это предыдущий P2 или P3, который вы спрятали в OutlineVec. Расчет внутренней точки среза может помочь с этим, потому что он удалит другие точки.

0 голосов
/ 17 июля 2010

Если вы используете X / Motif, для этого есть поле в графическом контексте; Я забыл, что это такое, как это было давным-давно, когда я последний раз использовал его.

Я тоже не могу найти ничего стоящего в Интернете. Сожалею. Книги О'Рейли отлично обсуждают эту тему.

0 голосов
/ 17 июля 2010

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

По сути, вам нужна функция, которая имеет следующий прототип:

void DrawRoundedRectangle(Rectangle rect, Angle angle);

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

Я считаю, что GDI + способен сделать это.

Для какой платформы вы разрабатываете и какую библиотеку используете,могу ли я спросить?:)

Исходное сообщение

Алгоритм генерации строк может оставаться в основном одинаковым.Это код рендеринга, который должен соединять точки в виде кривых Безье, а не прямых линий.

Так что вам в основном нужна библиотека рендеринга Безье.Я использовал GDI + для этого в Windows.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...