Open WesternFastShooters opened 11 months ago
This is something I wanted for a long time... it will happen eventually
This is something I wanted for a long time... it will happen eventually
Some time before,I wanted to code for these features in some excalidraw`s fork,but the complexity blocks me.
@zsviczian Dear Developer, I saw you reply in a number of topics that you will consider adding foldable mind mapping at a later stage, and I am very excited because it is a great and useful feature. I saw someone implement it through a script, here is the URL, you can have a look. Look forward to seeing him soon. Best wishes for you.
https://www.bilibili.com/video/BV1Je411e7Qx/?spm_id_from=333.999.0.0 https://www.bilibili.com/read/cv25597391/?from=readlist
@Loners-xq - Wow! this is great. Can you help translate, maybe convince the developer to publish this as a script in the script library?
@Loners-xq I am the author of this video. In order to implement the mind map functionality, I made some modifications to the obsidian-excalidraw-plugin, and I will consider adding it to the script library after refining the code.
The modifications include exposing hooks for onPointerDown and onKeyDown. Then, in the mouse events, I maintain the tree structure and show/hide corresponding elements (using element.opacity=0 and lock=true for hiding, and restoring the backed-up opacity and lock when showing). Then, I call the slightly modified Mindmap format script (fixing some coordinate calculation issues). In the keyboard events, I add new elements and also call the Mindmap format script.
On a side note, I have implemented the fourth point mentioned above, which allows converting hand-drawn quadrilaterals, triangles, circles, and straight lines into regular shapes. For more details, you can visit https://www.bilibili.com/read/cv26287346/ Here is the code provided for reference:
let editorElement = document.getElementById('iink-editor');
let state = api.getAppState()
function register() {
if (!editorElement) {
editorElement = document.createElement('div');
editorElement.id = "iink-editor";
editorElement.style.display = 'block'; // Hide the element
editorElement.style.width = `${state.width}px`;
editorElement.style.height = `${state.height}px`;
editorElement.style.position = "absolute";
document.body.appendChild(editorElement);
}
iink.register(editorElement, {
recognitionParams: {
type: 'DIAGRAM',
protocol: 'WEBSOCKET',
server: {
applicationKey: 'a10a3491-9281-4c18-a922-212daea0d5d8',
hmacKey: 'afd10a3d-8e68-4ab1-8a09-ad24fb9d70cc'
}
}
});
}
if (!editorElement) {
const script = document.createElement("script");
script.type = "text/javascript";
script.src = "http://localhost:55555/myscript/node_modules/iink-js/dist/iink.min.js";
script.onload = () => {
console.log("iink.min.js loaded")
register()
}
document.body.appendChild(script);
}
if (editorElement?.editor) {
console.log("editorElement.editor")
let selectedEl = ea.getViewSelectedElement();
if (!selectedEl) {
let elements = await api.getSceneElements()
selectedEl =elements.filter(el=>el.type=="freedraw").sort((a,b) => {
b.updated - a.updated
})[0]
}
let xs = []
let ys = []
scrollXFixed = state.scrollX - (1-state.zoom.value) * state.width
scrollYFixed = state.scrollY - (1-state.zoom.value) * state.height
if(selectedEl.type == "freedraw") {
selectedEl.points.forEach(p => {
xs.push(p[0])
ys.push(p[1])
})
}
xMin = Math.min(...xs)
yMin = Math.min(...ys)
xMax = Math.max(...xs)
yMax = Math.max(...ys)
width = xMax - xMin
height = yMax - yMin
centerX = (xMin + xMax) / 2
centerY = (yMin + yMax) / 2
offsetX = state.width / 2 - centerX//centerX = state.width / 2
offsetY = state.height / 2 - centerY
xs = xs.map(p => p+=offsetX)
ys = ys.map(p => p+=offsetY)
const strokes = { "events": [{
"pointerType": "PEN",
"pointerId": 1,
"x": xs,
"y": ys
}
]}
const processGestures = false;
try{
await editorElement.editor.clear()
}
catch(e){
console.log(`error: ${e}`)
}
await editorElement.editor.pointerEvents(strokes)
await editorElement.editor.convert()
let parser = new DOMParser();
let svgDoc = parser.parseFromString(editorElement.outerHTML, "image/svg+xml");
let svgModel = svgDoc.querySelectorAll('svg[data-layer="MODEL"]')[0];
let diagramArea = svgModel.querySelectorAll('[id$="DiagramArea"]')[0];
if (diagramArea) {
let gModels = diagramArea.querySelectorAll('g[id^="MODEL"]')
let viewTransform = svgDoc.querySelector('#MODEL-viewTransform')
let transform = viewTransform.getAttribute('transform')
transform = transform.substring(7, transform.length-1).split(',').map(el => Number(el))
let points = []
function addLine() {
points.map(p => {p[0] *= transform[0], p[1] *= transform[3]})
points.map(p => {p[0] += (selectedEl.x - offsetX), p[1] += (selectedEl.y - offsetY)})
ea.addLine(points)
points = []
}
ea.style.backgroundColor = selectedEl.backgroundColor;
ea.style.strokeColor = selectedEl.strokeColor
ea.style.strokeWidth = selectedEl.strokeWidth + 1
ea.style.roughness = 0
ea.style.fillStyle = "solid";
gModels.forEach(model => {
let child = model.firstElementChild
if (child.tagName == "line") {
let x1 = Number(child.getAttribute('x1'))
let x2 = Number(child.getAttribute('x2'))
let y1 = Number(child.getAttribute('y1'))
let y2 = Number(child.getAttribute('y2'))
points.push([x1, y1])
points.push([x2, y2])
}
else {
if (points.length > 0) {
addLine()
}
if (child.tagName == "path") {
let path = child.getAttribute('d')
let cmds = path.split(/(?=[AaCcHhLlMmQqSsTtVvZz])/);
let currentX = 0, currentY = 0
ciecleList = []
cmds.forEach(cmd => {
cmd = cmd.replace(/,/g, ' ')
cmdChar = cmd.split(' ')
if (cmdChar[0].toLowerCase() == 'm') {
currentX = Number(cmdChar[1])
currentY = Number(cmdChar[2])
}
else if (cmdChar[0].toLowerCase() == 'a') {
ciecleList.push(cmdChar)
}
if (ciecleList.length == 2 && ciecleList[0][1] == ciecleList[1][1] &&
ciecleList[0][2] == ciecleList[1][2] &&
ciecleList[0][3] == ciecleList[1][3] &&
ciecleList[0][4] == ciecleList[1][4] &&
ciecleList[0][5] == ciecleList[1][5] &&
ciecleList[0][7] == ciecleList[1][7]) {
currentXFixed = currentX * transform[0]
currentYFixed = currentY * transform[3]
currentXFixed += (selectedEl.x - offsetX)
currentYFixed += (selectedEl.y - offsetY)
let rx = ciecleList[0][1] * transform[0]
let ry = ciecleList[0][2] * transform[3]
ea.addEllipse(currentXFixed - 2*rx, currentYFixed-ry, rx * 2, ry * 2)
ciecleList = []
}
})
}
}
})
if (gModels.length == 4 && points.length == 8) {// for rect
//set(x).length = set(y).length == 2
}
// ea.addLine([...new Set(points.map(JSON.stringify))].map(JSON.parse))
if (points.length > 0) {
addLine()
}
ea.addElementsToView(false,false);
ea.deleteViewElements([selectedEl]);
}
else {
new Notice("can't convert");
console.log(`xs: ${xs}`)
}
}
Better boundingbox like this , these points attach that create arrows around this circle boundingbox
Arrow need to be optimized like this, users can choose styles of arrow directly
a new tool -- no-shape lasso
handwriting strokes recognization Shapes can be transfer into regular shapes in free draw mode reference: https://www.myscript.com/
Support for mindmap Though construct a mindmap with existed shapes such as rectangles and arrows,but a better mind map can support automate layout algorithm of mindmap and shortcut key that creates next node of mindmap. like this