public EllipsePieceSegment(FPoint center, float radiusX, float radiusY, float aStart = 0, float aEnd = FloatMath.TAU, CircularDirection dir = CircularDirection.CW) { this.center = center; this.radiusX = radiusX; this.radiusY = radiusY; this.direction = dir; angleStart = aStart; angleEnd = aEnd; if (dir == CircularDirection.CW) { Length = FloatMath.TAU * FloatMath.Sqrt((radiusX * radiusX + radiusY * radiusY) / 2f) * (angleEnd - angleStart) / FloatMath.TAU; Boundings = EllipseHelper.CalculateEllipseSegmentsBoundingBox(center.X, center.Y, radiusX, radiusY, angleStart, angleEnd); } else { // inverted Length = FloatMath.TAU * FloatMath.Sqrt((radiusX * radiusX + radiusY * radiusY) / 2f) * (angleStart - angleEnd) / FloatMath.TAU; Boundings = EllipseHelper.CalculateEllipseSegmentsBoundingBox(center.X, center.Y, radiusX, radiusY, angleEnd, angleStart); } }
public CirclePieceSegment(FPoint center, float radius, float aStart = 0, float aEnd = FloatMath.TAU) { this.center = center; this.radius = radius; if (aStart < aEnd) { direction = CircularDirection.CW; angleStart = aStart; angleEnd = aEnd; } else { direction = CircularDirection.CCW; angleStart = aEnd; angleEnd = aStart; } Length = (2 * FloatMath.PI * radius) * (angleEnd - angleStart) / FloatMath.TAU; directionZero = new Vector2(radius, 0); Boundings = GeometryHelper.CalculateEllipseSegmentsBoundingBox(center.X, center.Y, radius, radius, angleStart, angleEnd); }
public CirclePieceSegment(FPoint center, float radius, float aStart = 0, float aEnd = FloatMath.TAU) { this.center = center; this.radius = radius; if (aStart < aEnd) { direction = CircularDirection.CW; angleStart = aStart; angleEnd = aEnd; } else { direction = CircularDirection.CCW; angleStart = aEnd; angleEnd = aStart; } Length = (2 * FloatMath.PI * radius) * (angleEnd - angleStart) / FloatMath.TAU; directionZero = new Vector2(radius, 0); Boundings = EllipseHelper.CalculateEllipseSegmentsBoundingBox(center.X, center.Y, radius, radius, angleStart, angleEnd); }
public EllipsePieceSegment(FPoint center, float radiusX, float radiusY, float aStart = 0, float aEnd = FloatMath.TAU, CircularDirection dir = CircularDirection.CW) { this.center = center; this.radiusX = radiusX; this.radiusY = radiusY; this.direction = dir; angleStart = aStart; angleEnd = aEnd; if (dir == CircularDirection.CW) { Length = FloatMath.TAU * FloatMath.Sqrt((radiusX * radiusX + radiusY * radiusY) / 2f) * (angleEnd - angleStart) / FloatMath.TAU; Boundings = GeometryHelper.CalculateEllipseSegmentsBoundingBox(center.X, center.Y, radiusX, radiusY, angleStart, angleEnd); } else { // inverted Length = FloatMath.TAU * FloatMath.Sqrt((radiusX * radiusX + radiusY * radiusY) / 2f) * (angleStart - angleEnd) / FloatMath.TAU; Boundings = GeometryHelper.CalculateEllipseSegmentsBoundingBox(center.X, center.Y, radiusX, radiusY, angleEnd, angleStart); } }
public R1CDirection() : base() { circularDirection = new CircularDirection <R1CDirection, R1Point>(); directionHelper = new R1Direction <R1CDirection>(); InitializeAttributes(); }
public R1CDirection(R1Direction <R1CDirection> directionHelper) { this.directionHelper = directionHelper; circularDirection = new CircularDirection <R1CDirection, R1Point>( CreateCanSwitchList(), true, 1); InitializeAttributes(); }
public R1CDirection(R1Point startingPoint, int direction, float directionLength, float directionDivisor, List <float> speedList, List <bool> canSwitchList, int numberOfRepeatations, bool canSwitch, float speed) { circularDirection = new CircularDirection <R1CDirection, R1Point>( canSwitchList, canSwitch, numberOfRepeatations); directionHelper = new R1Direction <R1CDirection>(startingPoint, direction, directionLength, directionDivisor, speedList, speed); InitializeAttributes(); }
/// <summary> /// Finds the coords. of the next item in the <see cref="PackedLayout"/> if it's /// added vertically. /// </summary> /// <param name="x">X coord. of previous node</param> /// <param name="y">Y coord. of previous node</param> /// <param name="verts">The list of all nodes</param> /// <param name="v">The index of the previous node in verts</param> /// <param name="newx">Returns the x coord. of the new node</param> /// <param name="newy">Returns the y coord. of the new node</param> /// <param name="dir">Whether the nodes are arranged Clockwise or Counterclockwise</param> /// <returns>true if a node can be added vertically and still be on the ellipse</returns> private bool nextv(double x, double y, CircularVertex[] verts, int v, out double newx, out double newy, CircularDirection dir) { newx = 0; newy = 0; bool inv = false; //true if it changes sides if (x >= 0 ^ dir == CircularDirection.Clockwise) { // y decreases newy = y - ((verts[v].Height + verts[v + 1].Height) / 2 + ESpacing); if (newy < -Yradius) { // y increases newy = y + ((verts[v].Height + verts[v + 1].Height) / 2 + ESpacing); if (newy > Yradius) { return false; } inv = true; } } else { // y increases newy = y + ((verts[v].Height + verts[v + 1].Height) / 2 + ESpacing); if (newy > Yradius) { // y decreases newy = y - ((verts[v].Height + verts[v + 1].Height) / 2 + ESpacing); if (newy < -Yradius) { return false; } inv = true; } } newx = Math.Sqrt(1 - newy * newy / (Yradius * Yradius)) * ERadius; if (x < 0 ^ inv) { newx = -newx; } if (Math.Abs(x - newx) > (verts[v].Width + verts[v + 1].Width) / 2) { return false; } else return true; }
private Random rand; // Packed layout sometimes alternates between 2 radii. If this happens, a random value is added to stop the cycle. /// <summary> /// Finds the coords. of the next item in the <see cref="PackedLayout"/> if it's /// added horizontally. /// </summary> /// <param name="x">X coord. of previous node</param> /// <param name="y">Y coord. of previous node</param> /// <param name="verts">The list of all nodes</param> /// <param name="v">The index of the previous node in verts</param> /// <param name="newx">Returns the x coord. of the new node</param> /// <param name="newy">Returns the y coord. of the new node</param> /// <param name="dir">Whether the nodes are arranged Clockwise or Counterclockwise</param> /// <returns>true if a node can be added horizontally and still be on the ellipse</returns> private bool nexth(double x, double y, CircularVertex[] verts, int v, out double newx, out double newy, CircularDirection dir) { newx = 0; newy = 0; bool inv = false; //true if it changes sides if (y >= 0 ^ dir == CircularDirection.Clockwise) { // x increases newx = x + ((verts[v].Width + verts[v + 1].Width) / 2 + ESpacing); if (newx > ERadius) { // x decreases newx = x - ((verts[v].Width + verts[v + 1].Width) / 2 + ESpacing); if (newx < -ERadius) { return false; } inv = true; } } else { // x decreases newx = x - ((verts[v].Width + verts[v + 1].Width) / 2 + ESpacing); if (newx < -ERadius) { newx = x + ((verts[v].Width + verts[v + 1].Width) / 2 + ESpacing); if (newx > ERadius) { return false; } inv = true; } } newy = Math.Sqrt(1 - newx * newx / (ERadius * ERadius)) * Yradius; if (y < 0 ^ inv) { newy = -newy; } if (Math.Abs(y - newy) > (verts[v].Height + verts[v + 1].Height) / 2) { return false; } else return true; }
private double correctlastangle = 0; // when the sweep angle < 360, this is the target angle of the last node /// <summary> /// Arranges the items so the spacing between any item and /// an adjacent item is the same, but takes into account the assumption /// that the nodes are rectangular. This one assumes partial sweep, i.e., <see cref="SweepAngle" /> is /// less than 360. /// </summary> /// <param name="vertices">The items to arrange</param> /// <param name="sweep">The range of the angles of the nodes (in radians)</param> /// <param name="start">The angle of the first node</param> /// <param name="dir"> /// Specifies whether the nodes are arranged clockwise or counterclockwise. Other /// values will be assumed to indicate clockwise (this function doesn't do bidirectionals - those /// are achieved by calling this function once for even nodes and again in the other direction for /// odd nodes. /// </param> private void PackedLayoutSemi(List<CircularVertex> vertices, double sweep, double start, CircularDirection dir) { // do layout double x = ERadius * Math.Cos(start * Math.PI / 180); double y = Yradius * Math.Sin(start * Math.PI / 180); CircularVertex[] verts = vertices.ToArray(); for (int v = 0; v < verts.Length; v++) { verts[v].Center = new Point(x, y); if (v == verts.Length - 1) { break; } // avoid index out of bounds double newx; double newy; if (!nexth(x, y, verts, v, out newx, out newy, dir)) { nextv(x, y, verts, v, out newx, out newy, dir); } x = newx; y = newy; } // check for gap, find gap sizes packediters++; if (packediters > 128) { return; } // compute gap CircularVertex last = vertices.ElementAt(vertices.Count - 1); double lastangle = Math.Atan2(y, x); double diff = dir == CircularDirection.Clockwise ? correctlastangle - lastangle : lastangle - correctlastangle; diff = Math.Abs(diff) < Math.Abs(diff - 2 * Math.PI) ? diff : diff - 2 * Math.PI; double gap = diff * (ERadius + Yradius) / 2; //return; // record this vertexarrangement.compare2(gap, verts); // avoid period 2 stuff if (packediters > 36 && packediters % 13 == 0) { ERadius *= (packediters % 2 == 0 ? .75 : 1.1) + rand.NextDouble() * .15; Yradius = ERadius * EAspectRatio; } // change radii accordingly if (Math.Abs(gap) > 2.5) { this.ERadius -= gap / (2 * Math.PI); this.Yradius = ERadius * EAspectRatio; PackedLayoutSemi(vertices, sweep, start, dir); } }
/// <summary> /// Arranges the items so the spacing between any item and /// an adjacent item is the same, but takes into account the assumption /// that the nodes are rectangular. This one assumes 360 degree sweep /// </summary> /// <param name="vertices">The items to arrange</param> /// <param name="sweep">The range of the angles of the nodes (in radians)</param> /// <param name="start">The angle of the first node</param> /// <param name="dir"> /// Specifies whether the nodes are arranged clockwise or counterclockwise. Other /// values will be assumed to indicate clockwise (this function doesn't do bidirectionals - those /// are achieved by calling this function once for even nodes and again in the other direction for /// odd nodes. /// </param> private void PackedLayoutFull(List<CircularVertex> vertices, double sweep, double start, CircularDirection dir) { // do layout double x = ERadius * Math.Cos(start * Math.PI / 180); double y = Yradius * Math.Sin(start * Math.PI / 180); CircularVertex[] verts = vertices.ToArray(); for (int v = 0; v < verts.Length; v++) { verts[v].Center = new Point(x, y); if (v == verts.Length - 1) { break; } // avoid index out of bounds double newx; double newy; // ***** fix problems if (!nexth(x, y, verts, v, out newx, out newy, dir)) { nextv(x, y, verts, v, out newx, out newy, dir); } x = newx; y = newy; } // check for gap, find gap sizes packediters++; if (packediters > 128) { return; } double locfx = verts[0].Center.X; double locfy = verts[0].Center.Y; double loclx = verts[verts.Length - 1].Center.X; double locly = verts[verts.Length - 1].Center.Y; double gapx = Math.Abs(locfx - loclx) - ((verts[0].Width + verts[verts.Length - 1].Width) / 2 + ESpacing); double gapy = Math.Abs(locfy - locly) - ((verts[0].Height + verts[verts.Length - 1].Height) / 2 + ESpacing); // compute gap double gap = 0; if (Math.Abs(gapy) < 2.5) { double hgap = Math.Abs(locfx - loclx); double max = (verts[0].Width + verts[verts.Length - 1].Width) / 2; if (hgap < max) { gap = 0; } gapx = hgap - max; } else { // gapy not 0 if (gapy > 0) { gap = gapy; } else { if (Math.Abs(gapx) < 2.5) { gap = 0; } else gap = gapx; } } // check for excessive overlap bool overlap = false; if (Math.Abs(loclx) > Math.Abs(locly)) { // use x overlap = loclx > 0 ^ locfy > locly; } else { // use y overlap = locly > 0 ^ locfx < loclx; } overlap = dir == CircularDirection.Clockwise ? overlap : !overlap; // if there's an excessive overlap, gap must be <0 if (overlap) { gap = -Math.Abs(gap); gap = Math.Min(gap, -verts[verts.Length - 1].Width); gap = Math.Min(gap, -verts[verts.Length - 1].Height); } // record this vertexarrangement.compare(gap, verts); // avoid period 2 stuff if (packediters > 36 && packediters % 13 == 0) { ERadius *= (packediters % 2 == 0 ? .75 : 1.1) + rand.NextDouble() * .15; Yradius = ERadius * EAspectRatio; } // change radii accordingly if (Math.Abs(gap) > 2.5) { this.ERadius -= gap / (2 * Math.PI); this.Yradius = ERadius * EAspectRatio; PackedLayoutFull(vertices, sweep, start, dir); } }
/// <summary> /// Arranges the items so the spacing between any item and /// an adjacent item is the same, but takes into account the assumption /// that the nodes are rectangular. /// </summary> /// <param name="vertices">The items to arrange</param> /// <param name="sweep">The range of the angles of the nodes (in radians)</param> /// <param name="start">The angle of the first node</param> /// <param name="dir"> /// Specifies whether the nodes are arranged clockwise or counterclockwise. Other /// values will be assumed to indicate clockwise (this function doesn't do bidirectionals - those /// are achieved by calling this function once for even nodes and again in the other direction for /// odd nodes. /// </param> private void PackedLayout(List<CircularVertex> vertices, double sweep, double start, CircularDirection dir) { packediters = 0; vertexarrangement = new VertexArrangement(); // give rand a constant seed value each time so the same result is produced each time rand = new Random(0); if (sweep < 360) { correctlastangle = start + (dir == CircularDirection.Clockwise ? ESweepAngle : -ESweepAngle); while (correctlastangle < 0) { correctlastangle += 360; } correctlastangle %= 360; if (correctlastangle > 180) { correctlastangle -= 360; } correctlastangle *= Math.PI / 180; PackedLayoutSemi(vertices, sweep, start, dir); } else { PackedLayoutFull(vertices, sweep, start, dir); } vertexarrangement.commit(vertices); return; }
/// <summary> /// Arranges the items so the distance between any item and /// an adjacent item is the same /// </summary> /// <param name="vertices">The items to arrange</param> /// <param name="sweep">The range of the angles of the nodes (in radians)</param> /// <param name="start">The angle of the first node</param> /// <param name="dir"> /// Specifies whether the nodes are arranged clockwise or counterclockwise. Other /// values will be assumed to indicate clockwise (this function doesn't do bidirectionals - those /// are achieved by calling this function once for even nodes and again in the other direction for /// odd nodes. /// </param> private void DistanceLayout(List<CircularVertex> vertices, double sweep, double start, CircularDirection dir) { double theta = start * Math.PI / 180; int num = vertices.Count(); for (int i = 0; i < num; i++) { CircularVertex cv = vertices.ElementAt(i); cv.Center = new Point(ERadius * Math.Cos(theta), Yradius * Math.Sin(theta)); cv.ActualAngle = theta * 180 / Math.PI; double thetachange = EllipseAngle( ERadius, Yradius, dir == CircularDirection.Clockwise ? theta : -theta, constdist ); theta += dir == CircularDirection.Clockwise ? thetachange : -thetachange; } }
/// <summary> /// Arranges the items so the spacing between any item and /// an adjacent item is the same /// </summary> /// <param name="vertices">The items to arrange</param> /// <param name="sweep">The range of the angles of the nodes (in radians)</param> /// <param name="start">The angle of the first node</param> /// <param name="dir"> /// Specifies whether the nodes are arranged clockwise or counterclockwise. Other /// values will be assumed to indicate clockwise (this function doesn't do bidirectionals - those /// are achieved by calling this function once for even nodes and again in the other direction for /// odd nodes. /// </param> private void SpacingLayout(List<CircularVertex> vertices, double sweep, double start, CircularDirection dir) { double theta = start * Math.PI / 180; int num = vertices.Count(); for (int i = 0; i < num; i++) { CircularVertex curr = vertices.ElementAt(i); CircularVertex next = vertices.ElementAt(i == num - 1 ? 0 : i + 1); double x = ERadius * Math.Cos(theta); double y = Yradius * Math.Sin(theta); curr.Center = new Point(x, y); curr.ActualAngle = theta * 180 / Math.PI; double rad = Math.Sqrt(x * x + y * y); double meandiam = (curr.Diameter + next.Diameter) / 2; double thetachange = EllipseAngle( ERadius, Yradius, dir == CircularDirection.Clockwise ? theta : -theta, meandiam + ESpacing ); theta += dir == CircularDirection.Clockwise ? thetachange : -thetachange; } }
/// <summary> /// Arranges the items so the angle between any item and /// an adjacent item is the same /// </summary> /// <param name="vertices">The items to arrange</param> /// <param name="sweep">The range of the angles of the nodes (in radians)</param> /// <param name="start">The angle of the first node</param> /// <param name="dir"> /// Specifies whether the nodes are arranged clockwise or counterclockwise. Other /// values will be assumed to indicate clockwise (this function doesn't do bidirectionals - those /// are achieved by calling this function once for even nodes and again in the other direction for /// odd nodes. /// </param> private void AngleLayout(List<CircularVertex> vertices, double sweep, double start, CircularDirection dir) { double theta0 = start * Math.PI / 180; double _SweepingAngle = sweep * Math.PI / 180; //double e = ERadius > Yradius ? Math.Sqrt(ERadius * ERadius - Yradius * Yradius) / ERadius : Math.Sqrt(Yradius * Yradius - ERadius * ERadius) / Yradius; int num = vertices.Count(); for (int i = 0; i < num; i++) { double theta = theta0 + (dir == CircularDirection.Clockwise ? (i * _SweepingAngle) / (ESweepAngle >= 360 ? num : num - 1) : -(i * _SweepingAngle) / num); CircularVertex cv = vertices.ElementAt(i); double w = ERadius * Math.Tan(theta) / Yradius; double r = Math.Sqrt((ERadius * ERadius + Yradius * Yradius * w * w) / (1 + w * w)); cv.Center = new Point(r * Math.Cos(theta), r * Math.Sin(theta)); cv.ActualAngle = theta * 180 / Math.PI; } }