Nice! Should point out that JTS geometries already have a built in normalize and equalsNorm method for computing equality regardless of coordinate starting point. I'm not sure how they compare to your implementation but the motivation is similar.
Either way, you can optimize the performance of this approach by first doing a cheaper comparison (like coordinate count) and only computing the normalized geometry when you need to.
> After thinking far too long about how I can sort coordinates reliability, I chose the easy way out:
In addition to the code, please also give a plain language description: "We ensure that the first coordinate is the lexicographically smallest (i.e. most westward point). For line strings, reverse the coordinate list to make it so, and for polygons, rotate the coordinates to make it so."
> val reordered = coordinates.subList(index, coordinates.size - 1) + coordinates.subList(0, index)
This should say + coordinates.subList(0, index + 1), and then you can get rid of the code that checks for first != last.