Skip to content

Commit

Permalink
Mostly got this set up, but trying to figure out ts errors
Browse files Browse the repository at this point in the history
  • Loading branch information
lovettbarron committed Jul 4, 2024
1 parent 46e9b92 commit 583095d
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 155 deletions.
5 changes: 2 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
"@types/hast-format": "^2.3.0",
"hast-util-to-html": "^9.0.1",
"hastscript": "^9.0.0",
"mdast-util-from-markdown": "^2.0.1",
"mdast-util-to-hast": "^13.2.0",
"rehype-stringify": "^10.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0",
Expand Down
97 changes: 30 additions & 67 deletions src/embed.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { unified } from "unified";
import parse from "remark-parse";
import remark2rehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import { s } from "hastscript";
import { Element } from "hast";

import * as path from "path";
import { Processor, Transformer } from "unified";
import { Node, Parent } from "unist";
import { VFile } from "vfile";
import { h, s } from "hastscript";
import { Element as SvgElement } from "hast-format";
import { toHtml } from "hast-util-to-html";
import { fromMarkdown } from "mdast-util-from-markdown";
import { toHast } from "mdast-util-to-hast";

import { GenericNode } from "@trbn/jsoncanvas";
// import { applyDefaults, Options } from "./options";
Expand All @@ -25,75 +20,43 @@ export function checkImagesLoaded(callback: Function) {
}

