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 }