flyweight模式和图元几何变换

简介: 在flyweight模式,指的是具有大量的轻量级对象,我们为这些对象建立一个实体对象,其他则为“虚像”或者称为对该实体的一种“引用”。在我从前的项目中,电力系统的矢量图中,有大量设备,同种类型设备采用一种符号绘制,称为图元。
  在flyweight模式,指的是具有大量的轻量级对象,我们为这些对象建立一个实体对象,其他则为“虚像”或者称为对该实体的一种“引用”。在我从前的项目中,电力系统的矢量图中,有大量设备,同种类型设备采用一种符号绘制,称为图元。这里就属于一中flyweight模式应用。例如下图:

        

          可以看到上图中的图元,例如开关,变压器,杆塔,电站等。这里我们为图中使用到的图元建立一个图元库,把图元存放到图元库中。在地图上显示的所有图元都是对图元库中的对象的一个引用。而在地图上的地图对象可以通过对图元的几何变换得到,即旋转,缩放,平移。

          控件内部数据组织如下:

          控件

             |--------图元库(图元集合)

             |--------图层和地图对象集合

            因此我们定义一个变换参数结构如下:

public struct TransParams
 {

            public int angle;         //旋转角度
            public double scale;  //缩放因子
            public double dx;      //水平偏移
            public double dy;      //垂直偏移

} 

我们可以看到一个图元有自己的坐标系统,它在自身坐标系统中的坐标需要经过变换,才能得到最终的绘制坐标。

对于平移:

          x=x+dx,

按原点缩放:

          x=x*scale;

旋转,需要使用扩展到三维的矩阵方程:

(1)二维坐标变换的扩维矩阵方程为:

                               [  m11     m12     0 ]
   [x' y' 1]=[x y 1]   [  m21     m22     0 ]
                               [   dx        dy      1 ]
   即
          x' = m11*x + m21* y + dx
          y' = m12*x + m22*y + dy


 (2)后偏移法的变换矩阵:
       x' = x * scale + dx
       y' = y * scale + dy

  
        [ scale       0      0 ]
        [   0        scale   0 ]
        [   dx       dy      1 ]

(3)围绕原点(0,0)(向y轴正向)旋转a角度的变换矩阵:
        x' = x * cos(a) - y * sin(a)
        y' = x * sin(a) + y * cos(a)

         [  cos(a)   sin(a)   0 ]
         [ -sin(a)   cos(a)   0 ]
         [     0            0        1 ] 

 (4)先围绕原点正向旋转a度,再缩放z倍,再平移dx,dy的变换矩阵:
      x' = x *cos(a)*z -y *sin(a)*z +dx
      y' = x *sin(a)*z +y *cos(a)*z +dy

     [  cos(a)*z   sin(a)*z   0 ]
     [ -sin(a)*z   cos(a)*z   0 ]
     [     dx        dy       1 ]

(5)上式的逆变换是:
         x = x' *  cos(a)/z +y *sin(a)/z -(dx*cos(a)+dy*sin(a))/z
         y = x' * -sin(a)/z +y *cos(a)/z +(dx*sin(a)-dy*cos(a))/z
 
         [       cos(a)                                   -sin(a)               0 ]
         [       sin(a)                                     cos(a)              0 ] / z
         [ -dx*cos(a)-dy*sin(a)       dx*sin(a)-dy*cos(a)     1 ] 

因此我们可以给出正变换和逆变换的代码:

//  正变换:<returns>返回变换后的坐标数组</returns>
public   static   double [] Transform( double  x, double  y,TransParams tran)
... {
    
//换成弧度
    double a=tran.angle*MapHelper.Factor_DegreeToRadian;
    
double sina=Math.Sin(a);
    
double cosa=Math.Cos(a);
    
double[] result=new double[2];
    result[
0]=x* cosa *tran.scale - y* sina *tran.scale + tran.dx;
    result[
1]=x* sina *tran.scale + y* cosa *tran.scale + tran.dy;
    
return result;
}


