<commit_before># Bezier - last updated for NodeBox 1.8.3
# Author: <NAME> <<EMAIL>>
# Manual: http://nodebox.net/code/index.php/Bezier
# Copyright (c) 2007 by <NAME>.
# Refer to the "Use" section on http://nodebox.net/code
# Thanks to Dr. Florimond De Smedt at the Free University of Brussels for the math routines.


from nodebox.graphics import BezierPath, PathElement, NodeBoxError, Point
from nodebox.graphics import MOVETO, LINETO, CURVETO, CLOSE


try:
    import cPathmatics
    linepoint = cPathmatics.linepoint
    linelength = cPathmatics.linelength
    curvepoint = cPathmatics.curvepoint
    curvelength = cPathmatics.curvelength
except:
    import nodebox.geo.pathmatics
    linepoint = nodebox.geo.pathmatics.linepoint
    linelength = nodebox.geo.pathmatics.linelength
    curvepoint = nodebox.geo.pathmatics.curvepoint
    curvelength = nodebox.geo.pathmatics.curvelength


def segment_lengths(path, relative=False, n=20):
    """Returns a list with the lengths of each segment in the path.
    
    >>> path = BezierPath(None)
    >>> segment_lengths(path)
    []
    >>> path.moveto(0, 0)
    >>> segment_lengths(path)
    []
    >>> path.lineto(100, 0)
    >>> segment_lengths(path)
    [100.0]
    >>> path.lineto(100, 300)
    >>> segment_lengths(path)
    [100.0, 300.0]
    >>> segment_lengths(path, relative=True)
    [0.25, 0.75]
    >>> path = BezierPath(None)
    >>> path.moveto(1, 2)
    >>> path.curveto(3, 4, 5, 6, 7, 8)
    >>> segment_lengths(path)
    [8.48528137423857]
    """

    lengths = []
    first = True

    for el in path:
        if first == True:
            close_x, close_y = el.x, el.y
            first = False
        elif el.cmd == MOVETO:
            close_x, close_y = el.x, el.y
            lengths.append(0.0)
        elif el.cmd == CLOSE:
            lengths.append(linelength(x0, y0, close_x, close_y))
        elif el.cmd == LINETO:
            lengths.append(linelength(x0, y0, el.x, el.y))
        elif el.cmd == CURVETO:
            x3, y3, x1, y1, x2, y2 = (el.x, el.y, el.ctrl1.x, el.ctrl1.y,
                                      el.ctrl2.x, el.ctrl2.y)
            lengths.append(curvelength(x0, y0, x1, y1, x2, y2, x3, y3, n))
            
        if el.cmd != CLOSE:
            x0 = el.x
            y0 = el.y

    if relative:
        length = sum(lengths)
        try:
            return map(lambda l: l / length, lengths)
        except ZeroDivisionError:
            # If the length is zero, just return zero for all segments
            return [0.0] * len(lengths)
    else:
        return lengths

def length(path, segmented=False, n=20):

    """Returns the length of the path.

    Calculates the length of each spline in the path,
    using n as a number of points to measure.

    When segmented is True, returns a list
    containing the individual length of each spline
    as values between 0.0 and 1.0,
    defining the relative length of each spline
    in relation to the total path length.
    
    The length of an empty path is zero:
    >>> path = BezierPath(None)
    >>> length(path)
    0.0

    >>> path.moveto(0, 0)
    >>> path.lineto(100, 0)
    >>> length(path)
    100.0

    >>> path.lineto(100, 100)
    >>> length(path)
    200.0

    # Segmented returns a list of each segment
    >>> length(path, segmented=True)
    [0.5, 0.5]
    """

    if not segmented:
        return sum(segment_lengths(path, n=n), 0.0)
    else:
        return segment_lengths(path, relative=True, n=n)

