OpenWalnut  1.5.0dev
WOnscreenSelection.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <vector>
26 
27 #include "WOnscreenSelection.h"
28 
30  m_projection( new osg::Projection() ),
31  m_selectionType( WSelectionType::BRUSH ),
32  m_selectionHandler( new WOnscreenSelectionHandler( this ) ),
33  m_isSelecting( false ),
34  m_shader( new WGEShader( "WOnscreenSelection" ) ),
35  m_thickness( 25.0f ),
36  m_clickType( false )
37 {
38  WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getView()->addEventHandler( m_selectionHandler.get() );
39 }
40 
42 {
43  WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getView()->removeEventHandler( m_selectionHandler.get() );
44  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_projection );
45 }
46 
47 void WOnscreenSelection::start( float x, float y )
48 {
49  if( m_isSelecting )
50  {
51  return;
52  }
53 
54  clear();
55 
56  m_isSelecting = true;
57  m_moved = false;
58  WPosition pos( x, y, 0 );
59 
60  m_line.push_back( pos );
61  if( m_selectionType == WSelectionType::BOX )
62  {
63  m_line.push_back( pos );
64  }
65  updateDisplay();
66 
67  if( m_onstart )
68  {
69  m_onstart( m_selectionType, x, y );
70  }
71 }
72 
73 void WOnscreenSelection::end( float x, float y )
74 {
75  if( !m_isSelecting )
76  {
77  return;
78  }
79  m_isSelecting = false;
80 
81  WPosition pos( x, y, 0 );
82  switch( m_selectionType )
83  {
84  case WSelectionType::BRUSH:
85  m_line.push_back( pos );
86  break;
87  case WSelectionType::LINELOOP:
88  break;
89  case WSelectionType::BOX:
90  m_line.at( 1 ) = pos;
91  break;
92  }
93  updateDisplay();
94 
95  if( m_onend )
96  {
97  m_onend( m_selectionType, x, y );
98  }
99 }
100 
101 void WOnscreenSelection::move( float x, float y )
102 {
103  if( !m_isSelecting )
104  {
105  return;
106  }
107 
108  m_moved = true;
109 
110  WPosition pos( x, y, 0 );
111  switch( m_selectionType )
112  {
113  case WSelectionType::BRUSH:
114  case WSelectionType::LINELOOP:
115  m_line.push_back( pos );
116  break;
117  case WSelectionType::BOX:
118  m_line.at( 1 ) = pos;
119  break;
120  }
121 
122  updateDisplay();
123 
124  if( m_onmove )
125  {
126  m_onmove( m_selectionType, x, y );
127  }
128 }
129 
131 {
132  return m_selectionType;
133 }
134 
136 {
137  m_selectionType = selectionType;
138 }
139 
141 {
142  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_projection );
143 
144  if( m_line.empty() )
145  {
146  return;
147  }
148 
149  m_projection = new osg::Projection();
150  m_projection->setMatrix( osg::Matrix::ortho2D( 0, 1.0, 0, 1.0 ) );
151  m_projection->getOrCreateStateSet()->setRenderBinDetails( 15, "RenderBin" );
152  m_projection->getOrCreateStateSet()->setDataVariance( osg::Object::DYNAMIC );
153  m_projection->getOrCreateStateSet()->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
154  m_projection->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
155  m_projection->getOrCreateStateSet()->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
156  m_projection->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
157 
158  osg::ref_ptr< osg::Geode > lines = new osg::Geode();
159  osg::ref_ptr< osg::Geometry > geometry = new osg::Geometry();
160  osg::ref_ptr< osg::Vec3Array > vertices = osg::ref_ptr< osg::Vec3Array >( new osg::Vec3Array() );
161 
162  if( m_selectionType == WSelectionType::BOX )
163  {
164  WPosition pos1 = m_line.at( 0 );
165  WPosition pos2 = m_line.at( 1 );
166 
167  vertices->push_back( osg::Vec3( pos1.x(), pos2.y(), 0 ) );
168 
169  vertices->push_back( osg::Vec3( pos1.x(), pos1.y(), 0 ) );
170  vertices->push_back( osg::Vec3( pos2.x(), pos1.y(), 0 ) );
171  vertices->push_back( osg::Vec3( pos2.x(), pos2.y(), 0 ) );
172  vertices->push_back( osg::Vec3( pos1.x(), pos2.y(), 0 ) );
173  vertices->push_back( osg::Vec3( pos1.x(), pos1.y(), 0 ) );
174 
175  vertices->push_back( osg::Vec3( pos2.x(), pos1.y(), 0 ) );
176 
177  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_STRIP_ADJACENCY, 0, 7 ) );
178  }
179  else
180  {
181  size_t add = 2;
182  WPosition first = m_line.front();
183  WPosition last = m_line.back();
184 
185  // first element
186  if( m_selectionType == WSelectionType::LINELOOP )
187  {
188  vertices->push_back( osg::Vec3( last.x(), last.y(), last.z() ) );
189  }
190  else
191  {
192  vertices->push_back( osg::Vec3( first.x(), first.y(), first.z() ) );
193  }
194 
195  // all elements
196  for( size_t i = 0; i < m_line.size(); i++ )
197  {
198  WPosition pos = m_line.at( i );
199  vertices->push_back( osg::Vec3( pos.x(), pos.y(), pos.z() ) );
200  }
201 
202  // last element and close line loop
203  if( m_selectionType == WSelectionType::LINELOOP && m_line.size() > 1 )
204  {
205  vertices->push_back( osg::Vec3( first.x(), first.y(), first.z() ) );
206  vertices->push_back( osg::Vec3( m_line.at( 1 ).x(), m_line.at( 1 ).y(), m_line.at( 1 ).z() ) );
207  add++;
208  }
209  else
210  {
211  vertices->push_back( osg::Vec3( last.x(), last.y(), last.z() ) );
212  }
213 
214  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_STRIP_ADJACENCY, 0, m_line.size() + add ) );
215  }
216 
217  geometry->setVertexArray( vertices );
218 
219  lines->addDrawable( geometry );
220 
221  float thickness = m_selectionType == WSelectionType::BRUSH ? m_thickness : 5;
222 
223  osg::StateSet* state = lines->getOrCreateStateSet();
224  osg::Camera* camera = WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getCamera();
225  state->addUniform( new osg::Uniform( "u_viewport", osg::Vec2( camera->getViewport()->width(), camera->getViewport()->height() ) ) );
226  state->addUniform( new osg::Uniform( "u_thickness", thickness ) );
227  state->addUniform( new osg::Uniform( "u_clickType", m_clickType ) );
228 
229  osg::ref_ptr< osg::MatrixTransform > matrix = new osg::MatrixTransform();
230  matrix->setMatrix( osg::Matrix::identity() );
231  matrix->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
232 
233  m_shader->apply( lines );
234  matrix->addChild( lines );
235  m_projection->addChild( matrix );
236 
237  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_projection );
238 }
239 
241 {
242  m_onstart = onstart;
243 }
244 
246 {
247  m_onend = onend;
248 }
249 
251 {
252  m_onmove = onmove;
253 }
254 
256 {
257  return m_isSelecting;
258 }
259 
261 {
262  m_line.clear();
263  updateDisplay();
264 }
265 
266 bool WOnscreenSelection::isSelected( float x, float y, float z )
267 {
268  if( m_line.empty() )
269  {
270  return false;
271  }
272 
273  // Calculate normalized screen coordinates for 3D point.
274  osg::Camera* camera = WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getCamera();
275  osg::Matrix viewMatrix = camera->getViewMatrix();
276  osg::Matrix projectionMatrix = camera->getProjectionMatrix();
277  osg::Matrix cameraMatrix = viewMatrix * projectionMatrix;
278  osg::Vec3 point = osg::Vec3( x, y, z ) * cameraMatrix;
279  float xPos = ( point.x() + 1.0 ) / 2.0;
280  float yPos = ( point.y() + 1.0 ) / 2.0;
281 
282  if( xPos < 0 || xPos > 1 || yPos < 0 || yPos > 1 )
283  {
284  return false;
285  }
286 
287  switch( m_selectionType )
288  {
289  case WSelectionType::BRUSH:
290  return brushCheck( xPos, yPos );
291  case WSelectionType::LINELOOP:
292  return lineloopCheck( xPos, yPos );
293  case WSelectionType::BOX:
294  return boxCheck( xPos, yPos );
295  default:
296  return false;
297  }
298 }
299 
300 std::vector< WPosition > WOnscreenSelection::isSelected( std::vector< WPosition> positions )
301 {
302  std::vector< WPosition > out;
303  for( size_t i = 0; i < positions.size(); i++ )
304  {
305  WPosition pos = positions.at( i );
306  if( isSelected( pos.x(), pos.y(), pos.z() ) )
307  {
308  out.push_back( pos );
309  }
310  }
311 
312  return out;
313 }
314 
315 
316 bool WOnscreenSelection::boxCheck( float x, float y )
317 {
318  WPosition pos1 = m_line.at( 0 );
319  WPosition pos2 = m_line.at( 1 );
320 
321  float maxX = fmax( pos1.x(), pos2.x() );
322  float maxY = fmax( pos1.y(), pos2.y() );
323  float minX = fmin( pos1.x(), pos2.x() );
324  float minY = fmin( pos1.y(), pos2.y() );
325 
326  return x >= minX && x <= maxX && y >= minY && y <= maxY;
327 }
328 
329 bool WOnscreenSelection::brushCheck( float x, float y )
330 {
331  osg::Camera* camera = WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getCamera();
332  float thick2 = ( m_thickness * 0.5 ) * ( m_thickness * 0.5 ); // * 0.5 because we need distance to center of the brush
333  float width = camera->getViewport()->width();
334  float height = camera->getViewport()->height();
335 
336  x = x * width;
337  y = y * height;
338 
339  for( size_t i = 0; i < m_line.size() - 1; i++ )
340  {
341  WPosition pos1 = m_line.at( i );
342  WPosition pos2 = m_line.at( i + 1 );
343 
344  // denormalize
345  pos1 = WPosition( pos1.x() * width, pos1.y() * height, 0.0 );
346  pos2 = WPosition( pos2.x() * width, pos2.y() * height, 0.0 );
347 
348  // calculate distance to linesegment
349  float segLen2 = pow( pos1.x() - pos2.x(), 2.0 ) + pow( pos1.y() - pos2.y(), 2.0 );
350  if( segLen2 == 0.0 )
351  {
352  continue;
353  }
354 
355  float segParam = ( ( x - pos1.x() ) * ( pos2.x() - pos1.x() ) + ( y - pos1.y() ) * ( pos2.y() - pos1.y() ) ) / segLen2;
356  segParam = fmax( 0.0, fmin( 1.0, segParam ) );
357 
358  float xPos = pos1.x() + segParam * ( pos2.x() - pos1.x() );
359  float yPos = pos1.y() + segParam * ( pos2.y() - pos1.y() );
360 
361  float dis2 = pow( x - xPos, 2.0 ) + pow( y - yPos, 2.0 );
362 
363  if( dis2 <= thick2 )
364  {
365  return true;
366  }
367  }
368  return false;
369 }
370 
371 bool WOnscreenSelection::lineloopCheck( float x, float y )
372 {
373  int t = -1;
374  for( size_t i = 0; i < m_line.size(); i++ )
375  {
376  WPosition pos1 = m_line.at( i );
377  WPosition pos2 = i == m_line.size() - 1 ? m_line.at( 0 ) : m_line.at( i + 1 );
378 
379  t = t * crossingNumberProduct( x, y, pos1, pos2 );
380  if( t == 0 )
381  {
382  break;
383  }
384  }
385  return t >= 0;
386 }
387 
389 {
390  if( y == b.y() && y == c.y() )
391  {
392  if( ( b.x() <= x && x <= c.y() ) || ( c.x() <= x && x <= b.x() ) )
393  {
394  return 0;
395  }
396  else
397  {
398  return 1;
399  }
400  }
401  if( y == b.y() && x == b.x() )
402  {
403  return 0;
404  }
405 
406  if( b.y() > c.y() )
407  {
408  WPosition help = b;
409  b = c;
410  c = help;
411  }
412  if( y <= b.y() || y > c.y() )
413  {
414  return 1;
415  }
416 
417  float delta = ( b.x() - x ) * ( c.y() - y ) - ( b.y() - y ) * ( c.x() - x );
418  return delta > 0 ? -1 : delta < 0 ? 1 : 0;
419 }
420 
421 void WOnscreenSelection::setClickType( bool clickType )
422 {
423  m_clickType = clickType;
424 }
425 
427 {
428  return m_clickType;
429 }
430 
432 {
433  return m_moved;
434 }
Class encapsulating the OSG Program class for a more convenient way of adding and modifying shader.
Definition: WGEShader.h:48
static WKernel * getRunningKernel()
Returns pointer to the currently running kernel.
Definition: WKernel.cpp:117
std::shared_ptr< WGraphicsEngine > getGraphicsEngine() const
Returns pointer to currently running instance of graphics engine.
Definition: WKernel.cpp:122
Handles the GUIEvents in context of the WOnscreenSelection.
std::vector< WPosition > m_line
The points that are used for the selection.
bool lineloopCheck(float x, float y)
Check for the lineloop selection.
osg::ref_ptr< osg::Projection > m_projection
The Projection to draw on.
void setSelectionType(enum WSelectionType selectionType)
Sets the current selection type.
void clear()
Clears the current selection.
void start(float x, float y)
Starts the selection.
CallbackType m_onend
The Callback for the end function.
bool brushCheck(float x, float y)
Check for the brush selection.
osg::ref_ptr< WGEShader > m_shader
The shader for the selection.
int crossingNumberProduct(float x, float y, WPosition b, WPosition c)
Calculates the crossing number product.
void setOnmove(CallbackType onmove)
Sets the callback for the onmove function.
void setOnend(CallbackType onend)
Sets the callback for the onend function.
float m_thickness
The thickness of the brush.
CallbackType m_onmove
The Callback for the move function.
bool getClickType()
Gets the click type.
CallbackType m_onstart
The Callback for the start function.
void setClickType(bool clickType)
Sets the click type.
~WOnscreenSelection()
Cleans up all the now unneeded drawing context stuff.
bool m_clickType
Whether this is a left click or not.
bool isSelected(float x, float y, float z)
Whether a point is selected or not.
void move(float x, float y)
Handles mouse move while selecting.
bool m_moved
Whether the mouse has been moved.
WOnscreenSelection()
Not only creates the Object but also sets up the drawing context.
bool m_isSelecting
Whether a selection has been started or not.
enum WSelectionType getSelectionType()
Gets the current selection type.
void setOnstart(CallbackType onstart)
Sets the callback for the onstart function.
bool boxCheck(float x, float y)
Check for the box selection.
void updateDisplay()
Updates the current rendered data.
WSelectionType
The different types of selection.
bool isSelecting()
Returns whether this manager is currently creating a selection.
void end(float x, float y)
Ends the selection.
osg::ref_ptr< WOnscreenSelectionHandler > m_selectionHandler
The SelectionHandler for this object.
boost::function< void(enum WSelectionType, float, float) > CallbackType
A typedef for the type of the callbacks.
enum WSelectionType m_selectionType
The current selection type.
This only is a 3d double vector.