/*========================================================================= Program: Visualization Toolkit Module: vtkSliderRepresentation2D.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 "vtkSliderRepresentation2D.h" #include "vtkActor2D.h" #include "vtkCellArray.h" #include "vtkCommand.h" #include "vtkEvent.h" #include "vtkInteractorObserver.h" #include "vtkLine.h" #include "vtkMath.h" #include "vtkObjectFactory.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 "vtkTransform.h" #include "vtkTransformPolyDataFilter.h" #include "vtkWindow.h" vtkStandardNewMacro(vtkSliderRepresentation2D); //------------------------------------------------------------------------------ vtkSliderRepresentation2D::vtkSliderRepresentation2D() { // The coordinates defining the slider this->Point1Coordinate = vtkCoordinate::New(); this->Point1Coordinate->SetCoordinateSystemToWorld(); this->Point1Coordinate->SetValue(-1.0, 0.0, 0.0); this->Point2Coordinate = vtkCoordinate::New(); this->Point2Coordinate->SetCoordinateSystemToWorld(); this->Point2Coordinate->SetValue(1.0, 0.0, 0.0); // Default configuration this->LabelHeight = 0.025; this->TitleHeight = 0.030; this->SliderLength = 0.01; this->SliderWidth = 0.02; this->EndCapLength = 0.005; this->TubeWidth = 0.01; // The points and the transformation for the points. There are a total // of 18 points: 4 for each of slider, tube and caps, and two extra points // for the title and label text. this->XForm = vtkTransform::New(); this->Points = vtkPoints::New(); this->Points->SetNumberOfPoints(18); // The slider this->SliderCells = vtkCellArray::New(); this->SliderCells->AllocateEstimate(1, 4); this->SliderCells->InsertNextCell(4); this->SliderCells->InsertCellPoint(8); this->SliderCells->InsertCellPoint(9); this->SliderCells->InsertCellPoint(10); this->SliderCells->InsertCellPoint(11); this->Slider = vtkPolyData::New(); this->Slider->SetPoints(this->Points); this->Slider->SetPolys(this->SliderCells); this->SliderXForm = vtkTransformPolyDataFilter::New(); this->SliderXForm->SetInputData(this->Slider); this->SliderXForm->SetTransform(XForm); this->SliderMapper = vtkPolyDataMapper2D::New(); this->SliderMapper->SetInputConnection(this->SliderXForm->GetOutputPort()); this->SliderProperty = vtkProperty2D::New(); this->SliderProperty->SetColor(1, 1, 1); this->SliderActor = vtkActor2D::New(); this->SliderActor->SetMapper(this->SliderMapper); this->SliderActor->SetProperty(this->SliderProperty); // The tube (the slider moves along the tube) this->TubeCells = vtkCellArray::New(); this->TubeCells->AllocateEstimate(1, 4); this->TubeCells->InsertNextCell(4); this->TubeCells->InsertCellPoint(4); this->TubeCells->InsertCellPoint(5); this->TubeCells->InsertCellPoint(6); this->TubeCells->InsertCellPoint(7); this->Tube = vtkPolyData::New(); this->Tube->SetPoints(this->Points); this->Tube->SetPolys(this->TubeCells); this->TubeXForm = vtkTransformPolyDataFilter::New(); this->TubeXForm->SetInputData(this->Tube); this->TubeXForm->SetTransform(XForm); this->TubeMapper = vtkPolyDataMapper2D::New(); this->TubeMapper->SetInputConnection(this->TubeXForm->GetOutputPort()); this->TubeProperty = vtkProperty2D::New(); this->TubeProperty->SetColor(1, 1, 1); this->TubeActor = vtkActor2D::New(); this->TubeActor->SetMapper(this->TubeMapper); this->TubeActor->SetProperty(this->TubeProperty); this->SelectedProperty = vtkProperty2D::New(); this->SelectedProperty->SetColor(1.0000, 0.4118, 0.7059); // hot pink // The two caps this->CapCells = vtkCellArray::New(); this->CapCells->AllocateEstimate(2, 4); this->CapCells->InsertNextCell(4); this->CapCells->InsertCellPoint(0); this->CapCells->InsertCellPoint(1); this->CapCells->InsertCellPoint(2); this->CapCells->InsertCellPoint(3); this->CapCells->InsertNextCell(4); this->CapCells->InsertCellPoint(12); this->CapCells->InsertCellPoint(13); this->CapCells->InsertCellPoint(14); this->CapCells->InsertCellPoint(15); this->Cap = vtkPolyData::New(); this->Cap->SetPoints(this->Points); this->Cap->SetPolys(this->CapCells); this->CapXForm = vtkTransformPolyDataFilter::New(); this->CapXForm->SetInputData(this->Cap); this->CapXForm->SetTransform(XForm); this->CapMapper = vtkPolyDataMapper2D::New(); this->CapMapper->SetInputConnection(this->CapXForm->GetOutputPort()); this->CapProperty = vtkProperty2D::New(); this->CapProperty->SetColor(1, 1, 1); this->CapActor = vtkActor2D::New(); this->CapActor->SetMapper(this->CapMapper); this->CapActor->SetProperty(this->CapProperty); // Labels and text this->ShowSliderLabel = 1; this->LabelProperty = vtkTextProperty::New(); this->LabelProperty->SetBold(1); this->LabelProperty->SetShadow(1); this->LabelProperty->SetFontFamilyToArial(); this->LabelProperty->SetJustificationToCentered(); this->LabelProperty->SetVerticalJustificationToCentered(); this->LabelMapper = vtkTextMapper::New(); this->LabelMapper->SetInput(""); this->LabelMapper->SetTextProperty(this->LabelProperty); this->LabelActor = vtkActor2D::New(); this->LabelActor->SetMapper(this->LabelMapper); this->TitleProperty = vtkTextProperty::New(); this->TitleProperty->SetBold(1); this->TitleProperty->SetShadow(1); this->TitleProperty->SetFontFamilyToArial(); this->TitleProperty->SetJustificationToCentered(); this->TitleProperty->SetVerticalJustificationToCentered(); this->TitleMapper = vtkTextMapper::New(); this->TitleMapper->SetInput(""); this->TitleMapper->SetTextProperty(this->TitleProperty); this->TitleActor = vtkActor2D::New(); this->TitleActor->SetMapper(this->TitleMapper); } //------------------------------------------------------------------------------ vtkSliderRepresentation2D::~vtkSliderRepresentation2D() { this->Point1Coordinate->Delete(); this->Point2Coordinate->Delete(); this->XForm->Delete(); this->Points->Delete(); this->SliderCells->Delete(); this->Slider->Delete(); this->SliderXForm->Delete(); this->SliderMapper->Delete(); this->SliderActor->Delete(); this->SliderProperty->Delete(); this->TubeCells->Delete(); this->Tube->Delete(); this->TubeXForm->Delete(); this->TubeMapper->Delete(); this->TubeActor->Delete(); this->TubeProperty->Delete(); this->CapCells->Delete(); this->Cap->Delete(); this->CapXForm->Delete(); this->CapMapper->Delete(); this->CapActor->Delete(); this->CapProperty->Delete(); this->SelectedProperty->Delete(); this->LabelProperty->Delete(); this->LabelMapper->Delete(); this->LabelActor->Delete(); this->TitleProperty->Delete(); this->TitleMapper->Delete(); this->TitleActor->Delete(); } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::SetTitleText(const char* label) { this->TitleMapper->SetInput(label); if (this->TitleMapper->GetMTime() > this->GetMTime()) { this->Modified(); } } //------------------------------------------------------------------------------ const char* vtkSliderRepresentation2D::GetTitleText() { return this->TitleMapper->GetInput(); } //------------------------------------------------------------------------------ vtkCoordinate* vtkSliderRepresentation2D::GetPoint1Coordinate() { return this->Point1Coordinate; } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::StartWidgetInteraction(double eventPos[2]) { // Compute which polygon the pick is in (if any). int subId; double event[3], pcoords[3], closest[3], weights[4], dist2; event[0] = eventPos[0] - this->Renderer->GetOrigin()[0]; event[1] = eventPos[1] - this->Renderer->GetOrigin()[1]; event[2] = 0.0; vtkCell* sliderCell = this->SliderXForm->GetOutput()->GetCell(0); if (sliderCell->EvaluatePosition(event, closest, subId, pcoords, dist2, weights) > 0) { this->InteractionState = vtkSliderRepresentation::Slider; return; } vtkCell* tubeCell = this->TubeXForm->GetOutput()->GetCell(0); if (tubeCell->EvaluatePosition(event, closest, subId, pcoords, dist2, weights) > 0) { this->InteractionState = vtkSliderRepresentation::Tube; this->ComputePickPosition(eventPos); return; } vtkCell* leftCapCell = this->CapXForm->GetOutput()->GetCell(0); if (leftCapCell->EvaluatePosition(event, closest, subId, pcoords, dist2, weights) > 0) { this->InteractionState = vtkSliderRepresentation::LeftCap; this->PickedT = 0.0; return; } vtkCell* rightCapCell = this->CapXForm->GetOutput()->GetCell(1); if (rightCapCell->EvaluatePosition(event, closest, subId, pcoords, dist2, weights) > 0) { this->InteractionState = vtkSliderRepresentation::RightCap; this->PickedT = 1.0; return; } this->InteractionState = vtkSliderRepresentation::Outside; } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::WidgetInteraction(double eventPos[2]) { double t = this->ComputePickPosition(eventPos); this->SetValue(this->MinimumValue + t * (this->MaximumValue - this->MinimumValue)); this->BuildRepresentation(); } //------------------------------------------------------------------------------ vtkCoordinate* vtkSliderRepresentation2D::GetPoint2Coordinate() { return this->Point2Coordinate; } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::PlaceWidget(double* vtkNotUsed(bds[6])) { // Position the handles at the end of the lines this->BuildRepresentation(); } //------------------------------------------------------------------------------ double vtkSliderRepresentation2D::ComputePickPosition(double eventPos[2]) { double p4[3], p5[3], p6[3], p7[3]; double x1[3], x2[3]; // Project the event position onto the tube axis. this->SliderXForm->GetOutput()->GetPoints()->GetPoint(4, p4); this->SliderXForm->GetOutput()->GetPoints()->GetPoint(5, p5); this->SliderXForm->GetOutput()->GetPoints()->GetPoint(6, p6); this->SliderXForm->GetOutput()->GetPoints()->GetPoint(7, p7); x1[0] = (p4[0] + p7[0]) / 2.0; x1[1] = (p4[1] + p7[1]) / 2.0; x1[2] = (p4[2] + p7[2]) / 2.0; x2[0] = (p5[0] + p6[0]) / 2.0; x2[1] = (p5[1] + p6[1]) / 2.0; x2[2] = (p5[2] + p6[2]) / 2.0; double event[3], closestPoint[3]; event[0] = eventPos[0] - this->Renderer->GetOrigin()[0]; event[1] = eventPos[1] - this->Renderer->GetOrigin()[1]; event[2] = 0.0; // Intersect geometry. Don't forget to scale the pick because the tube // geometry is longer than the sliding region (due to the thickness of the // slider). vtkLine::DistanceToLine(event, x1, x2, this->PickedT, closestPoint); double scale = (2.0 * this->X - 2.0 * this->EndCapLength) / (2.0 * this->X - 2.0 * this->EndCapLength - this->SliderLength); this->PickedT = 0.5 + (this->PickedT - 0.5) * scale; this->PickedT = (this->PickedT < 0 ? 0.0 : (this->PickedT > 1.0 ? 1.0 : this->PickedT)); return this->PickedT; } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::Highlight(int highlight) { if (highlight) { this->SliderActor->SetProperty(this->SelectedProperty); } else { this->SliderActor->SetProperty(this->SliderProperty); } } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::BuildRepresentation() { if (this->GetMTime() > this->BuildTime || (this->Renderer && this->Renderer->GetVTKWindow() && this->Renderer->GetVTKWindow()->GetMTime() > this->BuildTime)) { const int* size = this->Renderer->GetSize(); if (0 == size[0] || 0 == size[1]) { // Renderer has no size yet: wait until the next // BuildRepresentation... return; } double t = (this->Value - this->MinimumValue) / (this->MaximumValue - this->MinimumValue); // Setup the geometry of the widget (canonical along the x-axis). // Later we will transform the widget into place. We take into account the length of // the widget here. int* p1 = this->Point1Coordinate->GetComputedDisplayValue(this->Renderer); int* p2 = this->Point2Coordinate->GetComputedDisplayValue(this->Renderer); double delX = static_cast(p2[0] - p1[0]); double delY = static_cast(p2[1] - p1[1]); double length = sqrt(delX * delX + delY * delY); length = (length <= 0.0 ? 1.0 : length); this->X = 0.5 * (length / size[0]); double theta = atan2(delY, delX); // Generate the points double x[6], y[6]; x[0] = -this->X; x[1] = -this->X + this->EndCapLength; x[2] = x[1] + t * (2.0 * X - 2.0 * this->EndCapLength - this->SliderLength); x[3] = x[2] + this->SliderLength; x[4] = this->X - this->EndCapLength; x[5] = this->X; y[0] = -0.5 * this->EndCapWidth; y[1] = -0.5 * this->SliderWidth; y[2] = -0.5 * this->TubeWidth; y[3] = 0.5 * this->TubeWidth; y[4] = 0.5 * this->SliderWidth; y[5] = 0.5 * this->EndCapWidth; this->Points->SetPoint(0, x[0], y[0], 0.0); this->Points->SetPoint(1, x[1], y[0], 0.0); this->Points->SetPoint(2, x[1], y[5], 0.0); this->Points->SetPoint(3, x[0], y[5], 0.0); this->Points->SetPoint(4, x[1], y[2], 0.0); this->Points->SetPoint(5, x[4], y[2], 0.0); this->Points->SetPoint(6, x[4], y[3], 0.0); this->Points->SetPoint(7, x[1], y[3], 0.0); this->Points->SetPoint(8, x[2], y[1], 0.0); this->Points->SetPoint(9, x[3], y[1], 0.0); this->Points->SetPoint(10, x[3], y[4], 0.0); this->Points->SetPoint(11, x[2], y[4], 0.0); this->Points->SetPoint(12, x[4], y[0], 0.0); this->Points->SetPoint(13, x[5], y[0], 0.0); this->Points->SetPoint(14, x[5], y[5], 0.0); this->Points->SetPoint(15, x[4], y[5], 0.0); // Specify the location of the text. Because the slider can rotate // we have to take into account the text height and width. int titleSize[2]; double textSize[2]; double maxY = (this->SliderWidth > this->TubeWidth ? (this->SliderWidth > this->EndCapWidth ? this->SliderWidth : this->EndCapWidth) : (this->TubeWidth > this->EndCapWidth ? this->TubeWidth : this->EndCapWidth)); if (!this->ShowSliderLabel) { this->LabelActor->VisibilityOff(); } else { this->LabelActor->VisibilityOn(); int labelSize[2]; char label[256]; snprintf(label, sizeof(label), this->LabelFormat, this->Value); this->LabelMapper->SetInput(label); this->LabelProperty->SetFontSize(static_cast(this->LabelHeight * size[1])); this->LabelMapper->GetSize(this->Renderer, labelSize); textSize[0] = static_cast(labelSize[0]) / static_cast(size[0]); textSize[1] = static_cast(labelSize[1]) / static_cast(size[1]); double radius = maxY / 2.0 + textSize[1] * cos(theta) + textSize[0] * sin(theta); this->Points->SetPoint(16, (x[2] + x[3]) / 2.0, radius, 0.0); // label } this->TitleProperty->SetFontSize(static_cast(this->TitleHeight * size[1])); this->TitleMapper->GetSize(this->Renderer, titleSize); textSize[0] = static_cast(titleSize[0]) / static_cast(size[0]); textSize[1] = static_cast(titleSize[1]) / static_cast(size[1]); double radius = maxY / 2.0 + textSize[1] * cos(theta) + textSize[0] * sin(theta); this->Points->SetPoint(17, 0.0, -radius, 0.0); // title // Begin transforming the slider double sx = static_cast(size[0]); double sy = static_cast(size[1]); double tx = static_cast((p1[0] + p2[0]) / 2.0); double ty = static_cast((p1[1] + p2[1]) / 2.0); this->XForm->Identity(); this->XForm->Translate(tx, ty, 0.0); this->XForm->Scale(sx, sy, 1.0); this->XForm->RotateZ(vtkMath::DegreesFromRadians(theta)); // The transform has done the work of finding the center point for the text. // Put the title and label at these points. double p16[3], p17[3]; this->SliderXForm->Update(); // want to get the points that were transformed this->SliderXForm->GetOutput()->GetPoints()->GetPoint(16, p16); this->SliderXForm->GetOutput()->GetPoints()->GetPoint(17, p17); this->LabelActor->SetPosition(p16[0], p16[1]); this->TitleActor->SetPosition(p17[0], p17[1]); this->BuildTime.Modified(); } } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::GetActors2D(vtkPropCollection* pc) { pc->AddItem(this->SliderActor); pc->AddItem(this->TubeActor); pc->AddItem(this->CapActor); pc->AddItem(this->LabelActor); pc->AddItem(this->TitleActor); } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::ReleaseGraphicsResources(vtkWindow* w) { this->SliderActor->ReleaseGraphicsResources(w); this->TubeActor->ReleaseGraphicsResources(w); this->CapActor->ReleaseGraphicsResources(w); this->LabelActor->ReleaseGraphicsResources(w); this->TitleActor->ReleaseGraphicsResources(w); } //------------------------------------------------------------------------------ int vtkSliderRepresentation2D::RenderOpaqueGeometry(vtkViewport* viewport) { this->BuildRepresentation(); int count = this->TubeActor->RenderOpaqueGeometry(viewport); count += this->SliderActor->RenderOpaqueGeometry(viewport); count += this->CapActor->RenderOpaqueGeometry(viewport); count += this->LabelActor->RenderOpaqueGeometry(viewport); count += this->TitleActor->RenderOpaqueGeometry(viewport); return count; } //------------------------------------------------------------------------------ int vtkSliderRepresentation2D::RenderOverlay(vtkViewport* viewport) { this->BuildRepresentation(); int count = this->TubeActor->RenderOverlay(viewport); count += this->SliderActor->RenderOverlay(viewport); count += this->CapActor->RenderOverlay(viewport); count += this->LabelActor->RenderOverlay(viewport); count += this->TitleActor->RenderOverlay(viewport); return count; } //------------------------------------------------------------------------------ void vtkSliderRepresentation2D::PrintSelf(ostream& os, vtkIndent indent) { // Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h this->Superclass::PrintSelf(os, indent); os << indent << "Label Text: " << (this->LabelMapper->GetInput() ? this->LabelMapper->GetInput() : "(none)") << "\n"; os << indent << "Title Text: " << (this->TitleMapper->GetInput() ? this->TitleMapper->GetInput() : "(none)") << "\n"; os << indent << "Point1 Coordinate: " << this->Point1Coordinate << "\n"; this->Point1Coordinate->PrintSelf(os, indent.GetNextIndent()); os << indent << "Point2 Coordinate: " << this->Point2Coordinate << "\n"; this->Point2Coordinate->PrintSelf(os, indent.GetNextIndent()); if (this->SliderProperty) { os << indent << "Slider Property:\n"; this->SliderProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Slider Property: (none)\n"; } if (this->SelectedProperty) { os << indent << "SelectedProperty:\n"; this->SelectedProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "SelectedProperty: (none)\n"; } if (this->TubeProperty) { os << indent << "TubeProperty:\n"; this->TubeProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "TubeProperty: (none)\n"; } if (this->CapProperty) { os << indent << "CapProperty:\n"; this->CapProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "CapProperty: (none)\n"; } if (this->SelectedProperty) { os << indent << "SelectedProperty:\n"; this->SelectedProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "SelectedProperty: (none)\n"; } if (this->LabelProperty) { os << indent << "LabelProperty:\n"; this->LabelProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "LabelProperty: (none)\n"; } if (this->TitleProperty) { os << indent << "TitleProperty:\n"; this->TitleProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "TitleProperty: (none)\n"; } }