iteration 3
This commit is contained in:
parent
05a8e93379
commit
495bac61ff
5 changed files with 1625 additions and 1631 deletions
|
|
@ -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 doesn’t 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 doesn’t 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 (
|
||||||
|
|
|
||||||
1440
e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json
generated
vendored
1440
e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json
generated
vendored
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 |
1440
e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json
generated
vendored
1440
e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json
generated
vendored
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 |
Loading…
Reference in a new issue