1 #include "gui/Factory.h"
2 #include "gui/Project.h"
3 #include "gui/objects/Camera.h"
4 #include "gui/objects/Colorizer.h"
5 #include "gui/renderers/Brdf.h"
6 #include "gui/renderers/ContourRenderer.h"
7 #include "gui/renderers/FrameBuffer.h"
8 #include "gui/renderers/MeshRenderer.h"
9 #include "gui/renderers/ParticleRenderer.h"
10 #include "gui/renderers/RayMarcher.h"
11 #include "gui/renderers/VolumeRenderer.h"
12 
13 NAMESPACE_SPH_BEGIN
14 
getTracker(const GuiSettings & settings)15 AutoPtr<ITracker> Factory::getTracker(const GuiSettings& settings) {
16     const int trackedIndex = settings.get<int>(GuiSettingsId::CAMERA_TRACK_PARTICLE);
17     if (trackedIndex >= 0) {
18         return makeAuto<ParticleTracker>(trackedIndex);
19     }
20     const bool useMedian = settings.get<bool>(GuiSettingsId::CAMERA_TRACK_MEDIAN);
21     if (useMedian) {
22         const Vector offset = settings.get<Vector>(GuiSettingsId::CAMERA_TRACKING_OFFSET);
23         return makeAuto<MedianTracker>(offset);
24     }
25     return nullptr;
26 }
27 
getCamera(const GuiSettings & settings,const Pixel size)28 AutoPtr<ICamera> Factory::getCamera(const GuiSettings& settings, const Pixel size) {
29     CameraEnum cameraId = settings.get<CameraEnum>(GuiSettingsId::CAMERA_TYPE);
30     CameraParams data;
31     data.imageSize = size;
32     data.position = settings.get<Vector>(GuiSettingsId::CAMERA_POSITION);
33     data.target = settings.get<Vector>(GuiSettingsId::CAMERA_TARGET);
34     data.up = settings.get<Vector>(GuiSettingsId::CAMERA_UP);
35     data.clipping = Interval(settings.get<Float>(GuiSettingsId::CAMERA_CLIP_NEAR),
36         settings.get<Float>(GuiSettingsId::CAMERA_CLIP_FAR));
37     data.perspective.fov = settings.get<Float>(GuiSettingsId::CAMERA_PERSPECTIVE_FOV);
38     data.ortho.fov = float(settings.get<Float>(GuiSettingsId::CAMERA_ORTHO_FOV));
39     data.ortho.cutoff = float(settings.get<Float>(GuiSettingsId::CAMERA_ORTHO_CUTOFF));
40     if (data.ortho.cutoff.value() == 0._f) {
41         data.ortho.cutoff = NOTHING;
42     }
43 
44     switch (cameraId) {
45     case CameraEnum::ORTHO:
46         return makeAuto<OrthoCamera>(data);
47     case CameraEnum::PERSPECTIVE:
48         return makeAuto<PerspectiveCamera>(data);
49     case CameraEnum::FISHEYE:
50         return makeAuto<FisheyeCamera>(data);
51     case CameraEnum::SPHERICAL:
52         return makeAuto<SphericalCamera>(data);
53     default:
54         NOT_IMPLEMENTED;
55     }
56 }
57 
getRenderer(const GuiSettings & settings)58 AutoPtr<IRenderer> Factory::getRenderer(const GuiSettings& settings) {
59     SharedPtr<IScheduler> scheduler = getScheduler(RunSettings::getDefaults());
60     return getRenderer(scheduler, settings);
61 }
62 
getRenderer(SharedPtr<IScheduler> scheduler,const GuiSettings & settings)63 AutoPtr<IRenderer> Factory::getRenderer(SharedPtr<IScheduler> scheduler, const GuiSettings& settings) {
64     RendererEnum id = settings.get<RendererEnum>(GuiSettingsId::RENDERER);
65     AutoPtr<IRenderer> renderer;
66     switch (id) {
67     case RendererEnum::NONE:
68         class NullRenderer : public IRenderer {
69             virtual void initialize(const Storage&, const IColorizer&, const ICamera&) override {}
70             virtual bool isInitialized() const override {
71                 return true;
72             }
73             virtual void setColorizer(const IColorizer&) override {}
74             virtual void render(const RenderParams&, Statistics&, IRenderOutput&) const override {}
75             virtual void cancelRender() override {}
76         };
77         renderer = makeAuto<NullRenderer>();
78         break;
79     case RendererEnum::PARTICLE:
80         renderer = makeAuto<ParticleRenderer>(settings);
81         break;
82     case RendererEnum::MESH:
83         renderer = makeAuto<MeshRenderer>(scheduler, settings);
84         break;
85     case RendererEnum::RAYMARCHER:
86         renderer = makeAuto<RayMarcher>(scheduler, settings);
87         break;
88     case RendererEnum::VOLUME:
89         renderer = makeAuto<VolumeRenderer>(scheduler, settings);
90         break;
91     case RendererEnum::CONTOUR:
92         renderer = makeAuto<ContourRenderer>(scheduler, settings);
93         break;
94     default:
95         NOT_IMPLEMENTED;
96     }
97 
98     return renderer;
99 }
100 
getBrdf(const GuiSettings & settings)101 AutoPtr<IBrdf> Factory::getBrdf(const GuiSettings& settings) {
102     const BrdfEnum id = settings.get<BrdfEnum>(GuiSettingsId::RAYTRACE_BRDF);
103     switch (id) {
104     case BrdfEnum::LAMBERT:
105         return makeAuto<LambertBrdf>(1._f);
106     case BrdfEnum::PHONG:
107         return makeAuto<PhongBrdf>(1._f);
108     default:
109         NOT_IMPLEMENTED;
110     }
111 }
112 
getColorMap(const GuiSettings & settings)113 AutoPtr<IColorMap> Factory::getColorMap(const GuiSettings& settings) {
114     const ColorMapEnum id = settings.get<ColorMapEnum>(GuiSettingsId::COLORMAP_TYPE);
115     switch (id) {
116     case ColorMapEnum::LINEAR:
117         return nullptr;
118     case ColorMapEnum::LOGARITHMIC: {
119         const float factor = settings.get<Float>(GuiSettingsId::COLORMAP_LOGARITHMIC_FACTOR);
120         return makeAuto<LogarithmicColorMap>(factor);
121     }
122     case ColorMapEnum::FILMIC:
123         return makeAuto<FilmicColorMap>();
124     default:
125         NOT_IMPLEMENTED;
126     }
127 }
128 
getColorizer(const GuiSettings & settings,const ExtColorizerId id)129 static AutoPtr<IColorizer> getColorizer(const GuiSettings& settings, const ExtColorizerId id) {
130     using Factory::getPalette;
131 
132     switch (id) {
133     case ColorizerId::VELOCITY:
134         return makeAuto<VelocityColorizer>(getPalette(id));
135     case ColorizerId::ACCELERATION:
136         return makeAuto<AccelerationColorizer>(getPalette(id));
137     case ColorizerId::MOVEMENT_DIRECTION:
138         return makeAuto<DirectionColorizer>(Vector(0._f, 0._f, 1._f), getPalette(id));
139     case ColorizerId::COROTATING_VELOCITY:
140         return makeAuto<CorotatingVelocityColorizer>(getPalette(ColorizerId::VELOCITY));
141     case ColorizerId::DENSITY_PERTURBATION:
142         return makeAuto<DensityPerturbationColorizer>(getPalette(id));
143     case ColorizerId::SUMMED_DENSITY:
144         return makeAuto<SummedDensityColorizer>(RunSettings::getDefaults(), getPalette(QuantityId::DENSITY));
145     case ColorizerId::TOTAL_ENERGY:
146         return makeAuto<EnergyColorizer>(getPalette(id));
147     case ColorizerId::TEMPERATURE:
148         return makeAuto<TemperatureColorizer>();
149     case ColorizerId::TOTAL_STRESS:
150         return makeAuto<StressColorizer>(getPalette(id));
151     case ColorizerId::YIELD_REDUCTION:
152         return makeAuto<YieldReductionColorizer>(getPalette(id));
153     case ColorizerId::DAMAGE_ACTIVATION:
154         return makeAuto<DamageActivationColorizer>(getPalette(id));
155     case ColorizerId::RADIUS:
156         return makeAuto<RadiusColorizer>(getPalette(id));
157     case ColorizerId::BOUNDARY:
158         return makeAuto<BoundaryColorizer>(BoundaryColorizer::Detection::NEIGBOUR_THRESHOLD, 40);
159     case ColorizerId::UVW:
160         return makeAuto<UvwColorizer>();
161     case ColorizerId::PARTICLE_ID:
162         return makeAuto<ParticleIdColorizer>(settings);
163     case ColorizerId::COMPONENT_ID:
164         return makeAuto<ComponentIdColorizer>(
165             settings, Post::ComponentFlag::OVERLAP | Post::ComponentFlag::SORT_BY_MASS);
166     case ColorizerId::BOUND_COMPONENT_ID:
167         return makeAuto<ComponentIdColorizer>(
168             settings, Post::ComponentFlag::ESCAPE_VELOCITY | Post::ComponentFlag::SORT_BY_MASS);
169     case ColorizerId::AGGREGATE_ID:
170         return makeAuto<AggregateIdColorizer>(settings);
171     case ColorizerId::FLAG:
172         return makeAuto<IndexColorizer>(QuantityId::FLAG, settings);
173     case ColorizerId::MATERIAL_ID:
174         return makeAuto<MaterialColorizer>(settings);
175     case ColorizerId::BEAUTY:
176         return makeAuto<BeautyColorizer>();
177     default:
178         QuantityId quantity = QuantityId(id);
179         SPH_ASSERT(int(quantity) >= 0);
180 
181         Palette palette = getPalette(id);
182         switch (getMetadata(quantity).expectedType) {
183         case ValueEnum::INDEX:
184             return makeAuto<TypedColorizer<Size>>(quantity, std::move(palette));
185         case ValueEnum::SCALAR:
186             return makeAuto<TypedColorizer<Float>>(quantity, std::move(palette));
187         case ValueEnum::VECTOR:
188             return makeAuto<TypedColorizer<Vector>>(quantity, std::move(palette));
189         case ValueEnum::TRACELESS_TENSOR:
190             return makeAuto<TypedColorizer<TracelessTensor>>(quantity, std::move(palette));
191         case ValueEnum::SYMMETRIC_TENSOR:
192             return makeAuto<TypedColorizer<SymmetricTensor>>(quantity, std::move(palette));
193         default:
194             NOT_IMPLEMENTED;
195         }
196     }
197 }
198 
getColorizer(const Project & project,const ExtColorizerId id)199 AutoPtr<IColorizer> Factory::getColorizer(const Project& project, const ExtColorizerId id) {
200     AutoPtr<IColorizer> colorizer = Sph::getColorizer(project.getGuiSettings(), id);
201     Optional<Palette> palette = colorizer->getPalette();
202     if (palette && project.getPalette(colorizer->name(), palette.value())) {
203         colorizer->setPalette(palette.value());
204     }
205     return colorizer;
206 }
207 
208 struct PaletteDesc {
209     Interval range;
210     PaletteScale scale;
211 };
212 
213 static FlatMap<ExtColorizerId, PaletteDesc> paletteDescs(ELEMENTS_UNIQUE,
214     {
215         { QuantityId::DENSITY, { Interval(2650._f, 2750._f), PaletteScale::LINEAR } },
216         { QuantityId::MASS, { Interval(1.e5_f, 1.e10_f), PaletteScale::LOGARITHMIC } },
217         { QuantityId::PRESSURE, { Interval(-1.e5_f, 1.e10_f), PaletteScale::HYBRID } },
218         { QuantityId::ENERGY, { Interval(1._f, 1.e6_f), PaletteScale::LOGARITHMIC } },
219         { QuantityId::DEVIATORIC_STRESS, { Interval(0._f, 1.e10_f), PaletteScale::LINEAR } },
220         { QuantityId::DAMAGE, { Interval(0._f, 1._f), PaletteScale::LINEAR } },
221         { QuantityId::VELOCITY_DIVERGENCE, { Interval(-0.1_f, 0.1_f), PaletteScale::LINEAR } },
222         { QuantityId::VELOCITY_GRADIENT, { Interval(0._f, 1.e-3_f), PaletteScale::LINEAR } },
223         { QuantityId::VELOCITY_LAPLACIAN, { Interval(0._f, 1.e-3_f), PaletteScale::LINEAR } },
224         { QuantityId::VELOCITY_GRADIENT_OF_DIVERGENCE, { Interval(0._f, 1.e-3_f), PaletteScale::LINEAR } },
225         { QuantityId::VELOCITY_ROTATION, { Interval(0._f, 4._f), PaletteScale::LINEAR } },
226         { QuantityId::SOUND_SPEED, { Interval(0._f, 5.e3_f), PaletteScale::LINEAR } },
227         { QuantityId::VIBRATIONAL_VELOCITY, { Interval(0._f, 5.e3_f), PaletteScale::LINEAR } },
228         { QuantityId::AV_ALPHA, { Interval(0.1_f, 1.5_f), PaletteScale::LINEAR } },
229         { QuantityId::AV_BALSARA, { Interval(0._f, 1._f), PaletteScale::LINEAR } },
230         { QuantityId::AV_STRESS, { Interval(0._f, 1.e8_f), PaletteScale::LINEAR } },
231         { QuantityId::ANGULAR_FREQUENCY, { Interval(0._f, 1.e-3_f), PaletteScale::LINEAR } },
232         { QuantityId::MOMENT_OF_INERTIA, { Interval(0._f, 1.e10_f), PaletteScale::LINEAR } },
233         { QuantityId::PHASE_ANGLE, { Interval(0._f, 10._f), PaletteScale::LINEAR } },
234         { QuantityId::STRAIN_RATE_CORRECTION_TENSOR, { Interval(0._f, 5._f), PaletteScale::LINEAR } },
235         { QuantityId::EPS_MIN, { Interval(0._f, 1._f), PaletteScale::LINEAR } },
236         { QuantityId::FRICTION, { Interval(0._f, 1._f), PaletteScale::LINEAR } },
237         { QuantityId::DELTASPH_DENSITY_GRADIENT, { Interval(0._f, 1._f), PaletteScale::LINEAR } },
238         { QuantityId::NEIGHBOR_CNT, { Interval(50._f, 150._f), PaletteScale::LINEAR } },
239         { ColorizerId::VELOCITY, { Interval(0.1_f, 100._f), PaletteScale::LOGARITHMIC } },
240         { ColorizerId::ACCELERATION, { Interval(0.1_f, 100._f), PaletteScale::LOGARITHMIC } },
241         { ColorizerId::MOVEMENT_DIRECTION, { Interval(0._f, 2._f * PI), PaletteScale::LINEAR } },
242         { ColorizerId::RADIUS, { Interval(0._f, 1.e3_f), PaletteScale::LINEAR } },
243         { ColorizerId::TOTAL_ENERGY, { Interval(1.e6_f, 1.e10_f), PaletteScale::LOGARITHMIC } },
244         { ColorizerId::TEMPERATURE, { Interval(100._f, 1.e7_f), PaletteScale::LOGARITHMIC } },
245         { ColorizerId::DENSITY_PERTURBATION, { Interval(-1.e-6_f, 1.e-6_f), PaletteScale::LINEAR } },
246         { ColorizerId::DAMAGE_ACTIVATION, { Interval(2.e-4_f, 8.e-4_f), PaletteScale::LINEAR } },
247         { ColorizerId::YIELD_REDUCTION, { Interval(0._f, 1._f), PaletteScale::LINEAR } },
248         { ColorizerId::TOTAL_STRESS, { Interval(0._f, 1e6_f), PaletteScale::LINEAR } },
249     });
250 
getDefaultPalette(const Interval range)251 static Palette getDefaultPalette(const Interval range) {
252     const float x0 = float(range.lower());
253     const float dx = float(range.size());
254     return Palette({ { x0, Rgba(0.f, 0.f, 0.6f) },
255                        { x0 + 0.2f * dx, Rgba(0.1f, 0.1f, 0.1f) },
256                        { x0 + 0.6f * dx, Rgba(0.9f, 0.9f, 0.9f) },
257                        { x0 + 0.8f * dx, Rgba(1.f, 1.f, 0.f) },
258                        { x0 + dx, Rgba(0.6f, 0.f, 0.f) } },
259         PaletteScale::LINEAR);
260 }
261 
getPalette(const ExtColorizerId id)262 Palette Factory::getPalette(const ExtColorizerId id) {
263     const PaletteDesc desc = paletteDescs[id];
264     const Interval range = desc.range;
265     const PaletteScale scale = desc.scale;
266     const float x0 = float(range.lower());
267     const float dx = float(range.size());
268     switch (id) {
269     case ColorizerId::VELOCITY:
270         return Palette({ { x0, Rgba(0.5f, 0.5f, 0.5f) },
271                            { x0 + 0.001f * dx, Rgba(0.0f, 0.0f, 0.2f) },
272                            { x0 + 0.01f * dx, Rgba(0.0f, 0.0f, 1.0f) },
273                            { x0 + 0.1f * dx, Rgba(1.0f, 0.0f, 0.2f) },
274                            { x0 + dx, Rgba(1.0f, 1.0f, 0.2f) } },
275             scale);
276     case ColorizerId::ACCELERATION:
277         return Palette({ { x0, Rgba(0.5f, 0.5f, 0.5f) },
278                            { x0 + 0.001f * dx, Rgba(0.0f, 0.0f, 0.2f) },
279                            { x0 + 0.01f * dx, Rgba(0.0f, 0.0f, 1.0f) },
280                            { x0 + 0.1f * dx, Rgba(1.0f, 0.0f, 0.2f) },
281                            { x0 + dx, Rgba(1.0f, 1.0f, 0.2f) } },
282             scale);
283     case ColorizerId::MOVEMENT_DIRECTION: {
284         SPH_ASSERT(range == Interval(0.f, 2._f * PI)); // in radians
285         const float pi = float(PI);
286         return Palette({ { 0.f, Rgba(0.1f, 0.1f, 1.f) },
287                            { pi / 3.f, Rgba(1.f, 0.1f, 1.f) },
288                            { 2.f * pi / 3.f, Rgba(1.f, 0.1f, 0.1f) },
289                            { 3.f * pi / 3.f, Rgba(1.f, 1.f, 0.1f) },
290                            { 4.f * pi / 3.f, Rgba(0.1f, 1.f, 0.1f) },
291                            { 5.f * pi / 3.f, Rgba(0.1f, 1.f, 1.f) },
292                            { 2.f * pi, Rgba(0.1f, 0.1f, 1.f) } },
293             scale);
294     }
295     case ColorizerId::DENSITY_PERTURBATION:
296         return Palette({ { x0, Rgba(0.1f, 0.1f, 1.f) },
297                            { x0 + 0.5f * dx, Rgba(0.7f, 0.7f, 0.7f) },
298                            { x0 + dx, Rgba(1.f, 0.1f, 0.1f) } },
299             scale);
300     case ColorizerId::TOTAL_ENERGY:
301         return Palette({ { x0, Rgba(0.f, 0.f, 0.6f) },
302                            { x0 + 0.01f * dx, Rgba(0.1f, 0.1f, 0.1f) },
303                            { x0 + 0.05f * dx, Rgba(0.9f, 0.9f, 0.9f) },
304                            { x0 + 0.2f * dx, Rgba(1.f, 1.f, 0.f) },
305                            { x0 + dx, Rgba(0.6f, 0.f, 0.f) } },
306             scale);
307     case ColorizerId::TEMPERATURE:
308         return Palette({ { x0, Rgba(0.1f, 0.1f, 0.1f) },
309                            { x0 + 0.001f * dx, Rgba(0.1f, 0.1f, 1.f) },
310                            { x0 + 0.01f * dx, Rgba(1.f, 0.f, 0.f) },
311                            { x0 + 0.1f * dx, Rgba(1.0f, 0.6f, 0.4f) },
312                            { x0 + dx, Rgba(1.f, 1.f, 0.f) } },
313             scale);
314     case ColorizerId::YIELD_REDUCTION:
315         return Palette({ { 0._f, Rgba(0.1f, 0.1f, 0.1f) }, { 1._f, Rgba(0.9f, 0.9f, 0.9f) } }, scale);
316     case ColorizerId::DAMAGE_ACTIVATION:
317         return Palette({ { x0, Rgba(0.1f, 0.1f, 1.f) },
318                            { x0 + 0.5f * dx, Rgba(0.7f, 0.7f, 0.7f) },
319                            { x0 + dx, Rgba(1.f, 0.1f, 0.1f) } },
320             scale);
321     case ColorizerId::RADIUS:
322         return Palette({ { x0, Rgba(0.1f, 0.1f, 1.f) },
323                            { x0 + 0.5f * dx, Rgba(0.7f, 0.7f, 0.7f) },
324                            { x0 + dx, Rgba(1.f, 0.1f, 0.1f) } },
325             scale);
326     default:
327         // check extended values
328         switch (QuantityId(id)) {
329         case QuantityId::PRESSURE:
330             SPH_ASSERT(x0 < -1.f);
331             return Palette({ { x0, Rgba(0.3f, 0.3f, 0.8f) },
332                                { -1.e4f, Rgba(0.f, 0.f, 0.2f) },
333                                { 0.f, Rgba(0.2f, 0.2f, 0.2f) },
334                                { 1.e4f, Rgba(0.8f, 0.8f, 0.8f) },
335                                { 2.e4f, Rgba(1.f, 1.f, 0.2f) },
336                                { x0 + dx, Rgba(0.5f, 0.f, 0.f) } },
337                 scale);
338         case QuantityId::ENERGY:
339             return Palette({ { x0, Rgba(0.1f, 0.1f, 0.1f) },
340                                { x0 + 0.001f * dx, Rgba(0.1f, 0.1f, 1.f) },
341                                { x0 + 0.01f * dx, Rgba(1.f, 0.f, 0.f) },
342                                { x0 + 0.1f * dx, Rgba(1.0f, 0.6f, 0.4f) },
343                                { x0 + dx, Rgba(1.f, 1.f, 0.f) } },
344                 scale);
345         case QuantityId::DEVIATORIC_STRESS:
346             return Palette({ { x0, Rgba(0.f, 0.f, 0.2f) },
347                                { x0 + 0.1f * dx, Rgba(0.9f, 0.9f, 0.9f) },
348                                { x0 + 0.25f * dx, Rgba(1.f, 1.f, 0.2f) },
349                                { x0 + 0.5f * dx, Rgba(1.f, 0.5f, 0.f) },
350                                { x0 + dx, Rgba(0.5f, 0.f, 0.f) } },
351                 scale);
352         case QuantityId::DENSITY:
353         case QuantityId::VELOCITY_LAPLACIAN:
354         case QuantityId::FRICTION:
355         case QuantityId::VELOCITY_GRADIENT_OF_DIVERGENCE:
356             return Palette({ { x0, Rgba(0.4f, 0.f, 0.4f) },
357                                { x0 + 0.3f * dx, Rgba(0.3f, 0.3f, 1.f) },
358                                { x0 + 0.5f * dx, Rgba(0.9f, 0.9f, 0.9f) },
359                                { x0 + 0.7f * dx, Rgba(1.f, 0.f, 0.f) },
360                                { x0 + dx, Rgba(1.f, 1.f, 0.f) } },
361                 scale);
362         case QuantityId::DAMAGE:
363             return Palette({ { x0, Rgba(0.1f, 0.1f, 0.1f) }, { x0 + dx, Rgba(0.9f, 0.9f, 0.9f) } }, scale);
364         case QuantityId::MASS:
365             return Palette({ { x0, Rgba(0.1f, 0.1f, 0.1f) }, { x0 + dx, Rgba(0.9f, 0.9f, 0.9f) } }, scale);
366         case QuantityId::VELOCITY_DIVERGENCE:
367             SPH_ASSERT(x0 < 0._f);
368             return Palette({ { x0, Rgba(0.3f, 0.3f, 0.8f) },
369                                { 0.1f * x0, Rgba(0.f, 0.f, 0.2f) },
370                                { 0.f, Rgba(0.2f, 0.2f, 0.2f) },
371                                { 0.1f * (x0 + dx), Rgba(0.8f, 0.8f, 0.8f) },
372                                { x0 + dx, Rgba(1.0f, 0.6f, 0.f) } },
373                 scale);
374         case QuantityId::VELOCITY_GRADIENT:
375             SPH_ASSERT(x0 == 0._f);
376             return Palette({ { 0._f, Rgba(0.3f, 0.3f, 0.8f) },
377                                { 0.01f * dx, Rgba(0.f, 0.f, 0.2f) },
378                                { 0.05f * dx, Rgba(0.2f, 0.2f, 0.2f) },
379                                { 0.2f * dx, Rgba(0.8f, 0.8f, 0.8f) },
380                                { dx, Rgba(1.0f, 0.6f, 0.f) } },
381                 scale);
382         case QuantityId::ANGULAR_FREQUENCY:
383             SPH_ASSERT(x0 == 0._f);
384             return Palette({ { 0._f, Rgba(0.3f, 0.3f, 0.8f) },
385                                { 0.25f * dx, Rgba(0.f, 0.f, 0.2f) },
386                                { 0.5f * dx, Rgba(0.2f, 0.2f, 0.2f) },
387                                { 0.75f * dx, Rgba(0.8f, 0.8f, 0.8f) },
388                                { dx, Rgba(1.0f, 0.6f, 0.f) } },
389                 scale);
390         case QuantityId::STRAIN_RATE_CORRECTION_TENSOR: {
391             // sqrt(3) is an important value, as it corresponds to identity tensor
392             const float actDx = max(dx, sqrt(3.f) + 0.2f);
393             const float eps = 0.05f;
394             return Palette({ { 0._f, Rgba(0.f, 0.0f, 0.5f) },
395                                { sqrt(3.f) - eps, Rgba(0.9f, 0.9f, 0.9f) },
396                                { sqrt(3.f), Rgba(1.f, 1.f, 0.f) },
397                                { sqrt(3.f) + eps, Rgba(0.9f, 0.9f, 0.9f) },
398                                { actDx, Rgba(0.5f, 0.0f, 0.0f) } },
399                 scale);
400         }
401         case QuantityId::AV_BALSARA:
402             return Palette({ { x0, Rgba(0.1f, 0.1f, 0.1f) }, { x0 + dx, Rgba(0.9f, 0.9f, 0.9f) } }, scale);
403         case QuantityId::EPS_MIN:
404             return Palette({ { x0, Rgba(0.1f, 0.1f, 1.f) },
405                                { x0 + 0.5f * dx, Rgba(0.7f, 0.7f, 0.7f) },
406                                { x0 + dx, Rgba(1.f, 0.1f, 0.1f) } },
407                 scale);
408         case QuantityId::MOMENT_OF_INERTIA:
409             return Palette({ { x0, Rgba(0.1f, 0.1f, 1.f) },
410                                { x0 + 0.5f * dx, Rgba(0.7f, 0.7f, 0.7f) },
411                                { x0 + dx, Rgba(1.f, 0.1f, 0.1f) } },
412                 scale);
413         default:
414             return getDefaultPalette(Interval(x0, x0 + dx));
415         }
416     }
417 }
418 
419 NAMESPACE_SPH_END
420