[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:line-shape-junctions-and-label-placement":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Runtime Line Shape, Junction, and Label Placement Controls\n\n## What This Example Builds\nThis example builds a full-screen relationship map where the same loaded graph can be restyled live. The canvas shows circular avatar nodes on a tiled background, color-coded relationship edges, and a floating control window that switches every line between straight and curved routing, changes how curved lines attach to nodes, and toggles labels between boxed text and text placed on the path.\n\nThe graph content itself stays fixed after load. The main highlight is that the user can compare several edge-rendering behaviors on one dense avatar graph without rebuilding the dataset or changing node positions.\n\n## How the Data Is Organized\nThe data comes from a local `RGJsonData` object in `mock-data-api.ts`. It declares `rootId: \"N13\"`, twenty-one nodes, and a larger set of line records. Each node carries visible styling fields such as `color`, `borderColor`, and `data.icon`, while each line carries `from`, `to`, `text`, `color`, `fontColor`, and a typed `data` payload.\n\nThere is no real preprocessing before layout. `fetchJsonData()` only wraps the static object in a short Promise and `initializeGraph()` sends that result straight into `setJsonData()`. Some node pairs intentionally appear more than once, such as `N1 -> N15` and `N13 -> N8`, which helps the example show how line shape and attachment choices behave on repeated connectors. In real projects, the same structure can represent people networks, investigation links, stakeholder maps, or service relationships with several connection types between the same entities.\n\n## How relation-graph Is Used\n`RGProvider` wraps the page and `RGHooks.useGraphInstance()` is the main runtime control surface inside `MyGraph`. The graph options use the built-in force layout, keep debug mode off, set circular nodes, keep `multiLineDistance` at `20`, cap the layout iteration count at `50`, and provide fallback node color and border settings.\n\nThe important implementation detail is that the example does not rebuild `RGJsonData` when the selectors change. After the async load completes, `initializeGraph()` calls `loading()`, `setJsonData()`, `updateMyGraphData()`, `clearLoading()`, `moveToCenter()`, and `zoomToFit()`. Later, a second effect watches `lineShape`, `lineJunctionPoint`, and `textOnPath`, then walks through `graphInstance.getLines()` and rewrites each rendered line with `updateLine(...)`.\n\nThat line update pass is where relation-graph features are combined. `lineShape` switches between `RGLineShape.StandardStraight` and `RGLineShape.StandardCurve`. When straight mode is active, both endpoints are forced back to `RGJunctionPoint.border`; when curved mode is active, the selected junction value is written into both `fromJunctionPoint` and `toJunctionPoint`. The same pass also toggles `useTextOnPath`, so label placement changes without reloading the graph.\n\nThe example also uses slots and shared helper components. `RGSlotOnNode` replaces the default node body with circular portrait avatars and labels below each node. `DraggableWindow` hosts the line controls and can open a shared `CanvasSettingsPanel`, where `RGHooks.useGraphStore()` and `graphInstance.setOptions()` switch wheel and drag behavior. The shared panel also exports the current graph image through `prepareForImageGeneration()`, `domToImageByModernScreenshot()`, and `restoreAfterImageGeneration()`.\n\nLocal SCSS finishes the presentation by adding the tiled canvas background, white boxed line labels, and a blue halo for checked nodes.\n\n## Key Interactions\n- The `Line Shape` selector rewrites every loaded edge to either straight or curved routing.\n- The `Line JunctionPoint` selector appears only when curved routing is active and lets the user compare border, paired-side, and single-side anchor modes.\n- The `Line Text On Path` selector toggles labels between ordinary boxed labels and text rendered directly on the line path.\n- Clicking empty canvas space calls `clearChecked()`, which removes checked highlights from the graph.\n- The floating helper window can be dragged, minimized, switched into a canvas-settings overlay, and used to download the current graph as an image.\n\n## Key Code Fragments\nThis data fragment shows that the example starts from a fixed `RGJsonData` object with a root node, per-node styling, and custom avatar URLs.\n\n```ts\nconst jsonData = {\n    \"rootId\": \"N13\",\n    \"nodes\": [\n        {\n            \"id\": \"N1\",\n            \"text\": \"Liangping.Hou\",\n            \"color\": \"#ec6941\",\n            \"borderColor\": \"#ff875e\",\n            \"data\": {\n```\n\nThis options block proves that the graph uses force layout, circular nodes, and a fixed repeated-edge spacing before runtime line updates begin.\n\n```tsx\nconst graphOptions: RGOptions = {\n    debug: false,\n    defaultLineShape: RGLineShape.StandardStraight,\n    defaultNodeShape: RGNodeShape.circle,\n    multiLineDistance: 20,\n    layout: {\n        layoutName: 'force',\n        maxLayoutTimes: 50\n    },\n    defaultNodeBorderWidth: 2,\n```\n\nThis initialization flow shows that the graph loads once, applies the current line settings, and then centers and fits the viewport.\n\n```tsx\nconst initializeGraph = async () => {\n    const myJsonData: RGJsonData = await fetchJsonData();\n\n    graphInstance.loading();\n    await graphInstance.setJsonData(myJsonData);\n    await updateMyGraphData();\n    graphInstance.clearLoading();\n    graphInstance.moveToCenter();\n    graphInstance.zoomToFit();\n};\n```\n\nThis update function is the core technique: it rewrites every already rendered line instead of regenerating the dataset.\n\n```tsx\nconst updateMyGraphData = async () => {\n    graphInstance.getLines().forEach((line) => {\n        graphInstance.updateLine(line, {\n            lineShape,\n            fromJunctionPoint: lineShape === RGLineShape.StandardStraight ? RGJunctionPoint.border : lineJunctionPoint,\n            toJunctionPoint: lineShape === RGLineShape.StandardStraight ? RGJunctionPoint.border : lineJunctionPoint,\n            useTextOnPath: textOnPath\n        });\n    });\n};\n```\n\nThis control fragment proves that junction selection is conditional and only matters when the graph is showing curved lines.\n\n```tsx\n{\n    lineShape !== RGLineShape.StandardStraight &&\n    \u003Cdiv>\n        \u003Cdiv className=\"text-base py-2\">Line JunctionPoint:\u003C/div>\n        \u003CSimpleUISelect\n            data={[\n                { value: RGJunctionPoint.border, text: 'Border' },\n                { value: RGJunctionPoint.ltrb, text: 'Left/Top/Right/Bottom' },\n                { value: RGJunctionPoint.lr, text: 'Left/Right' },\n                { value: RGJunctionPoint.tb, text: 'Top/Bottom' },\n```\n\nThis node slot shows that the example combines graph-wide line controls with custom avatar node rendering.\n\n```tsx\n\u003CRGSlotOnNode>\n    {({ node }: RGNodeSlotProps) => (\n        \u003Cdiv className=\"w-12 h-12 flex place-items-center justify-center\">\n            \u003Cdiv className=\"my-node-avatar\" style={{ backgroundImage: `url(${node.data?.icon})` }} />\n            \u003Cdiv className=\"absolute transform translate-y-[35px]\" style={{ color: node.color }}>{node.text}\u003C/div>\n        \u003C/div>\n    )}\n\u003C/RGSlotOnNode>\n```\n\nThis shared settings fragment shows that the floating workspace can also export the current graph image without changing graph data.\n\n```tsx\nconst downloadImage = async () => {\n    const canvasDom = await graphInstance.prepareForImageGeneration();\n    let graphBackgroundColor = graphInstance.getOptions().backgroundColor;\n    if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {\n        graphBackgroundColor = '#ffffff';\n    }\n    const imageBlob = await domToImageByModernScreenshot(canvasDom, {\n        backgroundColor: graphBackgroundColor\n    });\n    if (imageBlob) {\n        downloadBlob(imageBlob, 'my-image-name');\n    }\n```\n\n## What Makes This Example Distinct\nThe comparison records put this example closest to `line-multi-lines-gap`, `custom-line-style`, `search-and-focus`, and `use-dagre-layout-2`, but they also show a narrower lesson. Here the primary focus is graph-wide control of built-in edge behavior on an already loaded graph: shape switching, curved-line junction selection, and text-on-path switching.\n\nCompared with `line-multi-lines-gap`, this example spends less effort on spacing repeated edges and more effort on how curved connectors attach to nodes. Compared with `custom-line-style`, the distinguishing value is not CSS skinning but runtime control of built-in line geometry and label placement. Compared with `search-and-focus` and `use-dagre-layout-2`, the graph is not mainly about navigation or relayout; node placement stays in the force layout while only edge rendering changes.\n\nThe comparison file also limits the claims that are safe to make. The floating helper window, canvas settings, image export, and avatar-style relationship scene are not unique to this example, and it is not a structural editor. What makes it stand out is the combination of a dense avatar relationship map, graph-wide `getLines()` plus `updateLine()` updates, a curved-only junction selector, and a runtime switch between boxed labels and text on the path.\n\n## Where Else This Pattern Applies\n- Relationship or social graphs where teams need to compare readable connector styles on a fixed dataset before choosing a production default.\n- Investigation, risk, or fraud maps where one pair of entities can carry several different relationship types and edge readability matters more than structure editing.\n- Service and dependency diagrams that need a compact control surface for connector routing, anchor policy, and label placement without rerunning layout logic.\n- Internal demo or QA workbenches where designers and engineers need to review line behavior on a realistic graph scene and export snapshots of the current state.\n",false,500,1782615374140]