diff --git a/QtSLiM/help/SLiMHelpClasses.html b/QtSLiM/help/SLiMHelpClasses.html index 8d554e29..56a89887 100644 --- a/QtSLiM/help/SLiMHelpClasses.html +++ b/QtSLiM/help/SLiMHelpClasses.html @@ -702,6 +702,9 @@

5.14.2  SpatialMap methods

– (object<SpatialMap>$)add(ifo<SpatialMap> x)

Adds x to the spatial map.  One possibility is that x is a singleton integer or float value; in this case, x is added to each grid value of the target spatial map.  Another possibility is that x is an integer or float vector/matrix/array of the same dimensions as the target spatial map’s grid; in this case, each value of x is added to the corresponding grid value of the target spatial map.  The third possibility is that x is itself a (singleton) spatial map; in this case, each grid value of x is added to the corresponding grid value of the target spatial map (and thus the two spatial maps must match in their spatiality, their spatial bounds, and their grid dimensions).  The target spatial map is returned, to allow easy chaining of operations.

+

– (object<SpatialMap>$)blend(ifo<SpatialMap> x, float$ xFraction)

+

Blends x into the spatial map, giving x a weight of xFraction and the existing values in the target spatial map a weight of 1 - xFraction, such that the resulting values in the target spatial map are then given by x * xFraction + target * (1 - xFraction).  The value of xFraction must be in [0.0, 1.0].

+

One possibility is that x is a singleton integer or float value; in this case, x is blended with each grid value of the target spatial map.  Another possibility is that x is an integer or float vector/matrix/array of the same dimensions as the target spatial map’s grid; in this case, each value of x is blended with the corresponding grid value of the target spatial map.  The third possibility is that x is itself a (singleton) spatial map; in this case, each grid value of x is blended with the corresponding grid value of the target spatial map (and thus the two spatial maps must match in their spatiality, their spatial bounds, and their grid dimensions).  The target spatial map is returned, to allow easy chaining of operations.

– (void)changeColors([Nif valueRange = NULL], [Ns colors = NULL])

Changes the color scheme for the target spatial map.  The meaning of valueRange and colors are identical to their meaning in defineSpatialMap(), but are also described here.

The valueRange and colors parameters travel together; either both are NULL, or both are specified.  They control how map values will be transformed into colors, by SLiMgui and by the mapColor() method.  The valueRange parameter establishes the color-mapped range of spatial map values, as a vector of length two specifying a minimum and maximum; this does not need to match the actual range of values in the map.  The colors parameter then establishes the corresponding colors for values within the interval defined by valueRange: values less than or equal to valueRange[0] will map to colors[0], values greater than or equal to valueRange[1] will map to the last colors value, and intermediate values will shade continuously through the specified vector of colors, with interpolation between adjacent colors to produce a continuous spectrum.  This is much simpler than it sounds in this description; see the recipes in chapter 15 for an illustration of its use.

