Skip to content

Commit

Permalink
add a blend() method to SpatialMap
Browse files Browse the repository at this point in the history
  • Loading branch information
bhaller committed Sep 30, 2023
1 parent 11be0b2 commit 9d16863
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 1 deletion.
3 changes: 3 additions & 0 deletions QtSLiM/help/SLiMHelpClasses.html
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,9 @@
<p class="p11"><i>5.14.2<span class="Apple-converted-space">  </span></i><span class="s1"><i>SpatialMap</i></span><i> methods</i></p>
<p class="p5">– (object&lt;SpatialMap&gt;$)add(ifo&lt;SpatialMap&gt; x)</p>
<p class="p6">Adds <span class="s1">x</span> to the spatial map.<span class="Apple-converted-space">  </span>One possibility is that <span class="s1">x</span> is a singleton <span class="s1">integer</span> or <span class="s1">float</span> value; in this case, <span class="s1">x</span> is added to each grid value of the target spatial map.<span class="Apple-converted-space">  </span>Another possibility is that <span class="s1">x</span> is an <span class="s1">integer</span> or <span class="s1">float</span> vector/matrix/array of the same dimensions as the target spatial map’s grid; in this case, each value of <span class="s1">x</span> is added to the corresponding grid value of the target spatial map.<span class="Apple-converted-space">  </span>The third possibility is that <span class="s1">x</span> is itself a (singleton) spatial map; in this case, each grid value of <span class="s1">x</span> 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).<span class="Apple-converted-space">  </span>The target spatial map is returned, to allow easy chaining of operations.</p>
<p class="p5">– (object&lt;SpatialMap&gt;$)blend(ifo&lt;SpatialMap&gt; x, float$ xFraction)</p>
<p class="p6">Blends <span class="s1">x</span> into the spatial map, giving <span class="s1">x</span> a weight of <span class="s1">xFraction</span> and the existing values in the target spatial map a weight of <span class="s1">1 - xFraction</span>, such that the resulting values in the target spatial map are then given by <span class="s1">x * xFraction + target * (1 - xFraction)</span>.<span class="Apple-converted-space">  </span>The value of <span class="s1">xFraction</span> must be in [0.0, 1.0].</p>
<p class="p6">One possibility is that <span class="s1">x</span> is a singleton <span class="s1">integer</span> or <span class="s1">float</span> value; in this case, <span class="s1">x</span> is blended with each grid value of the target spatial map.<span class="Apple-converted-space">  </span>Another possibility is that <span class="s1">x</span> is an <span class="s1">integer</span> or <span class="s1">float</span> vector/matrix/array of the same dimensions as the target spatial map’s grid; in this case, each value of <span class="s1">x</span> is blended with the corresponding grid value of the target spatial map.<span class="Apple-converted-space">  </span>The third possibility is that <span class="s1">x</span> is itself a (singleton) spatial map; in this case, each grid value of <span class="s1">x</span> 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).<span class="Apple-converted-space">  </span>The target spatial map is returned, to allow easy chaining of operations.</p>
<p class="p5">– (void)changeColors([Nif valueRange = NULL], [Ns colors = NULL])</p>
<p class="p6">Changes the color scheme for the target spatial map.<span class="Apple-converted-space">  </span>The meaning of <span class="s1">valueRange</span> and <span class="s1">colors</span> are identical to their meaning in <span class="s1">defineSpatialMap()</span>, but are also described here.</p>
<p class="p6">The <span class="s1">valueRange</span> and <span class="s1">colors</span> parameters travel together; either both are <span class="s1">NULL</span>, or both are specified.<span class="Apple-converted-space">  </span>They control how map values will be transformed into colors, by SLiMgui and by the <span class="s1">mapColor()</span> method.<span class="Apple-converted-space">  </span>The <span class="s1">valueRange</span> 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.<span class="Apple-converted-space">  </span>The <span class="s1">colors</span> parameter then establishes the corresponding colors for values within the interval defined by <span class="s1">valueRange</span>: values less than or equal to <span class="s1">valueRange[0]</span> will map to <span class="s1">colors[0]</span>, values greater than or equal to <span class="s1">valueRange[1]</span> will map to the last <span class="s1">colors</span> value, and intermediate values will shade continuously through the specified vector of colors, with interpolation between adjacent colors to produce a continuous spectrum.<span class="Apple-converted-space">  </span>This is much simpler than it sounds in this description; see the recipes in chapter 15 for an illustration of its use.</p>
Expand Down
39 changes: 39 additions & 0 deletions SLiMgui/SLiMHelpClasses.rtf
Original file line number Diff line number Diff line change
Expand Up @@ -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<SpatialMap>$)blend(ifo<SpatialMap>\'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

Expand Down
2 changes: 1 addition & 1 deletion VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
1 change: 1 addition & 0 deletions core/slim_globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions core/slim_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1347,6 +1348,7 @@ enum _SLiMGlobalStringID : int {
gID_spatialMapImage,
gID_spatialMapValue,
gID_add,
gID_blend,
gID_multiply,
gID_subtract,
gID_divide,
Expand Down
53 changes: 53 additions & 0 deletions core/spatial_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<SpatialMap>)blend(ifo<SpatialMap> x, float$ xFraction)
//
EidosValue_SP SpatialMap::ExecuteMethod_blend(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &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<SpatialMap>)multiply(ifo<SpatialMap> x)
//
EidosValue_SP SpatialMap::ExecuteMethod_multiply(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter)
Expand Down Expand Up @@ -2241,6 +2293,7 @@ const std::vector<EidosMethodSignature_CSP> *SpatialMap_Class::Methods(void) con
methods = new std::vector<EidosMethodSignature_CSP>(*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));
Expand Down
1 change: 1 addition & 0 deletions core/spatial_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class SpatialMap : public EidosDictionaryRetained

virtual EidosValue_SP ExecuteInstanceMethod(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter) override;
EidosValue_SP ExecuteMethod_add(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_blend(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_multiply(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_subtract(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_divide(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
Expand Down

0 comments on commit 9d16863

Please sign in to comment.