/*========================================================================= Program: Visualization Toolkit Module: vtkBalloonRepresentation.cxx 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 "vtkBalloonRepresentation.h" #include "vtkActor2D.h" #include "vtkCellArray.h" #include "vtkFloatArray.h" #include "vtkImageData.h" #include "vtkInteractorObserver.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPoints.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper2D.h" #include "vtkProperty2D.h" #include "vtkRenderer.h" #include "vtkTextActor.h" #include "vtkTextMapper.h" #include "vtkTextProperty.h" #include "vtkTexture.h" #include "vtkTexturedActor2D.h" #include "vtkWindow.h" vtkStandardNewMacro(vtkBalloonRepresentation); vtkCxxSetObjectMacro(vtkBalloonRepresentation, TextProperty, vtkTextProperty); vtkCxxSetObjectMacro(vtkBalloonRepresentation, FrameProperty, vtkProperty2D); vtkCxxSetObjectMacro(vtkBalloonRepresentation, BalloonImage, vtkImageData); vtkCxxSetObjectMacro(vtkBalloonRepresentation, ImageProperty, vtkProperty2D); //------------------------------------------------------------------------------ vtkBalloonRepresentation::vtkBalloonRepresentation() { // Initially we are not visible this->Visibility = 0; this->TextVisible = 0; this->ImageVisible = 0; // Balloon related this->BalloonText = nullptr; this->BalloonImage = nullptr; this->BalloonLayout = ImageTop; // Displaying the image in the balloon using texture. Create a quad polygon // and apply the texture on top of it. this->ImageSize[0] = 50; this->ImageSize[1] = 50; this->Texture = vtkTexture::New(); this->TexturePolyData = vtkPolyData::New(); this->TexturePoints = vtkPoints::New(); this->TexturePoints->SetNumberOfPoints(4); this->TexturePolyData->SetPoints(this->TexturePoints); vtkCellArray* polys = vtkCellArray::New(); polys->InsertNextCell(4); polys->InsertCellPoint(0); polys->InsertCellPoint(1); polys->InsertCellPoint(2); polys->InsertCellPoint(3); this->TexturePolyData->SetPolys(polys); polys->Delete(); vtkFloatArray* tc = vtkFloatArray::New(); tc->SetNumberOfComponents(2); tc->SetNumberOfTuples(4); tc->InsertComponent(0, 0, 0.0); tc->InsertComponent(0, 1, 0.0); tc->InsertComponent(1, 0, 1.0); tc->InsertComponent(1, 1, 0.0); tc->InsertComponent(2, 0, 1.0); tc->InsertComponent(2, 1, 1.0); tc->InsertComponent(3, 0, 0.0); tc->InsertComponent(3, 1, 1.0); this->TexturePolyData->GetPointData()->SetTCoords(tc); tc->Delete(); this->TextureMapper = vtkPolyDataMapper2D::New(); this->TextureMapper->SetInputData(this->TexturePolyData); this->TextureActor = vtkTexturedActor2D::New(); this->TextureActor->SetMapper(this->TextureMapper); this->TextureActor->SetTexture(this->Texture); this->ImageProperty = vtkProperty2D::New(); this->ImageProperty->SetOpacity(1.0); this->TextureActor->SetProperty(this->ImageProperty); // Controlling layout this->Padding = 5; this->Offset[0] = 15; this->Offset[1] = -30; // The text actor this->TextMapper = vtkTextMapper::New(); this->TextActor = vtkActor2D::New(); this->TextActor->SetMapper(this->TextMapper); this->TextProperty = vtkTextProperty::New(); this->TextProperty->SetColor(0, 0, 0); this->TextProperty->SetFontSize(14); this->TextProperty->BoldOn(); this->TextMapper->SetTextProperty(this->TextProperty); // The frame this->FramePoints = vtkPoints::New(); this->FramePoints->SetNumberOfPoints(4); this->FramePolygon = vtkCellArray::New(); this->FramePolygon->AllocateEstimate(1, 5); this->FramePolygon->InsertNextCell(4); this->FramePolygon->InsertCellPoint(0); this->FramePolygon->InsertCellPoint(1); this->FramePolygon->InsertCellPoint(2); this->FramePolygon->InsertCellPoint(3); this->FramePolyData = vtkPolyData::New(); this->FramePolyData->SetPoints(this->FramePoints); this->FramePolyData->SetPolys(this->FramePolygon); this->FrameMapper = vtkPolyDataMapper2D::New(); this->FrameMapper->SetInputData(this->FramePolyData); this->FrameActor = vtkActor2D::New(); this->FrameActor->SetMapper(this->FrameMapper); this->FrameProperty = vtkProperty2D::New(); this->FrameProperty->SetColor(1, 1, .882); this->FrameProperty->SetOpacity(0.5); this->FrameActor->SetProperty(this->FrameProperty); } //------------------------------------------------------------------------------ vtkBalloonRepresentation::~vtkBalloonRepresentation() { delete[] this->BalloonText; if (this->BalloonImage) { this->BalloonImage->Delete(); } this->Texture->Delete(); this->TexturePolyData->Delete(); this->TexturePoints->Delete(); this->TextureMapper->Delete(); this->TextureActor->Delete(); this->ImageProperty->Delete(); this->TextMapper->Delete(); this->TextActor->Delete(); this->TextProperty->Delete(); // The frame this->FramePoints->Delete(); this->FramePolygon->Delete(); this->FramePolyData->Delete(); this->FrameMapper->Delete(); this->FrameActor->Delete(); this->FrameProperty->Delete(); } //------------------------------------------------------------------------------ void vtkBalloonRepresentation::StartWidgetInteraction(double e[2]) { this->StartEventPosition[0] = e[0]; this->StartEventPosition[1] = e[1]; this->VisibilityOn(); } //------------------------------------------------------------------------------ void vtkBalloonRepresentation::EndWidgetInteraction(double vtkNotUsed(e)[2]) { this->VisibilityOff(); } //------------------------------------------------------------------------------ inline void vtkBalloonRepresentation::AdjustImageSize(double imageSize[2]) { double r0 = this->ImageSize[0] / imageSize[0]; double r1 = this->ImageSize[1] / imageSize[1]; if (r0 > r1) { imageSize[0] *= r1; imageSize[1] *= r1; } else { imageSize[0] *= r0; imageSize[1] *= r0; } } //------------------------------------------------------------------------------ inline void vtkBalloonRepresentation::ScaleImage(double imageSize[2], double scale) { imageSize[0] *= scale; imageSize[1] *= scale; } //------------------------------------------------------------------------------ void vtkBalloonRepresentation::BuildRepresentation() { if (this->GetMTime() > this->BuildTime || (this->Renderer && this->Renderer->GetVTKWindow() && this->Renderer->GetVTKWindow()->GetMTime() > this->BuildTime)) { this->TextVisible = 0; this->ImageVisible = 0; int size[2]; size[0] = (this->Renderer->GetSize())[0]; size[1] = (this->Renderer->GetSize())[1]; int stringSize[2]; stringSize[0] = stringSize[1] = 0; double imageSize[2]; imageSize[0] = imageSize[1] = 0.0; double frameSize[2]; frameSize[0] = frameSize[1] = 0.0; double io[2], so[2], fo[2]; io[0] = 0.0; io[1] = 0.0; so[0] = 0.0; so[1] = 0.0; fo[0] = 0.0; fo[1] = 0.0; double e[2]; e[0] = static_cast(this->StartEventPosition[0] + this->Offset[0]); e[1] = static_cast(this->StartEventPosition[1] + this->Offset[1]); // Determine the size of the text if (this->BalloonText) { // Start by getting the size of the text this->TextMapper->SetInput(this->BalloonText); this->TextMapper->GetSize(this->Renderer, stringSize); this->TextVisible = ((stringSize[0] > 0 && stringSize[1] > 0) ? 1 : 0); } // Determine the size of the image if (this->BalloonImage) { // this->BalloonImage->Update(); if (this->BalloonImage->GetDataDimension() == 2) { int dims[3]; this->BalloonImage->GetDimensions(dims); imageSize[0] = static_cast(dims[0]); imageSize[1] = static_cast(dims[1]); this->ImageVisible = ((imageSize[0] > 0.0 && imageSize[1] > 0.0) ? 1 : 0); } } // Layout the text and image if (this->TextVisible || this->ImageVisible) { if (this->TextVisible && !this->ImageVisible) // just text { frameSize[0] = static_cast(stringSize[0] + 2 * this->Padding); frameSize[1] = static_cast(stringSize[1] + 2 * this->Padding); fo[0] = 0.0; fo[1] = 0.0; so[0] = static_cast(this->Padding); so[1] = static_cast(this->Padding); } else if (this->ImageVisible && !this->TextVisible) // just image { this->AdjustImageSize(imageSize); io[0] = 0.0; io[1] = 0.0; } else // both image and text { this->AdjustImageSize(imageSize); if (this->BalloonLayout == ImageTop) { frameSize[1] = stringSize[1] + 2 * this->Padding; double length = (imageSize[0] > (stringSize[0] + 2 * this->Padding) ? imageSize[0] : (stringSize[0] + 2 * this->Padding)); frameSize[0] = length; double scale = length / imageSize[0]; this->ScaleImage(imageSize, scale); io[0] = 0.0; io[1] = frameSize[1]; fo[0] = 0.0; fo[1] = 0.0; so[0] = length / 2.0 - stringSize[0] / 2.0; so[1] = this->Padding; } else if (this->BalloonLayout == ImageBottom) { frameSize[1] = stringSize[1] + 2 * this->Padding; double length = (imageSize[0] > (stringSize[0] + 2 * this->Padding) ? imageSize[0] : (stringSize[0] + 2 * this->Padding)); frameSize[0] = length; double scale = length / imageSize[0]; this->ScaleImage(imageSize, scale); io[0] = 0.0; io[1] = 0.0; fo[0] = 0.0; fo[1] = imageSize[1]; so[0] = length / 2.0 - stringSize[0] / 2.0; so[1] = imageSize[1] + this->Padding; } else if (this->BalloonLayout == ImageLeft) { frameSize[0] = stringSize[0] + 2 * this->Padding; double length = (imageSize[1] > (stringSize[1] + 2 * this->Padding) ? imageSize[1] : (stringSize[1] + 2 * this->Padding)); frameSize[1] = length; double scale = length / imageSize[1]; this->ScaleImage(imageSize, scale); io[0] = 0.0; io[1] = 0.0; fo[0] = imageSize[0]; fo[1] = 0.0; so[0] = imageSize[0] + this->Padding; so[1] = length / 2.0 - stringSize[1] / 2.0; } else if (this->BalloonLayout == ImageRight) { frameSize[0] = stringSize[0] + 2 * this->Padding; double length = (imageSize[1] > (stringSize[1] + 2 * this->Padding) ? imageSize[1] : (stringSize[1] + 2 * this->Padding)); frameSize[1] = length; double scale = length / imageSize[1]; this->ScaleImage(imageSize, scale); io[0] = frameSize[0]; io[1] = 0.0; fo[0] = 0.0; fo[1] = 0.0; so[0] = this->Padding; so[1] = length / 2.0 - stringSize[1] / 2.0; } } // Reposition the origin of the balloon if it's off the renderer if (e[0] < 0) { e[0] = 0.0; } if (e[1] < 0) { e[1] = 0.0; } if ((e[0] + frameSize[0] + imageSize[0]) > size[0]) { e[0] = size[0] - (frameSize[0] + imageSize[0]); } if ((e[1] + frameSize[1] + imageSize[1]) > size[1]) { e[1] = size[1] - (frameSize[1] + imageSize[1]); } // Draw the text if visible if (this->TextVisible) { this->FramePoints->SetPoint(0, e[0] + fo[0], e[1] + fo[1], 0.0); this->FramePoints->SetPoint(1, e[0] + fo[0] + frameSize[0], e[1] + fo[1], 0.0); this->FramePoints->SetPoint( 2, e[0] + fo[0] + frameSize[0], e[1] + fo[1] + frameSize[1], 0.0); this->FramePoints->SetPoint(3, e[0] + fo[0], e[1] + fo[1] + frameSize[1], 0.0); this->FramePoints->Modified(); this->TextActor->SetPosition(e[0] + so[0], e[1] + so[1]); } // Place the texture if (this->ImageVisible) { this->Texture->SetInputData(this->BalloonImage); this->TexturePoints->SetPoint(0, e[0] + io[0], e[1] + io[1], 0.0); this->TexturePoints->SetPoint(1, e[0] + io[0] + imageSize[0], e[1] + io[1], 0.0); this->TexturePoints->SetPoint( 2, e[0] + io[0] + imageSize[0], e[1] + io[1] + imageSize[1], 0.0); this->TexturePoints->SetPoint(3, e[0] + io[0], e[1] + io[1] + imageSize[1], 0.0); this->TexturePoints->Modified(); } } // if something visible // Update the properties this->TextureActor->SetProperty(this->ImageProperty); this->FrameActor->SetProperty(this->FrameProperty); this->TextMapper->SetTextProperty(this->TextProperty); this->BuildTime.Modified(); } } //------------------------------------------------------------------------------ void vtkBalloonRepresentation::ReleaseGraphicsResources(vtkWindow* w) { this->Texture->ReleaseGraphicsResources(w); this->TextActor->ReleaseGraphicsResources(w); this->FrameActor->ReleaseGraphicsResources(w); this->TextureActor->ReleaseGraphicsResources(w); } //------------------------------------------------------------------------------ int vtkBalloonRepresentation::RenderOverlay(vtkViewport* v) { int count = 0; this->BuildRepresentation(); if (this->TextVisible) { count += this->FrameActor->RenderOverlay(v); count += this->TextActor->RenderOverlay(v); } if (this->ImageVisible) { vtkRenderer* ren = vtkRenderer::SafeDownCast(v); if (ren) { count += this->TextureActor->RenderOverlay(v); } } return count; } //------------------------------------------------------------------------------ int vtkBalloonRepresentation::ComputeInteractionState(int X, int Y, int) { // Is it in the text region or the image region? double x0[3], x2[3]; int origin[2] = { 0, 0 }; if (this->Renderer) { origin[0] = (this->Renderer->GetOrigin())[0]; origin[1] = (this->Renderer->GetOrigin())[1]; } if (this->ImageVisible) { this->TexturePoints->GetPoint(0, x0); this->TexturePoints->GetPoint(2, x2); for (int i = 0; i < 2; ++i) { x0[i] += origin[i]; x2[i] += origin[i]; } if ((x0[0] <= X && X <= x2[0]) && (x0[1] <= Y && Y <= x2[1])) { return vtkBalloonRepresentation::OnImage; } } if (this->TextVisible) { this->FramePoints->GetPoint(0, x0); this->FramePoints->GetPoint(2, x2); for (int i = 0; i < 2; ++i) { x0[i] += origin[i]; x2[i] += origin[i]; } if ((x0[0] <= X && X <= x2[0]) && (x0[1] <= Y && Y <= x2[1])) { return vtkBalloonRepresentation::OnText; } } return vtkBalloonRepresentation::Outside; } //------------------------------------------------------------------------------ void vtkBalloonRepresentation::PrintSelf(ostream& os, vtkIndent indent) { // Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h this->Superclass::PrintSelf(os, indent); os << indent << "Balloon Text: "; if (this->BalloonText) { os << this->BalloonText << "\n"; } else { os << "(none)\n"; } os << indent << "Balloon Image: "; if (this->BalloonImage) { os << this->BalloonImage << "\n"; } else { os << "(none)\n"; } os << indent << "Balloon Layout: "; switch (this->BalloonLayout) { case ImageLeft: os << "Image Left\n"; break; case ImageRight: os << "Image Right\n"; break; case ImageBottom: os << "Image Bottom\n"; break; default: os << "Image Top\n"; } os << indent << "Image Size: (" << this->ImageSize[0] << "," << this->ImageSize[1] << ")\n"; os << indent << "Padding: " << this->Padding << "\n"; os << indent << "Offset: (" << this->Offset[0] << "," << this->Offset[1] << ")\n"; if (this->FrameProperty) { os << indent << "Frame Property:\n"; this->FrameProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Frame Property: (none)\n"; } if (this->ImageProperty) { os << indent << "Image Property:\n"; this->ImageProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Image Property: (none)\n"; } if (this->TextProperty) { os << indent << "Text Property:\n"; this->TextProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Text Property: (none)\n"; } }