import plotly.graph_objects as go
import plotly.io as pio
import numpy as np
import os
import uuid
"""
Interactive line chart example (3 curves + live slider)
The slider blends each curve from linear to exponential in real time (no mouseup required).
This fragment is safe to insert multiple times on the page (unique IDs per instance).
"""
# Grid (x) and parameterization
N = 240
x = np.linspace(0, 1, N)
# Linear baselines (increasing)
lin1 = 0.20 + 0.60 * x
lin2 = 0.15 + 0.70 * x
lin3 = 0.10 + 0.80 * x
# Helper: normalized exponential on [0,1]
def exp_norm(xv: np.ndarray, k: float) -> np.ndarray:
return (np.exp(k * xv) - 1.0) / (np.exp(k) - 1.0)
# Exponential counterparts (similar ranges)
exp1 = 0.20 + 0.60 * exp_norm(x, 3.0)
exp2 = 0.15 + 0.70 * exp_norm(x, 3.5)
exp3 = 0.10 + 0.80 * exp_norm(x, 2.8)
# Initial blend (alpha=0 ⇒ pure linear)
alpha0 = 0.0
blend = lambda l, e, a: (1 - a) * l + a * e
y1 = blend(lin1, exp1, alpha0)
y2 = blend(lin2, exp2, alpha0)
y3 = blend(lin3, exp3, alpha0)
color_base = "#64748b" # slate-500
color_improved = "#2563eb" # blue-600
color_target = "#4b5563" # gray-600 (dash)
fig = go.Figure()
fig.add_trace(
go.Scatter(
x=x,
y=y1,
name="Baseline",
mode="lines",
line=dict(color=color_base, width=2, shape="spline", smoothing=0.6),
hovertemplate="%{fullData.name}
x=%{x:.2f}
y=%{y:.3f}",
showlegend=True,
)
)
fig.add_trace(
go.Scatter(
x=x,
y=y2,
name="Improved",
mode="lines",
line=dict(color=color_improved, width=2, shape="spline", smoothing=0.6),
hovertemplate="%{fullData.name}
x=%{x:.2f}
y=%{y:.3f}",
showlegend=True,
)
)
fig.add_trace(
go.Scatter(
x=x,
y=y3,
name="Target",
mode="lines",
line=dict(color=color_target, width=2, dash="dash"),
hovertemplate="%{fullData.name}
x=%{x:.2f}
y=%{y:.3f}",
showlegend=True,
)
)
fig.update_layout(
autosize=True,
paper_bgcolor="rgba(0,0,0,0)",
plot_bgcolor="rgba(0,0,0,0)",
margin=dict(l=28, r=12, t=8, b=28),
hovermode="x unified",
hoverlabel=dict(
bgcolor="white",
font=dict(color="#111827", size=12),
bordercolor="rgba(0,0,0,0.15)",
align="left",
namelength=-1,
),
xaxis=dict(
showgrid=False,
zeroline=False,
showline=True,
linecolor="rgba(0,0,0,0.25)",
linewidth=1,
ticks="outside",
ticklen=6,
tickcolor="rgba(0,0,0,0.25)",
tickfont=dict(size=12, color="rgba(0,0,0,0.55)"),
title=None,
automargin=True,
fixedrange=True,
),
yaxis=dict(
showgrid=False,
zeroline=False,
showline=True,
linecolor="rgba(0,0,0,0.25)",
linewidth=1,
ticks="outside",
ticklen=6,
tickcolor="rgba(0,0,0,0.25)",
tickfont=dict(size=12, color="rgba(0,0,0,0.55)"),
title=None,
tickformat=".2f",
rangemode="tozero",
automargin=True,
fixedrange=True,
),
)
# Écrit le fragment de manière robuste à côté de ce fichier, dans src/fragments/line.html
output_path = os.path.join(os.path.dirname(__file__), "fragments", "line.html")
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Injecte un petit script post-rendu pour arrondir les coins de la boîte de hover
post_script = """
(function(){
function attach(gd){
function round(){
try {
var root = gd && gd.parentNode ? gd.parentNode : document;
var rects = root.querySelectorAll('.hoverlayer .hovertext rect');
rects.forEach(function(r){ r.setAttribute('rx', 8); r.setAttribute('ry', 8); });
} catch(e) {}
}
if (gd && gd.on) {
gd.on('plotly_hover', round);
gd.on('plotly_unhover', round);
gd.on('plotly_relayout', round);
}
setTimeout(round, 0);
}
var plots = document.querySelectorAll('.js-plotly-plot');
plots.forEach(attach);
})();
"""
html_plot = pio.to_html(
fig,
include_plotlyjs=False,
full_html=False,
post_script=post_script,
config={
"displayModeBar": False,
"responsive": True,
"scrollZoom": False,
"doubleClick": False,
"modeBarButtonsToRemove": [
"zoom2d", "pan2d", "select2d", "lasso2d",
"zoomIn2d", "zoomOut2d", "autoScale2d", "resetScale2d",
"toggleSpikelines"
],
},
)
# Build a self-contained fragment with a live slider (no mouseup required)
uid = uuid.uuid4().hex[:8]
slider_id = f"line-ex-alpha-{uid}"
container_id = f"line-ex-container-{uid}"
slider_tpl = '''
'''
slider_html = (slider_tpl
.replace('__CID__', container_id)
.replace('__SID__', slider_id)
.replace('__A0__', f"{alpha0:.2f}")
.replace('__N__', str(N))
.replace('__PLOT__', html_plot)
)
fig.write_html("../app/src/fragments/line.html",
include_plotlyjs=False,
full_html=False,
config={
'displayModeBar': False,
'responsive': True,
'scrollZoom': False,
})