
    hL                     X   d Z ddlZddlmZmZmZmZmZmZm	Z	m
Z
mZmZ ddlmZmZmZmZ ddlmZmZmZmZ dededed	eeeee   f      fd
Zdeded	eeeee   f      fdZded	eeeee   f      fdZdeeef   deeef   deeef   d	eeeef   eeef   eeef   eeef   f   fdZdeeef   deeef   d	efdZ	 d%dededededededededed	eeeeeeef   fdZ	 d&ded ededed!ed"ed	eeeeeeeeeef      fd#Z dededededededededed	eeeeeeeeeef      fd$Z!y)'a
  Utility functions for SVG processing and path manipulation.

This module provides low-level utility functions used throughout the svglib
package for processing SVG content. It includes functions for parsing SVG path
data, converting between different curve representations, and handling
geometric transformations.

The module includes:
- SVG path parsing and normalization
- Bezier curve conversion utilities
- Elliptical arc processing functions
- Vector mathematics helpers
- String parsing utilities for SVG attributes
    N)
acosceilcopysigncosdegreesfabshypotradianssinsqrt)ListTupleUnioncast)mmultrotatetransformPoint	translateopmin_numvaluereturnc                 8   t        j                  d|      D cg c]  }|rt        |       }}g }t        dt	        |      |      D ]K  }|dkD  r| dv r	| dk(  rdnd} |j                  | t        t        t           t        ||||z                g       M |S c c}w )a  Parse SVG coordinate string into alternating operators and coordinate lists.

    Splits a string of numeric values into groups and pairs each group with the
    appropriate SVG path operation. Automatically converts 'M' to 'L' for subsequent
    coordinate pairs to handle move-to followed by line-to sequences.

    Args:
        op: SVG path operation character (e.g., 'M', 'L', 'm', 'l').
        min_num: Minimum number of coordinates expected per operation.
        value: String containing comma/whitespace-separated numeric values.

    Returns:
        List alternating between operation strings and coordinate lists.
        Example: ['M', [10.0, 20.0], 'L', [30.0, 40.0]]

    Examples:
        >>> split_floats('M', 2, '10,20 30,40')
        ['M', [10.0, 20.0], 'L', [30.0, 40.0]]

        >>> split_floats('L', 2, '100 200')
        ['L', [100.0, 200.0]]

    Note:
        Supports scientific notation (e.g., '1.23e-4') and handles various
        whitespace and comma separators automatically.
    (-?\d*\.?\d*(?:[eE][+-]?\d+)?)r   >   Mmr   lL)	refindallfloatrangelenextendr   r   list)r   r   r   seqfloatsresis          </home/dcms/DCMS/lib/python3.12/site-packages/svglib/utils.pysplit_floatsr+      s    : ::?G 	c
F 
 *,C1c&k7+ Kq5R:%c	sB

BT%[$va!g+/F*GHIJK Js   Bc                 &   d}d}dj                  |||||||g      dz   }g }t        j                  ||j                               D ]H  }|j	                  | t        t        t           t        d |j                         D                    g       J |S )a  Parse SVG elliptical arc parameters into structured format.

    Parses SVG arc command parameters which consist of: rx, ry, x-axis-rotation,
    large-arc-flag, sweep-flag, x, y. Each complete parameter set is paired with
    the operation character.

    Args:
        op: SVG arc operation character ('A' or 'a').
        value: String containing arc parameters in the format:
            "rx,ry x-axis-rotation large-arc-flag,sweep-flag x,y"

    Returns:
        List alternating between operation strings and parameter lists.
        Each parameter list contains [rx, ry, x_axis_rotation, large_arc_flag,
        sweep_flag, x, y] as floats.

    Examples:
        >>> split_arc_values('A', '50,50 0 1,0 100,100')
        ['A', [50.0, 50.0, 0.0, 1.0, 0.0, 100.0, 100.0]]

        >>> split_arc_values('a', '25 25 30 0 1 50 75')
        ['a', [25.0, 25.0, 30.0, 0.0, 1.0, 50.0, 75.0]]

    Note:
        The large-arc-flag and sweep-flag are converted to float but should
        be treated as boolean values (0 or 1).
    r   z([1|0])z[\s,]*c              3   2   K   | ]  }t        |        y w)N)r!   ).0nums     r*   	<genexpr>z#split_arc_values.<locals>.<genexpr>f   s     .RcuSz.Rs   )
