1 /**
2     Combinition functions for two layers
3 */
4 module dosimplex.ext.combine;
5 
6 import dosimplex.ext.layer;
7 
8 /// The alias for the signature needed by CombineLayers functions
9 alias LayerCombineFunc = double function(double,double) pure nothrow @nogc @safe;
10 
11 /**
12     Template holds functions for layer combination.
13     Params:
14         D = number of dimensions. Must be 0 < D <= 4
15 */
16 template CombineLayers(uint D) 
17 if (D > 0 && D < 5)
18 {
19 
20     /**
21         Generic function to combine different noise layers.
22         Params:
23             f = the combination function. must have a signature "double function(double,double) pure nothrow @nogc @safe"
24             args = var length arguments 
25                 - slice 0..D is the values for the dimensions
26                 - D..$ is the slice with the layers - they can be double or instance of layer
27                 - The Layers can also have other types as long as they have a "equal"-function that has the signature as in the layer interface.
28     */
29     @safe pure double generic(A...) (LayerCombineFunc f, A args)
30     if (args.length > D+1)
31     {
32         auto layers = args[D..$];
33         auto dimensions = args[0..D];
34 
35         // check type for the dimensions
36         foreach(d;dimensions) {
37             static assert(is(typeof(d)==double));
38         }
39         
40         double result = 0.0; // we dont set to 1.0 and then just multiply to avoid this tiny bit of error it might cause - and its probably a tiny bit faster
41 
42         // check and evaluate layers
43         foreach(size_t i, a;layers) {
44             import std.traits;
45 
46             double x = 0.0;
47 
48             static if (is(typeof(a) == double)) {
49                 x = a;
50             }
51             else static if(__traits(compiles, a.eval(dimensions))) {
52                 x = a.eval(dimensions);
53             } else {
54                 pragma(msg, __FILE__, "(", __LINE__, "): Can only accept Layers and doubles as arguments for noise functions - not ", typeof(a), "!");
55                 static assert(false);               
56             }
57             
58             static if (i == 0) { // see declaration of result
59                 result = x;
60             }
61             else {
62                 result = f(result,x);
63             }           
64         }
65 
66         return result;
67     }
68 
69     /**
70         Adds the vlaues of each layer
71         Params:
72         args = var length arguments 
73             - slice 0..D is the values for the dimensions
74             - D..$ is the slice with the layers - they can be double or instance of layer
75     */
76     @safe pure double add(L...) (L args)
77     {
78         return CombineLayers!D.generic(function (double l,double r) => l+r, args);
79     }
80 
81     /**
82         Subtracts the vlaues of each layer
83         Params:
84             args = var length arguments 
85                 - slice 0..D is the values for the dimensions
86                 - D..$ is the slice with the layers - they can be double or instance of layer
87     */
88     @safe pure double sub(L...) (lazy L args)
89     {
90         return CombineLayers!D.generic(function (double l,double r) => l-r, args);
91     }
92 
93     /**
94         Multiplies the vlaues of each layer
95         Params:
96             args = var length arguments 
97                 - slice 0..D is the values for the dimensions
98                 - D..$ is the slice with the layers - they can be double or instance of layer
99     */
100     @safe pure double mul(L...) (lazy L args)
101     {
102         return CombineLayers!D.generic(function (double l,double r) => l*r, args);
103     }
104 
105     /**
106         Pows the vlaues of each layer
107         Params:
108             args = var length arguments 
109                 - slice 0..D is the values for the dimensions
110                 - D..$ is the slice with the layers - they can be double or instance of layer
111     */
112     @safe pure double pow(L...) (lazy L args)
113     {
114         import std.math;
115         return CombineLayers!D.generic(function (double l,double r) => pow(l,r), args);
116     }
117 }