Skip to content

Commit 18a5328

Browse files
Add Bracket Annotation (#108)
* add bracket annotation * coordinate update * update * bfix * bfix * test bfix * bfix * bfix: Fix line chart cross both axes regression test. (pow function overload fix) Co-authored-by: Karthik Ramesh Iyer <[email protected]>
1 parent ca8fabd commit 18a5328

File tree

7 files changed

+194
-23
lines changed

7 files changed

+194
-23
lines changed

Sources/SwiftPlot/PlotStyleHelpers.swift

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public struct Coordinate {
5555
}
5656

5757
public protocol CoordinateResolver {
58-
func resolve(_ coordinate: Coordinate) -> Point
58+
func resolve(_ coordinate: Coordinate) -> Point
5959
}
6060

6161
public protocol Annotation {
@@ -69,7 +69,7 @@ public enum Direction {
6969
case west
7070
}
7171

72-
public protocol AnchorableAnnotation : Annotation {
72+
public protocol AnchorableAnnotation: Annotation {
7373
var direction: Direction { get set }
7474
var margin: Float { get set }
7575
mutating func resolve(renderer: Renderer, center: Point)
@@ -107,7 +107,7 @@ struct Box: Annotation, AnchorableAnnotation {
107107
}
108108
}
109109

110-
struct Text : Annotation, AnchorableAnnotation {
110+
struct Text: Annotation, AnchorableAnnotation {
111111
public var text = ""
112112
public var color = Color.black
113113
public var size: Float = 15
@@ -157,7 +157,7 @@ struct Text : Annotation, AnchorableAnnotation {
157157
}
158158
}
159159

160-
struct Arrow : Annotation {
160+
struct Arrow: Annotation {
161161
public var color = Color.black
162162
public var start = Point(0.0, 0.0)
163163
public var end = Point(0.0, 0.0)
@@ -277,3 +277,44 @@ struct Arrow : Annotation {
277277
self.overrideAnchor = overrideAnchor
278278
}
279279
}
280+
281+
struct Bracket: Annotation {
282+
public var color = Color.black
283+
public var start = Coordinate()
284+
public var end = Coordinate()
285+
public var strokeWidth: Float = 3
286+
public var isDashed: Bool = false
287+
public var legLength: Float = 15
288+
public var anchorableAnnotation: AnchorableAnnotation?
289+
public mutating func draw(resolver: CoordinateResolver, renderer: Renderer) {
290+
// Calculate leg points.
291+
let startPoint = resolver.resolve(start), endPoint = resolver.resolve(end)
292+
var startLegPoint = startPoint + Point(0.0, -strokeWidth/2-legLength)
293+
let startLegRotateAngle = -atan2(endPoint.x - startPoint.x, endPoint.y - startPoint.y)
294+
startLegPoint = rotatePoint(point: startLegPoint, center: startPoint, angleRadians: startLegRotateAngle + 0.5 * Float.pi)
295+
var endLegPoint = endPoint + Point(0.0, -strokeWidth/2-legLength)
296+
endLegPoint = rotatePoint(point: endLegPoint, center: endPoint, angleRadians: startLegRotateAngle + 0.5 * Float.pi)
297+
298+
// Draws bracket.
299+
renderer.drawPolyline(Polyline(startLegPoint, startPoint, endPoint, endLegPoint)!,
300+
strokeWidth: strokeWidth,
301+
strokeColor: color,
302+
isDashed: isDashed)
303+
304+
// Calculate anchor point.
305+
let anchorPoint = Point((startPoint.x + endPoint.x)/2, (startPoint.y + endPoint.y)/2)
306+
307+
// Draws AnchorableAnnotation if specified.
308+
anchorableAnnotation?.resolve(renderer: renderer, center: anchorPoint)
309+
anchorableAnnotation?.draw(resolver: resolver, renderer: renderer)
310+
}
311+
public init(color: Color = .black, start: Coordinate = Coordinate(), end: Coordinate = Coordinate(), strokeWidth: Float = 5, isDashed: Bool = false, legLength: Float = 15, anchorableAnnotation: AnchorableAnnotation? = nil) {
312+
self.color = color
313+
self.start = start
314+
self.end = end
315+
self.strokeWidth = strokeWidth
316+
self.isDashed = isDashed
317+
self.legLength = legLength
318+
self.anchorableAnnotation = anchorableAnnotation
319+
}
320+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
@testable import SwiftPlot
2+
import Foundation
3+
import SVGRenderer
4+
#if canImport(AGGRenderer)
5+
import AGGRenderer
6+
#endif
7+
#if canImport(QuartzRenderer)
8+
import QuartzRenderer
9+
#endif
10+
11+
@available(tvOS 13, watchOS 13, *)
12+
extension AnnotationTests {
13+
14+
func testAnnotationBracket() throws {
15+
16+
let fileName = "_35_bracket_annotation"
17+
18+
var lineGraph = LineGraph<Float, Float>(enablePrimaryAxisGrid: true)
19+
lineGraph.addFunction(sin, minX: -5.0, maxX: 5.0, label: "sin(x)", color: .lightBlue)
20+
lineGraph.plotTitle.title = "FUNCTION"
21+
lineGraph.plotLabel.xLabel = "X-AXIS"
22+
lineGraph.plotLabel.yLabel = "Y-AXIS"
23+
lineGraph.plotLineThickness = 3.0
24+
25+
lineGraph.addAnnotation(annotation: Bracket(start: Coordinate(point: Point(150.0, 607.0)),
26+
end: Coordinate(point: Point(630.0, 607.0)),
27+
anchorableAnnotation: Text(text: "Period",
28+
direction: .south)))
29+
lineGraph.addAnnotation(annotation: Bracket(start: Coordinate(point: Point(900, 585.0)),
30+
end: Coordinate(point: Point(900, 340.0)),
31+
anchorableAnnotation: Text(text: "Amplitude",
32+
direction: .east)))
33+
let svg_renderer = SVGRenderer()
34+
try lineGraph.drawGraphAndOutput(fileName: svgOutputDirectory+fileName,
35+
renderer: svg_renderer)
36+
verifyImage(name: fileName, renderer: .svg)
37+
#if canImport(AGGRenderer)
38+
let agg_renderer = AGGRenderer()
39+
try lineGraph.drawGraphAndOutput(fileName: aggOutputDirectory+fileName,
40+
renderer: agg_renderer)
41+
verifyImage(name: fileName, renderer: .agg)
42+
#endif
43+
/*
44+
#if canImport(QuartzRenderer)
45+
let quartz_renderer = QuartzRenderer()
46+
try lineGraph.drawGraphAndOutput(fileName: coreGraphicsOutputDirectory+fileName,
47+
renderer: quartz_renderer)
48+
verifyImage(name: fileName, renderer: .coreGraphics)
49+
#endif
50+
*/
51+
}
52+
}

Tests/SwiftPlotTests/LineChart/linechart-regressiontests.swift

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import SwiftPlot
22
import Foundation
33

44
extension LineChartTests {
5-
5+
66
func testLineChart_positiveYOrigin() throws {
77
let x:[Float] = [0, 1, 2, 3]
88
let y:[Float] = [70, 80, 95, 100]
@@ -16,7 +16,7 @@ extension LineChartTests {
1616

1717
try renderAndVerify(lineGraph, size: Size(width: 300, height: 300))
1818
}
19-
19+
2020
func testLineChart_positiveYOrigin_secondary() throws {
2121
let x:[Float] = [0, 1, 2, 3]
2222
let y:[Float] = [70, 80, 95, 100]
@@ -32,10 +32,10 @@ extension LineChartTests {
3232
lineGraph.plotLineThickness = 3.0
3333
lineGraph.enablePrimaryAxisGrid = true
3434
lineGraph.enableSecondaryAxisGrid = true
35-
35+
3636
try renderAndVerify(lineGraph, size: Size(width: 400, height: 400))
3737
}
38-
38+
3939
func testLineChart_negativeYOrigin() throws {
4040
let x:[Float] = [0, 1, 2, 3]
4141
let y:[Float] = [-70, -80, -95, -100]
@@ -49,7 +49,7 @@ extension LineChartTests {
4949

5050
try renderAndVerify(lineGraph, size: Size(width: 300, height: 300))
5151
}
52-
52+
5353
func testLineChart_positiveXOrigin() throws {
5454
let x:[Float] = [5, 6, 7, 8]
5555
let y:[Float] = [70, 80, 95, 100]
@@ -63,7 +63,7 @@ extension LineChartTests {
6363

6464
try renderAndVerify(lineGraph, size: Size(width: 300, height: 300))
6565
}
66-
66+
6767
func testLineChart_negativeXOrigin() throws {
6868
let x:[Float] = [-5, -6, -7, -8]
6969
let y:[Float] = [-70, -80, -95, -100]
@@ -77,7 +77,7 @@ extension LineChartTests {
7777

7878
try renderAndVerify(lineGraph, size: Size(width: 300, height: 300))
7979
}
80-
80+
8181
func testLineChart_positiveXOrigin_secondary() throws {
8282
let x:[Float] = [0, 1, 2, 3]
8383
let y:[Float] = [70, 80, 95, 100]
@@ -94,10 +94,10 @@ extension LineChartTests {
9494
lineGraph.plotLineThickness = 3.0
9595
lineGraph.enablePrimaryAxisGrid = true
9696
lineGraph.enableSecondaryAxisGrid = true
97-
97+
9898
try renderAndVerify(lineGraph, size: Size(width: 400, height: 400))
9999
}
100-
100+
101101
func testLineChart_negativeXOrigin_unsorted() throws {
102102
let x:[Float] = [-8, -7, -6, -5]
103103
let y:[Float] = [70, 80, 95, 100]
@@ -111,7 +111,7 @@ extension LineChartTests {
111111

112112
try renderAndVerify(lineGraph, size: Size(width: 300, height: 300))
113113
}
114-
114+
115115
func testLineChart_crossX() throws {
116116
func someFunction(_ x: Float) -> Float { (x * x) + 10 }
117117
var lineGraph_func = LineGraph<Float,Float>(enablePrimaryAxisGrid: true)
@@ -126,7 +126,7 @@ extension LineChartTests {
126126
lineGraph_func.plotLabel.yLabel = "Y-AXIS"
127127
try renderAndVerify(lineGraph_func, size: Size(width: 400, height: 400))
128128
}
129-
129+
130130
func testLineChart_crossY() throws {
131131
func someFunction(_ x: Float) -> Float { 5 * cos(2 * x * x) / x }
132132
var lineGraph_func = LineGraph<Float,Float>(enablePrimaryAxisGrid: true)
@@ -141,22 +141,22 @@ extension LineChartTests {
141141
lineGraph_func.plotLabel.yLabel = "Y-AXIS"
142142
try renderAndVerify(lineGraph_func, size: Size(width: 400, height: 400))
143143
}
144-
144+
145145
func testLineChart_crossBothAxes() throws {
146146
var lineGraph = LineGraph<Float,Float>(enablePrimaryAxisGrid: true,
147147
enableSecondaryAxisGrid: false)
148148
let clamp: ClosedRange<Float>? = -150...150
149-
lineGraph.addFunction({ pow($0, 2) }, minX: -5, maxX: 5, clampY: clamp, label: "2", color: .lightBlue)
150-
lineGraph.addFunction({ pow($0, 3) }, minX: -5, maxX: 5, clampY: clamp, label: "3", color: .orange)
151-
lineGraph.addFunction({ pow($0, 4) }, minX: -5, maxX: 5, clampY: clamp, label: "4", color: .red)
152-
lineGraph.addFunction({ pow($0, 5) }, minX: -5, maxX: 5, clampY: clamp, label: "5", color: .brown)
153-
lineGraph.addFunction({ pow($0 , 6) }, minX: -5, maxX: 5, clampY: clamp, label: "6", color: .purple)
154-
lineGraph.addFunction({ pow($0 , 7) }, minX: -5, maxX: 5, clampY: clamp, label: "7", color: .green)
149+
lineGraph.addFunction({ Float(truncating: pow(Decimal(Double($0)), Int(2)) as NSNumber) }, minX: -5, maxX: 5, clampY: clamp, label: "2", color: .lightBlue)
150+
lineGraph.addFunction({ Float(truncating: pow(Decimal(Double($0)), Int(3)) as NSNumber) }, minX: -5, maxX: 5, clampY: clamp, label: "3", color: .orange)
151+
lineGraph.addFunction({ Float(truncating: pow(Decimal(Double($0)), Int(4)) as NSNumber) }, minX: -5, maxX: 5, clampY: clamp, label: "4", color: .red)
152+
lineGraph.addFunction({ Float(truncating: pow(Decimal(Double($0)), Int(5)) as NSNumber) }, minX: -5, maxX: 5, clampY: clamp, label: "5", color: .brown)
153+
lineGraph.addFunction({ Float(truncating: pow(Decimal(Double($0)), Int(6)) as NSNumber) }, minX: -5, maxX: 5, clampY: clamp, label: "6", color: .purple)
154+
lineGraph.addFunction({ Float(truncating: pow(Decimal(Double($0)), Int(7)) as NSNumber) }, minX: -5, maxX: 5, clampY: clamp, label: "7", color: .green)
155155
lineGraph.plotTitle.title = "y = x^n"
156156
lineGraph.plotLabel.xLabel = "x"
157157
lineGraph.plotLabel.yLabel = "y"
158158
lineGraph.backgroundColor = .transparent
159-
159+
160160
try renderAndVerify(lineGraph, size: Size(width: 800, height: 400))
161161
}
162162
}
41 KB
Loading

0 commit comments

Comments
 (0)