from functools import reduce def lines_to_voxels(line_list, pixels): current_line_indices = set() x = 0 for event_x, status, line_ind in generate_line_events(line_list): while event_x - x >= 0: lines = reduce(lambda acc, cur: acc + [line_list[cur]], current_line_indices, []) paint_y_axis(lines, pixels, x) x += 1 if status == "start": assert line_ind not in current_line_indices current_line_indices.add(line_ind) elif status == "end": assert line_ind in current_line_indices current_line_indices.remove(line_ind) def slope_intercept(p1, p2): x1, y1 = p1[:2] x2, y2 = p2[:2] slope = (y2 - y1) / (x2 - x1) intercept = y1 - slope * x1 return slope, intercept def generate_y(p1, p2, x): slope, intercept = slope_intercept(p1, p2) y = slope * x + intercept return y def paint_y_axis(lines, pixels, x): is_black = False target_ys = sorted(map(lambda line: int(generate_y(line[0], line[1], x)), lines)) if len(target_ys) % 2: distances = [target_ys[i + 1] - target_ys[i] for i in range(len(target_ys) - 1)] # https://stackoverflow.com/a/17952763 min_idx = -min((x, -i) for i, x in enumerate(distances))[1] del target_ys[min_idx] yi = 0 for target_y in target_ys: if is_black: # Bulk assign all pixels between yi -> target_y pixels[yi:target_y, x] = True pixels[target_y][x] = True is_black = not is_black yi = target_y assert is_black is False, f"an error has occured at x{x}" def generate_line_events(line_list): events = [] for i, line in enumerate(line_list): first, second = sorted(line, key=lambda pt: pt[0]) events.append((first[0], "start", i)) events.append((second[0], "end", i)) return sorted(events, key=lambda tup: tup[0])