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