Commit 962324b7 authored by Yumin Yuan's avatar Yumin Yuan
Browse files

Added polygon selection to hardware selector.

A new Interaction style is added, and the hardware selector is
modifed to handle polygon selection. This new type of selection,
like the rubber band selection, is also based on pixel buffer.

Change-Id: Ic886670fb8cf1bb4a6471b239d8767c03cfea2dc
parent d5767a46
set(Module_SRCS
vtkInteractorStyleDrawPolygon.cxx
vtkInteractorStyleFlight.cxx
vtkInteractorStyleImage.cxx
vtkInteractorStyleJoystickActor.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkInteractorStyleDrawPolygon.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 "vtkInteractorStyleDrawPolygon.h"
#include "vtkCommand.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkUnsignedCharArray.h"
#include "vtkVectorOperators.h"
vtkStandardNewMacro(vtkInteractorStyleDrawPolygon);
//-----------------------------------------------------------------------------
class vtkInteractorStyleDrawPolygon::vtkInternal
{
public:
std::vector<vtkVector2i> points;
void AddPoint(const vtkVector2i &point)
{
this->points.push_back(point);
}
void AddPoint(int x, int y)
{
this->AddPoint(vtkVector2i(x, y));
}
vtkVector2i GetPoint(vtkIdType index) const
{
return this->points[index];
}
vtkIdType GetNumberOfPoints() const
{
return this->points.size();
}
void Clear()
{
this->points.clear();
}
void DrawPixels(const vtkVector2i& StartPos,
const vtkVector2i& EndPos, unsigned char *pixels, int *size)
{
int x1=StartPos.GetX(), x2=EndPos.GetX();
int y1=StartPos.GetY(), y2=EndPos.GetY();
double x = x2 - x1;
double y = y2 - y1;
double length = sqrt( x*x + y*y );
if(length == 0)
{
return;
}
double addx = x / length;
double addy = y / length;
x = x1;
y = y1;
int row, col;
for(double i = 0; i < length; i += 1)
{
col = (int)x;
row = (int)y;
pixels[3*(row*size[0]+col)] = 255 ^ pixels[3*(row*size[0]+col)];
pixels[3*(row*size[0]+col)+1] = 255 ^ pixels[3*(row*size[0]+col)+1];
pixels[3*(row*size[0]+col)+2] = 255 ^ pixels[3*(row*size[0]+col)+2];
x += addx;
y += addy;
}
}
};
//----------------------------------------------------------------------------
vtkInteractorStyleDrawPolygon::vtkInteractorStyleDrawPolygon()
{
this->Internal = new vtkInternal();
this->StartPosition[0] = this->StartPosition[1] = 0;
this->EndPosition[0] = this->EndPosition[1] = 0;
this->Moving = 0;
this->PixelArray = vtkUnsignedCharArray::New();
}
//----------------------------------------------------------------------------
vtkInteractorStyleDrawPolygon::~vtkInteractorStyleDrawPolygon()
{
this->PixelArray->Delete();
delete this->Internal;
}
//----------------------------------------------------------------------------
std::vector<vtkVector2i> vtkInteractorStyleDrawPolygon::GetPolygonPoints()
{
return this->Internal->points;
}
//----------------------------------------------------------------------------
void vtkInteractorStyleDrawPolygon::OnMouseMove()
{
if (!this->Interactor || !this->Moving)
{
return;
}
this->EndPosition[0] = this->Interactor->GetEventPosition()[0];
this->EndPosition[1] = this->Interactor->GetEventPosition()[1];
int *size = this->Interactor->GetRenderWindow()->GetSize();
if (this->EndPosition[0] > (size[0]-1))
{
this->EndPosition[0] = size[0]-1;
}
if (this->EndPosition[0] < 0)
{
this->EndPosition[0] = 0;
}
if (this->EndPosition[1] > (size[1]-1))
{
this->EndPosition[1] = size[1]-1;
}
if (this->EndPosition[1] < 0)
{
this->EndPosition[1] = 0;
}
vtkVector2i lastPoint =
this->Internal->GetPoint(
this->Internal->GetNumberOfPoints() - 1);
vtkVector2i newPoint(this->EndPosition[0], this->EndPosition[1]);
if((lastPoint - newPoint).SquaredNorm() > 100)
{
this->Internal->AddPoint(newPoint);
this->DrawPolygon();
}
}
//----------------------------------------------------------------------------
void vtkInteractorStyleDrawPolygon::OnLeftButtonDown()
{
if (!this->Interactor)
{
return;
}
this->Moving = 1;
vtkRenderWindow *renWin = this->Interactor->GetRenderWindow();
this->StartPosition[0] = this->Interactor->GetEventPosition()[0];
this->StartPosition[1] = this->Interactor->GetEventPosition()[1];
this->EndPosition[0] = this->StartPosition[0];
this->EndPosition[1] = this->StartPosition[1];
this->PixelArray->Initialize();
this->PixelArray->SetNumberOfComponents(3);
int *size = renWin->GetSize();
this->PixelArray->SetNumberOfTuples(size[0]*size[1]);
renWin->GetPixelData(0, 0, size[0]-1, size[1]-1, 1, this->PixelArray);
this->Internal->Clear();
this->Internal->AddPoint(this->StartPosition[0], this->StartPosition[1]);
this->InvokeEvent(vtkCommand::StartInteractionEvent);
}
//----------------------------------------------------------------------------
void vtkInteractorStyleDrawPolygon::OnLeftButtonUp()
{
if (!this->Interactor || !this->Moving)
{
return;
}
this->Moving = 0;
this->InvokeEvent(vtkCommand::SelectionChangedEvent);
this->InvokeEvent(vtkCommand::EndInteractionEvent);
}
//----------------------------------------------------------------------------
void vtkInteractorStyleDrawPolygon::DrawPolygon()
{
vtkNew<vtkUnsignedCharArray> tmpPixelArray;
tmpPixelArray->DeepCopy(this->PixelArray);
unsigned char *pixels = tmpPixelArray->GetPointer(0);
int *size = this->Interactor->GetRenderWindow()->GetSize();
// draw each line segment
for(vtkIdType i = 0; i < this->Internal->GetNumberOfPoints() - 1; i++)
{
const vtkVector2i &a = this->Internal->GetPoint(i);
const vtkVector2i &b = this->Internal->GetPoint(i+1);
this->Internal->DrawPixels(a, b, pixels, size);
}
// draw a line from the end to the start
if(this->Internal->GetNumberOfPoints() >= 3)
{
const vtkVector2i &start = this->Internal->GetPoint(0);
const vtkVector2i &end = this->Internal->GetPoint(this->Internal->GetNumberOfPoints() - 1);
this->Internal->DrawPixels(start, end, pixels, size);
}
this->Interactor->GetRenderWindow()->SetPixelData(0, 0, size[0]-1, size[1]-1, pixels, 1);
}
//----------------------------------------------------------------------------
void vtkInteractorStyleDrawPolygon::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkInteractorStyleDrawPolygon.h
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.
=========================================================================*/
// .NAME vtkInteractorStyleDrawPolygon - draw polygon during mouse move
// .SECTION Description
// This interactor style allows the user to draw a polygon in the render
// window using the left mouse button while mouse is moving.
// When the mouse button is released, a SelectionChangedEvent will be fired.
#ifndef __vtkInteractorStyleDrawPolygon_h
#define __vtkInteractorStyleDrawPolygon_h
#include "vtkInteractionStyleModule.h" // For export macro
#include "vtkInteractorStyle.h"
#include <vector> // For returning Polygon Points
#include "vtkVector.h" // For Polygon Points
class vtkUnsignedCharArray;
class VTKINTERACTIONSTYLE_EXPORT vtkInteractorStyleDrawPolygon : public vtkInteractorStyle
{
public:
static vtkInteractorStyleDrawPolygon *New();
vtkTypeMacro(vtkInteractorStyleDrawPolygon, vtkInteractorStyle);
void PrintSelf(ostream& os, vtkIndent indent);
// Description:
// Event bindings
virtual void OnMouseMove();
virtual void OnLeftButtonDown();
virtual void OnLeftButtonUp();
// Description:
// Get the current polygon points in display units
std::vector<vtkVector2i> GetPolygonPoints();
protected:
vtkInteractorStyleDrawPolygon();
~vtkInteractorStyleDrawPolygon();
virtual void DrawPolygon();
int StartPosition[2];
int EndPosition[2];
int Moving;
vtkUnsignedCharArray *PixelArray;
private:
vtkInteractorStyleDrawPolygon(const vtkInteractorStyleDrawPolygon&); // Not implemented
void operator=(const vtkInteractorStyleDrawPolygon&); // Not implemented
class vtkInternal;
vtkInternal* Internal;
};
#endif
......@@ -34,6 +34,39 @@
#include <map>
#define ID_OFFSET 1
//----------------------------------------------------------------------------
namespace
{
class PixelInformationComparator
{
public:
bool operator() (const vtkHardwareSelector::PixelInformation& a,
const vtkHardwareSelector::PixelInformation& b) const
{
if (a.Valid != b.Valid)
{
return a.Valid < b.Valid;
}
if (a.ProcessID != b.ProcessID)
{
return a.ProcessID < b.ProcessID;
}
if (a.Prop != b.Prop)
{
return a.Prop < b.Prop;
}
if (a.PropID != b.PropID)
{
return a.PropID < b.PropID;
}
return a.CompositeID < b.CompositeID;
// We don't consider AttributeID in this comparison
}
};
}
class vtkHardwareSelector::vtkInternals
{
public:
......@@ -45,6 +78,103 @@ public:
int OriginalMultisample;
int OriginalLighting;
int OriginalBlending;
typedef std::map<PixelInformation, std::set<vtkIdType>,
PixelInformationComparator> MapOfAttributeIds;
typedef std::map<PixelInformation, vtkIdType,
PixelInformationComparator> PixelCountType;
//-----------------------------------------------------------------------------
vtkSelection* ConvertSelection(
int fieldassociation, const MapOfAttributeIds& dataMap,
const PixelCountType& pixelCounts)
{
vtkSelection* sel = vtkSelection::New();
MapOfAttributeIds::const_iterator iter;
for (iter = dataMap.begin(); iter != dataMap.end(); ++iter)
{
const PixelInformation &key = iter->first;
const std::set<vtkIdType> &id_values = iter->second;
vtkSelectionNode* child = vtkSelectionNode::New();
child->SetContentType(vtkSelectionNode::INDICES);
switch (fieldassociation)
{
case vtkDataObject::FIELD_ASSOCIATION_CELLS:
child->SetFieldType(vtkSelectionNode::CELL);
break;
case vtkDataObject::FIELD_ASSOCIATION_POINTS:
child->SetFieldType(vtkSelectionNode::POINT);
break;
}
child->GetProperties()->Set(vtkSelectionNode::PROP_ID(), key.PropID);
child->GetProperties()->Set(vtkSelectionNode::PROP(), key.Prop);
PixelCountType::const_iterator pit = pixelCounts.find(key);
child->GetProperties()->Set(vtkSelectionNode::PIXEL_COUNT(), pit->second);
if (key.ProcessID >= 0)
{
child->GetProperties()->Set(vtkSelectionNode::PROCESS_ID(),
key.ProcessID);
}
child->GetProperties()->Set(vtkSelectionNode::COMPOSITE_INDEX(),
key.CompositeID);
vtkIdTypeArray* ids = vtkIdTypeArray::New();
ids->SetName("SelectedIds");
ids->SetNumberOfComponents(1);
ids->SetNumberOfTuples(iter->second.size());
vtkIdType* ptr = ids->GetPointer(0);
std::set<vtkIdType>::const_iterator idIter;
vtkIdType cc=0;
for (idIter = id_values.begin(); idIter != id_values.end(); ++idIter, ++cc)
{
ptr[cc] = *idIter;
}
child->SetSelectionList(ids);
ids->FastDelete();
sel->AddNode(child);
child->FastDelete();
}
return sel;
}
//-----------------------------------------------------------------------------
bool PixelInsidePolygon(float x, float y, int* polygonPoints, vtkIdType count)
{
// http://en.wikipedia.org/wiki/Point_in_polygon
// RayCasting method shooting the ray along the x axis, using float
bool inside = false;
float xintersection;
for(vtkIdType i=0; i<count; i+=2)
{
float p1X = polygonPoints[i];
float p1Y = polygonPoints[i+1];
float p2X = polygonPoints[(i+2) % count];
float p2Y = polygonPoints[(i+3) % count];
if (y > std::min(p1Y, p2Y) &&
y <= std::max(p1Y,p2Y) &&
p1Y != p2Y)
{
if (x <= std::max(p1X, p2X) )
{
xintersection = (y - p1Y)*(p2X - p1X)/(p2Y - p1Y) + p1X;
if ( p1X == p2X || x <= xintersection)
{
// each time intersect, toggle inside
inside = !inside;
}
}
}
}
return inside;
}
};
vtkStandardNewMacro(vtkHardwareSelector);
......@@ -590,63 +720,13 @@ bool vtkHardwareSelector::GetPixelInformation(unsigned int display_position[2],
}
#endif
//----------------------------------------------------------------------------
namespace
{
class PixelInformationComparator
{
public:
bool operator() (const vtkHardwareSelector::PixelInformation& a,
const vtkHardwareSelector::PixelInformation& b) const
{
if (a.Valid != b.Valid)
{
return a.Valid < b.Valid;
}
if (a.ProcessID != b.ProcessID)
{
return a.ProcessID < b.ProcessID;
}
if (a.Prop != b.Prop)
{
return a.Prop < b.Prop;
}
if (a.PropID != b.PropID)
{
return a.PropID < b.PropID;
}
return a.CompositeID < b.CompositeID;
// We don't consider AttributeID in this comparison
}
};
}
//----------------------------------------------------------------------------
vtkSelection* vtkHardwareSelector::GenerateSelection(
unsigned int x1, unsigned int y1,
unsigned int x2, unsigned int y2)
{
int extent[6] = { static_cast<int>(x1),
static_cast<int>(x2),
static_cast<int>(y1),
static_cast<int>(y2),
0,
0};
int whole_extent[6] = {static_cast<int>(this->Area[0]),
static_cast<int>(this->Area[2]),
static_cast<int>(this->Area[1]),
static_cast<int>(this->Area[3]),
0,
0};
vtkStructuredExtent::Clamp(extent, whole_extent);
typedef std::map<PixelInformation, std::set<vtkIdType>,
PixelInformationComparator> MapOfAttributeIds;
MapOfAttributeIds dataMap;
typedef std::map<PixelInformation, vtkIdType, PixelInformationComparator> PixelCountType;
PixelCountType pixelCounts;
vtkInternals::MapOfAttributeIds dataMap;
vtkInternals::PixelCountType pixelCounts;
for (unsigned int yy = y1; yy <= y2; yy++)
{
......@@ -661,56 +741,51 @@ vtkSelection* vtkHardwareSelector::GenerateSelection(
}
}
}
return this->Internals->ConvertSelection(
this->FieldAssociation, dataMap, pixelCounts);
}
vtkSelection* sel = vtkSelection::New();
MapOfAttributeIds::iterator iter;
for (iter = dataMap.begin(); iter != dataMap.end(); ++iter)
//----------------------------------------------------------------------------
vtkSelection* vtkHardwareSelector::GeneratePolygonSelection(
int* polygonPoints, vtkIdType count)
{
// we need at least three points (x,y) for a polygon selection.
if(!polygonPoints || count < 6)
{
const PixelInformation &key = iter->first;
std::set<vtkIdType> &id_values = iter->second;
vtkSelectionNode* child = vtkSelectionNode::New();
child->SetContentType(vtkSelectionNode::INDICES);
switch (this->FieldAssociation)
{
case vtkDataObject::FIELD_ASSOCIATION_CELLS:
child->SetFieldType(vtkSelectionNode::CELL);
break;
return NULL;
}
case vtkDataObject::FIELD_ASSOCIATION_POINTS:
child->SetFieldType(vtkSelectionNode::POINT);
break;
}
child->GetProperties()->Set(vtkSelectionNode::PROP_ID(), key.PropID);
child->GetProperties()->Set(vtkSelectionNode::PROP(), key.Prop);
child->GetProperties()->Set(vtkSelectionNode::PIXEL_COUNT(), pixelCounts[key]);
if (key.ProcessID >= 0)
{
child->GetProperties()->Set(vtkSelectionNode::PROCESS_ID(),
key.ProcessID);
}
int x1=VTK_INT_MAX, x2=VTK_INT_MIN, y1=VTK_INT_MAX, y2=VTK_INT_MIN;
// Get polygon bounds, so that we only check pixels within the bounds
for(vtkIdType i=0; i<count; i+=2)
{
x1 = std::min(polygonPoints[i], x1);
x2 = std::max(polygonPoints[i], x2);
y1 = std::min(polygonPoints[i+1], y1);
y2 = std::max(polygonPoints[i+1], y2);
}
child->GetProperties()->Set(vtkSelectionNode::COMPOSITE_INDEX(),
key.CompositeID);
vtkIdTypeArray* ids = vtkIdTypeArray::New();
ids->SetName("SelectedIds");
ids->SetNumberOfComponents(1);
ids->SetNumberOfTuples(iter->second.size());
vtkIdType* ptr = ids->GetPointer(0);
std::set<vtkIdType>::iterator idIter;
vtkIdType cc=0;
for (idIter = id_values.begin(); idIter != id_values.end(); ++idIter, ++cc)
vtkInternals::MapOfAttributeIds dataMap;
vtkInternals::PixelCountType pixelCounts;
for (int yy = y1; yy <= y2; yy++)
{
for (int xx = x1; xx <= x2; xx++)
{
ptr[cc] = *idIter;
if(this->Internals->PixelInsidePolygon(
xx, yy, polygonPoints, count))
{
unsigned int pos[2] = {xx, yy};
PixelInformation info = this->GetPixelInformation(pos, 0);
if (info.Valid)
{
dataMap[info].insert(info.AttributeID);
pixelCounts[info]++;
}
}