private Bitmap DrawGraph(Dictionary <int, List <int> > data, int startNodeId) { List <int> parentIds = new List <int>(); Dictionary <int, WedgeNode> nodes = new Dictionary <int, WedgeNode>(); Geometry.CoordinatePlane = Geometry.CoordinatePlanes.Screen; Shapes.Point center = new Shapes.Point(1000, 1000); nodes[startNodeId] = new WedgeNode() { Id = startNodeId, Center = center, Wedge = new Shapes.Wedge(new Shapes.Circle(center, nodeWidth / 2), 0, 360), ChildrenWedge = new Shapes.WedgeUnbound(center, 0, 360) }; parentIds.Add(startNodeId); int parentIdsIndex = 0; while (parentIdsIndex < parentIds.Count) { // if(parentIdsIndex > 9) // break; int parentId = parentIds[parentIdsIndex]; if (!data.ContainsKey(parentId)) { parentIdsIndex++; continue; } WedgeNode parentNode = nodes[parentId]; //if a sibling does not have any unplaced children, this node can take up the sibling's childrenwedge space if (parentNode.SiblingClockwise != null && CountChildrenNotPlaced(parentNode.SiblingClockwise.Id, data, nodes) == 0) { parentNode.ChildrenWedgeDegrees += parentNode.SiblingClockwise.ChildrenWedgeDegrees; } if (parentNode.SiblingCounterClockwise != null && CountChildrenNotPlaced(parentNode.SiblingCounterClockwise.Id, data, nodes) == 0) { parentNode.ChildrenWedgeDegrees += parentNode.SiblingCounterClockwise.ChildrenWedgeDegrees; } ChildCalculations calculations; int childCount = CountChildrenNotPlaced(parentId, data, nodes); if (childCount == 0) { parentIdsIndex++; continue; } if (childCount > 2 && parentNode.ParentNode != null) { //move node out from old parent to make room for new children calculations = new ChildCalculations(childCount + 1, 360, nodeWidth); Shapes.Point newCenter = Geometry.PointPastLine(parentNode.ParentNode.Center, parentNode.Center, calculations.Radius * 1.2); parentNode.Center = newCenter; Shapes.Circle newCircle = new Shapes.Circle(newCenter, 1); double connectionToParentAtDegrees = newCircle.DegreesAtPoint(parentNode.ParentNode.Center); parentNode.ChildrenWedge = new Shapes.WedgeUnbound(newCenter, connectionToParentAtDegrees + calculations.AngleUnit, 360 - calculations.AngleUnit); } else { calculations = new ChildCalculations(childCount, parentNode.ChildrenWedge.Span, nodeWidth); } while (DoesCollide(parentId, nodes, parentNode.Center, calculations.Radius + (nodeWidth / 2))) { //move node out from old parent to make room for new children Shapes.Point newCenter = Geometry.PointPastLine(parentNode.ParentNode.Center, parentNode.Center, nodeWidth * 1.2); parentNode.Center = newCenter; parentNode.ChildrenWedgeCenter = newCenter; } double childAngle = parentNode.ChildrenWedge.Start; Shapes.Point childCenter = parentNode.ChildrenWedge.Center; WedgeNode siblingCounterClockwise = null; foreach (int childId in data[parentId]) { if (nodes.ContainsKey(childId)) { continue; } //todo: if the sibling nodes next to you have no children, you can take up a wider space with your children Shapes.Point childPoint = new Shapes.Point(childCenter.X + (Math.Cos(Shapes.Circle.DegreesToRadians(childAngle)) * calculations.Radius), childCenter.Y + (Math.Sin(Shapes.Circle.DegreesToRadians(childAngle)) * calculations.Radius)); nodes[childId] = new WedgeNode() { Id = childId, Center = childPoint, Wedge = new Shapes.Wedge(new Shapes.Circle(parentNode.Center, calculations.Radius + (nodeWidth / 2)), childAngle - (0.5 * calculations.AngleUnit), childAngle + (0.5 * calculations.AngleUnit)), ParentNode = parentNode, ChildrenWedge = new Shapes.WedgeUnbound(childCenter, Shapes.RangeCircular.Centered(childAngle, calculations.ChildAngleSpan, 360)), SiblingCounterClockwise = siblingCounterClockwise }; if (siblingCounterClockwise != null) { siblingCounterClockwise.SiblingClockwise = nodes[childId]; } siblingCounterClockwise = nodes[childId]; parentIds.Add(childId); childAngle += calculations.AngleUnit; } parentIdsIndex++; } //todo: check for lines very closely overlapping each other (see graph 1952, up to parentId 7) //adjust graph location as close to origin as possible int margin = 20; int minX = (int)(nodes.Select(pair => pair.Value.Center.X).Min() - (nodeWidth / 2) - margin); int minY = (int)(nodes.Select(pair => pair.Value.Center.Y).Min() - (nodeWidth / 2) - margin); Shapes.Point minPoint = new Shapes.Point(minX, minY); foreach (WedgeNode node in nodes.Values) { node.Center -= minPoint; } int maxX = (int)(nodes.Select(pair => pair.Value.Center.X).Max() + (nodeWidth / 2) + margin); int maxY = (int)(nodes.Select(pair => pair.Value.Center.Y).Max() + (nodeWidth / 2) + margin); Bitmap bitmap = new Bitmap(maxX, maxY); using (Graphics graphics = Graphics.FromImage(bitmap)) { graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.Clear(Color.White); //draw results //lines Pen pen = new Pen(Color.Black, 1); Pen thickPen = new Pen(Color.Black, 4); foreach (int fromId in data.Keys) { if (!nodes.ContainsKey(fromId)) { continue; } foreach (int toId in data[fromId].Distinct()) { if (!nodes.ContainsKey(toId)) { continue; } graphics.DrawLine(pen, new System.Drawing.Point((int)nodes[fromId].Center.X, (int)nodes[fromId].Center.Y), new System.Drawing.Point((int)nodes[toId].Center.X, (int)nodes[toId].Center.Y) ); //Point pointAlongLine = PointAlongLine(nodeLocations[toId], nodeLocations[fromId], nodeWidth * 0.75F); //graphics.DrawLine(thickPen, pointAlongLine, nodeLocations[toId]); } } //nodes foreach (int id in nodes.Keys) { DrawNode(graphics, nodes[id].Center, ShortLabel(id)); } //debugging: wedges /* * Pen redPen = new Pen(Color.Red, 1); * foreach(int id in nodes.Keys) * { * nodes[id].Wedge.Paint(graphics, redPen, 1); * } */ } return(bitmap); }
private bool DoesCollide(WedgeNode otherNode, Shapes.Point center, double radius) { return(otherNode.Wedge.Overlaps(new Shapes.Circle(center, radius))); }