FuncViz examples
Generalized bitree/quadtree/octree library
Loading...
Searching...
No Matches
complex_magnitude_surface.cpp
Go to the documentation of this file.
1// -*- Mode:C++; Coding:us-ascii-unix; fill-column:158 -*-
2/*******************************************************************************************************************************************************.H.S.**/
3/**
4 @file complex_magnitude_surface.cpp
5 @author Mitch Richling http://www.mitchr.me/
6 @date 2024-07-13
7 @brief Complex magnitude surface with coloring.@EOL
8 @std C++23
9 @copyright
10 @parblock
11 Copyright (c) 2024, Mitchell Jay Richling <http://www.mitchr.me/> All rights reserved.
12
13 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
14
15 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
16
17 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19
20 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software
21 without specific prior written permission.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
28 DAMAGE.
29 @endparblock
30 @filedetails
31
32 One popular way to plot complex functions is to use a surface plot of @f$\vert f(z)\vert@f$ and color the surface with @f$\arg(f(z))@f$. This way we can
33 simultaneously represent the magnitude and phase over the complex plain. There are several ways to color the plots, and we will be following the method
34 described by Richardson in 1991. In this example, we demonstrates several techniques:
35
36 - Alternate ways to do an initial sample (grid vs recursive)
37 - Sample near a point in the domain
38 - Sample below a level in the range
39 - Sample near level curves
40 - Sample based on a data value that is *not* part of the geometry
41 - Sample near domain axis
42 - Use MRaster to compute colors for complex numbers
43 - Directly attach colors to geometric points
44 - Do rough clipping with high sampling and cell filtering.
45 - Precision clipping by folding & culling.
46*/
47/*******************************************************************************************************************************************************.H.E.**/
48/** @cond exj */
49
50////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
51#include "MR_rect_tree.hpp"
52#include "MR_cell_cplx.hpp"
53#include "MR_rt_to_cc.hpp"
54#include "MRcolor.hpp"
55
56////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
57typedef mjr::tree15b2d9rT tt_t;
58typedef mjr::MRccT5 cc_t;
59typedef mjr::MR_rt_to_cc<tt_t, cc_t> tc_t;
60typedef mjr::color3c64F ct_t;
61
62////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
63tt_t::rrpt_t cpf(tt_t::drpt_t xvec) {
64 std::complex<double> z(xvec[0], xvec[1]);
65 double z_abs, z_arg, f_re, f_im, f_abs, f_arg, red, green, blue;
66
67 z_abs = std::abs(z);
68 z_arg = std::arg(z);
69
70 if ( (std::abs(z-1.0) > 1.0e-5) && (std::abs(z+1.0) > 1.0e-5) ) {
71 std::complex<double> f;
72 f = 1.0/(z+1.0) + 1.0/(z-1.0);
73 f_re = std::real(f);
74 f_im = std::imag(f);
75 f_abs = std::abs(f);
76 f_arg = std::arg(f);
77 ct_t c = ct_t::cs2dIdxPalArg<ct_t::csCColdeRainbow, 3, 5.0, 20.0, 2.0, 1>::c(f);
78 red = c.getRed();
79 green = c.getGreen();
80 blue = c.getBlue();
81 } else {
82 f_re = f_im = f_abs = f_arg = red = green = blue = std::numeric_limits<double>::quiet_NaN();
83 }
84
85 return {z_abs, z_arg, f_re, f_im, f_abs, f_arg, red, green, blue};
86}
87
88////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
89tt_t::src_t cpfd(tt_t::drpt_t xvec) {
90 int idx_for_z = 4;
91 double cut_for_z = 3.5;
92 auto fv = cpf(xvec);
93
94 if(std::isnan(fv[idx_for_z]))
95 return 100000.0;
96 else
97 return fv[idx_for_z]-cut_for_z;
98}
99
100////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
101int main() {
102 tt_t tree({-2.2, -1.2},
103 { 2.2, 1.2});
104 cc_t ccplx;
105
106 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
107 // Initial sample
108
109 // On a uniform grid
110 tree.refine_grid(7, cpf);
111
112 // Alternately we can use refine_recursive() instead (refine_grid() is faster)
113 // tree.refine_recursive(4, cpf);
114
115 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
116 // Sample near 0+0i because we have a minimum at that piont
117
118 // The most direct method
119 // tree.refine_leaves_recursive_cell_pred(6, cpf, [&tree](tt_t::diti_t i) { return (tree.cell_near_domain_point({0, 0}, 1.0e-2, i)); });
120
121 // This function is positive with a universal minimum at 0+0i, so we could just sample where |f| is below 1/4
122 tree.refine_leaves_recursive_cell_pred(6, cpf, [&tree](tt_t::diti_t i) { return !(tree.cell_above_range_level(i, 4, 0.25)); });
123
124 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
125 // Sample around the poles where we will clip the graph
126
127 // With nice ranges the singularities will be precicely located on cell vertexes. So we can just refine NaNs.
128 // tree.refine_recursive_if_cell_vertex_is_nan(6, cpf);
129
130 // Or we can directly sample on the clip level at |f|=3.5.
131 tree.refine_leaves_recursive_cell_pred(7, cpf, [&tree](tt_t::diti_t i) { return (tree.cell_cross_range_level(i, 4, 3.5)); });
132
133 // We can do the above with a constructed SDF instead.
134 // tree.refine_leaves_recursive_cell_pred(6, cpf, [&tree](tt_t::diti_t i) { return (tree.cell_cross_sdf(i, cpfd)); });
135
136 // Just like the previous, but with atomic refinement.
137 // tree.refine_leaves_atomically_if_cell_pred(6, cpf, [&tree](tt_t::diti_t i) { return (tree.cell_cross_sdf(i, cpfd)); });
138
139 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
140 // Refine where we plan to draw level curves
141
142 // The easiest thing is to use cell_cross_range_level() for this.
143 for(auto lev: {0.4, 0.7, 1.1, 1.4, 1.8, 2.6, 3.5})
144 tree.refine_leaves_recursive_cell_pred(7, cpf, [&tree, lev](tt_t::diti_t i) { return (tree.cell_cross_range_level(i, 4, lev)); });
145
146 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
147 // We will be coloring based on arg(f), and so want to sample near the abrubpt change near arg(f)=0.
148
149 // We can do this just like the level curves with |f|, but use arg(f) instead -- i.e. index 5 instead of 4.
150 tree.refine_leaves_recursive_cell_pred(7, cpf, [&tree](tt_t::diti_t i) { return (tree.cell_cross_range_level(i, 5, 0.0)); });
151
152 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
153 // We can sample near the real & imagaxes axes.
154
155 // Sample near the real axis
156 tree.refine_leaves_recursive_cell_pred(5, cpf, [&tree](tt_t::diti_t i) { return (tree.cell_near_domain_level(i, 0, 0.0, 1.0e-6)); });
157
158 // Sample near the imaginary axis
159 tree.refine_leaves_recursive_cell_pred(5, cpf, [&tree](tt_t::diti_t i) { return (tree.cell_near_domain_level(i, 1, 0.0, 1.0e-6)); });
160
161 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
162 // We don't need to balance the three, but it makes things look nice.
163
164 // Balance the three to the traditional level of 1 (no cell borders a cell more than half it's size)
165 tree.balance_tree(1, cpf);
166
167 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
168 // At this point the tree is adequately sampled, so we print a bit out to the screen.
169 tree.dump_tree(5);
170
171 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
172 // Create the cell complex from cells that have at least one point below our clipping plane.
173 auto tcret = tc_t::construct_geometry_fans(ccplx,
174 tree,
175 tree.get_leaf_cells_pred(tree.ccc_get_top_cell(),
176 [&tree](tt_t::diti_t i) { return !(tree.cell_above_range_level(i, 4, 3.5)); }),
177 2,
178 {{tc_t::val_src_spc_t::FDOMAIN, 0},
179 {tc_t::val_src_spc_t::FDOMAIN, 1},
180 {tc_t::val_src_spc_t::FRANGE, 4}});
181 std::cout << "TC Return: " << tcret << std::endl;
182 ccplx.create_named_datasets({"Re(z)", "Im(z)", "abs(z)", "arg(z)", "Re(f(z))", "Im(f(z))", "abs(f(z))", "arg(f(z))"}, {{"COLORS", {8, 9, 10}}});
183 std::cout << "POST CONST" << std::endl;
184 ccplx.dump_cplx(5);
185
186 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
187 // Fold the triangles on our clipping plane
188 ccplx.triangle_folder([](cc_t::node_data_t x){return tc_t::tsampf_to_cdatf( cpf, x); },
189 [](cc_t::node_data_t x){return tc_t::tsampf_to_clcdf(4, 3.5, cpf, x); });
190 std::cout << "POST FOLD" << std::endl;
191 ccplx.dump_cplx(5);
192
193 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
194 // Remove all triangles above our clipping plane
195
196 // We can do this directly with ccplx using index 6 into the point data (point data is domain data appended with range data)
197 // ccplx.cull_cells([&ccplx](cc_t::cell_verts_t c){ return !(ccplx.cell_below_level(c, 6, 3.5)); });
198
199 // Or we can use the index in the original sample function along with the converter rt_rng_idx_to_pd_idx().
200 ccplx.cull_cells([&ccplx](cc_t::cell_verts_t c){ return !(ccplx.cell_below_level(c, tc_t::rt_rng_idx_to_pd_idx(4), 3.5)); });
201
202 std::cout << "POST CULL" << std::endl;
203 ccplx.dump_cplx(5);
204
205 //--------------------------------------------------------------------------------------------------------------------------------------------------------------
206 ccplx.write_legacy_vtk("complex_magnitude_surface.vtk", "complex_magnitude_surface");
207 ccplx.write_xml_vtk( "complex_magnitude_surface.vtu", "complex_magnitude_surface");
208 ccplx.write_ply( "complex_magnitude_surface.ply", "complex_magnitude_surface");
209}
210/** @endcond */
int main()
double f(double x, double y)