package d2graph

import (
	
	
	
	
	
	
	
	
	
	

	
	

	

	
	
	
	
	
	
	
	
	
	
	
	
	
)

const INNER_LABEL_PADDING int = 5
const DEFAULT_SHAPE_SIZE = 100.
const MIN_SHAPE_SIZE = 5

type Graph struct {
	FS     fs.FS  `json:"-"`
	Parent *Graph `json:"-"`
	Name   string `json:"name"`
	// IsFolderOnly indicates a board or scenario itself makes no modifications from its
	// base. Folder only boards do not have a render and are used purely for organizing
	// the board tree.
	IsFolderOnly bool       `json:"isFolderOnly"`
	AST          *d2ast.Map `json:"ast"`
	// BaseAST is the AST of the original graph without inherited fields and edges
	BaseAST *d2ast.Map `json:"-"`

	Root    *Object   `json:"root"`
	Edges   []*Edge   `json:"edges"`
	Objects []*Object `json:"objects"`

	Layers    []*Graph `json:"layers,omitempty"`
	Scenarios []*Graph `json:"scenarios,omitempty"`
	Steps     []*Graph `json:"steps,omitempty"`

	Theme *d2themes.Theme `json:"theme,omitempty"`

	// Object.Level uses the location of a nested graph
	RootLevel int `json:"rootLevel,omitempty"`

	// Currently this holds data embedded from source code configuration variables
	// Plugins only have access to exported graph, so this data structure allows
	// carrying arbitrary metadata that any plugin might handle
	Data map[string]interface{} `json:"data,omitempty"`
}

func () *Graph {
	 := &Graph{}
	.Root = &Object{
		Graph:    ,
		Parent:   nil,
		Children: make(map[string]*Object),
	}
	return 
}

func ( *Graph) () *Graph {
	for .Parent != nil {
		 = .Parent
	}
	return 
}

func ( *Graph) () []string {
	if  == nil {
		return nil
	}

	var  []string

	 := 
	for  != nil {
		if .Name != "" {
			 = append(, .Name)
		}
		 = .Parent
	}

	for  := 0;  < len()/2; ++ {
		 := len() - 1 - 
		[], [] = [], []
	}

	if len() == 0 {
		return []string{"root"}
	}

	 = append([]string{"root"}, ...)

	if .Parent != nil {
		var  string
		if len(.Parent.Layers) > 0 {
			for ,  := range .Parent.Layers {
				if  ==  {
					 = "layers"
					break
				}
			}
		}
		if len(.Parent.Scenarios) > 0 {
			for ,  := range .Parent.Scenarios {
				if  ==  {
					 = "scenarios"
					break
				}
			}
		}
		if len(.Parent.Steps) > 0 {
			for ,  := range .Parent.Steps {
				if  ==  {
					 = "steps"
					break
				}
			}
		}
		if  != "" {
			 = append([:1], append([]string{}, [1:]...)...)
		}
	}

	return 
}

type LayoutGraph func(context.Context, *Graph) error
type RouteEdges func(context.Context, *Graph, []*Edge) error

// TODO consider having different Scalar types
// Right now we'll hold any types in Value and just convert, e.g. floats
type Scalar struct {
	Value  string     `json:"value"`
	MapKey *d2ast.Key `json:"-"`
}

// TODO maybe rename to Shape
type Object struct {
	Graph  *Graph  `json:"-"`
	Parent *Object `json:"-"`

	// IDVal is the actual value of the ID whereas ID is the value in d2 syntax.
	// e.g. ID:    "yes'\""
	//      IDVal: yes'"
	//
	// ID allows joining on . naively and construct a valid D2 key path
	ID         string      `json:"id"`
	IDVal      string      `json:"id_val"`
	Map        *d2ast.Map  `json:"-"`
	References []Reference `json:"references,omitempty"`

	*geo.Box      `json:"box,omitempty"`
	LabelPosition *string `json:"labelPosition,omitempty"`
	IconPosition  *string `json:"iconPosition,omitempty"`

	ContentAspectRatio *float64 `json:"contentAspectRatio,omitempty"`

	Class    *d2target.Class    `json:"class,omitempty"`
	SQLTable *d2target.SQLTable `json:"sql_table,omitempty"`

	Children      map[string]*Object `json:"-"`
	ChildrenArray []*Object          `json:"-"`

	Attributes `json:"attributes"`

	ZIndex int `json:"zIndex"`
}

type Attributes struct {
	Label           Scalar                  `json:"label"`
	LabelDimensions d2target.TextDimensions `json:"labelDimensions"`

	Style   Style    `json:"style"`
	Icon    *url.URL `json:"icon,omitempty"`
	Tooltip *Scalar  `json:"tooltip,omitempty"`
	Link    *Scalar  `json:"link,omitempty"`

	WidthAttr  *Scalar `json:"width,omitempty"`
	HeightAttr *Scalar `json:"height,omitempty"`

	Top  *Scalar `json:"top,omitempty"`
	Left *Scalar `json:"left,omitempty"`

	// TODO consider separate Attributes struct for shape-specific and edge-specific
	// Shapes only
	NearKey  *d2ast.KeyPath `json:"near_key"`
	Language string         `json:"language,omitempty"`
	// TODO: default to ShapeRectangle instead of empty string
	Shape Scalar `json:"shape"`

	Direction  Scalar   `json:"direction"`
	Constraint []string `json:"constraint"`

	GridRows      *Scalar `json:"gridRows,omitempty"`
	GridColumns   *Scalar `json:"gridColumns,omitempty"`
	GridGap       *Scalar `json:"gridGap,omitempty"`
	VerticalGap   *Scalar `json:"verticalGap,omitempty"`
	HorizontalGap *Scalar `json:"horizontalGap,omitempty"`

	LabelPosition *Scalar `json:"labelPosition,omitempty"`
	IconPosition  *Scalar `json:"iconPosition,omitempty"`

	// These names are attached to the rendered elements in SVG
	// so that users can target them however they like outside of D2
	Classes []string `json:"classes,omitempty"`
}

// ApplyTextTransform will alter the `Label.Value` of the current object based
// on the specification of the `text-transform` styling option. This function
// has side-effects!
func ( *Attributes) () {
	if .Style.NoneTextTransform() {
		return
	}

	if .Style.TextTransform != nil && .Style.TextTransform.Value == "uppercase" {
		.Label.Value = strings.ToUpper(.Label.Value)
	}
	if .Style.TextTransform != nil && .Style.TextTransform.Value == "lowercase" {
		.Label.Value = strings.ToLower(.Label.Value)
	}
	if .Style.TextTransform != nil && .Style.TextTransform.Value == "capitalize" {
		.Label.Value = cases.Title(language.Und).String(.Label.Value)
	}
}

