#Subfigures with caption

35 messages · Page 1 of 1 (latest)

worn idol
tough nymph
#

@worn idol Did you try to make a ref to subfigures ? I have implemented such à feature, but I am struggling with counters

worn idol
tough nymph
#

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
#

@tough nymph I think I got something that works reasonably well

#

with examples:

tough nymph
#

@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)
]
worn idol
#

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

worn idol
#
// 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)

tough nymph
#

I think you can now propose typst package for subfigure 😀

worn idol
#

The idea already crossed my mind 😀

worthy solstice
lime jungle
#

this is to make it optional I guess ?

worn idol
lime jungle
#

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" ) }

worn idol
worn idol
edgy ocean
#

where package smh

worn idol
edgy ocean
#

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

neon crow
#

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.

tough nymph
#

My current solution for this is to define a show ref.

winter shadow
worn idol
#

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