Skip to content

Instantly share code, notes, and snippets.

@JensMunkHansen
Created October 30, 2024 18:47
Show Gist options
  • Save JensMunkHansen/c949a1841bb003a9368170b05f34e869 to your computer and use it in GitHub Desktop.
Save JensMunkHansen/c949a1841bb003a9368170b05f34e869 to your computer and use it in GitHub Desktop.
#pragma once
// TODO: Consider making this using the newer vtkAbstractWidget and
// vtkWidgetRepresentation
//
#include "spsInteractionWidgetsModule.h" // For export macro
#include <vtk3DWidget.h>
class vtkActor;
class vtkCellPicker;
class vtkConeSource;
class vtkLineSource;
class vtkCubeSource;
class vtkPlaneSource;
class vtkPointPlacer;
class vtkPolyData;
class vtkPolyDataMapper;
class vtkProp;
class vtkProperty;
class vtkTransform;
class SPSINTERACTIONWIDGETS_EXPORT vtkImplicitRectangleWidget : public vtk3DWidget
{
public:
static vtkImplicitRectangleWidget* New();
vtkTypeMacro(vtkImplicitRectangleWidget, vtk3DWidget)
static const double MaxDepth;
static const double MinWidth;
static const double MinHeight;
///@{
/**
* Methods that satisfy the superclass' API.
*/
void PrintSelf(std::ostream& os, vtkIndent indent) override;
void SetEnabled(int) override;
void PlaceWidget(double bounds[6]) override;
void PlaceWidget() override { this->Superclass::PlaceWidget(); }
void PlaceWidget(
double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) override
{
this->Superclass::PlaceWidget(xmin, xmax, ymin, ymax, zmin, zmax);
}
///@}
///@{
/**
* Set/Get the point placer. Point placers can be used to dictate constraints
* on the placement of handles. As an example, see vtkBoundedPlanePointPlacer
* (constrains the placement of handles to a set of bounded planes)
* vtkFocalPlanePointPlacer (constrains placement on the focal plane) etc.
* The default point placer is vtkPointPlacer (which does not apply any
* constraints, so the handles are free to move anywhere).
*/
virtual void SetPointPlacer(vtkPointPlacer*);
vtkGetObjectMacro(PointPlacer, vtkPointPlacer);
///@}
///@{
/**
* Set/Get the resolution (number of subdivisions) of the x-axis
*/
void SetResolutionX(int r);
int GetResolutionX();
///@}
///@{
/**
* Set/Get the resolution (number of subdivisions) of the y-axis
*/
void SetResolutionY(int r);
int GetResolutionY();
///@}
///@{
/**
* Set/Get the origin of the rectamgle
*/
double* GetOrigin() VTK_SIZEHINT(3);
void GetOrigin(double xyz[3]);
///@}
///@{
/**
* Get the center of the rectangular box. This must be different
* from the origin for spinning to work.
*/
double* GetCenter() VTK_SIZEHINT(3);
void GetCenter(double xyz[3]);
///@}
double GetWidth();
void SetWidth(double width);
double GetHeight();
void SetHeight(double height);
double GetDepth();
void SetDepth(double height);
void SetPlaneProperty(vtkProperty*);
vtkGetObjectMacro(PlaneProperty, vtkProperty);
void SetSelectedPlaneProperty(vtkProperty*);
vtkGetObjectMacro(SelectedPlaneProperty, vtkProperty);
void SetSelectedPlaneCtrlProperty(vtkProperty*);
vtkGetObjectMacro(SelectedPlaneCtrlProperty, vtkProperty);
void SetHandleProperty(vtkProperty*);
vtkGetObjectMacro(HandleProperty, vtkProperty);
void SetSelectedHandleProperty(vtkProperty*);
vtkGetObjectMacro(SelectedHandleProperty, vtkProperty);
void SetSelectedHandleCtrlProperty(vtkProperty*);
vtkGetObjectMacro(SelectedHandleCtrlProperty, vtkProperty);
void SetSelectedPolyHandleProperty(vtkProperty*);
vtkGetObjectMacro(SelectedPolyHandleProperty, vtkProperty);
void SetPolyHandleProperty(vtkProperty*);
vtkGetObjectMacro(PolyHandleProperty, vtkProperty);
void SetCubeProperty(vtkProperty*);
vtkGetObjectMacro(CubeProperty, vtkProperty);
// Set operation must be handled explicitly (easy we hide it!)
// void SetUserTransform(vtkTransform*);
vtkGetObjectMacro(UserTransform, vtkTransform);
vtkGetObjectMacro(Handle, vtkPolyData);
void SetHandle(vtkPolyData* args);
///@{
/**
* Height offset at which points may be placed on the polygonal surface.
* If you specify a non-zero value here, be sure to compute cell normals
* on your input polygonal data (easily done with vtkPolyDataNormals).
*/
vtkSetMacro(DistanceOffset, double);
vtkGetMacro(DistanceOffset, double);
///@}
vtkGetObjectMacro(PlaneSource, vtkPlaneSource);
virtual void UpdateCamera();
protected:
virtual void ShowBox();
vtkImplicitRectangleWidget();
~vtkImplicitRectangleWidget();
void ReleaseHandle();
enum WidgetState
{
Start = 0,
Moving,
MovingOnSurface,
Scaling,
Pushing,
Rotating,
Spinning,
SpinningAxis,
Outside,
Pinching // Semantic zooming by spreading two fingers
};
// handles the events
static void ProcessEvents(
vtkObject* object, unsigned long event, void* clientdata, void* calldata);
// ProcessEvents() dispatches to these methods.
void OnLeftButtonDown();
void OnLeftButtonUp();
void OnMiddleButtonDown();
void OnMiddleButtonUp();
void OnRightButtonDown();
void OnRightButtonUp();
void OnMouseMove();
void OnStartPinch();
void OnPinch();
void OnEndPinch();
void SelectRepresentation();
void SizeHandles() override;
void HighlightRectangle(int highlight);
void HighlightHandle(vtkProp* prop);
int State;
vtkTransform* UserTransform;
vtkCubeSource* CubeSource;
vtkPolyDataMapper* CubeMapper;
vtkActor* CubeActor;
vtkPlaneSource* PlaneSource;
vtkPolyDataMapper* PlaneMapper;
vtkActor* PlaneActor;
double Center[3];
double Origin[3];
// the handle cone
vtkConeSource* ConeSource;
vtkPolyDataMapper* ConeMapper;
vtkActor* ConeActor;
// the normal line
vtkLineSource* LineSource;
vtkPolyDataMapper* LineMapper;
vtkActor* LineActor;
vtkPolyDataMapper* PolyDataMapper;
vtkActor* PolyDataActor;
vtkPointPlacer* PointPlacer;
// Do the picking
vtkCellPicker* HandlePicker;
vtkCellPicker* PlanePicker;
vtkActor* CurrentHandle;
int LastPickValid;
double HandleSizeFactor;
// Properties used to control the appearance of selected objects and
// the manipulator in general.
vtkProperty* HandleProperty;
vtkProperty* SelectedHandleProperty;
vtkProperty* SelectedHandleCtrlProperty;
vtkProperty* PlaneProperty;
vtkProperty* SelectedPlaneProperty;
vtkProperty* SelectedPlaneCtrlProperty;
vtkProperty* PolyHandleProperty;
vtkProperty* SelectedPolyHandleProperty;
vtkProperty* CubeProperty;
double DistanceOffset;
double Width;
double Height;
double Depth;
// Experimental - some of it is for sure redundant
double StartEventPosition[3];
double WorldPosition[3];
// Like done in vtkIterativeClosestPoint
vtkPolyData* Handle;
// Register internal Pickers within PickingManager
void RegisterPickers() override;
void Rotate(int X, int Y, double* p1, double* p2, double* vpn);
void Spin(double* p1, double* p2, double* spinAxis);
void Scale(double* p1, double* p2, int X, int Y);
void Translate(double* p1, double* p2);
void Push(double* p1, double* p2);
void CreateDefaultProperties();
void PositionHandle();
// Taken from vtkAbstractPolygonalHandleRepresentation3D
void MoveFocusRequest(
const double* p1, const double* p2, const double currPos[2], double center[3]);
private:
vtkImplicitRectangleWidget(const vtkImplicitRectangleWidget&) = delete;
void operator=(const vtkImplicitRectangleWidget&) = delete;
};
// TODO: - Convert this to the new way of making widgets (postponed)
#include <vtkActor.h>
#include <vtkAssemblyNode.h>
#include <vtkAssemblyPath.h>
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkCellPicker.h>
#include <vtkConeSource.h>
#include <vtkCubeSource.h>
#include <vtkImplicitRectangleWidget.h>
#include <vtkLineSource.h>
#include <vtkMatrix4x4.h>
#include <vtkObjectFactory.h>
#include <vtkPickingManager.h>
#include <vtkPlaneSource.h>
#include <vtkPointPlacer.h>
#include <vtkPolyDataCollection.h>
#include <vtkPolyDataMapper.h>
#include <vtkPropPicker.h>
#include <vtkProperty.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkTransform.h>
#include <algorithm>
#ifndef SQUARE
#define SQUARE(z) ((z) * (z))
#endif // SQUARE
vtkStandardNewMacro(vtkImplicitRectangleWidget);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, PlaneProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, SelectedPlaneProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, SelectedPlaneCtrlProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, HandleProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, SelectedHandleProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, SelectedHandleCtrlProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, PolyHandleProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, SelectedPolyHandleProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, CubeProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, PointPlacer, vtkPointPlacer);
// Do not allow setting this - or ensure this is propagated to all actors
// vtkCxxSetObjectMacro(vtkImplicitRectangleWidget, UserTransform, vtkTransform)
const double vtkImplicitRectangleWidget::MaxDepth = 1000.0;
const double vtkImplicitRectangleWidget::MinWidth = 0.01;
const double vtkImplicitRectangleWidget::MinHeight = 0.01;
void vtkImplicitRectangleWidget::ReleaseHandle()
{
if (this->Handle)
{
this->Handle->UnRegister(this);
this->Handle = nullptr;
}
}
void vtkImplicitRectangleWidget::SetHandle(vtkPolyData* args)
{
// TODO: Verify nullptr argument is okay
if (this->Handle == args)
{
return;
}
if (this->Handle)
{
this->ReleaseHandle();
}
if (args)
{
args->Register(this);
}
this->Handle = args;
if (this->PolyDataActor)
{
this->HandlePicker->DeletePickList(this->PolyDataActor);
this->CurrentRenderer->RemoveActor(this->PolyDataActor);
}
// Extra stuff needed
this->PolyDataMapper->SetInputData(this->Handle);
this->PolyDataActor->SetMapper(this->PolyDataMapper);
this->PolyDataActor->SetUserTransform(this->UserTransform);
this->HandlePicker->AddPickList(this->PolyDataActor);
this->CurrentRenderer->AddActor(this->PolyDataActor);
this->Modified();
}
void vtkImplicitRectangleWidget::ShowBox()
{
if (this->Interactor->GetKeyCode() == 'b' || this->Interactor->GetKeyCode() == 'B')
{
// Show/hide clipping boxxs
// TODO: After moving camera verify bounds for box equal bounds for frustum
if (this->CurrentRenderer->HasViewProp(this->CubeActor))
{
this->CurrentRenderer->RemoveActor(this->CubeActor);
}
else
{
this->CurrentRenderer->RemoveActor(this->CubeActor);
this->CurrentRenderer->AddActor(this->CubeActor);
}
this->Interactor->Render();
}
else if (this->Interactor->GetKeyCode() == 'a' || this->Interactor->GetKeyCode() == 'A')
{
// Show/hide actor for polydata
if (this->CurrentRenderer->HasViewProp(this->PolyDataActor))
{
this->CurrentRenderer->RemoveActor(this->PolyDataActor);
}
else
{
this->CurrentRenderer->RemoveActor(this->PolyDataActor);
this->CurrentRenderer->AddActor(this->PolyDataActor);
}
this->Interactor->Render();
}
else if (this->Interactor->GetKeyCode() == 'h' || this->Interactor->GetKeyCode() == 'H')
{
if (this->CurrentRenderer->HasViewProp(this->ConeActor))
{
this->CurrentRenderer->RemoveActor(this->ConeActor);
this->CurrentRenderer->RemoveActor(this->LineActor);
}
else
{
this->CurrentRenderer->RemoveActor(this->ConeActor);
this->CurrentRenderer->RemoveActor(this->LineActor);
this->CurrentRenderer->AddActor(this->ConeActor);
this->CurrentRenderer->AddActor(this->LineActor);
}
this->Interactor->Render();
}
}
vtkImplicitRectangleWidget::vtkImplicitRectangleWidget()
{
// The user must enable this widget to use it
this->Enabled = 0;
this->EventCallbackCommand->SetCallback(vtkImplicitRectangleWidget::ProcessEvents);
this->State = vtkImplicitRectangleWidget::Start;
this->Width = 15;
this->Height = 15;
this->Depth = 30.0;
// Transform used for all actors
this->UserTransform = vtkTransform::New();
this->UserTransform->Identity();
this->PlaneSource = vtkPlaneSource::New();
this->PlaneSource->SetXResolution(1);
this->PlaneSource->SetYResolution(1);
this->CubeSource = vtkCubeSource::New();
this->CubeSource->SetBounds(-0.5 * this->Width, 0.5 * this->Width, -0.5 * this->Height,
0.5 * this->Width, 0.0, this->Depth);
this->CubeMapper = vtkPolyDataMapper::New();
this->CubeMapper->SetInputConnection(this->CubeSource->GetOutputPort());
this->CubeActor = vtkActor::New();
this->CubeActor->SetMapper(this->CubeMapper);
this->CubeActor->SetUserTransform(this->UserTransform);
this->Origin[0] = 0.0;
this->Origin[1] = 0.0;
this->Origin[2] = 0.0;
this->PlaneSource->SetOrigin(-0.5 * this->Width, -0.5 * this->Height, 0.0);
this->PlaneSource->SetPoint1(0.5 * this->Width, -0.5 * this->Height, 0.0);
this->PlaneSource->SetPoint2(-0.5 * this->Width, 0.5 * this->Height, 0.0);
this->PlaneSource->Update(); // Get the output before pipeline executes
this->PlaneMapper = vtkPolyDataMapper::New();
this->PlaneMapper->SetInputConnection(this->PlaneSource->GetOutputPort());
this->PlaneActor = vtkActor::New();
this->PlaneActor->SetMapper(this->PlaneMapper);
this->PlaneActor->SetUserTransform(this->UserTransform);
this->Center[0] = 0.0;
this->Center[1] = 0.0;
this->Center[2] =
1.0; // Needs to be different than origin for computing spin. TODO: Add offset elsewhere
this->ConeSource = vtkConeSource::New();
this->ConeSource->SetResolution(12);
this->ConeSource->SetAngle(25.0);
this->ConeSource->SetRadius(4.0);
// Direction of the cone, parallel to (Center - Origin)
double direction[3] = { 0.0, 0.0, 1.0 };
this->ConeSource->SetDirection(direction);
this->ConeMapper = vtkPolyDataMapper::New();
this->ConeMapper->SetInputConnection(this->ConeSource->GetOutputPort());
this->ConeActor = vtkActor::New();
this->ConeActor->SetMapper(this->ConeMapper);
this->ConeActor->SetUserTransform(this->UserTransform);
// Create the line to the handle
this->LineSource = vtkLineSource::New();
this->LineSource->SetResolution(1);
this->LineMapper = vtkPolyDataMapper::New();
this->LineMapper->SetInputConnection(this->LineSource->GetOutputPort());
this->LineActor = vtkActor::New();
this->LineActor->SetMapper(this->LineMapper);
this->LineActor->SetUserTransform(this->UserTransform);
this->PolyDataMapper = vtkPolyDataMapper::New();
this->PolyDataActor = vtkActor::New();
this->PointPlacer = vtkPointPlacer::New();
double bounds[6];
this->PlaneSource->GetOutput()->GetBounds(bounds);
// Set InitialLength of parent class (otherwise unitialized variable)
this->InitialLength = sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) +
(bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) +
(bounds[5] - bounds[4]) * (bounds[5] - bounds[4]));
// Manage the picking stuff
this->HandlePicker = vtkCellPicker::New();
this->HandlePicker->SetTolerance(0.001);
this->HandlePicker->AddPickList(this->ConeActor);
this->HandlePicker->AddPickList(this->LineActor);
this->HandlePicker->PickFromListOn();
this->PlanePicker = vtkCellPicker::New();
this->PlanePicker->SetTolerance(0.005); // need some fluff
this->PlanePicker->AddPickList(this->PlaneActor);
this->PlanePicker->PickFromListOn();
this->CurrentHandle = nullptr;
this->LastPickValid = 0;
this->HandleSizeFactor = 1.25;
this->SetHandleSize(0.15);
// Set up the initial properties
this->CreateDefaultProperties();
this->DistanceOffset = 0.0;
this->StartEventPosition[0] = this->StartEventPosition[1] = this->StartEventPosition[2] = 0.0;
this->WorldPosition[0] = this->WorldPosition[1] = this->WorldPosition[2] = 0.0;
this->Handle = nullptr;
// Initial creation of the widget, serves to initialize it
// Call PlaceWidget() LAST in the constructor as it depends on ivar values.
this->PlaceWidget(bounds);
}
void vtkImplicitRectangleWidget::SelectRepresentation()
{
if (!this->CurrentRenderer)
{
return;
}
else
{
this->CurrentRenderer->RemoveActor(this->PlaneActor);
this->CurrentRenderer->RemoveActor(this->PolyDataActor);
this->CurrentRenderer->RemoveActor(this->CubeActor);
this->CurrentRenderer->AddActor(this->PlaneActor);
this->CurrentRenderer->AddActor(this->PolyDataActor);
this->CurrentRenderer->AddActor(this->CubeActor);
}
}
void vtkImplicitRectangleWidget::PlaceWidget(double vtkNotUsed(bounds)[6])
{
this->PlaneSource->Update();
this->CubeSource->Update();
this->PositionHandle();
this->SizeHandles();
}
vtkImplicitRectangleWidget::~vtkImplicitRectangleWidget()
{
this->PlaneActor->Delete();
this->PlaneMapper->Delete();
this->PlaneSource->Delete();
this->CubeActor->Delete();
this->CubeMapper->Delete();
this->CubeSource->Delete();
this->ConeActor->Delete();
this->ConeMapper->Delete();
this->ConeSource->Delete();
this->LineActor->Delete();
this->LineMapper->Delete();
this->LineSource->Delete();
this->HandlePicker->Delete();
this->PlanePicker->Delete();
if (this->PolyDataActor)
{
this->PolyDataActor->Delete();
}
if (this->PolyDataMapper)
{
this->PolyDataMapper->Delete();
}
if (this->CubeProperty)
{
this->CubeProperty->Delete();
this->CubeProperty = nullptr;
}
if (this->HandleProperty)
{
this->HandleProperty->Delete();
this->HandleProperty = nullptr;
}
if (this->SelectedHandleProperty)
{
this->SelectedHandleProperty->Delete();
this->SelectedHandleProperty = nullptr;
}
if (this->SelectedHandleCtrlProperty)
{
this->SelectedHandleCtrlProperty->Delete();
this->SelectedHandleCtrlProperty = nullptr;
}
if (this->PlaneProperty)
{
this->PlaneProperty->Delete();
this->PlaneProperty = nullptr;
}
if (this->SelectedPlaneProperty)
{
this->SelectedPlaneProperty->Delete();
this->SelectedPlaneProperty = nullptr;
}
if (this->SelectedPlaneCtrlProperty)
{
this->SelectedPlaneCtrlProperty->Delete();
this->SelectedPlaneCtrlProperty = nullptr;
}
if (this->PolyHandleProperty)
{
this->PolyHandleProperty->Delete();
this->PolyHandleProperty = nullptr;
}
if (this->SelectedPolyHandleProperty)
{
this->SelectedPolyHandleProperty->Delete();
this->SelectedPolyHandleProperty = nullptr;
}
this->UserTransform->Delete();
this->UserTransform = nullptr;
this->ReleaseHandle();
this->SetPointPlacer(nullptr);
}
void vtkImplicitRectangleWidget::PrintSelf(std::ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Width: " << this->Width << endl;
os << indent << "Height: " << this->Height << endl;
os << indent << "Depth: " << this->Depth << endl;
if (this->HandleProperty)
{
os << indent << "Handle Property: " << this->HandleProperty << "\n";
}
else
{
os << indent << "Handle Property: (none)\n";
}
if (this->SelectedHandleProperty)
{
os << indent << "Selected Handle Property: " << this->SelectedHandleProperty << "\n";
}
else
{
os << indent << "SelectedHandle Property: (none)\n";
}
if (this->PlaneProperty)
{
os << indent << "Plane Property: " << this->PlaneProperty << "\n";
}
else
{
os << indent << "Plane Property: (none)\n";
}
if (this->SelectedPlaneProperty)
{
os << indent << "Selected Plane Property: " << this->SelectedPlaneProperty << "\n";
}
else
{
os << indent << "Selected Plane Property: (none)\n";
}
double* baseOrigin = this->Origin;
double* baseCenter = this->Center;
double o[3];
double c[3];
this->UserTransform->TransformPoint(baseOrigin, o);
this->UserTransform->TransformPoint(baseCenter, c);
os << indent << "Origin: [" << o[0] << ", " << o[1] << ", " << o[2] << "] (explicit)\n";
os << indent << "Center: [" << c[0] << ", " << c[1] << ", " << c[2] << "] (explicit)\n";
if (this->PointPlacer)
{
os << indent << "PointPlacer:\n";
this->PointPlacer->PrintSelf(os, indent.GetNextIndent());
}
else
{
os << indent << "PointPlacer: (none)\n";
}
}
void vtkImplicitRectangleWidget::SetEnabled(int enabling)
{
if (!this->Interactor)
{
vtkErrorMacro(<< "The interactor must be set prior to enabling/disabling widget");
return;
}
if (enabling) //-----------------------------------------------------------
{
vtkDebugMacro(<< "Enabling sector widget");
if (this->Enabled) // already enabled, just return
{
return;
}
if (!this->CurrentRenderer)
{
this->SetCurrentRenderer(this->Interactor->FindPokedRenderer(
this->Interactor->GetLastEventPosition()[0], this->Interactor->GetLastEventPosition()[1]));
if (this->CurrentRenderer == nullptr)
{
return;
}
}
this->Enabled = 1;
// listen for the following events
vtkRenderWindowInteractor* i = this->Interactor;
i->AddObserver(vtkCommand::MouseMoveEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::LeftButtonPressEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::MiddleButtonPressEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(
vtkCommand::MiddleButtonReleaseEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::RightButtonPressEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::RightButtonReleaseEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::StartPinchEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::PinchEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::EndPinchEvent, this->EventCallbackCommand, this->Priority);
i->AddObserver(vtkCommand::KeyPressEvent, this->EventCallbackCommand, this->Priority);
this->CurrentRenderer->AddActor(this->CubeActor);
this->CubeActor->SetProperty(this->CubeProperty);
// Add the plane
this->CurrentRenderer->AddActor(this->PlaneActor);
this->PlaneActor->SetProperty(this->PlaneProperty);
// Add the handle
this->CurrentRenderer->AddActor(this->LineActor);
this->LineActor->SetProperty(this->HandleProperty);
this->CurrentRenderer->AddActor(this->ConeActor);
this->ConeActor->SetProperty(this->HandleProperty);
this->CurrentRenderer->AddActor(this->PolyDataActor);
this->SelectRepresentation();
this->RegisterPickers();
this->InvokeEvent(vtkCommand::EnableEvent, nullptr);
}
else // disabling----------------------------------------------------------
{
vtkDebugMacro(<< "Disabling sector widget");
if (!this->Enabled) // already disabled, just return
{
return;
}
this->Enabled = 0;
// don't listen for events any more
this->Interactor->RemoveObserver(this->EventCallbackCommand);
// turn off the sector
this->CurrentRenderer->RemoveActor(this->PlaneActor);
// turn off the handle
this->CurrentRenderer->RemoveActor(this->LineActor);
this->CurrentRenderer->RemoveActor(this->ConeActor);
// turn of the polygonal actor
this->CurrentRenderer->RemoveActor(this->PolyDataActor);
this->CurrentHandle = nullptr;
this->InvokeEvent(vtkCommand::DisableEvent, nullptr);
this->SetCurrentRenderer(nullptr);
this->UnRegisterPickers();
}
this->UpdateCamera();
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::SetResolutionX(int r)
{
this->PlaneSource->SetXResolution(r);
}
int vtkImplicitRectangleWidget::GetResolutionX()
{
return this->PlaneSource->GetXResolution();
}
void vtkImplicitRectangleWidget::SetResolutionY(int r)
{
this->PlaneSource->SetYResolution(r);
}
int vtkImplicitRectangleWidget::GetResolutionY()
{
return this->PlaneSource->GetYResolution();
}
void vtkImplicitRectangleWidget::PositionHandle()
{
double handleLength;
double handleOrigin[3];
double origin[3];
// Right
handleLength = 3.0 * this->Height;
origin[0] = origin[1] = origin[2] = 0.0;
handleOrigin[0] = handleOrigin[1] = 0.0;
handleOrigin[2] = -handleLength;
// Update line source and cone source
this->LineSource->SetPoint1(origin);
this->LineSource->SetPoint2(handleOrigin);
this->ConeSource->SetCenter(handleOrigin);
// Add center property for widget (to rotate about)
this->SelectRepresentation();
}
void vtkImplicitRectangleWidget::HighlightHandle(vtkProp* prop)
{
// first unhighlight anything picked
if (this->CurrentHandle)
{
this->CurrentHandle->SetProperty(this->HandleProperty);
}
// Only the current part of the handle is selected
this->CurrentHandle = static_cast<vtkActor*>(prop);
if (this->CurrentHandle)
{
this->ValidPick = 1;
this->HandlePicker->GetPickPosition(this->LastPickPosition);
if (this->Interactor->GetControlKey())
{
this->CurrentHandle->SetProperty(this->SelectedHandleCtrlProperty);
}
else
{
this->CurrentHandle->SetProperty(this->SelectedHandleProperty);
}
}
}
double vtkImplicitRectangleWidget::GetWidth()
{
return this->Width;
}
double vtkImplicitRectangleWidget::GetHeight()
{
return this->Height;
}
double vtkImplicitRectangleWidget::GetDepth()
{
return this->Depth;
}
void vtkImplicitRectangleWidget::SetWidth(double width)
{
if (width != this->Width)
{
if (width < vtkImplicitRectangleWidget::MinWidth)
{
// We do not support very small widths.
return;
}
this->Width = width;
this->PlaneSource->SetOrigin(-0.5 * this->Width, -0.5 * this->Height, 0.0);
this->PlaneSource->SetPoint1(0.5 * this->Width, -0.5 * this->Height, 0.0);
this->PlaneSource->SetPoint2(-0.5 * this->Width, 0.5 * this->Height, 0.0);
this->PlaneSource->Update(); // Get the output before pipeline executes
double bounds[6];
this->CubeSource->GetBounds(bounds);
bounds[0] = -0.5 * this->Width;
bounds[1] = 0.5 * this->Width;
this->CubeSource->SetBounds(bounds);
this->CubeSource->Update();
this->Modified();
}
}
void vtkImplicitRectangleWidget::SetHeight(double height)
{
if (height != this->Height)
{
if (height < vtkImplicitRectangleWidget::MinHeight)
{
return;
}
this->Height = height;
this->PlaneSource->SetOrigin(-0.5 * this->Width, -0.5 * this->Height, 0.0);
this->PlaneSource->SetPoint1(0.5 * this->Width, -0.5 * this->Height, 0.0);
this->PlaneSource->SetPoint2(-0.5 * this->Width, 0.5 * this->Height, 0.0);
this->PlaneSource->Update(); // Get the output before pipeline executes
double bounds[6];
this->CubeSource->GetBounds(bounds);
bounds[2] = -0.5 * this->Height;
bounds[3] = 0.5 * this->Height;
this->CubeSource->SetBounds(bounds);
this->CubeSource->Update();
this->Modified();
}
}
void vtkImplicitRectangleWidget::SetDepth(double depth)
{
if (this->Depth != depth)
{
if (depth > vtkImplicitRectangleWidget::MaxDepth)
{
return;
}
this->Depth = depth;
double bounds[6];
this->CubeSource->GetBounds(bounds);
bounds[4] = 0.0;
bounds[5] = this->Depth;
this->CubeSource->SetBounds(bounds);
this->CubeSource->Update();
this->Modified();
}
}
double* vtkImplicitRectangleWidget::GetOrigin()
{
return this->Origin;
}
void vtkImplicitRectangleWidget::GetOrigin(double xyz[3])
{
std::copy(this->Origin, this->Origin + 3, xyz);
}
double* vtkImplicitRectangleWidget::GetCenter()
{
return this->Center;
}
void vtkImplicitRectangleWidget::GetCenter(double xyz[3])
{
std::copy(this->Center, this->Center + 3, xyz);
}
void vtkImplicitRectangleWidget::ProcessEvents(
vtkObject* vtkNotUsed(object), unsigned long event, void* clientdata, void* vtkNotUsed(calldata))
{
vtkImplicitRectangleWidget* self = reinterpret_cast<vtkImplicitRectangleWidget*>(clientdata);
switch (event)
{
case vtkCommand::LeftButtonPressEvent:
self->OnLeftButtonDown();
break;
case vtkCommand::LeftButtonReleaseEvent:
self->OnLeftButtonUp();
break;
case vtkCommand::MiddleButtonPressEvent:
self->OnMiddleButtonDown();
break;
case vtkCommand::MiddleButtonReleaseEvent:
self->OnMiddleButtonUp();
break;
case vtkCommand::RightButtonPressEvent:
self->OnRightButtonDown();
break;
case vtkCommand::RightButtonReleaseEvent:
self->OnRightButtonUp();
break;
case vtkCommand::MouseMoveEvent:
self->OnMouseMove();
break;
case vtkCommand::StartPinchEvent:
self->OnStartPinch();
break;
case vtkCommand::PinchEvent:
self->OnPinch();
break;
case vtkCommand::EndPinchEvent:
self->OnEndPinch();
break;
case vtkCommand::KeyPressEvent:
self->ShowBox();
break;
}
}
void vtkImplicitRectangleWidget::OnLeftButtonDown()
{
// TODO: Allow spinning when PolyActor is hit
vtkDebugMacro(<< __FUNCTION__);
int X = this->Interactor->GetEventPosition()[0];
int Y = this->Interactor->GetEventPosition()[1];
// Okay, make sure that the pick is in the current renderer
if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y))
{
this->State = vtkImplicitRectangleWidget::Outside;
return;
}
vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);
// Some experimental stuff (take from new widget design)
this->StartEventPosition[0] = static_cast<double>(X);
this->StartEventPosition[1] = static_cast<double>(Y);
this->StartEventPosition[2] = 0.0;
if (path != nullptr)
{
vtkProp* prop = path->GetFirstNode()->GetViewProp();
if (prop == this->ConeActor || prop == this->LineActor)
{
if (this->Interactor->GetControlKey())
{
this->State = vtkImplicitRectangleWidget::SpinningAxis;
this->HighlightHandle(path->GetFirstNode()->GetViewProp());
}
else
{
this->State = vtkImplicitRectangleWidget::Rotating;
this->HighlightHandle(path->GetFirstNode()->GetViewProp());
}
}
else if (prop == this->PolyDataActor)
{
// Hit if polydata is clicked (the transducer)
if (this->Interactor->GetShiftKey())
{
this->State = vtkImplicitRectangleWidget::MovingOnSurface;
}
else if (this->Interactor->GetControlKey())
{
// When polydata is hit and constrol pressed - spin around x-axis
this->State = vtkImplicitRectangleWidget::Spinning;
this->HighlightHandle(path->GetFirstNode()->GetViewProp());
}
else
{
this->State = vtkImplicitRectangleWidget::Moving;
}
}
}
else
{
path = this->GetAssemblyPath(X, Y, 0., this->PlanePicker);
if (path != nullptr)
{
if (this->Interactor->GetControlKey())
{
this->State = vtkImplicitRectangleWidget::SpinningAxis;
this->HighlightRectangle(1);
}
else if (this->Interactor->GetShiftKey())
{
this->State = vtkImplicitRectangleWidget::MovingOnSurface;
}
else
{
vtkDebugMacro(<< "Moving");
this->State = vtkImplicitRectangleWidget::Moving;
}
}
else
{
this->State = vtkImplicitRectangleWidget::Outside;
this->ValidPick = 0;
this->HighlightHandle(nullptr);
return;
}
}
this->EventCallbackCommand->SetAbortFlag(1);
this->StartInteraction();
this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::OnLeftButtonUp()
{
vtkDebugMacro(<< __FUNCTION__);
if (this->State == vtkImplicitRectangleWidget::Outside ||
this->State == vtkImplicitRectangleWidget::Start)
{
return;
}
this->State = vtkImplicitRectangleWidget::Start;
this->HighlightHandle(nullptr);
this->HighlightRectangle(0);
this->SizeHandles();
this->EventCallbackCommand->SetAbortFlag(1);
this->EndInteraction();
this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::SizeHandles()
{
double radius = this->vtk3DWidget::SizeHandles(this->HandleSizeFactor);
if (this->ValidPick && !this->LastPickValid)
{
// Adjust factor to preserve old radius.
double oldradius = this->ConeSource->GetRadius();
if (oldradius != 0 && radius != 0)
{
this->HandleSizeFactor = oldradius / radius;
radius = oldradius;
}
}
this->LastPickValid = this->ValidPick;
// Set the height and radius of the cone
this->ConeSource->SetRadius(radius);
this->ConeSource->SetHeight(2 * radius);
}
void vtkImplicitRectangleWidget::OnMiddleButtonDown()
{
int X = this->Interactor->GetEventPosition()[0];
int Y = this->Interactor->GetEventPosition()[1];
// Okay, make sure that the pick is in the current renderer
if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y))
{
this->State = vtkImplicitRectangleWidget::Outside;
return;
}
// Okay, we can process this. If anything is picked, then we
// can start pushing the plane.
vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);
if (path != nullptr)
{
this->State = vtkImplicitRectangleWidget::Pushing;
this->HighlightRectangle(1);
this->HighlightHandle(path->GetFirstNode()->GetViewProp());
}
else
{
path = this->GetAssemblyPath(X, Y, 0., this->PlanePicker);
if (path == nullptr) // nothing picked
{
this->State = vtkImplicitRectangleWidget::Outside;
return;
}
else
{
this->State = vtkImplicitRectangleWidget::Pushing;
this->HighlightRectangle(1);
}
}
this->EventCallbackCommand->SetAbortFlag(1);
this->StartInteraction();
this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::OnMiddleButtonUp()
{
vtkDebugMacro(<< __FUNCTION__);
if (this->State == vtkImplicitRectangleWidget::Outside ||
this->State == vtkImplicitRectangleWidget::Start)
{
return;
}
this->State = vtkImplicitRectangleWidget::Start;
this->HighlightRectangle(0);
this->HighlightHandle(nullptr);
this->SizeHandles();
this->EventCallbackCommand->SetAbortFlag(1);
this->EndInteraction();
this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::OnRightButtonDown()
{
vtkDebugMacro(<< __FUNCTION__);
int X = this->Interactor->GetEventPosition()[0];
int Y = this->Interactor->GetEventPosition()[1];
// Okay, make sure that the pick is in the current renderer
if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y))
{
this->State = vtkImplicitRectangleWidget::Outside;
return;
}
// Okay, we can process this. Try to pick handles first;
// if no handles picked, then pick the bounding box.
vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);
if (path != nullptr)
{
this->State = vtkImplicitRectangleWidget::Scaling;
this->HighlightRectangle(1);
this->HighlightHandle(path->GetFirstNode()->GetViewProp());
}
else // see if we picked the plane or a normal
{
path = this->GetAssemblyPath(X, Y, 0., this->PlanePicker);
if (path == nullptr)
{
this->State = vtkImplicitRectangleWidget::Outside;
this->ValidPick = 0; // I believe this is how it should be
return;
}
else
{
this->State = vtkImplicitRectangleWidget::Scaling;
this->HighlightRectangle(1);
}
}
this->EventCallbackCommand->SetAbortFlag(1);
this->StartInteraction();
this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::OnRightButtonUp()
{
vtkDebugMacro(<< __FUNCTION__);
if (this->State == vtkImplicitRectangleWidget::Outside ||
this->State == vtkImplicitRectangleWidget::Start)
{
return;
}
this->State = vtkImplicitRectangleWidget::Start;
this->HighlightRectangle(0);
this->SizeHandles();
this->EventCallbackCommand->SetAbortFlag(1);
this->EndInteraction();
this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::MoveFocusRequest(
const double* p1, const double* p2, const double currPos[2], double center[3])
{
const bool SmoothMotion = true;
if (SmoothMotion)
{
double focus[4], v[3] = { 0, 0, 0 };
// Returns vtkCoordinate corresponding to the world position
focus[0] = this->WorldPosition[0];
focus[1] = this->WorldPosition[1];
focus[2] = this->WorldPosition[2];
v[0] = p2[0] - p1[0];
v[1] = p2[1] - p1[1];
v[2] = p2[2] - p1[2];
focus[0] += v[0];
focus[1] += v[1];
focus[2] += v[2];
focus[3] = 1.0;
// Get the display position that this center would fall on.
this->CurrentRenderer->SetWorldPoint(focus);
this->CurrentRenderer->WorldToDisplay();
this->CurrentRenderer->GetDisplayPoint(center);
}
else
{
center[0] = currPos[0];
center[1] = currPos[1];
center[2] = 1.0;
}
}
void vtkImplicitRectangleWidget::UpdateCamera()
{
// Overload this in descendants
}
void vtkImplicitRectangleWidget::OnMouseMove()
{
// See whether we're active
if (this->State == vtkImplicitRectangleWidget::Outside ||
this->State == vtkImplicitRectangleWidget::Start)
{
return;
}
int X = this->Interactor->GetEventPosition()[0];
int Y = this->Interactor->GetEventPosition()[1];
// Do different things depending on state
vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
if (!camera)
{
return;
}
// Calculations everybody does
double focalPoint[4], pickPoint[4], prevPickPoint[4];
double z, vpn[3];
// Compute the two points defining the motion vector
this->ComputeWorldToDisplay(
this->LastPickPosition[0], this->LastPickPosition[1], this->LastPickPosition[2], focalPoint);
z = focalPoint[2];
// Z-coordinate is world-coordinate depth of picked object
this->ComputeDisplayToWorld(double(this->Interactor->GetLastEventPosition()[0]),
double(this->Interactor->GetLastEventPosition()[1]), z, prevPickPoint);
this->ComputeDisplayToWorld(double(X), double(Y), z, pickPoint);
// Process the motion
if (this->State == vtkImplicitRectangleWidget::MovingOnSurface)
{
vtkDebugMacro(<< "MovingOnSurface");
double eventPos[2] = { static_cast<double>(X), static_cast<double>(Y) };
// Taken from vtkAbstractPolygonalHandleRepresentation::WidgetInteraction
double startPickPoint[4];
this->ComputeDisplayToWorld(
this->StartEventPosition[0], this->StartEventPosition[1], z, startPickPoint);
double newCenterPointRequested[3]; // displayPosition
double newCenterPoint[3], worldOrient[9];
this->WorldPosition[0] = pickPoint[0];
this->WorldPosition[1] = pickPoint[1];
this->WorldPosition[2] = pickPoint[2];
this->WorldPosition[0] = prevPickPoint[0];
this->WorldPosition[1] = prevPickPoint[1];
this->WorldPosition[2] = prevPickPoint[2];
// displayPos is output from MoveFocusRequest
this->MoveFocusRequest(prevPickPoint, pickPoint, eventPos, newCenterPointRequested);
// See what the placer has to say
if (this->PointPlacer)
{
// See what the placer says.
if (this->PointPlacer->ComputeWorldPosition(
this->CurrentRenderer, newCenterPointRequested, newCenterPoint, worldOrient))
{
// Actually, a new origin
double newCenter[3]{};
this->UserTransform->TransformPoint(this->Origin, newCenter);
double translation[3]{};
vtkMath::Subtract(newCenterPoint, newCenter, translation);
// Once the placer has validated us, update the handle position
this->UserTransform->PostMultiply();
this->UserTransform->Translate(translation[0], translation[1], translation[2]);
this->UserTransform->PreMultiply();
}
}
}
else if (this->State == vtkImplicitRectangleWidget::Moving)
{
vtkDebugMacro(<< "Moving");
// Consider to check if handle is active and CTRL
this->Translate(prevPickPoint, pickPoint);
}
else if (this->State == vtkImplicitRectangleWidget::Scaling)
{
vtkDebugMacro(<< "Scaling");
this->Scale(prevPickPoint, pickPoint, X, Y);
}
else if (this->State == vtkImplicitRectangleWidget::Pushing)
{
vtkDebugMacro(<< "Pushing");
this->Push(prevPickPoint, pickPoint);
}
else if (this->State == vtkImplicitRectangleWidget::Rotating)
{
vtkDebugMacro(<< "Rotating");
camera->GetViewPlaneNormal(vpn);
this->Rotate(X, Y, prevPickPoint, pickPoint, vpn);
}
else if (this->State == vtkImplicitRectangleWidget::SpinningAxis ||
this->State == vtkImplicitRectangleWidget::Spinning)
{
// Rotate in sector plane around center
double spinAxis[3] = { 0.0, 0.0, 0.0 };
if (this->State == vtkImplicitRectangleWidget::SpinningAxis)
{
vtkDebugMacro(<< "SpinningAxis");
// Spinning around handle or in-plane
if (this->CurrentHandle)
{
vtkDebugMacro(<< "SPIN AROUND HANDLE");
// Compute spin axis. Here center must be different from origin
vtkMath::Subtract(this->Center, this->Origin, spinAxis);
vtkMath::Normalize(spinAxis);
}
else
{
// Not implemented - was used for spinning in another plane.
vtkDebugMacro(<< "SPIN IN-PLANE");
// Z-axis - orthogonal to sector
spinAxis[2] = 1.0;
}
}
else if (this->State == vtkImplicitRectangleWidget::Spinning)
{
vtkDebugMacro(<< "Spinning");
// Spin around 'x-axis'
cout << "Spin around x-axis" << endl;
double handleAxis[3] = { 0.0, 0.0, 0.0 };
double zAxis[3] = { 0.0, 0.0, 1.0 };
vtkMath::Subtract(this->Center, this->Origin, handleAxis);
vtkMath::Normalize(handleAxis);
vtkMath::Cross(handleAxis, zAxis, spinAxis);
}
this->Spin(prevPickPoint, pickPoint, spinAxis);
}
// Interact, if desired
this->UpdateCamera();
this->EventCallbackCommand->SetAbortFlag(1);
this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::OnStartPinch()
{
// We could start interaction for scaling the handles when zooming
}
void vtkImplicitRectangleWidget::OnPinch()
{
if (this->State != vtkImplicitRectangleWidget::Pinching)
{
return;
}
// We do nothing. We could scale the handles - make them smaller
// when zooming
}
void vtkImplicitRectangleWidget::OnEndPinch()
{
if (this->State != vtkImplicitRectangleWidget::Pinching)
{
return;
}
this->HighlightHandle(nullptr);
this->HighlightRectangle(0);
this->SizeHandles();
this->EventCallbackCommand->SetAbortFlag(1);
this->EndInteraction();
this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
this->Interactor->Render();
}
void vtkImplicitRectangleWidget::HighlightRectangle(int highlight)
{
if (highlight)
{
// Store last pick position
this->ValidPick = 1;
this->PlanePicker->GetPickPosition(this->LastPickPosition);
if (this->Interactor->GetControlKey())
{
this->PlaneActor->SetProperty(this->SelectedPlaneCtrlProperty);
}
else
{
this->PlaneActor->SetProperty(this->SelectedPlaneProperty);
}
}
else
{
this->PlaneActor->SetProperty(this->PlaneProperty);
}
}
void vtkImplicitRectangleWidget::RegisterPickers()
{
vtkPickingManager* pm = this->GetPickingManager();
if (!pm)
{
return;
}
pm->AddPicker(this->HandlePicker, this);
}
void vtkImplicitRectangleWidget::Rotate(int X, int Y, double* p1, double* p2, double* vpn)
{
vtkDebugMacro(<< __FUNCTION__);
double v[3]; // vector of motion
double axis[3]; // axis of rotation
double theta; // rotation angle
// mouse motion vector in world space
v[0] = p2[0] - p1[0];
v[1] = p2[1] - p1[1];
v[2] = p2[2] - p1[2];
// Create axis of rotation and angle of rotation
vtkMath::Cross(vpn, v, axis);
if (vtkMath::Normalize(axis) == 0.0)
{
vtkDebugMacro(<< "View plane normal parallel to motion vector");
return;
}
const int* size = this->CurrentRenderer->GetSize();
double l2 = SQUARE(double(X) - this->Interactor->GetLastEventPosition()[0]) +
SQUARE(double(Y) - this->Interactor->GetLastEventPosition()[1]);
theta = 360.0 * sqrt(l2 / (SQUARE(double(size[0])) + SQUARE(double(size[1]))));
// Manipulate the transform to reflect the rotation
vtkNew<vtkTransform> transform;
transform->Identity();
transform->PostMultiply();
// Works - consider doing this without concatenation!!!
double newCenter[3]{};
this->UserTransform->TransformPoint(this->Center, newCenter);
transform->Translate(-newCenter[0], -newCenter[1], -newCenter[2]);
transform->RotateWXYZ(theta, axis);
transform->Translate(newCenter[0], newCenter[1], newCenter[2]);
// Update user transform - test
this->UserTransform->PostMultiply();
this->UserTransform->Concatenate(transform);
this->UserTransform->PreMultiply();
// TODO: We can do this without concatenation
this->UserTransform->SetMatrix(this->UserTransform->GetMatrix());
this->UserTransform->Update();
this->PlaneSource->Update();
}
void vtkImplicitRectangleWidget::Spin(double* p1, double* p2, double* spinAxis)
{
// Mouse motion vector in world space
double v[3];
v[0] = p2[0] - p1[0];
v[1] = p2[1] - p1[1];
v[2] = p2[2] - p1[2];
// Current spin axis
double axis[3];
double normal[3], position[3];
this->UserTransform->TransformPoint(spinAxis, normal);
this->UserTransform->GetPosition(position);
vtkMath::Subtract(normal, position, axis);
vtkMath::Normalize(axis); // Should not be necessary
// Get current center
double center[3];
this->UserTransform->TransformPoint(this->Center, center);
// Radius vector (from center to cursor position)
double rv[3] = { p2[0] - center[0], p2[1] - center[1], p2[2] - center[2] };
// Distance between center and cursor location
double rs = vtkMath::Normalize(rv);
// Spin direction
double ax_cross_rv[3];
vtkMath::Cross(axis, rv, ax_cross_rv);
// Spin angle
double theta =
vtkMath::DegreesFromRadians(vtkMath::Dot(v, ax_cross_rv) / std::max<double>(rs, 1e-6));
// Manipulate the transform to reflect the rotation
vtkNew<vtkTransform> transform;
// Pre-multiply. Move from origin to original center, rotate and move back
transform->Translate(this->Center[0], this->Center[1], this->Center[2]);
transform->RotateWXYZ(theta, spinAxis);
transform->Translate(-this->Center[0], -this->Center[1], -this->Center[2]);
// Update user transform
this->UserTransform->Concatenate(transform);
this->UserTransform->Update();
// TODO: Do this differently
this->UserTransform->SetMatrix(this->UserTransform->GetMatrix());
this->UserTransform->Update();
}
void vtkImplicitRectangleWidget::Scale(
double* vtkNotUsed(p1), double* vtkNotUsed(p2), int vtkNotUsed(X), int vtkNotUsed(Y))
{
// We do not support scaling
}
void vtkImplicitRectangleWidget::Translate(double* p1, double* p2)
{
vtkDebugMacro(<< __FUNCTION__);
// Get the motion vector
double v[3];
v[0] = p2[0] - p1[0];
v[1] = p2[1] - p1[1];
v[2] = p2[2] - p1[2];
// We need to move the widget as we see it - hence post multiply
vtkNew<vtkTransform> transform;
transform->Identity();
transform->PostMultiply();
transform->Translate(v);
this->UserTransform->PostMultiply();
this->UserTransform->Concatenate(transform);
this->UserTransform->PreMultiply();
// TODO: Do this differently
this->UserTransform->SetMatrix(this->UserTransform->GetMatrix());
this->UserTransform->Update();
this->PlaneSource->Update(); // Needed to trigger translation
}
void vtkImplicitRectangleWidget::Push(double* vtkNotUsed(p1), double* vtkNotUsed(p2))
{
// TODO: Implement function for positioning the handle
}
void vtkImplicitRectangleWidget::CreateDefaultProperties()
{
// Handle properties
this->HandleProperty = vtkProperty::New();
this->HandleProperty->SetColor(1, 1, 1);
this->SelectedHandleProperty = vtkProperty::New();
this->SelectedHandleProperty->SetColor(1, 0, 0);
this->SelectedHandleCtrlProperty = vtkProperty::New();
this->SelectedHandleCtrlProperty->SetColor(0, 0, 1);
// Plane properties
this->PlaneProperty = vtkProperty::New();
this->PlaneProperty->SetAmbient(1.0);
this->PlaneProperty->SetAmbientColor(1.0, 1.0, 1.0);
this->PlaneProperty->SetOpacity(0.7);
this->SelectedPlaneProperty = vtkProperty::New();
this->SelectedPlaneProperty->SetAmbient(1.0);
this->SelectedPlaneProperty->SetAmbientColor(0.0, 1.0, 0.0);
this->SelectedPlaneProperty->SetOpacity(0.9);
this->SelectedPlaneCtrlProperty = vtkProperty::New();
this->SelectedPlaneCtrlProperty->SetAmbient(1.0);
this->SelectedPlaneCtrlProperty->SetAmbientColor(0.0, 0.0, 1.0);
this->CubeProperty = vtkProperty::New();
this->CubeProperty->SetAmbient(1.0);
this->CubeProperty->SetAmbientColor(1.0, 1.0, 1.0);
this->CubeProperty->SetOpacity(0.3);
vtkProperty* props1[3] = { this->PlaneProperty, this->SelectedPlaneProperty,
this->SelectedPlaneCtrlProperty };
for (int i = 0; i < 3; i++)
{
props1[i]->SetEdgeVisibility(1);
props1[i]->SetPointSize(3);
props1[i]->SetLineWidth(2);
props1[i]->SetRenderLinesAsTubes(1);
}
this->PolyHandleProperty = vtkProperty::New();
this->SelectedPolyHandleProperty = vtkProperty::New();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment