Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion d2cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ func _render(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts
Charset: charsetType,
}
asciiArtist := d2ascii.NewASCIIartist()
ascii, err := asciiArtist.Render(diagram, renderOpts)
ascii, err := asciiArtist.Render(ctx, diagram, renderOpts)
if err != nil {
return ascii, err
}
Expand Down
3 changes: 2 additions & 1 deletion d2js/d2wasm/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ func Render(args []js.Value) (interface{}, error) {
return nil, &WASMError{Message: "ASCII rendering does not support multi-board targets", Code: 400}
}

ctx := log.WithDefault(context.Background())
artist := d2ascii.NewASCIIartist()
asciiOpts := &d2ascii.RenderOpts{}
if input.Opts.Scale != nil {
Expand All @@ -435,7 +436,7 @@ func Render(args []js.Value) (interface{}, error) {
}
asciiOpts.Charset = charsetType

out, err := artist.Render(diagram, asciiOpts)
out, err := artist.Render(ctx, diagram, asciiOpts)
if err != nil {
return nil, &WASMError{Message: fmt.Sprintf("ASCII render failed: %s", err.Error()), Code: 500}
}
Expand Down
4 changes: 2 additions & 2 deletions d2js/js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion d2js/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@terrastruct/d2",
"author": "Terrastruct, Inc.",
"description": "D2.js is a wrapper around the WASM build of D2, the modern text-to-diagram language.",
"version": "0.1.32",
"version": "0.1.33",
"repository": {
"type": "git",
"url": "git+https://github.com/terrastruct/d2.git",
Expand Down
37 changes: 17 additions & 20 deletions d2renderers/d2ascii/asciiroute/asciiroute.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package asciiroute

import (
"fmt"
"context"
"log/slog"
"math"
"strings"

"oss.terrastruct.com/d2/d2renderers/d2ascii/asciicanvas"
"oss.terrastruct.com/d2/d2renderers/d2ascii/charset"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/log"
)

const (
Expand Down Expand Up @@ -45,52 +47,47 @@ type RouteDrawer interface {
GetScale() float64
GetBoundaryForShape(s d2target.Shape) (Point, Point)
CalibrateXY(x, y float64) (float64, float64)
GetContext() context.Context
}

func DrawRoute(rd RouteDrawer, conn d2target.Connection) {
routes := conn.Route
label := conn.Label
ctx := rd.GetContext()

fmt.Printf("[D2ASCII] Starting edge route for connection %s -> %s\n", conn.Src, conn.Dst)
fmt.Printf("[D2ASCII] Initial route points (%d points):\n", len(routes))
log.Debug(ctx, "starting edge route", slog.String("src", conn.Src), slog.String("dst", conn.Dst))
log.Debug(ctx, "initial route points", slog.Int("count", len(routes)))
for i, pt := range routes {
fmt.Printf("[D2ASCII] Point %d: (%.2f, %.2f)\n", i, pt.X, pt.Y)
log.Debug(ctx, "route point", slog.Int("index", i), slog.Float64("x", pt.X), slog.Float64("y", pt.Y))
}

frmShapeBoundary, toShapeBoundary := getConnectionBoundaries(rd, conn.Src, conn.Dst)
fmt.Printf("[D2ASCII] Source boundary: TL(%d,%d) BR(%d,%d)\n",
frmShapeBoundary.TL.X, frmShapeBoundary.TL.Y,
frmShapeBoundary.BR.X, frmShapeBoundary.BR.Y)
fmt.Printf("[D2ASCII] Dest boundary: TL(%d,%d) BR(%d,%d)\n",
toShapeBoundary.TL.X, toShapeBoundary.TL.Y,
toShapeBoundary.BR.X, toShapeBoundary.BR.Y)
log.Debug(ctx, "boundaries", slog.Int("srcTLX", frmShapeBoundary.TL.X), slog.Int("srcTLY", frmShapeBoundary.TL.Y), slog.Int("srcBRX", frmShapeBoundary.BR.X), slog.Int("srcBRY", frmShapeBoundary.BR.Y), slog.Int("dstTLX", toShapeBoundary.TL.X), slog.Int("dstTLY", toShapeBoundary.TL.Y), slog.Int("dstBRX", toShapeBoundary.BR.X), slog.Int("dstBRY", toShapeBoundary.BR.Y))

routes = processRoute(rd, routes, frmShapeBoundary, toShapeBoundary)
routes = processRoute(ctx, rd, routes, frmShapeBoundary, toShapeBoundary)

turnDir := calculateTurnDirections(routes)
fmt.Printf("[D2ASCII] Turn directions calculated: %d turns\n", len(turnDir))
log.Debug(ctx, "turn directions calculated", slog.Int("count", len(turnDir)))
for key, dir := range turnDir {
fmt.Printf("[D2ASCII] Turn at %s: direction %s\n", key, dir)
log.Debug(ctx, "turn direction", slog.String("key", key), slog.String("dir", dir))
}

var labelPos *RouteLabelPosition
if strings.TrimSpace(label) != "" {
labelPos = calculateBestLabelPosition(rd, routes, label)
if labelPos != nil {
fmt.Printf("[D2ASCII] Label position calculated: segment %d, pos (%d, %d), maxDiff %.2f\n",
labelPos.I, labelPos.X, labelPos.Y, labelPos.MaxDiff)
log.Debug(ctx, "label position calculated", slog.Int("segmentIndex", labelPos.I), slog.Int("x", labelPos.X), slog.Int("y", labelPos.Y), slog.Float64("maxDiff", labelPos.MaxDiff))
}
}

corners, arrows := getCharacterMaps(rd)

fmt.Printf("[D2ASCII] Drawing %d segments\n", len(routes)-1)
log.Debug(ctx, "drawing segments", slog.Int("count", len(routes)-1))
for i := 1; i < len(routes); i++ {
fmt.Printf("[D2ASCII] Drawing segment %d: (%.2f,%.2f) -> (%.2f,%.2f)\n",
i-1, routes[i-1].X, routes[i-1].Y, routes[i].X, routes[i].Y)
drawSegmentBetweenPoints(rd, routes[i-1], routes[i], i, conn, corners, arrows, turnDir, frmShapeBoundary, toShapeBoundary, labelPos, label)
log.Debug(ctx, "drawing segment", slog.Int("index", i-1), slog.Float64("x1", routes[i-1].X), slog.Float64("y1", routes[i-1].Y), slog.Float64("x2", routes[i].X), slog.Float64("y2", routes[i].Y))
drawSegmentBetweenPoints(ctx, rd, routes[i-1], routes[i], i, conn, corners, arrows, turnDir, frmShapeBoundary, toShapeBoundary, labelPos, label)
}
fmt.Printf("[D2ASCII] Edge route completed for %s -> %s\n", conn.Src, conn.Dst)
log.Debug(ctx, "edge route completed", slog.String("src", conn.Src), slog.String("dst", conn.Dst))
}

func getCharacterMaps(rd RouteDrawer) (corners, arrows map[string]string) {
Expand Down
68 changes: 33 additions & 35 deletions d2renderers/d2ascii/asciiroute/drawing.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
package asciiroute

import (
"context"
"fmt"
"log/slog"
"math"

"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/log"
)

func drawSegmentBetweenPoints(rd RouteDrawer, start, end *geo.Point, segmentIndex int, conn d2target.Connection,
func drawSegmentBetweenPoints(ctx context.Context, rd RouteDrawer, start, end *geo.Point, segmentIndex int, conn d2target.Connection,
corners, arrows, turnDir map[string]string, frmBoundary, toBoundary Boundary, labelPos *RouteLabelPosition, label string) {

ax, ay := start.X, start.Y
cx, cy := end.X, end.Y

fmt.Printf("[D2ASCII] Drawing segment %d: (%.2f,%.2f) -> (%.2f,%.2f)\n",
segmentIndex-1, ax, ay, cx, cy)
log.Debug(ctx, "drawing segment", slog.Int("index", segmentIndex-1), slog.Float64("x1", ax), slog.Float64("y1", ay), slog.Float64("x2", cx), slog.Float64("y2", cy))

sx := cx - ax
sy := cy - ay
step := math.Max(math.Abs(sx), math.Abs(sy))
if step == 0 {
fmt.Printf("[D2ASCII] Zero-length segment, skipping\n")
log.Debug(ctx, "zero-length segment, skipping")
return
}
sx /= step
sy /= step

fmt.Printf("[D2ASCII] Step vector: (%.2f, %.2f), total steps: %.0f\n", sx, sy, step)
log.Debug(ctx, "step vector", slog.Float64("x", sx), slog.Float64("y", sy), slog.Float64("steps", step))

fx, fy := ax, ay
attempt := 0
Expand All @@ -38,9 +40,9 @@ func drawSegmentBetweenPoints(rd RouteDrawer, start, end *geo.Point, segmentInde
attempt++
if x == int(math.Round(cx)) && y == int(math.Round(cy)) || attempt == MaxRouteAttempts {
if attempt == MaxRouteAttempts {
fmt.Printf("[D2ASCII] Max route attempts (%d) reached\n", MaxRouteAttempts)
log.Debug(ctx, "max route attempts reached", slog.Int("attempts", MaxRouteAttempts))
} else {
fmt.Printf("[D2ASCII] Reached segment endpoint at (%d, %d)\n", x, y)
log.Debug(ctx, "reached segment endpoint", slog.Int("x", x), slog.Int("y", y))
}
break
}
Expand All @@ -49,13 +51,13 @@ func drawSegmentBetweenPoints(rd RouteDrawer, start, end *geo.Point, segmentInde

// Skip if out of bounds or contains alphanumeric character
if !isInBounds(rd, x, y) {
fmt.Printf("[D2ASCII] Position (%d, %d) out of bounds, skipping\n", x, y)
log.Debug(ctx, "position out of bounds, skipping", slog.Int("x", x), slog.Int("y", y))
fx += sx
fy += sy
continue
}
if containsAlphaNumeric(rd, x, y) {
fmt.Printf("[D2ASCII] Position (%d, %d) contains alphanumeric, skipping\n", x, y)
log.Debug(ctx, "position contains alphanumeric, skipping", slog.Int("x", x), slog.Int("y", y))
fx += sx
fy += sy
continue
Expand Down Expand Up @@ -85,41 +87,40 @@ func drawRoutePoint(rd RouteDrawer, x, y int, sx, sy float64, segmentIndex, rout

// Check for corners first
if char, ok := corners[turnDir[key]]; ok {
fmt.Printf("[D2ASCII] Drawing corner at (%d, %d): '%s' (direction: %s)\n", x, y, char, turnDir[key])
log.Debug(rd.GetContext(), "drawing corner", slog.Int("x", x), slog.Int("y", y), slog.String("char", char), slog.String("direction", turnDir[key]))
canvas.Set(x, y, char)
return
}

// Check for destination arrow
if segmentIndex == routeLen-1 && x == int(math.Round(cx)) && y == int(math.Round(cy)) && conn.DstArrow != d2target.NoArrowhead {
fmt.Printf("[D2ASCII] Drawing destination arrow at (%d, %d)\n", x, y)
log.Debug(rd.GetContext(), "drawing destination arrow", slog.Int("x", x), slog.Int("y", y))
drawArrowhead(rd, x, y, sx, sy, arrows)
if conn.DstLabel != nil {
fmt.Printf("[D2ASCII] Drawing destination label: %s\n", conn.DstLabel.Label)
log.Debug(rd.GetContext(), "drawing destination label", slog.String("label", conn.DstLabel.Label))
drawDestinationLabel(rd, conn.DstLabel.Label, cx, cy, sx, sy)
}
return
}

// Check for source arrow
if segmentIndex == 1 && x == int(math.Round(ax)) && y == int(math.Round(ay)) && conn.SrcArrow != d2target.NoArrowhead {
fmt.Printf("[D2ASCII] Drawing source arrow at (%d, %d)\n", x, y)
log.Debug(rd.GetContext(), "drawing source arrow", slog.Int("x", x), slog.Int("y", y))
arrowKey := fmt.Sprintf("%d%d", geo.Sign(sx)*-1, geo.Sign(sy)*-1)
canvas.Set(x, y, arrows[arrowKey])
if conn.SrcLabel != nil {
fmt.Printf("[D2ASCII] Drawing source label: %s\n", conn.SrcLabel.Label)
log.Debug(rd.GetContext(), "drawing source label", slog.String("label", conn.SrcLabel.Label))
drawSourceLabel(rd, conn.SrcLabel.Label, ax, cy, cx, sx, sy)
}
return
}

// Default: draw route segment
fmt.Printf("[D2ASCII] Drawing route segment at (%d, %d), existing: '%s'\n",
x, y, existingChar)
drawRouteSegment(rd, x, y, sx, sy, frmBoundary, toBoundary)
log.Debug(rd.GetContext(), "drawing route segment", slog.Int("x", x), slog.Int("y", y), slog.String("existing", string(existingChar)))
drawRouteSegment(rd.GetContext(), rd, x, y, sx, sy, frmBoundary, toBoundary)
}

func drawRouteSegment(rd RouteDrawer, x, y int, sx, sy float64, frmBoundary, toBoundary Boundary) {
func drawRouteSegment(ctx context.Context, rd RouteDrawer, x, y int, sx, sy float64, frmBoundary, toBoundary Boundary) {
if !isInBounds(rd, x, y) {
return
}
Expand All @@ -129,56 +130,53 @@ func drawRouteSegment(rd RouteDrawer, x, y int, sx, sy float64, frmBoundary, toB
overWrite := existingChar != " "

if sx == 0 { // Vertical line
fmt.Printf("[D2ASCII] Drawing vertical segment at (%d, %d), overwrite=%t, existing='%s'\n",
x, y, overWrite, existingChar)
drawVerticalSegment(rd, x, y, sy, overWrite, frmBoundary, toBoundary)
log.Debug(ctx, "drawing vertical segment", slog.Int("x", x), slog.Int("y", y), slog.Bool("overwrite", overWrite), slog.String("existing", string(existingChar)))
drawVerticalSegment(ctx, rd, x, y, sy, overWrite, frmBoundary, toBoundary)
} else { // Horizontal line
fmt.Printf("[D2ASCII] Drawing horizontal segment at (%d, %d), overwrite=%t, existing='%s'\n",
x, y, overWrite, existingChar)
drawHorizontalSegment(rd, x, y, sx, overWrite, frmBoundary, toBoundary)
log.Debug(ctx, "drawing horizontal segment", slog.Int("x", x), slog.Int("y", y), slog.Bool("overwrite", overWrite), slog.String("existing", string(existingChar)))
drawHorizontalSegment(ctx, rd, x, y, sx, overWrite, frmBoundary, toBoundary)
}

newChar := canvas.Get(x, y)
if newChar != existingChar {
fmt.Printf("[D2ASCII] Character placed: '%s' -> '%s' at (%d, %d)\n",
existingChar, newChar, x, y)
log.Debug(ctx, "character placed", slog.String("from", string(existingChar)), slog.String("to", string(newChar)), slog.Int("x", x), slog.Int("y", y))
}
}

func drawVerticalSegment(rd RouteDrawer, x, y int, sy float64, overWrite bool, frmBoundary, toBoundary Boundary) {
func drawVerticalSegment(ctx context.Context, rd RouteDrawer, x, y int, sy float64, overWrite bool, frmBoundary, toBoundary Boundary) {
canvas := rd.GetCanvas()
chars := rd.GetChars()

if overWrite && shouldDrawTJunction(rd, x, y, frmBoundary, toBoundary, true) {
if sy > 0 {
fmt.Printf("[D2ASCII] Drawing T-junction down at (%d, %d)\n", x, y)
log.Debug(ctx, "drawing T-junction down", slog.Int("x", x), slog.Int("y", y))
canvas.Set(x, y, chars.TDown())
} else {
fmt.Printf("[D2ASCII] Drawing T-junction up at (%d, %d)\n", x, y)
log.Debug(ctx, "drawing T-junction up", slog.Int("x", x), slog.Int("y", y))
canvas.Set(x, y, chars.TUp())
}
} else if overWrite && shouldSkipOverwrite(rd, x, y, frmBoundary, toBoundary) {
fmt.Printf("[D2ASCII] Skipping overwrite at (%d, %d)\n", x, y)
log.Debug(ctx, "skipping overwrite", slog.Int("x", x), slog.Int("y", y))
} else {
fmt.Printf("[D2ASCII] Drawing vertical line at (%d, %d)\n", x, y)
log.Debug(ctx, "drawing vertical line", slog.Int("x", x), slog.Int("y", y))
canvas.Set(x, y, chars.Vertical())
}
}

func drawHorizontalSegment(rd RouteDrawer, x, y int, sx float64, overWrite bool, frmBoundary, toBoundary Boundary) {
func drawHorizontalSegment(ctx context.Context, rd RouteDrawer, x, y int, sx float64, overWrite bool, frmBoundary, toBoundary Boundary) {
canvas := rd.GetCanvas()
chars := rd.GetChars()

if overWrite && shouldDrawTJunction(rd, x, y, frmBoundary, toBoundary, false) {
if sx > 0 {
fmt.Printf("[D2ASCII] Drawing T-junction right at (%d, %d)\n", x, y)
log.Debug(ctx, "drawing T-junction right", slog.Int("x", x), slog.Int("y", y))
canvas.Set(x, y, chars.TRight())
} else {
fmt.Printf("[D2ASCII] Drawing T-junction left at (%d, %d)\n", x, y)
log.Debug(ctx, "drawing T-junction left", slog.Int("x", x), slog.Int("y", y))
canvas.Set(x, y, chars.TLeft())
}
} else {
fmt.Printf("[D2ASCII] Drawing horizontal line at (%d, %d)\n", x, y)
log.Debug(ctx, "drawing horizontal line", slog.Int("x", x), slog.Int("y", y))
canvas.Set(x, y, chars.Horizontal())
}
}
Expand Down
Loading