func ( *Attributes) () d2target.Arrowhead {
	var  *bool
	if .Style.Filled != nil {
		,  := strconv.ParseBool(.Style.Filled.Value)
		 = go2.Pointer()
	}
	return d2target.ToArrowhead(.Shape.Value, )
}

type Reference struct {
	Key          *d2ast.KeyPath `json:"key"`
	KeyPathIndex int            `json:"key_path_index"`

	MapKey          *d2ast.Key `json:"-"`
	MapKeyEdgeIndex int        `json:"map_key_edge_index"`
	Scope           *d2ast.Map `json:"-"`
	ScopeObj        *Object    `json:"-"`
	ScopeAST        *d2ast.Map `json:"-"`
	IsVar           bool       `json:"-"`
}

func ( Reference) () bool {
	return .Key == .MapKey.Edges[.MapKeyEdgeIndex].Dst
}

func ( Reference) () bool {
	return .Key != .MapKey.Key
}

type Style struct {
	Opacity       *Scalar `json:"opacity,omitempty"`
	Stroke        *Scalar `json:"stroke,omitempty"`
	Fill          *Scalar `json:"fill,omitempty"`
	FillPattern   *Scalar `json:"fillPattern,omitempty"`
	StrokeWidth   *Scalar `json:"strokeWidth,omitempty"`
	StrokeDash    *Scalar `json:"strokeDash,omitempty"`
	BorderRadius  *Scalar `json:"borderRadius,omitempty"`
	Shadow        *Scalar `json:"shadow,omitempty"`
	ThreeDee      *Scalar `json:"3d,omitempty"`
	Multiple      *Scalar `json:"multiple,omitempty"`
	Font          *Scalar `json:"font,omitempty"`
	FontSize      *Scalar `json:"fontSize,omitempty"`
	FontColor     *Scalar `json:"fontColor,omitempty"`
	Animated      *Scalar `json:"animated,omitempty"`
	Bold          *Scalar `json:"bold,omitempty"`
	Italic        *Scalar `json:"italic,omitempty"`
	Underline     *Scalar `json:"underline,omitempty"`
	Filled        *Scalar `json:"filled,omitempty"`
	DoubleBorder  *Scalar `json:"doubleBorder,omitempty"`
	TextTransform *Scalar `json:"textTransform,omitempty"`
}

// NoneTextTransform will return a boolean if the text should not have any
// transformation applied. This should overwrite theme specific transformations
// like `CapsLock` from the `terminal` theme.
func ( Style) () bool {
	return .TextTransform != nil && .TextTransform.Value == "none"
}

func ( *Style) (,  string) error {
	switch  {
	case "opacity":
		if .Opacity == nil {
			break
		}
		,  := strconv.ParseFloat(, 64)
		if  != nil || ( < 0 ||  > 1) {
			return errors.New(`expected "opacity" to be a number between 0.0 and 1.0`)
		}
		.Opacity.Value = 
	case "stroke":
		if .Stroke == nil {
			break
		}
		if !color.ValidColor() {
			return errors.New(`expected "stroke" to be a valid named color ("orange"), a hex code ("#f0ff3a"), or a gradient ("linear-gradient(red, blue)")`)
		}
		.Stroke.Value = 
	case "fill":
		if .Fill == nil {
			break
		}
		if !color.ValidColor() {
			return errors.New(`expected "fill" to be a valid named color ("orange"), a hex code ("#f0ff3a"), or a gradient ("linear-gradient(red, blue)")`)
		}
		.Fill.Value = 
	case "fill-pattern":
		if .FillPattern == nil {
			break
		}
		if !go2.Contains(d2ast.FillPatterns, strings.ToLower()) {
			return fmt.Errorf(`expected "fill-pattern" to be one of: %s`, strings.Join(d2ast.FillPatterns, ", "))
		}
		.FillPattern.Value = 
	case "stroke-width":
		if .StrokeWidth == nil {
			break
		}
		,  := strconv.Atoi()
		if  != nil || ( < 0 ||  > 15) {
			return errors.New(`expected "stroke-width" to be a number between 0 and 15`)
		}
		.StrokeWidth.Value = 
	case "stroke-dash":
		if .StrokeDash == nil {
			break
		}
		,  := strconv.Atoi()
		if  != nil || ( < 0 ||  > 10) {
			return errors.New(`expected "stroke-dash" to be a number between 0 and 10`)
		}
		.StrokeDash.Value = 
	case "border-radius":
		if .BorderRadius == nil {
			break
		}
		,  := strconv.Atoi()
		if  != nil || ( < 0) {
			return errors.New(`expected "border-radius" to be a number greater or equal to 0`)
		}
		.BorderRadius.Value = 
	case "shadow":
		if .Shadow == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "shadow" to be true or false`)
		}
		.Shadow.Value = 
	case "3d":
		if .ThreeDee == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "3d" to be true or false`)
		}
		.ThreeDee.Value = 
	case "multiple":
		if .Multiple == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "multiple" to be true or false`)
		}
		.Multiple.Value = 
	case "font":
		if .Font == nil {
			break
		}
		if ,  := d2fonts.D2_FONT_TO_FAMILY[strings.ToLower()]; ! {
			return fmt.Errorf(`"%v" is not a valid font in our system`, )
		}
		.Font.Value = strings.ToLower()
	case "font-size":
		if .FontSize == nil {
			break
		}
		,  := strconv.Atoi()
		if  != nil || ( < 8 ||  > 100) {
			return errors.New(`expected "font-size" to be a number between 8 and 100`)
		}
		.FontSize.Value = 
	case "font-color":
		if .FontColor == nil {
			break
		}
		if !color.ValidColor() {
			return errors.New(`expected "font-color" to be a valid named color ("orange"), a hex code ("#f0ff3a"), or a gradient ("linear-gradient(red, blue)")`)
		}
		.FontColor.Value = 
	case "animated":
		if .Animated == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "animated" to be true or false`)
		}
		.Animated.Value = 
	case "bold":
		if .Bold == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "bold" to be true or false`)
		}
		.Bold.Value = 
	case "italic":
		if .Italic == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "italic" to be true or false`)
		}
		.Italic.Value = 
	case "underline":
		if .Underline == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "underline" to be true or false`)
		}
		.Underline.Value = 
	case "filled":
		if .Filled == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "filled" to be true or false`)
		}
		.Filled.Value = 
	case "double-border":
		if .DoubleBorder == nil {
			break
		}
		,  := strconv.ParseBool()
		if  != nil {
			return errors.New(`expected "double-border" to be true or false`)
		}
		.DoubleBorder.Value = 
	case "text-transform":
		if .TextTransform == nil {
			break
		}
		if !go2.Contains(d2ast.TextTransforms, strings.ToLower()) {
			return fmt.Errorf(`expected "text-transform" to be one of (%s)`, strings.Join(d2ast.TextTransforms, ", "))
		}
		.TextTransform.Value = 
	default:
		return fmt.Errorf("unknown style key: %s", )
	}

	return nil
}

