#Subfigures with caption
35 messages · Page 1 of 1 (latest)
@worn idol Did you try to make a ref to subfigures ? I have implemented such à feature, but I am struggling with counters
No, I haven't but I can try and give it a shot
I have just tried and it throws an error. Anyway, it has almost all the features required. I will try to see how to circumvent this referencing issue.
@worn idol Thank you very much your solution works well. Your code is amazing. @visual glade proposed a solution closer of my initial implementation and that seems also works well.
I post it here
// Updates subfigure counter properly
#show figure.where(kind:image): it => {
counter(figure.where(kind:"subfigure")).update(0)
it
}
// Reference subfigures properly
#show ref: it => {
let el = it.element
if el != none and el.func() == figure and el.kind == "subfigure" [
#locate(loc => {
let fig-counter = counter(figure.where(kind: image)).at(el.location()).first()
let subfig-numbering = numbering(el.numbering, counter(figure.where(kind: "subfigure")).at(el.location()).first())
[Figure #fig-counter.#subfig-numbering]
})
] else {
it
}
}
// Proper caption for subfigures
#show figure.where(kind:"subfigure"): it => align(center)[
#it.body
#if it.caption != none {
v(-5pt)
[(#it.counter.display(it.numbering)) #it.caption]
}
#v(5pt)
]
I must admit that the handling of counters looks a lot cleaner in that solution. I will probably try and incorporate some of it into my code
// Make resetting of subfigure-counter configurable via
// `#reset-on-new-figure(false/true)`
#state("reset-on-new-figure").update(true)
#let reset-on-new-figure(reset) = {
locate(loc => {
state("reset-on-new-figure").update(reset)
})
}
// Update subfigure counter (if not disabled)
#show figure: it => {
locate(loc => {
if state("reset-on-new-figure").at(loc) and it.kind != "subfigure" {
counter(figure.where(kind: "subfigure")).update(0)
it
} else {
it
}
})
}
// Render subfigure
#show figure.where(kind: "subfigure"): it => {
let number = it.counter.display(it.numbering)
let caption = if not it.caption == none {
align(center)[#number #it.caption]
} else {
[#" "] // fixes spacing issues if caption-pos == top and it.caption == none
}
if it.caption-pos == top {
caption
v(-0.65em + it.gap) // removes native spacing and inserts customizable one
}
it.body // put in the body
if it.caption-pos == bottom {
v(-0.65em + it.gap)
caption
}
if it.caption-pos != top and it.caption-pos != bottom {
panic("This caption position is not supported, please file an issue with the maintainer")
}
}
// Make subfigures referencable
#show ref: it => {
let elem = it.element
if elem != none and elem.kind == "subfigure" {
let number = locate(loc => {
let figures = query(selector(figure).before(loc), loc)
let parent-figure = figures.filter(f => f.kind != "subfigure").last()
let fig-counter = parent-figure.counter.at(elem.location()).first()
let subfig-numbering = numbering(elem.numbering, counter(figure.where(kind: "subfigure")).at(elem.location()).first())
[Figure #fig-counter.#subfig-numbering]
})
[#elem.supplement #number]
} else {
it
}
}
// Notes:
// Subfigures can not be inside other subfigures (probably not easily fixable)
// Might break if parent figure is numbered differently (e.g. "1.1" instead of just "1"), haven't tested that
#let subfigure(caption: none, caption-pos: bottom, supplement: "Subfigure", numbering: "(a)", gap: 0em, outlined: false, ..body) = {
if body.named().len() != 0 {
panic("You specified an arguemnt with an invalid name!")
}
if body.pos().len() > 1 {
panic("The subfigure body is the only positional argument allowed, you specified more than one!")
}
figure(caption: caption, caption-pos: caption-pos, supplement: supplement, numbering: numbering, gap: gap, outlined: outlined, kind: "subfigure", ..body.pos())
}
@tough nymph thanks to @visual glade's solution my code now looks a lot cleaner!
It should be more configurable than their solution, one can all parameters that figure() has as well (those that make sense for subfigure). Additionally one can now decide if the subfigure counter should be reset for a new figure (decidable on a figure to figure basis, comparable to set-rule)
I think you can now propose typst package for subfigure 😀
The idea already crossed my mind 😀
why have variadic arguments when you then panic upon any keyword argument or more than one positional argument? why not just take one positional argument body?
this is to make it optional I guess ?
the idea was to have the interface as similar to figure as possible (and I don't know if there is a more elegant way to achieve that)
but having a named arguments called body should work just as well, but it would differ more from figure
use a positional argument ? figure's body isn't optional
so something like #let subfigure( body, caption: none, caption-pos: bottom, supplement: "Subfigure", numbering: "(a)", gap: 0em, outlined: false ) = { figure( body, caption: caption, caption-pos: caption-pos, supplement: supplement, numbering: numbering, gap: gap, outlined: outlined, kind: "subfigure" ) }
That would indeed be better
An updated version that works with the newest Typst compiler
where package smh
I am planning to submit a PR
Just today I asked for some nice naming ideas (#discussions message) :D
ah well yea thats difficult lol
it should be quite clear that its just about subfigures but that name is ofc too general
multi-fig idk lol
i mean, i-figured exists so having "figure" in there somehow is fine
Is there a way to just have a subcaptions (but not subfigures) in the caption and references to them?
More generally to be able to provide a list like this:
A Main Point: There are many reasons to want an inline list A) They are cool B) They are more condensed C) You can used them in figure captions without huge white space.
Somehow like this:
#mainpoint()[There are many reasons to want an inline list][#enum(space=" ")[They are cool]<fig1_a> [They are more condensed]<fig1_b> [You can used them in figure captions without huge white space.]<fig1_c> ] <fig1>
Now we can refer to some points like @fig1_b. @fig1 overall is useful, but the specific point in @fig1_c is most compelling.
Or some other syntax which makes sense.
One important point would be for the label or reference to be able to generate the "Figure 1a" text when called with @fig1_a or "Point 1b" if used in the in-line text case.
My current solution for this is to define a show ref.
How do you reference a subfigure? Using <> after subfigures gives me an error.
It should just work™
be sure to use <> only in content mode, in script mode it doesn't work
// this works
#figure(
grid(columns: 3, gutter: 2em,
[#subfigure(image("cat.jpg", width: 100%), caption: "") <test1>],
),
caption: "Figure caption"
)
// this doesn't work
#figure(
grid(columns: 3, gutter: 2em,
subfigure(image("cat.jpg", width: 100%), caption: "") <test1>,
),
caption: "Figure caption"
)
<> should work with subfigure at all places where it works with figure