Files
ThirdParty-6/ParaView-5.0.1/ParaViewCore/ServerManager/Rendering/vtkSMViewProxy.cxx

789 lines
24 KiB
C++

/*=========================================================================
Program: ParaView
Module: vtkSMViewProxy.cxx
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 "vtkSMViewProxy.h"
#include "vtkClientServerStream.h"
#include "vtkCommand.h"
#include "vtkErrorCode.h"
#include "vtkGenericRenderWindowInteractor.h"
#include "vtkImageData.h"
#include "vtkMath.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkProcessModule.h"
#include "vtkPVOptions.h"
#include "vtkPVView.h"
#include "vtkPVXMLElement.h"
#include "vtkRendererCollection.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkSmartPointer.h"
#include "vtkSMParaViewPipelineControllerWithRendering.h"
#include "vtkSMProperty.h"
#include "vtkSMProxyManager.h"
#include "vtkSMRepresentationProxy.h"
#include "vtkSMSession.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMUncheckedPropertyHelper.h"
#include "vtkSMUtilities.h"
#include <assert.h>
namespace
{
const char* vtkGetRepresentationNameFromHints(
const char* viewType, vtkPVXMLElement* hints, int port)
{
if (!hints)
{
return NULL;
}
for (unsigned int cc=0, max=hints->GetNumberOfNestedElements(); cc<max; ++cc)
{
vtkPVXMLElement* child = hints->GetNestedElement(cc);
if (child == NULL || child->GetName() == NULL)
{
continue;
}
// LEGACY: support DefaultRepresentations hint.
// <Hints>
// <DefaultRepresentations representation="Foo" />
// </Hints>
if (strcmp(child->GetName(), "DefaultRepresentations") == 0)
{
return child->GetAttribute("representation");
}
// <Hints>
// <Representation port="outputPort" view="ViewName" type="ReprName" />
// </Hints>
else if (strcmp(child->GetName(), "Representation") == 0 &&
// has an attribute "view" that matches the viewType.
child->GetAttribute("view") &&
strcmp(child->GetAttribute("view"), viewType) == 0 &&
child->GetAttribute("type") != NULL)
{
// if port is present, it must match "port".
int xmlPort;
if (child->GetScalarAttribute("port", &xmlPort) == 0 || xmlPort == port)
{
return child->GetAttribute("type");
}
}
}
return NULL;
}
};
bool vtkSMViewProxy::TransparentBackground = false;
vtkStandardNewMacro(vtkSMViewProxy);
//----------------------------------------------------------------------------
vtkSMViewProxy::vtkSMViewProxy()
{
this->SetLocation(vtkProcessModule::CLIENT_AND_SERVERS);
this->DefaultRepresentationName = 0;
this->Enable = true;
}
//----------------------------------------------------------------------------
vtkSMViewProxy::~vtkSMViewProxy()
{
this->SetDefaultRepresentationName(0);
}
//----------------------------------------------------------------------------
vtkView* vtkSMViewProxy::GetClientSideView()
{
if (this->ObjectsCreated)
{
return vtkView::SafeDownCast(this->GetClientSideObject());
}
return NULL;
}
//----------------------------------------------------------------------------
void vtkSMViewProxy::CreateVTKObjects()
{
if (this->ObjectsCreated)
{
return;
}
this->Superclass::CreateVTKObjects();
// If prototype, no need to go further...
if(this->Location == 0)
{
return;
}
if (!this->ObjectsCreated)
{
return;
}
vtkClientServerStream stream;
stream << vtkClientServerStream::Invoke
<< VTKOBJECT(this)
<< "Initialize"
<< static_cast<int>(this->GetGlobalID())
<< vtkClientServerStream::End;
this->ExecuteStream(stream);
vtkObject::SafeDownCast(this->GetClientSideObject())->AddObserver(
vtkPVView::ViewTimeChangedEvent,
this, &vtkSMViewProxy::ViewTimeChanged);
}
//----------------------------------------------------------------------------
void vtkSMViewProxy::ViewTimeChanged()
{
vtkSMPropertyHelper helper1(this, "Representations");
for (unsigned int cc=0; cc < helper1.GetNumberOfElements(); cc++)
{
vtkSMRepresentationProxy* repr = vtkSMRepresentationProxy::SafeDownCast(
helper1.GetAsProxy(cc));
if (repr)
{
repr->ViewTimeChanged();
}
}
vtkSMPropertyHelper helper2(this, "HiddenRepresentations", true);
for (unsigned int cc=0; cc < helper2.GetNumberOfElements(); cc++)
{
vtkSMRepresentationProxy* repr = vtkSMRepresentationProxy::SafeDownCast(
helper2.GetAsProxy(cc));
if (repr)
{
repr->ViewTimeChanged();
}
}
}
//----------------------------------------------------------------------------
void vtkSMViewProxy::StillRender()
{
// bug 0013947
// on Mac OSX don't render into invalid drawable, all subsequent
// OpenGL calls fail with invalid framebuffer operation.
if (this->IsContextReadyForRendering() == false)
{
return;
}
int interactive = 0;
this->InvokeEvent(vtkCommand::StartEvent, &interactive);
this->GetSession()->PrepareProgress();
// We call update separately from the render. This is done so that we don't
// get any synchronization issues with GUI responding to the data-updated
// event by making some data information requests(for example). If those
// happen while StillRender/InteractiveRender is being executed on the server
// side then we get deadlocks.
this->Update();
vtkTypeUInt32 render_location = this->PreRender(interactive==1);
if (this->ObjectsCreated)
{
vtkClientServerStream stream;
stream << vtkClientServerStream::Invoke
<< VTKOBJECT(this)
<< "StillRender"
<< vtkClientServerStream::End;
this->ExecuteStream(stream, false, render_location);
}
this->PostRender(interactive==1);
this->GetSession()->CleanupPendingProgress();
this->InvokeEvent(vtkCommand::EndEvent, &interactive);
}
//----------------------------------------------------------------------------
void vtkSMViewProxy::InteractiveRender()
{
int interactive = 1;
this->InvokeEvent(vtkCommand::StartEvent, &interactive);
this->GetSession()->PrepareProgress();
// Interactive render will not call Update() at all. It's expected that you
// must have either called a StillRender() or an Update() before triggering an
// interactive render. This is critical to keep interactive rates fast when
// working over a slow client-server connection.
// this->Update();
vtkTypeUInt32 render_location = this->PreRender(interactive==1);
if (this->ObjectsCreated)
{
vtkClientServerStream stream;
stream << vtkClientServerStream::Invoke
<< VTKOBJECT(this)
<< "InteractiveRender"
<< vtkClientServerStream::End;
this->ExecuteStream(stream, false, render_location);
}
this->PostRender(interactive==1);
this->GetSession()->CleanupPendingProgress();
this->InvokeEvent(vtkCommand::EndEvent, &interactive);
}
//----------------------------------------------------------------------------
void vtkSMViewProxy::Update()
{
if (this->ObjectsCreated && this->NeedsUpdate)
{
vtkClientServerStream stream;
// To avoid race conditions in multi-client modes, we are taking a peculiar
// approach. Any ivar that affect parallel communication are overridden
// using the client-side values in the same ExecuteStream() call. That
// ensures that two clients cannot enter race condition. This results in minor
// increase in the size of the messages sent, but overall the benefits are
// greater.
vtkPVView* pvview = vtkPVView::SafeDownCast(this->GetClientSideObject());
if (pvview)
{
int use_cache = pvview->GetUseCache()? 1 : 0;
stream << vtkClientServerStream::Invoke
<< VTKOBJECT(this)
<< "SetUseCache" << use_cache
<< vtkClientServerStream::End;
}
stream << vtkClientServerStream::Invoke
<< VTKOBJECT(this)
<< "Update"
<< vtkClientServerStream::End;
this->GetSession()->PrepareProgress();
this->ExecuteStream(stream);
this->GetSession()->CleanupPendingProgress();
unsigned int numProducers = this->GetNumberOfProducers();
for (unsigned int i=0; i<numProducers; i++)
{
vtkSMRepresentationProxy* repr = vtkSMRepresentationProxy::SafeDownCast(
this->GetProducerProxy(i));
if (repr)
{
repr->ViewUpdated(this);
}
else
{
//this->GetProducerProxy(i)->PostUpdateData();
}
}
this->PostUpdateData();
}
}
//----------------------------------------------------------------------------
vtkSMRepresentationProxy* vtkSMViewProxy::CreateDefaultRepresentation(
vtkSMProxy* proxy, int outputPort)
{
assert("The session should be valid" && this->Session);
vtkSMSourceProxy* producer = vtkSMSourceProxy::SafeDownCast(proxy);
if (
(producer == NULL) ||
(outputPort < 0) ||
(static_cast<int>(producer->GetNumberOfOutputPorts()) <= outputPort) ||
(producer->GetSession() != this->GetSession()))
{
return NULL;
}
// Update with time from the view to ensure we have up-to-date data.
double view_time = vtkSMPropertyHelper(this, "ViewTime").GetAsDouble();
producer->UpdatePipeline(view_time);
const char* representationType =
this->GetRepresentationType(producer, outputPort);
if (!representationType)
{
return NULL;
}
vtkSMSessionProxyManager* pxm = this->GetSessionProxyManager();
vtkSmartPointer<vtkSMProxy> p;
p.TakeReference(pxm->NewProxy("representations", representationType));
vtkSMRepresentationProxy* repr = vtkSMRepresentationProxy::SafeDownCast(p);
if (repr)
{
repr->Register(this);
return repr;
}
vtkWarningMacro("Failed to create representation (representations,"
<< representationType <<").");
return NULL;
}
//----------------------------------------------------------------------------
const char* vtkSMViewProxy::GetRepresentationType(
vtkSMSourceProxy* producer, int outputPort)
{
assert(producer &&
static_cast<int>(producer->GetNumberOfOutputPorts()) > outputPort);
// Process producer hints to see if indicates what type of representation
// to create for this view.
if (const char* reprName = vtkGetRepresentationNameFromHints(
this->GetXMLName(), producer->GetHints(), outputPort))
{
return reprName;
}
// check if we have default representation name specified in XML.
if (this->DefaultRepresentationName)
{
vtkSMSessionProxyManager* pxm = this->GetSessionProxyManager();
vtkSMProxy* prototype = pxm->GetPrototypeProxy(
"representations", this->DefaultRepresentationName);
if (prototype)
{
vtkSMProperty* inputProp = prototype->GetProperty("Input");
vtkSMUncheckedPropertyHelper helper(inputProp);
helper.Set(producer, outputPort);
bool acceptable = (inputProp->IsInDomains() > 0);
helper.SetNumberOfElements(0);
if (acceptable)
{
return this->DefaultRepresentationName;
}
}
}
return NULL;
}
//----------------------------------------------------------------------------
bool vtkSMViewProxy::CanDisplayData(vtkSMSourceProxy* producer, int outputPort)
{
if (producer == NULL || outputPort < 0 ||
static_cast<int>(producer->GetNumberOfOutputPorts()) <= outputPort ||
producer->GetSession() != this->GetSession())
{
return false;
}
const char* type = this->GetRepresentationType(producer, outputPort);
if (type != NULL)
{
vtkSMSessionProxyManager* pxm = this->GetSessionProxyManager();
return (pxm->GetPrototypeProxy("representations", type) != NULL);
}
return false;
}
//----------------------------------------------------------------------------
vtkSMRepresentationProxy* vtkSMViewProxy::FindRepresentation(
vtkSMSourceProxy* producer, int outputPort)
{
vtkSMPropertyHelper helper (this, "Representations");
for (unsigned int cc=0, max=helper.GetNumberOfElements(); cc < max; ++cc)
{
vtkSMRepresentationProxy* repr =
vtkSMRepresentationProxy::SafeDownCast(helper.GetAsProxy(cc));
if (repr &&
repr->GetProperty("Input"))
{
vtkSMPropertyHelper helper2(repr, "Input");
if (helper2.GetAsProxy() == producer &&
static_cast<int>(helper2.GetOutputPort()) == outputPort)
{
return repr;
}
}
}
return NULL;
}
//----------------------------------------------------------------------------
int vtkSMViewProxy::ReadXMLAttributes(
vtkSMSessionProxyManager* pm, vtkPVXMLElement* element)
{
if (!this->Superclass::ReadXMLAttributes(pm, element))
{
return 0;
}
const char* repr_name = element->GetAttribute("representation_name");
if (repr_name)
{
this->SetDefaultRepresentationName(repr_name);
}
return 1;
}
class vtkSMViewProxy::vtkRendererSaveInfo
{
public:
vtkRendererSaveInfo(vtkRenderer* renderer)
: Gradient(renderer->GetGradientBackground())
, Textured(renderer->GetTexturedBackground())
, Red(renderer->GetBackground()[0])
, Green(renderer->GetBackground()[1])
, Blue(renderer->GetBackground()[2])
{
}
const bool Gradient;
const bool Textured;
const double Red;
const double Green;
const double Blue;
private:
vtkRendererSaveInfo(const vtkRendererSaveInfo&); // Not implemented
void operator=(const vtkRendererSaveInfo&); // Not implemented
};
//----------------------------------------------------------------------------
vtkImageData* vtkSMViewProxy::CaptureWindow(int magnification)
{
vtkRenderWindow* window = this->GetRenderWindow();
if (window && this->TransparentBackground)
{
vtkRendererCollection* renderers = window->GetRenderers();
vtkRenderer* renderer = renderers->GetFirstRenderer();
while (renderer)
{
if (renderer->GetErase())
{
// Found a background-writing renderer.
break;
}
renderer = renderers->GetNextItem();
}
if (!renderer)
{
// No renderer?
return NULL;
}
vtkRendererSaveInfo* info = this->PrepareRendererBackground(renderer, 255, 255, 255, true);
vtkImageData* captureWhite = this->CaptureWindowSingle(magnification);
this->PrepareRendererBackground(renderer, 0, 0, 0, false);
vtkImageData* captureBlack = this->CaptureWindowSingle(magnification);
vtkImageData* capture = vtkImageData::New();
capture->CopyStructure(captureWhite);
capture->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
const unsigned char* white;
const unsigned char* black;
unsigned char* out;
vtkIdType whiteStep[3];
vtkIdType blackStep[3];
vtkIdType outStep[3];
white = static_cast<const unsigned char*>(captureWhite->GetScalarPointerForExtent(captureWhite->GetExtent()));
black = static_cast<const unsigned char*>(captureBlack->GetScalarPointerForExtent(captureBlack->GetExtent()));
out = static_cast<unsigned char*>(capture->GetScalarPointerForExtent(capture->GetExtent()));
captureWhite->GetIncrements(whiteStep[0], whiteStep[1], whiteStep[2]);
captureBlack->GetIncrements(blackStep[0], blackStep[1], blackStep[2]);
capture->GetIncrements(outStep[0], outStep[1], outStep[2]);
int* extent = capture->GetExtent();
for (int i = extent[4]; i <= extent[5]; ++i)
{
const unsigned char* whiteRow = white;
const unsigned char* blackRow = black;
unsigned char* outRow = out;
for (int j = extent[2]; j <= extent[3]; ++j)
{
const unsigned char* whitePx = whiteRow;
const unsigned char* blackPx = blackRow;
unsigned char* outPx = outRow;
for (int k = extent[0]; k <= extent[1]; ++k)
{
if (whitePx[0] == blackPx[0] &&
whitePx[1] == blackPx[1] &&
whitePx[2] == blackPx[2])
{
outPx[0] = whitePx[0];
outPx[1] = whitePx[1];
outPx[2] = whitePx[2];
outPx[3] = 255;
}
else
{
// Some kind of translucency; use values from the black capture.
// The opacity is the derived from the V difference of the HSV
// colors.
double whiteHSV[3];
double blackHSV[3];
vtkMath::RGBToHSV(whitePx[0] / 255., whitePx[1] / 255., whitePx[2] / 255.,
whiteHSV + 0, whiteHSV + 1, whiteHSV + 2);
vtkMath::RGBToHSV(blackPx[0] / 255., blackPx[1] / 255., blackPx[2] / 255.,
blackHSV + 0, blackHSV + 1, blackHSV + 2);
double alpha = 1. - (whiteHSV[2] - blackHSV[2]);
outPx[0] = static_cast<unsigned char>(blackPx[0] / alpha);
outPx[1] = static_cast<unsigned char>(blackPx[1] / alpha);
outPx[2] = static_cast<unsigned char>(blackPx[2] / alpha);
outPx[3] = static_cast<unsigned char>(255 * alpha);
}
whitePx += whiteStep[0];
blackPx += blackStep[0];
outPx += outStep[0];
}
whiteRow += whiteStep[1];
blackRow += blackStep[1];
outRow += outStep[1];
}
white += whiteStep[2];
black += blackStep[2];
out += outStep[2];
}
this->RestoreRendererBackground(renderer, info);
captureWhite->Delete();
captureBlack->Delete();
return capture;
}
// Fall back to using no transparency.
return this->CaptureWindowSingle(magnification);
}
//----------------------------------------------------------------------------
vtkSMViewProxy::vtkRendererSaveInfo* vtkSMViewProxy::PrepareRendererBackground(
vtkRenderer* renderer,
double r, double g, double b, bool save)
{
vtkRendererSaveInfo* info = NULL;
if (save)
{
info = new vtkRendererSaveInfo(renderer);
}
renderer->SetGradientBackground(false);
renderer->SetTexturedBackground(false);
renderer->SetBackground(r, g, b);
return info;
}
//----------------------------------------------------------------------------
void vtkSMViewProxy::RestoreRendererBackground(vtkRenderer* renderer,
vtkRendererSaveInfo* info)
{
renderer->SetGradientBackground(info->Gradient);
renderer->SetTexturedBackground(info->Textured);
renderer->SetBackground(info->Red, info->Green, info->Blue);
delete info;
}
//----------------------------------------------------------------------------
vtkImageData* vtkSMViewProxy::CaptureWindowSingle(int magnification)
{
if (this->ObjectsCreated)
{
vtkClientServerStream stream;
stream << vtkClientServerStream::Invoke
<< VTKOBJECT(this)
<< "PrepareForScreenshot"
<< vtkClientServerStream::End;
this->ExecuteStream(stream);
}
vtkImageData* capture = this->CaptureWindowInternal(magnification);
if (this->ObjectsCreated)
{
vtkClientServerStream stream;
stream << vtkClientServerStream::Invoke
<< VTKOBJECT(this)
<< "CleanupAfterScreenshot"
<< vtkClientServerStream::End;
this->ExecuteStream(stream);
}
if (capture)
{
int position[2];
vtkSMPropertyHelper(this, "ViewPosition").Get(position, 2);
// Update image extents based on ViewPosition
int extents[6];
capture->GetExtent(extents);
for (int cc=0; cc < 4; cc++)
{
extents[cc] += position[cc/2]*magnification;
}
capture->SetExtent(extents);
}
return capture;
}
//-----------------------------------------------------------------------------
int vtkSMViewProxy::WriteImage(const char* filename,
const char* writerName, int magnification)
{
if (!filename || !writerName)
{
return vtkErrorCode::UnknownError;
}
vtkSmartPointer<vtkImageData> shot;
shot.TakeReference(this->CaptureWindow(magnification));
if (vtkProcessModule::GetProcessModule()->GetOptions()->GetSymmetricMPIMode())
{
return vtkSMUtilities::SaveImageOnProcessZero(shot, filename, writerName);
}
return vtkSMUtilities::SaveImage(shot, filename, writerName);
}
//----------------------------------------------------------------------------
void vtkSMViewProxy::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}
//----------------------------------------------------------------------------
void vtkSMViewProxy::SetTransparentBackground(bool val)
{
vtkSMViewProxy::TransparentBackground = val;
}
//----------------------------------------------------------------------------
bool vtkSMViewProxy::GetTransparentBackground()
{
return vtkSMViewProxy::TransparentBackground;
}
//----------------------------------------------------------------------------
bool vtkSMViewProxy::IsContextReadyForRendering()
{
if (vtkRenderWindow* window = this->GetRenderWindow())
{
return window->IsDrawable();
}
return true;
}
//----------------------------------------------------------------------------
bool vtkSMViewProxy::HideOtherRepresentationsIfNeeded(vtkSMProxy* repr)
{
if (repr == NULL ||
this->GetHints() == NULL ||
this->GetHints()->FindNestedElementByName("ShowOneRepresentationAtATime") == NULL)
{
return false;
}
vtkNew<vtkSMParaViewPipelineControllerWithRendering> controller;
bool modified = false;
vtkSMPropertyHelper helper(this, "Representations");
for (unsigned int cc=0, max=helper.GetNumberOfElements(); cc < max; ++cc)
{
vtkSMRepresentationProxy* arepr =
vtkSMRepresentationProxy::SafeDownCast(helper.GetAsProxy(cc));
if (arepr && arepr != repr)
{
if (vtkSMPropertyHelper(arepr, "Visibility", /*quiet*/true).GetAsInt() == 1)
{
controller->Hide(arepr, this);
modified = true;
}
}
}
return modified;
}
//----------------------------------------------------------------------------
bool vtkSMViewProxy::GetLocalProcessSupportsInteraction()
{
this->CreateVTKObjects();
vtkPVView* pvview = vtkPVView::SafeDownCast(this->GetClientSideObject());
return pvview? pvview->GetLocalProcessSupportsInteraction() : false;
}
//----------------------------------------------------------------------------
bool vtkSMViewProxy::MakeRenderWindowInteractor(bool quiet)
{
if (this->GetInteractor() != NULL)
{
// all's setup already. nothing to do.
return true;
}
if (!this->GetLocalProcessSupportsInteraction())
{
return false;
}
vtkRenderWindow* renWin = this->GetRenderWindow();
if (!renWin)
{
if (!quiet)
{
vtkWarningMacro("Not a view that has a vtkRenderWindow. Cannot setup interactor.");
}
return false;
}
if (renWin->GetMapped())
{
if (!quiet)
{
vtkErrorMacro("Window is currently mapped. "
"Currently, interaction is only supported on unmapped windows.");
}
return false;
}
// in reality batch shouldn't have an interactor at all. However, to avoid the
// mismatch in the vtkPVAxesWidget (orientation widget) when using pvpython or
// pvbatch, we do create one. However, lets create a non-interactive
// interactor in batch mode.
vtkSmartPointer<vtkRenderWindowInteractor> iren;
if (vtkProcessModule::GetProcessType() != vtkProcessModule::PROCESS_BATCH)
{
iren = renWin->MakeRenderWindowInteractor();
}
else
{
iren = vtkSmartPointer<vtkGenericRenderWindowInteractor>::New();
// This initialize is essential. Otherwise vtkRenderWindow::Render causes
// the interactor to initialize which in turn triggers a render!
iren->Initialize();
}
this->SetupInteractor(iren);
return this->GetInteractor() != NULL;
}