diff --git a/src/readybase/ImageRD.cpp b/src/readybase/ImageRD.cpp
index e9da18a0..9ef5c3f7 100644
--- a/src/readybase/ImageRD.cpp
+++ b/src/readybase/ImageRD.cpp
@@ -1,1713 +1,1720 @@
-/* Copyright 2011-2021 The Ready Bunch
-
- This file is part of Ready.
-
- Ready is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Ready is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Ready. If not, see . */
-
-// local:
-#include "ImageRD.hpp"
-#include "IO_XML.hpp"
-#include "overlays.hpp"
-#include "Properties.hpp"
-#include "scene_items.hpp"
-#include "utils.hpp"
-
-// STL:
-#include
-#include
-#include
-
-// VTK:
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using namespace std;
-
-// -------------------------------------------------------------------
-
-ImageRD::ImageRD(int data_type)
- : AbstractRD(data_type)
- , image_top1D(2.0)
- , image_ratio1D(30.0)
-{
- this->starting_pattern = vtkSmartPointer::New();
- this->assign_attribute_filter = NULL;
- this->rearrange_fields_filter = NULL;
-}
-
-// ---------------------------------------------------------------------
-
-ImageRD::~ImageRD()
-{
- this->DeallocateImages();
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::DeallocateImages()
-{
- this->images.clear();
- this->n_chemicals = 0;
-}
-
-// ---------------------------------------------------------------------
-
-int ImageRD::GetArenaDimensionality() const
-{
- assert(this->images.front());
- int dimensionality=0;
- for(int iDim=0;iDim<3;iDim++)
- if(this->images.front()->GetDimensions()[iDim]>1)
- dimensionality++;
- return dimensionality;
-}
-
-// ---------------------------------------------------------------------
-
-float ImageRD::GetX() const
-{
- return this->images.front()->GetDimensions()[0];
-}
-
-// ---------------------------------------------------------------------
-
-float ImageRD::GetY() const
-{
- return this->images.front()->GetDimensions()[1];
-}
-
-// ---------------------------------------------------------------------
-
-float ImageRD::GetZ() const
-{
- return this->images.front()->GetDimensions()[2];
-}
-
-// ---------------------------------------------------------------------
-
-vtkImageData* ImageRD::GetImage(int iChemical) const
-{
- return this->images[iChemical];
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::GetImage(vtkImageData *im) const
-{
- vtkSmartPointer iac = vtkSmartPointer::New();
- for(int i=0;iGetNumberOfChemicals();i++)
- {
- iac->AddInputData(this->GetImage(i));
- }
- iac->Update();
- im->DeepCopy(iac->GetOutput());
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::CopyFromImage(vtkImageData* im)
-{
- int n_arrays = im->GetPointData()->GetNumberOfArrays();
- int n_components = im->GetNumberOfScalarComponents();
-
- bool has_named_arrays = true;
- for(int iChem=0;iChemGetNumberOfChemicals();iChem++)
- {
- if(!im->GetPointData()->HasArray(GetChemicalName(iChem).c_str()))
- {
- has_named_arrays = false;
- break;
- }
- }
-
- if(has_named_arrays && n_components==1 && n_arrays==this->GetNumberOfChemicals())
- {
- // convert named array data to single-component data in multiple images
- for(int iChem=0;iChemGetNumberOfChemicals();iChem++)
- {
- this->images[iChem]->SetExtent(im->GetExtent());
- this->images[iChem]->GetPointData()->SetScalars(im->GetPointData()->GetArray(GetChemicalName(iChem).c_str()));
- }
- }
- else if(n_arrays==1 && n_components==this->GetNumberOfChemicals())
- {
- // convert multi-component data to single-component data in multiple images
- vtkSmartPointer iec = vtkSmartPointer::New();
- iec->SetInputData(im);
- for(int i=0;iGetNumberOfChemicals();i++)
- {
- iec->SetComponents(i);
- iec->Update();
- this->images[i]->DeepCopy(iec->GetOutput());
- }
- }
- else
- throw runtime_error("ImageRD::CopyFromImage : chemical count mismatch");
-
- this->undo_stack.clear();
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::CopyFromMesh(
- vtkUnstructuredGrid* mesh,
- const int num_chemicals,
- const size_t target_chemical,
- const size_t largest_dimension,
- const float value_inside,
- const float value_outside)
-{
- // decide the size of the image
- mesh->ComputeBounds();
- double bounds[6];
- mesh->GetBounds(bounds);
- const double mesh_size[3] = { bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4] };
- const float max_mesh_size = std::max(mesh_size[0], std::max(mesh_size[1], mesh_size[2]));
- const float scale = (largest_dimension-1) / max_mesh_size;
- int image_size[3];
- for(size_t xyz = 0; xyz < 3; ++xyz)
- {
- image_size[xyz] = 1;
- while( image_size[xyz] < mesh_size[xyz] * scale )
- {
- image_size[xyz] <<= 1;
- }
- }
- AllocateImages(image_size[0], image_size[1], image_size[2], num_chemicals, this->GetDataType());
- BlankImage(value_outside);
-
- // write data into the image
- vtkSmartPointer transform = vtkSmartPointer::New();
- transform->PostMultiply();
- transform->Translate(-(bounds[0] + bounds[1]) / 2.0, -(bounds[2] + bounds[3]) / 2.0, -(bounds[4] + bounds[5]) / 2.0); // center at origin
- transform->Scale(scale, scale, scale);
- transform->Translate(image_size[0] / 2, image_size[1] / 2, image_size[2] / 2); // center in volume
- vtkSmartPointer transform_filter = vtkSmartPointer::New();
- transform_filter->SetTransform(transform);
- transform_filter->SetInputData(mesh);
- vtkSmartPointer get_surface = vtkSmartPointer::New();
- get_surface->SetInputConnection(transform_filter->GetOutputPort());
- vtkSmartPointer pol2stenc = vtkSmartPointer::New();
- pol2stenc->SetInputConnection(get_surface->GetOutputPort());
- vtkSmartPointer imgstenc = vtkSmartPointer::New();
- imgstenc->SetInputData(this->images[target_chemical]);
- imgstenc->SetStencilConnection(pol2stenc->GetOutputPort());
- imgstenc->ReverseStencilOn();
- imgstenc->SetBackgroundValue(value_inside);
- imgstenc->Update();
- this->images[target_chemical]->DeepCopy(imgstenc->GetOutput());
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::AllocateImages(int x,int y,int z,int nc,int data_type)
-{
- this->DeallocateImages();
- this->n_chemicals = nc;
- this->images.resize(nc);
- for(int i=0;iimages[i] = AllocateVTKImage(x,y,z,data_type);
- this->is_modified = true;
- this->undo_stack.clear();
-}
-
-// ---------------------------------------------------------------------
-
-/* static */ vtkSmartPointer ImageRD::AllocateVTKImage(int x,int y,int z,int data_type)
-{
- vtkSmartPointer im = vtkSmartPointer::New();
- im->SetDimensions(x,y,z);
- im->AllocateScalars(data_type,1);
- if(im->GetDimensions()[0]!=x || im->GetDimensions()[1]!=y || im->GetDimensions()[2]!=z)
- throw runtime_error("ImageRD::AllocateVTKImage : Failed to allocate image data - dimensions too big?");
- return im;
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::GenerateInitialPattern()
-{
- if (this->initial_pattern_generator.ShouldZeroFirst()) {
- this->BlankImage();
- }
-
- const int X = this->images.front()->GetDimensions()[0];
- const int Y = this->images.front()->GetDimensions()[1];
- const int Z = this->images.front()->GetDimensions()[2];
-
- for (size_t iOverlay = 0; iOverlay < this->initial_pattern_generator.GetNumberOfOverlays(); iOverlay++)
- {
- this->initial_pattern_generator.GetOverlay(iOverlay).Reseed();
- }
-
- for(int z=0;zinitial_pattern_generator.GetNumberOfOverlays(); iOverlay++)
- {
- const Overlay& overlay = this->initial_pattern_generator.GetOverlay(iOverlay);
-
- int iC = overlay.GetTargetChemical();
- if(iC<0 || iC>=this->GetNumberOfChemicals())
- continue; // best for now to silently ignore this overlay, because the user has no way of editing the overlays (short of editing the file)
- //throw runtime_error("Overlay: chemical out of range: "+GetChemicalName(iC));
-
- vector vals(this->GetNumberOfChemicals());
- for(int i=0;iGetNumberOfChemicals();i++)
- vals[i] = this->GetImage(i)->GetScalarComponentAsDouble(x,y,z,0);
- this->GetImage(iC)->SetScalarComponentFromDouble(x, y, z, 0, overlay.Apply(vals, *this, x, y, z));
- }
- }
- }
- }
- for(int i=0;i<(int)this->images.size();i++)
- this->images[i]->Modified();
- this->timesteps_taken = 0;
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::BlankImage(float value)
-{
- for(int iImage=0;iImage<(int)this->images.size();iImage++)
- {
- this->images[iImage]->GetPointData()->GetScalars()->FillComponent(0, value);
- this->images[iImage]->Modified();
- }
- this->timesteps_taken = 0;
- this->undo_stack.clear();
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::Update(int n_steps)
-{
- this->undo_stack.clear();
- this->InternalUpdate(n_steps);
-
- this->timesteps_taken += n_steps;
-
- for(int ic=0;icGetNumberOfChemicals();ic++)
- this->images[ic]->Modified();
-
- if(this->rearrange_fields_filter && this->assign_attribute_filter)
- {
- // manually update the rendering pipeline to allow for changing array names
- // TODO: this step shouldn't be necessary
- this->rearrange_fields_filter->Update();
- assign_attribute_filter->Assign(this->rearrange_fields_filter->GetOutput()->GetCellData()->GetArray(0)->GetName(),
- vtkDataSetAttributes::SCALARS, vtkAssignAttribute::CELL_DATA);
- }
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::InitializeRenderPipeline(vtkRenderer* pRenderer,const Properties& render_settings)
-{
- this->rearrange_fields_filter = NULL;
- this->assign_attribute_filter = NULL;
-
- switch(this->GetArenaDimensionality())
- {
- // TODO: merge the dimensionalities (often want one/more slices from lower dimensionalities)
- case 0:
- case 1: this->InitializeVTKPipeline_1D(pRenderer,render_settings); break;
- case 2: this->InitializeVTKPipeline_2D(pRenderer,render_settings); break;
- case 3: this->InitializeVTKPipeline_3D(pRenderer,render_settings); break;
- default:
- throw runtime_error("ImageRD::InitializeRenderPipeline : Unsupported dimensionality");
- }
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::InitializeVTKPipeline_1D(vtkRenderer* pRenderer,const Properties& render_settings)
-{
- float low = render_settings.GetProperty("low").GetFloat();
- float high = render_settings.GetProperty("high").GetFloat();
- float vertical_scale_1D = render_settings.GetProperty("vertical_scale_1D").GetFloat();
- bool use_image_interpolation = render_settings.GetProperty("use_image_interpolation").GetBool();
- int iActiveChemical = IndexFromChemicalName(render_settings.GetProperty("active_chemical").GetChemical());
- bool show_multiple_chemicals = render_settings.GetProperty("show_multiple_chemicals").GetBool();
- bool show_color_scale = render_settings.GetProperty("show_color_scale").GetBool();
- bool show_cell_edges = render_settings.GetProperty("show_cell_edges").GetBool();
- bool show_chemical_label = render_settings.GetProperty("show_chemical_label").GetBool();
- bool show_phase_plot = render_settings.GetProperty("show_phase_plot").GetBool();
- int iPhasePlotX = IndexFromChemicalName(render_settings.GetProperty("phase_plot_x_axis").GetChemical());
- int iPhasePlotY = IndexFromChemicalName(render_settings.GetProperty("phase_plot_y_axis").GetChemical());
- int iPhasePlotZ = IndexFromChemicalName(render_settings.GetProperty("phase_plot_z_axis").GetChemical());
- bool plot_ab_orthogonally = render_settings.GetProperty("plot_ab_orthogonally").GetBool();
- if (plot_ab_orthogonally && this->GetNumberOfChemicals() <= 1)
- {
- plot_ab_orthogonally = false;
- }
-
- int iFirstChem = 0;
- int iLastChem = this->GetNumberOfChemicals() - 1;
- if(!show_multiple_chemicals)
- {
- if (plot_ab_orthogonally && iActiveChemical < 2)
- {
- iFirstChem = 0;
- iLastChem = 1;
- }
- else
- {
- iFirstChem = iActiveChemical;
- iLastChem = iFirstChem;
- }
- }
-
- float scaling = vertical_scale_1D / (high-low); // vertical_scale gives the height of the graph in worldspace units
- const float image_height = this->GetX() / this->image_ratio1D; // we thicken it
- const float y_gap = image_height;
-
- vtkSmartPointer lut = GetColorMap(render_settings);
-
- for(int iChem = iFirstChem; iChem <= iLastChem; ++iChem)
- {
- // pass the image through the lookup table
- vtkSmartPointer image_mapper = vtkSmartPointer::New();
- image_mapper->SetLookupTable(lut);
- image_mapper->SetInputData(this->GetImage(iChem));
-
- // will convert the x*y 2D image to a x*y grid of quads
- const float image_offset = this->image_top1D - image_height * 2.0f * (iChem - iFirstChem);
- vtkSmartPointer plane = vtkSmartPointer::New();
- plane->SetXResolution(this->GetX());
- plane->SetYResolution(this->GetY());
- plane->SetOrigin(0,image_offset-image_height,0);
- plane->SetPoint1(this->GetX(),image_offset-image_height,0);
- plane->SetPoint2(0,image_offset,0);
-
- vtkSmartPointer texture = vtkSmartPointer::New();
- texture->SetInputConnection(image_mapper->GetOutputPort());
- if(use_image_interpolation)
- texture->InterpolateOn();
- vtkSmartPointer mapper = vtkSmartPointer::New();
- mapper->SetInputConnection(plane->GetOutputPort());
-
- vtkSmartPointer actor = vtkSmartPointer::New();
- actor->SetMapper(mapper);
- actor->SetTexture(texture);
- if(show_cell_edges)
- actor->GetProperty()->EdgeVisibilityOn();
- actor->GetProperty()->SetEdgeColor(0,0,0);
- actor->GetProperty()->LightingOff();
- pRenderer->AddActor(actor);
-
- // add a text label
- if(show_chemical_label && this->GetNumberOfChemicals()>1)
- {
- vtkSmartPointer captionActor = vtkSmartPointer::New();
- captionActor->SetAttachmentPoint(-image_height, image_offset - image_height/2, 0);
- captionActor->SetPosition(0, 0);
- captionActor->SetCaption(GetChemicalName(iChem).c_str());
- captionActor->BorderOff();
- captionActor->LeaderOff();
- captionActor->SetPadding(0);
- captionActor->GetCaptionTextProperty()->SetJustificationToLeft();
- captionActor->GetCaptionTextProperty()->BoldOff();
- captionActor->GetCaptionTextProperty()->ShadowOff();
- captionActor->GetCaptionTextProperty()->ItalicOff();
- captionActor->GetCaptionTextProperty()->SetFontFamilyToArial();
- captionActor->GetCaptionTextProperty()->SetFontSize(16);
- captionActor->GetCaptionTextProperty()->SetVerticalJustificationToCentered();
- captionActor->GetTextActor()->SetTextScaleModeToNone();
- pRenderer->AddActor(captionActor);
- }
- }
-
- // also add a scalar bar to show how the colors correspond to values
- if(show_color_scale)
- {
- AddScalarBar(pRenderer,lut);
- }
-
- // add a line graph for all the chemicals (active one highlighted)
- const float graph_bottom = this->image_top1D + y_gap;
- const float graph_top = graph_bottom + (high-low) * scaling;
- for(int iChemical = iFirstChem; iChemical <= iLastChem; iChemical++)
- {
- if (plot_ab_orthogonally && iChemical == 1)
- {
- continue;
- }
- vtkSmartPointer mapper = vtkSmartPointer::New();
- vtkSmartPointer actor = vtkSmartPointer::New();
- if (plot_ab_orthogonally && iChemical == 0)
- {
- // plot the merged ab pair here
- vtkSmartPointer plane = vtkSmartPointer::New();
- plane->SetInputData(this->GetImage(0));
- vtkSmartPointer warp_y = vtkSmartPointer::New();
- warp_y->SetInputConnection(plane->GetOutputPort());
- warp_y->SetScaleFactor(scaling);
- warp_y->SetNormal(0, 1, 0);
- vtkSmartPointer plane2 = vtkSmartPointer::New();
- plane2->SetInputData(this->GetImage(1));
- vtkSmartPointer merge = vtkSmartPointer::New();
- merge->SetGeometryConnection(warp_y->GetOutputPort());
- merge->SetScalarsConnection(plane2->GetOutputPort());
- vtkSmartPointer warp_z = vtkSmartPointer::New();
- warp_z->SetInputConnection(merge->GetOutputPort());
- warp_z->SetScaleFactor(scaling);
- warp_z->SetNormal(0, 0, -1);
- vtkSmartPointer stripper = vtkSmartPointer::New();
- stripper->SetInputConnection(warp_z->GetOutputPort());
- vtkSmartPointer tube = vtkSmartPointer::New();
- tube->SetInputConnection(stripper->GetOutputPort());
- tube->SetRadius(this->GetX() / 512.0);
- tube->SetNumberOfSides(6);
- mapper->SetInputConnection(tube->GetOutputPort());
- actor->GetProperty()->SetColor(1, 1, 1);
- actor->GetProperty()->SetAmbient(0.3);
- }
- else
- {
- // plot this chemical in the normal way
- vtkSmartPointer plane = vtkSmartPointer::New();
- plane->SetInputData(this->GetImage(iChemical));
- vtkSmartPointer warp = vtkSmartPointer::New();
- warp->SetInputConnection(plane->GetOutputPort());
- warp->SetScaleFactor(scaling);
- warp->SetNormal(0, 1, 0);
- mapper->SetInputConnection(warp->GetOutputPort());
- actor->GetProperty()->SetAmbient(1);
- if (iChemical == iActiveChemical)
- actor->GetProperty()->SetColor(1, 1, 1);
- else
- actor->GetProperty()->SetColor(0.5, 0.5, 0.5);
- }
- mapper->ScalarVisibilityOff();
- actor->SetMapper(mapper);
- actor->PickableOff();
- actor->SetPosition(0, graph_bottom - low * scaling,0);
- pRenderer->AddActor(actor);
- }
-
- // add an axis
- vtkSmartPointer axis = vtkSmartPointer::New();
- axis->SetCamera(pRenderer->GetActiveCamera());
- axis->SetBounds(0,0,graph_bottom,graph_top,0,0);
- axis->SetRanges(0,0,low,high,0,0);
- axis->UseRangesOn();
- axis->XAxisVisibilityOff();
- axis->ZAxisVisibilityOff();
- axis->SetYLabel("");
- axis->SetLabelFormat("%.2f");
- axis->SetInertia(10000);
- axis->SetCornerOffset(0);
- axis->SetNumberOfLabels(5);
- axis->PickableOff();
- pRenderer->AddActor(axis);
- if (plot_ab_orthogonally)
- {
- axis->SetYLabel("a");
- vtkSmartPointer axis = vtkSmartPointer::New();
- axis->SetCamera(pRenderer->GetActiveCamera());
- axis->SetBounds(0, 0, graph_top, graph_top, -low * scaling, -high * scaling);
- axis->SetRanges(0, 0, 0, 0, low, high);
- axis->UseRangesOn();
- axis->XAxisVisibilityOff();
- axis->YAxisVisibilityOff();
- axis->SetZLabel("b");
- axis->SetLabelFormat("%.2f");
- axis->SetInertia(10000);
- axis->SetCornerOffset(0);
- axis->SetNumberOfLabels(5);
- axis->PickableOff();
- pRenderer->AddActor(axis);
- }
-
- // add a phase plot
- const float phase_plot_bottom = graph_top + y_gap*2;
- if(show_phase_plot && this->GetNumberOfChemicals()>=2)
- {
- this->AddPhasePlot(pRenderer,scaling,low,high,0.0f, phase_plot_bottom,0.0f,iPhasePlotX,iPhasePlotY,iPhasePlotZ);
- }
-}
-
-// ---------------------------------------------------------------------
-
-void ImageRD::InitializeVTKPipeline_2D(vtkRenderer* pRenderer,const Properties& render_settings)
-{
- float low = render_settings.GetProperty("low").GetFloat();
- float high = render_settings.GetProperty("high").GetFloat();
- float vertical_scale_2D = render_settings.GetProperty("vertical_scale_2D").GetFloat();
- bool use_image_interpolation = render_settings.GetProperty("use_image_interpolation").GetBool();
- int iActiveChemical = IndexFromChemicalName(render_settings.GetProperty("active_chemical").GetChemical());
- bool use_wireframe = render_settings.GetProperty("use_wireframe").GetBool();
- float surface_r,surface_g,surface_b;
- render_settings.GetProperty("surface_color").GetColor(surface_r,surface_g,surface_b);
- bool show_multiple_chemicals = render_settings.GetProperty("show_multiple_chemicals").GetBool();
- bool show_displacement_mapped_surface = render_settings.GetProperty("show_displacement_mapped_surface").GetBool();
- bool show_color_scale = render_settings.GetProperty("show_color_scale").GetBool();
- bool color_displacement_mapped_surface = render_settings.GetProperty("color_displacement_mapped_surface").GetBool();
- bool show_cell_edges = render_settings.GetProperty("show_cell_edges").GetBool();
- bool show_bounding_box = render_settings.GetProperty("show_bounding_box").GetBool();
- bool show_chemical_label = render_settings.GetProperty("show_chemical_label").GetBool();
- bool show_phase_plot = render_settings.GetProperty("show_phase_plot").GetBool();
- int iPhasePlotX = IndexFromChemicalName(render_settings.GetProperty("phase_plot_x_axis").GetChemical());
- int iPhasePlotY = IndexFromChemicalName(render_settings.GetProperty("phase_plot_y_axis").GetChemical());
- int iPhasePlotZ = IndexFromChemicalName(render_settings.GetProperty("phase_plot_z_axis").GetChemical());
-
- const float scaling = vertical_scale_2D / (high-low); // vertical_scale gives the height of the graph in worldspace units
- const float x_gap = this->x_spacing_proportion * this->GetX();
- const float y_gap = this->y_spacing_proportion * this->GetY();
-
- double offset[3] = {0,0,0};
- const float surface_bottom = offset[1] + 0.5 + this->GetY() + y_gap;
- const float surface_top = surface_bottom + this->GetY();
-
- int iFirstChem = 0;
- int iLastChem = this->GetNumberOfChemicals() - 1;
- if(!show_multiple_chemicals) { iFirstChem = iActiveChemical; iLastChem = iFirstChem; }
-
+/* Copyright 2011-2021 The Ready Bunch
+
+ This file is part of Ready.
+
+ Ready is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Ready is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Ready. If not, see . */
+
+// local:
+#include "ImageRD.hpp"
+#include "IO_XML.hpp"
+#include "overlays.hpp"
+#include "Properties.hpp"
+#include "scene_items.hpp"
+#include "utils.hpp"
+
+// STL:
+#include
+#include
+#include
+
+// VTK:
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+// -------------------------------------------------------------------
+
+ImageRD::ImageRD(int data_type)
+ : AbstractRD(data_type)
+ , image_top1D(2.0)
+ , image_ratio1D(30.0)
+{
+ this->starting_pattern = vtkSmartPointer::New();
+ this->assign_attribute_filter = NULL;
+ this->rearrange_fields_filter = NULL;
+}
+
+// ---------------------------------------------------------------------
+
+ImageRD::~ImageRD()
+{
+ this->DeallocateImages();
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::DeallocateImages()
+{
+ this->images.clear();
+ this->n_chemicals = 0;
+}
+
+// ---------------------------------------------------------------------
+
+int ImageRD::GetArenaDimensionality() const
+{
+ assert(this->images.front());
+ int dimensionality=0;
+ for(int iDim=0;iDim<3;iDim++)
+ if(this->images.front()->GetDimensions()[iDim]>1)
+ dimensionality++;
+ return dimensionality;
+}
+
+// ---------------------------------------------------------------------
+
+float ImageRD::GetX() const
+{
+ return this->images.front()->GetDimensions()[0];
+}
+
+// ---------------------------------------------------------------------
+
+float ImageRD::GetY() const
+{
+ return this->images.front()->GetDimensions()[1];
+}
+
+// ---------------------------------------------------------------------
+
+float ImageRD::GetZ() const
+{
+ return this->images.front()->GetDimensions()[2];
+}
+
+// ---------------------------------------------------------------------
+
+vtkImageData* ImageRD::GetImage(int iChemical) const
+{
+ return this->images[iChemical];
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::GetImage(vtkImageData *im) const
+{
+ vtkSmartPointer iac = vtkSmartPointer::New();
+ for(int i=0;iGetNumberOfChemicals();i++)
+ {
+ iac->AddInputData(this->GetImage(i));
+ }
+ iac->Update();
+ im->DeepCopy(iac->GetOutput());
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::CopyFromImage(vtkImageData* im)
+{
+ int n_arrays = im->GetPointData()->GetNumberOfArrays();
+ int n_components = im->GetNumberOfScalarComponents();
+
+ bool has_named_arrays = true;
+ for(int iChem=0;iChemGetNumberOfChemicals();iChem++)
+ {
+ if(!im->GetPointData()->HasArray(GetChemicalName(iChem).c_str()))
+ {
+ has_named_arrays = false;
+ break;
+ }
+ }
+
+ if(has_named_arrays && n_components==1 && n_arrays==this->GetNumberOfChemicals())
+ {
+ // convert named array data to single-component data in multiple images
+ for(int iChem=0;iChemGetNumberOfChemicals();iChem++)
+ {
+ this->images[iChem]->SetExtent(im->GetExtent());
+ this->images[iChem]->GetPointData()->SetScalars(im->GetPointData()->GetArray(GetChemicalName(iChem).c_str()));
+ }
+ }
+ else if(n_arrays==1 && n_components==this->GetNumberOfChemicals())
+ {
+ // convert multi-component data to single-component data in multiple images
+ vtkSmartPointer iec = vtkSmartPointer::New();
+ iec->SetInputData(im);
+ for(int i=0;iGetNumberOfChemicals();i++)
+ {
+ iec->SetComponents(i);
+ iec->Update();
+ this->images[i]->DeepCopy(iec->GetOutput());
+ }
+ }
+ else
+ throw runtime_error("ImageRD::CopyFromImage : chemical count mismatch");
+
+ this->undo_stack.clear();
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::CopyFromMesh(
+ vtkUnstructuredGrid* mesh,
+ const int num_chemicals,
+ const size_t target_chemical,
+ const size_t largest_dimension,
+ const float value_inside,
+ const float value_outside)
+{
+ // decide the size of the image
+ mesh->ComputeBounds();
+ double bounds[6];
+ mesh->GetBounds(bounds);
+ const double mesh_size[3] = { bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4] };
+ const float max_mesh_size = std::max(mesh_size[0], std::max(mesh_size[1], mesh_size[2]));
+ const float scale = (largest_dimension-1) / max_mesh_size;
+ int image_size[3];
+ for(size_t xyz = 0; xyz < 3; ++xyz)
+ {
+ image_size[xyz] = 1;
+ while( image_size[xyz] < mesh_size[xyz] * scale )
+ {
+ image_size[xyz] <<= 1;
+ }
+ }
+ AllocateImages(image_size[0], image_size[1], image_size[2], num_chemicals, this->GetDataType());
+ BlankImage(value_outside);
+
+ // write data into the image
+ vtkSmartPointer transform = vtkSmartPointer::New();
+ transform->PostMultiply();
+ transform->Translate(-(bounds[0] + bounds[1]) / 2.0, -(bounds[2] + bounds[3]) / 2.0, -(bounds[4] + bounds[5]) / 2.0); // center at origin
+ transform->Scale(scale, scale, scale);
+ transform->Translate(image_size[0] / 2, image_size[1] / 2, image_size[2] / 2); // center in volume
+ vtkSmartPointer transform_filter = vtkSmartPointer::New();
+ transform_filter->SetTransform(transform);
+ transform_filter->SetInputData(mesh);
+ vtkSmartPointer get_surface = vtkSmartPointer::New();
+ get_surface->SetInputConnection(transform_filter->GetOutputPort());
+ vtkSmartPointer pol2stenc = vtkSmartPointer::New();
+ pol2stenc->SetInputConnection(get_surface->GetOutputPort());
+ vtkSmartPointer imgstenc = vtkSmartPointer::New();
+ imgstenc->SetInputData(this->images[target_chemical]);
+ imgstenc->SetStencilConnection(pol2stenc->GetOutputPort());
+ imgstenc->ReverseStencilOn();
+ imgstenc->SetBackgroundValue(value_inside);
+ imgstenc->Update();
+ this->images[target_chemical]->DeepCopy(imgstenc->GetOutput());
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::AllocateImages(int x,int y,int z,int nc,int data_type)
+{
+ this->DeallocateImages();
+ this->n_chemicals = nc;
+ this->images.resize(nc);
+ for(int i=0;iimages[i] = AllocateVTKImage(x,y,z,data_type);
+ this->is_modified = true;
+ this->undo_stack.clear();
+}
+
+// ---------------------------------------------------------------------
+
+/* static */ vtkSmartPointer ImageRD::AllocateVTKImage(int x,int y,int z,int data_type)
+{
+ vtkSmartPointer im = vtkSmartPointer::New();
+ im->SetDimensions(x,y,z);
+ im->AllocateScalars(data_type,1);
+ if(im->GetDimensions()[0]!=x || im->GetDimensions()[1]!=y || im->GetDimensions()[2]!=z)
+ throw runtime_error("ImageRD::AllocateVTKImage : Failed to allocate image data - dimensions too big?");
+ return im;
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::GenerateInitialPattern()
+{
+ if (this->initial_pattern_generator.ShouldZeroFirst()) {
+ this->BlankImage();
+ }
+
+ const int X = this->images.front()->GetDimensions()[0];
+ const int Y = this->images.front()->GetDimensions()[1];
+ const int Z = this->images.front()->GetDimensions()[2];
+
+ for (size_t iOverlay = 0; iOverlay < this->initial_pattern_generator.GetNumberOfOverlays(); iOverlay++)
+ {
+ this->initial_pattern_generator.GetOverlay(iOverlay).Reseed();
+ }
+
+ for(int z=0;zinitial_pattern_generator.GetNumberOfOverlays(); iOverlay++)
+ {
+ const Overlay& overlay = this->initial_pattern_generator.GetOverlay(iOverlay);
+
+ int iC = overlay.GetTargetChemical();
+ if(iC<0 || iC>=this->GetNumberOfChemicals())
+ continue; // best for now to silently ignore this overlay, because the user has no way of editing the overlays (short of editing the file)
+ //throw runtime_error("Overlay: chemical out of range: "+GetChemicalName(iC));
+
+ vector vals(this->GetNumberOfChemicals());
+ for(int i=0;iGetNumberOfChemicals();i++)
+ vals[i] = this->GetImage(i)->GetScalarComponentAsDouble(x,y,z,0);
+ this->GetImage(iC)->SetScalarComponentFromDouble(x, y, z, 0, overlay.Apply(vals, *this, x, y, z));
+ }
+ }
+ }
+ }
+ for(int i=0;i<(int)this->images.size();i++)
+ this->images[i]->Modified();
+ this->timesteps_taken = 0;
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::BlankImage(float value)
+{
+ for(int iImage=0;iImage<(int)this->images.size();iImage++)
+ {
+ this->images[iImage]->GetPointData()->GetScalars()->FillComponent(0, value);
+ this->images[iImage]->Modified();
+ }
+ this->timesteps_taken = 0;
+ this->undo_stack.clear();
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::Update(int n_steps)
+{
+ this->undo_stack.clear();
+ this->InternalUpdate(n_steps);
+
+ this->timesteps_taken += n_steps;
+
+ for(int ic=0;icGetNumberOfChemicals();ic++)
+ this->images[ic]->Modified();
+
+ if(this->rearrange_fields_filter && this->assign_attribute_filter)
+ {
+ // manually update the rendering pipeline to allow for changing array names
+ // TODO: this step shouldn't be necessary
+ this->rearrange_fields_filter->Update();
+ assign_attribute_filter->Assign(this->rearrange_fields_filter->GetOutput()->GetCellData()->GetArray(0)->GetName(),
+ vtkDataSetAttributes::SCALARS, vtkAssignAttribute::CELL_DATA);
+ }
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::InitializeRenderPipeline(vtkRenderer* pRenderer,const Properties& render_settings)
+{
+ this->rearrange_fields_filter = NULL;
+ this->assign_attribute_filter = NULL;
+
+ switch(this->GetArenaDimensionality())
+ {
+ // TODO: merge the dimensionalities (often want one/more slices from lower dimensionalities)
+ case 0:
+ case 1: this->InitializeVTKPipeline_1D(pRenderer,render_settings); break;
+ case 2: this->InitializeVTKPipeline_2D(pRenderer,render_settings); break;
+ case 3: this->InitializeVTKPipeline_3D(pRenderer,render_settings); break;
+ default:
+ throw runtime_error("ImageRD::InitializeRenderPipeline : Unsupported dimensionality");
+ }
+}
+
+// ---------------------------------------------------------------------
+
+void ImageRD::InitializeVTKPipeline_1D(vtkRenderer* pRenderer,const Properties& render_settings)
+{
+ float low = render_settings.GetProperty("low").GetFloat();
+ float high = render_settings.GetProperty("high").GetFloat();
+ float vertical_scale_1D = render_settings.GetProperty("vertical_scale_1D").GetFloat();
+ bool use_image_interpolation = render_settings.GetProperty("use_image_interpolation").GetBool();
+ int iActiveChemical = IndexFromChemicalName(render_settings.GetProperty("active_chemical").GetChemical());
+ bool show_multiple_chemicals = render_settings.GetProperty("show_multiple_chemicals").GetBool();
+ bool show_color_scale = render_settings.GetProperty("show_color_scale").GetBool();
+ bool show_cell_edges = render_settings.GetProperty("show_cell_edges").GetBool();
+ bool show_chemical_label = render_settings.GetProperty("show_chemical_label").GetBool();
+ bool show_phase_plot = render_settings.GetProperty("show_phase_plot").GetBool();
+ int iPhasePlotX = IndexFromChemicalName(render_settings.GetProperty("phase_plot_x_axis").GetChemical());
+ int iPhasePlotY = IndexFromChemicalName(render_settings.GetProperty("phase_plot_y_axis").GetChemical());
+ int iPhasePlotZ = IndexFromChemicalName(render_settings.GetProperty("phase_plot_z_axis").GetChemical());
+ bool plot_ab_orthogonally = render_settings.GetProperty("plot_ab_orthogonally").GetBool();
+ if (plot_ab_orthogonally && this->GetNumberOfChemicals() <= 1)
+ {
+ plot_ab_orthogonally = false;
+ }
+
+ int iFirstChem = 0;
+ int iLastChem = this->GetNumberOfChemicals() - 1;
+ if(!show_multiple_chemicals)
+ {
+ if (plot_ab_orthogonally && iActiveChemical < 2)
+ {
+ iFirstChem = 0;
+ iLastChem = 1;
+ }
+ else
+ {
+ iFirstChem = iActiveChemical;
+ iLastChem = iFirstChem;
+ }
+ }
+
+ float scaling = vertical_scale_1D / (high-low); // vertical_scale gives the height of the graph in worldspace units
+ const float image_height = this->GetX() / this->image_ratio1D; // we thicken it
+ const float y_gap = image_height;
+
+ vtkSmartPointer lut = GetColorMap(render_settings);
+
+ for(int iChem = iFirstChem; iChem <= iLastChem; ++iChem)
+ {
+ // pass the image through the lookup table
+ vtkSmartPointer image_mapper = vtkSmartPointer::New();
+ image_mapper->SetLookupTable(lut);
+ image_mapper->SetInputData(this->GetImage(iChem));
+
+ // will convert the x*y 2D image to a x*y grid of quads
+ const float image_offset = this->image_top1D - image_height * 2.0f * (iChem - iFirstChem);
+ vtkSmartPointer plane = vtkSmartPointer::New();
+ plane->SetXResolution(this->GetX());
+ plane->SetYResolution(this->GetY());
+ plane->SetOrigin(0,image_offset-image_height,0);
+ plane->SetPoint1(this->GetX(),image_offset-image_height,0);
+ plane->SetPoint2(0,image_offset,0);
+
+ vtkSmartPointer texture = vtkSmartPointer::New();
+ texture->SetInputConnection(image_mapper->GetOutputPort());
+ if(use_image_interpolation)
+ texture->InterpolateOn();
+ vtkSmartPointer mapper = vtkSmartPointer::New();
+ mapper->SetInputConnection(plane->GetOutputPort());
+
+ vtkSmartPointer actor = vtkSmartPointer::New();
+ actor->SetMapper(mapper);
+ actor->SetTexture(texture);
+ if(show_cell_edges)
+ actor->GetProperty()->EdgeVisibilityOn();
+ actor->GetProperty()->SetEdgeColor(0,0,0);
+ actor->GetProperty()->LightingOff();
+ pRenderer->AddActor(actor);
+
+ // add a text label
+ if(show_chemical_label && this->GetNumberOfChemicals()>1)
+ {
+ vtkSmartPointer captionActor = vtkSmartPointer::New();
+ captionActor->SetAttachmentPoint(-image_height, image_offset - image_height/2, 0);
+ captionActor->SetPosition(0, 0);
+ captionActor->SetCaption(GetChemicalName(iChem).c_str());
+ captionActor->BorderOff();
+ captionActor->LeaderOff();
+ captionActor->SetPadding(0);
+ captionActor->GetCaptionTextProperty()->SetJustificationToLeft();
+ captionActor->GetCaptionTextProperty()->BoldOff();
+ captionActor->GetCaptionTextProperty()->ShadowOff();
+ captionActor->GetCaptionTextProperty()->ItalicOff();
+ captionActor->GetCaptionTextProperty()->SetFontFamilyToArial();
+ captionActor->GetCaptionTextProperty()->SetFontSize(16);
+ captionActor->GetCaptionTextProperty()->SetVerticalJustificationToCentered();
+ captionActor->GetTextActor()->SetTextScaleModeToNone();
+ pRenderer->AddActor(captionActor);
+ }
+ }
+
+ // also add a scalar bar to show how the colors correspond to values
+ if(show_color_scale)
+ {
+ AddScalarBar(pRenderer,lut);
+ }
+
+ // add a line graph for all the chemicals (active one highlighted)
+ const float graph_bottom = this->image_top1D + y_gap;
+ const float graph_top = graph_bottom + (high-low) * scaling;
+ for(int iChemical = iFirstChem; iChemical <= iLastChem; iChemical++)
+ {
+ if (plot_ab_orthogonally && iChemical == 1)
+ {
+ continue;
+ }
+ vtkSmartPointer mapper = vtkSmartPointer::New();
+ vtkSmartPointer actor = vtkSmartPointer::New();
+ if (plot_ab_orthogonally && iChemical == 0)
+ {
+ // plot the merged ab pair here
+ vtkSmartPointer plane = vtkSmartPointer::New();
+ plane->SetInputData(this->GetImage(0));
+ vtkSmartPointer warp_y = vtkSmartPointer::New();
+ warp_y->SetInputConnection(plane->GetOutputPort());
+ warp_y->SetScaleFactor(scaling);
+ warp_y->SetNormal(0, 1, 0);
+ vtkSmartPointer plane2 = vtkSmartPointer::New();
+ plane2->SetInputData(this->GetImage(1));
+ vtkSmartPointer merge = vtkSmartPointer::New();
+ merge->SetGeometryConnection(warp_y->GetOutputPort());
+ merge->SetScalarsConnection(plane2->GetOutputPort());
+ vtkSmartPointer