%% tikzlibrarytikzphysics.mechanics.code.tex
%% -----------------------------------------------------------
%% Mechanics module for tikzphysics.
%%
%% Phase 1 scope (migrated from the pre-release surface prototype):
%%   - physicsblock  : simple rectangular block style
%%   - physicspulley : circle with optional axle-dot
%%   - physicsspring : configurable coil with attachment anchors
%%   - \physicsstringoverpulley : tangent string segments plus circular wrap
%%
%% Later phases will add pulleys with grooves and inclined-plane aliases.
%%
%% Internal namespace : \tikzphysics@mech@...
%% Requires           : tikz, calc, tikzphysics.core
%%
%% Author  : Vaibhav Blayer
%% Version : v1.1.0 (2026-08-15)
%% License : LPPL 1.3c
%% -----------------------------------------------------------

\usetikzlibrary{calc,decorations.pathmorphing}
\usetikzlibrary{tikzphysics.core}

\makeatletter

%% ============================================================
%%  INTERNAL STATE
%% ============================================================

\def\tikzphysics@mech@pulleycenterfill{2pt}
\def\tikzphysics@mech@pulleycenterfillcolor{black}
\def\tikzphysics@mech@stringstartsolution{2}
\def\tikzphysics@mech@stringendsolution{1}
\def\tikzphysics@mech@stringdelta{-(Mod(\n1-\n2,360))}
\def\tikzphysics@mech@springpre{5pt}
\def\tikzphysics@mech@springpost{5pt}
\def\tikzphysics@mech@springamplitude{4.5pt}
\def\tikzphysics@mech@springsegment{4.5pt}
\def\tikzphysics@mech@springaspect{0.5}
\newdimen\tikzphysics@mech@springkeydim

