works
This commit is contained in:
parent
07500f4586
commit
590caa243c
9 changed files with 118 additions and 20 deletions
|
|
@ -30,6 +30,14 @@ var setupJS string
|
|||
//go:embed dagre.js
|
||||
var dagreJS string
|
||||
|
||||
type Opts struct {
|
||||
NodeSep int
|
||||
}
|
||||
|
||||
var DefaultOpts = Opts{
|
||||
NodeSep: 60,
|
||||
}
|
||||
|
||||
type DagreNode struct {
|
||||
ID string `json:"id"`
|
||||
X float64 `json:"x"`
|
||||
|
|
@ -51,7 +59,10 @@ type dagreGraphAttrs struct {
|
|||
rankdir string
|
||||
}
|
||||
|
||||
func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||
func Layout(ctx context.Context, g *d2graph.Graph, opts *Opts) (err error) {
|
||||
if opts == nil {
|
||||
opts = &DefaultOpts
|
||||
}
|
||||
defer xdefer.Errorf(&err, "failed to dagre layout")
|
||||
|
||||
debugJS := false
|
||||
|
|
@ -65,7 +76,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
|||
|
||||
rootAttrs := dagreGraphAttrs{
|
||||
edgesep: 40,
|
||||
nodesep: 60,
|
||||
nodesep: opts.NodeSep,
|
||||
}
|
||||
isHorizontal := false
|
||||
switch g.Root.Attributes.Direction.Value {
|
||||
|
|
|
|||
|
|
@ -77,6 +77,15 @@ type ELKGraph struct {
|
|||
Edges []*ELKEdge `json:"edges,omitempty"`
|
||||
}
|
||||
|
||||
var DefaultOpts = ELKLayoutOptions{
|
||||
Algorithm: "layered",
|
||||
HierarchyHandling: "INCLUDE_CHILDREN",
|
||||
NodeSpacing: 100.0,
|
||||
EdgeNodeSpacing: 50.0,
|
||||
SelfLoopSpacing: 50.0,
|
||||
ConsiderModelOrder: "NODES_AND_EDGES",
|
||||
}
|
||||
|
||||
type ELKLayoutOptions struct {
|
||||
Algorithm string `json:"elk.algorithm,omitempty"`
|
||||
HierarchyHandling string `json:"elk.hierarchyHandling,omitempty"`
|
||||
|
|
@ -90,7 +99,10 @@ type ELKLayoutOptions struct {
|
|||
ForceNodeModelOrder bool `json:"elk.layered.crossingMinimization.forceNodeModelOrder,omitempty"`
|
||||
}
|
||||
|
||||
func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||
func Layout(ctx context.Context, g *d2graph.Graph, opts *ELKLayoutOptions) (err error) {
|
||||
if opts == nil {
|
||||
opts = &DefaultOpts
|
||||
}
|
||||
defer xdefer.Errorf(&err, "failed to ELK layout")
|
||||
|
||||
vm := goja.New()
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
package d2lib
|
||||
|
||||
import "oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
|
||||
func init() {
|
||||
dagreLayout = d2dagrelayout.Layout
|
||||
}
|
||||
11
d2lib/d2.go
11
d2lib/d2.go
|
|
@ -9,6 +9,7 @@ import (
|
|||
"oss.terrastruct.com/d2/d2compiler"
|
||||
"oss.terrastruct.com/d2/d2exporter"
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2near"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||
|
|
@ -74,12 +75,12 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target
|
|||
func getLayout(opts *CompileOptions) (func(context.Context, *d2graph.Graph) error, error) {
|
||||
if opts.Layout != nil {
|
||||
return opts.Layout, nil
|
||||
} else if os.Getenv("D2_LAYOUT") == "dagre" && dagreLayout != nil {
|
||||
return dagreLayout, nil
|
||||
} else if os.Getenv("D2_LAYOUT") == "dagre" {
|
||||
defaultLayout := func(ctx context.Context, g *d2graph.Graph) error {
|
||||
return d2dagrelayout.Layout(ctx, g, nil)
|
||||
}
|
||||
return defaultLayout, nil
|
||||
} else {
|
||||
return nil, errors.New("no available layout")
|
||||
}
|
||||
}
|
||||
|
||||
// See c.go
|
||||
var dagreLayout func(context.Context, *d2graph.Graph) error
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"oss.terrastruct.com/util-go/xdefer"
|
||||
"oss.terrastruct.com/util-go/xmain"
|
||||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
)
|
||||
|
|
@ -37,6 +38,19 @@ import (
|
|||
// the error to stderr.
|
||||
type execPlugin struct {
|
||||
path string
|
||||
opts map[string]string
|
||||
}
|
||||
|
||||
func (p execPlugin) HydrateOpts(ctx context.Context, opts interface{}) error {
|
||||
if opts != nil {
|
||||
execOpts, ok := opts.(map[string]string)
|
||||
if !ok {
|
||||
return xmain.UsageErrorf("non-exec layout options given for exec")
|
||||
}
|
||||
|
||||
p.opts = execOpts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ type Plugin interface {
|
|||
// Info returns the current info information of the plugin.
|
||||
Info(context.Context) (*PluginInfo, error)
|
||||
|
||||
HydrateOpts(context.Context, interface{}) error
|
||||
|
||||
// Layout runs the plugin's autolayout algorithm on the input graph
|
||||
// and returns a new graph with the computed placements.
|
||||
Layout(context.Context, *d2graph.Graph) error
|
||||
|
|
|
|||
|
|
@ -7,15 +7,30 @@ import (
|
|||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/util-go/xmain"
|
||||
)
|
||||
|
||||
var DagrePlugin = dagrePlugin{}
|
||||
|
||||
func init() {
|
||||
plugins = append(plugins, DagrePlugin)
|
||||
plugins = append(plugins, &DagrePlugin)
|
||||
}
|
||||
|
||||
type dagrePlugin struct{}
|
||||
type dagrePlugin struct {
|
||||
opts *d2dagrelayout.Opts
|
||||
}
|
||||
|
||||
func (p *dagrePlugin) HydrateOpts(ctx context.Context, opts interface{}) error {
|
||||
if opts != nil {
|
||||
dagreOpts, ok := opts.(d2dagrelayout.Opts)
|
||||
if !ok {
|
||||
return xmain.UsageErrorf("non-dagre layout options given for dagre")
|
||||
}
|
||||
|
||||
p.opts = &dagreOpts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p dagrePlugin) Info(context.Context) (*PluginInfo, error) {
|
||||
return &PluginInfo{
|
||||
|
|
@ -33,7 +48,7 @@ note: dagre is the primary layout algorithm for text to diagram generator Mermai
|
|||
}
|
||||
|
||||
func (p dagrePlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
||||
return d2dagrelayout.Layout(ctx, g)
|
||||
return d2dagrelayout.Layout(ctx, g, p.opts)
|
||||
}
|
||||
|
||||
func (p dagrePlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
|
||||
"oss.terrastruct.com/util-go/xmain"
|
||||
)
|
||||
|
||||
var ELKPlugin = elkPlugin{}
|
||||
|
|
@ -15,7 +16,21 @@ func init() {
|
|||
plugins = append(plugins, ELKPlugin)
|
||||
}
|
||||
|
||||
type elkPlugin struct{}
|
||||
type elkPlugin struct {
|
||||
opts *d2elklayout.ELKLayoutOptions
|
||||
}
|
||||
|
||||
func (p elkPlugin) HydrateOpts(ctx context.Context, opts interface{}) error {
|
||||
if opts != nil {
|
||||
elkOpts, ok := opts.(d2elklayout.ELKLayoutOptions)
|
||||
if !ok {
|
||||
return xmain.UsageErrorf("non-dagre layout options given for dagre")
|
||||
}
|
||||
|
||||
p.opts = &elkOpts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p elkPlugin) Info(context.Context) (*PluginInfo, error) {
|
||||
return &PluginInfo{
|
||||
|
|
@ -28,7 +43,7 @@ See https://github.com/kieler/elkjs for more.`,
|
|||
}
|
||||
|
||||
func (p elkPlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
||||
return d2elklayout.Layout(ctx, g)
|
||||
return d2elklayout.Layout(ctx, g, p.opts)
|
||||
}
|
||||
|
||||
func (p elkPlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) {
|
||||
|
|
|
|||
35
main.go
35
main.go
|
|
@ -17,6 +17,7 @@ import (
|
|||
"oss.terrastruct.com/util-go/go2"
|
||||
"oss.terrastruct.com/util-go/xmain"
|
||||
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2lib"
|
||||
"oss.terrastruct.com/d2/d2plugin"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||
|
|
@ -75,6 +76,11 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
err = populateLayoutOpts(ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ms.Opts.Flags.Parse(ms.Opts.Args)
|
||||
if !errors.Is(err, pflag.ErrHelp) && err != nil {
|
||||
return xmain.UsageErrorf("failed to parse flags: %v", err)
|
||||
|
|
@ -144,6 +150,16 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
layoutOpts, err := parseLayoutOpts(ms, *layoutFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = plugin.HydrateOpts(ctx, layoutOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pluginLocation := "bundled"
|
||||
if path != "" {
|
||||
pluginLocation = fmt.Sprintf("executable plugin at %s", humanPath(path))
|
||||
|
|
@ -288,3 +304,22 @@ func renameExt(fp string, newExt string) string {
|
|||
func DiscardSlog(ctx context.Context) context.Context {
|
||||
return ctxlog.With(ctx, slog.Make(sloghuman.Sink(io.Discard)))
|
||||
}
|
||||
|
||||
func populateLayoutOpts(ms *xmain.State) error {
|
||||
_, err := ms.Opts.Int64("", "dagre-nodesep", "", int64(d2dagrelayout.DefaultOpts.NodeSep), "number of pixels that separate nodes horizontally in the layout.")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseLayoutOpts(ms *xmain.State, layout string) (interface{}, error) {
|
||||
if layout == "dagre" {
|
||||
nodesep, _ := ms.Opts.Flags.GetInt64("dagre-nodesep")
|
||||
return d2dagrelayout.Opts{
|
||||
NodeSep: int(nodesep),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unexpected error, layout not found for parsing opts")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue