 																								 maxY: Float(screen.maxY),
 																								 maxDepth: 6)
 		
+		let margin: Float = 3.0 // $ Stylesheet
 		var layout: [Int : LabelPlacement] = [:]
-		let margin: Float = 3.0 // $ Stylesheet
 		for marker in workingSet {
 			var anchor: LayoutAnchor? = (marker.kind == .Region ? .Center : .NE)	// Choose starting layout anchor
 			let origin = project(marker.worldPos)

	static func < (lhs: LabelMarker, rhs: LabelMarker) -> Bool {
		let lhsScore = lhs.rank - (lhs.kind == .Region ? 1 : 0)	// Value regions one step higher
		let rhsScore = rhs.rank - (rhs.kind == .Region ? 1 : 0)
		return lhsScore <= rhsScore
	}
}



class LabelLayoutEngine {
	let maxLabels: Int
	var labelSizeCache: [Int : (w: Float, h: Float)] = [:]	// $ Limit size of this
	
	init(maxLabels: Int) {
		self.maxLabels = maxLabels
		// Collision detection structure for screen-space layout
	}
	
	// $ Re-implement zoom culling as a 3D box sweeping through point cloud
	
	func layoutLabels(labeledMarkers: [LabelMarker],
										unlabeledMarkers: [LabelMarker],
										projection project: (Vertex) -> CGPoint) -> [Int : LabelPlacement] {
		let newCandidates = unlabeledMarkers.sorted(by: <)
		let prioritizedMarkers = labeledMarkers + newCandidates
		let workingSet = prioritizedMarkers.prefix(maxLabels * 2)	// Take enough markers to give each label two candidates
		
		let screen = UIScreen.main.bounds
		var labelQuadTree = QuadTree<LabelPlacement>(minX: Float(screen.minX),
																								 minY: Float(screen.minY),
