Наложение полигонов на 2-мерный массив
От: Atilla Россия  
Дата: 30.11.02 13:42
Оценка:
Вот здесь
Автор: Demiurg
Дата: 29.11.02
просили этот код.
Выкладываю:


#ifndef _FIGURES_H_
#define _FIGURES_H_

// FIGURES:

// это многомерная точка. Раньше я юзал обычный std::vector<>, но потом меня задолбало помнить, что p[0] - это иксовая
// координата, p[1] - игрековая и т.д.  :)
template<class T>
struct point
{
    std::vector<T> p;

    point(){}
    point(size_t n): p(n) {}
    point(T x, T y): p(2) {p[0]=x; p[1]=y;}
    point(std::vector<T> P): p(P) {}
    point(const point& pt): p(pt.p) {}
    ~point(){}

    point& operator= (const point& pt){p=pt.p; return *this;}
    T& x() {return p[0];}
    const T& x() const {return p[0];}
    T& y() 
    {
        assert(p.size()>1);
        return p[1];
    }
    const T& y() const
    {
        assert(p.size()>1);
        return p[1];
    }
    T& z() 
    {
        assert(p.size()>2);
        return p[2];
    }
    const T& z() const
    {
        assert(p.size()>2);
        return p[2];
    }
};

// ну это - так, для удобства
typedef point<size_t> PixelPoint;
typedef point<double> DoublePoint;
typedef point<float> FloatPoint;

// в общем, фигура будет массивом этих структур. Каждая линия (отрезок) задается точкой в которой она начинается,
// длиной линии. Все линии горизонтальные (меняется только координата x, начало отрезка - слева)
struct figure_line
{
    PixelPoint start;
    size_t length;
    figure_line(){}
    figure_line(size_t ndims):start(ndims, 0), length(1){}
    figure_line(const figure_line& f): start(f.start), length(f.length){}
    ~figure_line(){}
};

typedef std::vector<figure_line> figure;


// Вот оно! По полигону (массиву вершин с координатами возможно вещественного типа) строит массив линий.
// работает оно так: для каждой горизонтальной прямой, которая сечет полигон, считает пересечения со сторонами полигона.
template<class T>
figure polygon(const std::vector<point<T> >& poly)
{
    size_t n=poly.size();
#ifndef NDEBUG
    {
        for(size_t i=0;i<n;i++)
            assert(poly[i].p.size()==2);
    }
#endif
    double ymin=poly[0].y(), ymax=poly[0].y();
    size_t i;
    for(i=1;i<n;i++)
        if(ymin>poly[i].y())
            ymin=poly[i].y();
        else if(ymax<poly[i].y())
            ymax=poly[i].y();

    std::vector<DoublePoint> P(poly.size());
    for(i=0;i<n;i++)
    {
        P[i]=DoublePoint(poly[i].x(), poly[i].y());
    }
    if(P[0].x()!=P[n-1].x() || P[0].y()!=P[n-1].y())
        P.push_back(P[0]);

    if(ymin<1) 
        ymin=0;
    else
        ymin-=1;

    if(ymax<0)
        return figure();
    else
        ymax+=1;

    std::vector<double> k(n), h(n);
    for(i=0;i<n;i++)
    {
        k[i]=(P[i+1].x()-P[i].x())/(P[i+1].y()-P[i].y());
        h[i]=P[i].x()-P[i].y()*k[i];
    }
    figure res;
    figure_line l(2);
    res.reserve(n*int(ymax-ymin+2));
    for(size_t y=(size_t)floor(ymin), yend=(size_t)ceil(ymax);y<yend;y++)
    {
        std::vector<int> cross;
        cross.reserve(n);
        for(i=0;i<n;i++)
            if(P[i].y()>y && P[i+1].y()<y || P[i].y()<y && P[i+1].y()>y)
                cross.push_back((int)floor(h[i]+y*k[i]+0.5));
            else if(P[i].y()==y)
                cross.push_back((int)floor(P[i].x()+0.5));

        std::sort(cross.begin(), cross.end());

        size_t m=cross.size();

        l.start.y()=y;
        for(i=0;i<m-m%2;i+=2)
            if(cross[i+1]>=0)
            {
                l.start.x()=cross[i]<0 ? 0 : cross[i];
                l.length=cross[i+1]+1-l.start.x();
                res.push_back(l);
            }
        if(m>0)
            if(m%2!=0 && cross[m-1]>=0)
            {
                l.start.x()=cross[m-1];
                l.length=1;
                res.push_back(l);
            }
    }
    return res;
}

// делает то же самое, но для эллипса
figure ellipse(double x, double y, double a, double b)
{
    assert(a>0 && b>0);
    size_t n=size_t(b*2+1.5);
    figure res;
    res.reserve(n);
    size_t y0=0;
    if(y-b+0.5>=0)
        y0=size_t(y-b+0.5);
    else
        n-=size_t(b-y+0.5);

    double b2_1=1/(b*b);
    figure_line l(2);
    for(size_t i=0;i<n;i++)
    {
        l.start.y()=y0+i;
        double h=float(y0)+i-y;
        double w=sqrt(1.-h*h*b2_1)*a;
        if(finite(w))
            if(x+w+1.5>=0)
            {
                l.start.x()=( x-w+0.5 >=0 ? size_t(x-w+0.5) : 0);
                l.length=size_t(x+w+1.5)-l.start.x();
                if(l.length>0)
                    res.push_back(l);
            }
    }
    return res;
}