// This renders out the images
export async function drawEmbedded(svg: SvgElement, node: GenericNode | any) {
if (node.type === "file" && canvas) {
export async function drawEmbedded(svg: Element, node: GenericNode | any) {
if (node.type === "file" && svg) {
if (node.file.match(/\.(jpg|jpeg|png|gif)$/i)) {
const drawImg = new Image() as any;
const img = await loadImage(node.file);

drawImg.onload = () => {
ctx.drawImage(
drawImg,
node.x + canvas.width / 2,
node.y + canvas.height / 2,
node.width,
node.height
);
};
drawImg.src = img.src;
imagesLoaded.push(drawImg);
const image = s("image", {
x: node.x,
y: node.y,
width: node.width,
height: node.height,
"xlink:href": node.file,
});

svg.children.push(image);
}
}
}

// This renders out the images
export async function drawMarkdownEmbed(
canvas: Canvas,
ctx: CanvasRenderingContext2D,
node: GenericNode | any
) {
if (node.type === "file" && canvas) {
export async function drawMarkdownEmbed(svg: Element, node: GenericNode | any) {
if (node.type === "file" && svg) {
if (node.file.match(/\.(md|mdx)$/i)) {
const resp = await fetch(node.file);
const mdFile = await resp.text();
const renderedMarkdown = await unified()
.use(parse)
.use(remark2rehype) // Convert Markdown to HTML
.use(rehypeStringify)
.process(mdFile);

const htmlString = String(renderedMarkdown);
const div = document.createElement("div");
div.innerHTML = htmlString;

div.style.width = `${node.width}px`;
div.style.height = `${node.height}px`;
div.style.color = "black";
div.style.backgroundColor = "red";
div.style.position = "absolute";
// div.style.left = "-9999px";
document.body.appendChild(div);

// Use html2canvas to render the div to an image
const canvasElement = await html2canvas(div);
const img = new Image(node.width, node.height) as any;

img.onload = async () => {
await ctx.drawImage(
img,
node.x + canvas.width / 2,
node.y + canvas.height / 2,
node.width,
node.height
);
const mdast = fromMarkdown(mdFile);
const hast = toHast(mdast);
const html = toHtml(hast);

console.log("Markdown", img);
// Ref: https://stackoverflow.com/questions/45518545/svg-foreignobject-not-showing-on-any-browser-why
const embed = s("foreignObject", {
x: node.x,
y: node.y,
width: node.width,
height: node.height,
});
embed.children.push(hast as Element); // If this breaks, this is probably the spot it breaks

// Cleanup
document.body.removeChild(div);
};
img.src = canvasElement.toDataURL();
imagesLoaded.push(img);
svg.children.push(embed);
}
}
}
135 changes: 50 additions & 85 deletions src/jsoncanvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Processor, Transformer } from "unified";
import { Node, Parent } from "unist";
import { VFile } from "vfile";
import { h, s } from "hastscript";
import { Element as SvgElement } from "hast-format";
import { Element } from "hast";

import { JSONCanvas, Edge, GenericNode } from "@trbn/jsoncanvas";

Expand Down Expand Up @@ -46,7 +46,7 @@ export function render(
calculateMinimumCanvasSize(jsc);

// Init Canvas objects
const svg = initRender("jsc", canvasWidth + offsetX, canvasHeight + offsetY);
const svg = initRender(canvasWidth + offsetX, canvasHeight + offsetY);

if (svg === null) return null;

Expand All @@ -63,20 +63,20 @@ export function render(
drawEdge(svg, toNode, fromNode, edge, options);
});

return checkImagesLoaded(() => renderToBuffer(canvas));
return checkImagesLoaded(() => renderToBuffer(svg));
}

function renderToBuffer(config?: Partial<Options>) {
function renderToBuffer(svg: Element, config?: Partial<Options>) {
const options = applyDefaults(config);
console.log;
console.log("Rendering", svg, options);
return null;
}

function initRender(
width: number,
height: number,
config?: Partial<Options>
): SvgElement {
): Element {
const options = applyDefaults(config);

const BASE_SVG_PROPS = {
Expand All @@ -100,12 +100,11 @@ function initRender(

const svg = s("svg", props);

return;
svg;
return svg;
}

async function drawNode(
svg: SvgElement,
svg: Element,
node: GenericNode | any,
config?: Partial<Options>
) {
Expand Down Expand Up @@ -134,93 +133,88 @@ async function drawNode(
strokeStyle = "rgba(100,10,100,1)";
}

const group = s("g");

const rect = s("rect", {
x: node.x + svg.properties?.width / 2,
y: node.y + svg.properties?.height / 2,
x: node.x + svg.properties.width / 2,
y: node.y + svg.properties.height / 2,
width: node.width,
height: node.height,
rx: 5,
ry: 5,
stroke: strokeStyle,
fill: fillStyle,
"stroke-width": options.lineStrokeWidth,
});

group.children.push(rect);

drawEmbedded(svg, node);
drawMarkdownEmbed(svg, node);

ctx.lineWidth = options.nodeStrokeWidth;
ctx.stroke();
ctx.fill();
ctx.closePath();

ctx.fillStyle = "rgba(0, 0, 0, 1)";
// ctx.fillStyle = "rgba(0, 0, 0, 1)";
if (node.label) {
ctx.fillText(
node.label,
node.x + 5 + canvas.width / 2,
node.y + 20 + canvas.height / 2
);
// ctx.fillText(
// node.label,
// node.x + 5 + canvas.width / 2,
// node.y + 20 + canvas.height / 2
// );
}

if (node.type === "text" && node.text) {
ctx.fillText(
node.text,
node.x + 5 + canvas.width / 2,
node.y + 40 + canvas.height / 2
);
// ctx.fillText(
// node.text,
// node.x + 5 + canvas.width / 2,
// node.y + 40 + canvas.height / 2
// );
}

svg.children.push(rect);
svg.children.push(group);
}

function drawEdge(
svg: SvgElement,
svg: Element,
toNode: GenericNode,
fromNode: GenericNode,
edge: Edge | any,
config?: Partial<Options>
) {
const options = applyDefaults(config);

ctx.lineWidth = options.lineStrokeWidth;
ctx.strokeStyle = "rgba(0,0,0,1)";

if (fromNode && toNode) {
if (svg === null || svg == undefined) return null;
else if (fromNode && toNode) {
let startX =
fromNode.x +
(edge.fromSide == "top" || edge.fromSide == "bottom"
? fromNode.width / 2
: fromNode.width) +
canvas.width / 2;
let startY = fromNode.y + fromNode.height / 2 + canvas.height / 2;
svg.properties!.width / 2;
let startY = fromNode.y + fromNode.height / 2 + svg.properties.height / 2;
let endX =
toNode.x +
(edge.toSide == "top" || edge.toSide == "bottom"
? toNode.width / 2
: toNode.width) +
canvas.width / 2;
let endY = toNode.y + toNode.height / 2 + canvas.height / 2;
svg.properties.width / 2;
let endY = toNode.y + toNode.height / 2 + svg.properties.height / 2;

if (edge.fromSide === "left") {
startX = fromNode.x + canvas.width / 2;
startX = fromNode.x + svg.properties.width / 2;
} else if (edge.fromSide === "top") {
startY = fromNode.y + canvas.height / 2;
startY = fromNode.y + svg.properties.height / 2;
} else if (edge.fromSide === "bottom") {
startY = fromNode.y + fromNode.height + canvas.height / 2;
startY = fromNode.y + fromNode.height + svg.properties.height / 2;
}

if (edge.toSide === "right") {
endX = toNode.x + toNode.width + canvas.width / 2;
endX = toNode.x + toNode.width + svg.properties.width / 2;
} else if (edge.toSide === "top") {
endY = toNode.y + canvas.height / 2;
endY = toNode.y + svg.properties.height / 2;
} else if (edge.toSide === "bottom") {
endY = toNode.y + toNode.height + canvas.height / 2;
endY = toNode.y + toNode.height + svg.properties.height / 2;
} else if (edge.toSide === "left") {
endX = toNode.x + canvas.width / 2;
endX = toNode.x + svg.properties.width / 2;
}

ctx.beginPath();
ctx.moveTo(startX, startY);
// ctx.lineTo(endX, endY);

// Change the control point logic based on fromSide/toSide
const cp1 = {
x: startX,
Expand All @@ -232,41 +226,12 @@ function drawEdge(
y: startY,
};

ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, endX, endY);
ctx.stroke();
ctx.closePath();

const t = 1.0; // At the end of the curve
const dx =
3 * (1 - t) * (1 - t) * (cp1.x - startX) +
6 * (1 - t) * t * (cp2.x - cp1.x) +
3 * t * t * (endX - cp2.x);
const dy =
3 * (1 - t) * (1 - t) * (cp1.y - startY) +
6 * (1 - t) * t * (cp2.y - cp1.y) +
3 * t * t * (endY - cp2.y);
const angle = Math.atan2(dy, dx);

// Draw arrowhead
const headlen = 20; // length of head in pixels
ctx.beginPath();
ctx.moveTo(endX, endY);
ctx.lineTo(
endX - headlen * Math.cos(angle - Math.PI / 6),
endY - headlen * Math.sin(angle - Math.PI / 6)
);
ctx.lineTo(
endX - headlen * Math.cos(angle + Math.PI / 6),
endY - headlen * Math.sin(angle + Math.PI / 6)
);
ctx.lineTo(endX, endY);
ctx.lineTo(
endX - headlen * Math.cos(angle - Math.PI / 6),
endY - headlen * Math.sin(angle - Math.PI / 6)
);
ctx.stroke();
ctx.fill();
const line = s("path", {
d: `M ${startX} ${startY} C ${cp1.x} ${cp1.y}, ${cp2.x} ${cp2.y}, ${endX} ${endY}`,
stroke: "black",
"stroke-width": options.lineStrokeWidth,
fill: "none",
});
svg.children.push(line);
}

svg.children.push(arrow);
}

0 comments on commit 583095d

Please sign in to comment.