Commit 5ed29efe authored by Utkarsh Ayachit's avatar Utkarsh Ayachit
Browse files

Refactoring axis range properties for charts.

Charts did not save the updated axes ranges when the user interacted with the
charts in the state files. This was because the property for axes range was
not updated on interaction. Changing the entire infrastructure to get this
working.

Hence forth, as soon as the user interacts with the charts, the axes become
"fixed" and don't auto-update unless user hits "reset-camera".

There are 4 new properies LeftAxisRange, RightAxisRange, TopAxisRange, and
BottomAxisRange that can be set to empty to enabled "auto" ranges or set to
specific values to use those ranges.

As soon as user interacts with the view, vtkSMContextViewProxy listens to the
interaction event and updates the axes range properties using the current range,
thus "fixing" them to the chosen values.

Also creating special vtkContextViewInteractorStyle subclass to ensure that we
call StillRender() on proxy every time the user interacts with the view.

All these fixes now ensure that charts on tile displays show current axes ranges
and update as user interacts with the charts.

WIP: This breaks the synchronization of chart viewports in collaboration mode.
That needs to be fixed.
parent 5c972370
......@@ -316,22 +316,29 @@ void vtkPVXYChartView::SetAxisLabelPrecision(int index, int precision)
}
//----------------------------------------------------------------------------
void vtkPVXYChartView::SetAxisBehavior(int index, int behavior)
void vtkPVXYChartView::SetAxisRange(int index, double min, double max)
{
if (this->Chart)
{
this->Chart->GetAxis(index)->SetBehavior(behavior);
vtkAxis* axis = this->Chart->GetAxis(index);
axis->SetBehavior(vtkAxis::FIXED);
axis->SetMinimum(min);
axis->SetMaximum(max);
this->Chart->RecalculateBounds();
}
}
//----------------------------------------------------------------------------
void vtkPVXYChartView::SetAxisRange(int index, double min, double max)
void vtkPVXYChartView::UnsetAxisRange(int index)
{
if (this->Chart && this->Chart->GetAxis(index)->GetBehavior() > 0)
if (this->Chart)
{
this->Chart->GetAxis(index)->SetMinimum(min);
this->Chart->GetAxis(index)->SetMaximum(max);
vtkAxis* axis = this->Chart->GetAxis(index);
axis->SetBehavior(vtkAxis::AUTO);
// we set some random min and max so we can notice them when they get used.
axis->SetMinimum(0.0);
axis->SetMaximum(6.66);
this->Chart->RecalculateBounds();
}
}
......
......@@ -21,6 +21,7 @@
#define __vtkPVXYChartView_h
#include "vtkPVContextView.h"
#include "vtkAxis.h" //for enums.
class vtkChart;
class vtkChartView;
......@@ -144,18 +145,26 @@ public:
void SetAxisLabelPrecision(int index, int precision);
// Description:
// Sets the behavior for the given axis, 0 - best fit, 1 - fixed, 2 - custom.
// These methods should not be called directly. They are made public only so
// that the client-server-stream-interpreter can invoke them. Use the
// corresponding properties to change these values.
void SetAxisBehavior(int index, int behavior);
// Description:
// Sets the range for the given axis.
// These methods should not be called directly. They are made public only so
// that the client-server-stream-interpreter can invoke them. Use the
// corresponding properties to change these values.
void SetAxisRange(int index, double minimum, double maximum);
// For axis ranges, ParaView overrides the VTK charts behavior. Instead of
// letting the user choose the behavior, we only have 2 modes, if any range is
// set, then the range is always used. If no range is set, then alone we let
// the chart determine the range.
void SetLeftAxisRange(double minimum, double maximum)
{ this->SetAxisRange(vtkAxis::LEFT, minimum, maximum); }
void SetRightAxisRange(double minimum, double maximum)
{ this->SetAxisRange(vtkAxis::RIGHT, minimum, maximum); }
void SetTopAxisRange(double minimum, double maximum)
{ this->SetAxisRange(vtkAxis::TOP, minimum, maximum); }
void SetBottomAxisRange(double minimum, double maximum)
{ this->SetAxisRange(vtkAxis::BOTTOM, minimum, maximum); }
void UnsetLeftAxisRange()
{ this->UnsetAxisRange(vtkAxis::LEFT); }
void UnsetRightAxisRange()
{ this->UnsetAxisRange(vtkAxis::RIGHT); }
void UnsetTopAxisRange()
{ this->UnsetAxisRange(vtkAxis::TOP); }
void UnsetBottomAxisRange()
{ this->UnsetAxisRange(vtkAxis::BOTTOM); }
// Description:
// Sets whether or not the given axis uses a log10 scale.
......@@ -242,6 +251,9 @@ protected:
vtkPVXYChartView();
~vtkPVXYChartView();
void SetAxisRange(int index, double min, double max);
void UnsetAxisRange(int index);
// Description:
// Actual rendering implementation.
virtual void Render(bool interactive);
......
......@@ -805,25 +805,52 @@
<IntRangeDomain name="range" min="0" max="6" />
</IntVectorProperty>
<IntVectorProperty
name="AxisBehavior"
command="SetAxisBehavior"
repeat_command="1"
use_index="1"
number_of_elements_per_command="1"
number_of_elements="4"
default_values="0 0 0 0">
<IntRangeDomain name="range" min="0" max="2" />
</IntVectorProperty>
<DoubleVectorProperty name="LeftAxisRange"
command="SetLeftAxisRange"
clean_command="UnsetLeftAxisRange"
ignore_synchronization="1"
number_of_elements="2"
default_values="none">
<Documentation>
Set the axis range for the Left axis. Set to empty to let the chart
determine the range automatically.
</Documentation>
</DoubleVectorProperty>
<DoubleVectorProperty
name="AxisRange"
command="SetAxisRange"
repeat_command="1"
use_index="1"
number_of_elements_per_command="2"
number_of_elements="8"
default_values="0 1 0 1 0 1 0 1">
<DoubleVectorProperty name="RightAxisRange"
command="SetRightAxisRange"
clean_command="UnsetRightAxisRange"
ignore_synchronization="1"
number_of_elements="2"
default_values="none">
<Documentation>
Set the axis range for the Right axis. Set to empty to let the chart
determine the range automatically.
</Documentation>
</DoubleVectorProperty>
<DoubleVectorProperty name="TopAxisRange"
command="SetTopAxisRange"
clean_command="UnsetTopAxisRange"
ignore_synchronization="1"
number_of_elements="2"
default_values="none">
<Documentation>
Set the axis range for the Top axis. Set to empty to let the chart
determine the range automatically.
</Documentation>
</DoubleVectorProperty>
<DoubleVectorProperty name="BottomAxisRange"
command="SetBottomAxisRange"
clean_command="UnsetBottomAxisRange"
ignore_synchronization="1"
number_of_elements="2"
default_values="none">
<Documentation>
Set the axis range for the Bottom axis. Set to empty to let the chart
determine the range automatically.
</Documentation>
</DoubleVectorProperty>
<StringVectorProperty
......@@ -4541,27 +4568,29 @@
<Proxy name="RootView" proxygroup="views" proxyname="XYChartView">
</Proxy>
<ExposedProperties>
<Property name="ChartTitle"/>
<Property name="ChartTitleFont" />
<Property name="ChartTitleColor" />
<Property name="ChartTitleAlignment" />
<Property name="ShowLegend" />
<Property name="LegendLocation" />
<Property name="ShowAxis" />
<Property name="AxisLogScale" />
<Property name="ShowAxisGrid" />
<Property name="AxisColor" />
<Property name="AxisGridColor" />
<Property name="ShowAxisLabels" />
<Property name="AxisLabelFont" />
<Property name="AxisLabelColor" />
<Property name="AxisLabelFont" />
<Property name="AxisLabelNotation" />
<Property name="AxisLabelPrecision" />
<Property name="AxisBehavior" />
<Property name="AxisRange" />
<Property name="AxisLogScale" />
<Property name="AxisTitle" />
<Property name="AxisTitleFont" />
<Property name="AxisTitleColor" />
<Property name="AxisTitleFont" />
<Property name="BottomAxisRange" />
<Property name="ChartTitle"/>
<Property name="ChartTitleAlignment" />
<Property name="ChartTitleColor" />
<Property name="ChartTitleFont" />
<Property name="LeftAxisRange" />
<Property name="LegendLocation" />
<Property name="RightAxisRange" />
<Property name="ShowAxis" />
<Property name="ShowAxisGrid" />
<Property name="ShowAxisLabels" />
<Property name="ShowLegend" />
<Property name="TopAxisRange" />
</ExposedProperties>
<!-- End of "RootView" subproxy -->
</SubProxy>
......@@ -4582,26 +4611,28 @@
<Proxy name="RootView" proxygroup="views" proxyname="XYBarChartView">
</Proxy>
<ExposedProperties>
<Property name="ChartTitle"/>
<Property name="ChartTitleFont" />
<Property name="ChartTitleColor" />
<Property name="ChartTitleAlignment" />
<Property name="ShowLegend" />
<Property name="ShowAxis" />
<Property name="AxisLogScale" />
<Property name="ShowAxisGrid" />
<Property name="AxisColor" />
<Property name="AxisGridColor" />
<Property name="ShowAxisLabels" />
<Property name="AxisLabelFont" />
<Property name="AxisLabelColor" />
<Property name="AxisLabelFont" />
<Property name="AxisLabelNotation" />
<Property name="AxisLabelPrecision" />
<Property name="AxisBehavior" />
<Property name="AxisRange" />
<Property name="AxisLogScale" />
<Property name="AxisTitle" />
<Property name="AxisTitleFont" />
<Property name="AxisTitleColor" />
<Property name="AxisTitleFont" />
<Property name="BottomAxisRange" />
<Property name="ChartTitle"/>
<Property name="ChartTitleAlignment" />
<Property name="ChartTitleColor" />
<Property name="ChartTitleFont" />
<Property name="LeftAxisRange" />
<Property name="RightAxisRange" />
<Property name="ShowAxis" />
<Property name="ShowAxisGrid" />
<Property name="ShowAxisLabels" />
<Property name="ShowLegend" />
<Property name="TopAxisRange" />
</ExposedProperties>
<!-- End of "RootView" subproxy -->
</SubProxy>
......
......@@ -289,11 +289,9 @@ bool vtkSIVectorPropertyTemplate<T, force_idtype>::Push(vtkSMMessage* message, i
AppendValues(values, *variant, force_idtype());
if(values.size() > 0)
{
return this->Push(&values[0], static_cast<int>(values.size()));
}
return true;
return (values.size() > 0) ?
this->Push(&values[0], static_cast<int>(values.size())) :
this->Push(static_cast<T*>(NULL), static_cast<int>(values.size()));
}
//---------------------------------------------------------------------------
......@@ -375,7 +373,7 @@ bool vtkSIVectorPropertyTemplate<T, force_idtype>::Push(T* values, int number_of
<< vtkClientServerStream::End;
}
if (!this->Repeatable)
if (!this->Repeatable && number_of_elements > 0)
{
stream << vtkClientServerStream::Invoke << object << this->Command;
if (this->ArgumentIsArray)
......@@ -391,7 +389,7 @@ bool vtkSIVectorPropertyTemplate<T, force_idtype>::Push(T* values, int number_of
}
stream << vtkClientServerStream::End;
}
else
else if (this->Repeatable)
{
int numCommands = number_of_elements / this->NumberOfElementsPerCommand;
for(int i=0; i<numCommands; i++)
......
......@@ -14,88 +14,64 @@
=========================================================================*/
#include "vtkSMContextViewProxy.h"
#include "vtkAxis.h"
#include "vtkChartXY.h"
#include "vtkCommand.h"
#include "vtkContextInteractorStyle.h"
#include "vtkContextView.h"
#include "vtkErrorCode.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkProcessModule.h"
#include "vtkPVContextView.h"
#include "vtkRenderWindow.h"
#include "vtkSMUtilities.h"
#include "vtkWindowToImageFilter.h"
#include "vtkAxis.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSMPropertyHelper.h"
#include "vtkWeakPointer.h"
#include "vtkNew.h"
#include "vtkEventForwarderCommand.h"
#include "vtkWindowToImageFilter.h"
//-----------------------------------------------------------------------------
// Minimal storage class for STL containers etc.
class vtkSMContextViewProxy::Private
//****************************************************************************
// vtkSMContextViewInteractorStyle makes it possible for us to call
// StillRender() as the user interacts with the chart views on the client side
// instead of directly calling Render() on the render-window. This makes no
// difference in general, except in tile-display mode, where the StillRender()
// ensures that the server-side views are updated as well.
class vtkSMContextViewInteractorStyle : public vtkContextInteractorStyle
{
public:
Private()
{
ViewBounds[0] = ViewBounds[2] = ViewBounds[4] = ViewBounds[6] = 0.0;
ViewBounds[1] = ViewBounds[3] = ViewBounds[5] = ViewBounds[7] = 1.0;
}
static vtkSMContextViewInteractorStyle*New();
vtkTypeMacro(vtkSMContextViewInteractorStyle, vtkContextInteractorStyle);
~Private()
void SetView(vtkSMContextViewProxy* view)
{ this->ViewProxy = view; }
protected:
virtual void RenderNow()
{
if (this->Proxy && this->Proxy->GetContextItem() &&
this->Forwarder.GetPointer() != NULL)
if (this->ViewProxy)
{
this->Proxy->GetContextItem()->RemoveObserver(this->Forwarder.GetPointer());
this->ViewProxy->StillRender();
}
}
void AttachCallback(vtkSMContextViewProxy* proxy)
{
this->Forwarder->SetTarget(proxy);
this->Proxy = proxy;
if(this->Proxy && this->Proxy->GetContextItem())
{
this->Proxy->GetContextItem()->AddObserver(
vtkChart::UpdateRange, this->Forwarder.GetPointer());
}
}
void UpdateBounds()
{
if(this->Proxy && this->Proxy->GetContextItem())
{
for(int i=0; i < 4; i++)
{
// FIXME: Generalize to support charts with zero to many axes.
vtkChartXY *chart = vtkChartXY::SafeDownCast(this->Proxy->GetContextItem());
if (chart)
{
chart->GetAxis(i)->GetRange(&this->ViewBounds[i*2]);
}
}
}
}
public:
double ViewBounds[8];
vtkNew<vtkEventForwarderCommand> Forwarder;
private:
vtkWeakPointer<vtkSMContextViewProxy> Proxy;
vtkSMContextViewInteractorStyle() {}
~vtkSMContextViewInteractorStyle() {}
vtkWeakPointer<vtkSMContextViewProxy> ViewProxy;
};
vtkStandardNewMacro(vtkSMContextViewInteractorStyle);
//****************************************************************************
vtkStandardNewMacro(vtkSMContextViewProxy);
//----------------------------------------------------------------------------
vtkSMContextViewProxy::vtkSMContextViewProxy()
{
this->ChartView = NULL;
this->Storage = NULL;
}
//----------------------------------------------------------------------------
vtkSMContextViewProxy::~vtkSMContextViewProxy()
{
delete this->Storage;
this->Storage = NULL;
}
//----------------------------------------------------------------------------
......@@ -120,12 +96,22 @@ void vtkSMContextViewProxy::CreateVTKObjects()
vtkPVContextView* pvview = vtkPVContextView::SafeDownCast(
this->GetClientSideObject());
this->Storage = new Private;
this->ChartView = pvview->GetContextView();
// Try to attach viewport listener on chart
this->Storage->AttachCallback(this);
// if user interacts with the chart, we need to ensure that we set the axis
// behaviors to "FIXED" so that the user chosen axis ranges are preserved in
// state files, etc.
this->GetContextItem()->AddObserver(
vtkCommand::InteractionEvent, this,
&vtkSMContextViewProxy::OnInteractionEvent);
// update the interactor style.
vtkSMContextViewInteractorStyle* style =
vtkSMContextViewInteractorStyle::New();
style->SetScene(this->ChartView->GetScene());
style->SetView(this);
this->ChartView->GetInteractor()->SetInteractorStyle(style);
style->Delete();
}
//----------------------------------------------------------------------------
......@@ -147,30 +133,6 @@ vtkAbstractContextItem* vtkSMContextViewProxy::GetContextItem()
this->GetClientSideObject());
return pvview? pvview->GetContextItem() : NULL;
}
//-----------------------------------------------------------------------------
void vtkSMContextViewProxy::ResetDisplay()
{
// FIXME: We should generalize this to support all charts (zero to many axes).
vtkChartXY *chart = vtkChartXY::SafeDownCast(this->GetContextItem());
if (chart)
{
int previousBehaviour[4];
for (int i = 0; i < 4; ++i)
{
previousBehaviour[i] = chart->GetAxis(i)->GetBehavior();
chart->GetAxis(i)->SetBehavior(vtkAxis::AUTO);
}
chart->RecalculateBounds();
this->GetContextView()->Render();
// Revert behaviour as it use to be...
for (int i = 0; i < 4; ++i)
{
chart->GetAxis(i)->SetBehavior(previousBehaviour[i]);
}
}
}
//-----------------------------------------------------------------------------
vtkImageData* vtkSMContextViewProxy::CaptureWindowInternal(int magnification)
......@@ -208,47 +170,50 @@ vtkImageData* vtkSMContextViewProxy::CaptureWindowInternal(int magnification)
return capture;
}
//----------------------------------------------------------------------------
void vtkSMContextViewProxy::PrintSelf(ostream& os, vtkIndent indent)
static void update_property(vtkAxis* axis, vtkSMProperty* prop)
{
this->Superclass::PrintSelf(os, indent);
if (axis && prop)
{
double range[2];
axis->GetRange(range);
vtkSMPropertyHelper(prop).SetNumberOfElements(2);
vtkSMPropertyHelper(prop).Set(range, 2);
}
}
//----------------------------------------------------------------------------
double* vtkSMContextViewProxy::GetViewBounds()
void vtkSMContextViewProxy::OnInteractionEvent()
{
this->Storage->UpdateBounds();
return this->Storage->ViewBounds;
vtkChartXY *chartXY = vtkChartXY::SafeDownCast(this->GetContextItem());
if (chartXY)
{
// FIXME: Generalize to support charts with zero to many axes.
update_property(
chartXY->GetAxis(vtkAxis::LEFT), this->GetProperty("LeftAxisRange"));
update_property(
chartXY->GetAxis(vtkAxis::RIGHT), this->GetProperty("RightAxisRange"));
update_property(
chartXY->GetAxis(vtkAxis::TOP), this->GetProperty("TopAxisRange"));
update_property(
chartXY->GetAxis(vtkAxis::BOTTOM), this->GetProperty("BottomAxisRange"));
this->UpdateVTKObjects();
this->InvokeEvent(vtkCommand::InteractionEvent);
}
}
//----------------------------------------------------------------------------
void vtkSMContextViewProxy::SetViewBounds(double* bounds)
//-----------------------------------------------------------------------------
void vtkSMContextViewProxy::ResetDisplay()
{
if(this->GetContextItem())
{
// Disable notification...
this->Storage->Forwarder->SetTarget(NULL);
// FIXME: This also needs generalizing to support all chart types.
vtkChartXY *chart = vtkChartXY::SafeDownCast(this->GetContextItem());
if (chart)
{
for (int i = 0; i < 4; i++)
{
this->Storage->ViewBounds[i*2] = bounds[i*2];
this->Storage->ViewBounds[i*2+1] = bounds[i*2+1];
chart->GetAxis(i)->SetBehavior(vtkAxis::FIXED);
chart->GetAxis(i)->SetRange(bounds[i*2], bounds[i*2+1]);
chart->GetAxis(i)->RecalculateTickSpacing();
}
}
// Do the rendering with the new range
this->StillRender();
this->GetContextView()->Render();
vtkSMPropertyHelper(this, "LeftAxisRange", true).SetNumberOfElements(0);
vtkSMPropertyHelper(this, "RightAxisRange", true).SetNumberOfElements(0);
vtkSMPropertyHelper(this, "TopAxisRange", true).SetNumberOfElements(0);
vtkSMPropertyHelper(this, "BottomAxisRange", true).SetNumberOfElements(0);
this->UpdateVTKObjects();
this->StillRender();
}
// Bring the notification back
this->Storage->Forwarder->SetTarget(this);
}
//----------------------------------------------------------------------------
void vtkSMContextViewProxy::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}
......@@ -34,15 +34,6 @@ public:
vtkTypeMacro(vtkSMContextViewProxy, vtkSMViewProxy);
void PrintSelf(ostream& os, vtkIndent indent);
// Description:
// Return the bounds of the chart
double* GetViewBounds();
// Description:
// Change the bounds of the current chart and change its behavior to be FIXED
// FIXME: We shouldn't have to change the behavior.
void SetViewBounds(double*);
//BTX
// Description:
// Provides access to the vtk chart view.
......@@ -74,6 +65,11 @@ protected:
// Description:
virtual void CreateVTKObjects();
// Description:
// Used to update the axis range properties on each interaction event.
// This also fires the vtkCommand::InteractionEvent.
void OnInteractionEvent();
// Description:
// The context view that is used for all context derived charts.
vtkContextView* ChartView;
......@@ -81,12 +77,6 @@ protected:
private:
vtkSMContextViewProxy(const vtkSMContextViewProxy&); // Not implemented
void operator=(const vtkSMContextViewProxy&); // Not implemented
// Description:
// Private storage object.
class Private;
Private *Storage;
//ETX
};
......
......@@ -268,7 +268,8 @@ void pqCollaborationManager::onClientMessage(pqServer* server, vtkSMMessage* msg
viewId));
if(view && (this->Internals->AciveSession == server->session())) // Make sure we share the same active server
{
view->SetViewBounds(range);
// FIXME
// view->SetViewBounds(range);