1 module dsurf.algoritm;
2 
3 import std.math;
4 import std.range;
5 import std.parallelism;
6 
7 import dsurf.cartesian;
8 
9 /***********************************
10 * Set of algoritms to work with surfaces
11 * Authors: Dmitriy Linev
12 * License: MIT
13 * Date: 2019-2020
14 */
15 
16 /// Returns surface gradient along X axis in the node with index i and j
17 double gX(CartesianSurface surface, int i, int j) {
18     if (i == 0)
19         return (surface.z[i + 1][j] - surface.z[i][j]) / surface.dx;
20     if (i == surface.nx - 1)
21         return (surface.z[i][j] - surface.z[i - 1][j]) / surface.dx;
22     return (surface.z[i + 1][j] - surface.z[i - 1][j]) / (2 * surface.dx);
23 }
24 
25 /// Returns surface gradient along Y axis in the node with index i and j
26 double gY(CartesianSurface surface, int i, int j) {
27     if (j == 0)
28         return (surface.z[i][j + 1] - surface.z[i][j]) / surface.dy;
29     if (j == surface.ny-1)
30         return (surface.z[i][j] - surface.z[i][j - 1]) / surface.dy;
31     return (surface.z[i][j + 1] - surface.z[i][j - 1]) / (2 * surface.dy);
32 }
33 
34 /// Returns CartesianSurface containing gradient map along X axis based on a given surface
35 CartesianSurface buildGradientXMap(CartesianSurface surface) {
36     CartesianSurface gradient = new CartesianSurface(surface);
37     foreach (i; taskPool.parallel(iota(surface.nx))) {
38         foreach (j; 0..surface.ny) {
39             gradient.z[i][j] = surface.gX(i, j);
40         }
41     }
42     return gradient;
43 }
44 
45 /// Returns CartesianSurface containing gradient map along Y axis based on a given surface
46 CartesianSurface buildGradientYMap(CartesianSurface surface) {
47     CartesianSurface gradient = new CartesianSurface(surface);
48     foreach (i; taskPool.parallel(iota(surface.nx))) {
49         foreach (j; 0..surface.ny) {
50             gradient.z[i][j] = surface.gY(i, j);
51         }
52     }
53     return gradient;
54 }
55 
56 /// Returns dip angle in radians of the surface in the node with index i and j
57 double dipAngle(CartesianSurface surface, int i, int j) {
58     immutable double gx = surface.gX(i, j);
59     immutable double gy = surface.gY(i, j);
60     if (isNaN(gx) || isNaN(gy))
61         return double.nan;
62     return atan(sqrt(gx * gx + gy * gy));
63 }
64 
65 /// Returns CartesianSurface containing dip angle map in radians based on a given surface
66 CartesianSurface buildDipAngle(CartesianSurface surface) {
67     CartesianSurface result = new CartesianSurface(surface);
68     foreach (i; taskPool.parallel(iota(surface.nx))) {
69         foreach (j; 0..surface.ny) {
70             result.z[i][j] = surface.dipAngle(i, j);
71         }
72     }
73     return result;
74 }
75 
76 /** 
77  * Scales the given surface around its origin point
78  * Params:
79  *   surface = surface to scale
80  *   xf = scale factor along X direction
81  *   xf = scale factor value along Y direction
82  * Returns: translated surface for more convenient call chaining
83  */
84 CartesianSurface scale(CartesianSurface surface, double xf, double yf) {  //scales around origin point
85     //TODO filter negative factors
86     surface.m_dx *= xf;
87     surface.m_dy *= yf;
88     return surface;
89 }
90 
91 /** 
92  * Normalizes the given surface (minimum value will be 0, maximum value will be 1)
93  * Doesn`t alter surface limits and origin point
94  * Case of equal min and max is not controlled (division by zero will occur)
95  * Params:
96  *   surface = surface to normalize
97  * Returns: Normalized surface for more convenient call chaining
98  */
99 CartesianSurface normalize(CartesianSurface surface) {
100     immutable double zmax = surface.max();
101     immutable double zmin = surface.min();
102     surface -= zmin;
103     surface /= (zmax - zmin);
104     return surface;
105 }