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