Skip to content

WIP Node Graph Introspection #33

@kwokcb

Description

@kwokcb

Progress:

  • Export to Mermaid and dot (GraphViz) from arbitrary <output> or surfaceshader as roots.
  • Allow export using category names or instance names for nodes. Interface inputs and outputs use instance names for
    clarity
  • Extrapolate a new interface for input / output filtering Filter class (under discussion)
  • Determined ability to embed mermaid into parsed cpp files with additional html header.
  • Allow for merging together of graphs as every element created per graph is unique within a document

Functional NodeGraph

  • Supported and tested in unit test and mxdoc.py
  • Example from Lama generation via new version of mxdoc.py. Mermaid graph inside HTML output, with
    using DoxygenAwesome CSS file

duced in unit tests (GraphViz output)

GraphViz Results:

  • dot exporter uses the exact same logic (common) as the Mermaid one with a few methods to override
  • Example brick shader (category names)
    standard_surface_brick_procedural_ dot
  • Example cascading nodegraphs
    cascade_nodegraphs_ dot

Mermaid Results:

  • Example Brick shader (category names)
graph TD;
    NG_BrickPattern_node_clamp_0[clamp] --> NG_BrickPattern_base_color_output([base_color_output]) --.base_color--> N_StandardSurface[standard_surface]
    style NG_BrickPattern_base_color_output fill:#1b1,color:#111
    NG_BrickPattern_node_mix_8[mix] --.in--> NG_BrickPattern_node_clamp_0[clamp]
    NG_BrickPattern_node_multiply_5[multiply] --.fg--> NG_BrickPattern_node_mix_8[mix]
    NG_BrickPattern_node_mix_6[mix] --.in1--> NG_BrickPattern_node_multiply_5[multiply]
    NG_BrickPattern_dirt_color([dirt_color]) ==.fg==> NG_BrickPattern_node_mix_6[mix]
    style NG_BrickPattern_dirt_color fill:#0bb,color:#111
    NG_BrickPattern_node_hsvtorgb_17[hsvtorgb] --.bg--> NG_BrickPattern_node_mix_6[mix]
    NG_BrickPattern_node_add_16[add] --.in--> NG_BrickPattern_node_hsvtorgb_17[hsvtorgb]
    NG_BrickPattern_node_combine3_color3_13[combine3] --.in1--> NG_BrickPattern_node_add_16[add]
    NG_BrickPattern_node_multiply_14[multiply] --.in1--> NG_BrickPattern_node_combine3_color3_13[combine3]
    NG_BrickPattern_hue_variation([hue_variation]) ==.in2==> NG_BrickPattern_node_multiply_14[multiply]
    style NG_BrickPattern_hue_variation fill:#0bb,color:#111
    NG_BrickPattern_node_subtract_18[subtract] --.in1--> NG_BrickPattern_node_multiply_14[multiply]
    NG_BrickPattern_node_add_19[add] --.in1--> NG_BrickPattern_node_subtract_18[subtract]
    NG_BrickPattern_node_multiply_25[multiply] --.in1--> NG_BrickPattern_node_add_19[add]
    NG_BrickPattern_hue_variation([hue_variation]) ==.in1==> NG_BrickPattern_node_multiply_25[multiply]
    style NG_BrickPattern_hue_variation fill:#0bb,color:#111
    NG_BrickPattern_node_tiledimage_float_26[tiledimage] --.in2--> NG_BrickPattern_node_multiply_25[multiply]
    NG_BrickPattern_node_convert_1[convert] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_26[tiledimage]
    NG_BrickPattern_uvtiling([uvtiling]) ==.in==> NG_BrickPattern_node_convert_1[convert]
    style NG_BrickPattern_uvtiling fill:#0bb,color:#111
    NG_BrickPattern_node_tiledimage_float_7[tiledimage] --.in2--> NG_BrickPattern_node_add_19[add]
    NG_BrickPattern_node_convert_1[convert] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_7[tiledimage]
    NG_BrickPattern_node_multiply_15[multiply] --.in3--> NG_BrickPattern_node_combine3_color3_13[combine3]
    NG_BrickPattern_node_add_19[add] --.in1--> NG_BrickPattern_node_multiply_15[multiply]
    NG_BrickPattern_node_multiply_20[multiply] --.in2--> NG_BrickPattern_node_multiply_15[multiply]
    NG_BrickPattern_value_variation([value_variation]) ==.in1==> NG_BrickPattern_node_multiply_20[multiply]
    style NG_BrickPattern_value_variation fill:#0bb,color:#111
    NG_BrickPattern_node_tiledimage_float_26[tiledimage] --.in2--> NG_BrickPattern_node_multiply_20[multiply]
    NG_BrickPattern_node_rgbtohsv_12[rgbtohsv] --.in2--> NG_BrickPattern_node_add_16[add]
    NG_BrickPattern_brick_color([brick_color]) ==.in==> NG_BrickPattern_node_rgbtohsv_12[rgbtohsv]
    style NG_BrickPattern_brick_color fill:#0bb,color:#111
    NG_BrickPattern_node_multiply_23[multiply] --.mix--> NG_BrickPattern_node_mix_6[mix]
    NG_BrickPattern_dirt_amount([dirt_amount]) ==.in1==> NG_BrickPattern_node_multiply_23[multiply]
    style NG_BrickPattern_dirt_amount fill:#0bb,color:#111
    NG_BrickPattern_node_tiledimage_float_24[tiledimage] --.in2--> NG_BrickPattern_node_multiply_23[multiply]
    NG_BrickPattern_node_convert_1[convert] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_24[tiledimage]
    NG_BrickPattern_node_tiledimage_float_7[tiledimage] --.in2--> NG_BrickPattern_node_multiply_5[multiply]
    NG_BrickPattern_node_multiply_9[multiply] --.bg--> NG_BrickPattern_node_mix_8[mix]
    NG_BrickPattern_node_color_11[constant] --.in1--> NG_BrickPattern_node_multiply_9[multiply]
    NG_BrickPattern_node_tiledimage_float_7[tiledimage] --.in2--> NG_BrickPattern_node_multiply_9[multiply]
    NG_BrickPattern_node_tiledimage_float_10[tiledimage] --.mix--> NG_BrickPattern_node_mix_8[mix]
    NG_BrickPattern_node_convert_1[convert] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_10[tiledimage]
    NG_BrickPattern_node_multiply_1[multiply] --> NG_BrickPattern_specular_roughness_output([specular_roughness_output]) --.specular_roughness--> N_StandardSurface[standard_surface]
    style NG_BrickPattern_specular_roughness_output fill:#1b1,color:#111
    NG_BrickPattern_node_divide_21[divide] --.in1--> NG_BrickPattern_node_multiply_1[multiply]
    NG_BrickPattern_roughness_amount([roughness_amount]) ==.in1==> NG_BrickPattern_node_divide_21[divide]
    style NG_BrickPattern_roughness_amount fill:#0bb,color:#111
    NG_BrickPattern_node_max_1[max] --.in2--> NG_BrickPattern_node_divide_21[divide]
    NG_BrickPattern_node_tiledimage_float_10[tiledimage] --.in1--> NG_BrickPattern_node_max_1[max]
    NG_BrickPattern_node_tiledimage_float_22[tiledimage] --.in2--> NG_BrickPattern_node_multiply_1[multiply]
    NG_BrickPattern_node_convert_1[convert] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_22[tiledimage]
    NG_BrickPattern_node_normalmap_3[normalmap] --> NG_BrickPattern_normal_output([normal_output]) --.normal--> N_StandardSurface[standard_surface]
    style NG_BrickPattern_normal_output fill:#1b1,color:#111
    NG_BrickPattern_node_tiledimage_vector3_27[tiledimage] --.in--> NG_BrickPattern_node_normalmap_3[normalmap]
    NG_BrickPattern_node_convert_1[convert] --.uvtiling--> NG_BrickPattern_node_tiledimage_vector3_27[tiledimage]
  subgraph NG_BrickPattern
    NG_BrickPattern_base_color_output
    NG_BrickPattern_brick_color
    NG_BrickPattern_dirt_amount
    NG_BrickPattern_dirt_color
    NG_BrickPattern_hue_variation
    NG_BrickPattern_node_add_16
    NG_BrickPattern_node_add_19
    NG_BrickPattern_node_clamp_0
    NG_BrickPattern_node_color_11
    NG_BrickPattern_node_combine3_color3_13
    NG_BrickPattern_node_convert_1
    NG_BrickPattern_node_divide_21
    NG_BrickPattern_node_hsvtorgb_17
    NG_BrickPattern_node_max_1
    NG_BrickPattern_node_mix_6
    NG_BrickPattern_node_mix_8
    NG_BrickPattern_node_multiply_1
    NG_BrickPattern_node_multiply_14
    NG_BrickPattern_node_multiply_15
    NG_BrickPattern_node_multiply_20
    NG_BrickPattern_node_multiply_23
    NG_BrickPattern_node_multiply_25
    NG_BrickPattern_node_multiply_5
    NG_BrickPattern_node_multiply_9
    NG_BrickPattern_node_normalmap_3
    NG_BrickPattern_node_rgbtohsv_12
    NG_BrickPattern_node_subtract_18
    NG_BrickPattern_node_tiledimage_float_10
    NG_BrickPattern_node_tiledimage_float_22
    NG_BrickPattern_node_tiledimage_float_24
    NG_BrickPattern_node_tiledimage_float_26
    NG_BrickPattern_node_tiledimage_float_7
    NG_BrickPattern_node_tiledimage_vector3_27
    NG_BrickPattern_normal_output
    NG_BrickPattern_roughness_amount
    NG_BrickPattern_specular_roughness_output
    NG_BrickPattern_uvtiling
    NG_BrickPattern_value_variation
  end
