diff --git a/Source/render/ShapeRenderer.swift b/Source/render/ShapeRenderer.swift index 504af085..c8ac9970 100644 --- a/Source/render/ShapeRenderer.swift +++ b/Source/render/ShapeRenderer.swift @@ -36,8 +36,10 @@ class ShapeRenderer: NodeRenderer { return } - setGeometry(shape.form, ctx: ctx.cgContext!) - drawPath(shape.fill, stroke: shape.stroke, ctx: ctx.cgContext!, opacity: opacity) + if (shape.fill != nil || shape.stroke != nil) { + setGeometry(shape.form, ctx: ctx.cgContext!) + drawPath(shape.fill, stroke: shape.stroke, ctx: ctx.cgContext!, opacity: opacity) + } } override func doFindNodeAt(location: CGPoint, ctx: CGContext) -> Node? { @@ -96,309 +98,6 @@ class ShapeRenderer: NodeRenderer { } } - fileprivate func toBezierPath(_ arc: Arc) -> MBezierPath { - let shift = CGFloat(arc.shift) - let end = shift + CGFloat(arc.extent) - let ellipse = arc.ellipse - let center = CGPoint(x: CGFloat(ellipse.cx), y: CGFloat(ellipse.cy)) - return MBezierPath(arcCenter: center, radius: CGFloat(ellipse.rx), startAngle: shift, endAngle: end, clockwise: true) - } - - fileprivate func toBezierPath(_ points: [Double]) -> MBezierPath { - let parts = stride(from: 0, to: points.count, by: 2).map { Array(points[$0 ..< $0 + 2]) } - let path = MBezierPath() - var first = true - for part in parts { - let point = CGPoint(x: CGFloat(part[0]), y: CGFloat(part[1])) - if first { - path.move(to: point) - first = false - } else { - path.addLine(to: point) - } - } - return path - } - - fileprivate func toBezierPath(_ path: Path) -> MBezierPath { - let bezierPath = MBezierPath() - - var currentPoint: CGPoint? - var cubicPoint: CGPoint? - var quadrPoint: CGPoint? - var initialPoint: CGPoint? - - func M(_ x: Double, y: Double) { - let point = CGPoint(x: CGFloat(x), y: CGFloat(y)) - bezierPath.move(to: point) - setInitPoint(point) - } - - func m(_ x: Double, y: Double) { - if let cur = currentPoint { - let next = CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(y) + cur.y) - bezierPath.move(to: next) - setInitPoint(next) - } else { - M(x, y: y) - } - } - - func L(_ x: Double, y: Double) { - lineTo(CGPoint(x: CGFloat(x), y: CGFloat(y))) - } - - func l(_ x: Double, y: Double) { - if let cur = currentPoint { - lineTo(CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(y) + cur.y)) - } else { - L(x, y: y) - } - } - - func H(_ x: Double) { - if let cur = currentPoint { - lineTo(CGPoint(x: CGFloat(x), y: CGFloat(cur.y))) - } - } - - func h(_ x: Double) { - if let cur = currentPoint { - lineTo(CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(cur.y))) - } - } - - func V(_ y: Double) { - if let cur = currentPoint { - lineTo(CGPoint(x: CGFloat(cur.x), y: CGFloat(y))) - } - } - - func v(_ y: Double) { - if let cur = currentPoint { - lineTo(CGPoint(x: CGFloat(cur.x), y: CGFloat(y) + cur.y)) - } - } - - func lineTo(_ p: CGPoint) { - bezierPath.addLine(to: p) - setPoint(p) - } - - func c(_ x1: Double, y1: Double, x2: Double, y2: Double, x: Double, y: Double) { - if let cur = currentPoint { - let endPoint = CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(y) + cur.y) - let controlPoint1 = CGPoint(x: CGFloat(x1) + cur.x, y: CGFloat(y1) + cur.y) - let controlPoint2 = CGPoint(x: CGFloat(x2) + cur.x, y: CGFloat(y2) + cur.y) - bezierPath.addCurve(to: endPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2) - setCubicPoint(endPoint, cubic: controlPoint2) - } - } - - func C(_ x1: Double, y1: Double, x2: Double, y2: Double, x: Double, y: Double) { - let endPoint = CGPoint(x: CGFloat(x), y: CGFloat(y)) - let controlPoint1 = CGPoint(x: CGFloat(x1), y: CGFloat(y1)) - let controlPoint2 = CGPoint(x: CGFloat(x2), y: CGFloat(y2)) - bezierPath.addCurve(to: endPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2) - setCubicPoint(endPoint, cubic: controlPoint2) - } - - func s(_ x2: Double, y2: Double, x: Double, y: Double) { - if let cur = currentPoint { - let nextCubic = CGPoint(x: CGFloat(x2) + cur.x, y: CGFloat(y2) + cur.y) - let next = CGPoint(x: CGFloat(x) + cur.x, y: CGFloat(y) + cur.y) - - var xy1: CGPoint? - if let curCubicVal = cubicPoint { - xy1 = CGPoint(x: CGFloat(2 * cur.x) - curCubicVal.x, y: CGFloat(2 * cur.y) - curCubicVal.y) - } else { - xy1 = cur - } - bezierPath.addCurve(to: next, controlPoint1: xy1!, controlPoint2: nextCubic) - setCubicPoint(next, cubic: nextCubic) - } - } - - func S(_ x2: Double, y2: Double, x: Double, y: Double) { - if let cur = currentPoint { - let nextCubic = CGPoint(x: CGFloat(x2), y: CGFloat(y2)) - let next = CGPoint(x: CGFloat(x), y: CGFloat(y)) - var xy1: CGPoint? - if let curCubicVal = cubicPoint { - xy1 = CGPoint(x: CGFloat(2 * cur.x) - curCubicVal.x, y: CGFloat(2 * cur.y) - curCubicVal.y) - } else { - xy1 = cur - } - bezierPath.addCurve(to: next, controlPoint1: xy1!, controlPoint2: nextCubic) - setCubicPoint(next, cubic: nextCubic) - } - } - - func a(_ rx: Double, ry: Double, angle: Double, largeArc: Bool, sweep: Bool, x: Double, y: Double) { - if let cur = currentPoint { - A(rx, ry: ry, angle: angle, largeArc: largeArc, sweep: sweep, x: x + Double(cur.x), y: y + Double(cur.y)) - } - } - - func A(_ rx: Double, ry: Double, angle: Double, largeArc: Bool, sweep: Bool, x: Double, y: Double) { - if let cur = currentPoint { - let x1 = Double(cur.x) - let y1 = Double(cur.y) - - // find arc center coordinates and points angles as per - // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter - let x1_ = cos(angle) * (x1 - x) / 2 + sin(angle) * (y1 - y) / 2 - let y1_ = -1 * sin(angle) * (x1 - x) / 2 + cos(angle) * (y1 - y) / 2 - // make sure the value under the root is positive - let underroot = (rx * rx * ry * ry - rx * rx * y1_ * y1_ - ry * ry * x1_ * x1_) - / (rx * rx * y1_ * y1_ + ry * ry * x1_ * x1_) - var bigRoot = (underroot > 0) ? sqrt(underroot) : 0 - // TODO: Replace concrete number with 1e-2 - bigRoot = (bigRoot <= 0.01) ? 0 : bigRoot - let coef: Double = (sweep != largeArc) ? 1 : -1 - let cx_ = coef * bigRoot * rx * y1_ / ry - let cy_ = -1 * coef * bigRoot * ry * x1_ / rx - let cx = (cos(angle) * cx_ - sin(angle) * cy_ + (x1 + x) / 2) - let cy = (sin(angle) * cx_ + cos(angle) * cy_ + (y1 + y) / 2) - let t1 = -1 * atan2(y1 - cy, x1 - cx) - let t2 = atan2(y - cy, x - cx) - var delta = -(t1 + t2) - // recalculate delta depending on arc. Preserve rotation direction - if largeArc { - let sg = copysign(1.0, delta) - if abs(delta) < Double.pi { - delta = -1 * (sg * M_2_PI - delta) - } - } else { - let sg = copysign(1.0, delta) - if abs(delta) > Double.pi { - delta = -1 * (sg * M_2_PI - delta) - } - } - E(cx - rx, y: cy - ry, w: 2 * rx, h: 2 * ry, startAngle: t1, arcAngle: delta) - setPoint(CGPoint(x: CGFloat(x), y: CGFloat(y))) - } - } - - func E(_ x: Double, y: Double, w: Double, h: Double, startAngle: Double, arcAngle: Double) { - // TODO: only circle now - let extent = CGFloat(startAngle) - let end = extent + CGFloat(arcAngle) - let center = CGPoint(x: CGFloat(x + w / 2), y: CGFloat(y + h / 2)) - bezierPath.addArc(withCenter: center, radius: CGFloat(w / 2), startAngle: extent, endAngle: end, clockwise: true) - } - - func e(_ x: Double, y: Double, w: Double, h: Double, startAngle: Double, arcAngle: Double) { - // TODO: only circle now - if let cur = currentPoint { - E(x + Double(cur.x), y: y + Double(cur.y), w: w, h: h, startAngle: startAngle, arcAngle: arcAngle) - } - } - - func Z() { - if let initPoint = initialPoint { - lineTo(initPoint) - } - bezierPath.close() - } - - func setCubicPoint(_ p: CGPoint, cubic: CGPoint) { - currentPoint = p - cubicPoint = cubic - quadrPoint = nil - } - - func setInitPoint(_ p: CGPoint) { - setPoint(p) - initialPoint = p - } - - func setPoint(_ p: CGPoint) { - currentPoint = p - cubicPoint = nil - quadrPoint = nil - } - - // TODO: think about this - for part in path.segments { - var data = part.data - switch part.type { - case .M: - M(data[0], y: data[1]) - data.removeSubrange((0 ..< 2)) - while data.count >= 2 { - L(data[0], y: data[1]) - data.removeSubrange((0 ..< 2)) - } - case .m: - m(data[0], y: data[1]) - data.removeSubrange((0 ..< 2)) - while data.count >= 2 { - l(data[0], y: data[1]) - data.removeSubrange((0 ..< 2)) - } - case .L: - while data.count >= 2 { - L(data[0], y: data[1]) - data.removeSubrange((0 ..< 2)) - } - case .l: - while data.count >= 2 { - l(data[0], y: data[1]) - data.removeSubrange((0 ..< 2)) - } - case .H: - H(data[0]) - case .h: - h(data[0]) - case .V: - V(data[0]) - case .v: - v(data[0]) - case .C: - while data.count >= 6 { - C(data[0], y1: data[1], x2: data[2], y2: data[3], x: data[4], y: data[5]) - data.removeSubrange((0 ..< 6)) - } - case .c: - while data.count >= 6 { - c(data[0], y1: data[1], x2: data[2], y2: data[3], x: data[4], y: data[5]) - data.removeSubrange((0 ..< 6)) - } - case .S: - while data.count >= 4 { - S(data[0], y2: data[1], x: data[2], y: data[3]) - data.removeSubrange((0 ..< 4)) - } - case .s: - while data.count >= 4 { - s(data[0], y2: data[1], x: data[2], y: data[3]) - data.removeSubrange((0 ..< 4)) - } - case .A: - let flags = numToBools(data[3]) - A(data[0], ry: data[1], angle: data[2], largeArc: flags[0], sweep: flags[1], x: data[4], y: data[5]) - case .a: - let flags = numToBools(data[3]) - a(data[0], ry: data[1], angle: data[2], largeArc: flags[0], sweep: flags[1], x: data[4], y: data[5]) - case .E: - E(data[0], y: data[1], w: data[2], h: data[3], startAngle: data[4], arcAngle: data[5]) - case .e: - e(data[0], y: data[1], w: data[2], h: data[3], startAngle: data[4], arcAngle: data[5]) - case .z: - Z() - default: - fatalError("Unknown segment: \(part.type)") - } - } - return bezierPath - } - - fileprivate func numToBools(_ num: Double) -> [Bool] { - let val: Int = Int(num) - return [(val & 1) > 0, (val & 2) > 0] - } - fileprivate func newCGRect(_ rect: Rect) -> CGRect { return CGRect(x: CGFloat(rect.x), y: CGFloat(rect.y), width: CGFloat(rect.w), height: CGFloat(rect.h)) } @@ -429,10 +128,6 @@ class ShapeRenderer: NodeRenderer { drawWithStroke(stroke, ctx: ctx, opacity: opacity, shouldStrokePath: shouldStrokePath, mode: .stroke) return } - - ctx!.setLineWidth(1.0) - ctx!.setStrokeColor(MColor.black.cgColor) - ctx!.drawPath(using: .stroke) } fileprivate func setFill(_ fill: Fill?, ctx: CGContext?, opacity: Double) {