type ContainerLevel int

func ( ContainerLevel) () int {
	// Largest to smallest
	if  == 1 {
		return d2fonts.FONT_SIZE_XXL
	} else if  == 2 {
		return d2fonts.FONT_SIZE_XL
	} else if  == 3 {
		return d2fonts.FONT_SIZE_L
	}
	return d2fonts.FONT_SIZE_M
}

func ( *Object) () string {
	 := int(.Level())
	 := .Shape.Value

	if strings.EqualFold(, d2target.ShapeSQLTable) || strings.EqualFold(, d2target.ShapeClass) {
		return color.N1
	}

	if .IsSequenceDiagramNote() {
		return color.N7
	} else if .IsSequenceDiagramGroup() {
		return color.N5
	} else if .Parent.IsSequenceDiagram() {
		return color.B5
	}

	// fill for spans
	 := .OuterSequenceDiagram()
	if  != nil {
		 -= int(.Level())
		if  == 1 {
			return color.B3
		} else if  == 2 {
			return color.B4
		} else if  == 3 {
			return color.B5
		} else if  == 4 {
			return color.N6
		}
		return color.N7
	}

	if .IsSequenceDiagram() {
		return color.N7
	}

	if  == "" || strings.EqualFold(, d2target.ShapeSquare) || strings.EqualFold(, d2target.ShapeCircle) || strings.EqualFold(, d2target.ShapeOval) || strings.EqualFold(, d2target.ShapeRectangle) || strings.EqualFold(, d2target.ShapeHierarchy) {
		if  == 1 {
			if !.IsContainer() {
				return color.B6
			}
			return color.B4
		} else if  == 2 {
			return color.B5
		} else if  == 3 {
			return color.B6
		}
		return color.N7
	}

	if strings.EqualFold(, d2target.ShapeCylinder) || strings.EqualFold(, d2target.ShapeStoredData) || strings.EqualFold(, d2target.ShapePackage) {
		if  == 1 {
			return color.AA4
		}
		return color.AA5
	}

	if strings.EqualFold(, d2target.ShapeStep) || strings.EqualFold(, d2target.ShapePage) || strings.EqualFold(, d2target.ShapeDocument) {
		if  == 1 {
			return color.AB4
		}
		return color.AB5
	}

	if strings.EqualFold(, d2target.ShapePerson) {
		return color.B3
	}
	if strings.EqualFold(, d2target.ShapeDiamond) {
		return color.N4
	}
	if strings.EqualFold(, d2target.ShapeCloud) || strings.EqualFold(, d2target.ShapeCallout) {
		return color.N7
	}
	if strings.EqualFold(, d2target.ShapeQueue) || strings.EqualFold(, d2target.ShapeParallelogram) || strings.EqualFold(, d2target.ShapeHexagon) {
		return color.N5
	}

	return color.N7
}

func ( *Object) ( interface{}) string {
	 := .Shape.Value
	if strings.EqualFold(, d2target.ShapeCode) ||
		strings.EqualFold(, d2target.ShapeText) {
		return color.N1
	}
	if strings.EqualFold(, d2target.ShapeClass) ||
		strings.EqualFold(, d2target.ShapeSQLTable) {
		return color.N7
	}
	if  != 0.0 {
		return color.B2
	}
	return color.B1
}

func ( *Object) () ContainerLevel {
	if .Parent == nil {
		return ContainerLevel(.Graph.RootLevel)
	}
	return 1 + .Parent.()
}

func ( *Object) () bool {
	return len(.Children) > 0
}

func ( *Object) () bool {
	if  == nil {
		return false
	}
	switch .Shape.Value {
	case d2target.ShapeImage, d2target.ShapePerson:
		return true
	default:
		return false
	}
}

func ( *Object) () bool {
	if  == nil {
		return false
	}
	switch .Shape.Value {
	case d2target.ShapeText, d2target.ShapeClass, d2target.ShapeSQLTable, d2target.ShapeCode:
		return false
	default:
		return .Label.Value != ""
	}
}

func ( *Object) () bool {
	return .Icon != nil && .Shape.Value != d2target.ShapeImage
}

func ( *Object) () string {
	if .Parent != nil && .Parent.ID != "" {
		return .Parent.() + "." + .ID
	}
	return .ID
}

func ( *Object) () []string {
	if .Parent == nil {
		return nil
	}
	return append(.Parent.(), .ID)
}

func ( *Object) () *d2target.MText {
	 := !.IsContainer() && .Shape.Value != "text"
	 := false
	if .Style.Bold != nil && .Style.Bold.Value == "true" {
		 = true
	}
	if .Style.Italic != nil && .Style.Italic.Value == "true" {
		 = true
	}
	 := d2fonts.FONT_SIZE_M

	if .Class != nil || .SQLTable != nil {
		 = d2fonts.FONT_SIZE_L
	}

	if .OuterSequenceDiagram() == nil {
		// Note: during grid layout when children are temporarily removed `IsContainer` is false
		if (.IsContainer() || .IsGridDiagram()) && .Shape.Value != "text" {
			 = .Level().LabelSize()
		}
	} else {
		 = false
	}
	if .Style.FontSize != nil {
		, _ = strconv.Atoi(.Style.FontSize.Value)
	}
	// Class and Table objects have Label set to header
	if .Class != nil || .SQLTable != nil {
		 += d2target.HeaderFontAdd
	}
	if .Class != nil {
		 = false
	}
	return &d2target.MText{
		Text:     .Label.Value,
		FontSize: ,
		IsBold:   ,
		IsItalic: ,
		Language: .Language,
		Shape:    .Shape.Value,

		Dimensions: .LabelDimensions,
	}
}

func ( *Object) ( d2ast.String) *Object {
	 := d2format.Format(&d2ast.KeyPath{
		Path: []*d2ast.StringBox{d2ast.MakeValueBox(d2ast.RawString(.ScalarString(), true)).StringBox()},
	})
	 := 
	,  := d2parser.ParseKey()
	if  != nil && len(.Path) > 0 {
		 = .Path[0].Unbox().ScalarString()
	}
	 := &Object{
		ID:    ,
		IDVal: ,
		Attributes: Attributes{
			Label: Scalar{
				Value: ,
			},
			Shape: Scalar{
				Value: d2target.ShapeRectangle,
			},
		},

		Graph:  .Graph,
		Parent: ,

		Children: make(map[string]*Object),
	}

	.Children[strings.ToLower()] = 
	.ChildrenArray = append(.ChildrenArray, )

	if .Graph != nil {
		.Graph.Objects = append(.Graph.Objects, )
	}

	return 
}

func ( *Object) ( []string) (*Object, bool) {
	if len() == 0 {
		return , true
	}
	if len() == 1 && [0] != "style" {
		,  := d2ast.ReservedKeywords[[0]]
		if  {
			return , true
		}
	}

	 := [0]
	 = [1:]

	,  := .Children[strings.ToLower()]
	if ! {
		return nil, false
	}

	if len() >= 1 {
		return .()
	}
	return , true
}

func ( *Object) ( *d2ast.Key) (*Edge, bool) {
	,  := .FindEdges()
	if ! {
		return nil, false
	}
	for ,  := range  {
		if .Index == *.EdgeIndex.Int {
			return , true
		}
	}
	return nil, false
}

// TODO: remove once not used anywhere
func ( []string,  *Object) ( *Object,  []string,  error) {
	if len() > 0 && !.IsSequenceDiagram() {
		 := .OuterSequenceDiagram()
		if  != nil {
			 := false
			for ,  := range .ChildrenArray {
				if .ID == [0] {
					 = true
					break
				}
			}
			if  {
				 = 
			}
		}
	}

	 = 
	 = 

	for ,  := range  {
		if  != "_" {
			continue
		}
		if  != 0 && [-1] != "_" {
			return nil, nil, errors.New(`parent "_" can only be used in the beginning of paths, e.g. "_.x"`)
		}
		if  == .Graph.Root {
			return nil, nil, errors.New(`parent "_" cannot be used in the root scope`)
		}
		if  == len()-1 {
			return nil, nil, errors.New(`invalid use of parent "_"`)
		}
		 = .Parent
		 = [1:]
	}

	return , , nil
}

// TODO: remove edges []edge and scope each edge inside Object.
func ( *Object) ( *d2ast.Key) ([]*Edge, bool) {
	if len(.Edges) != 1 {
		return nil, false
	}
	if .EdgeIndex.Int == nil {
		return nil, false
	}
	 := .Edges[0]

	, ,  := ResolveUnderscoreKey(Key(.Src), )
	if  != nil {
		return nil, false
	}
	, ,  := ResolveUnderscoreKey(Key(.Dst), )
	if  != nil {
		return nil, false
	}

	 := strings.Join(, ".")
	 := strings.Join(, ".")
	if .Parent != nil {
		 = .AbsID() + "." + 
	}
	if .Parent != nil {
		 = .AbsID() + "." + 
	}

	var  []*Edge
	for ,  := range .Graph.Edges {
		if strings.EqualFold(, .Src.AbsID()) &&
			((.SrcArrow == "<" && .SrcArrow) || (.SrcArrow == "" && !.SrcArrow)) &&
			strings.EqualFold(, .Dst.AbsID()) &&
			((.DstArrow == ">" && .DstArrow) || (.DstArrow == "" && !.DstArrow)) {
			 = append(, )
		}
	}
	return , true
}

func ( *Object) ( []d2ast.String) *Object {
	for  := range  {
		switch .Shape.Value {
		case d2target.ShapeClass, d2target.ShapeSQLTable:
			// This will only be called for connecting edges where we want to truncate to the
			// container.
			return 
		default:
			 = .EnsureChild([ : +1])
		}
	}
	return 
}

// EnsureChild grabs the child by ids or creates it if it does not exist including all
// intermediate nodes.
func ( *Object) ( []d2ast.String) *Object {
	 := .OuterSequenceDiagram()
	if  != nil {
		for ,  := range .ChildrenArray {
			if .ID == [0].ScalarString() {
				if .ID == [0].ScalarString() {
					// In cases of a.a where EnsureChild is called on the parent a, the second a should
					// be created as a child of a and not as a child of the diagram. This is super
					// unfortunate code but alas.
					break
				}
				 = 
				break
			}
		}
	}

	if len() == 0 {
		return 
	}

	,  := d2ast.ReservedKeywordHolders[[0].ScalarString()]
	 =  && [0].IsUnquoted()
	if len() == 1 && ! {
		,  := d2ast.ReservedKeywords[[0].ScalarString()]
		 =  && [0].IsUnquoted()
		if  {
			return 
		}
	}

	 := [0]
	 = [1:]

	if .ScalarString() == "_" && .IsUnquoted() {
		return .Parent.()
	}

	 := d2format.Format(&d2ast.KeyPath{
		Path: []*d2ast.StringBox{d2ast.MakeValueBox(d2ast.RawString(.ScalarString(), true)).StringBox()},
	})
	,  := .Children[strings.ToLower()]
	if ! {
		 = .newObject()
	}

	if len() >= 1 {
		return .()
	}
	return 
}

func ( *Object) ( []string,  Reference,  *Object) {
	.ScopeObj = 
	 := 0
	for  := range  {
		if [] == "_" {
			++
			continue
		}
		,  := .HasChild([ : +1])
		if ! {
			return
		}
		.KeyPathIndex = 
		.References = append(.References, )
	}
}

func ( *Object) ( []*d2target.MText,  *textmeasure.Ruler,  *d2fonts.FontFamily) (*d2target.TextDimensions, error) {
	 := strings.ToLower(.Shape.Value)

	if .Style.Font != nil {
		 := d2fonts.D2_FONT_TO_FAMILY[.Style.Font.Value]
		 = &
	}

	var  *d2target.TextDimensions
	switch  {
	case d2target.ShapeText:
		if .Language == "latex" {
			, ,  := d2latex.Measure(.Text().Text)
			if  != nil {
				return nil, 
			}
			 = d2target.NewTextDimensions(, )
		} else if .Language != "" {
			var  error
			,  = getMarkdownDimensions(, , .Text(), )
			if  != nil {
				return nil, 
			}
		} else {
			 = GetTextDimensions(, , .Text(), )
		}

	case d2target.ShapeClass:
		 = GetTextDimensions(, , .Text(), go2.Pointer(d2fonts.SourceCodePro))

	default:
		 = GetTextDimensions(, , .Text(), )
	}

	if  == d2target.ShapeSQLTable && .Label.Value == "" {
		// measure with placeholder text to determine height
		 := *.Text()
		.Text = "Table"
		 = GetTextDimensions(, , &, )
	}

	if  == nil {
		if .Text().Text == "" {
			return d2target.NewTextDimensions(0, 0), nil
		}
		if  == d2target.ShapeImage {
			 = d2target.NewTextDimensions(0, 0)
		} else {
			return nil, fmt.Errorf("dimensions for object label %#v not found", .Text())
		}
	}

	return , nil
}

func ( *Object) ( []*d2target.MText,  *textmeasure.Ruler,  *d2fonts.FontFamily,  d2target.TextDimensions,  bool) (*d2target.TextDimensions, error) {
	 := d2target.TextDimensions{}
	 := strings.ToLower(.Shape.Value)

	if  == d2target.ShapeCode {
		 := .Text().FontSize
		// 0.5em padding on each side
		.Width += 
		.Height += 
	} else if  {
		.Width += INNER_LABEL_PADDING
		.Height += INNER_LABEL_PADDING
	}

	switch  {
	default:
		return d2target.NewTextDimensions(.Width, .Height), nil
	case d2target.ShapeText:
		 := .Width
		if  < MIN_SHAPE_SIZE {
			 = MIN_SHAPE_SIZE
		}
		 := .Height
		if  < MIN_SHAPE_SIZE {
			 = MIN_SHAPE_SIZE
		}
		return d2target.NewTextDimensions(, ), nil

	case d2target.ShapeImage:
		return d2target.NewTextDimensions(128, 128), nil

	case d2target.ShapeClass:
		 := go2.Max(12, .Width)

		 := d2fonts.FONT_SIZE_L
		if .Style.FontSize != nil {
			, _ = strconv.Atoi(.Style.FontSize.Value)
		}

		for ,  := range .Class.Fields {
			 := GetTextDimensions(, , .Text(), go2.Pointer(d2fonts.SourceCodePro))
			if  == nil {
				return nil, fmt.Errorf("dimensions for class field %#v not found", .Text())
			}
			 = go2.Max(, .Width)
		}
		for ,  := range .Class.Methods {
			 := GetTextDimensions(, , .Text(), go2.Pointer(d2fonts.SourceCodePro))
			if  == nil {
				return nil, fmt.Errorf("dimensions for class method %#v not found", .Text())
			}
			 = go2.Max(, .Width)
		}
		//    ┌─PrefixWidth ┌─CenterPadding
		// ┌─┬─┬───────┬──────┬───┬──┐
		// │ + getJobs()      Job[]  │
		// └─┴─┴───────┴──────┴───┴──┘
		//  └─PrefixPadding        └──TypePadding
		//     ├───────┤   +  ├───┤  = maxWidth
		.Width = d2target.PrefixPadding + d2target.PrefixWidth +  + d2target.CenterPadding + d2target.TypePadding

		// All rows should be the same height
		var  *d2target.MText
		if len(.Class.Fields) > 0 {
			 = .Class.Fields[0].Text()
		} else if len(.Class.Methods) > 0 {
			 = .Class.Methods[0].Text()
		}
		if  != nil {
			 := GetTextDimensions(, , , go2.Pointer(d2fonts.SourceCodePro)).Height + d2target.VerticalPadding
			.Height = *(len(.Class.Fields)+len(.Class.Methods)) + go2.Max(2*, .Height+2*label.PADDING)
		} else {
			.Height = 2*go2.Max(12, .Height) + d2target.VerticalPadding
		}

	case d2target.ShapeSQLTable:
		 := 0
		 := 0
		 := 0

		 := d2fonts.FONT_SIZE_L
		if .Style.FontSize != nil {
			, _ = strconv.Atoi(.Style.FontSize.Value)
		}

		for  := range .SQLTable.Columns {
			// Note: we want to set dimensions of actual column not the for loop copy of the struct
			 := &.SQLTable.Columns[]

			 := .Texts()

			 := GetTextDimensions(, , [0], )
			if  == nil {
				return nil, fmt.Errorf("dimensions for sql_table name %#v not found", [0].Text)
			}
			.Name.LabelWidth = .Width
			.Name.LabelHeight = .Height
			 = go2.Max(, .Width)

			 := GetTextDimensions(, , [1], )
			if  == nil {
				return nil, fmt.Errorf("dimensions for sql_table type %#v not found", [1].Text)
			}
			.Type.LabelWidth = .Width
			.Type.LabelHeight = .Height
			 = go2.Max(, .Width)

			if  := len(.Constraint);  > 0 {
				 := GetTextDimensions(, , [2], )
				if  == nil {
					return nil, fmt.Errorf("dimensions for sql_table constraint %#v not found", [2].Text)
				}
				 = go2.Max(, .Width)
			}
		}

		// The rows get padded a little due to header font being larger than row font
		.Height = go2.Max(12, .Height*(len(.SQLTable.Columns)+1))
		 := d2target.HeaderPadding + .Width + d2target.HeaderPadding
		 := d2target.NamePadding +  + d2target.TypePadding +  + d2target.TypePadding + 
		if  != 0 {
			 += d2target.ConstraintPadding
		}
		.Width = go2.Max(12, go2.Max(, ))
	}

	return &, nil
}

// resizes the object to fit content of the given width and height in its inner box with the given padding.
// this accounts for the shape of the object, and if there is a desired width or height set for the object
func ( *Object) (, , ,  float64) {
	 := strings.ToLower(.Shape.Value)
	 := d2target.DSL_SHAPE_TO_SHAPE_TYPE[]
	 := shape.NewShape(, geo.NewBox(geo.NewPoint(0, 0), , ))

	var ,  float64
	if  == shape.PERSON_TYPE {
		 =  + 
		 =  + 
	} else {
		,  = .GetDimensionsToFit(, , , )
	}

	var  int
	if .WidthAttr != nil {
		, _ = strconv.Atoi(.WidthAttr.Value)
		.Width = float64()
	} else {
		.Width = 
	}

	var  int
	if .HeightAttr != nil {
		, _ = strconv.Atoi(.HeightAttr.Value)
		.Height = float64()
	} else {
		.Height = 
	}

	if .SQLTable != nil || .Class != nil || .Language != "" {
		.Width = math.Max(float64(), )
		.Height = math.Max(float64(), )
	}

	if .AspectRatio1() {
		 := math.Max(.Width, .Height)
		.Width = 
		.Height = 
	} else if  == 0 ||  == 0 {
		switch  {
		case shape.PERSON_TYPE:
			.Width, .Height = shape.LimitAR(.Width, .Height, shape.PERSON_AR_LIMIT)
		case shape.OVAL_TYPE:
			.Width, .Height = shape.LimitAR(.Width, .Height, shape.OVAL_AR_LIMIT)
		}
	}
	if  == shape.CLOUD_TYPE {
		 := .GetInnerBoxForContent(, )
		.ContentAspectRatio = go2.Pointer(.Width / .Height)
	}
}

func ( *Object) () *Object {
	for  != nil {
		if .NearKey != nil {
			return 
		}
		 = .Parent
	}
	return nil
}

func ( *Object) () bool {
	if .NearKey == nil {
		return false
	}
	 := Key(.NearKey)

	// interesting if there is a shape with id=top-left, then top-left isn't treated a constant near
	,  := .Graph.Root.HasChild()
	if  {
		return false
	}
	,  := d2ast.NearConstants[[0]]
	return 
}

type Edge struct {
	Index int `json:"index"`

	SrcTableColumnIndex *int `json:"srcTableColumnIndex,omitempty"`
	DstTableColumnIndex *int `json:"dstTableColumnIndex,omitempty"`

	LabelPosition   *string  `json:"labelPosition,omitempty"`
	LabelPercentage *float64 `json:"labelPercentage,omitempty"`

	IsCurve bool         `json:"isCurve"`
	Route   []*geo.Point `json:"route,omitempty"`

	Src          *Object     `json:"-"`
	SrcArrow     bool        `json:"src_arrow"`
	SrcArrowhead *Attributes `json:"srcArrowhead,omitempty"`
	Dst          *Object     `json:"-"`
	// TODO alixander (Mon Sep 12 2022): deprecate SrcArrow and DstArrow and just use SrcArrowhead and DstArrowhead
	DstArrow     bool        `json:"dst_arrow"`
	DstArrowhead *Attributes `json:"dstArrowhead,omitempty"`

	References []EdgeReference `json:"references,omitempty"`
	Attributes `json:"attributes,omitempty"`

	ZIndex int `json:"zIndex"`
}

type EdgeReference struct {
	Edge *d2ast.Edge `json:"-"`

	MapKey          *d2ast.Key `json:"-"`
	MapKeyEdgeIndex int        `json:"map_key_edge_index"`
	Scope           *d2ast.Map `json:"-"`
	ScopeObj        *Object    `json:"-"`
	ScopeAST        *d2ast.Map `json:"-"`
}

func ( *Edge) () *d2ast.Edge {
	return .References[0].Edge
}

func ( *Edge) ( interface{}) string {
	if  != 0.0 {
		return color.B2
	}
	return color.B1
}

func ( *Edge) () string {
	if .SrcArrow && .DstArrow {
		return "<->"
	}
	if .SrcArrow {
		return "<-"
	}
	if .DstArrow {
		return "->"
	}
	return "--"
}

func ( *Edge) () *d2target.MText {
	 := d2fonts.FONT_SIZE_M
	if .Style.FontSize != nil {
		, _ = strconv.Atoi(.Style.FontSize.Value)
	}
	 := false
	if .Style.Bold != nil {
		, _ = strconv.ParseBool(.Style.Bold.Value)
	}
	return &d2target.MText{
		Text:     .Label.Value,
		FontSize: ,
		IsBold:   ,
		IsItalic: true,

		Dimensions: .LabelDimensions,
	}
}

func ( *Edge) (,  float64) {
	for ,  := range .Route {
		.X += 
		.Y += 
	}
}

func ( *Edge) () string {
	 := .Src.AbsIDArray()
	 := .Dst.AbsIDArray()

	var  []string
	for len() > 1 && len() > 1 {
		if !strings.EqualFold([0], [0]) {
			break
		}
		 = append(, [0])
		 = [1:]
		 = [1:]
	}

	 := ""
	if len() > 0 {
		 = strings.Join(, ".") + "."
	}

	return fmt.Sprintf("%s(%s %s %s)[%d]", , strings.Join(, "."), .ArrowString(), strings.Join(, "."), .Index)
}

func ( *Object) (,  []d2ast.String, ,  bool,  string) (*Edge, error) {
	for ,  := range [][]d2ast.String{, } {
		for ,  := range  {
			if ,  := d2ast.ReservedKeywords[.ScalarString()];  && .IsUnquoted() {
				return nil, errors.New("cannot connect to reserved keyword")
			}
		}
	}

	 := .ensureChildEdge()
	 := .ensureChildEdge()

	 := &Edge{
		Attributes: Attributes{
			Label: Scalar{
				Value: ,
			},
		},
		Src:      ,
		SrcArrow: ,
		Dst:      ,
		DstArrow: ,
	}
	.initIndex()

	addSQLTableColumnIndices(, , , , , )

	.Graph.Edges = append(.Graph.Edges, )
	return , nil
}

func addSQLTableColumnIndices( *Edge, ,  []d2ast.String, , ,  *Object) {
	if .Shape.Value == d2target.ShapeSQLTable {
		if  ==  {
			// Ignore edge to column inside table.
			return
		}
		 := .AbsIDArray()
		 := .AbsIDArray()
		if len()+len() > len() {
			for ,  := range .SQLTable.Columns {
				if .Name.Label == [len()-1].ScalarString() {
					.Reference = .AbsID()
					.SrcTableColumnIndex = new(int)
					*.SrcTableColumnIndex = 
					break
				}
			}
		}
	}
	if .Shape.Value == d2target.ShapeSQLTable {
		 := .AbsIDArray()
		 := .AbsIDArray()
		if len()+len() > len() {
			for ,  := range .SQLTable.Columns {
				if .Name.Label == [len()-1].ScalarString() {
					.Reference = .AbsID()
					.DstTableColumnIndex = new(int)
					*.DstTableColumnIndex = 
					break
				}
			}
		}
	}
}

// TODO: Treat undirectional/bidirectional edge here and in HasEdge flipped. Same with
// SrcArrow.
func ( *Edge) () {
	for ,  := range .Src.Graph.Edges {
		if .Src == .Src &&
			.SrcArrow == .SrcArrow &&
			.Dst == .Dst &&
			.DstArrow == .DstArrow {
			.Index++
		}
	}
}

func findMeasured( []*d2target.MText,  *d2target.MText) *d2target.TextDimensions {
	for ,  := range  {
		if .Text != .Text {
			continue
		}
		if .FontSize != .FontSize {
			continue
		}
		if .IsBold != .IsBold {
			continue
		}
		if .IsItalic != .IsItalic {
			continue
		}
		if .Language != .Language {
			continue
		}
		return &[].Dimensions
	}
	return nil
}

func getMarkdownDimensions( []*d2target.MText,  *textmeasure.Ruler,  *d2target.MText,  *d2fonts.FontFamily) (*d2target.TextDimensions, error) {
	if  := findMeasured(, );  != nil {
		return , nil
	}

	if  != nil {
		, ,  := textmeasure.MeasureMarkdown(.Text, , , .FontSize)
		if  != nil {
			return nil, 
		}
		return d2target.NewTextDimensions(, ), nil
	}

	if strings.TrimSpace(.Text) == "" {
		return d2target.NewTextDimensions(1, 1), nil
	}

	return nil, fmt.Errorf("text not pre-measured and no ruler provided")
}