def _locate(path, t, segments=None):
    
    """Locates t on a specific segment in the path.
    
    Returns (index, t, PathElement)
    
    A path is a combination of lines and curves (segments).
    The returned index indicates the start of the segment
    that contains point t.
    
    The returned t is the absolute time on that segment,
    in contrast to the relative t on the whole of the path.
    The returned point is the last MOVETO,
    any subsequent CLOSETO after i closes to that point.
    
    When you supply the list of segment lengths yourself,
    as returned from length(path, segmented=True),
    point() works about thirty times faster in a for-loop,
    since it doesn't need to recalculate the length
    during each iteration. Note that this has been deprecated:
    the BezierPath now caches the segment lengths the moment you use
    them.
    
    >>> path = BezierPath(None)
    >>> _locate(path, 0.0)
    Traceback (most recent call last):
        ...
    NodeBoxError: The given path is empty
    >>> path.moveto(0,0)
    >>> _locate(path, 0.0)
    Traceback (most recent call last):
        ...
    NodeBoxError: The given path is empty
    >>> path.lineto(100, 100)
    >>> _locate(path, 0.0)
    (0, 0.0, Point(x=0.000, y=0.000))
    >>> _locate(path, 1.0)
    (0, 1.0, Point(x=0.000, y=0.000))
    """
    
    if segments == None:
        segments = path.segmentlengths(relative=True)
        
    if len(segments) == 0:
        raise NodeBoxError("The given path is empty")
    
    for i, el in enumerate(path):
        if i == 0 or el.cmd == MOVETO:
            closeto = Point(el.x, el.y)
        if t <= segments[i] or i == len(segments)-1: break
        else: t -= segments[i]

    try: t /= segments[i]
    except ZeroDivisionError: pass
    if i == len(segments)-1 and segments[i] == 0: i -= 1
    
    return (i, t, closeto)