%% Optional command arguments are often split over lines. Trim only their
%% leading/trailing spaces before handing the unchanged key list to TikZ.
\ExplSyntaxOn
\cs_new_protected:Npn \tikzphysics_mech_tikzset_trim:n #1
  {
    \tl_set:Nn \l_tmpa_tl {#1}
    \tl_trim_spaces:N \l_tmpa_tl
    \exp_args:NV \tikzset \l_tmpa_tl
  }
\cs_new_eq:NN \tikzphysics@mech@tikzsettrim \tikzphysics_mech_tikzset_trim:n
\ExplSyntaxOff

%% ============================================================
%%  TIKZ KEYS (scoped, unit-aware)
%% ============================================================

\tikzset{
  %% ---- block sizing ----
  physics block width/.code  = {\tikzphysics@length@keyhandler{/pgf/minimum width}{#1}},
  physics block height/.code = {\tikzphysics@length@keyhandler{/pgf/minimum height}{#1}},
  %% ---- pulley sizing ----
  physics pulley diameter/.code = {\tikzphysics@length@keyhandler{/pgf/minimum size}{#1}},
  %% ---- spring geometry ----
  physics spring pre length/.code={%
    \tikzphysics@resolve@length{\tikzphysics@mech@springkeydim}{#1}%
    \edef\tikzphysics@mech@springpre{\the\tikzphysics@mech@springkeydim}},
  physics spring post length/.code={%
    \tikzphysics@resolve@length{\tikzphysics@mech@springkeydim}{#1}%
    \edef\tikzphysics@mech@springpost{\the\tikzphysics@mech@springkeydim}},
  physics spring amplitude/.code={%
    \tikzphysics@resolve@length{\tikzphysics@mech@springkeydim}{#1}%
    \edef\tikzphysics@mech@springamplitude{\the\tikzphysics@mech@springkeydim}%
    \pgfkeysalso{/pgf/minimum height={2*(\tikzphysics@mech@springamplitude)}}},
  physics spring segment length/.code={%
    \tikzphysics@resolve@length{\tikzphysics@mech@springkeydim}{#1}%
    \edef\tikzphysics@mech@springsegment{\the\tikzphysics@mech@springkeydim}},
  physics spring aspect/.code={\def\tikzphysics@mech@springaspect{#1}},
  pre length/.style={physics spring pre length={#1}},
  post length/.style={physics spring post length={#1}},
  amplitude/.style={physics spring amplitude={#1}},
  segment length/.style={physics spring segment length={#1}},
  aspect/.style={physics spring aspect={#1}},
  %% ---- pulley axle-dot ----
  physics pulley axle radius/.code = {\def\tikzphysics@mech@pulleycenterfill{#1}},
  physics pulley axle color/.code  = {\def\tikzphysics@mech@pulleycenterfillcolor{#1}},
  physics pulley axle radius       = 2pt,
  physics pulley axle color        = black,
  %% ---- tangent string construction ----
  physics string start solution/.code = {\def\tikzphysics@mech@stringstartsolution{#1}},
  physics string end solution/.code   = {\def\tikzphysics@mech@stringendsolution{#1}},
  physics string wrap/.is choice,
  physics string wrap/clockwise/.code = {\def\tikzphysics@mech@stringdelta{-(Mod(\n1-\n2,360))}},
  physics string wrap/counterclockwise/.code = {\def\tikzphysics@mech@stringdelta{Mod(\n2-\n1,360)}},
  physics string wrap/shortest/.code = {\def\tikzphysics@mech@stringdelta{Mod(\n2-\n1+180,360)-180}},
  %% Named routes for the common case in which the first endpoint lies
  %% generally to the left of the pulley and the second lies to its right.
  %% The explicit solution/wrap keys remain available for every orientation.
  physics string route/.is choice,
  physics string route/over/.style={
    physics string start solution=2,
    physics string end solution=1,
    physics string wrap=clockwise,
  },
  physics string route/under/.style={
    physics string start solution=1,
    physics string end solution=2,
    physics string wrap=counterclockwise,
  },
  physics string route/surface-right/.style={
    physics string start solution=2,
    physics string end solution=1,
    physics string wrap=clockwise,
  },
  physics string route/shortest/.style={physics string wrap=shortest},
  physics string/.style={line width=0.5pt},
}

%% ============================================================
%%  BLOCK, SPRING, AND PULLEY SHAPES/STYLES
%% ============================================================

%% A spring is an empty rectangle-derived node whose visible path runs from
%% west to east.  Using a node rather than an overlaid decoration gives the
%% spring stable attachment and coil-boundary anchors and lets ordinary TikZ
%% transformations rotate the complete object.
\pgfdeclareshape{physicsspring}{
  \inheritsavedanchors[from=rectangle]
  \inheritanchorborder[from=rectangle]
  \inheritanchor[from=rectangle]{center}
  \inheritanchor[from=rectangle]{north}
  \inheritanchor[from=rectangle]{south}
  \inheritanchor[from=rectangle]{east}
  \inheritanchor[from=rectangle]{west}
  \inheritanchor[from=rectangle]{north east}
  \inheritanchor[from=rectangle]{north west}
  \inheritanchor[from=rectangle]{south east}
  \inheritanchor[from=rectangle]{south west}

  \savedmacro{\tikzphysics@mech@spring@pre}{\edef\tikzphysics@mech@spring@pre{\tikzphysics@mech@springpre}}
  \savedmacro{\tikzphysics@mech@spring@post}{\edef\tikzphysics@mech@spring@post{\tikzphysics@mech@springpost}}
  \savedmacro{\tikzphysics@mech@spring@amplitude}{\edef\tikzphysics@mech@spring@amplitude{\tikzphysics@mech@springamplitude}}
  \savedmacro{\tikzphysics@mech@spring@segment}{\edef\tikzphysics@mech@spring@segment{\tikzphysics@mech@springsegment}}
  \savedmacro{\tikzphysics@mech@spring@aspect}{\edef\tikzphysics@mech@spring@aspect{\tikzphysics@mech@springaspect}}

  \anchor{start}{\pgf@anchor@rectangle@west}
  \anchor{end}{\pgf@anchor@rectangle@east}
  \anchor{coil-start}{\pgf@anchor@rectangle@west\advance\pgf@x by\tikzphysics@mech@spring@pre\relax}
  \anchor{coil-mid}{\pgf@anchor@rectangle@center}
  \anchor{coil-end}{\pgf@anchor@rectangle@east\advance\pgf@x by-\tikzphysics@mech@spring@post\relax}

  \foregroundpath{%
    \pgf@process{\pgf@anchor@rectangle@west}%
    \pgf@xa=\pgf@x \pgf@ya=\pgf@y
    \pgf@process{\pgf@anchor@rectangle@east}%
    \pgf@xb=\pgf@x \pgf@yb=\pgf@y
    \pgf@xc=\pgf@xa \advance\pgf@xc by\tikzphysics@mech@spring@pre\relax
    \pgf@yc=\pgf@xb \advance\pgf@yc by-\tikzphysics@mech@spring@post\relax
    \ifdim\pgf@yc>\pgf@xc\relax\else
      \PackageError{tikzphysics}{spring pre length plus post length must be smaller than minimum width}%
        {Increase minimum width or reduce the spring's pre/post lengths.}%
    \fi
    %% The decoration engine reuses PGF's scratch dimensions, so freeze the
    %% four axial coordinates before invoking it.
    \edef\tikzphysics@mech@spring@left{\the\pgf@xa}%
    \edef\tikzphysics@mech@spring@right{\the\pgf@xb}%
    \edef\tikzphysics@mech@spring@coilstart{\the\pgf@xc}%
    \edef\tikzphysics@mech@spring@coilend{\the\pgf@yc}%
    \edef\tikzphysics@mech@spring@axis{\the\pgf@ya}%
    \pgfpathmoveto{\pgfqpoint{\tikzphysics@mech@spring@left}{\tikzphysics@mech@spring@axis}}%
    \pgfpathlineto{\pgfqpoint{\tikzphysics@mech@spring@coilstart}{\tikzphysics@mech@spring@axis}}%
    \pgfusepath{stroke}%
    \pgfkeys{/pgf/decoration/.cd,
      amplitude=\tikzphysics@mech@spring@amplitude,
      segment length=\tikzphysics@mech@spring@segment,
      aspect=\tikzphysics@mech@spring@aspect}%
    \pgfdecoratepath{coil}{%
      \pgfpathmoveto{\pgfqpoint{\tikzphysics@mech@spring@coilstart}{\tikzphysics@mech@spring@axis}}%
      \pgfpathlineto{\pgfqpoint{\tikzphysics@mech@spring@coilend}{\tikzphysics@mech@spring@axis}}}%
    \pgfusepath{stroke}%
    \pgfpathmoveto{\pgfqpoint{\tikzphysics@mech@spring@coilend}{\tikzphysics@mech@spring@axis}}%
    \pgfpathlineto{\pgfqpoint{\tikzphysics@mech@spring@right}{\tikzphysics@mech@spring@axis}}%
    \pgfusepath{stroke}%
  }
}

\tikzset{
  physicsblock/.style={
    shape=rectangle,
    physics logical shape=physicsblock,
    draw=black,
    line width=0.5pt,
    fill=none,
    inner sep=0pt,
    outer sep=0pt,
    minimum width=1cm,
    minimum height=1cm,
  },
  physicspulley/.style={
    shape=circle,
    physics logical shape=physicspulley,
    draw=black,
    line width=0.5pt,
    fill=none,
    inner sep=0pt,
    outer sep=0pt,
    minimum size=1cm,
    path picture={%
      \def\tikzphysics@mech@pulley@none{none}%
      \ifx\tikzphysics@mech@pulleycenterfill\tikzphysics@mech@pulley@none\relax
      \else
        \fill[\tikzphysics@mech@pulleycenterfillcolor]
          (path picture bounding box.center)
          circle[radius=\tikzphysics@mech@pulleycenterfill];%
      \fi
    },
  },
  physicsspring/.style={
    shape=physicsspring,
    physics logical shape=physicsspring,
    draw=black,
    line width=0.5pt,
    fill=none,
    inner sep=0pt,
    outer sep=0pt,
    minimum width=3cm,
    physics spring pre length=5pt,
    physics spring post length=5pt,
    physics spring amplitude=4.5pt,
    physics spring segment length=4.5pt,
    physics spring aspect=0.5,
  },
}

%% Short public names; use the physics-prefixed forms if a document already
%% defines generic block or pulley styles.
\tikzset{
  block/.style={physicsblock},
  pulley/.style={physicspulley},
  spring/.style={physicsspring},
}

%% ============================================================
%%  TANGENT STRING OVER A PULLEY
%%
%%  Usage:
%%    \physicsstringoverpulley[<TikZ options>]{<start>}{<pulley>}{<end>}
%%
%%  The start and end arguments are coordinate names without parentheses;
%%  the pulley argument is the name of a physicspulley node. The two straight
%%  portions terminate at exact tangent points computed by TikZ's calc
%%  library. Between them, the path follows the pulley's true circular arc.
%%
%%  The default surface-right route (solutions 2/1, clockwise wrap) matches a
%%  block approaching parallel to a horizontal or rising surface. The string
%%  passes over the pulley's upper rim before descending on its right. The
%%  three component keys remain available for other configurations.
%% ============================================================

\newcommand{\physicsstringoverpulley}[4][]{%
  \begin{scope}[
    physics string route=surface-right]
    \tikzphysics@mech@tikzsettrim{#1}%
    \draw[physics string]
      let
        \p1=(tangent cs:node=#3,point={(#2)},solution=\tikzphysics@mech@stringstartsolution),
        \p2=(#3.center),
        \p3=(tangent cs:node=#3,point={(#4)},solution=\tikzphysics@mech@stringendsolution),
        \n1={atan2(\y1-\y2,\x1-\x2)},
        \n2={atan2(\y3-\y2,\x3-\x2)},
        \n3={veclen(\x1-\x2,\y1-\y2)},
        \n4={\tikzphysics@mech@stringdelta}
      in
        (#2) -- (\p1)
        arc[start angle=\n1,delta angle=\n4,radius=\n3]
        -- (#4);
  \end{scope}%
}

%% ============================================================
%%  ANCHOR AND KEY REGISTRATION (for debug overlays)
%%
%%  physicsblock and physicspulley are thin styles over rectangle and
%%  circle, so we register a curated list of anchors that are
%%  most useful in mechanics diagrams.
%% ============================================================

\tikzphysics@registeranchors{physicsblock}{%
  center, north, south, east, west,
  north east, north west, south east, south west}

\tikzphysics@registeranchors{physicspulley}{%
  center, north, south, east, west,
  north east, north west, south east, south west}

\tikzphysics@registeranchors{physicsspring}{%
  center, start, end, coil-start, coil-mid, coil-end,
  north, south, east, west}

\tikzphysics@registerdisplayname{physicsblock}{block}
\tikzphysics@registerdisplayname{physicspulley}{pulley}
\tikzphysics@registerdisplayname{physicsspring}{spring}

\tikzphysics@registerkeys{physicsblock}{%
  minimum width, minimum height}
\tikzphysics@registerkeydefaults{physicsblock}{%
  minimum width/1cm, minimum height/1cm}

\tikzphysics@registerkeys{physicspulley}{%
  minimum size, physics pulley axle radius, physics pulley axle color}
\tikzphysics@registerkeydefaults{physicspulley}{%
  minimum size/1cm, physics pulley axle radius/2pt, physics pulley axle color/black}

\tikzphysics@registerkeys{physicsspring}{%
  minimum width, pre length, post length, amplitude, segment length, aspect}
\tikzphysics@registerkeydefaults{physicsspring}{%
  minimum width/3cm, pre length/5pt, post length/5pt,
  amplitude/4.5pt, segment length/4.5pt, aspect/0.5}

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