SymbolicML / DynamicQuantities.jl

Efficient and type-stable physical quantities in Julia
https://ai.damtp.cam.ac.uk/dynamicquantities
Apache License 2.0
133 stars 19 forks source link

Logo #59

Open jkrumbiegel opened 1 year ago

jkrumbiegel commented 1 year ago

Played around with Luxor yesterday, thought this package is still missing some kind of logo. Here's one idea, something like a "decoder ring". I thought about one being able to change between different units dynamically, "dialing them in", sort of.

Here's the Luxor code ```julia using Luxor using Luxor.Colors image_width = 500 @drawsvg begin circle_radius = 0.85 * image_width / 2 scale(circle_radius, circle_radius) offsets_deg = [20, 160, -2.5] colors = Base.splat(RGB).([Luxor.julia_green, Luxor.julia_red, Luxor.julia_purple]) inner_radius = 0.25 ring_thicknesses = [0.25, 0.25, 0.25] ringgap = 0.02 sectorgap_deg = 5 sector_whiten = 0.25 casing_opacity = 0.8 casing_overhang = 0.05 prefixes = ["p", "n", "μ", "m", "", "k"] units = ["g", "s", "m"] whiten(c, fraction) = Colors.weighted_color_mean(fraction, c, colorant"white") sector_width = 360 / length(prefixes) for i in 1:3 color = colors[i] r1 = inner_radius + (i-1) * ringgap + sum(ring_thicknesses[1:i-1], init = 0.0) r2 = r1 + ring_thicknesses[i] for (j, pref) in enumerate(prefixes) ang = offsets_deg[i] + ((j - 1) / length(prefixes) * 360) angstart = ang - 0.5 * sector_width angstop = ang + 0.5 * sector_width sethue(iseven(j) ? colors[i] : whiten(colors[i], 1 - sector_whiten)) sector(O, r1, r2, deg2rad(angstart), deg2rad(angstop), action = :fill) radius = 0.5 * (r1 + r2) center = Point(reverse(sincosd(ang))) * radius sethue("white") s = pref * units[i] @show s # textcurvecentered is buggy when scaling is active @layer begin scale(1/circle_radius, 1/circle_radius) fs = 25 fontsize(fs) fontface("Helvetica Bold") textcurvecentered(s, deg2rad(ang), radius * circle_radius, O, letter_spacing = 0, baselineshift = -0.3 * fs) end end end r1 = inner_radius - casing_overhang r2 = r1 + sum(ring_thicknesses) + ((length(ring_thicknesses) - 1) * ringgap) + (2 * casing_overhang) sethue(Luxor.julia_blue) setopacity(casing_opacity) sector(O, r1, r2, deg2rad(270 + 0.5 * sector_width), deg2rad(270 - 0.5 * sector_width), action = :fill) end image_width image_width ```
image
MilesCranmer commented 1 year ago

My god that is beautiful!!! Very nice work! It very cleanly conveys the concepts I think, and is also nice to look at.

My suggestions, if you would like them(?), are:

  1. The blue overlay is a bit heavy. I wonder if it could be lighter, and in the center of the transparent part, there could be a single line (like hands of a watch), to indicate that is the current value.
  2. In general, I wonder if a circle is the right symbolism here, since the physical units don't really 'wrap around' per se? Maybe 3 sliding rulers on top of eachother (basically the circles, but unwrapped) might work?
  3. The 'dynamic' part is moreso with respect to the exponent of a physical dimension (like m^1, m^2, m^3 - the integer can change without changing the type) rather than the prefix. I wonder if that could be indicated somehow...? Although it is also true that SymbolicDimensions let you change the prefix without changing the type! So it could be good as-is in that sense.

I'm very happy to work towards using this as the logo! Thanks so much for this.

jkrumbiegel commented 1 year ago

Maybe 3 sliding rulers on top of eachother (basically the circles, but unwrapped) might work?

Yeah I had been thinking the same, I'll try that out when I have some time

jkrumbiegel commented 1 year ago

Something in that direction?

using Luxor
using Luxor.Colors

image_width = 500

