1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
|
#include "SelectionMouseTools.h"
#include "iscenegraph.h"
#include "i18n.h"
#include "icolourscheme.h"
#include "registry/registry.h"
#include "selection/Device.h"
#include "Rectangle.h"
#include "selection/SelectionVolume.h"
#include "igl.h"
#include <climits>
namespace ui
{
namespace
{
const char* const RKEY_SELECT_EPSILON = "user/ui/selectionEpsilon";
}
SelectMouseTool::SelectMouseTool() :
_selectEpsilon(registry::getValue<float>(RKEY_SELECT_EPSILON))
{}
MouseTool::Result SelectMouseTool::onMouseDown(Event& ev)
{
_view = render::View(ev.getInteractiveView().getVolumeTest());
// Reset the epsilon
_epsilon.x() = _selectEpsilon / ev.getInteractiveView().getDeviceWidth();
_epsilon.y() = _selectEpsilon / ev.getInteractiveView().getDeviceHeight();
return Result::Activated;
}
MouseTool::Result SelectMouseTool::onMouseUp(Event& ev)
{
// Invoke the testselect virtual
testSelect(ev);
// Refresh the view now that we're done
ev.getInteractiveView().queueDraw();
return Result::Finished;
}
// DragSelection
const std::string& BasicSelectionTool::getName()
{
static std::string name("BasicSelectionTool");
return name;
}
const std::string& BasicSelectionTool::getDisplayName()
{
static std::string displayName(_("Select"));
return displayName;
}
MouseTool::Result BasicSelectionTool::onMouseDown(Event& ev)
{
_start = _current = ev.getDevicePosition();
return SelectMouseTool::onMouseDown(ev);
}
MouseTool::Result BasicSelectionTool::onMouseMove(Event& ev)
{
_current = ev.getDevicePosition();
if (ev.getInteractiveView().supportsDragSelections())
{
updateDragSelectionRectangle(ev);
}
return Result::Continued;
}
void BasicSelectionTool::onMouseCaptureLost(IInteractiveView& view)
{
onCancel(view); // same behaviour as cancel
}
BasicSelectionTool::Result BasicSelectionTool::onCancel(IInteractiveView& view)
{
// Reset the mouse position to zero
_start = _current = Vector2(0.0f, 0.0f);
_dragSelectionRect = selection::Rectangle();
// Update the views
SceneChangeNotify();
return Result::Finished;
}
void BasicSelectionTool::renderOverlay()
{
// Define the blend function for transparency
glEnable(GL_BLEND);
glBlendColor(0, 0, 0, 0.2f);
glBlendFunc(GL_CONSTANT_ALPHA_EXT, GL_ONE_MINUS_CONSTANT_ALPHA_EXT);
Vector3 dragBoxColour = GlobalColourSchemeManager().getColour("drag_selection");
glColor3dv(dragBoxColour);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// The transparent fill rectangle
glBegin(GL_QUADS);
glVertex2f(_dragSelectionRect.min.x(), _dragSelectionRect.min.y());
glVertex2f(_dragSelectionRect.max.x(), _dragSelectionRect.min.y());
glVertex2f(_dragSelectionRect.max.x(), _dragSelectionRect.max.y());
glVertex2f(_dragSelectionRect.min.x(), _dragSelectionRect.max.y());
glEnd();
// The solid borders
glBlendColor(0, 0, 0, 0.8f);
glBegin(GL_LINE_LOOP);
glVertex2f(_dragSelectionRect.min.x(), _dragSelectionRect.min.y());
glVertex2f(_dragSelectionRect.max.x(), _dragSelectionRect.min.y());
glVertex2f(_dragSelectionRect.max.x(), _dragSelectionRect.max.y());
glVertex2f(_dragSelectionRect.min.x(), _dragSelectionRect.max.y());
glEnd();
glDisable(GL_BLEND);
}
void BasicSelectionTool::performSelectionTest(SelectionVolume& volume, SelectionType type, MouseTool::Event& ev)
{
if (type == SelectionType::Area)
{
GlobalSelectionSystem().selectArea(volume, selection::SelectionSystem::eToggle, selectFacesOnly());
}
else
{
GlobalSelectionSystem().selectPoint(volume, selection::SelectionSystem::eToggle, selectFacesOnly());
}
}
void BasicSelectionTool::testSelect(MouseTool::Event& ev)
{
// Get the distance of the mouse pointer from the starting point
Vector2 delta(ev.getDevicePosition() - _start);
// If the mouse pointer has moved more than <epsilon>, this is considered a drag operation
if (ev.getInteractiveView().supportsDragSelections() && fabs(delta.x()) > _epsilon.x() && fabs(delta.y()) > _epsilon.y())
{
// Construct the selection test according to the area the user covered with his drag
render::View scissored(_view);
ConstructSelectionTest(scissored, selection::Rectangle::ConstructFromArea(_start, delta));
SelectionVolume volume(scissored);
performSelectionTest(volume, SelectionType::Area, ev);
}
else
{
// Mouse has barely moved, call the point selection routine
//
// Copy the view to create a scissored volume
render::View scissored(_view);
// Create a volume out of a small box with 2*epsilon edge length
ConstructSelectionTest(scissored,
selection::Rectangle::ConstructFromPoint(ev.getDevicePosition(), _epsilon));
// Create a selection test using that volume
SelectionVolume volume(scissored);
performSelectionTest(volume, SelectionType::Point, ev);
}
// Reset the mouse position to zero, this mouse operation is finished so far
_start = _current = Vector2(0.0f, 0.0f);
_dragSelectionRect = selection::Rectangle();
}
void BasicSelectionTool::updateDragSelectionRectangle(Event& ev)
{
// get the mouse position relative to the starting point
Vector2 delta(_current - _start);
if (fabs(delta.x()) > _epsilon.x() && fabs(delta.y()) > _epsilon.y())
{
_dragSelectionRect = selection::Rectangle::ConstructFromArea(_start, delta);
_dragSelectionRect.toScreenCoords(ev.getInteractiveView().getDeviceWidth(),
ev.getInteractiveView().getDeviceHeight());
}
else // ...otherwise return an empty area
{
_dragSelectionRect = selection::Rectangle();
}
ev.getInteractiveView().queueDraw();
}
const std::string& DragSelectionMouseToolFaceOnly::getName()
{
static std::string name("DragSelectionMouseToolFaceOnly");
return name;
}
const std::string& DragSelectionMouseToolFaceOnly::getDisplayName()
{
static std::string displayName(_("Select Faces"));
return displayName;
}
// Cycle Selection
CycleSelectionMouseTool::CycleSelectionMouseTool() :
_mouseMovedSinceLastSelect(true),
_lastSelectPos(INT_MAX, INT_MAX)
{}
const std::string& CycleSelectionMouseTool::getName()
{
static std::string name("CycleSelectionMouseTool");
return name;
}
const std::string& CycleSelectionMouseTool::getDisplayName()
{
static std::string displayName(_("Cycle Selection"));
return displayName;
}
MouseTool::Result CycleSelectionMouseTool::onMouseMove(Event& ev)
{
// Reset the counter, mouse has moved
_mouseMovedSinceLastSelect = true;
return Result::Continued;
}
void CycleSelectionMouseTool::onMouseCaptureLost(IInteractiveView& view)
{
onCancel(view); // same as cancel
}
CycleSelectionMouseTool::Result CycleSelectionMouseTool::onCancel(IInteractiveView& view)
{
_mouseMovedSinceLastSelect = true;
return Result::Finished;
}
void CycleSelectionMouseTool::performPointSelection(SelectionVolume& volume, selection::SelectionSystem::EModifier modifier)
{
GlobalSelectionSystem().selectPoint(volume, modifier, selectFacesOnly());
}
void CycleSelectionMouseTool::testSelect(MouseTool::Event& ev)
{
const Vector2& curPos = ev.getDevicePosition();
// If the mouse has moved in between selections, reset the depth counter
if (!_mouseMovedSinceLastSelect && curPos != _lastSelectPos)
{
_mouseMovedSinceLastSelect = true;
}
// If we already replaced a selection, switch to cycle mode
// eReplace should only be active during the first call without mouse movement
auto modifier = _mouseMovedSinceLastSelect ? selection::SelectionSystem::eReplace : selection::SelectionSystem::eCycle;
// Copy the view to create a scissored volume
render::View scissored(_view);
// Create a volume out of a small box with 2*epsilon edge length
ConstructSelectionTest(scissored, selection::Rectangle::ConstructFromPoint(curPos, _epsilon));
// Create a selection test using that volume
SelectionVolume volume(scissored);
// Invoke the virtual function to dispatch the selection request
performPointSelection(volume, modifier);
// Remember this position
_lastSelectPos = curPos;
_mouseMovedSinceLastSelect = false;
}
const std::string& CycleSelectionMouseToolFaceOnly::getName()
{
static std::string name("CycleSelectionMouseToolFaceOnly");
return name;
}
const std::string& CycleSelectionMouseToolFaceOnly::getDisplayName()
{
static std::string displayName(_("Cycle Face Selection"));
return displayName;
}
}
|