/*========================================================================= Program: Visualization Toolkit Module: vtkParallelCoordinatesHistogramRepresentation.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. =========================================================================*/ /*------------------------------------------------------------------------- Copyright 2009 Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. -------------------------------------------------------------------------*/ #include "vtkParallelCoordinatesHistogramRepresentation.h" //------------------------------------------------------------------------------ #include "vtkActor2D.h" #include "vtkArrayIteratorIncludes.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkComputeHistogram2DOutliers.h" #include "vtkDoubleArray.h" #include "vtkExtractSelectedPolyDataIds.h" #include "vtkExtractHistogram2D.h" #include "vtkFloatArray.h" #include "vtkIdList.h" #include "vtkImageData.h" #include "vtkImageGaussianSmooth.h" #include "vtkImageMedian3D.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkLookupTable.h" #include "vtkMath.h" #include "vtkMultiBlockDataSet.h" #include "vtkObjectFactory.h" #include "vtkPairwiseExtractHistogram2D.h" #include "vtkPointData.h" #include "vtkPoints.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper2D.h" #include "vtkPropCollection.h" #include "vtkProperty2D.h" #include "vtkRenderer.h" #include "vtkRenderView.h" #include "vtkSCurveSpline.h" #include "vtkSelection.h" #include "vtkSelectionNode.h" #include "vtkStringArray.h" #include "vtkTable.h" #include "vtkUnsignedIntArray.h" #include "vtkViewTheme.h" //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkParallelCoordinatesHistogramRepresentation); //------------------------------------------------------------------------------ vtkParallelCoordinatesHistogramRepresentation::vtkParallelCoordinatesHistogramRepresentation() { this->SetNumberOfInputPorts(vtkParallelCoordinatesHistogramRepresentation::NUM_INPUT_PORTS); this->UseHistograms = 0; this->HistogramLookupTableRange[0] = 0.0; this->HistogramLookupTableRange[1] = -1.0; this->HistogramFilter = vtkSmartPointer::New(); this->HistogramFilter->SetInputData(this->InputArrayTable); this->HistogramLookupTable = vtkSmartPointer::New(); this->HistogramLookupTable->SetAlphaRange(0,1); this->HistogramLookupTable->SetHueRange(1,1); this->HistogramLookupTable->SetValueRange(1,1); this->HistogramLookupTable->SetSaturationRange(0,0); this->HistogramLookupTable->ForceBuild(); this->PlotMapper->SetScalarModeToUseCellData(); this->PlotMapper->UseLookupTableScalarRangeOn(); this->PlotMapper->SetLookupTable(this->HistogramLookupTable); this->PlotMapper->ScalarVisibilityOff(); this->ShowOutliers = 0; this->OutlierFilter = vtkSmartPointer::New(); this->OutlierFilter->SetInputData(vtkComputeHistogram2DOutliers::INPUT_TABLE_DATA, this->InputArrayTable); // this->HistogramFilter->GetOutputPort(vtkPairwiseExtractHistogram2D::REORDERED_INPUT)); this->OutlierFilter->SetInputConnection(vtkComputeHistogram2DOutliers::INPUT_HISTOGRAMS_MULTIBLOCK, this->HistogramFilter->GetOutputPort(vtkPairwiseExtractHistogram2D::HISTOGRAM_IMAGE)); this->OutlierData = vtkSmartPointer::New(); this->OutlierActor = vtkSmartPointer::New(); this->OutlierActor->GetProperty()->SetColor(1,1,1); this->OutlierMapper = vtkSmartPointer::New(); this->OutlierMapper.TakeReference(this->InitializePlotMapper(this->OutlierData,this->OutlierActor)); this->SetHistogramLookupTableRange(0,10); this->SetPreferredNumberOfOutliers(100); this->SetNumberOfHistogramBins(10,10); // Apply default theme // you would think that calling this in the superclass would take care of it, // but it turns out that the superclass constructor will only call its own // version there. So I have to call it again to make sure that the local // version gets called. vtkViewTheme* theme = vtkViewTheme::New(); theme->SetCellOpacity(1.0); theme->SetCellColor(1.0,1.0,1.0); theme->SetEdgeLabelColor(1.0,.8,.3); this->ApplyViewTheme(theme); theme->Delete(); } //------------------------------------------------------------------------------ vtkParallelCoordinatesHistogramRepresentation::~vtkParallelCoordinatesHistogramRepresentation() { } //------------------------------------------------------------------------------ // Histogram quad color is defined by theme->CellColor void vtkParallelCoordinatesHistogramRepresentation::ApplyViewTheme(vtkViewTheme* theme) { this->Superclass::ApplyViewTheme(theme); double *c = theme->GetCellColor(); double hsv[3] = {0,0,0}; vtkMath::RGBToHSV(c,hsv); this->HistogramLookupTable->SetHueRange(hsv[0],hsv[0]); this->HistogramLookupTable->SetSaturationRange(hsv[1],hsv[1]); this->HistogramLookupTable->SetValueRange(hsv[2],hsv[2]); this->HistogramLookupTable->ForceBuild(); } //------------------------------------------------------------------------------ // Make sure all of the histogram/outlier stuff is up-to-date. Also, if not using // histograms, make sure that lookup table for the plot data mapper is // disabled, since that's the behavior for the parent class. int vtkParallelCoordinatesHistogramRepresentation::ComputeDataProperties() { if (!this->Superclass::ComputeDataProperties()) return 0; if (this->UseHistograms) { this->GetHistogramImage(0); this->SetHistogramLookupTableRange(0,this->HistogramFilter->GetMaximumBinCount()); this->HistogramLookupTable->SetRange(this->HistogramLookupTableRange[0],this->HistogramLookupTableRange[1]); this->PlotMapper->ScalarVisibilityOn(); } else { this->PlotMapper->ScalarVisibilityOff(); } if (this->ShowOutliers) { this->OutlierActor->VisibilityOn(); } else { this->OutlierActor->VisibilityOff(); } return 1; } //------------------------------------------------------------------------------ // outliers have the same properties as plot lines. int vtkParallelCoordinatesHistogramRepresentation::UpdatePlotProperties(vtkStringArray* inputTitles) { if (!this->Superclass::UpdatePlotProperties(inputTitles)) return 0; this->OutlierActor->GetProperty()->SetOpacity(this->LineOpacity); this->OutlierActor->GetProperty()->SetColor(this->LineColor); return 1; } //------------------------------------------------------------------------------ int vtkParallelCoordinatesHistogramRepresentation::RequestData( vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // do everything the superclass does // * histogram quad computation happens automatically since this // class overrides the plotting functions. if (!this->Superclass::RequestData(request,inputVector,outputVector)) return 0; // but also show outliers if (this->ShowOutliers) { vtkTable* outlierTable = this->GetOutlierData(); if (this->UseCurves) { vtkParallelCoordinatesRepresentation::PlaceCurves(this->OutlierData,outlierTable,nullptr); } else { vtkParallelCoordinatesRepresentation::PlaceLines(this->OutlierData,outlierTable,nullptr); } } this->BuildTime.Modified(); return 1; } //------------------------------------------------------------------------------ bool vtkParallelCoordinatesHistogramRepresentation::AddToView(vtkView* view) { this->Superclass::AddToView(view); vtkRenderView* rv = vtkRenderView::SafeDownCast(view); if (rv) { rv->GetRenderer()->AddActor(this->OutlierActor); // not sure what these are for //rv->RegisterProgress(...); return true; } return false; } //------------------------------------------------------------------------------ bool vtkParallelCoordinatesHistogramRepresentation::RemoveFromView(vtkView* view) { this->Superclass::RemoveFromView(view); vtkRenderView* rv = vtkRenderView::SafeDownCast(view); if (rv) { rv->GetRenderer()->RemoveActor(this->OutlierActor); // not sure what these are for //rv->UnRegisterProgress(this->OutlineMapper); return true; } return false; } //------------------------------------------------------------------------------ // redirect the line plotting function to the histogram plotting function, // if histograms are enabled int vtkParallelCoordinatesHistogramRepresentation::PlaceLines(vtkPolyData* polyData, vtkTable* data, vtkIdTypeArray* idsToPlot) { if (this->UseHistograms) { return this->PlaceHistogramLineQuads(polyData); } else { return this->Superclass::PlaceLines(polyData,data,idsToPlot); } } //------------------------------------------------------------------------------ // redirect the line plotting function to the histogram plotting function, // if histograms are enabled int vtkParallelCoordinatesHistogramRepresentation::PlaceCurves(vtkPolyData* polyData, vtkTable* data, vtkIdTypeArray* idsToPlot) { if (this->UseHistograms) { return this->PlaceHistogramCurveQuads(polyData); } else { return this->Superclass::PlaceCurves(polyData,data,idsToPlot); } } //------------------------------------------------------------------------------ // this is a bit tricky. This class plots selections as lines, regardless // of whether or not histograms are enabled. That means it needs to explicitly // call the superclass plotting functions on the selection so that the // histogram plotting functions don't get used. int vtkParallelCoordinatesHistogramRepresentation::PlaceSelection(vtkPolyData* polyData, vtkTable* data, vtkSelectionNode* selectionNode) { vtkIdTypeArray* selectedIds = vtkArrayDownCast(selectionNode->GetSelectionList()); if (!selectedIds) return 1; if (this->UseCurves) { this->Superclass::PlaceCurves(polyData,data,selectedIds); } else { this->Superclass::PlaceLines(polyData,data,selectedIds); } return 1; } //------------------------------------------------------------------------------ int vtkParallelCoordinatesHistogramRepresentation::PlaceHistogramLineQuads(vtkPolyData* polyData) { // figure out how many samples there are by looking at each of the // histograms and counting the bins. int numberOfQuads = 0; for (int i=0; iNumberOfAxes-1; i++) { vtkImageData* histogram = this->GetHistogramImage(i); if (histogram) numberOfQuads += histogram->GetPointData()->GetScalars()->GetNumberOfTuples(); } if (this->UseCurves) numberOfQuads *= this->CurveResolution; this->AllocatePolyData(polyData,0,0,0,0, numberOfQuads, numberOfQuads*4, numberOfQuads,0); vtkPoints* points = polyData->GetPoints(); float* pointsp = vtkArrayDownCast(points->GetData())->GetPointer(0); vtkDoubleArray* scalars = vtkArrayDownCast(polyData->GetCellData()->GetScalars()); double* scalarsp = scalars->GetPointer(0);//vtkArrayDownCast(polyData->GetCellData()->GetScalars()); // for each histogram, draw a quad for each bin. vtkIdType ptId=0; // vtkIdType quadId=0; for (int pos=0; posNumberOfAxes-1; pos++) { int dims[3] = {0,0,0}; double spacing[3] = {0,0,0}; vtkImageData* image = this->GetHistogramImage(pos); if (!image) continue; image->GetDimensions(dims); image->GetSpacing(spacing); double binWidth[] = { (this->YMax-this->YMin)/static_cast(dims[0]), (this->YMax-this->YMin)/static_cast(dims[1]) }; double x1[3] = {0.,0.,0.}; double x2[3] = {0.,0.,0.}; x1[0] = this->Xs[pos]; x2[0] = this->Xs[pos+1]; // for each bin, draw a quad for (int y=0; yYMin + y * binWidth[1]; for (int x=0; xYMin + x * binWidth[0]; // the number of rows that fit into this bin double v = image->GetScalarComponentAsDouble(x,y,0,0); *(pointsp++) = x1[0]; *(pointsp++) = x1[1]+binWidth[0]; *(pointsp++) = 0.; ptId++; *(pointsp++) = x1[0]; *(pointsp++) = x1[1]; *(pointsp++) = 0.; ptId++; *(pointsp++) = x2[0]; *(pointsp++) = x2[1]; *(pointsp++) = 0.; ptId++; *(pointsp++) = x2[0]; *(pointsp++) = x2[1]+binWidth[1]; *(pointsp++) = 0.; ptId++; // points->SetPoint(ptId++, x1[0], x1[1]+binWidth[0], 0.); // points->SetPoint(ptId++, x1[0], x1[1], 0.); // points->SetPoint(ptId++, x2[0], x2[1], 0.); // points->SetPoint(ptId++, x2[0], x2[1]+binWidth[1], 0.); // scalars used for lookup table mapping. More rows // in a bin means bright quad. //scalars->SetTuple1(quadId++,v); *(scalarsp++) = v;//->SetTuple1(quadId++,v); } } } polyData->Modified(); return 1; } //------------------------------------------------------------------------------ int vtkParallelCoordinatesHistogramRepresentation::PlaceHistogramCurveQuads(vtkPolyData* polyData) { // figure out how many samples there are by looking at each of the // histograms and counting the bins. int numberOfStrips = 0; for (int i=0; iNumberOfAxes-1; i++) { vtkImageData* histogram = this->GetHistogramImage(i); if (histogram) numberOfStrips += histogram->GetPointData()->GetScalars()->GetNumberOfTuples(); } int numberOfPointsPerStrip = this->CurveResolution * 2; this->AllocatePolyData(polyData,0,0, numberOfStrips,numberOfPointsPerStrip,0, numberOfStrips*numberOfPointsPerStrip, numberOfStrips,0); vtkPoints *points = polyData->GetPoints(); float* pointsp = vtkArrayDownCast(points->GetData())->GetPointer(0); vtkDoubleArray* scalars = vtkArrayDownCast(polyData->GetCellData()->GetScalars()); double* scalarsp = scalars->GetPointer(0); // build the default spline vtkSmartPointer defSplineValues = vtkSmartPointer::New(); this->BuildDefaultSCurve(defSplineValues,this->CurveResolution); vtkIdType ptId=0; //vtkIdType stripId = 0; for (int pos=0; posNumberOfAxes-1; pos++) { int dims[3] = {0,0,0}; double spacing[3] = {0,0,0}; vtkImageData* image = this->GetHistogramImage(pos); if (!image) continue; image->GetDimensions(dims); image->GetSpacing(spacing); double binWidth[] = { (this->YMax-this->YMin)/static_cast(dims[0]), (this->YMax-this->YMin)/static_cast(dims[1]) }; double x1[3] = {0.,0.,0.}; double x2[3] = {0.,0.,0.}; double xc[3] = {0.,0.,0.}; double w = 0.0; x1[0] = this->Xs[pos]; x2[0] = this->Xs[pos+1]; double dx = (x2[0]-x1[0]) / static_cast(this->CurveResolution-1); double dw = binWidth[1] - binWidth[0]; for (int y=0; yYMin + y * binWidth[1]; for (int x=0; xYMin + x * binWidth[0]; double v = image->GetScalarComponentAsDouble(x,y,0,0); double dy = x2[1] - x1[1]; for (int c=0; cCurveResolution; c++) { xc[0] = this->Xs[pos] + dx*c; xc[1] = defSplineValues->GetValue(c)*dy + x1[1];//spline->Evaluate(x1[0]);//spline->Evaluate(x1[0]); w = defSplineValues->GetValue(c)*dw + binWidth[0];//bwspline->Evaluate(x1[0]); // points->SetPoint(ptId++, xc[0], xc[1]+w, 0.); // points->SetPoint(ptId++, xc[0], xc[1], 0.); *(pointsp++) = xc[0]; *(pointsp++) = xc[1]+w; *(pointsp++) = 0.0; ptId++; *(pointsp++) = xc[0]; *(pointsp++) = xc[1]; *(pointsp++) = 0.0; ptId++; } *(scalarsp++) = v;//->SetTuple1(stripId++,v); } } } polyData->Modified(); return 1; } //------------------------------------------------------------------------------ void vtkParallelCoordinatesHistogramRepresentation::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << "UseHistograms: " << this->UseHistograms << endl; os << "HistogramLookupTableRange: " << this->HistogramLookupTableRange[0] << "," << this->HistogramLookupTableRange[1] << endl; os << "NumberOfHistogramBins: " << this->NumberOfHistogramBins[0] << "," << this->NumberOfHistogramBins[1] << endl; os << "ShowOutliers: " << this->ShowOutliers << endl; os << "PreferredNumberOfOutliers: " << this->PreferredNumberOfOutliers << endl; } //------------------------------------------------------------------------------ int vtkParallelCoordinatesHistogramRepresentation::SwapAxisPositions(int position1, int position2) { if (this->Superclass::SwapAxisPositions(position1,position2)) { this->HistogramFilter->Modified(); if (this->ShowOutliers) this->OutlierFilter->Modified(); return 1; } return 0; } //------------------------------------------------------------------------------ int vtkParallelCoordinatesHistogramRepresentation::SetRangeAtPosition(int position, double range[2]) { if (this->Superclass::SetRangeAtPosition(position,range)) { this->HistogramFilter->SetCustomColumnRange(position,range); this->HistogramFilter->Modified(); if (ShowOutliers) this->OutlierFilter->Modified(); return 1; } return 0; } //------------------------------------------------------------------------------ void vtkParallelCoordinatesHistogramRepresentation::SetUseHistograms(int use) { if (use && this->UseHistograms != use) { this->HistogramFilter->Modified(); if (this->ShowOutliers) this->OutlierFilter->Modified(); } this->UseHistograms = use; this->Modified(); } //------------------------------------------------------------------------------ void vtkParallelCoordinatesHistogramRepresentation::SetShowOutliers(int show) { if (show && this->ShowOutliers != show) { this->HistogramFilter->Modified(); this->OutlierFilter->Modified(); } this->ShowOutliers = show; this->Modified(); } //------------------------------------------------------------------------------ void vtkParallelCoordinatesHistogramRepresentation::SetPreferredNumberOfOutliers(int num) { if (num >= 0) { this->PreferredNumberOfOutliers = num; this->OutlierFilter->SetPreferredNumberOfOutliers(num); this->Modified(); } } //------------------------------------------------------------------------------ void vtkParallelCoordinatesHistogramRepresentation::SetNumberOfHistogramBins(int nx, int ny) { if (nx > 0 && ny > 0) { this->NumberOfHistogramBins[0] = nx; this->NumberOfHistogramBins[1] = ny; this->HistogramFilter->SetNumberOfBins(nx,ny); this->Modified(); } } //------------------------------------------------------------------------------ void vtkParallelCoordinatesHistogramRepresentation::SetNumberOfHistogramBins(int* n) { this->SetNumberOfHistogramBins(n[0],n[1]); } //------------------------------------------------------------------------------ vtkImageData* vtkParallelCoordinatesHistogramRepresentation::GetHistogramImage(int idx) { return this->HistogramFilter->GetOutputHistogramImage(idx); } //------------------------------------------------------------------------------ vtkTable* vtkParallelCoordinatesHistogramRepresentation::GetOutlierData() { return this->OutlierFilter->GetOutputTable(); }