//диаграмма Вороного + вывод на битмап
#include "s_bmp.h"
#include <iostream>
#include <cmath>
#include <algorithm>
#include <random>
#include <optional>
#include <utility>
int main()
{
auto file_name = "DV.bmp";
s_bmp bmp;
bmp.SetSize(200,200);
constexpr size_t points_count=20;
constexpr bool borders=true;
constexpr RGBTRIPLE borders_color{40,40,40};
const auto wid=bmp.Wid();
const auto hig=bmp.Hig();
std::random_device rd;
std::mt19937 gen{rd()};
std::uniform_int_distribution<unsigned int> dist_x(0, wid-1);
std::uniform_int_distribution<unsigned int> dist_y(0, hig-1);
std::uniform_int_distribution<unsigned int> dist_r(50, 230);//0...255
std::uniform_int_distribution<unsigned int> dist_g(50, 230);//0...255
std::uniform_int_distribution<unsigned int> dist_b(50, 230);//0...255
using s_point = s_bmp::s_point;
using s_point_colored = s_bmp::s_point_colored;
using COORDTYPE = s_bmp::COORDTYPE;
std::vector<s_point_colored> points(points_count);
for(auto& p : points)
{
p.p.x=dist_x(gen);
p.p.y=dist_y(gen);
p.color.R=dist_r(gen);
p.color.G=dist_g(gen);
p.color.B=dist_b(gen);
}
if(!points.empty())
{
//хранит квадраты расстояний
std::vector<COORDTYPE> distances(points.size());
auto GetMinDistanceIndex=[&distances,&points,wid,hig](s_point p_pix)->size_t
{
//assert(distances.size()==points.size());
for(size_t i=0; i<distances.size(); ++i)
{
auto dx1=std::abs(p_pix.x - points[i].p.x);
auto dx2=wid-dx1;
auto dx = std::min(dx1,dx2);
auto dy1=std::fabs(p_pix.y - points[i].p.y);
auto dy2=hig-dy1;
auto dy = std::min(dy1,dy2);
distances[i] = dx*dx+dy*dy;
}
//assert(points.size());
auto it_d = std::min_element(distances.begin(),distances.end());
return it_d - distances.begin();
};
std::vector<size_t> distances_prev_y;
if(borders)
{
distances_prev_y.reserve(wid);
}
for(COORDTYPE y = 0; y<hig; ++y)
{
std::optional<size_t> index_L;
size_t p_index{};
for(COORDTYPE x = 0; x<wid; index_L=p_index, ++x)
{
const s_point p_pix{x,y};
p_index =GetMinDistanceIndex(p_pix);
if(borders)
{
//точка левее границы
if(index_L)
{
if(*index_L!=p_index){bmp.DrawPoint({p_pix.x-1,p_pix.y}, borders_color); continue;}
}
//точка выше границы
if(y==0)
{
distances_prev_y[x]=p_index;
}
else
{
auto prev_y_index=std::exchange(distances_prev_y[x],p_index);
s_point pT{x,y-1};
if(prev_y_index!=p_index){bmp.DrawPoint(p_pix, borders_color); continue;}
}
}
//точка с цветом локального центра
bmp.DrawPoint(p_pix, points[p_index].color);
}
}
}
//локальные центры
#if 0
for(auto& p : points)
{
bmp.DrawEllipse(p.p, 1, {});
}
#endif
bmp.SaveToFile(file_name);
}
#pragma once
#include <cstdint>
using BYTE = uint8_t;
using WORD = uint16_t;
using DWORD = uint32_t;
using LONG = uint32_t;
#pragma pack(push, 1)
struct BITMAPFILEHEADER
{
WORD bfType;//'B', 'M'
DWORD bfSize;//574
WORD bfReserved1;//==0
WORD bfReserved2;//==0
DWORD bfOffBits;//54
};
#pragma pack(pop)
static_assert( sizeof(BITMAPFILEHEADER) == 14);
#pragma pack(push, 1)
struct BITMAPINFOHEADER
{
DWORD biSize;//40
LONG biWidth;//17
LONG biHeight;//10
WORD biPlanes;//1
WORD biBitCount;//24
DWORD biCompression;//0
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
#pragma pack(pop)
static_assert( sizeof(BITMAPINFOHEADER) == 40);
#pragma pack(push, 1)
struct RGBTRIPLE
{
BYTE B;
BYTE G;
BYTE R;
};
#pragma pack(pop)
static_assert( sizeof(RGBTRIPLE) == 3);
#pragma once
#include "bitmap_headers.h"
#include <vector>
#include <string>
#include <cmath>
#include <cassert>
#include <climits>
#include <fstream>
class s_bmp
{
public:
using COORDTYPE = float;
inline static const COORDTYPE accur = 0.00001;
private:
using PHISCOORDTYPE=uint32_t;
PHISCOORDTYPE m_wid{};
PHISCOORDTYPE m_hig{};
std::vector<RGBTRIPLE> m_pixels;
public:
struct s_point
{
COORDTYPE x;
COORDTYPE y;
};
struct s_point_colored
{
s_point p;
RGBTRIPLE color;
};
struct s_line
{
s_point p1;
s_point p2;
bool IsHor()const
{
return std::fabs(p1.y-p2.y)<accur;
}
bool IsVer()const
{
return std::fabs(p1.x-p2.x)<accur;
}
bool IsPoint()const
{
return IsHor() && IsVer();
}
auto DeltaX()const
{
return p2.x-p1.x;
}
auto DeltaY()const
{
return p2.y-p1.y;
}
};
struct s_rect
{
s_point p1;
s_point p2;
auto Wid()const
{
return p2.x-p1.x;
}
auto Hig()const
{
return p2.y-p1.y;
}
};
public:
auto Wid()const{return m_wid;}
auto Hig()const{return m_hig;}
auto Center()const{return s_point{Wid()/2.0f, Hig()/2.0f};}
auto Rect()const
{
return s_rect{{0,0},{ static_cast<COORDTYPE>(Wid()-1), static_cast<COORDTYPE>(Hig()-1)}};
}
void SetSize(PHISCOORDTYPE w, PHISCOORDTYPE h)
{
m_wid = std::min(w,PHISCOORDTYPE{400});
m_hig = std::min(h,PHISCOORDTYPE{400});
m_pixels.resize(m_wid*m_hig, RGBTRIPLE{0,0,0});
}
void DrawPoint(s_point p, RGBTRIPLE color)
{
auto x_ph=static_cast<PHISCOORDTYPE>(p.x);
if(x_ph>=Wid())return;
auto y_ph=static_cast<PHISCOORDTYPE>(p.y);
if(y_ph>=Hig())return;
auto plain = map_xy_to_plain(x_ph,y_ph);
assert(plain<m_pixels.size());
m_pixels[plain]=color;
}
void DrawPoint(s_point_colored p)
{
DrawPoint(p.p, p.color);
}
void DrawLine(s_line line, RGBTRIPLE color)
{
/*if(line.IsHor()) [[unlikely]]//C++20
{
//слева направо
if(line.p1.x > line.p2.x)
{
std::swap(line.p1, line.p2);
}
for(PHISCOORDTYPE x0 = 0; x0 <= (line.p2.x-line.p1.x); ++x0)
{
DrawPoint({x0+line.p1.x,line.p1.y},color);
}
}
else if(line.IsVer()) [[unlikely]]
{
//сверху вниз
if(line.p1.y > line.p2.y)
{
std::swap(line.p1, line.p2);
}
for(PHISCOORDTYPE y0 = 0; y0 <= (line.p2.y-line.p1.y); ++y0)
{
DrawPoint({line.p1.x,y0+line.p1.y},color);
}
}
else [[likely]]*/
{
if( std::fabs(line.DeltaX()) >= std::fabs(line.DeltaY()))
{
if(line.p1.x > line.p2.x)
{
std::swap(line.p1, line.p2);
}
auto k = (line.p2.y-line.p1.y)/(line.p2.x-line.p1.x);
//линия - слева направо
for(PHISCOORDTYPE x0 = 0; x0 <= (line.p2.x-line.p1.x); ++x0)
{
auto y0 = x0 * k;
DrawPoint({ x0+line.p1.x, y0+line.p1.y},color);
}
}
else
{
if(line.p1.y > line.p2.y)
{
std::swap(line.p1, line.p2);
}
auto k = (line.p2.x-line.p1.x)/(line.p2.y-line.p1.y);
//линия - сверху вниз
for(PHISCOORDTYPE y0 = 0; y0 <= (line.p2.y-line.p1.y); ++y0)
{
auto x0 = y0 * k;
DrawPoint({x0+line.p1.x , y0+line.p1.y},color);
}
}
}
}
void DrawRect(s_rect rect, RGBTRIPLE color)
{
if( std::fabs(rect.Wid()) >= std::fabs(rect.Hig()))
{
if(rect.p1.x > rect.p2.x)
{
std::swap(rect.p1, rect.p2);
}
//рисуем вертикальные линии
for(PHISCOORDTYPE x0 = 0; x0 <= (rect.p2.x-rect.p1.x); ++x0)
{
DrawLine({{x0+rect.p1.x,rect.p1.y},{x0+rect.p1.x,rect.p2.y}},color);
}
}
else
{
if(rect.p1.y > rect.p2.y)
{
std::swap(rect.p1, rect.p2);
}
//рисуем горизонтальные линии
for(PHISCOORDTYPE y0 = 0; y0 <= (rect.p2.y-rect.p1.y); ++y0)
{
DrawLine({{rect.p1.x,y0+rect.p1.y},{rect.p2.x,y0+rect.p1.y}},color);
}
}
}
void DrawEllipse(s_point center, COORDTYPE R , RGBTRIPLE color)
{
//center={30,10};
const auto R2= R*R;
for(COORDTYPE x = 0; ; ++x)
{
COORDTYPE acc = 0.5f;
auto y = std::sqrt(R2 - x*x);
DrawPoint({ +x + center.x + acc, +y + center.y + acc},color);
DrawPoint({ +y + center.x + acc, +x + center.y + acc},color);
DrawPoint({ -x + center.x - acc, +y + center.y + acc},color);
DrawPoint({ -y + center.x - acc, +x + center.y + acc},color);
DrawPoint({ +x + center.x + acc, -y + center.y - acc},color);
DrawPoint({ +y + center.x + acc, -x + center.y - acc},color);
DrawPoint({ -x + center.x - acc, -y + center.y - acc},color);
DrawPoint({ -y + center.x - acc, -x + center.y - acc},color);
if(y < x)break;
}
}
private:
size_t map_xy_to_plain(size_t x, size_t y)const
{
return y*m_wid+x;
}
public:
public:
void SaveToFile(std::string file_name)const
{
std::ofstream file(file_name);
BITMAPFILEHEADER FILE{};
FILE.bfType = 0x4d42; //'M','B'
file.write((const char*) &FILE, sizeof FILE);
BITMAPINFOHEADER INFO{};
file.write((const char*) &INFO, sizeof INFO);
FILE.bfOffBits=file.tellp();
auto pix_line_no_padding = sizeof( decltype(m_pixels)::value_type ) * m_wid;
constexpr size_t pad_step=4;
//%4 pad
//0 +0
//1 +3
//2 +2
//3 +1
auto pix_padding=0;
if(pix_line_no_padding%pad_step)//pix_line_no_padding & 0b11
{
pix_padding = pad_step - pix_line_no_padding%pad_step;
}
constexpr unsigned char zeros[pad_step]{};
//пикселы
for(size_t y=m_hig; y-->0;)
{
const RGBTRIPLE* beg = m_pixels.data() + y*m_wid;
const RGBTRIPLE* end = beg+m_wid;
file.write((const char*) beg, pix_line_no_padding);
//паддинг
if(pix_padding)
{
file.write((const char*) &zeros[0], pix_padding);
}
}
FILE.bfSize=file.tellp();
INFO.biSize = sizeof INFO;
INFO.biWidth = {m_wid};
INFO.biHeight = {m_hig};
INFO.biPlanes = 1;
INFO.biBitCount = {sizeof(decltype(m_pixels)::value_type) * CHAR_BIT}; //24;
//INFO.biCompression == 0;
file.seekp(0) ;
file.write((const char*) &FILE, sizeof FILE);
file.write((const char*) &INFO, sizeof INFO);
}
//тест точек
static void test0(bool MakeFilesEmpty)
{
auto file_name = std::string{__func__}+".bmp";
if(MakeFilesEmpty)
{
std::ofstream{file_name};
return;
}
s_bmp bmp;
bmp.SetSize(111,300);
const auto center = bmp.Center();
const auto rect = bmp.Rect();
bmp.DrawPoint( {10,30}, {0,255,0} );
bmp.SaveToFile(file_name);
}
//тест линий
static void test1(bool MakeFilesEmpty)
{
auto file_name = std::string{__func__}+".bmp";
if(MakeFilesEmpty)
{
std::ofstream{file_name};
return;
}
s_bmp bmp;
bmp.SetSize(111,300);
const auto center = bmp.Center();
const auto rect = bmp.Rect();
bmp.DrawLine( {{10,30},{10,30}}, {0,255,0} );
bmp.DrawLine( {{center.x,center.y-2000},{center.x,center.y+2000}} , {255,100,100} );
bmp.DrawLine( {{center.x-2000,center.y},{center.x+2000,center.y}} , {255,100,100} );
bmp.DrawLine( {center,{center.x+50,center.y-1000}} , {0,255,0} );//вправо вверх 60*
bmp.DrawLine( {center,{center.x+500,center.y-500}} , {0,255,0} );//вправо вверх 45*
bmp.DrawLine( {center,{center.x+500,center.y-200}} , {0,255,0} );//вправо вверх 30*
bmp.DrawLine( {center,{center.x+50,center.y+1000}} , {0,255,0} );//вправо вниз 60*
bmp.DrawLine( {center,{center.x+500,center.y+500}} , {0,255,0} );//вправо вниз 45*
bmp.DrawLine( {center,{center.x+500,center.y+200}} , {0,255,0} );//вправо вниз 30*
bmp.DrawLine( {center,{center.x-50,center.y-1000}} , {0,255,0} );//влево вверх 60*
bmp.DrawLine( {center,{center.x-500,center.y-500}} , {0,255,0} );//влево вверх 45*
bmp.DrawLine( {center,{center.x-500,center.y-200}} , {0,255,0} );//влево вверх 30*
bmp.DrawLine( {center,{center.x-50,center.y+1000}} , {0,255,0} );//влево вниз 60*
bmp.DrawLine( {center,{center.x-500,center.y+500}} , {0,255,0} );//влево вниз 45*
bmp.DrawLine( {center,{center.x-500,center.y+200}} , {0,255,0} );//влево вниз 30*
bmp.SaveToFile(file_name);
}
//тест прямоугольников
static void test2(bool MakeFilesEmpty)
{
auto file_name = std::string{__func__}+".bmp";
if(MakeFilesEmpty)
{
std::ofstream{file_name};
return;
}
s_bmp bmp;
bmp.SetSize(111,300);
const auto center = bmp.Center();
const auto rect = bmp.Rect();
bmp.DrawRect( {{rect.p1.x-20,rect.p1.y-20},{rect.p2.x+20,rect.p2.y+20}} , {0,255,0} );//выход за все границы
// bmp.DrawRect( {{rect.p1.x,rect.p1.y},{rect.p2.x,rect.p2.y}} , {0,255,0} );//по границе
bmp.DrawRect( {{rect.p1.x+20,rect.p1.y+20},{rect.p2.x-20,rect.p2.y-20}} , {0,128,0} );//полностью внутри
bmp.SaveToFile(file_name);
}
//тест окружностей
static void test3(bool MakeFilesEmpty)
{
auto file_name = std::string{__func__}+".bmp";
if(MakeFilesEmpty)
{
std::ofstream{file_name};
return;
}
s_bmp bmp;
bmp.SetSize(111,300);
const auto center = bmp.Center();
const auto rect = bmp.Rect();
{
s_point c{50,100};
bmp.DrawPoint( c , {255,255,255} );
for(int R=1; R<50; R+=3)
{
bmp.DrawEllipse( c, R , {0,255,0} );
}
for(int R=2; R<50; R+=3)
{
bmp.DrawEllipse( c, R , {0,255,255} );
}
}
bmp.SaveToFile(file_name);
}
};