iteration 3

This commit is contained in:
Mayank77maruti 2025-02-21 17:20:01 +00:00
parent 05a8e93379
commit 495bac61ff
5 changed files with 1625 additions and 1631 deletions

View file

@ -255,7 +255,7 @@ import (
const ( const (
MIN_RADIUS = 200 MIN_RADIUS = 200
PADDING = 20 PADDING = 20
ARC_STEPS = 60 // High resolution for perfect circles ARC_STEPS = 60
) )
func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) error { func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) error {
@ -268,17 +268,19 @@ func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) e
positionLabelsIcons(obj) positionLabelsIcons(obj)
} }
baseRadius := calculateBaseRadius(objects) // Calculate layout parameters
baseRadius, maxNodeSize := calculateLayoutParams(objects)
positionObjects(objects, baseRadius) positionObjects(objects, baseRadius)
// Create edges with boundary-perfect arcs
for _, edge := range g.Edges { for _, edge := range g.Edges {
createPerfectArc(edge, baseRadius) createBoundaryArc(edge, baseRadius, maxNodeSize)
} }
return nil return nil
} }
func calculateBaseRadius(objects []*d2graph.Object) float64 { func calculateLayoutParams(objects []*d2graph.Object) (float64, float64) {
numNodes := float64(len(objects)) numNodes := float64(len(objects))
maxSize := 0.0 maxSize := 0.0
for _, obj := range objects { for _, obj := range objects {
@ -286,7 +288,7 @@ func calculateBaseRadius(objects []*d2graph.Object) float64 {
maxSize = math.Max(maxSize, size) maxSize = math.Max(maxSize, size)
} }
minRadius := (maxSize/2 + PADDING) / math.Sin(math.Pi/numNodes) minRadius := (maxSize/2 + PADDING) / math.Sin(math.Pi/numNodes)
return math.Max(minRadius, MIN_RADIUS) return math.Max(minRadius, MIN_RADIUS), maxSize
} }
func positionObjects(objects []*d2graph.Object, radius float64) { func positionObjects(objects []*d2graph.Object, radius float64) {
@ -298,6 +300,7 @@ func positionObjects(objects []*d2graph.Object, radius float64) {
x := radius * math.Cos(angle) x := radius * math.Cos(angle)
y := radius * math.Sin(angle) y := radius * math.Sin(angle)
// Center object at calculated position
obj.TopLeft = geo.NewPoint( obj.TopLeft = geo.NewPoint(
x-obj.Width/2, x-obj.Width/2,
y-obj.Height/2, y-obj.Height/2,
@ -305,21 +308,24 @@ func positionObjects(objects []*d2graph.Object, radius float64) {
} }
} }
func createPerfectArc(edge *d2graph.Edge, baseRadius float64) { func createBoundaryArc(edge *d2graph.Edge, baseRadius, maxNodeSize float64) {
if edge.Src == nil || edge.Dst == nil || edge.Src == edge.Dst { if edge.Src == nil || edge.Dst == nil || edge.Src == edge.Dst {
return return
} }
// Calculate arc radius outside node boundaries
arcRadius := baseRadius + maxNodeSize/2 + PADDING
srcCenter := edge.Src.Center() srcCenter := edge.Src.Center()
dstCenter := edge.Dst.Center() dstCenter := edge.Dst.Center()
layoutCenter := geo.NewPoint(0, 0) layoutCenter := geo.NewPoint(0, 0)
// Calculate angles with proper wrapping // Calculate angles with shortest path
startAngle := math.Atan2(srcCenter.Y-layoutCenter.Y, srcCenter.X-layoutCenter.X) startAngle := math.Atan2(srcCenter.Y-layoutCenter.Y, srcCenter.X-layoutCenter.X)
endAngle := math.Atan2(dstCenter.Y-layoutCenter.Y, dstCenter.X-layoutCenter.X) endAngle := math.Atan2(dstCenter.Y-layoutCenter.Y, dstCenter.X-layoutCenter.X)
// Calculate angular distance taking shortest path
angleDiff := endAngle - startAngle angleDiff := endAngle - startAngle
// Normalize angle difference
if angleDiff < 0 { if angleDiff < 0 {
angleDiff += 2 * math.Pi angleDiff += 2 * math.Pi
} }
@ -327,24 +333,23 @@ func createPerfectArc(edge *d2graph.Edge, baseRadius float64) {
angleDiff -= 2 * math.Pi angleDiff -= 2 * math.Pi
} }
// Generate perfect circular arc // Generate arc points
path := make([]*geo.Point, 0, ARC_STEPS+1) path := make([]*geo.Point, 0, ARC_STEPS+1)
for i := 0; i <= ARC_STEPS; i++ { for i := 0; i <= ARC_STEPS; i++ {
t := float64(i) / ARC_STEPS t := float64(i) / ARC_STEPS
currentAngle := startAngle + t*angleDiff angle := startAngle + t*angleDiff
x := layoutCenter.X + baseRadius*math.Cos(currentAngle) x := layoutCenter.X + arcRadius*math.Cos(angle)
y := layoutCenter.Y + baseRadius*math.Sin(currentAngle) y := layoutCenter.Y + arcRadius*math.Sin(angle)
path = append(path, geo.NewPoint(x, y)) path = append(path, geo.NewPoint(x, y))
} }
// Clip to shape boundaries while preserving arc properties // Clip to actual node boundaries
edge.Route = path edge.Route = path
startIdx, endIdx := edge.TraceToShape(edge.Route, 0, len(edge.Route)-1) startIdx, endIdx := edge.TraceToShape(edge.Route, 0, len(edge.Route)-1)
// Maintain smooth arc after clipping // Maintain smooth arc after clipping
if startIdx < endIdx { if startIdx < endIdx {
edge.Route = edge.Route[startIdx : endIdx+1] edge.Route = edge.Route[startIdx : endIdx+1]
// Ensure minimal points for smooth rendering // Ensure minimal points for smooth rendering
if len(edge.Route) < 3 { if len(edge.Route) < 3 {
edge.Route = []*geo.Point{path[0], path[len(path)-1]} edge.Route = []*geo.Point{path[0], path[len(path)-1]}
@ -354,28 +359,6 @@ func createPerfectArc(edge *d2graph.Edge, baseRadius float64) {
edge.IsCurve = true edge.IsCurve = true
} }
// Keep existing helper functions (positionLabelsIcons, boxContains, boxIntersections)
// Helper if your geo.Box doesnt implement Contains()
func boxContains(b *geo.Box, p *geo.Point) bool {
// typical bounding-box check
return p.X >= b.TopLeft.X &&
p.X <= b.TopLeft.X+b.Width &&
p.Y >= b.TopLeft.Y &&
p.Y <= b.TopLeft.Y+b.Height
}
// Helper if your geo.Box doesnt implement Intersections(geo.Segment) yet
func boxIntersections(b *geo.Box, seg geo.Segment) []*geo.Point {
// We'll assume d2's standard geo.Box has a built-in Intersections(*Segment) method.
// If not, implement manually. For example, checking each of the 4 edges:
// left, right, top, bottom
// For simplicity, if you do have b.Intersections(...) you can just do:
// return b.Intersections(seg)
return b.Intersections(seg)
// If you don't have that, you'd code the line-rect intersection yourself.
}
// positionLabelsIcons is basically your logic that sets default label/icon positions if needed
func positionLabelsIcons(obj *d2graph.Object) { func positionLabelsIcons(obj *d2graph.Object) {
// If there's an icon but no icon position, give it a default // If there's an icon but no icon position, give it a default
if obj.Icon != nil && obj.IconPosition == nil { if obj.Icon != nil && obj.IconPosition == nil {
@ -415,6 +398,17 @@ func positionLabelsIcons(obj *d2graph.Object) {
} }
} }
} }
func boxContains(b *geo.Box, p *geo.Point) bool {
return p.X >= b.TopLeft.X &&
p.X <= b.TopLeft.X+b.Width &&
p.Y >= b.TopLeft.Y &&
p.Y <= b.TopLeft.Y+b.Height
}
func boxIntersections(b *geo.Box, seg geo.Segment) []*geo.Point {
return b.Intersections(seg)
}
// package d2cycle // package d2cycle
// import ( // import (

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB