/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="parallel"></param> /// <returns></returns> public static GridField2d <double> GetCurl(this GridField2d <Vector2d> field, bool parallel = false) { var result = GridField2d.Double.Create(field); GetCurl(field, result, parallel); return(result); }
/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="parallel"></param> /// <returns></returns> public static GridField2d <Vector2d> GetGradient(this GridField2d <double> field, bool parallel = false) { var result = GridField2d.Vector2d.Create(field); GetGradient(field, result, parallel); return(result); }
/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="parallel"></param> /// <returns></returns> public static GridField2d <double> GetLaplacian(this GridField2d <double> field, bool parallel = false) { var result = GridField2d.Double.Create(field); GetLaplacian(field, result, parallel); return(result); }
/// <summary> /// /// </summary> /// <param name="other"></param> /// <returns></returns> public GridField2d <T> CreateCopy(GridField2d <T> other) { var result = Create(other); result.Set(other); return(result); }
/// <summary> /// /// </summary> /// <typeparam name="U"></typeparam> /// <param name="other"></param> /// <param name="converter"></param> /// <param name="parallel"></param> /// <returns></returns> public GridField2d <T> CreateCopy <U>(GridField2d <U> other, Func <U, T> converter, bool parallel = false) where U : struct { var result = Create(other); other.Convert(converter, result, parallel); return(result); }
/// <summary> /// /// </summary> /// <typeparam name="U"></typeparam> /// <param name="other"></param> /// <returns></returns> public GridField2d <T> Create <U>(GridField2d <U> other) where U : struct { var result = Create((Grid2d)other); result.SampleMode = other.SampleMode; return(result); }
/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="point"></param> /// <param name="value"></param> public static void SetAt(this GridField2d <Vector2d> field, ref GridPoint2d point, Vector2d value) { var vals = field.Values; vals[point.Index0] += (value - vals[point.Index0]) * point.Weight0; vals[point.Index1] += (value - vals[point.Index1]) * point.Weight1; vals[point.Index2] += (value - vals[point.Index2]) * point.Weight2; vals[point.Index3] += (value - vals[point.Index3]) * point.Weight3; }
/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="point"></param> /// <param name="amount"></param> public static void IncrementAt(this GridField2d <Vector2d> field, ref GridPoint2d point, Vector2d amount) { var vals = field.Values; vals[point.Index0] += amount * point.Weight0; vals[point.Index1] += amount * point.Weight1; vals[point.Index2] += amount * point.Weight2; vals[point.Index3] += amount * point.Weight3; }
/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="result"></param> /// <param name="parallel"></param> public static void GetLaplacian(this GridField2d <double> field, double[] result, bool parallel = false) { if (parallel) { Parallel.ForEach(Partitioner.Create(0, field.CountXY), range => Body(range.Item1, range.Item2)); } else { Body(0, field.CountXY); } void Body(int from, int to) { var vals = field.Values; (var nx, var ny) = field.Count; (var dx, var dy) = field.Scale; dx = 1.0 / (dx * dx); dy = 1.0 / (dy * dy); (int di, int dj) = field.GetBoundaryOffsets(); (int i, int j) = field.ToGridSpace(from); for (int index = from; index < to; index++, i++) { if (i == nx) { j++; i = 0; } double tx0 = (i == 0) ? vals[index + di] : vals[index - 1]; double tx1 = (i == nx - 1) ? vals[index - di] : vals[index + 1]; double ty0 = (j == 0) ? vals[index + dj] : vals[index - nx]; double ty1 = (j == ny - 1) ? vals[index - dj] : vals[index + nx]; double t = vals[index] * 2.0; result[index] = (tx0 + tx1 - t) * dx + (ty0 + ty1 - t) * dy; } } }
/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="result"></param> /// <param name="parallel"></param> public static void GetCurl(this GridField2d <Vector2d> field, double[] result, bool parallel = false) { // implementation reference // http://www.math.harvard.edu/archive/21a_spring_09/PDF/13-05-curl-and-divergence.pdf if (parallel) { Parallel.ForEach(Partitioner.Create(0, field.CountXY), range => Body(range.Item1, range.Item2)); } else { Body(0, field.CountXY); } void Body(int from, int to) { var vals = field.Values; (var nx, var ny) = field.Count; (var tx, var ty) = (0.5 / field.Scale); (int di, int dj) = field.GetBoundaryOffsets(); (int i, int j) = field.ToGridSpace(from); for (int index = from; index < to; index++, i++) { if (i == nx) { j++; i = 0; } Vector2d tx0 = (i == 0) ? vals[index + di] : vals[index - 1]; Vector2d tx1 = (i == nx - 1) ? vals[index - di] : vals[index + 1]; Vector2d ty0 = (j == 0) ? vals[index + dj] : vals[index - nx]; Vector2d ty1 = (j == ny - 1) ? vals[index - dj] : vals[index + nx]; result[index] = (tx1.Y - tx0.Y) * tx - (ty1.X - ty0.X) * ty; } } }
/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="result"></param> /// <param name="parallel"></param> public static void GetDivergence(this GridField2d <Vector2d> field, double[] result, bool parallel = false) { if (parallel) { Parallel.ForEach(Partitioner.Create(0, field.CountXY), range => Body(range.Item1, range.Item2)); } else { Body(0, field.CountXY); } void Body(int from, int to) { var vals = field.Values; (var nx, var ny) = field.Count; (var dx, var dy) = (0.5 / field.Scale); (int di, int dj) = field.GetBoundaryOffsets(); (int i, int j) = field.ToGridSpace(from); for (int index = from; index < to; index++, i++) { if (i == nx) { j++; i = 0; } Vector2d tx0 = (i == 0) ? vals[index + di] : vals[index - 1]; Vector2d tx1 = (i == nx - 1) ? vals[index - di] : vals[index + 1]; Vector2d ty0 = (j == 0) ? vals[index + dj] : vals[index - nx]; Vector2d ty1 = (j == ny - 1) ? vals[index - dj] : vals[index + nx]; result[index] = (tx1.X - tx0.X) * dx + (ty1.Y - ty0.Y) * dy; } } }
/// <summary> /// Calculates the L2 (Euclidiean) geodesic distance from the given sources. /// </summary> /// <param name="cost"></param> /// <param name="sources"></param> /// <param name="result"></param> /// <param name="exclude"></param> public static void CalculateL2(GridField2d <double> cost, IEnumerable <int> sources, double[] result, IEnumerable <int> exclude = null) { // impl ref // http://www.numerical-tours.com/matlab/fastmarching_0_implementing/ var costVals = cost.Values; (var nx, var ny) = cost.Count; (var dx, var dy) = Vector2d.Abs(cost.Scale); var eikonal = new Eikonal2d(dx, dy); var queue = new PriorityQueue <double, int>(); result.SetRange(double.PositiveInfinity, cost.CountXY); // enqueue sources foreach (int i in sources) { result[i] = 0.0; queue.Insert(0.0, i); } // exclude if (exclude != null) { foreach (var i in exclude) { result[i] = 0.0; } } // breadth first search from sources while (queue.Count > 0) { (double d0, int i0) = queue.RemoveMin(); if (result[i0] < d0) { continue; // skip if lower value has been assigned } (int x0, int y0) = cost.ToGridSpace(i0); if (x0 > 0) { TryUpdateX(i0 - 1); } if (x0 < nx - 1) { TryUpdateX(i0 + 1); } if (y0 > 0) { TryUpdateY(i0 - nx); } if (y0 < ny - 1) { TryUpdateY(i0 + nx); } // process x neighbor void TryUpdateX(int index) { var d1 = result[index]; if (d1 < d0) { return; // no backtracking } double d2; double minY = GetMinY(index); // will return infinity if neither neighbor has been visited if (minY > double.MaxValue || !eikonal.Solve(d0, minY, costVals[index], out d2)) { d2 = d0 + dx * costVals[index]; } // add to queue if less than current min if (d2 < d1) { result[index] = d2; queue.Insert(d2, index); } } // process y neighbor void TryUpdateY(int index) { var d1 = result[index]; if (d1 < d0) { return; // no backtracking } double d2; double minX = GetMinX(index); // will return infinity if neither neighbor has been visited if (minX > double.MaxValue || !eikonal.Solve(minX, d0, costVals[index], out d2)) { d2 = d0 + dy * costVals[index]; } // add to queue if less than current min if (d2 < d1) { result[index] = d2; queue.Insert(d2, index); } } // returns the minimum adjacent value in the x double GetMinX(int index) { if (x0 == 0) { return(result[index + 1]); } else if (x0 == nx - 1) { return(result[index - 1]); } return(Math.Min(result[index - 1], result[index + 1])); } // returns the minimum adjacent value in the y double GetMinY(int index) { if (y0 == 0) { return(result[index + nx]); } else if (y0 == ny - 1) { return(result[index - nx]); } return(Math.Min(result[index - nx], result[index + nx])); } } }
/// <summary> /// Calculates the L1 (Manhattan) geodesic distance from the given sources. /// </summary> /// <param name="cost"></param> /// <param name="sources"></param> /// <param name="result"></param> /// <param name="exclude"></param> public static void CalculateL1(GridField2d <double> cost, IEnumerable <int> sources, double[] result, IEnumerable <int> exclude = null) { // impl ref // http://www.numerical-tours.com/matlab/fastmarching_0_implementing/ var costVals = cost.Values; (var nx, var ny) = cost.Count; (double dx, double dy) = Vector2d.Abs(cost.Scale); var queue = new PriorityQueue <double, int>(); result.SetRange(double.PositiveInfinity, cost.CountXY); // enqueue sources foreach (int i in sources) { result[i] = 0.0; queue.Insert(0.0, i); } // exclude if (exclude != null) { foreach (int i in exclude) { result[i] = 0.0; } } // breadth first search from sources while (queue.Count > 0) { (var d0, int i0) = queue.RemoveMin(); if (result[i0] < d0) { continue; // skip if lower value has been assigned } (int x0, int y0) = cost.ToGridSpace(i0); // -x if (x0 > 0) { TryUpdate(d0 + dx * costVals[i0 - 1], i0 - 1); } // +x if (x0 < nx - 1) { TryUpdate(d0 + dx * costVals[i0 + 1], i0 + 1); } // -y if (y0 > 0) { TryUpdate(d0 + dy * costVals[i0 - nx], i0 - nx); } // +y if (y0 < ny - 1) { TryUpdate(d0 + dy * costVals[i0 + nx], i0 + nx); } // add to queue if less than current min void TryUpdate(double distance, int index) { if (distance < result[index]) { result[index] = distance; queue.Insert(distance, index); } } } }