diff --git a/CHANGELOG.md b/CHANGELOG.md index f02703156..c97b90dd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s - mfem@4.7 ### Added +- Added a compressed color table format. - Added action options relating to logging functionality including `open_log`, `flush_log`, and `close_log` to toggle logging as well as `set_log_threshold` and `set_echo_threshold` to control logging and standard output levels. - Added a new unified logging infrastructure. - Added support for unstructured topologies with mixed elements types (for example, hexs and tets). diff --git a/src/docs/sphinx/Actions/Examples.rst b/src/docs/sphinx/Actions/Examples.rst index 2910d5fae..247c4fbea 100644 --- a/src/docs/sphinx/Actions/Examples.rst +++ b/src/docs/sphinx/Actions/Examples.rst @@ -612,6 +612,17 @@ Resulting image: .. image:: examples/milk_chocolate100.png +An example of creating a custom color map using a compressed format. +------------------------------------------- + +YAML actions: + +.. literalinclude:: examples/milk_chocolate_compressed100.yaml + +Resulting image: + +.. image:: examples/milk_chocolate100.png + An example if using the composite vector filter to compose three scalar fields into a vector. ----------------------------------------------------------------------------------------------- diff --git a/src/docs/sphinx/Actions/Scenes.rst b/src/docs/sphinx/Actions/Scenes.rst index 348c1e4c2..5bb86eee8 100644 --- a/src/docs/sphinx/Actions/Scenes.rst +++ b/src/docs/sphinx/Actions/Scenes.rst @@ -255,6 +255,29 @@ but when a color table is specified, then the color table needs to include alpha values. Otherwise, the volume plot will look exactly the same as a pseudocolor plot. +There are two formats that can be used to add a custom color table. The first is +a compact color table format: + +.. code-block:: c++ + + conduit::Node control_points; + control_points["r"] = {.23, .48, .99}; + control_points["g"] = {0.08, .23, 1.}; + control_points["b"] = {0.08, .04, .96}; + control_points["a"] = {1., 1., 1.}; + control_points["position"] = {0., .5, 1.}; + + conduit::Node scenes; + scenes["s1/plots/p1/type"] = "volume"; + scenes["s1/plots/p1/field"] = "braid"; + scenes["s1/plots/p1/color_table/control_points"] = control_points; + + conduit::Node actions; + conduit::Node &add_plots = actions.append(); + add_plots["action"] = "add_scenes"; + add_plots["scenes"] = scenes; + +Alternatively, there is a second, expanded color table format that can be used. Here is an example of adding a custom color table to the volume plot: .. code-block:: c++ diff --git a/src/docs/sphinx/Actions/examples/milk_chocolate_compressed100.yaml b/src/docs/sphinx/Actions/examples/milk_chocolate_compressed100.yaml new file mode 100644 index 000000000..44a12d5e4 --- /dev/null +++ b/src/docs/sphinx/Actions/examples/milk_chocolate_compressed100.yaml @@ -0,0 +1,23 @@ +#An example of creating a custom color map. + +- + action: "add_scenes" + scenes: + s1: + plots: + p1: + type: "pseudocolor" + field: "braid" + color_table: + control_points: + r: [0.23, 0.48, 0.99] + g: [0.08, 0.23, 1.0] + b: [0.08, 0.04, 0.9] + a: [1.0, 1.0, 1.0] + position: [0.0, 0.5, 1.0] + image_prefix: "/Users/harrison37/Work/alpine/ascent/build-debug/tests/_output/milk_chocolate" + renders: + r1: + image_width: 512 + image_height: 512 + image_prefix: "/Users/harrison37/Work/alpine/ascent/build-debug/tests/_output/milk_chocolate" diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_conduit_to_vtkm_parsing.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_conduit_to_vtkm_parsing.cpp index d4af2066b..fe9bc7797 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_conduit_to_vtkm_parsing.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_conduit_to_vtkm_parsing.cpp @@ -229,16 +229,30 @@ parse_color_table(const conduit::Node &color_table_node) if(color_table_node.has_child("control_points")) { - bool clear = false; + const Node &control_points_node = color_table_node.fetch("control_points"); + // check to see if we have rgb points and clear the table - NodeConstIterator itr = color_table_node.fetch("control_points").children(); - while(itr.has_next()) + bool clear = false; + if (control_points_node.dtype().is_list()) { - const Node &peg = itr.next(); - if (peg["type"].as_string() == "rgb") + NodeConstIterator itr = control_points_node.children(); + while(itr.has_next()) { - clear = true; - break; + const Node &peg = itr.next(); + if (peg["type"].as_string() == "rgb") + { + clear = true; + break; + } + } + } + else if (control_points_node.dtype().is_object()) + { + if (control_points_node.has_child("r") && + control_points_node.has_child("g") && + control_points_node.has_child("b")) + { + clear = true; } } @@ -247,51 +261,122 @@ parse_color_table(const conduit::Node &color_table_node) color_table.ClearColors(); } - itr = color_table_node.fetch("control_points").children(); - while(itr.has_next()) + if (control_points_node.dtype().is_list()) { - const Node &peg = itr.next(); - if(!peg.has_child("position")) + NodeConstIterator itr = control_points_node.children(); + while(itr.has_next()) { - // FIXME: This should be an error - ASCENT_WARN("Color map control point must have a position"); + const Node &peg = itr.next(); + if(!peg.has_child("position")) + { + // FIXME: This should be an error + ASCENT_WARN("Color map control point must have a position"); + } + + float64 position = peg["position"].to_float64(); + + if(position > 1.0 || position < 0.0) + { + ASCENT_WARN("Cannot add color map control point position " + << position + << ". Must be a normalized scalar."); + } + + if (peg["type"].as_string() == "rgb") + { + conduit::Node n; + peg["color"].to_float64_array(n); + const float64 *color = n.as_float64_ptr(); + + vtkm::Vec ecolor(color[0], color[1], color[2]); + + for(int i = 0; i < 3; ++i) + { + ecolor[i] = std::min(1., std::max(ecolor[i], 0.)); + } + + color_table.AddPoint(position, ecolor); + } + else if (peg["type"].as_string() == "alpha") + { + float64 alpha = peg["alpha"].to_float64(); + alpha = std::min(1., std::max(alpha, 0.)); + color_table.AddPointAlpha(position, alpha); + } + else + { + ASCENT_WARN("Unknown color table control point type " + << peg["type"].as_string()<< + "\nValid types are 'alpha' and 'rgb'"); + } + } + } + else if (control_points_node.dtype().is_object()) + { + if(!control_points_node.has_child("r")) + { + ASCENT_ERROR("Color map control point must provide r values"); + } + + if(!control_points_node.has_child("g")) + { + ASCENT_ERROR("Color map control point must provide g values"); } - float64 position = peg["position"].to_float64(); + if(!control_points_node.has_child("b")) + { + ASCENT_ERROR("Color map control point must provide b values"); + } - if(position > 1.0 || position < 0.0) + if(!control_points_node.has_child("position")) { - ASCENT_WARN("Cannot add color map control point position " - << position - << ". Must be a normalized scalar."); + ASCENT_ERROR("Color map control point must have a position"); } - if (peg["type"].as_string() == "rgb") + float64_array r_vals = control_points_node.fetch("r").value(); + float64_array g_vals = control_points_node.fetch("g").value(); + float64_array b_vals = control_points_node.fetch("b").value(); + float64_array pos_vals = control_points_node.fetch("position").value(); + + if(r_vals.number_of_elements() != g_vals.number_of_elements() || + g_vals.number_of_elements() != b_vals.number_of_elements() || + b_vals.number_of_elements() != pos_vals.number_of_elements()) { - conduit::Node n; - peg["color"].to_float64_array(n); - const float64 *color = n.as_float64_ptr(); + ASCENT_ERROR("Color map color channels should all be of the same size"); + } - vtkm::Vec ecolor(color[0], color[1], color[2]); + for(index_t i=0; i ecolor(r_vals[i], g_vals[i], b_vals[i]); for(int i = 0; i < 3; ++i) { - ecolor[i] = std::min(1., std::max(ecolor[i], 0.)); + ecolor[i] = std::min(1., std::max(ecolor[i], 0.)); } - color_table.AddPoint(position, ecolor); - } - else if (peg["type"].as_string() == "alpha") - { - float64 alpha = peg["alpha"].to_float64(); - alpha = std::min(1., std::max(alpha, 0.)); - color_table.AddPointAlpha(position, alpha); + if(pos_vals[i] > 1.0 || pos_vals[i] < 0.0) + { + ASCENT_ERROR("Cannot add color map control point position " + << pos_vals[i] + << ". Must be a normalized scalar."); + } + + color_table.AddPoint(pos_vals[i], ecolor); } - else + + if(control_points_node.has_child("a")) { - ASCENT_WARN("Unknown color table control point type " - << peg["type"].as_string()<< - "\nValid types are 'alpha' and 'rgb'"); + float64_array alpha_vals = control_points_node.fetch("a").value(); + + if(pos_vals.number_of_elements() != alpha_vals.number_of_elements()) + { + ASCENT_ERROR("Color map alpha channel should have same size as color channels"); + } + + for(index_t i=0; i c_valid_paths; - c_valid_paths.push_back("type"); - c_valid_paths.push_back("alpha"); - c_valid_paths.push_back("color"); - c_valid_paths.push_back("position"); + const Node &control_points_node = color_table.fetch("control_points"); - const conduit::Node &control_points = color_table["control_points"]; - const int num_points = control_points.number_of_children(); - for(int i = 0; i < num_points; ++i) + if (control_points_node.dtype().is_list()) { - const conduit::Node &point = control_points.child(i); - surprises += surprise_check(c_valid_paths, point); + std::vector c_valid_paths; + c_valid_paths.push_back("type"); + c_valid_paths.push_back("alpha"); + c_valid_paths.push_back("color"); + c_valid_paths.push_back("position"); + + const conduit::Node &control_points = color_table["control_points"]; + const int num_points = control_points.number_of_children(); + for(int i = 0; i < num_points; ++i) + { + const conduit::Node &point = control_points.child(i); + surprises += surprise_check(c_valid_paths, point); + } + } + else if (control_points_node.dtype().is_object()) + { + // Valid path options for the compressed control points input format + std::vector c_valid_paths; + c_valid_paths.push_back("r"); + c_valid_paths.push_back("g"); + c_valid_paths.push_back("b"); + c_valid_paths.push_back("a"); + c_valid_paths.push_back("position"); + + + surprises += surprise_check(c_valid_paths, control_points_node); } } @@ -418,20 +436,39 @@ parse_color_table(const conduit::Node &color_table_node) if(color_table_node.has_child("control_points")) { + const Node &control_points_node = color_table_node.fetch("control_points"); + + // check to see if we have rgb points and clear the table bool clear = false; bool clear_alphas = false; - // check to see if we have rgb points and clear the table - NodeConstIterator itr = color_table_node.fetch("control_points").children(); - while(itr.has_next()) + if (control_points_node.dtype().is_list()) { - const Node &peg = itr.next(); - if (peg["type"].as_string() == "rgb") + NodeConstIterator itr = control_points_node.children(); + while(itr.has_next()) { - clear = true; + const Node &peg = itr.next(); + if (peg["type"].as_string() == "rgb") + { + clear = true; + } + if (peg["type"].as_string() == "alpha") + { + clear_alphas = true; + } } - if (peg["type"].as_string() == "alpha") + } + else if (control_points_node.dtype().is_object()) + { + if (control_points_node.has_child("r") && + control_points_node.has_child("g") && + control_points_node.has_child("b")) { - clear_alphas = true; + clear = true; + } + + if (control_points_node.has_child("a")) + { + clear_alphas = true; } } @@ -445,50 +482,132 @@ parse_color_table(const conduit::Node &color_table_node) color_table.clear_alphas(); } - itr = color_table_node.fetch("control_points").children(); - while(itr.has_next()) + if (control_points_node.dtype().is_list()) + { + NodeConstIterator itr = control_points_node.children(); + while (itr.has_next()) + { + const Node &peg = itr.next(); + if (!peg.has_child("position")) + { + // FIXME: This should be an error + ASCENT_WARN("Color map control point must have a position"); + } + + float64 position = peg["position"].to_float64(); + + if (position > 1.0 || position < 0.0) + { + ASCENT_WARN("Cannot add color map control point position " + << position + << ". Must be a normalized scalar."); + } + + if (peg["type"].as_string() == "rgb") + { + conduit::Node n; + peg["color"].to_float32_array(n); + const float *color = n.as_float32_ptr(); + + dray::Vec ecolor({color[0], color[1], color[2]}); + + for (int i = 0; i < 3; ++i) + { + ecolor[i] = std::min(1.f, std::max(ecolor[i], 0.f)); + } + + color_table.add_point(position, ecolor); + } + else if (peg["type"].as_string() == "alpha") + { + float alpha = peg["alpha"].to_float32(); + alpha = std::min(1.f, std::max(alpha, 0.f)); + color_table.add_alpha(position, alpha); + } + else + { + ASCENT_WARN("Unknown color table control point type " << peg["type"].as_string()<< + "\nValid types are 'alpha' and 'rgb'"); + } + } + } + else if (control_points_node.dtype().is_object()) { - const Node &peg = itr.next(); - if(!peg.has_child("position")) + if (!control_points_node.has_child("r")) { - // FIXME: This should be an error - ASCENT_WARN("Color map control point must have a position"); + ASCENT_ERROR("Color map control point must provide r values"); } - float64 position = peg["position"].to_float64(); + if (!control_points_node.has_child("g")) + { + ASCENT_ERROR("Color map control point must provide g values"); + } - if(position > 1.0 || position < 0.0) + if (!control_points_node.has_child("b")) { - ASCENT_WARN("Cannot add color map control point position " - << position - << ". Must be a normalized scalar."); + ASCENT_ERROR("Color map control point must provide b values"); } - if (peg["type"].as_string() == "rgb") + if (!control_points_node.has_child("position")) { - conduit::Node n; - peg["color"].to_float32_array(n); - const float *color = n.as_float32_ptr(); + ASCENT_ERROR("Color map control point must have a position"); + } - dray::Vec ecolor({color[0], color[1], color[2]}); + conduit::Node r_vals_node; + control_points_node["r"].to_float32_array(r_vals_node); + float32_array r_vals = r_vals_node.as_float32_array(); - for(int i = 0; i < 3; ++i) - { - ecolor[i] = std::min(1.f, std::max(ecolor[i], 0.f)); - } + conduit::Node g_vals_node; + control_points_node["g"].to_float32_array(g_vals_node); + float32_array g_vals = g_vals_node.as_float32_array(); + + conduit::Node b_vals_node; + control_points_node["b"].to_float32_array(b_vals_node); + float32_array b_vals = b_vals_node.as_float32_array(); - color_table.add_point(position, ecolor); + float64_array pos_vals = control_points_node.fetch("position").value(); + + if (r_vals.number_of_elements() != g_vals.number_of_elements() || + g_vals.number_of_elements() != b_vals.number_of_elements() || + b_vals.number_of_elements() != pos_vals.number_of_elements()) + { + ASCENT_ERROR("Color map color channels should all be of the same size"); } - else if (peg["type"].as_string() == "alpha") + + for (index_t i=0; i ecolor({r_vals[i], g_vals[i], b_vals[i]}); + + for (int e = 0; e < 3; ++e) + { + ecolor[e] = std::min(1.f, std::max(ecolor[e], 0.f)); + } + + if (pos_vals[i] > 1.0 || pos_vals[i] < 0.0) + { + ASCENT_ERROR("Cannot add color map control point position " + << pos_vals[i] + << ". Must be a normalized scalar."); + } + + color_table.add_point(pos_vals[i], ecolor); } - else + + if (control_points_node.has_child("a")) { - ASCENT_WARN("Unknown color table control point type " << peg["type"].as_string()<< - "\nValid types are 'alpha' and 'rgb'"); + conduit::Node alpha_vals_node; + control_points_node["a"].to_float32_array(alpha_vals_node); + float32_array alpha_vals = alpha_vals_node.as_float32_array(); + + if (pos_vals.number_of_elements() != alpha_vals.number_of_elements()) + { + ASCENT_ERROR("Color map alpha channel should have same size as color channels"); + } + + for (index_t i=0; i c_valid_paths; - c_valid_paths.push_back("type"); - c_valid_paths.push_back("alpha"); - c_valid_paths.push_back("color"); - c_valid_paths.push_back("position"); - - const conduit::Node &control_points = color_table["control_points"]; - const int num_points = control_points.number_of_children(); - for(int i = 0; i < num_points; ++i) - { - const conduit::Node &point = control_points.child(i); - surprises += surprise_check(c_valid_paths, point); + const Node &control_points_node = color_table.fetch("control_points"); + + if (control_points_node.dtype().is_list()) + { + // Valid path options for the expanded control points input format + std::vector c_valid_paths; + c_valid_paths.push_back("type"); + c_valid_paths.push_back("alpha"); + c_valid_paths.push_back("color"); + c_valid_paths.push_back("position"); + + const int num_points = control_points_node.number_of_children(); + for(int i = 0; i < num_points; ++i) + { + const conduit::Node &point = control_points_node.child(i); + surprises += surprise_check(c_valid_paths, point); + } + } + else if (control_points_node.dtype().is_object()) + { + // Valid path options for the compressed control points input format + std::vector c_valid_paths; + c_valid_paths.push_back("r"); + c_valid_paths.push_back("g"); + c_valid_paths.push_back("b"); + c_valid_paths.push_back("a"); + c_valid_paths.push_back("position"); + + + surprises += surprise_check(c_valid_paths, control_points_node); } } diff --git a/src/libs/dray/utilities/furnace/parsing.hpp b/src/libs/dray/utilities/furnace/parsing.hpp index fa6bd2180..68455c361 100644 --- a/src/libs/dray/utilities/furnace/parsing.hpp +++ b/src/libs/dray/utilities/furnace/parsing.hpp @@ -88,10 +88,9 @@ parse_color_table(const conduit::Node &color_table_node) ss<<","; } } - std::cout<<"Invalid color table name '"< 1.0 || position < 0.0) + { + std::cout<<"Cannot add color map control point position " + << position + << ". Must be a normalized scalar.\n"; + } + + if (peg["type"].as_string() == "rgb") + { + conduit::Node n; + peg["color"].to_float32_array(n); + const float *color = n.as_float32_ptr(); + + dray::Vec ecolor({color[0], color[1], color[2]}); + + for(int i = 0; i < 3; ++i) + { + ecolor[i] = std::min(1.f, std::max(ecolor[i], 0.f)); + } + + color_table.add_point(position, ecolor); + } + else if (peg["type"].as_string() == "alpha") + { + float alpha = peg["alpha"].to_float32(); + alpha = std::min(1.f, std::max(alpha, 0.f)); + color_table.add_alpha(position, alpha); + } + else + { + std::cout<<"Unknown color table control point type " + << peg["type"].as_string() + << "\nValid types are 'alpha' and 'rgb'\n"; + } + } + } + else if (control_points_node.dtype().is_object()) + { + if (!control_points_node.has_child("r")) + { + std::cout<<"Color map control point must provide r values\n"; + } - float64 position = peg["position"].to_float64(); + if (!control_points_node.has_child("g")) + { + std::cout<<"Color map control point must provide g values\n"; + } - if(position > 1.0 || position < 0.0) - { - std::cout<<"Cannot add color map control point position " - << position - << ". Must be a normalized scalar.\n"; - } + if (!control_points_node.has_child("b")) + { + std::cout<<"Color map control point must provide b values\n"; + } - if (peg["type"].as_string() == "rgb") - { - conduit::Node n; - peg["color"].to_float32_array(n); - const float *color = n.as_float32_ptr(); + if (!control_points_node.has_child("position")) + { + std::cout<<"Color map control point must have a position\n"; + } + + conduit::Node r_vals_node; + control_points_node["r"].to_float32_array(r_vals_node); + float32_array r_vals = r_vals_node.as_float32_array(); + + conduit::Node g_vals_node; + control_points_node["g"].to_float32_array(g_vals_node); + float32_array g_vals = g_vals_node.as_float32_array(); - dray::Vec ecolor({color[0], color[1], color[2]}); + conduit::Node b_vals_node; + control_points_node["b"].to_float32_array(b_vals_node); + float32_array b_vals = b_vals_node.as_float32_array(); - for(int i = 0; i < 3; ++i) + float64_array pos_vals = control_points_node.fetch("position").value(); + + if (r_vals.number_of_elements() != g_vals.number_of_elements() || + g_vals.number_of_elements() != b_vals.number_of_elements() || + b_vals.number_of_elements() != pos_vals.number_of_elements()) { - ecolor[i] = std::min(1.f, std::max(ecolor[i], 0.f)); + std::cout<<"Color map color channels should all be of the same size\n"; } - color_table.add_point(position, ecolor); - } - else if (peg["type"].as_string() == "alpha") - { - float alpha = peg["alpha"].to_float32(); - alpha = std::min(1.f, std::max(alpha, 0.f)); - color_table.add_alpha(position, alpha); - } - else - { - std::cout<<"Unknown color table control point type " - << peg["type"].as_string() - << "\nValid types are 'alpha' and 'rgb'"; - } + for (index_t i=0; i ecolor({r_vals[i], g_vals[i], b_vals[i]}); + + for (int e = 0; e < 3; ++e) + { + ecolor[e] = std::min(1.f, std::max(ecolor[e], 0.f)); + } + + if (pos_vals[i] > 1.0 || pos_vals[i] < 0.0) + { + std::cout<<"Cannot add color map control point position " + << pos_vals[i] + << ". Must be a normalized scalar.\n"; + } + + color_table.add_point(pos_vals[i], ecolor); + } + + if (control_points_node.has_child("a")) + { + conduit::Node alpha_vals_node; + control_points_node["a"].to_float32_array(alpha_vals_node); + float32_array alpha_vals = alpha_vals_node.as_float32_array(); + + if (pos_vals.number_of_elements() != alpha_vals.number_of_elements()) + { + std::cout<<"Color map alpha channel should have same size as color channels\n"; + } + + for (index_t i=0; i