2021-04-15 00:35:27 +02:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using MatterHackers.Agg;
|
|
|
|
|
using MatterHackers.Agg.UI;
|
2021-04-30 15:06:14 -07:00
|
|
|
using MatterHackers.Agg.VertexSource;
|
2021-04-15 00:35:27 +02:00
|
|
|
using MatterHackers.RayTracer;
|
|
|
|
|
using MatterHackers.VectorMath;
|
|
|
|
|
using MatterHackers.VectorMath.TrackBall;
|
|
|
|
|
|
|
|
|
|
namespace MatterHackers.MatterControl.PartPreviewWindow
|
|
|
|
|
{
|
|
|
|
|
public class TrackballTumbleWidgetExtended : GuiWidget
|
|
|
|
|
{
|
|
|
|
|
public NearFarAction GetNearFar;
|
|
|
|
|
private double _centerOffsetX = 0;
|
|
|
|
|
private Vector2 currentVelocityPerMs = new Vector2();
|
|
|
|
|
private readonly MotionQueue motionQueue = new MotionQueue();
|
|
|
|
|
private RunningInterval runningInterval;
|
|
|
|
|
private readonly GuiWidget sourceWidget;
|
|
|
|
|
private readonly int updatesPerSecond = 30;
|
|
|
|
|
private WorldView world;
|
|
|
|
|
private Object3DControlsLayer Object3DControlLayer;
|
|
|
|
|
public float ZoomDelta { get; set; } = 0.2f;
|
|
|
|
|
public TrackBallController TrackBallController { get; }
|
2021-04-30 15:06:14 -07:00
|
|
|
|
|
|
|
|
private ThemeConfig theme;
|
|
|
|
|
|
2021-04-15 00:35:27 +02:00
|
|
|
public TrackBallTransformType CurrentTrackingType { get; set; } = TrackBallTransformType.None;
|
|
|
|
|
public TrackBallTransformType TransformState { get; set; }
|
|
|
|
|
|
|
|
|
|
// tracks the displacement of the camera to accurately rotate, translate and zoom
|
|
|
|
|
public Vector3 DisplacementVec = Vector3.Zero;
|
|
|
|
|
|
|
|
|
|
private bool isRotating = false;
|
|
|
|
|
private Vector3 lastRotationOrigin = Vector3.Zero;
|
|
|
|
|
private Vector3 lastTranslationOrigin = Vector3.Zero;
|
2021-04-15 09:53:43 -07:00
|
|
|
private Vector2 lastScaleMousePosition = Vector2.Zero;
|
2021-04-15 00:35:27 +02:00
|
|
|
private Vector3 rotateVec = Vector3.Zero;
|
|
|
|
|
private Vector3 rotateVecOriginal = Vector3.Zero;
|
|
|
|
|
private Vector2 mouseDownPosition = Vector2.Zero;
|
2021-04-30 15:06:14 -07:00
|
|
|
private Vector3 mouseDownWorldPosition;
|
2021-04-15 00:35:27 +02:00
|
|
|
|
2021-04-30 15:06:14 -07:00
|
|
|
public TrackballTumbleWidgetExtended(WorldView world, GuiWidget sourceWidget, Object3DControlsLayer Object3DControlLayer, ThemeConfig theme)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
AnchorAll();
|
|
|
|
|
TrackBallController = new TrackBallController(world);
|
2021-04-30 15:06:14 -07:00
|
|
|
this.theme = theme;
|
2021-04-15 00:35:27 +02:00
|
|
|
this.world = world;
|
|
|
|
|
this.sourceWidget = sourceWidget;
|
|
|
|
|
this.Object3DControlLayer = Object3DControlLayer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnMouseDown(MouseEventArgs mouseEvent)
|
|
|
|
|
{
|
|
|
|
|
base.OnMouseDown(mouseEvent);
|
|
|
|
|
|
|
|
|
|
if (MouseCaptured)
|
|
|
|
|
{
|
|
|
|
|
Vector2 currentMousePosition = GetMousePosition(mouseEvent);
|
|
|
|
|
ZeroVelocity();
|
|
|
|
|
|
2021-04-30 13:56:59 -07:00
|
|
|
CalculateMouseDownPostionAndPlane(mouseEvent.Position);
|
|
|
|
|
|
2021-04-15 09:53:43 -07:00
|
|
|
if (mouseEvent.Button == MouseButtons.Left)
|
|
|
|
|
{
|
|
|
|
|
if (TrackBallController.CurrentTrackingType == TrackBallTransformType.None)
|
|
|
|
|
{
|
|
|
|
|
switch (TransformState)
|
|
|
|
|
{
|
|
|
|
|
case TrackBallTransformType.Rotation:
|
|
|
|
|
CurrentTrackingType = TrackBallTransformType.Rotation;
|
|
|
|
|
StartRotateAroundOrigin(currentMousePosition);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TrackBallTransformType.Translation:
|
|
|
|
|
CurrentTrackingType = TrackBallTransformType.Translation;
|
|
|
|
|
mouseDownPosition = currentMousePosition;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TrackBallTransformType.Scale:
|
|
|
|
|
CurrentTrackingType = TrackBallTransformType.Scale;
|
|
|
|
|
mouseDownPosition = currentMousePosition;
|
|
|
|
|
lastScaleMousePosition = currentMousePosition;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (mouseEvent.Button == MouseButtons.Middle)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
if (CurrentTrackingType == TrackBallTransformType.None)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
CurrentTrackingType = TrackBallTransformType.Translation;
|
|
|
|
|
mouseDownPosition = currentMousePosition;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (mouseEvent.Button == MouseButtons.Right)
|
|
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
if (CurrentTrackingType == TrackBallTransformType.None)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
CurrentTrackingType = TrackBallTransformType.Rotation;
|
|
|
|
|
StartRotateAroundOrigin(currentMousePosition);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:06:14 -07:00
|
|
|
private void CalculateMouseDownPostionAndPlane(Vector2 mousePosition)
|
2021-04-30 13:56:59 -07:00
|
|
|
{
|
2021-04-30 15:06:14 -07:00
|
|
|
mouseDownPosition = mousePosition;
|
|
|
|
|
Ray rayToCenter = world.GetRayForLocalBounds(mousePosition);
|
|
|
|
|
IntersectInfo intersectionInfo = Object3DControlLayer.Scene.GetBVHData().GetClosestIntersection(rayToCenter);
|
|
|
|
|
mouseDownWorldPosition = intersectionInfo == null ? Vector3.Zero : intersectionInfo.HitPosition;
|
|
|
|
|
|
|
|
|
|
if (mouseDownWorldPosition == Vector3.Zero)
|
|
|
|
|
{
|
|
|
|
|
mouseDownWorldPosition = IntersectXYPlane(rayToCenter.origin, new Vector3(rayToCenter.directionNormal).GetNormal());
|
|
|
|
|
if (mouseDownWorldPosition.Length > 1000)
|
|
|
|
|
{
|
|
|
|
|
mouseDownWorldPosition = Vector3.Zero;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mouseDownWorldPosition == Vector3.Zero)
|
|
|
|
|
{
|
|
|
|
|
mouseDownWorldPosition = lastRotationOrigin;
|
|
|
|
|
}
|
2021-04-30 13:56:59 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-15 00:35:27 +02:00
|
|
|
public override void OnMouseMove(MouseEventArgs mouseEvent)
|
|
|
|
|
{
|
|
|
|
|
base.OnMouseMove(mouseEvent);
|
|
|
|
|
|
|
|
|
|
Vector2 currentMousePosition = GetMousePosition(mouseEvent);
|
|
|
|
|
|
|
|
|
|
if (CurrentTrackingType == TrackBallTransformType.Rotation)
|
|
|
|
|
{
|
|
|
|
|
motionQueue.AddMoveToMotionQueue(currentMousePosition, UiThread.CurrentTimerMs);
|
|
|
|
|
DoRotateAroundOrigin(currentMousePosition);
|
|
|
|
|
}
|
2021-04-16 12:59:43 -07:00
|
|
|
else if (CurrentTrackingType == TrackBallTransformType.Translation)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
Translate(currentMousePosition);
|
|
|
|
|
}
|
2021-04-16 12:59:43 -07:00
|
|
|
else if (CurrentTrackingType == TrackBallTransformType.Scale)
|
2021-04-15 09:53:43 -07:00
|
|
|
{
|
|
|
|
|
Vector2 mouseDelta = currentMousePosition - lastScaleMousePosition;
|
|
|
|
|
double zoomDelta = 1;
|
|
|
|
|
if (mouseDelta.Y < 0)
|
|
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
zoomDelta = -(-1 * mouseDelta.Y / 100);
|
2021-04-15 09:53:43 -07:00
|
|
|
}
|
|
|
|
|
else if (mouseDelta.Y > 0)
|
|
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
zoomDelta = +(1 * mouseDelta.Y / 100);
|
2021-04-15 09:53:43 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:06:14 -07:00
|
|
|
ZoomToScreenPosition(-mouseDownWorldPosition, zoomDelta);
|
2021-04-15 09:53:43 -07:00
|
|
|
lastScaleMousePosition = currentMousePosition;
|
|
|
|
|
}
|
2021-04-15 00:35:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnMouseUp(MouseEventArgs mouseEvent)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentTrackingType != TrackBallTransformType.None)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentTrackingType == TrackBallTransformType.Rotation)
|
|
|
|
|
{
|
|
|
|
|
EndRotateAroundOrigin();
|
|
|
|
|
|
|
|
|
|
// try and preserve some of the velocity
|
|
|
|
|
motionQueue.AddMoveToMotionQueue(mouseEvent.Position, UiThread.CurrentTimerMs);
|
|
|
|
|
if (!Keyboard.IsKeyDown(Keys.ShiftKey))
|
|
|
|
|
{
|
|
|
|
|
currentVelocityPerMs = motionQueue.GetVelocityPixelsPerMs();
|
|
|
|
|
if (currentVelocityPerMs.LengthSquared > 0)
|
|
|
|
|
{
|
|
|
|
|
Vector2 center = LocalBounds.Center;
|
|
|
|
|
StartRotateAroundOrigin(center);
|
|
|
|
|
runningInterval = UiThread.SetInterval(ApplyVelocity, 1.0 / updatesPerSecond);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CurrentTrackingType = TrackBallTransformType.None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base.OnMouseUp(mouseEvent);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 13:56:59 -07:00
|
|
|
private Vector3 IntersectXYPlane(Vector3 rayOrigin, Vector3 rayDirection)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
2021-04-30 13:56:59 -07:00
|
|
|
return IntersectPlane(new Vector3(0, 0, 1), rayOrigin, rayDirection);
|
2021-04-15 00:35:27 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-30 13:56:59 -07:00
|
|
|
private Vector3 IntersectPlane(Vector3 planeNormal, Vector3 rayOrigin, Vector3 rayDirection)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
2021-04-30 13:56:59 -07:00
|
|
|
var d = Vector3Ex.Dot(planeNormal, rayDirection);
|
|
|
|
|
var t = -(Vector3Ex.Dot(rayOrigin, planeNormal) + d) / d;
|
|
|
|
|
return rayOrigin + t * rayDirection;
|
2021-04-15 00:35:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnMouseWheel(MouseEventArgs mouseEvent)
|
|
|
|
|
{
|
2021-04-19 07:38:37 -07:00
|
|
|
if (this.ContainsFirstUnderMouseRecursive())
|
|
|
|
|
{
|
2021-04-30 15:06:14 -07:00
|
|
|
Ray ray = world.GetRayForLocalBounds(mouseEvent.Position);
|
|
|
|
|
IntersectInfo intersectionInfo = Object3DControlLayer.Scene.GetBVHData().GetClosestIntersection(ray);
|
|
|
|
|
Vector3 hitPos = intersectionInfo == null ? Vector3.Zero : intersectionInfo.HitPosition;
|
|
|
|
|
|
|
|
|
|
// if no object is found under the mouse trace from center to xy plane
|
|
|
|
|
if (hitPos == Vector3.Zero)
|
|
|
|
|
{
|
|
|
|
|
Ray rayToCenter = world.GetRayForLocalBounds(new Vector2(Width / 2, Height / 2));
|
|
|
|
|
hitPos = IntersectXYPlane(rayToCenter.origin, new Vector3(rayToCenter.directionNormal).GetNormal());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ZoomToScreenPosition(hitPos, mouseEvent.WheelDelta > 0 ? -ZoomDelta : ZoomDelta);
|
2021-04-19 07:38:37 -07:00
|
|
|
}
|
2021-04-15 00:35:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Vector2 GetMousePosition(MouseEventArgs mouseEvent)
|
|
|
|
|
{
|
|
|
|
|
Vector2 currentMousePosition;
|
|
|
|
|
if (mouseEvent.NumPositions == 1)
|
|
|
|
|
{
|
|
|
|
|
currentMousePosition.X = mouseEvent.X;
|
|
|
|
|
currentMousePosition.Y = mouseEvent.Y;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
currentMousePosition = (mouseEvent.GetPosition(1) + mouseEvent.GetPosition(0)) / 2;
|
|
|
|
|
}
|
|
|
|
|
return currentMousePosition;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StartRotateAroundOrigin(Vector2 mousePosition)
|
|
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
if (isRotating)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
ZeroVelocity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isRotating = true;
|
|
|
|
|
|
2021-04-30 15:06:14 -07:00
|
|
|
rotateVec = -mouseDownWorldPosition - DisplacementVec;
|
2021-04-15 00:35:27 +02:00
|
|
|
rotateVecOriginal = rotateVec;
|
2021-04-30 15:06:14 -07:00
|
|
|
lastRotationOrigin = -mouseDownWorldPosition;
|
2021-04-15 00:35:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DoRotateAroundOrigin(Vector2 mousePosition)
|
|
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
if (isRotating)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
Quaternion activeRotationQuaternion = TrackBallController.GetRotationForMove(TrackBallController.ScreenCenter, TrackBallController.TrackBallRadius, mouseDownPosition, mousePosition, false);
|
|
|
|
|
mouseDownPosition = mousePosition;
|
|
|
|
|
world.Translate(rotateVec);
|
|
|
|
|
rotateVec = Vector3Ex.TransformVector(rotateVec, world.RotationMatrix);
|
|
|
|
|
|
|
|
|
|
world.Rotate(activeRotationQuaternion);
|
|
|
|
|
|
|
|
|
|
rotateVec = Vector3Ex.TransformVector(rotateVec, Matrix4X4.Invert(world.RotationMatrix));
|
|
|
|
|
world.Translate(-rotateVec);
|
|
|
|
|
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void EndRotateAroundOrigin()
|
|
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
if (isRotating)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
isRotating = false;
|
|
|
|
|
DisplacementVec += rotateVecOriginal - rotateVec;
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetRotationWithDisplacement(Quaternion rotationQ)
|
|
|
|
|
{
|
|
|
|
|
StartRotateAroundOrigin(Vector2.Zero);
|
|
|
|
|
|
|
|
|
|
Matrix4X4 rotationM = Matrix4X4.CreateRotation(rotationQ);
|
|
|
|
|
world.Translate(rotateVec);
|
|
|
|
|
rotateVec = Vector3Ex.TransformVector(rotateVec, world.RotationMatrix);
|
|
|
|
|
|
|
|
|
|
world.RotationMatrix = rotationM;
|
|
|
|
|
|
|
|
|
|
rotateVec = Vector3Ex.TransformVector(rotateVec, Matrix4X4.Invert(world.RotationMatrix));
|
|
|
|
|
world.Translate(-rotateVec);
|
|
|
|
|
|
|
|
|
|
EndRotateAroundOrigin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Translate(Vector2 position)
|
|
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
if (isRotating)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
ZeroVelocity();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:06:14 -07:00
|
|
|
double distanceToCenter = (-mouseDownWorldPosition - Vector3Ex.Transform(Vector3.Zero, world.InverseModelviewMatrix)).Length;
|
2021-04-15 00:35:27 +02:00
|
|
|
Vector2 mouseDelta = position - mouseDownPosition;
|
|
|
|
|
var offset = new Vector3(mouseDelta.X, mouseDelta.Y, 0);
|
|
|
|
|
offset = Vector3Ex.TransformPosition(offset, Matrix4X4.Invert(world.RotationMatrix));
|
|
|
|
|
offset *= distanceToCenter / 1000;
|
|
|
|
|
DisplacementVec += offset;
|
|
|
|
|
world.Translate(offset);
|
|
|
|
|
|
|
|
|
|
mouseDownPosition = position;
|
2021-04-30 15:06:14 -07:00
|
|
|
lastTranslationOrigin = -mouseDownWorldPosition;
|
2021-04-15 00:35:27 +02:00
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:06:14 -07:00
|
|
|
public void ZoomToScreenPosition(Vector3 worldPosition, double zoomDelta)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
if (isRotating)
|
2021-04-15 00:35:27 +02:00
|
|
|
{
|
|
|
|
|
ZeroVelocity();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:06:14 -07:00
|
|
|
var unitsPerPixel = world.GetWorldUnitsPerScreenPixelAtPosition(worldPosition);
|
2021-04-15 00:35:27 +02:00
|
|
|
|
|
|
|
|
// calculate the vector between the camera and the intersection position and move the camera along it by ZoomDelta, then set it's direction
|
2021-04-30 15:06:14 -07:00
|
|
|
Vector3 zoomVec = (worldPosition - world.EyePosition) * zoomDelta * Math.Min(unitsPerPixel * 100, 1);
|
2021-04-15 00:35:27 +02:00
|
|
|
|
|
|
|
|
DisplacementVec += zoomVec;
|
|
|
|
|
world.Translate(zoomVec);
|
|
|
|
|
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Reset(Vector3 BedCenter)
|
|
|
|
|
{
|
|
|
|
|
ZeroVelocity();
|
|
|
|
|
DisplacementVec = Vector3.Zero;
|
|
|
|
|
lastRotationOrigin = Vector3.Zero;
|
|
|
|
|
lastTranslationOrigin = Vector3.Zero;
|
|
|
|
|
rotateVec = Vector3.Zero;
|
|
|
|
|
rotateVecOriginal = Vector3.Zero;
|
|
|
|
|
mouseDownPosition = Vector2.Zero;
|
|
|
|
|
DisplacementVec = BedCenter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public delegate void NearFarAction(out double zNear, out double zFar);
|
|
|
|
|
|
|
|
|
|
public double CenterOffsetX
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _centerOffsetX;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_centerOffsetX = value;
|
|
|
|
|
RecalculateProjection();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnDraw(Graphics2D graphics2D)
|
|
|
|
|
{
|
|
|
|
|
RecalculateProjection();
|
|
|
|
|
|
2021-04-30 13:56:59 -07:00
|
|
|
if (TrackBallController.CurrentTrackingType == TrackBallTransformType.None)
|
|
|
|
|
{
|
|
|
|
|
switch (TransformState)
|
|
|
|
|
{
|
|
|
|
|
case TrackBallTransformType.Rotation:
|
2021-04-30 15:06:14 -07:00
|
|
|
var circle = new Ellipse(world.GetScreenPosition(mouseDownWorldPosition), 8 * DeviceScale);
|
|
|
|
|
graphics2D.Render(new Stroke(circle, 2 * DeviceScale), theme.PrimaryAccentColor);
|
|
|
|
|
graphics2D.Render(new Stroke(new Stroke(circle, 4 * DeviceScale), DeviceScale), theme.TextColor.WithAlpha(128));
|
2021-04-30 13:56:59 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-04-15 00:35:27 +02:00
|
|
|
base.OnDraw(graphics2D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RecalculateProjection()
|
|
|
|
|
{
|
|
|
|
|
double trackingRadius = Math.Min(Width * .45, Height * .45);
|
|
|
|
|
TrackBallController.ScreenCenter = new Vector2(Width / 2 - CenterOffsetX, Height / 2);
|
|
|
|
|
|
|
|
|
|
TrackBallController.TrackBallRadius = trackingRadius;
|
|
|
|
|
|
|
|
|
|
var zNear = .1;
|
|
|
|
|
var zFar = 100.0;
|
|
|
|
|
|
|
|
|
|
GetNearFar?.Invoke(out zNear, out zFar);
|
|
|
|
|
|
|
|
|
|
if (CenterOffsetX != 0)
|
|
|
|
|
{
|
|
|
|
|
this.world.CalculateProjectionMatrixOffCenter(sourceWidget.Width, sourceWidget.Height, CenterOffsetX, zNear, zFar);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this.world.CalculateProjectionMatrix(sourceWidget.Width, sourceWidget.Height, zNear, zFar);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ZeroVelocity()
|
|
|
|
|
{
|
|
|
|
|
motionQueue.Clear();
|
|
|
|
|
currentVelocityPerMs = Vector2.Zero;
|
2021-04-16 12:59:43 -07:00
|
|
|
if (runningInterval != null)
|
|
|
|
|
{
|
2021-04-15 00:35:27 +02:00
|
|
|
UiThread.ClearInterval(runningInterval);
|
|
|
|
|
}
|
|
|
|
|
EndRotateAroundOrigin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyVelocity()
|
|
|
|
|
{
|
2021-04-16 12:59:43 -07:00
|
|
|
if (isRotating)
|
|
|
|
|
{
|
2021-04-15 00:35:27 +02:00
|
|
|
if (HasBeenClosed || currentVelocityPerMs.LengthSquared <= 0)
|
|
|
|
|
{
|
|
|
|
|
ZeroVelocity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double msPerUpdate = 1000.0 / updatesPerSecond;
|
|
|
|
|
if (currentVelocityPerMs.LengthSquared > 0)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentTrackingType == TrackBallTransformType.None)
|
|
|
|
|
{
|
|
|
|
|
Vector2 center = LocalBounds.Center;
|
|
|
|
|
mouseDownPosition = center;
|
|
|
|
|
DoRotateAroundOrigin(center + currentVelocityPerMs * msPerUpdate);
|
|
|
|
|
Invalidate();
|
|
|
|
|
|
|
|
|
|
currentVelocityPerMs *= .85;
|
|
|
|
|
if (currentVelocityPerMs.LengthSquared < .01 / msPerUpdate)
|
|
|
|
|
{
|
|
|
|
|
ZeroVelocity();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal class MotionQueue
|
|
|
|
|
{
|
|
|
|
|
private readonly List<TimeAndPosition> motionQueue = new List<TimeAndPosition>();
|
|
|
|
|
|
|
|
|
|
internal void AddMoveToMotionQueue(Vector2 position, long timeMs)
|
|
|
|
|
{
|
|
|
|
|
if (motionQueue.Count > 4)
|
|
|
|
|
{
|
|
|
|
|
// take off the last one
|
|
|
|
|
motionQueue.RemoveAt(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
motionQueue.Add(new TimeAndPosition(position, timeMs));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void Clear()
|
|
|
|
|
{
|
|
|
|
|
motionQueue.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal Vector2 GetVelocityPixelsPerMs()
|
|
|
|
|
{
|
|
|
|
|
if (motionQueue.Count > 1)
|
|
|
|
|
{
|
|
|
|
|
// Get all the movement that is less 100 ms from the last time (the mouse up)
|
|
|
|
|
TimeAndPosition lastTime = motionQueue[motionQueue.Count - 1];
|
|
|
|
|
int firstTimeIndex = motionQueue.Count - 1;
|
|
|
|
|
while (firstTimeIndex > 0 && motionQueue[firstTimeIndex - 1].timeMs + 100 > lastTime.timeMs)
|
|
|
|
|
{
|
|
|
|
|
firstTimeIndex--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimeAndPosition firstTime = motionQueue[firstTimeIndex];
|
|
|
|
|
|
|
|
|
|
double milliseconds = lastTime.timeMs - firstTime.timeMs;
|
|
|
|
|
if (milliseconds > 0)
|
|
|
|
|
{
|
|
|
|
|
Vector2 pixels = lastTime.position - firstTime.position;
|
|
|
|
|
Vector2 pixelsPerSecond = pixels / milliseconds;
|
|
|
|
|
|
|
|
|
|
return pixelsPerSecond;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Vector2.Zero;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal struct TimeAndPosition
|
|
|
|
|
{
|
|
|
|
|
internal Vector2 position;
|
|
|
|
|
|
|
|
|
|
internal long timeMs;
|
|
|
|
|
|
|
|
|
|
internal TimeAndPosition(Vector2 position, long timeMs)
|
|
|
|
|
{
|
|
|
|
|
this.timeMs = timeMs;
|
|
|
|
|
this.position = position;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|