|
| 1 | + // Copyright (c) Ryan Schmidt ([email protected]) - All Rights Reserved |
| 2 | +// Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt |
| 3 | +using System; |
| 4 | +using System.Collections.Generic; |
| 5 | + |
| 6 | + |
| 7 | +namespace g3 |
| 8 | +{ |
| 9 | + |
| 10 | + /// <summary> |
| 11 | + /// [RMS] variant of DenseGridTrilinearImplicit that does lazy evaluation |
| 12 | + /// of Grid values. |
| 13 | + /// |
| 14 | + /// Tri-linear interpolant for a 3D dense grid. Supports grid translation |
| 15 | + /// via GridOrigin, but does not support scaling or rotation. If you need those, |
| 16 | + /// you can wrap this in something that does the xform. |
| 17 | + /// </summary> |
| 18 | + public class CachingDenseGridTrilinearImplicit : BoundedImplicitFunction3d |
| 19 | + { |
| 20 | + public DenseGrid3f Grid; |
| 21 | + public double CellSize; |
| 22 | + public Vector3d GridOrigin; |
| 23 | + |
| 24 | + public ImplicitFunction3d AnalyticF; |
| 25 | + |
| 26 | + // value to return if query point is outside grid (in an SDF |
| 27 | + // outside is usually positive). Need to do math with this value, |
| 28 | + // so don't use double.MaxValue or square will overflow |
| 29 | + public double Outside = Math.Sqrt(Math.Sqrt(double.MaxValue)); |
| 30 | + |
| 31 | + public float Invalid = float.MaxValue; |
| 32 | + |
| 33 | + public CachingDenseGridTrilinearImplicit(Vector3d gridOrigin, double cellSize, Vector3i gridDimensions) |
| 34 | + { |
| 35 | + Grid = new DenseGrid3f(gridDimensions.x, gridDimensions.y, gridDimensions.z, Invalid); |
| 36 | + GridOrigin = gridOrigin; |
| 37 | + CellSize = cellSize; |
| 38 | + } |
| 39 | + |
| 40 | + public AxisAlignedBox3d Bounds() |
| 41 | + { |
| 42 | + return new AxisAlignedBox3d( |
| 43 | + GridOrigin.x, GridOrigin.y, GridOrigin.z, |
| 44 | + GridOrigin.x + CellSize * Grid.ni, |
| 45 | + GridOrigin.y + CellSize * Grid.nj, |
| 46 | + GridOrigin.z + CellSize * Grid.nk); |
| 47 | + } |
| 48 | + |
| 49 | + |
| 50 | + public double Value(ref Vector3d pt) |
| 51 | + { |
| 52 | + Vector3d gridPt = new Vector3d( |
| 53 | + ((pt.x - GridOrigin.x) / CellSize), |
| 54 | + ((pt.y - GridOrigin.y) / CellSize), |
| 55 | + ((pt.z - GridOrigin.z) / CellSize)); |
| 56 | + |
| 57 | + // compute integer coordinates |
| 58 | + int x0 = (int)gridPt.x; |
| 59 | + int y0 = (int)gridPt.y, y1 = y0 + 1; |
| 60 | + int z0 = (int)gridPt.z, z1 = z0 + 1; |
| 61 | + |
| 62 | + // clamp to grid |
| 63 | + if (x0 < 0 || (x0+1) >= Grid.ni || |
| 64 | + y0 < 0 || y1 >= Grid.nj || |
| 65 | + z0 < 0 || z1 >= Grid.nk) |
| 66 | + return Outside; |
| 67 | + |
| 68 | + // convert double coords to [0,1] range |
| 69 | + double fAx = gridPt.x - (double)x0; |
| 70 | + double fAy = gridPt.y - (double)y0; |
| 71 | + double fAz = gridPt.z - (double)z0; |
| 72 | + double OneMinusfAx = 1.0 - fAx; |
| 73 | + |
| 74 | + // compute trilinear interpolant. The code below tries to do this with the fewest |
| 75 | + // number of variables, in hopes that optimizer will be clever about re-using registers, etc. |
| 76 | + // Commented code at bottom is fully-expanded version. |
| 77 | + // [TODO] it is possible to implement lerps here as a+(b-a)*t, saving a multiply and a variable. |
| 78 | + // This is numerically worse, but since the grid values are floats and |
| 79 | + // we are computing in doubles, does it matter? |
| 80 | + double xa, xb; |
| 81 | + |
| 82 | + get_value_pair(x0, y0, z0, out xa, out xb); |
| 83 | + double yz = (1 - fAy) * (1 - fAz); |
| 84 | + double sum = (OneMinusfAx * xa + fAx * xb) * yz; |
| 85 | + |
| 86 | + get_value_pair(x0, y0, z1, out xa, out xb); |
| 87 | + yz = (1 - fAy) * (fAz); |
| 88 | + sum += (OneMinusfAx * xa + fAx * xb) * yz; |
| 89 | + |
| 90 | + get_value_pair(x0, y1, z0, out xa, out xb); |
| 91 | + yz = (fAy) * (1 - fAz); |
| 92 | + sum += (OneMinusfAx * xa + fAx * xb) * yz; |
| 93 | + |
| 94 | + get_value_pair(x0, y1, z1, out xa, out xb); |
| 95 | + yz = (fAy) * (fAz); |
| 96 | + sum += (OneMinusfAx * xa + fAx * xb) * yz; |
| 97 | + |
| 98 | + return sum; |
| 99 | + |
| 100 | + // fV### is grid cell corner index |
| 101 | + //return |
| 102 | + // fV000 * (1 - fAx) * (1 - fAy) * (1 - fAz) + |
| 103 | + // fV001 * (1 - fAx) * (1 - fAy) * (fAz) + |
| 104 | + // fV010 * (1 - fAx) * (fAy) * (1 - fAz) + |
| 105 | + // fV011 * (1 - fAx) * (fAy) * (fAz) + |
| 106 | + // fV100 * (fAx) * (1 - fAy) * (1 - fAz) + |
| 107 | + // fV101 * (fAx) * (1 - fAy) * (fAz) + |
| 108 | + // fV110 * (fAx) * (fAy) * (1 - fAz) + |
| 109 | + // fV111 * (fAx) * (fAy) * (fAz); |
| 110 | + } |
| 111 | + |
| 112 | + |
| 113 | + |
| 114 | + void get_value_pair(int i, int j, int k, out double a, out double b) |
| 115 | + { |
| 116 | + float fa, fb; |
| 117 | + Grid.get_x_pair(i, j, k, out fa, out fb); |
| 118 | + |
| 119 | + if (fa == Invalid) { |
| 120 | + Vector3d p = grid_position(i, j, k); |
| 121 | + a = AnalyticF.Value(ref p); |
| 122 | + Grid[i, j, k] = (float)a; |
| 123 | + } else |
| 124 | + a = fa; |
| 125 | + |
| 126 | + if (fb == Invalid) { |
| 127 | + Vector3d p = grid_position(i+1, j, k); |
| 128 | + b = AnalyticF.Value(ref p); |
| 129 | + Grid[i+1, j, k] = (float)b; |
| 130 | + } else |
| 131 | + b = fb; |
| 132 | + } |
| 133 | + |
| 134 | + |
| 135 | + Vector3d grid_position(int i, int j, int k) { |
| 136 | + return new Vector3d( GridOrigin.x + CellSize * i, GridOrigin.y + CellSize * j, GridOrigin.z + CellSize*k ); |
| 137 | + } |
| 138 | + |
| 139 | + |
| 140 | + public Vector3d Gradient(ref Vector3d pt) |
| 141 | + { |
| 142 | + Vector3d gridPt = new Vector3d( |
| 143 | + ((pt.x - GridOrigin.x) / CellSize), |
| 144 | + ((pt.y - GridOrigin.y) / CellSize), |
| 145 | + ((pt.z - GridOrigin.z) / CellSize)); |
| 146 | + |
| 147 | + // clamp to grid |
| 148 | + if (gridPt.x < 0 || gridPt.x >= Grid.ni - 1 || |
| 149 | + gridPt.y < 0 || gridPt.y >= Grid.nj - 1 || |
| 150 | + gridPt.z < 0 || gridPt.z >= Grid.nk - 1) |
| 151 | + return Vector3d.Zero; |
| 152 | + |
| 153 | + // compute integer coordinates |
| 154 | + int x0 = (int)gridPt.x; |
| 155 | + int y0 = (int)gridPt.y, y1 = y0 + 1; |
| 156 | + int z0 = (int)gridPt.z, z1 = z0 + 1; |
| 157 | + |
| 158 | + // convert double coords to [0,1] range |
| 159 | + double fAx = gridPt.x - (double)x0; |
| 160 | + double fAy = gridPt.y - (double)y0; |
| 161 | + double fAz = gridPt.z - (double)z0; |
| 162 | + |
| 163 | + double fV000, fV100; |
| 164 | + get_value_pair(x0, y0, z0, out fV000, out fV100); |
| 165 | + double fV010, fV110; |
| 166 | + get_value_pair(x0, y1, z0, out fV010, out fV110); |
| 167 | + double fV001, fV101; |
| 168 | + get_value_pair(x0, y0, z1, out fV001, out fV101); |
| 169 | + double fV011, fV111; |
| 170 | + get_value_pair(x0, y1, z1, out fV011, out fV111); |
| 171 | + |
| 172 | + // [TODO] can re-order this to vastly reduce number of ops! |
| 173 | + double gradX = |
| 174 | + -fV000 * (1 - fAy) * (1 - fAz) + |
| 175 | + -fV001 * (1 - fAy) * (fAz) + |
| 176 | + -fV010 * (fAy) * (1 - fAz) + |
| 177 | + -fV011 * (fAy) * (fAz) + |
| 178 | + fV100 * (1 - fAy) * (1 - fAz) + |
| 179 | + fV101 * (1 - fAy) * (fAz) + |
| 180 | + fV110 * (fAy) * (1 - fAz) + |
| 181 | + fV111 * (fAy) * (fAz); |
| 182 | + |
| 183 | + double gradY = |
| 184 | + -fV000 * (1 - fAx) * (1 - fAz) + |
| 185 | + -fV001 * (1 - fAx) * (fAz) + |
| 186 | + fV010 * (1 - fAx) * (1 - fAz) + |
| 187 | + fV011 * (1 - fAx) * (fAz) + |
| 188 | + -fV100 * (fAx) * (1 - fAz) + |
| 189 | + -fV101 * (fAx) * (fAz) + |
| 190 | + fV110 * (fAx) * (1 - fAz) + |
| 191 | + fV111 * (fAx) * (fAz); |
| 192 | + |
| 193 | + double gradZ = |
| 194 | + -fV000 * (1 - fAx) * (1 - fAy) + |
| 195 | + fV001 * (1 - fAx) * (1 - fAy) + |
| 196 | + -fV010 * (1 - fAx) * (fAy) + |
| 197 | + fV011 * (1 - fAx) * (fAy) + |
| 198 | + -fV100 * (fAx) * (1 - fAy) + |
| 199 | + fV101 * (fAx) * (1 - fAy) + |
| 200 | + -fV110 * (fAx) * (fAy) + |
| 201 | + fV111 * (fAx) * (fAy); |
| 202 | + |
| 203 | + return new Vector3d(gradX, gradY, gradZ); |
| 204 | + } |
| 205 | + |
| 206 | + } |
| 207 | + |
| 208 | +} |
0 commit comments