The rule I use for limiting complexity is that functions should not need more than 30 seconds to understand.
I really don't maintain any hard and fast rules, flexibility is required depending on what you're working with, for example:
A single pixel filter without the process loop i.e. only a specific formula to manipulate the color components could easily run between 8 to 30+ lines; separating this into more functions only detracts from process steps & increases cognitive stress. More complex algorithms like pixel dithering, blur or median filters can easily get substantially more bloated.
Similarly I've built parsers, tokenizers and compression algorithms where sub parts can easily run up 40 or 50 lines; splitting this makes no practical sense in process comprehension., as there is no reuse, and many small functions or objects will only further complicate the understanding of something that is already very complex to begin with.
Note: the counting of this is based on code typically formatted to 80 character width, including all asserts and preconditions (quite a few which are multi-line)
Yes these are specific areas of complexity, most functions fall within 6 to 7 lines, but I mentioned this as it serves to challenge the notion that a fixed set of rules can be practical.
Anyway I mentioned in the Swift thread that I'd most probably post something about refactoring: i.e. what Swift offers to streamline code as part of a refactor; if I have the time I'll try to include a few before and after scenarios: the single pixel image filters (binary, grayscale, tint, color shading, ...) are probably great examples. Alternative would be to tackle pixel dithering; if there's any interest let me know which you prefer.
Btw I picked these two areas re you encounter many different algorithms, some more complex than others; and jointly they have parts that can be simplified to reduce duplication and improve the overall API.
At least with code examples it'll be easier to discuss / challenge my view.