Related
I am implementing radial layout drawing algorithm, according to the publication of mr.Andy Pavlo link [page 18]
The problem is, that my result contains crossed edges. Which is something that is unacceptable. I found some solution, similiar problem link but I was not able to implement them into this algorithm (I would have to change the whole approach to the solution). In addition, the algorithm by Mr. Andy Pavlo should be able to solve this problem. When we look at the result of its algorithm, there are no crossed edges here. What am I doing wrong? Am I missing something? Thank you in advance.
Mr.Pavlo pseudo code of algorithm
My implementation of algorithm
public void RadialPositions(Tree<string> rootedTree, Node<string> vertex, double alfa, double beta,
List<RadialPoint<string>> outputGraph)
{
//check if vertex is root of rootedTree
if (vertex.IsRoot)
{
vertex.Point.X = 0;
vertex.Point.Y = 0;
outputGraph.Add(new RadialPoint<string>
{
Node = vertex,
Point = new Point
{
X = 0,
Y = 0
},
ParentPoint = null
});
}
//Depth of vertex starting from 0
int depthOfVertex = vertex.Depth;
double theta = alfa;
double radius = Constants.CircleRadius + (Constants.Delta * depthOfVertex);
//Leaves number in the subtree rooted at v
int leavesNumber = BFS.BreatFirstSearch(vertex);
foreach (var child in vertex.Children)
{
//Leaves number in the subtree rooted at child
int lambda = BFS.BreatFirstSearch(child);
double mi = theta + ((double)lambda / leavesNumber * (beta - alfa));
double x = radius * Math.Cos((theta + mi) / 2.0);
double y = radius * Math.Sin((theta + mi) / 2.0);
//setting x and y
child.Point.X = x;
child.Point.Y = y;
outputGraph.Add(new RadialPoint<string>
{
Node = child,
Point = new Point
{
X = x,
Y = y,
Radius = radius
},
ParentPoint = vertex.Point
});
if (child.Children.Count > 0)
{
child.Point.Y = y;
child.Point.X = x;
RadialPositions(rootedTree, child, theta, mi, outputGraph);
}
theta = mi;
}
}
BFS algorithm for getting leaves
public static int BreatFirstSearch<T>(Node<T> root)
{
var visited = new List<Node<T>>();
var queue = new Queue<Node<T>>();
int leaves = 0;
visited.Add(root);
queue.Enqueue(root);
while (queue.Count != 0)
{
var current = queue.Dequeue();
if (current.Children.Count == 0)
leaves++;
foreach (var node in current.Children)
{
if (!visited.Contains(node))
{
visited.Add(node);
queue.Enqueue(node);
}
}
}
return leaves;
}
Initial call
var outputPoints = new List<RadialPoint<string>>();
alg.RadialPositions(tree, tree.Root,0, 360, outputPoints);
mr.Pavlo result
My result on simple sample
Math.Cos and Sin expect the input angle to be in radians, not degrees. In your initial method call, your upper angle limit (beta) should be 2 * Math.PI, not 360. This will ensure that all the angles you calculate will be in radians and not degrees.
* Update *
Found a solution using Clipper library. Solution added as answer. New / better / easier ideas are still welcome though!
Given a path like this:
I want to create a path surrounding this path with a given distance, e.g. 1 cm. The following sketch demonstrates that - the red path surrounds the black path with a distance of 1 cm.
How can this be done in a generic way using PDFSharp? (Meaning I want to finally draw it with PDFSharp, I don't care where the calculations are done)
Here is the code for the black path:
// helper for easily getting an XPoint in centimeters
private XPoint cmPoint(double x, double y)
{
return new XPoint(
XUnit.FromCentimeter(x),
XUnit.FromCentimeter(y)
);
}
// the path to be drawn
private XGraphicsPath getMyPath()
{
XGraphicsPath path = new XGraphicsPath();
XPoint[] points = new XPoint[3];
points[0] = cmPoint(0, 0);
points[1] = cmPoint(5, 2);
points[2] = cmPoint(10,0);
path.AddCurve(points);
path.AddLine(cmPoint(10, 0), cmPoint(10, 10));
path.AddLine(cmPoint(10, 10), cmPoint(0, 10));
path.CloseFigure();
return path;
}
// generate the PDF file
private void button3_Click(object sender, RoutedEventArgs e)
{
// Create a temporary file
string filename = String.Format("{0}_tempfile.pdf", Guid.NewGuid().ToString("D").ToUpper());
XPen penBlack = new XPen(XColors.Black, 1);
XPen penRed = new XPen(XColors.Red, 1);
PdfDocument pdfDocument = new PdfDocument();
PdfPage page = pdfDocument.AddPage();
page.Size = PdfSharp.PageSize.A1;
XGraphics gfx = XGraphics.FromPdfPage(page);
//give us some space to the left and top
gfx.TranslateTransform(XUnit.FromCentimeter(3), XUnit.FromCentimeter(3));
// draw the desired path
gfx.DrawPath(penBlack, getMyPath());
// Save the pdfDocument...
pdfDocument.Save(filename);
// ...and start a viewer
Process.Start(filename);
}
Thanks for any help on this topic!
You can use Widen() function, which replaces the path with curves that enclose the area that is filled when the path is drawn by a specified pen, adding an additional outline to the path.
This function receives as parameter a XPen, so you can create this XPen using the desired offset as width and an outer path will be added at a constant distance (pen's width).
XGraphicsPath class is in fact a wrapper of System.Drawing.Drawing2D.GraphicsPath, so you can use Widen() function in XGraphicsPath, get the internal object and iterate on it using GraphicsPathIterator class to get the path added.
This method will do the job:
public XGraphicsPath GetSurroundPath(XGraphicsPath path, double width)
{
XGraphicsPath container = new XGraphicsPath();
container.StartFigure();
container.AddPath(path, false);
container.CloseFigure();
var penOffset = new XPen(XColors.Black, width);
container.StartFigure();
container.Widen(penOffset);
container.CloseFigure();
var iterator = new GraphicsPathIterator(container.Internals.GdiPath);
bool isClosed;
var outline = new XGraphicsPath();
iterator.NextSubpath(outline.Internals.GdiPath, out isClosed);
return outline;
}
You can handle level of flatness in curves using the overload Widen(XPen pen, XMatrix matrix, double flatness). Doing this call container.Widen(penOffset, XMatrix.Identity, 0.05); results in more rounded edges.
Then draw an outer path using this function:
string filename = String.Format("{0}_tempfile.pdf", Guid.NewGuid().ToString("D").ToUpper());
XPen penBlack = new XPen(XColors.Black, 1);
XPen penRed = new XPen(XColors.Red, 1);
PdfDocument pdfDocument = new PdfDocument();
PdfPage page = pdfDocument.AddPage();
page.Size = PdfSharp.PageSize.A1;
XGraphics gfx = XGraphics.FromPdfPage(page);
//give us some space to the left and top
gfx.TranslateTransform(XUnit.FromCentimeter(3), XUnit.FromCentimeter(3));
var path = getMyPath();
// draw the desired path
gfx.DrawPath(penBlack, path);
gfx.DrawPath(penRed, GetSurroundPath(path, XUnit.FromCentimeter(1).Point));
// Save the pdfDocument...
pdfDocument.Save(filename);
// ...and start a viewer
Process.Start(filename);
This is what you get:
Another way may be using reflection to retrieve internal Pen in XPen and setup CompoundArray property. This allows you draws parallel lines and spaces. Using this property you can do something like this:
But the problem is that you can only use one color, anyway this is just an idea, I have not tried in PDFsharp
Also, you should search for offset polyline curves or offsetting polygon algorithms.
This can be done using Clipper
double scale = 1024.0;
List<IntPoint> points = new List<IntPoint>();
points.Add(new IntPoint(0*scale, 0*scale));
points.Add(new IntPoint(5*scale, 2*scale));
points.Add(new IntPoint(10*scale, 0*scale));
points.Add(new IntPoint(10*scale, 10*scale));
points.Add(new IntPoint(0*scale, 10*scale));
points.Reverse();
List<List<IntPoint>> solution = new List<List<IntPoint>>();
ClipperOffset co = new ClipperOffset();
co.AddPath(points, JoinType.jtMiter, EndType.etClosedPolygon);
co.Execute(ref solution, 1 * scale);
foreach (IntPoint point in solution[0])
{
Console.WriteLine("OUTPUT: " + point.X + "/" + point.Y + " -> " + point.X/scale + "/" + point.Y/scale);
}
And the output:
OUTPUT: 11264/11264 -> 11/11
OUTPUT: -1024/11264 -> -1/11
OUTPUT: -1024/-1512 -> -1/-1,4765625
OUTPUT: 5120/945 -> 5/0,9228515625
OUTPUT: 11264/-1512 -> 11/-1,4765625
Drawn original and offset path:
This is still not perfect for various mathematical reasons, but already quite good.
This is an updated Answer requested
The XGraphicPath is sealed class which was implemented with bad practices IMO, so the only way is to use a wrapper around it. I tried to make the code as self documented as possible
public class OGraphicPath
{
private readonly ICollection<XPoint[]> _curves;
private readonly ICollection<Tuple<XPoint, XPoint>> _lines;
public OGraphicPath()
{
_lines = new List<Tuple<XPoint, XPoint>>();
_curves = new List<XPoint[]>();
}
public XGraphicsPath XGraphicsPath
{
get
{
var path = new XGraphicsPath();
foreach (var curve in _curves)
{
path.AddCurve(curve);
}
foreach (var line in _lines)
{
path.AddLine(line.Item1, line.Item2);
}
path.CloseFigure();
return path;
}
}
public void AddCurve(XPoint[] points)
{
_curves.Add(points);
}
public void AddLine(XPoint point1, XPoint point2)
{
_lines.Add(new Tuple<XPoint, XPoint>(point1, point2));
}
// Finds Highest and lowest X and Y to find the Center O(x,y)
private XPoint FindO()
{
var xs = new List<double>();
var ys = new List<double>();
foreach (var point in _curves.SelectMany(points => points))
{
xs.Add(point.X);
ys.Add(point.Y);
}
foreach (var line in _lines)
{
xs.Add(line.Item1.X);
xs.Add(line.Item2.X);
ys.Add(line.Item1.Y);
ys.Add(line.Item2.Y);
}
var OX = xs.Min() + xs.Max()/2;
var OY = ys.Min() + ys.Max()/2;
return new XPoint(OX, OY);
}
// If a point is above O, it's surrounded point is even higher, if it's below O, it's surrunded point is below O too...
private double FindPlace(double p, double o, double distance)
{
var dp = p - o;
if (dp < 0)
{
return p - distance;
}
if (dp > 0)
{
return p + distance;
}
return p;
}
public XGraphicsPath Surrond(double distance)
{
var path = new XGraphicsPath();
var O = FindO();
foreach (var curve in _curves)
{
var points = new XPoint[curve.Length];
for (var i = 0; i < curve.Length; i++)
{
var point = curve[i];
var x = FindPlace(point.X, O.X, distance);
var y = FindPlace(point.Y, O.Y, distance);
points[i] = new XPoint(x, y);
}
path.AddCurve(points);
}
foreach (var line in _lines)
{
var ax = FindPlace(line.Item1.X, O.X, distance);
var ay = FindPlace(line.Item1.Y, O.Y, distance);
var a = new XPoint(ax, ay);
var bx = FindPlace(line.Item2.X, O.X, distance);
var by = FindPlace(line.Item2.Y, O.Y, distance);
var b = new XPoint(bx, by);
path.AddLine(a, b);
}
path.CloseFigure();
return path;
}
}
And is Consumed Like this
// draw the desired path
var path = getMyPath();
gfx.DrawPath(penBlack, path.XGraphicsPath);
gfx.DrawPath(penRed, path.Surrond(XUnit.FromCentimeter(1)));
What if we made a "DrawOutline" extension to xGraphics?
public static class XGraphicsExtentions
{
public static void DrawOutline(this XGraphics gfx, XPen pen, XGraphicsPath path, int offset)
{
// finding the size of the original path so that we know how much to scale it in x and y
var points = path.Internals.GdiPath.PathPoints;
float minX, minY;
float maxX, maxY;
GetMinMaxValues(points, out minX, out minY, out maxX, out maxY);
var deltaY = XUnit.FromPoint(maxY - minY);
var deltaX = XUnit.FromPoint(maxX - minX);
var offsetInPoints = XUnit.FromCentimeter(offset);
var scaleX = XUnit.FromPoint((deltaX + offsetInPoints)/deltaX);
var scaleY = XUnit.FromPoint((deltaY + offsetInPoints)/deltaY);
var transform = -offsetInPoints/2.0;
gfx.TranslateTransform(transform, transform);
gfx.ScaleTransform(scaleX, scaleY);
gfx.DrawPath(pen, path);
// revert changes to graphics object before exiting
gfx.ScaleTransform(1/scaleX,1/scaleY);
gfx.TranslateTransform(-transform, -transform);
}
private static void GetMinMaxValues(PointF[] points, out float minX, out float minY, out float maxX, out float maxY)
{
minX = float.MaxValue;
maxX = float.MinValue;
minY = float.MaxValue;
maxY = float.MinValue;
foreach (var point in points)
{
if (point.X < minX)
minX = point.X;
if (point.X > maxX)
maxX = point.X;
if (point.Y < minY)
minY = point.Y;
if (point.Y > maxY)
maxY = point.Y;
}
}
}
Usage:
// draw the desired path
gfx.DrawPath(penBlack, getMyPath());
gfx.DrawOutline(penRed, getMyPath(), 2);
Result:
Clipper is a great choice, but depending on your needs it will not result in a perfect offset.
offset from edge is not equal to offset from corner
A better solution, which will require you to remove any beizer curves and only use line primitives, is using CGAL library for contour offsets: http://doc.cgal.org/latest/Straight_skeleton_2/index.html
Another way of doing it, which is actually pretty cool (albeit taking a lot of memory), is to convert your path to a bitmap and then apply a dilate operation, https://en.wikipedia.org/wiki/Dilation_(morphology). This will give you a correct transformation, but in the bitmap resolution.
You can the convert the bitmap to vector graphics, using a tool like https://en.wikipedia.org/wiki/Potrace
A good image toolbox is OpenCV, and http://www.emgu.com/wiki/index.php/Main_Page for .NET/C#. It includes dilation.
This will give you a somewhat limited resolution approach, but the end result will be precise to the bitmap resolution (and actually a lot higher since you are using a contour offset that, in fact, is limiting the offsetted contour details).
try this:
public Lis<Point> Draw(Point[] points /*Current polygon*/, int distance /*distance to new polygon*/) {
List<Point> lResult = new List<Point>();
foreach(Point lPoint in points) {
Point lNewPoint = new Point(lPoint.X - distance, lPoint.Y);
if(!CheckCurrentPoint(lNewPoint, points)) {
lResult.Add(lNewPoint)
continue;
}
lNewPoint = new Point(lPoint.X + distance, lPoint.Y);
if(!CheckCurrentPoint(lNewPoint, points)) {
lResult.Add(lNewPoint)
continue;
}
lNewPoint = new Point(lPoint.X, lPoint.Y - distance);
if(!CheckCurrentPoint(lNewPoint, points)) {
lResult.Add(lNewPoint)
continue;
}
lNewPoint = new Point(lPoint.X, lPoint.Y + distance);
if(!CheckCurrentPoint(lNewPoint, points)) {
lResult.Add(lNewPoint)
continue;
}
}
return lResult; // Points of new polygon
}
private static int Crs(Point a1, Point a2, Point p, ref bool ans) {
const double e = 0.00000000000001;
int lCrsResult = 0;
if (Math.Abs(a1.Y - a2.Y) < e)
if ((Math.Abs(p.Y - a1.Y) < e) && ((p.X - a1.X) * (p.X - a2.X) < 0.0))
ans = false;
if ((a1.Y - p.Y) * (a2.Y - p.Y) > 0.0)
return lCrsResult;
double lX = a2.X - (a2.Y - p.Y) / (a2.Y - a1.Y) * (a2.X - a1.X);
if (Math.Abs(lX - p.X) < e)
ans = false;
else if (lX < p.X) {
lCrsResult = 1;
if ((Math.Abs(a1.Y - p.Y) < e) && (a1.Y < a2.Y))
lCrsResult = 0;
else if ((Math.Abs(a2.Y - p.Y) < e) && (a2.Y < a1.Y))
lCrsResult = 0;
}
return lCrsResult;
}
private static bool CheckCurrentPoint(Point p /*Point of new posible polygon*/, Points[] points /*points of current polygon*/) {
if (points.Count == 0)
return false;
int lC = 0;
bool lAns = true;
for (int lIndex = 1; lIndex < points.Count; lIndex++) {
lC += Crs(points[lIndex - 1], points[lIndex], p, ref lAns);
if (!lAns)
return false;
}
lC += Crs(points[points.Count - 1], points[0], p, ref lAns);
if (!lAns)
return false;
return (lC & 1) > 0;
}
From mentioned sample in comments
I am calculating center Lat/Lng fron list of available Lat/Lng using C# and rendering on OpenLayer Map.
I observed the calculation of getting center lat/lng will give me slightly difference in lat/lng. I am referring this link for the calculation
Calculate the center point of multiple latitude/longitude coordinate pairs.
C# Code:
static void Main(string[] args)
{
List<GeoCoordinate> listCoordinate = new List<GeoCoordinate>();
listCoordinate.Add(new GeoCoordinate() { Latitude = 22.9833, Longitude = 72.5000 }); //Sarkhej
listCoordinate.Add(new GeoCoordinate() { Latitude = 18.9750, Longitude = 72.8258 }); //Mumbai
listCoordinate.Add(new GeoCoordinate() { Latitude = 22.3000, Longitude = 73.2003 }); //Vadodara
listCoordinate.Add(new GeoCoordinate() { Latitude = 26.9260, Longitude = 75.8235 }); //Jaipur
listCoordinate.Add(new GeoCoordinate() { Latitude = 28.6100, Longitude = 77.2300 }); //Delhi
listCoordinate.Add(new GeoCoordinate() { Latitude = 22.3000, Longitude = 70.7833 }); //Rajkot
GeoCoordinate centerCoordinate = GetCentralGeoCoordinate(listCoordinate); //Output (Latitude:23.696708071960074, Longitude:73.681549202080149)
Console.WriteLine("Lat:" + centerCoordinate.Latitude + ",Lon:" + centerCoordinate.Longitude);
Console.ReadKey();
}
public static GeoCoordinate GetCentralGeoCoordinate(List<GeoCoordinate> geoCoordinates)
{
if (geoCoordinates.Count == 1)
{
return geoCoordinates.Single();
}
double x = 0, y = 0, z = 0;
foreach (var geoCoordinate in geoCoordinates)
{
var latitude = geoCoordinate.Latitude * Math.PI / 180;
var longitude = geoCoordinate.Longitude * Math.PI / 180;
x += Math.Cos(latitude) * Math.Cos(longitude);
y += Math.Cos(latitude) * Math.Sin(longitude);
z += Math.Sin(latitude);
}
var total = geoCoordinates.Count;
x = x / total;
y = y / total;
z = z / total;
var centralLongitude = Math.Atan2(y, x);
var centralSquareRoot = Math.Sqrt(x * x + y * y);
var centralLatitude = Math.Atan2(z, centralSquareRoot);
return new GeoCoordinate(centralLatitude * 180 / Math.PI, centralLongitude * 180 / Math.PI);
}
Javascrip Code:
var arrLonLat = [
{'Lon' : 72.5000, 'Lat' : 22.9833},
{'Lon' : 72.8258, 'Lat' : 18.9750},
{'Lon' : 73.2003, 'Lat' : 22.3000},
{'Lon' : 75.8235, 'Lat' : 26.9260},
{'Lon' : 77.2300, 'Lat' : 28.6100},
{'Lon' : 70.7833, 'Lat' : 22.3000}];
var centerLonLat = getCenterLonLat(arrLonLat);
var lonLatSarkhej = new OpenLayers.LonLat(arrLonLat[0].Lon,arrLonLat[0].Lat).transform(epsg4326,projectTo);
var lonLatMumbai = new OpenLayers.LonLat(arrLonLat[1].Lon,arrLonLat[1].Lat).transform(epsg4326,projectTo);
var lonLatVadodara = new OpenLayers.LonLat(arrLonLat[2].Lon,arrLonLat[2].Lat).transform(epsg4326,projectTo);
var lonLatJaipur = new OpenLayers.LonLat(arrLonLat[3].Lon,arrLonLat[3].Lat).transform(epsg4326,projectTo);
var lonLatDelhi = new OpenLayers.LonLat(arrLonLat[4].Lon,arrLonLat[4].Lat).transform(epsg4326,projectTo);
var lonLatRajkot = new OpenLayers.LonLat(arrLonLat[5].Lon,arrLonLat[5].Lat).transform(epsg4326,projectTo);
//Center Point of Average Markers
var lonLatCenter = new OpenLayers.LonLat(73.681549202080149,23.696708071960074).transform(epsg4326,projectTo);
var markers = new OpenLayers.Layer.Markers("Markers");
map.addLayer(markers);
var size = new OpenLayers.Size(24,24);
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
var icon = new OpenLayers.Icon('icon/Marker-Pink.png', size, offset);
var iconCenter = new OpenLayers.Icon('icon/Marker-Green.png', size, offset);
markers.addMarker(new OpenLayers.Marker(lonLatSarkhej,icon)); //Sarkhej
markers.addMarker(new OpenLayers.Marker(lonLatMumbai,icon.clone())); //Mumbai
markers.addMarker(new OpenLayers.Marker(lonLatVadodara,icon.clone())); //Vadodara
markers.addMarker(new OpenLayers.Marker(lonLatJaipur,icon.clone())); //Jaipur
markers.addMarker(new OpenLayers.Marker(lonLatDelhi,icon.clone())); //Delhi
markers.addMarker(new OpenLayers.Marker(lonLatRajkot,icon.clone())); //Rajkot
I am rendering 6 different location with Pink marker, and center with Green marker.
Please see below image for more clarification.
Now have drawn box to get idea about the center marker (Green) which is not actually center. I think it should be positioned on Green dot which is crossed by green Horizontal and Vertical line.
Can anybody let me know, does my center point is calculated correctly
or not?
Why it is not display at center of the box?
I have also added Ruler for calculation of center point.
Please help me to find the actual solution, please do let me know if you need more details for the same.
The correct center depends actually on the definition for center and there are several possibilites:
One might calculate the smallest rectangle containing all points and
use the center of this rectangle as you did with your green lines
and green point. (This is unusual)
One might ask which point is closest to all other points, i.e.
find a center point so that all distances between this center and
all other points are smallest (abs-norm, used for practical
problems)
One might ask which center point results in the least error, i.e.
the smallest (sum of) quadratic distances to all other points
(used ins math, statistics, etc)
You see, depending on the definition, you'll have to use a different algorithm and will arrive on a different center point.
The algorithm you show in your question seems to calculate (2.)
I have a Rectangle which is an array of 4 Point structures. It can be rotated in place on any angle (0 to 360 degrees) and will draw properly.
The user can also drag a corner to resize the rectangle. For example, if they move the bottom-left point, it will also update the X coordinate of the upper-left point, and the Y coordinate of the lower-right point. In this way, it will always be a rectangle no matter which point they move.
Points[point] = newValue;
switch (point)
{
case TopLeft:
Points[BottomLeft].X = newValue.X;
Points[TopRight].Y = newValue.Y;
break;
case BottomRight:
Points[TopRight].X = newValue.X;
Points[BottomLeft].Y = newValue.Y;
break;
case BottomLeft:
Points[TopLeft].X = newValue.X;
Points[BottomRight].Y = newValue.Y;
break;
case TopRight:
Points[BottomRight].X = newValue.X;
Points[TopLeft].Y = newValue.Y;
break;
}
Here I change any of the four points to the given input point (newValue), and then modify the linked points so that it stays a Rectangle shape.
However, I need to modify the above code to work if my rectangle is on an angle like this:
Sample code added here:
http://www.assembla.com/code/moozhe-testing/subversion/nodes/rotateRectangle
I see 2 solutions. The first one theoretically works, but because of rounding, it ends up not working. I'll let the first solution there, but the second one is the good one.
In these samples, I'll call the 4 corners CornerA, B, C and D, named in a clockwise fashion. Let's say you're moving "CornerA" from a position Point oldPoint to position Point newPoint.
First solution :
Get the position delta
Do a projection of that delta on Side sideAtoB and add that vector to PointD.
Do a projection of that delta on Side sideDtoA and add that vector to PointB.
Set PointA to newPoint.
Second solution :
Get the vector linking the opposite corner to the moving corner's new position, let's call it "Diagonal".
Set B's position to "C + [Projection of Diagonal on sideAtoD].
Set D's position to "C + [Projection of Diagonal on sideAtoB].
Set PointA to newPoint.
Here is the code for that 2nd solution :
public class Rectangle
{
// Obviously, one would need to assign values to these points.
Point CornerA = new Point();
Point CornerB = new Point();
Point CornerC = new Point();
Point CornerD = new Point();
Dictionary<int, Point> points = new Dictionary<int, Point>();
public Rectangle()
{
points.Add(0, CornerA);
points.Add(1, CornerB);
points.Add(2, CornerC);
points.Add(3, CornerD);
}
public void MoveAPoint(int id, Point newPoint)
{
// Get the old point
Point oldPoint = points[id];
// Get the previous point
Point pointPrevious = points[(id + 3) % 4];
// Get the next point
Point pointNext = points[(id + 1) % 4];
// Get the opposite point
Point pointOpposite = points[(id + 2) % 4];
// Get the delta (variation) of the moving point
Point delta = newPoint.Substract(oldPoint);
// I call sides points, but they are actually vectors.
// Get side from 'oldPoint' to 'pointPrevious'.
Point sidePrevious = pointPrevious.Substract(oldPoint);
// Get side from 'oldPoint' to 'pointNext'.
Point sideNext = pointNext.Substract(oldPoint);
// Get side from 'pointOpposite' to 'newPoint'.
Point sideTransversal = newPoint.Substract(pointOpposite);
PointF previousProjection;
PointF nextProjection;
if (sideNext.X == 0 && sideNext.Y == 0)
{
if (sidePrevious.X == 0 && sidePrevious.Y == 0)
{
return;
}
sideNext = new PointF(-sidePrevious.Y, sidePrevious.X);
}
else
{
sidePrevious = new PointF(-sideNext.Y, sideNext.X);
}
Point previousProjection = Projection(delta, sidePrevious);
Point nextProjection = Projection(delta, sideNext);
pointNext.SetToPoint(pointNext.AddPoints(previousProjection));
pointPrevious.SetToPoint(pointPrevious.AddPoints(nextProjection));
oldPoint.SetToPoint(newPoint);
}
private static Point Projection(Point vectorA, Point vectorB)
{
Point vectorBUnit = new Point(vectorB.X, vectorB.Y);
vectorBUnit = vectorBUnit.Normalize();
decimal dotProduct = vectorA.X * vectorBUnit.X + vectorA.Y * vectorBUnit.Y;
return vectorBUnit.MultiplyByDecimal(dotProduct);
}
}
public static class ExtendPoint
{
public static Point Normalize(this Point pointA)
{
double length = Math.Sqrt(pointA.X * pointA.X + pointA.Y * pointA.Y);
return new Point(pointA.X / length, pointA.Y / length);
}
public static Point MultiplyByDecimal (this Point point, decimal length)
{
return new Point((int)(point.X * length), (int)(point.Y * length));
}
public static Point AddPoints(this Point firstPoint, Point secondPoint)
{
return new Point(firstPoint.X + secondPoint.X, firstPoint.Y + secondPoint.Y);
}
public static Point Substract(this Point firstPoint, Point secondPoint)
{
return new Point(firstPoint.X - secondPoint.X, firstPoint.Y - secondPoint.Y);
}
public static void SetToPoint(this Point oldPoint, Point newPoint)
{
oldPoint.X = newPoint.X;
oldPoint.Y = newPoint.Y;
}
}
I used a solution that calculates the intersection between perpendicular lines:
Given a rotated rectangle (NOT axis aligned) with points, A, B, C, and D, and a dragged point D1 (which is the new point dragged from D), the goal is to find the new points for C1 and A1. B is opposite of D and will not move.
To find C1 and A1, find the intersection of the lines with points D1, which intersect with AB and BC and that are perpendicular to lines AB and BC (right angle). Those intersections will give you the points C1 and A1. Since the lines are perpendicular to AB and BC, they will form the new rectangle. Use the linear equations to calculate the slopes, intercepts and reciprocal slopes, which will give you both lines needed to calculate the intersection between AB and D1A. Repeat for the other side to get the intersection between BC and D1C (Remember, for axis-aligned rectangles, the below is not necessary and much easier)
Solution:
Find the slopes, opposite reciprocal slopes, and y-intercepts of the lines AB and AC. And find the y-intercepts of the lines D1A and D1C. Then calculate the intersection to get x and plug x into the lienar equation of one of the lines to get y:
slope_AB = (B.y - A.y) / (B.x - A.x)
y_intercept_AB = B.y - slope_AB * B.x
reciprocal_slope_AB = -1 / slope_AB
y_intercept_AD1 = D1.y - reciprocal_slope_AB * D1.x
A1x = (y_intercept_AB - y_intercept_AD1) / (reciprocal_slope_AB - slope_AB);
A1y = (slope_AB * B1x) + y_intercept_AB;
Repeat above 6 calculations accordingly to get C1x and C1y
I have to do something like this.
When I click on a node, it expands, and this is OK (I am using Powercharts to do it).
My big problem is creating random coordinates so that when I open the subnode, it doesn't overlap with another node/subnode.
In the Powercharts I have to pass the coordinates, so the big problem is in passing it.
I have to do the random coordinates in C#.
//-------------------------------------------------------
This is what i did so far:
This is what i do, is not overlaping, but i have a problem.
how can i start do the circles from a starting point?
for example, starts in the middle (300,300) and then do circles around it. Is possible?
private void button2_Click(object sender, EventArgs e)
{
g = pictureBox1.CreateGraphics();
g.Clear(pictureBox1.BackColor);
double angle;
Circle item0 = new Circle();
item0.x=200;
item0.y=150;
item0.r=50;
listaCirculos.Add(item0);
Random randomMember = new Random();
g.DrawEllipse(pen1, 200, 150, 50, 50);
while(listaCirculos.Count!=11)
{
int[] it = GenerateNewCircle(600);
Circle item = new Circle();
item.x = it[0];
item.y = it[1];
item.r = 50;
if (circleIsAllowed(listaCirculos, item))
{
listaCirculos.Add(item);
g.DrawEllipse(pen1, Convert.ToInt32(item.x), Convert.ToInt32(item.y), 50, 50);
}
}
}
bool circleIsAllowed(List<Circle> circles, Circle newCircle)
{
foreach(Circle it in circles)
{
//double sumR = it.x + newCircle.r;
//double dx = it.x - newCircle.x;
//double dy = it.y - newCircle.y;
//double squaredDist = dx * dx + dy * dy;
double aX = Math.Pow(it.x - newCircle.x, 2);
double aY = Math.Pow(it.y - newCircle.y, 2);
double Dif = Math.Abs(aX - aY);
double ra1 = it.r / 2;
double ra2 = it.r / 2;
double raDif = Math.Pow(ra1 + ra2, 2);
if ((raDif + 1) > Dif) return false;
//if (squaredDist < sumR*sumR) return false;
}
return true; // no existing circle overlaps
}
public int[] GenerateNewCircle(int maxSize)
{
int x, y;
Random randomMember = new Random();
x = randomMember.Next(0,maxSize);
if (x - 50 < 0)
y = randomMember.Next(x + 50, maxSize);
else if (x + 50 > 600)
y = randomMember.Next(0, x - 50);
else
// in this case, x splits the range 0..n into 2 subranges.
// get a random number and skip the "gap" if necessary
y = randomMember.Next(0, maxSize - 50);
if (y > x - 50)
{
y += 20;
}
int[] abc = new int[2];
abc[0] = x;
abc[1] = y;
return abc;
}
Size sizeShape = new Size("SomeWidth" , "SomeHeight");
List<Retangle> rects = new List<Retangle>();
Random r = new Random();
while(rects.count != "someCount")
{
Point rPoint = new Point(r.Next(500) , r.Next(500))
bool isNew = true;
foreach(Rectangle r in rects)
{
if(r.contains(rPoint))
isNew = false;
}
if(isNew)
rects.Add(GetRect(rPoint , sizeShape));
}
private Rectangle GetRect(Point p , Size s)
{
return new Rectangle(p,s);
}
After than you can use from rects ,
rects has random coordinates.
Here's the link to let you know how to create Random Numbers in C#.
You have to keep in mind that you will generate random numbers, but you also have to make sure the numbers generated will not make objects overlap.
Despite this being pretty poorly worded, I believe what you are looking for is the Random class. It is instantiated as such:
Random thisRandom = new Random();
Then from there, if you want to generate a random coordinate, you need to know the maximum possible X and Y coordinates. Knowing these will allow you to generate X and Y coordinates within the given canvas as such:
int maxX = 500; //This is the maximum X value on your canvas
int maxY = 500; //This is the maximum Y value on your canvas
int newX, newY;
newX = thisRandom.Next(maxX);
newY = thisRandom.Next(maxY);
While its not the absolute best in terms of "true" randomization this should give you what you need.