%% tikzlibrarytikzphysics.optics.code.tex
%% -----------------------------------------------------------
%% Optics module for tikzphysics.
%%
%% Phase 1 scope:
%%   - physicslens : thin curved optical element, drawn as a patterned
%%              strip between two circular arcs.
%%
%% Later phases can add mirrors, prisms, slabs, rays, focal markers,
%% and principal-axis helpers without coupling them to surface shapes.
%%
%% Internal namespace : \tikzphysics@optics@...
%% Requires           : tikz, patterns, calc, tikzphysics.core
%%
%% Author  : Vaibhav Blayer
%% Version : v1.0.0 (2026-08-12)
%% License : LPPL 1.3c
%% -----------------------------------------------------------

\usetikzlibrary{patterns, calc}
\usetikzlibrary{tikzphysics.core}

\makeatletter

%% ============================================================
%%  INTERNAL DIMENSIONS AND STATE
%% ============================================================

\newdimen\tikzphysics@optics@R
\newdimen\tikzphysics@optics@T
\newdimen\tikzphysics@optics@Rout
\newdimen\tikzphysics@optics@y
\newdimen\tikzphysics@optics@frontxraw
\newdimen\tikzphysics@optics@backxraw
\newdimen\tikzphysics@optics@bcxraw
\newdimen\tikzphysics@optics@backmidraw
\newdimen\tikzphysics@optics@shift
\newdimen\tikzphysics@optics@fcx
\newdimen\tikzphysics@optics@frontx
\newdimen\tikzphysics@optics@backx
\newdimen\tikzphysics@optics@bcx
\newdimen\tikzphysics@optics@frontmid
\newdimen\tikzphysics@optics@backmid
\newdimen\tikzphysics@optics@capmid

\def\tikzphysics@optics@lensradius{5cm}
\def\tikzphysics@optics@lensthickness{0.25cm}
\def\tikzphysics@optics@lensapertureangle{30}

%% ============================================================
%%  HELPERS
%% ============================================================