Loading
  • Example Brick shader (instance names)
graph TD;
    NG_BrickPattern_node_clamp_0[NG_BrickPattern_node_clamp_0] --> NG_BrickPattern_base_color_output([base_color_output]) --.base_color--> N_StandardSurface[N_StandardSurface]
    style NG_BrickPattern_base_color_output fill:#1b1,color:#111
    NG_BrickPattern_node_mix_8[NG_BrickPattern_node_mix_8] --.in--> NG_BrickPattern_node_clamp_0[node_clamp_0]
    NG_BrickPattern_node_multiply_5[NG_BrickPattern_node_multiply_5] --.fg--> NG_BrickPattern_node_mix_8[node_mix_8]
    NG_BrickPattern_node_mix_6[NG_BrickPattern_node_mix_6] --.in1--> NG_BrickPattern_node_multiply_5[node_multiply_5]
    NG_BrickPattern_dirt_color([dirt_color]) ==.fg==> NG_BrickPattern_node_mix_6[NG_BrickPattern/node_mix_6]
    style NG_BrickPattern_dirt_color fill:#0bb,color:#111
    NG_BrickPattern_node_hsvtorgb_17[NG_BrickPattern_node_hsvtorgb_17] --.bg--> NG_BrickPattern_node_mix_6[node_mix_6]
    NG_BrickPattern_node_add_16[NG_BrickPattern_node_add_16] --.in--> NG_BrickPattern_node_hsvtorgb_17[node_hsvtorgb_17]
    NG_BrickPattern_node_combine3_color3_13[NG_BrickPattern_node_combine3_color3_13] --.in1--> NG_BrickPattern_node_add_16[node_add_16]
    NG_BrickPattern_node_multiply_14[NG_BrickPattern_node_multiply_14] --.in1--> NG_BrickPattern_node_combine3_color3_13[node_combine3_color3_13]
    NG_BrickPattern_hue_variation([hue_variation]) ==.in2==> NG_BrickPattern_node_multiply_14[NG_BrickPattern/node_multiply_14]
    style NG_BrickPattern_hue_variation fill:#0bb,color:#111
    NG_BrickPattern_node_subtract_18[NG_BrickPattern_node_subtract_18] --.in1--> NG_BrickPattern_node_multiply_14[node_multiply_14]
    NG_BrickPattern_node_add_19[NG_BrickPattern_node_add_19] --.in1--> NG_BrickPattern_node_subtract_18[node_subtract_18]
    NG_BrickPattern_node_multiply_25[NG_BrickPattern_node_multiply_25] --.in1--> NG_BrickPattern_node_add_19[node_add_19]
    NG_BrickPattern_hue_variation([hue_variation]) ==.in1==> NG_BrickPattern_node_multiply_25[NG_BrickPattern/node_multiply_25]
    style NG_BrickPattern_hue_variation fill:#0bb,color:#111
    NG_BrickPattern_node_tiledimage_float_26[NG_BrickPattern_node_tiledimage_float_26] --.in2--> NG_BrickPattern_node_multiply_25[node_multiply_25]
    NG_BrickPattern_node_convert_1[NG_BrickPattern_node_convert_1] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_26[node_tiledimage_float_26]
    NG_BrickPattern_uvtiling([uvtiling]) ==.in==> NG_BrickPattern_node_convert_1[NG_BrickPattern/node_convert_1]
    style NG_BrickPattern_uvtiling fill:#0bb,color:#111
    NG_BrickPattern_node_tiledimage_float_7[NG_BrickPattern_node_tiledimage_float_7] --.in2--> NG_BrickPattern_node_add_19[node_add_19]
    NG_BrickPattern_node_convert_1[NG_BrickPattern_node_convert_1] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_7[node_tiledimage_float_7]
    NG_BrickPattern_node_multiply_15[NG_BrickPattern_node_multiply_15] --.in3--> NG_BrickPattern_node_combine3_color3_13[node_combine3_color3_13]
    NG_BrickPattern_node_add_19[NG_BrickPattern_node_add_19] --.in1--> NG_BrickPattern_node_multiply_15[node_multiply_15]
    NG_BrickPattern_node_multiply_20[NG_BrickPattern_node_multiply_20] --.in2--> NG_BrickPattern_node_multiply_15[node_multiply_15]
    NG_BrickPattern_value_variation([value_variation]) ==.in1==> NG_BrickPattern_node_multiply_20[NG_BrickPattern/node_multiply_20]
    style NG_BrickPattern_value_variation fill:#0bb,color:#111
    NG_BrickPattern_node_tiledimage_float_26[NG_BrickPattern_node_tiledimage_float_26] --.in2--> NG_BrickPattern_node_multiply_20[node_multiply_20]
    NG_BrickPattern_node_rgbtohsv_12[NG_BrickPattern_node_rgbtohsv_12] --.in2--> NG_BrickPattern_node_add_16[node_add_16]
    NG_BrickPattern_brick_color([brick_color]) ==.in==> NG_BrickPattern_node_rgbtohsv_12[NG_BrickPattern/node_rgbtohsv_12]
    style NG_BrickPattern_brick_color fill:#0bb,color:#111
    NG_BrickPattern_node_multiply_23[NG_BrickPattern_node_multiply_23] --.mix--> NG_BrickPattern_node_mix_6[node_mix_6]
    NG_BrickPattern_dirt_amount([dirt_amount]) ==.in1==> NG_BrickPattern_node_multiply_23[NG_BrickPattern/node_multiply_23]
    style NG_BrickPattern_dirt_amount fill:#0bb,color:#111
    NG_BrickPattern_node_tiledimage_float_24[NG_BrickPattern_node_tiledimage_float_24] --.in2--> NG_BrickPattern_node_multiply_23[node_multiply_23]
    NG_BrickPattern_node_convert_1[NG_BrickPattern_node_convert_1] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_24[node_tiledimage_float_24]
    NG_BrickPattern_node_tiledimage_float_7[NG_BrickPattern_node_tiledimage_float_7] --.in2--> NG_BrickPattern_node_multiply_5[node_multiply_5]
    NG_BrickPattern_node_multiply_9[NG_BrickPattern_node_multiply_9] --.bg--> NG_BrickPattern_node_mix_8[node_mix_8]
    NG_BrickPattern_node_color_11[NG_BrickPattern_node_color_11] --.in1--> NG_BrickPattern_node_multiply_9[node_multiply_9]
    NG_BrickPattern_node_tiledimage_float_7[NG_BrickPattern_node_tiledimage_float_7] --.in2--> NG_BrickPattern_node_multiply_9[node_multiply_9]
    NG_BrickPattern_node_tiledimage_float_10[NG_BrickPattern_node_tiledimage_float_10] --.mix--> NG_BrickPattern_node_mix_8[node_mix_8]
    NG_BrickPattern_node_convert_1[NG_BrickPattern_node_convert_1] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_10[node_tiledimage_float_10]
    NG_BrickPattern_node_multiply_1[NG_BrickPattern_node_multiply_1] --> NG_BrickPattern_specular_roughness_output([specular_roughness_output]) --.specular_roughness--> N_StandardSurface[N_StandardSurface]
    style NG_BrickPattern_specular_roughness_output fill:#1b1,color:#111
    NG_BrickPattern_node_divide_21[NG_BrickPattern_node_divide_21] --.in1--> NG_BrickPattern_node_multiply_1[node_multiply_1]
    NG_BrickPattern_roughness_amount([roughness_amount]) ==.in1==> NG_BrickPattern_node_divide_21[NG_BrickPattern/node_divide_21]
    style NG_BrickPattern_roughness_amount fill:#0bb,color:#111
    NG_BrickPattern_node_max_1[NG_BrickPattern_node_max_1] --.in2--> NG_BrickPattern_node_divide_21[node_divide_21]
    NG_BrickPattern_node_tiledimage_float_10[NG_BrickPattern_node_tiledimage_float_10] --.in1--> NG_BrickPattern_node_max_1[node_max_1]
    NG_BrickPattern_node_tiledimage_float_22[NG_BrickPattern_node_tiledimage_float_22] --.in2--> NG_BrickPattern_node_multiply_1[node_multiply_1]
    NG_BrickPattern_node_convert_1[NG_BrickPattern_node_convert_1] --.uvtiling--> NG_BrickPattern_node_tiledimage_float_22[node_tiledimage_float_22]
    NG_BrickPattern_node_normalmap_3[NG_BrickPattern_node_normalmap_3] --> NG_BrickPattern_normal_output([normal_output]) --.normal--> N_StandardSurface[N_StandardSurface]
    style NG_BrickPattern_normal_output fill:#1b1,color:#111
    NG_BrickPattern_node_tiledimage_vector3_27[NG_BrickPattern_node_tiledimage_vector3_27] --.in--> NG_BrickPattern_node_normalmap_3[node_normalmap_3]
    NG_BrickPattern_node_convert_1[NG_BrickPattern_node_convert_1] --.uvtiling--> NG_BrickPattern_node_tiledimage_vector3_27[node_tiledimage_vector3_27]
  subgraph NG_BrickPattern
    NG_BrickPattern_base_color_output
    NG_BrickPattern_brick_color
    NG_BrickPattern_dirt_amount
    NG_BrickPattern_dirt_color
    NG_BrickPattern_hue_variation
    NG_BrickPattern_node_add_16
    NG_BrickPattern_node_add_19
    NG_BrickPattern_node_clamp_0
    NG_BrickPattern_node_color_11
    NG_BrickPattern_node_combine3_color3_13
    NG_BrickPattern_node_convert_1
    NG_BrickPattern_node_divide_21
    NG_BrickPattern_node_hsvtorgb_17
    NG_BrickPattern_node_max_1
    NG_BrickPattern_node_mix_6
    NG_BrickPattern_node_mix_8
    NG_BrickPattern_node_multiply_1
    NG_BrickPattern_node_multiply_14
    NG_BrickPattern_node_multiply_15
    NG_BrickPattern_node_multiply_20
    NG_BrickPattern_node_multiply_23
    NG_BrickPattern_node_multiply_25
    NG_BrickPattern_node_multiply_5
    NG_BrickPattern_node_multiply_9
    NG_BrickPattern_node_normalmap_3
    NG_BrickPattern_node_rgbtohsv_12
    NG_BrickPattern_node_subtract_18
    NG_BrickPattern_node_tiledimage_float_10
    NG_BrickPattern_node_tiledimage_float_22
    NG_BrickPattern_node_tiledimage_float_24
    NG_BrickPattern_node_tiledimage_float_26
    NG_BrickPattern_node_tiledimage_float_7
    NG_BrickPattern_node_tiledimage_vector3_27
    NG_BrickPattern_normal_output
    NG_BrickPattern_roughness_amount
    NG_BrickPattern_specular_roughness_output
    NG_BrickPattern_uvtiling
    NG_BrickPattern_value_variation
  end
