Files
01-Siyuan-MesProject/DataMirrorTools_Simulator/GanttChartExample/Chart/Viewport.cs
2026-05-29 10:07:05 +08:00

495 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Braincase.GanttChart
{
/// <summary>
/// IViewport moves in world coordinate and projects models to device coordinate space
/// </summary>
public interface IViewport
{
/// <summary>
/// Get the projection matrix to transform world coordinates to device coordinates
/// </summary>
Matrix Projection { get; }
/// <summary>
/// Get the rectangle boundary in world coordinates to be captured and projected onto viewport
/// </summary>
RectangleF Rectangle { get; }
/// <summary>
/// Request viewport to recalculate its get properties.
/// </summary>
void Resize();
/// <summary>
/// Convert device coordinates to world coordinates
/// </summary>
/// <param name="screencoord"></param>
/// <returns></returns>
PointF DeviceToWorldCoord(Point screencoord);
/// <summary>
/// Convert device coordinates to world coordinates
/// </summary>
/// <param name="screencoord"></param>
/// <returns></returns>
PointF DeviceToWorldCoord(PointF screencoord);
/// <summary>
/// Convert world coordinates to device coordinates
/// </summary>
/// <param name="worldcoord"></param>
/// <returns></returns>
PointF WorldToDeviceCoord(PointF worldcoord);
/// <summary>
/// Get or set the world height
/// </summary>
float WorldHeight { get; set; }
/// <summary>
/// Get or set the world width
/// </summary>
float WorldWidth { get; set; }
/// <summary>
/// Get or set the X world-coordinate of the position of the viewport.
/// This is also the same as the IViewport.Rectangle.Left.
/// </summary>
float X { get; set; }
/// <summary>
/// Get or set the Y world-coordinate of the position of the viewport.
/// This is also the same as the IViewport.Rectangle.Top.
/// </summary>
float Y { get; set; }
}
/// <summary>
/// IViewport for printing to file
/// </summary>
class PrintViewport : IViewport
{
public PrintViewport(Graphics graphics,
float worldWidth, float worldHeight,
int deviceWidth, int deviceHeight,
int marginLeft, int marginTop)
{
WorldWidth = worldWidth;
WorldHeight = worldHeight;
_mDeviceWidth = deviceWidth;
_mDeviceHeight = deviceHeight;
_mMarginTop = marginTop;
_mMarginLeft = marginLeft;
}
/// <summary>
/// Get or set viewport X world coordinate
/// </summary>
public float X { get; set; }
/// <summary>
/// Get or set viewport Y world coordinate
/// </summary>
public float Y { get; set; }
/// <summary>
/// Get or set scaling factor between 0.0f and 1.0f (obviously, scale of zero would mean you can't see anything)
/// </summary>
public float Scale { get { return _mScale; } set { _mScale = value; } }
/// <summary>
/// Get or set width of the world
/// </summary>
public float WorldWidth { get; set; }
/// <summary>
/// Get or set height of the world
/// </summary>
public float WorldHeight { get; set; }
/// <summary>
/// Get the projection matrix for transforming models into viewport
/// </summary>
public Matrix Projection
{
get
{
Matrix matrix = new Matrix();
matrix.Translate(this._mMarginLeft, this._mMarginTop);
matrix.Scale(_mScale, _mScale);
matrix.Translate(-this.X, -this.Y);
return matrix;
}
}
/// <summary>
/// Get the rectangle bounds of the viewport in world coordinate
/// </summary>
public RectangleF Rectangle
{
get { return new RectangleF(this.X, this.Y, _mDeviceWidth / _mScale, _mDeviceHeight / _mScale); }
}
/// <summary>
/// Resize the viewport, recalculating and correcting dimensions
/// </summary>
public void Resize()
{
}
/// <summary>
/// Convert view coordinates to world coordinate
/// </summary>
/// <param name="screencoord"></param>
/// <returns></returns>
public PointF DeviceToWorldCoord(Point screencoord)
{
return new PointF(screencoord.X + X, screencoord.Y + Y);
}
/// <summary>
/// Convert view coordinates to world coordinate
/// </summary>
/// <param name="screencoord"></param>
/// <returns></returns>
public PointF DeviceToWorldCoord(PointF screencoord)
{
return new PointF(screencoord.X + X, screencoord.Y + Y);
}
/// <summary>
/// Convert world coordinates to view coordinate
/// </summary>
/// <param name="worldcoord"></param>
/// <returns></returns>
public PointF WorldToDeviceCoord(PointF worldcoord)
{
return new PointF(worldcoord.X - X, worldcoord.Y - Y);
}
int _mDeviceWidth, _mDeviceHeight;
int _mMarginLeft, _mMarginTop;
float _mScale = 1.0f;
}
/// <summary>
/// An IViewport that is placed over a world coordinate system and provides methods to transform between world and view coordinates
/// </summary>
class ControlViewport : IViewport
{
/// <summary>
/// Construct a Viewport
/// </summary>
/// <param name="view"></param>
public ControlViewport(Control view)
{
_mDevice = view;
_mhScroll = new HScrollBar();
_mvScroll = new VScrollBar();
_mScrollHolePatch = new UserControl();
WorldWidth = view.Width;
WorldHeight = view.Height;
_mDevice.Controls.Add(_mhScroll);
_mDevice.Controls.Add(_mvScroll);
_mDevice.Controls.Add(_mScrollHolePatch);
_mhScroll.Scroll += (s, e) => X = e.NewValue;
_mvScroll.Scroll += (s, e) => Y = e.NewValue;
_mDevice.Resize += (s, e) => this.Resize();
_mDevice.MouseWheel += (s, e) => Y -= e.Delta > 0 ? WheelDelta : -WheelDelta;
WheelDelta = _mvScroll.LargeChange;
_RecalculateMatrix();
_RecalculateRectangle();
}
/// <summary>
/// Identity Matrix
/// </summary>
public static readonly Matrix Identity = new Matrix(1, 0, 0, 1, 0, 0);
/// <summary>
/// Get or set the number of pixels to scroll on each click of the mouse
/// </summary>
public int WheelDelta { get; set; }
/// <summary>
/// Get the Rectangle area in world coordinates where the Viewport is currently viewing over
/// </summary>
public RectangleF Rectangle
{
get
{
return _mRectangle;
}
}
/// <summary>
/// Get the projection transformation matrix required for drawing models in the world projected into view
/// </summary>
public Matrix Projection
{
get
{
return _mMatrix;
}
}
/// <summary>
/// Resize the Viewport according to the view control and world dimensions, which ever larger and add scrollbars where approperiate
/// </summary>
public void Resize()
{
if (WorldWidth <= _mDevice.Width)
{
_mhScroll.Hide();
}
else
{
_mhScroll.Show();
_mhScroll.Maximum = (int)(WorldWidth - _mDevice.Width);
_mhScroll.Dock = DockStyle.None;
_mhScroll.Location = new Point(0, _mDevice.Height - _mhScroll.Height);
_mhScroll.Width = _mDevice.Width - _mvScroll.Width;
}
if (WorldHeight <= _mDevice.Height)
{
_mvScroll.Hide();
}
else
{
_mvScroll.Show();
_mvScroll.Maximum = (int)(WorldHeight - _mDevice.Height);
_mvScroll.Dock = DockStyle.None;
_mvScroll.Location = new Point(_mDevice.Width - _mvScroll.Width, 0);
_mvScroll.Height = _mDevice.Height - _mhScroll.Height;
}
_mScrollHolePatch.Location = new Point(_mhScroll.Right, _mvScroll.Bottom);
_mScrollHolePatch.Size = new Size(_mvScroll.Width, _mhScroll.Height);
_RecalculateRectangle();
_RecalculateMatrix();
_mDevice.Invalidate();
}
/// <summary>
/// Convert view coordinates to world coordinates
/// </summary>
/// <param name="screencoord"></param>
/// <returns></returns>
public PointF DeviceToWorldCoord(Point screencoord)
{
return new PointF(screencoord.X + X, screencoord.Y + Y);
}
/// <summary>
/// Convert view coordinates to world coordinates
/// </summary>
/// <param name="screencoord"></param>
/// <returns></returns>
public PointF DeviceToWorldCoord(PointF screencoord)
{
return new PointF(screencoord.X + X, screencoord.Y + Y);
}
/// <summary>
/// Convert world coordinates to view coordinates
/// </summary>
/// <param name="worldcoord"></param>
/// <returns></returns>
public PointF WorldToDeviceCoord(PointF worldcoord)
{
return new PointF(worldcoord.X - X, worldcoord.Y - Y);
}
/// <summary>
/// Get or set the world width
/// </summary>
public float WorldWidth
{
get { return _mWorldWidth; }
set
{
if (!value.Equals(_mWorldWidth))
{
if (value < _mDevice.Width) value = _mDevice.Width;
_mWorldWidth = value;
Resize();
}
}
}
/// <summary>
/// Get or set the world height
/// </summary>
public float WorldHeight
{
get { return _mWorldHeight; }
set
{
if (!value.Equals(_mWorldHeight))
{
if (value < _mDevice.Height) value = _mDevice.Height;
_mWorldHeight = value;
Resize();
}
}
}
/// <summary>
/// Get or set the world X coordinate of the Viewport location, represented by the top left corner of the Viewport Rectangle
/// </summary>
public float X
{
get { return _mhScroll.Value; }
set
{
if (!((int)value).Equals(_mhScroll.Value))
{
if (value > _mhScroll.Maximum) value = _mhScroll.Maximum;
else if (value < 0) value = 0;
_mhScroll.Value = (int)value;
_RecalculateRectangle();
_RecalculateMatrix();
_mDevice.Invalidate();
}
}
}
/// <summary>
/// Get or set the wordl Y coordinate of the Viewport location, represented by the top left corner of the Viewport Rectangle
/// </summary>
public float Y
{
get { return _mvScroll.Value; }
set
{
if (!((int)value).Equals(_mvScroll.Value))
{
if (value > _mvScroll.Maximum) value = _mvScroll.Maximum;
else if (value < 0) value = 0;
_mvScroll.Value = (int)value;
_RecalculateRectangle();
_RecalculateMatrix();
_mDevice.Invalidate();
}
}
}
private void _RecalculateRectangle()
{
_mRectangle = new RectangleF(X, Y, _mDevice.Width, _mDevice.Height);
}
private void _RecalculateMatrix()
{
_mMatrix = new Matrix();
_mMatrix.Translate(-X, -Y);
}
Control _mDevice;
HScrollBar _mhScroll;
VScrollBar _mvScroll;
UserControl _mScrollHolePatch;
RectangleF _mRectangle = RectangleF.Empty;
Matrix _mMatrix = new Matrix();
float _mWorldHeight, _mWorldWidth;
}
/// <summary>
/// IViewport for printing to image. The full chart in the world is printed as-is.
/// </summary>
class ImageViewport : IViewport
{
private float _mScale;
public ImageViewport(float scale, float worldWidth, float worldHeight)
{
_mScale = scale;
WorldWidth = worldWidth;
WorldHeight = worldHeight;
}
/// <summary>
/// Get the projection matrix to transform world coordinates to device coordinates
/// </summary>
public Matrix Projection
{
get
{
Matrix projection = new Matrix();
projection.Scale(_mScale, _mScale);
return projection;
}
}
/// <summary>
/// The device rectangle boundary in the world to capture and project onto device is the same rectangle of the world for ImageViewport
/// So that the entire world is projected onto the ImageViewport
/// </summary>
public RectangleF Rectangle
{
get
{
return new RectangleF(0, 0, this.WorldWidth, this.WorldHeight);
}
}
public void Resize()
{
}
/// <summary>
/// The device and world coordinates are the same for ImageViewport
/// </summary>
public PointF DeviceToWorldCoord(Point screencoord)
{
return screencoord;
}
/// <summary>
/// The device and world coordinates are the same for ImageViewport
/// </summary>
public PointF DeviceToWorldCoord(PointF screencoord)
{
return screencoord;
}
/// <summary>
/// The device and world coordinates are the same for ImageViewport
/// </summary>
public PointF WorldToDeviceCoord(PointF worldcoord)
{
return worldcoord;
}
/// <summary>
/// Get or set the world height
/// </summary>
public float WorldHeight { get; set; }
/// <summary>
/// Get or set the world width
/// </summary>
public float WorldWidth { get; set; }
/// <summary>
/// No effect for ImageViewport. X and Y world-coordinate offset is always at (0,0).
/// </summary>
public float X { get; set; }
/// <summary>
/// No effect for ImageViewport. X and Y world-coordinate offset is always at (0,0).
/// </summary>
public float Y { get; set; }
}
}