<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def project_onto(objs, axis, min_gap_size=4.0):
 """ Projects object bboxes onto the axis and return the unioned intervals and groups of objects in intervals. """

    if axis == "x":
        axis = 0
    if axis == "y":
        axis = 1
    axis_end = axis + 2
    if axis == 0:  # if projecting onto X axis
        objs.sort(key=lambda o: o.x0)
    else:
        objs.sort(key=lambda o: o.y0)

    intervals = []
    groups = []

    start_i = 0
    start = objs[0].bbox[axis]
    end = objs[0].bbox[axis_end]

    # Use _inf_bbox to trigger the last interval divide
    for o_i, o in enumerate(chain(objs, [_inf_bbox])):

        # Get current interval
        o_start = o.bbox[axis]
        o_end = o.bbox[axis_end]

        # start new interval when gap with previous end is big
        if o_start > end + min_gap_size:

            # Append new interval coordinates for children
            intervals.append((start, end))

            # Append child object group on page
            groups.append(objs[start_i:o_i])

            # Mark next obj list range
            start_i = o_i
            start = o_start

        # Always check to extend current interval to new end
        if o_end > end:
            end = o_end
        # else do nothing
    return intervals, groups