Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ jobs:
uses: codecov/codecov-action@v6.0.0
with:
files: ./coverage.xml
fail_ci_if_error: true
fail_ci_if_error: false
use_oidc: true
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<p align="center"><img src="docs/logo.svg" alt="StericRender" width="720"></p>
<p align="center"><img src="docs/logo.png" alt="StericRender" width="720"></p>

# StericRender: Topographic Steric Mapping with Molecular Visualisation

Expand Down
Binary file added docs/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 0 additions & 12 deletions docs/logo.svg

This file was deleted.

9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "stericrender"
version = "1.0.0"
version = "1.1.0"
description = "Topographic steric maps and buried volume (%VBur) with publication-quality SVG output."
readme = "README.md"
requires-python = ">=3.10"
Expand All @@ -28,8 +28,8 @@ validation = [
]
dev = [
"morfeus-ml>=0.8.0",
"ruff",
"ty",
"ruff>=0.15.12",
"ty>=0.0.33",
"pytest",
"pytest-cov",
]
Expand All @@ -56,6 +56,9 @@ python = ".venv"

[dependency-groups]
dev = [
"morfeus-ml>=0.8.0",
"ruff>=0.15.12",
"ty>=0.0.33",
"pytest",
"pytest-cov",
]
1 change: 1 addition & 0 deletions scripts/run_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from split_sambvca_si import split_multi_xyz


Expand Down
119 changes: 0 additions & 119 deletions scripts/validate_references.py

This file was deleted.

7 changes: 4 additions & 3 deletions src/stericrender/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def process_frame(args: argparse.Namespace, frame: StructureFrame, prefix: Path)
steric_map = compute_steric_map(
selected_positions,
selected_radii,
sphere_radius=map_radius,
sphere_radius=sphere_radius,
mesh=args.mesh,
)

