You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
507 lines
14 KiB
507 lines
14 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkTextMapper.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 "vtkTextMapper.h"
|
|
|
|
#include "vtkImagingFactory.h"
|
|
#include "vtkTextProperty.h"
|
|
#include "vtkToolkits.h"
|
|
|
|
vtkCxxRevisionMacro(vtkTextMapper, "$Revision: 1.54 $");
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Needed when we don't use the vtkStandardNewMacro.
|
|
vtkInstantiatorNewMacro(vtkTextMapper);
|
|
//----------------------------------------------------------------------------
|
|
|
|
vtkCxxSetObjectMacro(vtkTextMapper,TextProperty,vtkTextProperty);
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Creates a new text mapper
|
|
|
|
vtkTextMapper::vtkTextMapper()
|
|
{
|
|
this->Input = (char*)NULL;
|
|
// consistent Register/unregister
|
|
this->TextProperty = NULL;
|
|
this->SetTextProperty(vtkTextProperty::New());
|
|
this->TextProperty->Delete();
|
|
|
|
this->TextLines = NULL;
|
|
this->NumberOfLines = 0;
|
|
this->NumberOfLinesAllocated = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Shallow copy of an actor.
|
|
|
|
void vtkTextMapper::ShallowCopy(vtkTextMapper *tm)
|
|
{
|
|
this->SetInput(tm->GetInput());
|
|
this->SetTextProperty(tm->GetTextProperty());
|
|
|
|
this->SetClippingPlanes(tm->GetClippingPlanes());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkTextMapper *vtkTextMapper::New()
|
|
{
|
|
// First try to create the object from the vtkObjectFactory
|
|
vtkObject* ret = vtkImagingFactory::CreateInstance("vtkTextMapper");
|
|
return (vtkTextMapper*)ret;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkTextMapper::~vtkTextMapper()
|
|
{
|
|
if (this->Input)
|
|
{
|
|
delete [] this->Input;
|
|
this->Input = NULL;
|
|
}
|
|
|
|
if (this->TextLines != NULL)
|
|
{
|
|
for (int i=0; i < this->NumberOfLinesAllocated; i++)
|
|
{
|
|
this->TextLines[i]->Delete();
|
|
}
|
|
delete [] this->TextLines;
|
|
}
|
|
|
|
this->SetTextProperty(NULL);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkTextMapper::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
if (this->TextProperty)
|
|
{
|
|
os << indent << "Text Property:\n";
|
|
this->TextProperty->PrintSelf(os,indent.GetNextIndent());
|
|
}
|
|
else
|
|
{
|
|
os << indent << "Text Property: (none)\n";
|
|
}
|
|
|
|
os << indent << "Input: " << (this->Input ? this->Input : "(none)") << "\n";
|
|
os << indent << "NumberOfLines: " << this->NumberOfLines << "\n";
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkTextMapper::GetWidth(vtkViewport* viewport)
|
|
{
|
|
int size[2];
|
|
this->GetSize(viewport, size);
|
|
return size[0];
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkTextMapper::GetHeight(vtkViewport* viewport)
|
|
{
|
|
int size[2];
|
|
this->GetSize(viewport, size);
|
|
return size[1];
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
#define VTK_TM_DEBUG 0
|
|
|
|
int vtkTextMapper::SetConstrainedFontSize(vtkViewport *viewport,
|
|
int targetWidth,
|
|
int targetHeight)
|
|
{
|
|
// If target "empty"
|
|
|
|
if (targetWidth == 0 && targetHeight == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Get text property
|
|
|
|
vtkTextProperty *tprop = this->GetTextProperty();
|
|
if (!tprop)
|
|
{
|
|
vtkErrorMacro(<<"Need text property to apply constaint");
|
|
return 0;
|
|
}
|
|
|
|
int fontSize = tprop->GetFontSize();
|
|
#if VTK_TM_DEBUG
|
|
int oldfontSize = fontSize;
|
|
#endif
|
|
|
|
// Use the last size as a first guess
|
|
|
|
int tempi[2];
|
|
this->GetSize(viewport, tempi);
|
|
|
|
#if 1
|
|
// Now get an estimate of the target font size using bissection
|
|
|
|
#if VTK_TM_DEBUG
|
|
printf("vtkTextMapper::SetConstrainedFontSize: init size: (%d, %d) => (%d, %d)\n", tempi[0], tempi[1], targetWidth, targetHeight);
|
|
#endif
|
|
|
|
// Based on experimentation with big and small font size increments,
|
|
// ceil() gives the best result.
|
|
// big: floor: 10749, ceil: 10106, cast: 10749, vtkMath::Round: 10311
|
|
// small: floor: 12122, ceil: 11770, cast: 12122, vtkMath::Round: 11768
|
|
// I guess the best optim would be to have a look at the shape of the
|
|
// font size growth curve (probably not that linear)
|
|
|
|
if (tempi[0] && tempi[1])
|
|
{
|
|
float fx = (float)targetWidth / (float)tempi[0];
|
|
float fy = (float)targetHeight / (float)tempi[1] ;
|
|
fontSize = (int)ceil((float)fontSize * ((fx <= fy) ? fx : fy));
|
|
tprop->SetFontSize(fontSize);
|
|
this->GetSize(viewport, tempi);
|
|
}
|
|
|
|
#if VTK_TM_DEBUG
|
|
printf("vtkTextMapper::SetConstrainedFontSize: estimate size: %2d (was: %2d)\n",
|
|
fontSize, oldfontSize);
|
|
printf("vtkTextMapper::SetConstrainedFontSize: estimate size: (%d, %d) => (%d, %d)\n", tempi[0], tempi[1], targetWidth, targetHeight);
|
|
#endif
|
|
#endif
|
|
|
|
// While the size is too small increase it
|
|
|
|
while (tempi[1] <= targetHeight &&
|
|
tempi[0] <= targetWidth &&
|
|
fontSize < 100)
|
|
{
|
|
fontSize++;
|
|
#if VTK_TM_DEBUG
|
|
printf("vtkTextMapper::SetConstrainedFontSize: search+ size: %2d\n",
|
|
fontSize);
|
|
#endif
|
|
tprop->SetFontSize(fontSize);
|
|
this->GetSize(viewport, tempi);
|
|
}
|
|
|
|
#if VTK_TM_DEBUG
|
|
printf("vtkTextMapper::SetConstrainedFontSize: search+ size: (%d, %d) => (%d, %d)\n", tempi[0], tempi[1], targetWidth, targetHeight);
|
|
#endif
|
|
|
|
// While the size is too large decrease it
|
|
|
|
while ((tempi[1] > targetHeight || tempi[0] > targetWidth)
|
|
&& fontSize > 0)
|
|
{
|
|
fontSize--;
|
|
#if VTK_TM_DEBUG
|
|
printf("vtkTextMapper::SetConstrainedFontSize: search- size: %2d\n",
|
|
fontSize);
|
|
#endif
|
|
tprop->SetFontSize(fontSize);
|
|
this->GetSize(viewport, tempi);
|
|
}
|
|
|
|
#if VTK_TM_DEBUG
|
|
printf("vtkTextMapper::SetConstrainedFontSize: search- size: (%d, %d) => (%d, %d)\n", tempi[0], tempi[1], targetWidth, targetHeight);
|
|
|
|
printf("vtkTextMapper::SetConstrainedFontSize: new size: %2d (was: %2d)\n",
|
|
fontSize, oldfontSize);
|
|
#endif
|
|
|
|
return fontSize;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkTextMapper::SetMultipleConstrainedFontSize(vtkViewport *viewport,
|
|
int targetWidth,
|
|
int targetHeight,
|
|
vtkTextMapper **mappers,
|
|
int nbOfMappers,
|
|
int *maxResultingSize)
|
|
{
|
|
maxResultingSize[0] = maxResultingSize[1] = 0;
|
|
|
|
if (nbOfMappers == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int fontSize, aSize;
|
|
|
|
// First try to find the constrained font size of the first mapper: it
|
|
// will be used minimize the search for the remaining mappers, given the
|
|
// fact that all mappers are likely to have the same constrained font size.
|
|
|
|
int i, first;
|
|
for (first = 0; first < nbOfMappers && !mappers[first]; first++) {}
|
|
|
|
if (first >= nbOfMappers)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
fontSize = mappers[first]->SetConstrainedFontSize(
|
|
viewport, targetWidth, targetHeight);
|
|
|
|
#if VTK_TM_DEBUG
|
|
printf("vtkTextMapper::SetMultipleConstrainedFontSize: first size: %2d\n",
|
|
fontSize);
|
|
#endif
|
|
|
|
// Find the constrained font size for the remaining mappers and
|
|
// pick the smallest
|
|
|
|
for (i = first + 1; i < nbOfMappers; i++)
|
|
{
|
|
if (mappers[i])
|
|
{
|
|
mappers[i]->GetTextProperty()->SetFontSize(fontSize);
|
|
aSize = mappers[i]->SetConstrainedFontSize(
|
|
viewport, targetWidth, targetHeight);
|
|
if (aSize < fontSize)
|
|
{
|
|
fontSize = aSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if VTK_TM_DEBUG
|
|
printf("vtkTextMapper::SetMultipleConstrainedFontSize: smallest size: %2d\n",
|
|
fontSize);
|
|
#endif
|
|
|
|
// Assign the smallest size to all text mappers and find the largest area
|
|
|
|
int tempi[2];
|
|
for (i = first; i < nbOfMappers; i++)
|
|
{
|
|
if (mappers[i])
|
|
{
|
|
mappers[i]->GetTextProperty()->SetFontSize(fontSize);
|
|
mappers[i]->GetSize(viewport, tempi);
|
|
if (tempi[0] > maxResultingSize[0])
|
|
{
|
|
maxResultingSize[0] = tempi[0];
|
|
}
|
|
if (tempi[1] > maxResultingSize[1])
|
|
{
|
|
maxResultingSize[1] = tempi[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
// The above code could be optimized further since the mappers
|
|
// labels are likely to have the same height: in that case, we could
|
|
// have searched for the largest label, find the constrained size
|
|
// for this one, then applied this size to all others. But who
|
|
// knows, maybe one day the text property will support a text
|
|
// orientation/rotation, and in that case the height will vary.
|
|
|
|
return fontSize;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Parse the input and create multiple text mappers if multiple lines
|
|
// (delimited by \n) are specified.
|
|
|
|
void vtkTextMapper::SetInput(const char *input)
|
|
{
|
|
if ( this->Input && input && (!strcmp(this->Input,input)))
|
|
{
|
|
return;
|
|
}
|
|
if (this->Input)
|
|
{
|
|
delete [] this->Input;
|
|
}
|
|
if (input)
|
|
{
|
|
this->Input = new char[strlen(input)+1];
|
|
strcpy(this->Input,input);
|
|
}
|
|
else
|
|
{
|
|
this->Input = NULL;
|
|
}
|
|
this->Modified();
|
|
|
|
int numLines = this->GetNumberOfLines(input);
|
|
|
|
if ( numLines <= 1) // a line with no "\n"
|
|
{
|
|
this->NumberOfLines = numLines;
|
|
}
|
|
|
|
else //multiple lines
|
|
{
|
|
char *line;
|
|
int i;
|
|
|
|
if ( numLines > this->NumberOfLinesAllocated )
|
|
{
|
|
// delete old stuff
|
|
if ( this->TextLines )
|
|
{
|
|
for (i=0; i < this->NumberOfLinesAllocated; i++)
|
|
{
|
|
this->TextLines[i]->Delete();
|
|
}
|
|
delete [] this->TextLines;
|
|
}
|
|
|
|
// allocate new text mappers
|
|
this->NumberOfLinesAllocated = numLines;
|
|
this->TextLines = new vtkTextMapper *[numLines];
|
|
for (i=0; i < numLines; i++)
|
|
{
|
|
this->TextLines[i] = vtkTextMapper::New();
|
|
}
|
|
} //if we need to reallocate
|
|
|
|
// set the input strings
|
|
this->NumberOfLines = numLines;
|
|
for (i=0; i < this->NumberOfLines; i++)
|
|
{
|
|
line = this->NextLine(input, i);
|
|
this->TextLines[i]->SetInput( line );
|
|
delete [] line;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Determine the number of lines in the Input string (delimited by "\n").
|
|
|
|
int vtkTextMapper::GetNumberOfLines(const char *input)
|
|
{
|
|
if ( input == NULL || input[0] == '\0')
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int numLines=1;
|
|
const char *ptr = input;
|
|
|
|
while ( ptr != NULL )
|
|
{
|
|
if ( (ptr=strstr(ptr,"\n")) != NULL )
|
|
{
|
|
numLines++;
|
|
ptr++; //skip over \n
|
|
}
|
|
}
|
|
|
|
return numLines;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Get the next \n delimited line. Returns a string that
|
|
// must be freed by the calling function.
|
|
|
|
char *vtkTextMapper::NextLine(const char *input, int lineNum)
|
|
{
|
|
const char *ptr, *ptrEnd;
|
|
int strLen;
|
|
char *line;
|
|
|
|
ptr = input;
|
|
for (int i=0; i != lineNum; i++)
|
|
{
|
|
ptr = strstr(ptr,"\n");
|
|
ptr++;
|
|
}
|
|
ptrEnd = strstr(ptr,"\n");
|
|
if ( ptrEnd == NULL )
|
|
{
|
|
ptrEnd = strchr(ptr, '\0');
|
|
}
|
|
|
|
strLen = ptrEnd - ptr;
|
|
line = new char[strLen+1];
|
|
strncpy(line, ptr, strLen);
|
|
line[strLen] = '\0';
|
|
|
|
return line;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Get the size of a multi-line text string
|
|
|
|
void vtkTextMapper::GetMultiLineSize(vtkViewport* viewport, int size[2])
|
|
{
|
|
int i;
|
|
int lineSize[2];
|
|
|
|
vtkTextProperty *tprop = this->GetTextProperty();
|
|
if (!tprop)
|
|
{
|
|
vtkErrorMacro(<<"Need text property to get multiline size of mapper");
|
|
size[0] = size[1] = 0;
|
|
return;
|
|
}
|
|
|
|
lineSize[0] = lineSize[1] = size[0] = size[1] = 0;
|
|
for ( i=0; i < this->NumberOfLines; i++ )
|
|
{
|
|
this->TextLines[i]->GetTextProperty()->ShallowCopy(tprop);
|
|
this->TextLines[i]->GetSize(viewport, lineSize);
|
|
size[0] = (lineSize[0] > size[0] ? lineSize[0] : size[0]);
|
|
size[1] = (lineSize[1] > size[1] ? lineSize[1] : size[1]);
|
|
}
|
|
|
|
// add in the line spacing
|
|
this->LineSize = size[1];
|
|
size[1] = (int)((float)size[1] * (1.0 + (this->NumberOfLines - 1) * tprop->GetLineSpacing()));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkTextMapper::RenderOverlayMultipleLines(vtkViewport *viewport,
|
|
vtkActor2D *actor)
|
|
{
|
|
float offset = 0.0;
|
|
int size[2];
|
|
// make sure LineSize is up to date
|
|
this->GetMultiLineSize(viewport,size);
|
|
|
|
vtkTextProperty *tprop = this->GetTextProperty();
|
|
if (!tprop)
|
|
{
|
|
vtkErrorMacro(<<"Need text property to render multiple lines of mapper");
|
|
return;
|
|
}
|
|
|
|
switch (tprop->GetVerticalJustification())
|
|
{
|
|
case VTK_TEXT_TOP:
|
|
offset = 0.0;
|
|
break;
|
|
case VTK_TEXT_CENTERED:
|
|
offset = (-this->NumberOfLines + 1.0) / 2.0;
|
|
break;
|
|
case VTK_TEXT_BOTTOM:
|
|
offset = -this->NumberOfLines + 1.0;
|
|
break;
|
|
}
|
|
|
|
for (int lineNum=0; lineNum < this->NumberOfLines; lineNum++)
|
|
{
|
|
this->TextLines[lineNum]->GetTextProperty()->ShallowCopy(tprop);
|
|
this->TextLines[lineNum]->GetTextProperty()->SetLineOffset
|
|
(tprop->GetLineOffset() + (int)((float)this->LineSize * (lineNum + offset) * tprop->GetLineSpacing()));
|
|
this->TextLines[lineNum]->RenderOverlay(viewport,actor);
|
|
}
|
|
}
|
|
|