system: [SYSTEM]

user: 3. 关键技术 这里以波形图为例，介绍快速高效绘制算法及图形选择算法 [ 11 ] [ 12 ] [ 13 ] 。同样，文本窗的情形与此类似，为节省篇幅不再赘述。 3.1. 波形图快速高效绘制算法 在可视化雷达数据处理软件平台上，当对波形图进行平移、缩放等操作特别是放大操作时，整个波形图占据的显示区域较大，往往超出了显示设备如显示器的整个显示区域。绘制显示器可显示区域以外的波形图会消耗计算机处理器时间，但显然这种绘制是不必要的，因为这部分图形根本不会显示出来。再者，对于水平或竖直平移波形图来说，移动后仍显示在显示器上的图形部分也不需要重新绘制，因为这部分图形没有变化，只是显示位置发生了变化。为提高波形图绘制的速度，在此提出一种快速高效绘制算法。该算法计算波形图应该显示的部分，显示波形图时只处理这部分图形的绘制工作，从而避免大量不必要的图形重绘工作，提高雷达数据处理软件平台的实时性和响应速度 [ 9 ] [ 10 ] 。 3.1.1. 算法推导 在Windows操作系统中，当移动某个应用程序窗口，致使窗口原来被遮盖的部分显露出来，或者改变窗口大小时，Windows都要向应用程序发送WM_PAINT消息，以告知应用程序有一部分客户区域已变为无效，需要重新绘制。这部分需要重绘的区域称为客户区无效区域，由于一般是矩形，故又称无效矩形。通常情况下无效矩形的大小不等于客户区，如上文所述，重绘时没有必要绘制整个客户区，只需绘制客户区中位于无效矩形中的那部分图形即可，以提高图形绘制代码的执行速度，减少绘制工作量，实现实时操作。 如图4所示，在客户区坐标系中，采用MM_TEXT映射模式，即坐标度量单位是像素，窗口客户区左上角O为坐标原点，OX轴方向水平向右，OY轴方向竖直向下。为便于叙述，设要显示的图形只包含一条曲线。曲线上共有N个数据点，各数据大小分别为 Y 0 ， Y 1 ， ⋯ ， Y N − 1 ，数据中的最大、最小值分别为 Y max 、 Y min ，各数据点在客户区坐标系中的坐标分别为 ( x 0 ， y 0 ) ， ( x 1 ， y 1 ) ， ⋯ ， ( x I ， y I ) ， ⋯ ， ( x N − 1 ， y N − 1 ) 图4. 应用程序客户区图形绘制示意图 这里I为基于0的数据索引，即 0 ≤ I ≤ N − 1 。为简单起见，假设曲线上各数据点在水平方向上均匀分布，Δx为两个相邻数据点之间的水平距离。 ( x ， y ) 为曲线上任意一点，它对应于数据Y，y c 为图形纵向分布中心的纵坐标，它对应于数据Y c ，根据简单的数学推导，可得下列方程组 { I = x − x 0 Δ x Y = Y c − y − y c 2 R l y   ( Δ x > 0 ，   l y > 0 ，   R > 0 ) 其中R为图形纵向分布半径，且 Y c = ( Y max + Y min ) / 2 l y = Y max − Y min 3.1.2. 自由平移、水平缩放、竖直缩放关键技术 快速高效图形绘制算法的总体思想是，当对图形实施操作时，保持客户区坐标系和映射模式固定不变，通过改变x 0 、y c 、Δx、R的值并重绘，实现图形实时变化，具体分析如下： 保持y c 、Δx、R不变，增大x 0 ，图形整体水平右移，减小x 0 ，图形整体水平左移。 保持x 0 、Δx、R不变，增大y c ，图形竖直向上整体平移，减小y c ，图形竖直向下整体平移。 自由平移图形，可视作先对图形作水平平移，再对图形作竖直平移的组合。 保持x 0 、y c 、R不变，增大Δx，图形在水平方向上放大，减小Δx，图形在水平方向上缩小。 保持x 0 、y c 、Δx不变，增大R，图形在竖直方向上放大，减小R，图形在竖直方向上缩小。 当x 0 、y c 、Δx、R中某一个值改变后，及时调用InvalidateRect函数使需要重绘的区域无效，驱使Windows操作系统给应用程序发送WM_PAINT消息。在该消息的响应处理中，利用更新的x 0 、y c 、Δx、R重绘无效区域，达到动态、实时显示图形的目的。 3.1.3. 水平方向缩放图形 对图形的操作常用鼠标进行，当需要水平缩放图形时，为改善图形缩放视觉效果，应使位于鼠标指针处的数据在缩放前后的位置保持不变。设 ( x ， y ) 是鼠标指针当前位置， ( x ′ ， y ′ ) 是水平缩放后的位置，则应有 { x − x 0 Δ x = x ′ − x ′ 0 Δ x ′ x ′ = x ，   y ′ = y 其中 Δ x ′ 是已知参数，解上述方程组得到 x ′ 0 ，然后重绘客户区图形，实现水平缩放。 3.1.4. 竖直方向缩放图形 竖直缩放图形时，为了增加缩放视觉效果，同样要求鼠标指针处的图形在缩放前后的位置保持不变，设(x， y)是鼠标指针当前位置， ( x ′ ， y ′ ) 是竖直缩放后的位置，则有 { y − y c R = y ′ − y ′ c R ′ x ′ = x ，   y ′ = y 其中 R ′ 是已知参数，解上述方程组得到 y ′ c ，然后重绘客户区图形，实现竖直缩放。 3.1.5. 自由缩放图形 自由缩放是指在按下鼠标键后不放开的情况下拖动鼠标，形成一个矩形区域，对该区域中的图形进行缩放，以观察这部分图形的详细情况。在自由缩放模式下，鼠标拖出矩形区域与客户区当前可见范围在缩放前后相对应。设 ( l ， t ) 、 ( r ， b ) 分别为鼠标选定矩形左上角和右下角坐标， ( x 1 ， y 1 ) 、 ( x 2 ， y 2 ) 分别为客户区左上角和右下角坐标，易知 { l − x 0 Δ x = x 1 − x ′ 0 Δ x ′ r − x 0 Δ x = x 2 − x ′ 0 Δ x ′       { t − y c R = y 1 − y ′ c R ′ b − y c R = y 2 − y ′ c R ′ 解上述两个方程组，得到 x ′ 0 、 Δ x ′ 、 y ′ c 、 R ′ ，并重绘客户区图形，实现图形选定区域缩放。 3.1.6. 查看图形上某点数据 设鼠标指针坐标是(x， y)，将x、y代入下式 { I = x − x 0 Δ x Y = Y c − y − y c 2 R l y   ( Δ x > 0 ， l y > 0 ， R > 0 ) 得到该点的索引I和数值Y，在此基础上，应用程序可进一步采用适当的方式显示该数据。 3.1.7. 确定无效矩形包含的数据索引 由以上分析可知，应用程序接收到WM_PAINT消息后要重绘客户区，为了保证波形图显示的实时性，只需绘制无效矩形区域内的图形部分。这就需要准确计算需要重绘的与无效矩形关联的数据索引范围和坐标轴刻度范围，见图5。 设s 1 、s 2 分别是无效矩形左、右两边界的横坐标，处于无效矩形中待显示数据的索引范围为 iIndexBeg ≤ I ≤ iIndexEnd ，则计算iIndexBeg和iIndexEnd的步骤示例如下： 图5. 确定与无效矩形关联的数据范围和坐标轴刻度范围 iIndexBeg = min ( max ( 0 ， int ( s 1 − x 0 Δ x ) ) ， N − 1 ) ; iTemp = int ( s 2 − x 0 Δ x ) ; if ( s 2 > x 0 且 s 2 不 是 整 数 ) + + iTemp ; iIndexEnd = max ( min ( N − 1 ， iTemp ) ， 0 ) ; 其中int是C++语言中的强制数据类型转换运算符，min、max是两个宏，分别用来返回两个数中的最小值和最大值。一旦确定数据索引范围后，即可绘制图形： if ( iIndexBeg < iIndexEnd ) { / / 绘 制 该 部 分 图 形 } 3.1.8. 确定无效矩形包含的OX轴刻度 假定x 0 处的刻度线索引为0，对于小于x 0 的数据，规定其刻度索引为负，大于x 0 的数据，刻度索引为正。假定OX轴上各刻度线均匀分布且固定。设两相邻刻度线间水平像素数为Δp x ，无效矩形包含的刻度索引范围是[k 1 ， k 2 ]，则确定k 1 、k 2 的示例代码如下： k 1 = int ( s 1 − x 0 Δ p x ) ; k 2 = int ( s 2 − x 0 Δ p x ) ; if ( s 1 > x 0 且 s 1 不 是 整 数 ) + + k 1 ; if ( s 2 < x 0 且 s 2 不 是 整 数 ) − − k 2 ; 一旦确定刻度线索引范围后，即可如图5所示绘制OX轴刻度线： if ( k 1 ≤ k 2 ) { / / 绘 制 O X 轴 刻 度 线 } 3.1.9. 确定无效矩形包含的OY轴刻度 假定OY轴刻度线索引是整数，且y c 处刻度线索引为0，小于y c 的刻度线索引为负，大于y c 的刻度线索引为正。刻度线在Y轴上均匀分布且位置固定，设两相邻刻度线竖直距离为Δp y ，s 1 、s 2 分别表示无效矩形上、下两边界的纵坐标。无效矩形包含的刻度索引范围是[m 1 ，m 2 ]，则确定m 1 、m 2 的示例代码如下： m 1 = int ( s 1 − y c Δ p y ) ; m 2 = int ( s 2 − y c Δ p y ) ; if ( s 1 > y c 且 s 1 不 是 整 数 ) + + m 1 ; if ( s 2 < y c 且 s 2 不 是 整 数 ) − − m 2 ; if ( m 1 ≤ m 2 ) { / / 绘 制 O Y 轴 刻 度 线 } 波形图快速高效绘制算法的应用如图6所示。 图6. 波形图快速高效绘制算法应用示例 3.1.10. 快捷菜单设计的关键技术 为了设计文本窗快捷菜单，需要处理鼠标右键的WM_CONTEXTMENU消息，窗口过程示例如下： LRESULT CALLBACK WindowProc( HWND hwnd， // 窗口句柄 WM_CONTEXTMENU，// 要处理的消息 WPARAM wParam， // 窗口句柄 LPARAM lParam // 鼠标指针位置 ); xPos = GET_X_LPARAM(lParam); // 获取鼠标位置的x坐标 yPos = GET_Y_LPARAM(lParam); // 获取鼠标位置的x坐标 获取鼠标位置后，即可调用TrackPopupMenu函数显示快捷菜单： TrackPopupMenu(hmenuTrackPopup， TPM_LEFTALIGN | TPM_RIGHTBUTTON， xPos， yPos， 0， hwnd， NULL); 用户选择快捷菜单命令后，对应功能在消息处理函数中实现。 3.2. 图形选择算法 图形选择算法原理如图7所示。当用户在波形图中按下鼠标左键后，程序初始化Beg、End两个标识选择区域左、右边界的变量，并捕获鼠标输入。因为通常情况下只有鼠标在波形图窗口内按下时，程序才会接收到输入消息，而为了选取可见区域之外的图形部分，窗口应该能够自动滚动，由实际操作过程可知这时鼠标应该位于窗口边界外面。捕获鼠标输入就是为了在这时候也能接收到鼠标输入消息。捕获鼠标后的代码由两个循环组成。 当应用程序的消息队列中没有消息时，执行流程进入第一个循环部分。这时候首先获取鼠标指针位置，并将此位置转化到波形图窗口坐标系内。如果指针位于窗口右边界以外，说明要选择图形中尚未显示出来的右边部分，显然应让图形向左边滚动；相反，如果指针位于窗口左边界以外，说明要选择图形中尚未显示出来的左边部分，显然应让图形向右边滚动。无论是向左还是向右滚动，滚动速度应该与指针离开相应边界的距离成正比，以便符合人的感官体验。窗口滚动后，更新End并重绘波形图。 当应用程序的消息队列中存在要处理的消息时，执行流程进入第二个循环部分。针对的消息不同，处理方式也不同：如果是鼠标移动消息，则为了显示正确的图形选取范围，首先应擦除原有图形，再绘制更新后的图形，之后再次检查消息队列；如果是鼠标释放消息，说明图形选取过程结束，此时应检查Beg、End的有效性，必要时做出调整，执行流程结束；如果是鼠标移动、鼠标释放之外的消息，则调用DiapatchMessage函数进行默认处理。当队列中没有消息时，流程返回到第一个循环部分。 图8给出了波形图选取示例，选取部分用图中的黑色部分表示。 图7. 图形选择算法示意图 图8. 图形选择示例