问题
I need to aspectFit arbitrarily shaped paths to the interior of a circle.
I can fit rectangular paths to the circle by calculating the size, building a dest rect, then scaling the path to fit the dest rect.
let targetSize = circleDiameter / sqrt(2)
let destRect = CGRect(x: 0, y: 0, width: targetSize, height: targetSize)
// ... Scale the path to the rect, using boundingBox of path.
But the scaling process isn't the issue; it's really how to calculate the required scale.
A bounding-box based approach provides a poor fit for non-rectangular shapes, and a very poor fit for a shape such as a circle.
Can anyone suggest a way to fit an arbitrary path to the interior of a circle? Thanks.
[edit - lots of research later]
The problem can be seen in this image:
The blue rectangle represents the native image, trimmed to the path.
The red circle is the minimum bounding circle.
The green rectangle represents the bounding box of the bounding circle.
I need to calculate the red circle, so I can use it to scale the path.
Turns out this is known as the Minimum Enclosing Circle Problem (AKA Smallest Enclosing Circle).
Alas, I cannot find a Swift or Objective-C implementation of any of the known algorithms, and will probably have to tackle some Java/JS/C++ code. If someone can save me the time, please do! Warm fuzzies inbound.
[another edit, time flies]
Turns out to be quite an "interesting" problem, in the Chinese sense.
Current approach is:
get all points in the path(s). Since
CGPath/UIBEzier
don't have this functionality, the paths have to be walked using the (disgraceful)apply(:)
method. This includes control points, so it's not the same thing as the end result, but hopefully it's close enough.combine all points in to one array. Hooray for the wondrous
flatMap()
!create some json from the points, load some javascript *, feed the json to a javascript method via JavaScriptCore. Fun, fun times, spend a few hours in console.log() ... Finally, get a circle in return.
get the bounds of the circle; use it bounds as a framing device for the path when transforming.
then, if it works, port the MEC JS to in Swift and hope like hell I never have to use an UnsafeMutablePointer in the process.
Thanks to: https://www.nayuki.io/page/smallest-enclosing-circle
[another edit, after a week's hard work]
OK, this is one tough problem. Some shapes lend themselves to centering using rects, others centering using a bounding circle. Detecting which is which is quite the trick.
It may be because of the quality of the path points I was able to get from UIBezierPath/CGPath, but the computed boundingCircle for a small percentage of shapes was too inaccurate to use. Whereas for most other shapes it was very accurate. But that discrepancy means I can't use this as a general solution; some offsets (fixups) still have to be applied to certain shapes to make them look centered. Argh.
来源:https://stackoverflow.com/questions/44239513/fitting-uibezierpath-cgpath-to-circle