Skip to content

Commit ae57115

Browse files
committed
An example implementation of the algorithm for calculating and rendering the Voronoi diagram.
0 parents  commit ae57115

File tree

6 files changed

+254
-0
lines changed

6 files changed

+254
-0
lines changed

.gitignore

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.dub
2+
docs.json
3+
__dummy.html
4+
docs/
5+
/voronoidiagram
6+
voronoidiagram.so
7+
voronoidiagram.dylib
8+
voronoidiagram.dll
9+
voronoidiagram.a
10+
voronoidiagram.lib
11+
voronoidiagram-test-*
12+
*.exe
13+
*.o
14+
*.obj
15+
*.lst
16+
.vs/
17+
dub.selections.json
18+
*.png

dub.sdl

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name "voronoidiagram"
2+
description "An example implementation of the algorithm for calculating and rendering the Voronoi diagram."
3+
authors "Bagomot"
4+
license "ESL"
5+
dependency "dlib" version="~>0.19.0-beta1"

source/app.d

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import std.stdio;
2+
import std.random;
3+
import std.format;
4+
import dlib.image;
5+
import voronoi;
6+
import cell;
7+
import point;
8+
9+
void main() {
10+
uint width = 512;
11+
uint height = 512;
12+
auto img = image(width, height);
13+
auto sitesColor = Color4f(0.0f, 0.0f, 0.0f);
14+
15+
Voronoi voronoi = new Voronoi(width, height, 100);
16+
voronoi.toImage(img);
17+
voronoi.drawSites(img, 3, sitesColor, true);
18+
img.savePNG("voronoi_diagram.png");
19+
}

source/cell.d

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module cell;
2+
3+
import point;
4+
import std.algorithm.iteration;
5+
import std.conv;
6+
import std.container.array;
7+
import dlib.image;
8+
9+
class Cell {
10+
11+
private Point[] points;
12+
private Point massCenter;
13+
private Color4f color;
14+
15+
this(Point massCenter, Color4f color) {
16+
this.massCenter = massCenter;
17+
this.color = color;
18+
}
19+
20+
void addPoint(Point point) {
21+
this.points ~= point;
22+
}
23+
24+
// пересчитать центр масс
25+
void recalcMassCenter() {
26+
auto x = points.map!(point => point.getX).sum;
27+
auto y = points.map!(point => point.getY).sum;
28+
29+
this.massCenter.setLocation(x/points.length, y/points.length);
30+
}
31+
32+
Point getMassCenter() {
33+
return this.massCenter;
34+
}
35+
36+
Point[] getPoints() {
37+
return this.points;
38+
}
39+
40+
Color4f getColor() {
41+
return this.color;
42+
}
43+
44+
void clearPoints() {
45+
this.points.length = 0;
46+
}
47+
}

source/point.d

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module point;
2+
3+
import std.format;
4+
5+
class Point {
6+
7+
private float x, y;
8+
9+
this(float x, float y) {
10+
this.x = x;
11+
this.y = y;
12+
}
13+
14+
float getX() {
15+
return this.x;
16+
}
17+
18+
float getY() {
19+
return this.y;
20+
}
21+
22+
void setLocation(float x, float y) {
23+
this.x = x;
24+
this.y = y;
25+
}
26+
27+
override string toString() {
28+
return format("[%d, %d]", x, y);
29+
}
30+
}

