SymbolicML / DynamicQuantities.jl

Efficient and type-stable physical quantities in Julia
https://symbolicml.org/DynamicQuantities.jl/dev/
Apache License 2.0
132 stars 17 forks source link

Logo #59

Open jkrumbiegel opened 11 months ago

jkrumbiegel commented 11 months 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 11 months 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 11 months 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 11 months 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 11 months 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 11 months 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 10 months 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 10 months ago

I'll try doing that soon :)