import CoreText
import SwiftUI
import UniformTypeIdentifiers
private func hex(_ c: Color, _ scheme: ColorScheme) -> String {
var env = EnvironmentValues()
env.colorScheme = scheme
let comps = c.resolve(in: env).cgColor.converted(to: CGColorSpace(name: CGColorSpace.sRGB)!, intent: .defaultIntent, options: nil)!.components!
return "#" + comps.prefix(3).map { String(Int(($0 * 255).rounded()) + 0x100, radix: 16).dropFirst() }.joined()
}
private func num(_ v: CGFloat) -> String {
"\((v * 10).rounded() / 10)"
}
private func esc(_ s: String) -> String {
s.replacingOccurrences(of: "&", with: "&").replacingOccurrences(of: "<", with: "<").replacingOccurrences(of: ">", with: ">").replacingOccurrences(of: "\"", with: """)
}
private func json(_ v: Any) -> String {
String(decoding: try! JSONSerialization.data(withJSONObject: v), as: UTF8.self).replacingOccurrences(of: "", with: "<\\/")
}
private func fontFace(_ subset: String) -> String {
let url = Bundle.main.resourceURL!.appending(path: "fonts/atkinson-hyperlegible-next-\(subset)-wght-normal.woff2")
let data = try! Data(contentsOf: url)
return "@font-face{font-family:Atkinson;font-weight:200 800;src:url(data:font/woff2;base64,\(data.base64EncodedString())) format(\"woff2\")}"
}
private func edgePath(_ pts: [CGPoint]) -> String {
guard let first = pts.first else {
return ""
}
var d = "M\(num(first.x)) \(num(first.y))"
if pts.count > 2 {
for i in 1..<(pts.count - 1) {
let a = pts[i - 1]
let b = pts[i]
let c = pts[i + 1]
let lin = hypot(b.x - a.x, b.y - a.y)
let lout = hypot(c.x - b.x, c.y - b.y)
let r = min(6, lin / 2, lout / 2)
let p = CGPoint(x: b.x - (b.x - a.x) / lin * r, y: b.y - (b.y - a.y) / lin * r)
let q = CGPoint(x: b.x + (c.x - b.x) / lout * r, y: b.y + (c.y - b.y) / lout * r)
d += "L\(num(p.x)) \(num(p.y))Q\(num(b.x)) \(num(b.y)) \(num(q.x)) \(num(q.y))"
}
}
let last = pts[pts.count - 1]
return d + "L\(num(last.x)) \(num(last.y))"
}
private func svgPath(_ p: Path) -> String {
var d = ""
p.forEach { e in
switch e {
case .move(let to):
d += "M\(num(to.x)) \(num(to.y))"
case .line(let to):
d += "L\(num(to.x)) \(num(to.y))"
case .quadCurve(let to, let c):
d += "Q\(num(c.x)) \(num(c.y)) \(num(to.x)) \(num(to.y))"
case .curve(let to, let c1, let c2):
d += "C\(num(c1.x)) \(num(c1.y)) \(num(c2.x)) \(num(c2.y)) \(num(to.x)) \(num(to.y))"
case .closeSubpath:
d += "Z"
}
}
return d
}
private func arrowHead(_ from: CGPoint, _ to: CGPoint) -> String {
let angle = atan2(to.y - from.y, to.x - from.x)
let size: CGFloat = 8
let a = CGPoint(x: to.x - size * cos(angle - .pi / 7), y: to.y - size * sin(angle - .pi / 7))
let b = CGPoint(x: to.x - size * cos(angle + .pi / 7), y: to.y - size * sin(angle + .pi / 7))
return "\(num(to.x)),\(num(to.y)) \(num(a.x)),\(num(a.y)) \(num(b.x)),\(num(b.y))"
}
@MainActor
private func levelSVG(_ layout: BoxLayout) -> String {
let titleLine = textSize("X", titleFont).height
let detailLine = textSize("X", detailFont).height
var svg = "