holosphere 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hexlib.js +332 -0
- package/holosphere.d.ts +22 -0
- package/{index.js → holosphere.js} +207 -179
- package/package.json +3 -3
package/hexlib.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// Generated code -- CC0 -- No Rights Reserved -- http://www.redblobgames.com/grids/hexagons/
|
|
2
|
+
export class Point {
|
|
3
|
+
constructor(x, y) {
|
|
4
|
+
this.x = x;
|
|
5
|
+
this.y = y;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class Hex {
|
|
9
|
+
constructor(q, r, s) {
|
|
10
|
+
this.q = q;
|
|
11
|
+
this.r = r;
|
|
12
|
+
this.s = s;
|
|
13
|
+
if (Math.round(q + r + s) !== 0)
|
|
14
|
+
throw "q + r + s must be 0";
|
|
15
|
+
}
|
|
16
|
+
add(b) {
|
|
17
|
+
return new Hex(this.q + b.q, this.r + b.r, this.s + b.s);
|
|
18
|
+
}
|
|
19
|
+
subtract(b) {
|
|
20
|
+
return new Hex(this.q - b.q, this.r - b.r, this.s - b.s);
|
|
21
|
+
}
|
|
22
|
+
scale(k) {
|
|
23
|
+
return new Hex(this.q * k, this.r * k, this.s * k);
|
|
24
|
+
}
|
|
25
|
+
rotateLeft() {
|
|
26
|
+
return new Hex(-this.s, -this.q, -this.r);
|
|
27
|
+
}
|
|
28
|
+
rotateRight() {
|
|
29
|
+
return new Hex(-this.r, -this.s, -this.q);
|
|
30
|
+
}
|
|
31
|
+
static direction(direction) {
|
|
32
|
+
return Hex.directions[direction];
|
|
33
|
+
}
|
|
34
|
+
neighbor(direction) {
|
|
35
|
+
return this.add(Hex.direction(direction));
|
|
36
|
+
}
|
|
37
|
+
diagonalNeighbor(direction) {
|
|
38
|
+
return this.add(Hex.diagonals[direction]);
|
|
39
|
+
}
|
|
40
|
+
len() {
|
|
41
|
+
return (Math.abs(this.q) + Math.abs(this.r) + Math.abs(this.s)) / 2;
|
|
42
|
+
}
|
|
43
|
+
distance(b) {
|
|
44
|
+
return this.subtract(b).len();
|
|
45
|
+
}
|
|
46
|
+
round() {
|
|
47
|
+
var qi = Math.round(this.q);
|
|
48
|
+
var ri = Math.round(this.r);
|
|
49
|
+
var si = Math.round(this.s);
|
|
50
|
+
var q_diff = Math.abs(qi - this.q);
|
|
51
|
+
var r_diff = Math.abs(ri - this.r);
|
|
52
|
+
var s_diff = Math.abs(si - this.s);
|
|
53
|
+
if (q_diff > r_diff && q_diff > s_diff) {
|
|
54
|
+
qi = -ri - si;
|
|
55
|
+
}
|
|
56
|
+
else if (r_diff > s_diff) {
|
|
57
|
+
ri = -qi - si;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
si = -qi - ri;
|
|
61
|
+
}
|
|
62
|
+
return new Hex(qi, ri, si);
|
|
63
|
+
}
|
|
64
|
+
lerp(b, t) {
|
|
65
|
+
return new Hex(this.q * (1.0 - t) + b.q * t, this.r * (1.0 - t) + b.r * t, this.s * (1.0 - t) + b.s * t);
|
|
66
|
+
}
|
|
67
|
+
linedraw(b) {
|
|
68
|
+
var N = this.distance(b);
|
|
69
|
+
var a_nudge = new Hex(this.q + 1e-06, this.r + 1e-06, this.s - 2e-06);
|
|
70
|
+
var b_nudge = new Hex(b.q + 1e-06, b.r + 1e-06, b.s - 2e-06);
|
|
71
|
+
var results = [];
|
|
72
|
+
var step = 1.0 / Math.max(N, 1);
|
|
73
|
+
for (var i = 0; i <= N; i++) {
|
|
74
|
+
results.push(a_nudge.lerp(b_nudge, step * i).round());
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
Hex.directions = [new Hex(1, 0, -1), new Hex(1, -1, 0), new Hex(0, -1, 1), new Hex(-1, 0, 1), new Hex(-1, 1, 0), new Hex(0, 1, -1)];
|
|
80
|
+
Hex.diagonals = [new Hex(2, -1, -1), new Hex(1, -2, 1), new Hex(-1, -1, 2), new Hex(-2, 1, 1), new Hex(-1, 2, -1), new Hex(1, 1, -2)];
|
|
81
|
+
export class OffsetCoord {
|
|
82
|
+
constructor(col, row) {
|
|
83
|
+
this.col = col;
|
|
84
|
+
this.row = row;
|
|
85
|
+
}
|
|
86
|
+
static qoffsetFromCube(offset, h) {
|
|
87
|
+
var col = h.q;
|
|
88
|
+
var row = h.r + (h.q + offset * (h.q & 1)) / 2;
|
|
89
|
+
if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) {
|
|
90
|
+
throw "offset must be EVEN (+1) or ODD (-1)";
|
|
91
|
+
}
|
|
92
|
+
return new OffsetCoord(col, row);
|
|
93
|
+
}
|
|
94
|
+
static qoffsetToCube(offset, h) {
|
|
95
|
+
var q = h.col;
|
|
96
|
+
var r = h.row - (h.col + offset * (h.col & 1)) / 2;
|
|
97
|
+
var s = -q - r;
|
|
98
|
+
if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) {
|
|
99
|
+
throw "offset must be EVEN (+1) or ODD (-1)";
|
|
100
|
+
}
|
|
101
|
+
return new Hex(q, r, s);
|
|
102
|
+
}
|
|
103
|
+
static roffsetFromCube(offset, h) {
|
|
104
|
+
var col = h.q + (h.r + offset * (h.r & 1)) / 2;
|
|
105
|
+
var row = h.r;
|
|
106
|
+
if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) {
|
|
107
|
+
throw "offset must be EVEN (+1) or ODD (-1)";
|
|
108
|
+
}
|
|
109
|
+
return new OffsetCoord(col, row);
|
|
110
|
+
}
|
|
111
|
+
static roffsetToCube(offset, h) {
|
|
112
|
+
var q = h.col - (h.row + offset * (h.row & 1)) / 2;
|
|
113
|
+
var r = h.row;
|
|
114
|
+
var s = -q - r;
|
|
115
|
+
if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) {
|
|
116
|
+
throw "offset must be EVEN (+1) or ODD (-1)";
|
|
117
|
+
}
|
|
118
|
+
return new Hex(q, r, s);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
OffsetCoord.EVEN = 1;
|
|
122
|
+
OffsetCoord.ODD = -1;
|
|
123
|
+
export class DoubledCoord {
|
|
124
|
+
constructor(col, row) {
|
|
125
|
+
this.col = col;
|
|
126
|
+
this.row = row;
|
|
127
|
+
}
|
|
128
|
+
static qdoubledFromCube(h) {
|
|
129
|
+
var col = h.q;
|
|
130
|
+
var row = 2 * h.r + h.q;
|
|
131
|
+
return new DoubledCoord(col, row);
|
|
132
|
+
}
|
|
133
|
+
qdoubledToCube() {
|
|
134
|
+
var q = this.col;
|
|
135
|
+
var r = (this.row - this.col) / 2;
|
|
136
|
+
var s = -q - r;
|
|
137
|
+
return new Hex(q, r, s);
|
|
138
|
+
}
|
|
139
|
+
static rdoubledFromCube(h) {
|
|
140
|
+
var col = 2 * h.q + h.r;
|
|
141
|
+
var row = h.r;
|
|
142
|
+
return new DoubledCoord(col, row);
|
|
143
|
+
}
|
|
144
|
+
rdoubledToCube() {
|
|
145
|
+
var q = (this.col - this.row) / 2;
|
|
146
|
+
var r = this.row;
|
|
147
|
+
var s = -q - r;
|
|
148
|
+
return new Hex(q, r, s);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export class Orientation {
|
|
152
|
+
constructor(f0, f1, f2, f3, b0, b1, b2, b3, start_angle) {
|
|
153
|
+
this.f0 = f0;
|
|
154
|
+
this.f1 = f1;
|
|
155
|
+
this.f2 = f2;
|
|
156
|
+
this.f3 = f3;
|
|
157
|
+
this.b0 = b0;
|
|
158
|
+
this.b1 = b1;
|
|
159
|
+
this.b2 = b2;
|
|
160
|
+
this.b3 = b3;
|
|
161
|
+
this.start_angle = start_angle;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export class Layout {
|
|
165
|
+
constructor(orientation, size, origin) {
|
|
166
|
+
this.orientation = orientation;
|
|
167
|
+
this.size = size;
|
|
168
|
+
this.origin = origin;
|
|
169
|
+
}
|
|
170
|
+
hexToPixel(h) {
|
|
171
|
+
var M = this.orientation;
|
|
172
|
+
var size = this.size;
|
|
173
|
+
var origin = this.origin;
|
|
174
|
+
var x = (M.f0 * h.q + M.f1 * h.r) * size.x;
|
|
175
|
+
var y = (M.f2 * h.q + M.f3 * h.r) * size.y;
|
|
176
|
+
return new Point(x + origin.x, y + origin.y);
|
|
177
|
+
}
|
|
178
|
+
pixelToHex(p) {
|
|
179
|
+
var M = this.orientation;
|
|
180
|
+
var size = this.size;
|
|
181
|
+
var origin = this.origin;
|
|
182
|
+
var pt = new Point((p.x - origin.x) / size.x, (p.y - origin.y) / size.y);
|
|
183
|
+
var q = M.b0 * pt.x + M.b1 * pt.y;
|
|
184
|
+
var r = M.b2 * pt.x + M.b3 * pt.y;
|
|
185
|
+
return new Hex(q, r, -q - r);
|
|
186
|
+
}
|
|
187
|
+
hexCornerOffset(corner) {
|
|
188
|
+
var M = this.orientation;
|
|
189
|
+
var size = this.size;
|
|
190
|
+
var angle = 2.0 * Math.PI * (M.start_angle - corner) / 6.0;
|
|
191
|
+
return new Point(size.x * Math.cos(angle), size.y * Math.sin(angle));
|
|
192
|
+
}
|
|
193
|
+
polygonCorners(h) {
|
|
194
|
+
var corners = [];
|
|
195
|
+
var center = this.hexToPixel(h);
|
|
196
|
+
for (var i = 0; i < 6; i++) {
|
|
197
|
+
var offset = this.hexCornerOffset(i);
|
|
198
|
+
corners.push(new Point(center.x + offset.x, center.y + offset.y));
|
|
199
|
+
}
|
|
200
|
+
return corners;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
Layout.pointy = new Orientation(Math.sqrt(3.0), Math.sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, Math.sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0, 0.5);
|
|
204
|
+
Layout.flat = new Orientation(3.0 / 2.0, 0.0, Math.sqrt(3.0) / 2.0, Math.sqrt(3.0), 2.0 / 3.0, 0.0, -1.0 / 3.0, Math.sqrt(3.0) / 3.0, 0.0);
|
|
205
|
+
class Tests {
|
|
206
|
+
constructor() { }
|
|
207
|
+
static equalHex(name, a, b) {
|
|
208
|
+
if (!(a.q === b.q && a.s === b.s && a.r === b.r)) {
|
|
209
|
+
complain(name);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
static equalOffsetcoord(name, a, b) {
|
|
213
|
+
if (!(a.col === b.col && a.row === b.row)) {
|
|
214
|
+
complain(name);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
static equalDoubledcoord(name, a, b) {
|
|
218
|
+
if (!(a.col === b.col && a.row === b.row)) {
|
|
219
|
+
complain(name);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
static equalInt(name, a, b) {
|
|
223
|
+
if (!(a === b)) {
|
|
224
|
+
complain(name);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
static equalHexArray(name, a, b) {
|
|
228
|
+
Tests.equalInt(name, a.length, b.length);
|
|
229
|
+
for (var i = 0; i < a.length; i++) {
|
|
230
|
+
Tests.equalHex(name, a[i], b[i]);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
static testHexArithmetic() {
|
|
234
|
+
Tests.equalHex("hex_add", new Hex(4, -10, 6), new Hex(1, -3, 2).add(new Hex(3, -7, 4)));
|
|
235
|
+
Tests.equalHex("hex_subtract", new Hex(-2, 4, -2), new Hex(1, -3, 2).subtract(new Hex(3, -7, 4)));
|
|
236
|
+
}
|
|
237
|
+
static testHexDirection() {
|
|
238
|
+
Tests.equalHex("hex_direction", new Hex(0, -1, 1), Hex.direction(2));
|
|
239
|
+
}
|
|
240
|
+
static testHexNeighbor() {
|
|
241
|
+
Tests.equalHex("hex_neighbor", new Hex(1, -3, 2), new Hex(1, -2, 1).neighbor(2));
|
|
242
|
+
}
|
|
243
|
+
static testHexDiagonal() {
|
|
244
|
+
Tests.equalHex("hex_diagonal", new Hex(-1, -1, 2), new Hex(1, -2, 1).diagonalNeighbor(3));
|
|
245
|
+
}
|
|
246
|
+
static testHexDistance() {
|
|
247
|
+
Tests.equalInt("hex_distance", 7, new Hex(3, -7, 4).distance(new Hex(0, 0, 0)));
|
|
248
|
+
}
|
|
249
|
+
static testHexRotateRight() {
|
|
250
|
+
Tests.equalHex("hex_rotate_right", new Hex(1, -3, 2).rotateRight(), new Hex(3, -2, -1));
|
|
251
|
+
}
|
|
252
|
+
static testHexRotateLeft() {
|
|
253
|
+
Tests.equalHex("hex_rotate_left", new Hex(1, -3, 2).rotateLeft(), new Hex(-2, -1, 3));
|
|
254
|
+
}
|
|
255
|
+
static testHexRound() {
|
|
256
|
+
var a = new Hex(0.0, 0.0, 0.0);
|
|
257
|
+
var b = new Hex(1.0, -1.0, 0.0);
|
|
258
|
+
var c = new Hex(0.0, -1.0, 1.0);
|
|
259
|
+
Tests.equalHex("hex_round 1", new Hex(5, -10, 5), new Hex(0.0, 0.0, 0.0).lerp(new Hex(10.0, -20.0, 10.0), 0.5).round());
|
|
260
|
+
Tests.equalHex("hex_round 2", a.round(), a.lerp(b, 0.499).round());
|
|
261
|
+
Tests.equalHex("hex_round 3", b.round(), a.lerp(b, 0.501).round());
|
|
262
|
+
Tests.equalHex("hex_round 4", a.round(), new Hex(a.q * 0.4 + b.q * 0.3 + c.q * 0.3, a.r * 0.4 + b.r * 0.3 + c.r * 0.3, a.s * 0.4 + b.s * 0.3 + c.s * 0.3).round());
|
|
263
|
+
Tests.equalHex("hex_round 5", c.round(), new Hex(a.q * 0.3 + b.q * 0.3 + c.q * 0.4, a.r * 0.3 + b.r * 0.3 + c.r * 0.4, a.s * 0.3 + b.s * 0.3 + c.s * 0.4).round());
|
|
264
|
+
}
|
|
265
|
+
static testHexLinedraw() {
|
|
266
|
+
Tests.equalHexArray("hex_linedraw", [new Hex(0, 0, 0), new Hex(0, -1, 1), new Hex(0, -2, 2), new Hex(1, -3, 2), new Hex(1, -4, 3), new Hex(1, -5, 4)], new Hex(0, 0, 0).linedraw(new Hex(1, -5, 4)));
|
|
267
|
+
}
|
|
268
|
+
static testLayout() {
|
|
269
|
+
var h = new Hex(3, 4, -7);
|
|
270
|
+
var flat = new Layout(Layout.flat, new Point(10.0, 15.0), new Point(35.0, 71.0));
|
|
271
|
+
Tests.equalHex("layout", h, flat.pixelToHex(flat.hexToPixel(h)).round());
|
|
272
|
+
var pointy = new Layout(Layout.pointy, new Point(10.0, 15.0), new Point(35.0, 71.0));
|
|
273
|
+
Tests.equalHex("layout", h, pointy.pixelToHex(pointy.hexToPixel(h)).round());
|
|
274
|
+
}
|
|
275
|
+
static testOffsetRoundtrip() {
|
|
276
|
+
var a = new Hex(3, 4, -7);
|
|
277
|
+
var b = new OffsetCoord(1, -3);
|
|
278
|
+
Tests.equalHex("conversion_roundtrip even-q", a, OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, a)));
|
|
279
|
+
Tests.equalOffsetcoord("conversion_roundtrip even-q", b, OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, b)));
|
|
280
|
+
Tests.equalHex("conversion_roundtrip odd-q", a, OffsetCoord.qoffsetToCube(OffsetCoord.ODD, OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, a)));
|
|
281
|
+
Tests.equalOffsetcoord("conversion_roundtrip odd-q", b, OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, OffsetCoord.qoffsetToCube(OffsetCoord.ODD, b)));
|
|
282
|
+
Tests.equalHex("conversion_roundtrip even-r", a, OffsetCoord.roffsetToCube(OffsetCoord.EVEN, OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, a)));
|
|
283
|
+
Tests.equalOffsetcoord("conversion_roundtrip even-r", b, OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, OffsetCoord.roffsetToCube(OffsetCoord.EVEN, b)));
|
|
284
|
+
Tests.equalHex("conversion_roundtrip odd-r", a, OffsetCoord.roffsetToCube(OffsetCoord.ODD, OffsetCoord.roffsetFromCube(OffsetCoord.ODD, a)));
|
|
285
|
+
Tests.equalOffsetcoord("conversion_roundtrip odd-r", b, OffsetCoord.roffsetFromCube(OffsetCoord.ODD, OffsetCoord.roffsetToCube(OffsetCoord.ODD, b)));
|
|
286
|
+
}
|
|
287
|
+
static testOffsetFromCube() {
|
|
288
|
+
Tests.equalOffsetcoord("offset_from_cube even-q", new OffsetCoord(1, 3), OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, new Hex(1, 2, -3)));
|
|
289
|
+
Tests.equalOffsetcoord("offset_from_cube odd-q", new OffsetCoord(1, 2), OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, new Hex(1, 2, -3)));
|
|
290
|
+
}
|
|
291
|
+
static testOffsetToCube() {
|
|
292
|
+
Tests.equalHex("offset_to_cube even-", new Hex(1, 2, -3), OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, new OffsetCoord(1, 3)));
|
|
293
|
+
Tests.equalHex("offset_to_cube odd-q", new Hex(1, 2, -3), OffsetCoord.qoffsetToCube(OffsetCoord.ODD, new OffsetCoord(1, 2)));
|
|
294
|
+
}
|
|
295
|
+
static testDoubledRoundtrip() {
|
|
296
|
+
var a = new Hex(3, 4, -7);
|
|
297
|
+
var b = new DoubledCoord(1, -3);
|
|
298
|
+
Tests.equalHex("conversion_roundtrip doubled-q", a, DoubledCoord.qdoubledFromCube(a).qdoubledToCube());
|
|
299
|
+
Tests.equalDoubledcoord("conversion_roundtrip doubled-q", b, DoubledCoord.qdoubledFromCube(b.qdoubledToCube()));
|
|
300
|
+
Tests.equalHex("conversion_roundtrip doubled-r", a, DoubledCoord.rdoubledFromCube(a).rdoubledToCube());
|
|
301
|
+
Tests.equalDoubledcoord("conversion_roundtrip doubled-r", b, DoubledCoord.rdoubledFromCube(b.rdoubledToCube()));
|
|
302
|
+
}
|
|
303
|
+
static testDoubledFromCube() {
|
|
304
|
+
Tests.equalDoubledcoord("doubled_from_cube doubled-q", new DoubledCoord(1, 5), DoubledCoord.qdoubledFromCube(new Hex(1, 2, -3)));
|
|
305
|
+
Tests.equalDoubledcoord("doubled_from_cube doubled-r", new DoubledCoord(4, 2), DoubledCoord.rdoubledFromCube(new Hex(1, 2, -3)));
|
|
306
|
+
}
|
|
307
|
+
static testDoubledToCube() {
|
|
308
|
+
Tests.equalHex("doubled_to_cube doubled-q", new Hex(1, 2, -3), new DoubledCoord(1, 5).qdoubledToCube());
|
|
309
|
+
Tests.equalHex("doubled_to_cube doubled-r", new Hex(1, 2, -3), new DoubledCoord(4, 2).rdoubledToCube());
|
|
310
|
+
}
|
|
311
|
+
static testAll() {
|
|
312
|
+
Tests.testHexArithmetic();
|
|
313
|
+
Tests.testHexDirection();
|
|
314
|
+
Tests.testHexNeighbor();
|
|
315
|
+
Tests.testHexDiagonal();
|
|
316
|
+
Tests.testHexDistance();
|
|
317
|
+
Tests.testHexRotateRight();
|
|
318
|
+
Tests.testHexRotateLeft();
|
|
319
|
+
Tests.testHexRound();
|
|
320
|
+
Tests.testHexLinedraw();
|
|
321
|
+
Tests.testLayout();
|
|
322
|
+
Tests.testOffsetRoundtrip();
|
|
323
|
+
Tests.testOffsetFromCube();
|
|
324
|
+
Tests.testOffsetToCube();
|
|
325
|
+
Tests.testDoubledRoundtrip();
|
|
326
|
+
Tests.testDoubledFromCube();
|
|
327
|
+
Tests.testDoubledToCube();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Tests
|
|
331
|
+
function complain(name) { console.log("FAIL", name); }
|
|
332
|
+
Tests.testAll();
|
package/holosphere.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
declare module 'holosphere' {
|
|
2
|
+
export default class HoloSphere {
|
|
3
|
+
constructor(name: string, openaikey?: string | null);
|
|
4
|
+
subscribe(hex: string, lense: string, callback: (item: any, key: string) => void): void;
|
|
5
|
+
put(hex: string, lense: string, content: any): Promise<void>;
|
|
6
|
+
delete(id: string, tag: string): Promise<void>;
|
|
7
|
+
get(hex: string, lense: string): Promise<any[]>;
|
|
8
|
+
getKey(hex: string, lense: string, key: string): Promise<any | null>;
|
|
9
|
+
getNode(hex: string, lense: string, key: string): Promise<any | null>;
|
|
10
|
+
compute(hex: string, lense: string, operation: string): Promise<void>;
|
|
11
|
+
clearlense(hex: string, lense: string): Promise<void>;
|
|
12
|
+
summarize(history: string): Promise<string>;
|
|
13
|
+
upcast(hex: string, lense: string, content: any): Promise<any>;
|
|
14
|
+
updateParent(id: string, report: string): Promise<any>;
|
|
15
|
+
getHex(lat: number, lng: number, resolution: number): Promise<string>;
|
|
16
|
+
getScalespace(lat: number, lng: number): string[];
|
|
17
|
+
getHexScalespace(hex: string): string[];
|
|
18
|
+
aggregateVotes(hexId: string, topic: string): object;
|
|
19
|
+
delegateVote(userId: string, topic: string, delegateTo: string): Promise<void>;
|
|
20
|
+
vote(userId: string, hexId: string, topic: string, vote: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -2,13 +2,17 @@ import * as h3 from 'h3-js';
|
|
|
2
2
|
import OpenAI from 'openai';
|
|
3
3
|
import Gun from 'gun'
|
|
4
4
|
import Ajv2019 from 'ajv/dist/2019.js'
|
|
5
|
-
import { createHash } from "crypto";
|
|
6
5
|
|
|
7
6
|
class HoloSphere {
|
|
7
|
+
/**
|
|
8
|
+
* Initializes a new instance of the HoloSphere class.
|
|
9
|
+
* @param {string} appname - The name of the application.
|
|
10
|
+
* @param {string|null} openaikey - The OpenAI API key.
|
|
11
|
+
*/
|
|
8
12
|
constructor(appname, openaikey = null) {
|
|
9
13
|
this.validator = new Ajv2019({ allErrors: false, strict: false });
|
|
10
14
|
this.gun = Gun({
|
|
11
|
-
peers: [
|
|
15
|
+
peers: ['https://59.src.eco/gun'],
|
|
12
16
|
axe: false,
|
|
13
17
|
// uuid: (content) => { // generate a unique id for each node
|
|
14
18
|
// console.log('uuid', content);
|
|
@@ -16,119 +20,22 @@ class HoloSphere {
|
|
|
16
20
|
});
|
|
17
21
|
|
|
18
22
|
this.gun = this.gun.get(appname)
|
|
23
|
+
this.users = {}; // Initialize users
|
|
24
|
+
this.hexagonVotes = {}; // Initialize hexagonVotes
|
|
19
25
|
|
|
20
26
|
if (openaikey != null) {
|
|
21
27
|
this.openai = new OpenAI({
|
|
22
28
|
apiKey: openaikey,
|
|
23
29
|
});
|
|
24
30
|
}
|
|
25
|
-
|
|
26
|
-
//this.bot.command('sethex', async (ctx) => { this.setHex(ctx) }) TODO: MOVE HERE FROM SETTINGS
|
|
27
|
-
|
|
28
|
-
// this.bot.command('resethex', async (ctx) => {
|
|
29
|
-
// let chatID = ctx.message.chat.id;
|
|
30
|
-
// let hex = (await this.db.get('settings', chatID)).hex
|
|
31
|
-
// this.delete(hex, ctx.message.text.split(' ')[1])
|
|
32
|
-
// })
|
|
33
|
-
|
|
34
|
-
// this.bot.command('get', async (ctx) => {
|
|
35
|
-
// const chatID = ctx.message.chat.id;
|
|
36
|
-
// const lense = ctx.message.text.split(' ')[1];
|
|
37
|
-
// if (!lense) {
|
|
38
|
-
// return ctx.reply('Please specify a tag.');
|
|
39
|
-
// }
|
|
40
|
-
// let hex = (await this.db.get('settings', chatID)).hex
|
|
41
|
-
// //let hex = settings.hex
|
|
42
|
-
// console.log('hex', hex)
|
|
43
|
-
|
|
44
|
-
// let data = await this.get(ctx, hex, lense)
|
|
45
|
-
|
|
46
|
-
// })
|
|
47
|
-
|
|
48
|
-
// this.bot.command('gethex', async (ctx) => {
|
|
49
|
-
// let settings = await this.db.get('settings', chatID)
|
|
50
|
-
// let id = settings.hex ? settings.hex : 'Hex not set, use /sethex'
|
|
51
|
-
// ctx.reply(id)
|
|
52
|
-
// })
|
|
53
|
-
|
|
54
|
-
// this.bot.command('compute', async (ctx) => {
|
|
55
|
-
// const chatID = ctx.message.chat.id;
|
|
56
|
-
// let operation = ctx.message.text.split(' ')[1];
|
|
57
|
-
// if (operation != 'sum') {
|
|
58
|
-
// ctx.reply('Operation not implemented')
|
|
59
|
-
// return
|
|
60
|
-
// }
|
|
61
|
-
// let lense = ctx.message.text.split(' ')[2]
|
|
62
|
-
// if (!lense) {
|
|
63
|
-
// ctx.reply('Please specify a lense where to perform the operation ')
|
|
64
|
-
// return
|
|
65
|
-
// }
|
|
66
|
-
// let hex = (await this.db.get('settings', chatID)).hex
|
|
67
|
-
// await this.compute(hex, lense, operation)
|
|
68
|
-
|
|
69
|
-
// // let parentInfo = await this.getCellInfo(parent)
|
|
70
|
-
// // parentInfo.wisdom[id] = report
|
|
71
|
-
// // //update summary
|
|
72
|
-
// // let summary = await this.summarize(Object.values(parentInfo.wisdom).join('\n'))
|
|
73
|
-
// // parentInfo.summary = summary
|
|
74
|
-
|
|
75
|
-
// // await this.db.put('cell', parentInfo)
|
|
76
|
-
// // return parentInfo
|
|
77
|
-
|
|
78
|
-
// // let content = await this.db.getAll(hex+'/tags')
|
|
79
|
-
// })
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// this.bot.command('cast', async (ctx) => {
|
|
85
|
-
// if (!ctx.message.reply_to_message) {
|
|
86
|
-
// return ctx.reply('Please reply to a message you want to tag.');
|
|
87
|
-
// }
|
|
88
|
-
// const tags = ctx.message.text.split(' ').slice(1);
|
|
89
|
-
// if (tags.length === 0) {
|
|
90
|
-
// return ctx.reply('Please provide at least one tag.');
|
|
91
|
-
// }
|
|
92
|
-
|
|
93
|
-
// const messageID = ctx.message.reply_to_message.message_id;
|
|
94
|
-
// const chatID = ctx.message.chat.id;
|
|
95
|
-
// const messageContent = ctx.message.reply_to_message.text;
|
|
96
|
-
// let settings = await this.db.get('settings', chatID)
|
|
97
|
-
// let id = settings.hex ? settings.hex : 'Hex not set, use /sethex'
|
|
98
|
-
// //create root node for the item
|
|
99
|
-
// let node = await this.gun.get(chatID + '/' + messageID).put({ id: chatID + '/' + messageID, content: messageContent })
|
|
100
|
-
// for (let tag of tags) {
|
|
101
|
-
// await this.gun.get(id).get(tag).set(node)
|
|
102
|
-
// this.upcast(id, tag, node)
|
|
103
|
-
// }
|
|
104
|
-
// })
|
|
105
|
-
|
|
106
|
-
// this.bot.command('publish', async (ctx) => {
|
|
107
|
-
// console.log(ctx.message)
|
|
108
|
-
// if (!ctx.message.reply_to_message) {
|
|
109
|
-
// return ctx.reply('Please reply to a message you want to tag.');
|
|
110
|
-
// }
|
|
111
|
-
// const tags = ctx.message.text.split(' ').slice(1);
|
|
112
|
-
// if (tags.length === 0) {
|
|
113
|
-
// return ctx.reply('Please provide at least one tag.');
|
|
114
|
-
// }
|
|
115
|
-
|
|
116
|
-
// const messageID = ctx.message.reply_to_message.message_id;
|
|
117
|
-
// const chatID = ctx.message.chat.id;
|
|
118
|
-
// const messageContent = ctx.message.reply_to_message.text;
|
|
119
|
-
// let settings = await this.db.get('settings', chatID)
|
|
120
|
-
// let hex = settings.hex
|
|
121
|
-
|
|
122
|
-
// for (let tag of tags) {
|
|
123
|
-
// let node = await this.gun.get(chatID + '/' + messageID).put({ id: chatID + '/' + messageID, content: messageContent })
|
|
124
|
-
// await this.put(hex, tag, node)
|
|
125
|
-
// }
|
|
126
|
-
|
|
127
|
-
// ctx.reply('Tag published.');
|
|
128
|
-
// });
|
|
129
|
-
|
|
130
31
|
}
|
|
131
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Sets the JSON schema for a specific lense.
|
|
35
|
+
* @param {string} lense - The lense identifier.
|
|
36
|
+
* @param {object} schema - The JSON schema to set.
|
|
37
|
+
* @returns {Promise} - Resolves when the schema is set.
|
|
38
|
+
*/
|
|
132
39
|
async setSchema(lense, schema) {
|
|
133
40
|
return new Promise((resolve, reject) => {
|
|
134
41
|
this.gun.get(lense).get('schema').put(JSON.stringify(schema), ack => {
|
|
@@ -142,6 +49,11 @@ class HoloSphere {
|
|
|
142
49
|
})
|
|
143
50
|
}
|
|
144
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves the JSON schema for a specific lense.
|
|
54
|
+
* @param {string} lense - The lense identifier.
|
|
55
|
+
* @returns {Promise<object|null>} - The retrieved schema or null if not found.
|
|
56
|
+
*/
|
|
145
57
|
async getSchema(lense) {
|
|
146
58
|
return new Promise((resolve) => {
|
|
147
59
|
this.gun.get(lense).get('schema').once(data => {
|
|
@@ -160,28 +72,21 @@ class HoloSphere {
|
|
|
160
72
|
})
|
|
161
73
|
})
|
|
162
74
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
this.db.gun.get('settings').get(chatID).put(hex)
|
|
169
|
-
return hex
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async getHexContent(ctx) {
|
|
173
|
-
const chatID = ctx.message.chat.id;
|
|
174
|
-
let settings = await this.getSettings(chatID)
|
|
175
|
-
let hex = settings.hex
|
|
176
|
-
let content = await this.db.getAll(hex + '/tags')
|
|
177
|
-
//console.log(content)
|
|
178
|
-
return content ? content[0].id : 'not found'
|
|
179
|
-
}
|
|
180
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Deletes a specific tag from a given ID.
|
|
77
|
+
* @param {string} id - The identifier from which to delete the tag.
|
|
78
|
+
* @param {string} tag - The tag to delete.
|
|
79
|
+
*/
|
|
181
80
|
async delete(id, tag) {
|
|
182
81
|
await this.gun.get(id).get(tag).put(null)
|
|
183
82
|
}
|
|
184
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Stores content in the specified hex and lense.
|
|
86
|
+
* @param {string} hex - The hex identifier.
|
|
87
|
+
* @param {string} lense - The lense under which to store the content.
|
|
88
|
+
* @param {object} content - The content to store.
|
|
89
|
+
*/
|
|
185
90
|
async put(hex, lense, content) {
|
|
186
91
|
// Retrieve the schema for the lense
|
|
187
92
|
let schema = await this.getSchema(lense)
|
|
@@ -203,24 +108,25 @@ class HoloSphere {
|
|
|
203
108
|
noderef = this.gun.get(lense).get(content.id).put(payload)
|
|
204
109
|
this.gun.get(hex.toString()).get(lense).get(content.id).put(payload)
|
|
205
110
|
} else { // create a content-addressable reference like IPFS. Note: no updates possible using put
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
111
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload));
|
|
112
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
113
|
+
const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, "0")).join("");
|
|
114
|
+
noderef = this.gun.get(lense).get(hashHex).put(payload)
|
|
115
|
+
this.gun.get(hex.toString()).get(lense).get(hashHex).put(payload)
|
|
209
116
|
}
|
|
210
117
|
|
|
211
|
-
|
|
212
|
-
// this.gun.get(hex.toString()).get(lense).set(noderef, ack => {
|
|
213
|
-
// if (ack.err) {
|
|
214
|
-
// reject(new Error('Failed to add content: ' + ack.err));
|
|
215
|
-
// } else {
|
|
216
|
-
// console.log('Content added successfully under tag:', lense);
|
|
217
|
-
// resolve(ack);
|
|
218
|
-
// }
|
|
219
|
-
// });
|
|
220
|
-
// });
|
|
118
|
+
}
|
|
221
119
|
|
|
120
|
+
async putNode(hex, lense, node) {
|
|
121
|
+
this.gun.get(hex).get(lense).set(node)
|
|
222
122
|
}
|
|
223
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Retrieves content from the specified hex and lense.
|
|
126
|
+
* @param {string} hex - The hex identifier.
|
|
127
|
+
* @param {string} lense - The lense from which to retrieve content.
|
|
128
|
+
* @returns {Promise<Array<object>>} - The retrieved content.
|
|
129
|
+
*/
|
|
224
130
|
async get(hex, lense) {
|
|
225
131
|
// Wrap the GunDB operation in a promise
|
|
226
132
|
//retrieve lense schema
|
|
@@ -241,11 +147,16 @@ class HoloSphere {
|
|
|
241
147
|
this.gun.get(hex.toString()).get(lense).map().once(async (itemdata, key) => {
|
|
242
148
|
counter += 1
|
|
243
149
|
if (itemdata) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
150
|
+
if (itemdata._["#"]) {
|
|
151
|
+
|
|
152
|
+
// If the data is a reference, fetch the actual content
|
|
153
|
+
let query = itemdata._['#'].split('/')
|
|
154
|
+
let hex = query[1]
|
|
155
|
+
let lense = query[2]
|
|
156
|
+
let key = query[3]
|
|
157
|
+
itemdata = await this.getNode(hex, lense, key)
|
|
158
|
+
|
|
159
|
+
}
|
|
249
160
|
var parsed = {}
|
|
250
161
|
try {
|
|
251
162
|
parsed = JSON.parse(itemdata);
|
|
@@ -280,8 +191,47 @@ class HoloSphere {
|
|
|
280
191
|
);
|
|
281
192
|
}
|
|
282
193
|
|
|
283
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Retrieves a specific key from the specified hex and lense.
|
|
196
|
+
* @param {string} hex - The hex identifier.
|
|
197
|
+
* @param {string} lense - The lense from which to retrieve the key.
|
|
198
|
+
* @param {string} key - The specific key to retrieve.
|
|
199
|
+
* @returns {Promise<object|null>} - The retrieved content or null if not found.
|
|
200
|
+
*/
|
|
201
|
+
async getKey(hex, lense, key) {
|
|
202
|
+
return new Promise((resolve) => {
|
|
203
|
+
// Use Gun to get the data
|
|
204
|
+
this.gun.get(hex).get(lense).get(key).once((data, key) => {
|
|
205
|
+
if (data) {
|
|
206
|
+
resolve(JSON.parse(data)); // Resolve the promise with the data if data is found
|
|
207
|
+
} else {
|
|
208
|
+
resolve(null); // Reject the promise if no data is found
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Retrieves a specific gundb node from the specified hex and lense.
|
|
217
|
+
* @param {string} hex - The hex identifier.
|
|
218
|
+
* @param {string} lense - The lense from which to retrieve the key.
|
|
219
|
+
* @param {string} key - The specific key to retrieve.
|
|
220
|
+
* @returns {Promise<object|null>} - The retrieved content or null if not found.
|
|
221
|
+
*/
|
|
222
|
+
getNode(hex, lense, key) {
|
|
223
|
+
// Use Gun to get the data
|
|
224
|
+
return this.gun.get(hex).get(lense).get(key)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
284
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Computes summaries based on the content within a hex and lense.
|
|
231
|
+
* @param {string} hex - The hex identifier.
|
|
232
|
+
* @param {string} lense - The lense to compute.
|
|
233
|
+
* @param {string} operation - The operation to perform.
|
|
234
|
+
*/
|
|
285
235
|
async compute(hex, lense, operation) {
|
|
286
236
|
|
|
287
237
|
let res = h3.getResolution(hex);
|
|
@@ -301,7 +251,7 @@ class HoloSphere {
|
|
|
301
251
|
resolve(); // Resolve the promise to prevent it from hanging
|
|
302
252
|
}, 1000); // Timeout of 5 seconds
|
|
303
253
|
|
|
304
|
-
this.
|
|
254
|
+
this.gun.get(siblings[i]).get(lense).map().once((data, key) => {
|
|
305
255
|
clearTimeout(timeout); // Clear the timeout if data is received
|
|
306
256
|
if (data) {
|
|
307
257
|
content.push(data.content);
|
|
@@ -321,6 +271,11 @@ class HoloSphere {
|
|
|
321
271
|
this.compute(parent, lense, operation)
|
|
322
272
|
}
|
|
323
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Clears all entities under a specific hex and lense.
|
|
276
|
+
* @param {string} hex - The hex identifier.
|
|
277
|
+
* @param {string} lense - The lense to clear.
|
|
278
|
+
*/
|
|
324
279
|
async clearlense(hex, lense) {
|
|
325
280
|
let entities = {};
|
|
326
281
|
|
|
@@ -333,6 +288,11 @@ class HoloSphere {
|
|
|
333
288
|
}
|
|
334
289
|
|
|
335
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Summarizes provided history text using OpenAI.
|
|
293
|
+
* @param {string} history - The history text to summarize.
|
|
294
|
+
* @returns {Promise<string>} - The summarized text.
|
|
295
|
+
*/
|
|
336
296
|
async summarize(history) {
|
|
337
297
|
if (!this.openai) {
|
|
338
298
|
return 'OpenAI not initialized, please specify the API key in the constructor.'
|
|
@@ -365,21 +325,34 @@ class HoloSphere {
|
|
|
365
325
|
return summary
|
|
366
326
|
}
|
|
367
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Upcasts content to parent hexagons recursively.
|
|
330
|
+
* @param {string} hex - The current hex identifier.
|
|
331
|
+
* @param {string} lense - The lense under which to upcast.
|
|
332
|
+
* @param {object} content - The content to upcast.
|
|
333
|
+
* @returns {Promise<object>} - The upcasted content.
|
|
334
|
+
*/
|
|
368
335
|
async upcast(hex, lense, content) {
|
|
369
336
|
let res = h3.getResolution(hex)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
console.log('Upcasting ', hex, lense, content)
|
|
373
|
-
let parent = h3.cellToParent(hex, res - 1)
|
|
374
|
-
await this.put(parent, lense, content)
|
|
375
|
-
if (res == 0)
|
|
337
|
+
if (res == 0) {
|
|
338
|
+
await this.putNode(hex, lense, content)
|
|
376
339
|
return content
|
|
377
|
-
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
console.log('Upcasting ', hex, lense, content, res)
|
|
343
|
+
await this.putNode(hex, lense, content)
|
|
344
|
+
let parent = h3.cellToParent(hex, res - 1)
|
|
378
345
|
return this.upcast(parent, lense, content)
|
|
346
|
+
}
|
|
379
347
|
}
|
|
380
348
|
|
|
381
349
|
|
|
382
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Updates the parent hexagon with a new report.
|
|
352
|
+
* @param {string} id - The child hex identifier.
|
|
353
|
+
* @param {string} report - The report to update.
|
|
354
|
+
* @returns {Promise<object>} - The updated parent information.
|
|
355
|
+
*/
|
|
383
356
|
async updateParent(id, report) {
|
|
384
357
|
let cellinfo = await this.getCellInfo(id)
|
|
385
358
|
let res = h3.getResolution(id)
|
|
@@ -395,11 +368,23 @@ class HoloSphere {
|
|
|
395
368
|
}
|
|
396
369
|
|
|
397
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Converts latitude and longitude to a hex identifier.
|
|
373
|
+
* @param {number} lat - The latitude.
|
|
374
|
+
* @param {number} lng - The longitude.
|
|
375
|
+
* @param {number} resolution - The resolution level.
|
|
376
|
+
* @returns {Promise<string>} - The resulting hex identifier.
|
|
377
|
+
*/
|
|
398
378
|
async getHex(lat, lng, resolution) {
|
|
399
379
|
return h3.latLngToCell(lat, lng, resolution);
|
|
400
380
|
}
|
|
401
381
|
|
|
402
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Retrieves all containing hexagons at all scales for given coordinates.
|
|
384
|
+
* @param {number} lat - The latitude.
|
|
385
|
+
* @param {number} lng - The longitude.
|
|
386
|
+
* @returns {Array<string>} - List of hex identifiers.
|
|
387
|
+
*/
|
|
403
388
|
getScalespace(lat, lng) {
|
|
404
389
|
let list = []
|
|
405
390
|
let cell = h3.latLngToCell(lat, lng, 14);
|
|
@@ -410,7 +395,11 @@ class HoloSphere {
|
|
|
410
395
|
return list
|
|
411
396
|
}
|
|
412
397
|
|
|
413
|
-
|
|
398
|
+
/**
|
|
399
|
+
* Retrieves all containing hexagons at all scales for a given hex.
|
|
400
|
+
* @param {string} hex - The hex identifier.
|
|
401
|
+
* @returns {Array<string>} - List of hex identifiers.
|
|
402
|
+
*/
|
|
414
403
|
getHexScalespace(hex) {
|
|
415
404
|
let list = []
|
|
416
405
|
let res = h3.getResolution(hex)
|
|
@@ -420,43 +409,73 @@ class HoloSphere {
|
|
|
420
409
|
return list
|
|
421
410
|
}
|
|
422
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Subscribes to changes in a specific hex and lense.
|
|
414
|
+
* @param {string} hex - The hex identifier.
|
|
415
|
+
* @param {string} lense - The lense to subscribe to.
|
|
416
|
+
* @param {function} callback - The callback to execute on changes.
|
|
417
|
+
*/
|
|
423
418
|
subscribe(hex, lense, callback) {
|
|
424
419
|
this.gun.get(hex).get(lense).map().on((data, key) => {
|
|
425
420
|
callback(data, key)
|
|
426
421
|
})
|
|
427
422
|
}
|
|
428
423
|
|
|
429
|
-
|
|
424
|
+
/**
|
|
425
|
+
* Retrieves the final vote for a user, considering delegations.
|
|
426
|
+
* @param {string} userId - The user's identifier.
|
|
427
|
+
* @param {string} topic - The voting topic.
|
|
428
|
+
* @param {object} votes - The current votes.
|
|
429
|
+
* @param {Set<string>} [visited=new Set()] - Set of visited users to prevent cycles.
|
|
430
|
+
* @returns {string|null} - The final vote or null if not found.
|
|
431
|
+
*/
|
|
432
|
+
getFinalVote(userId, topic, votes, visited = new Set()) {
|
|
433
|
+
if (this.users[userId]) { // Added this.users
|
|
434
|
+
if (visited.has(userId)) {
|
|
435
|
+
return null; // Avoid circular delegations
|
|
436
|
+
}
|
|
437
|
+
visited.add(userId);
|
|
430
438
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
visited.add(userId);
|
|
439
|
+
const delegation = this.users[userId].delegations[topic];
|
|
440
|
+
if (delegation && votes[delegation] === undefined) {
|
|
441
|
+
return this.getFinalVote(delegation, topic, votes, visited); // Prefixed with this
|
|
442
|
+
}
|
|
436
443
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
444
|
+
return votes[userId] !== undefined ? votes[userId] : null;
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
441
448
|
|
|
442
|
-
|
|
443
|
-
|
|
449
|
+
/**
|
|
450
|
+
* Aggregates votes for a specific hex and topic.
|
|
451
|
+
* @param {string} hexId - The hex identifier.
|
|
452
|
+
* @param {string} topic - The voting topic.
|
|
453
|
+
* @returns {object} - Aggregated vote counts.
|
|
454
|
+
*/
|
|
455
|
+
aggregateVotes(hexId, topic) {
|
|
456
|
+
if (!this.hexagonVotes[hexId] || !this.hexagonVotes[hexId][topic]) {
|
|
457
|
+
return {}; // Handle undefined votes
|
|
458
|
+
}
|
|
459
|
+
const votes = this.hexagonVotes[hexId][topic];
|
|
460
|
+
const aggregatedVotes = {};
|
|
444
461
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
462
|
+
Object.keys(votes).forEach(userId => {
|
|
463
|
+
const finalVote = this.getFinalVote(userId, topic, votes); // Prefixed with this
|
|
464
|
+
if (finalVote !== null) {
|
|
465
|
+
aggregatedVotes[finalVote] = (aggregatedVotes[finalVote] || 0) + 1;
|
|
466
|
+
}
|
|
467
|
+
});
|
|
448
468
|
|
|
449
|
-
|
|
450
|
-
const finalVote = getFinalVote(userId, topic, votes);
|
|
451
|
-
if (finalVote !== null) {
|
|
452
|
-
aggregatedVotes[finalVote] = (aggregatedVotes[finalVote] || 0) + 1;
|
|
469
|
+
return aggregatedVotes;
|
|
453
470
|
}
|
|
454
|
-
});
|
|
455
471
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
472
|
+
/**
|
|
473
|
+
* Delegates a user's vote to another user.
|
|
474
|
+
* @param {string} userId - The user's identifier.
|
|
475
|
+
* @param {string} topic - The voting topic.
|
|
476
|
+
* @param {string} delegateTo - The user to delegate the vote to.
|
|
477
|
+
*/
|
|
478
|
+
async delegateVote(userId, topic, delegateTo) {
|
|
460
479
|
const response = await fetch('/delegate', {
|
|
461
480
|
method: 'POST',
|
|
462
481
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -465,6 +484,13 @@ async delegateVote(userId, topic, delegateTo) {
|
|
|
465
484
|
alert(await response.text());
|
|
466
485
|
}
|
|
467
486
|
|
|
487
|
+
/**
|
|
488
|
+
* Casts a vote for a user on a specific topic and hex.
|
|
489
|
+
* @param {string} userId - The user's identifier.
|
|
490
|
+
* @param {string} hexId - The hex identifier.
|
|
491
|
+
* @param {string} topic - The voting topic.
|
|
492
|
+
* @param {string} vote - The vote choice.
|
|
493
|
+
*/
|
|
468
494
|
async vote(userId, hexId, topic, vote) {
|
|
469
495
|
const response = await fetch('/vote', {
|
|
470
496
|
method: 'POST',
|
|
@@ -475,6 +501,8 @@ async delegateVote(userId, topic, delegateTo) {
|
|
|
475
501
|
}
|
|
476
502
|
|
|
477
503
|
|
|
504
|
+
|
|
505
|
+
|
|
478
506
|
}
|
|
479
507
|
|
|
480
508
|
export default HoloSphere;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "holosphere",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Holonic Geospatial Communication Infrastructure based on h3.js and gun.js",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "holosphere.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"license": "GPL-3.0-or-later",
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"ajv": "^8.12.0",
|
|
14
|
-
"gun": "^0.2020.
|
|
14
|
+
"gun": "^0.2020.1240",
|
|
15
15
|
"h3-js": "^4.1.0",
|
|
16
16
|
"openai": "^4.29.2"
|
|
17
17
|
}
|