ArcObjects Library Reference  

TriangleElementClass

About the Triangle graphic element Sample

[C#]

TriangleElementClass.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using ESRI.ArcGIS.ADF;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.esriSystem;

namespace TriangleElement
{
  [ComVisible(true)]
  [Guid("DC8482C9-5DD6-44dc-BF3C-54B18AB813C9")]
  public interface ITriangleElement
  {
    ISimpleFillSymbol FillSymbol { get; set;}
    double Size { get; set;}
    double Angle { get; set;}
  }

  [Guid(TriangleElementClass.CLASSGUID)]
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("TriangleElement.TriangleElementClass")]
  public sealed class TriangleElementClass : ITriangleElement,
                                             IElement, 
                                             IElementProperties, 
                                             IElementProperties2, 
                                             IElementProperties3,
                                             IBoundsProperties, 
                                             ITransform2D, 
                                             IGraphicElement, 
                                             IPersistVariant,
                                             IClone,
                                             IDocumentVersionSupportGEN
  {
    #region class members

    //some win32 imports and constants
    [System.Runtime.InteropServices.DllImport("gdi32", EntryPoint = "GetDeviceCaps", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
    public static extern int GetDeviceCaps(int hDC, int nIndex);
    private const double          c_Cosine30          = 0.866025403784439;
    private const double          c_Deg2Rad           = (Math.PI / 180.0);
    private const double          c_Rad2Deg           = (180.0 / Math.PI);
    private const int             c_Version           = 2;
    public const string           CLASSGUID           = "cbf943e2-ce6d-49f4-a4a7-ce16f02379ad";
    public const int              LOGPIXELSX          = 88;
    public const int              LOGPIXELSY          = 90;

    private IPolygon              m_triangle          = null;
    private IPoint                m_pointGeometry     = null;
    private ISimpleFillSymbol     m_fillSymbol        = null;
    private double                m_rotation          = 0.0;
    private double                m_size              = 20.0;
    private ISelectionTracker     m_selectionTracker  = null;
    private IDisplay              m_cachedDisplay     = null;
    private ISpatialReference     m_nativeSR          = null;
    private string                m_elementName       = string.Empty;
    private string                m_elementType       = "TriangleElement";
    private object                m_customProperty    = null;
    private bool                  m_autoTrans         = true;
    private double                m_scaleRef          = 0.0;
    private esriAnchorPointEnum   m_anchorPointType   = esriAnchorPointEnum.esriCenterPoint;
    private double                m_dDeviceRatio      = 0;
    #endregion

    #region class constructor
    public TriangleElementClass()
    {
      //initialize the element's geometry
      m_triangle = new PolygonClass();
      m_triangle.SetEmpty();

      InitMembers();
    }
    #endregion

    #region ITriangleElement Members

    public ISimpleFillSymbol FillSymbol
    {
      get
      {
        return m_fillSymbol;
      }
      set
      {
        m_fillSymbol = value;
      }
    }

    public double Size
    {
      get
      {
        return m_size;
      }
      set
      {
        m_size = value;
      }
    }

    public double Angle
    {
      get
      {
        return m_rotation;
      }
      set
      {
        m_rotation = value;
      }
    }

    #endregion

    #region IElement Members

    public void Activate(IDisplay Display)
    {
      //cache the display
      m_cachedDisplay = Display;

      SetupDeviceRatio(Display.hDC, Display);

      //need to calculate the points of the triangle polygon
      if(m_triangle.IsEmpty)
        BuildTriangleGeometry(m_pointGeometry);

      //need to refresh the element's tracker
      RefreshTracker();
    }

    public void Deactivate()
    {
      m_cachedDisplay = null;
    }

    public void Draw(IDisplay Display, ITrackCancel TrackCancel)
    {      
      if (null != m_triangle && null != m_fillSymbol)
      {
        Display.SetSymbol((ISymbol)m_fillSymbol);
        Display.DrawPolygon(m_triangle);
      }
    }

    public IGeometry Geometry
    {
      get
      {
        return Clone(m_pointGeometry) as IGeometry;
      }
      set
      {
        try
        {
          m_pointGeometry = Clone(value) as IPoint;

          UpdateElementSpatialRef();
        }
        catch (Exception ex)
        {
          System.Diagnostics.Trace.WriteLine(ex.Message);
        }
      }
    }

    public bool HitTest(double x, double y, double Tolerance)
    {
      if (null == m_cachedDisplay)
        return false;

      IPoint point = new PointClass();
      point.PutCoords(x,y);

      return ((IRelationalOperator)m_triangle).Contains((IGeometry)point);
    }

    public bool Locked
    {
      get
      {
        return false;
      }
      set
      {
        
      }
    }
    
    public void QueryBounds(IDisplay Display, IEnvelope Bounds)
    {
      //return a bounding envelope
      IPolygon polygon = new PolygonClass();
      polygon.SetEmpty();

      ((ISymbol)m_fillSymbol).QueryBoundary(Display.hDC, Display.DisplayTransformation, m_triangle, polygon);

      Bounds.XMin = polygon.Envelope.XMin;
      Bounds.XMax = polygon.Envelope.XMax;
      Bounds.YMin = polygon.Envelope.YMin;
      Bounds.YMax = polygon.Envelope.YMax;
      Bounds.SpatialReference = polygon.Envelope.SpatialReference;
    }

    public void QueryOutline(IDisplay Display, IPolygon Outline)
    {
      //return a polygon which is the outline of the element
      IPolygon polygon = new PolygonClass();
      polygon.SetEmpty();
      ((ISymbol)m_fillSymbol).QueryBoundary(Display.hDC, Display.DisplayTransformation, m_triangle, polygon);
      ((IPointCollection)Outline).AddPointCollection((IPointCollection)polygon);
    }

    public ISelectionTracker SelectionTracker
    {
      get { return m_selectionTracker; }
    }

    #endregion

    #region IElementProperties Members

    /// <summary>
    /// Indicates if transform is applied to symbols and other parts of element.
    /// False = only apply transform to geometry.
    /// Update font size in ITransform2D routines
    /// </summary>
    public bool AutoTransform
    {
      get
      {
        return m_autoTrans;
      }
      set
      {
        m_autoTrans = value;
      }
    }

    public object CustomProperty
    {
      get
      {
        return m_customProperty;
      }
      set
      {
        m_customProperty = value;
      }
    }

    public string Name
    {
      get
      {
        return m_elementName;
      }
      set
      {
        m_elementName = value;
      }
    }

    public string Type
    {
      get
      {
        return m_elementType;
      }
      set
      {
        m_elementType = value;
      }
    }

    #endregion

    #region IElementProperties2 Members


    public bool CanRotate()
    {
      return true;
    }

    public double ReferenceScale
    {
      get
      {
        return m_scaleRef;
      }
      set
      {
        m_scaleRef = value;
      }
    }

    #endregion

    #region IElementProperties3 Members

    public esriAnchorPointEnum AnchorPoint
    {
      get
      {
        return m_anchorPointType;
      }
      set
      {
        m_anchorPointType = value;
      }
    }

    #endregion

    #region IBoundsProperties Members

    public bool FixedAspectRatio
    {
      get
      {
        return true;
      }
      set
      {
        throw new Exception("The method or operation is not implemented.");
      }
    }

    public bool FixedSize
    {
      get { return true; }
    }

    #endregion

    #region ITransform2D Members

    public void Move(double dx, double dy)
    {
      if (null == m_triangle)
        return;

      ((ITransform2D)m_triangle).Move(dx, dy);
      ((ITransform2D)m_pointGeometry).Move(dx, dy);
      
      RefreshTracker();
    }

    public void MoveVector(ILine v)
    {
      if (null == m_triangle)
        return;

      ((ITransform2D)m_triangle).MoveVector(v);
      ((ITransform2D)m_pointGeometry).MoveVector(v);

      RefreshTracker();
    }

    public void Rotate(IPoint Origin, double rotationAngle)
    {
      if (null == m_triangle)
        return;

      ((ITransform2D)m_triangle).Rotate(Origin, rotationAngle);
      ((ITransform2D)m_pointGeometry).Rotate(Origin, rotationAngle);

      m_rotation = rotationAngle * c_Rad2Deg;

      RefreshTracker();
    }

    public void Scale(IPoint Origin, double sx, double sy)
    {
      if (null == m_triangle)
        return;

      ((ITransform2D)m_triangle).Scale(Origin, sx, sy);
      ((ITransform2D)m_pointGeometry).Scale(Origin, sx, sy);

      if (m_autoTrans)
      {
        m_size *= Math.Max(sx, sy);
      }

      RefreshTracker();
    }

    public void Transform(esriTransformDirection direction, ITransformation transformation)
    {
      if (null == m_triangle)
        return;

      //Geometry
      ((ITransform2D)m_triangle).Transform(direction, transformation);

      IAffineTransformation2D affineTrans = (IAffineTransformation2D)transformation;
      if (affineTrans.YScale != 1.0)
        m_size *= Math.Max(affineTrans.YScale, affineTrans.XScale);

      RefreshTracker();
    }

    #endregion

    #region IGraphicElement Members

    public ISpatialReference SpatialReference
    {
      get
      {
        return m_nativeSR;
      }
      set
      {
        m_nativeSR = value;
        UpdateElementSpatialRef();
      }
    }

    #endregion

    #region IPersistVariant Members

    public UID ID
    {
      get
      {
        UID uid = new UIDClass();
        uid.Value = "{" + TriangleElementClass.CLASSGUID + "}";
        return uid;
      }
    }

    public void Load(IVariantStream Stream)
    {
      int ver = (int)Stream.Read();
      if (ver > c_Version || ver <= 0)
        throw new Exception("Wrong version!");

      InitMembers();

      m_size = (double)Stream.Read();
      m_scaleRef = (double)Stream.Read();
      m_anchorPointType = (esriAnchorPointEnum)Stream.Read();
      m_autoTrans = (bool)Stream.Read();
      m_elementType = (string)Stream.Read();
      m_elementName = (string)Stream.Read();
      m_nativeSR = Stream.Read() as ISpatialReference;
      m_fillSymbol = Stream.Read() as ISimpleFillSymbol;
      m_pointGeometry = Stream.Read() as IPoint;
      m_triangle = Stream.Read() as IPolygon;

      if (ver == 2)
      {
        m_rotation = (double)Stream.Read();
      }
    }

    public void Save(IVariantStream Stream)
    {
      Stream.Write(c_Version);

      Stream.Write(m_size);
      Stream.Write(m_scaleRef);
      Stream.Write(m_anchorPointType);
      Stream.Write(m_autoTrans);
      Stream.Write(m_elementType);
      Stream.Write(m_elementName);
      Stream.Write(m_nativeSR);
      Stream.Write(m_fillSymbol);
      Stream.Write(m_pointGeometry);
      Stream.Write(m_triangle);

      Stream.Write(m_rotation);
    }

    #endregion

    #region IClone Members

    public void Assign(IClone src)
    {

      //1. make sure that src is pointing to a valid object
      if (null == src)
      {
        throw new COMException("Invalid object.");
      }

      //2. make sure that the type of src is of type 'TriangleElementClass'
      if (!(src is TriangleElementClass))
      {
        throw new COMException("Bad object type.");
      }

      //3. assign the properties of src to the current instance
      TriangleElementClass srcTriangle = (TriangleElementClass)src;
      m_elementName = srcTriangle.Name;
      m_elementType = srcTriangle.Type;
      m_autoTrans = srcTriangle.AutoTransform;
      m_scaleRef = srcTriangle.ReferenceScale;
      m_rotation = srcTriangle.Angle;
      m_size = srcTriangle.Size;
      m_anchorPointType = srcTriangle.AnchorPoint;

      IObjectCopy objCopy = new ObjectCopyClass();

      //take care of the custom property
      if (null != srcTriangle.CustomProperty)
      {
        if (srcTriangle.CustomProperty is IClone)
          m_customProperty = (object)((IClone)srcTriangle.CustomProperty).Clone();
        else if (srcTriangle.CustomProperty is IPersistStream)
        {
          m_customProperty = objCopy.Copy((object)srcTriangle.CustomProperty);
        }
        else if (srcTriangle.CustomProperty.GetType().IsSerializable)
        {
          //serialize to a memory stream
          MemoryStream memoryStream = new MemoryStream();
          BinaryFormatter binaryFormatter = new BinaryFormatter();
          binaryFormatter.Serialize(memoryStream, srcTriangle.CustomProperty);
          byte[] bytes = memoryStream.ToArray();

          memoryStream = new MemoryStream(bytes);
          m_customProperty = binaryFormatter.Deserialize(memoryStream);
        }
      }

      if (null != srcTriangle.SpatialReference)
        m_nativeSR = objCopy.Copy(srcTriangle.SpatialReference) as ISpatialReference;
      else
        m_nativeSR = null;

      if (null != srcTriangle.FillSymbol)
      {
        m_fillSymbol = objCopy.Copy(srcTriangle.FillSymbol) as ISimpleFillSymbol;
      }
      else
        m_fillSymbol = null;

      if (null != srcTriangle.Geometry)
      {
        m_triangle = objCopy.Copy(srcTriangle.Geometry) as IPolygon;
        m_pointGeometry = objCopy.Copy(((IArea)m_triangle).Centroid) as IPoint;
      }
      else
      {
        m_triangle = null;
        m_pointGeometry = null;
      }
    }

    public IClone Clone()
    {
      TriangleElementClass triangle = new TriangleElementClass();
      triangle.Assign((IClone)this);

      return (IClone)triangle;
    }

    public bool IsEqual(IClone other)
    {
      //1. make sure that the 'other' object is pointing to a valid object
      if (null == other)
        throw new COMException("Invalid object.");

      //2. verify the type of 'other'
      if (!(other is TriangleElementClass))
        throw new COMException("Bad object type.");

      TriangleElementClass otherTriangle = (TriangleElementClass)other;
      //test that all of the object's properties are the same.
      //please note the usage of IsEqual when using ArcObjects components that
      //supports cloning
      if (otherTriangle.Name == m_elementName &&
          otherTriangle.Type == m_elementType &&
          otherTriangle.AutoTransform == m_autoTrans &&
          otherTriangle.ReferenceScale == m_scaleRef &&
          otherTriangle.Angle == m_rotation &&
          otherTriangle.Size == m_size &&
          otherTriangle.AnchorPoint == m_anchorPointType &&
          ((IClone)otherTriangle.Geometry).IsEqual((IClone)m_triangle) &&
          ((IClone)otherTriangle.FillSymbol).IsEqual((IClone)m_fillSymbol) &&
          ((IClone)otherTriangle.SpatialReference).IsEqual((IClone)m_nativeSR))
        return true;

      return false;
    }

    public bool IsIdentical(IClone other)
    {
      //1. make sure that the 'other' object is pointing to a valid object
      if (null == other)
        throw new COMException("Invalid object.");

      //2. verify the type of 'other'
      if (!(other is TriangleElementClass))
        throw new COMException("Bad object type.");

      //3. test if the other is the 'this'
      if ((TriangleElementClass)other == this)
        return true;

      return false;
    }

    #endregion

    #region IDocumentVersionSupportGEN Members

    public object ConvertToSupportedObject(esriArcGISVersion docVersion)
    {
      //in case of 8.3, create a character marker element and use a triangle marker...
      ICharacterMarkerSymbol charMarkerSymbol = new CharacterMarkerSymbolClass();
      charMarkerSymbol.Color = m_fillSymbol.Color;
      charMarkerSymbol.Angle = m_rotation;
      charMarkerSymbol.Size = m_size;
      charMarkerSymbol.Font = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToStdFont(new Font("ESRI Default Marker", (float)m_size, FontStyle.Regular));
      charMarkerSymbol.CharacterIndex = 184;

      IMarkerElement markerElement = new MarkerElementClass();
      markerElement.Symbol = (IMarkerSymbol)charMarkerSymbol;

      IPoint point = ((IClone)m_pointGeometry).Clone() as IPoint;
      IElement element = (IElement)markerElement;
      element.Geometry = (IGeometry)point;

      return element;
    }

    public bool IsSupportedAtVersion(esriArcGISVersion docVersion)
    {
      //support all versions except 8.3
      if (esriArcGISVersion.esriArcGISVersion83 == docVersion)
        return false;
      else
        return true;
    }

    #endregion

    #region private methods
    private IClone Clone(object obj)
    {
      if (null == obj || !(obj is IClone))
        return null;

      return ((IClone)obj).Clone();
    }

    private int TwipsPerPixelX()
    {
      return 16;
    }

    private int TwipsPerPixelY()
    {
      return 16;
    }

    private void SetupDeviceRatio(int hDC, ESRI.ArcGIS.Display.IDisplay display)
    {
      if (display.DisplayTransformation != null)
      {
        if (display.DisplayTransformation.Resolution != 0)
        {
          m_dDeviceRatio = display.DisplayTransformation.Resolution / 72;
          //  Check the ReferenceScale of the display transformation. If not zero, we need to
          //  adjust the Size, XOffset and YOffset of the Symbol we hold internally before drawing.
          if (display.DisplayTransformation.ReferenceScale != 0)
            m_dDeviceRatio = m_dDeviceRatio * display.DisplayTransformation.ReferenceScale / display.DisplayTransformation.ScaleRatio;
        }
      }
      else
      {
        // If we don't have a display transformation, calculate the resolution
        // from the actual device.
        if (display.hDC != 0)
        {
          // Get the resolution from the device context hDC.
          m_dDeviceRatio = System.Convert.ToDouble(GetDeviceCaps(hDC, LOGPIXELSX)) / 72;
        }
        else
        {
          // If invalid hDC assume we're drawing to the screen.
          m_dDeviceRatio = 1 / (TwipsPerPixelX() / 20); // 1 Point = 20 Twips.
        }
      }
    }

    private double PointsToMap(IDisplayTransformation displayTransform, double dPointSize)
    {
      double tempPointsToMap = 0;
      if (displayTransform == null)
        tempPointsToMap = dPointSize * m_dDeviceRatio;
      else
      {
        tempPointsToMap = displayTransform.FromPoints(dPointSize);
      }
      return tempPointsToMap;
    }

    private void BuildTriangleGeometry(IPoint pointGeometry)
    {
      try
      {
        if (null == m_triangle || null == pointGeometry || null == m_cachedDisplay)
          return;

        m_triangle.SpatialReference = pointGeometry.SpatialReference;
        m_triangle.SetEmpty();

        object missing = System.Reflection.Missing.Value;
        IPointCollection pointCollection = (IPointCollection)m_triangle;

        double radius = PointsToMap(m_cachedDisplay.DisplayTransformation, m_size);

        double X = pointGeometry.X;
        double Y = pointGeometry.Y;

        IPoint point = new PointClass();
        point.X = X + radius * c_Cosine30;
        point.Y = Y - 0.5 * radius;
        pointCollection.AddPoint(point, ref missing, ref missing);

        point = new PointClass();
        point.X = X;
        point.Y = Y + radius;
        pointCollection.AddPoint(point, ref missing, ref missing);

        point = new PointClass();
        point.X = X - radius * c_Cosine30;
        point.Y = Y - 0.5 * radius;
        pointCollection.AddPoint(point, ref missing, ref missing);

        m_triangle.Close();

        if (m_rotation != 0.0)
        {
          ((ITransform2D)pointCollection).Rotate(pointGeometry, m_rotation * c_Deg2Rad);
        }

        return;
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    private void SetDefaultDymbol()
    {
      IColor color = (IColor)ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.Black);
      ISimpleLineSymbol lineSymbol = new SimpleLineSymbolClass();
      lineSymbol.Style = esriSimpleLineStyle.esriSLSSolid;
      lineSymbol.Width = 1.0;
      lineSymbol.Color = color;

      color = (IColor)ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.Navy);
      if (null == m_fillSymbol)
        m_fillSymbol = new SimpleFillSymbolClass();
      m_fillSymbol.Color = color;
      m_fillSymbol.Style = esriSimpleFillStyle.esriSFSSolid;
      m_fillSymbol.Outline = (ILineSymbol)lineSymbol;
    }


    /// <summary>
    /// assign the triangle's geometry to the selection tracker
    /// </summary>
    private void RefreshTracker()
    {
      if (null == m_cachedDisplay)
        return;

      m_selectionTracker.Display = (IScreenDisplay)m_cachedDisplay;

      
      IPolygon outline = new PolygonClass(); 
      this.QueryOutline(m_cachedDisplay, outline);

      m_selectionTracker.Geometry = (IGeometry)outline;
    }

    private void UpdateElementSpatialRef()
    {
      if (null == m_cachedDisplay ||
          null == m_nativeSR ||
          null == m_triangle ||
          null == m_cachedDisplay.DisplayTransformation.SpatialReference)
        return;

      if (null == m_triangle.SpatialReference)
        m_triangle.SpatialReference = m_cachedDisplay.DisplayTransformation.SpatialReference;

      m_triangle.Project(m_nativeSR);

      RefreshTracker();
    }

    private void InitMembers()
    {
      //initialize the selection tracker
      m_selectionTracker = new PolygonTrackerClass();
      m_selectionTracker.Locked = false;
      m_selectionTracker.ShowHandles = true;

      //set a default symbol
      SetDefaultDymbol();
    }
    #endregion

  }
}

[Visual Basic .NET]

TriangleElementClass.vb

Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Imports ESRI.ArcGIS.ADF
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Display
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.esriSystem

<ComVisible(True), Guid("DC8482C9-5DD6-44dc-BF3C-54B18AB813C9")> _
  Public Interface ITriangleElement
  Property FillSymbol() As ISimpleFillSymbol
  Property Size() As Double
  Property Angle() As Double
End Interface

<Guid(TriangleElementClass.CLASSGUID), ClassInterface(ClassInterfaceType.None), ProgId("TriangleElement.TriangleElementClass")> _
Public NotInheritable Class TriangleElementClass
  Implements ITriangleElement, _
             IElement, _
             IElementProperties, _
             IElementProperties2, _
             IElementProperties3, _
             IBoundsProperties, _
             ITransform2D, _
             IGraphicElement, _
             IPersistVariant, _
             IClone, _
             IDocumentVersionSupportGEN

#Region "class members"

  'some win32 imports and constants
  <System.Runtime.InteropServices.DllImport("gdi32", EntryPoint:="GetDeviceCaps", ExactSpelling:=True, CharSet:=System.Runtime.InteropServices.CharSet.Ansi, SetLastError:=True)> _
  Public Shared Function GetDeviceCaps(ByVal hDC As Integer, ByVal nIndex As Integer) As Integer
  End Function
  Private Const c_Cosine30 As Double = 0.866025403784439
  Private Const c_Deg2Rad As Double = (Math.PI / 180.0)
  Private Const c_Rad2Deg As Double = (180.0 / Math.PI)
  Private Const c_Version As Integer = 2
  Public Const CLASSGUID As String = "cbf943e2-ce6d-49f4-a4a7-ce16f02379ad"
  Public Const LOGPIXELSX As Integer = 88
  Public Const LOGPIXELSY As Integer = 90

  Private m_triangle As IPolygon = Nothing
  Private m_pointGeometry As IPoint = Nothing
  Private m_fillSymbol As ISimpleFillSymbol = Nothing
  Private m_rotation As Double = 0.0
  Private m_size As Double = 20.0
  Private m_selectionTracker As ISelectionTracker = Nothing
  Private m_cachedDisplay As IDisplay = Nothing
  Private m_nativeSR As ISpatialReference = Nothing
  Private m_elementName As String = String.Empty
  Private m_elementType As String = "TriangleElement"
  Private m_customProperty As Object = Nothing
  Private m_autoTrans As Boolean = True
  Private m_scaleRef As Double = 0.0
  Private m_anchorPointType As esriAnchorPointEnum = esriAnchorPointEnum.esriCenterPoint
  Private m_dDeviceRatio As Double = 0
#End Region

#Region "class constructor"
  Public Sub New()
    'initialize the element's geometry
    m_triangle = New PolygonClass()
    m_triangle.SetEmpty()

    InitMembers()
  End Sub
#End Region

#Region "ITriangleElement Members"

  Public Property FillSymbol() As ISimpleFillSymbol Implements ITriangleElement.FillSymbol
    Get
      Return m_fillSymbol
    End Get
    Set(ByVal value As ISimpleFillSymbol)
      m_fillSymbol = value
    End Set
  End Property

  Public Property Size() As Double Implements ITriangleElement.Size
    Get
      Return m_size
    End Get
    Set(ByVal value As Double)
      m_size = value
    End Set
  End Property

  Public Property Angle() As Double Implements ITriangleElement.Angle
    Get
      Return m_rotation
    End Get
    Set(ByVal value As Double)
      m_rotation = value
    End Set
  End Property

#End Region

#Region "IElement Members"

  Public Sub Activate(ByVal Display As IDisplay) Implements IElement.Activate
    'cache the display
    m_cachedDisplay = Display

    SetupDeviceRatio(Display.hDC, Display)

    'need to calculate the points of the triangle polygon
    If m_triangle.IsEmpty Then
      BuildTriangleGeometry(m_pointGeometry)
    End If

    'need to refresh the element's tracker
    RefreshTracker()
  End Sub

  Public Sub Deactivate() Implements IElement.Deactivate
    m_cachedDisplay = Nothing
  End Sub

  Public Sub Draw(ByVal Display As IDisplay, ByVal TrackCancel As ITrackCancel) Implements IElement.Draw
    If Not Nothing Is m_triangle AndAlso Not Nothing Is m_fillSymbol Then
      Display.SetSymbol(CType(m_fillSymbol, ISymbol))
      Display.DrawPolygon(m_triangle)
    End If
  End Sub

  Public Property Geometry() As IGeometry Implements IElement.Geometry
    Get
      Return TryCast(Clone(m_pointGeometry), IGeometry)
    End Get
    Set(ByVal value As IGeometry)
      Try
        m_pointGeometry = TryCast(Clone(value), IPoint)

        UpdateElementSpatialRef()
      Catch ex As Exception
        System.Diagnostics.Trace.WriteLine(ex.Message)
      End Try
    End Set
  End Property

  Public Function HitTest(ByVal x As Double, ByVal y As Double, ByVal Tolerance As Double) As Boolean Implements IElement.HitTest
    If Nothing Is m_cachedDisplay Then
      Return False
    End If

    Dim point As IPoint = New PointClass()
    point.PutCoords(x, y)

    Return (CType(m_triangle, IRelationalOperator)).Contains(CType(point, IGeometry))
  End Function

  Public Property Locked() As Boolean Implements IElement.Locked
    Get
      Return False
    End Get
    Set(ByVal value As Boolean)

    End Set
  End Property

  Public Sub QueryBounds(ByVal Display As IDisplay, ByVal Bounds As IEnvelope) Implements IElement.QueryBounds
    'return a bounding envelope
    Dim polygon As IPolygon = New PolygonClass()
    polygon.SetEmpty()

    CType(m_fillSymbol, ISymbol).QueryBoundary(Display.hDC, Display.DisplayTransformation, m_triangle, polygon)

    Bounds.XMin = polygon.Envelope.XMin
    Bounds.XMax = polygon.Envelope.XMax
    Bounds.YMin = polygon.Envelope.YMin
    Bounds.YMax = polygon.Envelope.YMax
    Bounds.SpatialReference = polygon.Envelope.SpatialReference
  End Sub

  Public Sub QueryOutline(ByVal Display As IDisplay, ByVal Outline As IPolygon) Implements IElement.QueryOutline
    'return a polygon which is the outline of the element
    Dim polygon As IPolygon = New PolygonClass()
    polygon.SetEmpty()
    CType(m_fillSymbol, ISymbol).QueryBoundary(Display.hDC, Display.DisplayTransformation, m_triangle, polygon)
    CType(Outline, IPointCollection).AddPointCollection(CType(polygon, IPointCollection))
  End Sub

  Public ReadOnly Property SelectionTracker() As ISelectionTracker Implements IElement.SelectionTracker
    Get
      Return m_selectionTracker
    End Get
  End Property

#End Region

#Region "IElementProperties Members"

  ''' <summary>
  ''' Indicates if transform is applied to symbols and other parts of element.
  ''' False = only apply transform to geometry.
  ''' Update font size in ITransform2D routines
  ''' </summary>
  Public Property AutoTransform() As Boolean Implements IElementProperties.AutoTransform, IElementProperties2.AutoTransform, IElementProperties3.AutoTransform
    Get
      Return m_autoTrans
    End Get
    Set(ByVal value As Boolean)
      m_autoTrans = value
    End Set
  End Property

  Public Property CustomProperty() As Object Implements IElementProperties.CustomProperty, IElementProperties2.CustomProperty, IElementProperties3.CustomProperty
    Get
      Return m_customProperty
    End Get
    Set(ByVal value As Object)
      m_customProperty = value
    End Set
  End Property

  Public Property Name() As String Implements IElementProperties.Name, IElementProperties2.Name, IElementProperties3.Name
    Get
      Return m_elementName
    End Get
    Set(ByVal value As String)
      m_elementName = value
    End Set
  End Property

  Public Property Type() As String Implements IElementProperties.Type, IElementProperties2.Type, IElementProperties3.Type
    Get
      Return m_elementType
    End Get
    Set(ByVal value As String)
      m_elementType = value
    End Set
  End Property

#End Region

#Region "IElementProperties2 Members"


  Public Function CanRotate() As Boolean Implements IElementProperties2.CanRotate, IElementProperties3.CanRotate
    Return True
  End Function

  Public Property ReferenceScale() As Double Implements IElementProperties2.ReferenceScale, IElementProperties3.ReferenceScale
    Get
      Return m_scaleRef
    End Get
    Set(ByVal value As Double)
      m_scaleRef = value
    End Set
  End Property

#End Region

#Region "IElementProperties3 Members"

  Public Property AnchorPoint() As esriAnchorPointEnum Implements IElementProperties3.AnchorPoint
    Get
      Return m_anchorPointType
    End Get
    Set(ByVal value As esriAnchorPointEnum)
      m_anchorPointType = value
    End Set
  End Property

#End Region

#Region "IBoundsProperties Members"

  Public Property FixedAspectRatio() As Boolean Implements IBoundsProperties.FixedAspectRatio
    Get
      Return True
    End Get
    Set(ByVal value As Boolean)
      Throw New Exception("The method or operation is not implemented.")
    End Set
  End Property

  Public ReadOnly Property FixedSize() As Boolean Implements IBoundsProperties.FixedSize
    Get
      Return True
    End Get
  End Property

#End Region

#Region "ITransform2D Members"

  Public Sub Move(ByVal dx As Double, ByVal dy As Double) Implements ITransform2D.Move
    If Nothing Is m_triangle Then
      Return
    End If

    CType(m_triangle, ITransform2D).Move(dx, dy)
    CType(m_pointGeometry, ITransform2D).Move(dx, dy)

    RefreshTracker()
  End Sub

  Public Sub MoveVector(ByVal v As ILine) Implements ITransform2D.MoveVector
    If Nothing Is m_triangle Then
      Return
    End If

    CType(m_triangle, ITransform2D).MoveVector(v)
    CType(m_pointGeometry, ITransform2D).MoveVector(v)

    RefreshTracker()
  End Sub

  Public Sub Rotate(ByVal Origin As IPoint, ByVal rotationAngle As Double) Implements ITransform2D.Rotate
    If Nothing Is m_triangle Then
      Return
    End If

    CType(m_triangle, ITransform2D).Rotate(Origin, rotationAngle)
    CType(m_pointGeometry, ITransform2D).Rotate(Origin, rotationAngle)

    m_rotation = rotationAngle * c_Rad2Deg

    RefreshTracker()
  End Sub

  Public Sub Scale(ByVal Origin As IPoint, ByVal sx As Double, ByVal sy As Double) Implements ITransform2D.Scale
    If Nothing Is m_triangle Then
      Return
    End If

    CType(m_triangle, ITransform2D).Scale(Origin, sx, sy)
    CType(m_pointGeometry, ITransform2D).Scale(Origin, sx, sy)

    If m_autoTrans Then
      m_size *= Math.Max(sx, sy)
    End If

    RefreshTracker()
  End Sub

  Public Sub Transform(ByVal direction As esriTransformDirection, ByVal transformation As ITransformation) Implements ITransform2D.Transform
    If Nothing Is m_triangle Then
      Return
    End If

    'Geometry
    CType(m_triangle, ITransform2D).Transform(direction, transformation)

    Dim affineTrans As IAffineTransformation2D = CType(transformation, IAffineTransformation2D)
    If affineTrans.YScale <> 1.0 Then
      m_size *= Math.Max(affineTrans.YScale, affineTrans.XScale)
    End If

    RefreshTracker()
  End Sub

#End Region

#Region "IGraphicElement Members"

  Public Property SpatialReference() As ISpatialReference Implements IElementProperties3.SpatialReference, IGraphicElement.SpatialReference
    Get
      Return m_nativeSR
    End Get
    Set(ByVal value As ISpatialReference)
      m_nativeSR = value
      UpdateElementSpatialRef()
    End Set
  End Property

#End Region

#Region "IPersistVariant Members"

  Public ReadOnly Property ID() As UID Implements IPersistVariant.ID
    Get
      Dim uid As UID = New UIDClass()
      uid.Value = "{" & TriangleElementClass.CLASSGUID & "}"
      Return uid
    End Get
  End Property

  Public Sub Load(ByVal Stream As IVariantStream) Implements IPersistVariant.Load
    Dim ver As Integer = CInt(Fix(Stream.Read()))
    If ver > c_Version OrElse ver <= 0 Then
      Throw New Exception("Wrong version!")
    End If

    InitMembers()

    m_size = CDbl(Stream.Read())
    m_scaleRef = CDbl(Stream.Read())
    m_anchorPointType = CType(Stream.Read(), esriAnchorPointEnum)
    m_autoTrans = CBool(Stream.Read())
    m_elementType = CStr(Stream.Read())
    m_elementName = CStr(Stream.Read())
    m_nativeSR = TryCast(Stream.Read(), ISpatialReference)
    m_fillSymbol = TryCast(Stream.Read(), ISimpleFillSymbol)
    m_pointGeometry = TryCast(Stream.Read(), IPoint)
    m_triangle = TryCast(Stream.Read(), IPolygon)

    If ver = 2 Then
      m_rotation = CDbl(Stream.Read())
    End If
  End Sub

  Public Sub Save(ByVal Stream As IVariantStream) Implements IPersistVariant.Save
    Stream.Write(c_Version)

    Stream.Write(m_size)
    Stream.Write(m_scaleRef)
    Stream.Write(m_anchorPointType)
    Stream.Write(m_autoTrans)
    Stream.Write(m_elementType)
    Stream.Write(m_elementName)
    Stream.Write(m_nativeSR)
    Stream.Write(m_fillSymbol)
    Stream.Write(m_pointGeometry)
    Stream.Write(m_triangle)

    Stream.Write(m_rotation)
  End Sub

#End Region

#Region "IClone Members"

  Public Sub Assign(ByVal src As IClone) Implements IClone.Assign

    '1. make sure that src is pointing to a valid object
    If Nothing Is src Then
            Throw New COMException("Invalid object.")
    End If

    '2. make sure that the type of src is of type 'TriangleElementClass'
    If Not (TypeOf src Is TriangleElementClass) Then
      Throw New COMException("Bad object type.")
    End If

    '3. assign the properties of src to the current instance
    Dim srcTriangle As TriangleElementClass = CType(src, TriangleElementClass)
    m_elementName = srcTriangle.Name
    m_elementType = srcTriangle.Type
    m_autoTrans = srcTriangle.AutoTransform
    m_scaleRef = srcTriangle.ReferenceScale
    m_rotation = srcTriangle.Angle
    m_size = srcTriangle.Size
    m_anchorPointType = srcTriangle.AnchorPoint

    Dim objCopy As IObjectCopy = New ObjectCopyClass()

    'take care of the custom property
    If Not Nothing Is srcTriangle.CustomProperty Then
      If TypeOf srcTriangle.CustomProperty Is IClone Then
        m_customProperty = CObj((CType(srcTriangle.CustomProperty, IClone)).Clone())
      ElseIf TypeOf srcTriangle.CustomProperty Is IPersistStream Then
        m_customProperty = objCopy.Copy(CObj(srcTriangle.CustomProperty))
      ElseIf srcTriangle.CustomProperty.GetType().IsSerializable Then
        'serialize to a memory stream
        Dim memoryStream As MemoryStream = New MemoryStream()
        Dim binaryFormatter As BinaryFormatter = New BinaryFormatter()
        binaryFormatter.Serialize(memoryStream, srcTriangle.CustomProperty)
        Dim bytes As Byte() = memoryStream.ToArray()

        memoryStream = New MemoryStream(bytes)
        m_customProperty = binaryFormatter.Deserialize(memoryStream)
      End If
    End If

    If Not Nothing Is srcTriangle.SpatialReference Then
      m_nativeSR = TryCast(objCopy.Copy(srcTriangle.SpatialReference), ISpatialReference)
    Else
      m_nativeSR = Nothing
    End If

    If Not Nothing Is srcTriangle.FillSymbol Then
      m_fillSymbol = TryCast(objCopy.Copy(srcTriangle.FillSymbol), ISimpleFillSymbol)
    Else
      m_fillSymbol = Nothing
    End If

    If Not Nothing Is srcTriangle.Geometry Then
      m_triangle = TryCast(objCopy.Copy(srcTriangle.Geometry), IPolygon)
      m_pointGeometry = TryCast(objCopy.Copy((CType(m_triangle, IArea)).Centroid), IPoint)
    Else
      m_triangle = Nothing
      m_pointGeometry = Nothing
    End If
  End Sub

  Public Function Clone() As IClone Implements IClone.Clone
    Dim triangle As TriangleElementClass = New TriangleElementClass()
    triangle.Assign(CType(Me, IClone))

    Return CType(triangle, IClone)
  End Function

  Public Function IsEqual(ByVal other As IClone) As Boolean Implements IClone.IsEqual
    '1. make sure that the 'other' object is pointing to a valid object
    If Nothing Is other Then
            Throw New COMException("Invalid object.")
    End If

    '2. verify the type of 'other'
    If Not (TypeOf other Is TriangleElementClass) Then
      Throw New COMException("Bad object type.")
    End If

    Dim otherTriangle As TriangleElementClass = CType(other, TriangleElementClass)
    'test that all ot the object's properties are the same.
        'please note the usage of IsEqual when using ArcObjects components that
    'supports cloning
    If otherTriangle.Name = m_elementName AndAlso otherTriangle.Type = m_elementType AndAlso otherTriangle.AutoTransform = m_autoTrans AndAlso otherTriangle.ReferenceScale = m_scaleRef AndAlso otherTriangle.Angle = m_rotation AndAlso otherTriangle.Size = m_size AndAlso otherTriangle.AnchorPoint = m_anchorPointType AndAlso (CType(otherTriangle.Geometry, IClone)).IsEqual(CType(m_triangle, IClone)) AndAlso (CType(otherTriangle.FillSymbol, IClone)).IsEqual(CType(m_fillSymbol, IClone)) AndAlso (CType(otherTriangle.SpatialReference, IClone)).IsEqual(CType(m_nativeSR, IClone)) Then
      Return True
    End If

    Return False
  End Function

  Public Function IsIdentical(ByVal other As IClone) As Boolean Implements IClone.IsIdentical
    '1. make sure that the 'other' object is pointing to a valid object
    If Nothing Is other Then
            Throw New COMException("Invalid object.")
    End If

    '2. verify the type of 'other'
    If Not (TypeOf other Is TriangleElementClass) Then
      Throw New COMException("Bad object type.")
    End If

    '3. test if the other is the 'this'
    If CType(other, TriangleElementClass) Is Me Then
      Return True
    End If

    Return False
  End Function

#End Region

#Region "IDocumentVersionSupportGEN Members"

  Public Function ConvertToSupportedObject(ByVal docVersion As esriArcGISVersion) As Object Implements IDocumentVersionSupportGEN.ConvertToSupportedObject
    'in case of 8.3, create a character marker element and use a triangle marker...
    Dim charMarkerSymbol As ICharacterMarkerSymbol = New CharacterMarkerSymbolClass()
    charMarkerSymbol.Color = m_fillSymbol.Color
    charMarkerSymbol.Angle = m_rotation
    charMarkerSymbol.Size = m_size
        charMarkerSymbol.Font = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToStdFont(New Font("ESRI Default Marker", CSng(m_size), FontStyle.Regular))
    charMarkerSymbol.CharacterIndex = 184

    Dim markerElement As IMarkerElement = New MarkerElementClass()
    markerElement.Symbol = CType(charMarkerSymbol, IMarkerSymbol)

    Dim point As IPoint = TryCast((CType(m_pointGeometry, IClone)).Clone(), IPoint)
    Dim element As IElement = CType(markerElement, IElement)
    element.Geometry = CType(point, IGeometry)

    Return element
  End Function

  Public Function IsSupportedAtVersion(ByVal docVersion As esriArcGISVersion) As Boolean Implements IDocumentVersionSupportGEN.IsSupportedAtVersion
    'support all versions except 8.3
    If esriArcGISVersion.esriArcGISVersion83 = docVersion Then
      Return False
    Else
      Return True
    End If
  End Function

#End Region

#Region "private methods"
  Private Function Clone(ByVal obj As Object) As IClone
    If Nothing Is obj OrElse Not (TypeOf obj Is IClone) Then
      Return Nothing
    End If

    Return (CType(obj, IClone)).Clone()
  End Function

  Private Function TwipsPerPixelX() As Integer
    Return 16
  End Function

  Private Function TwipsPerPixelY() As Integer
    Return 16
  End Function

  Private Sub SetupDeviceRatio(ByVal hDC As Integer, ByVal display As ESRI.ArcGIS.Display.IDisplay)
    If Not display.DisplayTransformation Is Nothing Then
      If display.DisplayTransformation.Resolution <> 0 Then
        m_dDeviceRatio = display.DisplayTransformation.Resolution / 72
        '  Check the ReferenceScale of the display transformation. If not zero, we need to
        '  adjust the Size, XOffset and YOffset of the Symbol we hold internally before drawing.
        If display.DisplayTransformation.ReferenceScale <> 0 Then
          m_dDeviceRatio = m_dDeviceRatio * display.DisplayTransformation.ReferenceScale / display.DisplayTransformation.ScaleRatio
        End If
      End If
    Else
            ' If we don't have a display transformation, calculate the resolution
      ' from the actual device.
      If display.hDC <> 0 Then
        ' Get the resolution from the device context hDC.
        m_dDeviceRatio = System.Convert.ToDouble(GetDeviceCaps(hDC, LOGPIXELSX)) / 72
      Else
        ' If invalid hDC assume we're drawing to the screen.
        m_dDeviceRatio = 1 / (TwipsPerPixelX() / 20) ' 1 Point = 20 Twips.
      End If
    End If
  End Sub

  Private Function PointsToMap(ByVal displayTransform As IDisplayTransformation, ByVal dPointSize As Double) As Double
    Dim tempPointsToMap As Double = 0
    If displayTransform Is Nothing Then
      tempPointsToMap = dPointSize * m_dDeviceRatio
    Else
      tempPointsToMap = displayTransform.FromPoints(dPointSize)
    End If
    Return tempPointsToMap
  End Function

  Private Sub BuildTriangleGeometry(ByVal pointGeometry As IPoint)
    Try
      If Nothing Is m_triangle OrElse Nothing Is pointGeometry OrElse Nothing Is m_cachedDisplay Then
        Return
      End If

      m_triangle.SpatialReference = pointGeometry.SpatialReference
      m_triangle.SetEmpty()

      Dim missing As Object = System.Reflection.Missing.Value
      Dim pointCollection As IPointCollection = CType(m_triangle, IPointCollection)

      Dim radius As Double = PointsToMap(m_cachedDisplay.DisplayTransformation, m_size)

      Dim X As Double = pointGeometry.X
      Dim Y As Double = pointGeometry.Y

      Dim point As IPoint = New PointClass()
      point.X = X + radius * c_Cosine30
      point.Y = Y - 0.5 * radius
      pointCollection.AddPoint(point, missing, missing)

      point = New PointClass()
      point.X = X
      point.Y = Y + radius
      pointCollection.AddPoint(point, missing, missing)

      point = New PointClass()
      point.X = X - radius * c_Cosine30
      point.Y = Y - 0.5 * radius
      pointCollection.AddPoint(point, missing, missing)

      m_triangle.Close()

      If m_rotation <> 0.0 Then
        CType(pointCollection, ITransform2D).Rotate(pointGeometry, m_rotation * c_Deg2Rad)
      End If

      Return
    Catch ex As Exception
      System.Diagnostics.Trace.WriteLine(ex.Message)
    End Try
  End Sub

  Private Sub SetDefaultDymbol()
        Dim c As IColor = CType(ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.Black), IColor)
    Dim lineSymbol As ISimpleLineSymbol = New SimpleLineSymbolClass()
    lineSymbol.Style = esriSimpleLineStyle.esriSLSSolid
    lineSymbol.Width = 1.0
    lineSymbol.Color = c

        c = CType(ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.Navy), IColor)
    If Nothing Is m_fillSymbol Then
      m_fillSymbol = New SimpleFillSymbolClass()
    End If
    m_fillSymbol.Color = c
    m_fillSymbol.Style = esriSimpleFillStyle.esriSFSSolid
    m_fillSymbol.Outline = CType(lineSymbol, ILineSymbol)
  End Sub


  ''' <summary>
  ''' assign the triangle's geometry to the selection tracker
  ''' </summary>
  Private Sub RefreshTracker()
    If Nothing Is m_cachedDisplay Then
      Return
    End If

    m_selectionTracker.Display = CType(m_cachedDisplay, IScreenDisplay)


    Dim outline As IPolygon = New PolygonClass()
    Me.QueryOutline(m_cachedDisplay, outline)

    m_selectionTracker.Geometry = CType(outline, IGeometry)
  End Sub

  Private Sub UpdateElementSpatialRef()
    If Nothing Is m_cachedDisplay OrElse Nothing Is m_nativeSR OrElse Nothing Is m_triangle OrElse Nothing Is m_cachedDisplay.DisplayTransformation.SpatialReference Then
      Return
    End If

    If Nothing Is m_triangle.SpatialReference Then
      m_triangle.SpatialReference = m_cachedDisplay.DisplayTransformation.SpatialReference
    End If

    m_triangle.Project(m_nativeSR)

    RefreshTracker()
  End Sub

  Private Sub InitMembers()
    'initialize the selection tracker
    m_selectionTracker = New PolygonTrackerClass()
    m_selectionTracker.Locked = False
    m_selectionTracker.ShowHandles = True

    'set a default symbol
    SetDefaultDymbol()
  End Sub
#End Region

End Class