这个其实本身没啥好研究的,但是历史证明,我两年前由于没有明确直观地对各个坐标系统及其转换关系进行定义,导致实际开发的时候非常混乱,好几次坐标转换都是通过试错的方式来完成的。为了这一次的重新开发顺利,将坐标系统定义整理在此。
首先说明涉及到的三个坐标系统:Document,ZoomedDocument 及 Canvas。它们都是以像素为单位的,坐标方向从左上至右下。
- Document:基于图像文档本身实际像素,原点即图像左上角。所有的真实绘图操作都应基于该坐标系。
- ZoomedDocument:基于图像文档在实际显示比例下的显示像素,原点也为图像左上角。这是一个过渡性质的坐标系,用于联系真实文档和屏幕显示操作。
- Canvas:基于屏幕绘图区域的显示像素,原点为绘图区域的左上角。所有的屏幕显示和 UI 处理都应基于该坐标系。
我们以一个 120 x 120 px 的图像为例,下图是基于其 Document 坐标系本身的尺寸:
当图像文档显示在窗口中时,用户实际操作的是绘图窗口中的主区域,即画布(Canvas)。下图显示了在不同缩放比例下,基于 Canvas 坐标系的一些常用值:
Canvas 坐标系中,自身的长宽是由窗口尺寸决定的,原点位于画布自身左上角。CanvasOffset 是相对于 ZoomedDocument 原点的偏移参考值,并不影响 Canvas 的坐标原点。之所以将这个偏移参考值放在 Canvas 坐标系中定义,是因为从概念上来说,文档是不动的,而编辑窗口则是在整个文档中四处移动的一个可见区域。
通过 Canvas 坐标系和其偏移量,我们可以得到相对于已经经过缩放的图像文档坐标系,ZoomedDocument:
该坐标系由于表示的是已经经过缩放的文档,因此其使用的也是实际的屏幕像素尺寸。其中,可见区域即为文档出现在画布内的区域,由于原点是文档的左上角,因此它具有完整的边界(Bounds)定义。
需要注意的是,可见区域并不等价于画布。当文档显示尺寸大于画布时(上图右),可见区域与画布尺寸是相等的;但当文档显示尺寸小于画布时(上图左),可见区域只是画布中的一部分,不会超出文档的显示尺寸本身。
最后通过缩放比例,可以还原至文档实际的坐标系,也就是最开始的文档尺寸定义使用的坐标系:
可见,Document 坐标系与 ZoomedDoc 坐标系的全部区别即在于缩放系数。
下面列出了这些坐标系的转换计算。其中 DocSize、CanvasSize、ZoomRatio、ZoomedDocVisibleOffset 这四组值已经事先获得或指定。由于宽度与高度概念在这里是可以完全互换的,这里只给出 x 方向上的计算:
ZoomedDocWidth := DocWidth * ZoomRatio;
ZoomedDocVisibleWidth := MIN ( CanvasWidth, ZoomedDocWidth );
DocVisibleWidth := ZoomedDocVisibleWidth / ZoomRatio;
DocVisibleLeft := ZoomedDocVisibleLeft / ZoomRatio;
在画布自由平移模式下,用户可以随意在画布中移动图像文档,此时 CanvasOffset 的值可以认为是由外部指定的。当处于非自由平移模式下时,其计算方法为:
IF CanvasWidth > ZoomedDocWidth THEN
CanvasOffsetLeft := (CanvasWidth – ZoomedDocWidth) / 2;
ELSE
CanvasOffsetLeft := MIN ( ZoomedDocVisibleLeft, ZoomedDocWidth – CanvasWidth );
END IF
通过这些计算表达式,我们可以在三种坐标系中随意转换。当 UI 接受到鼠标操作时,首先得到的是 Canvas 坐标,经过转换可以得到 Document 坐标以便进行实际的图像操作,处理结果再通过转换还原为 Canvas 坐标显示出来。