Нарисуйте изогнутую линию от края дуги

Вот скриншот того, что я делаю. В настоящее время я застрял в рисовании изогнутых границ в этом прямоугольнике.

naah

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

neeh

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

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

niih

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

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

void drawArc(int x, int y,
             int startAngle, int endAngle,
             uint32_t radiusX, uint32_t radiusY,
             int border_x, int border_y,
             const rgb color,
             const rgb bcX, const rgb bcY,
             uint8_t opacity)
{
    if (radiusX <= 0 || radiusY <= 0) return;

    static constexpr float DTR = 3.14159 / 180;

    float cx, cy;
    int step;

    static std::vector<float> verts;
    static std::vector<uint8_t> colors;

    if (startAngle < endAngle)
    {
        step = +1;
        ++ endAngle;
    } else
    {
        step = -1;
        -- endAngle;
    }

    verts.clear();
    colors.clear();

    verts.push_back(x);
    verts.push_back(y);

    colors.push_back(color[R]);
    colors.push_back(color[G]);
    colors.push_back(color[B]);
    colors.push_back(opacity);

    while (startAngle != endAngle)
    {
        cx = cos(DTR * startAngle) * radiusX;
        cy = sin(DTR * startAngle) * radiusY;

        verts.push_back(x + cx);
        verts.push_back(y - cy);

        colors.push_back(color[R]);
        colors.push_back(color[G]);
        colors.push_back(color[B]);
        colors.push_back(opacity);

        startAngle += step;
    }

    drawElements(GL_POLYGON, sizeof(arcIndices) / sizeof(arcIndices[0]), GL_FLOAT,
                 &verts[0], &colors[0], &arcIndices[0]);

    if (border_x != 0 || border_y != 0)
    {
        //remove (x, y)
        verts.erase(verts.begin(), verts.begin() + 2);

//        float px, py;
//
//        px = *(verts.begin() + 0);
//        py = *(verts.begin() + 1);
//
//        glPointSize(5);
//
//        glBegin(GL_POINTS);
//
//        glColor3ub(0,0,255);
//        glVertex2i(px, py);
//
//        px = *(verts.end() - 2);
//        py = *(verts.end() - 1);
//
//        glColor3ub(255,0,0);
//        glVertex2i(px , py);
//        glEnd();

        //attempting to reuse the edges
        //I think the last vertices are opposed
        //that's why I got a crossed out lines??
        for (int i = 0;i <= 90; ++i)
        {
            verts.push_back(verts[i + 0] + border_x);
            verts.push_back(verts[i + 1] + border_y);

            colors.push_back(bcX[R]);
            colors.push_back(bcX[G]);
            colors.push_back(bcX[B]);
            colors.push_back(opacity);
        }

        //91 = steps from 0-90 degree revolution
        //182 = 91 * 2
        unsigned int index[182 + 91 * 2];
        for (int i = 0;i < 182 + 91 * 2; ++i)
            index[i] = i;

        drawElements(GL_LINE_LOOP, verts.size() / 2, GL_FLOAT,
                    &verts[0], &colors[0], &index[0]);
    }

}

Изменить:

Разве я не могу просто повторно использовать предварительно вычисленные (x, y) раньше?

нет

Извините за слишком много изображений

Красные точки предварительно рассчитаны (x, y), о которых я говорю, и просто добавляют к этому следующую дугу.

Я собираюсь визуализировать многие из них, поэтому мне нужно как можно эффективнее (без слишком большого использования функций триго).

Обновление:

И вот результат, который я получил от использования stencil buffer, как предложил Andon M. Coleman:

nuuh

Кстати, как видите, я пытаюсь эмулировать свой собственный пользовательский интерфейс с помощью OpenGL: D


person mr5    schedule 11.12.2013    source источник
comment
Рассматривали ли вы использование буфера трафарета, чтобы буквально вырезать области, которые покрывает ваша серая фигура? Вам придется немного изменить порядок рисования вещей, но вам не придется использовать неприятные примитивы линий.   -  person Andon M. Coleman    schedule 11.12.2013
comment
@AndonM.Coleman AndonM.Coleman Я мог бы использовать его, когда применяю текстуру из этого прямоугольника, но не дорого ли это?   -  person mr5    schedule 11.12.2013
comment
@ AndonM.Coleman, можешь дать ссылку, как мне это сделать? Я думаю, что это интересно и удовлетворит мои будущие потребности, когда я начну накладывать на него текстуру.   -  person mr5    schedule 11.12.2013
comment
На самом деле ненамного дороже, чем глубинное тестирование. Эти две вещи очень тесно связаны. Это далеко не идеальное решение, но вам не придется беспокоиться о совпадении вершин внешней части закругленного квадрата и внутренней части формы контура. Я не знаю ни одной ссылки, которая описывала бы, как это сделать, мне пришлось бы написать какой-то псевдокод для демонстрации... Я могу сделать это завтра, если вам интересно.   -  person Andon M. Coleman    schedule 11.12.2013
comment
@ AndonM.Coleman По какой-то причине я хочу нарисовать его так же, как прямоугольник HTML, например, границу с острыми краями вместе с закругленными краями, в то время как внутренний прямоугольник отличается от внешнего прямоугольника (граница)   -  person mr5    schedule 11.12.2013
comment
@ AndonM.Coleman Хорошо, мне действительно интересно. Я буду ждать этого. Спасибо   -  person mr5    schedule 11.12.2013


