Skip to content

Commit

Permalink
Fix #260: Shape with no fill and stroke still rendered with default s…
Browse files Browse the repository at this point in the history
…ettings
  • Loading branch information
ystrot committed Dec 27, 2017
1 parent 5ce2d1a commit e67803a
Showing 1 changed file with 4 additions and 309 deletions.
313 changes: 4 additions & 309 deletions Source/render/ShapeRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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? {
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit e67803a

Please sign in to comment.