1 #pragma once
2 
3 /// \file Colorizer.h
4 /// \brief Object converting quantity values of particles into colors.
5 /// \author Pavel Sevecek (sevecek at sirrah.troja.mff.cuni.cz)
6 /// \date 2016-2021
7 
8 #include "gravity/AggregateSolver.h"
9 #include "gui/Settings.h"
10 #include "gui/objects/Palette.h"
11 #include "gui/objects/Point.h"
12 #include "gui/objects/Texture.h"
13 #include "gui/renderers/Spectrum.h"
14 #include "objects/containers/ArrayRef.h"
15 #include "objects/finders/NeighborFinder.h"
16 #include "objects/utility/Dynamic.h"
17 #include "post/Analysis.h"
18 #include "quantities/IMaterial.h"
19 #include "quantities/Particle.h"
20 #include "sph/kernel/Kernel.h"
21 #include "system/Factory.h"
22 #include "thread/Scheduler.h"
23 
24 NAMESPACE_SPH_BEGIN
25 
26 class Particle;
27 
28 /// \brief Interface for objects assigning colors to particles.
29 ///
30 /// Used to add a layer of abstraction between quantity values and displayed colors, allowing to visualize
31 /// various information that isn't directly stored as quantity, like relative values of quantitiees, angular
32 /// dependence of velocities, etc. Usually though, one wants to display raw quantity values, which can be
33 /// accomplished by \ref TypedColorizer.
34 class IColorizer : public Polymorphic {
35 public:
36     /// \brief Checks if the storage constains all data necessary to initialize the colorizer.
37     virtual bool hasData(const Storage& storage) const = 0;
38 
39     /// \brief Initialize the colorizer before by getting necessary quantities from storage.
40     ///
41     /// Can only be called if \ref hasData returns true. Must be called before \ref evalColor is called, every
42     /// time step as ArrayViews taken from storage might be invalidated.
43     /// \param storage Particle storage containing source data to be drawn.
44     /// \param ref Specifies how the object refereneces the data required for evaluation; either the buffers
45     ///            are copied and stored in the colorizer, or only references to the the storage are kept.
46     virtual void initialize(const Storage& storage, const RefEnum ref) = 0;
47 
48     /// \brief Checks if the colorizer has been initialized.
49     virtual bool isInitialized() const = 0;
50 
51     /// \brief Returns the color of idx-th particle.
52     virtual Rgba evalColor(const Size idx) const = 0;
53 
54     /// \brief Returns the scalar representation of the colorized quantity for idx-th particle.
55     ///
56     /// If there is no reasonable scalar representation (boundary particles, for example), returns NOTHING
evalScalar(const Size UNUSED (idx))57     virtual Optional<float> evalScalar(const Size UNUSED(idx)) const {
58         return NOTHING;
59     }
60 
61     /// \brief Returns the vector representation of the colorized quantity for idx-th particle.
62     ///
63     /// If there is no reasonable vector representation (which is true for any non-vector quantity) or the
64     /// function is not defined, return NOTHING.
evalVector(const Size UNUSED (idx))65     virtual Optional<Vector> evalVector(const Size UNUSED(idx)) const {
66         return NOTHING;
67     }
68 
69     /// \brief Returns the original value of the displayed quantity.
70     ///
71     /// If no such value exists, returns NOTHING.
72     virtual Optional<Particle> getParticle(const Size idx) const = 0;
73 
74     /// \brief Returns recommended palette for drawing this colorizer.
75     ///
76     /// In case there is no palette, returns NOTHING.
77     virtual Optional<Palette> getPalette() const = 0;
78 
79     /// \brief Modifies the palette used by ths colorizer.
80     virtual void setPalette(const Palette& newPalette) = 0;
81 
82     /// \brief Returns the name of the colorizer.
83     ///
84     /// This is used when showing the colorizer in the window and as filename suffix.
85     virtual String name() const = 0;
86 };
87 
88 /// \todo
89 /// Types
90 /// Scalar -> scalar
91 /// Vector -> size, x, y, z
92 /// Tensor -> trace, 2nd inv, xx, yy, zz, xy, xz, yz, largest eigen, smallest eigen
93 
94 namespace Detail {
95 /// Helper function returning a scalar representation of given quantity.
96 ///
97 /// This value is later converted to color, using provided palette.
98 template <typename Type>
getColorizerValue(const Type & value)99 INLINE float getColorizerValue(const Type& value) {
100     SPH_ASSERT(isReal(value));
101     return float(value);
102 }
103 template <>
getColorizerValue(const Vector & value)104 INLINE float getColorizerValue(const Vector& value) {
105     const Float result = getLength(value);
106     SPH_ASSERT(isReal(result), value);
107     return float(result);
108 }
109 template <>
getColorizerValue(const TracelessTensor & value)110 INLINE float getColorizerValue(const TracelessTensor& value) {
111     return float(sqrt(ddot(value, value)));
112 }
113 template <>
getColorizerValue(const SymmetricTensor & value)114 INLINE float getColorizerValue(const SymmetricTensor& value) {
115     return float(sqrt(ddot(value, value)));
116 }
117 
118 /// Helper function returning vector representation of given quantity.
119 ///
120 /// Only meaningful result is returned for vector quantity, other quantities simply return zero vector.
121 /// The function is useful to avoid specializing colorizers for different types.
122 template <typename Type>
getColorizerVector(const Type & UNUSED (value))123 INLINE Optional<Vector> getColorizerVector(const Type& UNUSED(value)) {
124     return NOTHING;
125 }
126 template <>
getColorizerVector(const Vector & value)127 INLINE Optional<Vector> getColorizerVector(const Vector& value) {
128     return value;
129 }
130 } // namespace Detail
131 
132 /// \brief Special colorizers that do not directly correspond to quantities.
133 ///
134 /// Must have strictly negative values. Function taking \ref ColorizerId as an argument also acceps \ref
135 /// QuantityId casted to \ref ColorizerId, interpreting as \ref TypedColorizer with given quantity ID.
136 enum class ColorizerId {
137     VELOCITY = -1,             ///< Particle velocities
138     ACCELERATION = -2,         ///< Acceleration of particles
139     MOVEMENT_DIRECTION = -3,   ///< Projected direction of motion
140     COROTATING_VELOCITY = -4,  ///< Velocities with a respect to the rotating body
141     DISPLACEMENT = -5,         ///< Difference between current positions and initial position
142     DENSITY_PERTURBATION = -6, ///< Relative difference of density and initial density (rho/rho0 - 1)
143     SUMMED_DENSITY = -7,       ///< Density computed from particle masses by direct summation of neighbors
144     TOTAL_STRESS = -8,         ///< Total stress (sigma = S - pI)
145     TOTAL_ENERGY = -9,         ///< Sum of kinetic and internal energy for given particle
146     TEMPERATURE = -10,         ///< Temperature, computed from internal energy
147     YIELD_REDUCTION = -11,     ///< Reduction of stress tensor due to yielding (1 - f_vonMises)
148     DAMAGE_ACTIVATION = -12,   ///< Ratio of the stress and the activation strain
149     RADIUS = -13,              ///< Radii/smoothing lenghts of particles
150     UVW = -15,                 ///< Shows UV mapping, u-coordinate in red and v-coordinate in blur
151     BOUNDARY = -16,            ///< Shows boundary particles
152     PARTICLE_ID = -17,         ///< Each particle drawn with different color
153     COMPONENT_ID = -18,        ///< Color assigned to each component (group of connected particles)
154     BOUND_COMPONENT_ID = -19,  ///< Color assigned to each group of gravitationally bound particles
155     AGGREGATE_ID = -20,        ///< Color assigned to each aggregate
156     FLAG = -21,                ///< Particles of different bodies are colored differently
157     MATERIAL_ID = -22,         ///< Particles with different materials are colored differently
158     BEAUTY = -23,              ///< Attempts to show the real-world look
159 };
160 
161 using ExtColorizerId = ExtendedEnum<ColorizerId>;
162 
163 SPH_EXTEND_ENUM(QuantityId, ColorizerId);
164 
165 /// \brief Default colorizer simply converting quantity value to color using defined palette.
166 ///
167 /// Vector and tensor quantities are converted to floats using suitable norm.
168 template <typename Type>
169 class TypedColorizer : public IColorizer {
170 protected:
171     QuantityId id;
172     Palette palette;
173     ArrayRef<const Type> values;
174 
175 public:
TypedColorizer(const QuantityId id,Palette palette)176     TypedColorizer(const QuantityId id, Palette palette)
177         : id(id)
178         , palette(std::move(palette)) {}
179 
hasData(const Storage & storage)180     virtual bool hasData(const Storage& storage) const override {
181         return storage.has(id);
182     }
183 
initialize(const Storage & storage,const RefEnum ref)184     virtual void initialize(const Storage& storage, const RefEnum ref) override {
185         values = makeArrayRef(storage.getValue<Type>(id), ref);
186     }
187 
isInitialized()188     virtual bool isInitialized() const override {
189         return !values.empty();
190     }
191 
evalColor(const Size idx)192     virtual Rgba evalColor(const Size idx) const override {
193         return palette(this->evalScalar(idx).value());
194     }
195 
evalScalar(const Size idx)196     virtual Optional<float> evalScalar(const Size idx) const override {
197         SPH_ASSERT(this->isInitialized());
198         return Detail::getColorizerValue(values[idx]);
199     }
200 
evalVector(const Size idx)201     virtual Optional<Vector> evalVector(const Size idx) const override {
202         return Detail::getColorizerVector(values[idx]);
203     }
204 
getParticle(const Size idx)205     virtual Optional<Particle> getParticle(const Size idx) const override {
206         return Particle(id, values[idx], idx);
207     }
208 
getPalette()209     virtual Optional<Palette> getPalette() const override {
210         return palette;
211     }
212 
setPalette(const Palette & newPalette)213     virtual void setPalette(const Palette& newPalette) override {
214         palette = newPalette;
215     }
216 
name()217     virtual String name() const override {
218         return getMetadata(id).quantityName;
219     }
220 };
221 
hasVelocity(const Storage & storage)222 inline bool hasVelocity(const Storage& storage) {
223     return storage.has<Vector>(QuantityId::POSITION, OrderEnum::FIRST) ||
224            storage.has<Vector>(QuantityId::POSITION, OrderEnum::SECOND);
225 }
226 
227 /// \brief Displays the magnitudes of particle velocities.
228 class VelocityColorizer : public TypedColorizer<Vector> {
229 public:
VelocityColorizer(Palette palette)230     explicit VelocityColorizer(Palette palette)
231         : TypedColorizer<Vector>(QuantityId::POSITION, std::move(palette)) {}
232 
hasData(const Storage & storage)233     virtual bool hasData(const Storage& storage) const override {
234         return hasVelocity(storage);
235     }
236 
initialize(const Storage & storage,const RefEnum ref)237     virtual void initialize(const Storage& storage, const RefEnum ref) override {
238         values = makeArrayRef(storage.getDt<Vector>(QuantityId::POSITION), ref);
239     }
240 
evalVector(const Size idx)241     virtual Optional<Vector> evalVector(const Size idx) const override {
242         return values[idx];
243     }
244 
getParticle(const Size idx)245     virtual Optional<Particle> getParticle(const Size idx) const override {
246         return Particle(idx).addDt(QuantityId::POSITION, values[idx]);
247     }
248 
name()249     virtual String name() const override {
250         return "Velocity";
251     }
252 };
253 
254 /// \brief Displays the magnitudes of accelerations.
255 class AccelerationColorizer : public TypedColorizer<Vector> {
256 public:
AccelerationColorizer(Palette palette)257     explicit AccelerationColorizer(Palette palette)
258         : TypedColorizer<Vector>(QuantityId::POSITION, std::move(palette)) {}
259 
hasData(const Storage & storage)260     virtual bool hasData(const Storage& storage) const override {
261         return storage.has<Vector>(QuantityId::POSITION, OrderEnum::SECOND);
262     }
263 
initialize(const Storage & storage,const RefEnum ref)264     virtual void initialize(const Storage& storage, const RefEnum ref) override {
265         values = makeArrayRef(storage.getD2t<Vector>(QuantityId::POSITION), ref);
266     }
267 
getParticle(const Size idx)268     virtual Optional<Particle> getParticle(const Size idx) const override {
269         return Particle(idx).addD2t(QuantityId::POSITION, values[idx]);
270     }
271 
name()272     virtual String name() const override {
273         return "Acceleration";
274     }
275 };
276 
277 /// \brief Shows direction of particle movement in color.
278 class DirectionColorizer : public IColorizer {
279 private:
280     Palette palette;
281     Vector axis;
282     Vector dir1, dir2;
283 
284     ArrayRef<const Vector> values;
285 
286 public:
287     DirectionColorizer(const Vector& axis, const Palette& palette);
288 
hasData(const Storage & storage)289     virtual bool hasData(const Storage& storage) const override {
290         return hasVelocity(storage);
291     }
292 
initialize(const Storage & storage,const RefEnum ref)293     virtual void initialize(const Storage& storage, const RefEnum ref) override {
294         values = makeArrayRef(storage.getDt<Vector>(QuantityId::POSITION), ref);
295     }
296 
isInitialized()297     virtual bool isInitialized() const override {
298         return !values.empty();
299     }
300 
301     virtual Optional<float> evalScalar(const Size idx) const override;
302 
evalColor(const Size idx)303     virtual Rgba evalColor(const Size idx) const override {
304         return palette(this->evalScalar(idx).value());
305     }
306 
getParticle(const Size idx)307     virtual Optional<Particle> getParticle(const Size idx) const override {
308         // return velocity of the particle
309         return Particle(idx).addDt(QuantityId::POSITION, values[idx]);
310     }
311 
getPalette()312     virtual Optional<Palette> getPalette() const override {
313         return palette;
314     }
315 
setPalette(const Palette & newPalette)316     virtual void setPalette(const Palette& newPalette) override {
317         palette = newPalette;
318     }
319 
name()320     virtual String name() const override {
321         return "Direction";
322     }
323 };
324 
325 /// \brief Shows particle velocities with subtracted corotating component
326 class CorotatingVelocityColorizer : public IColorizer {
327 private:
328     Palette palette;
329     ArrayRef<const Vector> r;
330     ArrayRef<const Vector> v;
331     ArrayRef<const Size> matIds;
332 
333     struct BodyMetadata {
334         Vector center;
335         Vector omega;
336     };
337 
338     Array<BodyMetadata> data;
339 
340 public:
CorotatingVelocityColorizer(Palette palette)341     explicit CorotatingVelocityColorizer(Palette palette)
342         : palette(std::move(palette)) {}
343 
hasData(const Storage & storage)344     virtual bool hasData(const Storage& storage) const override {
345         return hasVelocity(storage) && storage.has(QuantityId::MATERIAL_ID);
346     }
347 
348     virtual void initialize(const Storage& storage, const RefEnum ref) override;
349 
isInitialized()350     virtual bool isInitialized() const override {
351         return !v.empty();
352     }
353 
evalColor(const Size idx)354     virtual Rgba evalColor(const Size idx) const override {
355         SPH_ASSERT(!v.empty() && !r.empty());
356         return palette(float(getLength(this->getCorotatingVelocity(idx))));
357     }
358 
evalVector(const Size idx)359     virtual Optional<Vector> evalVector(const Size idx) const override {
360         return this->getCorotatingVelocity(idx);
361     }
362 
getParticle(const Size idx)363     virtual Optional<Particle> getParticle(const Size idx) const override {
364         return Particle(idx).addDt(QuantityId::POSITION, this->getCorotatingVelocity(idx));
365     }
366 
getPalette()367     virtual Optional<Palette> getPalette() const override {
368         return palette;
369     }
370 
setPalette(const Palette & newPalette)371     virtual void setPalette(const Palette& newPalette) override {
372         palette = newPalette;
373     }
374 
name()375     virtual String name() const override {
376         return "Corot. velocity";
377     }
378 
379 private:
getCorotatingVelocity(const Size idx)380     Vector getCorotatingVelocity(const Size idx) const {
381         const BodyMetadata& body = data[matIds[idx]];
382         return v[idx] - cross(body.omega, r[idx] - body.center);
383     }
384 };
385 
386 class DensityPerturbationColorizer : public IColorizer {
387 private:
388     Palette palette;
389     ArrayRef<const Float> rho;
390     Array<Float> rho0;
391 
392 public:
DensityPerturbationColorizer(Palette palette)393     explicit DensityPerturbationColorizer(Palette palette)
394         : palette(std::move(palette)) {}
395 
hasData(const Storage & storage)396     virtual bool hasData(const Storage& storage) const override {
397         return storage.has(QuantityId::DENSITY);
398     }
399 
initialize(const Storage & storage,const RefEnum ref)400     virtual void initialize(const Storage& storage, const RefEnum ref) override {
401         rho = makeArrayRef(storage.getValue<Float>(QuantityId::DENSITY), ref);
402 
403         rho0.resize(rho.size());
404         for (Size i = 0; i < rho.size(); ++i) {
405             rho0[i] = storage.getMaterialOfParticle(i)->getParam<Float>(BodySettingsId::DENSITY);
406         }
407     }
408 
isInitialized()409     virtual bool isInitialized() const override {
410         return !rho.empty();
411     }
412 
evalColor(const Size idx)413     virtual Rgba evalColor(const Size idx) const override {
414         SPH_ASSERT(this->isInitialized());
415         return palette(float(rho[idx] / rho0[idx] - 1._f));
416     }
417 
getParticle(const Size idx)418     virtual Optional<Particle> getParticle(const Size idx) const override {
419         return Particle(QuantityId::DENSITY, rho[idx] / rho0[idx] - 1.f, idx);
420     }
421 
getPalette()422     virtual Optional<Palette> getPalette() const override {
423         return palette;
424     }
425 
setPalette(const Palette & newPalette)426     virtual void setPalette(const Palette& newPalette) override {
427         palette = newPalette;
428     }
429 
name()430     virtual String name() const override {
431         return "Delta Density";
432     }
433 };
434 
435 class SummedDensityColorizer : public IColorizer {
436 private:
437     Palette palette;
438     ArrayRef<const Float> m;
439     ArrayRef<const Vector> r;
440 
441     AutoPtr<IBasicFinder> finder;
442 
443     LutKernel<3> kernel;
444 
445 public:
446     SummedDensityColorizer(const RunSettings& settings, Palette palette);
447 
hasData(const Storage & UNUSED (storage))448     virtual bool hasData(const Storage& UNUSED(storage)) const override {
449         // mass and positions must always be present
450         return true;
451     }
452 
453     virtual void initialize(const Storage& storage, const RefEnum ref) override;
454 
isInitialized()455     virtual bool isInitialized() const override {
456         return !m.empty();
457     }
458 
evalScalar(const Size idx)459     virtual Optional<float> evalScalar(const Size idx) const override {
460         return sum(idx);
461     }
462 
evalColor(const Size idx)463     virtual Rgba evalColor(const Size idx) const override {
464         return palette(sum(idx));
465     }
466 
getParticle(const Size idx)467     virtual Optional<Particle> getParticle(const Size idx) const override {
468         return Particle(QuantityId::DENSITY, Float(sum(idx)), idx);
469     }
470 
getPalette()471     virtual Optional<Palette> getPalette() const override {
472         return palette;
473     }
474 
setPalette(const Palette & newPalette)475     virtual void setPalette(const Palette& newPalette) override {
476         palette = newPalette;
477     }
478 
name()479     virtual String name() const override {
480         return "Summed Density";
481     }
482 
483 private:
484     float sum(const Size idx) const;
485 };
486 
487 class StressColorizer : public IColorizer {
488     Palette palette;
489     ArrayRef<const Float> p;
490     ArrayRef<const TracelessTensor> s;
491 
492 public:
StressColorizer(Palette palette)493     explicit StressColorizer(Palette palette)
494         : palette(std::move(palette)) {}
495 
hasData(const Storage & storage)496     virtual bool hasData(const Storage& storage) const override {
497         return storage.has(QuantityId::DEVIATORIC_STRESS) && storage.has(QuantityId::PRESSURE);
498     }
499 
initialize(const Storage & storage,const RefEnum ref)500     virtual void initialize(const Storage& storage, const RefEnum ref) override {
501         s = makeArrayRef(storage.getValue<TracelessTensor>(QuantityId::DEVIATORIC_STRESS), ref);
502         p = makeArrayRef(storage.getValue<Float>(QuantityId::PRESSURE), ref);
503     }
504 
isInitialized()505     virtual bool isInitialized() const override {
506         return !s.empty() && !p.empty();
507     }
508 
evalColor(const Size idx)509     virtual Rgba evalColor(const Size idx) const override {
510         return palette(this->evalScalar(idx).value());
511     }
512 
evalScalar(const Size idx)513     virtual Optional<float> evalScalar(const Size idx) const override {
514         SPH_ASSERT(this->isInitialized());
515         SymmetricTensor sigma = SymmetricTensor(s[idx]) - p[idx] * SymmetricTensor::identity();
516         // StaticArray<Float, 3> eigens = findEigenvalues(sigma);
517         // return max(abs(eigens[0]), abs(eigens[1]), abs(eigens[2]));
518         return float(sqrt(ddot(sigma, sigma)));
519     }
520 
evalVector(const Size UNUSED (idx))521     virtual Optional<Vector> evalVector(const Size UNUSED(idx)) const override {
522         return NOTHING;
523     }
524 
getParticle(const Size idx)525     virtual Optional<Particle> getParticle(const Size idx) const override {
526         SymmetricTensor sigma = SymmetricTensor(s[idx]) - p[idx] * SymmetricTensor::identity();
527         return Particle(QuantityId::DEVIATORIC_STRESS, sigma, idx);
528     }
529 
getPalette()530     virtual Optional<Palette> getPalette() const override {
531         return palette;
532     }
533 
setPalette(const Palette & newPalette)534     virtual void setPalette(const Palette& newPalette) override {
535         palette = newPalette;
536     }
537 
name()538     virtual String name() const override {
539         return "Total stress";
540     }
541 };
542 
543 class EnergyColorizer : public IColorizer {
544     Palette palette;
545     ArrayRef<const Float> u;
546     ArrayRef<const Vector> v;
547 
548 public:
EnergyColorizer(Palette palette)549     explicit EnergyColorizer(Palette palette)
550         : palette(std::move(palette)) {}
551 
hasData(const Storage & storage)552     virtual bool hasData(const Storage& storage) const override {
553         return hasVelocity(storage) && storage.has(QuantityId::ENERGY);
554     }
555 
initialize(const Storage & storage,const RefEnum ref)556     virtual void initialize(const Storage& storage, const RefEnum ref) override {
557         u = makeArrayRef(storage.getValue<Float>(QuantityId::ENERGY), ref);
558         v = makeArrayRef(storage.getDt<Vector>(QuantityId::POSITION), ref);
559     }
560 
isInitialized()561     virtual bool isInitialized() const override {
562         return !u.empty();
563     }
564 
evalColor(const Size idx)565     virtual Rgba evalColor(const Size idx) const override {
566         return palette(this->evalScalar(idx).value());
567     }
568 
evalScalar(const Size idx)569     virtual Optional<float> evalScalar(const Size idx) const override {
570         SPH_ASSERT(this->isInitialized());
571         return float(u[idx] + 0.5_f * getSqrLength(v[idx]));
572     }
573 
evalVector(const Size UNUSED (idx))574     virtual Optional<Vector> evalVector(const Size UNUSED(idx)) const override {
575         return NOTHING;
576     }
577 
getParticle(const Size idx)578     virtual Optional<Particle> getParticle(const Size idx) const override {
579         const Float value = evalScalar(idx).value();
580         return Particle(QuantityId::ENERGY, value, idx);
581     }
582 
getPalette()583     virtual Optional<Palette> getPalette() const override {
584         return palette;
585     }
586 
setPalette(const Palette & newPalette)587     virtual void setPalette(const Palette& newPalette) override {
588         palette = newPalette;
589     }
590 
name()591     virtual String name() const override {
592         return "Total energy";
593     }
594 };
595 
596 class TemperatureColorizer : public TypedColorizer<Float> {
597     Float cp;
598 
599 public:
TemperatureColorizer()600     explicit TemperatureColorizer()
601         : TypedColorizer<Float>(QuantityId::ENERGY, getEmissionPalette(Interval(500, 10000))) {}
602 
hasData(const Storage & storage)603     virtual bool hasData(const Storage& storage) const override {
604         return storage.has(QuantityId::ENERGY) && storage.getMaterialCnt() > 0;
605     }
606 
initialize(const Storage & storage,const RefEnum ref)607     virtual void initialize(const Storage& storage, const RefEnum ref) override {
608         TypedColorizer<Float>::initialize(storage, ref);
609         cp = storage.getMaterial(0)->getParam<Float>(BodySettingsId::HEAT_CAPACITY);
610     }
611 
evalScalar(const Size idx)612     virtual Optional<float> evalScalar(const Size idx) const override {
613         SPH_ASSERT(this->isInitialized());
614         return float(this->values[idx] / cp);
615     }
616 
getParticle(const Size idx)617     virtual Optional<Particle> getParticle(const Size idx) const override {
618         return Particle(QuantityId::TEMPERATURE, values[idx] / cp, idx);
619     }
620 
getPalette()621     virtual Optional<Palette> getPalette() const override {
622         return palette;
623     }
624 
setPalette(const Palette & newPalette)625     virtual void setPalette(const Palette& newPalette) override {
626         palette = newPalette;
627     }
628 
name()629     virtual String name() const override {
630         return "Temperature";
631     }
632 };
633 
634 
635 class YieldReductionColorizer : public TypedColorizer<Float> {
636 public:
YieldReductionColorizer(Palette palette)637     explicit YieldReductionColorizer(Palette palette)
638         : TypedColorizer<Float>(QuantityId::STRESS_REDUCING, std::move(palette)) {}
639 
evalColor(const Size idx)640     virtual Rgba evalColor(const Size idx) const override {
641         SPH_ASSERT(this->isInitialized());
642         SPH_ASSERT(values[idx] >= 0._f && values[idx] <= 1._f);
643         return palette(float(1._f - values[idx]));
644     }
645 
name()646     virtual String name() const override {
647         return "Yield reduction";
648     }
649 };
650 
651 class DamageActivationColorizer : public IColorizer {
652 private:
653     Palette palette;
654     Array<float> ratio;
655 
656 public:
DamageActivationColorizer(const Palette & palette)657     explicit DamageActivationColorizer(const Palette& palette)
658         : palette(std::move(palette)) {}
659 
660     virtual bool hasData(const Storage& storage) const override;
661 
662     virtual void initialize(const Storage& storage, const RefEnum ref) override;
663 
isInitialized()664     virtual bool isInitialized() const override {
665         return !ratio.empty();
666     }
667 
evalColor(const Size idx)668     virtual Rgba evalColor(const Size idx) const override {
669         return palette(ratio[idx]);
670     }
671 
getParticle(const Size UNUSED (idx))672     virtual Optional<Particle> getParticle(const Size UNUSED(idx)) const override {
673         return NOTHING;
674     }
675 
getPalette()676     virtual Optional<Palette> getPalette() const override {
677         return palette;
678     }
679 
setPalette(const Palette & newPalette)680     virtual void setPalette(const Palette& newPalette) override {
681         palette = newPalette;
682     }
683 
name()684     virtual String name() const override {
685         return "Damage activation ratio";
686     }
687 };
688 
689 class BeautyColorizer : public IColorizer {
690 private:
691     ArrayRef<const Float> u;
692     Palette palette;
693 
694     const float u_0 = 3.e4f;
695     const float u_red = 3.e5f;
696     const float u_glow = 0.5f * u_red;
697     const float u_yellow = 5.e6f;
698 
699     float f_glow;
700 
701 public:
702     BeautyColorizer();
703 
hasData(const Storage & storage)704     virtual bool hasData(const Storage& storage) const override {
705         return storage.has(QuantityId::ENERGY);
706     }
707 
initialize(const Storage & storage,const RefEnum ref)708     virtual void initialize(const Storage& storage, const RefEnum ref) override {
709         u = makeArrayRef(storage.getValue<Float>(QuantityId::ENERGY), ref);
710     }
711 
isInitialized()712     virtual bool isInitialized() const override {
713         return !u.empty();
714     }
715 
evalColor(const Size idx)716     virtual Rgba evalColor(const Size idx) const override {
717         SPH_ASSERT(this->isInitialized());
718         return palette(float(u[idx]));
719     }
720 
evalScalar(const Size idx)721     virtual Optional<float> evalScalar(const Size idx) const override {
722         const float f = palette.paletteToRelative(u[idx]);
723         return max(0.f, (f - f_glow) / (1.f - f_glow));
724     }
725 
getParticle(const Size idx)726     virtual Optional<Particle> getParticle(const Size idx) const override {
727         return Particle(idx).addValue(QuantityId::ENERGY, u[idx]);
728     }
729 
getPalette()730     virtual Optional<Palette> getPalette() const override {
731         return palette;
732     }
733 
setPalette(const Palette & newPalette)734     virtual void setPalette(const Palette& newPalette) override {
735         palette = newPalette;
736     }
737 
name()738     virtual String name() const override {
739         return "Beauty";
740     }
741 };
742 
743 class RadiusColorizer : public TypedColorizer<Vector> {
744 public:
RadiusColorizer(Palette palette)745     explicit RadiusColorizer(Palette palette)
746         : TypedColorizer<Vector>(QuantityId::SMOOTHING_LENGTH, std::move(palette)) {}
747 
initialize(const Storage & storage,const RefEnum ref)748     virtual void initialize(const Storage& storage, const RefEnum ref) override {
749         values = makeArrayRef(storage.getValue<Vector>(QuantityId::POSITION), ref);
750     }
751 
evalColor(const Size idx)752     virtual Rgba evalColor(const Size idx) const override {
753         SPH_ASSERT(this->isInitialized());
754         return palette(float(values[idx][H]));
755     }
756 
getParticle(const Size idx)757     virtual Optional<Particle> getParticle(const Size idx) const override {
758         return Particle(idx).addValue(QuantityId::SMOOTHING_LENGTH, values[idx][H]);
759     }
760 
hasData(const Storage & UNUSED (storage))761     virtual bool hasData(const Storage& UNUSED(storage)) const override {
762         // radii are always present
763         return true;
764     }
765 
name()766     virtual String name() const override {
767         return "Radius";
768     }
769 };
770 
771 class UvwColorizer : public IColorizer {
772 private:
773     ArrayRef<const Vector> uvws;
774 
775 public:
hasData(const Storage & storage)776     virtual bool hasData(const Storage& storage) const override {
777         return storage.has(QuantityId::UVW);
778     }
779 
initialize(const Storage & storage,const RefEnum ref)780     virtual void initialize(const Storage& storage, const RefEnum ref) override {
781         uvws = makeArrayRef(storage.getValue<Vector>(QuantityId::UVW), ref);
782     }
783 
isInitialized()784     virtual bool isInitialized() const override {
785         return !uvws.empty();
786     }
787 
evalColor(const Size idx)788     virtual Rgba evalColor(const Size idx) const override {
789         SPH_ASSERT(this->isInitialized());
790         return Rgba(float(uvws[idx][X]), 0.f, float(uvws[idx][Y]));
791     }
792 
getParticle(const Size UNUSED (idx))793     virtual Optional<Particle> getParticle(const Size UNUSED(idx)) const override {
794         return NOTHING;
795     }
796 
getPalette()797     virtual Optional<Palette> getPalette() const override {
798         return NOTHING;
799     }
800 
setPalette(const Palette & UNUSED (newPalette))801     virtual void setPalette(const Palette& UNUSED(newPalette)) override {}
802 
name()803     virtual String name() const override {
804         return "Uvws";
805     }
806 };
807 
808 /// Shows boundary of bodies in the simulation.
809 class BoundaryColorizer : public IColorizer {
810 public:
811     enum class Detection {
812         /// Particles with fewer neighbors are considered boundary. Not suitable if number of neighbors is
813         /// enforced by adapting smoothing length. Note that increasing the threshold adds more particles into
814         /// the boundary.
815         NEIGBOUR_THRESHOLD,
816 
817         /// Boundary is determined by relative position vectors approximating surface normal. Has higher
818         /// overhead, but does not depend sensitively on number of neighbors. Here, increasing the threshold
819         /// leads to fewer boundary particles.
820         NORMAL_BASED,
821     };
822 
823 private:
824     Detection detection;
825 
826     struct {
827         ArrayRef<const Vector> values;
828         Float threshold;
829     } normals;
830 
831     struct {
832         ArrayRef<const Size> values;
833         Size threshold;
834     } neighbors;
835 
836 public:
837     BoundaryColorizer(const Detection detection, const Float threshold = 15._f);
838 
839     virtual bool hasData(const Storage& storage) const override;
840 
841     virtual void initialize(const Storage& storage, const RefEnum ref) override;
842 
843     virtual bool isInitialized() const override;
844 
845     virtual Rgba evalColor(const Size idx) const override;
846 
getParticle(const Size UNUSED (idx))847     virtual Optional<Particle> getParticle(const Size UNUSED(idx)) const override {
848         // doesn't really make sense to assign some value to boundary
849         return NOTHING;
850     }
851 
getPalette()852     virtual Optional<Palette> getPalette() const override {
853         return NOTHING;
854     }
855 
setPalette(const Palette & UNUSED (newPalette))856     virtual void setPalette(const Palette& UNUSED(newPalette)) override {}
857 
name()858     virtual String name() const override {
859         return "Boundary";
860     }
861 
862 private:
863     bool isBoundary(const Size idx) const;
864 };
865 
866 template <typename TDerived>
867 class IdColorizerTemplate : public IColorizer {
868 private:
869     Rgba backgroundColor;
870     Size seed = 1;
871 
872 public:
IdColorizerTemplate(const GuiSettings & gui)873     explicit IdColorizerTemplate(const GuiSettings& gui) {
874         backgroundColor = gui.get<Rgba>(GuiSettingsId::BACKGROUND_COLOR);
875     }
876 
setSeed(const Size newSeed)877     void setSeed(const Size newSeed) {
878         seed = newSeed;
879     }
880 
881     virtual Rgba evalColor(const Size idx) const override;
882 
883     virtual Optional<Particle> getParticle(const Size idx) const override;
884 
getPalette()885     virtual Optional<Palette> getPalette() const override {
886         return NOTHING;
887     }
888 
setPalette(const Palette & UNUSED (newPalette))889     virtual void setPalette(const Palette& UNUSED(newPalette)) override {}
890 };
891 
892 class ParticleIdColorizer : public IdColorizerTemplate<ParticleIdColorizer> {
893 private:
894     ArrayRef<const Size> persistentIdxs;
895 
896 public:
897     using IdColorizerTemplate<ParticleIdColorizer>::IdColorizerTemplate;
898 
evalId(const Size idx)899     INLINE Optional<Size> evalId(const Size idx) const {
900         if (!persistentIdxs.empty() && idx < persistentIdxs.size()) {
901             return persistentIdxs[idx];
902         } else {
903             return idx;
904         }
905     }
906 
hasData(const Storage & UNUSED (storage))907     virtual bool hasData(const Storage& UNUSED(storage)) const override {
908         return true;
909     }
910 
911     virtual void initialize(const Storage& storage, const RefEnum ref) override;
912 
isInitialized()913     virtual bool isInitialized() const override {
914         return true;
915     }
916 
917     virtual Optional<Particle> getParticle(const Size idx) const override;
918 
name()919     virtual String name() const override {
920         return "Particle ID";
921     }
922 };
923 
924 class ComponentIdColorizer : public IdColorizerTemplate<ComponentIdColorizer> {
925 private:
926     Flags<Post::ComponentFlag> connectivity;
927 
928     Array<Size> components;
929 
930     ArrayRef<const Float> m;
931     ArrayRef<const Vector> r, v;
932 
933     Optional<Size> highlightIdx;
934 
935     /// \todo hacked optimization to avoid rebuilding the colorizer if only highlight idx changed. Should be
936     /// done more generally and with lower memory footprint
937     struct {
938         Array<Vector> r;
939     } cached;
940 
941 public:
942     explicit ComponentIdColorizer(const GuiSettings& gui,
943         const Flags<Post::ComponentFlag> connectivity,
944         const Optional<Size> highlightIdx = NOTHING);
945 
946     void setHighlightIdx(const Optional<Size> newHighlightIdx);
947 
getHighlightIdx()948     Optional<Size> getHighlightIdx() const {
949         return highlightIdx;
950     }
951 
setConnectivity(const Flags<Post::ComponentFlag> newConnectivity)952     void setConnectivity(const Flags<Post::ComponentFlag> newConnectivity) {
953         connectivity = newConnectivity;
954         cached.r.clear();
955     }
956 
getConnectivity()957     Flags<Post::ComponentFlag> getConnectivity() const {
958         return connectivity;
959     }
960 
evalId(const Size idx)961     INLINE Optional<Size> evalId(const Size idx) const {
962         return components[idx];
963     }
964 
965     virtual Rgba evalColor(const Size idx) const override;
966 
967     virtual Optional<Particle> getParticle(const Size idx) const override;
968 
969     virtual bool hasData(const Storage& storage) const override;
970 
971     virtual void initialize(const Storage& storage, const RefEnum ref) override;
972 
isInitialized()973     virtual bool isInitialized() const override {
974         return !components.empty();
975     }
976 
977     virtual String name() const override;
978 };
979 
980 class AggregateIdColorizer : public IdColorizerTemplate<AggregateIdColorizer> {
981 private:
982     ArrayView<const Size> ids;
983 
984 public:
985     using IdColorizerTemplate<AggregateIdColorizer>::IdColorizerTemplate;
986 
evalId(const Size idx)987     INLINE Optional<Size> evalId(const Size idx) const {
988         if (ids[idx] != Size(-1)) {
989             return ids[idx];
990         } else {
991             return NOTHING;
992         }
993     }
994 
hasData(const Storage & storage)995     virtual bool hasData(const Storage& storage) const override {
996         return storage.has(QuantityId::AGGREGATE_ID) && storage.getUserData() != nullptr;
997     }
998 
initialize(const Storage & storage,const RefEnum UNUSED (ref))999     virtual void initialize(const Storage& storage, const RefEnum UNUSED(ref)) override {
1000         ids = storage.getValue<Size>(QuantityId::AGGREGATE_ID);
1001     }
1002 
isInitialized()1003     virtual bool isInitialized() const override {
1004         return ids != nullptr;
1005     }
1006 
name()1007     virtual String name() const override {
1008         return "Aggregate ID";
1009     }
1010 };
1011 
1012 class IndexColorizer : public IdColorizerTemplate<IndexColorizer> {
1013 private:
1014     QuantityId id;
1015     ArrayRef<const Size> idxs;
1016 
1017 public:
IndexColorizer(const QuantityId id,const GuiSettings & gui)1018     IndexColorizer(const QuantityId id, const GuiSettings& gui)
1019         : IdColorizerTemplate<IndexColorizer>(gui)
1020         , id(id) {}
1021 
evalId(const Size idx)1022     INLINE Optional<Size> evalId(const Size idx) const {
1023         return idxs[idx];
1024     }
1025 
hasData(const Storage & storage)1026     virtual bool hasData(const Storage& storage) const override {
1027         return storage.has(id);
1028     }
1029 
initialize(const Storage & storage,const RefEnum ref)1030     virtual void initialize(const Storage& storage, const RefEnum ref) override {
1031         idxs = makeArrayRef(storage.getValue<Size>(id), ref);
1032     }
1033 
isInitialized()1034     virtual bool isInitialized() const override {
1035         return !idxs.empty();
1036     }
1037 
name()1038     virtual String name() const override {
1039         return getMetadata(id).quantityName;
1040     }
1041 };
1042 
1043 class MaterialColorizer : public IndexColorizer {
1044 private:
1045     Array<String> eosNames;
1046     Array<String> rheoNames;
1047 
1048 public:
MaterialColorizer(const GuiSettings & gui)1049     MaterialColorizer(const GuiSettings& gui)
1050         : IndexColorizer(QuantityId::MATERIAL_ID, gui) {}
1051 
1052     virtual void initialize(const Storage& storage, const RefEnum ref) override;
1053 
1054     virtual Optional<Particle> getParticle(const Size idx) const override;
1055 };
1056 
1057 NAMESPACE_SPH_END
1058