// чтобы побыстрее работать с figure, ее нужно вытянуть в линейный массив.
// ну т.е. N-мерные координаты пересчитываются в 1-мерные

typedef std::pair<size_t, size_t> split_line;

template<class Image>
std::vector<split_line> lining(const figure& fig, const Image& img)
// второй параметр - N-мерный массив
{
    assert(fig.size()>0);
#ifndef NDEBUG
    {
        PixelPoint min, max;
        circumscribe(fig, min, max);
        img.index(max.p);
    }
#endif // DEBUG
    std::vector<split_line> res(fig.size());
    PixelPoint pt(img.dims().size());
    for(size_t i=0;i<fig.size();i++)
    {
        assert(img.dims().size()>=fig[i].start.p.size());
        const PixelPoint& pt0=fig[i].start;
        std::fill(pt.p.begin(), pt.p.end(), 0);
        std::copy(pt0.p.begin(), pt0.p.end(), pt.p.begin());
        res[i].first=img.index(pt.p);
        res[i].second=fig[i].length;
#ifndef NDEBUG
        pt.x()+=fig[i].length-1;
        img.index(pt.p);
#endif
    }
    return res;
}


// эта штука описывает вокруг фигуры паралелепипед.
// Ну, т.е. считает наибольшие и наименьшие значения координат :))
void circumscribe(const figure& fig, PixelPoint& minbounds, PixelPoint& maxbounds)
{
    assert(fig.size()>0);
    size_t n=fig[0].start.p.size();
#ifndef NDEBUG
    {
        for(size_t i=1;i<fig.size();i++)
            assert(fig[i].start.p.size()==n);
    }
#endif // DEBUG
    minbounds.p.resize(n);
    maxbounds.p.resize(n);
    std::copy(fig[0].start.p.begin(), fig[0].start.p.end(), minbounds.p.begin());
    std::copy(fig[0].start.p.begin(), fig[0].start.p.end(), maxbounds.p.begin());
    maxbounds.p[0]+=fig[0].length-1;
    for(size_t i=1;i<fig.size();i++)
    {
        const figure_line& fl=fig[i];
        for(size_t j=0;j<n;j++)
        {
            size_t h=fl.start.p[j];
            if(minbounds.p[j]>h)
                minbounds.p[j]=h;
            if(j==0) h+=fl.length-1;
            if(maxbounds.p[j]<h)
                maxbounds.p[j]=h;
        }
    }
}

#endif


В качестве типа Image функции lining() надо использовать типы образованные от шаблона array<>, который я уже
выкладывал здесь
Автор: Atilla
Дата: 29.11.02
, либо чего-нибудь в этом роде.

Ну и как все это юзать:

IntegerArray d(2); d[0]=1000, d[1]=1000;
array<float> img(d, 0); // делаем картинку 1000х1000, заполняем нулями

figure fig=ellipse(10, 10, 10, 10); // "рисуем" круг радиусом 10, центром (10,10)
std::vector<split_line> l=lining(fig, img);

PixelPoint pt(10, 10);
for(;pt.y()<1000;pt.y()+=30)
   for(pt.x()=10;pt.x()<1000;pt.x()+=30)
   {
      size_t offset=img.index(pt.p); // считаем 1-мерный индекс точки pt
      for(size_t i=0;i<l.size();i++) // пробегаем по всем линиям
         for(size_t j=offset+l[i].first, jend=j+l[i].second;j<jend;j++) // по всем точкам линии
           img[j]=1; // в каждой точке круга - единица
   }

// и того, на выходе в картинке img единицами нарисованы круги радиусом 10 и с центрами в точках (10,10), (10,40) и т.д.

d[0]=500; d[1]=500;
array<float> img2(d, 0); // делаем другую картинку, поменьше

std::vector<DoublePoint> poly(3);
poly[0].x()=100, poly[0].y()=10;
poly[1].x()=300, poly[1].y()=400;
poly[2].x()=50, poly[2].y()=450;
fig=polygon(poly); // рисуем такой вот треугольник

l=lining(fig, img);
std::vector<split_line> l2=lining(fig, img2);

// у меня еще был специально обученный класс типа slice_array, только с итераторами, но он ща с этим
// кодом не совместим :( 

for(size_t i=0;i<l.size();i++) // пробегаем по всем линиям треугольника
   for(size_t j1=l[i].first, j1end=j1+l[i].second,
              j2=l2[i].first;j1<j1end;j1++, j2++) // по всем точкам линии
      img2[j2]=img[j1]; // копируем ту часть 1-й картинки, которая покрыта треугольником во 2-ю картинку


код примера скорее всего с ошибками, но сами функции проверены электроникой
Кр-ть — с.т.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.