I am very new to Swift's CoreGraphics, so there is likely a lot of things I'm doing wrong here, but I couldn't find a question like mine, so I decided to post my own.
I am using CoreGraphics to draw circles in specific locations on the screen. When I simply use cgContext.addEllipse(in: rect)
, I can render up to a hundred circles no-problem. However, I found a quick radial gradient code block online that can add a 3D effect to the otherwise 2D circles. The following code is that code block, modified by myself to apply to my application:
func drawSphere(in rect: CGRect, withColor color: CGColor, withContext ctx: CGContext) {
let locations: [CGFloat] = [0.0, 1.0]
let colors = [UIColor.white.cgColor, color]
let colorspace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorspace,
colors: colors as CFArray, locations: locations)
var startPoint = CGPoint()
var endPoint = CGPoint()
startPoint.x = rect.minX
startPoint.y = rect.minY
endPoint.x = rect.minX + rect.width / 4
endPoint.y = rect.minY + rect.height / 4
let startRadius: CGFloat = 0
let endRadius: CGFloat = rect.width / 2.0
ctx.drawRadialGradient (gradient!, startCenter: startPoint,
startRadius: startRadius, endCenter: endPoint,
endRadius: endRadius,
options: .drawsBeforeStartLocation)
}
This works well, visually. However, my device slows to a crawl, struggling to even render a single sphere with this method. When I drag around on the screen, the spheres are supposed to move along with my drags, but when I use the above rendering code, I see a very significant drop in frames.
Here is the context from which the above function is called:
func drawPlanet(_ planet: Planet, withContext ctx: CGContext) {
let distance = MathHelper.hypot3(x: planet.x, y: planet.y, z: flatDistance + planet.z)
let angularDiameter = MathHelper.angularDiameter(diameter: planet.diameter, distance: distance)
let angularX = MathHelper.angularDiameter(diameter: planet.x, distance: distance)
let angularY = MathHelper.angularDiameter(diameter: planet.y, distance: distance)
let fieldOfView = MathHelper.toRadians(fov)
let pitchRad = MathHelper.toRadians(pitch)
let yawRad = MathHelper.toRadians(yaw)
let pitchedY = distance * tan(pitchRad)
let yawedX = distance * tan(yawRad)
let adjustedDiameter = Double(frame.width) * angularDiameter / fieldOfView
let adjustedX = Double(frame.width) * angularX / fieldOfView
let adjustedY = Double(frame.width) * angularY / fieldOfView
let middleX = Double(frame.width) / 2.0 + yawedX
let middleY = Double(frame.height) / 2.0 + pitchedY
let rect = CGRect(x: middleX - adjustedX - adjustedDiameter / 2, y: middleY - adjustedY - adjustedDiameter / 2, width: adjustedDiameter, height: adjustedDiameter)
if (rect.minX < frame.maxX || rect.maxX > frame.minX) && (rect.minY < frame.maxY || rect.maxY > frame.minY) {
ctx.beginPath()
ctx.setLineWidth(1)
ctx.setStrokeColor(planet.color.cgColor)
ctx.setFillColor(planet.color.cgColor)
drawSphere(in: rect, withColor: planet.color.cgColor, withContext: ctx)
if planet.selected {
ctx.beginPath()
ctx.setLineWidth(2)
ctx.setStrokeColor(UIColor.white.cgColor)
ctx.addRect(rect)
ctx.drawPath(using: .stroke)
}
//ctx.closePath()
}
}
And here is the context from which that function is called:
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.setFillColor(UIColor.black.cgColor)
ctx.fill(rect)
for planet in currentGalaxy.planets {
drawPlanet(planet, withContext: ctx)
}
}
I am not constantly redrawing the screen. I am only calling view.setNeedsDisplay()
whenever the device changes its orientation or a UIGestureRecognizer
is triggered.
I imagine there is some glaring inefficiency in my code somewhere, but I do not know where that inefficiency lies. Any help would be greatly appreciated. Thank you!