Files
wg_cpso/vtkControl/vtkMax/Widget/03_vtkMaxTextWithArrowWidget.cs
2026-03-25 18:20:24 +08:00

414 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kitware.VTK;
namespace vtkControl
{
class vtkMaxTextWithArrowWidget : vtkMaxTextWidget
{
// Variables
string _name;
private bool _anchorAlreadySet;
vtkCoordinate _worldAnchorPoint;
vtkCoordinate _worldPositionPoint;
//
vtkPolyData _leaderPolyData;
vtkPolyDataMapper2D _leaderMapper2D;
vtkActor2D _leaderActor2D;
//
vtkPolyData _headPolyData;
vtkGlyph3D _headGlyph;
vtkActor2D _headActor2D;
// Constructors
public vtkMaxTextWithArrowWidget(string name)
{
_name = name;
//
_anchorAlreadySet = false;
_scaleBySectors = true;
vtkDoubleArray vecs;
_worldAnchorPoint = vtkCoordinate.New();
_worldAnchorPoint.SetCoordinateSystemToWorld();
_worldAnchorPoint.SetValue(0, 0, 0);
_worldPositionPoint = vtkCoordinate.New();
_worldPositionPoint.SetCoordinateSystemToWorld();
// Leader - This is the leader (line) from the attachment point to the caption
_leaderPolyData = vtkPolyData.New();
vtkPoints pts = vtkPoints.New();
pts.SetNumberOfPoints(2);
pts.SetPoint(0, 0, 0, 0);
pts.SetPoint(0, 100, 100, 0);
_leaderPolyData.SetPoints(pts);
vtkCellArray leader = vtkCellArray.New();
leader.InsertNextCell(2);
leader.InsertCellPoint(0);
leader.InsertCellPoint(1); //at the attachment point
_leaderPolyData.SetLines(leader);
// mapper/actor combination
_leaderMapper2D = vtkPolyDataMapper2D.New();
_leaderMapper2D.SetInput(_leaderPolyData);
_leaderActor2D = vtkActor2D.New();
_leaderActor2D.SetMapper(_leaderMapper2D);
_leaderActor2D.GetProperty().SetColor(0, 0, 0);
// Arrow
vtkArrowSource arrowSource = vtkArrowSource.New();
arrowSource.SetTipLength(1);
arrowSource.SetTipRadius(0.2);
arrowSource.SetShaftRadius(0.3);
vtkTransform transform = vtkTransform.New();
transform.Translate(-1, 0, 0); // so that tip is at the coordinates point
// Transform the polydata
vtkTransformPolyDataFilter translatedArrow = vtkTransformPolyDataFilter.New();
translatedArrow.SetTransform(transform);
translatedArrow.SetInputConnection(arrowSource.GetOutputPort());
// Glyph - This is for glyphing the head of the leader: A single point with a vector for glyph orientation
_headPolyData = vtkPolyData.New();
pts = vtkPoints.New();
pts.SetNumberOfPoints(1);
pts.SetPoint(0, 0, 0, 0);
_headPolyData.SetPoints(pts);
vecs = vtkDoubleArray.New();
vecs.SetNumberOfComponents(3);
vecs.SetNumberOfTuples(1);
vecs.SetTuple3(0, 1, 1, 1);
_headPolyData.GetPointData().SetVectors(vecs);
_headGlyph = vtkGlyph3D.New();
_headGlyph.SetSourceConnection(translatedArrow.GetOutputPort());
_headGlyph.SetInput(_headPolyData);
_headGlyph.SetScaleModeToDataScalingOff();
_headGlyph.SetScaleFactor(20);
_headGlyph.Update();
// Mapper/actor combination
vtkPolyDataMapper2D arrowMapper2D = vtkPolyDataMapper2D.New();
arrowMapper2D.SetInput(_headGlyph.GetOutput());
_headActor2D = vtkActor2D.New();
_headActor2D.SetMapper(arrowMapper2D);
_headActor2D.GetProperty().SetColor(0, 0, 0);
}
// Private methods
private void RecomputeLeader()
{
if (_position == null) return; // if border is changed before _positionInPixels is initialized
double[] end = RecomputeLeaderEnd(); // this is arrow head
double[] start = RecomputeLeaderStart(); // this is the start of the arrow
RecomputeHead(start, end);
}
private double[] RecomputeLeaderStart()
{
vtkPoints pts = _leaderPolyData.GetPoints();
double[] head = pts.GetPoint(1);
double[] start;
if (head[0] < _position[0]) // head is left from border
start = new double[] { _position[0], _position[1] + _size[1] / 2 };
else if (head[0] > _position[0] + _size[0]) // head is right from border
start = new double[] { _position[0] + _size[0], _position[1] + _size[1] / 2 };
else if (head[1] < _position[1]) // head is below the border
start = new double[] { _position[0] + _size[0] / 2, _position[1] };
else // head is over the border
start = new double[] { _position[0] + _size[0] / 2, _position[1] + _size[1] };
pts.SetPoint(0, start[0], start[1], 0);
return start;
}
private double[] RecomputeLeaderEnd()
{
vtkPoints pts = _leaderPolyData.GetPoints();
double[] attach = _worldAnchorPoint.GetComputedWorldValue(_renderer);
_renderer.SetWorldPoint(attach[0], attach[1], attach[2], 1.0);
_renderer.WorldToDisplay();
double[] display = _renderer.GetDisplayPoint();
pts.SetPoint(1, display[0], display[1], 0);
return display;
}
private void RecomputeHead(double[] start, double[] end)
{
try
{
_headPolyData.GetPoints().SetPoint(0, end[0], end[1], 0);
_headPolyData.GetPointData().GetVectors().SetTuple3(0, end[0] - start[0], end[1] - start[1], 0);
_headPolyData.Modified();
}
catch { }
}
private double[] ProjectPointOnPlane(double[] point, double[] pointOnPlane, double[] normal)
{
//The projection of a point q = (x, y, z) onto a plane given by a point p = (a, b, c) and a normal n = (d, e, f) is
//q_proj = q - dot(q - p, n) * n
double[] tmp = new double[3];
tmp[0] = point[0] - pointOnPlane[0];
tmp[1] = point[1] - pointOnPlane[1];
tmp[2] = point[2] - pointOnPlane[2];
double dot = tmp[0] * normal[0] + tmp[1] * normal[1] + tmp[2] * normal[2];
tmp[0] = point[0] - dot * normal[0];
tmp[1] = point[1] - dot * normal[1];
tmp[2] = point[2] - dot * normal[2];
return tmp;
}
// Public methods
public override void OnRenderWindowModified()
{
if (!_visibility) return;
base.OnRenderWindowModified();
RecomputeLeader();
}
public override void CameraModified()
{
if (!_visibility) return;
base.CameraModified();
SetPositionFromWorldPosition();
RecomputeLeader();
}
//
public override void MiddleButtonPress(int x, int y)
{
if (!_visibility) return;
base.MiddleButtonPress(x, y);
RecomputeLeader();
}
public override void MiddleButtonRelease(int x, int y)
{
if (!_visibility) return;
base.MiddleButtonRelease(x, y);
SetPositionFromWorldPosition();
RecomputeLeader();
}
public override bool MouseMove(int x, int y)
{
if (!_visibility) return false;
if (base.MouseMove(x, y))
{
SetWorldPositionFromPosition();
RecomputeLeader();
return true;
}
return false;
}
public override void MousePan(int dx, int dy)
{
if (!_visibility) return;
base.MousePan(dx, dy);
SetPosition(_position[0] + dx, _position[1] + dy);
RecomputeLeader();
}
public override void MouseRotate(double azimuthAngle, double ElevationAngle)
{
if (!_visibility) return;
base.MouseRotate(azimuthAngle, ElevationAngle);
SetPositionFromWorldPosition();
RecomputeLeader();
}
public override void MouseWheelScrolled()
{
if (!_visibility) return;
base.MouseWheelScrolled();
SetPositionFromWorldPosition();
RecomputeLeader();
}
//
public override void VisibilityOn()
{
if (_visibility == false)
{
SetWorldPositionFromPosition();
// Arrow first
if (_leaderActor2D != null) _renderer.AddActor(_leaderActor2D);
if (_headActor2D != null) _renderer.AddActor(_headActor2D);
// Box last
base.VisibilityOn();
}
}
public override void VisibilityOff()
{
if (_visibility == true)
{
base.VisibilityOff();
if (_leaderActor2D != null) _renderer.RemoveActor(_leaderActor2D);
if (_headActor2D != null) _renderer.RemoveActor(_headActor2D);
}
}
//
public void ResetInitialPosition()
{
_anchorAlreadySet = false;
}
// Public setters
public override void SetInteractor(vtkRenderer renderer, vtkRenderWindowInteractor renderWindowInteractor)
{
// Arrow first
renderer.AddActor(_leaderActor2D);
renderer.AddActor(_headActor2D);
// Box last
base.SetInteractor(renderer, renderWindowInteractor);
}
public override void RemoveInteractor()
{
_renderer.RemoveActor(_leaderActor2D);
_renderer.RemoveActor(_headActor2D);
//
base.RemoveInteractor();
}
public void SetAnchorPoint(double x, double y, double z)
{
_worldAnchorPoint.SetValue(x, y, z);
if (!_anchorAlreadySet) // change the position only the first time the anchor point is set
{
_renderer.SetWorldPoint(x, y, z, 1.0);
_renderer.WorldToDisplay();
double[] displayAnchor = _renderer.GetDisplayPoint();
int[] windowSize = _renderer.GetSize();
double dx = -80;
double dy = 50;
if (displayAnchor[0] + dx + _size[0] > windowSize[0]) dx = -(_size[0] + dx); // if there is no room to the left, go to the right
if (displayAnchor[1] + dy + _size[1] > windowSize[1]) dy = -(_size[1] + dy); // if there is no room to the top, go down
_renderer.SetDisplayPoint(displayAnchor[0] + dx, displayAnchor[1] + dy, 0);
_renderer.DisplayToWorld();
double[] positionWorld = _renderer.GetWorldPoint();
double[] normal = _renderer.GetActiveCamera().GetViewPlaneNormal();
double[] pointOnPlane = _worldAnchorPoint.GetValue();
double[] projectedOnPlane = ProjectPointOnPlane(positionWorld, pointOnPlane, normal);
_worldPositionPoint.SetValue(projectedOnPlane[0], projectedOnPlane[1], projectedOnPlane[2]);
SetPositionFromWorldPosition();
_anchorAlreadySet = true;
}
RecomputeLeader();
//OnBorderRepresentationModified();
}
private void SetPositionFromWorldPosition()
{
double[] positionWorld = _worldPositionPoint.GetValue();
// Get display projection from the world position
_renderer.SetWorldPoint(positionWorld[0], positionWorld[1], positionWorld[2], 1.0);
_renderer.WorldToDisplay();
double[] displayPosition = _renderer.GetDisplayPoint();
//
if (false)
{
CaeGlobals.Vec3D center = new CaeGlobals.Vec3D(displayPosition[0], displayPosition[1], 0);
CaeGlobals.Vec3D size = new CaeGlobals.Vec3D(_size[0], _size[1], 0);
RecomputeLeaderStart();
vtkPoints pts = _leaderPolyData.GetPoints();
CaeGlobals.Vec3D arrow = new CaeGlobals.Vec3D(pts.GetPoint(1));
CaeGlobals.Vec3D direction = center - arrow;
double minDist = size.Len / 2 + 20;
if (direction.Len < minDist)
{
direction.Normalize();
center = arrow + direction * minDist;
displayPosition[0] = center.X;
displayPosition[1] = center.Y;
}
}
//
displayPosition[0] -= _size[0] / 2;
displayPosition[1] -= _size[1] / 2;
//
SetPosition(displayPosition[0], displayPosition[1]);
}
private void SetWorldPositionFromPosition()
{
if (_position == null) return; // if borederRepresentation is changed before _positionInPixels is initialized
//
vtkCamera camera = _renderer.GetActiveCamera();
// Get world projection from display position
_renderer.SetDisplayPoint(_position[0] + _size[0] / 2, _position[1] + _size[1] / 2, 0);
_renderer.DisplayToWorld();
double[] positionWorld = _renderer.GetWorldPoint();
// Project world position on the plane through anchor point
double[] normal = camera.GetViewPlaneNormal();
double[] pointOnPlane = _worldAnchorPoint.GetValue();
double[] projectedOnPlane = ProjectPointOnPlane(positionWorld, pointOnPlane, normal);
// Set world position
_worldPositionPoint.SetValue(projectedOnPlane[0], projectedOnPlane[1], projectedOnPlane[2]);
}
//
public void OffsetPosition(double x, double y)
{
_position[0] += x;
_position[1] += y;
//
SetWorldPositionFromPosition();
//
SetPositionFromWorldPosition(); // must be here
RecomputeLeader(); // must be here
}
// Public getters
public string GetName()
{
return _name;
}
public CaeMesh.BoundingBox GetAnchorPointBox()
{
vtkPoints pts = _leaderPolyData.GetPoints();
double[] head = pts.GetPoint(1);
//
double offset = 15; //px
CaeMesh.BoundingBox box = new CaeMesh.BoundingBox();
box.MinX = head[0] - offset;
box.MaxX = head[0] + offset;
box.MinY = head[1] - offset;
box.MaxY = head[1] + offset;
box.MinZ = head[2] - offset;
box.MaxZ = head[2] + offset;
box.MinZ = 0;
box.MaxZ = 1;
//
return box;
}
}
}