def point(path, t, segments=None):

    """Returns coordinates for point at t on the path.

    Gets the length of the path, based on the length
    of each curve and line in the path.<commit_msg>More readable.<commit_after># Bezier - last updated for NodeBox 1.8.3
# Author: <NAME> <<EMAIL>>
# Manual: http://nodebox.net/code/index.php/Bezier
# Copyright (c) 2007 by <NAME>.
# Refer to the "Use" section on http://nodebox.net/code
# Thanks to Dr. <NAME> at the Free University of Brussels for the math routines.


from nodebox.graphics import BezierPath, PathElement, NodeBoxError, Point
from nodebox.graphics import MOVETO, LINETO, CURVETO, CLOSE


try:
    import cPathmatics
    linepoint = cPathmatics.linepoint
    linelength = cPathmatics.linelength
    curvepoint = cPathmatics.curvepoint
    curvelength = cPathmatics.curvelength
except:
    import nodebox.geo.pathmatics
    linepoint = nodebox.geo.pathmatics.linepoint
    linelength = nodebox.geo.pathmatics.linelength
    curvepoint = nodebox.geo.pathmatics.curvepoint
    curvelength = nodebox.geo.pathmatics.curvelength


def segment_lengths(path, relative=False, n=20):
    """Returns a list with the lengths of each segment in the path.
    
    >>> path = BezierPath(None)
    >>> segment_lengths(path)
    []
    >>> path.moveto(0, 0)
    >>> segment_lengths(path)
    []
    >>> path.lineto(100, 0)
    >>> segment_lengths(path)
    [100.0]
    >>> path.lineto(100, 300)
    >>> segment_lengths(path)
    [100.0, 300.0]
    >>> segment_lengths(path, relative=True)
    [0.25, 0.75]
    >>> path = BezierPath(None)
    >>> path.moveto(1, 2)
    >>> path.curveto(3, 4, 5, 6, 7, 8)
    >>> segment_lengths(path)
    [8.48528137423857]
    """

    lengths = []
    first = True

    for el in path:
        if first == True:
            close_x, close_y = el.x, el.y
            first = False
        elif el.cmd == MOVETO:
            close_x, close_y = el.x, el.y
            lengths.append(0.0)
        elif el.cmd == CLOSE:
            lengths.append(linelength(x0, y0, close_x, close_y))
        elif el.cmd == LINETO:
            lengths.append(linelength(x0, y0, el.x, el.y))
        elif el.cmd == CURVETO:
            x3, y3, x1, y1, x2, y2 = (el.x, el.y, el.ctrl1.x, el.ctrl1.y,
                                      el.ctrl2.x, el.ctrl2.y)
            lengths.append(curvelength(x0, y0, x1, y1, x2, y2, x3, y3, n))
            
        if el.cmd != CLOSE:
            x0 = el.x
            y0 = el.y

    if relative:
        length = sum(lengths)
        try:
            return map(lambda l: l / length, lengths)
        except ZeroDivisionError:
            # If the length is zero, just return zero for all segments
            return [0.0] * len(lengths)
    else:
        return lengths

def length(path, segmented=False, n=20):

    """Returns the length of the path.

    Calculates the length of each spline in the path,
    using n as a number of points to measure.

    When segmented is True, returns a list
    containing the individual length of each spline
    as values between 0.0 and 1.0,
    defining the relative length of each spline
    in relation to the total path length.
    
    The length of an empty path is zero:
    >>> path = BezierPath(None)
    >>> length(path)
    0.0

    >>> path.moveto(0, 0)
    >>> path.lineto(100, 0)
    >>> length(path)
    100.0

    >>> path.lineto(100, 100)
    >>> length(path)
    200.0

    # Segmented returns a list of each segment
    >>> length(path, segmented=True)
    [0.5, 0.5]
    """

    if not segmented:
        return sum(segment_lengths(path, n=n), 0.0)
    else:
        return segment_lengths(path, relative=True, n=n)

def _locate(path, t, segments=None):
    
    """Locates t on a specific segment in the path.
    
    Returns (index, t, PathElement)
    
    A path is a combination of lines and curves (segments).
    The returned index indicates the start of the segment
    that contains point t.
    
    The returned t is the absolute time on that segment,
    in contrast to the relative t on the whole of the path.
    The returned point is the last MOVETO,
    any subsequent CLOSETO after i closes to that point.
    
    When you supply the list of segment lengths yourself,
    as returned from length(path, segmented=True),
    point() works about thirty times faster in a for-loop,
    since it doesn't need to recalculate the length
    during each iteration. Note that this has been deprecated:
    the BezierPath now caches the segment lengths the moment you use
    them.
    
    >>> path = BezierPath(None)
    >>> _locate(path, 0.0)
    Traceback (most recent call last):
        ...
    NodeBoxError: The given path is empty
    >>> path.moveto(0,0)
    >>> _locate(path, 0.0)
    Traceback (most recent call last):
        ...
    NodeBoxError: The given path is empty
    >>> path.lineto(100, 100)
    >>> _locate(path, 0.0)
    (0, 0.0, Point(x=0.000, y=0.000))
    >>> _locate(path, 1.0)
    (0, 1.0, Point(x=0.000, y=0.000))
    """
    
    if segments == None:
        segments = path.segmentlengths(relative=True)
        
    if len(segments) == 0:
        raise NodeBoxError("The given path is empty")
    
    for i, el in enumerate(path):
        if i == 0 or el.cmd == MOVETO:
            closeto = Point(el.x, el.y)
        if t <= segments[i] or i == len(segments)-1:
            break
        else: t -= segments[i]

    try:
        t /= segments[i]
    except ZeroDivisionError:
        pass
    if i == len(segments)-1 and segments[i] == 0:
        i -= 1

    return (i, t, closeto)


def point(path, t, segments=None):

    """Returns coordinates for point at t on the path.

    Gets the length of the path, based on the length
    of each curve and line in the path.
{'fraction_non_alphanumeric': 0.11141035999640901, 'fraction_numerical': 0.030164287638028548, 'mean_word_length': 2.3105497771173846, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 180, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2330097', 'n_tokens_mistral': 3889, 'n_tokens_neox': 3654, 'n_words': 1176}