-
-
Notifications
You must be signed in to change notification settings - Fork 1
Equation Inspection/Editing #11
Comments
From the MathML 3 standard, we can use
|
Some notes on how cursor navigation could work:
|
I'd recommend looking at how other editors handle this, mathlive definitely handles a lot of tricky questions rather nicely. Also, it would be quite possible to have a flattened tree purely for navigating. See https://github.com/jacobp100/technicalc-core/tree/master/packages/technicalc-editor The alternative, box-based approach is definitely an interesting one, I'd very much like to see an editor that implements that. |
Out of sheer curiosity, I tried out this approach. So, if I understood it correctly, the algorithm would be
For simplicity, this description ignores the part about trying to navigate inside the current element, if it's a bottom-most element and there is text in it. |
After some fiddling, I ended up implementing this (Try it out with Firefox. Chrome doesn't support MathML yet) It does not implement the "special rules here (depending on the parent)" part. So moving up in a fraction doesn't work with it, but could be added rather easily. I also quite recommend putting some bigger and trickier cases into the jsfiddle. |
Hi, thanks for the interest! In the time since I wrote that comment, I've had the chance to prototype some of the other approaches. To summarise my findings:
Your prototype of the box-based approach is really interesting. At a high level, it works surprisingly well using just the basic set of rules. I suppose it's quite close to what I had in mind for "Leaf Navigation", where we'd take all the leaf nodes (e.g. caret locations) and lay them out linearly. Left/Right would simply go back or forward one index, while Up/Down might do something more complex involving parent/child nodes.
This is the exact same problem I had with all the methods I've tried so far. My current thinking is to have elements define their own caret locations, and have some rules defining how and when caret locations should be "collapsed". We could start by giving every non-layout MML element two caret locations, at the start and the end: And say that for every element which has a left sibling within an Which gives us the caret locations we want. This is easily extendable to give intermediate caret positions for e.g. text. I'll have a go at implementing this in Lasem and see how viable it is. I think it could be a reasonable starting point paired with your cursor navigation approach from the jsfiddle. |
So as it turns out, this approach works remarkably well: Lasem.Cursor.Demo.mp4Fractions also have an ending caret location, just not shown in the video. Presentation aside, the cursor largely moves where it should in response to Left/Right arrow keys. Adding special casing for fractions and other containers (tables/matrices/etc) shouldn't be too difficult. My current ruleset is dead simple:
|
Sweet, glad to see that you've gone ahead and built prototypes of them. Now we know for sure which approaches are good and which ones don't quite work out.
I just attempted to implement that approach, the shoddy code is over here. It works beautifully well! |
After some further testing, I've come to the conclusion that your ruleset can elegantly handle pretty much anything. The only one that needs to be extended a bit is
There are at least 3 slightly special cases:
<math>
<mo>-</mo>
<msup><mn>2</mn><mn>3</mn></msup>
<mo>+</mo>
<msup>
<mn>2</mn>
<msup><mn>4</mn><mn>8</mn></msup>
</msup>
<mo>*</mo>
<msup>
<msup><mn>2</mn><mn>3</mn></msup>
<mn>3</mn>
</msup>
</math> <math>
<mrow>
<mo>-</mo>
<mrow>
<mrow><mn>2</mn></mrow>
</mrow>
</mrow>
</math> |
After thinking a bit about the special cases, I've settled for the following solution. |
I fiddled around more with this and now have to ask: Is the plan to build a MathML editor or an equation editor? And depending on the answer, how should one deal with cases such as
If one wants to simply build a equation editor, it probably means that the core focus is on the mathematical editing experience and not on being able to edit every single possible MathML tree. And then, the approach outlined below might be interesting: |
That's a very good question actually. I'm mainly interested in supporting inline equation editing within rich text for this word processor that I'll hopefully finish someday. I've chosen ODT as the primary format, which uses MathML 2.0 for storing equation data. My primary goal is to seamlessly integrate caret positions between the text and equation a bit like how Microsoft Word handles things, rather than the equation being a separate object within the text content. So the focus is definitely on the core editing experience rather than handling arbitrary MathML. (I'd also like to build a standalone editor, so I'm aiming to reuse as much as possible between the two)
I've been leaning very heavily towards going down this route. While MathML works at the moment for simple equations, I can see it easily becoming a limitation on the editor in the future. I think I'll explore the approach you've linked. Does the AST replace the MathML DOM or work parallel to it? |
Sounds like a solid plan then! I wonder how different MathML 2.0 is from MathML Core. The latter definitely has a nice and simple specification.
The AST approach works by taking the MathML, turning it into an AST and doing all operations on it. And to render it, you turn it back into MathML. So, I'd say it's more of a replacement. |
I have a quick update regarding the AST approach, now that I've finally implemented it and have basic caret movement working. I settled on the following concept:
And then, stuff like "a caret can only be placed in a row or a string" can be exploited to make the code much easier to reason about. I also made it so that directly nested rows are not allowed. Furthermore, every container element always contains a fixed number of rows and nothing else. video.mp4Here is my MathIR for the equation above {
"type": "row",
"values": [
{
"type": "root",
"values": [
{
"type": "row",
"values": [ {"type": "symbol", "value": "2"} ]
},
{
"type": "row",
"values": [
{
"type": "root",
"values": [
{
"type": "row",
"values": [ {"type": "symbol", "value": "2"} ]
},
{
"type": "row",
"values": [
{ "type": "symbol", "value": "x" },
{ "type": "symbol", "value": "x" },
{ "type": "symbol", "value": "x" }
]
}
]
},
{"type": "symbol", "value": "x"}
]
}
]
},
{
"type": "frac",
"values": [
{
"type": "row",
"values": [ {"type": "symbol", "value": "x"} ]
},
{
"type": "row",
"values": [ {"type": "symbol", "value": "x"} ]
}
]
}
]
} |
Looks very promising! Coming back with fresh eyes (after taking a short break from text), I've largely come to the same conclusion. I've found that using a DOM-like structure for storing documents is not well suited for actual text manipulation. I think a similar case applies with equation rendering here; MathML makes sense for storing interoperable maths data, but using it for an editor is a "square peg round hole" sort of issue.
I think these simplifications make a lot of sense. A problem I had with the 'mathml editor' is the scope creep of wanting to support the entire MathML standard. Whereas an equation editor can convert this to a more convenient internal format, like you've done here. With my rich text editor, I'm leaning towards adopting something based on quill's parchment or maybe a custom AST. Continuing on from that, I'm looking into representing equations in a compatible format and how this could work with undo/redo, which is always a tricky topic. |
This makes sense, I never realized how much the DOM was designed for layouting and other non-editing purposes. Using a custom AST or quill's parchment sounds like a solid approach. One question for a math editor is: How flexible should it be? And how smart?
Notice how the However, if the user inputs
In this case, the Should the editor automatically figure this out? Or should it be up to the user? Finally, the usual way I've seen undo/redo being done is by never modifying the datastructure directly. Instead, one generates commands that both have a "redo" and an "undo" callback. And then one makes sure to implement those correctly. |
Implement some form of WYSIWYG equation editing with Lasem.
A good first step would be to support picking - e.g. the user can hover over an element within the DOM and the LsmMathmlView is able to resolve the mouse coordinates to a specific element or subtree.
Following steps might include:
The text was updated successfully, but these errors were encountered: