[Proposal] Add a high-level declarative syntax for diagrams#20
[Proposal] Add a high-level declarative syntax for diagrams#20anko9801 wants to merge 36 commits into
Conversation
|
I'll take time to look at it more in-depth. It seems like awesome work. Thanks ! Also, maybe skeletize should be able to directly detect a single string instead of doing |
…re for molecule-ring tests
…r outgoing direction
|
Doesn't the typed-smiles package already exist? I feel like I usually use alchemist for better control over the angles, so maybe a chemfig-like syntax would be better. |
|
Just found the molchemist package that supports SMILES and uses alchemist under the hood. For fine control,
From the molchemist documentation. |
b4479c9 to
3f64e10
Compare
Replace the recursion-limited pure-Typst molecule parser with a declarative engine under src/elements/chem/. A DSL/SMILES string is parsed by a Rust engine (engine/ -> chem.wasm), laid out by CoordgenLibs (coordgen/ -> coordgen.wasm), then drawn by render.typ with IUPAC GR-2008 orientation, labels, stereo and the GR feature set. One public entry point, chem(), returns stand-alone content; reaction()/rxn-arrow()/curly-arrow() build schemes; electron-pushing arrows are addressed by atom id via chem's `arrows` option. `molecule` stays the hand-drawn fragment alias. DSL ring bodies list vertices with inferred bonds — @6 benzene, @6(N) pyridine, @6(C(-CH3)CCCCC) toluene — auto-aromatised via a graph-level kekulize shared with the SMILES front-end; explicit bonds override (@6(------) cyclohexane). The build sources live next to the chem Typst code and are excluded from the package. Assisted-by: Claude Code (model: Opus 4.8)
Five visual-regression galleries covering the engine pipeline (chem-parse, chem-layout, chem-render, chem-compose, chem-gallery), alongside the hand-drawn fragment/link tests (cetz-skeleton-anchors, resonance, molecules-*) kept intact now that `molecule` aliases `fragment`. Assisted-by: Claude Code (model: Opus 4.8)
Add a Part III "Molecule engine" chapter — chem(), the DSL (incl. inferred-bond rings), SMILES, stereochemistry, render options, electron-pushing arrows, reactions and formula — plus a command reference. Assisted-by: Claude Code (model: Opus 4.8)
There was a problem hiding this comment.
Why are you building a new renderer? I don't think it's a good idea. If you miss any features on the current renderer, I can add them if you don't have time/don't want to do it. This duplicates the logic and as far as I can see is not compatible with the current renderer
I just talked with the author of this module, and apparently, the smiles support of molchemist was only there as someone requested it (rice8y/molchemist#1). If I understood correctly, the author is quite ok with making this a part of alchemist instead of keeping it in the molchemist module. |
Yeah, I think this is the right approach. I'm just very slowly working on the smiles implementation but it's going to have inchi support as well as some other smaller file types. I was thinking of making a new renderer for it, since it's going to output more coordinate-based information. I even started working on it, but I kind of want to keep alchemist as the go to solution. I feel kind of guilty that my smiles implementation is taking so long. There are already packages trying to fill the vacuum, creating more fragmentation. Even though typed-smiles is useful, it'd be cooler if there was just one package & styling system to use for everything. The diagrams do look very different and it would be hard to use both in the same document. |
|
Hi everyone, I’m the author of molchemist. molchemist was originally created for rendering Molfile/SDF data. SMILES support began as experimental work in response to rice8y/molchemist#1, and v0.1.2 was the first release to include it. I’m therefore positive about consolidating the overlapping SMILES functionality into Alchemist rather than maintaining multiple partially overlapping implementations. I checked out the current PR head ( For the SMILES path specifically, both implementations use CoordgenLibs for 2D layout, but their parser and intermediate-representation boundaries differ. molchemist uses a vendored copy of If the current shared graph and layout/depiction pipeline are intended to remain, one option would be to evaluate Regarding rendering, I share Robotechnic’s concern about duplicating behavior that already exists in At the same time, I understand Ants-Aare’s point that coordinate-based output may benefit from a different rendering layer. I do not yet have a strong conclusion about whether reusing |
|
I have to say that the current rendering engine fully supports coordinate based rendering as the current way it works is by first placing all the fragments and anchors and then drawing the links between them. The api is just not exposed. We can discuss it a bit but I can provide a more computer friendly api that takes fragments, their coordinates and the links between them to do the rendering. It's just connecting the right piece together. My main concern with a separate renderer is that if it's not fully compatible with the current one, you can't really mix up the two ways of rendering a molecule and the cetz anchors won't be the same if you want to integrate a molecule in cetz. This would be more confusing for the end user. https://github.com/Typsium/alchemist/blob/master/src%2Fdrawer.typ#L434 This call is the place where all the links are drawn and it's just a matter of giving the right context to this function |
|
Thanks — and you're right that those pieces are addable. I think we actually converge here. The way I see it, the renderer's job is "draw a molecule whose atoms already have coordinates" — that part I want to share, not fork. The hard part is upstream: parsing + IUPAC 2D layout (fused/bridged/macrocycles), which has no counterpart in the current renderer to improve. It's a coordinate solver (CoordgenLibs, same as molchemist) + orientation/stereo, and it has to run as a compiled WASM plugin, not in Typst itself — the earlier pure-Typst version here hit Typst's recursion limit and had to fake rings with invisible bonds. So the WASM engine isn't a reimplementation of your renderer; it's the missing layer. And what I'd need from you is exactly what you offered. Reading In return I'd drop @rice8y — following your suggestion, I'll use opensmiles as the SMILES frontend in this PR. @Ants-Aare — I don't want to step on your InChI/Molfile work; it can feed the same graph + renderer, and I'm happy to coordinate however you prefer. So PR would be DSL + opensmiles → engine → that coordinate entry, render.typ's core removed and anchors/config unified. Want to design the entry's signature together? |
Replace the dedicated coordinate renderer (`render.typ`) with a pipeline that drives alchemist's own drawing, so molecules share one rendering path — and therefore identical CeTZ anchors, spacing and styling — with hand-drawn `skeletize` molecules instead of duplicating the logic: - `skeleton.typ`: place each engine atom as a native fragment at its absolute coordinate, then draw bonds turtle-free through the existing `draw-link-decoration`. Reuses link/lewis/fragment drawing; one bond-length unit maps to `atom-sep` (resolved to canvas units via `convert-length` so the manual geometry stays float-valued), keeping engine and hand-drawn molecules at the same scale. - `decorations.typ`: thin overlay for the non-bond GR features that have no counterpart in alchemist (ionic dots, brackets, aromatic circles, curly arrows, partial charges, hapto/Markush, delocalized/bent/wavy bonds). - `labels.typ`: shared GR label/colour helpers (subscripts, isotope/charge/ oxidation superscripts, Jmol CPK colours). - styling now flows from alchemist's `default` config (single styling system). `draw-chem` is exposed alongside `chem` so several molecules, reaction arrows and cross-molecule electron-pushing arrows compose in one shared canvas. Assisted-by: Claude Code (model: claude-opus-4-8)
The alchemist-primitive rendering and atom-sep scaling change the output, so refresh the golden images for the chem test suite. Assisted-by: Claude Code (model: claude-opus-4-8)
This pull request introduces a proposal for a new
moleculemodule, intended to offer a more declarative and concise way to write chemical diagrams.The goal is to provide a syntax that is even more intuitive and concise than established tools like ChemFig, allowing the natural way of writing a structure to yield an IUPAC preferred diagram.
This module is designed to integrate with core Alchemist functions, enabling a powerful hybrid workflow. While core Alchemist provides ultimate flexibility through direct cetz integration, this new module, at the cost of that flexibility, offers exceptional conciseness for the most common use cases.
Proposed Features
#molecule("CH3-CH2-OH")) and parse it into a diagram.:labelfor points,::labelfor lines, and alabel: "..."argument) is included to allow for the creation of more complex structures with non-sequential bonds or mechanism arrows.Example Usage
What's implemented
CH3->$C$ $H_3$)NH3+,COO-)^13C,^2H)-=#><:><:|><|)@6(-=-=-(-CH3)=)-CH3)O(lewis: (dots(0), dots(180)))-(angle: 60deg))O:O1=::bond)Refereneces
This is an initial proposal, and I would be very grateful for any feedback, suggestions, or critiques to help improve it. Thank you for your consideration.