/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkLegendBoxActor.cxx,v $ 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 "vtkLegendBoxActor.h" #include "vtkActor.h" #include "vtkCellArray.h" #include "vtkDoubleArray.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkPolyDataMapper2D.h" #include "vtkProperty2D.h" #include "vtkTextMapper.h" #include "vtkTextProperty.h" #include "vtkTransform.h" #include "vtkTransformPolyDataFilter.h" #include "vtkViewport.h" vtkCxxRevisionMacro(vtkLegendBoxActor, "$Revision: 1.30 $"); vtkStandardNewMacro(vtkLegendBoxActor); vtkCxxSetObjectMacro(vtkLegendBoxActor,EntryTextProperty,vtkTextProperty); //---------------------------------------------------------------------------- vtkLegendBoxActor::vtkLegendBoxActor() { // Positioning information this->PositionCoordinate->SetCoordinateSystemToNormalizedViewport(); this->PositionCoordinate->SetValue(0.75, 0.75); this->Position2Coordinate->SetValue(0.2, 0.2); this->LockBorder = 0; this->ScalarVisibility = 1; // Control font properties this->EntryTextProperty = vtkTextProperty::New(); this->EntryTextProperty->SetBold(0); this->EntryTextProperty->SetItalic(0); this->EntryTextProperty->SetShadow(0); this->EntryTextProperty->SetFontFamily(VTK_ARIAL); this->EntryTextProperty->SetJustification(VTK_TEXT_LEFT); this->EntryTextProperty->SetVerticalJustification(VTK_TEXT_CENTERED); this->Border = 1; this->Box = 0; this->Padding = 3; // Symbols and text strings this->NumberOfEntries = 0; this->Size = 0; this->Colors = NULL; this->Symbol = NULL; this->Transform = NULL; this->SymbolTransform = NULL; this->SymbolMapper = NULL; this->SymbolActor = NULL; this->TextMapper = NULL; this->TextActor = NULL; // Construct the border this->BorderPolyData = vtkPolyData::New(); vtkPoints *points = vtkPoints::New(); points->SetNumberOfPoints(4); this->BorderPolyData->SetPoints(points); points->Delete(); vtkCellArray *lines = vtkCellArray::New(); lines->InsertNextCell(5); //points will be updated later lines->InsertCellPoint(0); lines->InsertCellPoint(1); lines->InsertCellPoint(2); lines->InsertCellPoint(3); lines->InsertCellPoint(0); this->BorderPolyData->SetLines(lines); lines->Delete(); this->BorderMapper = vtkPolyDataMapper2D::New(); this->BorderMapper->SetInput(this->BorderPolyData); this->BorderActor = vtkActor2D::New(); this->BorderActor->SetMapper(this->BorderMapper); // Construct the box this->BoxPolyData = vtkPolyData::New(); this->BoxPolyData->SetPoints(this->BorderPolyData->GetPoints()); vtkCellArray *polys = vtkCellArray::New(); polys->InsertNextCell(4); polys->InsertCellPoint(0); polys->InsertCellPoint(1); polys->InsertCellPoint(2); polys->InsertCellPoint(3); this->BoxPolyData->SetPolys(polys); polys->Delete(); this->BoxMapper = vtkPolyDataMapper2D::New(); this->BoxMapper->SetInput(this->BoxPolyData); this->BoxActor = vtkActor2D::New(); this->BoxActor->SetMapper(this->BoxMapper); } //---------------------------------------------------------------------------- vtkLegendBoxActor::~vtkLegendBoxActor() { this->InitializeEntries(); if ( this->BorderActor ) { this->BorderActor->Delete(); this->BorderMapper->Delete(); this->BorderPolyData->Delete(); } if ( this->BoxActor ) { this->BoxActor->Delete(); this->BoxMapper->Delete(); this->BoxPolyData->Delete(); } this->SetEntryTextProperty(NULL); } //---------------------------------------------------------------------------- void vtkLegendBoxActor::InitializeEntries() { int i; if ( this->Size > 0 ) { this->Colors->Delete(); for (i=0; iSize; i++) { if ( this->Symbol[i] ) { this->Symbol[i]->Delete(); } this->Transform[i]->Delete(); this->SymbolTransform[i]->Delete(); this->SymbolMapper[i]->Delete(); this->SymbolActor[i]->Delete(); if ( this->TextMapper[i] ) { this->TextMapper[i]->Delete(); this->TextActor[i]->Delete(); } }//for all entries delete [] this->Symbol; this->Symbol = NULL; delete [] this->Transform; this->Transform = NULL; delete [] this->SymbolTransform; this->SymbolTransform = NULL; delete [] this->SymbolMapper; this->SymbolMapper = NULL; delete [] this->SymbolActor; this->SymbolActor = NULL; delete [] this->TextMapper; this->TextMapper = NULL; delete [] this->TextActor; this->TextActor = NULL; }//if entries have been defined } //---------------------------------------------------------------------------- void vtkLegendBoxActor::SetNumberOfEntries(int num) { if ( num == this->NumberOfEntries ) { return; } else if ( num < this->Size ) { this->NumberOfEntries = num; } else //allocate space { int i; //Create internal actors, etc. vtkDoubleArray *colors = vtkDoubleArray::New(); colors->SetNumberOfComponents(3); colors->SetNumberOfTuples(num); vtkTextMapper **textMapper= new vtkTextMapper* [num]; vtkActor2D **textActor = new vtkActor2D* [num]; vtkPolyData **symbol = new vtkPolyData* [num]; vtkTransform **transform= new vtkTransform* [num]; vtkTransformPolyDataFilter **symbolTransform = new vtkTransformPolyDataFilter* [num]; vtkPolyDataMapper2D **symbolMapper = new vtkPolyDataMapper2D* [num]; vtkActor2D **symbolActor = new vtkActor2D* [num]; //copy old values for (i=0; iNumberOfEntries; i++) { colors->SetTuple(i,this->Colors->GetTuple(i)); textMapper[i] = this->TextMapper[i]; textMapper[i]->Register(this); textActor[i] = this->TextActor[i]; textActor[i]->Register(this); symbol[i] = this->Symbol[i]; if ( symbol[i] ) { symbol[i]->Register(this); } transform[i] = this->Transform[i]; transform[i]->Register(this); symbolTransform[i] = this->SymbolTransform[i]; symbolTransform[i]->Register(this); symbolMapper[i] = this->SymbolMapper[i]; symbolMapper[i]->Register(this); symbolActor[i] = this->SymbolActor[i]; symbolActor[i]->Register(this); } //initialize data values static double color[3]={-1.0,-1.0,-1.0}; for (i=this->NumberOfEntries; iSetTuple(i,color); textMapper[i] = vtkTextMapper::New(); textActor[i] = vtkActor2D::New(); textActor[i]->SetMapper(textMapper[i]); symbol[i] = NULL; transform[i] = vtkTransform::New(); symbolTransform[i] = vtkTransformPolyDataFilter::New(); symbolTransform[i]->SetTransform(transform[i]); symbolMapper[i] = vtkPolyDataMapper2D::New(); symbolMapper[i]->SetInput(symbolTransform[i]->GetOutput()); symbolActor[i] = vtkActor2D::New(); symbolActor[i]->SetMapper(symbolMapper[i]); } //Clear out the old stuff this->InitializeEntries(); //Bring everything up to date this->NumberOfEntries = this->Size = num; this->Colors = colors; this->TextMapper = textMapper; this->TextActor = textActor; this->Symbol = symbol; this->Transform = transform; this->SymbolTransform = symbolTransform; this->SymbolMapper = symbolMapper; this->SymbolActor = symbolActor; } this->Modified(); return; } //---------------------------------------------------------------------------- void vtkLegendBoxActor::SetEntry(int i, vtkPolyData *symbol, const char* string, double color[3]) { if ( i >= 0 && i < this->NumberOfEntries ) { this->SetEntrySymbol(i,symbol); this->SetEntryString(i,string); this->SetEntryColor(i,color); } return; } //---------------------------------------------------------------------------- void vtkLegendBoxActor::SetEntrySymbol(int i, vtkPolyData *symbol) { if ( i >= 0 && i < this->NumberOfEntries ) { if ( this->Symbol[i] == symbol ) { return; } if ( this->Symbol[i] ) { this->Symbol[i]->Delete(); } this->Symbol[i] = symbol; if ( this->Symbol[i] ) { this->Symbol[i]->Register(this); } this->Modified(); } } //---------------------------------------------------------------------------- void vtkLegendBoxActor::SetEntryString(int i, const char* string) { if ( i >= 0 && i < this->NumberOfEntries ) { if ( this->TextMapper[i]->GetInput() && string && (!strcmp(this->TextMapper[i]->GetInput(),string))) { return; } this->TextMapper[i]->SetInput(string); this->Modified(); } } //---------------------------------------------------------------------------- void vtkLegendBoxActor::SetEntryColor(int i, double color[3]) { if ( i >= 0 && i < this->NumberOfEntries ) { double oldColor[3]; this->Colors->GetTuple(i, oldColor); if ( oldColor[0] != color[0] || oldColor[1] != color[1] || oldColor[2] != color[2] ) { this->Colors->SetTuple3(i,color[0],color[1],color[2]); this->Modified(); } } } //---------------------------------------------------------------------------- void vtkLegendBoxActor::SetEntryColor(int i, double r, double g, double b) { double rgb[3]; rgb[0] = r; rgb[1] = g; rgb[2] = b; this->SetEntryColor(i,rgb); } //---------------------------------------------------------------------------- vtkPolyData *vtkLegendBoxActor::GetEntrySymbol(int i) { if ( i < 0 || i >= this->NumberOfEntries ) { return NULL; } else { return this->Symbol[i]; } } //---------------------------------------------------------------------------- const char* vtkLegendBoxActor::GetEntryString(int i) { if ( i < 0 || i >= this->NumberOfEntries ) { return NULL; } else { return this->TextMapper[i]->GetInput(); } } //---------------------------------------------------------------------------- double* vtkLegendBoxActor::GetEntryColor(int i) { if ( i < 0 || i >= this->NumberOfEntries ) { return NULL; } else { return vtkDoubleArray::SafeDownCast(this->Colors)->GetPointer(i*3); } } //---------------------------------------------------------------------------- // Release any graphics resources that are being consumed by this actor. // The parameter window could be used to determine which graphic // resources to release. void vtkLegendBoxActor::ReleaseGraphicsResources(vtkWindow *win) { if ( this->BorderActor ) { this->BorderActor->ReleaseGraphicsResources(win); } if ( this->BoxActor ) { this->BoxActor->ReleaseGraphicsResources(win); } for (int i=0; i < this->Size; i++) { this->TextActor[i]->ReleaseGraphicsResources(win); this->SymbolActor[i]->ReleaseGraphicsResources(win); } } //---------------------------------------------------------------------------- int vtkLegendBoxActor::RenderOverlay(vtkViewport *viewport) { if ( this->NumberOfEntries <= 0 ) { return 0; } int renderedSomething = 0; if ( this->Border ) { renderedSomething += this->BorderActor->RenderOverlay(viewport); } if ( this->Box ) { renderedSomething += this->BoxActor->RenderOverlay(viewport); } if ( this->LegendEntriesVisible ) { for (int i=0; iNumberOfEntries; i++) { if ( this->Symbol[i] ) { renderedSomething += this->SymbolActor[i]->RenderOverlay(viewport); } renderedSomething += this->TextActor[i]->RenderOverlay(viewport); } } return renderedSomething; } //---------------------------------------------------------------------------- int vtkLegendBoxActor::RenderOpaqueGeometry(vtkViewport *viewport) { int i; double symbolSize; if ( this->NumberOfEntries <= 0 ) { return 0; } if (!this->EntryTextProperty) { vtkErrorMacro(<<"Need entry text property to render legend box actor"); return 0; } // Check to see whether we have to rebuild everything int *vsize = viewport->GetSize(); if (this->GetMTime() > this->BuildTime || this->EntryTextProperty->GetMTime() > this->BuildTime || vsize[0] != this->CachedSize[0] || vsize[1] != this->CachedSize[1] ) { vtkDebugMacro(<<"Rebuilding text"); this->CachedSize[0] = vsize[0]; this->CachedSize[1] = vsize[1]; // If text prop has changed, recopy it to all mappers // We have to use shallow copy since the color of each text prop // can be overriden if (this->EntryTextProperty->GetMTime() > this->BuildTime) { for (i = 0; i < this->NumberOfEntries; i++) { this->TextMapper[i]->GetTextProperty()->ShallowCopy( this->EntryTextProperty); } } //Get position information int *x1, *x2; double p1[3], p2[3]; x1 = this->PositionCoordinate->GetComputedViewportValue(viewport); x2 = this->Position2Coordinate->GetComputedViewportValue(viewport); p1[0] = (double)x1[0]; p1[1] = (double)x1[1]; p1[2] = 0.0; p2[0] = (double)x2[0]; p2[1] = (double)x2[1]; p2[2] = 0.0; //Compute spacing...trying to keep things proportional // //Find the longest string and symbol width ratio int length, maxLength; int maxTextMapper = 0; char *str; int tempi[2], fontSize; double sf, twr, swr; double *bounds; for (swr=0.0, maxLength=i=0; iNumberOfEntries; i++) { str = this->TextMapper[i]->GetInput(); if ( str ) //if there is a string { length = static_cast(strlen(str)); if ( length > maxLength ) { maxLength = length; maxTextMapper = i; } }//if string if ( this->Symbol[i] ) //if there is a symbol { this->Symbol[i]->Update(); bounds = this->Symbol[i]->GetBounds(); if ( (bounds[3]-bounds[2]) == 0.0 ) { sf = 1.0; } else { sf = (bounds[1]-bounds[0]) / (bounds[3]-bounds[2]); } if ( sf > swr ) { swr = sf; } }//if symbol defined } //Compute the final proportion (symbol width to text width) fontSize = 12; this->TextMapper[maxTextMapper]->GetTextProperty()->SetFontSize(fontSize); this->TextMapper[maxTextMapper]->GetSize(viewport,tempi); twr = (double)tempi[0]/tempi[1]; symbolSize = swr / (swr + twr); //Okay, now that the proportions are okay, let's size everything //First the text int size[2]; size[0] = (int)((1.0-symbolSize)*(p2[0] - p1[0] - 2.0*this->Padding)); size[1] = (int)((p2[1] - p1[1] - 2.0*this->Padding)/this->NumberOfEntries); fontSize = this->TextMapper[maxTextMapper]->SetConstrainedFontSize( viewport, size[0], size[1]); this->TextMapper[maxTextMapper]->GetSize(viewport,tempi); // don't draw anything if it's too small if ( size[1] > 0 && fontSize > 0) { this->LegendEntriesVisible = 1; } else { this->LegendEntriesVisible = 0; } //Border and box - may adjust spacing based on font size relationship //to the proportions relative to the border // if (this->Border || this->Box) { //adjust the border/box placement if too much whitespace if ( !this->LockBorder && tempi[0] < size[0] ) { p2[0] = p1[0] + 2.0*this->Padding + symbolSize*(p2[0] - p1[0] - 2.0*this->Padding) + tempi[0]; } vtkPoints *pts = this->BorderPolyData->GetPoints(); pts->SetPoint(0, p1); pts->SetPoint(1, p2[0],p1[1],0.0); pts->SetPoint(2, p2[0],p2[1],0.0); pts->SetPoint(3, p1[0],p2[1],0.0); } if (this->Border) { this->BorderActor->SetProperty(this->GetProperty()); } //Place text strings double color[3]; double posY; double posX = p1[0] + this->Padding + symbolSize*(p2[0] - p1[0] - 2.0*this->Padding); for (i=0; iNumberOfEntries; i++) { posY = p2[1] - this->Padding - (double)i*size[1] - 0.5*size[1]; this->TextActor[i]->SetPosition(posX,posY); this->TextMapper[i]->GetTextProperty()->SetFontSize(fontSize); this->TextActor[i]->GetProperty()->DeepCopy(this->GetProperty()); this->Colors->GetTuple(i, color); if ( color[0] >= 0.0 && color[1] >= 0.0 && color[2] >= 0.0 ) { this->TextMapper[i]->GetTextProperty()->SetColor(color[0], color[1], color[2]); } } //Place symbols // //Find the x-y bounds of the symbols...we'll be scaling these as well size[0] = (int)(symbolSize*(p2[0] - p1[0] - 2.0*this->Padding)); posX = p1[0] + this->Padding + 0.5*symbolSize*(p2[0] - p1[0] - 2.0*this->Padding); for (i=0; iNumberOfEntries; i++) { if ( this->Symbol[i] ) { this->SymbolTransform[i]->SetInput(this->Symbol[i]); bounds = this->Symbol[i]->GetBounds(); if ( (bounds[1]-bounds[0]) == 0.0 ) { sf = VTK_DOUBLE_MAX; } else { sf = size[0]/(bounds[1]-bounds[0]); } if ( (bounds[3]-bounds[2]) == 0.0 ) { if ( sf >= VTK_DOUBLE_MAX ) { sf = 1.0; } } else if ( (size[1]/(bounds[3]-bounds[2])) < sf ) { sf = size[1]/(bounds[3]-bounds[2]); } posY = p2[1] - this->Padding - (double)i*size[1] - 0.5*size[1] - 0.25*tempi[1]; this->Transform[i]->Identity(); this->Transform[i]->Translate(posX, posY, 0.0); this->Transform[i]->Scale(0.5*sf, 0.5*sf, 1); this->SymbolMapper[i]->SetScalarVisibility(this->ScalarVisibility); this->SymbolActor[i]->GetProperty()->DeepCopy(this->GetProperty()); this->Colors->GetTuple(i, color); if ( color[0] >= 0.0 && color[1] >= 0.0 && color[2] >= 0.0 ) { this->SymbolActor[i]->GetProperty()->SetColor(color[0], color[1], color[2]); } }//if symbol defined }//for all entries this->BuildTime.Modified(); }//rebuild legend box //Okay, now we're ready to render something //Border int renderedSomething = 0; if ( this->Border ) { renderedSomething += this->BorderActor->RenderOpaqueGeometry(viewport); } if ( this->Box ) { renderedSomething += this->BoxActor->RenderOpaqueGeometry(viewport); } if ( this->LegendEntriesVisible ) { for (i=0; iNumberOfEntries; i++) { if ( this->Symbol[i] ) { renderedSomething += this->SymbolActor[i]->RenderOpaqueGeometry(viewport); } renderedSomething += this->TextActor[i]->RenderOpaqueGeometry(viewport); } } return renderedSomething; } //---------------------------------------------------------------------------- void vtkLegendBoxActor::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); if (this->EntryTextProperty) { os << indent << "Entry Text Property:\n"; this->EntryTextProperty->PrintSelf(os,indent.GetNextIndent()); } else { os << indent << "Entry Text Property: (none)\n"; } os << indent << "Number Of Entries: " << this->NumberOfEntries << "\n"; os << indent << "Scalar Visibility: " << (this->ScalarVisibility ? "On\n" : "Off\n"); os << indent << "Padding: " << this->Padding << "\n"; os << indent << "Border: " << (this->Border ? "On\n" : "Off\n"); os << indent << "Box: " << (this->Box ? "On\n" : "Off\n"); os << indent << "LockBorder: " << (this->LockBorder ? "On\n" : "Off\n"); } //---------------------------------------------------------------------------- void vtkLegendBoxActor::ShallowCopy(vtkProp *prop) { vtkLegendBoxActor *a = vtkLegendBoxActor::SafeDownCast(prop); if ( a != NULL ) { this->SetPosition2(a->GetPosition2()); this->SetEntryTextProperty(a->GetEntryTextProperty()); this->SetBorder(a->GetBorder()); this->SetLockBorder(a->GetLockBorder()); this->SetPadding(a->GetPadding()); this->SetScalarVisibility(a->GetScalarVisibility()); this->SetNumberOfEntries(a->GetNumberOfEntries()); for (int i=0; iNumberOfEntries; i++) { this->SetEntrySymbol(i,a->GetEntrySymbol(i)); this->SetEntryString(i,a->GetEntryString(i)); this->SetEntryColor(i,a->GetEntryColor(i)); } } // Now do superclass this->vtkActor2D::ShallowCopy(prop); }