func ( []*d2target.MText,  *textmeasure.Ruler,  *d2target.MText,  *d2fonts.FontFamily) *d2target.TextDimensions {
	if  := findMeasured(, );  != nil {
		return 
	}

	if  != nil {
		var  int
		var  int
		if .Language != "" {
			 := .LineHeightFactor
			.LineHeightFactor = textmeasure.CODE_LINE_HEIGHT
			,  = .MeasureMono(d2fonts.SourceCodePro.Font(.FontSize, d2fonts.FONT_STYLE_REGULAR), .Text)
			.LineHeightFactor = 

			// count empty leading and trailing lines since ruler will not be able to measure it
			 := strings.Split(.Text, "\n")
			 := false
			if len() > 0 && strings.TrimSpace([0]) == "" {
				 = true
			}
			 := 0
			for  := len() - 1;  >= 0; -- {
				if strings.TrimSpace([]) == "" {
					++
				} else {
					break
				}
			}
			if  &&  < len() {
				 += .FontSize
			}
			 += int(math.Ceil(textmeasure.CODE_LINE_HEIGHT * float64(.FontSize*)))
		} else {
			 := d2fonts.FONT_STYLE_REGULAR
			if .IsBold {
				 = d2fonts.FONT_STYLE_BOLD
			} else if .IsItalic {
				 = d2fonts.FONT_STYLE_ITALIC
			}
			if  == nil {
				 = go2.Pointer(d2fonts.SourceSansPro)
			}
			,  = .Measure(.Font(.FontSize, ), .Text)
		}
		return d2target.NewTextDimensions(, )
	}

	return nil
}

func appendTextDedup( []*d2target.MText,  *d2target.MText) []*d2target.MText {
	if GetTextDimensions(, nil, , nil) == nil {
		return append(, )
	}
	return 
}

func ( *Graph) ( []*d2target.MText,  *textmeasure.Ruler,  *d2fonts.FontFamily) error {
	if  != nil &&  != nil {
		if  := .HasFontFamilyLoaded(); ! {
			return fmt.Errorf("ruler does not have entire font family %s loaded, is a style missing?", *)
		}
	}

	if .Theme != nil && .Theme.SpecialRules.Mono {
		 := d2fonts.SourceCodePro
		 = &
	}

	for ,  := range .Objects {
		.Box = &geo.Box{}

		// user-specified label/icon positions
		if .HasLabel() && .Attributes.LabelPosition != nil {
			 := *.Attributes.LabelPosition
			 := d2ast.LabelPositionsMapping[.Value]
			.LabelPosition = go2.Pointer(.String())
		}
		if .Icon != nil && .Attributes.IconPosition != nil {
			 := *.Attributes.IconPosition
			 := d2ast.LabelPositionsMapping[.Value]
			.IconPosition = go2.Pointer(.String())
		}

		var  int
		var  int
		if .WidthAttr != nil {
			, _ = strconv.Atoi(.WidthAttr.Value)
		}
		if .HeightAttr != nil {
			, _ = strconv.Atoi(.HeightAttr.Value)
		}

		 := strings.ToLower(.Shape.Value)

		if .Label.Value == "" &&
			 != d2target.ShapeImage &&
			 != d2target.ShapeSQLTable &&
			 != d2target.ShapeClass {

			if  == d2target.ShapeCircle ||  == d2target.ShapeSquare {
				 := DEFAULT_SHAPE_SIZE
				if  != 0 ||  != 0 {
					 = float64(go2.Max(, ))
				}
				.Width = 
				.Height = 
			} else {
				.Width = DEFAULT_SHAPE_SIZE
				.Height = DEFAULT_SHAPE_SIZE
				if  != 0 {
					.Width = float64()
				}
				if  != 0 {
					.Height = float64()
				}
			}

			continue
		}

		if .Theme != nil && .Theme.SpecialRules.CapsLock && !strings.EqualFold(.Shape.Value, d2target.ShapeCode) {
			if .Language != "latex" && !.Style.NoneTextTransform() {
				.Label.Value = strings.ToUpper(.Label.Value)
			}
		}
		.ApplyTextTransform()

		,  := .GetLabelSize(, , )
		if  != nil {
			return 
		}
		.LabelDimensions = *

		// if there is a desired width or height, fit to content box without inner label padding for smallest minimum size
		 :=  == 0 &&  == 0 &&
			 != d2target.ShapeText && .Label.Value != ""
		,  := .GetDefaultSize(, , , *, )
		if  != nil {
			return 
		}

		if  == d2target.ShapeImage {
			if  == 0 {
				 = .Width
			}
			if  == 0 {
				 = .Height
			}
			.Width = float64(go2.Max(MIN_SHAPE_SIZE, ))
			.Height = float64(go2.Max(MIN_SHAPE_SIZE, ))
			// images don't need further processing
			continue
		}

		 := geo.NewBox(geo.NewPoint(0, 0), float64(.Width), float64(.Height))
		 := d2target.DSL_SHAPE_TO_SHAPE_TYPE[]
		 := shape.NewShape(, )
		,  := .GetDefaultPadding()
		if  != 0 {
			 = 0.
		}
		if  != 0 {
			 = 0.
		}

		// give shapes with icons extra padding to fit their label
		if .Icon != nil {
			switch  {
			case shape.TABLE_TYPE, shape.CLASS_TYPE, shape.CODE_TYPE, shape.TEXT_TYPE:
			default:
				 := float64(.Height + INNER_LABEL_PADDING)
				// Evenly pad enough to fit label above icon
				if  == 0 {
					 += 
				}
				if  == 0 {
					 += 
				}
			}
		}
		if  == 0 {
			switch  {
			case shape.TABLE_TYPE, shape.CLASS_TYPE, shape.CODE_TYPE:
			default:
				if .Link != nil && .Tooltip != nil {
					 += 64
				}
			}
		}

		.SizeToContent(.Width, .Height, , )
	}
	for ,  := range .Edges {
		 := 
		if .Style.Font != nil {
			 := d2fonts.D2_FONT_TO_FAMILY[.Style.Font.Value]
			 = &
		}

		if .SrcArrowhead != nil && .SrcArrowhead.Label.Value != "" {
			 := .Text()
			.Text = .SrcArrowhead.Label.Value
			 := GetTextDimensions(, , , )
			.SrcArrowhead.LabelDimensions = *
		}
		if .DstArrowhead != nil && .DstArrowhead.Label.Value != "" {
			 := .Text()
			.Text = .DstArrowhead.Label.Value
			 := GetTextDimensions(, , , )
			.DstArrowhead.LabelDimensions = *
		}

		if .Label.Value == "" {
			continue
		}

		if .Theme != nil && .Theme.SpecialRules.CapsLock && !.Style.NoneTextTransform() {
			.Label.Value = strings.ToUpper(.Label.Value)
		}
		.ApplyTextTransform()

		 := GetTextDimensions(, , .Text(), )
		if  == nil {
			return fmt.Errorf("dimensions for edge label %#v not found", .Text())
		}

		.LabelDimensions = *
	}
	return nil
}

