mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
1093 lines
30 KiB
C++
1093 lines
30 KiB
C++
/*=========================================================================
|
|
|
|
Program: ParaView
|
|
Module: $RCSfile$
|
|
|
|
Copyright (c) Kitware, Inc.
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkSMViewLayoutProxy.h"
|
|
|
|
#include "vtkClientServerStream.h"
|
|
#include "vtkCommand.h"
|
|
#include "vtkImageData.h"
|
|
#include "vtkMemberFunctionCommand.h"
|
|
#include "vtkNew.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPVXMLElement.h"
|
|
#include "vtkSmartPointer.h"
|
|
#include "vtkSMMessage.h"
|
|
#include "vtkSMPropertyHelper.h"
|
|
#include "vtkSMProxyIterator.h"
|
|
#include "vtkSMProxyLocator.h"
|
|
#include "vtkSMProxyProperty.h"
|
|
#include "vtkSMSession.h"
|
|
#include "vtkSMSessionProxyManager.h"
|
|
#include "vtkSMTrace.h"
|
|
#include "vtkSMUtilities.h"
|
|
#include "vtkSMViewProxy.h"
|
|
#include "vtkWeakPointer.h"
|
|
|
|
#include <assert.h>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
class vtkSMViewLayoutProxy::vtkInternals
|
|
{
|
|
public:
|
|
struct Cell
|
|
{
|
|
vtkSMViewLayoutProxy::Direction Direction;
|
|
double SplitFraction;
|
|
vtkWeakPointer<vtkSMViewProxy> ViewProxy;
|
|
|
|
Cell() :
|
|
Direction(vtkSMViewLayoutProxy::NONE),
|
|
SplitFraction(0.5)
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
bool IsCellValid(int location)
|
|
{
|
|
if (location < 0 ||
|
|
location >= static_cast<int>(this->KDTree.size()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (location == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// now verify that every parent node for location is a split cell.
|
|
int parent = (static_cast<int>(location) - 1) / 2;
|
|
while (this->KDTree[parent].Direction != vtkSMViewLayoutProxy::NONE)
|
|
{
|
|
if (parent == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
parent = (static_cast<int>(parent) - 1) / 2;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MoveSubtree(int destination, int source)
|
|
{
|
|
assert(destination >= 0 && source >= 0);
|
|
|
|
// we only support moving a subtree "up".
|
|
assert(destination < source);
|
|
|
|
if (source >= static_cast<int>(this->KDTree.size()) ||
|
|
destination >= static_cast<int>(this->KDTree.size()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Cell source_cell = this->KDTree[source];
|
|
this->KDTree[source] = Cell();
|
|
this->MoveSubtree(2*destination+1, 2*source+1);
|
|
this->MoveSubtree(2*destination+2, 2*source+2);
|
|
this->KDTree[destination] = source_cell;
|
|
}
|
|
|
|
void Shrink()
|
|
{
|
|
size_t max_index = this->GetMaxChildIndex(0);
|
|
assert(max_index < this->KDTree.size());
|
|
this->KDTree.resize(max_index + 1);
|
|
}
|
|
|
|
void UpdateViewPositions(int root=0, int posx=0, int posy=0)
|
|
{
|
|
if (root == 0)
|
|
{
|
|
this->Sizes.resize(this->KDTree.size() * 2);
|
|
this->ComputeSizes();
|
|
}
|
|
|
|
const Cell& cell = this->KDTree[root];
|
|
if (cell.Direction == vtkSMViewLayoutProxy::NONE)
|
|
{
|
|
if (cell.ViewProxy)
|
|
{
|
|
int pos[2] = { posx, posy };
|
|
vtkSMPropertyHelper(cell.ViewProxy, "ViewPosition").Set(pos, 2);
|
|
cell.ViewProxy->UpdateProperty("ViewPosition");
|
|
}
|
|
//cout << "View Position: " << cell.ViewProxy << " = "
|
|
// << posx << "," << posy << endl;
|
|
}
|
|
else
|
|
{
|
|
// root is a split-cell. Determine sizes for the two children.
|
|
const int *size = &this->Sizes[2* (2*root+1)];
|
|
|
|
if (cell.Direction == vtkSMViewLayoutProxy::HORIZONTAL)
|
|
{
|
|
this->UpdateViewPositions(2*root+1, posx, posy);
|
|
this->UpdateViewPositions(2*root+2, posx + size[0], posy);
|
|
}
|
|
else // cell.Direction == VERTICAL
|
|
{
|
|
this->UpdateViewPositions(2*root+1, posx, posy);
|
|
this->UpdateViewPositions(2*root+2, posx, posy + size[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef std::vector<Cell> KDTreeType;
|
|
KDTreeType KDTree;
|
|
vtkCommand* Observer;
|
|
|
|
private:
|
|
size_t GetMaxChildIndex(size_t parent)
|
|
{
|
|
if (this->KDTree[parent].Direction == vtkSMViewLayoutProxy::NONE)
|
|
{
|
|
return parent;
|
|
}
|
|
|
|
size_t child1 = this->GetMaxChildIndex(2*parent + 1);
|
|
size_t child2 = this->GetMaxChildIndex(2*parent + 2);
|
|
return std::max(child1, child2);
|
|
}
|
|
|
|
// temporary vector uses by ComputeSizes() and allocated by
|
|
// UpdateViewPositions().
|
|
std::vector<int> Sizes;
|
|
|
|
const int *ComputeSizes(int root=0)
|
|
{
|
|
assert(2*root+1 < static_cast<int>(this->Sizes.size()));
|
|
|
|
const Cell& cell = this->KDTree[root];
|
|
if (cell.Direction == vtkSMViewLayoutProxy::NONE)
|
|
{
|
|
int size[2] = {0, 0};
|
|
if (cell.ViewProxy)
|
|
{
|
|
vtkSMPropertyHelper(cell.ViewProxy, "ViewSize").Get(size, 2);
|
|
}
|
|
this->Sizes[2*root] = size[0];
|
|
this->Sizes[2*root + 1] = size[1];
|
|
return &this->Sizes[2*root];
|
|
}
|
|
|
|
const int *size0 = this->ComputeSizes(2*root+1);
|
|
const int *size1 = this->ComputeSizes(2*root+2);
|
|
|
|
// now double the width (or height) based on the split direction.
|
|
if (cell.Direction == vtkSMViewLayoutProxy::HORIZONTAL)
|
|
{
|
|
this->Sizes[2*root] = size0[0] + size1[0];
|
|
this->Sizes[2*root + 1] = std::max(size0[1], size1[1]);
|
|
}
|
|
else
|
|
{
|
|
this->Sizes[2*root] = std::max(size0[0], size1[0]);
|
|
this->Sizes[2*root + 1] = size0[1] + size1[1];
|
|
}
|
|
return &this->Sizes[2*root];
|
|
}
|
|
};
|
|
|
|
//============================================================================
|
|
double vtkSMViewLayoutProxy::MultiViewImageBorderColor[3] = {0.0, 0.0, 0.0};
|
|
int vtkSMViewLayoutProxy::MultiViewImageBorderWidth = 0;
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::SetMultiViewImageBorderColor(double r, double g, double b)
|
|
{
|
|
vtkSMViewLayoutProxy::MultiViewImageBorderColor[0] = std::max(0.0, std::min(1.0, r));
|
|
vtkSMViewLayoutProxy::MultiViewImageBorderColor[1] = std::max(0.0, std::min(1.0, g));
|
|
vtkSMViewLayoutProxy::MultiViewImageBorderColor[2] = std::max(0.0, std::min(1.0, b));
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::SetMultiViewImageBorderWidth(int width)
|
|
{
|
|
vtkSMViewLayoutProxy::MultiViewImageBorderWidth = std::max(0, width);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const double* vtkSMViewLayoutProxy::GetMultiViewImageBorderColor()
|
|
{
|
|
return vtkSMViewLayoutProxy::MultiViewImageBorderColor;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkSMViewLayoutProxy::GetMultiViewImageBorderWidth()
|
|
{
|
|
return vtkSMViewLayoutProxy::MultiViewImageBorderWidth;
|
|
}
|
|
|
|
//============================================================================
|
|
vtkStandardNewMacro(vtkSMViewLayoutProxy);
|
|
//----------------------------------------------------------------------------
|
|
vtkSMViewLayoutProxy::vtkSMViewLayoutProxy() :
|
|
MaximizedCell(-1),
|
|
Internals(new vtkInternals()),
|
|
BlockUpdate(false)
|
|
{
|
|
this->Internals->Observer = vtkMakeMemberFunctionCommand(
|
|
*this, &vtkSMViewLayoutProxy::UpdateViewPositions);
|
|
|
|
// Push the root element.
|
|
this->Internals->KDTree.resize(1);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkSMViewLayoutProxy::~vtkSMViewLayoutProxy()
|
|
{
|
|
vtkMemberFunctionCommand<vtkSMViewLayoutProxy>::SafeDownCast(
|
|
this->Internals->Observer)->Reset();
|
|
this->Internals->Observer->Delete();
|
|
this->Internals->Observer = NULL;
|
|
delete this->Internals;
|
|
this->Internals = NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::Reset()
|
|
{
|
|
this->Internals->KDTree.clear();
|
|
this->Internals->KDTree.resize(1);
|
|
this->UpdateState();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::LoadState(
|
|
const vtkSMMessage* message, vtkSMProxyLocator* locator)
|
|
{
|
|
this->Superclass::LoadState(message, locator);
|
|
|
|
if (message->ExtensionSize(ProxyState::user_data) != 1)
|
|
{
|
|
// vtkWarningMacro("Missing ViewLayoutState");
|
|
return;
|
|
}
|
|
|
|
const ProxyState_UserData& user_data =
|
|
message->GetExtension(ProxyState::user_data, 0);
|
|
if (user_data.key() != "ViewLayoutState")
|
|
{
|
|
//vtkWarningMacro("Unexpected user_data. Expecting ViewLayoutState.");
|
|
return;
|
|
}
|
|
|
|
this->Internals->KDTree.clear();
|
|
this->Internals->KDTree.resize(user_data.variant_size());
|
|
|
|
for (int cc=0; cc < user_data.variant_size(); cc++)
|
|
{
|
|
vtkInternals::Cell &cell = this->Internals->KDTree[cc];
|
|
const Variant& value = user_data.variant(cc);
|
|
|
|
cell.SplitFraction = value.float64(0);
|
|
|
|
switch (value.integer(0))
|
|
{
|
|
case HORIZONTAL:
|
|
cell.Direction = HORIZONTAL;
|
|
break;
|
|
|
|
case VERTICAL:
|
|
cell.Direction = VERTICAL;
|
|
break;
|
|
|
|
case NONE:
|
|
default:
|
|
cell.Direction = NONE;
|
|
}
|
|
|
|
if (locator && vtkSMProxyProperty::CanCreateProxy())
|
|
{
|
|
cell.ViewProxy = locator->LocateProxy(value.proxy_global_id(0));
|
|
}
|
|
else
|
|
{
|
|
cell.ViewProxy = vtkSMViewProxy::SafeDownCast(
|
|
this->GetSession()->GetRemoteObject(value.proxy_global_id(0)));
|
|
}
|
|
}
|
|
|
|
// let the world know that the layout has been reconfigured.
|
|
this->InvokeEvent(vtkCommand::ConfigureEvent);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::UpdateState()
|
|
{
|
|
if (this->BlockUpdate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// ensure that the state is created correctly.
|
|
this->CreateVTKObjects();
|
|
|
|
// push current state.
|
|
this->State->ClearExtension(ProxyState::user_data);
|
|
|
|
ProxyState_UserData* user_data =
|
|
this->State->AddExtension(ProxyState::user_data);
|
|
user_data->set_key("ViewLayoutState");
|
|
|
|
for (size_t cc=0; cc < this->Internals->KDTree.size(); cc++)
|
|
{
|
|
const vtkInternals::Cell &cell = this->Internals->KDTree[cc];
|
|
|
|
Variant* variant = user_data->add_variant();
|
|
variant->set_type(Variant::INT); // type is just arbitrary here.
|
|
variant->add_integer(cell.Direction);
|
|
variant->add_float64(cell.SplitFraction);
|
|
variant->add_proxy_global_id(
|
|
cell.ViewProxy? cell.ViewProxy->GetGlobalID() : 0);
|
|
}
|
|
|
|
this->PushState(this->State);
|
|
this->InvokeEvent(vtkCommand::ConfigureEvent);
|
|
|
|
this->UpdateViewPositions();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkPVXMLElement* vtkSMViewLayoutProxy::SaveXMLState(
|
|
vtkPVXMLElement* root, vtkSMPropertyIterator* iter)
|
|
{
|
|
vtkPVXMLElement* element = this->Superclass::SaveXMLState(root, iter);
|
|
if (!element)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
vtkPVXMLElement* layout = vtkPVXMLElement::New();
|
|
layout->SetName("Layout");
|
|
layout->AddAttribute("number_of_elements",
|
|
static_cast<int>(this->Internals->KDTree.size()));
|
|
element->AddNestedElement(layout);
|
|
layout->Delete();
|
|
|
|
for (size_t cc=0; cc < this->Internals->KDTree.size(); cc++)
|
|
{
|
|
const vtkInternals::Cell &cell = this->Internals->KDTree[cc];
|
|
|
|
vtkPVXMLElement* item = vtkPVXMLElement::New();
|
|
item->SetName("Item");
|
|
item->AddAttribute("direction", static_cast<int>(cell.Direction));
|
|
item->AddAttribute("fraction", cell.SplitFraction);
|
|
item->AddAttribute("view", (cell.ViewProxy?
|
|
static_cast<unsigned int>(cell.ViewProxy->GetGlobalID()) : 0));
|
|
layout->AddNestedElement(item);
|
|
item->Delete();
|
|
}
|
|
|
|
return element;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkSMViewLayoutProxy::LoadXMLState(
|
|
vtkPVXMLElement* element, vtkSMProxyLocator* locator)
|
|
{
|
|
if (!this->Superclass::LoadXMLState(element, locator))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (!locator)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
vtkPVXMLElement* layout = element->FindNestedElementByName("Layout");
|
|
int number_of_elements = 0;
|
|
if (!layout->GetScalarAttribute("number_of_elements", &number_of_elements) ||
|
|
(number_of_elements <= 0))
|
|
{
|
|
vtkErrorMacro("Missing (or invalid) 'number_of_elements' attribute.");
|
|
return 0;
|
|
}
|
|
|
|
if (static_cast<int>(layout->GetNumberOfNestedElements()) != number_of_elements)
|
|
{
|
|
vtkErrorMacro("Mismatch in number_of_elements and nested elements.");
|
|
return 0;
|
|
}
|
|
|
|
this->Internals->KDTree.clear();
|
|
this->Internals->KDTree.resize(number_of_elements);
|
|
for (unsigned int cc = 0; cc < layout->GetNumberOfNestedElements(); cc++)
|
|
{
|
|
vtkPVXMLElement* item = layout->GetNestedElement(cc);
|
|
if (item == NULL || item->GetName() == NULL ||
|
|
strcmp(item->GetName(), "Item") != 0)
|
|
{
|
|
vtkErrorMacro("Invalid nested element at index : " << cc);
|
|
return 0;
|
|
}
|
|
int direction, viewid;
|
|
double fraction;
|
|
if (!item->GetScalarAttribute("direction", &direction) ||
|
|
!item->GetScalarAttribute("fraction", &fraction) ||
|
|
!item->GetScalarAttribute("view", &viewid))
|
|
{
|
|
vtkErrorMacro("Invalid nested element at index : " << cc);
|
|
return 0;
|
|
}
|
|
vtkInternals::Cell &cell = this->Internals->KDTree[cc];
|
|
cell.Direction = ((direction == NONE)? NONE : (
|
|
(direction == HORIZONTAL)? HORIZONTAL : VERTICAL));
|
|
cell.SplitFraction = fraction;
|
|
if (viewid)
|
|
{
|
|
cell.ViewProxy = locator->LocateProxy(viewid);
|
|
}
|
|
else
|
|
{
|
|
cell.ViewProxy = NULL;
|
|
}
|
|
}
|
|
|
|
this->UpdateViewPositions();
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkSMViewLayoutProxy::Split(int location, int direction, double fraction)
|
|
{
|
|
if (!this->Internals->IsCellValid(location))
|
|
{
|
|
vtkErrorMacro("Invalid location '" << location << "' specified.");
|
|
return 0;
|
|
}
|
|
|
|
// we don't get by value, since we resize the vector.
|
|
vtkInternals::Cell cell = this->Internals->KDTree[location];
|
|
if (cell.Direction != NONE)
|
|
{
|
|
vtkErrorMacro("Cell identified by location '" << location
|
|
<< "' is already split. Cannot split the cell again.");
|
|
return 0;
|
|
}
|
|
|
|
if (direction <= NONE || direction > HORIZONTAL)
|
|
{
|
|
vtkErrorMacro("Invalid direction : " << direction);
|
|
return 0;
|
|
}
|
|
|
|
if (fraction < 0.0 || fraction > 1.0)
|
|
{
|
|
vtkErrorMacro("Invalid fraction : " << fraction
|
|
<< ". Must be in the range [0, 1]");
|
|
return 0;
|
|
}
|
|
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg(direction == VERTICAL? "SplitVertical" : "SplitHorizontal")
|
|
.arg(location)
|
|
.arg(fraction)
|
|
.arg("comment", "split cell");
|
|
|
|
cell.Direction = (direction == VERTICAL)? VERTICAL : HORIZONTAL;
|
|
cell.SplitFraction = fraction;
|
|
|
|
// ensure that both the children (2i+1), (2i+2) can fit in the KDTree.
|
|
if ( (2*location + 2) >= static_cast<int>(this->Internals->KDTree.size()))
|
|
{
|
|
this->Internals->KDTree.resize(2*location + 2 + 1);
|
|
}
|
|
|
|
int child_location = (2*location + 1);
|
|
if (cell.ViewProxy)
|
|
{
|
|
vtkInternals::Cell &child = this->Internals->KDTree[child_location];
|
|
child.ViewProxy = cell.ViewProxy;
|
|
cell.ViewProxy = NULL;
|
|
}
|
|
this->Internals->KDTree[location] = cell;
|
|
this->MaximizedCell = -1;
|
|
this->UpdateState();
|
|
return child_location;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMViewLayoutProxy::AssignView(int location, vtkSMViewProxy* view)
|
|
{
|
|
if (view == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!this->Internals->IsCellValid(location))
|
|
{
|
|
vtkErrorMacro("Invalid location '" << location << "' specified.");
|
|
return false;
|
|
}
|
|
|
|
vtkInternals::Cell &cell = this->Internals->KDTree[location];
|
|
if (cell.Direction != NONE)
|
|
{
|
|
vtkErrorMacro("Cell is not a leaf '" << location << "'."
|
|
" Cannot assign a view to it.");
|
|
return false;
|
|
}
|
|
|
|
if (cell.ViewProxy != NULL && cell.ViewProxy != view)
|
|
{
|
|
vtkErrorMacro("Cell is not empty.");
|
|
return false;
|
|
}
|
|
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg("AssignView")
|
|
.arg(location)
|
|
.arg(view)
|
|
.arg("assign view to a paricular cell in the layout");
|
|
|
|
if (cell.ViewProxy == view)
|
|
{
|
|
// nothing to do.
|
|
return true;
|
|
}
|
|
|
|
cell.ViewProxy = view;
|
|
|
|
if (view->GetProperty("ViewSize"))
|
|
{
|
|
// every time view-size changes, we update the view positions for all views.
|
|
view->GetProperty("ViewSize")->AddObserver(
|
|
vtkCommand::ModifiedEvent, this->Internals->Observer);
|
|
}
|
|
|
|
this->UpdateState();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkSMViewLayoutProxy::AssignViewToAnyCell(
|
|
vtkSMViewProxy* view, int location_hint)
|
|
{
|
|
if (!view)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int cur_location = this->GetViewLocation(view);
|
|
if (cur_location != -1)
|
|
{
|
|
// already assigned to a frame. return that index.
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg("AssignView")
|
|
.arg(cur_location)
|
|
.arg(view)
|
|
.arg("comment", "place view in the layout");
|
|
return cur_location;
|
|
}
|
|
|
|
if (location_hint < 0)
|
|
{
|
|
location_hint = 0;
|
|
}
|
|
|
|
// If location_hint refers to an empty cell, use it.
|
|
if (this->Internals->IsCellValid(location_hint))
|
|
{
|
|
int empty_cell = this->GetEmptyCell(location_hint);
|
|
if (empty_cell >= 0)
|
|
{
|
|
return this->AssignView(empty_cell, view);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// make location_hint a valid location.
|
|
location_hint = 0;
|
|
}
|
|
|
|
// Find any empty cell.
|
|
int empty_cell = this->GetEmptyCell(0);
|
|
if (empty_cell >= 0)
|
|
{
|
|
return this->AssignView(empty_cell, view);
|
|
}
|
|
|
|
// No empty cell found, split a view, starting with the location_hint.
|
|
Direction direction = HORIZONTAL;
|
|
|
|
int parent = this->GetParent(location_hint);
|
|
if (parent >= 0)
|
|
{
|
|
direction = this->GetSplitDirection(parent) == HORIZONTAL?
|
|
VERTICAL: HORIZONTAL;
|
|
}
|
|
int split_cell = this->GetSplittableCell(location_hint, direction);
|
|
assert(split_cell >= 0);
|
|
|
|
bool prev = this->SetBlockUpdate(true);
|
|
int new_cell = this->Split(split_cell, direction, 0.5);
|
|
this->SetBlockUpdate(prev);
|
|
|
|
if (this->GetView(new_cell) == NULL)
|
|
{
|
|
return this->AssignView(new_cell, view);
|
|
}
|
|
|
|
return this->AssignView(new_cell + 1, view);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMViewLayoutProxy::RemoveView(int index)
|
|
{
|
|
return this->RemoveView(this->GetView(index)) != -1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkSMViewLayoutProxy::RemoveView(vtkSMViewProxy* view)
|
|
{
|
|
if (!view)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int index = 0;
|
|
for (vtkInternals::KDTreeType::iterator iter =
|
|
this->Internals->KDTree.begin();
|
|
iter != this->Internals->KDTree.end(); ++iter, ++index)
|
|
{
|
|
if (iter->ViewProxy == view)
|
|
{
|
|
if (iter->ViewProxy->GetProperty("ViewSize"))
|
|
{
|
|
iter->ViewProxy->GetProperty("ViewSize")->RemoveObserver(
|
|
this->Internals->Observer);
|
|
}
|
|
iter->ViewProxy = NULL;
|
|
this->UpdateState();
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMViewLayoutProxy::SwapCells(int location1, int location2)
|
|
{
|
|
if (!this->Internals->IsCellValid(location1) ||
|
|
!this->Internals->IsCellValid(location2))
|
|
{
|
|
vtkErrorMacro("Invalid locations specified.");
|
|
return false;
|
|
}
|
|
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg("SwapCells")
|
|
.arg(location1)
|
|
.arg(location2)
|
|
.arg("comment", "swap view locations");
|
|
|
|
vtkInternals::Cell &cell1 = this->Internals->KDTree[location1];
|
|
vtkInternals::Cell &cell2 = this->Internals->KDTree[location2];
|
|
if (cell1.Direction == NONE && cell2.Direction == NONE)
|
|
{
|
|
vtkSMViewProxy* temp1 = cell1.ViewProxy;
|
|
cell1.ViewProxy = cell2.ViewProxy;
|
|
cell2.ViewProxy = temp1;
|
|
this->UpdateState();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMViewLayoutProxy::Collapse(int location)
|
|
{
|
|
if (!this->Internals->IsCellValid(location))
|
|
{
|
|
vtkErrorMacro("Invalid location '" << location << "' specified.");
|
|
return false;
|
|
}
|
|
|
|
vtkInternals::Cell &cell = this->Internals->KDTree[location];
|
|
if (cell.Direction != NONE)
|
|
{
|
|
vtkErrorMacro("Only leaf cells can be collapsed.");
|
|
return false;
|
|
}
|
|
|
|
if (cell.ViewProxy != NULL)
|
|
{
|
|
vtkErrorMacro("Only empty cells can be collapsed.");
|
|
return false;
|
|
}
|
|
|
|
if (location == 0)
|
|
{
|
|
// sure, trying to collapse the root node...whatever!!!
|
|
return true;
|
|
}
|
|
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg("Collapse")
|
|
.arg(location)
|
|
.arg("comment", "close an empty frame");
|
|
|
|
int parent = (location - 1) / 2;
|
|
int sibling = ((location % 2) == 0)? (2*parent + 1) : (2*parent + 2);
|
|
|
|
this->Internals->MoveSubtree(parent, sibling);
|
|
this->Internals->Shrink();
|
|
this->MaximizedCell = -1;
|
|
this->UpdateState();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMViewLayoutProxy::IsSplitCell(int location)
|
|
{
|
|
if (!this->Internals->IsCellValid(location))
|
|
{
|
|
vtkErrorMacro("Invalid location '" << location << "' specified.");
|
|
return false;
|
|
}
|
|
|
|
const vtkInternals::Cell &cell = this->Internals->KDTree[location];
|
|
return (cell.Direction != NONE);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkSMViewLayoutProxy::GetEmptyCell(int root)
|
|
{
|
|
vtkInternals::Cell &cell = this->Internals->KDTree[root];
|
|
if (cell.Direction == NONE && cell.ViewProxy == NULL)
|
|
{
|
|
return root;
|
|
}
|
|
else if (cell.Direction == HORIZONTAL ||
|
|
cell.Direction == VERTICAL)
|
|
{
|
|
int child0 = 2*root + 1;
|
|
int empty_cell = this->GetEmptyCell(child0);
|
|
if (empty_cell >= 0)
|
|
{
|
|
return empty_cell;
|
|
}
|
|
int child1 = 2*root + 2;
|
|
empty_cell = this->GetEmptyCell(child1);
|
|
if (empty_cell >= 0)
|
|
{
|
|
return empty_cell;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkSMViewLayoutProxy::GetSplittableCell(int root,
|
|
vtkSMViewLayoutProxy::Direction& suggested_direction)
|
|
{
|
|
vtkInternals::Cell &cell = this->Internals->KDTree[root];
|
|
if (cell.Direction == NONE)
|
|
{
|
|
return root;
|
|
}
|
|
else if (cell.Direction == HORIZONTAL ||
|
|
cell.Direction == VERTICAL)
|
|
{
|
|
suggested_direction = cell.Direction == HORIZONTAL?
|
|
VERTICAL : HORIZONTAL;
|
|
int child0 = 2*root + 1;
|
|
return this->GetSplittableCell(child0, suggested_direction);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkSMViewLayoutProxy::Direction vtkSMViewLayoutProxy::GetSplitDirection(
|
|
int location)
|
|
{
|
|
if (!this->Internals->IsCellValid(location))
|
|
{
|
|
vtkErrorMacro("Invalid location '" << location << "' specified.");
|
|
return NONE;
|
|
}
|
|
|
|
return this->Internals->KDTree[location].Direction;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
double vtkSMViewLayoutProxy::GetSplitFraction(int location)
|
|
{
|
|
if (!this->Internals->IsCellValid(location))
|
|
{
|
|
vtkErrorMacro("Invalid location '" << location << "' specified.");
|
|
return NONE;
|
|
}
|
|
|
|
return this->Internals->KDTree[location].SplitFraction;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkSMViewProxy* vtkSMViewLayoutProxy::GetView(int location)
|
|
{
|
|
if (!this->Internals->IsCellValid(location))
|
|
{
|
|
vtkErrorMacro("Invalid location '" << location << "' specified.");
|
|
return NULL;
|
|
}
|
|
|
|
return this->Internals->KDTree[location].ViewProxy;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkSMViewLayoutProxy::GetViewLocation(vtkSMViewProxy* view)
|
|
{
|
|
int index = 0;
|
|
for (vtkInternals::KDTreeType::iterator iter =
|
|
this->Internals->KDTree.begin();
|
|
iter != this->Internals->KDTree.end(); ++iter, ++index)
|
|
{
|
|
if (iter->ViewProxy == view)
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMViewLayoutProxy::SetSplitFraction(int location, double val)
|
|
{
|
|
if (val < 0.0 || val > 1.0)
|
|
{
|
|
vtkErrorMacro("Invalid fraction : " << val
|
|
<< ". Must be in the range [0, 1]");
|
|
return 0;
|
|
}
|
|
|
|
if (!this->IsSplitCell(location))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg("SetSplitFraction")
|
|
.arg(location)
|
|
.arg(val)
|
|
.arg("comment", "resize frame");
|
|
|
|
if (this->Internals->KDTree[location].SplitFraction != val)
|
|
{
|
|
this->Internals->KDTree[location].SplitFraction = val;
|
|
this->MaximizedCell = -1;
|
|
this->UpdateState();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::UpdateViewPositions()
|
|
{
|
|
if (this->MaximizedCell == -1)
|
|
{
|
|
this->Internals->UpdateViewPositions();
|
|
}
|
|
else
|
|
{
|
|
// simply set all ViewPositions to 0.
|
|
for (vtkInternals::KDTreeType::iterator iter =
|
|
this->Internals->KDTree.begin();
|
|
iter != this->Internals->KDTree.end();
|
|
++iter)
|
|
{
|
|
if (iter->ViewProxy.GetPointer() != NULL)
|
|
{
|
|
int pos[2] = {0, 0};
|
|
vtkSMPropertyHelper(iter->ViewProxy, "ViewPosition").Set(pos, 2);
|
|
iter->ViewProxy->UpdateProperty("ViewPosition");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::ShowViewsOnTileDisplay()
|
|
{
|
|
this->CreateVTKObjects();
|
|
|
|
vtkSMProxy* proxy = this->GetSessionProxyManager()->NewProxy(
|
|
"misc", "InternalViewLayoutHelper");
|
|
proxy->UpdateVTKObjects();
|
|
|
|
vtkClientServerStream stream;
|
|
stream << vtkClientServerStream::Invoke
|
|
<< VTKOBJECT(proxy)
|
|
<< "ResetTileDisplay"
|
|
<< vtkClientServerStream::End;
|
|
for (vtkInternals::KDTreeType::iterator iter =
|
|
this->Internals->KDTree.begin();
|
|
iter != this->Internals->KDTree.end();
|
|
++iter)
|
|
{
|
|
if (iter->ViewProxy.GetPointer() != NULL)
|
|
{
|
|
stream << vtkClientServerStream::Invoke
|
|
<< VTKOBJECT(proxy)
|
|
<< "ShowOnTileDisplay"
|
|
<< static_cast<unsigned int>(iter->ViewProxy->GetGlobalID())
|
|
<< vtkClientServerStream::End;
|
|
}
|
|
}
|
|
this->GetSession()->ExecuteStream(
|
|
proxy->GetLocation(), stream, false);
|
|
proxy->Delete();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMViewLayoutProxy::MaximizeCell(int location)
|
|
{
|
|
if (this->Internals->IsCellValid(location) == false ||
|
|
this->IsSplitCell(location))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
this->MaximizedCell = location;
|
|
this->UpdateState();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::RestoreMaximizedState()
|
|
{
|
|
if (this->MaximizedCell != -1)
|
|
{
|
|
this->MaximizedCell = -1;
|
|
this->UpdateState();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageData* vtkSMViewLayoutProxy::CaptureWindow(int magnification)
|
|
{
|
|
if (this->MaximizedCell != -1)
|
|
{
|
|
vtkSMViewProxy* view = this->GetView(this->MaximizedCell);
|
|
if (view)
|
|
{
|
|
return view->CaptureWindow(magnification);
|
|
}
|
|
vtkErrorMacro("No view present in the layout.");
|
|
return NULL;
|
|
}
|
|
|
|
this->UpdateState();
|
|
|
|
int extent[6] = {VTK_INT_MAX, VTK_INT_MIN,
|
|
VTK_INT_MAX, VTK_INT_MIN, VTK_INT_MAX, VTK_INT_MIN};
|
|
|
|
std::vector<vtkSmartPointer<vtkImageData> > images;
|
|
for (vtkInternals::KDTreeType::iterator iter =
|
|
this->Internals->KDTree.begin();
|
|
iter != this->Internals->KDTree.end(); ++iter)
|
|
{
|
|
if (iter->ViewProxy.GetPointer())
|
|
{
|
|
vtkImageData* image = iter->ViewProxy->CaptureWindow(magnification);
|
|
if (image)
|
|
{
|
|
const int* image_extent = image->GetExtent();
|
|
extent[0] = std::min(extent[0], image_extent[0]);
|
|
extent[2] = std::min(extent[2], image_extent[2]);
|
|
extent[4] = std::min(extent[4], image_extent[4]);
|
|
extent[1] = std::max(extent[1], image_extent[1]);
|
|
extent[3] = std::max(extent[3], image_extent[3]);
|
|
extent[5] = std::max(extent[5], image_extent[5]);
|
|
|
|
images.push_back(image);
|
|
image->FastDelete();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (images.size() == 0)
|
|
{
|
|
vtkErrorMacro("No view present in the layout.");
|
|
return NULL;
|
|
}
|
|
|
|
int numComponents = vtkSMViewProxy::GetTransparentBackground()? 4 : 3;
|
|
vtkImageData* image = vtkImageData::New();
|
|
image->SetExtent(extent);
|
|
image->AllocateScalars(VTK_UNSIGNED_CHAR, numComponents);
|
|
|
|
unsigned char* image_data =
|
|
reinterpret_cast<unsigned char*>(image->GetScalarPointer());
|
|
std::fill(
|
|
image_data, image_data + image->GetNumberOfPoints() * numComponents,
|
|
static_cast<unsigned char>(0));
|
|
|
|
unsigned char color[3];
|
|
color[0] = static_cast<unsigned char>(vtkSMViewLayoutProxy::MultiViewImageBorderColor[0] * 0xff);
|
|
color[1] = static_cast<unsigned char>(vtkSMViewLayoutProxy::MultiViewImageBorderColor[1] * 0xff);
|
|
color[2] = static_cast<unsigned char>(vtkSMViewLayoutProxy::MultiViewImageBorderColor[2] * 0xff);
|
|
for (size_t cc=0; cc < images.size(); cc++)
|
|
{
|
|
vtkSMUtilities::Merge(image, images[cc],
|
|
vtkSMViewLayoutProxy::MultiViewImageBorderWidth, color);
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkSMViewLayoutProxy* vtkSMViewLayoutProxy::FindLayout(
|
|
vtkSMViewProxy* view, const char* reggroup/*=layouts*/)
|
|
{
|
|
if (!view)
|
|
{
|
|
return NULL;
|
|
}
|
|
vtkSMSessionProxyManager* pxm = view->GetSessionProxyManager();
|
|
vtkNew<vtkSMProxyIterator> iter;
|
|
iter->SetSessionProxyManager(pxm);
|
|
iter->SetModeToOneGroup();
|
|
for (iter->Begin(reggroup); !iter->IsAtEnd(); iter->Next())
|
|
{
|
|
vtkSMViewLayoutProxy* layout = vtkSMViewLayoutProxy::SafeDownCast(iter->GetProxy());
|
|
if (layout != NULL && layout->GetViewLocation(view) != -1)
|
|
{
|
|
return layout;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMViewLayoutProxy::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
}
|