Ответы (2)


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

glClearStencil (0x0);
glClear        (GL_STENCIL_BUFFER_BIT);

glEnable       (GL_STENCIL_TEST);
glStencilFunc  (GL_ALWAYS, 0x0, 0x0);

// Add 1 to stencil buffer at every location the object to be bordered is visible
glStencilOp    (GL_KEEP, GL_KEEP, GL_INCR);

// Draw your grey object

// Only draw the red border where the grey object was never drawn (stencil = 0x0)
glStencilFunc  (GL_EQUAL, 0x0, 0xff);

// Draw your red quarter circles

glDisable     (GL_STENCIL_TEST);

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

Конечно, чтобы это работало, ваш формат пикселей должен иметь буфер трафарета. Мне придется оставить эту часть на ваше усмотрение, потому что процесс настройки зависит от реализации.

person Andon M. Coleman    schedule 11.12.2013
comment
Если я перейду наверх, glEnable(GL_STENCIL_TEST) это сработает, но если я буду следовать тому, что вы опубликовали, это не сработает. Во всяком случае, теперь это работает очень хорошо! но если я попытаюсь добавить текстуру, она не появится. Может быть, мне просто нужно предварительно рассчитать порядок моих рисунков. Может быть, я оставлю на мгновение setup for pixel format, о котором вы сказали, пока я не вижу причин для беспокойства. Большое спасибо вам :D stencil buffer это одна из лучших вещей, которые я обнаружил в OpenGL - person mr5; 12.12.2013
comment
@ mr5: А, хорошая мысль. Это совершенно вылетело из головы, я сделал пару изменений, которые должны работать лучше. - person Andon M. Coleman; 12.12.2013
comment
Также он работает даже без glStencilFunc (GL_ALWAYS, 0x0, 0x0);, почему? - person mr5; 13.12.2013
comment
Потому что вы очищаете буфер трафарета. Он повторно использует последний набор функций трафарета, который был EQUAL 0x0... это проходит только тогда, когда буфер трафарета уже очищен. Как правило, хорошей идеей является использование GL_ALWAYS, когда вы хотите нарисовать что-то, что безоговорочно записывает в буфер трафарета, таким образом, это будет работать, даже если буфер трафарета не был очищен непосредственно перед рисованием. - person Andon M. Coleman; 13.12.2013

GL_POLYGON предназначен только для выпуклых многоугольников.

Соедините вершины внутреннего и внешнего радиусов, чтобы сформировать четырехугольники/треугольники:

частичный тор

#include <GL/glut.h>
#include <cmath>

void Torus2d
    ( 
    float angle,            // starting angle in radians
    float length,           // length of arc in radians, >0
    float radius,           // inner radius, >0
    float width,            // width of torus, >0
    unsigned int samples    // number of circle samples, >=3
    )
{
    if( samples < 3 ) samples = 3;
    const float outer = radius + width;
    glBegin( GL_QUAD_STRIP );
    for( unsigned int i = 0; i <= samples; ++i )
    {
        float a = angle + ( i / (float)samples ) * length;
        glVertex2f( radius * cos( a ), radius * sin( a ) );
        glVertex2f( outer * cos( a ), outer * sin( a ) );
    }
    glEnd();
}

void display()
{
    glClear( GL_COLOR_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    double w = glutGet( GLUT_WINDOW_WIDTH );
    double h = glutGet( GLUT_WINDOW_HEIGHT );
    double ar = w / h;
    glOrtho( -4 * ar, 4 * ar, -4, 4, -1, 1);

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    glColor3ub( 255, 0, 0 );
    Torus2d( 0, 1.57079633, 2, 1, 20 );

    glutSwapBuffers();
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
    glutInitWindowSize( 640, 480 );
    glutCreateWindow( "GLUT" );
    glutDisplayFunc( display );
    glutMainLoop();
    return 0;
}
person genpfault    schedule 11.12.2013
comment
Я на самом деле запутался в параметрах, которые будут переданы в Torus2D. Можете ли вы объяснить немного? - person mr5; 11.12.2013
comment
@ mr5: отредактировано. angle - это то, как далеко против часовой стрелки должен начаться тор. length - это длина сегмента тора. radius — внутренний радиус тора. width - это ширина тора. Чем больше samples, тем лучше будет выглядеть тор за счет большей геометрии. - person genpfault; 11.12.2013
comment
Я немного запутался в этих тригонометрических функциях. В моем случае радиусы дуг разделены, т.е. rX, rY. Как я могу изменить его, чтобы достичь этого? Разве я не могу снова избавиться от этих триго? Разве я не могу просто повторно использовать предварительно рассчитанные x, y раньше? - person mr5; 11.12.2013