Skip to content

Commit

Permalink
Oh well... I tried
Browse files Browse the repository at this point in the history
  • Loading branch information
stereobooster committed Oct 14, 2024
1 parent 6bd07e2 commit f948194
Show file tree
Hide file tree
Showing 20 changed files with 1,424 additions and 76 deletions.
131 changes: 131 additions & 0 deletions packages/rehype-vizdom/src/dotToSvg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { fromDot, NodeModel, NodeRef } from "ts-graphviz";
import {
DirectedGraph,
RankDir,
Shape,
VertexWeakRef,
} from "@stereobooster/vizdom-ts-esm";

function titleCase(str: string) {
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
}

const shapes: Record<string, number> = {
Rectangle: Shape.Rectangle,
Rect: Shape.Rectangle,
Square: Shape.Square,
Box: Shape.Square,
Circle: Shape.Circle,
Ellipse: Shape.Ellipse,
Oval: Shape.Ellipse,
Default: Shape.Ellipse,
Triangle: Shape.Triangle,
Diamond: Shape.Diamond,
Plaintext: Shape.Plaintext,
Underline: Shape.Underline,
};

function shape(node: NodeModel) {
const sh = node.attributes.get("shape");
if (!sh) return shapes.Default;
return shapes[titleCase(sh)] ?? shapes.Default;
}

const rankDirs = {
TB: RankDir.TB,
BT: RankDir.BT,
LR: RankDir.LR,
RL: RankDir.RL,
};

export async function dotToSvg(code: string) {
const graph = new DirectedGraph();
const model = fromDot(code);
graph.attrs().set((graph) => {
if (!graph.layout) graph.layout = {};
if (model.get("rankdir")) {
graph.layout.rank_dir = rankDirs[model.get("rankdir")!];
}
return graph;
});

const nodes: Record<string, VertexWeakRef> = {};
model.nodes.forEach((node) => {
nodes[node.id] = graph.new_vertex({
render: {
id: node.id,
label: node.attributes.get("label"),
tooltip: node.attributes.get("tooltip"),
shape: shape(node),
// fill_color: node.attributes.get("fillcolor") as string,
// font_color: node.attributes.get("fontcolor") as string,
// color: node.attributes.get("color") as string,
// font_size: node.attributes.get("fontsize"),
// pen_width: node.attributes.get("penwidth"),
// style: node.attributes.get("style") as any,
},
});
});

model.subgraphs.forEach((subg) => {
// TODO
});

model.edges.forEach((edge) => {
for (let i = 0; i <= edge.targets.length - 2; i++) {
const from: NodeRef[] = (
Array.isArray(edge.targets[i]) ? edge.targets[i] : [edge.targets[i]]
) as any;
const to: NodeRef[] = (
Array.isArray(edge.targets[i + 1])
? edge.targets[i + 1]
: [edge.targets[i + 1]]
) as any;

from.forEach((f) => {
if (!nodes[f.id]) {
nodes[f.id] = graph.new_vertex({
render: {
label: f.id,
id: f.id,
shape: shapes.Default,
},
});
}
to.forEach((t) => {
if (!nodes[t.id]) {
nodes[t.id] = graph.new_vertex({
render: {
label: t.id,
id: t.id,
shape: shapes.Default,
},
});
}

graph.new_edge(nodes[f.id], nodes[t.id], {
render: {
label: edge.attributes.get("label"),
tooltip: edge.attributes.get("tooltip"),
// pen_width: edge.attributes.get("penwidth"),
// font_size: edge.attributes.get("fontsize"),
// style: edge.attributes.get("style") as any,
// // shape: edge.attributes.get("shape") as any,
// // curve: edge.attributes.get("curve") as any,
// arrow_head: edge.attributes.get("arrowhead") as any,
// dir: edge.attributes.get("dir") as any,
// color: edge.attributes.get("color") as string,
// font_color: edge.attributes.get("fontcolor") as string,
// fill_color: edge.attributes.get("fillcolor") as string,
// // id?: string;
},
});
});
});
}
});

// const positioned = graph.layout();
const positioned = graph.layout();
return await positioned.to_svg().to_string();
}
78 changes: 3 additions & 75 deletions packages/rehype-vizdom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,8 @@ import type { Plugin } from "unified";
import type { Root } from "hast";
import { rehypeCodeHook, type MapLike } from "@beoe/rehype-code-hook";
import { type Config as SvgoConfig } from "svgo";
import { fromDot } from "ts-graphviz";
import { processVizdomSvg } from "./vizdom.js";
import { DirectedGraph, VertexWeakRef } from "@stereobooster/vizdom-ts-esm";

export async function getSvg(code: string) {
const graph = new DirectedGraph();
const model = fromDot(code);

const nodes: Record<string, VertexWeakRef> = {};
model.nodes.forEach((node) => {
nodes[node.id] = graph.new_vertex({
render: {
id: node.id,
label: node.attributes.get("label"),
tooltip: node.attributes.get("tooltip"),
fill_color: node.attributes.get("fillcolor") as string,
font_color: node.attributes.get("fontcolor") as string,
color: node.attributes.get("color") as string,
font_size: node.attributes.get("fontsize"),
pen_width: node.attributes.get("penwidth"),
shape: node.attributes.get("shape") as any,
style: node.attributes.get("style") as any,
},
});
});

model.edges.forEach((edge) => {
const from = edge.targets[0];
const to = edge.targets[1];

if (Array.isArray(from) || Array.isArray(to)) {
throw new Error("what is it?");
}

if (!nodes[from.id]) {
nodes[from.id] = graph.new_vertex({
render: {
label: from.id,
id: from.id,
},
});
}

if (!nodes[to.id]) {
nodes[to.id] = graph.new_vertex({
render: {
label: to.id,
id: to.id,
},
});
}

graph.new_edge(nodes[from.id], nodes[to.id], {
render: {
pen_width: edge.attributes.get("penwidth"),
font_size: edge.attributes.get("fontsize"),
style: edge.attributes.get("style") as any,
// shape: edge.attributes.get("shape") as any,
// curve: edge.attributes.get("curve") as any,
arrow_head: edge.attributes.get("arrowhead") as any,
dir: edge.attributes.get("dir") as any,
color: edge.attributes.get("color") as string,
font_color: edge.attributes.get("fontcolor") as string,
fill_color: edge.attributes.get("fillcolor") as string,
// id?: string;
label: edge.attributes.get("label"),
tooltip: edge.attributes.get("tooltip"),
},
});
});

const positioned = graph.layout();
return await positioned.to_svg().to_string();
}
import { dotToSvg } from "./dotToSvg.js";

export type RenderVizdomOptions = {
code: string;
Expand All @@ -101,8 +29,8 @@ export const rehypeVizdom: Plugin<[RehypeVizdomConfig?], Root> = (
salt,
language: "vizdom",
code: ({ code }) =>
getSvg(code).then(
(str) => processVizdomSvg(str, options.class, options.svgo)
dotToSvg(code).then((str) =>
processVizdomSvg(str, options.class, options.svgo)
),
});
};
Expand Down
28 changes: 28 additions & 0 deletions packages/rehype-vizdom/test/dotToSvg.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect, it, describe } from "vitest";
import fs from "node:fs/promises";

import { dotToSvg } from "../src/dotToSvg";

function example(x: number) {
it(`example ${x}`, async () => {
const file = await dotToSvg(
(
await fs.readFile(new URL(`./dotToSvg/${x}.dot`, import.meta.url))
).toString()
);

expect(file.toString()).toMatchFileSnapshot(`./dotToSvg/${x}.svg`);
});
}

describe("dotToSvg", () => {
example(1);
example(2);
// I have no idea how to set rank
// example(3);
example(7);
// parser doesn't support it
// example(8);
example(10);
example(11);
});
3 changes: 3 additions & 0 deletions packages/rehype-vizdom/test/dotToSvg/1.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
digraph {
a -> b
}
65 changes: 65 additions & 0 deletions packages/rehype-vizdom/test/dotToSvg/1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions packages/rehype-vizdom/test/dotToSvg/10.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
digraph {

V1 -> V2;
V1 -> V4;
V2 -> V5;
V3 -> V1;
V4 -> V2;
V4 -> V3;
V4 -> V6;
V4 -> V7;
V5 -> V4;
V5 -> V7;
V6 -> V3;
V7 -> V6;

}
Loading

0 comments on commit f948194

Please sign in to comment.