JuliaPhysics / JuliaPhysics/PeriodicTable.jl
Creating Periodic Table chart for inclusion in publications?
- Dominant language
- Julia
- Stars
- 124
- Forks
- 28
- PR merge metrics
- No merged PRs in 30d
Description
I was playing around with AI for code generation yesterday (MS Copilot in the Edge browser..., mixed feelings about it), and tried to create code that displays some info in PeriodicTable.jl in a "publishable" form.
**I just noticed that running `elements` in Jupyter Notebook creates a simple chart, similar to what is below. I had just run it in the REPL before**. So the simpler thing is probably to use what `elements` produces.
Here is an HTML version:
I think it is decent, but not perfect.
* colors and fonts can always be discussed
* compared to some tables on the internet, this version lacks the name of elements (typically found between the symbol and the atomic mass)
* the legend fonts and location stick out
* some internet versions put the group number just above the top element of the group, not like in this table.
The table has been created via PrettyTables.jl. I was considering exporting to LaTeX too, but am not sure how well LaTeX handles tables with colors. (I use LyX; LyX v2.5.0, RC1 has better support for tables with colors, but I didn't "dare" to try it.)
Here is an SVG version *without* annotation (Co-pilot didn't manage to use CairoMakie to produce annotation), using some Computer Modern like font.
I add the code below, in case anyone with better command of PrettyTables.jl and CairoMakie.jl and better sense of aesthetics find it interesting to improve the chart generation and make it available in PeriodicTable.jl. Or perhaps this already exists. I will probably check around in the PrettyTables.jl community and Makie.jl community, too.
### Packages
```julia
using PeriodicTable
using PrettyTables
using PrettyTables: HTML
using Colors
using Unitful
#
using CairoMakie
using GeometryBasics
```
### HTML with annotation
In the code below, first specify `figpath`, `figname`, and `figtype_h` before running the code. I used `figname = "Periodic_Table"`, while `figtype_h = ".html"`.
```julia
# --- 1. Define base category colors ---
const RAW_CATEGORY_COLORS = Dict(
"alkali metal" => colorant"tomato",
"alkaline earth metal" => colorant"gold",
"transition metal" => colorant"lightblue",
"post-transition metal" => colorant"lightsalmon",
"metalloid" => colorant"mediumseagreen",
"nonmetal" => colorant"palegreen",
"diatomic nonmetal" => colorant"palegreen",
"polyatomic nonmetal" => colorant"palegreen",
"noble gas" => colorant"skyblue",
"halogen" => colorant"plum",
"lanthanide" => colorant"lightgray",
"actinide" => colorant"darkgray"
)
# --- 2. Normalize categories ---
function normalize_category(cat::String)
if cat in ("nonmetal", "diatomic nonmetal", "polyatomic nonmetal")
return "nonmetal"
elseif occursin("unknown", lowercase(cat))
return "unknown / theoretical"
else
return lowercase(cat)
end
end
# --- 3. Define phase-based symbol colors ---
const PHASE_COLORS = Dict(
"Solid" => "black",
"Liquid" => "blue",
"Gas" => "red"
)
# --- 4. Convert elements to array for iteration ---
all_elements = collect(elements)
# --- 5. Build grid based on xpos/ypos ---
max_x = maximum(el -> el.xpos, all_elements)
max_y = maximum(el -> el.ypos, all_elements)
grid = Matrix{Union{PeriodicTable.Element, Nothing}}(undef, max_y, max_x)
fill!(grid, nothing)
for el in all_elements
x, y = el.xpos, el.ypos
if x > 0 && y > 0
grid[y, x] = el
end
end
# --- 6. Prepare raw table data ---
table_data = [el === nothing ? "" : el.symbol for el in grid]
# --- 7. Define HTML formatter with hierarchical alignment ---
function html_formatter(v, i, j)
el = grid[i, j]
if el === nothing
return ""
else
norm_cat = normalize_category(el.category)
phase = el.phase
raw_cat = el.category
rgb = get(RAW_CATEGORY_COLORS, raw_cat, colorant"white")
hex_bg = Colors.hex(rgb)
fg_color = get(PHASE_COLORS, phase, "gray")
rounded_mass = round(el.atomic_mass.val; digits=3)
mass_val = string(rounded_mass) * " u"
border_style = norm_cat == "unknown / theoretical" ? "1px solid lightgray" : "none"
return HTML("""
""")
end
end
# --- 8. Generate dynamic legend block from actual usage ---
function generate_legend()
used_categories = Dict{String, Colorant}()
used_phases = Set{String}()
for el in all_elements
norm_cat = normalize_category(el.category)
color = get(RAW_CATEGORY_COLORS, el.category, colorant"white")
used_categories[norm_cat] = color
push!(used_phases, el.phase)
end
category_lines = join([
norm_cat == "unknown / theoretical" ?
"$norm_cat" :
"$norm_cat"
for (norm_cat, c) in sort(collect(used_categories); by = first)
], "\n")
phase_lines = join([
"$p"
for p in ["Solid", "Liquid", "Gas"] if p in used_phases
], " ")
return """
legend:
element categories
$category_lines
phase at room temperature
$phase_lines
"""
end
# --- 9. Save to HTML file with centered title and synced legend ---
open(figpath*figname*figtype_h, "w") do io
write(io, generate_legend())
pretty_table(io, table_data;
column_labels = [[string(i) for i in 1:max_x]],
formatters = [html_formatter],
backend = :html,
allow_html_in_cells = true,
alignment = :c,
title = "
)
end
```
### SVG without annotation
In the code below, first specify `figpath`, `figname`, and `figtype_h` before running the code. I used `figname = "Periodic_Table"`, while `figtype = ".svg"`.
```julia
# --- Normalize categories ---
function normalize_category(cat::String)
if cat in ("nonmetal", "diatomic nonmetal", "polyatomic nonmetal")
return "nonmetal"
elseif occursin("unknown", lowercase(cat))
return "unknown / theoretical"
else
return lowercase(cat)
end
end
# --- Define category and phase colors ---
const CATEGORY_COLORS = Dict(
"alkali metal" => colorant"tomato",
"alkaline earth metal" => colorant"gold",
"transition metal" => colorant"lightblue",
"post-transition metal" => colorant"lightsalmon",
"metalloid" => colorant"mediumseagreen",
"nonmetal" => colorant"palegreen",
"noble gas" => colorant"skyblue",
"halogen" => colorant"plum",
"lanthanide" => colorant"lightgray",
"actinide" => colorant"darkgray"
)
const PHASE_COLORS = Dict(
"Solid" => :black,
"Liquid" => :blue,
"Gas" => :red
)
# --- Grid setup ---
elements_list = collect(elements)
max_x = maximum(e -> e.xpos, elements_list)
max_y = maximum(e -> e.ypos, elements_list)
# --- Cell size scaling factor ---
scale = 0.5 # Shrinks each cell to half its default size
fig = Figure(
size = (800, 600),
fontsize = 12,
fonts = (
regular = "Latin Modern Roman",
bold = "Latin Modern Roman Bold"
)
)
ax = Axis(
fig[1, 1];
limits = ((0, (max_x + 1) * scale), (0, (max_y + 1) * scale)),
aspect = DataAspect()
)
hidedecorations!(ax)
# --- Draw each element cell ---
for el in elements_list
x, y = el.xpos, el.ypos
if x > 0 && y > 0
flipped_y = max_y - y + 1
norm_cat = normalize_category(el.category)
bg_color = get(CATEGORY_COLORS, norm_cat, colorant"white")
fg_color = get(PHASE_COLORS, el.phase, :gray)
x0 = (x - 1) * scale
y0 = (flipped_y - 1) * scale
w, h = scale, scale
# Draw rectangle
poly!(ax, Rect(x0, y0, w, h), color = bg_color, strokewidth = 0.4, strokecolor = :black)
# Atomic number (top-left)
text!(ax, string(el.number),
position = (x0 + 0.05 * w, y0 + h - 0.05 * h),
align = (:left, :top),
fontsize = 9,
font = :regular
)
# Element symbol (centered, bold)
text!(ax, el.symbol,
position = (x0 + 0.5 * w, y0 + 0.5 * h),
align = (:center, :center),
color = fg_color,
fontsize = 12,
font = :bold
)
# Atomic mass (bottom-center)
text!(ax, string(round(el.atomic_mass.val; digits = 3)) * " u",
position = (x0 + 0.5 * w, y0 + 0.05 * h),
align = (:center, :bottom),
fontsize = 9,
font = :regular
)
end
end
# --- Export to SVG ---
save(figpath*figname*figtype, fig)
```
Contributor guide
No contributing guide indexed for this repository
Research direction
The issue provides Julia examples using PeriodicTable, PrettyTables, CairoMakie, and Jupyter Notebook rather than naming repository files or tests. Begin by reproducing the HTML and SVG examples, then determine the intended publication output and styling; done should mean an agreed chart-generation path is available in PeriodicTable.jl.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- html, julia, jupyter-notebook, latex
- Domain
- data-visualization
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100