using System;
using System .Collections .Generic;
using System .ComponentModel;
using System .Drawing;
using System .Drawing .Drawing2D;
using System .Windows .Forms;
using System .IO;
using Microsoft .Win32;
using MoveGraphLibrary;
namespace UserDrivenApplications
{
public class PrimitiveRing : GraphicalObject
{
int version = 606;
PointF center;
float rOuter;
float rInner;
double [] vals;
List<Color> clrs = new List<Color> (); // one color per each sector
Rotation dirDrawing;
double angle;
double [] sweep;
double compensation;
bool bResize, bRotate;
Color clrBorder;
int nNodesOnOuter;
int nNodesOnInner;
// all these are used only for moving the border between two sectors
int iBorderToMove;
int iSector_Counterclock, iSector_Clockwise;
double min_angle_Resectoring, // clockwise from the moving border
max_angle_Resectoring; // counterclock from the moving border
double two_sectors_sum_values;
static int minInner = 10;
static int minWidth = 15;
int hSmall = 4; // this is half !!! so the same hSmall is going inside and outside
int distanceNeighbours = 10;
// -------------------------------------------------
public PrimitiveRing (PointF ptC, float rOut, float rIn, double angleDegree, double [] fVals)
{
bResize = true;
bRotate = true;
center = ptC;
rInner = Math .Max (rIn, minInner);
rOuter = Math .Max (rOut, rIn + minWidth);
vals = CheckedValues (fVals);
DefaultColors ();
clrBorder = Color .DarkGray;
dirDrawing = Rotation .Clockwise;
angle = Auxi_Convert .DegreeToRadian (angleDegree);
sweep = new double [vals .Length];
SweepAngles ();
NodesOnBorders ();
}
// -------------------------------------------------
public PrimitiveRing (PointF ptC, float rOut, float rIn, Color clr)
: this (ptC, rOut, rIn, 0, new double [] { 5 })
{
clrs [0] = clr;
}
// -------------------------------------------------
public override sealed bool ShowCover
{
get { return (false); }
}
// ------------------------------------------------- NodesOnBorders
private void NodesOnBorders ()
{
if (bResize)
{
nNodesOnOuter = Convert .ToInt32 ((2 * Math .PI * rOuter) / distanceNeighbours);
nNodesOnInner = Convert .ToInt32 ((2 * Math .PI * rInner) / distanceNeighbours);
}
else
{
nNodesOnOuter = 0;
nNodesOnInner = 0;
}
}
// ------------------------------------------------- CheckedValues
private double [] CheckedValues (double [] fVals)
{
if (fVals == null || fVals .Length < 1 || Auxi_Common .SumArray (fVals, true) == 0.0)
{
return (new double [] { 1, 2, 3, 4 });
}
else
{
double [] vs = new double [fVals .Length];
for (int i = 0; i < vs .Length; i++)
{
vs [i] = Math .Abs (fVals [i]);
}
return (vs);
}
}
// ------------------------------------------------- DefaultColors
public void DefaultColors ()
{
clrs .Clear ();
for (int i = 0; i < vals .Length; i++)
{
clrs .Add (Auxi_Colours .ColorPredefined (i + 1));
}
}
// ------------------------------------------------- SmoothColorLine
public void SmoothColorLine (Color clr_0, Color clr_1)
{
clrs = Auxi_Colours .SmoothColorsList (vals .Length, clr_0, clr_1);
}
// ------------------------------------------------- SweepAngles
private void SweepAngles ()
{
double fSum = Auxi_Common .SumArray (vals, false);
for (int i = 0; i < vals .Length; i++)
{
sweep [i] = 2 * Math .PI * vals [i] / fSum;
}
if (dirDrawing == Rotation .Clockwise)
{
for (int i = 0; i < vals .Length; i++)
{
sweep [i] *= -1;
}
}
}
// ------------------------------------------------- SectorFromPointAngle
public int SectorFromPointAngle (Point pt)
{
double angleToPoint = Auxi_Geometry .Line_Angle (center, pt);
int iSector = 0;
double [] angleBorder = new double [sweep .Length + 1];
if (dirDrawing == Rotation .Counterclock)
{
if (angle < 0)
{
angleBorder [0] = angle + 2 * Math .PI;
}
else
{
angleBorder [0] = angle;
}
for (int i = 0; i < sweep .Length; i++)
{
angleBorder [i + 1] = angleBorder [i] + sweep [i];
}
while (angleToPoint < angleBorder [0])
{
angleToPoint += 2 * Math .PI;
}
for (iSector = 0; iSector < sweep .Length; iSector++)
{
if (angleToPoint < angleBorder [iSector + 1])
{
break;
}
}
}
else
{
if (angle > 0)
{
angleBorder [0] = angle - 2 * Math .PI;
}
else
{
angleBorder [0] = angle;
}
for (int i = 0; i < sweep .Length; i++)
{
angleBorder [i + 1] = angleBorder [i] + sweep [i];
}
while (angleToPoint > angleBorder [0])
{
angleToPoint -= 2 * Math .PI;
}
for (iSector = 0; iSector < sweep .Length; iSector++)
{
if (angleToPoint > angleBorder [iSector + 1])
{
break;
}
}
}
iSector = Math .Min (iSector, vals .Length - 1);
return (iSector);
}
// ------------------------------------------------- Center
public PointF Center
{
get { return (center); }
}
// ------------------------------------------------- InnerRadius
public float InnerRadius
{
get { return (rInner); }
}
// ------------------------------------------------- OuterRadius
public float OuterRadius
{
get { return (rOuter); }
}
// ------------------------------------------------- MinimumInnerRadius
static public int MinimumInnerRadius
{
get { return (minInner); }
}
// ------------------------------------------------- MinimumWidth
static public int MinimumWidth
{
get { return (minWidth); }
}
// ------------------------------------------------- RectAround
new public RectangleF RectAround
{
get { return (new RectangleF (center .X - rOuter, center .Y - rOuter, 2 * rOuter, 2 * rOuter)); }
}
// ------------------------------------------------- Angle
public double Angle
{
get { return (angle); }
set
{
angle = Auxi_Common .LimitedRadian (value);
DefineCover ();
}
}
// ------------------------------------------------- SetSectorColor
public void SetSectorColor (int iSector, Color color)
{
if (0 <= iSector && iSector < clrs .Count)
{
clrs [iSector] = color;
}
}
// ------------------------------------------------- DrawingDirection
public Rotation DrawingDirection
{
get { return (dirDrawing); }
set
{
dirDrawing = value;
SweepAngles ();
}
}
// ------------------------------------------------- ChangeDrawingDirection
public void ChangeDrawingDirection ()
{
dirDrawing = (dirDrawing == Rotation .Clockwise) ? Rotation .Counterclock : Rotation .Clockwise;
SweepAngles ();
}
// ------------------------------------------------- Values
public double [] Values
{
get { return (vals); }
}
// ------------------------------------------------- Colors
public List<Color> Colors
{
get { return (clrs); }
}
// ------------------------------------------------- Draw
public void Draw (Graphics grfx)
{
Rectangle rcIn = new Rectangle (Convert .ToInt32 (center .X - rInner), Convert .ToInt32 (center .Y - rInner),
Convert .ToInt32 (2 * rInner), Convert .ToInt32 (2 * rInner));
Rectangle rcOut = new Rectangle (Convert .ToInt32 (center .X - rOuter), Convert .ToInt32 (center .Y - rOuter),
Convert .ToInt32 (2 * rOuter), Convert .ToInt32 (2 * rOuter));
Pen penBorder = new Pen (clrBorder);
Point pt0, pt1;
SolidBrush brush;
GraphicsPath path = new GraphicsPath ();
// for Drawing fStart[] and fSweep[] are used in Microsoft way, which means changing of sign
float fStartDegree, fSweepDegree;
fStartDegree = -(float) Auxi_Convert .RadianToDegree (angle);
for (int i = 0; i < vals .Length; i++)
{
brush = new SolidBrush (clrs [i]);
fSweepDegree = -(float) Auxi_Convert .RadianToDegree (sweep [i]);
path .AddArc (rcIn, fStartDegree, fSweepDegree);
path .AddArc (rcOut, fStartDegree + fSweepDegree, -fSweepDegree);
grfx .FillPath (brush, path);
path .Reset ();
grfx .DrawArc (penBorder, rcIn, fStartDegree, fSweepDegree);
grfx .DrawArc (penBorder, rcOut, fStartDegree, fSweepDegree);
pt0 = Auxi_Geometry .EllipsePoint (rcIn, -fStartDegree, 1);
pt1 = Auxi_Geometry .EllipsePoint (rcOut, -fStartDegree, 1);
grfx .DrawLine (penBorder, pt0, pt1);
fStartDegree += fSweepDegree;
}
}
// ------------------------------------------------- StartRotation
public void StartRotation (Point ptMouse)
{
double angleMouse = Auxi_Geometry .Line_Angle (center, ptMouse);
compensation = Auxi_Common .LimitedRadian (angleMouse - angle);
}
// ------------------------------------------------- StartRotation
public void SectorsUnderChange (out int iCounterClock, out int iClockwise)
{
iCounterClock = iSector_Counterclock;
iClockwise = iSector_Clockwise;
}
// ------------------------------------------------- StartResectoring
public void StartResectoring (int iNode)
{
iBorderToMove = iNode - (nNodesOnOuter + nNodesOnInner);
double angleCaughtBorder = angle;
for (int i = 0; i < iBorderToMove; i++)
{
angleCaughtBorder += sweep [i];
}
if (dirDrawing == Rotation .Clockwise)
{
iSector_Clockwise = iBorderToMove;
min_angle_Resectoring = angleCaughtBorder + sweep [iSector_Clockwise];
iSector_Counterclock = (iSector_Clockwise == 0) ? (vals .Length - 1) : (iSector_Clockwise - 1);
max_angle_Resectoring = angleCaughtBorder - sweep [iSector_Counterclock];
}
else
{
iSector_Counterclock = iBorderToMove;
max_angle_Resectoring = angleCaughtBorder + sweep [iSector_Counterclock];
iSector_Clockwise = (iSector_Counterclock == 0) ? (vals .Length - 1) : (iSector_Counterclock - 1);
min_angle_Resectoring = angleCaughtBorder - sweep [iSector_Clockwise];
}
two_sectors_sum_values = vals [iSector_Clockwise] + vals [iSector_Counterclock];
}
// ------------------------------------------------- RedefineCover
public void RedefineCover ()
{
if (bResize)
{
NodesOnBorders ();
DefineCover ();
}
}
// ------------------------------------------------- DefineCover
public override void DefineCover ()
{
PointF [] pts = new PointF [4];
CoverNode [] nodes;
if (bResize)
{
// order of nodes: outer border - inner border
// - strips on sectors' borders - inner circle (Transparent) - outer circle
nodes = new CoverNode [nNodesOnOuter + nNodesOnInner + vals .Length + 2];
float rBelow, rAbove;
rBelow = rOuter - hSmall;
rAbove = rOuter + hSmall;
pts [0] = Auxi_Geometry .PointToPoint (center, 0, rBelow);
pts [1] = Auxi_Geometry .PointToPoint (center, 0, rAbove);
for (int i = 0; i < nNodesOnOuter; i++) // nodes on outer border
{
pts [2] = Auxi_Geometry .PointToPoint (center, 2 * Math .PI * (i + 1) / nNodesOnOuter, rAbove);
pts [3] = Auxi_Geometry .PointToPoint (center, 2 * Math .PI * (i + 1) / nNodesOnOuter, rBelow);
nodes [i] = new CoverNode (i, pts, Cursors .Hand);
pts [0] = pts [3];
pts [1] = pts [2];
}
rBelow = rInner - hSmall;
rAbove = rInner + hSmall;
pts [0] = Auxi_Geometry .PointToPoint (center, 0, rBelow);
pts [1] = Auxi_Geometry .PointToPoint (center, 0, rAbove);
for (int i = 0; i < nNodesOnInner; i++) // nodes on inner border
{
pts [2] = Auxi_Geometry .PointToPoint (center, 2 * Math .PI * (i + 1) / nNodesOnInner, rAbove);
pts [3] = Auxi_Geometry .PointToPoint (center, 2 * Math .PI * (i + 1) / nNodesOnInner, rBelow);
nodes [nNodesOnOuter + i] = new CoverNode (nNodesOnOuter + i, pts, Cursors .Hand);
pts [0] = pts [3];
pts [1] = pts [2];
}
int nSmallNodes = nNodesOnOuter + nNodesOnInner;
PointF ptIn, ptOut;
double angleForLine = angle;
for (int i = 0; i < vals .Length; i++) // nodes on borders between sectors
{
ptIn = Auxi_Geometry .PointToPoint (center, angleForLine, rInner);
ptOut = Auxi_Geometry .PointToPoint (center, angleForLine, rOuter);
nodes [nSmallNodes + i] = new CoverNode (nSmallNodes + i, ptIn, ptOut);
angleForLine += sweep [i];
}
int j = nodes .Length - 2;
nodes [j] = new CoverNode (j, center, rInner, MovementFreedom .Transparent);
nodes [j + 1] = new CoverNode (j + 1, center, rOuter, Cursors .SizeAll);
}
else
{
nodes = new CoverNode [2];
nodes [0] = new CoverNode (0, center, rInner, MovementFreedom .Transparent);
nodes [1] = new CoverNode (1, center, rOuter, Cursors .SizeAll);
}
cover = new Cover (nodes);
}
// -------------------------------------------------
public override void Move (int dx, int dy)
{
center += new Size (dx, dy);
}
// ------------------------------------------------- MoveNode
public override bool MoveNode (int i, int dx, int dy, Point ptM, MouseButtons catcher)
{
bool bRet = false;
if (catcher == MouseButtons .Left)
{
if (bResize)
{
if (i == cover .NodesCount - 1)
{
Move (dx, dy);
}
else if (i < nNodesOnOuter + nNodesOnInner)
{
// small node on either outer or inner border
if (i < nNodesOnOuter)
{
int newOuter = Convert .ToInt32 (Auxi_Geometry .Distance (center, ptM));
if (rInner + minWidth <= newOuter)
{
rOuter = newOuter;
bRet = true;
}
}
else
{
int newInner = Convert .ToInt32 (Auxi_Geometry .Distance (center, ptM));
if (minInner <= newInner && newInner <= rOuter - minWidth)
{
rInner = newInner;
bRet = true;
}
}
}
else
{
// border between two sectors
double angleMouse = Auxi_Geometry .Line_Angle (center, ptM);
if (angleMouse > max_angle_Resectoring)
{
angleMouse -= 2 * Math .PI;
}
else if (angleMouse < min_angle_Resectoring)
{
angleMouse += 2 * Math .PI;
}
if (min_angle_Resectoring + 0.05 < angleMouse && angleMouse < max_angle_Resectoring - 0.05)
{
double part_Counterclock = (max_angle_Resectoring - angleMouse) / (max_angle_Resectoring - min_angle_Resectoring);
if (iBorderToMove == 0)
{
angle = angleMouse;
}
vals [iSector_Counterclock] = two_sectors_sum_values * part_Counterclock;
vals [iSector_Clockwise] = two_sectors_sum_values - vals [iSector_Counterclock];
SweepAngles ();
}
}
}
else
{
Move (dx, dy);
}
}
else if (catcher == MouseButtons .Right && bRotate)
{
double angleMouse = Auxi_Geometry .Line_Angle (center, ptM);
angle = angleMouse - compensation;
bRet = true;
}
return (bRet);
}
const string nameMain = "PrimRing_";
const string nameValues = "PrimRingValues_";
const string nameColors = "PrimRingClrs_";
// ------------------------------------------------- IntoRegistry
public void IntoRegistry (RegistryKey regkey, string strB)
{
try
{
regkey .SetValue (nameMain + strB, new string [] {version .ToString (), // 0
id .ToString (), // 1
center .X .ToString (), // 2
center .Y .ToString (), // 3
rOuter .ToString (), // 4
rInner .ToString (), // 5
angle .ToString (), // 6
((int) DrawingDirection) .ToString (), // 7
},
RegistryValueKind .MultiString);
string [] strVal = new string [vals .Length];
for (int i = 0; i < vals .Length; i++)
{
strVal [i] = vals [i] .ToString ();
}
regkey .SetValue (nameValues + strB, strVal, RegistryValueKind .MultiString);
string [] strClrs = new string [clrs .Count * 4];
for (int i = 0; i < clrs .Count; i++)
{
strClrs [i * 4] = ((int) (clrs [i] .A)) .ToString ();
strClrs [i * 4 + 1] = ((int) (clrs [i] .R)) .ToString ();
strClrs [i * 4 + 2] = ((int) (clrs [i] .G)) .ToString ();
strClrs [i * 4 + 3] = ((int) (clrs [i] .B)) .ToString ();
}
regkey .SetValue (nameColors + strB, strClrs, RegistryValueKind .MultiString);
}
catch
{
}
finally
{
}
}
// ------------------------------------------------- FromRegistry
public static PrimitiveRing FromRegistry (RegistryKey regkey, string strB)
{
try
{
string [] strMain = (string []) regkey .GetValue (nameMain + strB);
string [] strVal = (string []) regkey .GetValue (nameValues + strB);
if (strMain == null ||
strMain .Length != 8 ||
Convert .ToInt32 (strMain [0]) != 606 ||
strVal == null ||
strVal .Length < 2)
{
return (null);
}
//int nVer = Convert .ToInt32 (strGeneral [0]);
double [] fVals = new double [strVal .Length];
for (int i = 0; i < strVal .Length; i++)
{
fVals [i] = Convert .ToDouble (strVal [i]);
}
PrimitiveRing ring = new PrimitiveRing (Auxi_Convert .ToPointF (strMain, 2),
Convert .ToSingle (strMain [4]),
Convert .ToSingle (strMain [5]),
Auxi_Convert .RadianToDegree (Convert .ToDouble (strMain [6])),
fVals);
if (ring == null)
{
return (null);
}
ring .ID = Convert .ToInt64 (strMain [1]);
ring .DrawingDirection = (Rotation) (Convert .ToInt32 (strMain [7]));
string [] strClrs = (string []) regkey .GetValue (nameColors + strB);
if (fVals .Length == strClrs .Length / 4)
{
for (int i = 0; i < fVals .Length; i++)
{
ring .SetSectorColor (i, Auxi_Convert .ToColor (strClrs, i * 4));
}
}
return (ring);
}
catch
{
return (null);
}
finally
{
}
}
}
}