@drawsvg begin
    circle_radius = 0.85 * image_width / 2
    scale(circle_radius, circle_radius)

    offsets = [-0.07, 0.07, -0.02]
    colors = Base.splat(RGB).([Luxor.julia_green, Luxor.julia_red, Luxor.julia_purple])
    strip_thickness = 0.25
    stripgap = 0.04
    sectorwidth = 0.25
    sector_whiten = 0.2

    exponents = -3:3
    exp_shifts = [0, 3, -2]
    units = ["g", "s", "m"]

    whiten(c, fraction) = Colors.weighted_color_mean(fraction, c, colorant"white")

    n = length(units)

    full_height = n * stripgap + n * strip_thickness

    sethue(Luxor.julia_blue)
    box(O, sectorwidth * 0.8, full_height + 0.15, action = :fill)

    for i in 1:3
        color = colors[i]

        y = (i-1) * stripgap + (i-1) * strip_thickness - (full_height * (n-1) / n / 2) #+ (.5 * strip_thickness)

        for (j, exponent) in enumerate(exponents)
            e = exp_shifts[i] + exponent
            x = offsets[i] + (j * sectorwidth) - ((length(exponents)+1)/2 * sectorwidth)

            # sethue(colors[i])
            sethue(iseven(j) ? colors[i] : whiten(colors[i], 1 - sector_whiten))

            box(Point(x, y), sectorwidth, strip_thickness, action = :fill)

            sethue("white")
            s = "$(units[i])<sup>$e</sup>"

            @layer begin
                setfont("Helvetica Bold", 20)
                settext(s, Point(x, y); valign = "center", halign = "center", markup = true)
            end
        end
    end

    sethue(Luxor.julia_blue)
    setline(20)
    box(O, sectorwidth + 0.1, full_height + 0.15, action = :stroke)

end image_width image_width
image
MilesCranmer commented 1 year ago

Brilliant! What do you think?

I almost feel like the ms and km are more suggestive of physical units than s and m. I wonder if there's a sensible way to mix them.

With SymbolicDimensions you can basically have a sliding scale across whatever symbols you like. So it is valid to have powers of ms or km or whatever other symbol/unit desired. Even physical constants like ℏ can have arbitrary powers. So perhaps that could be interesting to display instead, as it is more indicative of physics maybe... Wdyt?

jkrumbiegel commented 1 year ago

The exact semantics of the package might be difficult to put into logo form exactly :) But at least I think the units with prefixes are more easily understood as units than the version with different powers. ks is probably not great and could be h instead

using Luxor
using Luxor.Colors

image_width = 500

@drawsvg begin
    circle_radius = 0.85 * image_width / 2
    scale(circle_radius, circle_radius)

    offsets = [-0.13, 0.06, -0.07]
    colors = Base.splat(RGB).([Luxor.julia_green, Luxor.julia_red, Luxor.julia_purple])
    strip_thickness = 0.25
    stripgap = 0.04
    sectorwidth = 0.25
    sector_whiten = 0.2

    prefixes = ["p", "f", "μ", "m", "", "k"]
    units = ["g", "s", "m"]

    whiten(c, fraction) = Colors.weighted_color_mean(fraction, c, colorant"white")

    n = length(units)

    full_height = n * stripgap + n * strip_thickness

    sethue(Luxor.julia_blue)
    box(O, sectorwidth * 0.75, full_height + 0.15, action = :fill)

    for i in 1:3
        color = colors[i]

        y = (i-1) * stripgap + (i-1) * strip_thickness - (full_height * (n-1) / n / 2) #+ (.5 * strip_thickness)

        for (j, prefix) in enumerate(prefixes)
            x = offsets[i] + (j * sectorwidth) - ((length(prefixes)+1)/2 * sectorwidth)

            # sethue(colors[i])
            sethue(iseven(j) ? colors[i] : whiten(colors[i], 1 - sector_whiten))

            box(Point(x, y), sectorwidth, strip_thickness, action = :fill)

            sethue("white")
            s = "$(prefix)$(units[i])"

            @layer begin
                setfont("Helvetica Bold", 20)
                settext(s, Point(x, y); valign = "center", halign = "center", markup = true)
            end
        end
    end

    sethue(Luxor.julia_blue)
    setline(20)
    box(O, sectorwidth + 0.08, full_height + 0.15, action = :stroke)

end image_width image_width
image
MilesCranmer commented 1 year ago

Hi @jkrumbiegel, I think this looks awesome. Do you want to push it in a PR to create the logo on the README so that the commit is credited to your username? Cheers! Miles

jkrumbiegel commented 1 year ago

I'll try doing that soon :)