Loading
  • Example of cascading nodegraph output from 2 source materials "merge" (text copied) into same graph:
graph TD;
    upstream1_remove_red[multiply] --> upstream1_upstream1_out2([upstream1_out2]) --.base_color--> standard_surface1[standard_surface]
    style upstream1_upstream1_out2 fill:#1b1,color:#111
    upstream2_make_red[multiply] --.in1--> upstream1_remove_red[multiply]
    upstream3_upstream_image1[image] --.in1--> upstream2_make_red[multiply]
    upstream3_file1([file1]) ==.file==> upstream3_upstream_image1[image]
    style upstream3_file1 fill:#0bb,color:#111
  subgraph upstream1
    upstream1_remove_red
    upstream1_upstream1_out2
  end
  subgraph upstream2
    upstream2_make_red
  end
  subgraph upstream3
    upstream3_file1
    upstream3_upstream_image1
  end

    upstream1_make_yellow[multiply] --> upstream1_upstream1_out1([upstream1_out1]) --.base_color--> standard_surface[standard_surface]
    style upstream1_upstream1_out1 fill:#1b1,color:#111
    upstream2_multiply_by_image[multiply] --.in1--> upstream1_make_yellow[multiply]
    upstream3_upstream_image[image] --.in1--> upstream2_multiply_by_image[multiply]
    upstream3_file([file]) ==.file==> upstream3_upstream_image[image]
    style upstream3_file fill:#0bb,color:#111
    upstream2_image[image] --.in2--> upstream2_multiply_by_image[multiply]
  subgraph upstream1
    upstream1_make_yellow
    upstream1_upstream1_out1
  end
  subgraph upstream2
    upstream2_image
    upstream2_multiply_by_image
  end
  subgraph upstream3
    upstream3_file
    upstream3_upstream_image
  end