joinr   finditerstripr$   r   r   r!   r%   groups)r   r   float_reflag_rea_seq_rer(   r&   s          r*   split_arc_valuesr8   ?   s    8 1HG 	x7GXxP	
 	  *,C{{8U[[]3 V

BT%[$.RSZZ\.R*RSTUVJ    attrc                 ~   i ddddddddddd	dd
ddddddddddddddddddddddddd}|j                         }g }t        j                  d| j                         t        j                        }d}|D ]  }|j                         dk(  r||v r9|dk(  r|dk(  rd}n|dk(  r|dk(  rd}n|}||   dk(  s@|j                  |g g       T|j                         dk(  r|j                  t        ||             n|j                  t        |||   |             t        t        |d         } |S )a  Normalize SVG path data into structured format.

    Parses raw SVG path string and converts it into a standardized list format
    where each path command is paired with its coordinate parameters. Automatically
    handles command sequences and converts implicit line commands after move commands.

    Args:
        attr: Raw SVG path string (e.g., "M 10 20 L 30 40 Z").

    Returns:
        Normalized list alternating between command strings and coordinate lists.
        Close path commands ('Z', 'z') are paired with empty lists for consistency.

    Examples:
        >>> normalise_svg_path("M 10 20 L 30 40 Z")
        ['M', [10.0, 20.0], 'L', [30.0, 40.0], 'Z', []]

        >>> normalise_svg_path("M 0 0 L 10 0 10 10 Z")
        ['M', [0.0, 0.0], 'L', [10.0, 0.0], 'L', [10.0, 10.0], 'Z', []]

        >>> normalise_svg_path("m 100,200 300,400")
        ['m', [100.0, 200.0], 'l', [300.0, 400.0]]

    Note:
        - Converts sequences of M/m commands to M/m followed by L/l commands
        - Handles all SVG path commands: M, L, H, V, C, c, S, s, Q, q, T, t, A, a, Z, z
        - Supports various whitespace and comma separators
        - All coordinates are converted to float values
    A   aQ   qT   tSsr   r   r   r   H   VhvC   r   )cZzz([achlmqstvz]))flags )keysr   splitr3   Ir$   lowerr8   r+   r   str)r:   opsop_keysresultr4   r   items          r*   normalise_svg_pathr]   j   s   @QQ 	Q 	Q	
 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q  	Q!" 	Q#$ )C, hhjG -/FXX&

