Skip to content

@graphty/graphty-element / index / NodeShapes

Variable: NodeShapes ​

const NodeShapes: ZodEnum<{ box: "box"; capsule: "capsule"; cone: "cone"; cylinder: "cylinder"; dodecahedron: "dodecahedron"; elongated_pentagonal_cupola: "elongated_pentagonal_cupola"; elongated_pentagonal_dipyramid: "elongated_pentagonal_dipyramid"; elongated_square_dipyramid: "elongated_square_dipyramid"; geodesic: "geodesic"; goldberg: "goldberg"; hexagonal_prism: "hexagonal_prism"; icosahedron: "icosahedron"; icosphere: "icosphere"; octahedron: "octahedron"; pentagonal_dipyramid: "pentagonal_dipyramid"; pentagonal_prism: "pentagonal_prism"; pentagonal_pyramid: "pentagonal_pyramid"; rhombicuboctahedron: "rhombicuboctahedron"; sphere: "sphere"; square_pyramid: "square_pyramid"; tetrahedron: "tetrahedron"; torus: "torus"; torus-knot: "torus-knot"; triangular_dipyramid: "triangular_dipyramid"; triangular_prism: "triangular_prism"; }>

Defined in: graphty-element/src/config/NodeStyle.ts:50

The single source of truth for the node shape vocabulary.

WHY THIS IS EXPORTED, and what went wrong when it was not: this enum used to be an internal detail of the zod schema. The graphty app therefore hand-copied the list into its own NODE_SHAPE_OPTIONS table, and the two copies drifted. The editor ended up offering three shapes -- "plane", "disc" and "torus" -- that this enum did not contain, and the app's write bridge quietly substituted the nearest shape it could build ("plane" -> "box", "disc" -> "geodesic", "torus" -> "torus-knot"). That is the product owner's report verbatim: "there is no plane shape, and when selected a box shows up". A control that silently draws something other than what it says is worse than a control that is absent, so the fix is structural rather than cosmetic: this enum is now a public value export (see src/config/index.ts and the package root index.ts) and the app DERIVES its picker from NodeShapes.options. The two lists can no longer drift, because there is only one list.

TWO INVARIANTS HANG OFF THIS ENUM. Break either one and nodes stop rendering:

  1. NodeMesh's static registration block (src/meshes/NodeMesh.ts) MUST register a shape creator for every member here. NodeMesh.createMeshWithoutCache throws unknown shape: <type> for anything unregistered, which surfaces as a node that never appears. test/node-mesh-gradient.test.ts asserts the whole enum is covered, so adding a member without a creator is a test failure rather than a runtime surprise.

  2. The app's picker is generated from NodeShapes.options, so adding a member here adds an option to the dropdown and removing one removes it. Do not add a member speculatively.

WHY "torus" IS HERE NOW: NodeMesh has always registered a working torus creator built on MeshBuilder.CreateTorus; only this enum omitted it, which is the sole reason Torus was being degraded to torus-knot. Adding the member widens the zod enum, which is backward compatible -- every previously valid style still parses. Layers already saved with "torus-knot" keep rendering a torus-knot; only a fresh pick of Torus gets the new shape.

WHY "plane" AND "disc" ARE NOT HERE, and must not be added casually: MeshBuilder.CreatePlane produces a zero-thickness, single-sided quad. NodeMesh.create caches ONE mesh per style id and instances it, so a plane would need explicit double-sided orientation or half the graph's nodes would vanish depending on camera side, and edge-on it vanishes for every camera regardless. Worse, edge attachment is bounding-sphere and ray-intersection based (see Edge.ts), and a ray misses a near-zero-thickness quad for most incident angles, so every edge touching a plane node would fall back to centre-to-centre attachment. A node you cannot see, wired by edges that do not touch it, is a bigger defect than a missing dropdown entry. If flat, camera-facing nodes are wanted, the right feature is a billboarded quad with its own answer for edge attachment -- a specced piece of work, not an enum entry.