Showing preview only (1,175K chars total). Download the full file or copy to clipboard to get everything.
Repository: nat/keyrace
Branch: main
Commit: 8c4f3d38e2c9
Files: 198
Total size: 1.1 MB
Directory structure:
gitextract_yvxi4i9t/
├── .github/
│ └── workflows/
│ ├── build_app.yml
│ └── make-test.yml
├── .gitignore
├── Makefile
├── README.md
├── go.mod
├── go.sum
├── keyrace.c
├── mac/
│ ├── Podfile
│ ├── Pods/
│ │ ├── Charts/
│ │ │ ├── LICENSE
│ │ │ ├── README.md
│ │ │ └── Source/
│ │ │ └── Charts/
│ │ │ ├── Animation/
│ │ │ │ ├── Animator.swift
│ │ │ │ └── ChartAnimationEasing.swift
│ │ │ ├── Charts/
│ │ │ │ ├── BarChartView.swift
│ │ │ │ ├── BarLineChartViewBase.swift
│ │ │ │ ├── BubbleChartView.swift
│ │ │ │ ├── CandleStickChartView.swift
│ │ │ │ ├── ChartViewBase.swift
│ │ │ │ ├── CombinedChartView.swift
│ │ │ │ ├── HorizontalBarChartView.swift
│ │ │ │ ├── LineChartView.swift
│ │ │ │ ├── PieChartView.swift
│ │ │ │ ├── PieRadarChartViewBase.swift
│ │ │ │ ├── RadarChartView.swift
│ │ │ │ └── ScatterChartView.swift
│ │ │ ├── Components/
│ │ │ │ ├── AxisBase.swift
│ │ │ │ ├── ChartLimitLine.swift
│ │ │ │ ├── ComponentBase.swift
│ │ │ │ ├── Description.swift
│ │ │ │ ├── IMarker.swift
│ │ │ │ ├── Legend.swift
│ │ │ │ ├── LegendEntry.swift
│ │ │ │ ├── MarkerImage.swift
│ │ │ │ ├── MarkerView.swift
│ │ │ │ ├── XAxis.swift
│ │ │ │ └── YAxis.swift
│ │ │ ├── Data/
│ │ │ │ ├── Implementations/
│ │ │ │ │ ├── ChartBaseDataSet.swift
│ │ │ │ │ └── Standard/
│ │ │ │ │ ├── BarChartData.swift
│ │ │ │ │ ├── BarChartDataEntry.swift
│ │ │ │ │ ├── BarChartDataSet.swift
│ │ │ │ │ ├── BarLineScatterCandleBubbleChartData.swift
│ │ │ │ │ ├── BarLineScatterCandleBubbleChartDataSet.swift
│ │ │ │ │ ├── BubbleChartData.swift
│ │ │ │ │ ├── BubbleChartDataEntry.swift
│ │ │ │ │ ├── BubbleChartDataSet.swift
│ │ │ │ │ ├── CandleChartData.swift
│ │ │ │ │ ├── CandleChartDataEntry.swift
│ │ │ │ │ ├── CandleChartDataSet.swift
│ │ │ │ │ ├── ChartData.swift
│ │ │ │ │ ├── ChartDataEntry.swift
│ │ │ │ │ ├── ChartDataEntryBase.swift
│ │ │ │ │ ├── ChartDataSet.swift
│ │ │ │ │ ├── CombinedChartData.swift
│ │ │ │ │ ├── LineChartData.swift
│ │ │ │ │ ├── LineChartDataSet.swift
│ │ │ │ │ ├── LineRadarChartDataSet.swift
│ │ │ │ │ ├── LineScatterCandleRadarChartDataSet.swift
│ │ │ │ │ ├── PieChartData.swift
│ │ │ │ │ ├── PieChartDataEntry.swift
│ │ │ │ │ ├── PieChartDataSet.swift
│ │ │ │ │ ├── RadarChartData.swift
│ │ │ │ │ ├── RadarChartDataEntry.swift
│ │ │ │ │ ├── RadarChartDataSet.swift
│ │ │ │ │ ├── ScatterChartData.swift
│ │ │ │ │ └── ScatterChartDataSet.swift
│ │ │ │ └── Interfaces/
│ │ │ │ ├── IBarChartDataSet.swift
│ │ │ │ ├── IBarLineScatterCandleBubbleChartDataSet.swift
│ │ │ │ ├── IBubbleChartDataSet.swift
│ │ │ │ ├── ICandleChartDataSet.swift
│ │ │ │ ├── IChartDataSet.swift
│ │ │ │ ├── ILineChartDataSet.swift
│ │ │ │ ├── ILineRadarChartDataSet.swift
│ │ │ │ ├── ILineScatterCandleRadarChartDataSet.swift
│ │ │ │ ├── IPieChartDataSet.swift
│ │ │ │ ├── IRadarChartDataSet.swift
│ │ │ │ └── IScatterChartDataSet.swift
│ │ │ ├── Filters/
│ │ │ │ ├── DataApproximator+N.swift
│ │ │ │ └── DataApproximator.swift
│ │ │ ├── Formatters/
│ │ │ │ ├── DefaultAxisValueFormatter.swift
│ │ │ │ ├── DefaultFillFormatter.swift
│ │ │ │ ├── DefaultValueFormatter.swift
│ │ │ │ ├── IAxisValueFormatter.swift
│ │ │ │ ├── IFillFormatter.swift
│ │ │ │ ├── IValueFormatter.swift
│ │ │ │ └── IndexAxisValueFormatter.swift
│ │ │ ├── Highlight/
│ │ │ │ ├── BarHighlighter.swift
│ │ │ │ ├── ChartHighlighter.swift
│ │ │ │ ├── CombinedHighlighter.swift
│ │ │ │ ├── Highlight.swift
│ │ │ │ ├── HorizontalBarHighlighter.swift
│ │ │ │ ├── IHighlighter.swift
│ │ │ │ ├── PieHighlighter.swift
│ │ │ │ ├── PieRadarHighlighter.swift
│ │ │ │ ├── RadarHighlighter.swift
│ │ │ │ └── Range.swift
│ │ │ ├── Interfaces/
│ │ │ │ ├── BarChartDataProvider.swift
│ │ │ │ ├── BarLineScatterCandleBubbleChartDataProvider.swift
│ │ │ │ ├── BubbleChartDataProvider.swift
│ │ │ │ ├── CandleChartDataProvider.swift
│ │ │ │ ├── ChartDataProvider.swift
│ │ │ │ ├── CombinedChartDataProvider.swift
│ │ │ │ ├── LineChartDataProvider.swift
│ │ │ │ └── ScatterChartDataProvider.swift
│ │ │ ├── Jobs/
│ │ │ │ ├── AnimatedMoveViewJob.swift
│ │ │ │ ├── AnimatedViewPortJob.swift
│ │ │ │ ├── AnimatedZoomViewJob.swift
│ │ │ │ ├── MoveViewJob.swift
│ │ │ │ ├── ViewPortJob.swift
│ │ │ │ └── ZoomViewJob.swift
│ │ │ ├── Renderers/
│ │ │ │ ├── AxisRendererBase.swift
│ │ │ │ ├── BarChartRenderer.swift
│ │ │ │ ├── BarLineScatterCandleBubbleRenderer.swift
│ │ │ │ ├── BubbleChartRenderer.swift
│ │ │ │ ├── CandleStickChartRenderer.swift
│ │ │ │ ├── ChartDataRendererBase.swift
│ │ │ │ ├── CombinedChartRenderer.swift
│ │ │ │ ├── HorizontalBarChartRenderer.swift
│ │ │ │ ├── LegendRenderer.swift
│ │ │ │ ├── LineChartRenderer.swift
│ │ │ │ ├── LineRadarRenderer.swift
│ │ │ │ ├── LineScatterCandleRadarRenderer.swift
│ │ │ │ ├── PieChartRenderer.swift
│ │ │ │ ├── RadarChartRenderer.swift
│ │ │ │ ├── Renderer.swift
│ │ │ │ ├── Scatter/
│ │ │ │ │ ├── ChevronDownShapeRenderer.swift
│ │ │ │ │ ├── ChevronUpShapeRenderer.swift
│ │ │ │ │ ├── CircleShapeRenderer.swift
│ │ │ │ │ ├── CrossShapeRenderer.swift
│ │ │ │ │ ├── IShapeRenderer.swift
│ │ │ │ │ ├── SquareShapeRenderer.swift
│ │ │ │ │ ├── TriangleShapeRenderer.swift
│ │ │ │ │ └── XShapeRenderer.swift
│ │ │ │ ├── ScatterChartRenderer.swift
│ │ │ │ ├── XAxisRenderer.swift
│ │ │ │ ├── XAxisRendererHorizontalBarChart.swift
│ │ │ │ ├── XAxisRendererRadarChart.swift
│ │ │ │ ├── YAxisRenderer.swift
│ │ │ │ ├── YAxisRendererHorizontalBarChart.swift
│ │ │ │ └── YAxisRendererRadarChart.swift
│ │ │ └── Utils/
│ │ │ ├── ChartColorTemplates.swift
│ │ │ ├── ChartUtils.swift
│ │ │ ├── Fill.swift
│ │ │ ├── Platform+Accessibility.swift
│ │ │ ├── Platform+Color.swift
│ │ │ ├── Platform+Gestures.swift
│ │ │ ├── Platform+Graphics.swift
│ │ │ ├── Platform+Touch Handling.swift
│ │ │ ├── Platform.swift
│ │ │ ├── Transformer.swift
│ │ │ ├── TransformerHorizontalBarChart.swift
│ │ │ └── ViewPortHandler.swift
│ │ ├── Pods.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ └── xcshareddata/
│ │ │ └── xcschemes/
│ │ │ ├── Charts.xcscheme
│ │ │ └── Pods-keyrace-mac.xcscheme
│ │ └── Target Support Files/
│ │ ├── Charts/
│ │ │ ├── Charts-Info.plist
│ │ │ ├── Charts-dummy.m
│ │ │ ├── Charts-prefix.pch
│ │ │ ├── Charts-umbrella.h
│ │ │ ├── Charts.debug.xcconfig
│ │ │ ├── Charts.modulemap
│ │ │ └── Charts.release.xcconfig
│ │ └── Pods-keyrace-mac/
│ │ ├── Pods-keyrace-mac-Info.plist
│ │ ├── Pods-keyrace-mac-acknowledgements.markdown
│ │ ├── Pods-keyrace-mac-acknowledgements.plist
│ │ ├── Pods-keyrace-mac-dummy.m
│ │ ├── Pods-keyrace-mac-frameworks-Debug-input-files.xcfilelist
│ │ ├── Pods-keyrace-mac-frameworks-Debug-output-files.xcfilelist
│ │ ├── Pods-keyrace-mac-frameworks-Release-input-files.xcfilelist
│ │ ├── Pods-keyrace-mac-frameworks-Release-output-files.xcfilelist
│ │ ├── Pods-keyrace-mac-frameworks.sh
│ │ ├── Pods-keyrace-mac-umbrella.h
│ │ ├── Pods-keyrace-mac.debug.xcconfig
│ │ ├── Pods-keyrace-mac.modulemap
│ │ └── Pods-keyrace-mac.release.xcconfig
│ ├── keyrace-mac/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AccentColor.colorset/
│ │ │ │ └── Contents.json
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── magic-keyboard.imageset/
│ │ │ └── Contents.json
│ │ ├── ContentView.swift
│ │ ├── GitHub.swift
│ │ ├── Info.plist
│ │ ├── KeyTap.swift
│ │ ├── KeyboardView.swift
│ │ ├── LeaderboardView.swift
│ │ ├── MenuView.swift
│ │ ├── Preview Content/
│ │ │ └── Preview Assets.xcassets/
│ │ │ └── Contents.json
│ │ ├── SettingsView.swift
│ │ ├── TypingChart.swift
│ │ ├── UserDefaults.swift
│ │ └── keyrace_mac.entitlements
│ ├── keyrace-mac.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata/
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── keyrace-mac.xcscheme
│ └── keyrace-mac.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ └── IDEWorkspaceChecks.plist
└── server.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/build_app.yml
================================================
name: build mac app
on: [push, pull_request]
jobs:
build:
runs-on: macOS-latest
steps:
- name: Checkout repository
uses: actions/checkout@v1
with:
fetch-depth: 0
- name: Select Xcode 12.4
run: sudo xcode-select -switch /Applications/Xcode_12.4.app
- name: Build project
run: make keyrace-mac
- name: Save .zip as artifact
uses: actions/upload-artifact@v1
with:
name: keyrace-mac.app
path: ./build/mac
================================================
FILE: .github/workflows/make-test.yml
================================================
on: [push, pull_request]
name: make test
jobs:
maketest:
name: make test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/setup-go@v2
with:
go-version: '^1.13.1' # The Go version to download (if necessary) and use.
- name: make test
run: make test
shell: bash
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
================================================
FILE: .gitignore
================================================
# Xcode .gitignore
#
# Source:
# https://github.com/github/gitignore/blob/master/Global/Xcode.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Gcc Patch
/*.gcno
## Sample Data
.assets/*.csv
## Secrets
Generated
Secrets.xcconfig
# Other
keyrace
keyrace.db
keyrace.dSYM
keyrace-server*
vendor
================================================
FILE: Makefile
================================================
SERVER=keyrace.app
BUILDTAGS=libsqlite3 sqlite_omit_load_extension
keyrace-server: $(wildcard *.go)
go mod vendor || true
go build -o $@ \
-tags "$(BUILDTAGS)" $?
server: keyrace-server keyrace-server-linux ## Build the server.
keyrace-server-linux: $(wildcard *.go)
# On a mac you need to `brew install sqlite`
echo "Using sqlite broke the static binary building on macos"
#go mod vendor
#GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build \
# -o $@ \
# -tags "$(BUILDTAGS)" \
# -installsuffix netgo -ldflags "-w -extldflags" $?
keyrace-mac:
set -eo pipefail
mkdir -p build/mac
xcodebuild \
-workspace mac/keyrace-mac.xcworkspace/ \
-scheme keyrace-mac \
-archivePath $(PWD)/build/keyrace.xcarchive \
clean archive
cp -r build/keyrace.xcarchive/Products/Applications/keyrace-mac.app build/mac
server-test: $(wildcard *.go)
@echo "Running the go tests..."
go mod vendor
go test $?
test: server-test ## Run the tests.
deploy: keyrace-server-linux ## Deploy the server binary.
scp keyrace-server-linux $(SERVER):
clean:
rm -rf keyrace.dSYM
.PHONY: help
help: ## Show this help.
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
================================================
FILE: README.md
================================================
# keyrace
Daily multiplayer keyboard races for macos
<img width="383" alt="image" src="https://user-images.githubusercontent.com/56260/108459319-47481a80-722b-11eb-89c5-7245d32aa64a.png">
## To install
```bash
git clone https://github.com/nat/keyrace.git
cd keyrace/mac
open keyrace-mac.xcworkspace
```
## To build and run
<kbd>Cmd (⌘)</kbd> + <kbd>R</kbd> or `Product` > `Run` in Xcode!
**NOTE:** If the `Run` option is grayed out in the menu, make sure that the correct scheme is selected: `Product` > `Scheme` > `keyrace-mac`.
================================================
FILE: go.mod
================================================
module github.com/nat/keyrace
go 1.14
require (
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect
github.com/improbable-eng/go-httpwares v0.0.0-20200609095714-edc8019f93cc
github.com/karalabe/xgo v0.0.0-20191115072854-c5ccff8648a7 // indirect
github.com/mattn/go-sqlite3 v1.14.6
github.com/sirupsen/logrus v1.7.0
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
)
================================================
FILE: go.sum
================================================
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/TwinProduction/go-away v1.0.0 h1:C+l/Q6eC/epJu26V17zXgi3SDxrkGdSUq3kAyJHOlVU=
github.com/TwinProduction/go-away v1.0.0/go.mod h1:VB/lNzhkzh7Xw2QgU+tYBjMheldukJaIJzVaIx2rh30=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
github.com/improbable-eng/go-httpwares v0.0.0-20200609095714-edc8019f93cc h1:jPofYCdWojUaUhjlAe5yM/H4PFDfrZ6ldrlqoVv5YDM=
github.com/improbable-eng/go-httpwares v0.0.0-20200609095714-edc8019f93cc/go.mod h1:LE9Hs6fsYQ7RoDuFUQlYmlRAku9vUlSlO++jWNj+D0I=
github.com/karalabe/xgo v0.0.0-20191115072854-c5ccff8648a7 h1:AYzjK/SHz6m6mg5iuFwkrAhCc14jvCpW9d6frC9iDPE=
github.com/karalabe/xgo v0.0.0-20191115072854-c5ccff8648a7/go.mod h1:iYGcTYIPUvEWhFo6aKUuLchs+AV4ssYdyuBbQJZGcBk=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215 h1:0Uz5jLJQioKgVozXa1gzGbzYxbb/rhQEVvSWxzw5oUs=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
================================================
FILE: keyrace.c
================================================
// NOTE: This client no longer works. Run the native mac client instead!
//
// gcc keyrace.c -o keyrace -framework ApplicationServices -framework Carbon -Wall -g
//
// To start tracking, run as: keyrace <username> <team>
// To print leaderboard, run: keyrace <team>
//
// Just use "default" as your team if you don't have one.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h>
#include <string.h>
#include <sys/stat.h>
const char KEYRACE_HOST[] = "159.89.136.69";
const char TMPFILE[] = "/tmp/keyrace.tmp";
char *username;
char *team;
int keycount = 0;
int last_day = -1;
int last_min = -1;
void update_savefile(int kc)
{
FILE *f;
f = fopen(TMPFILE, "w");
fprintf(f, "%d\n", kc);
fclose(f);
}
int load_savefile(void)
{
struct stat stat_buffer;
int saved_keycount;
FILE *f;
f = fopen(TMPFILE, "r");
if (f == NULL)
return 0;
fscanf(f, "%d", &saved_keycount);
if (stat(TMPFILE, &stat_buffer) != 0)
return 0;
struct tm mtime = *localtime(&stat_buffer.st_mtime);
last_day = mtime.tm_yday;
fclose(f);
return saved_keycount;
}
void upload_count(char *name, int count)
{
char s[1024];
snprintf(s, 1024, "curl \"http://%s/count?team=%s&name=%s&count=%d\" 2> /dev/null > /dev/null", KEYRACE_HOST, team, name, count);
system(s);
}
// invoked on every keypress
CGEventRef CGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
{
time_t t = time(NULL);
if (type != kCGEventKeyDown && type != kCGEventFlagsChanged && type != kCGEventKeyUp)
{
return event;
}
struct tm tm = *localtime(&t);
// Reset to 0 at midnight
if (last_day != tm.tm_yday)
{
last_day = tm.tm_yday;
keycount = 0;
}
keycount++;
// Upload every minute
if (last_min != tm.tm_min)
{
last_min = tm.tm_min;
upload_count(username, keycount);
}
update_savefile(keycount);
return event;
}
void strclean(char *src)
{
char *p = src;
while (*src)
{
if (isalnum(*src))
*p++ = *src;
src++;
}
*p = '\0';
}
void run_loop(void)
{
// Create an event tap to retrieve keypresses.
CGEventMask eventMask = (CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventFlagsChanged));
CFMachPortRef eventTap = CGEventTapCreate(
kCGSessionEventTap, kCGHeadInsertEventTap, 0, eventMask, CGEventCallback, NULL);
// Exit the program if unable to create the event tap.
if (!eventTap)
{
fprintf(stderr, "ERROR: Unable to create event tap.\n");
exit(1);
}
// Create a run loop source and add enable the event tap.
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRunLoopRun();
}
int main(int argc, char **argv)
{
if (argc < 2 || argc > 3 || !strcmp (argv[1], "--help"))
{
printf("Try: \n");
printf(" %s <team> <username> -- to start logging\n", argv[0]);
printf(" %s <team> -- to get the tracking\n", argv[0]);
printf("Just use \"default\" as your team if you don't have one.\n");
return 1;
}
if (argc == 2)
{
char cmd[1024];
snprintf(cmd, 1024, "curl http://%s/?team=%s", KEYRACE_HOST, argv[1]);
system(cmd);
return 0;
}
username = argv[2];
team = argv[1];
strclean(username);
strclean(team);
keycount = load_savefile();
printf("Starting counting keystrokes in the background.\n");
int pid = fork();
if (pid == 0)
{ // child
setpgid(0, 0);
run_loop();
}
}
================================================
FILE: mac/Podfile
================================================
use_frameworks!
inhibit_all_warnings!
target 'keyrace-mac' do
pod 'Charts'
end
================================================
FILE: mac/Pods/Charts/LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2016-2019 Daniel Cohen Gindi & Philipp Jahoda
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: mac/Pods/Charts/README.md
================================================
**Version 3.5.0**, synced to [MPAndroidChart #f6a398b](https://github.com/PhilJay/MPAndroidChart/commit/f6a398b)

 [](https://github.com/danielgindi/Charts/releases) [](http://cocoapods.org/pods/charts) [](https://github.com/Carthage/Carthage) [](https://travis-ci.org/danielgindi/Charts) [](https://codecov.io/gh/danielgindi/Charts)
[](https://gitter.im/danielgindi/Charts?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
### Just a heads up: Charts 3.0 has some breaking changes. Please read [the release/migration notes](https://github.com/danielgindi/Charts/releases/tag/v3.0.0).
### Another heads up: ChartsRealm is now in a [separate repo](https://github.com/danielgindi/ChartsRealm). Pods is also now `Charts` and `ChartsRealm`, instead of ~`Charts/Core`~ and ~`Charts/Realm`~
### One more heads up: As Swift evolves, if you are not using the latest Swift compiler, you shouldn't check out the master branch. Instead, you should go to the release page and pick up whatever suits you.
* Xcode 11 / Swift 5 (master branch)
* iOS >= 8.0 (Use as an **Embedded** Framework)
* tvOS >= 9.0
* macOS >= 10.11
Okay so there's this beautiful library called [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) by [Philipp Jahoda](https://www.linkedin.com/in/philippjahoda) which has become very popular amongst Android developers, but there was no decent solution to create charts for iOS.
I've chosen to write it in `Swift` as it can be highly optimized by the compiler, and can be used in both `Swift` and `ObjC` project. The demo project is written in `ObjC` to demonstrate how it works.
**An amazing feature** of this library now, for Android, iOS, tvOS and macOS, is the time it saves you when developing for both platforms, as the learning curve is singleton- it happens only once, and the code stays very similar so developers don't have to go around and re-invent the app to produce the same output with a different library. (And that's not even considering the fact that there's not really another good choice out there currently...)
## Having trouble running the demo?
* `ChartsDemo/ChartsDemo.xcodeproj` is the demo project for iOS/tvOS
* `ChartsDemo-OSX/ChartsDemo-OSX.xcodeproj` is the demo project for macOS
* Make sure you are running a supported version of Xcode.
* Usually it is specified here a few lines above.
* In most cases it will be the latest Xcode version.
* Make sure that your project supports Swift 5.0
* Optional: Run `carthage checkout` in the project folder, to fetch dependencies (i.e testing dependencies).
* If you don't have Carthage - you can get it [here](https://github.com/Carthage/Carthage/releases).
## Usage
In order to correctly compile:
1. Drag the `Charts.xcodeproj` to your project
2. Go to your target's settings, hit the "+" under the "Embedded Binaries" section, and select the Charts.framework
3. `@import Charts`
4. When using Swift in an ObjC project:
- You need to import your Bridging Header. Usually it is "*YourProject-Swift.h*", so in ChartsDemo it's "*ChartsDemo-Swift.h*". Do not try to actually include "*ChartsDemo-Swift.h*" in your project :-)
- (Xcode 8.1 and earlier) Under "Build Options", mark "Embedded Content Contains Swift Code"
- (Xcode 8.2+) Under "Build Options", mark "Always Embed Swift Standard Libraries"
5. When using [Realm.io](https://realm.io/):
- Note that the Realm framework is not linked with Charts - it is only there for *optional* bindings. Which means that you need to have the framework in your project, and in a compatible version to whatever is compiled with Charts. We will do our best to always compile against the latest version.
- You'll need to add `ChartsRealm` as a dependency too.
## 3rd party tutorials
#### Video tutorials
* [Chart in Swift - Setting Up a Basic Line Chart Using iOS Charts(Alex Nagy)](https://www.youtube.com/watch?v=mWhwe_tLNE8&list=PL_csAAO9PQ8bjzg-wxEff1Fr0Y5W1hrum&index=5)
#### Blog posts
* [Using Realm and Charts with Swift 3 in iOS 10 (Sami Korpela)](https://medium.com/@skoli/using-realm-and-charts-with-swift-3-in-ios-10-40c42e3838c0#.2gyymwfh8)
* [Creating a Line Chart in Swift 3 and iOS 10 (Osian Smith)](https://medium.com/@OsianSmith/creating-a-line-chart-in-swift-3-and-ios-10-2f647c95392e)
* [Beginning Set-up and Example Using Charts with Swift 3](https://github.com/annalizhaz/ChartsForSwiftBasic)
* [Creating a Radar Chart in Swift (David Piper)](https://medium.com/@HeyDaveTheDev/creating-a-radar-chart-in-swift-5791afcf92f0)
* [Plotting in IOS using Charts framework with SwiftUI (Evgeny Basisty)](https://medium.com/@zzzzbh/plotting-in-ios-using-charts-framework-with-swiftui-222034a2bea6)
Want your tutorial to show here? Create a PR!
## Troubleshooting
#### Can't compile?
* Please note the difference between installing a compiled framework from CocoaPods or Carthage, and copying the source code.
* Please read the **Usage** section again.
* Search in the issues
* Try to politely ask in the issues section
#### Other problems / feature requests
* Search in the issues
* Try to politely ask in the issues section
## CocoaPods Install
Add `pod 'Charts'` to your Podfile. "Charts" is the name of the library.
For [Realm](https://realm.io/) support, please add `pod 'ChartsRealm'` too.
**Note:** ~~`pod 'ios-charts'`~~ is not the correct library, and refers to a different project by someone else.
## Carthage Install
Charts now include Carthage prebuilt binaries.
```carthage
github "danielgindi/Charts" == 3.5.0
github "danielgindi/Charts" ~> 3.5.0
```
In order to build the binaries for a new release, use `carthage build --no-skip-current && carthage archive Charts`.
## 3rd party bindings
Xamarin (by @Flash3001): *iOS* - [GitHub](https://github.com/Flash3001/iOSCharts.Xamarin)/[NuGet](https://www.nuget.org/packages/iOSCharts/). *Android* - [GitHub](https://github.com/Flash3001/MPAndroidChart.Xamarin)/[NuGet](https://www.nuget.org/packages/MPAndroidChart/).
## Help
If you like what you see here, and want to support the work being done in this repository, you could:
* Contribute code, issues and pull requests
* Let people know this library exists (:fire: spread the word :fire:)
* [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=68UL6Y8KUPS96) (You can buy me a beer, or you can buy me dinner :-)
**Note:** The author of [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) is the reason that this library exists, and is accepting [donations](https://github.com/PhilJay/MPAndroidChart#donations) on his page. He deserves them!
Questions & Issues
-----
If you are having questions or problems, you should:
- Make sure you are using the latest version of the library. Check the [**release-section**](https://github.com/danielgindi/Charts/releases).
- Study the Android version's [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki)
- Study the (Still incomplete [](http://cocoadocs.org/docsets/Charts/)) [**Pod-Documentation**](http://cocoadocs.org/docsets/Charts/)
- Search or open questions on [**stackoverflow**](http://stackoverflow.com/questions/tagged/ios-charts) with the `ios-charts` tag
- Search [**known issues**](https://github.com/danielgindi/Charts/issues) for your problem (open and closed)
- Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues)
Features
=======
**Core features:**
- 8 different chart types
- Scaling on both axes (with touch-gesture, axes separately or pinch-zoom)
- Dragging / Panning (with touch-gesture)
- Combined-Charts (line-, bar-, scatter-, candle-stick-, bubble-)
- Dual (separate) Axes
- Customizable Axes (both x- and y-axis)
- Highlighting values (with customizable popup-views)
- Save chart to camera-roll / export to PNG/JPEG
- Predefined color templates
- Legends (generated automatically, customizable)
- Animations (build up animations, on both x- and y-axis)
- Limit lines (providing additional information, maximums, ...)
- Fully customizable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...)
- Plotting data directly from [**Realm.io**](https://realm.io) mobile database ([here](https://github.com/danielgindi/ChartsRealm))
**Chart types:**
*Screenshots are currently taken from the original repository, as they render exactly the same :-)*
- **LineChart (with legend, simple design)**

- **LineChart (with legend, simple design)**

- **LineChart (cubic lines)**

- **LineChart (gradient fill)**

- **Combined-Chart (bar- and linechart in this case)**

- **BarChart (with legend, simple design)**

- **BarChart (grouped DataSets)**

- **Horizontal-BarChart**

- **PieChart (with selection, ...)**

- **ScatterChart** (with squares, triangles, circles, ... and more)

- **CandleStickChart** (for financial data)

- **BubbleChart** (area covered by bubbles indicates the value)

- **RadarChart** (spider web chart)

Documentation
=======
Currently there's no need for documentation for the iOS/tvOS/macOS version, as the API is **95% the same** as on Android.
You can read the official [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) documentation here: [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki)
Or you can see the Charts Demo project in both Objective-C and Swift ([**ChartsDemo-iOS**](https://github.com/danielgindi/Charts/tree/master/ChartsDemo-iOS), as well as macOS [**ChartsDemo-macOS**](https://github.com/danielgindi/Charts/tree/master/ChartsDemo-macOS)) and learn the how-tos from it.
Special Thanks
=======
Goes to [@liuxuan30](https://github.com/liuxuan30), [@petester42](https://github.com/petester42) and [@AlBirdie](https://github.com/AlBirdie) for new features, bugfixes, and lots and lots of involvement in our open-sourced community! You guys are a huge help to all of those coming here with questions and issues, and I couldn't respond to all of those without you.
### Our amazing sponsors
[Debricked](https://debricked.com/): Use open source securely
[](https://debricked.com/)
License
=======
Copyright 2016 Daniel Cohen Gindi & Philipp Jahoda
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: mac/Pods/Charts/Source/Charts/Animation/Animator.swift
================================================
//
// Animator.swift
// Charts
//
// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
import Foundation
import CoreGraphics
import QuartzCore
@objc(ChartAnimatorDelegate)
public protocol AnimatorDelegate
{
/// Called when the Animator has stepped.
func animatorUpdated(_ animator: Animator)
/// Called when the Animator has stopped.
func animatorStopped(_ animator: Animator)
}
@objc(ChartAnimator)
open class Animator: NSObject
{
@objc open weak var delegate: AnimatorDelegate?
@objc open var updateBlock: (() -> Void)?
@objc open var stopBlock: (() -> Void)?
/// the phase that is animated and influences the drawn values on the x-axis
@objc open var phaseX: Double = 1.0
/// the phase that is animated and influences the drawn values on the y-axis
@objc open var phaseY: Double = 1.0
private var _startTimeX: TimeInterval = 0.0
private var _startTimeY: TimeInterval = 0.0
private var _displayLink: NSUIDisplayLink?
private var _durationX: TimeInterval = 0.0
private var _durationY: TimeInterval = 0.0
private var _endTimeX: TimeInterval = 0.0
private var _endTimeY: TimeInterval = 0.0
private var _endTime: TimeInterval = 0.0
private var _enabledX: Bool = false
private var _enabledY: Bool = false
private var _easingX: ChartEasingFunctionBlock?
private var _easingY: ChartEasingFunctionBlock?
public override init()
{
super.init()
}
deinit
{
stop()
}
@objc open func stop()
{
guard _displayLink != nil else { return }
_displayLink?.remove(from: .main, forMode: RunLoop.Mode.common)
_displayLink = nil
_enabledX = false
_enabledY = false
// If we stopped an animation in the middle, we do not want to leave it like this
if phaseX != 1.0 || phaseY != 1.0
{
phaseX = 1.0
phaseY = 1.0
delegate?.animatorUpdated(self)
updateBlock?()
}
delegate?.animatorStopped(self)
stopBlock?()
}
private func updateAnimationPhases(_ currentTime: TimeInterval)
{
if _enabledX
{
let elapsedTime: TimeInterval = currentTime - _startTimeX
let duration: TimeInterval = _durationX
var elapsed: TimeInterval = elapsedTime
if elapsed > duration
{
elapsed = duration
}
phaseX = _easingX?(elapsed, duration) ?? elapsed / duration
}
if _enabledY
{
let elapsedTime: TimeInterval = currentTime - _startTimeY
let duration: TimeInterval = _durationY
var elapsed: TimeInterval = elapsedTime
if elapsed > duration
{
elapsed = duration
}
phaseY = _easingY?(elapsed, duration) ?? elapsed / duration
}
}
@objc private func animationLoop()
{
let currentTime: TimeInterval = CACurrentMediaTime()
updateAnimationPhases(currentTime)
delegate?.animatorUpdated(self)
updateBlock?()
if currentTime >= _endTime
{
stop()
}
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
/// - easingX: an easing function for the animation on the x axis
/// - easingY: an easing function for the animation on the y axis
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingX: ChartEasingFunctionBlock?, easingY: ChartEasingFunctionBlock?)
{
stop()
_startTimeX = CACurrentMediaTime()
_startTimeY = _startTimeX
_durationX = xAxisDuration
_durationY = yAxisDuration
_endTimeX = _startTimeX + xAxisDuration
_endTimeY = _startTimeY + yAxisDuration
_endTime = _endTimeX > _endTimeY ? _endTimeX : _endTimeY
_enabledX = xAxisDuration > 0.0
_enabledY = yAxisDuration > 0.0
_easingX = easingX
_easingY = easingY
// Take care of the first frame if rendering is already scheduled...
updateAnimationPhases(_startTimeX)
if _enabledX || _enabledY
{
_displayLink = NSUIDisplayLink(target: self, selector: #selector(animationLoop))
_displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
}
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
/// - easingOptionX: the easing function for the animation on the x axis
/// - easingOptionY: the easing function for the animation on the y axis
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingOptionX: ChartEasingOption, easingOptionY: ChartEasingOption)
{
animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingX: easingFunctionFromOption(easingOptionX), easingY: easingFunctionFromOption(easingOptionY))
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
/// - easing: an easing function for the animation
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?)
{
animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingX: easing, easingY: easing)
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
/// - easingOption: the easing function for the animation
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingOption: ChartEasingOption = .easeInOutSine)
{
animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easing: easingFunctionFromOption(easingOption))
}
/// Animates the drawing / rendering of the chart the x-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - easing: an easing function for the animation
@objc open func animate(xAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?)
{
_startTimeX = CACurrentMediaTime()
_durationX = xAxisDuration
_endTimeX = _startTimeX + xAxisDuration
_endTime = _endTimeX > _endTimeY ? _endTimeX : _endTimeY
_enabledX = xAxisDuration > 0.0
_easingX = easing
// Take care of the first frame if rendering is already scheduled...
updateAnimationPhases(_startTimeX)
if _enabledX || _enabledY,
_displayLink == nil
{
_displayLink = NSUIDisplayLink(target: self, selector: #selector(animationLoop))
_displayLink?.add(to: .main, forMode: RunLoop.Mode.common)
}
}
/// Animates the drawing / rendering of the chart the x-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - easingOption: the easing function for the animation
@objc open func animate(xAxisDuration: TimeInterval, easingOption: ChartEasingOption = .easeInOutSine)
{
animate(xAxisDuration: xAxisDuration, easing: easingFunctionFromOption(easingOption))
}
/// Animates the drawing / rendering of the chart the y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - yAxisDuration: duration for animating the y axis
/// - easing: an easing function for the animation
@objc open func animate(yAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?)
{
_startTimeY = CACurrentMediaTime()
_durationY = yAxisDuration
_endTimeY = _startTimeY + yAxisDuration
_endTime = _endTimeX > _endTimeY ? _endTimeX : _endTimeY
_enabledY = yAxisDuration > 0.0
_easingY = easing
// Take care of the first frame if rendering is already scheduled...
updateAnimationPhases(_startTimeY)
if _enabledX || _enabledY,
_displayLink == nil
{
_displayLink = NSUIDisplayLink(target: self, selector: #selector(animationLoop))
_displayLink?.add(to: .main, forMode: RunLoop.Mode.common)
}
}
/// Animates the drawing / rendering of the chart the y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - yAxisDuration: duration for animating the y axis
/// - easingOption: the easing function for the animation
@objc open func animate(yAxisDuration: TimeInterval, easingOption: ChartEasingOption = .easeInOutSine)
{
animate(yAxisDuration: yAxisDuration, easing: easingFunctionFromOption(easingOption))
}
}
================================================
FILE: mac/Pods/Charts/Source/Charts/Animation/ChartAnimationEasing.swift
================================================
//
// ChartAnimationUtils.swift
// Charts
//
// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
import Foundation
import CoreGraphics
@objc
public enum ChartEasingOption: Int
{
case linear
case easeInQuad
case easeOutQuad
case easeInOutQuad
case easeInCubic
case easeOutCubic
case easeInOutCubic
case easeInQuart
case easeOutQuart
case easeInOutQuart
case easeInQuint
case easeOutQuint
case easeInOutQuint
case easeInSine
case easeOutSine
case easeInOutSine
case easeInExpo
case easeOutExpo
case easeInOutExpo
case easeInCirc
case easeOutCirc
case easeInOutCirc
case easeInElastic
case easeOutElastic
case easeInOutElastic
case easeInBack
case easeOutBack
case easeInOutBack
case easeInBounce
case easeOutBounce
case easeInOutBounce
}
public typealias ChartEasingFunctionBlock = ((_ elapsed: TimeInterval, _ duration: TimeInterval) -> Double)
internal func easingFunctionFromOption(_ easing: ChartEasingOption) -> ChartEasingFunctionBlock
{
switch easing
{
case .linear:
return EasingFunctions.Linear
case .easeInQuad:
return EasingFunctions.EaseInQuad
case .easeOutQuad:
return EasingFunctions.EaseOutQuad
case .easeInOutQuad:
return EasingFunctions.EaseInOutQuad
case .easeInCubic:
return EasingFunctions.EaseInCubic
case .easeOutCubic:
return EasingFunctions.EaseOutCubic
case .easeInOutCubic:
return EasingFunctions.EaseInOutCubic
case .easeInQuart:
return EasingFunctions.EaseInQuart
case .easeOutQuart:
return EasingFunctions.EaseOutQuart
case .easeInOutQuart:
return EasingFunctions.EaseInOutQuart
case .easeInQuint:
return EasingFunctions.EaseInQuint
case .easeOutQuint:
return EasingFunctions.EaseOutQuint
case .easeInOutQuint:
return EasingFunctions.EaseInOutQuint
case .easeInSine:
return EasingFunctions.EaseInSine
case .easeOutSine:
return EasingFunctions.EaseOutSine
case .easeInOutSine:
return EasingFunctions.EaseInOutSine
case .easeInExpo:
return EasingFunctions.EaseInExpo
case .easeOutExpo:
return EasingFunctions.EaseOutExpo
case .easeInOutExpo:
return EasingFunctions.EaseInOutExpo
case .easeInCirc:
return EasingFunctions.EaseInCirc
case .easeOutCirc:
return EasingFunctions.EaseOutCirc
case .easeInOutCirc:
return EasingFunctions.EaseInOutCirc
case .easeInElastic:
return EasingFunctions.EaseInElastic
case .easeOutElastic:
return EasingFunctions.EaseOutElastic
case .easeInOutElastic:
return EasingFunctions.EaseInOutElastic
case .easeInBack:
return EasingFunctions.EaseInBack
case .easeOutBack:
return EasingFunctions.EaseOutBack
case .easeInOutBack:
return EasingFunctions.EaseInOutBack
case .easeInBounce:
return EasingFunctions.EaseInBounce
case .easeOutBounce:
return EasingFunctions.EaseOutBounce
case .easeInOutBounce:
return EasingFunctions.EaseInOutBounce
}
}
internal struct EasingFunctions
{
internal static let Linear = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in return Double(elapsed / duration) }
internal static let EaseInQuad = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
return position * position
}
internal static let EaseOutQuad = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
return -position * (position - 2.0)
}
internal static let EaseInOutQuad = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / (duration / 2.0))
if position < 1.0
{
return 0.5 * position * position
}
return -0.5 * ((position - 1.0) * (position - 3.0) - 1.0)
}
internal static let EaseInCubic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
return position * position * position
}
internal static let EaseOutCubic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
position -= 1.0
return (position * position * position + 1.0)
}
internal static let EaseInOutCubic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / (duration / 2.0))
if position < 1.0
{
return 0.5 * position * position * position
}
position -= 2.0
return 0.5 * (position * position * position + 2.0)
}
internal static let EaseInQuart = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
return position * position * position * position
}
internal static let EaseOutQuart = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
position -= 1.0
return -(position * position * position * position - 1.0)
}
internal static let EaseInOutQuart = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / (duration / 2.0))
if position < 1.0
{
return 0.5 * position * position * position * position
}
position -= 2.0
return -0.5 * (position * position * position * position - 2.0)
}
internal static let EaseInQuint = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
return position * position * position * position * position
}
internal static let EaseOutQuint = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
position -= 1.0
return (position * position * position * position * position + 1.0)
}
internal static let EaseInOutQuint = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / (duration / 2.0))
if position < 1.0
{
return 0.5 * position * position * position * position * position
}
else
{
position -= 2.0
return 0.5 * (position * position * position * position * position + 2.0)
}
}
internal static let EaseInSine = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position: TimeInterval = elapsed / duration
return Double( -cos(position * Double.pi / 2) + 1.0 )
}
internal static let EaseOutSine = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position: TimeInterval = elapsed / duration
return Double( sin(position * Double.pi / 2) )
}
internal static let EaseInOutSine = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position: TimeInterval = elapsed / duration
return Double( -0.5 * (cos(Double.pi * position) - 1.0) )
}
internal static let EaseInExpo = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
return (elapsed == 0) ? 0.0 : Double(pow(2.0, 10.0 * (elapsed / duration - 1.0)))
}
internal static let EaseOutExpo = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
return (elapsed == duration) ? 1.0 : (-Double(pow(2.0, -10.0 * elapsed / duration)) + 1.0)
}
internal static let EaseInOutExpo = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
if elapsed == 0
{
return 0.0
}
if elapsed == duration
{
return 1.0
}
var position: TimeInterval = elapsed / (duration / 2.0)
if position < 1.0
{
return Double( 0.5 * pow(2.0, 10.0 * (position - 1.0)) )
}
position = position - 1.0
return Double( 0.5 * (-pow(2.0, -10.0 * position) + 2.0) )
}
internal static let EaseInCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
return -(Double(sqrt(1.0 - position * position)) - 1.0)
}
internal static let EaseOutCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position = Double(elapsed / duration)
position -= 1.0
return Double( sqrt(1 - position * position) )
}
internal static let EaseInOutCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position: TimeInterval = elapsed / (duration / 2.0)
if position < 1.0
{
return Double( -0.5 * (sqrt(1.0 - position * position) - 1.0) )
}
position -= 2.0
return Double( 0.5 * (sqrt(1.0 - position * position) + 1.0) )
}
internal static let EaseInElastic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
if elapsed == 0.0
{
return 0.0
}
var position: TimeInterval = elapsed / duration
if position == 1.0
{
return 1.0
}
var p = duration * 0.3
var s = p / (2.0 * Double.pi) * asin(1.0)
position -= 1.0
return Double( -(pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p)) )
}
internal static let EaseOutElastic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
if elapsed == 0.0
{
return 0.0
}
var position: TimeInterval = elapsed / duration
if position == 1.0
{
return 1.0
}
var p = duration * 0.3
var s = p / (2.0 * Double.pi) * asin(1.0)
return Double( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p) + 1.0 )
}
internal static let EaseInOutElastic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
if elapsed == 0.0
{
return 0.0
}
var position: TimeInterval = elapsed / (duration / 2.0)
if position == 2.0
{
return 1.0
}
var p = duration * (0.3 * 1.5)
var s = p / (2.0 * Double.pi) * asin(1.0)
if position < 1.0
{
position -= 1.0
return Double( -0.5 * (pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p)) )
}
position -= 1.0
return Double( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p) * 0.5 + 1.0 )
}
internal static let EaseInBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
let s: TimeInterval = 1.70158
var position: TimeInterval = elapsed / duration
return Double( position * position * ((s + 1.0) * position - s) )
}
internal static let EaseOutBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
let s: TimeInterval = 1.70158
var position: TimeInterval = elapsed / duration
position -= 1.0
return Double( position * position * ((s + 1.0) * position + s) + 1.0 )
}
internal static let EaseInOutBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var s: TimeInterval = 1.70158
var position: TimeInterval = elapsed / (duration / 2.0)
if position < 1.0
{
s *= 1.525
return Double( 0.5 * (position * position * ((s + 1.0) * position - s)) )
}
s *= 1.525
position -= 2.0
return Double( 0.5 * (position * position * ((s + 1.0) * position + s) + 2.0) )
}
internal static let EaseInBounce = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
return 1.0 - EaseOutBounce(duration - elapsed, duration)
}
internal static let EaseOutBounce = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
var position: TimeInterval = elapsed / duration
if position < (1.0 / 2.75)
{
return Double( 7.5625 * position * position )
}
else if position < (2.0 / 2.75)
{
position -= (1.5 / 2.75)
return Double( 7.5625 * position * position + 0.75 )
}
else if position < (2.5 / 2.75)
{
position -= (2.25 / 2.75)
return Double( 7.5625 * position * position + 0.9375 )
}
else
{
position -= (2.625 / 2.75)
return Double( 7.5625 * position * position + 0.984375 )
}
}
internal static let EaseInOutBounce = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in
if elapsed < (duration / 2.0)
{
return EaseInBounce(elapsed * 2.0, duration) * 0.5
}
return EaseOutBounce(elapsed * 2.0 - duration, duration) * 0.5 + 0.5
}
}
================================================
FILE: mac/Pods/Charts/Source/Charts/Charts/BarChartView.swift
================================================
//
// BarChartView.swift
// Charts
//
// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
import Foundation
import CoreGraphics
/// Chart that draws bars.
open class BarChartView: BarLineChartViewBase, BarChartDataProvider
{
/// if set to true, all values are drawn above their bars, instead of below their top
private var _drawValueAboveBarEnabled = true
/// if set to true, a grey area is drawn behind each bar that indicates the maximum value
private var _drawBarShadowEnabled = false
internal override func initialize()
{
super.initialize()
renderer = BarChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler)
self.highlighter = BarHighlighter(chart: self)
self.xAxis.spaceMin = 0.5
self.xAxis.spaceMax = 0.5
}
internal override func calcMinMax()
{
guard let data = self.data as? BarChartData
else { return }
if fitBars
{
_xAxis.calculate(
min: data.xMin - data.barWidth / 2.0,
max: data.xMax + data.barWidth / 2.0)
}
else
{
_xAxis.calculate(min: data.xMin, max: data.xMax)
}
// calculate axis range (min / max) according to provided data
leftAxis.calculate(
min: data.getYMin(axis: .left),
max: data.getYMax(axis: .left))
rightAxis.calculate(
min: data.getYMin(axis: .right),
max: data.getYMax(axis: .right))
}
/// - Returns: The Highlight object (contains x-index and DataSet index) of the selected value at the given touch point inside the BarChart.
open override func getHighlightByTouchPoint(_ pt: CGPoint) -> Highlight?
{
if _data === nil
{
Swift.print("Can't select by touch. No data set.")
return nil
}
guard let h = self.highlighter?.getHighlight(x: pt.x, y: pt.y)
else { return nil }
if !isHighlightFullBarEnabled { return h }
// For isHighlightFullBarEnabled, remove stackIndex
return Highlight(
x: h.x, y: h.y,
xPx: h.xPx, yPx: h.yPx,
dataIndex: h.dataIndex,
dataSetIndex: h.dataSetIndex,
stackIndex: -1,
axis: h.axis)
}
/// - Returns: The bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be found in the charts data.
@objc open func getBarBounds(entry e: BarChartDataEntry) -> CGRect
{
guard let
data = _data as? BarChartData,
let set = data.getDataSetForEntry(e) as? IBarChartDataSet
else { return CGRect.null }
let y = e.y
let x = e.x
let barWidth = data.barWidth
let left = x - barWidth / 2.0
let right = x + barWidth / 2.0
let top = y >= 0.0 ? y : 0.0
let bottom = y <= 0.0 ? y : 0.0
var bounds = CGRect(x: left, y: top, width: right - left, height: bottom - top)
getTransformer(forAxis: set.axisDependency).rectValueToPixel(&bounds)
return bounds
}
/// Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries.
/// Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified by the parameters.
/// Calls `notifyDataSetChanged()` afterwards.
///
/// - Parameters:
/// - fromX: the starting point on the x-axis where the grouping should begin
/// - groupSpace: the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f
/// - barSpace: the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f
@objc open func groupBars(fromX: Double, groupSpace: Double, barSpace: Double)
{
guard let barData = self.barData
else
{
Swift.print("You need to set data for the chart before grouping bars.", terminator: "\n")
return
}
barData.groupBars(fromX: fromX, groupSpace: groupSpace, barSpace: barSpace)
notifyDataSetChanged()
}
/// Highlights the value at the given x-value in the given DataSet. Provide -1 as the dataSetIndex to undo all highlighting.
///
/// - Parameters:
/// - x:
/// - dataSetIndex:
/// - stackIndex: the index inside the stack - only relevant for stacked entries
@objc open func highlightValue(x: Double, dataSetIndex: Int, stackIndex: Int)
{
highlightValue(Highlight(x: x, dataSetIndex: dataSetIndex, stackIndex: stackIndex))
}
// MARK: Accessors
/// if set to true, all values are drawn above their bars, instead of below their top
@objc open var drawValueAboveBarEnabled: Bool
{
get { return _drawValueAboveBarEnabled }
set
{
_drawValueAboveBarEnabled = newValue
setNeedsDisplay()
}
}
/// if set to true, a grey area is drawn behind each bar that indicates the maximum value
@objc open var drawBarShadowEnabled: Bool
{
get { return _drawBarShadowEnabled }
set
{
_drawBarShadowEnabled = newValue
setNeedsDisplay()
}
}
/// Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be fully displayed.
/// **default**: false
@objc open var fitBars = false
/// Set this to `true` to make the highlight operation full-bar oriented, `false` to make it highlight single values (relevant only for stacked).
/// If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry was tapped.
@objc open var highlightFullBarEnabled: Bool = false
/// `true` the highlight is be full-bar oriented, `false` ifsingle-value
open var isHighlightFullBarEnabled: Bool { return highlightFullBarEnabled }
// MARK: - BarChartDataProvider
open var barData: BarChartData? { return _data as? BarChartData }
/// `true` if drawing values above bars is enabled, `false` ifnot
open var isDrawValueAboveBarEnabled: Bool { return drawValueAboveBarEnabled }
/// `true` if drawing shadows (maxvalue) for each bar is enabled, `false` ifnot
open var isDrawBarShadowEnabled: Bool { return drawBarShadowEnabled }
}
================================================
FILE: mac/Pods/Charts/Source/Charts/Charts/BarLineChartViewBase.swift
================================================
//
// BarLineChartViewBase.swift
// Charts
//
// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
import Foundation
import CoreGraphics
#if canImport(UIKit)
import UIKit
#endif
#if canImport(Cocoa)
import Cocoa
#endif
/// Base-class of LineChart, BarChart, ScatterChart and CandleStickChart.
open class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChartDataProvider, NSUIGestureRecognizerDelegate
{
/// the maximum number of entries to which values will be drawn
/// (entry numbers greater than this value will cause value-labels to disappear)
internal var _maxVisibleCount = 100
/// flag that indicates if auto scaling on the y axis is enabled
private var _autoScaleMinMaxEnabled = false
private var _pinchZoomEnabled = false
private var _doubleTapToZoomEnabled = true
private var _dragXEnabled = true
private var _dragYEnabled = true
private var _scaleXEnabled = true
private var _scaleYEnabled = true
/// the color for the background of the chart-drawing area (everything behind the grid lines).
@objc open var gridBackgroundColor = NSUIColor(red: 240/255.0, green: 240/255.0, blue: 240/255.0, alpha: 1.0)
@objc open var borderColor = NSUIColor.black
@objc open var borderLineWidth: CGFloat = 1.0
/// flag indicating if the grid background should be drawn or not
@objc open var drawGridBackgroundEnabled = false
/// When enabled, the borders rectangle will be rendered.
/// If this is enabled, there is no point drawing the axis-lines of x- and y-axis.
@objc open var drawBordersEnabled = false
/// When enabled, the values will be clipped to contentRect, otherwise they can bleed outside the content rect.
@objc open var clipValuesToContentEnabled: Bool = false
/// When disabled, the data and/or highlights will not be clipped to contentRect. Disabling this option can
/// be useful, when the data lies fully within the content rect, but is drawn in such a way (such as thick lines)
/// that there is unwanted clipping.
@objc open var clipDataToContentEnabled: Bool = true
/// Sets the minimum offset (padding) around the chart, defaults to 10
@objc open var minOffset = CGFloat(10.0)
/// Sets whether the chart should keep its position (zoom / scroll) after a rotation (orientation change)
/// **default**: false
@objc open var keepPositionOnRotation: Bool = false
/// The left y-axis object. In the horizontal bar-chart, this is the
/// top axis.
@objc open internal(set) var leftAxis = YAxis(position: .left)
/// The right y-axis object. In the horizontal bar-chart, this is the
/// bottom axis.
@objc open internal(set) var rightAxis = YAxis(position: .right)
/// The left Y axis renderer. This is a read-write property so you can set your own custom renderer here.
/// **default**: An instance of YAxisRenderer
@objc open lazy var leftYAxisRenderer = YAxisRenderer(viewPortHandler: _viewPortHandler, yAxis: leftAxis, transformer: _leftAxisTransformer)
/// The right Y axis renderer. This is a read-write property so you can set your own custom renderer here.
/// **default**: An instance of YAxisRenderer
@objc open lazy var rightYAxisRenderer = YAxisRenderer(viewPortHandler: _viewPortHandler, yAxis: rightAxis, transformer: _rightAxisTransformer)
internal var _leftAxisTransformer: Transformer!
internal var _rightAxisTransformer: Transformer!
/// The X axis renderer. This is a read-write property so you can set your own custom renderer here.
/// **default**: An instance of XAxisRenderer
@objc open lazy var xAxisRenderer = XAxisRenderer(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer)
internal var _tapGestureRecognizer: NSUITapGestureRecognizer!
internal var _doubleTapGestureRecognizer: NSUITapGestureRecognizer!
#if !os(tvOS)
internal var _pinchGestureRecognizer: NSUIPinchGestureRecognizer!
#endif
internal var _panGestureRecognizer: NSUIPanGestureRecognizer!
/// flag that indicates if a custom viewport offset has been set
private var _customViewPortEnabled = false
public override init(frame: CGRect)
{
super.init(frame: frame)
}
public required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
deinit
{
stopDeceleration()
}
internal override func initialize()
{
super.initialize()
_leftAxisTransformer = Transformer(viewPortHandler: _viewPortHandler)
_rightAxisTransformer = Transformer(viewPortHandler: _viewPortHandler)
self.highlighter = ChartHighlighter(chart: self)
_tapGestureRecognizer = NSUITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:)))
_doubleTapGestureRecognizer = NSUITapGestureRecognizer(target: self, action: #selector(doubleTapGestureRecognized(_:)))
_doubleTapGestureRecognizer.nsuiNumberOfTapsRequired = 2
_panGestureRecognizer = NSUIPanGestureRecognizer(target: self, action: #selector(panGestureRecognized(_:)))
_panGestureRecognizer.delegate = self
self.addGestureRecognizer(_tapGestureRecognizer)
self.addGestureRecognizer(_doubleTapGestureRecognizer)
self.addGestureRecognizer(_panGestureRecognizer)
_doubleTapGestureRecognizer.isEnabled = _doubleTapToZoomEnabled
_panGestureRecognizer.isEnabled = _dragXEnabled || _dragYEnabled
#if !os(tvOS)
_pinchGestureRecognizer = NSUIPinchGestureRecognizer(target: self, action: #selector(BarLineChartViewBase.pinchGestureRecognized(_:)))
_pinchGestureRecognizer.delegate = self
self.addGestureRecognizer(_pinchGestureRecognizer)
_pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled
#endif
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
// Saving current position of chart.
var oldPoint: CGPoint?
if (keepPositionOnRotation && (keyPath == "frame" || keyPath == "bounds"))
{
oldPoint = viewPortHandler.contentRect.origin
getTransformer(forAxis: .left).pixelToValues(&oldPoint!)
}
// Superclass transforms chart.
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
// Restoring old position of chart
if var newPoint = oldPoint , keepPositionOnRotation
{
getTransformer(forAxis: .left).pointValueToPixel(&newPoint)
viewPortHandler.centerViewPort(pt: newPoint, chart: self)
}
else
{
viewPortHandler.refresh(newMatrix: viewPortHandler.touchMatrix, chart: self, invalidate: true)
}
}
open override func draw(_ rect: CGRect)
{
super.draw(rect)
guard data != nil, let renderer = renderer else { return }
let optionalContext = NSUIGraphicsGetCurrentContext()
guard let context = optionalContext else { return }
// execute all drawing commands
drawGridBackground(context: context)
if _autoScaleMinMaxEnabled
{
autoScale()
}
if leftAxis.isEnabled
{
leftYAxisRenderer.computeAxis(min: leftAxis._axisMinimum, max: leftAxis._axisMaximum, inverted: leftAxis.isInverted)
}
if rightAxis.isEnabled
{
rightYAxisRenderer.computeAxis(min: rightAxis._axisMinimum, max: rightAxis._axisMaximum, inverted: rightAxis.isInverted)
}
if _xAxis.isEnabled
{
xAxisRenderer.computeAxis(min: _xAxis._axisMinimum, max: _xAxis._axisMaximum, inverted: false)
}
xAxisRenderer.renderAxisLine(context: context)
leftYAxisRenderer.renderAxisLine(context: context)
rightYAxisRenderer.renderAxisLine(context: context)
// The renderers are responsible for clipping, to account for line-width center etc.
if xAxis.drawGridLinesBehindDataEnabled
{
xAxisRenderer.renderGridLines(context: context)
leftYAxisRenderer.renderGridLines(context: context)
rightYAxisRenderer.renderGridLines(context: context)
}
if _xAxis.isEnabled && _xAxis.isDrawLimitLinesBehindDataEnabled
{
xAxisRenderer.renderLimitLines(context: context)
}
if leftAxis.isEnabled && leftAxis.isDrawLimitLinesBehindDataEnabled
{
leftYAxisRenderer.renderLimitLines(context: context)
}
if rightAxis.isEnabled && rightAxis.isDrawLimitLinesBehindDataEnabled
{
rightYAxisRenderer.renderLimitLines(context: context)
}
context.saveGState()
// make sure the data cannot be drawn outside the content-rect
if clipDataToContentEnabled {
context.clip(to: _viewPortHandler.contentRect)
}
renderer.drawData(context: context)
// The renderers are responsible for clipping, to account for line-width center etc.
if !xAxis.drawGridLinesBehindDataEnabled
{
xAxisRenderer.renderGridLines(context: context)
leftYAxisRenderer.renderGridLines(context: context)
rightYAxisRenderer.renderGridLines(context: context)
}
// if highlighting is enabled
if (valuesToHighlight())
{
renderer.drawHighlighted(context: context, indices: _indicesToHighlight)
}
context.restoreGState()
renderer.drawExtras(context: context)
if _xAxis.isEnabled && !_xAxis.isDrawLimitLinesBehindDataEnabled
{
xAxisRenderer.renderLimitLines(context: context)
}
if leftAxis.isEnabled && !leftAxis.isDrawLimitLinesBehindDataEnabled
{
leftYAxisRenderer.renderLimitLines(context: context)
}
if rightAxis.isEnabled && !rightAxis.isDrawLimitLinesBehindDataEnabled
{
rightYAxisRenderer.renderLimitLines(context: context)
}
xAxisRenderer.renderAxisLabels(context: context)
leftYAxisRenderer.renderAxisLabels(context: context)
rightYAxisRenderer.renderAxisLabels(context: context)
if clipValuesToContentEnabled
{
context.saveGState()
context.clip(to: _viewPortHandler.contentRect)
renderer.drawValues(context: context)
context.restoreGState()
}
else
{
renderer.drawValues(context: context)
}
_legendRenderer.renderLegend(context: context)
drawDescription(context: context)
drawMarkers(context: context)
}
private var _autoScaleLastLowestVisibleX: Double?
private var _autoScaleLastHighestVisibleX: Double?
/// Performs auto scaling of the axis by recalculating the minimum and maximum y-values based on the entries currently in view.
internal func autoScale()
{
guard let data = _data
else { return }
data.calcMinMaxY(fromX: self.lowestVisibleX, toX: self.highestVisibleX)
_xAxis.calculate(min: data.xMin, max: data.xMax)
// calculate axis range (min / max) according to provided data
if leftAxis.isEnabled
{
leftAxis.calculate(min: data.getYMin(axis: .left), max: data.getYMax(axis: .left))
}
if rightAxis.isEnabled
{
rightAxis.calculate(min: data.getYMin(axis: .right), max: data.getYMax(axis: .right))
}
calculateOffsets()
}
internal func prepareValuePxMatrix()
{
_rightAxisTransformer.prepareMatrixValuePx(chartXMin: _xAxis._axisMinimum, deltaX: CGFloat(xAxis.axisRange), deltaY: CGFloat(rightAxis.axisRange), chartYMin: rightAxis._axisMinimum)
_leftAxisTransformer.prepareMatrixValuePx(chartXMin: xAxis._axisMinimum, deltaX: CGFloat(xAxis.axisRange), deltaY: CGFloat(leftAxis.axisRange), chartYMin: leftAxis._axisMinimum)
}
internal func prepareOffsetMatrix()
{
_rightAxisTransformer.prepareMatrixOffset(inverted: rightAxis.isInverted)
_leftAxisTransformer.prepareMatrixOffset(inverted: leftAxis.isInverted)
}
open override func notifyDataSetChanged()
{
renderer?.initBuffers()
calcMinMax()
leftYAxisRenderer.computeAxis(min: leftAxis._axisMinimum, max: leftAxis._axisMaximum, inverted: leftAxis.isInverted)
rightYAxisRenderer.computeAxis(min: rightAxis._axisMinimum, max: rightAxis._axisMaximum, inverted: rightAxis.isInverted)
if let data = _data
{
xAxisRenderer.computeAxis(
min: _xAxis._axisMinimum,
max: _xAxis._axisMaximum,
inverted: false)
if _legend !== nil
{
legendRenderer?.computeLegend(data: data)
}
}
calculateOffsets()
setNeedsDisplay()
}
internal override func calcMinMax()
{
// calculate / set x-axis range
_xAxis.calculate(min: _data?.xMin ?? 0.0, max: _data?.xMax ?? 0.0)
// calculate axis range (min / max) according to provided data
leftAxis.calculate(min: _data?.getYMin(axis: .left) ?? 0.0, max: _data?.getYMax(axis: .left) ?? 0.0)
rightAxis.calculate(min: _data?.getYMin(axis: .right) ?? 0.0, max: _data?.getYMax(axis: .right) ?? 0.0)
}
internal func calculateLegendOffsets(offsetLeft: inout CGFloat, offsetTop: inout CGFloat, offsetRight: inout CGFloat, offsetBottom: inout CGFloat)
{
// setup offsets for legend
if _legend !== nil && _legend.isEnabled && !_legend.drawInside
{
switch _legend.orientation
{
case .vertical:
switch _legend.horizontalAlignment
{
case .left:
offsetLeft += min(_legend.neededWidth, _viewPortHandler.chartWidth * _legend.maxSizePercent) + _legend.xOffset
case .right:
offsetRight += min(_legend.neededWidth, _viewPortHandler.chartWidth * _legend.maxSizePercent) + _legend.xOffset
case .center:
switch _legend.verticalAlignment
{
case .top:
offsetTop += min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) + _legend.yOffset
case .bottom:
offsetBottom += min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) + _legend.yOffset
default:
break
}
}
case .horizontal:
switch _legend.verticalAlignment
{
case .top:
offsetTop += min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) + _legend.yOffset
case .bottom:
offsetBottom += min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) + _legend.yOffset
default:
break
}
}
}
}
internal override func calculateOffsets()
{
if !_customViewPortEnabled
{
var offsetLeft = CGFloat(0.0)
var offsetRight = CGFloat(0.0)
var offsetTop = CGFloat(0.0)
var offsetBottom = CGFloat(0.0)
calculateLegendOffsets(offsetLeft: &offsetLeft,
offsetTop: &offsetTop,
offsetRight: &offsetRight,
offsetBottom: &offsetBottom)
// offsets for y-labels
if leftAxis.needsOffset
{
offsetLeft += leftAxis.requiredSize().width
}
if rightAxis.needsOffset
{
offsetRight += rightAxis.requiredSize().width
}
if xAxis.isEnabled && xAxis.isDrawLabelsEnabled
{
let xlabelheight = xAxis.labelRotatedHeight + xAxis.yOffset
// offsets for x-labels
if xAxis.labelPosition == .bottom
{
offsetBottom += xlabelheight
}
else if xAxis.labelPosition == .top
{
offsetTop += xlabelheight
}
else if xAxis.labelPosition == .bothSided
{
offsetBottom += xlabelheight
offsetTop += xlabelheight
}
}
offsetTop += self.extraTopOffset
offsetRight += self.extraRightOffset
offsetBottom += self.extraBottomOffset
offsetLeft += self.extraLeftOffset
_viewPortHandler.restrainViewPort(
offsetLeft: max(self.minOffset, offsetLeft),
offsetTop: max(self.minOffset, offsetTop),
offsetRight: max(self.minOffset, offsetRight),
offsetBottom: max(self.minOffset, offsetBottom))
}
prepareOffsetMatrix()
prepareValuePxMatrix()
}
/// draws the grid background
internal func drawGridBackground(context: CGContext)
{
if drawGridBackgroundEnabled || drawBordersEnabled
{
context.saveGState()
}
if drawGridBackgroundEnabled
{
// draw the grid background
context.setFillColor(gridBackgroundColor.cgColor)
context.fill(_viewPortHandler.contentRect)
}
if drawBordersEnabled
{
context.setLineWidth(borderLineWidth)
context.setStrokeColor(borderColor.cgColor)
context.stroke(_viewPortHandler.contentRect)
}
if drawGridBackgroundEnabled || drawBordersEnabled
{
context.restoreGState()
}
}
// MARK: - Gestures
private enum GestureScaleAxis
{
case both
case x
case y
}
private var _isDragging = false
private var _isScaling = false
private var _gestureScaleAxis = GestureScaleAxis.both
private var _closestDataSetToTouch: IChartDataSet!
private var _panGestureReachedEdge: Bool = false
private weak var _outerScrollView: NSUIScrollView?
private var _lastPanPoint = CGPoint() /// This is to prevent using setTranslation which resets velocity
private var _decelerationLastTime: TimeInterval = 0.0
private var _decelerationDisplayLink: NSUIDisplayLink!
private var _decelerationVelocity = CGPoint()
@objc private func tapGestureRecognized(_ recognizer: NSUITapGestureRecognizer)
{
if _data === nil
{
return
}
if recognizer.state == NSUIGestureRecognizerState.ended
{
if !isHighLightPerTapEnabled { return }
let h = getHighlightByTouchPoint(recognizer.location(in: self))
if h === nil || h == self.lastHighlighted
{
lastHighlighted = nil
highlightValue(nil, callDelegate: true)
}
else
{
lastHighlighted = h
highlightValue(h, callDelegate: true)
}
}
}
@objc private func doubleTapGestureRecognized(_ recognizer: NSUITapGestureRecognizer)
{
if _data === nil
{
return
}
if recognizer.state == NSUIGestureRecognizerState.ended
{
if _data !== nil && _doubleTapToZoomEnabled && (data?.entryCount ?? 0) > 0
{
var location = recognizer.location(in: self)
location.x = location.x - _viewPortHandler.offsetLeft
if isTouchInverted()
{
location.y = -(location.y - _viewPortHandler.offsetTop)
}
else
{
location.y = -(self.bounds.size.height - location.y - _viewPortHandler.offsetBottom)
}
let scaleX: CGFloat = isScaleXEnabled ? 1.4 : 1.0
let scaleY: CGFloat = isScaleYEnabled ? 1.4 : 1.0
self.zoom(scaleX: scaleX, scaleY: scaleY, x: location.x, y: location.y)
delegate?.chartScaled?(self, scaleX: scaleX, scaleY: scaleY)
}
}
}
#if !os(tvOS)
@objc private func pinchGestureRecognized(_ recognizer: NSUIPinchGestureRecognizer)
{
if recognizer.state == NSUIGestureRecognizerState.began
{
stopDeceleration()
if _data !== nil &&
(_pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled)
{
_isScaling = true
if _pinchZoomEnabled
{
_gestureScaleAxis = .both
}
else
{
let x = abs(recognizer.location(in: self).x - recognizer.nsuiLocationOfTouch(1, inView: self).x)
let y = abs(recognizer.location(in: self).y - recognizer.nsuiLocationOfTouch(1, inView: self).y)
if _scaleXEnabled != _scaleYEnabled
{
_gestureScaleAxis = _scaleXEnabled ? .x : .y
}
else
{
_gestureScaleAxis = x > y ? .x : .y
}
}
}
}
else if recognizer.state == NSUIGestureRecognizerState.ended ||
recognizer.state == NSUIGestureRecognizerState.cancelled
{
if _isScaling
{
_isScaling = false
// Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets.
calculateOffsets()
setNeedsDisplay()
}
}
else if recognizer.state == NSUIGestureRecognizerState.changed
{
let isZoomingOut = (recognizer.nsuiScale < 1)
var canZoomMoreX = isZoomingOut ? _viewPortHandler.canZoomOutMoreX : _viewPortHandler.canZoomInMoreX
var canZoomMoreY = isZoomingOut ? _viewPortHandler.canZoomOutMoreY : _viewPortHandler.canZoomInMoreY
if _isScaling
{
canZoomMoreX = canZoomMoreX && _scaleXEnabled && (_gestureScaleAxis == .both || _gestureScaleAxis == .x)
canZoomMoreY = canZoomMoreY && _scaleYEnabled && (_gestureScaleAxis == .both || _gestureScaleAxis == .y)
if canZoomMoreX || canZoomMoreY
{
var location = recognizer.location(in: self)
location.x = location.x - _viewPortHandler.offsetLeft
if isTouchInverted()
{
location.y = -(location.y - _viewPortHandler.offsetTop)
}
else
{
location.y = -(_viewPortHandler.chartHeight - location.y - _viewPortHandler.offsetBottom)
}
let scaleX = canZoomMoreX ? recognizer.nsuiScale : 1.0
let scaleY = canZoomMoreY ? recognizer.nsuiScale : 1.0
var matrix = CGAffineTransform(translationX: location.x, y: location.y)
matrix = matrix.scaledBy(x: scaleX, y: scaleY)
matrix = matrix.translatedBy(x: -location.x, y: -location.y)
matrix = _viewPortHandler.touchMatrix.concatenating(matrix)
_viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true)
if delegate !== nil
{
delegate?.chartScaled?(self, scaleX: scaleX, scaleY: scaleY)
}
}
recognizer.nsuiScale = 1.0
}
}
}
#endif
@objc private func panGestureRecognized(_ recognizer: NSUIPanGestureRecognizer)
{
if recognizer.state == NSUIGestureRecognizerState.began && recognizer.nsuiNumberOfTouches() > 0
{
stopDeceleration()
if _data === nil || !self.isDragEnabled
{ // If we have no data, we have nothing to pan and no data to highlight
return
}
// If drag is enabled and we are in a position where there's something to drag:
// * If we're zoomed in, then obviously we have something to drag.
// * If we have a drag offset - we always have something to drag
if !self.hasNoDragOffset || !self.isFullyZoomedOut
{
_isDragging = true
_closestDataSetToTouch = getDataSetByTouchPoint(point: recognizer.nsuiLocationOfTouch(0, inView: self))
var translation = recognizer.translation(in: self)
if !self.dragXEnabled
{
translation.x = 0.0
}
else if !self.dragYEnabled
{
translation.y = 0.0
}
let didUserDrag = translation.x != 0.0 || translation.y != 0.0
// Check to see if user dragged at all and if so, can the chart be dragged by the given amount
if didUserDrag && !performPanChange(translation: translation)
{
if _outerScrollView !== nil
{
// We can stop dragging right now, and let the scroll view take control
_outerScrollView = nil
_isDragging = false
}
}
else
{
if _outerScrollView !== nil
{
// Prevent the parent scroll view from scrolling
_outerScrollView?.nsuiIsScrollEnabled = false
}
}
_lastPanPoint = recognizer.translation(in: self)
}
else if self.isHighlightPerDragEnabled
{
// We will only handle highlights on NSUIGestureRecognizerState.Changed
_isDragging = false
}
}
else if recognizer.state == NSUIGestureRecognizerState.changed
{
if _isDragging
{
let originalTranslation = recognizer.translation(in: self)
var translation = CGPoint(x: originalTranslation.x - _lastPanPoint.x, y: originalTranslation.y - _lastPanPoint.y)
if !self.dragXEnabled
{
translation.x = 0.0
}
else if !self.dragYEnabled
{
translation.y = 0.0
}
let _ = performPanChange(translation: translation)
_lastPanPoint = originalTranslation
}
else if isHighlightPerDragEnabled
{
let h = getHighlightByTouchPoint(recognizer.location(in: self))
let lastHighlighted = self.lastHighlighted
if h != lastHighlighted
{
self.lastHighlighted = h
self.highlightValue(h, callDelegate: true)
}
}
}
else if recognizer.state == NSUIGestureRecognizerState.ended || recognizer.state == NSUIGestureRecognizerState.cancelled
{
if _isDragging
{
if recognizer.state == NSUIGestureRecognizerState.ended && isDragDecelerationEnabled
{
stopDeceleration()
_decelerationLastTime = CACurrentMediaTime()
_decelerationVelocity = recognizer.velocity(in: self)
_decelerationDisplayLink = NSUIDisplayLink(target: self, selector: #selector(BarLineChartViewBase.decelerationLoop))
_decelerationDisplayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
}
_isDragging = false
delegate?.chartViewDidEndPanning?(self)
}
if _outerScrollView !== nil
{
_outerScrollView?.nsuiIsScrollEnabled = true
_outerScrollView = nil
}
}
}
private func performPanChange(translation: CGPoint) -> Bool
{
var translation = translation
if isTouchInverted()
{
if self is HorizontalBarChartView
{
translation.x = -translation.x
}
else
{
translation.y = -translation.y
}
}
let originalMatrix = _viewPortHandler.touchMatrix
var matrix = CGAffineTransform(translationX: translation.x, y: translation.y)
matrix = originalMatrix.concatenating(matrix)
matrix = _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true)
if matrix != originalMatrix
{
delegate?.chartTranslated?(self, dX: translation.x, dY: translation.y)
}
// Did we managed to actually drag or did we reach the edge?
return matrix.tx != originalMatrix.tx || matrix.ty != originalMatrix.ty
}
private func isTouchInverted() -> Bool
{
return isAnyAxisInverted &&
_closestDataSetToTouch !== nil &&
getAxis(_closestDataSetToTouch.axisDependency).isInverted
}
@objc open func stopDeceleration()
{
if _decelerationDisplayLink !== nil
{
_decelerationDisplayLink.remove(from: RunLoop.main, forMode: RunLoop.Mode.common)
_decelerationDisplayLink = nil
}
}
@objc private func decelerationLoop()
{
let currentTime = CACurrentMediaTime()
_decelerationVelocity.x *= self.dragDecelerationFrictionCoef
_decelerationVelocity.y *= self.dragDecelerationFrictionCoef
let timeInterval = CGFloat(currentTime - _decelerationLastTime)
let distance = CGPoint(
x: _decelerationVelocity.x * timeInterval,
y: _decelerationVelocity.y * timeInterval
)
if !performPanChange(translation: distance)
{
// We reached the edge, stop
_decelerationVelocity.x = 0.0
_decelerationVelocity.y = 0.0
}
_decelerationLastTime = currentTime
if abs(_decelerationVelocity.x) < 0.001 && abs(_decelerationVelocity.y) < 0.001
{
stopDeceleration()
// Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets.
calculateOffsets()
setNeedsDisplay()
}
}
private func nsuiGestureRecognizerShouldBegin(_ gestureRecognizer: NSUIGestureRecognizer) -> Bool
{
if gestureRecognizer == _panGestureRecognizer
{
let velocity = _panGestureRecognizer.velocity(in: self)
if _data === nil || !isDragEnabled ||
(self.hasNoDragOffset && self.isFullyZoomedOut && !self.isHighlightPerDragEnabled) ||
(!_dragYEnabled && abs(velocity.y) > abs(velocity.x)) ||
(!_dragXEnabled && abs(velocity.y) < abs(velocity.x))
{
return false
}
}
else
{
#if !os(tvOS)
if gestureRecognizer == _pinchGestureRecognizer
{
if _data === nil || (!_pinchZoomEnabled && !_scaleXEnabled && !_scaleYEnabled)
{
return false
}
}
#endif
}
return true
}
#if !os(OSX)
open override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool
{
if !super.gestureRecognizerShouldBegin(gestureRecognizer)
{
return false
}
return nsuiGestureRecognizerShouldBegin(gestureRecognizer)
}
#endif
#if os(OSX)
public func gestureRecognizerShouldBegin(gestureRecognizer: NSGestureRecognizer) -> Bool
{
return nsuiGestureRecognizerShouldBegin(gestureRecognizer)
}
#endif
open func gestureRecognizer(_ gestureRecognizer: NSUIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: NSUIGestureRecognizer) -> Bool
{
#if !os(tvOS)
if ((gestureRecognizer is NSUIPinchGestureRecognizer && otherGestureRecognizer is NSUIPanGestureRecognizer) ||
(gestureRecognizer is NSUIPanGestureRecognizer && otherGestureRecognizer is NSUIPinchGestureRecognizer))
{
return true
}
#endif
if gestureRecognizer is NSUIPanGestureRecognizer,
otherGestureRecognizer is NSUIPanGestureRecognizer,
gestureRecognizer == _panGestureRecognizer
{
var scrollView = self.superview
while scrollView != nil && !(scrollView is NSUIScrollView)
{
scrollView = scrollView?.superview
}
// If there is two scrollview together, we pick the superview of the inner scrollview.
// In the case of UITableViewWrepperView, the superview will be UITableView
if let superViewOfScrollView = scrollView?.superview,
superViewOfScrollView is NSUIScrollView
{
scrollView = superViewOfScrollView
}
var foundScrollView = scrollView as? NSUIScrollView
if !(foundScrollView?.nsuiIsScrollEnabled ?? true)
{
foundScrollView = nil
}
let scrollViewPanGestureRecognizer = foundScrollView?.nsuiGestureRecognizers?.first {
$0 is NSUIPanGestureRecognizer
}
if otherGestureRecognizer === scrollViewPanGestureRecognizer
{
_outerScrollView = foundScrollView
return true
}
}
return false
}
/// MARK: Viewport modifiers
/// Zooms in by 1.4, into the charts center.
@objc open func zoomIn()
{
let center = _viewPortHandler.contentCenter
let matrix = _viewPortHandler.zoomIn(x: center.x, y: -center.y)
_viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false)
// Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets.
calculateOffsets()
setNeedsDisplay()
}
/// Zooms out by 0.7, from the charts center.
@objc open func zoomOut()
{
let center = _viewPortHandler.contentCenter
let matrix = _viewPortHandler.zoomOut(x: center.x, y: -center.y)
_viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false)
// Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets.
calculateOffsets()
setNeedsDisplay()
}
/// Zooms out to original size.
@objc open func resetZoom()
{
let matrix = _viewPortHandler.resetZoom()
_viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false)
// Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets.
calculateOffsets()
setNeedsDisplay()
}
/// Zooms in or out by the given scale factor. x and y are the coordinates
/// (in pixels) of the zoom center.
///
/// - Parameters:
/// - scaleX: if < 1 --> zoom out, if > 1 --> zoom in
/// - scaleY: if < 1 --> zoom out, if > 1 --> zoom in
/// - x:
/// - y:
@objc open func zoom(
scaleX: CGFloat,
scaleY: CGFloat,
x: CGFloat,
y: CGFloat)
{
let matrix = _viewPortHandler.zoom(scaleX: scaleX, scaleY: scaleY, x: x, y: -y)
_viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false)
// Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets.
calculateOffsets()
setNeedsDisplay()
}
/// Zooms in or out by the given scale factor.
/// x and y are the values (**not pixels**) of the zoom center.
///
/// - Parameters:
/// - scaleX: if < 1 --> zoom out, if > 1 --> zoom in
/// - scaleY: if < 1 --> zoom out, if > 1 --> zoom in
/// - xValue:
/// - yValue:
/// - axis:
@objc open func zoom(
scaleX: CGFloat,
scaleY: CGFloat,
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency)
{
let job = ZoomViewJob(
viewPortHandler: viewPortHandler,
scaleX: scaleX,
scaleY: scaleY,
xValue: xValue,
yValue: yValue,
transformer: getTransformer(forAxis: axis),
axis: axis,
view: self)
addViewportJob(job)
}
/// Zooms to the center of the chart with the given scale factor.
///
/// - Parameters:
/// - scaleX: if < 1 --> zoom out, if > 1 --> zoom in
/// - scaleY: if < 1 --> zoom out, if > 1 --> zoom in
/// - xValue:
/// - yValue:
/// - axis:
@objc open func zoomToCenter(
scaleX: CGFloat,
scaleY: CGFloat)
{
let center = centerOffsets
let matrix = viewPortHandler.zoom(
scaleX: scaleX,
scaleY: scaleY,
x: center.x,
y: -center.y)
viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false)
}
/// Zooms by the specified scale factor to the specified values on the specified axis.
///
/// - Parameters:
/// - scaleX:
/// - scaleY:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func zoomAndCenterViewAnimated(
scaleX: CGFloat,
scaleY: CGFloat,
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval,
easing: ChartEasingFunctionBlock?)
{
let origin = valueForTouchPoint(
point: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop),
axis: axis)
let job = AnimatedZoomViewJob(
viewPortHandler: viewPortHandler,
transformer: getTransformer(forAxis: axis),
view: self,
yAxis: getAxis(axis),
xAxisRange: _xAxis.axisRange,
scaleX: scaleX,
scaleY: scaleY,
xOrigin: viewPortHandler.scaleX,
yOrigin: viewPortHandler.scaleY,
zoomCenterX: CGFloat(xValue),
zoomCenterY: CGFloat(yValue),
zoomOriginX: origin.x,
zoomOriginY: origin.y,
duration: duration,
easing: easing)
addViewportJob(job)
}
/// Zooms by the specified scale factor to the specified values on the specified axis.
///
/// - Parameters:
/// - scaleX:
/// - scaleY:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func zoomAndCenterViewAnimated(
scaleX: CGFloat,
scaleY: CGFloat,
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval,
easingOption: ChartEasingOption)
{
zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption))
}
/// Zooms by the specified scale factor to the specified values on the specified axis.
///
/// - Parameters:
/// - scaleX:
/// - scaleY:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func zoomAndCenterViewAnimated(
scaleX: CGFloat,
scaleY: CGFloat,
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval)
{
zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .easeInOutSine)
}
/// Resets all zooming and dragging and makes the chart fit exactly it's bounds.
@objc open func fitScreen()
{
let matrix = _viewPortHandler.fitScreen()
_viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false)
calculateOffsets()
setNeedsDisplay()
}
/// Sets the minimum scale value to which can be zoomed out. 1 = fitScreen
@objc open func setScaleMinima(_ scaleX: CGFloat, scaleY: CGFloat)
{
_viewPortHandler.setMinimumScaleX(scaleX)
_viewPortHandler.setMinimumScaleY(scaleY)
}
@objc open var visibleXRange: Double
{
return abs(highestVisibleX - lowestVisibleX)
}
/// Sets the size of the area (range on the x-axis) that should be maximum visible at once (no further zooming out allowed).
///
/// If this is e.g. set to 10, no more than a range of 10 values on the x-axis can be viewed at once without scrolling.
///
/// If you call this method, chart must have data or it has no effect.
@objc open func setVisibleXRangeMaximum(_ maxXRange: Double)
{
let xScale = _xAxis.axisRange / maxXRange
_viewPortHandler.setMinimumScaleX(CGFloat(xScale))
}
/// Sets the size of the area (range on the x-axis) that should be minimum visible at once (no further zooming in allowed).
///
/// If this is e.g. set to 10, no less than a range of 10 values on the x-axis can be viewed at once without scrolling.
///
/// If you call this method, chart must have data or it has no effect.
@objc open func setVisibleXRangeMinimum(_ minXRange: Double)
{
let xScale = _xAxis.axisRange / minXRange
_viewPortHandler.setMaximumScaleX(CGFloat(xScale))
}
/// Limits the maximum and minimum value count that can be visible by pinching and zooming.
///
/// e.g. minRange=10, maxRange=100 no less than 10 values and no more that 100 values can be viewed
/// at once without scrolling.
///
/// If you call this method, chart must have data or it has no effect.
@objc open func setVisibleXRange(minXRange: Double, maxXRange: Double)
{
let minScale = _xAxis.axisRange / maxXRange
let maxScale = _xAxis.axisRange / minXRange
_viewPortHandler.setMinMaxScaleX(
minScaleX: CGFloat(minScale),
maxScaleX: CGFloat(maxScale))
}
/// Sets the size of the area (range on the y-axis) that should be maximum visible at once.
///
/// - Parameters:
/// - yRange:
/// - axis: - the axis for which this limit should apply
@objc open func setVisibleYRangeMaximum(_ maxYRange: Double, axis: YAxis.AxisDependency)
{
let yScale = getAxisRange(axis: axis) / maxYRange
_viewPortHandler.setMinimumScaleY(CGFloat(yScale))
}
/// Sets the size of the area (range on the y-axis) that should be minimum visible at once, no further zooming in possible.
///
/// - Parameters:
/// - yRange:
/// - axis: - the axis for which this limit should apply
@objc open func setVisibleYRangeMinimum(_ minYRange: Double, axis: YAxis.AxisDependency)
{
let yScale = getAxisRange(axis: axis) / minYRange
_viewPortHandler.setMaximumScaleY(CGFloat(yScale))
}
/// Limits the maximum and minimum y range that can be visible by pinching and zooming.
///
/// - Parameters:
/// - minYRange:
/// - maxYRange:
/// - axis:
@objc open func setVisibleYRange(minYRange: Double, maxYRange: Double, axis: YAxis.AxisDependency)
{
let minScale = getAxisRange(axis: axis) / minYRange
let maxScale = getAxisRange(axis: axis) / maxYRange
_viewPortHandler.setMinMaxScaleY(minScaleY: CGFloat(minScale), maxScaleY: CGFloat(maxScale))
}
/// Moves the left side of the current viewport to the specified x-value.
/// This also refreshes the chart by calling setNeedsDisplay().
@objc open func moveViewToX(_ xValue: Double)
{
let job = MoveViewJob(
viewPortHandler: viewPortHandler,
xValue: xValue,
yValue: 0.0,
transformer: getTransformer(forAxis: .left),
view: self)
addViewportJob(job)
}
/// Centers the viewport to the specified y-value on the y-axis.
/// This also refreshes the chart by calling setNeedsDisplay().
///
/// - Parameters:
/// - yValue:
/// - axis: - which axis should be used as a reference for the y-axis
@objc open func moveViewToY(_ yValue: Double, axis: YAxis.AxisDependency)
{
let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY)
let job = MoveViewJob(
viewPortHandler: viewPortHandler,
xValue: 0.0,
yValue: yValue + yInView / 2.0,
transformer: getTransformer(forAxis: axis),
view: self)
addViewportJob(job)
}
/// This will move the left side of the current viewport to the specified x-value on the x-axis, and center the viewport to the specified y-value on the y-axis.
/// This also refreshes the chart by calling setNeedsDisplay().
///
/// - Parameters:
/// - xValue:
/// - yValue:
/// - axis: - which axis should be used as a reference for the y-axis
@objc open func moveViewTo(xValue: Double, yValue: Double, axis: YAxis.AxisDependency)
{
let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY)
let job = MoveViewJob(
viewPortHandler: viewPortHandler,
xValue: xValue,
yValue: yValue + yInView / 2.0,
transformer: getTransformer(forAxis: axis),
view: self)
addViewportJob(job)
}
/// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated.
/// This also refreshes the chart by calling setNeedsDisplay().
///
/// - Parameters:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func moveViewToAnimated(
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval,
easing: ChartEasingFunctionBlock?)
{
let bounds = valueForTouchPoint(
point: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop),
axis: axis)
let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY)
let job = AnimatedMoveViewJob(
viewPortHandler: viewPortHandler,
xValue: xValue,
yValue: yValue + yInView / 2.0,
transformer: getTransformer(forAxis: axis),
view: self,
xOrigin: bounds.x,
yOrigin: bounds.y,
duration: duration,
easing: easing)
addViewportJob(job)
}
/// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated.
/// This also refreshes the chart by calling setNeedsDisplay().
///
/// - Parameters:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func moveViewToAnimated(
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval,
easingOption: ChartEasingOption)
{
moveViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption))
}
/// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated.
/// This also refreshes the chart by calling setNeedsDisplay().
///
/// - Parameters:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func moveViewToAnimated(
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval)
{
moveViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .easeInOutSine)
}
/// This will move the center of the current viewport to the specified x-value and y-value.
/// This also refreshes the chart by calling setNeedsDisplay().
///
/// - Parameters:
/// - xValue:
/// - yValue:
/// - axis: - which axis should be used as a reference for the y-axis
@objc open func centerViewTo(
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency)
{
let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY)
let xInView = xAxis.axisRange / Double(_viewPortHandler.scaleX)
let job = MoveViewJob(
viewPortHandler: viewPortHandler,
xValue: xValue - xInView / 2.0,
yValue: yValue + yInView / 2.0,
transformer: getTransformer(forAxis: axis),
view: self)
addViewportJob(job)
}
/// This will move the center of the current viewport to the specified x-value and y-value animated.
///
/// - Parameters:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func centerViewToAnimated(
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval,
easing: ChartEasingFunctionBlock?)
{
let bounds = valueForTouchPoint(
point: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop),
axis: axis)
let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY)
let xInView = xAxis.axisRange / Double(_viewPortHandler.scaleX)
let job = AnimatedMoveViewJob(
viewPortHandler: viewPortHandler,
xValue: xValue - xInView / 2.0,
yValue: yValue + yInView / 2.0,
transformer: getTransformer(forAxis: axis),
view: self,
xOrigin: bounds.x,
yOrigin: bounds.y,
duration: duration,
easing: easing)
addViewportJob(job)
}
/// This will move the center of the current viewport to the specified x-value and y-value animated.
///
/// - Parameters:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func centerViewToAnimated(
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval,
easingOption: ChartEasingOption)
{
centerViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption))
}
/// This will move the center of the current viewport to the specified x-value and y-value animated.
///
/// - Parameters:
/// - xValue:
/// - yValue:
/// - axis: which axis should be used as a reference for the y-axis
/// - duration: the duration of the animation in seconds
/// - easing:
@objc open func centerViewToAnimated(
xValue: Double,
yValue: Double,
axis: YAxis.AxisDependency,
duration: TimeInterval)
{
centerViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .easeInOutSine)
}
/// Sets custom offsets for the current `ChartViewPort` (the offsets on the sides of the actual chart window). Setting this will prevent the chart from automatically calculating it's offsets. Use `resetViewPortOffsets()` to undo this.
/// ONLY USE THIS WHEN YOU KNOW WHAT YOU ARE DOING, else use `setExtraOffsets(...)`.
@objc open func setViewPortOffsets(left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat)
{
_customViewPortEnabled = true
if Thread.isMainThread
{
self._viewPortHandler.restrainViewPort(offsetLeft: left, offsetTop: top, offsetRight: right, offsetBottom: bottom)
prepareOffsetMatrix()
prepareValuePxMatrix()
}
else
{
DispatchQueue.main.async(execute: {
self.setViewPortOffsets(left: left, top: top, right: right, bottom: bottom)
})
}
}
/// Resets all custom offsets set via `setViewPortOffsets(...)` method. Allows the chart to again calculate all offsets automatically.
@objc open func resetViewPortOffsets()
{
_customViewPortEnabled = false
calculateOffsets()
}
// MARK: - Accessors
/// - Returns: The range of the specified axis.
@objc open func getAxisRange(axis: YAxis.AxisDependency) -> Double
{
if axis == .left
{
return leftAxis.axisRange
}
else
{
return rightAxis.axisRange
}
}
/// - Returns: The position (in pixels) the provided Entry has inside the chart view
@objc open func getPosition(entry e: ChartDataEntry, axis: YAxis.AxisDependency) -> CGPoint
{
var vals = CGPoint(x: CGFloat(e.x), y: CGFloat(e.y))
getTransformer(forAxis: axis).pointValueToPixel(&vals)
return vals
}
/// is dragging enabled? (moving the chart with the finger) for the chart (this does not affect scaling).
@objc open var dragEnabled: Bool
{
get
{
return _dragXEnabled || _dragYEnabled
}
set
{
_dragYEnabled = newValue
_dragXEnabled = newValue
}
}
/// is dragging enabled? (moving the chart with the finger) for the chart (this does not affect scaling).
@objc open var isDragEnabled: Bool
{
return dragEnabled
}
/// is dragging on the X axis enabled?
@objc open var dragXEnabled: Bool
{
get
{
return _dragXEnabled
}
set
{
_dragXEnabled = newValue
}
}
/// is dragging on the Y axis enabled?
@objc open var dragYEnabled: Bool
{
get
{
return _dragYEnabled
}
set
{
_dragYEnabled = newValue
}
}
/// is scaling enabled? (zooming in and out by gesture) for the chart (this does not affect dragging).
@objc open func setScaleEnabled(_ enabled: Bool)
{
if _scaleXEnabled != enabled || _scaleYEnabled != enabled
{
_scaleXEnabled = enabled
_scaleYEnabled = enabled
#if !os(tvOS)
_pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled
#endif
}
}
@objc open var scaleXEnabled: Bool
{
get
{
return _scaleXEnabled
}
set
{
if _scaleXEnabled != newValue
{
_scaleXEnabled = newValue
#if !os(tvOS)
_pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled
#endif
}
}
}
@objc open var scaleYEnabled: Bool
{
get
{
return _scaleYEnabled
}
set
{
if _scaleYEnabled != newValue
{
_scaleYEnabled = newValue
#if !os(tvOS)
_pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled
#endif
}
}
}
@objc open var isScaleXEnabled: Bool { return scaleXEnabled }
@objc open var isScaleYEnabled: Bool { return scaleYEnabled }
/// flag that indicates if double tap zoom is enabled or not
@objc open var doubleTapToZoomEnabled: Bool
{
get
{
return _doubleTapToZoomEnabled
}
set
{
if _doubleTapToZoomEnabled != newValue
{
_doubleTapToZoomEnabled = newValue
_doubleTapGestureRecognizer.isEnabled = _doubleTapToZoomEnabled
}
}
}
/// **default**: true
/// `true` if zooming via double-tap is enabled `false` ifnot.
@objc open var isDoubleTapToZoomEnabled: Bool
{
return doubleTapToZoomEnabled
}
/// flag that indicates if highlighting per dragging over a fully zoomed out chart is enabled
@objc open var highlightPerDragEnabled = true
/// If set to true, highlighting per dragging over a fully zoomed out chart is enabled
/// You might want to disable this when using inside a `NSUIScrollView`
///
/// **default**: true
@objc open var isHighlightPerDragEnabled: Bool
{
return highlightPerDragEnabled
}
/// **default**: true
/// `true` if drawing the grid background is enabled, `false` ifnot.
@objc open var isDrawGridBackgroundEnabled: Bool
{
return drawGridBackgroundEnabled
}
/// **default**: false
/// `true` if drawing the borders rectangle is enabled, `false` ifnot.
@objc open var isDrawBordersEnabled: Bool
{
return drawBordersEnabled
}
/// - Returns: The x and y values in the chart at the given touch point
/// (encapsulated in a `CGPoint`). This method transforms pixel coordinates to
/// coordinates / values in the chart. This is the opposite method to
/// `getPixelsForValues(...)`.
@objc open func valueForTouchPoint(point pt: CGPoint, axis: YAxis.AxisDependency) -> CGPoint
{
return getTransformer(forAxis: axis).valueForTouchPoint(pt)
}
/// Transforms the given chart values into pixels. This is the opposite
/// method to `valueForTouchPoint(...)`.
@objc open func pixelForValues(x: Double, y: Double, axis: YAxis.AxisDependency) -> CGPoint
{
return getTransformer(forAxis: axis).pixelForValues(x: x, y: y)
}
/// - Returns: The Entry object displayed at the touched position of the chart
@objc open func getEntryByTouchPoint(point pt: CGPoint) -> ChartDataEntry!
{
if let h = getHighlightByTouchPoint(pt)
{
return _data!.entryForHighlight(h)
}
return nil
}
/// - Returns: The DataSet object displayed at the touched position of the chart
@objc open func getDataSetByTouchPoint(point pt: CGPoint) -> IBarLineScatterCandleBubbleChartDataSet?
{
let h = getHighlightByTouchPoint(pt)
if h !== nil
{
return _data?.getDataSetByIndex(h!.dataSetIndex) as? IBarLineScatterCandleBubbleChartDataSet
}
return nil
}
/// The current x-scale factor
@objc open var scaleX: CGFloat
{
if _viewPortHandler === nil
{
return 1.0
}
return _viewPortHandler.scaleX
}
/// The current y-scale factor
@objc open var scaleY: CGFloat
{
if _viewPortHandler === nil
{
return 1.0
}
return _viewPortHandler.scaleY
}
/// if the chart is fully zoomed out, return true
@objc open var isFullyZoomedOut: Bool { return _viewPortHandler.isFullyZoomedOut }
/// - Returns: The y-axis object to the corresponding AxisDependency. In the
/// horizontal bar-chart, LEFT == top, RIGHT == BOTTOM
@objc open func getAxis(_ axis: YAxis.AxisDependency) -> YAxis
{
if axis == .left
{
return leftAxis
}
else
{
return rightAxis
}
}
/// flag that indicates if pinch-zoom is enabled. if true, both x and y axis can be scaled simultaneously with 2 fingers, if false, x and y axis can be scaled separately
@objc open var pinchZoomEnabled: Bool
{
get
{
return _pinchZoomEnabled
}
set
{
if _pinchZoomEnabled != newValue
{
_pinchZoomEnabled = newValue
#if !os(tvOS)
_pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled
#endif
}
}
}
/// **default**: false
/// `true` if pinch-zoom is enabled, `false` ifnot
@objc open var isPinchZoomEnabled: Bool { return pinchZoomEnabled }
/// Set an offset in dp that allows the user to drag the chart over it's
/// bounds on the x-axis.
@objc open func setDragOffsetX(_ offset: CGFloat)
{
_viewPortHandler.setDragOffsetX(offset)
}
/// Set an offset in dp that allows the user to drag the chart over it's
/// bounds on the y-axis.
@objc open func setDragOffsetY(_ offset: CGFloat)
{
_viewPortHandler.setDragOffsetY(offset)
}
/// `true` if both drag offsets (x and y) are zero or smaller.
@objc open var hasNoDragOffset: Bool { return _viewPortHandler.hasNoDragOffset }
open override var chartYMax: Double
{
return max(leftAxis._axisMaximum, rightAxis._axisMaximum)
}
open override var chartYMin: Double
{
return min(leftAxis._axisMinimum, rightAxis._axisMinimum)
}
/// `true` if either the left or the right or both axes are inverted.
@objc open var isAnyAxisInverted: Bool
{
return leftAxis.isInverted || rightAxis.isInverted
}
/// flag that indicates if auto scaling on the y axis is enabled.
/// if yes, the y axis automatically adjusts to the min and max y values of the current x axis range whenever the viewport changes
@objc open var autoScaleMinMaxEnabled: Bool
{
get { return _autoScaleMinMaxEnabled }
set { _autoScaleMinMaxEnabled = newValue }
}
/// **default**: false
/// `true` if auto scaling on the y axis is enabled.
@objc open var isAutoScaleMinMaxEnabled : Bool { return autoScaleMinMaxEnabled }
/// Sets a minimum width to the specified y axis.
@objc open func setYAxisMinWidth(_ axis: YAxis.AxisDependency, width: CGFloat)
{
if axis == .left
{
leftAxis.minWidth = width
}
else
{
rightAxis.minWidth = width
}
}
/// **default**: 0.0
///
/// - Returns: The (custom) minimum width of the specified Y axis.
@objc open func getYAxisMinWidth(_ axis: YAxis.AxisDependency) -> CGFloat
{
if axis == .left
{
return leftAxis.minWidth
}
else
{
return rightAxis.minWidth
}
}
/// Sets a maximum width to the specified y axis.
/// Zero (0.0) means there's no maximum width
@objc open func setYAxisMaxWidth(_ axis: YAxis.AxisDependency, width: CGFloat)
{
if axis == .left
{
leftAxis.maxWidth = width
}
else
{
rightAxis.maxWidth = width
}
}
/// Zero (0.0) means there's no maximum width
///
/// **default**: 0.0 (no maximum specified)
///
/// - Returns: The (custom) maximum width of the specified Y axis.
@objc open func getYAxisMaxWidth(_ axis: YAxis.AxisDependency) -> CGFloat
{
if axis == .left
{
return leftAxis.maxWidth
}
else
{
return rightAxis.maxWidth
}
}
/// - Returns the width of the specified y axis.
@objc open func getYAxisWidth(_ axis: YAxis.AxisDependency) -> CGFloat
{
if axis == .left
{
return leftAxis.requiredSize().width
}
else
{
return rightAxis.requiredSize().width
}
}
// MARK: - BarLineScatterCandleBubbleChartDataProvider
/// - Returns: The Transformer class that contains all matrices and is
/// responsible for transforming values into pixels on the screen and
/// backwards.
open func getTransformer(forAxis axis: YAxis.AxisDependency) -> Transformer
{
if axis == .left
{
return _leftAxisTransformer
}
else
{
return _rightAxisTransformer
}
}
/// the number of maximum visible drawn values on the chart only active when `drawValuesEnabled` is enabled
open override var maxVisibleCount: Int
{
get
{
return _maxVisibleCount
}
set
{
_maxVisibleCount = newValue
}
}
open func isInverted(axis: YAxis.AxisDependency) -> Bool
{
return getAxis(axis).isInverted
}
/// The lowest x-index (value on the x-axis) that is still visible on he chart.
open var lowestVisibleX: Double
{
var pt = CGPoint(
x: viewPortHandler.contentLeft,
y: viewPortHandler.contentBottom)
getTransformer(forAxis: .left).pixelToValues(&pt)
return max(xAxis._axisMinimum, Double(pt.x))
}
/// The highest x-index (value on the x-axis) that is still visible on the chart.
open var highestVisibleX: Double
{
var pt = CGPoint(
x: viewPortHandler.contentRight,
y: viewPortHandler.contentBottom)
getTransformer(forAxis: .left).pixelToValues(&pt)
return min(xAxis._axisMaximum, Double(pt.x))
}
}
================================================
FILE: mac/Pods/Charts/Source/Charts/Charts/BubbleChartView.swift
================================================
//
// BubbleChartView.swift
// Charts
//
// Bubble chart implementation:
// Copyright 2015 Pierre-Marc Airoldi
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
import Foundation
import CoreGraphics
open class BubbleChartView: BarLineChartViewBase, BubbleChartDataProvider
{
open override func initialize()
{
super.initialize()
renderer = BubbleChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler)
}
// MARK: - BubbleChartDataProvider
open var bubbleData: BubbleChartData? { return _data as? BubbleChartData }
}
================================================
FILE: mac/Pods/Charts/Source/Charts/Charts/CandleStickChartView.swift
================================================
//
// CandleStickChartView.swift
// Charts
//
// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
import Foundation
import CoreGraphics
/// Financial chart type that draws candle-sticks.
open class CandleStickChartView: BarLineChartViewBase, CandleChartDataProvider
{
internal override func initialize()
{
super.initialize()
renderer = CandleStickChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler)
self.xAxis.spaceMin = 0.5
self.xAxis.spaceMax = 0.5
}
// MARK: - CandleChartDataProvider
open var candleData: CandleChartData?
{
return _data as? CandleChartData
}
}
================================================
FILE: mac/Pods/Charts/Source/Charts/Charts/ChartViewBase.swift
================================================
//
// ChartViewBase.swift
// Charts
//
// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
// Based on https://github.com/PhilJay/MPAndroidChart/commit/c42b880
import Foundation
import CoreGraphics
#if canImport(UIKit)
import UIKit
#endif
#if canImport(Cocoa)
import Cocoa
#endif
@objc
public protocol ChartViewDelegate
{
/// Called when a value has been selected inside the chart.
///
/// - Parameters:
/// - entry: The selected Entry.
/// - highlight: The corresponding highlight object that contains information about the highlighted position such as dataSetIndex etc.
@objc optional func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight)
/// Called when a user stops panning between values on the chart
@objc optional func chartViewDidEndPanning(_ chartView: ChartViewBase)
// Called when nothing has been selected or an "un-select" has been made.
@objc optional func chartValueNothingSelected(_ chartView: ChartViewBase)
// Callbacks when the chart is scaled / zoomed via pinch zoom gesture.
@objc optional func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat)
// Callbacks when the chart is moved / translated via drag gesture.
@objc optional func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat)
// Callbacks when Animator stops animating
@objc optional func chartView(_ chartView: ChartViewBase, animatorDidStop animator: Animator)
}
open class ChartViewBase: NSUIView, ChartDataProvider, AnimatorDelegate
{
// MARK: - Properties
/// - Returns: The object representing all x-labels, this method can be used to
/// acquire the XAxis object and modify it (e.g. change the position of the
/// labels)
@objc open var xAxis: XAxis
{
return _xAxis
}
/// The default IValueFormatter that has been determined by the chart considering the provided minimum and maximum values.
internal var _defaultValueFormatter: IValueFormatter? = DefaultValueFormatter(decimals: 0)
/// object that holds all data that was originally set for the chart, before it was modified or any filtering algorithms had been applied
internal var _data: ChartData?
/// Flag that indicates if highlighting per tap (touch) is enabled
private var _highlightPerTapEnabled = true
/// If set to true, chart continues to scroll after touch up
@objc open var dragDecelerationEnabled = true
/// Deceleration friction coefficient in [0 ; 1] interval, higher values indicate that speed will decrease slowly, for example if it set to 0, it will stop immediately.
/// 1 is an invalid value, and will be converted to 0.999 automatically.
private var _dragDecelerationFrictionCoef: CGFloat = 0.9
/// if true, units are drawn next to the values in the chart
internal var _drawUnitInChart = false
/// The object representing the labels on the x-axis
internal var _xAxis: XAxis!
/// The `Description` object of the chart.
/// This should have been called just "description", but
@objc open var chartDescription: Description?
/// The legend object containing all data associated with the legend
internal var _legend: Legend!
/// delegate to receive chart events
@objc open weak var delegate: ChartViewDelegate?
/// text that is displayed when the chart is empty
@objc open var noDataText = "No chart data available."
/// Font to be used for the no data text.
@objc open var noDataFont = NSUIFont.systemFont(ofSize: 12)
/// color of the no data text
@objc open var noDataTextColor: NSUIColor = .labelOrBlack
/// alignment of the no data text
@objc open var noDataTextAlignment: NSTextAlignment = .left
internal var _legendRenderer: LegendRenderer!
/// object responsible for rendering the data
@objc open var renderer: DataRenderer?
@objc open var highlighter: IHighlighter?
/// object that manages the bounds and drawing constraints of the chart
internal var _viewPortHandler: ViewPortHandler!
/// object responsible for animations
internal var _animator: Animator!
/// flag that indicates if offsets calculation has already been done or not
private var _offsetsCalculated = false
/// array of Highlight objects that reference the highlighted slices in the chart
internal var _indicesToHighlight = [Highlight]()
/// `true` if drawing the marker is enabled when tapping on values
/// (use the `marker` property to specify a marker)
@objc open var drawMarkers = true
/// - Returns: `true` if drawing the marker is enabled when tapping on values
/// (use the `marker` property to specify a marker)
@objc open var isDrawMarkersEnabled: Bool { return drawMarkers }
/// The marker that is displayed when a value is clicked on the chart
@objc open var marker: IMarker?
private var _interceptTouchEvents = false
/// An extra offset to be appended to the viewport's top
@objc open var extraTopOffset: CGFloat = 0.0
/// An extra offset to be appended to the viewport's right
@objc open var extraRightOffset: CGFloat = 0.0
/// An extra offset to be appended to the viewport's bottom
@objc open var extraBottomOffset: CGFloat = 0.0
/// An extra offset to be appended to the viewport's left
@objc open var extraLeftOffset: CGFloat = 0.0
@objc open func setExtraOffsets(left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat)
{
extraLeftOffset = left
extraTopOffset = top
extraRightOffset = right
extraBottomOffset = bottom
}
// MARK: - Initializers
public override init(frame: CGRect)
{
super.init(frame: frame)
initialize()
}
public required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
initialize()
}
deinit
{
self.removeObserver(self, forKeyPath: "bounds")
self.removeObserver(self, forKeyPath: "frame")
}
internal func initialize()
{
#if os(iOS)
self.backgroundColor = NSUIColor.clear
#endif
_animator = Animator()
_animator.delegate = self
_viewPortHandler = ViewPortHandler(width: bounds.size.width, height: bounds.size.height)
chartDescription = Description()
_legend = Legend()
_legendRenderer = LegendRenderer(viewPortHandler: _viewPortHandler, legend: _legend)
_xAxis = XAxis()
self.addObserver(self, forKeyPath: "bounds", options: .new, context: nil)
self.addObserver(self, forKeyPath: "frame", options: .new, context: nil)
}
// MARK: - ChartViewBase
/// The data for the chart
open var data: ChartData?
{
get
{
return _data
}
set
{
_data = newValue
_offsetsCalculated = false
guard let _data = _data else
{
setNeedsDisplay()
return
}
// calculate how many digits are needed
setupDefaultFormatter(min: _data.getYMin(), max: _data.getYMax())
for set in _data.dataSets
{
if set.needsFormatter || set.valueFormatter === _defaultValueFormatter
{
set.valueFormatter = _defaultValueFormatter
}
}
// let the chart know there is new data
notifyDataSetChanged()
}
}
/// Clears the chart from all data (sets it to null) and refreshes it (by calling setNeedsDisplay()).
@objc open func clear()
{
_data = nil
_offsetsCalculated = false
_indicesToHighlight.removeAll()
lastHighlighted = nil
setNeedsDisplay()
}
/// Removes all DataSets (and thereby Entries) from the chart. Does not set the data object to nil. Also refreshes the chart by calling setNeedsDisplay().
@objc open func clearValues()
{
_data?.clearValues()
setNeedsDisplay()
}
/// - Returns: `true` if the chart is empty (meaning it's data object is either null or contains no entries).
@objc open func isEmpty() -> Bool
{
guard let data = _data else { return true }
if data.entryCount <= 0
{
return true
}
else
{
return false
}
}
/// Lets the chart know its underlying data has changed and should perform all necessary recalculations.
/// It is crucial that this method is called everytime data is changed dynamically. Not calling this method can lead to crashes or unexpected behaviour.
@objc open func notifyDataSetChanged()
{
fatalError("notifyDataSetChanged() cannot be called on ChartViewBase")
}
/// Calculates the offsets of the chart to the border depending on the position of an eventual legend or depending on the length of the y-axis and x-axis labels and their position
internal func calculateOffsets()
{
fatalError("calculateOffsets() cannot be called on ChartViewBase")
}
/// calcualtes the y-min and y-max value and the y-delta and x-delta value
internal func calcMinMax()
{
fatalError("calcMinMax() cannot be called on ChartViewBase")
}
/// calculates the required number of digits for the values that might be drawn in the chart (if enabled), and creates the default value formatter
internal func setupDefaultFormatter(min: Double, max: Double)
{
// check if a custom formatter is set or not
var reference = Double(0.0)
if let data = _data , data.entryCount >= 2
{
reference = fabs(max - min)
}
else
{
let absMin = fabs(min)
let absMax = fabs(max)
reference = absMin > absMax ? absMin : absMax
}
if _defaultValueFormatter is DefaultValueFormatter
{
// setup the formatter with a new number of digits
let digits = reference.decimalPlaces
(_defaultValueFormatter as? DefaultValueFormatter)?.decimals
= digits
}
}
open override func draw(_ rect: CGRect)
{
let optionalContext = NSUIGraphicsGetCurrentContext()
guard let context = optionalContext else { return }
let frame = self.bounds
if _data === nil && noDataText.count > 0
{
context.saveGState()
defer { context.restoreGState() }
let paragraphStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
paragraphStyle.minimumLineHeight = noDataFont.lineHeight
paragraphStyle.lineBreakMode = .byWordWrapping
paragraphStyle.alignment = noDataTextAlignment
ChartUtils.drawMultilineText(
context: context,
text: noDataText,
point: CGPoint(x: frame.width / 2.0, y: frame.height / 2.0),
attributes:
[.font: noDataFont,
.foregroundColor: noDataTextColor,
.paragraphStyle: paragraphStyle],
constrainedToSize: self.bounds.size,
anchor: CGPoint(x: 0.5, y: 0.5),
angleRadians: 0.0)
return
}
if !_offsetsCalculated
{
calculateOffsets()
_offsetsCalculated = true
}
}
/// Draws the description text in the bottom right corner of the chart (per default)
internal func drawDescription(context: CGContext)
{
// check if description should be drawn
guard
let description = chartDescription,
description.isEnabled,
let descriptionText = description.text,
descriptionText.count > 0
else { return }
let position = description.position ?? CGPoint(x: bounds.width - _viewPortHandler.offsetRight - description.xOffset,
y: bounds.height - _viewPortHandler.offsetBottom - description.yOffset - description.font.lineHeight)
var attrs = [NSAttributedString.Key : Any]()
attrs[NSAttributedString.Key.font] = description.font
attrs[NSAttributedString.Key.foregroundColor] = description.textColor
ChartUtils.drawText(
context: context,
text: descriptionText,
point: position,
align: description.textAlign,
attributes: attrs)
}
// MARK: - Accessibility
open override func accessibilityChildren() -> [Any]? {
return renderer?.accessibleChartElements
}
// MARK: - Highlighting
/// The array of currently highlighted values. This might an empty if nothing is highlighted.
@objc open var highlighted: [Highlight]
{
return _indicesToHighlight
}
/// Set this to false to prevent values from being highlighted by tap gesture.
/// Values can still be highlighted via drag or programmatically.
/// **default**: true
@objc open var highlightPerTapEnabled: Bool
{
get { return _highlightPerTapEnabled }
set { _highlightPerTapEnabled = newValue }
}
/// `true` if values can be highlighted via tap gesture, `false` ifnot.
@objc open var isHighLightPerTapEnabled: Bool
{
return highlightPerTapEnabled
}
/// Checks if the highlight array is null, has a length of zero or if the first object is null.
///
/// - Returns: `true` if there are values to highlight, `false` ifthere are no values to highlight.
@objc open func valuesToHighlight() -> Bool
{
return !_indicesToHighlight.isEmpty
}
/// Highlights the values at the given indices in the given DataSets. Provide
/// null or an empty array to undo all highlighting.
/// This should be used to programmatically highlight values.
/// This method *will not* call the delegate.
@objc open func highlightValues(_ highs: [Highlight]?)
{
// set the indices to highlight
_indicesToHighlight = highs ?? [Highlight]()
if _indicesToHighlight.isEmpty
{
self.lastHighlighted = nil
}
else
{
self.lastHighlighted = _indicesToHighlight[0]
}
// redraw the chart
setNeedsDisplay()
}
/// Highlights any y-value at the given x-value in the given DataSet.
/// Provide -1 as the dataSetIndex to undo all highlighting.
/// This method will call the delegate.
///
/// - Parameters:
/// - x: The x-value to highlight
/// - dataSetIndex: The dataset index to search in
/// - dataIndex: The data index to search in (only used in CombinedChartView currently)
@objc open func highlightValue(x: Double, dataSetIndex: Int, dataIndex: Int = -1)
{
highlightValue(x: x, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: true)
}
/// Highlights the value at the given x-value and y-value in the given DataSet.
/// Provide -1 as the dataSetIndex to undo all highlighting.
/// This method will call the delegate.
///
/// - Parameters:
/// - x: The x-value to highlight
/// - y: The y-value to highlight. Supply `NaN` for "any"
/// - dataSetIndex: The dataset index to search in
/// - dataIndex: The data index to search in (only used in CombinedChartView currently)
@objc open func highlightValue(x: Double, y: Double, dataSetIndex: Int, dataIndex: Int = -1)
{
highlightValue(x: x, y: y, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: true)
}
/// Highlights any y-value at the given x-value in the given DataSet.
/// Provide -1 as the dataSetIndex to undo all highlighting.
///
/// - Parameters:
/// - x: The x-value to highlight
/// - dataSetIndex: The dataset index to search in
/// - dataIndex: The data index to search in (only used in CombinedChartView currently)
/// - callDelegate: Should the delegate be called for this change
@objc open func highlightValue(x: Double, dataSetIndex: Int, dataIndex: Int = -1, callDelegate: Bool)
{
highlightValue(x: x, y: .nan, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: callDelegate)
}
/// Highlights the value at the given x-value and y-value in the given DataSet.
/// Provide -1 as the dataSetIndex to undo all highlighting.
///
/// - Parameters:
/// - x: The x-value to highlight
/// - y: The y-value to highlight. Supply `NaN` for "any"
/// - dataSetIndex: The dataset index to search in
/// - dataIndex: The data index to search in (only used in CombinedChartView currently)
/// - callDelegate: Should the delegate be called for this change
@objc open func highlightValue(x: Double, y: Double, dataSetIndex: Int, dataIndex: Int = -1, callDelegate: Bool)
{
guard let data = _data else
{
Swift.print("Value not highlighted because data is nil")
return
}
if dataSetIndex < 0 || dataSetIndex >= data.dataSetCount
{
highlightValue(nil, callDelegate: callDelegate)
}
else
{
highlightValue(Highlight(x: x, y: y, dataSetIndex: dataSetIndex, dataIndex: dataIndex), callDelegate: callDelegate)
}
}
/// Highlights the values represented by the provided Highlight object
/// This method *will not* call the delegate.
///
/// - Parameters:
/// - highlight: contains information about which entry should be highlighted
@objc open func highlightValue(_ highlight: Highlight?)
{
highlightValue(highlight, callDelegate: false)
}
/// Highlights the value selected by touch gesture.
@objc open func highlightValue(_ highlight: Highlight?, callDelegate: Bool)
{
var entry: ChartDataEntry?
var h = highlight
if h == nil
{
self.lastHighlighted = nil
_indicesToHighlight.removeAll(keepingCapacity: false)
}
else
{
// set the indices to highlight
entry = _data?.entryForHighlight(h!)
if entry == nil
{
h = nil
_indicesToHighlight.removeAll(keepingCapacity: false)
}
else
{
_indicesToHighlight = [h!]
}
}
if callDelegate, let delegate = delegate
{
if let h = h
{
// notify the listener
delegate.chartValueSelected?(self, entry: entry!, highlight: h)
}
else
{
delegate.chartValueNothingSelected?(self)
}
}
// redraw the chart
setNeedsDisplay()
}
/// - Returns: The Highlight object (contains x-index and DataSet index) of the
/// selected value at the given touch point inside the Line-, Scatter-, or
/// CandleStick-Chart.
@objc open func getHighlightByTouchPoint(_ pt: CGPoint) -> Highlight?
{
if _data === nil
{
Swift.print("Can't select by touch. No data set.")
return nil
}
return self.highlighter?.getHighlight(x: pt.x, y: pt.y)
}
/// The last value that was highlighted via touch.
@objc open var lastHighlighted: Highlight?
// MARK: - Markers
/// draws all MarkerViews on the highlighted positions
internal func drawMarkers(context: CGContext)
{
// if there is no marker view or drawing marker is disabled
guard
let marker = marker
, isDrawMarkersEnabled &&
valuesToHighlight()
else { return }
for i in 0 ..< _indicesToHighlight.count
{
let highlight = _indicesToHighlight[i]
guard let
set = data?.getDataSetByIndex(highlight.dataSetIndex),
let e = _data?.entryForHighlight(highlight)
else { continue }
let entryIndex = set.entryIndex(entry: e)
if entryIndex > Int(Double(set.entryCount) * _animator.phaseX)
{
continue
}
let pos = getMarkerPosition(highlight: highlight)
// check bounds
if !_viewPortHandler.isInBounds(x: pos.x, y: pos.y)
{
continue
}
// callbacks to update the content
marker.refreshContent(entry: e, highlight: highlight)
// draw the marker
marker.draw(context: context, point: pos)
}
}
/// - Returns: The actual position in pixels of the MarkerView for the given Entry in the given DataSet.
@objc open func getMarkerPosition(highlight: Highlight) -> CGPoint
{
return CGPoint(x: highlight.drawX, y: highlight.drawY)
}
// MARK: - Animation
/// The animator responsible for animating chart values.
@objc open var chartAnimator: Animator!
{
return _animator
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
/// - easingX: an easing function for the animation on the x axis
/// - easingY: an easing function for the animation on the y axis
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingX: ChartEasingFunctionBlock?, easingY: ChartEasingFunctionBlock?)
{
_animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingX: easingX, easingY: easingY)
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
/// - easingOptionX: the easing function for the animation on the x axis
/// - easingOptionY: the easing function for the animation on the y axis
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingOptionX: ChartEasingOption, easingOptionY: ChartEasingOption)
{
_animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingOptionX: easingOptionX, easingOptionY: easingOptionY)
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
/// - easing: an easing function for the animation
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?)
{
_animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easing: easing)
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
/// - easingOption: the easing function for the animation
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingOption: ChartEasingOption)
{
_animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingOption: easingOption)
}
/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - yAxisDuration: duration for animating the y axis
@objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval)
{
_animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration)
}
/// Animates the drawing / rendering of the chart the x-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - easing: an easing function for the animation
@objc open func animate(xAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?)
{
_animator.animate(xAxisDuration: xAxisDuration, easing: easing)
}
/// Animates the drawing / rendering of the chart the x-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
/// - easingOption: the easing function for the animation
@objc open func animate(xAxisDuration: TimeInterval, easingOption: ChartEasingOption)
{
_animator.animate(xAxisDuration: xAxisDuration, easingOption: easingOption)
}
/// Animates the drawing / rendering of the chart the x-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - xAxisDuration: duration for animating the x axis
@objc open func animate(xAxisDuration: TimeInterval)
{
_animator.animate(xAxisDuration: xAxisDuration)
}
/// Animates the drawing / rendering of the chart the y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - yAxisDuration: duration for animating the y axis
/// - easing: an easing function for the animation
@objc open func animate(yAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?)
{
_animator.animate(yAxisDuration: yAxisDuration, easing: easing)
}
/// Animates the drawing / rendering of the chart the y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - yAxisDuration: duration for animating the y axis
/// - easingOption: the easing function for the animation
@objc open func animate(yAxisDuration: TimeInterval, easingOption: ChartEasingOption)
{
_animator.animate(yAxisDuration: yAxisDuration, easingOption: easingOption)
}
/// Animates the drawing / rendering of the chart the y-axis with the specified animation time.
/// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart.
///
/// - Parameters:
/// - yAxisDuration: duration for animating the y axis
@objc open func animate(yAxisDuration: TimeInterval)
{
_animator.animate(yAxisDuration: yAxisDuration)
}
// MARK: - Accessors
/// The current y-max value across all DataSets
open var chartYMax: Double
{
return _data?.yMax ?? 0.0
}
/// The current y-min value across all DataSets
open var chartYMin: Double
{
return _data?.yMin ?? 0.0
}
open var chartXMax: Double
{
return _xAxis._axisMaximum
}
open var chartXMin: Double
{
return _xAxis._axisMinimum
}
open var xRange: Double
{
return _xAxis.axisRange
}
/// - Note: (Equivalent of getCenter() in MPAndroidChart, as center is already a standard in iOS that returns the center point relative to superview, and MPAndroidChart returns relative to self)*
/// The center point of the chart (the whole View) in pixels.
@objc open var midPoint: CGPoint
{
let bounds = self.bounds
return CGPoint(x: bounds.origin.x + bounds.size.width / 2.0, y: bounds.origin.y + bounds.size.height / 2.0)
}
/// The center of the chart taking offsets under consideration. (returns the center of the content rectangle)
open var centerOffsets: CGPoint
{
return _viewPortHandler.contentCenter
}
/// The Legend object of the chart. This method can be used to get an instance of the legend in order to customize the automatically generated Legend.
@objc open var legend: Legend
{
return _legend
}
/// The renderer object responsible for rendering / drawing the Legend.
@objc open var legendRenderer: LegendRenderer!
{
return _legendRenderer
}
/// The rectangle that defines the borders of the chart-value surface (into which the actual values are drawn).
@objc open var contentRect: CGRect
{
return _viewPortHandler.contentRect
}
/// - Returns: The ViewPortHandler of the chart that is responsible for the
/// content area of the chart and its offsets and dimensions.
@objc open var viewPortHandler: ViewPortHandler!
{
return _viewPortHandler
}
/// - Returns: The bitmap that represents the chart.
@objc open func getChartImage(transparent: Bool) -> NSUIImage?
{
NSUIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque || !transparent, NSUIScreen.nsuiMain?.nsuiScale ?? 1.0)
guard let context = NSUIGraphicsGetCurrentContext()
else { return nil }
let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: bounds.size)
if isOpaque || !transparent
{
// Background color may be partially transparent, we must fill with white if we want to output an opaque image
context.setFillColor(NSUIColor.white.cgColor)
context.fill(rect)
if let backgroundColor = self.backgroundColor
{
context.setFillColor(backgroundColor.cgColor)
context.fill(rect)
}
}
nsuiLayer?.render(in: context)
let image = NSUIGraphicsGetImageFromCurrentImageContext()
NSUIGraphicsEndImageContext()
return image
}
public enum ImageFormat
{
case jpeg
case png
}
/// Saves the current chart state with the given name to the given path on
/// the sdcard leaving the path empty "" will put the saved file directly on
/// the SD card chart is saved as a PNG image, example:
/// saveToPath("myfilename", "foldername1/foldername2")
///
/// - Parameters:
/// - to: path to the image to save
/// - format: the format to save
/// - compressionQuality: compression quality for lossless formats (JPEG)
/// - Returns: `true` if the image was saved successfully
open func save(to path: String, format: ImageFormat, compressionQuality: Double) -> Bool
{
guard let image = getChartImage(transparent: format != .jpeg) else { return false }
let imageData: Data?
switch (format)
{
case .png: imageData = NSUIImagePNGRepresentation(image)
case .jpeg: imageData = NSUIImageJPEGRepresentation(image, CGFloat(compressionQuality))
}
guard let data = imageData else { return false }
do
{
try data.write(to: URL(fileURLWithPath: path), options: .atomic)
}
catch
{
return false
}
return true
}
internal var _viewportJobs = [ViewPortJob]()
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
if keyPath == "bounds" || keyPath == "frame"
{
let bounds = self.bounds
if (_viewPortHandler !== nil &&
(bounds.size.width != _viewPortHandler.chartWidth ||
bounds.size.height != _viewPortHandler.chartHeight))
{
_viewPortHandler.setChartDimens(width: bounds.size.width, height: bounds.size.height)
// This may cause the chart view to mutate properties affecting the view port -- lets do this
// before we try to run any pending jobs on the view port itself
notifyDataSetChanged()
// Finish any pending viewport changes
while (!_viewportJobs.isEmpty)
{
let job = _viewportJobs.remove(at: 0)
job.doJob()
}
}
}
}
@objc open func removeViewportJob(_ job: ViewPortJob)
{
if let index = _viewportJobs.firstIndex(where: { $0 === job })
{
_viewportJobs.remove(at: index)
}
}
@objc open func clearAllViewportJobs()
{
_viewportJobs.removeAll(keepingCapacity: false)
}
@objc open func addViewportJob(_ job: ViewPortJob)
{
if _viewPortHandler.hasChartDimens
{
job.doJob()
}
else
{
_viewportJobs.append(job)
}
}
/// **default**: true
/// `true` if chart continues to scroll after touch up, `false` ifnot.
@objc open var isDragDecelerationEnabled: Bool
{
return dragDecelerationEnabled
}
/// Deceleration friction coefficient in [0 ; 1] interval, higher values indicate that speed will decrease slowly, for example if it set to 0, it will stop immediately.
/// 1 is an invalid value, and will be converted to 0.999 automatically.
///
/// **default**: true
@objc open var dragDecelerationFrictionCoef: CGFloat
{
get
{
return _dragDecelerationFrictionCoef
}
set
{
var val = newValue
if val < 0.0
{
val = 0.0
}
if val >= 1.0
{
val = 0.999
}
_dragDecelerationFrictionCoef = val
}
}
/// The maximum distance in screen pixels away from an entry causing it to highlight.
/// **default**: 500.0
open var maxHighlightDistance: CGFloat = 500.0
/// the number of maximum visible drawn values on the chart only active when `drawValuesEnabled` is enabled
open var maxVisibleCount: Int
{
return Int(INT_MAX)
}
// MARK: - AnimatorDelegate
open func animatorUpdated(_ chartAnimator: Animator)
{
setNeedsDisplay()
}
open func animatorStopped(_ chartAnimator: Animator)
{
delegate?.chartView?(self, animatorDidStop: chartAnimator)
}
// MARK: - Touches
open override func nsuiTouchesBegan(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
{
if !_interceptTouchEvents
{
super.nsuiTouchesBegan(touches, withEvent: event)
}
}
open override func nsuiTouchesMoved(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
{
if !_interceptTouchEvents
{
super.nsuiTouchesMoved(touches, withEvent: event)
}
}
open override func nsuiTouchesEnded(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
{
if !_interceptTouchEvents
{
super.nsuiTouchesEnded(touches, withEvent: event)
}
}
open override func nsuiTouchesCancelled(_ touches: Set<NSUITouch>?, withEvent event: NSUIEvent?)
{
if !_interceptTouchEvents
{
super.nsuiTouchesCancelled(touches, withEvent: event)
}
}
}
================================================
FILE: mac/Pods/Charts/Source/Charts/Charts/CombinedChartView.swift
================================================
//
// CombinedChartView.swift
// Charts
//
// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
import Foundation
import CoreGraphics
/// This chart class allows the combination of lines, bars, scatter and candle data all displayed in one chart area.
open class CombinedChartView: BarLineChartViewBase, CombinedChartDataProvider
{
/// the fill-formatter used for determining the position of the fill-line
internal var _fillFormatter: IFillFormatter!
/// enum that allows to specify the order in which the different data objects for the combined-chart are drawn
@objc(CombinedChartDrawOrder)
public enum DrawOrder: Int
{
case bar
case bubble
case line
case candle
case scatter
}
open override func initialize()
{
super.initialize()
self.highlighter = CombinedHighlighter(chart: self, barDataProvider: self)
// Old default behaviour
self.highlightFullBarEnabled = true
_fillFormatter = DefaultFillFormatter()
renderer = CombinedChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler)
}
open override var data: ChartData?
{
get
{
return super.data
}
set
{
super.data = newValue
self.highlighter = CombinedHighlighter(chart: self, barDataProvider: self)
(renderer as? CombinedChartRenderer)?.createRenderers()
renderer?.initBuffers()
}
}
@objc open var fillFormatter: IFillFormatter
{
get
{
return _fillFormatter
}
set
{
_fillFormatter = newValue
if _fillFormatter == nil
{
_fillFormatter = DefaultFillFormatter()
}
}
}
/// - Returns: The Highlight object (contains x-index and DataSet index) of the selected value at the given touch point inside the CombinedChart.
open override func getHighlightByTouchPoint(_ pt: CGPoint) -> Highlight?
{
if _data === nil
{
Swift.print("Can't select by touch. No data set.")
return nil
}
guard let h = self.highlighter?.getHighlight(x: pt.x, y: pt.y)
else { return nil }
if !isHighlightFullBarEnabled { return h }
// For isHighlightFullBarEnabled, remove stackIndex
return Highlight(
x: h.x, y: h.y,
xPx: h.xPx, yPx: h.yPx,
dataIndex: h.dataIndex,
dataSetIndex: h.dataSetIndex,
stackIndex: -1,
axis: h.axis)
}
// MARK: - CombinedChartDataProvider
open var combinedData: CombinedChartData?
{
get
{
return _data as? CombinedChartData
}
}
// MARK: - LineChartDataProvider
open var lineData: LineChartData?
{
get
{
return combinedData?.lineData
}
}
// MARK: - BarChartDataProvider
open var barData: BarChartData?
{
get
{
return combinedData?.barData
}
}
// MARK: - ScatterChartDataProvider
open var scatterData: ScatterChartData?
{
get
{
return combinedData?.scatterData
}
}
// MARK: - CandleChartDataProvider
open var candleData: CandleChartData?
{
get
{
return combinedData?.candleData
}
}
// MARK: - BubbleChartDataProvider
open var bubbleData: BubbleChartData?
{
get
{
return combinedData?.bubbleData
}
}
// MARK: - Accessors
/// if set to true, all values are drawn above their bars, instead of below their top
@objc open var drawValueAboveBarEnabled: Bool
{
get { return (renderer as! CombinedChartRenderer).drawValueAboveBarEnabled }
set { (renderer as! CombinedChartRenderer).drawValueAboveBarEnabled = newValue }
}
/// if set to true, a grey area is drawn behind each bar that indicates the maximum value
@objc open var drawBarShadowEnabled: Bool
{
get { return (renderer as! CombinedChartRenderer).drawBarShadowEnabled }
set { (renderer as! CombinedChartRenderer).drawBarShadowEnabled = newValue }
}
/// `true` if drawing values above bars is enabled, `false` ifnot
open var isDrawValueAboveBarEnabled: Bool { return (renderer as! CombinedChartRenderer).drawValueAboveBarEnabled }
/// `true` if drawing shadows (maxvalue) for each bar is enabled, `false` ifnot
open var isDrawBarShadowEnabled: Bool { return (renderer as! CombinedChartRenderer).drawBarShadowEnabled }
/// the order in which the provided data objects should be drawn.
/// The earlier you place them in the provided array, the further they will be in the background.
/// e.g. if you provide [DrawOrder.Bar, DrawOrder.Line], the bars will be drawn behind the lines.
@objc open var drawOrder: [Int]
{
get
{
return (renderer as! CombinedChartRenderer).drawOrder.map { $0.rawValue }
}
set
{
(renderer as! CombinedChartRenderer).drawOrder = newValue.map { DrawOrder(rawValue: $0)! }
}
}
/// Set this to `true` to make the highlight operation full-bar oriented, `false` to make it highlight single values
@objc open var highlightFullBarEnabled: Bool = false
/// `true` the highlight is be full-bar oriented, `false` ifsingle-value
open var isHighlightFullBarEnabled: Bool { return highlightFullBarEnabled }
// MARK: - ChartViewBase
/// draws all MarkerViews on the highlighted positions
override func drawMarkers(context: CGContext)
{
guard
let marker = marker,
isDrawMarkersEnabled && valuesToHighlight()
else { return }
for i in 0 ..< _indicesToHighlight.count
{
let highlight = _indicesToHighlight[i]
guard
let set = combinedData?.getDataSetByHighlight(highlight),
let e = _data?.entryForHighlight(highlight)
else { continue }
let entryIndex = set.entryIndex(entry: e)
if entryIndex > Int(Double(set.entryCount) * _animator.phaseX)
{
continue
}
let pos = getMarkerPosition(highlight: highlight)
// check bounds
if !_viewPortHandler.isInBounds(x: pos.x, y: pos.y)
{
continue
}
// callbacks to update the content
marker.refreshContent(entry: e, highlight: highlight)
// draw the marker
marker.draw(context: context, point: pos)
}
}
}
================================================
FILE: mac/Pods/Charts/Source/Charts/Charts/HorizontalBarChartView.swift
================================================
//
// HorizontalBarChartView.swift
// Charts
//
// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//
import Foundation
import CoreGraphics
/// BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched.
open class HorizontalBarChartView: BarChartView
{
internal override func initialize()
{
super.initialize()
_leftAxisTransformer = TransformerHorizontalBarChart(viewPortHandler: _viewPortHandler)
_rightAxisTransformer = TransformerHorizontalBarChart(viewPortHandler: _viewPortHandler)
renderer = HorizontalBarChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler)
leftYAxisRenderer = YAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, yAxis: leftAxis, transformer: _leftAxisTransformer)
rightYAxisRenderer = YAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, yAxis: rightAxis, transformer: _rightAxisTransformer)
xAxisRenderer = XAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer, chart: self)
self.highlighter = HorizontalBarHighlighter(chart: self)
}
internal override func calculateLegendOffsets(offsetLeft: inout CGFloat, offsetTop: inout CGFloat, offsetRight: inout CGFloat, offsetBottom: inout CGFloat)
{
guard
let legend = _legend,
legend.isEnabled,
!legend.drawInside
else { return }
// setup offsets for legend
switch legend.orientation
{
case .vertical:
switch legend.horizontalAlignment
{
case .left:
offsetLeft += min(legend.neededWidth, _viewPortHandler.chartWidth * legend.maxSizePercent) + legend.xOffset
case .right:
offsetRight += min(legend.neededWidth, _viewPortHandler.chartWidth * legend.maxSizePercent) + legend.xOffset
case .center:
switch legend.verticalAlignment
{
case .top:
offsetTop += min(legend.neededHeight, _viewPortHandler.chartHeight * legend.maxSizePercent) + legend.yOffset
case .bottom:
offsetBottom += min(legend.neededHeight, _viewPortHandler.chartHeight * legend.maxSizePercent) + legend.yOffset
default:
break
}
}
case .horizontal:
switch legend.verticalAlignment
{
case .top:
offsetTop += min(legend.neededHeight, _viewPortHandler.chartHeight * legend.maxSizePercent) + legend.yOffset
// left axis equals the top x-axis in a horizontal chart
if leftAxis.isEnabled && leftAxis.isDrawLabelsEnabled
{
offsetTop += leftAxis.getRequiredHeightSpace()
}
case .bottom:
offsetBottom += min(legend.neededHeight, _viewPortHandler.chartHeight * legend.maxSizePercent) + legend.yOffset
// right axis equals the bottom x-axis in a horizontal chart
if rightAxis.isEnabled && rightAxis.isDrawLabelsEnabled
{
offsetBottom += rightAxis.getRequiredHeightSpace()
}
default:
break
}
}
}
internal override func calculateOffsets()
{
var offsetLeft: CGFloat = 0.0,
offsetRight: CGFloat = 0.0,
offsetTop: CGFloat = 0.0,
offsetBottom: CGFloat = 0.0
calculateLegendOffsets(offsetLeft: &offsetLeft,
offsetTop: &offsetTop,
offsetRight: &offsetRight,
offsetBottom: &offsetBottom)
// offsets for y-labels
if leftAxis.needsOffset
{
offsetTop += leftAxis.getRequiredHeightSpace()
}
if rightAxis.needsOffset
{
offsetBottom += rightAxis.getRequiredHeightSpace()
}
let xlabelwidth = _xAxis.labelRotatedWidth
if _xAxis.isEnabled
{
// offsets for x-labels
if _xAxis.labelPosition == .bottom
{
offsetLeft += xlabelwidth
}
else if _xAxis.labelPosition == .top
{
offsetRight += xlabelwidth
}
else if _xAxis.labelPosition == .bothSided
{
offsetLeft += xlabelwidth
offsetRight += xlabelwidth
}
}
offsetTop += self.extraTopOffset
offsetRight += self.extraRightOffset
offsetBottom += self.extraBottomOffset
offsetLeft += self.extraLeftOffset
_viewPortHandler.restrainViewPort(
offsetLeft: max(self.minOffset, offsetLeft),
offsetTop: max(self.minOffset, offsetTop),
offsetRight: max(self.minOffset, offsetRight),
offsetBottom: max(self.minOffset, offsetBottom))
prepareOffsetMatrix()
prepareValuePxMatrix()
}
internal override func prepareValuePxMatrix()
{
_rightAxisTransformer.prepareMatrixValuePx(chartXMin: rightAxis._axisMinimum, deltaX: CGFloat(rightAxis.axisRange), deltaY: CGFloat(_xAxis.axisRange), chartYMin: _xAxis._axisMinimum)
_leftAxisTransformer.prepareMatrixValuePx(chartXMin: leftAxis._axisMinimum, deltaX: CGFloat(leftAxis.axisRange), deltaY: CGFloat(_xAxis.axisRange), chartYMin: _xAxis._axisMinimum)
}
open override func getMarkerPosition(highlight: Highlight) -> CGPoint
{
return CGPoint(x: highlight.drawY, y: highlight.drawX)
}
open override func getBarBounds(entry e: BarChartDataEntry) -> CGRect
{
guard
let data = _data as? BarChartData,
let set = data.getDataSetForEntry(e) as? IBarChartDataSet
else { return CGRect.null }
let y = e.y
let x = e.x
let barWidth = data.barWidth
let top = x - 0.5 + barWidth / 2.0
let bottom = x + 0.5 - barWidth / 2.0
let left = y >= 0.0 ? y : 0.0
let right = y
gitextract_yvxi4i9t/ ├── .github/ │ └── workflows/ │ ├── build_app.yml │ └── make-test.yml ├── .gitignore ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── keyrace.c ├── mac/ │ ├── Podfile │ ├── Pods/ │ │ ├── Charts/ │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── Source/ │ │ │ └── Charts/ │ │ │ ├── Animation/ │ │ │ │ ├── Animator.swift │ │ │ │ └── ChartAnimationEasing.swift │ │ │ ├── Charts/ │ │ │ │ ├── BarChartView.swift │ │ │ │ ├── BarLineChartViewBase.swift │ │ │ │ ├── BubbleChartView.swift │ │ │ │ ├── CandleStickChartView.swift │ │ │ │ ├── ChartViewBase.swift │ │ │ │ ├── CombinedChartView.swift │ │ │ │ ├── HorizontalBarChartView.swift │ │ │ │ ├── LineChartView.swift │ │ │ │ ├── PieChartView.swift │ │ │ │ ├── PieRadarChartViewBase.swift │ │ │ │ ├── RadarChartView.swift │ │ │ │ └── ScatterChartView.swift │ │ │ ├── Components/ │ │ │ │ ├── AxisBase.swift │ │ │ │ ├── ChartLimitLine.swift │ │ │ │ ├── ComponentBase.swift │ │ │ │ ├── Description.swift │ │ │ │ ├── IMarker.swift │ │ │ │ ├── Legend.swift │ │ │ │ ├── LegendEntry.swift │ │ │ │ ├── MarkerImage.swift │ │ │ │ ├── MarkerView.swift │ │ │ │ ├── XAxis.swift │ │ │ │ └── YAxis.swift │ │ │ ├── Data/ │ │ │ │ ├── Implementations/ │ │ │ │ │ ├── ChartBaseDataSet.swift │ │ │ │ │ └── Standard/ │ │ │ │ │ ├── BarChartData.swift │ │ │ │ │ ├── BarChartDataEntry.swift │ │ │ │ │ ├── BarChartDataSet.swift │ │ │ │ │ ├── BarLineScatterCandleBubbleChartData.swift │ │ │ │ │ ├── BarLineScatterCandleBubbleChartDataSet.swift │ │ │ │ │ ├── BubbleChartData.swift │ │ │ │ │ ├── BubbleChartDataEntry.swift │ │ │ │ │ ├── BubbleChartDataSet.swift │ │ │ │ │ ├── CandleChartData.swift │ │ │ │ │ ├── CandleChartDataEntry.swift │ │ │ │ │ ├── CandleChartDataSet.swift │ │ │ │ │ ├── ChartData.swift │ │ │ │ │ ├── ChartDataEntry.swift │ │ │ │ │ ├── ChartDataEntryBase.swift │ │ │ │ │ ├── ChartDataSet.swift │ │ │ │ │ ├── CombinedChartData.swift │ │ │ │ │ ├── LineChartData.swift │ │ │ │ │ ├── LineChartDataSet.swift │ │ │ │ │ ├── LineRadarChartDataSet.swift │ │ │ │ │ ├── LineScatterCandleRadarChartDataSet.swift │ │ │ │ │ ├── PieChartData.swift │ │ │ │ │ ├── PieChartDataEntry.swift │ │ │ │ │ ├── PieChartDataSet.swift │ │ │ │ │ ├── RadarChartData.swift │ │ │ │ │ ├── RadarChartDataEntry.swift │ │ │ │ │ ├── RadarChartDataSet.swift │ │ │ │ │ ├── ScatterChartData.swift │ │ │ │ │ └── ScatterChartDataSet.swift │ │ │ │ └── Interfaces/ │ │ │ │ ├── IBarChartDataSet.swift │ │ │ │ ├── IBarLineScatterCandleBubbleChartDataSet.swift │ │ │ │ ├── IBubbleChartDataSet.swift │ │ │ │ ├── ICandleChartDataSet.swift │ │ │ │ ├── IChartDataSet.swift │ │ │ │ ├── ILineChartDataSet.swift │ │ │ │ ├── ILineRadarChartDataSet.swift │ │ │ │ ├── ILineScatterCandleRadarChartDataSet.swift │ │ │ │ ├── IPieChartDataSet.swift │ │ │ │ ├── IRadarChartDataSet.swift │ │ │ │ └── IScatterChartDataSet.swift │ │ │ ├── Filters/ │ │ │ │ ├── DataApproximator+N.swift │ │ │ │ └── DataApproximator.swift │ │ │ ├── Formatters/ │ │ │ │ ├── DefaultAxisValueFormatter.swift │ │ │ │ ├── DefaultFillFormatter.swift │ │ │ │ ├── DefaultValueFormatter.swift │ │ │ │ ├── IAxisValueFormatter.swift │ │ │ │ ├── IFillFormatter.swift │ │ │ │ ├── IValueFormatter.swift │ │ │ │ └── IndexAxisValueFormatter.swift │ │ │ ├── Highlight/ │ │ │ │ ├── BarHighlighter.swift │ │ │ │ ├── ChartHighlighter.swift │ │ │ │ ├── CombinedHighlighter.swift │ │ │ │ ├── Highlight.swift │ │ │ │ ├── HorizontalBarHighlighter.swift │ │ │ │ ├── IHighlighter.swift │ │ │ │ ├── PieHighlighter.swift │ │ │ │ ├── PieRadarHighlighter.swift │ │ │ │ ├── RadarHighlighter.swift │ │ │ │ └── Range.swift │ │ │ ├── Interfaces/ │ │ │ │ ├── BarChartDataProvider.swift │ │ │ │ ├── BarLineScatterCandleBubbleChartDataProvider.swift │ │ │ │ ├── BubbleChartDataProvider.swift │ │ │ │ ├── CandleChartDataProvider.swift │ │ │ │ ├── ChartDataProvider.swift │ │ │ │ ├── CombinedChartDataProvider.swift │ │ │ │ ├── LineChartDataProvider.swift │ │ │ │ └── ScatterChartDataProvider.swift │ │ │ ├── Jobs/ │ │ │ │ ├── AnimatedMoveViewJob.swift │ │ │ │ ├── AnimatedViewPortJob.swift │ │ │ │ ├── AnimatedZoomViewJob.swift │ │ │ │ ├── MoveViewJob.swift │ │ │ │ ├── ViewPortJob.swift │ │ │ │ └── ZoomViewJob.swift │ │ │ ├── Renderers/ │ │ │ │ ├── AxisRendererBase.swift │ │ │ │ ├── BarChartRenderer.swift │ │ │ │ ├── BarLineScatterCandleBubbleRenderer.swift │ │ │ │ ├── BubbleChartRenderer.swift │ │ │ │ ├── CandleStickChartRenderer.swift │ │ │ │ ├── ChartDataRendererBase.swift │ │ │ │ ├── CombinedChartRenderer.swift │ │ │ │ ├── HorizontalBarChartRenderer.swift │ │ │ │ ├── LegendRenderer.swift │ │ │ │ ├── LineChartRenderer.swift │ │ │ │ ├── LineRadarRenderer.swift │ │ │ │ ├── LineScatterCandleRadarRenderer.swift │ │ │ │ ├── PieChartRenderer.swift │ │ │ │ ├── RadarChartRenderer.swift │ │ │ │ ├── Renderer.swift │ │ │ │ ├── Scatter/ │ │ │ │ │ ├── ChevronDownShapeRenderer.swift │ │ │ │ │ ├── ChevronUpShapeRenderer.swift │ │ │ │ │ ├── CircleShapeRenderer.swift │ │ │ │ │ ├── CrossShapeRenderer.swift │ │ │ │ │ ├── IShapeRenderer.swift │ │ │ │ │ ├── SquareShapeRenderer.swift │ │ │ │ │ ├── TriangleShapeRenderer.swift │ │ │ │ │ └── XShapeRenderer.swift │ │ │ │ ├── ScatterChartRenderer.swift │ │ │ │ ├── XAxisRenderer.swift │ │ │ │ ├── XAxisRendererHorizontalBarChart.swift │ │ │ │ ├── XAxisRendererRadarChart.swift │ │ │ │ ├── YAxisRenderer.swift │ │ │ │ ├── YAxisRendererHorizontalBarChart.swift │ │ │ │ └── YAxisRendererRadarChart.swift │ │ │ └── Utils/ │ │ │ ├── ChartColorTemplates.swift │ │ │ ├── ChartUtils.swift │ │ │ ├── Fill.swift │ │ │ ├── Platform+Accessibility.swift │ │ │ ├── Platform+Color.swift │ │ │ ├── Platform+Gestures.swift │ │ │ ├── Platform+Graphics.swift │ │ │ ├── Platform+Touch Handling.swift │ │ │ ├── Platform.swift │ │ │ ├── Transformer.swift │ │ │ ├── TransformerHorizontalBarChart.swift │ │ │ └── ViewPortHandler.swift │ │ ├── Pods.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ ├── Charts.xcscheme │ │ │ └── Pods-keyrace-mac.xcscheme │ │ └── Target Support Files/ │ │ ├── Charts/ │ │ │ ├── Charts-Info.plist │ │ │ ├── Charts-dummy.m │ │ │ ├── Charts-prefix.pch │ │ │ ├── Charts-umbrella.h │ │ │ ├── Charts.debug.xcconfig │ │ │ ├── Charts.modulemap │ │ │ └── Charts.release.xcconfig │ │ └── Pods-keyrace-mac/ │ │ ├── Pods-keyrace-mac-Info.plist │ │ ├── Pods-keyrace-mac-acknowledgements.markdown │ │ ├── Pods-keyrace-mac-acknowledgements.plist │ │ ├── Pods-keyrace-mac-dummy.m │ │ ├── Pods-keyrace-mac-frameworks-Debug-input-files.xcfilelist │ │ ├── Pods-keyrace-mac-frameworks-Debug-output-files.xcfilelist │ │ ├── Pods-keyrace-mac-frameworks-Release-input-files.xcfilelist │ │ ├── Pods-keyrace-mac-frameworks-Release-output-files.xcfilelist │ │ ├── Pods-keyrace-mac-frameworks.sh │ │ ├── Pods-keyrace-mac-umbrella.h │ │ ├── Pods-keyrace-mac.debug.xcconfig │ │ ├── Pods-keyrace-mac.modulemap │ │ └── Pods-keyrace-mac.release.xcconfig │ ├── keyrace-mac/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AccentColor.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ └── magic-keyboard.imageset/ │ │ │ └── Contents.json │ │ ├── ContentView.swift │ │ ├── GitHub.swift │ │ ├── Info.plist │ │ ├── KeyTap.swift │ │ ├── KeyboardView.swift │ │ ├── LeaderboardView.swift │ │ ├── MenuView.swift │ │ ├── Preview Content/ │ │ │ └── Preview Assets.xcassets/ │ │ │ └── Contents.json │ │ ├── SettingsView.swift │ │ ├── TypingChart.swift │ │ ├── UserDefaults.swift │ │ └── keyrace_mac.entitlements │ ├── keyrace-mac.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── keyrace-mac.xcscheme │ └── keyrace-mac.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist └── server.go
SYMBOL INDEX (19 symbols across 2 files)
FILE: keyrace.c
function update_savefile (line 27) | void update_savefile(int kc)
function load_savefile (line 35) | int load_savefile(void)
function upload_count (line 57) | void upload_count(char *name, int count)
function CGEventRef (line 65) | CGEventRef CGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv...
function strclean (line 98) | void strclean(char *src)
function run_loop (line 110) | void run_loop(void)
function main (line 132) | int main(int argc, char **argv)
FILE: server.go
type PlayerScore (line 26) | type PlayerScore struct
type Player (line 32) | type Player struct
method upsertInDB (line 41) | func (p *Player) upsertInDB() error {
method getLeaderboard (line 167) | func (p Player) getLeaderboard(onlyFollows bool) string {
method setGitHubDataFromToken (line 261) | func (p *Player) setGitHubDataFromToken() {
method doGitHubCall (line 287) | func (p Player) doGitHubCall(endpoint string) *http.Response {
function count (line 106) | func count(w http.ResponseWriter, req *http.Request) {
function removeFromSlice (line 210) | func removeFromSlice(slice []Player, index int) []Player {
function getStringParam (line 216) | func getStringParam(req *http.Request, key string) string {
function humanTime (line 228) | func humanTime(t time.Time) string {
type GitHubUser (line 254) | type GitHubUser struct
function main (line 306) | func main() {
Condensed preview — 198 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,185K chars).
[
{
"path": ".github/workflows/build_app.yml",
"chars": 515,
"preview": "name: build mac app\n\non: [push, pull_request]\n\njobs:\n build:\n runs-on: macOS-latest\n steps:\n - name: Checkou"
},
{
"path": ".github/workflows/make-test.yml",
"chars": 418,
"preview": "on: [push, pull_request]\nname: make test\njobs:\n maketest:\n name: make test\n runs-on: ubuntu-latest\n steps:\n "
},
{
"path": ".gitignore",
"chars": 641,
"preview": "# Xcode .gitignore\n#\n# Source:\n# https://github.com/github/gitignore/blob/master/Global/Xcode.gitignore\n\n## User setting"
},
{
"path": "Makefile",
"chars": 1197,
"preview": "SERVER=keyrace.app\nBUILDTAGS=libsqlite3 sqlite_omit_load_extension\n\nkeyrace-server: $(wildcard *.go)\n\tgo mod vendor || t"
},
{
"path": "README.md",
"chars": 537,
"preview": "# keyrace\nDaily multiplayer keyboard races for macos\n\n<img width=\"383\" alt=\"image\" src=\"https://user-images.githubuserco"
},
{
"path": "go.mod",
"chars": 392,
"preview": "module github.com/nat/keyrace\n\ngo 1.14\n\nrequire (\n\tgithub.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect\n\tgith"
},
{
"path": "go.sum",
"chars": 12309,
"preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-2"
},
{
"path": "keyrace.c",
"chars": 3868,
"preview": "// NOTE: This client no longer works. Run the native mac client instead!\n//\n// gcc keyrace.c -o keyrace -framework Appli"
},
{
"path": "mac/Podfile",
"chars": 81,
"preview": "use_frameworks!\ninhibit_all_warnings!\n\ntarget 'keyrace-mac' do\n\tpod 'Charts'\nend\n"
},
{
"path": "mac/Pods/Charts/LICENSE",
"chars": 11338,
"preview": "Apache License\n Version 2.0, January 2004\n http://www.apache.org/licens"
},
{
"path": "mac/Pods/Charts/README.md",
"chars": 12863,
"preview": "**Version 3.5.0**, synced to [MPAndroidChart #f6a398b](https://github.com/PhilJay/MPAndroidChart/commit/f6a398b)\n\n![alt "
},
{
"path": "mac/Pods/Charts/Source/Charts/Animation/Animator.swift",
"chars": 10635,
"preview": "//\n// Animator.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart "
},
{
"path": "mac/Pods/Charts/Source/Charts/Animation/ChartAnimationEasing.swift",
"chars": 13524,
"preview": "//\n// ChartAnimationUtils.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/BarChartView.swift",
"chars": 6740,
"preview": "//\n// BarChartView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCh"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/BarLineChartViewBase.swift",
"chars": 69393,
"preview": "//\n// BarLineChartViewBase.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPA"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/BubbleChartView.swift",
"chars": 650,
"preview": "//\n// BubbleChartView.swift\n// Charts\n//\n// Bubble chart implementation:\n// Copyright 2015 Pierre-Marc Airoldi\n// "
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/CandleStickChartView.swift",
"chars": 829,
"preview": "//\n// CandleStickChartView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPA"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/ChartViewBase.swift",
"chars": 37355,
"preview": "//\n// ChartViewBase.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidC"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/CombinedChartView.swift",
"chars": 7226,
"preview": "//\n// CombinedChartView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/HorizontalBarChartView.swift",
"chars": 9766,
"preview": "//\n// HorizontalBarChartView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of M"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/LineChartView.swift",
"chars": 705,
"preview": "//\n// LineChartView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidC"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/PieChartView.swift",
"chars": 18712,
"preview": "//\n// PieChartView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCh"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/PieRadarChartViewBase.swift",
"chars": 27158,
"preview": "//\n// PieRadarChartViewBase.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MP"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/RadarChartView.swift",
"chars": 6951,
"preview": "//\n// RadarChartView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Charts/ScatterChartView.swift",
"chars": 824,
"preview": "//\n// ScatterChartView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/AxisBase.swift",
"chars": 12198,
"preview": "//\n// AxisBase.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart "
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/ChartLimitLine.swift",
"chars": 1896,
"preview": "//\n// ChartLimitLine.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/ComponentBase.swift",
"chars": 901,
"preview": "//\n// ComponentBase.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidC"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/Description.swift",
"chars": 1315,
"preview": "//\n// Description.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCha"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/IMarker.swift",
"chars": 1741,
"preview": "//\n// ChartMarker.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCha"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/Legend.swift",
"chars": 14379,
"preview": "//\n// Legend.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart fo"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/LegendEntry.swift",
"chars": 2971,
"preview": "//\n// LegendEntry.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCha"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/MarkerImage.swift",
"chars": 2585,
"preview": "//\n// ChartMarkerImage.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/MarkerView.swift",
"chars": 2549,
"preview": "//\n// ChartMarkerView.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroi"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/XAxis.swift",
"chars": 2511,
"preview": "//\n// XAxis.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart for"
},
{
"path": "mac/Pods/Charts/Source/Charts/Components/YAxis.swift",
"chars": 6039,
"preview": "//\n// YAxis.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart for"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/ChartBaseDataSet.swift",
"chars": 12991,
"preview": "//\n// BaseDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCha"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/BarChartData.swift",
"chars": 3359,
"preview": "//\n// BarChartData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCh"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/BarChartDataEntry.swift",
"chars": 5902,
"preview": "//\n// BarChartDataEntry.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/BarChartDataSet.swift",
"chars": 4857,
"preview": "//\n// BarChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroi"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/BarLineScatterCandleBubbleChartData.swift",
"chars": 492,
"preview": "//\n// BarLineScatterCandleBubbleChartData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n//"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/BarLineScatterCandleBubbleChartDataSet.swift",
"chars": 1189,
"preview": "//\n// BarLineScatterCandleBubbleChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/BubbleChartData.swift",
"chars": 782,
"preview": "//\n// BubbleChartData.swift\n// Charts\n//\n// Bubble chart implementation:\n// Copyright 2015 Pierre-Marc Airoldi\n// "
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/BubbleChartDataEntry.swift",
"chars": 2109,
"preview": "//\n// BubbleDataEntry.swift\n// Charts\n//\n// Bubble chart implementation: \n// Copyright 2015 Pierre-Marc Airoldi\n//"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/BubbleChartDataSet.swift",
"chars": 1517,
"preview": "//\n// BubbleChartDataSet.swift\n// Charts\n//\n// Bubble chart implementation:\n// Copyright 2015 Pierre-Marc Airoldi\n"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/CandleChartData.swift",
"chars": 478,
"preview": "//\n// CandleChartData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroi"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/CandleChartDataEntry.swift",
"chars": 2543,
"preview": "//\n// CandleChartDataEntry.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPA"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/CandleChartDataSet.swift",
"chars": 3286,
"preview": "//\n// CandleChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAnd"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/ChartData.swift",
"chars": 18034,
"preview": "//\n// ChartData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/ChartDataEntry.swift",
"chars": 2605,
"preview": "//\n// ChartDataEntry.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/ChartDataEntryBase.swift",
"chars": 2141,
"preview": "//\n// ChartDataEntryBase.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAnd"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift",
"chars": 17565,
"preview": "//\n// ChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCh"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/CombinedChartData.swift",
"chars": 7418,
"preview": "//\n// CombinedChartData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/LineChartData.swift",
"chars": 520,
"preview": "//\n// LineChartData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidC"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/LineChartDataSet.swift",
"chars": 5082,
"preview": "//\n// LineChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/LineRadarChartDataSet.swift",
"chars": 2324,
"preview": "//\n// LineRadarChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MP"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/LineScatterCandleRadarChartDataSet.swift",
"chars": 1877,
"preview": "//\n// LineScatterCandleRadarChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// "
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/PieChartData.swift",
"chars": 2996,
"preview": "//\n// PieData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart f"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/PieChartDataEntry.swift",
"chars": 3098,
"preview": "//\n// PieChartDataEntry.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/PieChartDataSet.swift",
"chars": 4047,
"preview": "//\n// PieChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroi"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/RadarChartData.swift",
"chars": 1271,
"preview": "//\n// RadarChartData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/RadarChartDataEntry.swift",
"chars": 1140,
"preview": "//\n// RadarChartDataEntry.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/RadarChartDataSet.swift",
"chars": 1580,
"preview": "//\n// RadarChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/ScatterChartData.swift",
"chars": 788,
"preview": "//\n// ScatterChartData.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Implementations/Standard/ScatterChartDataSet.swift",
"chars": 2481,
"preview": "//\n// ScatterChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/IBarChartDataSet.swift",
"chars": 1423,
"preview": "//\n// IBarChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/IBarLineScatterCandleBubbleChartDataSet.swift",
"chars": 670,
"preview": "//\n// IBarLineScatterCandleBubbleChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahod"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/IBubbleChartDataSet.swift",
"chars": 671,
"preview": "//\n// IBubbleChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/ICandleChartDataSet.swift",
"chars": 1822,
"preview": "//\n// ICandleChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/IChartDataSet.swift",
"chars": 11269,
"preview": "//\n// IChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidC"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/ILineChartDataSet.swift",
"chars": 2653,
"preview": "//\n// ILineChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/ILineRadarChartDataSet.swift",
"chars": 1400,
"preview": "//\n// ILineRadarChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of M"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/ILineScatterCandleRadarChartDataSet.swift",
"chars": 1242,
"preview": "//\n// ILineScatterCandleRadarChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n//"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/IPieChartDataSet.swift",
"chars": 2145,
"preview": "//\n// IPieChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/IRadarChartDataSet.swift",
"chars": 1085,
"preview": "//\n// IRadarChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAnd"
},
{
"path": "mac/Pods/Charts/Source/Charts/Data/Interfaces/IScatterChartDataSet.swift",
"chars": 1050,
"preview": "//\n// IScatterChartDataSet.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPA"
},
{
"path": "mac/Pods/Charts/Source/Charts/Filters/DataApproximator+N.swift",
"chars": 4648,
"preview": "//\n// DataApproximator+N.swift\n// Charts\n//\n// Created by M Ivaniushchenko on 9/6/17.\n// Licensed under Apache Licen"
},
{
"path": "mac/Pods/Charts/Source/Charts/Filters/DataApproximator.swift",
"chars": 3289,
"preview": "//\n// DataApproximator.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Formatters/DefaultAxisValueFormatter.swift",
"chars": 2380,
"preview": "//\n// DefaultAxisValueFormatter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port o"
},
{
"path": "mac/Pods/Charts/Source/Charts/Formatters/DefaultFillFormatter.swift",
"chars": 1515,
"preview": "//\n// DefaultFillFormatter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPA"
},
{
"path": "mac/Pods/Charts/Source/Charts/Formatters/DefaultValueFormatter.swift",
"chars": 2536,
"preview": "//\n// DefaultValueFormatter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MP"
},
{
"path": "mac/Pods/Charts/Source/Charts/Formatters/IAxisValueFormatter.swift",
"chars": 897,
"preview": "//\n// IAxisValueFormatter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Formatters/IFillFormatter.swift",
"chars": 692,
"preview": "//\n// IFillFormatter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Formatters/IValueFormatter.swift",
"chars": 1346,
"preview": "//\n// IValueFormatter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroi"
},
{
"path": "mac/Pods/Charts/Source/Charts/Formatters/IndexAxisValueFormatter.swift",
"chars": 1357,
"preview": "//\n// IndexAxisValueFormatter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of "
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/BarHighlighter.swift",
"chars": 3450,
"preview": "//\n// BarHighlighter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/ChartHighlighter.swift",
"chars": 6260,
"preview": "//\n// ChartHighlighter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/CombinedHighlighter.swift",
"chars": 2187,
"preview": "//\n// CombinedHighlighter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/Highlight.swift",
"chars": 6677,
"preview": "//\n// Highlight.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/HorizontalBarHighlighter.swift",
"chars": 2115,
"preview": "//\n// HorizontalBarHighlighter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/IHighlighter.swift",
"chars": 537,
"preview": "//\n// IHighlighter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCh"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/PieHighlighter.swift",
"chars": 714,
"preview": "//\n// PieHighlighter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/PieRadarHighlighter.swift",
"chars": 1673,
"preview": "//\n// PieRadarHighlighter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/RadarHighlighter.swift",
"chars": 2466,
"preview": "//\n// RadarHighlighter.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Highlight/Range.swift",
"chars": 1039,
"preview": "//\n// Range.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart for"
},
{
"path": "mac/Pods/Charts/Source/Charts/Interfaces/BarChartDataProvider.swift",
"chars": 543,
"preview": "//\n// BarChartDataProvider.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPA"
},
{
"path": "mac/Pods/Charts/Source/Charts/Interfaces/BarLineScatterCandleBubbleChartDataProvider.swift",
"chars": 588,
"preview": "//\n// BarLineScatterCandleBubbleChartDataProvider.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp J"
},
{
"path": "mac/Pods/Charts/Source/Charts/Interfaces/BubbleChartDataProvider.swift",
"chars": 408,
"preview": "//\n// BubbleChartDataProvider.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of "
},
{
"path": "mac/Pods/Charts/Source/Charts/Interfaces/CandleChartDataProvider.swift",
"chars": 408,
"preview": "//\n// CandleChartDataProvider.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of "
},
{
"path": "mac/Pods/Charts/Source/Charts/Interfaces/ChartDataProvider.swift",
"chars": 977,
"preview": "//\n// ChartDataProvider.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Interfaces/CombinedChartDataProvider.swift",
"chars": 492,
"preview": "//\n// CombinedChartDataProvider.swoft\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port o"
},
{
"path": "mac/Pods/Charts/Source/Charts/Interfaces/LineChartDataProvider.swift",
"chars": 462,
"preview": "//\n// LineChartDataProvider.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MP"
},
{
"path": "mac/Pods/Charts/Source/Charts/Interfaces/ScatterChartDataProvider.swift",
"chars": 412,
"preview": "//\n// ScatterChartDataProvider.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of"
},
{
"path": "mac/Pods/Charts/Source/Charts/Jobs/AnimatedMoveViewJob.swift",
"chars": 822,
"preview": "//\n// AnimatedMoveViewJob.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Jobs/AnimatedViewPortJob.swift",
"chars": 3005,
"preview": "//\n// AnimatedViewPortJob.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Jobs/AnimatedZoomViewJob.swift",
"chars": 2959,
"preview": "//\n// AnimatedZoomViewJob.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Jobs/MoveViewJob.swift",
"chars": 734,
"preview": "//\n// MoveViewJob.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCha"
},
{
"path": "mac/Pods/Charts/Source/Charts/Jobs/ViewPortJob.swift",
"chars": 1180,
"preview": "//\n// ViewPortJob.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCha"
},
{
"path": "mac/Pods/Charts/Source/Charts/Jobs/ZoomViewJob.swift",
"chars": 2090,
"preview": "//\n// ZoomViewJob.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCha"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/AxisRendererBase.swift",
"chars": 6939,
"preview": "//\n// AxisRendererBase.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/BarChartRenderer.swift",
"chars": 39137,
"preview": "//\n// BarChartRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/BarLineScatterCandleBubbleRenderer.swift",
"chars": 4474,
"preview": "//\n// BarLineScatterCandleBubbleRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// "
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/BubbleChartRenderer.swift",
"chars": 14487,
"preview": "//\n// BubbleChartRenderer.swift\n// Charts\n//\n// Bubble chart implementation:\n// Copyright 2015 Pierre-Marc Airoldi"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/CandleStickChartRenderer.swift",
"chars": 16341,
"preview": "//\n// CandleStickChartRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/ChartDataRendererBase.swift",
"chars": 3829,
"preview": "//\n// DataRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCh"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/CombinedChartRenderer.swift",
"chars": 7141,
"preview": "//\n// CombinedChartRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MP"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/HorizontalBarChartRenderer.swift",
"chars": 25628,
"preview": "//\n// HorizontalBarChartRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port "
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/LegendRenderer.swift",
"chars": 20712,
"preview": "//\n// LegendRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/LineChartRenderer.swift",
"chars": 30352,
"preview": "//\n// LineChartRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/LineRadarRenderer.swift",
"chars": 1550,
"preview": "//\n// LineRadarRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/LineScatterCandleRadarRenderer.swift",
"chars": 1574,
"preview": "//\n// LineScatterCandleRadarRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A p"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/PieChartRenderer.swift",
"chars": 37652,
"preview": "//\n// PieChartRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndro"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/RadarChartRenderer.swift",
"chars": 18229,
"preview": "//\n// RadarChartRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAnd"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Renderer.swift",
"chars": 582,
"preview": "//\n// Renderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart "
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Scatter/ChevronDownShapeRenderer.swift",
"chars": 1112,
"preview": "//\n// ChevronDownShapeRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Scatter/ChevronUpShapeRenderer.swift",
"chars": 1108,
"preview": "//\n// ChevronUpShapeRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of M"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Scatter/CircleShapeRenderer.swift",
"chars": 2172,
"preview": "//\n// CircleShapeRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Scatter/CrossShapeRenderer.swift",
"chars": 1052,
"preview": "//\n// CrossShapeRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAnd"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Scatter/IShapeRenderer.swift",
"chars": 882,
"preview": "//\n// IShapeRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Scatter/SquareShapeRenderer.swift",
"chars": 2139,
"preview": "//\n// SquareShapeRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Scatter/TriangleShapeRenderer.swift",
"chars": 2553,
"preview": "//\n// TriangleShapeRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MP"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/Scatter/XShapeRenderer.swift",
"chars": 1092,
"preview": "//\n// XShapeRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/ScatterChartRenderer.swift",
"chars": 9157,
"preview": "//\n// ScatterChartRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPA"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/XAxisRenderer.swift",
"chars": 15171,
"preview": "//\n// XAxisRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidC"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/XAxisRendererHorizontalBarChart.swift",
"chars": 12730,
"preview": "//\n// XAxisRendererHorizontalBarChart.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A "
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/XAxisRendererRadarChart.swift",
"chars": 2902,
"preview": "//\n// XAxisRendererRadarChart.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of "
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/YAxisRenderer.swift",
"chars": 12761,
"preview": "//\n// YAxisRenderer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidC"
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/YAxisRendererHorizontalBarChart.swift",
"chars": 12406,
"preview": "//\n// YAxisRendererHorizontalBarChart.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A "
},
{
"path": "mac/Pods/Charts/Source/Charts/Renderers/YAxisRendererRadarChart.swift",
"chars": 8241,
"preview": "//\n// YAxisRendererRadarChart.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of "
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/ChartColorTemplates.swift",
"chars": 7888,
"preview": "//\n// ChartColorTemplates.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAn"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/ChartUtils.swift",
"chars": 9708,
"preview": "//\n// Utils.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart for"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/Fill.swift",
"chars": 8430,
"preview": "//\n// Fill.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidChart for "
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/Platform+Accessibility.swift",
"chars": 5951,
"preview": "//\n// Platform+Accessibility.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of M"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/Platform+Color.swift",
"chars": 995,
"preview": "//\n// Platform+Color.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroid"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/Platform+Gestures.swift",
"chars": 4159,
"preview": "//\n// Platform+Gestures.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/Platform+Graphics.swift",
"chars": 4097,
"preview": "//\n// Platform+Graphics.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndr"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/Platform+Touch Handling.swift",
"chars": 3615,
"preview": "//\n// Platform+Touch Handling.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of "
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/Platform.swift",
"chars": 5326,
"preview": "import Foundation\n\n/** This file provides a thin abstraction layer atop of UIKit (iOS, tvOS) and Cocoa (OS X). The two A"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/Transformer.swift",
"chars": 5690,
"preview": "//\n// Transformer.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroidCha"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/TransformerHorizontalBarChart.swift",
"chars": 999,
"preview": "//\n// TransformerHorizontalBarChart.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A po"
},
{
"path": "mac/Pods/Charts/Source/Charts/Utils/ViewPortHandler.swift",
"chars": 18381,
"preview": "//\n// ViewPortHandler.swift\n// Charts\n//\n// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda\n// A port of MPAndroi"
},
{
"path": "mac/Pods/Pods.xcodeproj/project.pbxproj",
"chars": 109537,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "mac/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Charts.xcscheme",
"chars": 2021,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1240\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "mac/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Pods-keyrace-mac.xcscheme",
"chars": 2041,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1240\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "mac/Pods/Target Support Files/Charts/Charts-Info.plist",
"chars": 828,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "mac/Pods/Target Support Files/Charts/Charts-dummy.m",
"chars": 116,
"preview": "#import <Foundation/Foundation.h>\n@interface PodsDummy_Charts : NSObject\n@end\n@implementation PodsDummy_Charts\n@end\n"
},
{
"path": "mac/Pods/Target Support Files/Charts/Charts-prefix.pch",
"chars": 195,
"preview": "#ifdef __OBJC__\n#import <Cocoa/Cocoa.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
},
{
"path": "mac/Pods/Target Support Files/Charts/Charts-umbrella.h",
"chars": 304,
"preview": "#ifdef __OBJC__\n#import <Cocoa/Cocoa.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
},
{
"path": "mac/Pods/Target Support Files/Charts/Charts.debug.xcconfig",
"chars": 678,
"preview": "CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO\nCODE_SIGN_IDENTITY =\nCONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_B"
},
{
"path": "mac/Pods/Target Support Files/Charts/Charts.modulemap",
"chars": 102,
"preview": "framework module Charts {\n umbrella header \"Charts-umbrella.h\"\n\n export *\n module * { export * }\n}\n"
},
{
"path": "mac/Pods/Target Support Files/Charts/Charts.release.xcconfig",
"chars": 678,
"preview": "CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO\nCODE_SIGN_IDENTITY =\nCONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_B"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-Info.plist",
"chars": 828,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-acknowledgements.markdown",
"chars": 11484,
"preview": "# Acknowledgements\nThis application makes use of the following third party libraries:\n\n## Charts\n\nApache License\n "
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-acknowledgements.plist",
"chars": 12385,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-dummy.m",
"chars": 136,
"preview": "#import <Foundation/Foundation.h>\n@interface PodsDummy_Pods_keyrace_mac : NSObject\n@end\n@implementation PodsDummy_Pods_k"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-frameworks-Debug-input-files.xcfilelist",
"chars": 127,
"preview": "${PODS_ROOT}/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-frameworks.sh\n${BUILT_PRODUCTS_DIR}/Charts/Charts.fr"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-frameworks-Debug-output-files.xcfilelist",
"chars": 62,
"preview": "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Charts.framework"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-frameworks-Release-input-files.xcfilelist",
"chars": 127,
"preview": "${PODS_ROOT}/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-frameworks.sh\n${BUILT_PRODUCTS_DIR}/Charts/Charts.fr"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-frameworks-Release-output-files.xcfilelist",
"chars": 62,
"preview": "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Charts.framework"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-frameworks.sh",
"chars": 8442,
"preview": "#!/bin/sh\nset -e\nset -u\nset -o pipefail\n\nfunction on_error {\n echo \"$(realpath -mq \"${0}\"):$1: error: Unexpected failur"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac-umbrella.h",
"chars": 324,
"preview": "#ifdef __OBJC__\n#import <Cocoa/Cocoa.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac.debug.xcconfig",
"chars": 1005,
"preview": "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES\nCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO\nFRAMEWORK_SEARCH_PATHS = "
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac.modulemap",
"chars": 122,
"preview": "framework module Pods_keyrace_mac {\n umbrella header \"Pods-keyrace-mac-umbrella.h\"\n\n export *\n module * { export * }\n"
},
{
"path": "mac/Pods/Target Support Files/Pods-keyrace-mac/Pods-keyrace-mac.release.xcconfig",
"chars": 1005,
"preview": "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES\nCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO\nFRAMEWORK_SEARCH_PATHS = "
},
{
"path": "mac/keyrace-mac/AppDelegate.swift",
"chars": 2324,
"preview": "//\n// keyrace-mac\n//\n// Created by Nat Friedman on 1/2/21.\n//\n\nimport Cocoa\nimport Foundation\nimport SwiftUI\n\n@availab"
},
{
"path": "mac/keyrace-mac/Assets.xcassets/AccentColor.colorset/Contents.json",
"chars": 123,
"preview": "{\n \"colors\" : [\n {\n \"idiom\" : \"universal\"\n }\n ],\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }"
},
{
"path": "mac/keyrace-mac/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 904,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"mac\",\n \"scale\" : \"1x\",\n \"size\" : \"16x16\"\n },\n {\n \"idiom\" : "
},
{
"path": "mac/keyrace-mac/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "mac/keyrace-mac/Assets.xcassets/magic-keyboard.imageset/Contents.json",
"chars": 400,
"preview": "{\n \"images\" : [\n {\n \"filename\" : \"magic-keyboard.png\",\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },"
},
{
"path": "mac/keyrace-mac/ContentView.swift",
"chars": 1554,
"preview": "//\n// ContentView.swift\n// keyrace-mac\n//\n// Created by Jessie Frazelle on 2/21/21.\n//\n\nimport Foundation\nimport Swif"
},
{
"path": "mac/keyrace-mac/GitHub.swift",
"chars": 7741,
"preview": "//\n// github_auth.swift\n// keyrace-mac\n//\n// Created by Nat Friedman on 1/2/21.\n//\nimport Cocoa\nimport Combine\nimport"
},
{
"path": "mac/keyrace-mac/Info.plist",
"chars": 841,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "mac/keyrace-mac/KeyTap.swift",
"chars": 13819,
"preview": "// Created by Nat Friedman on 1/2/21.\n\nimport Accessibility\nimport Cocoa\nimport Combine\nimport Foundation\nimport SwiftUI"
},
{
"path": "mac/keyrace-mac/KeyboardView.swift",
"chars": 6679,
"preview": "//\n// KeyboardView.swift\n// keyrace-mac\n//\n// Created by Jessie Frazelle on 2/21/21.\n//\n\nimport Foundation\nimport Swi"
},
{
"path": "mac/keyrace-mac/LeaderboardView.swift",
"chars": 3880,
"preview": "//\n// LeaderboardView.swift\n// keyrace-mac\n//\n// Created by Jessie Frazelle on 2/20/21.\n//\n\nimport Foundation\nimport "
},
{
"path": "mac/keyrace-mac/MenuView.swift",
"chars": 123,
"preview": "//\n// MenuView.swift\n// keyrace-mac\n//\n// Created by Jessie Frazelle on 2/21/21.\n//\n\nimport Foundation\nimport SwiftUI"
},
{
"path": "mac/keyrace-mac/Preview Content/Preview Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "mac/keyrace-mac/SettingsView.swift",
"chars": 2674,
"preview": "//\n// SettingsView.swift\n// keyrace-mac\n//\n// Created by Jessie Frazelle on 2/16/21.\n//\n\nimport Foundation\nimport Swi"
},
{
"path": "mac/keyrace-mac/TypingChart.swift",
"chars": 3930,
"preview": "//\n// MenubarItem.swift\n// keyrace-mac\n//\n// Created by Nat Friedman on 1/3/21.\n//\n\nimport Charts\nimport Cocoa\nimport"
},
{
"path": "mac/keyrace-mac/UserDefaults.swift",
"chars": 1444,
"preview": "//\n// UserDefaults.swift\n// keyrace-mac\n//\n// Created by Jessie Frazelle on 2/21/21.\n//\n\nimport Foundation\n\n// Store "
},
{
"path": "mac/keyrace-mac/keyrace_mac.entitlements",
"chars": 181,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "mac/keyrace-mac.xcodeproj/project.pbxproj",
"chars": 19056,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 51;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "mac/keyrace-mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "mac/keyrace-mac.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "mac/keyrace-mac.xcodeproj/xcshareddata/xcschemes/keyrace-mac.xcscheme",
"chars": 2876,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1240\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "mac/keyrace-mac.xcworkspace/contents.xcworkspacedata",
"chars": 229,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:keyrace-mac.xc"
},
{
"path": "mac/keyrace-mac.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "server.go",
"chars": 11171,
"preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"strconv"
}
]
About this extraction
This page contains the full source code of the nat/keyrace GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 198 files (1.1 MB), approximately 280.8k tokens, and a symbol index with 19 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.