|
| 1 | +import collections |
| 2 | + |
| 3 | +import numpy as np |
| 4 | + |
| 5 | +from mpas_tools.mesh.creation.util import circumcenter |
| 6 | + |
| 7 | +point = collections.namedtuple('Point', ['x', 'y', 'z']) |
| 8 | + |
| 9 | + |
| 10 | +def test_circumcenter_planar(): |
| 11 | + # Use provided cell coordinates and vertex as expected output |
| 12 | + x1, y1, z1 = 0.0, 17320.50807569, 0.0 |
| 13 | + x2, y2, z2 = 10000.0, 17320.50807569, 0.0 |
| 14 | + x3, y3, z3 = 5000.0, 25980.76211353, 0.0 |
| 15 | + expected = (5000.0, 20207.25942164, 0.0) |
| 16 | + |
| 17 | + xv, yv, zv = circumcenter(False, x1, y1, z1, x2, y2, z2, x3, y3, z3) |
| 18 | + assert np.allclose([xv, yv, zv], expected, atol=1e-8), ( |
| 19 | + f'Planar circumcenter failed: got {[xv, yv, zv]}, expected {expected}' |
| 20 | + ) |
| 21 | + |
| 22 | + xv_old, yv_old, zv_old = _old_circumcenter( |
| 23 | + False, x1, y1, z1, x2, y2, z2, x3, y3, z3 |
| 24 | + ) |
| 25 | + # Print both sets and their difference |
| 26 | + print('Planar circumcenter:') |
| 27 | + print(' circumcenter: ', (xv, yv, zv)) |
| 28 | + print(' _old_circumcenter: ', (xv_old, yv_old, zv_old)) |
| 29 | + print(' difference: ', (xv - xv_old, yv - yv_old, zv - zv_old)) |
| 30 | + # Check for exact equality |
| 31 | + assert np.array_equal([xv, yv, zv], [xv_old, yv_old, zv_old]), ( |
| 32 | + f'circumcenter and _old_circumcenter results differ: ' |
| 33 | + f'({xv}, {yv}, {zv}) != ({xv_old}, {yv_old}, {zv_old})' |
| 34 | + ) |
| 35 | + |
| 36 | + |
| 37 | +def test_circumcenter_sphere(): |
| 38 | + # Use provided cell coordinates and vertex as expected output |
| 39 | + x1, y1, z1 = -3590.95704026, -684.52527287, -6371218.95125671 |
| 40 | + x2, y2, z2 = -9926.94769501, 18734.26792821, -6371184.72274307 |
| 41 | + x3, y3, z3 = 12921.92507847, 11340.69768405, -6371196.8028643 |
| 42 | + expected = (296.83807146, 11327.05862805, -6371209.92418473) |
| 43 | + |
| 44 | + xv, yv, zv = circumcenter(True, x1, y1, z1, x2, y2, z2, x3, y3, z3) |
| 45 | + assert np.allclose([xv, yv, zv], expected, atol=1e-8), ( |
| 46 | + f'Spherical circumcenter failed: got {[xv, yv, zv]}, expected ' |
| 47 | + f'{expected}' |
| 48 | + ) |
| 49 | + |
| 50 | + xv_old, yv_old, zv_old = _old_circumcenter( |
| 51 | + True, x1, y1, z1, x2, y2, z2, x3, y3, z3 |
| 52 | + ) |
| 53 | + # Print both sets and their difference |
| 54 | + print('Spherical circumcenter:') |
| 55 | + print(' circumcenter: ', (xv, yv, zv)) |
| 56 | + print(' _old_circumcenter: ', (xv_old, yv_old, zv_old)) |
| 57 | + print(' difference: ', (xv - xv_old, yv - yv_old, zv - zv_old)) |
| 58 | + # Check for exact equality |
| 59 | + assert np.array_equal([xv, yv, zv], [xv_old, yv_old, zv_old]), ( |
| 60 | + f'circumcenter and _old_circumcenter results differ: ' |
| 61 | + f'({xv}, {yv}, {zv}) != ({xv_old}, {yv_old}, {zv_old})' |
| 62 | + ) |
| 63 | + |
| 64 | + |
| 65 | +def _old_circumcenter(on_sphere, x1, y1, z1, x2, y2, z2, x3, y3, z3): |
| 66 | + """ |
| 67 | + Compute the circumcenter of the triangle (possibly on a sphere) |
| 68 | + with the three given vertices in Cartesian coordinates. |
| 69 | +
|
| 70 | + Returns |
| 71 | + ------- |
| 72 | + center : point |
| 73 | + The circumcenter of the triangle with x, y and z attributes |
| 74 | +
|
| 75 | + """ |
| 76 | + p1 = point(x1, y1, z1) |
| 77 | + p2 = point(x2, y2, z2) |
| 78 | + p3 = point(x3, y3, z3) |
| 79 | + if on_sphere: |
| 80 | + a = (p2.x - p3.x) ** 2 + (p2.y - p3.y) ** 2 + (p2.z - p3.z) ** 2 |
| 81 | + b = (p3.x - p1.x) ** 2 + (p3.y - p1.y) ** 2 + (p3.z - p1.z) ** 2 |
| 82 | + c = (p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2 + (p1.z - p2.z) ** 2 |
| 83 | + |
| 84 | + pbc = a * (-a + b + c) |
| 85 | + apc = b * (a - b + c) |
| 86 | + abp = c * (a + b - c) |
| 87 | + |
| 88 | + xv = (pbc * p1.x + apc * p2.x + abp * p3.x) / (pbc + apc + abp) |
| 89 | + yv = (pbc * p1.y + apc * p2.y + abp * p3.y) / (pbc + apc + abp) |
| 90 | + zv = (pbc * p1.z + apc * p2.z + abp * p3.z) / (pbc + apc + abp) |
| 91 | + else: |
| 92 | + d = 2 * ( |
| 93 | + p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y) |
| 94 | + ) |
| 95 | + |
| 96 | + xv = ( |
| 97 | + (p1.x**2 + p1.y**2) * (p2.y - p3.y) |
| 98 | + + (p2.x**2 + p2.y**2) * (p3.y - p1.y) |
| 99 | + + (p3.x**2 + p3.y**2) * (p1.y - p2.y) |
| 100 | + ) / d |
| 101 | + yv = ( |
| 102 | + (p1.x**2 + p1.y**2) * (p3.x - p2.x) |
| 103 | + + (p2.x**2 + p2.y**2) * (p1.x - p3.x) |
| 104 | + + (p3.x**2 + p3.y**2) * (p2.x - p1.x) |
| 105 | + ) / d |
| 106 | + zv = 0.0 |
| 107 | + |
| 108 | + # Optional method to use barycenter instead. |
| 109 | + # xv = p1.x + p2.x + p3.x |
| 110 | + # xv = xv / 3.0 |
| 111 | + # yv = p1.y + p2.y + p3.y |
| 112 | + # yv = yv / 3.0 |
| 113 | + return point(xv, yv, zv) |
0 commit comments