Loading
  • Same graphs with instance instead of category names:
graph TD;
    upstream1_remove_red[upstream1_remove_red] --> upstream1_upstream1_out2([upstream1_out2]) --.base_color--> standard_surface1[standard_surface1]
    style upstream1_upstream1_out2 fill:#1b1,color:#111
    upstream2_make_red[upstream2_make_red] --.in1--> upstream1_remove_red[remove_red]
    upstream3_upstream_image1[upstream3_upstream_image1] --.in1--> upstream2_make_red[make_red]
    upstream3_file1([file1]) ==.file==> upstream3_upstream_image1[upstream3/upstream_image1]
    style upstream3_file1 fill:#0bb,color:#111
  subgraph upstream1
    upstream1_remove_red
    upstream1_upstream1_out2
  end
  subgraph upstream2
    upstream2_make_red
  end
  subgraph upstream3
    upstream3_file1
    upstream3_upstream_image1
  end

    upstream1_make_yellow[upstream1_make_yellow] --> upstream1_upstream1_out1([upstream1_out1]) --.base_color--> standard_surface[standard_surface]
    style upstream1_upstream1_out1 fill:#1b1,color:#111
    upstream2_multiply_by_image[upstream2_multiply_by_image] --.in1--> upstream1_make_yellow[make_yellow]
    upstream3_upstream_image[upstream3_upstream_image] --.in1--> upstream2_multiply_by_image[multiply_by_image]
    upstream3_file([file]) ==.file==> upstream3_upstream_image[upstream3/upstream_image]
    style upstream3_file fill:#0bb,color:#111
    upstream2_image[upstream2_image] --.in2--> upstream2_multiply_by_image[multiply_by_image]
  subgraph upstream1
    upstream1_make_yellow
    upstream1_upstream1_out1
  end
  subgraph upstream2
    upstream2_image
    upstream2_multiply_by_image
  end
  subgraph upstream3
    upstream3_file
    upstream3_upstream_image
  end
Loading
  • Conditional node test. Note TBD if this should be an option as it
    is not cheap to find the corresponding NodeDef for a Node.
graph TD; 
  subgraph ifgreater_float
    ifgreater_float_ifgreater1
    ifgreater_float_out
  end
    ifgreater_float_ifgreater1{ifgreater} --> ifgreater_float_out([output])
    style ifgreater_float_out fill:#1b1, color:#111
Loading

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions