WorldWind学习系列十二:Measure插件学习

it2024-11-24  25

  我在写自己的WorldWind插件时,遇到很大挫折,上周六本来想写个简单的画线的插件,费了九牛二虎之力终于画出了,如何以动画效果画出线的问题没解决。Direct3D中画线本来是个简单的事,画到球面上也不难,但是实践告诉我:我前期学习WW,又犯了眼高手低的毛病!改动人家写好的插件代码容易,但要把插件的整个流程都自己写,就没想象的简单啦,写代码不严谨的小问题就不说了,我周六画线的主要问题是Direct3D编程都浮在表面,连PrimitiveType中各类型的基元数和顶点的关系没搞清楚。(如想了解请参看:http://www.cnblogs.com/wuhenke/archive/2009/12/27/1633411.html红色部分)

  自己在画线上体验,让我决定先学习Measure插件。另外,我一直想做个类似VE插件,支持加载ArcGIS切图方式的影像,自己想了很久,有几个主要困惑没解决:投影方式不同如何处理、只要部分影像(如何计算行列数)、切图的中心问题(VE影像是全球的,切图中心经纬度为(0°,0°))等等。所以,前段WW实践,让我很受打击,博客就没心情更新啦!虽然理论和实践还有很大的距离,但是总结还是很重要的!

  上面都是题外话了,开始说说Measure插件吧!总体感觉Measure插件很强大,如果能搞清楚,在球面上画点、线、面都不是难事啦。(前提:要有点DirectX编程基础)

  MeasureTool.cs中有两个大类:MeasureTool(插件类)和MeasureToolLayer(渲染对象类)。MeasureToolLayer类中又包含五个内部类:MeasureLine、MeasureMultiLine 、MeasurePropertiesDialog、 MeasureState 、SaveMultiLine(如下图)

  

  MeasureTool作为插件类,需要实现Load() 和Unload()方法,不详说。Load()中注册了一些事件。

加载代码      public   override   void  Load()         {       //构造渲染对象            layer  =   new  MeasureToolLayer(                 this ,                ParentApplication.WorldWindow.DrawArgs );       //设置纹理路径            layer.TexturePath  =  Path.Combine(PluginDirectory, " Plugins\\Measure " );            ParentApplication.WorldWindow.CurrentWorld.RenderableObjects.Add(layer);            menuItem  =   new  MenuItem( " Measure\tM " );            menuItem.Click  +=   new  EventHandler(menuItemClicked);            ParentApplication.ToolsMenu.MenuItems.Add( menuItem );             //  Subscribe events 注册了事件             ParentApplication.WorldWindow.MouseMove  +=   new  MouseEventHandler(layer.MouseMove);            ParentApplication.WorldWindow.MouseDown  +=   new  MouseEventHandler(layer.MouseDown);            ParentApplication.WorldWindow.MouseUp  +=   new  MouseEventHandler(layer.MouseUp);            ParentApplication.WorldWindow.KeyUp  += new  KeyEventHandler(layer.KeyUp);        }

 

  MeasureToolLayer作为渲染对象类,是WW插件实现的重点。必须重载的方法Initialize()、Update()、Render()和PerformSelectionAction(DrawArgs drawArgs)。

  我们先分别看看MeasureToolLayer的五个内部类。

    public enum MeasureState    {     Idle,     Measuring,     Complete    }

  MeasureState是个枚举类型,存放Measure的当前状态的(空闲、测量中、完成)。

 

 

  从上图中,我们可看到MeasurePropertiesDialog和 SaveMultiLine类。

  MeasurePropertiesDialog继承自Form,主要是设置画线的类型:单线、多条线。

设置MeasureMode代码              private   void  okButton_Click( object  sender, EventArgs e)            {                 if  (lineModeButton.Checked  ==   true )                     World.Settings.MeasureMode  =  MeasureMode.Single;                 else                      World.Settings.MeasureMode  =  MeasureMode.Multi;                 this .Close();            }

 

  SaveMultiLine类基础自Form。主要实现将画出的多线,保存为KML或Shp格式。

保存代码 private   void  saveButton_Click( object  sender, System.EventArgs e)            {                 //  Heh.                 SaveFileDialog chooser  =   new  SaveFileDialog();                chooser.DefaultExt  =   " *.csv " ;                chooser.Filter  =   " kml files (*.kml)|*.kml|Shape files (*.shp)|*.shp " ;                chooser.Title  =   " Save Multiline " ;                chooser.ShowDialog(MainApplication.ActiveForm);                String filename  =  chooser.FileName;                Console.WriteLine(filename);                 try                 {                     if (filename.EndsWith( " .kml " ))                    {                         StreamWriter writer  =   new  StreamWriter(filename);                         string  kml  =  writeKML();                         writer.WriteLine(kml);                         writer.Close();                     }                     // need to be able to save to a network a shapefile accessible                      if (filename.EndsWith( " .shp " ))                    {                        writeShape(filename);                    }                }                 catch (Exception ex)                {                    MessageBox.Show(ex.Message);                }            }

 

输出KML文件代码;

KML代码      private   string  writeKML()            {                 // construct XML to send                 XmlDocument doc  =   new  XmlDocument();                XmlNode kmlnode  =  doc.CreateElement( " kml " );                XmlNode node  =  doc.CreateElement( " Placemark " );                                XmlNode name  =  doc.CreateElement( " name " );                name.InnerText  =   " New Measurement " ;                node.AppendChild(name);                XmlNode desc  =  doc.CreateElement( " description " );                 string  description  =   " New Measurement " ;                desc.InnerXml  =  description;                node.AppendChild(desc);                            XmlNode polygon  =  doc.CreateElement( " Polygon " );                 string  request  =   " <outerBoundaryIs><LinearRing><coordinates> " ;                 foreach (MeasureLine line  in  m_multiline)                {                    Double lat  =  line.StartLatitude.Degrees;                    Double lon  =  line.StartLongitude.Degrees;                    request  +=      lon + " , " + lat + " ,100\n " ;                }                request  +=   " </coordinates></LinearRing></outerBoundaryIs> " ;                                polygon.InnerXml =  request;                node.AppendChild(polygon);                kmlnode.AppendChild(node);                doc.AppendChild(kmlnode);                 return  doc.OuterXml;            }

 

保存为SHP格式文件代码

保存为Shap代码              private   void  writeShape( string  filename)            {                IntPtr shphandle  =  ShapeLib.SHPCreate(filename,ShapeLib.ShapeType.PolyLine);                                         double [] lat  =   new   double [m_multiline.Count];                 double [] lon  =   new   double [m_multiline.Count];                                         int  i = 0 ;                 foreach (MeasureLine line  in  m_multiline)                {                    lat[i]  =  line.StartLatitude.Degrees;                    lon[i]  =  line.StartLongitude.Degrees;                    i ++ ;                }                                        ShapeLib.SHPObject poly  =  ShapeLib.SHPCreateSimpleObject(ShapeLib.ShapeType.Polygon,m_multiline.Count,lon,lat, null );                ShapeLib.SHPWriteObject(shphandle, 0 ,poly);                ShapeLib.SHPDestroyObject(poly);                ShapeLib.SHPClose(shphandle);            }

 

上面是右键菜单的两个功能,如果实现添加右键菜单呢??很简单,MeasureToolLayer类只要重载RenderObject类的BuildContextMenu(ContextMenu menu)方法。示例代码如下:

添加右键菜单代码          ///   <summary>          ///  Fills the context menu with menu items specific to the layer.         ///   </summary>          public   override   void  BuildContextMenu(ContextMenu menu)        {        menu.MenuItems.Add( " Properties " new  System.EventHandler(OnPropertiesClick));        menu.MenuItems.Add( " Save Multi-Point Line " new  System.EventHandler(saveLine));        }

 

OnPropertiesClick和saveLine就是用来调用两个窗体类的。

  MeasureMultiLine继承自ArrayList,主要是存放MeasureLine的集合。

         internal   class  MeasureMultiLine:ArrayList        {            //添加线             public   void  addLine(MeasureLine line)            {                Add(line);            }       //删除最后一条线             public   void  deleteLine()            {                RemoveAt(Count - 1 );            }       //计算集合中线的总长度,我们关注如何计算单条线的长度。             public   double  getLength()             {                 double  sum  =   0.0 ;                 foreach (MeasureLine line  in   this )                    sum  +=  line.Linear;                 return  sum;            }       //线集合的渲染方法。             public   void  Render(DrawArgs drawArgs)            {                 foreach (MeasureLine line  in   this )                {                     try                     {               //调用线的渲染方法                        line.Render(drawArgs);                    }                     catch                     {}                }            }        }

 

  MeasureLine继承自ListViewItem,是该Measure插件的关键部分,主要是对线对象的计算和部分渲染。这里面知识点比较重要,很多可以被我们借鉴重用。其中用到的重要方法Calculate() 和Render(),还有一些没用到的方法(这里暂不分析)。

     public   void  Calculate(World world,  bool  useTerrain)            {          /计算球面上两点间圆弧(对应的角度)                 Angle angularDistance  =   World.ApproxAngularDistance( startLatitude, startLongitude, endLatitude, endLongitude  );           //计算圆弧长度=弧度值*半径                 Linear  =  angularDistance.Radians  *  world.EquatorialRadius;               //每两度一个点(下面计算不是好理解,但是我们可以借鉴的重点)            // 2°的弧度为 (2*PI/180)即约等于 2*3/180=1/30;(作者将PI取整为3啦)          //每两度一个点:samples = (int)(angularDistance.Radians/2度的弧度值);          //即 samples = (int)(angularDistance.Radians/(1/30));                   int  samples  =  ( int )(angularDistance.Radians * 30 );   //  1 point for every 2 degrees.                  if (samples < 2 )                    samples  =   2 ;         //构建点集合(线中取samples个点)                LinearTrackLine  =   new  CustomVertex.PositionColored[samples];                 for ( int  i = 0 ;i < LinearTrackLine.Length;i ++ )                    LinearTrackLine[i].Color  =  World.Settings.MeasureLineLinearColorXml;;                            Angle lat,lon = Angle.Zero;                 for ( int  i = 0 ; i < samples; i ++ )                {                     float  t  =  ( float )i  /  (samples - 1 );              //计算各样本点的经纬度                      World.IntermediateGCPoint(t, startLatitude, startLongitude, endLatitude, endLongitude, angularDistance,  out  lat,  out  lon  );                                      double  elevation  =   0 ;             //计算样本点的高程(该方法可借鉴重用)                      if (useTerrain)                        elevation  =  world.TerrainAccessor.GetElevationAt(lat.Degrees,lon.Degrees, 1024 );            //将球面坐标,转为笛卡尔三维坐标(左手坐标系)                     Vector3 subSegmentXyz  =   MathEngine.SphericalToCartesian(lat, lon,                          world.EquatorialRadius  +  elevation  *  World.Settings.VerticalExaggeration );                     LinearTrackLine[i].X  =  subSegmentXyz.X;                    LinearTrackLine[i].Y  =  subSegmentXyz.Y;                    LinearTrackLine[i].Z  =  subSegmentXyz.Z;                }          //计算两点连线的中点坐标(重点)                  WorldXyzMid  =  world.IntermediateGCPoint( 0.5f , startLatitude, startLongitude, endLatitude, endLongitude, angularDistance );             }

 

   Render()方法:

             public   void  Render(DrawArgs drawArgs)            {                 //  Draw the measure line + ends                 Vector3 referenceCenter  =   new  Vector3(                    ( float )drawArgs.WorldCamera.ReferenceCenter.X,                    ( float )drawArgs.WorldCamera.ReferenceCenter.Y,                    ( float )drawArgs.WorldCamera.ReferenceCenter.Z);         //将球体放在啥位置上!(我的理解)                drawArgs.device.Transform.World  =  Matrix.Translation(                     - referenceCenter                    );                 if (World.Settings.MeasureShowGroundTrack  &&  IsGroundTrackValid)                    drawArgs.device.DrawUserPrimitives(PrimitiveType.LineStrip, GroundTrackLine.Length - 1 , GroundTrackLine);       //画出样本点的连线(注意:PrimitiveType.LineStrip类型的基元个数为 LinearTrackLine. Length-1                 drawArgs.device.DrawUserPrimitives(PrimitiveType.LineStrip, LinearTrackLine.Length - 1 , LinearTrackLine);                drawArgs.device.Transform.World  =  drawArgs.WorldCamera.WorldMatrix;          //判断一个点是否可见(方法重要)                  if ( ! drawArgs.WorldCamera.ViewFrustum.ContainsPoint(WorldXyzMid))                      //  Label is invisible                      return ;          //投影:将球面上的点转换为笛卡尔坐标点(重点学习)                  Vector3 labelXy  =  drawArgs.WorldCamera.Project(WorldXyzMid  -  referenceCenter);                  string  label  = "" ; // = Text;                  if ( groundTrack > 0 )                    label  +=   FormatDistance(groundTrack)  +  Units;                 else                     label  +=   FormatDistance(linearDistance)  +  Units;   //在线的中点处画出线段长度(DrawText将文字渲染到球面上某点)                  drawArgs.defaultDrawingFont.DrawText( null , label, ( int )labelXy.X, ( int )labelXy.Y, World.Settings.MeasureLineLinearColor );             }

 上面代码画出的线和长度,在任何缩放级别下都是可见的,不是太好。下面是我借鉴VE插件代码,实现了缩放级别控制,在一定级别下才显示线的长度。

// 判断缩放级别              public   int  GetZoomLevelByTrueViewRange( double  trueViewRange)            {                 int  maxLevel  =   3 // 视角范围为45度                  int  minLevel  =   19 ;                 int  numLevels  =  minLevel  -  maxLevel  +   1 ;                 int  retLevel  =  maxLevel;                 for  ( int  i  =   0 ; i  <  numLevels; i ++ )                {                    retLevel  =  i  +  maxLevel;                     double  viewAngle  =   180 ;                     for  ( int  j  =   0 ; j  <  i; j ++ )                    {                        viewAngle  =  viewAngle  /   2.0 ;                    }                     if  (trueViewRange  >=  viewAngle)                    {                         break ;                    }                }                 return  retLevel;            }

 

然后,在上面的Render()里添加控制条件  if (GetZoomLevelByTrueViewRange(drawArgs.WorldCamera.TrueViewRange.Degrees) > 4) ,来控制长度的显示。

添加层次控制              public   void  Render(DrawArgs drawArgs)            {                 //  Draw the measure line + ends                 Vector3 referenceCenter  =   new  Vector3(                    ( float )drawArgs.WorldCamera.ReferenceCenter.X,                    ( float )drawArgs.WorldCamera.ReferenceCenter.Y,                    ( float )drawArgs.WorldCamera.ReferenceCenter.Z);                drawArgs.device.Transform.World  =  Matrix.Translation(                     - referenceCenter                    );                 if (World.Settings.MeasureShowGroundTrack  &&  IsGroundTrackValid)                    drawArgs.device.DrawUserPrimitives(PrimitiveType.LineStrip, GroundTrackLine.Length - 1 , GroundTrackLine);                drawArgs.device.DrawUserPrimitives(PrimitiveType.LineStrip, LinearTrackLine.Length - 1 , LinearTrackLine);                drawArgs.device.Transform.World  =  drawArgs.WorldCamera.WorldMatrix;    if  (GetZoomLevelByTrueViewRange(drawArgs.WorldCamera.TrueViewRange.Degrees)  >   4 ) {                  if ( ! drawArgs.WorldCamera.ViewFrustum.ContainsPoint(WorldXyzMid))                     //  Label is invisible                      return ;                Vector3 labelXy  =  drawArgs.WorldCamera.Project(WorldXyzMid  -  referenceCenter);                 string  label  = "" ; // = Text;                  if ( groundTrack > 0 )                    label  +=   FormatDistance(groundTrack)  +  Units;                 else                     label  +=   FormatDistance(linearDistance)  +  Units;                drawArgs.defaultDrawingFont.DrawText( null , label, ( int )labelXy.X, ( int )labelXy.Y, World.Settings.MeasureLineLinearColor ); }             }

   下面我们看一下MeasureToolLayer类。

代码 public   override   void  Render(DrawArgs drawArgs)        {             if ( ! isOn)                 return ;             //  Turn off light              if  (World.Settings.EnableSunShading) drawArgs.device.RenderState.Lighting  =   false ;             //  Check that textures are initialised              if ( ! isInitialized)                Initialize(drawArgs);             if (DrawArgs.MouseCursor  ==  CursorType.Arrow)          //  Use our cursor when the mouse isn't over other elements requiring different cursor               //使用自己的鼠标类型(可以借鉴学习)          DrawArgs.MouseCursor  =  CursorType.Measure;              if (State  ==  MeasureState.Idle)                 return ;       //稍后分析             if  ( ! CalculateRectPlacement (drawArgs))                  return ;             if (Distance  <   0.01 )                 return ;            Device device  =  drawArgs.device;            device.RenderState.ZBufferEnable  =   false ;            device.TextureState[ 0 ].ColorOperation  =  TextureOperation.Disable;            device.VertexFormat  =  CustomVertex.PositionColored.Format;                         //  Draw the measure line + ends              /*             device.DrawUserPrimitives(PrimitiveType.LineStrip, measureLine.Length-1, measureLine);            device.DrawUserPrimitives(PrimitiveType.LineStrip, startPoint.Length-1, startPoint);            device.DrawUserPrimitives(PrimitiveType.LineList, endPoint.Length>>1, endPoint);             */        //绘制线集合            multiline.Render(drawArgs);             //  Draw the info rect         //赋予纹理             device.TextureState[ 0 ].ColorOperation  =  TextureOperation.SelectArg1;             device.SetTexture( 0 ,m_texture);             device.VertexFormat  =  CustomVertex.TransformedColoredTextured.Format;         //绘制矩形(由两个三角形构成)             device.DrawUserPrimitives(PrimitiveType.TriangleStrip,  2 , rect);            device.TextureState[ 0 ].ColorOperation  =  TextureOperation.Disable;       //绘制连接线(三个点)            device.DrawUserPrimitives(PrimitiveType.LineStrip,  2 , rectLineConnection);       //绘制矩形边框            device.DrawUserPrimitives(PrimitiveType.LineStrip, rectFrame.Length - 1 , rectFrame);        //渲染绘制矩形上的文字             drawArgs.defaultDrawingFont.DrawText( null , labelText, labelTextRect, DrawTextFormat.None,  0xff   <<   24 );            device.RenderState.ZBufferEnable  =   true ;             if  (World.Settings.EnableSunShading) drawArgs.device.RenderState.Lighting  =   true ;        }

 光标问题

DrawArgs.cs中CursorType中所有光标类型。

 /// <summary> /// Mouse cursor /// </summary> public enum CursorType {  Arrow = 0,  Hand,  Cross,  Measure,  SizeWE,  SizeNS,  SizeNESW,  SizeNWSE }

 更新光标方法340行

更新光标代码          public   void  UpdateMouseCursor(System.Windows.Forms.Control parent)        {             if (lastCursor  ==  mouseCursor)                 return ;             switch ( mouseCursor )            {                 case  CursorType.Hand:                    parent.Cursor  =  System.Windows.Forms.Cursors.Hand;                     break ;                 case  CursorType.Cross:                    parent.Cursor  =  System.Windows.Forms.Cursors.Cross;                     break ;                 case  CursorType.Measure:                     if (measureCursor  ==   null )                     //从外界加载光标                          measureCursor  =   ImageHelper.LoadCursor( " measure.cur " );                     parent.Cursor  =  measureCursor;                     break ;                 case  CursorType.SizeWE:                    parent.Cursor  =  System.Windows.Forms.Cursors.SizeWE;                     break ;                 case  CursorType.SizeNS:                    parent.Cursor  =  System.Windows.Forms.Cursors.SizeNS;                     break ;                 case  CursorType.SizeNESW:                    parent.Cursor  =  System.Windows.Forms.Cursors.SizeNESW;                     break ;                 case  CursorType.SizeNWSE:                    parent.Cursor  =  System.Windows.Forms.Cursors.SizeNWSE;                     break ;                 default :                    parent.Cursor  =  System.Windows.Forms.Cursors.Arrow;                     break ;            }            lastCursor  =  mouseCursor;        }

 在重点处绘制矩形,原来是绘制圆圈的,其实可以在球上任意点绘制多边形的。

 

 关键代码为:

代码 public   void  RenderWaypointIcon(DrawArgs drawArgs, Vector3 position)            {                 if ( ! drawArgs.WorldCamera.ViewFrustum.ContainsPoint(position))                     return ;                 //  Draw the circle - TODO: if the circle doesn't have to always face the user it can be pre-calculated                 Vector3 referenceCenter  =   new  Vector3(                    ( float )drawArgs.WorldCamera.ReferenceCenter.X,                    ( float )drawArgs.WorldCamera.ReferenceCenter.Y,                    ( float )drawArgs.WorldCamera.ReferenceCenter.Z);          //投影,将三维笛卡尔坐标系转换成平面坐标系                Vector3 startXy  =  drawArgs.WorldCamera.Project(position  -  referenceCenter);                 float  circleRadius  =   8 ;                  for ( int  i = 0 ;i < circle.Length;i ++ )                {                     float  angle  =  ( float )(i * 2 * Math.PI / (circle.Length - 1 ));             //这里涉及到圆相关几何计算(看成平面圆,拿笔画画看看,不难的)                    circle[i].X  =  ( float )(startXy.X  + Math.Sin(angle) * circleRadius );                    circle[i].Y  =  ( float )(startXy.Y  + Math.Cos(angle) * circleRadius) ;                    circle[i].Color  =  World.Settings.MeasureLineLinearColorXml;;                }                drawArgs.device.VertexFormat  =  CustomVertex.TransformedColored.Format;                drawArgs.device.Transform.World  =  Matrix.Translation(                     - referenceCenter                    );          //这里我有个疑惑:为啥要是TransformedColored而不是PositionColored?为什么PrimitiveType必须为线形(如:LineStrip)而不能是TrangileList??TrangileList是不会出现结果的!                 //这里是画方形的(顶点数为5)                drawArgs.device.DrawUserPrimitives(PrimitiveType.LineStrip, circle.Length - 1 , circle);         //这是画圆的,顶点数为8个               //   drawArgs.device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, circle);                 drawArgs.device.Transform.World  =  drawArgs.WorldCamera.WorldMatrix;                drawArgs.device.VertexFormat  =  CustomVertex.PositionColored.Format;            }

 我们最后来看一下CalculateRectPlacement()方法

bool  CalculateRectPlacement(DrawArgs drawArgs)        {        //选择可见点(优先选中点)             int  labelLinePoint  =  FindAnchorPoint();             if (labelLinePoint  <   0 )            {                 //  Measure line is not visible                  return   false ;            }            Vector3 referenceCenter  =   new  Vector3(                ( float )drawArgs.WorldCamera.ReferenceCenter.X,                ( float )drawArgs.WorldCamera.ReferenceCenter.Y,                ( float )drawArgs.WorldCamera.ReferenceCenter.Z                );            Angle displayAngle  =  CalcAngle(labelLinePoint, referenceCenter);             if ( Angle.IsNaN(displayAngle) )                 return   false ;             const   int  leg1Len  =   30 ;             const   int  leg2Len  =   5 ;                        Vector3 screenAnchor  =  m_drawArgs.WorldCamera.Project(                 new  Vector3(                 measureLine[labelLinePoint].X,                measureLine[labelLinePoint].Y,                measureLine[labelLinePoint].Z )  -  referenceCenter);             float  x1  =  ( float )(screenAnchor.X  +  Math.Cos(displayAngle.Radians) * leg1Len);             float  y1  =  ( float )(screenAnchor.Y  +  Math.Sin(displayAngle.Radians) * leg1Len);             float  x2  =  x1;             float  y2  =  y1;             //  Find direction of 2nd leg.              int  quadrant  =  ( int )((displayAngle.Radians) / (Math.PI / 2 ));             switch  (quadrant  %   4 )            {                 case   0 :                 case   3 :                    x2  +=  leg2Len;                     break ;                 case   1 :                 case   2 :                    x2  -=  leg2Len;                     break ;            }             //  Calculate label box position / size              if  (World.Settings.MeasureMode  ==  MeasureMode.Multi)            {                Distance  =  multiline.getLength();                 // labelText = Distance>=10000 ?                 //     string.Format( "Total Distance: {0:f1}km", Distance/1000 ) :                 //     string.Format( "Total Distance: {0:f1}m", Distance );                 labelText  =   " Total Distance:  "   +  ConvertUnits.GetDisplayString(Distance);            }             else             {                 // labelText = Distance>=10000 ?                 //     string.Format( "Distance: {0:f1}km", Distance/1000 ) :                 //     string.Format( "Distance: {0:f1}m", Distance );                 labelText  =   " Distance:  "   +  ConvertUnits.GetDisplayString(Distance);            }            labelText  +=   string .Format( " \nBearing: {0:f1} " , Azimuth.Degrees );       //获取绘制文本的外矩形            labelTextRect  =  m_drawArgs.defaultDrawingFont.MeasureString( null , labelText, DrawTextFormat.None,  0 );                        Rectangle tsize  =  labelTextRect;             const   int  xPad  =   4 ;             const   int  yPad  =   1 ;            tsize.Inflate( xPad, yPad );            labelTextRect.Offset( - tsize.Left,  - tsize.Top);            tsize.Offset( - tsize.Left,  - tsize.Top);                        rectLineConnection[ 0 ].X  =  screenAnchor.X;            rectLineConnection[ 0 ].Y  =  screenAnchor.Y;            rectLineConnection[ 1 ].X  =  x1;            rectLineConnection[ 1 ].Y  =  y1;            rectLineConnection[ 2 ].X  =  x2;            rectLineConnection[ 2 ].Y  =  y2;             if (x2 > x1)            {                labelTextRect.Offset(( int )x2,  0 );                tsize.Offset(( int )x2,  0 );            }             else             {                 int  xof  =  ( int )(x2 - tsize.Width);                labelTextRect.Offset(xof,  0 );                tsize.Offset(xof,  0 );            }            tsize.Offset( 0 , ( int )(y2  -  tsize.Height / 2 ));            labelTextRect.Offset( 0 , ( int )(y2  -  tsize.Height / 2 ));            rect[ 0 ].X  =  tsize.Left;            rect[ 0 ].Y  =  tsize.Top;            rect[ 1 ].X  =  rect[ 0 ].X;            rect[ 1 ].Y  =  tsize.Bottom;            rect[ 2 ].X  =  tsize.Right;            rect[ 2 ].Y  =  rect[ 0 ].Y;            rect[ 3 ].X  =  rect[ 2 ].X;            rect[ 3 ].Y  =  rect[ 1 ].Y;            rect[ 4 ].X  =  rect[ 0 ].X;            rect[ 4 ].Y  =  rect[ 1 ].Y;            rectFrame[ 0 ].X  =  tsize.Left;            rectFrame[ 0 ].Y  =  tsize.Top;            rectFrame[ 1 ].X  =  rectFrame[ 0 ].X;            rectFrame[ 1 ].Y  =  tsize.Bottom;            rectFrame[ 2 ].X  =  tsize.Right;            rectFrame[ 2 ].Y  =  rectFrame[ 1 ].Y;            rectFrame[ 3 ].X  =  rectFrame[ 2 ].X;            rectFrame[ 3 ].Y  =  rectFrame[ 0 ].Y;            rectFrame[ 4 ].X  =  rectFrame[ 0 ].X;            rectFrame[ 4 ].Y  =  rectFrame[ 0 ].Y;             //  Cap at start of measure             Vector3 a  =   new  Vector3(measureLine[ 0 ].X, measureLine[ 0 ].Y, measureLine[ 0 ].Z );            Vector3 b  =   new  Vector3(measureLine[ 1 ].X, measureLine[ 1 ].Y, measureLine[ 1 ].Z );            Vector3 vCap   =  Vector3.Cross(a,b);            vCap.Normalize();             const   int  lineCapSize  =   6 ;            vCap.Scale( ( float )m_drawArgs.WorldCamera.Distance / 750f * lineCapSize );            Vector3 worldXyzStart  =   new  Vector3( measureLine[ 0 ].X, measureLine[ 0 ].Y, measureLine[ 0 ].Z );            Vector3 va  =  Vector3.Add( worldXyzStart, vCap );            Vector3 vb  =  Vector3.Add( worldXyzStart,  - vCap );            startPoint[ 0 ].X  =  va.X;            startPoint[ 0 ].Y  =  va.Y;            startPoint[ 0 ].Z  =  va.Z;            startPoint[ 1 ].X  =  vb.X;            startPoint[ 1 ].Y  =  vb.Y;            startPoint[ 1 ].Z  =  vb.Z;             //  Cap at end of measure              int  last  =  measureLine.Length - 1 ;            Vector3 worldXyzEnd  =   new  Vector3(                 measureLine[last].X,                measureLine[last].Y,                measureLine[last].Z );             int  beforeLast  =  last - 1 ;            vCap  =   new  Vector3(                 measureLine[beforeLast].X,                measureLine[beforeLast].Y,                measureLine[beforeLast].Z );            vCap.Subtract(worldXyzEnd);            vCap.Normalize();            vCap.Scale( ( float )(m_drawArgs.WorldCamera.Distance / 750f * lineCapSize) );            vb  =  va  =  Vector3.Add( worldXyzEnd , vCap );             const   float  arrowHeadAngle  =   0.25f * ( float )Math.PI;            va.TransformCoordinate( Matrix.RotationAxis( worldXyzEnd, ( float )Math.PI + arrowHeadAngle ));            vb.TransformCoordinate( Matrix.RotationAxis( worldXyzEnd, arrowHeadAngle));            endPoint[ 0 ].X  =  va.X;            endPoint[ 0 ].Y  =  va.Y;            endPoint[ 0 ].Z  =  va.Z;            endPoint[ 1 ].X  =  vb.X;            endPoint[ 1 ].Y  =  vb.Y;            endPoint[ 1 ].Z  =  vb.Z;            Matrix rotate90  =  Matrix.RotationAxis( worldXyzEnd, ( float )Math.PI * 0.5f  );            va.TransformCoordinate( rotate90 );            vb.TransformCoordinate( rotate90 );            endPoint[ 2 ].X  =  va.X;            endPoint[ 2 ].Y  =  va.Y;            endPoint[ 2 ].Z  =  va.Z;            endPoint[ 3 ].X  =  vb.X;            endPoint[ 3 ].Y  =  vb.Y;            endPoint[ 3 ].Z  =  vb.Z;             return   true ;        }

 

选择可见点(该代码可被我们重用:判断球面上一点是否落在可见区域中)

         bool  IsMeasureLinePointVisible( int  linePoint)        {            Vector3 v  =   new  Vector3( measureLine[linePoint].X, measureLine[linePoint].Y, measureLine[linePoint].Z );             return  m_drawArgs.WorldCamera.ViewFrustum.ContainsPoint(v);        }

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/wuhenke/archive/2009/12/28/1634464.html

相关资源:数据结构—成绩单生成器
最新回复(0)