%% Resolve a length with tikzphysics semantics:
%% explicit units are respected; bare numbers are centimetres.
\newcommand{\tikzphysics@optics@resolvelength}[2]{%
  \tikzphysics@resolve@length{#1}{#2}%
}

%% Lens geometry.
%%
%% The front arc follows the reference construction:
%%   radius R, aperture angle A, path from -A to +A.
%%
%% The back arc has radius R + T.  Its angular aperture is solved
%% so the top and bottom caps remain horizontal while preserving
%% the requested cap thickness T.  This is the exact version of
%% the small-angle DA correction often used in hand-written TikZ.
\newcommand{\tikzphysics@optics@setlensdims}{%
  \tikzphysics@optics@resolvelength{\tikzphysics@optics@R}{\tikzphysics@optics@lensradius}%
  \tikzphysics@optics@resolvelength{\tikzphysics@optics@T}{\tikzphysics@optics@lensthickness}%
  \tikzphysics@optics@Rout=\tikzphysics@optics@R
  \advance\tikzphysics@optics@Rout by \tikzphysics@optics@T
  \pgfmathsetmacro{\tikzphysics@optics@A}{\tikzphysics@optics@lensapertureangle}%
  \ifdim\tikzphysics@optics@A pt>0pt\relax\else
    \PackageError{tikzphysics}{physics lens aperture angle must be greater than 0 degrees}%
      {Choose an aperture angle strictly between 0 and 90 degrees.}%
  \fi
  \ifdim\tikzphysics@optics@A pt<90pt\relax\else
    \PackageError{tikzphysics}{physics lens aperture angle must be less than 90 degrees}%
      {Choose an aperture angle strictly between 0 and 90 degrees.}%
  \fi
  \edef\tikzphysics@optics@Rnum{\strip@pt\tikzphysics@optics@R}%
  \edef\tikzphysics@optics@Tnum{\strip@pt\tikzphysics@optics@T}%
  \pgfmathsetmacro{\tikzphysics@optics@B}{%
    asin(\tikzphysics@optics@Rnum*sin(\tikzphysics@optics@A)/
         (\tikzphysics@optics@Rnum+\tikzphysics@optics@Tnum))}%
  \pgfmathsetlength{\tikzphysics@optics@y}{\tikzphysics@optics@R*sin(\tikzphysics@optics@A)}%
  \pgfmathsetlength{\tikzphysics@optics@frontxraw}{\tikzphysics@optics@R*cos(\tikzphysics@optics@A)}%
  \tikzphysics@optics@backxraw=\tikzphysics@optics@frontxraw
  \advance\tikzphysics@optics@backxraw by \tikzphysics@optics@T
  \pgfmathsetlength{\tikzphysics@optics@bcxraw}{%
    \tikzphysics@optics@backxraw-\tikzphysics@optics@Rout*cos(\tikzphysics@optics@B)}%
  \tikzphysics@optics@backmidraw=\tikzphysics@optics@bcxraw
  \advance\tikzphysics@optics@backmidraw by \tikzphysics@optics@Rout
  %% Centre the node on the midpoint between the two on-axis vertices.
  \tikzphysics@optics@shift=\tikzphysics@optics@R
  \advance\tikzphysics@optics@shift by \tikzphysics@optics@backmidraw
  \divide\tikzphysics@optics@shift by 2
  \tikzphysics@optics@fcx=-\tikzphysics@optics@shift
  \tikzphysics@optics@frontx=\tikzphysics@optics@frontxraw
  \advance\tikzphysics@optics@frontx by -\tikzphysics@optics@shift
  \tikzphysics@optics@backx=\tikzphysics@optics@backxraw
  \advance\tikzphysics@optics@backx by -\tikzphysics@optics@shift
  \tikzphysics@optics@bcx=\tikzphysics@optics@bcxraw
  \advance\tikzphysics@optics@bcx by -\tikzphysics@optics@shift
  \tikzphysics@optics@frontmid=\tikzphysics@optics@R
  \advance\tikzphysics@optics@frontmid by -\tikzphysics@optics@shift
  \tikzphysics@optics@backmid=\tikzphysics@optics@backmidraw
  \advance\tikzphysics@optics@backmid by -\tikzphysics@optics@shift
  \tikzphysics@optics@capmid=\tikzphysics@optics@frontx
  \advance\tikzphysics@optics@capmid by \tikzphysics@optics@backx
  \divide\tikzphysics@optics@capmid by 2
}

%% Numeric circular-arc anchors, analogous to core's linear edge
%% anchors.  Angles are in degrees; t=0 is the start angle and
%% t=100 is the end angle.
\def\tikzphysics@optics@declarearcanchor@one#1#2#3#4#5#6#7{%
  \anchor{#1-#2}{%
    \pgfmathsetmacro{\tikzphysics@optics@@theta}{(#6)+((#2)/100)*((#7)-(#6))}%
    \pgfmathsetlength{\pgf@x}{(#3)+(#5)*cos(\tikzphysics@optics@@theta)}%
    \pgfmathsetlength{\pgf@y}{(#4)+(#5)*sin(\tikzphysics@optics@@theta)}%
  }%
}

\def\tikzphysics@optics@declarearcanchors#1#2#3#4#5#6{%
  \foreach \tikzphysics@optics@@t in {0,1,...,100} {%
    \edef\tikzphysics@optics@@partial{%
      \noexpand\tikzphysics@optics@declarearcanchor@one{#1}{\tikzphysics@optics@@t}%
    }%
    \tikzphysics@optics@@partial{#2}{#3}{#4}{#5}{#6}%
  }%
}

%% ============================================================
%%  TIKZ KEYS
%% ============================================================

\tikzset{
  physics lens radius/.code         = {\def\tikzphysics@optics@lensradius{#1}},
  physics lens thickness/.code      = {\def\tikzphysics@optics@lensthickness{#1}},
  physics lens aperture angle/.code = {\def\tikzphysics@optics@lensapertureangle{#1}},
}

%% ============================================================
%%  LENS SHAPE
%% ============================================================

\pgfdeclareshape{physicslens}{
  \inheritsavedanchors[from=rectangle]
  \inheritanchorborder[from=rectangle]

  \savedmacro{\tikzphysics@optics@l@R}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@R{\the\tikzphysics@optics@R}%
  }
  \savedmacro{\tikzphysics@optics@l@Rout}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@Rout{\the\tikzphysics@optics@Rout}%
  }
  \savedmacro{\tikzphysics@optics@l@A}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@A{\tikzphysics@optics@A}%
  }
  \savedmacro{\tikzphysics@optics@l@B}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@B{\tikzphysics@optics@B}%
  }
  \savedmacro{\tikzphysics@optics@l@y}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@y{\the\tikzphysics@optics@y}%
  }
  \savedmacro{\tikzphysics@optics@l@fcx}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@fcx{\the\tikzphysics@optics@fcx}%
  }
  \savedmacro{\tikzphysics@optics@l@frontx}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@frontx{\the\tikzphysics@optics@frontx}%
  }
  \savedmacro{\tikzphysics@optics@l@backx}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@backx{\the\tikzphysics@optics@backx}%
  }
  \savedmacro{\tikzphysics@optics@l@bcx}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@bcx{\the\tikzphysics@optics@bcx}%
  }
  \savedmacro{\tikzphysics@optics@l@frontmid}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@frontmid{\the\tikzphysics@optics@frontmid}%
  }
  \savedmacro{\tikzphysics@optics@l@backmid}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@backmid{\the\tikzphysics@optics@backmid}%
  }
  \savedmacro{\tikzphysics@optics@l@capmid}{%
    \tikzphysics@optics@setlensdims
    \edef\tikzphysics@optics@l@capmid{\the\tikzphysics@optics@capmid}%
  }

  %% ---- bounding-box / compass anchors ----
  \anchor{center}{\pgf@x=0pt \pgf@y=0pt}
  \anchor{north}{\pgf@x=0pt \pgf@y=\tikzphysics@optics@l@y}
  \anchor{south}{\pgf@x=0pt \pgf@y=-\tikzphysics@optics@l@y}
  \anchor{east}{\pgf@x=\tikzphysics@optics@l@backmid \pgf@y=0pt}
  \anchor{west}{\pgf@x=\tikzphysics@optics@l@frontx \pgf@y=0pt}
  \anchor{north east}{\pgf@x=\tikzphysics@optics@l@backmid \pgf@y=\tikzphysics@optics@l@y}
  \anchor{north west}{\pgf@x=\tikzphysics@optics@l@frontx \pgf@y=\tikzphysics@optics@l@y}
  \anchor{south east}{\pgf@x=\tikzphysics@optics@l@backmid \pgf@y=-\tikzphysics@optics@l@y}
  \anchor{south west}{\pgf@x=\tikzphysics@optics@l@frontx \pgf@y=-\tikzphysics@optics@l@y}

  %% ---- optics anchors ----
  \anchor{vertex}{\pgf@x=\tikzphysics@optics@l@frontmid \pgf@y=0pt}
  \anchor{arc-mid}{\pgf@x=\tikzphysics@optics@l@frontmid \pgf@y=0pt}
  \anchor{front-mid}{\pgf@x=\tikzphysics@optics@l@frontmid \pgf@y=0pt}
  \anchor{back-mid}{\pgf@x=\tikzphysics@optics@l@backmid \pgf@y=0pt}
  \anchor{front-top}{\pgf@x=\tikzphysics@optics@l@frontx \pgf@y=\tikzphysics@optics@l@y}
  \anchor{front-bottom}{\pgf@x=\tikzphysics@optics@l@frontx \pgf@y=-\tikzphysics@optics@l@y}
  \anchor{back-top}{\pgf@x=\tikzphysics@optics@l@backx \pgf@y=\tikzphysics@optics@l@y}
  \anchor{back-bottom}{\pgf@x=\tikzphysics@optics@l@backx \pgf@y=-\tikzphysics@optics@l@y}
  \anchor{top}{\pgf@x=\tikzphysics@optics@l@capmid \pgf@y=\tikzphysics@optics@l@y}
  \anchor{bottom}{\pgf@x=\tikzphysics@optics@l@capmid \pgf@y=-\tikzphysics@optics@l@y}

  %% ---- background (fill/pattern) path ----
  \backgroundpath{%
    \pgfpathmoveto{\pgfpoint{\tikzphysics@optics@l@frontx}{-\tikzphysics@optics@l@y}}%
    \pgfpatharc{-\tikzphysics@optics@l@A}{\tikzphysics@optics@l@A}{\tikzphysics@optics@l@R}%
    \pgfpathlineto{\pgfpoint{\tikzphysics@optics@l@backx}{\tikzphysics@optics@l@y}}%
    \pgfpatharc{\tikzphysics@optics@l@B}{-\tikzphysics@optics@l@B}{\tikzphysics@optics@l@Rout}%
    \pgfpathlineto{\pgfpoint{\tikzphysics@optics@l@frontx}{-\tikzphysics@optics@l@y}}%
    \pgfpathclose%
  }

  %% Foreground: the optical surface arc from the reference shape.
  \foregroundpath{%
    \pgfsetlinewidth{0.5pt}%
    \pgfpathmoveto{\pgfpoint{\tikzphysics@optics@l@frontx}{-\tikzphysics@optics@l@y}}%
    \pgfpatharc{-\tikzphysics@optics@l@A}{\tikzphysics@optics@l@A}{\tikzphysics@optics@l@R}%
    \pgfusepath{stroke}%
  }

  %% front/back arc anchors, bottom to top.
  \tikzphysics@optics@declarearcanchors{front}
    {\tikzphysics@optics@l@fcx}{0pt}{\tikzphysics@optics@l@R}
    {-\tikzphysics@optics@l@A}{\tikzphysics@optics@l@A}
  \tikzphysics@optics@declarearcanchors{back}
    {\tikzphysics@optics@l@bcx}{0pt}{\tikzphysics@optics@l@Rout}
    {-\tikzphysics@optics@l@B}{\tikzphysics@optics@l@B}
}

%% ============================================================
%%  ANCHOR AND KEY REGISTRATION (for debug overlays)
%% ============================================================

\tikzphysics@registeranchors{physicslens}{%
  center, vertex, arc-mid, front-mid, back-mid,
  front-top, front-bottom, back-top, back-bottom,
  top, bottom, east, west}

\tikzphysics@registerkeys{physicslens}{%
  physics lens radius, physics lens thickness, physics lens aperture angle}
\tikzphysics@registerkeydefaults{physicslens}{%
  physics lens radius/5cm, physics lens thickness/0.25cm, physics lens aperture angle/$30^\circ$}

%% ============================================================
%%  COMPOUND NODE STYLES
%% ============================================================

\tikzset{
  physicslens/.style={
    shape=physicslens,
    anchor=center,
    draw=none,
    fill=white,
    pattern=north east lines,
    pattern color=black,
    inner sep=0pt,
    outer sep=0pt,
  },
}

\makeatother
\endinput
%%
%% End of file `tikzlibrarytikzphysics.optics.code.tex'.
