-
Notifications
You must be signed in to change notification settings - Fork 16
feat: LaTeX and Typst text handlers for Makie's text_handler hook #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jkrumbiegel
wants to merge
74
commits into
master
Choose a base branch
from
jk/text-primitive-integration
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 13 commits
Commits
Show all changes
74 commits
Select commit
Hold shift + click to select a range
6ef2206
feat: TeXString to feed LaTeX into Makie's text() via AbstractTextPri…
jkrumbiegel 96b83e6
feat(TeXString): Support align = (..., :baseline) via LaTeX box-depth…
jkrumbiegel 309ee6d
refactor(TeXString): Fold baseline detection into the single render c…
jkrumbiegel c4e45d3
fix(TeXString): Compensate for PDF crop margin in alignment math
jkrumbiegel ab4b9a4
fix(TeXString): HiRes crop + standalone border=3pt for exact ink padding
jkrumbiegel 02b854d
feat(CairoMakie): Vector-render CachedPDF markers via Poppler instead…
jkrumbiegel bfce63d
refactor(TeXString): Drop AbstractString conformance; opt in via Maki…
jkrumbiegel e2478ca
refactor: Replace TeXString with makietex_latex_handler for theming
jkrumbiegel 0ac5ff4
feat: MakieTeXLaTeX struct as configurable, themable handler (preambl…
jkrumbiegel 406fc5d
feat(MakieTeXLaTeX): Declare text_primitive_types = (ImageTextPrimiti…
jkrumbiegel 28e6f74
chore: Drop text_primitive_types override; no longer needed with Plot…
jkrumbiegel e1df599
refactor: Emit PlotSpec directly into text_specs channel; drop ImageT…
jkrumbiegel 30510dc
feat(GLMakieExt): Wire CachedPDF rasterization via rasterize_marker_f…
jkrumbiegel 90f566b
refactor(rasterize_marker_for_gpu): Move override into MakieTeX core;…
jkrumbiegel 71d4b8a
refactor: Centralize LaTeX rendering via text recipe; drop legacy types
jkrumbiegel df5d1f2
refactor(text): Hoist place_text! onto shared AbstractPdfTextHandler
jkrumbiegel b397c0b
feat(text): Add Typst / FullTypst text_handler
jkrumbiegel cf9bc39
refactor: Move LaTeX and Typst integration into package extensions
jkrumbiegel f938bd1
refactor(typst): Drop Makie.is_text_input piracy
jkrumbiegel 66991ba
refactor: Drop legacy Typst markers; unify handler structs; trim deps
jkrumbiegel 0d15ece
test: Rewrite suite as PixelMatch reftests; drop SVG support
jkrumbiegel 00c4959
fix(latex): Pass --keep-logs to tectonic so baseline depth is parsed
jkrumbiegel 4b73dae
test: Use PixelMatch.@test_pixelmatch directly; switch font_scaling t…
jkrumbiegel 743add8
fix(typst): Skip text-line strut for display math so bbox is tight
jkrumbiegel 5347286
fix(gpu): Pre-fill rasterization buffer with transparent black
jkrumbiegel e7ee9e0
feat: Add PDF and SVG scatter markers
jkrumbiegel 80f753c
refactor: Eager-load PDF/SVG markers at construction, free on GC
jkrumbiegel 51b6f0a
feat(markers): Preserve PDF/SVG aspect ratio under scalar markersize
jkrumbiegel e98ac03
test: Use transparent page fill in marker_pdf fixture
jkrumbiegel 3907aee
test(markers): Label SVG marker fixture 'SVG' instead of single 'S'
jkrumbiegel c1f1563
ci: Use test env [sources] directly; gate xvfb to Linux; auto-pick arch
jkrumbiegel 49dd1d7
compat: Allow Colors 0.13
jkrumbiegel 52cb588
ci: Bump julia-actions/cache to v3 (saves on failure)
jkrumbiegel ac6a0db
ci: Back to standard julia-buildpkg + julia-runtest
jkrumbiegel 2a41c78
ci: Gate GLMakie behind GPU availability
jkrumbiegel 5d211e9
ci: Suppress auto-precompile around Pkg.rm(GLMakie)
jkrumbiegel 6ca516e
test: Gate GLMakie load on CI + (apple|windows) instead of try/catch
jkrumbiegel 00c9c42
test: Rename gate to SKIP_GLMAKIE; use plain conditional using
jkrumbiegel 1c1862d
ci: Filter reference_images artifact to just failing tests
jkrumbiegel ede5106
test: Generate SVG/PDF marker fixtures via Typst with TGHM
jkrumbiegel d8989bf
test: Pin every LaTeX/Typst handler to TeX Gyre Heros Makie
jkrumbiegel 7e512e6
fixup: Drop accidentally-committed .DS_Store
jkrumbiegel 77ac983
ci: Wipe stale test/Manifest and instantiate after Pkg.rm(GLMakie)
jkrumbiegel 2c16fae
ci: Use Pkg.update after Pkg.rm(GLMakie) to refresh manifest cleanly
jkrumbiegel e94355a
ci: Drop GLMakie via TOML edit, not Pkg.rm
jkrumbiegel 56f1899
ci: Drop julia-buildpkg — julia-runtest's Pkg.test resolves on its own
jkrumbiegel 288b2d0
ci: Skip windows-latest for now; bump docs cache to v3
jkrumbiegel 2eb1e8a
docs: Rewrite around text_handler / PDF / SVG markers
jkrumbiegel f205e9d
feat(markers): 1-based page index for PDF; docs polish + Feynman demos
jkrumbiegel b771294
docs(typst): Move fletcher #import inline into the Label content
jkrumbiegel d49576c
docs(latex): Correct the compile-cost note for full mode
jkrumbiegel 785bb65
docs: Add tellheight = false to Feynman Labels so neighbor axis isn't…
jkrumbiegel 501cc99
docs: Drop manual colsize! in Feynman examples; auto width is the point
jkrumbiegel 2743431
docs: Use full = true for both Feynman examples so all text matches
jkrumbiegel b94d7e4
docs: Lift Feynman diagram source into a local `diagram` variable
jkrumbiegel 759e314
docs: Use DocumenterVitepress.deploydocs to handle per-base layout
jkrumbiegel 0df6bae
docs: warnonly only locally; CI should fail on doc errors
jkrumbiegel dcbcbd1
docs: checkdocs = :none — internal helpers stay out of the docs
jkrumbiegel af9bc41
docs: checkdocs = :public
jkrumbiegel c0d761d
fix(latex): Always load xcolor in the template
jkrumbiegel 83a990e
ci: Grant statuses: write so Documenter can post the docs preview link
jkrumbiegel 10a46d4
rename: `full` -> `render_strings` on LaTeX / Typst handlers
jkrumbiegel 4c60320
docs(index): Minimal example uses set_theme! + render_strings
jkrumbiegel 795b2dd
docs(index): Plain-string xlabel; split x and ylabel onto separate lines
jkrumbiegel 1b183b9
release: v0.5.0 (breaking)
jkrumbiegel f80efd9
ci: Enforce CHANGELOG.md update on PRs (skip with skip-changelog label)
jkrumbiegel d343245
docs(markers): Vector-of-SVGs example with Julia / Python / MATLAB logos
jkrumbiegel bdbf2d2
docs: Rename section 'Vector markers' -> 'PDF & SVG'
jkrumbiegel e904a6b
docs: Rename markers.md -> pdf_svg.md so URL matches the section title
jkrumbiegel cd67a5e
docs(readme): Generate README via Quarto with PixelMatch CI check
jkrumbiegel f62ef64
docs(pdf_svg): Reuse README barplot as the vector-of-SVGs example
jkrumbiegel 121bf72
fix(readme): Use Pkg.instantiate instead of Pkg.develop so CI honors …
jkrumbiegel 36e0aab
ci(readme): Pin Julia to 1.12.5 to match _readme/Manifest.toml
jkrumbiegel 9ef247c
ci(readme): Drop committed Manifest; let CI instantiate from scratch
jkrumbiegel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,37 @@ | ||
| module MakieTeXGLMakieExt | ||
| #= | ||
| This extension tells GLMakie how to rasterize a cached document, if it is required. | ||
| =# | ||
|
|
||
| # Rasterize cached LaTeX/PDF documents into a Matrix{ARGB32} so GLMakie's | ||
| # scatter marker pipeline can upload them as a GPU texture. CairoMakie has | ||
| # its own native vector dispatch (see MakieTeXCairoMakieExt) and does NOT | ||
| # go through this hook, so this extension only affects GL/WGL-style | ||
| # backends and doesn't compromise CairoMakie's vector quality. | ||
|
|
||
| using GLMakie, MakieTeX | ||
| using Makie | ||
|
|
||
| # Heuristic for rasterization density. `s` is per-marker pixel-size in | ||
| # markerspace (Vec2f); take its longest axis and scale by a factor so that | ||
| # a 30pt fontsize → ~density 4 (good visual fidelity without huge textures). | ||
| _density_for_size(s) = max(2, ceil(Int, maximum(s) / 8)) | ||
|
|
||
| function Makie.rasterize_marker_for_gpu(doc::MakieTeX.AbstractCachedDocument, scale) | ||
| s = scale isa AbstractVector ? first(scale) : scale | ||
| return _rasterize_doc(doc, _density_for_size(s)) | ||
| end | ||
|
|
||
| function Makie.rasterize_marker_for_gpu(docs::AbstractVector{<:MakieTeX.AbstractCachedDocument}, scale) | ||
| # Per-marker scale if `scale` is a Vector, otherwise broadcast the | ||
| # scalar. | ||
| sizes = scale isa AbstractVector ? scale : fill(scale, length(docs)) | ||
| return [_rasterize_doc(d, _density_for_size(sz)) for (d, sz) in zip(docs, sizes)] | ||
| end | ||
|
|
||
| # GLMakie.GLAbstraction.gl_convert(ct::MakieTeX.AbstractCachedDocument) = MakieTeX.rasterize(ct, #=scale=#1) | ||
| # GLMakie.GLAbstraction.gl_convert((ct::MakieTeX.AbstractCachedDocument, scale::Real)) = MakieTeX.rasterize(ct, scale) | ||
| # `MakieTeX.rasterize(::CachedTEX/CachedTypst, _)` currently ignores its | ||
| # `scale` arg; `page2img(…; render_density)` honors it. Route through that | ||
| # uniformly so fontsize controls bitmap resolution. | ||
| function _rasterize_doc(doc::MakieTeX.AbstractCachedDocument, density::Int) | ||
| return MakieTeX.page2img(doc, doc.doc isa Nothing ? 0 : doc.doc.page; | ||
| render_density = density) | ||
| end | ||
|
|
||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,248 @@ | ||
| # Integration with Makie's `text` recipe via the `AbstractTextPrimitive` + | ||
| # `latex_handler` extension hooks (added in Makie 0.25). Lets MakieTeX | ||
| # render any `LaTeXString` passed into `text()` (and therefore Axis labels, | ||
| # titles, tick labels via `xtickformat`, etc.) with a real LaTeX engine | ||
| # instead of MathTeXEngine's in-process glyph approximation. | ||
| # | ||
| # Usage: | ||
| # | ||
| # set_theme!(latex_handler = MakieTeXLaTeX()) # global default | ||
| # with_theme(latex_handler = MakieTeXLaTeX(preamble = "…")) do … end # scoped override | ||
| # text!(scene, L"…"; latex_handler = MakieTeXLaTeX(preamble = "…")) # per-plot override | ||
| # | ||
| # Each `MakieTeXLaTeX` value carries its own preamble / class options / | ||
| # engine, so different text plots in the same figure can compile against | ||
| # different LaTeX setups (e.g. one Axis using `physics` package macros and | ||
| # another using `siunitx`). | ||
| # | ||
| # Without a handler set, Makie's default MathTeXEngine path is used — good | ||
| # for quick iteration; switch to MakieTeXLaTeX to polish a publication- | ||
| # quality figure without changing plot code (especially useful when the | ||
| # LaTeXStrings come from a third-party plotting function you don't control). | ||
| # | ||
| # See companion PR: https://github.com/MakieOrg/Makie.jl/pull/5632 | ||
|
|
||
| # Scatter centers its marker on the position, then adds `marker_offset`. To | ||
| # make `position` correspond to the alignment edge of the marker, shift by | ||
| # half the marker size scaled by the alignment fraction. `valign === :baseline` | ||
| # is supported: `baseline_from_bottom` is the descender depth in markerspace. | ||
| function _latex_align_offset(align::Tuple, wh::Makie.Vec2f, baseline_from_bottom::Real = 0.0f0) | ||
| halign, valign = align | ||
| fhalign = halign === :left ? 0.0f0 : | ||
| halign === :center ? 0.5f0 : | ||
| halign === :right ? 1.0f0 : Float32(halign) | ||
| ox = (0.5f0 - fhalign) * wh[1] | ||
| oy = if valign === :baseline | ||
| 0.5f0 * wh[2] - Float32(baseline_from_bottom) | ||
| else | ||
| fvalign = valign === :bottom ? 0.0f0 : | ||
| valign === :center ? 0.5f0 : | ||
| valign === :top ? 1.0f0 : Float32(valign) | ||
| (0.5f0 - fvalign) * wh[2] | ||
| end | ||
| return Makie.Vec2f(ox, oy) | ||
| end | ||
|
|
||
| const _DEFAULT_PREAMBLE = "\\usepackage{amsmath, amsfonts, xcolor}\n\\pagestyle{empty}\n\\nopagecolor" | ||
| const _DEFAULT_CLASSOPTIONS = "preview, tightpage, 12pt" | ||
|
|
||
| """ | ||
| MakieTeXLaTeX(; preamble, classoptions, engine, border_pt, | ||
| crop_margin_pt, base_pt) | ||
|
|
||
| A `latex_handler` for Makie's `text` recipe that renders `LaTeXString` | ||
| content with a real LaTeX engine (via MakieTeX). Construct one and pass it | ||
| to `set_theme!` / `with_theme` / a plot's `latex_handler` attribute. | ||
|
|
||
| # Fields | ||
|
|
||
| * `preamble` — LaTeX preamble (everything between `\\documentclass{…}` and | ||
| `\\begin{document}`). Default loads `amsmath, amsfonts, xcolor` and sets | ||
| `\\pagestyle{empty}\\nopagecolor` so the page background is transparent. | ||
| * `classoptions` — options to the standalone class (without `border=`, that | ||
| field is appended automatically from `border_pt`). Default | ||
| `"preview, tightpage, 12pt"`. | ||
| * `engine` — `nothing` (use `MakieTeX.CURRENT_TEX_ENGINE[]`) or a `Cmd` like | ||
| `` `lualatex` `` / `` `pdflatex` `` / `` `tectonic` ``. | ||
| * `border_pt` — pt margin built into the standalone page MediaBox; must be | ||
| larger than `crop_margin_pt` so the safety crop below has room to extend. | ||
| * `crop_margin_pt` — symmetric pt safety pad cropped around the ink, so | ||
| anti-aliased glyph edges aren't clipped by the page boundary. | ||
| * `base_pt` — LaTeX font size assumed in the document (Makie's `fontsize` | ||
| is scaled by `fontsize / base_pt`). | ||
| """ | ||
| Base.@kwdef struct MakieTeXLaTeX | ||
| preamble::String = _DEFAULT_PREAMBLE | ||
| classoptions::String = _DEFAULT_CLASSOPTIONS | ||
| engine::Union{Nothing, Cmd} = nothing | ||
| border_pt::Int = 3 | ||
| crop_margin_pt::Float32 = 2.0f0 | ||
| base_pt::Float32 = 12.0f0 | ||
| end | ||
|
|
||
| """ | ||
| compile_latex_for_makie(h::MakieTeXLaTeX, latex_src::AbstractString) | ||
| -> (cached::CachedPDF, baseline_pt::Float32) | ||
|
|
||
| Compile a LaTeX snippet under the configuration `h` to a `CachedPDF` and | ||
| simultaneously extract the descender depth (`\\dp`) of the rendered box — | ||
| needed for `align = (:left, :baseline)`. Single LaTeX run: the content is | ||
| wrapped in `\\sbox` + `\\typeout`, then `\\usebox`'d, so the same compile | ||
| produces both the PDF that gets rendered and a log line we parse back for | ||
| the baseline. The returned `cached` document is cropped with a symmetric | ||
| `h.crop_margin_pt` margin to avoid clipping anti-aliased glyph edges. | ||
| """ | ||
| function compile_latex_for_makie(h::MakieTeXLaTeX, latex_src::AbstractString) | ||
| src = String(latex_src) | ||
| body = """ | ||
| \\newsavebox\\makietexbaselinebox% | ||
| \\sbox\\makietexbaselinebox{$(src)}% | ||
| \\typeout{MAKIETEX_BASELINE_DEPTH=\\the\\dp\\makietexbaselinebox}% | ||
| \\usebox\\makietexbaselinebox | ||
| """ | ||
| # Build the TEXDocument ourselves so we can set `border=Npt` from the | ||
| # handler config — gives the page MediaBox enough room for our crop | ||
| # margin (otherwise the crop clamps to MediaBox and the margin | ||
| # disappears in y). | ||
| doc = TEXDocument(body, true; | ||
| requires = "\\RequirePackage{luatex85}", | ||
| preamble = h.preamble, | ||
| class = "standalone", | ||
| classoptions = "$(h.classoptions), border=$(h.border_pt)pt", | ||
| ) | ||
| engine = h.engine === nothing ? CURRENT_TEX_ENGINE[] : h.engine | ||
| pdf, baseline_pt = _compile_latex_capture_baseline(String(doc.contents), engine, h.crop_margin_pt) | ||
| # `CachedTEX(::Vector{UInt8})` is broken upstream (it stashes `nothing` | ||
| # into a strictly-typed `doc::TEXDocument` field). `CachedPDF` works | ||
| # fine and is what `page2img` dispatches on anyway. | ||
| return CachedPDF(PDFDocument(pdf)), baseline_pt | ||
| end | ||
|
|
||
| # Like MakieTeX's `crop_pdf`, but reads Ghostscript's `%%HiResBoundingBox` | ||
| # (sub-pt precision) rather than the integer `%%BoundingBox`. With the | ||
| # integer version, glyph edges can be jittered by up to ~1pt within the | ||
| # crop, which propagates into visible baseline misalignment. | ||
| function _hires_pdf_bbox(path::String) | ||
| out = Pipe(); err = Pipe() | ||
| success(pipeline(`$(Ghostscript_jll.gs()) -q -dBATCH -dNOPAUSE -sDEVICE=bbox $path`, stdout = out, stderr = err)) | ||
| close(out.in); close(err.in) | ||
| result = read(err, String) | ||
| m = match(r"%%HiResBoundingBox: ([-0-9.]+) ([-0-9.]+) ([-0-9.]+) ([-0-9.]+)", result) | ||
| m === nothing && error("could not extract %%HiResBoundingBox from gs output") | ||
| return parse.(Float64, (m.captures[1], m.captures[2], m.captures[3], m.captures[4])) | ||
| end | ||
|
|
||
| function _hires_crop_pdf(path::String, margin::Real) | ||
| bb = _hires_pdf_bbox(path) | ||
| crop_box = (bb[1] - margin, bb[2] - margin, bb[3] + margin, bb[4] + margin) | ||
| crop_cmd = join(crop_box, " ") | ||
| out_path = "_hires_cropped.pdf" | ||
| out = Pipe(); err = Pipe() | ||
| try | ||
| redirect_stderr(err) do | ||
| redirect_stdout(out) do | ||
| Ghostscript_jll.gs() do gs_exe | ||
| run(`$gs_exe -o $out_path -sDEVICE=pdfwrite -c "[/CropBox [$crop_cmd]" -c "/PAGES pdfmark" -f $path`) | ||
| end | ||
| end | ||
| end | ||
| catch e | ||
| finally | ||
| close(out.in); close(err.in) | ||
| end | ||
| return read(out_path) | ||
| end | ||
|
|
||
| # Mirror of MakieTeX.compile_latex's tempdir-and-latexmk pipeline, but also | ||
| # reads `temp.log` before the directory is torn down and pulls the baseline | ||
| # depth out of it. Returns the cropped PDF as `Vector{UInt8}` plus the depth | ||
| # in pt. | ||
| function _compile_latex_capture_baseline(document::String, engine::Cmd, crop_margin_pt::Real) | ||
| return mktempdir() do dir | ||
| cd(dir) do | ||
| write("temp.tex", document) | ||
| out = Pipe(); err = Pipe() | ||
| try | ||
| cmd = if engine == `tectonic` | ||
| `$(tectonic_jll.tectonic()) temp.tex` | ||
| else | ||
| `latexmk -file-line-error --shell-escape -cd -$(engine) -interaction=nonstopmode temp.tex` | ||
| end | ||
| run(pipeline(ignorestatus(cmd), stdout = out, stderr = err)) | ||
| finally | ||
| close(out.in); close(err.in) | ||
| end | ||
| log_text = isfile("temp.log") ? read("temp.log", String) : "" | ||
| m = match(r"MAKIETEX_BASELINE_DEPTH=([-0-9.]+)pt", log_text) | ||
| baseline_pt = m === nothing ? 0.0f0 : max(0.0f0, parse(Float32, m.captures[1])) | ||
| pdf = _hires_crop_pdf("temp.pdf", crop_margin_pt) | ||
| return pdf, baseline_pt | ||
| end | ||
| end | ||
| end | ||
|
|
||
| # Makie calls the handler as `h(outputs, latex_str, i, N, _inputs...)`. We | ||
| # make `MakieTeXLaTeX` callable with that exact signature. | ||
| function (h::MakieTeXLaTeX)( | ||
| outputs::NamedTuple, latex_str::LaTeXString, | ||
| i, N, fontsize, font, align, rotation, justification, | ||
| lineheight, word_wrap_width, offset, fonts, color, strokecolor, strokewidth | ||
| ) | ||
|
|
||
| fs = Float32(Makie.sv_getindex(fontsize, i)) | ||
| al = Makie.sv_getindex(align, i) | ||
| rot = convert(Makie.Quaternionf, Makie.sv_getindex(rotation, i)) | ||
| off = Makie.Vec3f(Makie.sv_getindex(offset, i)) | ||
|
|
||
| # Single LaTeX compile: yields the rendered PDF + the box-depth in pt for | ||
| # `align = (..., :baseline)`. The `CachedPDF` is passed straight to scatter | ||
| # as the marker — CairoMakie's `draw_marker(::CachedPDF, …)` override (in | ||
| # MakieTeXCairoMakieExt) hands it to `poppler_page_render`, which puts | ||
| # the PDF's vector content directly into the figure's Cairo surface (no | ||
| # raster intermediate, so hairlines stay crisp). | ||
| cached, baseline_pt = compile_latex_for_makie(h, String(latex_str)) | ||
|
|
||
| # `dim_pt` includes the safety pad `h.crop_margin_pt` on each side (added | ||
| # at crop time to keep anti-aliased glyphs from clipping). The natural | ||
| # ink box is `dim_pt - 2 * crop_margin_pt`. Alignment refers to the ink | ||
| # box so the safety pad doesn't shift positions. | ||
| scale = fs / h.base_pt | ||
| dim_pt = Makie.Vec2f(Float32(cached.dims[1]), Float32(cached.dims[2])) | ||
| target_size = dim_pt .* scale | ||
| ink_size = (dim_pt .- 2 * h.crop_margin_pt) .* scale | ||
| baseline_from_bottom = baseline_pt * scale | ||
|
|
||
| align_off = _latex_align_offset(al, ink_size, baseline_from_bottom) | ||
| marker_offset = Makie.Vec3f(align_off[1], align_off[2], 0) + off | ||
|
|
||
| # `text_blocks` must have one entry per input string. This block has no | ||
| # glyphs — only a Scatter spec — so we push an empty range. | ||
| curr = length(outputs.glyphindices) | ||
| push!(outputs.text_blocks, (curr + 1):curr) | ||
| push!( | ||
| outputs.glyphcollections, Makie.GlyphCollection( | ||
| UInt64[], Makie.NativeFont[], Makie.Point3f[], Makie.GlyphExtent[], | ||
| Makie.Vec2f[], Makie.Quaternionf[], Makie.RGBAf[], Makie.RGBAf[], Float32[] | ||
| ) | ||
| ) | ||
|
|
||
| # Emit the LaTeX render as a single Scatter PlotSpec. Positions are in | ||
| # block-relative markerspace (origin = the text block's anchor point); | ||
| # the text recipe shifts by the projected block position late, so we | ||
| # only need to supply a zero point here. `space`/`markerspace` are | ||
| # patched in by the recipe to match the parent text plot. | ||
| push!(outputs.text_specs, Makie.PlotSpec(:Scatter, Makie.Point3f[Makie.Point3f(0, 0, 0)]; | ||
| marker = [cached], | ||
| markersize = [target_size], | ||
| marker_offset = [marker_offset], | ||
| rotation = [rot], | ||
| )) | ||
| push!(outputs.text_spec_block_indices, i) | ||
|
|
||
| # Block-relative bbox: marker_offset is the center, ink box is target | ||
| # minus the 2*margin safety pad on each side; rotation is applied. | ||
| half = 0.5f0 .* Makie.Vec3f(target_size..., 0) | ||
| bb = Makie.Rect3d(Makie.to_ndim(Makie.Point3d, marker_offset, 0) .- half, Makie.Vec3d(target_size..., 0)) | ||
| push!(outputs.text_spec_bboxes, Makie.rotate_bbox(bb, rot)) | ||
| return | ||
| end |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm what's
scalehere? Is it px_per_unit?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was markersize, but I don't think that part makes much sense yet.