Expand Down Expand Up @@ -236,7 +236,7 @@ def process_frame(args: argparse.Namespace, frame: StructureFrame, prefix: Path)
f"{prefix}_map.svg",
steric_map,
volume,
sphere_radius=map_radius,
sphere_radius=sphere_radius,
color_range=color_range,
palette=args.map_palette,
show_colorbar=not args.no_colorbar,
Expand All @@ -261,7 +261,8 @@ def process_frame(args: argparse.Namespace, frame: StructureFrame, prefix: Path)
output_svg=f"{prefix}_overlay.svg",
steric_map=steric_map,
volume=volume,
sphere_radius=map_radius,
sphere_radius=sphere_radius,
map_radius=map_radius,
render_config=args.render_config,
canvas_size=args.overlay_canvas_size,
zoom=args.zoom,
Expand Down
4 changes: 2 additions & 2 deletions src/stericrender/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ def sy(y: float) -> float:
lines.extend(
[
f'<circle cx="{cx:.2f}" cy="{cy:.2f}" r="{r_px:.2f}" fill="none" stroke="#1f2933" stroke-width="2"/>\n',
f'<line x1="{margin}" y1="{cy:.2f}" x2="{size - margin}" y2="{cy:.2f}" stroke="#1f2933" stroke-width="1.2"/>\n',
f'<line x1="{cx:.2f}" y1="{margin}" x2="{cx:.2f}" y2="{size - margin}" stroke="#1f2933" stroke-width="1.2"/>\n',
f'<line x1="{cx - r_px:.2f}" y1="{cy:.2f}" x2="{cx + r_px:.2f}" y2="{cy:.2f}" stroke="#1f2933" stroke-width="1.2"/>\n',
f'<line x1="{cx:.2f}" y1="{cy - r_px:.2f}" x2="{cx:.2f}" y2="{cy + r_px:.2f}" stroke="#1f2933" stroke-width="1.2"/>\n',
]
)
if show_quadrant_labels:
Expand Down
20 changes: 13 additions & 7 deletions src/stericrender/overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def write_xyzrender_overlay_svg(
steric_map: StericMapResult,
volume: BuriedVolumeResult,
sphere_radius: float,
map_radius: float | None = None,
render_config: str = "default",
canvas_size: int = 800,
zoom: float = 1.0,
Expand All @@ -47,30 +48,35 @@ def write_xyzrender_overlay_svg(
steric-map origin. The overlay layer then uses the same orthographic
projection convention as xyzrender: x to the right, y upward, z ignored for
the 2D topographic map.

sphere_radius controls the drawn circle and crosshairs (the analysis sphere
boundary). map_radius controls the viewport span and map fill extent; it
defaults to sphere_radius when not supplied.
"""
try:
from xyzrender import load, render
from xyzrender.config import build_config
except ModuleNotFoundError as exc:
raise RuntimeError("xyzrender is required for overlay SVG output") from exc

display_radius = map_radius if map_radius is not None else sphere_radius
cfg = build_config(render_config, canvas_size=canvas_size, orient=False, hy=include_hydrogens, bo=True)
cfg.fixed_center = (0.0, 0.0)
if zoom <= 0.0:
raise ValueError("--zoom must be greater than 0")
cfg.fixed_span = 2.0 * sphere_radius * zoom
cfg.fixed_span = 2.0 * display_radius * zoom
mol = load(oriented_xyz)
molecule_svg = str(render(mol, config=cfg, orient=False, stereo=stereo, stereo_style=stereo_style))
width, height = _svg_size(molecule_svg, default=canvas_size)
scale = (canvas_size - 2.0 * cfg.padding) / cfg.fixed_span
r = sphere_radius * scale
r_clip = display_radius * scale
if zoom > 1.0:
molecule_svg = _clip_molecule_to_viewport(molecule_svg)
else:
molecule_svg = _clip_molecule_to_disk(molecule_svg, cx=width / 2.0, cy=height / 2.0, r=r)
molecule_svg = _clip_molecule_to_disk(molecule_svg, cx=width / 2.0, cy=height / 2.0, r=r_clip)
footer_layout = _overlay_footer_layout(
height=height,
sphere_radius=sphere_radius,
map_radius=sphere_radius,
scale=scale,
show_colorbar=show_colorbar,
content_bottom=_molecule_content_bottom(molecule_svg),
Expand Down Expand Up @@ -167,7 +173,7 @@ def py(y: float) -> float:
if footer_layout is None:
footer_layout = _overlay_footer_layout(
height=height,
sphere_radius=sphere_radius,
map_radius=sphere_radius,
scale=scale,
show_colorbar=show_colorbar,
)
Expand Down Expand Up @@ -202,13 +208,13 @@ def py(y: float) -> float:
def _overlay_footer_layout(
*,
height: float,
sphere_radius: float,
map_radius: float,
scale: float,
show_colorbar: bool,
content_bottom: float | None = None,
) -> _OverlayFooterLayout:
"""Place overlay annotations after the visible map/molecule content."""
map_bottom = height / 2.0 + sphere_radius * scale
map_bottom = height / 2.0 + map_radius * scale
footer_anchor = max(map_bottom, content_bottom if content_bottom is not None else map_bottom)
label_y = footer_anchor + 40.0
if show_colorbar:
Expand Down
2 changes: 1 addition & 1 deletion src/stericrender/radii.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def radius_for_symbol(symbol: str, radii: str = "scaled-bondi", default: float =
if radii == "scaled-bondi":
return BONDI_RADII.get(normalized, default) * 1.17
if radii == "csd":
return CSD_RADII.get(normalized, default * 1.17)
return CSD_RADII.get(normalized, default)
raise ValueError(f"Unknown radii set: {radii}")


Expand Down
2 changes: 1 addition & 1 deletion src/stericrender/visual.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def colorbar_svg(
)
lines.append(
f'<text x="{x + width + 12:.2f}" y="{y + height + font_size + 7:.2f}" '
f'font-family="Arial,sans-serif" font-size="{font_size}" fill="#1f2933">(A)</text>\n'
f'font-family="Arial,sans-serif" font-size="{font_size}" fill="#1f2933">(Å)</text>\n'
)
lines.append("</g>\n")
return "".join(lines)
Expand Down
5 changes: 4 additions & 1 deletion tests/test_multi_xyz.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def test_cli_map_radius_independent_of_sphere_radius(tmp_path, monkeypatch):

def fake_overlay_renderer(**kwargs):
captured["sphere_radius"] = kwargs["sphere_radius"]
captured["map_radius"] = kwargs["map_radius"]

monkeypatch.setattr(cli, "write_xyzrender_overlay_svg", fake_overlay_renderer)

Expand All @@ -137,7 +138,9 @@ def fake_overlay_renderer(**kwargs):
)

metadata = json.loads(output_prefix.with_suffix(".json").read_text())["metadata"]
assert captured["sphere_radius"] == 5.0
# sphere_radius controls the circle and map; map_radius only widens the viewport
assert captured["sphere_radius"] == 3.5
assert captured["map_radius"] == 5.0
assert metadata["sphere_radius"] == 3.5
assert metadata["map_radius"] == 5.0

Expand Down
13 changes: 2 additions & 11 deletions tests/test_visual.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import sys
from pathlib import Path
from types import SimpleNamespace

import numpy as np
Expand Down Expand Up @@ -108,14 +107,6 @@ def test_steric_map_fill_svg_skips_empty_cells():
assert "data:image/png" not in svg


def test_readme_steric_map_example_has_no_quadrant_labels():
svg = Path("examples/images/sambvca/complex_04_meduphos_map.svg").read_text()
assert ">NE</text>" not in svg
assert ">NW</text>" not in svg
assert ">SW</text>" not in svg
assert ">SE</text>" not in svg


def test_contour_segments_find_crossing():
x = np.array([0.0, 1.0])
y = np.array([0.0, 1.0])
Expand Down Expand Up @@ -248,7 +239,7 @@ def fake_render(*args, **kwargs):
def test_overlay_footer_follows_zoomed_map_instead_of_viewport_bottom():
layout = _overlay_footer_layout(
height=800.0,
sphere_radius=3.5,
map_radius=3.5,
scale=67.857142857,
show_colorbar=True,
content_bottom=610.0,
Expand All @@ -262,7 +253,7 @@ def test_overlay_footer_follows_zoomed_map_instead_of_viewport_bottom():
def test_overlay_footer_respects_molecule_content_below_zoomed_map():
layout = _overlay_footer_layout(
height=800.0,
sphere_radius=3.5,
map_radius=3.5,
scale=67.857142857,
show_colorbar=True,
content_bottom=720.0,
Expand Down
14 changes: 10 additions & 4 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.