mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
1657 lines
49 KiB
C++
1657 lines
49 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkChartXYZ.h
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm 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 "vtkChartXYZ.h"
|
|
|
|
#include "vtkAnnotationLink.h"
|
|
#include "vtkAxis.h"
|
|
#include "vtkCommand.h"
|
|
#include "vtkContext2D.h"
|
|
#include "vtkContext3D.h"
|
|
#include "vtkContextKeyEvent.h"
|
|
#include "vtkContextMouseEvent.h"
|
|
#include "vtkContextScene.h"
|
|
#include "vtkLookupTable.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkPen.h"
|
|
#include "vtkPlane.h"
|
|
#include "vtkPlaneCollection.h"
|
|
#include "vtkPlot3D.h"
|
|
#include "vtkTable.h"
|
|
#include "vtkTextProperty.h"
|
|
#include "vtkTransform.h"
|
|
#include "vtkVector.h"
|
|
#include "vtkVectorOperators.h"
|
|
#include "vtkSelection.h"
|
|
#include "vtkSelectionNode.h"
|
|
#include "vtkIdTypeArray.h"
|
|
|
|
#include "vtkObjectFactory.h"
|
|
|
|
#include <sstream>
|
|
|
|
vtkStandardNewMacro(vtkChartXYZ)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
vtkChartXYZ::vtkChartXYZ() : Geometry(0, 0, 10, 10), IsX(false), Angle(0)
|
|
{
|
|
this->Pen->SetWidth(5);
|
|
this->Pen->SetColor(0, 0, 0, 255);
|
|
this->AxisPen->SetWidth(1);
|
|
this->AxisPen->SetColor(0, 0, 0, 255);
|
|
this->Rotation->Identity();
|
|
this->Rotation->PostMultiply();
|
|
this->Translation->Identity();
|
|
this->Translation->PostMultiply();
|
|
this->Scale->Identity();
|
|
this->Scale->PostMultiply();
|
|
this->Interactive = true;
|
|
this->SceneWidth = 0;
|
|
this->SceneHeight = 0;
|
|
this->InitializeAxesBoundaryPoints();
|
|
this->AutoRotate = false;
|
|
this->DrawAxesDecoration = true;
|
|
this->FitToScene = true;
|
|
this->Axes.resize(3);
|
|
for(unsigned int i = 0; i < 3; ++i)
|
|
{
|
|
vtkNew<vtkAxis> axis;
|
|
this->Axes[i] = axis.GetPointer();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
vtkChartXYZ::~vtkChartXYZ()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::SetAngle(double angle)
|
|
{
|
|
this->Angle = angle;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::SetAroundX(bool IsX_)
|
|
{
|
|
this->IsX = IsX_;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::SetAutoRotate(bool b)
|
|
{
|
|
this->AutoRotate = b;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::SetDecorateAxes(bool b)
|
|
{
|
|
this->DrawAxesDecoration = b;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::SetAnnotationLink(vtkAnnotationLink *link)
|
|
{
|
|
if (this->Link != link)
|
|
{
|
|
this->Link = link;
|
|
this->Modified();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
vtkAxis * vtkChartXYZ::GetAxis(int axis)
|
|
{
|
|
assert(axis >= 0 && axis < 3);
|
|
return this->Axes[axis].GetPointer();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::SetAxisColor(const vtkColor4ub& color)
|
|
{
|
|
this->AxisPen->SetColor(color);
|
|
this->Modified();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
vtkColor4ub vtkChartXYZ::GetAxisColor()
|
|
{
|
|
return this->AxisPen->GetColorObject();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::SetGeometry(const vtkRectf &bounds)
|
|
{
|
|
this->Geometry = bounds;
|
|
|
|
this->Axes[0]->SetPoint1(vtkVector2f(this->Geometry.GetX(),
|
|
this->Geometry.GetY()));
|
|
this->Axes[0]->SetPoint2(vtkVector2f(this->Geometry.GetX() + this->Geometry.GetWidth(),
|
|
this->Geometry.GetY()));
|
|
|
|
this->Axes[1]->SetPoint1(vtkVector2f(this->Geometry.GetX(),
|
|
this->Geometry.GetY()));
|
|
this->Axes[1]->SetPoint2(vtkVector2f(this->Geometry.GetX(),
|
|
this->Geometry.GetY() + this->Geometry.GetHeight()));
|
|
|
|
// Z is faked, largely to get valid ranges and rounded numbers...
|
|
this->Axes[2]->SetPoint1(vtkVector2f(this->Geometry.GetX(),
|
|
0));
|
|
if (this->IsX)
|
|
{
|
|
this->Axes[2]->SetPoint2(vtkVector2f(this->Geometry.GetX(),
|
|
this->Geometry.GetHeight()));
|
|
}
|
|
else
|
|
{
|
|
this->Axes[2]->SetPoint2(vtkVector2f(this->Geometry.GetX(),
|
|
this->Geometry.GetWidth()));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::RecalculateBounds()
|
|
{
|
|
if (this->Plots.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
double bounds[] = { VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
|
|
VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
|
|
VTK_DOUBLE_MAX, VTK_DOUBLE_MIN};
|
|
|
|
// Need to calculate the bounds in three dimensions and set up the axes.
|
|
for (unsigned int i = 0; i < this->Plots.size(); ++i)
|
|
{
|
|
std::vector<vtkVector3f> const& points = this->Plots[i]->GetPoints();
|
|
for (unsigned int j = 0; j < points.size(); ++j)
|
|
{
|
|
const vtkVector3f &v = points[j];
|
|
for (int k = 0; k < 3; ++k)
|
|
{
|
|
if (v[k] < bounds[2 * k])
|
|
{
|
|
bounds[2 * k] = v[k];
|
|
}
|
|
if (v[k] > bounds[2 * k + 1])
|
|
{
|
|
bounds[2 * k + 1] = v[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
this->Axes[i]->SetUnscaledRange(&bounds[2*i]);
|
|
}
|
|
|
|
// Recalculate transform since axes' ranges were modified
|
|
this->RecalculateTransform();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::PrintSelf(ostream &os, vtkIndent indent)
|
|
{
|
|
Superclass::PrintSelf(os, indent);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::Update()
|
|
{
|
|
if (this->Link)
|
|
{
|
|
vtkSelection *selection =
|
|
vtkSelection::SafeDownCast(this->Link->GetOutputDataObject(2));
|
|
if (selection->GetNumberOfNodes())
|
|
{
|
|
vtkSelectionNode *node = selection->GetNode(0);
|
|
vtkIdTypeArray *idArray =
|
|
vtkIdTypeArray::SafeDownCast(node->GetSelectionList());
|
|
for (size_t i = 0; i < this->Plots.size(); ++i)
|
|
{
|
|
this->Plots[i]->SetSelection(idArray);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::Paint(vtkContext2D *painter)
|
|
{
|
|
if (!this->Visible)
|
|
return false;
|
|
|
|
this->Update();
|
|
|
|
// Get the 3D context.
|
|
vtkContext3D *context = painter->GetContext3D();
|
|
|
|
if (!context)
|
|
return false;
|
|
|
|
this->Update();
|
|
|
|
// Check if the scene changed size
|
|
bool resizeHappened = false;
|
|
if (this->FitToScene)
|
|
{
|
|
resizeHappened = this->CheckForSceneResize();
|
|
}
|
|
|
|
// Calculate the transforms required for the current rotation.
|
|
this->CalculateTransforms();
|
|
|
|
// Set up clipping planes
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
double planeEquation[4];
|
|
this->GetClippingPlaneEquation(i, planeEquation);
|
|
context->EnableClippingPlane(i, planeEquation);
|
|
}
|
|
|
|
// Draw plots
|
|
context->PushMatrix();
|
|
context->AppendTransform(this->ContextTransform.GetPointer());
|
|
this->PaintChildren(painter);
|
|
|
|
// Remove clipping planes
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
context->DisableClippingPlane(i);
|
|
}
|
|
|
|
// Calculate the bounds of the data within the axes
|
|
this->ComputeDataBounds();
|
|
|
|
// Pop the ContextTransform now that we're done drawing data within the axes
|
|
context->PopMatrix();
|
|
|
|
// Draw the axes, tick marks, and labels
|
|
this->DrawAxes(context);
|
|
if(this->DrawAxesDecoration)
|
|
{
|
|
this->DetermineWhichAxesToLabel();
|
|
this->DrawTickMarks(painter);
|
|
this->DrawAxesLabels(painter);
|
|
}
|
|
|
|
// If necessary, rescale the axes so they fits our scene nicely
|
|
if (resizeHappened)
|
|
{
|
|
this->RescaleAxes();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::DrawAxes(vtkContext3D *context)
|
|
{
|
|
context->PushMatrix();
|
|
context->AppendTransform(this->Box.GetPointer());
|
|
context->ApplyPen(this->AxisPen.GetPointer());
|
|
|
|
vtkVector3f box[4];
|
|
box[0] = vtkVector3f(0, 0, 0);
|
|
box[1] = vtkVector3f(0, 1, 0);
|
|
box[2] = vtkVector3f(1, 1, 0);
|
|
box[3] = vtkVector3f(1, 0, 0);
|
|
context->DrawLine(box[0], box[1]);
|
|
context->DrawLine(box[1], box[2]);
|
|
context->DrawLine(box[2], box[3]);
|
|
context->DrawLine(box[3], box[0]);
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
box[i].SetZ(1);
|
|
}
|
|
context->DrawLine(box[0], box[1]);
|
|
context->DrawLine(box[1], box[2]);
|
|
context->DrawLine(box[2], box[3]);
|
|
context->DrawLine(box[3], box[0]);
|
|
context->DrawLine(vtkVector3f(0, 0, 0), vtkVector3f(0, 0, 1));
|
|
context->DrawLine(vtkVector3f(1, 0, 0), vtkVector3f(1, 0, 1));
|
|
context->DrawLine(vtkVector3f(0, 1, 0), vtkVector3f(0, 1, 1));
|
|
context->DrawLine(vtkVector3f(1, 1, 0), vtkVector3f(1, 1, 1));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::ComputeDataBounds()
|
|
{
|
|
double xMin = VTK_DOUBLE_MAX;
|
|
double xMax = VTK_DOUBLE_MIN;
|
|
double yMin = VTK_DOUBLE_MAX;
|
|
double yMax = VTK_DOUBLE_MIN;
|
|
float transformedPoint[3];
|
|
|
|
for (unsigned int i = 0; i < this->Plots.size(); ++i)
|
|
{
|
|
vtkPlot3D *plot = this->Plots[i];
|
|
|
|
// examine the eight corners of this plot's bounding cube
|
|
for (unsigned int j = 0; j < 8; ++j)
|
|
{
|
|
this->ContextTransform->TransformPoint(
|
|
plot->GetDataBounds()[j].GetData(), transformedPoint);
|
|
|
|
if (transformedPoint[0] < xMin)
|
|
{
|
|
xMin = transformedPoint[0];
|
|
}
|
|
if (transformedPoint[0] > xMax)
|
|
{
|
|
xMax = transformedPoint[0];
|
|
}
|
|
if (transformedPoint[1] < yMin)
|
|
{
|
|
yMin = transformedPoint[1];
|
|
}
|
|
if (transformedPoint[1] > yMax)
|
|
{
|
|
yMax = transformedPoint[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
this->DataBounds[0] = xMin;
|
|
this->DataBounds[1] = yMin;
|
|
this->DataBounds[2] = xMax;
|
|
this->DataBounds[3] = yMax;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::DrawAxesLabels(vtkContext2D *painter)
|
|
{
|
|
vtkContext3D *context = painter->GetContext3D();
|
|
|
|
// set up text property
|
|
vtkNew<vtkTextProperty> textProperties;
|
|
textProperties->SetJustificationToCentered();
|
|
textProperties->SetVerticalJustificationToCentered();
|
|
textProperties->SetColor(0.0, 0.0, 0.0);
|
|
textProperties->SetFontFamilyToArial();
|
|
textProperties->SetFontSize(14);
|
|
painter->ApplyTextProp(textProperties.GetPointer());
|
|
|
|
// if we're looking directly down any dimension, we shouldn't draw the
|
|
// corresponding label
|
|
bool shouldDrawAxis[3];
|
|
for (int axis = 0; axis < 3; ++axis)
|
|
{
|
|
shouldDrawAxis[axis] = true;
|
|
float start[3] = { 0, 0, 0 };
|
|
float end[3] = { 0, 0, 0 };
|
|
end[axis] = 1;
|
|
this->Box->TransformPoint(start, start);
|
|
this->Box->TransformPoint(end, end);
|
|
float axisLength = sqrt(
|
|
(end[0] - start[0]) * (end[0] - start[0]) +
|
|
(end[1] - start[1]) * (end[1] - start[1]));
|
|
if (axisLength == 0)
|
|
{
|
|
shouldDrawAxis[axis] = false;
|
|
}
|
|
}
|
|
|
|
float bounds[4];
|
|
float xLabelPos[3];
|
|
float yLabelPos[3];
|
|
float zLabelPos[3];
|
|
float offset[2] = {0, 0};
|
|
|
|
// calculate the pixel coordinates of the lines we wish to label
|
|
if (shouldDrawAxis[0])
|
|
{
|
|
xLabelPos[0] = 0.5;
|
|
xLabelPos[1] = this->XAxisToLabel[0];
|
|
xLabelPos[2] = this->XAxisToLabel[1];
|
|
this->Box->TransformPoint(xLabelPos, xLabelPos);
|
|
}
|
|
if (shouldDrawAxis[1])
|
|
{
|
|
yLabelPos[0] = this->YAxisToLabel[0];
|
|
yLabelPos[1] = 0.5;
|
|
yLabelPos[2] = this->YAxisToLabel[1];
|
|
this->Box->TransformPoint(yLabelPos, yLabelPos);
|
|
}
|
|
if (shouldDrawAxis[2])
|
|
{
|
|
zLabelPos[0] = this->ZAxisToLabel[0];
|
|
zLabelPos[1] = this->ZAxisToLabel[1];
|
|
zLabelPos[2] = 0.5;
|
|
this->Box->TransformPoint(zLabelPos, zLabelPos);
|
|
}
|
|
|
|
context->PopMatrix();
|
|
|
|
if (shouldDrawAxis[0])
|
|
{
|
|
painter->ComputeStringBounds(this->XAxisLabel, bounds);
|
|
this->GetOffsetForAxisLabel(0, bounds, offset);
|
|
xLabelPos[0] += (offset[0] + this->TickLabelOffset[0][0]);
|
|
xLabelPos[1] += (offset[1] + this->TickLabelOffset[0][1]);
|
|
painter->DrawString(xLabelPos[0], xLabelPos[1], this->XAxisLabel);
|
|
}
|
|
|
|
if (shouldDrawAxis[1])
|
|
{
|
|
painter->ComputeStringBounds(this->YAxisLabel, bounds);
|
|
offset[0] = 0;
|
|
offset[1] = 0;
|
|
this->GetOffsetForAxisLabel(1, bounds, offset);
|
|
yLabelPos[0] += (offset[0] + this->TickLabelOffset[1][0]);
|
|
yLabelPos[1] += (offset[1] + this->TickLabelOffset[1][1]);
|
|
painter->DrawString(yLabelPos[0], yLabelPos[1], this->YAxisLabel);
|
|
}
|
|
|
|
if (shouldDrawAxis[2])
|
|
{
|
|
painter->ComputeStringBounds(this->ZAxisLabel, bounds);
|
|
offset[0] = 0;
|
|
offset[1] = 0;
|
|
this->GetOffsetForAxisLabel(2, bounds, offset);
|
|
zLabelPos[0] += (offset[0] + this->TickLabelOffset[2][0]);
|
|
zLabelPos[1] += (offset[1] + this->TickLabelOffset[2][1]);
|
|
painter->DrawString(zLabelPos[0], zLabelPos[1], this->ZAxisLabel);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::GetOffsetForAxisLabel(int axis, float *bounds,
|
|
float *offset)
|
|
{
|
|
offset[0] = 0;
|
|
offset[1] = 0;
|
|
switch (this->DirectionToData[axis])
|
|
{
|
|
// data is to the north
|
|
// offset is -y
|
|
case 0:
|
|
offset[1] = -bounds[3];
|
|
break;
|
|
|
|
// data is northeast
|
|
// offset is -x, -y
|
|
case 1:
|
|
offset[0] = -bounds[2];
|
|
offset[1] = -bounds[3];
|
|
break;
|
|
|
|
// data is east
|
|
// offset is -x
|
|
case 2:
|
|
offset[0] = -bounds[2];
|
|
break;
|
|
|
|
// data is southeast
|
|
// offset is -x, +y
|
|
case 3:
|
|
offset[0] = -bounds[2];
|
|
offset[1] = bounds[3];
|
|
break;
|
|
|
|
// data is south
|
|
// offset is +y
|
|
case 4:
|
|
offset[1] = bounds[3];
|
|
break;
|
|
|
|
// data is southwest
|
|
// offset is +x, +y
|
|
case 5:
|
|
offset[0] = bounds[2];
|
|
offset[1] = bounds[3];
|
|
break;
|
|
|
|
// data is west
|
|
// offset is +y
|
|
case 6:
|
|
offset[0] = bounds[2];
|
|
break;
|
|
|
|
// data is northwest
|
|
// offset is +x, -y
|
|
case 7:
|
|
default:
|
|
offset[0] = bounds[2];
|
|
offset[1] = -bounds[3];
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::DrawTickMarks(vtkContext2D *painter)
|
|
{
|
|
vtkContext3D *context = painter->GetContext3D();
|
|
float bounds[4];
|
|
|
|
// draw points instead of lines
|
|
context->ApplyPen(this->Pen.GetPointer());
|
|
|
|
// treat each axis separately
|
|
for (int axis = 0; axis < 3; ++axis)
|
|
{
|
|
// pop matrix since we'll be drawing text in 2D before we draw the
|
|
// actual tick marks
|
|
context->PopMatrix();
|
|
float labelOffset[2] = { 0, 0 };
|
|
|
|
// initialize start and end of the axis to label in box coordinates
|
|
double startBox[3] = { 0, 0, 0 };
|
|
double endBox[3] = { 0, 0, 0 };
|
|
switch (axis)
|
|
{
|
|
case 0:
|
|
startBox[0] = 0;
|
|
endBox[0] = 1;
|
|
startBox[1] = endBox[1] = this->XAxisToLabel[0];
|
|
startBox[2] = endBox[2] = this->XAxisToLabel[1];
|
|
break;
|
|
case 1:
|
|
startBox[0] = this->YAxisToLabel[0];
|
|
startBox[1] = 0;
|
|
endBox[1] = 1;
|
|
startBox[2] = endBox[2] = this->YAxisToLabel[1];
|
|
break;
|
|
case 2:
|
|
default:
|
|
startBox[0] = endBox[0] = this->ZAxisToLabel[0];
|
|
startBox[1] = endBox[1] = this->ZAxisToLabel[1];
|
|
startBox[2] = 0;
|
|
endBox[2] = 1;
|
|
break;
|
|
}
|
|
|
|
// convert these values to pixel coordinates
|
|
double start[3];
|
|
double end[3];
|
|
this->Box->TransformPoint(startBox, start);
|
|
this->Box->TransformPoint(endBox, end);
|
|
|
|
// ...and then into data coordinates
|
|
this->ContextTransform->GetInverse()->TransformPoint(start, start);
|
|
this->ContextTransform->GetInverse()->TransformPoint(end, end);
|
|
|
|
// get "nice" values for min, max, and spacing (again, in data coordinates)
|
|
double tickSpacing =
|
|
this->CalculateNiceMinMax(start[axis], end[axis], axis);
|
|
|
|
if (tickSpacing == -1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
std::vector < vtkVector3f > tickPoints;
|
|
int currentTick = 0;
|
|
float tickPositionAlongAxis = start[axis];
|
|
while (tickPositionAlongAxis < end[axis])
|
|
{
|
|
vtkVector3f tick;
|
|
// convert tick position back into box coordinates
|
|
// during this process, we save the tick position in pixels for labeling
|
|
float tickPosition[3];
|
|
tickPosition[0] = start[0];
|
|
tickPosition[1] = start[1];
|
|
tickPosition[2] = start[2];
|
|
tickPosition[axis] = tickPositionAlongAxis;
|
|
float tickPositionInPixels[3];
|
|
this->ContextTransform->TransformPoint(tickPosition,
|
|
tickPositionInPixels);
|
|
this->Box->GetInverse()->TransformPoint(tickPositionInPixels,
|
|
tickPosition);
|
|
|
|
// determine the location of this tick mark and push it onto the vector
|
|
// if it falls within the bounds of the axis
|
|
tick[0] = startBox[0];
|
|
tick[1] = startBox[1];
|
|
tick[2] = startBox[2];
|
|
tick[axis] = tickPosition[axis];
|
|
|
|
if (tick[axis] >= startBox[axis] && tick[axis] <= endBox[axis])
|
|
{
|
|
tickPoints.push_back(tick);
|
|
|
|
// get the tick mark label
|
|
std::stringstream sstream;
|
|
sstream << std::fixed << setprecision(1) << tickPositionAlongAxis;
|
|
std::string tickLabel = sstream.str();
|
|
|
|
// offset the label from the axis
|
|
float offset[2] = {0, 0};
|
|
painter->ComputeStringBounds(tickLabel, bounds);
|
|
this->GetOffsetForAxisLabel(axis, bounds, offset);
|
|
tickPositionInPixels[0] += offset[0];
|
|
tickPositionInPixels[1] += offset[1];
|
|
|
|
// we store this offset so we know where to draw the axis label later
|
|
if (fabs(offset[0]) > fabs(labelOffset[0]))
|
|
{
|
|
labelOffset[0] = offset[0];
|
|
}
|
|
if (fabs(offset[1]) > fabs(labelOffset[1]))
|
|
{
|
|
labelOffset[1] = offset[1];
|
|
}
|
|
|
|
// draw the label for this tick mark
|
|
painter->DrawString(tickPositionInPixels[0], tickPositionInPixels[1],
|
|
tickLabel);
|
|
}
|
|
++currentTick;
|
|
tickPositionAlongAxis = start[axis] + (tickSpacing * currentTick);
|
|
}
|
|
|
|
// re-apply the Box matrix and draw the tick marks as points
|
|
if (tickPoints.size() != 0)
|
|
{
|
|
context->PushMatrix();
|
|
context->AppendTransform(this->Box.GetPointer());
|
|
context->DrawPoints(tickPoints[0].GetData(),
|
|
static_cast<int>(tickPoints.size()));
|
|
this->TickLabelOffset[axis][0] = labelOffset[0];
|
|
this->TickLabelOffset[axis][1] = labelOffset[1];
|
|
}
|
|
}
|
|
|
|
//revert from drawing points.
|
|
context->ApplyPen(this->AxisPen.GetPointer());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::DetermineWhichAxesToLabel()
|
|
{
|
|
// for each dimension (XYZ)
|
|
for (int axis = 0; axis < 3; ++axis)
|
|
{
|
|
double maxDistance = -1;
|
|
// for each of the four "axis" lines corresponding to this dimension
|
|
for (float i = 0; i < 2; ++i)
|
|
{
|
|
for (float j = 0; j < 2; ++j)
|
|
{
|
|
for (float k = 0; k < 2; ++k)
|
|
{
|
|
// convert this line's midpoint to screen (pixel) coordinates
|
|
float midpoint[3] = { i, j, k };
|
|
midpoint[axis] = 0.5;
|
|
this->Box->TransformPoint(midpoint, midpoint);
|
|
|
|
// ignore any lines whose midpoint falls within the data range.
|
|
// we increment the iterators so we don't evaluate the same line
|
|
// twice.
|
|
if (midpoint[0] > this->DataBounds[0] &&
|
|
midpoint[1] > this->DataBounds[1] &&
|
|
midpoint[0] < this->DataBounds[2] &&
|
|
midpoint[1] < this->DataBounds[3])
|
|
{
|
|
switch (axis)
|
|
{
|
|
case 0:
|
|
++i;
|
|
break;
|
|
case 1:
|
|
++j;
|
|
break;
|
|
case 2:
|
|
++k;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// calculate the distance from this line's midpoint to the data range
|
|
double d = 0;
|
|
int directionToData = 0;
|
|
|
|
// case 1: midpoint falls within x range (but not y)
|
|
if (midpoint[0] > this->DataBounds[0] &&
|
|
midpoint[0] < this->DataBounds[2])
|
|
{
|
|
double d1 = fabs(midpoint[1] - this->DataBounds[1]);
|
|
double d2 = fabs(midpoint[1] - this->DataBounds[3]);
|
|
if (d1 < d2)
|
|
{
|
|
directionToData = 0; // data is "up" from the axis
|
|
d = d1;
|
|
}
|
|
else
|
|
{
|
|
directionToData = 4; // data is "down" from the axis
|
|
d = d2;
|
|
}
|
|
}
|
|
|
|
// case 2: midpoint falls within y range (but not x)
|
|
else if (midpoint[1] > this->DataBounds[1] &&
|
|
midpoint[1] < this->DataBounds[3])
|
|
{
|
|
double d1 = fabs(midpoint[0] - this->DataBounds[0]);
|
|
double d2 = fabs(midpoint[0] - this->DataBounds[2]);
|
|
if (d1 < d2)
|
|
{
|
|
directionToData = 2; // data is "right" from the axis
|
|
d = d1;
|
|
}
|
|
else
|
|
{
|
|
directionToData = 6; // data is "left" from the axis
|
|
d = d2;
|
|
}
|
|
}
|
|
|
|
// case 3: compute distance to nearest corner
|
|
else
|
|
{
|
|
//x min, y min
|
|
d = sqrt( (this->DataBounds[0] - midpoint[0]) *
|
|
(this->DataBounds[0] - midpoint[0]) +
|
|
(this->DataBounds[1] - midpoint[1]) *
|
|
(this->DataBounds[1] - midpoint[1]) );
|
|
directionToData = 1; // data is to the northeast
|
|
|
|
//x min, y max
|
|
double d0 =
|
|
sqrt( (this->DataBounds[0] - midpoint[0]) *
|
|
(this->DataBounds[0] - midpoint[0]) +
|
|
(this->DataBounds[3] - midpoint[1]) *
|
|
(this->DataBounds[3] - midpoint[1]) );
|
|
if (d0 < d)
|
|
{
|
|
d = d0;
|
|
directionToData = 3; // data is to the southeast
|
|
}
|
|
//x max, y min
|
|
d0 = sqrt( (this->DataBounds[2] - midpoint[0]) *
|
|
(this->DataBounds[2] - midpoint[0]) +
|
|
(this->DataBounds[1] - midpoint[1]) *
|
|
(this->DataBounds[1] - midpoint[1]) );
|
|
if (d0 < d)
|
|
{
|
|
d = d0;
|
|
directionToData = 7; // data is to the northwest
|
|
}
|
|
//x max, y max
|
|
d0 = sqrt( (this->DataBounds[2] - midpoint[0]) *
|
|
(this->DataBounds[2] - midpoint[0]) +
|
|
(this->DataBounds[3] - midpoint[1]) *
|
|
(this->DataBounds[3] - midpoint[1]) );
|
|
if (d0 < d)
|
|
{
|
|
d = d0;
|
|
directionToData = 5; // data is to the southwest
|
|
}
|
|
|
|
// Test if the data falls within the bounds of our axis line,
|
|
// despite the fact that it is diagonal from the line's midpoint.
|
|
// This is performed to determine how the label should be offset
|
|
// from the line. To do this, we transform the line's start and
|
|
// end point to pixel coordinates.
|
|
float start[3] = { i, j, k };
|
|
start[axis] = 0;
|
|
this->Box->TransformPoint(start, start);
|
|
float end[3] = { i, j, k };
|
|
end[axis] = 1;
|
|
this->Box->TransformPoint(end, end);
|
|
|
|
if (start[0] < this->DataBounds[0] && end[0] > this->DataBounds[2])
|
|
{
|
|
// data falls within horizontal range of this axis line
|
|
// set directionToData as purely up or purely down
|
|
if (directionToData == 1 || directionToData == 7)
|
|
{
|
|
directionToData = 0;
|
|
}
|
|
else
|
|
{
|
|
directionToData = 4;
|
|
}
|
|
}
|
|
else if (start[1] < this->DataBounds[1] &&
|
|
end[1] > this->DataBounds[3])
|
|
{
|
|
// data falls within vertical range of this axis line
|
|
// set directionToData as purely left or purely right
|
|
if (directionToData == 1 || directionToData == 3)
|
|
{
|
|
directionToData = 2;
|
|
}
|
|
else
|
|
{
|
|
directionToData = 6;
|
|
}
|
|
}
|
|
}
|
|
|
|
// record this axis line if it has the greatest distance to the data
|
|
if (d > maxDistance)
|
|
{
|
|
this->DirectionToData[axis] = directionToData;
|
|
maxDistance = d;
|
|
switch (axis)
|
|
{
|
|
case 0:
|
|
this->XAxisToLabel[0] = static_cast<int>(j);
|
|
this->XAxisToLabel[1] = static_cast<int>(k);
|
|
break;
|
|
case 1:
|
|
this->YAxisToLabel[0] = static_cast<int>(i);
|
|
this->YAxisToLabel[1] = static_cast<int>(k);
|
|
break;
|
|
case 2:
|
|
default:
|
|
this->ZAxisToLabel[0] = static_cast<int>(i);
|
|
this->ZAxisToLabel[1] = static_cast<int>(j);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// these three cases keep us from evaluating the same line twice.
|
|
if (axis == 2)
|
|
{
|
|
++k;
|
|
}
|
|
}
|
|
if (axis == 1)
|
|
{
|
|
++j;
|
|
}
|
|
}
|
|
if (axis == 0)
|
|
{
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::Hit(const vtkContextMouseEvent& vtkNotUsed(mouse))
|
|
{
|
|
if (!this->Interactive || !this->Visible || this->AutoRotate)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::MouseButtonPressEvent(const vtkContextMouseEvent
|
|
&mouse)
|
|
{
|
|
if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::MouseMoveEvent(const vtkContextMouseEvent &mouse)
|
|
{
|
|
if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
|
|
{
|
|
if (mouse.GetModifiers() == vtkContextMouseEvent::SHIFT_MODIFIER)
|
|
{
|
|
return this->Spin(mouse);
|
|
}
|
|
else
|
|
{
|
|
return this->Rotate(mouse);
|
|
}
|
|
}
|
|
if (mouse.GetButton() == vtkContextMouseEvent::RIGHT_BUTTON)
|
|
{
|
|
if (mouse.GetModifiers() == vtkContextMouseEvent::SHIFT_MODIFIER)
|
|
{
|
|
return this->Pan(mouse);
|
|
}
|
|
else
|
|
{
|
|
return this->Zoom(mouse);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::MouseWheelEvent(const vtkContextMouseEvent&,
|
|
int delta)
|
|
{
|
|
// Ten "wheels" to double/halve zoom level
|
|
float scaling = pow(2.0f, delta/10.0f);
|
|
this->Scale->Scale(scaling, scaling, scaling);
|
|
|
|
// Mark the scene as dirty
|
|
this->Scene->SetDirty(true);
|
|
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::ZoomAxes(int delta)
|
|
{
|
|
float scaling = pow(2.0f, delta/10.0f);
|
|
this->BoxScale->Scale(scaling, scaling, scaling);
|
|
|
|
// Mark the scene as dirty
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::Rotate(const vtkContextMouseEvent &mouse)
|
|
{
|
|
// avoid NaNs in our transformation matrix if the scene has not yet been
|
|
// rendered.
|
|
if (this->Scene->GetSceneHeight() == 0 || this->Scene->GetSceneWidth() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Figure out how much the mouse has moved in plot coordinates
|
|
vtkVector2d screenPos(mouse.GetScreenPos().Cast<double>().GetData());
|
|
vtkVector2d lastScreenPos(mouse.GetLastScreenPos().Cast<double>().GetData());
|
|
|
|
double dx = screenPos[0] - lastScreenPos[0];
|
|
double dy = screenPos[1] - lastScreenPos[1];
|
|
|
|
double delta_elevation = -20.0 / this->Scene->GetSceneHeight();
|
|
double delta_azimuth = -20.0 / this->Scene->GetSceneWidth();
|
|
|
|
double rxf = -dx * delta_azimuth * 10.0;
|
|
double ryf = -dy * delta_elevation * 10.0;
|
|
|
|
this->Rotation->RotateY(rxf);
|
|
this->Rotation->RotateX(-ryf);
|
|
|
|
// Mark the scene as dirty
|
|
this->Scene->SetDirty(true);
|
|
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::Pan(const vtkContextMouseEvent &mouse)
|
|
{
|
|
// Figure out how much the mouse has moved in plot coordinates
|
|
vtkVector2d screenPos(mouse.GetScreenPos().Cast<double>().GetData());
|
|
vtkVector2d lastScreenPos(mouse.GetLastScreenPos().Cast<double>().GetData());
|
|
|
|
double dx = (screenPos[0] - lastScreenPos[0]);
|
|
double dy = (screenPos[1] - lastScreenPos[1]);
|
|
|
|
this->Translation->Translate(dx, dy, 0.0);
|
|
|
|
// Mark the scene as dirty
|
|
this->Scene->SetDirty(true);
|
|
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::Zoom(const vtkContextMouseEvent &mouse)
|
|
{
|
|
// Figure out how much the mouse has moved and scale accordingly
|
|
vtkVector2d screenPos(mouse.GetScreenPos().Cast<double>().GetData());
|
|
vtkVector2d lastScreenPos(mouse.GetLastScreenPos().Cast<double>().GetData());
|
|
|
|
float delta = 0.0f;
|
|
if (this->Scene->GetSceneHeight() > 0)
|
|
{
|
|
delta = static_cast<float>(
|
|
mouse.GetLastScreenPos()[1] - mouse.GetScreenPos()[1]) /
|
|
this->Scene->GetSceneHeight();
|
|
}
|
|
|
|
// Dragging full screen height zooms 4x.
|
|
float scaling = pow(4.0f, delta);
|
|
this->Scale->Scale(scaling, scaling, scaling);
|
|
|
|
// Mark the scene as dirty
|
|
this->Scene->SetDirty(true);
|
|
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::Spin(const vtkContextMouseEvent &mouse)
|
|
{
|
|
// Figure out how much the mouse has moved in plot coordinates
|
|
vtkVector2d screenPos(mouse.GetScreenPos().Cast<double>().GetData());
|
|
vtkVector2d lastScreenPos(mouse.GetLastScreenPos().Cast<double>().GetData());
|
|
|
|
double newAngle =
|
|
vtkMath::DegreesFromRadians(atan2(screenPos[1], screenPos[0]));
|
|
double oldAngle =
|
|
vtkMath::DegreesFromRadians(atan2(lastScreenPos[1], lastScreenPos[0]));
|
|
|
|
this->Rotation->RotateZ(-(newAngle - oldAngle));
|
|
|
|
// Mark the scene as dirty
|
|
this->Scene->SetDirty(true);
|
|
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::KeyPressEvent(const vtkContextKeyEvent &key)
|
|
{
|
|
switch (key.GetKeyCode())
|
|
{
|
|
// Change view to 2D, YZ chart
|
|
case 'x':
|
|
this->LookDownX();
|
|
break;
|
|
case 'X':
|
|
this->LookUpX();
|
|
break;
|
|
// Change view to 2D, XZ chart
|
|
case 'y':
|
|
this->LookDownY();
|
|
break;
|
|
case 'Y':
|
|
this->LookUpY();
|
|
break;
|
|
// Change view to 2D, XY chart
|
|
case 'z':
|
|
this->LookDownZ();
|
|
break;
|
|
case 'Z':
|
|
this->LookUpZ();
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::LookDownX()
|
|
{
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
this->Rotation->Identity();
|
|
this->Rotation->RotateY(-90.0);
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::LookDownY()
|
|
{
|
|
this->Rotation->Identity();
|
|
this->Rotation->RotateX(90.0);
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::LookDownZ()
|
|
{
|
|
this->Rotation->Identity();
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::LookUpX()
|
|
{
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
this->Rotation->Identity();
|
|
this->Rotation->RotateY(90.0);
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::LookUpY()
|
|
{
|
|
this->Rotation->Identity();
|
|
this->Rotation->RotateX(-90.0);
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::LookUpZ()
|
|
{
|
|
this->Rotation->Identity();
|
|
this->Rotation->RotateY(180.0);
|
|
this->InvokeEvent(vtkCommand::InteractionEvent);
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::CalculateTransforms()
|
|
{
|
|
// Calculate the correct translation vector so that rotation and scale
|
|
// are applied about the middle of the axes box.
|
|
vtkVector3f translation(
|
|
(this->Axes[0]->GetPosition2()[0] - this->Axes[0]->GetPosition1()[0]) / 2.0
|
|
+ this->Axes[0]->GetPosition1()[0],
|
|
(this->Axes[1]->GetPosition2()[1] - this->Axes[1]->GetPosition1()[1]) / 2.0
|
|
+ this->Axes[1]->GetPosition1()[1],
|
|
(this->Axes[2]->GetPosition2()[1] - this->Axes[2]->GetPosition1()[1]) / 2.0
|
|
+ this->Axes[2]->GetPosition1()[1]);
|
|
vtkVector3f mtranslation = -1.0 * translation;
|
|
|
|
this->ContextTransform->Identity();
|
|
this->ContextTransform->Concatenate(this->Translation.GetPointer());
|
|
this->ContextTransform->Translate(translation.GetData());
|
|
this->ContextTransform->Concatenate(this->Rotation.GetPointer());
|
|
this->ContextTransform->Concatenate(this->BoxScale.GetPointer());
|
|
if (this->AutoRotate)
|
|
{
|
|
if (this->IsX)
|
|
{
|
|
this->ContextTransform->RotateX(this->Angle);
|
|
}
|
|
else
|
|
{
|
|
this->ContextTransform->RotateY(this->Angle);
|
|
}
|
|
}
|
|
this->ContextTransform->Concatenate(this->Scale.GetPointer());
|
|
this->ContextTransform->Translate(mtranslation.GetData());
|
|
this->ContextTransform->Translate(
|
|
this->Axes[0]->GetPosition1()[0] - this->Geometry.GetX(),
|
|
this->Axes[1]->GetPosition1()[1] - this->Geometry.GetY(),
|
|
this->Axes[2]->GetPosition1()[1]);
|
|
this->ContextTransform->Concatenate(this->PlotTransform.GetPointer());
|
|
|
|
// Next construct the transform for the box axes.
|
|
double scale[3] = { 300, 300, 300 };
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
scale[i] = this->Axes[i]->GetPosition2()[0] -
|
|
this->Axes[i]->GetPosition1()[0];
|
|
}
|
|
else
|
|
{
|
|
scale[i] = this->Axes[i]->GetPosition2()[1] -
|
|
this->Axes[i]->GetPosition1()[1];
|
|
}
|
|
}
|
|
|
|
this->Box->Identity();
|
|
this->Box->PostMultiply();
|
|
this->Box->Translate(-0.5, -0.5, -0.5);
|
|
this->Box->Concatenate(this->Rotation.GetPointer());
|
|
this->Box->Concatenate(this->BoxScale.GetPointer());
|
|
if (this->AutoRotate)
|
|
{
|
|
if (this->IsX)
|
|
{
|
|
this->Box->RotateX(this->Angle);
|
|
}
|
|
else
|
|
{
|
|
this->Box->RotateY(this->Angle);
|
|
}
|
|
}
|
|
this->Box->Translate(0.5, 0.5, 0.5);
|
|
this->Box->Scale(scale);
|
|
this->Box->Translate(Axes[0]->GetPosition1()[0],
|
|
Axes[1]->GetPosition1()[1],
|
|
Axes[2]->GetPosition1()[1]);
|
|
|
|
// setup clipping planes
|
|
this->BoundingCube->RemoveAllItems();
|
|
vtkVector3d cube[8];
|
|
vtkVector3d transformedCube[8];
|
|
|
|
cube[0] = vtkVector3d(0, 0, 0);
|
|
cube[1] = vtkVector3d(0, 0, 1);
|
|
cube[2] = vtkVector3d(0, 1, 0);
|
|
cube[3] = vtkVector3d(0, 1, 1);
|
|
cube[4] = vtkVector3d(1, 0, 0);
|
|
cube[5] = vtkVector3d(1, 0, 1);
|
|
cube[6] = vtkVector3d(1, 1, 0);
|
|
cube[7] = vtkVector3d(1, 1, 1);
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
this->Box->TransformPoint(cube[i].GetData(), transformedCube[i].GetData());
|
|
}
|
|
|
|
double norm1[3];
|
|
double norm2[3];
|
|
double norm3[3];
|
|
double norm4[3];
|
|
double norm5[3];
|
|
double norm6[3];
|
|
|
|
//face 0,1,2,3 opposes face 4,5,6,7
|
|
vtkNew<vtkPlane> face1;
|
|
vtkMath::Cross((transformedCube[2] - transformedCube[0]).GetData(),
|
|
(transformedCube[1] - transformedCube[0]).GetData(), norm1);
|
|
vtkMath::Normalize(norm1);
|
|
face1->SetNormal(norm1);
|
|
face1->SetOrigin(transformedCube[3].GetData());
|
|
this->BoundingCube->AddItem(face1.GetPointer());
|
|
|
|
vtkNew<vtkPlane> face2;
|
|
vtkMath::Cross((transformedCube[5] - transformedCube[4]).GetData(),
|
|
(transformedCube[6] - transformedCube[4]).GetData(), norm2);
|
|
vtkMath::Normalize(norm2);
|
|
face2->SetNormal(norm2);
|
|
face2->SetOrigin(transformedCube[7].GetData());
|
|
this->BoundingCube->AddItem(face2.GetPointer());
|
|
|
|
//face 0,1,4,5 opposes face 2,3,6,7
|
|
vtkNew<vtkPlane> face3;
|
|
vtkMath::Cross((transformedCube[1] - transformedCube[0]).GetData(),
|
|
(transformedCube[4] - transformedCube[0]).GetData(), norm3);
|
|
vtkMath::Normalize(norm3);
|
|
face3->SetNormal(norm3);
|
|
face3->SetOrigin(transformedCube[5].GetData());
|
|
this->BoundingCube->AddItem(face3.GetPointer());
|
|
|
|
vtkNew<vtkPlane> face4;
|
|
vtkMath::Cross((transformedCube[6] - transformedCube[2]).GetData(),
|
|
(transformedCube[3] - transformedCube[2]).GetData(), norm4);
|
|
vtkMath::Normalize(norm4);
|
|
face4->SetNormal(norm4);
|
|
face4->SetOrigin(transformedCube[7].GetData());
|
|
this->BoundingCube->AddItem(face4.GetPointer());
|
|
|
|
//face 0,2,4,6 opposes face 1,3,5,7
|
|
vtkNew<vtkPlane> face5;
|
|
vtkMath::Cross((transformedCube[4] - transformedCube[0]).GetData(),
|
|
(transformedCube[2] - transformedCube[0]).GetData(), norm5);
|
|
vtkMath::Normalize(norm5);
|
|
face5->SetNormal(norm5);
|
|
face5->SetOrigin(transformedCube[6].GetData());
|
|
this->BoundingCube->AddItem(face5.GetPointer());
|
|
|
|
vtkNew<vtkPlane> face6;
|
|
vtkMath::Cross((transformedCube[3] - transformedCube[1]).GetData(),
|
|
(transformedCube[5] - transformedCube[1]).GetData(), norm6);
|
|
vtkMath::Normalize(norm6);
|
|
face6->SetNormal(norm6);
|
|
face6->SetOrigin(transformedCube[7].GetData());
|
|
this->BoundingCube->AddItem(face6.GetPointer());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::ScaleUpAxes()
|
|
{
|
|
float point[3];
|
|
int sceneWidth = this->Scene->GetSceneWidth();
|
|
int sceneHeight = this->Scene->GetSceneHeight();
|
|
float scaleStep = pow(2.0f, 1.0f/10.0f);
|
|
float stepBack = pow(2.0f, -1.0f/10.0f);
|
|
int numSteps = 0;
|
|
bool shouldScaleUp = true;
|
|
|
|
while (shouldScaleUp)
|
|
{
|
|
for (int i = 0; i < 14; ++i)
|
|
{
|
|
point[0] = this->AxesBoundaryPoints[i][0];
|
|
point[1] = this->AxesBoundaryPoints[i][1];
|
|
point[2] = this->AxesBoundaryPoints[i][2];
|
|
this->FutureBox->TransformPoint(point, point);
|
|
if (point[0] < 0 || point[0] > sceneWidth ||
|
|
point[1] < 0 || point[1] > sceneHeight)
|
|
{
|
|
shouldScaleUp = false;
|
|
}
|
|
}
|
|
if (shouldScaleUp)
|
|
{
|
|
this->FutureBoxScale->Scale(scaleStep, scaleStep, scaleStep);
|
|
++numSteps;
|
|
if (numSteps > 500)
|
|
{
|
|
// Break out of the loop.
|
|
shouldScaleUp = false;
|
|
}
|
|
}
|
|
}
|
|
// this while loop overshoots the mark by one step,
|
|
// so we take a step back afterwards.
|
|
this->FutureBoxScale->Scale(stepBack, stepBack, stepBack);
|
|
|
|
if (numSteps > 1)
|
|
{
|
|
this->ZoomAxes(numSteps - 1);
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::ScaleDownAxes()
|
|
{
|
|
float point[3];
|
|
int sceneWidth = this->Scene->GetSceneWidth();
|
|
int sceneHeight = this->Scene->GetSceneHeight();
|
|
|
|
float scaleStep = pow(2.0f, -1.0f/10.0f);
|
|
int numSteps = 0;
|
|
bool shouldScaleDown = true;
|
|
|
|
while (shouldScaleDown)
|
|
{
|
|
shouldScaleDown = false;
|
|
for (int i = 0; i < 14; ++i)
|
|
{
|
|
point[0] = this->AxesBoundaryPoints[i][0];
|
|
point[1] = this->AxesBoundaryPoints[i][1];
|
|
point[2] = this->AxesBoundaryPoints[i][2];
|
|
this->FutureBox->TransformPoint(point, point);
|
|
if (point[0] < 0 || point[0] > sceneWidth ||
|
|
point[1] < 0 || point[1] > sceneHeight)
|
|
{
|
|
shouldScaleDown = true;
|
|
break;
|
|
}
|
|
}
|
|
if (shouldScaleDown)
|
|
{
|
|
this->FutureBoxScale->Scale(scaleStep, scaleStep, scaleStep);
|
|
++numSteps;
|
|
if (numSteps > 500)
|
|
{
|
|
// Break out of the loop.
|
|
shouldScaleDown = false;
|
|
}
|
|
}
|
|
}
|
|
if (numSteps > 0)
|
|
{
|
|
this->ZoomAxes(-numSteps);
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::InitializeFutureBox()
|
|
{
|
|
double scale[3] = { 300, 300, 300 };
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
if (i == 0)
|
|
scale[i] = this->Axes[i]->GetPosition2()[0] -
|
|
this->Axes[i]->GetPosition1()[0];
|
|
else
|
|
scale[i] = this->Axes[i]->GetPosition2()[1] -
|
|
this->Axes[i]->GetPosition1()[1];
|
|
}
|
|
|
|
this->FutureBoxScale->DeepCopy(this->BoxScale.GetPointer());
|
|
|
|
this->FutureBox->Identity();
|
|
this->FutureBox->PostMultiply();
|
|
this->FutureBox->Translate(-0.5, -0.5, -0.5);
|
|
this->FutureBox->Concatenate(this->Rotation.GetPointer());
|
|
this->FutureBox->Concatenate(this->FutureBoxScale.GetPointer());
|
|
this->FutureBox->Translate(0.5, 0.5, 0.5);
|
|
this->FutureBox->Scale(scale);
|
|
this->FutureBox->Translate(this->Axes[0]->GetPosition1()[0],
|
|
this->Axes[1]->GetPosition1()[1],
|
|
this->Axes[2]->GetPosition1()[1]);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::CheckForSceneResize()
|
|
{
|
|
int currentWidth = this->Scene->GetSceneWidth();
|
|
int currentHeight = this->Scene->GetSceneHeight();
|
|
if (this->SceneWidth != currentWidth ||
|
|
this->SceneHeight != currentHeight)
|
|
{
|
|
// treat the initial render as a special case, as the scene size
|
|
// has not been recorded yet
|
|
if (this->SceneWidth > 0)
|
|
{
|
|
int dx = (currentWidth - this->SceneWidth) / 2;
|
|
int dy = (currentHeight - this->SceneHeight) / 2;
|
|
|
|
vtkVector2f axisPt = this->Axes[0]->GetPosition1();
|
|
axisPt[0] += dx;
|
|
axisPt[1] += dy;
|
|
this->Axes[0]->SetPoint1(axisPt);
|
|
axisPt = this->Axes[0]->GetPosition2();
|
|
axisPt[0] += dx;
|
|
axisPt[1] += dy;
|
|
this->Axes[0]->SetPoint2(axisPt);
|
|
axisPt = this->Axes[1]->GetPosition1();
|
|
axisPt[0] += dx;
|
|
axisPt[1] += dy;
|
|
this->Axes[1]->SetPoint1(axisPt);
|
|
axisPt = this->Axes[1]->GetPosition2();
|
|
axisPt[0] += dx;
|
|
axisPt[1] += dy;
|
|
this->Axes[1]->SetPoint2(axisPt);
|
|
axisPt = this->Axes[2]->GetPosition1();
|
|
axisPt[0] += dx;
|
|
this->Axes[2]->SetPoint1(axisPt);
|
|
axisPt = this->Axes[2]->GetPosition2();
|
|
axisPt[0] += dx;
|
|
this->Axes[2]->SetPoint2(axisPt);
|
|
this->RecalculateTransform();
|
|
}
|
|
else
|
|
{
|
|
this->SceneWidth = currentWidth;
|
|
this->SceneHeight = currentHeight;
|
|
this->InitializeFutureBox();
|
|
this->ScaleUpAxes();
|
|
this->ScaleDownAxes();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::RescaleAxes()
|
|
{
|
|
int currentWidth = this->Scene->GetSceneWidth();
|
|
int currentHeight = this->Scene->GetSceneHeight();
|
|
this->InitializeFutureBox();
|
|
if (currentWidth * currentHeight < this->SceneWidth * this->SceneHeight)
|
|
{
|
|
this->ScaleDownAxes();
|
|
}
|
|
else
|
|
{
|
|
this->ScaleUpAxes();
|
|
}
|
|
this->SceneWidth = currentWidth;
|
|
this->SceneHeight = currentHeight;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::InitializeAxesBoundaryPoints()
|
|
{
|
|
int currentPoint = 0;
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
for (int j = 0; j < 2; ++j)
|
|
{
|
|
for (int k = 0; k < 2; ++k)
|
|
{
|
|
this->AxesBoundaryPoints[currentPoint][0] = i;
|
|
this->AxesBoundaryPoints[currentPoint][1] = j;
|
|
this->AxesBoundaryPoints[currentPoint][2] = k;
|
|
++currentPoint;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
this->AxesBoundaryPoints[currentPoint][0] = 0.5;
|
|
this->AxesBoundaryPoints[currentPoint][1] = 0.5;
|
|
this->AxesBoundaryPoints[currentPoint][2] = 0.5;
|
|
this->AxesBoundaryPoints[currentPoint][i] += sqrt(0.75);
|
|
++currentPoint;
|
|
this->AxesBoundaryPoints[currentPoint][0] = 0.5;
|
|
this->AxesBoundaryPoints[currentPoint][1] = 0.5;
|
|
this->AxesBoundaryPoints[currentPoint][2] = 0.5;
|
|
this->AxesBoundaryPoints[currentPoint][i] -= sqrt(0.75);
|
|
++currentPoint;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
double vtkChartXYZ::CalculateNiceMinMax(double &min, double &max,
|
|
int axis)
|
|
{
|
|
// Calculate an upper limit on the number of tick marks - at least 30 pixels
|
|
// should be between each tick mark.
|
|
float start[3] = { 0, 0, 0 };
|
|
float end[3] = { 0, 0, 0 };
|
|
end[axis] = 1;
|
|
|
|
this->Box->TransformPoint(start, start);
|
|
this->Box->TransformPoint(end, end);
|
|
|
|
float pixelRange = sqrt(
|
|
(end[0] - start[0]) * (end[0] - start[0]) +
|
|
(end[1] - start[1]) * (end[1] - start[1]));
|
|
|
|
return vtkAxis::NiceMinMax(min, max, pixelRange, 30.0f);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::RecalculateTransform()
|
|
{
|
|
this->CalculatePlotTransform(this->Axes[0].GetPointer(),
|
|
this->Axes[1].GetPointer(),
|
|
this->Axes[2].GetPointer(),
|
|
this->PlotTransform.GetPointer());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool vtkChartXYZ::CalculatePlotTransform(vtkAxis *x, vtkAxis *y, vtkAxis *z,
|
|
vtkTransform *transform)
|
|
{
|
|
// Need to calculate the 3D transform this time.
|
|
assert(x && y && z && transform);
|
|
|
|
// Get the scale for the plot area from the x and y axes
|
|
float *min = x->GetPoint1();
|
|
float *max = x->GetPoint2();
|
|
if (fabs(max[0] - min[0]) == 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
float xScale =
|
|
(x->GetUnscaledMaximum() - x->GetUnscaledMinimum()) / (max[0] - min[0]);
|
|
|
|
// Now the y axis
|
|
min = y->GetPoint1();
|
|
max = y->GetPoint2();
|
|
if (fabs(max[1] - min[1]) == 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
float yScale =
|
|
(y->GetUnscaledMaximum() - y->GetUnscaledMinimum()) / (max[1] - min[1]);
|
|
|
|
// Now the z axis
|
|
min = z->GetPoint1();
|
|
max = z->GetPoint2();
|
|
if (fabs(max[1] - min[1]) == 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
float zScale =
|
|
(z->GetUnscaledMaximum() - z->GetUnscaledMinimum()) / (max[1] - min[1]);
|
|
|
|
transform->Identity();
|
|
transform->Translate(this->Geometry.GetX(), this->Geometry.GetY(), 0);
|
|
// Get the scale for the plot area from the x and y axes
|
|
transform->Scale(1.0 / xScale, 1.0 / yScale, 1.0 / zScale);
|
|
transform->Translate(
|
|
-x->GetUnscaledMinimum(),
|
|
-y->GetUnscaledMinimum(),
|
|
-z->GetUnscaledMinimum());
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
vtkIdType vtkChartXYZ::AddPlot(vtkPlot3D * plot)
|
|
{
|
|
if (plot == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
this->AddItem(plot);
|
|
plot->SetChart(this);
|
|
this->Plots.push_back(plot);
|
|
vtkIdType plotIndex = this->Plots.size() - 1;
|
|
|
|
// the first plot added to the chart defines the names of the axes
|
|
if (plotIndex == 0)
|
|
{
|
|
this->XAxisLabel = plot->GetXAxisLabel();
|
|
this->YAxisLabel = plot->GetYAxisLabel();
|
|
this->ZAxisLabel = plot->GetZAxisLabel();
|
|
}
|
|
|
|
this->RecalculateBounds();
|
|
|
|
// Mark the scene as dirty
|
|
if (this->Scene)
|
|
{
|
|
this->Scene->SetDirty(true);
|
|
}
|
|
return plotIndex;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::ClearPlots()
|
|
{
|
|
this->ClearItems();
|
|
this->Plots.clear();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::SetFitToScene(bool b)
|
|
{
|
|
this->FitToScene = b;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkChartXYZ::GetClippingPlaneEquation(int i, double *planeEquation)
|
|
{
|
|
int n = this->BoundingCube->GetNumberOfItems();
|
|
if (i >= 0 && i < n)
|
|
{
|
|
// Get the plane
|
|
vtkPlane *plane = this->BoundingCube->GetItem(i);
|
|
double *normal = plane->GetNormal();
|
|
double *origin = plane->GetOrigin();
|
|
|
|
// Compute the plane equation
|
|
planeEquation[0] = normal[0];
|
|
planeEquation[1] = normal[1];
|
|
planeEquation[2] = normal[2];
|
|
planeEquation[3] = -(normal[0] * origin[0] +
|
|
normal[1] * origin[1] +
|
|
normal[2] * origin[2]);
|
|
}
|
|
}
|