source/voronoi.d

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
module voronoi;
2+
3+
import std.stdio;
4+
import std.random;
5+
import std.math;
6+
import std.conv : to;
7+
import std.format;
8+
import dlib.image;
9+
import point;
10+
import cell;
11+
12+
class Voronoi {
13+
14+
private uint width, height, numOfPoints;
15+
private Cell[] cells;
16+
17+
this(uint width, uint height, uint numOfPoints = 10) {
18+
this.width = width;
19+
this.height = height;
20+
this.numOfPoints = numOfPoints;
21+
22+
auto rnd = Random(unpredictableSeed);
23+
24+
// генерация сайтов и выбор цвета их ячеек
25+
foreach(_; 0..numOfPoints) {
26+
Point point = new Point(uniform(0, this.width, rnd), uniform( 0, this.height, rnd));
27+
auto color = Color4f(uniform(0.0f, 1.0f, rnd), uniform(0.0f, 1.0f, rnd), uniform(0.0f, 1.0f, rnd));
28+
this.cells ~= new Cell(point, color);
29+
}
30+
31+
this.generate();
32+
}
33+
34+
// генерация ячеек
35+
private void generate() {
36+
int index;
37+
38+
for (int y = 0; y < height; ++y) {
39+
for (int x = 0; x < width; ++x) {
40+
Point point = new Point(x, y);
41+
index = findNearSite(point);
42+
43+
Cell cell = cells[index];
44+
cell.addPoint(point);
45+
this.cells[index] = cell;
46+
}
47+
}
48+
}
49+
50+
// поиск ближайшего к точке сайта
51+
private int findNearSite(Point point) {
52+
int index = 0;
53+
double minDistance = distance(point, this.cells[index].getMassCenter());
54+
55+
double currentDistance;
56+
for (int i = 0; i < numOfPoints; ++i) {
57+
currentDistance = distance(point, this.cells[i].getMassCenter());
58+
59+
if(currentDistance < minDistance) {
60+
minDistance = currentDistance;
61+
index = i;
62+
}
63+
}
64+
65+
return index;
66+
}
67+
68+
// евклидово расстояние между точками
69+
private double distance(Point pointA, Point pointB) {
70+
auto diffX = pointA.getX - pointB.getX;
71+
auto diffY = pointA.getY - pointB.getY;
72+
return sqrt((diffX ^^ 2) + (diffY ^^ 2));
73+
}
74+
75+
void clearCellsPoints() {
76+
for (int i = 0; i < cells.length; i++) {
77+
Cell cell = this.cells[i];
78+
cell.clearPoints();
79+
this.cells[i] = cell;
80+
}
81+
}
82+
83+
// превратить массив ячеек в изображение (dlib)
84+
SuperImage toImage(SuperImage img, bool drawSites = false, int radius = 1, bool filledSites = false) {
85+
foreach (cell; this.cells) {
86+
foreach (point; cell.getPoints) {
87+
img[point.getX.to!int, point.getY.to!int] = cell.getColor;
88+
}
89+
}
90+
91+
return img;
92+
}
93+
94+
// отрисовка сайтов
95+
SuperImage drawSites(SuperImage img, uint radius, Color4f color, bool filled = false) {
96+
for (size_t i = 0; i < numOfPoints; i++) {
97+
if(filled) {
98+
drawFillCircle(img, color, this.cells[i].getMassCenter.getX.to!int, this.cells[i].getMassCenter.getY.to!int, radius);
99+
} else {
100+
// отрисовка круга (dlib)
101+
drawCircle(img, color, this.cells[i].getMassCenter.getX.to!int, this.cells[i].getMassCenter.getY.to!int, radius);
102+
}
103+
}
104+
105+
return img;
106+
}
107+
108+
// отрисовка залитого круга
109+
void drawFillCircle(SuperImage img, Color4f col, int x0, int y0, uint r) {
110+
int f = 1 - r;
111+
int ddF_x = 0;
112+
int ddF_y = -2 * r;
113+
int x = 0;
114+
int y = r;
115+
116+
drawLine(img, col, x0, y0 - r, x0, y0 + r);
117+
drawLine(img, col, x0 - r, y0, x0 + r, y0);
118+
119+
while(x < y) {
120+
if(f >= 0) {
121+
y--;
122+
ddF_y += 2;
123+
f += ddF_y;
124+
}
125+
x++;
126+
ddF_x += 2;
127+
f += ddF_x + 1;
128+
129+
drawLine(img, col, x0 - x, y0 + y, x0 + x, y0 + y);
130+
drawLine(img, col, x0 - x, y0 - y, x0 + x, y0 - y);
131+
drawLine(img, col, x0 - y, y0 + x, x0 + y, y0 + x);
132+
drawLine(img, col, x0 - y, y0 - x, x0 + y, y0 - x);
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)