func ( *Graph) () []*d2target.MText {
	var  []*d2target.MText

	 := .Theme != nil && .Theme.SpecialRules.CapsLock

	for ,  := range .Objects {
		if .Label.Value != "" {
			.ApplyTextTransform()
			 := .Text()
			if  && !strings.EqualFold(.Shape.Value, d2target.ShapeCode) {
				if .Language != "latex" && !.Style.NoneTextTransform() {
					.Text = strings.ToUpper(.Text)
				}
			}
			 = appendTextDedup(, )
		}
		if .Class != nil {
			 := d2fonts.FONT_SIZE_L
			if .Style.FontSize != nil {
				, _ = strconv.Atoi(.Style.FontSize.Value)
			}
			for ,  := range .Class.Fields {
				 = appendTextDedup(, .Text())
			}
			for ,  := range .Class.Methods {
				 = appendTextDedup(, .Text())
			}
		} else if .SQLTable != nil {
			 := d2fonts.FONT_SIZE_L
			if .Style.FontSize != nil {
				, _ = strconv.Atoi(.Style.FontSize.Value)
			}
			for ,  := range .SQLTable.Columns {
				for ,  := range .Texts() {
					 = appendTextDedup(, )
				}
			}
		}
	}
	for ,  := range .Edges {
		if .Label.Value != "" {
			.ApplyTextTransform()
			 := .Text()
			if  && !.Style.NoneTextTransform() {
				.Text = strings.ToUpper(.Text)
			}
			 = appendTextDedup(, )
		}
		if .SrcArrowhead != nil && .SrcArrowhead.Label.Value != "" {
			 := .Text()
			.Text = .SrcArrowhead.Label.Value
			 = appendTextDedup(, )
		}
		if .DstArrowhead != nil && .DstArrowhead.Label.Value != "" {
			 := .Text()
			.Text = .DstArrowhead.Label.Value
			 = appendTextDedup(, )
		}
	}

	for ,  := range .Layers {
		for ,  := range .() {
			 = appendTextDedup(, )
		}
	}

	for ,  := range .Scenarios {
		for ,  := range .() {
			 = appendTextDedup(, )
		}
	}

	for ,  := range .Steps {
		for ,  := range .() {
			 = appendTextDedup(, )
		}
	}

	return 
}

func ( *d2ast.KeyPath) []string {
	return d2format.KeyPath()
}

func ( *Graph) ( string) *Graph {
	for ,  := range .Layers {
		if .Name ==  {
			return 
		}
	}
	for ,  := range .Scenarios {
		if .Name ==  {
			return 
		}
	}
	for ,  := range .Steps {
		if .Name ==  {
			return 
		}
	}
	return nil
}

func ( *Graph) () {
	 := append([]*Object(nil), .Objects...)
	sort.Slice(, func(,  int) bool {
		 := []
		 := []
		if len(.References) == 0 || len(.References) == 0 {
			return  < 
		}
		 := .References[0]
		 := .References[0]
		// If they are variable substitutions, leave them alone, as their
		// references reflect where the variable is, not where the substitution is
		if .IsVar || .IsVar {
			return  < 
		}
		return .Key.Path[.KeyPathIndex].Unbox().GetRange().Before(.Key.Path[.KeyPathIndex].Unbox().GetRange())
	})
	.Objects = 
}

func ( *Graph) () {
	 := append([]*Edge(nil), .Edges...)
	sort.Slice(, func(,  int) bool {
		 := []
		 := []
		if len(.References) == 0 || len(.References) == 0 {
			return  < 
		}
		return .References[0].Edge.Range.Before(.References[0].Edge.Range)
	})
	.Edges = 
}

func ( *Object) ( *Object) bool {
	if  ==  {
		return true
	}
	if .Parent == nil {
		return false
	}
	return .Parent.()
}

// ApplyTheme applies themes on the graph level
// This is different than on the render level, which only changes colors
// A theme applied on the graph level applies special rules that change the graph
func ( *Graph) ( int64) error {
	 := d2themescatalog.Find()
	if  == (d2themes.Theme{}) {
		return fmt.Errorf("theme %d not found", )
	}
	.Theme = &
	return nil
}

func ( *Graph) () string {
	 := &bytes.Buffer{}
	fmt.Fprint(, "Objects: [")
	for ,  := range .Objects {
		fmt.Fprintf(, "%v, ", .AbsID())
	}
	fmt.Fprint(, "]")
	return .String()
}

func ( *Object) ( func(,  *Object)) {
	for ,  := range .ChildrenArray {
		(, )
		.()
	}
}

func ( *Object) () bool {
	return .Style.Multiple != nil && .Style.Multiple.Value == "true"
}

func ( *Object) () bool {
	return .Style.ThreeDee != nil && .Style.ThreeDee.Value == "true"
}

func ( *Object) () (,  geo.Spacing) {
	return .SpacingOpt(2*label.PADDING, 2*label.PADDING, true)
}

func ( *Object) (,  float64,  bool) (,  geo.Spacing) {
	if .HasLabel() {
		var  label.Position
		if .LabelPosition != nil {
			 = label.FromString(*.LabelPosition)
		}

		var ,  float64
		if .LabelDimensions.Width > 0 {
			 = float64(.LabelDimensions.Width) + 
		}
		if .LabelDimensions.Height > 0 {
			 = float64(.LabelDimensions.Height) + 
		}

		switch  {
		case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
			.Top = 
		case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
			.Bottom = 
		case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
			.Left = 
		case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
			.Right = 
		case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
			.Top = 
		case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
			.Bottom = 
		case label.InsideMiddleLeft:
			.Left = 
		case label.InsideMiddleRight:
			.Right = 
		}
	}

	if .HasIcon() {
		var  label.Position
		if .IconPosition != nil {
			 = label.FromString(*.IconPosition)
		}

		 := float64(d2target.MAX_ICON_SIZE + )
		if ! {
			 = float64(d2target.GetIconSize(.Box, .String())) + 
		}
		switch  {
		case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
			.Top = math.Max(.Top, )
		case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
			.Bottom = math.Max(.Bottom, )
		case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
			.Left = math.Max(.Left, )
		case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
			.Right = math.Max(.Right, )
		case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
			.Top = math.Max(.Top, )
		case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
			.Bottom = math.Max(.Bottom, )
		case label.InsideMiddleLeft:
			.Left = math.Max(.Left, )
		case label.InsideMiddleRight:
			.Right = math.Max(.Right, )
		}
	}

	,  := .GetModifierElementAdjustments()
	.Right += 
	.Top += 

	return
}