/*========================================================================= Program: Visualization Toolkit Module: vtkContextScene.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 "vtkContextScene.h" #include "vtkContext2D.h" #include "vtkContextItem.h" #include "vtkContextKeyEvent.h" #include "vtkContextMouseEvent.h" #include "vtkContextScenePrivate.h" #include "vtkMatrix3x3.h" #include "vtkNew.h" #include "vtkTransform2D.h" // Get my new commands #include "vtkCommand.h" #include "vtkAbstractContextBufferId.h" #include "vtkAnnotationLink.h" #include "vtkObjectFactory.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" // My STL containers #include //------------------------------------------------------------------------------ // Minimal storage class for STL containers etc. class vtkContextScene::Private { public: Private() { this->Event.SetButton(vtkContextMouseEvent::NO_BUTTON); this->IsDirty = true; } ~Private() = default; // The item with a current mouse down vtkWeakPointer itemMousePressCurrent; // Item the mouse was last over vtkWeakPointer itemPicked; // Mouse event structure vtkContextMouseEvent Event; bool IsDirty; }; //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkContextScene); vtkCxxSetObjectMacro(vtkContextScene, AnnotationLink, vtkAnnotationLink); //------------------------------------------------------------------------------ vtkContextScene::vtkContextScene() { this->Storage = new Private; this->AnnotationLink = nullptr; this->Geometry[0] = 0; this->Geometry[1] = 0; this->BufferId = nullptr; this->BufferIdDirty = true; this->BufferIdSupportTested = false; this->BufferIdSupported = false; this->UseBufferId = true; this->ScaleTiles = true; this->Transform = nullptr; this->Children = new vtkContextScenePrivate(nullptr); this->Children->SetScene(this); } //------------------------------------------------------------------------------ vtkContextScene::~vtkContextScene() { delete this->Storage; this->Storage = nullptr; this->SetAnnotationLink(nullptr); if (this->BufferId != nullptr) { this->BufferId->Delete(); } if (this->Transform) { this->Transform->Delete(); } delete this->Children; } //------------------------------------------------------------------------------ void vtkContextScene::SetRenderer(vtkRenderer* r) { this->Renderer = r; this->BufferIdSupportTested = false; } //------------------------------------------------------------------------------ vtkRenderer* vtkContextScene::GetRenderer() { return this->Renderer; } //------------------------------------------------------------------------------ bool vtkContextScene::Paint(vtkContext2D* painter) { vtkDebugMacro("Paint event called."); size_t size = this->Children->size(); if (size && this->Transform) { painter->PushMatrix(); painter->SetTransform(this->Transform); } this->Children->PaintItems(painter); if (size && this->Transform) { painter->PopMatrix(); } if (this->Storage->IsDirty) { this->BufferIdDirty = true; } this->Storage->IsDirty = false; this->LastPainter = painter; return true; } //------------------------------------------------------------------------------ void vtkContextScene::PaintIds() { vtkDebugMacro("PaintId called."); size_t size = this->Children->size(); if (size > 16777214) // 24-bit limit, 0 reserved for background encoding. { vtkWarningMacro(<< "picking will not work properly as there are two many items. Items over " "16777214 will be ignored."); size = 16777214; } for (size_t i = 0; i < size; ++i) { this->LastPainter->ApplyId(static_cast(i + 1)); (*this->Children)[i]->Paint(this->LastPainter); } this->Storage->IsDirty = false; } //------------------------------------------------------------------------------ unsigned int vtkContextScene::AddItem(vtkAbstractContextItem* item) { return this->Children->AddItem(item); } //------------------------------------------------------------------------------ bool vtkContextScene::RemoveItem(vtkAbstractContextItem* item) { return this->Children->RemoveItem(item); } //------------------------------------------------------------------------------ bool vtkContextScene::RemoveItem(unsigned int index) { return this->Children->RemoveItem(index); } //------------------------------------------------------------------------------ vtkAbstractContextItem* vtkContextScene::GetItem(unsigned int index) { if (index < this->Children->size()) { return this->Children->at(index); } else { return nullptr; } } //------------------------------------------------------------------------------ unsigned int vtkContextScene::GetNumberOfItems() { return static_cast(this->Children->size()); } //------------------------------------------------------------------------------ void vtkContextScene::ClearItems() { this->Children->Clear(); } //------------------------------------------------------------------------------ int vtkContextScene::GetViewWidth() { if (this->Renderer) { return this->Renderer->GetRenderWindow()->GetSize()[0]; } else { return 0; } } //------------------------------------------------------------------------------ int vtkContextScene::GetViewHeight() { if (this->Renderer) { return this->Renderer->GetRenderWindow()->GetSize()[1]; } else { return 0; } } //------------------------------------------------------------------------------ int vtkContextScene::GetSceneWidth() { return this->Geometry[0]; } //------------------------------------------------------------------------------ int vtkContextScene::GetSceneHeight() { return this->Geometry[1]; } //------------------------------------------------------------------------------ vtkVector2i vtkContextScene::GetLogicalTileScale() { vtkVector2i result(1); if (this->ScaleTiles && this->Renderer && this->Renderer->GetRenderWindow()) { this->Renderer->GetRenderWindow()->GetTileScale(result.GetData()); } return result; } //------------------------------------------------------------------------------ void vtkContextScene::SetDirty(bool isDirty) { if (this->Storage->IsDirty == isDirty) { return; } this->Storage->IsDirty = isDirty; if (this->Storage->IsDirty) { this->BufferIdDirty = true; } this->Modified(); } //------------------------------------------------------------------------------ bool vtkContextScene::GetDirty() const { return this->Storage->IsDirty; } //------------------------------------------------------------------------------ void vtkContextScene::ReleaseGraphicsResources() { if (this->BufferId != nullptr) { this->BufferId->ReleaseGraphicsResources(); } for (vtkContextScenePrivate::const_iterator it = this->Children->begin(); it != this->Children->end(); ++it) { (*it)->ReleaseGraphicsResources(); } } //------------------------------------------------------------------------------ vtkWeakPointer vtkContextScene::GetLastPainter() { return this->LastPainter; } //------------------------------------------------------------------------------ vtkAbstractContextBufferId* vtkContextScene::GetBufferId() { return this->BufferId; } //------------------------------------------------------------------------------ void vtkContextScene::SetTransform(vtkTransform2D* transform) { if (this->Transform == transform) { return; } this->Transform->Delete(); this->Transform = transform; this->Transform->Register(this); } //------------------------------------------------------------------------------ vtkTransform2D* vtkContextScene::GetTransform() { if (this->Transform) { return this->Transform; } else { this->Transform = vtkTransform2D::New(); return this->Transform; } } //------------------------------------------------------------------------------ bool vtkContextScene::ProcessSelectionEvent(unsigned int rect[5]) { cout << "ProcessSelectionEvent called! " << endl; cout << "Rect:"; for (int i = 0; i < 5; ++i) { cout << "\t" << rect[i]; } cout << endl; return false; } //------------------------------------------------------------------------------ void vtkContextScene::TestBufferIdSupport() { if (!this->BufferIdSupportTested) { vtkNew b; b->SetContext(this->Renderer->GetRenderWindow()); this->BufferIdSupported = b->IsSupported(); b->ReleaseGraphicsResources(); this->BufferIdSupportTested = true; } } //------------------------------------------------------------------------------ void vtkContextScene::UpdateBufferId() { int lowerLeft[2]; int width; int height; this->Renderer->GetTiledSizeAndOrigin(&width, &height, lowerLeft, lowerLeft + 1); if (this->BufferId == nullptr || this->BufferIdDirty || width != this->BufferId->GetWidth() || height != this->BufferId->GetHeight()) { if (this->BufferId == nullptr) { this->BufferId = vtkAbstractContextBufferId::New(); this->BufferId->SetContext(this->Renderer->GetRenderWindow()); } this->BufferId->SetWidth(width); this->BufferId->SetHeight(height); this->BufferId->Allocate(); this->LastPainter->BufferIdModeBegin(this->BufferId); this->PaintIds(); this->LastPainter->BufferIdModeEnd(); this->BufferIdDirty = false; } } //------------------------------------------------------------------------------ vtkAbstractContextItem* vtkContextScene::GetPickedItem() { vtkContextMouseEvent& event = this->Storage->Event; for (vtkContextScenePrivate::const_reverse_iterator it = this->Children->rbegin(); it != this->Children->rend(); ++it) { vtkAbstractContextItem* item = (*it)->GetPickedItem(event); if (item) { return item; } } return nullptr; } //------------------------------------------------------------------------------ vtkIdType vtkContextScene::GetPickedItem(int x, int y) { vtkIdType result = -1; this->TestBufferIdSupport(); if (this->UseBufferId && this->BufferIdSupported) { this->UpdateBufferId(); result = this->BufferId->GetPickedItem(x, y); } else { size_t i = this->Children->size() - 1; vtkContextMouseEvent& event = this->Storage->Event; for (vtkContextScenePrivate::const_reverse_iterator it = this->Children->rbegin(); it != this->Children->rend(); ++it, --i) { if ((*it)->Hit(event)) { result = static_cast(i); break; } } } // Work-around for Qt bug under Linux (and maybe other platforms), 4.5.2 // or 4.6.2 : // when the cursor leaves the window, Qt returns an extra mouse move event // with coordinates out of the window area. The problem is that the pixel // underneath is not owned by the OpenGL context, hence the bufferid contains // garbage (see OpenGL pixel ownership test). // As a workaround, any value out of the scope of // [-1,this->GetNumberOfItems()-1] is set to -1 (<=> no hit) if (result < -1 || result >= static_cast(this->GetNumberOfItems())) { result = -1; } assert("post: valid_result" && result >= -1 && result < static_cast(this->GetNumberOfItems())); return result; } //------------------------------------------------------------------------------ bool vtkContextScene::MouseMoveEvent(const vtkContextMouseEvent& e) { bool res = false; vtkContextMouseEvent& event = this->Storage->Event; this->EventCopy(e); vtkAbstractContextItem* newItemPicked = this->GetPickedItem(); #if 0 if (newItemPicked) { cerr << "picked a " << newItemPicked->GetClassName() << endl; } else { cerr << "picked nothing" << endl; } #endif if (this->Storage->itemPicked != newItemPicked) { if (this->Storage->itemPicked) { // Make sure last picked object is still part of this scene. if (this->Storage->itemPicked->GetScene() == this) { vtkAbstractContextItem* cur = this->Storage->itemPicked; res = this->ProcessItem(cur, event, &vtkAbstractContextItem::MouseLeaveEvent) || res; } } if (newItemPicked) { vtkAbstractContextItem* cur = newItemPicked; res = this->ProcessItem(cur, event, &vtkAbstractContextItem::MouseEnterEvent) || res; } } this->Storage->itemPicked = newItemPicked; // Fire mouse move event regardless of where it occurred. // Check if there is a selected item that needs to receive a move event if (this->Storage->itemMousePressCurrent && this->Storage->itemMousePressCurrent->GetScene() == this) { vtkAbstractContextItem* cur = this->Storage->itemMousePressCurrent; res = this->ProcessItem(cur, event, &vtkAbstractContextItem::MouseMoveEvent) || res; } else if (this->Storage->itemPicked) { vtkAbstractContextItem* cur = this->Storage->itemPicked; res = this->ProcessItem(cur, event, &vtkAbstractContextItem::MouseMoveEvent) || res; } // Update the last positions now event.SetLastScreenPos(event.GetScreenPos()); event.SetLastScenePos(event.GetScenePos()); event.SetLastPos(event.GetPos()); return res; } //------------------------------------------------------------------------------ bool vtkContextScene::ButtonPressEvent(const vtkContextMouseEvent& e) { switch (e.GetButton()) { case vtkContextMouseEvent::LEFT_BUTTON: this->InvokeEvent(vtkCommand::LeftButtonPressEvent); break; case vtkContextMouseEvent::MIDDLE_BUTTON: this->InvokeEvent(vtkCommand::MiddleButtonPressEvent); break; case vtkContextMouseEvent::RIGHT_BUTTON: this->InvokeEvent(vtkCommand::RightButtonPressEvent); break; default: break; } bool res = false; vtkContextMouseEvent& event = this->Storage->Event; this->EventCopy(e); event.SetLastScreenPos(event.GetScreenPos()); event.SetLastScenePos(event.GetScenePos()); event.SetLastPos(event.GetPos()); event.SetButton(e.GetButton()); vtkAbstractContextItem* newItemPicked = this->GetPickedItem(); if (newItemPicked) { vtkAbstractContextItem* cur = newItemPicked; res = this->ProcessItem(cur, event, &vtkAbstractContextItem::MouseButtonPressEvent); } this->Storage->itemMousePressCurrent = newItemPicked; return res; } //------------------------------------------------------------------------------ bool vtkContextScene::ButtonReleaseEvent(const vtkContextMouseEvent& e) { switch (e.GetButton()) { case vtkContextMouseEvent::LEFT_BUTTON: this->InvokeEvent(vtkCommand::LeftButtonReleaseEvent); break; case vtkContextMouseEvent::MIDDLE_BUTTON: this->InvokeEvent(vtkCommand::MiddleButtonReleaseEvent); break; case vtkContextMouseEvent::RIGHT_BUTTON: this->InvokeEvent(vtkCommand::RightButtonReleaseEvent); break; default: break; } bool res = false; if (this->Storage->itemMousePressCurrent) { vtkContextMouseEvent& event = this->Storage->Event; this->EventCopy(e); event.SetButton(e.GetButton()); vtkAbstractContextItem* cur = this->Storage->itemMousePressCurrent; res = this->ProcessItem(cur, event, &vtkAbstractContextItem::MouseButtonReleaseEvent); this->Storage->itemMousePressCurrent = nullptr; } this->Storage->Event.SetButton(vtkContextMouseEvent::NO_BUTTON); return res; } //------------------------------------------------------------------------------ bool vtkContextScene::DoubleClickEvent(const vtkContextMouseEvent& e) { bool res = false; vtkContextMouseEvent& event = this->Storage->Event; this->EventCopy(e); event.SetLastScreenPos(event.GetScreenPos()); event.SetLastScenePos(event.GetScenePos()); event.SetLastPos(event.GetPos()); event.SetButton(e.GetButton()); vtkAbstractContextItem* newItemPicked = this->GetPickedItem(); if (newItemPicked) { vtkAbstractContextItem* cur = newItemPicked; res = this->ProcessItem(cur, event, &vtkAbstractContextItem::MouseDoubleClickEvent); } return res; } //------------------------------------------------------------------------------ bool vtkContextScene::MouseWheelEvent(int delta, const vtkContextMouseEvent& e) { bool res = false; vtkContextMouseEvent& event = this->Storage->Event; this->EventCopy(e); event.SetLastScreenPos(event.GetScreenPos()); event.SetLastScenePos(event.GetScenePos()); event.SetLastPos(event.GetPos()); event.SetButton(vtkContextMouseEvent::NO_BUTTON); vtkAbstractContextItem* newItemPicked = this->GetPickedItem(); if (newItemPicked) { vtkContextMouseEvent itemEvent = event; vtkAbstractContextItem* cur = newItemPicked; itemEvent.SetPos(cur->MapFromScene(event.GetPos())); itemEvent.SetLastPos(cur->MapFromScene(event.GetLastPos())); while (cur && !cur->MouseWheelEvent(itemEvent, delta)) { cur = cur->GetParent(); if (cur) { itemEvent.SetPos(cur->MapToParent(itemEvent.GetPos())); itemEvent.SetLastPos(cur->MapToParent(itemEvent.GetLastPos())); } } res = (cur != nullptr); } return res; } //------------------------------------------------------------------------------ bool vtkContextScene::KeyPressEvent(const vtkContextKeyEvent& keyEvent) { vtkContextMouseEvent& event = this->Storage->Event; event.SetScreenPos(keyEvent.GetPosition()); vtkAbstractContextItem* newItemPicked = this->GetPickedItem(); if (newItemPicked) { return newItemPicked->KeyPressEvent(keyEvent); } return false; } //------------------------------------------------------------------------------ bool vtkContextScene::KeyReleaseEvent(const vtkContextKeyEvent& keyEvent) { vtkContextMouseEvent& event = this->Storage->Event; event.SetScreenPos(keyEvent.GetPosition()); vtkAbstractContextItem* newItemPicked = this->GetPickedItem(); if (newItemPicked) { return newItemPicked->KeyReleaseEvent(keyEvent); } return false; } //------------------------------------------------------------------------------ inline bool vtkContextScene::ProcessItem( vtkAbstractContextItem* cur, const vtkContextMouseEvent& event, MouseEvents eventPtr) { bool res = false; vtkContextMouseEvent itemEvent = event; itemEvent.SetPos(cur->MapFromScene(event.GetPos())); itemEvent.SetLastPos(cur->MapFromScene(event.GetLastPos())); while (cur && !(cur->*eventPtr)(itemEvent)) { cur = cur->GetParent(); if (cur) { itemEvent.SetPos(cur->MapToParent(itemEvent.GetPos())); itemEvent.SetLastPos(cur->MapToParent(itemEvent.GetLastPos())); } } res = (cur != nullptr); return res; } //------------------------------------------------------------------------------ inline void vtkContextScene::EventCopy(const vtkContextMouseEvent& e) { vtkContextMouseEvent& event = this->Storage->Event; event.SetPos(e.GetPos()); event.SetScreenPos(vtkVector2i(e.GetPos().Cast().GetData())); event.SetScenePos(e.GetPos()); event.SetInteractor(e.GetInteractor()); } //------------------------------------------------------------------------------ void vtkContextScene::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); // Print out the chart's geometry if it has been set os << indent << "Widthxheight: " << this->Geometry[0] << "\t" << this->Geometry[1] << endl; }