// 逆变换:
public   static   double [] Detransform( double  x, double  y,TransParams tran)
... {
    
//注意z不可以为0!
    if(tran.scale==0)
        
return new double[]...{0,0};
    
//换成弧度
    double a=tran.angle*MapHelper.Factor_DegreeToRadian;
    
double sina=Math.Sin(a);
    
double cosa=Math.Cos(a);
    
double[] result=new double[2];
    result[
0]=(x*cosa + y*sina -tran.dx*cosa-tran.dy*sina)/tran.scale;
    result[
1]=(x*(-sina) + y*cosa + tran.dx*sina-tran.dy*cosa)/tran.scale;
    
return result;            
}

 最后,我们为何要使用逆变换?这是因为地图对象实际上在该位置并不存在,当鼠标点击时,只有具体的地图对象才能够做点击测试的判断,而对引用对象我们无法根据引用对象本身去评判是否选中。因此这时我们依然需要委托被引用对象去判断。这时,我们需要把实际的鼠标坐标点,采用一次和实际对象中的变换参数的逆变换,即把鼠标坐标变换到了实际图元坐标系统中,这时我们就可以用变换到图元坐标系统内的点去完成鼠标点击测试了。如下图所示:
        

        例如如下的代码用于判断鼠标按下时,对象是否被鼠标捕获:这里,,(x,y)是鼠标点在客户区内坐标,(Cx,Cy)是原始地图坐标。当鼠标按下时,我们首先从系统获知的是客户区坐标(x,y),然后我们根据当前视图参数(MapView,包含缩放倍数和起点坐标),从客户区坐标变换到原始地图坐标(Cx,Cy),然后把这两个坐标数据下发到各地图对象去尝试捕获。下面的代码给出了一个链接对象的捕获方式,首先把原始地图坐标反变换到被链接对象(m_linker)坐标系统内,然后调用m_linker的相应函数去判断。

public   override   bool  BeCaptured( int  x,  int  y,  double  Cx,  double  Cy,  double  error,MapView view)
... {
    
if(this.m_linker==null)
        
return false;
    
//先将坐标点反变换!
    double[] p=MapHelper.Detransform(Cx,Cy,this.m_tran);
    
int x1=LineViewCtl.CXToX(p[0],view.OffsetX,view.Zoom);
    
int y1=LineViewCtl.CYToY(p[1],view.OffsetY,view.Zoom);
    
return this.m_linker.BeCaptured(x1,y1,p[0],p[1],error,view);
}

目录
相关文章
|
6月前
|
算法 图形学
【计算机图形学】实验四 二维图形的缩放、旋转,平移,组合变换
【计算机图形学】实验四 二维图形的缩放、旋转,平移,组合变换
160 2
|
图形学 容器
材质、纹理、贴图的区别和关联
材质和纹理是相互配合使用的,材质定义了物体的属性,纹理贴图则通过提供具体的颜色和纹理信息来赋予模型真实感和细节效果。
215 0
|
6月前
ArcMap创建渔网从而获取网格状的矢量分区
本文介绍在ArcMap软件中,通过“Create Fishnet”工具创建渔网,从而获得指定大小的矢量格网数据的方法~
133 1
ArcMap创建渔网从而获取网格状的矢量分区
|
Windows
【OpenGL】十九、OpenGL 绘制模式 ( 绘制线框模式 | 绘制点模式 )(二)
【OpenGL】十九、OpenGL 绘制模式 ( 绘制线框模式 | 绘制点模式 )(二)
339 0
【OpenGL】十九、OpenGL 绘制模式 ( 绘制线框模式 | 绘制点模式 )(二)
|
数据可视化 算法 Serverless
使用分水岭算法分割图像中相互接触的对象
使用分水岭分割来分离图像中相互接触的对象。分水岭变换通过将图像视为一个曲面,其中亮像素表示较高处,暗像素表示较低处,从而找出图像中的“汇水盆地”和“分水岭脊线”。
95 0
|
图形学
unity基础-坦克炮管旋转发射炮弹(向量基础,射线碰撞,物体实例化)
unity基础-坦克炮管旋转发射炮弹(向量基础,射线碰撞,物体实例化)
395 0
MFC绘制二维图形【1】—— 使用映射模式函数自定义坐标系
MFC绘制二维图形【1】—— 使用映射模式函数自定义坐标系
351 0
MFC绘制二维图形【1】—— 使用映射模式函数自定义坐标系
碰撞检测——碰撞器和物理材质
碰撞检测——碰撞器和物理材质
216 0
碰撞检测——碰撞器和物理材质
|
API
使用Threejs创建几何体,动态添加几何体,删除几何体,添加坐标轴
使用Threejs创建几何体,动态添加几何体,删除几何体,添加坐标轴
434 0
使用Threejs创建几何体,动态添加几何体,删除几何体,添加坐标轴
SwiftUI—两个图像视图之间的色彩混合
SwiftUI—两个图像视图之间的色彩混合
437 0
SwiftUI—两个图像视图之间的色彩混合