BDDAF	B '::<27? s{rSys2w!|r2h'xxzS .r489l2s2w=>c6":&B''* Mr9   q0q1q2c                     | }| d   d|d   | d   z
  z  z   | d   d|d   | d   z
  z  z   f}|d   d|d   | d   z
  z  z   |d   d|d   | d   z
  z  z   f}|}||||fS )az  Convert quadratic Bezier curve to cubic Bezier curve.

    Converts a quadratic Bezier curve defined by control points q0, q1, q2
    into an equivalent cubic Bezier curve. This is useful for SVG processing
    since cubic curves are more commonly supported than quadratic curves.

    Args:
        q0: Starting point as (x, y) tuple.
        q1: Control point as (x, y) tuple.
        q2: End point as (x, y) tuple.

    Returns:
        Tuple of four (x, y) points defining the equivalent cubic Bezier curve:
        (start_point, control_point1, control_point2, end_point).

    Examples:
        >>> convert_quadratic_to_cubic_path((0, 0), (5, 10), (10, 0))
        ((0, 0), (3.3333333333, 6.6666666666), (6.6666666666, 6.6666666666), (10, 0))

        >>> # Simple case: straight line becomes straight line
        >>> convert_quadratic_to_cubic_path((0, 0), (5, 5), (10, 10))
        ((0, 0), (3.3333333333, 3.3333333333), (6.6666666666, 6.6666666666), (10, 10))

    Note:
        The conversion uses the standard formula where the cubic control points
        are calculated as: c1 = q0 + (2/3)*(q1 - q0), c2 = c1 + (1/3)*(q2 - q0).
    r   gUUUUUU?rH   gUUUUUU? )r^   r_   r`   c0c1c2c3s          r*   convert_quadratic_to_cubic_pathrg      s    @ 
B
Q%%2a52a5=)
)2a55BqEBqEM3J+J	KB
Q%%2a52a5=)
)2a55BqEBqEM3J+J	KB	Br2r>r9   urK   c                     t        |  t        | z  }|dk(  ry| d   |d   z  | d   |d   z  z   |z  }|dk  rd}n|dkD  rd}| d   |d   z  | d   |d   z  z
  }t        t        t        |      |            S )a  Calculate the signed angle between two 2D vectors.

    Computes the angle between vectors u and v using the atan2 method to
    determine the correct quadrant and sign. Returns angle in degrees.

    Args:
        u: First vector as (x, y) tuple.
        v: Second vector as (x, y) tuple.

    Returns:
        Signed angle in degrees between the vectors, ranging from -180 to 180.

    Examples:
        >>> vector_angle((1, 0), (0, 1))  # 90 degrees counterclockwise
        90.0

        >>> vector_angle((1, 0), (0, -1))  # 90 degrees clockwise
        -90.0

        >>> vector_angle((1, 0), (1, 0))  # Same direction
        0.0

        >>> vector_angle((1, 0), (-1, 0))  # Opposite direction
        180.0

    Note:
        - Handles zero-length vectors by returning 0
        - Uses numerical stability checks to avoid domain errors in acos
        - Result is always in the range [-180, 180] degrees
    r   rH   )r	   r   r   r   )rh   rK   drN   rF   s        r*   vector_anglerl      s    > 	q	E1IAAv	
1!qtad{	"a'A2v	
Q	!qtadQqTk!A8DGQ'((r9   x1y1x2y2fAfSrxryphic	                 t   t        |      }t        |      }|rHt        |      }	t        |	      }
t        |	      }d| |z
  z  }d||z
  z  }||z  |
|z  z
  }|
|z  ||z  z   }nd| |z
  z  }d||z
  z  }||z  ||z  z  ||z  ||z  z  z   }|dkD  r5t	        |      }||z  }||z  }||z  ||z  z  ||z  ||z  z  z   }d|z  dz
  }n|dk7  rd|z  dz
  }d|cxk  rdk  rn nd}t	        |      }||k(  r| }||z  |z  |z  }||z  |z   |z  }|r)|z  
|z  z
  d| |z   z  z   }|
|z  ||z  z   d||z   z  z   }n|d| |z   z  z   }|d||z   z  z   }t        d||z
  |z  ||z
  |z  f      }t        ||z
  |z  ||z
  |z  f| |z
  |z  | |z
  |z  f      dz  }|dk(  r|dkD  r|dz  }n|dk(  r
|dk  r|dz  }||||| | fS )aJ  Convert SVG arc endpoint parameters to center-based representation.

    Implements the algorithm from W3C SVG specification for converting
    elliptical arc parameters from endpoint format to center format.
    This is needed for proper arc rendering in ReportLab.

    Args:
        x1: X-coordinate of arc start point.
        y1: Y-coordinate of arc start point.
        x2: X-coordinate of arc end point.
        y2: Y-coordinate of arc end point.
        fA: Large arc flag (0 or 1).
        fS: Sweep flag (0 or 1).
        rx: Arc radius in X direction.
        ry: Arc radius in Y direction.
        phi: Rotation angle of the arc in degrees (default 0).

    Returns:
        Tuple of (cx, cy, rx, ry, start_angle, sweep_angle):
        - cx, cy: Center point coordinates
        - rx, ry: Adjusted radii (may be scaled up if too small)
        - start_angle: Starting angle in degrees
        - sweep_angle: Sweep angle in degrees

    Raises:
        This function handles all edge cases internally and doesn't raise exceptions.

    Examples:
        >>> end_point_to_center_parameters(0, 0, 10, 0, 0, 1, 5, 5)
        (5.0, 0.0, 5.0, 5.0, 180.0, 180.0)

        >>> # Degenerate case - identical points
        >>> end_point_to_center_parameters(5, 5, 5, 5, 0, 0, 10, 10)
        (5.0, 5.0, 10.0, 10.0, 0.0, 0.0)

    See Also:
        W3C SVG 1.1 Implementation Notes, Section F.6.5:
        http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes

    Note:
        The rotation angle phi is reduced to zero by coordinate transformation
        outside this function for simplicity.
          ?rH   r   g|۽)rH   r   ih  )r   r
   r   r   r   rl   )rm   rn   ro   rp   rq   rr   rs   rt   ru   phi_radsin_phicos_phitxtyx1dy1drrrcxdcydcxcytheta1dthetas                           r*   end_point_to_center_parametersr     s   l 
bB	bB #,g,g,BG_BG_lWr\)lWr\)R"WoR"Wo, 	c	R"Wc	R"W 55A1u!W
b
b#Ib!C#Ib$99EAI	
aEAI~A~QA	RxBr6C<2
CFSL/B
C s]Ws]*SBG_<s]Ws]*SBG_<3"r'?"3"r'?" &C#I#3cCi25E"FGFCi2c	R/0SD3J"3DtczUWFW2X	
 	  
Qw6A:#	qVaZ#r2rF7VG++r9   r   r   	start_angextentc                 d   t        |      dk  rd}|}nt        t        |      dz        }||z  }|dk(  rg S t        |      }|dz  }	t        ddt        |	      z
  z  t	        |	      z        }
|dk  r|
 }
g }t        |      }||z   }t        |      }t	        |      }t        |      D ]  }|}|}|||z  z   }t        |      }t	        |      }|j                  | ||z  z   |||z  z
  | |||
|z  z
  z  z   ||||
|z  z   z  z
  | |||
|z  z   z  z   ||||
|z  z
  z  z
  | ||z  z   |||z  z
  f        |S )uO  Convert elliptical arc to cubic Bezier curve segments.

    Approximates an elliptical arc with cubic Bezier curves using the kappa
    constant method. The arc is divided into segments of at most 90 degrees
    each for accurate approximation.

    Args:
        cx: X-coordinate of ellipse center.
        cy: Y-coordinate of ellipse center.
        rx: Radius in X direction.
        ry: Radius in Y direction.
        start_ang: Starting angle in degrees (default 0).
        extent: Angular extent in degrees (default 90).

    Returns:
        List of Bezier curve segments, each as an 8-tuple:
        (x1, y1, x2, y2, x3, y3, x4, y4) where:
        - (x1, y1): Start point
        - (x2, y2): First control point
        - (x3, y3): Second control point
        - (x4, y4): End point

    Examples:
        >>> # Quarter circle (90 degrees)
        >>> curves = bezier_arc_from_centre(0, 0, 10, 10, 0, 90)
        >>> len(curves)  # One segment for 90 degrees
        1

        >>> # Half circle (180 degrees) - split into two 90-degree segments
        >>> curves = bezier_arc_from_centre(0, 0, 10, 10, 0, 180)
        >>> len(curves)
        2

        >>> # Full circle (360 degrees) - split into four 90-degree segments
        >>> curves = bezier_arc_from_centre(0, 0, 10, 10, 0, 360)
        >>> len(curves)
        4

    Note:
        - Arcs are automatically subdivided into segments ≤ 90° for accuracy
        - Uses the standard kappa = 4/3 * (1 - cos(θ/2)) / sin(θ/2) formula
        - Handles both clockwise and counterclockwise arcs
        - Returns empty list for zero-extent arcs
    Z   rH   r   rw   gUUUUUU?)absr   r
   r   r   r"   append)r   r   rs   rt   r   r   nfrag
frag_anglefrag_radhalf_radkappa
point_listr   	start_radrd   s1r)   rc   s0s                      r*   bezier_arc_from_centrer     s   ^ 6{b
S[2%&e^
Q	z"H#~HS]*+c(m;<EA~JYF!I	VB	VB5\ 
Q\)[[R"WR"WR2
?++R2
?++R2
?++R2
?++R"WR"W		

$ r9   c	           
      "   | |k(  r||k(  rg S |rt        t        |       t        |  |             }	t        |	||f      \  }
}t	        dd|
|||||      \  }}}}}}t        ||||||      }t        t        | |      t        |            }	g }|D ]U  \  } }}}}}}}|j                  t        |	| |f      t        |	||f      z   t        |	||f      z   t        |	||f      z          W |S t	        | |||||||      \  }}}}}}t        ||||||      S )u  Convert SVG elliptical arc to cubic Bezier curve segments.

    High-level function that converts SVG elliptical arc parameters (endpoint format)
    to a series of cubic Bezier curves. Handles rotation, scaling, and all SVG arc
    flags. This is the main entry point for arc-to-Bezier conversion in SVG processing.

    Args:
        x1: X-coordinate of arc start point.
        y1: Y-coordinate of arc start point.
        rx: Arc radius in X direction.
        ry: Arc radius in Y direction.
        phi: Rotation angle of the arc in degrees.
        fA: Large arc flag (0 or 1) - chooses larger or smaller arc.
        fS: Sweep flag (0 or 1) - chooses clockwise or counterclockwise.
        x2: X-coordinate of arc end point.
        y2: Y-coordinate of arc end point.

    Returns:
        List of Bezier curve segments, each as an 8-tuple:
        (x1, y1, x2, y2, x3, y3, x4, y4) representing:
        - (x1, y1): Start point of segment
        - (x2, y2): First control point
        - (x3, y3): Second control point
        - (x4, y4): End point of segment

    Examples:
        >>> # Simple 180-degree arc
        >>> curves = bezier_arc_from_end_points(0, 0, 10, 10, 0, 0, 1, 20, 0)
        >>> len(curves)  # Split into segments
        2

        >>> # Degenerate case - identical points (returns empty list)
        >>> curves = bezier_arc_from_end_points(10, 10, 5, 5, 0, 0, 0, 10, 10)
        >>> len(curves)
        0

        >>> # Rotated ellipse arc
        >>> curves = bezier_arc_from_end_points(0, 0, 10, 5, 45, 1, 0, 7, 7)
        >>> len(curves)  # Will be split into multiple segments
        2

    Note:
        - Returns empty list if start and end points are identical
        - Automatically handles coordinate transformations for rotation
        - Splits arcs into ≤90° segments for accurate Bezier approximation
        - Follows W3C SVG 1.1 specification for arc parameter interpretation
    r   )r   r   r   r   r   r   r   )rm   rn   rs   rt   ru   rq   rr   ro   rp   mxtx2ty2r   r   r   r   bpr(   x3y3x4y4s                         r*   bezier_arc_from_end_pointsr     sd   t 
RxB"H 	
 63$<B3!45!"r2h/S,Jq#sBB-
)BB	6 $BBIvF9R$fSk2.0 	*BBBBJJrB8, b"X./ b"X./ !b"X./	 
,JBBB-
)BB	6 &b"b"iHHr9   )r   )r   r   )"__doc__r   mathr   r   r   r   r   r   r	   r
   r   r   typingr   r   r   r   reportlab.graphics.shapesr   r   r   r   rX   intr!   r+   r8   r]   rg   rl   r   r   r   rb   r9   r*   <module>r      s   
 T T T + + N N%S %3 %s %tE#tE{BR<S7T %P( (S (T%T%[8H2I-J (VQS QT%T%[0@*A%B Qh$eUl$!&ue|!4$:?u:M$
	%,ue|,eE5L.A5PUCVV$X()E%,' ()E%,,? ()E ()h ,,, 	, 		,
 	, 	, 	, 	, 
, 5%ue34,F WYWWW#W).W;@WNSW	%ueUE5%F
GHWtXIXIXI 	XI 		XI
 
XI 	XI 	XI 	XI 	XI 
%ueUE5%F
GHXIr9   