10 static inline int sqi(int x
) { return x
*x
; }
12 static inline double sqf(double x
) { return x
*x
; }
14 void points2d_rordered_free(points2d_rordered_t
*p
) {
21 points2d_rordered_t
*points2d_rordered_scale(const points2d_rordered_t
*orig
, const double f
)
23 points2d_rordered_t
*p
= malloc(sizeof(points2d_rordered_t
));
24 if(0 == orig
->nrs
) { // orig is empty
32 p
->rs
= malloc(p
->nrs
*sizeof(double));
33 p
->r_offsets
= malloc((p
->nrs
+1)*sizeof(ptrdiff_t));
35 const double af
= fabs(f
);
36 for(size_t i
= 0; i
< p
->nrs
; ++i
) {
37 p
->rs
[i
] = orig
->rs
[i
] * af
;
38 p
->r_offsets
[i
] = orig
->r_offsets
[i
];
40 p
->r_offsets
[p
->nrs
] = orig
->r_offsets
[p
->nrs
];
41 p
->base
= malloc(sizeof(point2d
) * p
->r_offsets
[p
->nrs
]);
42 for(size_t i
= 0; i
< p
->r_offsets
[p
->nrs
]; ++i
)
43 p
->base
[i
] = point2d_fromxy(orig
->base
[i
].x
* f
, orig
->base
[i
].y
* f
);
47 ptrdiff_t points2d_rordered_locate_r(const points2d_rordered_t
*p
, const double r
) {
50 //if(p->r_rs[p->nrs-1] < r)
52 ptrdiff_t lo
= 0, hi
= p
->nrs
-1, piv
;
54 piv
= (lo
+ hi
+ 1) / 2;
55 if(p
->rs
[piv
] > r
) // the result will be less or equal
63 points2d_rordered_t
points2d_rordered_annulus(const points2d_rordered_t
*orig
,
64 double minr
, bool inc_minr
, double maxr
, bool inc_maxr
) {
65 points2d_rordered_t p
;
67 imin
= points2d_rordered_locate_r(orig
, minr
);
68 imax
= points2d_rordered_locate_r(orig
, maxr
);
70 if(imax
>= orig
->nrs
) --imax
;
71 if(imax
< 0) goto nothing
;
73 if (!inc_minr
&& (orig
->rs
[imin
] <= minr
)) ++imin
;
74 if (!inc_maxr
&& (orig
->rs
[imax
] >= maxr
)) --imax
;
75 if (imax
< imin
) { // it's empty
83 p
.nrs
= imax
- imin
+ 1;
84 p
.rs
= orig
->rs
+ imin
;
85 p
.r_offsets
= orig
->r_offsets
+ imin
;
91 static inline double pr2(const point2d p
) {
92 return sqf(p
.x
) + sqf(p
.y
);
95 static inline double prn(const point2d p
) {
99 static int point2d_cmp_by_r2(const void *p1
, const void *p2
) {
100 const point2d
*z1
= (point2d
*) p1
, *z2
= (point2d
*) p2
;
101 double dif
= pr2(*z1
) - pr2(*z2
);
102 if(dif
> 0) return 1;
103 else if(dif
< 0) return -1;
107 static points2d_rordered_t
*points2d_rordered_frompoints_c(point2d
*orig_base
, const size_t nmemb
,
108 const double rtol
, const double atol
, bool copybase
)
110 // TODO should the rtol and atol relate to |r| or r**2? (Currently: |r|)
113 points2d_rordered_t
*p
= malloc(sizeof(points2d_rordered_t
));
123 p
->base
= malloc(nmemb
* sizeof(point2d
));
124 memcpy(p
->base
, orig_base
, nmemb
* sizeof(point2d
));
128 qsort(p
->base
, nmemb
, sizeof(point2d
), point2d_cmp_by_r2
);
130 // first pass: determine the number of "different" r's.
132 double rcur
= -INFINITY
;
133 double rcurmax
= -INFINITY
;
134 for (size_t i
= 0; i
< nmemb
; ++i
)
135 if ((rcur
= prn(p
->base
[i
])) > rcurmax
) {
137 rcurmax
= rcur
* (1 + rtol
) + atol
;
141 // TODO check malloc return values
142 p
->rs
= malloc(rcount
* sizeof(double));
143 p
->r_offsets
= malloc((rcount
+1) * sizeof(ptrdiff_t));
146 // second pass: fill teh rs;
148 size_t rcurcount
= 0;
149 rcur
= prn(p
->base
[0]);
150 rcurmax
= rcur
* (1 + rtol
) + atol
;
153 for (size_t i
= 0; i
< nmemb
; ++i
) {
154 rcur
= prn(p
->base
[i
]);
155 if (rcur
> rcurmax
) {
156 p
->rs
[ri
] = rcursum
/ (double) rcurcount
; // average of the accrued r's within tolerance
158 p
->r_offsets
[ri
] = i
; //r_offsets[ri-1] + rcurcount (is the same)
161 rcurmax
= rcur
* (1 + rtol
) + atol
;
166 p
->rs
[ri
] = rcursum
/ (double) rcurcount
;
167 p
->r_offsets
[rcount
] = nmemb
;
172 points2d_rordered_t
*points2d_rordered_frompoints(const point2d
*orig_base
, const size_t nmemb
,
173 const double rtol
, const double atol
)
175 return points2d_rordered_frompoints_c((point2d
*)orig_base
, nmemb
, rtol
, atol
, true);
178 points2d_rordered_t
*points2d_rordered_shift(const points2d_rordered_t
*orig
, const point2d shift
,
179 double rtol
, double atol
)
181 size_t n
= (orig
->nrs
> 0) ?
182 orig
->r_offsets
[orig
->nrs
] - orig
->r_offsets
[0] : 0;
184 point2d
* shifted
= malloc(n
* sizeof(point2d
));
186 for(size_t i
= 0; i
< n
; ++i
)
187 shifted
[i
] = cart2_add(orig
->base
[i
+orig
->r_offsets
[0]], shift
);
189 return points2d_rordered_frompoints_c(shifted
, n
,rtol
, atol
, false);
193 * EQUILATERAL TRIANGULAR LATTICE
198 * N. B. the possible radii (distances from origin) of the lattice points can be described as
200 * r**2 / a**2 == i**2 + j**2 + i*j ,
202 * where i, j are integer indices describing steps along two basis vectors (which have
203 * 60 degree angle between them).
205 * The plane can be divided into six sextants, characterized as:
207 * 0) i >= 0 && j >= 0,
210 * 1) i <= 0 && {j >= 0} && i + j >= 0,
213 * 2) {i <= 0} && j >= 0 && i + j <= 0,
216 * 3) i <= 0 && j <= 0,
219 * 4) i >= 0 && {j <= 0} && i + j <= 0,
222 * 5) {i >= 0} && j <= 0 && i + j >= 0,
226 * The [a], [b] are two variants that uniquely assign the points at the sextant boundaries.
227 * The {conditions} in braces are actually redundant.
229 * In each sextant, the "minimum steps from the origin" value is calculated as:
237 * The "spider web" generation for s steps from the origin (s-th layer) goes as following (variant [a]):
238 * 0) for (i = s, j = 0; i > 0; --i, ++j)
239 * 1) for (i = 0, j = s; i + j > 0; --i)
240 * 2) for (i = -s, j = s; j > 0; --j)
241 * 3) for (i = -s, j = 0; i < 0; ++i, --j)
242 * 4) for (i = 0, j = -s; i + j < 0; ++i)
243 * 5) for (i = s, j = -s; j < 0; ++j)
246 * Length of the s-th layer is 6*s for s >= 1. Size (number of lattice points) of the whole s-layer "spider web"
247 * is therefore 3*s*(s+1), excluding origin.
248 * The real area inside the web is (a*s)**2 * 3 * sqrt(3) / 2.
249 * Area of a unit cell is a**2 * sqrt(3)/2.
250 * Inside the web, but excluding the circumscribed circle, there is no more
251 * than 3/4.*s*(s+1) + 6*s lattice cells (FIXME pretty stupid but safe estimate).
253 * s-th layer circumscribes a circle of radius a * s * sqrt(3)/2.
258 typedef struct triangular_lattice_gen_privstuff_t
{
259 intcoord2_t
*pointlist_base
; // allocated memory for the point "buffer"
260 size_t pointlist_capacity
;
261 // beginning and end of the point "buffer"
262 // not 100% sure what type should I use here
263 // (these are both relative to pointlist_base, due to possible realloc's)
264 ptrdiff_t pointlist_beg
, pointlist_n
; // end of queue is at [(pointlist_beg+pointlist_n)%pointlist_capacity]
265 int maxs
; // the highest layer of the spider web generated (-1 by init, 0 is only origin (if applicable))
266 // capacities of the arrays in ps
267 size_t ps_rs_capacity
;
268 size_t ps_points_capacity
; // this is the "base" array
269 // TODO anything else?
270 } triangular_lattice_gen_privstuff_t
;
272 static inline int trilat_r2_ij(const int i
, const int j
) {
273 return sqi(i
) + sqi(j
) + i
*j
;
276 static inline int trilat_r2_coord(const intcoord2_t c
) {
277 return trilat_r2_ij(c
.i
, c
.j
);
280 // version with offset (n.b. this is includes a factor of 3)
281 static inline int trilat_3r2_ijs(const int i
, const int j
, const int s
) {
282 return 3*(sqi(i
) + sqi(j
) + i
*j
+ j
*s
) + sqi(s
);
285 static inline int trilat_3r2_coord_s(const intcoord2_t c
, const int s
) {
286 return trilat_3r2_ijs(c
.i
, c
.j
, s
);
291 // Classify points into sextants (variant [a] above)
292 static int trilat_sextant_ij_a(const int i
, const int j
) {
294 if (i
> 0 && j
>= 0) return 0;
295 if (i
<= 0 && w
> 0) return 1;
296 if (w
<= 0 && j
> 0) return 2;
297 if (i
< 0 && j
<= 0) return 3;
298 if (i
>= 0 && w
< 0) return 4;
299 if (w
>= 0 && j
< 0) return 5;
300 if (i
== 0 && j
== 0) return -1; // origin
301 assert(0); // other options should be impossible
304 static inline size_t tlgp_pl_end(const triangular_lattice_gen_privstuff_t
*p
) {
305 return (p
->pointlist_beg
+ p
->pointlist_n
) % p
->pointlist_capacity
;
309 static inline void tlgpl_end_inc(triangular_lattice_gen_privstuff_t
*p
) {
310 p
->p_pointlist_n
+= 1;
314 // Puts a point to the end of the point queue
315 static inline void trilatgen_pointlist_append_ij(triangular_lattice_gen_t
*g
, int i
, int j
) {
316 intcoord2_t thepoint
= {i
, j
};
317 triangular_lattice_gen_privstuff_t
*p
= g
->priv
;
318 assert(p
->pointlist_n
< p
->pointlist_capacity
);
320 // the actual addition
321 p
->pointlist_base
[tlgp_pl_end(p
)] = thepoint
;
325 // Arange the pointlist queue into a continuous chunk of memory, so that we can qsort() it
326 static void trilatgen_pointlist_linearise(triangular_lattice_gen_t
*g
) {
327 triangular_lattice_gen_privstuff_t
*p
= g
->priv
;
328 assert(p
->pointlist_n
<= p
->pointlist_capacity
);
329 if (p
->pointlist_beg
+ p
->pointlist_n
<= p
->pointlist_capacity
)
330 return; // already linear, do nothing
331 else if (p
->pointlist_n
== p
->pointlist_capacity
) { // full, therefore linear
332 p
->pointlist_beg
= 0;
334 } else { // non-linear; move "to the right"
335 while (p
->pointlist_beg
< p
->pointlist_capacity
) {
336 p
->pointlist_base
[tlgp_pl_end(p
)] = p
->pointlist_base
[p
->pointlist_beg
];
337 ++(p
->pointlist_beg
);
339 p
->pointlist_beg
= 0;
344 static inline intcoord2_t
trilatgen_pointlist_first(const triangular_lattice_gen_t
*g
) {
345 return g
->priv
->pointlist_base
[g
->priv
->pointlist_beg
];
348 static inline void trilatgen_pointlist_deletefirst(triangular_lattice_gen_t
*g
) {
349 triangular_lattice_gen_privstuff_t
*p
= g
->priv
;
350 assert(p
->pointlist_n
> 0);
352 if(p
->pointlist_beg
== p
->pointlist_capacity
)
353 p
->pointlist_beg
= 0;
357 // TODO abort() and void or errorchecks and int?
358 static int trilatgen_pointlist_extend_capacity(triangular_lattice_gen_t
*g
, size_t newcapacity
) {
359 triangular_lattice_gen_privstuff_t
*p
= g
->priv
;
360 if (newcapacity
<= p
->pointlist_capacity
)
363 trilatgen_pointlist_linearise(g
);
365 QPMS_CRASHING_REALLOC(p
->pointlist_base
, newcapacity
* sizeof(intcoord2_t
));
366 p
->pointlist_capacity
= newcapacity
;
370 // lower estimate for the number of lattice points inside the circumscribed hexagon, but outside the circle
371 static inline size_t tlg_circumscribe_reserve(int maxs
) {
374 return 3*maxs
*(maxs
+1)/4 + 6*maxs
;
377 static inline size_t tlg_websize(int maxs
) {
381 return 3*maxs
*(maxs
+1); // does not include origin point!
384 static int trilatgen_ensure_pointlist_capacity(triangular_lattice_gen_t
*g
, int newmaxs
) {
385 return trilatgen_pointlist_extend_capacity(g
,
386 tlg_circumscribe_reserve(g
->priv
->maxs
) // Space for those which are already in
387 + tlg_websize(newmaxs
) - tlg_websize(g
->priv
->maxs
) // space for the new web layers
388 + 1 // reserve for the origin
392 static int trilatgen_ensure_ps_rs_capacity(triangular_lattice_gen_t
*g
, int maxs
) {
396 size_t needed_capacity
= 1 // reserve for origin
397 + maxs
*(maxs
+1)/2; // stupid but safe estimate: number of points in a sextant of maxs-layered spider web
399 if (needed_capacity
<= g
->priv
->ps_rs_capacity
)
400 return 0; // probably does not happen, but fuck it
402 QPMS_CRASHING_REALLOC(g
->ps
.rs
, needed_capacity
* sizeof(double));
403 QPMS_CRASHING_REALLOC(g
->ps
.r_offsets
, (needed_capacity
+ 1) * sizeof(ptrdiff_t));
405 g
->priv
->ps_rs_capacity
= needed_capacity
;
409 static int trilatgen_ensure_ps_points_capacity(triangular_lattice_gen_t
*g
, int maxs
) {
412 size_t needed_capacity
= 1 /*res. for origin */ + tlg_websize(maxs
) /* stupid but safe */;
413 if(needed_capacity
<= g
->priv
->ps_points_capacity
)
416 QPMS_CRASHING_REALLOC(g
->ps
.base
, needed_capacity
* sizeof(point2d
));
417 g
->priv
->ps_points_capacity
= needed_capacity
;
421 static int trilat_cmp_intcoord2_by_r2(const void *p1
, const void *p2
) {
422 return trilat_r2_coord(*(const intcoord2_t
*)p1
) - trilat_r2_coord(*(const intcoord2_t
*)p2
);
426 static int trilat_cmp_intcoord2_by_3r2_plus1s(const void *p1
, const void *p2
) {
427 return trilat_3r2_coord_s(*(const intcoord2_t
*)p1
, +1) - trilat_3r2_coord_s(*(const intcoord2_t
*)p2
, +1);
430 static int trilat_cmp_intcoord2_by_3r2_minus1s(const void *p1
, const void *p2
) {
431 return trilat_3r2_coord_s(*(const intcoord2_t
*)p1
, -1) - trilat_3r2_coord_s(*(const intcoord2_t
*)p2
, -1);
434 static int trilat_cmp_intcoord2_by_3r2(const void *p1
, const void *p2
, void *sarg
) {
435 return trilat_3r2_coord_s(*(const intcoord2_t
*)p1
, *(int *)sarg
) - trilat_3r2_coord_s(*(const intcoord2_t
*)p2
, *(int *)sarg
);
438 static void trilatgen_sort_pointlist(triangular_lattice_gen_t
*g
) {
439 trilatgen_pointlist_linearise(g
);
440 triangular_lattice_gen_privstuff_t
*p
= g
->priv
;
441 int (*compar
)(const void *, const void *);
442 switch (g
->hexshift
) {
444 compar
= trilat_cmp_intcoord2_by_r2
;
447 compar
= trilat_cmp_intcoord2_by_3r2_minus1s
;
450 compar
= trilat_cmp_intcoord2_by_3r2_plus1s
;
455 qsort(p
->pointlist_base
+ p
->pointlist_beg
, p
->pointlist_n
, sizeof(intcoord2_t
), compar
);
458 triangular_lattice_gen_t
* triangular_lattice_gen_init(double a
, TriangularLatticeOrientation ori
, bool include_origin
,
461 triangular_lattice_gen_t
*g
= malloc(sizeof(triangular_lattice_gen_t
));
463 g
->hexshift
= ((hexshift
% 3)+3)%3; // reduce to the set {-1, 0, 1}
464 if (2 == g
->hexshift
)
466 g
->orientation
= ori
;
467 g
->includes_origin
= include_origin
;
471 g
->ps
.r_offsets
= NULL
;
472 g
->priv
= malloc(sizeof(triangular_lattice_gen_privstuff_t
));
474 g
->priv
->pointlist_capacity
= 0;
475 g
->priv
->pointlist_base
= NULL
;
476 g
->priv
->pointlist_beg
= 0;
477 g
->priv
->pointlist_n
= 0;
478 g
->priv
->ps_rs_capacity
= 0;
479 g
->priv
->ps_points_capacity
= 0;
483 void triangular_lattice_gen_free(triangular_lattice_gen_t
*g
) {
486 free(g
->ps
.r_offsets
);
487 free(g
->priv
->pointlist_base
);
492 const points2d_rordered_t
* triangular_lattice_gen_getpoints(const triangular_lattice_gen_t
*g
) {
496 int triangular_lattice_gen_extend_to_r(triangular_lattice_gen_t
* g
, const double maxr
) {
497 return triangular_lattice_gen_extend_to_steps(g
, maxr
/g
->a
);
500 int triangular_lattice_gen_extend_to_steps(triangular_lattice_gen_t
* g
, int maxsteps
)
502 if (maxsteps
<= g
->priv
->maxs
) // nothing needed
504 // TODO FIXME: check for maximum possible maxsteps (not sure what it is)
506 err
= trilatgen_ensure_pointlist_capacity(g
, maxsteps
507 + abs(g
->hexshift
) /*FIXME this is quite brainless addition, probably not even needed.*/);
509 err
= trilatgen_ensure_ps_rs_capacity(g
, maxsteps
510 + abs(g
->hexshift
) /*FIXME this is quite brainless addition, probably not even needed.*/);
512 err
= trilatgen_ensure_ps_points_capacity(g
, maxsteps
513 + abs(g
->hexshift
) /*FIXME this is quite brainless addition, probably not even needed.*/);
516 if(g
->includes_origin
&& g
->priv
->maxs
< 0) // Add origin if not there yet
517 trilatgen_pointlist_append_ij(g
, 0, 0);
519 for (int s
= g
->priv
->maxs
+ 1; s
<= maxsteps
; ++s
) {
521 // now go along the spider web layer as indicated in the lenghthy comment above
522 for (i
= s
, j
= 0; i
> 0; --i
, ++j
) trilatgen_pointlist_append_ij(g
,i
,j
);
523 for (i
= 0, j
= s
; i
+ j
> 0; --i
) trilatgen_pointlist_append_ij(g
,i
,j
);
524 for (i
= -s
, j
= s
; j
> 0; --j
) trilatgen_pointlist_append_ij(g
,i
,j
);
525 for (i
= -s
, j
= 0; i
< 0; ++i
, --j
) trilatgen_pointlist_append_ij(g
,i
,j
);
526 for (i
= 0, j
= -s
; i
+ j
< 0; ++i
) trilatgen_pointlist_append_ij(g
,i
,j
);
527 for (i
= s
, j
= -s
; j
< 0; ++j
) trilatgen_pointlist_append_ij(g
,i
,j
);
530 trilatgen_sort_pointlist(g
);
532 // initialise first r_offset if needed
534 g
->ps
.r_offsets
[0] = 0;
536 //ted je potřeba vytahat potřebný počet bodů z fronty a naflákat je do ps.
537 // FIXME pohlídat si kapacitu datových typů
538 //int maxr2i = sqi(maxsteps) * 3 / 4;
539 int maxr2i3
= sqi(maxsteps
) * 9 / 4 + sqi(g
->hexshift
) - abs(3*maxsteps
*g
->hexshift
);
540 while (g
->priv
->pointlist_n
> 0) { // This condition should probably be always true anyways.
541 intcoord2_t coord
= trilatgen_pointlist_first(g
);
542 //int r2i_cur = trilat_r2_coord(coord);
543 //if(r2i_cur > maxr2i)
544 int r2i3_cur
= trilat_3r2_coord_s(coord
, g
->hexshift
);
545 if(r2i3_cur
> maxr2i3
)
547 g
->ps
.rs
[g
->ps
.nrs
] = sqrt(/*r2i_cur*/ r2i3_cur
/3.) * g
->a
;
548 g
->ps
.r_offsets
[g
->ps
.nrs
+1] = g
->ps
.r_offsets
[g
->ps
.nrs
]; // the difference is the number of points on the circle
550 coord
= trilatgen_pointlist_first(g
);
551 //if(r2i_cur != trilat_r2_coord(coord))
552 if (r2i3_cur
!= trilat_3r2_coord_s(coord
, g
->hexshift
))
555 trilatgen_pointlist_deletefirst(g
);
557 switch (g
->orientation
) {
558 case TRIANGULAR_HORIZONTAL
:
559 thepoint
= point2d_fromxy((coord
.i
+.5*coord
.j
)*g
->a
, (M_SQRT3_2
*coord
.j
+ g
->hexshift
*M_1_SQRT3
)*g
->a
);
561 case TRIANGULAR_VERTICAL
:
562 thepoint
= point2d_fromxy(-(M_SQRT3_2
*coord
.j
+ g
->hexshift
*M_1_SQRT3
)*g
->a
, (coord
.i
+.5*coord
.j
)*g
->a
);
565 QPMS_INVALID_ENUM(g
->orientation
);
567 g
->ps
.base
[g
->ps
.r_offsets
[g
->ps
.nrs
+1]] = thepoint
;
568 ++(g
->ps
.r_offsets
[g
->ps
.nrs
+1]);
573 g
->priv
->maxs
= maxsteps
;
577 honeycomb_lattice_gen_t
*honeycomb_lattice_gen_init_h(double h
, TriangularLatticeOrientation ori
) {
578 double a
= M_SQRT3
* h
;
579 honeycomb_lattice_gen_t
*g
= honeycomb_lattice_gen_init_a(a
, ori
);
580 g
->h
= h
; // maybe it's not necessary as sqrt is "exact"
584 honeycomb_lattice_gen_t
*honeycomb_lattice_gen_init_a(double a
, TriangularLatticeOrientation ori
) {
585 honeycomb_lattice_gen_t
*g
= calloc(1, sizeof(honeycomb_lattice_gen_t
)); // this already inits g->ps to zeros
587 g
->h
= a
* M_1_SQRT3
;
588 g
->tg
= triangular_lattice_gen_init(a
, ori
, true, 1);
592 void honeycomb_lattice_gen_free(honeycomb_lattice_gen_t
*g
) {
595 free(g
->ps
.r_offsets
);
596 triangular_lattice_gen_free(g
->tg
);
600 int honeycomb_lattice_gen_extend_to_r(honeycomb_lattice_gen_t
*g
, double maxr
) {
601 return honeycomb_lattice_gen_extend_to_steps(g
, maxr
/g
->a
); /*CHECKME whether g->a is the correct denom.*/
604 int honeycomb_lattice_gen_extend_to_steps(honeycomb_lattice_gen_t
*g
, const int maxsteps
) {
605 if (maxsteps
<= g
->tg
->priv
->maxs
) // nothing needed
607 triangular_lattice_gen_extend_to_steps(g
->tg
, maxsteps
);
609 QPMS_CRASHING_REALLOC(g
->ps
.rs
, g
->tg
->ps
.nrs
* sizeof(double));
610 QPMS_CRASHING_REALLOC(g
->ps
.r_offsets
, (g
->tg
->ps
.nrs
+1) * sizeof(ptrdiff_t));
611 QPMS_CRASHING_REALLOC(g
->ps
.base
, 2 * (g
->tg
->ps
.r_offsets
[g
->tg
->ps
.nrs
]) * sizeof(point2d
));
613 // Now copy (new) contents of g->tg->ps into g->ps, but with inverse copy of each point
614 for (size_t ri
= g
->ps
.nrs
; ri
<= g
->tg
->ps
.nrs
; ++ri
)
615 g
->ps
.r_offsets
[ri
] = g
->tg
->ps
.r_offsets
[ri
] * 2;
616 for (ptrdiff_t i_orig
= g
->tg
->ps
.r_offsets
[g
->ps
.nrs
]; i_orig
< g
->tg
->ps
.r_offsets
[g
->tg
->ps
.nrs
]; ++i_orig
) {
617 point2d p
= g
->tg
->ps
.base
[i_orig
];
618 g
->ps
.base
[2*i_orig
] = p
;
619 p
.x
*= -1; p
.y
*= -1;
620 g
->ps
.base
[2*i_orig
+ 1] = p
;
622 g
->ps
.nrs
= g
->tg
->ps
.nrs
;
632 * Lagrange-Gauss reduction of a 2D basis.
633 * The output shall satisfy |out1| <= |out2| <= |out2 - out1|
635 void l2d_reduceBasis(cart2_t b1
, cart2_t b2
, cart2_t
*out1
, cart2_t
*out2
){
636 double B1
= cart2_dot(b1
, b1
);
637 double mu
= cart2_dot(b1
, b2
) / B1
;
638 b2
= cart2_substract(b2
, cart2_scale(round(mu
), b1
));
639 double B2
= cart2_dot(b2
, b2
);
645 mu
= cart2_dot(b1
, b2
) / B1
;
646 b2
= cart2_substract(b2
, cart2_scale(round(mu
), b1
));
647 B2
= cart2_dot(b2
, b2
);
653 void l3d_reduceBasis(const cart3_t in
[3], cart3_t out
[3]) {
654 memcpy(out
, in
, 3*sizeof(cart3_t
));
655 QPMS_ENSURE_SUCCESS(qpms_reduce_lattice_basis((double *)out
, 3, 3, 1.));
659 * This gives the "ordered shortest triple" of base vectors (each pair from the triple
660 * is a base) and there may not be obtuse angle between o1, o2 and between o2, o3
662 void l2d_shortestBase3(cart2_t b1
, cart2_t b2
, cart2_t
*o1
, cart2_t
*o2
, cart2_t
*o3
){
663 l2d_reduceBasis(b1
, b2
, &b1
, &b2
);
665 if (l2d_is_obtuse_r(b1
, b2
, 0)) {
667 *o2
= cart2_add(b2
, b1
);
670 *o3
= cart2_substract(b2
, b1
);
674 // Determines whether angle between inputs is obtuse
675 bool l2d_is_obtuse_r(cart2_t b1
, cart2_t b2
, double rtol
) {
676 const double B1
= cart2_normsq(b1
);
677 const double B2
= cart2_normsq(b2
);
678 const cart2_t b3
= cart2_substract(b2
, b1
);
679 const double B3
= cart2_normsq(b3
);
680 const double eps
= rtol
* (B1
+ B2
); // TODO check what kind of quantity this should be. Maybe rtol should relate to lengths, not lengths**2
681 return (B3
- B2
- B1
> eps
);
687 * return value is 4 or 6.
689 int l2d_shortestBase46(const cart2_t i1
, const cart2_t i2
, cart2_t
*o1
, cart2_t
*o2
, cart2_t
*o3
, cart2_t
*o4
, cart2_t
*o5
, cart2_t
*o6
, double rtol
){
691 l2d_reduceBasis(i1
, i2
, &b1
, &b2
);
692 const double b1s
= cart2_normsq(b1
);
693 const double b2s
= cart2_normsq(b2
);
694 b3
= cart2_substract(b2
, b1
);
695 const double b3s
= cart2_normsq(b3
);
696 const double eps
= rtol
* (b1s
+ b2s
); // TODO check the same as in l2d_is_obtuse_r
697 if(fabs(b3s
-b2s
-b1s
) < eps
) {
698 *o1
= b1
; *o2
= b2
; *o3
= cart2_scale(-1, b1
); *o4
= cart2_scale(-1, b2
);
702 if (b3s
-b2s
-b1s
> eps
) { //obtuse
704 b2
= cart2_add(b2
, b1
);
706 *o1
= b1
; *o2
= b2
; *o3
= b3
;
707 *o4
= cart2_scale(-1, b1
);
708 *o5
= cart2_scale(-1, b2
);
709 *o6
= cart2_scale(-1, b3
);
715 * Given two basis vectors, returns 2D Bravais lattice type.
717 LatticeType2
l2d_classifyLattice(cart2_t b1
, cart2_t b2
, double rtol
)
719 l2d_reduceBasis(b1
, b2
, &b1
, &b2
);
720 cart2_t b3
= cart2_substract(b2
, b1
);
721 double b1s
= cart2_normsq(b1
), b2s
= cart2_normsq(b2
), b3s
= cart2_normsq(b3
);
722 double eps
= rtol
* (b2s
+ b1s
); //FIXME what should eps be?
723 // avoid obtuse angle between b1 and b2. TODO this should be yet tested
724 // TODO use is_obtuse here?
725 if (b3s
- b2s
- b1s
> eps
) {
727 b2
= cart2_add(b2
, b1
);
728 // N.B. now the assumption |b3| >= |b2| is no longer valid
729 // b3 = cart2_substract(b2, b1)
730 b2s
= cart2_normsq(b2
);
731 b3s
= cart2_normsq(b3
);
733 if (fabs(b2s
-b1s
) < eps
|| fabs(b2s
- b3s
) < eps
) { // isoscele
734 if (fabs(b3s
-b1s
) < eps
)
735 return EQUILATERAL_TRIANGULAR
;
736 else if (fabs(b3s
- 2*b1s
))
740 } else if (fabs(b3s
-b2s
-b1s
) < eps
)
746 LatticeFlags
l2d_detectRightAngles(cart2_t b1
, cart2_t b2
, double rtol
)
748 l2d_reduceBasis(b1
, b2
, &b1
, &b2
);
749 cart2_t ht
= cart2_substract(b2
, b1
);
750 double b1s
= cart2_normsq(b1
), b2s
= cart2_normsq(b2
), hts
= cart2_normsq(ht
);
751 double eps
= rtol
* (b2s
+ b1s
); //FIXME what should eps be?
752 if (hts
- b2s
- b1s
<= eps
)
753 return ORTHOGONAL_01
;
755 return NOT_ORTHOGONAL
;
758 LatticeFlags
l3d_detectRightAngles(const cart3_t basis_nr
[3], double rtol
)
761 l3d_reduceBasis(basis_nr
, b
);
762 LatticeFlags res
= NOT_ORTHOGONAL
;
763 for (int i
= 0; i
< 3; ++i
) {
764 cart3_t ba
= b
[i
], bb
= b
[(i
+1) % 3];
765 cart3_t ht
= cart3_substract(ba
, bb
);
766 double bas
= cart3_normsq(ba
), bbs
= cart3_normsq(ba
), hts
= cart3_normsq(ht
);
767 double eps
= rtol
* (bas
+ bbs
);
768 if (hts
- bbs
- bas
<= eps
)
769 res
|= ((LatticeFlags
[]){ORTHOGONAL_01
, ORTHOGONAL_12
, ORTHOGONAL_02
})[i
];
776 int l2d_shortestBase46_arr(cart2_t i1
, cart2_t i2
, cart2_t
*oarr
, double rtol
);
778 // Determines whether angle between inputs is obtuse
779 bool l2d_is_obtuse_r(cart2_t i1
, cart2_t i2
, double rtol
);
782 // Other functions in lattices2d.py: TODO?
785 // generateLatticeDisk()
791 * Given basis vectors, returns the corners of the Wigner-Seits unit cell (W1, W2, -W1, W2)
792 * for rectangular and square lattice or (w1, w2, w3, -w1, -w2, -w3) otherwise.
794 int l2d_cellCornersWS(cart2_t i1
, cart2_t i2
, cart2_t
*o1
, cart2_t
*o2
, cart2_t
*o3
, cart2_t
*o4
, cart2_t
*o5
, cart2_t
*o6
, double rtol
);
796 int l2d_cellCornersWS_arr(cart2_t i1
, cart2_t i2
, cart2_t
*oarr
, double rtol
);
800 // Reciprocal bases; returns 0 on success, TODO non-zero if b1 and b2 are parallel
801 int l2d_reciprocalBasis1(cart2_t b1
, cart2_t b2
, cart2_t
*rb1
, cart2_t
*rb2
) {
802 l2d_reduceBasis(b1
, b2
, &b1
, &b2
);
803 const double det
= b1
.x
* b2
.y
- b1
.y
* b2
.x
;
805 rb1
->x
= rb1
->y
= rb2
->x
= rb2
->y
= NAN
;
806 return QPMS_ERROR
; // TODO more specific error code
809 rb1
->y
= -b2
.x
/ det
;
810 rb2
->x
= -b1
.y
/ det
;
816 int l2d_reciprocalBasis2pi(cart2_t b1
, cart2_t b2
, cart2_t
*rb1
, cart2_t
*rb2
) {
817 int retval
= l2d_reciprocalBasis1(b1
, b2
, rb1
, rb2
);
818 if (retval
== QPMS_SUCCESS
) {
819 *rb1
= cart2_scale(2 * M_PI
, *rb1
);
820 *rb2
= cart2_scale(2 * M_PI
, *rb2
);
825 // 3D reciprocal bases; returns (direct) unit cell volume. Assumes direct lattice basis already reduced.
826 double l3d_reciprocalBasis1(const cart3_t db
[3], cart3_t rb
[3]) {
827 double vol
= cart3_tripleprod(db
[0], db
[1], db
[2]);
828 for(int i
= 0; i
< 3; ++i
)
829 rb
[i
] = cart3_divscale(cart3_vectorprod(db
[(i
+1) % 3], db
[(i
+2) % 3]), vol
);
833 double l3d_reciprocalBasis2pi(const cart3_t db
[3], cart3_t rb
[3]) {
834 double vol
= l3d_reciprocalBasis1(db
, rb
);
835 for(int i
= 1; i
< 3; ++i
)
836 rb
[i
] = cart3_scale(2 * M_PI
, rb
[i
]);
840 // returns the radius of inscribed circle of a hexagon (or rectangle/square if applicable) created by the shortest base triple
841 double l2d_hexWebInCircleRadius(cart2_t i1
, cart2_t i2
) {
843 l2d_shortestBase3(i1
, i2
, &b1
, &b2
, &b3
);
844 const double r1
= cart2norm(b1
), r2
= cart2norm(b2
), r3
= cart2norm(b3
);
845 const double p
= (r1
+r2
+r3
)*0.5;
846 return 2*sqrt(p
*(p
-r1
)*(p
-r2
)*(p
-r3
))/r3
; // CHECK is r3 guaranteed to be longest?
850 double l2d_unitcell_area(cart2_t b1
, cart2_t b2
) {
851 l2d_reduceBasis(b1
, b2
, &b1
, &b2
);
852 const double det
= b1
.x
* b2
.y
- b1
.y
* b2
.x
;
858 double qpms_emptylattice2_mode_nth(
867 void qpms_emptylattice2_modes_maxindex(
868 double target_freqs
[],
878 static int dblcmp(const void *p1
, const void *p2
) {
879 const double *x1
= (double *) p1
, *x2
= (double *) p2
;
880 double dif
= *x1
- *x2
;
881 if(dif
> 0) return 1;
882 else if (dif
< 0) return -1;
886 size_t qpms_emptylattice2_modes_maxfreq(double **target_freqs
,
887 cart2_t b1
, cart2_t b2
, double rtol
, cart2_t k
,
888 double c
, double maxfreq
)
891 l2d_reduceBasis(b1
, b2
, &b1
, &b2
);
892 double maxk_safe
= cart2norm(k
) + maxfreq
/ c
+ cart2norm(b1
) + cart2norm(b2
); // This is an overkill
893 size_t capacity
= PGen_xyWeb_sizecap(b1
, b2
, rtol
, k
, 0, true, maxk_safe
, true);
895 QPMS_CRASHING_MALLOC(Kpoints
, sizeof(cart2_t
) * capacity
);
896 PGen Kgen
= PGen_xyWeb_new(b1
, b2
, rtol
, k
, 0, true, maxk_safe
, true);
898 size_t generated
= 0;
899 PGenReturnDataBulk rd
;
900 while((rd
= PGen_fetch_cart2(&Kgen
, capacity
- generated
, Kpoints
+ generated
)).flags
& PGEN_NOTDONE
) {
901 generated
+= rd
.generated
;
902 if (capacity
<= generated
) {
909 QPMS_CRASHING_MALLOC(thefreqs
, generated
* sizeof(double));
910 for(size_t i
= 0; i
< generated
; ++i
) thefreqs
[i
] = cart2norm(Kpoints
[i
]) * c
;
914 qsort(thefreqs
, generated
, sizeof(double), dblcmp
);
917 for(count
= 0; count
< generated
; ++count
)
918 if(thefreqs
[count
] > maxfreq
) {
925 *target_freqs
= thefreqs
;
929 void qpms_emptylattice2_modes_nearest(double target
[2],
930 cart2_t b1
, cart2_t b2
, double rtol
,
931 cart2_t k
, double c
, double omega
)
935 size_t n
= qpms_emptylattice2_modes_maxfreq(&freqlist
,
936 b1
, b2
, rtol
, k
, c
, omega
);
937 target
[0] = (n
> 1) ? freqlist
[n
-2] : NAN
;
938 target
[1] = freqlist
[n
-1];