"""Reproduce the approximate native diagram from its semantic JSON companion.

Run with Python 3; output is written beside this file. No fitted equation or raster
background is used. The upper band is categorical and has no linear velocity map.
"""
from pathlib import Path
import json
from html import escape

root = Path(__file__).resolve().parent
d = json.loads((root/'c13-oxygen-pressure-velocity.json').read_text())
g = d['drawing_definition']['ordinary_plot']
def x(p): return g['x0']+(g['x100']-g['x0'])*p/100
def y(v): return g['y0']+(g['y80']-g['y0'])*v/80
def pts(v): return ' '.join(f'{a:.2f},{b:.2f}' for a,b in v)
def text(a,b,s,size=18,anchor='start',fill='#173047',weight='normal'):
 return f'<text x="{a}" y="{b}" font-size="{size}" text-anchor="{anchor}" fill="{fill}" font-weight="{weight}">{escape(s)}</text>'
end=d['drawing_endpoint']
samples=[end]+d['samples']
lower=[(x(a['pressure_bar_g']),y(a['lower_m_s'])) for a in samples]
upper=[(x(a['pressure_bar_g']),y(a['upper_m_s'])) for a in samples]
s=['<svg xmlns="http://www.w3.org/2000/svg" width="1240" height="1140" viewBox="0 0 1240 1140" role="img" aria-labelledby="title desc">',
 '<title id="title">Approximate oxygen pressure–velocity regions</title>',
 '<desc id="desc">Gauge-pressure axis0 to100bar, ordinary velocity axis0 to80m/s. Two approximately recovered high-pressure curves with uncertainty markers; distinct low-pressure carbon and stainless regions, separate broken sonic band. Four condition-dependent material regions, not a universal material ranking.</desc>',
 '<defs><pattern id="cross" width="12" height="12" patternUnits="userSpaceOnUse"><rect width="12" height="12" fill="#eef6fa"/><path d="M0,0L12,12M0,12L12,0" stroke="#8ca9b7" stroke-width=".6"/></pattern>',
 '<pattern id="rising" width="12" height="12" patternUnits="userSpaceOnUse"><rect width="12" height="12" fill="#f0f5ed"/><path d="M-3,3L3,-3M0,12L12,0M9,15L15,9" stroke="#829b78" stroke-width=".8"/></pattern>',
 '<pattern id="falling" width="12" height="12" patternUnits="userSpaceOnUse"><rect width="12" height="12" fill="#fff6e9"/><path d="M-3,9L3,15M0,0L12,12M9,-3L15,3" stroke="#b29b73" stroke-width=".8"/></pattern></defs>',
 '<rect width="1240" height="1140" fill="#fff"/><g font-family="Arial, sans-serif">',
 text(60,40,'Approximate oxygen pressure–velocity regions',26,weight='bold'),
 text(60,69,'Restricted dry-gas basis; apply material, temperature, cleanliness and component conditions',17),
 f'<rect x="100" y="180" width="1040" height="560" fill="#fff" stroke="#173047"/>',
 f'<polygon points="{pts(lower+[(x(100),y(0)),(x(10),y(0))])}" fill="url(#cross)"/>',
 f'<polygon points="{pts(upper+list(reversed(lower)))}" fill="url(#falling)"/>',
 f'<rect x="100" y="{y(60)}" width="104" height="{y(0)-y(60)}" fill="url(#cross)"/>',
 f'<rect x="100" y="{y(80)}" width="104" height="{y(60)-y(80)}" fill="url(#rising)"/>',
 '<rect x="100" y="100" width="104" height="45" fill="url(#rising)" stroke="#173047"/>',
 '<path d="M91,154l18,-7M91,164l18,-7M195,154l18,-7M195,164l18,-7" stroke="#173047" stroke-width="2"/>',
 text(235,115,'≈350 m/s sonic label — separate categorical band',17),
 text(235,142,'Axis break: no continuous 80–350 scale or interpolation',16),
 text(65,125,'≈350',16,anchor='end'),
 text(100,169,'Velocity, m/s',17,weight='bold')]
for p in range(0,101,10):
 s.append(f'<path d="M{x(p)},180V740" stroke="#c7d0d6" stroke-width="1"/>')
 s.append(text(x(p),768,str(p),17,anchor='middle'))
for v in range(0,81,10):
 s.append(f'<path d="M100,{y(v)}H1140" stroke="#c7d0d6" stroke-width="1"/>')
 s.append(text(82,y(v)+6,str(v),17,anchor='end'))
s += [f'<polyline points="{pts(lower)}" fill="none" stroke="#175579" stroke-width="3"/>',
      f'<polyline points="{pts(upper)}" fill="none" stroke="#865525" stroke-width="3"/>',
      '<path d="M204,180V740" stroke="#a44538" stroke-width="2" stroke-dasharray="6 5"/>',
      f'<path d="M100,{y(60)}H204" stroke="#173047" stroke-width="2"/>']
for a in d['samples']:
 for k,c in [('lower_m_s','#175579'),('upper_m_s','#865525')]:
  cx=x(a['pressure_bar_g']);cy=y(a[k]);u=a['estimated_ordinate_uncertainty_m_s'];y1=y(a[k]+u);y2=y(a[k]-u)
  s.append(f'<path d="M{cx},{y1}V{y2}M{cx-4},{y1}H{cx+4}M{cx-4},{y2}H{cx+4}" stroke="{c}" stroke-width="1.3"/><circle cx="{cx}" cy="{cy}" r="2.7" fill="{c}"/>')
s += [text(620,795,'Pressure, bar(g) — gauge reference',18,anchor='middle',weight='bold'),
 text(655,236,'Selected nickel alloy region',18),
 text(395,478,'Upper boundary',17,fill='#865525'),
 text(450,690,'Lower boundary',17,fill='#175579'),
 '<path d="M389,485L308,411M445,679L412,621" stroke="#173047" stroke-width="1"/>',
 text(100,823,'10 bar(g) transition: exact inclusion unresolved; corner is drawing geometry only.',17,fill='#a44538'),
 text(60,860,'Region legend — conditions remain part of every numerical reading',21,weight='bold')]
leg=[(892,'cross','Carbon steel up to 100°C; narrower in-plot below 90°C condition remains.'),
     (927,'rising','Stainless steel up to 150°C, with the specified material/service conditions.'),
     (962,'falling','Nickel alloy in turbulent or impingement areas; otherwise carbon steel'),
     (997,None,'up to 100°C AND 60 m/s, or stainless steel up to 150°C.'),
     (1032,'none','Selected nickel-based alloy; family name does not qualify a component.')]
for yy,pat,label in leg:
 if pat:s.append(f'<rect x="60" y="{yy-19}" width="35" height="23" fill="'+('white' if pat=='none' else f'url(#{pat})')+'" stroke="#173047"/>')
 s.append(text(108,yy,label,18))
s += [text(60,1075,'Markers: approximate whole-m/s recovery; ±2 m/s at 15 bar(g), ±1 m/s at 20–100 bar(g).',17),
 text(60,1102,'Straight segments are a drawing approximation, not an equation, margin or approval boundary.',17),
 '</g></svg>']
(root/'c13-oxygen-pressure-velocity.svg').write_text('\n'.join(s)+'\n')