diff --git a/SLiMgui/SLiMHelpClasses.rtf b/SLiMgui/SLiMHelpClasses.rtf index 98419360..de76e97f 100644 --- a/SLiMgui/SLiMHelpClasses.rtf +++ b/SLiMgui/SLiMHelpClasses.rtf @@ -5744,6 +5744,45 @@ If the model is being profiled, or is executing forward to a tick number entered \f4\fs20 is added to the corresponding grid value of the target spatial map (and thus the two spatial maps must match in their spatiality, their spatial bounds, and their grid dimensions). The target spatial map is returned, to allow easy chaining of operations.\ \pard\pardeftab397\li720\fi-446\ri720\sb180\sa60\partightenfactor0 +\f3\fs18 \cf2 \'96\'a0(object$)blend(ifo\'a0x, float$\'a0xFraction)\ +\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 + +\f4\fs20 \cf2 Blends +\f3\fs18 x +\f4\fs20 into the spatial map, giving +\f3\fs18 x +\f4\fs20 a weight of +\f3\fs18 xFraction +\f4\fs20 and the existing values in the target spatial map a weight of +\f3\fs18 1 - xFraction +\f4\fs20 , such that the resulting values in the target spatial map are then given by +\f3\fs18 x\'a0*\'a0xFraction + target\'a0*\'a0(1\'a0-\'a0xFraction) +\f4\fs20 . The value of +\f3\fs18 xFraction +\f4\fs20 must be in [0.0, 1.0].\ +One possibility is that +\f3\fs18 x +\f4\fs20 is a singleton +\f3\fs18 integer +\f4\fs20 or +\f3\fs18 float +\f4\fs20 value; in this case, +\f3\fs18 x +\f4\fs20 is blended with each grid value of the target spatial map. Another possibility is that +\f3\fs18 x +\f4\fs20 is an +\f3\fs18 integer +\f4\fs20 or +\f3\fs18 float +\f4\fs20 vector/matrix/array of the same dimensions as the target spatial map\'92s grid; in this case, each value of +\f3\fs18 x +\f4\fs20 is blended with the corresponding grid value of the target spatial map. The third possibility is that +\f3\fs18 x +\f4\fs20 is itself a (singleton) spatial map; in this case, each grid value of +\f3\fs18 x +\f4\fs20 is blended with the corresponding grid value of the target spatial map (and thus the two spatial maps must match in their spatiality, their spatial bounds, and their grid dimensions). The target spatial map is returned, to allow easy chaining of operations.\ +\pard\pardeftab397\li720\fi-446\ri720\sb180\sa60\partightenfactor0 + \f3\fs18 \cf2 \'96\'a0(void)changeColors([Nif\'a0valueRange\'a0=\'a0NULL], [Ns\'a0colors\'a0=\'a0NULL])\ \pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 diff --git a/VERSIONS b/VERSIONS index 7cae3e4d..904a0040 100644 --- a/VERSIONS +++ b/VERSIONS @@ -155,7 +155,7 @@ development head (in the master branch): tweak WF reproduction, addCrossed(), addSelfed(), addCloned(), and addRecombinant() to give the new offspring the same spatial position as the first parent, allowing efficient positioning with pointDeviated() in the typical usage case policy change: type 'f' kernels now require a finite maxDistance, since the alternative doesn't really make sense (note that type 'l' already required a finite maxDistance) new option in the Individuals view for spatial models, to display the underlying grid points of the spatial map - add changeColors() method to SpatialMap + add changeColors() and blend() methods to SpatialMap version 4.0.1 (Eidos version 3.0.1): diff --git a/core/slim_globals.cpp b/core/slim_globals.cpp index 63a5383e..794cc4d7 100644 --- a/core/slim_globals.cpp +++ b/core/slim_globals.cpp @@ -1365,6 +1365,7 @@ const std::string &gStr_spatialMapColor = EidosRegisteredString("spatialMapColor const std::string &gStr_spatialMapImage = EidosRegisteredString("spatialMapImage", gID_spatialMapImage); const std::string &gStr_spatialMapValue = EidosRegisteredString("spatialMapValue", gID_spatialMapValue); const std::string &gStr_add = EidosRegisteredString("add", gID_add); +const std::string &gStr_blend = EidosRegisteredString("blend", gID_blend); const std::string &gStr_multiply = EidosRegisteredString("multiply", gID_multiply); const std::string &gStr_subtract = EidosRegisteredString("subtract", gID_subtract); const std::string &gStr_divide = EidosRegisteredString("divide", gID_divide); diff --git a/core/slim_globals.h b/core/slim_globals.h index 9a17c8c3..b715c9a4 100644 --- a/core/slim_globals.h +++ b/core/slim_globals.h @@ -953,6 +953,7 @@ extern const std::string &gStr_spatialMapColor; extern const std::string &gStr_spatialMapImage; extern const std::string &gStr_spatialMapValue; extern const std::string &gStr_add; +extern const std::string &gStr_blend; extern const std::string &gStr_multiply; extern const std::string &gStr_divide; extern const std::string &gStr_subtract; @@ -1347,6 +1348,7 @@ enum _SLiMGlobalStringID : int { gID_spatialMapImage, gID_spatialMapValue, gID_add, + gID_blend, gID_multiply, gID_subtract, gID_divide, diff --git a/core/spatial_map.cpp b/core/spatial_map.cpp index f0f39f68..04e9d4c0 100644 --- a/core/spatial_map.cpp +++ b/core/spatial_map.cpp @@ -1035,6 +1035,7 @@ EidosValue_SP SpatialMap::ExecuteInstanceMethod(EidosGlobalStringID p_method_id, switch (p_method_id) { case gID_add: return ExecuteMethod_add(p_method_id, p_arguments, p_interpreter); + case gID_blend: return ExecuteMethod_blend(p_method_id, p_arguments, p_interpreter); case gID_multiply: return ExecuteMethod_multiply(p_method_id, p_arguments, p_interpreter); case gID_subtract: return ExecuteMethod_subtract(p_method_id, p_arguments, p_interpreter); case gID_divide: return ExecuteMethod_divide(p_method_id, p_arguments, p_interpreter); @@ -1099,6 +1100,57 @@ EidosValue_SP SpatialMap::ExecuteMethod_add(EidosGlobalStringID p_method_id, con return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Object_singleton(this, gSLiM_SpatialMap_Class)); } +// ********************* - (object)blend(ifo x, float$ xFraction) +// +EidosValue_SP SpatialMap::ExecuteMethod_blend(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter) +{ +#pragma unused (p_method_id, p_arguments, p_interpreter) + EidosValue *x_value = p_arguments[0].get(); + EidosValue *xFraction_value = p_arguments[1].get(); + EidosValue_SP spatialmap_temp; + + if (x_value->Count() > 1) + { + if (x_value->Type() == EidosValueType::kValueObject) + EIDOS_TERMINATION << "ERROR (SpatialMap::ExecuteMethod_blend): blend() requires x to be a singleton if it is of type object (i.e., a singleton SpatialMap)." << EidosTerminate(); + + // handle a vector/matrix/array parameter by converting it to a spatial map and then following that code path + spatialmap_temp = _DeriveTemporarySpatialMapWithEidosValue(x_value, "SpatialMap::ExecuteMethod_add", "add()"); + x_value = spatialmap_temp.get(); + } + + double xFraction = xFraction_value->FloatAtIndex(0, nullptr); + double targetFraction = 1 - xFraction; + + if ((xFraction < 0.0) || (xFraction > 1.0)) + EIDOS_TERMINATION << "ERROR (SpatialMap::ExecuteMethod_blend): blend() requires xFraction to be in [0.0, 1.0]." << EidosTerminate(); + + if ((x_value->Type() == EidosValueType::kValueInt) || (x_value->Type() == EidosValueType::kValueFloat)) + { + double blend_scalar = x_value->FloatAtIndex(0, nullptr); + + // FIXME: TO BE PARALLELIZED + for (int64_t i = 0; i < values_size_; ++i) + values_[i] = blend_scalar * xFraction + values_[i] * targetFraction; + } + else + { + SpatialMap *blend_map = (SpatialMap *)x_value->ObjectElementAtIndex(0, nullptr); + double *blend_map_values = blend_map->values_; + + if (!IsCompatibleWithMap(blend_map)) + EIDOS_TERMINATION << "ERROR (SpatialMap::ExecuteMethod_blend): blend() requires the target SpatialMap to be compatible with the SpatialMap supplied in x (using the same spatiality and bounds, and having the same grid resolution)." << EidosTerminate(); + + // FIXME: TO BE PARALLELIZED + for (int64_t i = 0; i < values_size_; ++i) + values_[i] = blend_map_values[i] * xFraction + values_[i] * targetFraction; + } + + _ValuesChanged(); + + return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Object_singleton(this, gSLiM_SpatialMap_Class)); +} + // ********************* - (object)multiply(ifo x) // EidosValue_SP SpatialMap::ExecuteMethod_multiply(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter) @@ -2241,6 +2293,7 @@ const std::vector *SpatialMap_Class::Methods(void) con methods = new std::vector(*super::Methods()); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_add, kEidosValueMaskObject | kEidosValueMaskSingleton, gSLiM_SpatialMap_Class))->AddArg(kEidosValueMaskNumeric | kEidosValueMaskObject, "x", gSLiM_SpatialMap_Class)); + methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_blend, kEidosValueMaskObject | kEidosValueMaskSingleton, gSLiM_SpatialMap_Class))->AddArg(kEidosValueMaskNumeric | kEidosValueMaskObject, "x", gSLiM_SpatialMap_Class)->AddFloat_S("xFraction")); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_multiply, kEidosValueMaskObject | kEidosValueMaskSingleton, gSLiM_SpatialMap_Class))->AddArg(kEidosValueMaskNumeric | kEidosValueMaskObject, "x", gSLiM_SpatialMap_Class)); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_subtract, kEidosValueMaskObject | kEidosValueMaskSingleton, gSLiM_SpatialMap_Class))->AddArg(kEidosValueMaskNumeric | kEidosValueMaskObject, "x", gSLiM_SpatialMap_Class)); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_divide, kEidosValueMaskObject | kEidosValueMaskSingleton, gSLiM_SpatialMap_Class))->AddArg(kEidosValueMaskNumeric | kEidosValueMaskObject, "x", gSLiM_SpatialMap_Class)); diff --git a/core/spatial_map.h b/core/spatial_map.h index 568558ce..547cd488 100644 --- a/core/spatial_map.h +++ b/core/spatial_map.h @@ -122,6 +122,7 @@ class SpatialMap : public EidosDictionaryRetained virtual EidosValue_SP ExecuteInstanceMethod(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter) override; EidosValue_SP ExecuteMethod_add(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); + EidosValue_SP ExecuteMethod_blend(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); EidosValue_SP ExecuteMethod_multiply(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); EidosValue_SP ExecuteMethod_subtract(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); EidosValue_SP ExecuteMethod_divide(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter);