Elliptical arc

Hi,<?:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

I want to draw elliptical arc using Go Diagrams.

For this I am planning to use GoPie, but GoPie draws the middle lines also with the arc.

But my requirement is to make an arc without the inner straight lines as shown in the figure.

<?:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

So If you see the figure I want to remove the straight lines and want to draw only the curve.

Also if you can tell me drawing elliptical arc using any other GO Shape then that will also be fine.

Thanks,

Vin

[code]/*

  • Copyright © Northwoods Software Corporation, 1998-2010. All Rights
  • Reserved.
  • Restricted Rights: Use, duplication, or disclosure by the U.S.
  • Government is subject to restrictions as set forth in subparagraph
  • © (1) (ii) of DFARS 252.227-7013, or in FAR 52.227-19, or in FAR
  • 52.227-14 Alt. III, as applicable.
    */

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using Northwoods.Go;

namespace Demo1 {
///


/// An object in the shape of an elliptical curve.
///

[Serializable]
public class Arc : GoShape {
///
/// The constructor produces an arc with a standard black
/// outline and no fill.
///

public Arc() {
this.ResizesRealtime = true;
}

/// <summary>
/// Draw a possibly shadowed arc.
/// </summary>
/// <param name="g"></param>
/// <param name="view"></param>
public override void Paint(Graphics g, GoView view) {
  // ??? A SweepAngle of 0 draws nothing.
  float sa = this.StartAngle;
  float sweep = this.SweepAngle;
  // sa is always: 0 <= sa < 360
  // sweep is always: -360 <= sweep <= 360
  RectangleF rect = this.Bounds;
  if (this.Shadowed) {
    SizeF offset = GetShadowOffset(view);
    if (this.Pen != null) {
      Pen pen = GetShadowPen(view, this.PenWidth);
      g.DrawArc(pen, rect.X + offset.Width, rect.Y + offset.Height, rect.Width, rect.Height, sa, sweep);
    }
  }
  if (this.Pen != null) {
    g.DrawArc(this.Pen, rect.X, rect.Y, rect.Width, rect.Height, sa, sweep);
  }
}

/// <summary>
/// Produce a <c>GraphicsPath</c> by adding an arc.
/// </summary>
/// <returns></returns>
public override GraphicsPath MakePath() {
  GraphicsPath path = new GraphicsPath(FillMode.Winding);
  RectangleF rect = this.Bounds;
  if (rect.Width > 0 && rect.Height > 0)
    path.AddArc(rect.X, rect.Y, rect.Width, rect.Height, this.StartAngle, this.SweepAngle);
  return path;
}

/// <summary>
/// Gets or sets the initial angle of the section of the ellipse to be drawn.
/// </summary>
/// <value>
/// This value is in degrees, measured clockwise from zero along the positive X axis.
/// The value should range from zero to just below 360.  Values outside this range
/// are adjusted to equivalent angles that fall in this range.
/// </value>
[Category("Appearance"), DefaultValue(0)]
[Description("The start angle for the side of the arc")]
public float StartAngle {
  get { return myStartAngle; }
  set {
    float old = myStartAngle;
    if (value < 0) {
      value = 360 - (-value%360);
    } else if (value >= 360) {
      value = value%360;
    }
    if (old != value) {
      myStartAngle = value;
      ResetPath();
      Changed(ChangedStartAngle, 0, null, MakeRect(old), 0, null, MakeRect(value));
    }
  }
}

/// <summary>
/// Gets or sets the angle of the width of the section of the ellipse to be drawn.
/// </summary>
/// <value>
/// This value is in degrees, measured clockwise from the <see cref="StartAngle"/>.
/// Absolute values equal to or greater than 360 degrees are adjusted to the equivalent
/// angles less than 360 degrees.
/// The default value is 300.
/// </value>
[Category("Appearance"), DefaultValue(300)]
[Description("The sweep angle for the body of the arc")]
public float SweepAngle {
  get { return mySweepAngle; }
  set {
    float old = mySweepAngle;
    if (value > 360 || value < -360)
      value = value%360;
    if (old != value) {
      mySweepAngle = value;
      ResetPath();
      Changed(ChangedSweepAngle, 0, null, MakeRect(old), 0, null, MakeRect(value));
    }
  }
}

/// <summary>
/// Gets or sets whether to add the resizing handle controlling the start angle.
/// </summary>
/// <value>
/// This defaults to true.
/// </value>
[Category("Behavior"), DefaultValue(true)]
[Description("Whether users can resize the start angle of this resizable object.")]
public virtual bool ResizableStartAngle {
  get { return myResizableStartAngle; }
  set {
    bool old = myResizableStartAngle;
    if (old != value) {
      myResizableStartAngle = value;
      Changed(ChangedResizableStartAngle, 0, old, NullRect, 0, value, NullRect);
    }
  }
}
private bool myResizableStartAngle = true;

/// <summary>
/// Gets or sets whether to add the resizing handle controlling the end angle.
/// </summary>
/// <value>
/// This defaults to true.
/// </value>
[Category("Behavior"), DefaultValue(true)]
[Description("Whether users can resize the end angle of this resizable object.")]
public virtual bool ResizableEndAngle {
  get { return myResizableEndAngle; }
  set {
    bool old = myResizableEndAngle;
    if (old != value) {
      myResizableEndAngle = value;
      Changed(ChangedResizableEndAngle, 0, old, NullRect, 0, value, NullRect);
    }
  }
}
private bool myResizableEndAngle = true;

/// <summary>
/// Support allowing the user to move the angle control handles.
/// </summary>
/// <param name="view"></param>
/// <param name="origRect"></param>
/// <param name="newPoint"></param>
/// <param name="whichHandle"></param>
/// <param name="evttype"></param>
/// <param name="min"></param>
/// <param name="max"></param>
public override void DoResize(GoView view, RectangleF origRect, PointF newPoint,
  int whichHandle, GoInputState evttype, SizeF min, SizeF max) {

  if ((whichHandle == StartAngleHandleID || whichHandle == EndAngleHandleID) &&
      (this.ResizesRealtime ||
       evttype == GoInputState.Finish || evttype == GoInputState.Cancel)) {
    if (whichHandle == StartAngleHandleID) {
      RectangleF rect = this.Bounds;
      float Rx = rect.Width/2;
      float Ry = rect.Height/2;
      float Cx = rect.X + Rx;
      float Cy = rect.Y + Ry;
      float ang = GoStroke.GetAngle(newPoint.X-Cx, newPoint.Y-Cy);
      float sweep = this.SweepAngle - (ang-this.StartAngle);
      if (this.SweepAngle >= 0) {
        if (sweep < 0)
          sweep += 360;
      } else {
        if (sweep >= 0)
          sweep -= 360;
      }
      this.SweepAngle = sweep;
      this.StartAngle = ang;
    } else if (whichHandle == EndAngleHandleID) {
      RectangleF rect = this.Bounds;
      float Rx = rect.Width/2;
      float Ry = rect.Height/2;
      float Cx = rect.X + Rx;
      float Cy = rect.Y + Ry;
      float ang = GoStroke.GetAngle(newPoint.X-Cx, newPoint.Y-Cy);
      float sweep = ang-this.StartAngle;
      if (this.SweepAngle >= 0) {
        if (sweep < 0)
          sweep += 360;
      } else {
        if (sweep >= 0)
          sweep -= 360;
      }
      this.SweepAngle = sweep;
    }
  } else {
    base.DoResize(view, origRect, newPoint, whichHandle, evttype, min, max);
  }
}

/// <summary>
/// If <see cref="GoObject.CanReshape"/> is true, supports angle control handles if
/// <see cref="ResizableStartAngle"/> and/or <see cref="ResizableEndAngle"/> are true.
/// </summary>
/// <param name="sel"></param>
/// <param name="selectedObj"></param>
public override void AddSelectionHandles(GoSelection sel, GoObject selectedObj) {
  base.AddSelectionHandles(sel, selectedObj);

  if (CanReshape()) {
    if (this.ResizableStartAngle) {
      PointF handlePoint = GetPointAtAngle(this.StartAngle);
      IGoHandle handle = sel.CreateResizeHandle(this, selectedObj, handlePoint, StartAngleHandleID, true);
      MakeDiamondResizeHandle(handle, Middle);
    }

    if (this.ResizableEndAngle) {
      PointF handlePoint = GetPointAtAngle(this.StartAngle + this.SweepAngle);
      IGoHandle handle =  sel.CreateResizeHandle(this, selectedObj, handlePoint, EndAngleHandleID, true);
      MakeDiamondResizeHandle(handle, Middle);
    }
  }
}

private void MakeDiamondResizeHandle(IGoHandle handle, int spot) {
  GoHandle goh = handle.GoObject as GoHandle;
  if (goh != null) {
    goh.Style = GoHandleStyle.Diamond;
    if (!(goh.SelectedObject is IGoLink))
      goh.Brush = Brushes.Yellow;
    RectangleF bounds = goh.Bounds;
    bounds.Inflate(bounds.Width/6, bounds.Height/6);
    goh.Bounds = bounds;
    goh.CursorName = "move";
  }
}

internal PointF GetPointAtAngle(float ang) {
  RectangleF rect = this.Bounds;
  float Rx = rect.Width/2;
  float Ry = rect.Height/2;
  float Cx = rect.X + Rx;
  float Cy = rect.Y + Ry;
  if (Rx == 0) return new PointF(Cx, Cy);
  float cos1 = (float)Math.Cos(ang/180*Math.PI);
  float e2 = (float)(1 - ((Ry*Ry)/(Rx*Rx)));
  float r1 = (float)(Rx*Math.Sqrt((1-e2)/(1-e2*cos1*cos1)));
  float q1x = r1*cos1;
  PointF q1 = new PointF(Cx + q1x, (float)(Cy + Math.Tan(ang/180*Math.PI)*q1x));
  return q1;
}

/// <summary>
/// A point is in this object only if it really is inside the section of the ellipse.
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
public override bool ContainsPoint(PointF p) {
  if (!base.ContainsPoint(p))
    return false;

  // normalize
  RectangleF rect = this.Bounds;
  float pw2 = this.PenWidth / 2;
  float a = rect.Width/2;
  float b = rect.Height/2;
  float cx = rect.X + a;
  float cy = rect.Y + b;
  const float MARGIN = 2;
  a += pw2 + MARGIN;
  b += pw2 + MARGIN;
  if (a == 0 || b == 0)
    return false;
  float x = p.X - cx;
  float y = p.Y - cy;
  // outside ellipse
  if ((x*x)/(a*a) + (y*y)/(b*b) > 1)
    return false;
  // inside ellipse
  a = rect.Width/2 - pw2 - MARGIN;
  b = rect.Height/2 - pw2 - MARGIN;
  if (a > 0 && b > 0 && (x*x)/(a*a) + (y*y)/(b*b) < 1)
    return false;

  float pAngle = GoStroke.GetAngle(p.X - cx, p.Y - cy);

  float sa;
  float sw;
  if (this.SweepAngle < 0) {
    sa = StartAngle + SweepAngle;
    sw = -SweepAngle;
  } else {
    sa = StartAngle;
    sw = SweepAngle;
  }

  if (sw > 360)
    return true;
  if (sa + sw > 360) {
    return ((pAngle >= sa) ||
      (pAngle <= (sa + sw - 360)));
  }
  return (pAngle >= sa &&
    pAngle <= (sa + sw));
}

/// <summary>
/// Find the intersection points of an arc and the infinite line p1-p2
/// that is closest to point p1.
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool GetNearestIntersectionPoint(PointF p1, PointF p2, out PointF result) {
  RectangleF rect = this.Bounds;
  float pw2 = this.PenWidth / 2;
  rect.Inflate(pw2, pw2);
  return GoEllipse.NearestIntersectionOnArc(rect, p1, p2, out result, this.StartAngle, this.SweepAngle);
}

/// <summary>
/// Handle this class's property changes for undo and redo
/// </summary>
/// <param name="e"></param>
/// <param name="undo"></param>
public override void ChangeValue(GoChangedEventArgs e, bool undo) {
  switch (e.SubHint) {
    case ChangedStartAngle:
      this.StartAngle = e.GetFloat(undo);
      return;
    case ChangedSweepAngle:
      this.SweepAngle = e.GetFloat(undo);
      return;
    case ChangedResizableStartAngle:
      this.ResizableStartAngle = (bool)e.GetValue(undo);
      return;
    case ChangedResizableEndAngle:
      this.ResizableEndAngle = (bool)e.GetValue(undo);
      return;
    default:
      base.ChangeValue(e, undo);
      return;
  }
}

/// <summary>
/// This is a <see cref="GoObject.Changed"/> subhint identifying changes to the value of the <see cref="StartAngle"/> property.
/// </summary>
public const int ChangedStartAngle = 1471;
/// <summary>
/// This is a <see cref="GoObject.Changed"/> subhint identifying changes to the value of the <see cref="SweepAngle"/> property.
/// </summary>
public const int ChangedSweepAngle = 1472;
/// <summary>
/// This is a <see cref="GoObject.Changed"/> subhint identifying changes to the value of the <see cref="ResizableStartAngle"/> property.
/// </summary>
public const int ChangedResizableStartAngle = 1473;
/// <summary>
/// This is a <see cref="GoObject.Changed"/> subhint identifying changes to the value of the <see cref="ResizableEndAngle"/> property.
/// </summary>
public const int ChangedResizableEndAngle = 1474;
/// <summary>
/// A special handle ID for a handle which controls the start angle of the arc.
/// </summary>
public const int StartAngleHandleID = 1044;
/// <summary>
/// A special handle ID for a handle which controls the end angle of the arc.
/// </summary>
public const int EndAngleHandleID = 1045;

// Arc state
private float myStartAngle;
private float mySweepAngle = 300;

}
}[/code]