Simple beeswarm 2 (default) 
This algorithm was contributed by Julius Krumbiegel (@jkrumbiegel) in PR #29.
"""
    SimpleBeeswarm2()
A simple beeswarm implementation, that minimizes overlaps. This is the
default algorithm used in `beeswarm`.
This algorithm dodges in `x` but preserves the exact `y` coordinate of each point.
If you don't want to preserve the y coordinate, check out `WilkinsonBeeswarm`.
"""
struct SimpleBeeswarm2 <: BeeswarmAlgorithm
end
export SimpleBeeswarm2
function calculate!(buffer::AbstractVector{<: Point2}, alg::SimpleBeeswarm2, positions::AbstractVector{<: Point2}, markersize, side::Symbol)
    ys = last.(positions)
    xs = first.(positions)
    for x_val in unique(xs)
        group = findall(==(x_val), xs)
        view_ys = view(ys, group)
        perm = sortperm(view_ys)
        ys_sorted = view_ys[perm]
        ms = if markersize isa Number
            markersize
        else
            if length(unique(markersize[group])) == 1
                markersize[group][1]
            else # this should error
                markersize[group]
            end
        end
        if isempty(ys_sorted)
            continue
        else
            xs[group] .= simple_beeswarm2(ys_sorted, ms)[invperm(perm)]
        end
    end
    buffer .= Point2f.(xs .+ first.(positions), last.(positions))
end
absmin(a, b) = abs(a) < abs(b) ? a : b
covers_zero(a, b) = a <= 0 && b >= 0
function simple_beeswarm2(sorted_ys, markersize)
    @assert issorted(sorted_ys)
    xs = zeros(length(sorted_ys))
    ms_squared = markersize ^ 2
    blocked_x_intervals = Tuple{Float64,Float64}[]
    for i in eachindex(sorted_ys)
        y = sorted_ys[i]store all intervals that the y-adjacent markers are blocking
        empty!(blocked_x_intervals)
        backwards_iter = (i-1):-1:1go backwards through all markers that are overlapping in y
        for j in backwards_iter
            delta_y = y - sorted_ys[j]
            delta_y > markersize && breakcompute the x distance between two circles that touch, each is markersize in diameter and they are delta_y apart vertically, the current marker would have to be at least that distance away in positive or negative direction
            blocked_delta_x = sqrt(ms_squared - delta_y ^ 2)
            blocked_x = xs[j]
            blocked_x_interval = (blocked_x - blocked_delta_x, blocked_x + blocked_delta_x)
            push!(blocked_x_intervals, blocked_x_interval)
        endwe want to go through all blocked intervals from left to right and see if there's a gap between them (a point gap is enough as we've already included the new marker's size in these blocked intervals) the point where we'll place the new marker is either right at the edge of one of these intervals, or it's at zero
        sort!(blocked_x_intervals)this marker can be placed at zero as it doesn't overlap any of the previous ones in y
        isempty(blocked_x_intervals) && continue
        if length(blocked_x_intervals) == 1if there's just one blocked interval, we immediately know what the best value is and the loop below doesn't apply because it starts at 2
            only_interval = blocked_x_intervals[]
            if covers_zero(only_interval...)
                x_closest_to_zero = absmin(blocked_x_intervals[]...)
            else
                x_closest_to_zero = 0.0
            end
        elseotherwise our first candidate is the left edge of the first interval
            x_closest_to_zero = first(blocked_x_intervals)[1]
        end
        left_interval = first(blocked_x_intervals)
        for j in 2:length(blocked_x_intervals)
            right_interval = blocked_x_intervals[j]
            if right_interval[1] < left_interval[2]if two adjacent intervals overlap, we merge them together for the next loop iteration which we directly go to
                left_interval = (left_interval[1], max(left_interval[2], right_interval[2]))but if we're at the last interval, we will not see another loop iteration, so we have to check the last interval for a candidate directly
                if j == length(blocked_x_intervals) && abs(left_interval[2]) < abs(x_closest_to_zero)
                    x_closest_to_zero = left_interval[2]
                end
                continue
            else
                new_candidate_x = if covers_zero(left_interval[2], right_interval[1])
                    0.0
                elseif abs(left_interval[2]) < abs(right_interval[1])
                    left_interval[2]
                else
                    right_interval[1]
                end
                if abs(new_candidate_x) < abs(x_closest_to_zero)
                    x_closest_to_zero = new_candidate_x
                    left_interval = right_interval
                elsewe can't get closer once our candidates start getting worse than the best one we have so far, which means we're going further away from zero to the right
                    break
                end
            end
        end
        xs[i] = x_closest_to_zero
    endfor better centering, we zero out groups of markers by their mean where each group is separated from its neighbors by a gap of at least one markersize (which means they can't collide when moving horizontally)
    align_group_start = 1
    for i in eachindex(sorted_ys) .+ 1
        if (i - 1) == length(sorted_ys) || sorted_ys[i] - sorted_ys[i-1] >= markersize
            xs[align_group_start:i-1] .-= mean(@view(xs[align_group_start:i-1]))
            align_group_start = i
        end
    end
    return xs
endThis page was generated using Literate.jl.