├── .eslintignore
├── .gitignore
├── test
├── .eslintrc.yml
└── specs
│ └── scale.spec.js
├── docs
├── sidebars.js
├── src
│ ├── pages
│ │ ├── index.js
│ │ └── styles.module.css
│ └── css
│ │ └── custom.css
├── package.json
├── docs
│ ├── examples.mdx
│ └── index.md
└── docusaurus.config.js
├── .eslintrc.yml
├── bower.json
├── CONTRIBUTING.md
├── .codeclimate.yml
├── .github
├── workflows
│ ├── release-drafter.yml
│ ├── ci.yml
│ ├── documentation.yml
│ └── npmpublish.yml
└── release-drafter.yml
├── README.md
├── src
├── index.js
├── controller.js
└── scale.js
├── LICENSE.md
├── rollup.config.js
├── scripts
└── release.sh
├── samples
└── smith.html
├── package.json
├── gulpfile.js
└── karma.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*{.,-}min.js
2 | dist/**/*
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .vscode/
3 | bower.json
4 | cc-test-reporter
5 | coverage/
6 | dist/
7 | node_modules/
8 | *.stackdump
9 | docs/.docusaurus/
10 | docs/build/
--------------------------------------------------------------------------------
/test/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | env:
2 | jasmine: true
3 |
4 | globals:
5 | __karma__: true
6 |
7 | # http://eslint.org/docs/rules/
8 | rules:
9 | # Best Practices
10 | complexity: 0
11 | no-new-func: 0
12 |
--------------------------------------------------------------------------------
/docs/sidebars.js:
--------------------------------------------------------------------------------
1 | const pkg = require('../package.json');
2 | const docsVersion = pkg.version.indexOf('-') > -1 ? 'next' : 'latest';
3 |
4 | module.exports = {
5 | someSidebar: {
6 | Introduction: ['index'],
7 | Examples: ['examples'],
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/docs/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {Redirect} from '@docusaurus/router';
4 | import useBaseUrl from '@docusaurus/useBaseUrl';
5 |
6 | function Home() {
7 | return ;
8 | }
9 |
10 | export default Home;
11 |
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | extends:
2 | - chartjs
3 | - esnext
4 |
5 | parserOptions:
6 | ecmaVersion: 7
7 | sourceType: module
8 | ecmaFeatures:
9 | impliedStrict: true
10 | modules: true
11 | experimentalObjectRestSpread: true
12 |
13 | env:
14 | browser: true
15 | es6: true
16 | node: true
17 |
18 | parser: babel-eslint
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Chart.Smtih.js",
3 | "version": "0.0.1",
4 | "description": "Smith chart implementation for Chart.js",
5 | "homepage": "https://github.com/nnnick/Chart.js",
6 | "author": "etimberg",
7 | "main": [
8 | "Chart.Smith.js"
9 | ],
10 | "dependencies": {
11 | "Chart.js": ">=2.0.0-beta2"
12 | }
13 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Contributing
2 | ============
3 |
4 | New contributions to this extension are welcome, just a couple of guidelines:
5 |
6 | * Tabs for indentation, not spaces please.
7 | * Please check that your code will pass jshint code standards, `gulp jshint` will run this for you.
8 | * Please keep pull requests concise, and document new functionality in the relevant .md file.
9 | * Please ensure that tests correctly run and pass before opening a pull request.
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | checks:
3 | complex-logic:
4 | enabled: false
5 | method-complexity:
6 | enabled: false
7 | method-lines:
8 | enabled: false
9 | similar-code:
10 | enabled: false
11 | plugins:
12 | duplication:
13 | enabled: true
14 | config:
15 | languages:
16 | - javascript
17 | fixme:
18 | enabled: true
19 | exclude_patterns:
20 | - "coverage/"
21 | - "docs/"
22 | - "samples/"
23 | - "scripts/"
24 | - "test/"
25 | - "*.js"
26 | - "*.json"
27 | - "*.md"
28 | - ".*"
29 |
--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name: Release Drafter
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | correct_repository:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: fail on fork
13 | if: github.repository_owner != 'chartjs'
14 | run: exit 1
15 |
16 | update_release_draft:
17 | needs: correct_repository
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: release-drafter/release-drafter@v5
21 | env:
22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # chartjs-chart-smith
2 |
3 | [](https://github.com/chartjs/chartjs-chart-smith/blob/master/LICENSE.md)
4 | 
5 | [](https://coveralls.io/github/chartjs/chartjs-chart-smith?branch=master)
6 |
7 | Provides a Smith Chart for use with [Chart.js](http://www.chartjs.org)
8 |
9 | ## Documentation
10 |
11 | Documentation is available at [https://www.chartjs.org/chartjs-chart-smith/](https://www.chartjs.org/chartjs-chart-smith/)
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Chart from 'chart.js';
4 | import Controller from './controller';
5 | import Scale, {defaults} from './scale';
6 |
7 | // Register the Controller and Scale
8 | Chart.controllers.smith = Controller;
9 | Chart.defaults.smith = {
10 | aspectRatio: 1,
11 | scale: {
12 | type: 'smith',
13 | },
14 | tooltips: {
15 | callbacks: {
16 | title: () => null,
17 | label: (bodyItem, data) => {
18 | const dataset = data.datasets[bodyItem.datasetIndex];
19 | const d = dataset.data[bodyItem.index];
20 | return dataset.label + ': ' + d.real + ' + ' + d.imag + 'i';
21 | }
22 | }
23 | }
24 | };
25 | Chart.scaleService.registerScaleType('smith', Scale, defaults);
26 |
--------------------------------------------------------------------------------
/docs/src/pages/styles.module.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable docusaurus/copyright-header */
2 | /**
3 | * CSS files with the .module.css suffix will be treated as CSS modules
4 | * and scoped locally.
5 | */
6 |
7 | .heroBanner {
8 | padding: 4rem 0;
9 | text-align: center;
10 | position: relative;
11 | overflow: hidden;
12 | }
13 |
14 | @media screen and (max-width: 966px) {
15 | .heroBanner {
16 | padding: 2rem;
17 | }
18 | }
19 |
20 | .buttons {
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | }
25 |
26 | .features {
27 | display: flex;
28 | align-items: center;
29 | padding: 2rem 0;
30 | width: 100%;
31 | }
32 |
33 | .featureImage {
34 | height: 200px;
35 | width: 200px;
36 | }
37 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "docusaurus start",
7 | "build": "docusaurus build",
8 | "swizzle": "docusaurus swizzle",
9 | "deploy": "docusaurus deploy"
10 | },
11 | "dependencies": {
12 | "@docusaurus/core": "^3.1.0",
13 | "@docusaurus/preset-classic": "^3.1.0",
14 | "@docusaurus/theme-live-codeblock": "^2.0.0-alpha.39",
15 | "classnames": "^2.2.6",
16 | "react": "^16.13.1",
17 | "react-dom": "^16.13.1"
18 | },
19 | "browserslist": {
20 | "production": [
21 | ">0.2%",
22 | "not dead",
23 | "not op_mini all"
24 | ],
25 | "development": [
26 | "last 1 chrome version",
27 | "last 1 firefox version",
28 | "last 1 safari version"
29 | ]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/docs/docs/examples.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | id: examples
3 | title: General
4 | ---
5 |
6 | ```jsx live
7 | function example() {
8 | useEffect(() => {
9 | const ctx = document.getElementById('chartjs-0').getContext('2d');
10 | const cfg = {
11 | type: 'smith',
12 | options: {
13 | aspectRatio: 1,
14 | elements: {
15 | point: {
16 | pointStyle: 'cross',
17 | radius: 10,
18 | hoverRadius: 10,
19 | borderColor: 'black'
20 | }
21 | }
22 | },
23 | data: {
24 | datasets: [{
25 | label: 'My Dataset',
26 | data: [
27 | { real : 1.0, imag : 2.0},
28 | { real : 1.0, imag : 1.0},
29 | { real : 1.0, imag : -1.0}
30 | ],
31 | }]
32 | }
33 | };
34 | new Chart(ctx, cfg);
35 | });
36 | return
;
37 | }
38 | ```
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | lint:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: actions/setup-node@v2-beta
17 | with:
18 | node-version: '12'
19 | - run: npm install
20 | - run: gulp lint
21 | test:
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v2
25 | - uses: actions/setup-node@v2-beta
26 | with:
27 | node-version: '12'
28 | - run: npm install
29 | - name: RUN CI
30 | run: xvfb-run --auto-servernum gulp test --coverage
31 | - name: Publish Test Results
32 | env:
33 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
34 | run: cat ./coverage/lcov.info | ./node_modules/.bin/coveralls
35 | shell: bash
36 | continue-on-error: true
37 |
--------------------------------------------------------------------------------
/docs/src/css/custom.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable docusaurus/copyright-header */
2 | /**
3 | * Any CSS included here will be global. The classic template
4 | * bundles Infima by default. Infima is a CSS framework designed to
5 | * work well for content-centric websites.
6 | */
7 |
8 | /* You can override the default Infima variables here. */
9 | :root {
10 | --ifm-color-primary: #25c2a0;
11 | --ifm-color-primary-dark: rgb(33, 175, 144);
12 | --ifm-color-primary-darker: rgb(31, 165, 136);
13 | --ifm-color-primary-darkest: rgb(26, 136, 112);
14 | --ifm-color-primary-light: rgb(70, 203, 174);
15 | --ifm-color-primary-lighter: rgb(102, 212, 189);
16 | --ifm-color-primary-lightest: rgb(146, 224, 208);
17 | --ifm-code-font-size: 95%;
18 | }
19 |
20 | .docusaurus-highlight-code-line {
21 | background-color: rgb(72, 77, 91);
22 | display: block;
23 | margin: 0 calc(-1 * var(--ifm-pre-padding));
24 | padding: 0 var(--ifm-pre-padding);
25 | }
26 |
27 | .chartjs-wrapper {
28 | height: 500px;
29 | /* width: 500px; */
30 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Evert Timberg
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-commonjs */
2 | /* eslint-env es6 */
3 |
4 | const terser = require('rollup-plugin-terser').terser;
5 | const pkg = require('./package.json');
6 |
7 | const banner = `/*!
8 | * ${pkg.name} v${pkg.version}
9 | * ${pkg.homepage}
10 | * (c) ${new Date().getFullYear()} Chart.js Contributors
11 | * Released under the ${pkg.license} license
12 | */`;
13 |
14 | module.exports = [
15 | {
16 | input: 'src/index.js',
17 | output: {
18 | file: `dist/${pkg.name}.js`,
19 | banner,
20 | format: 'umd',
21 | indent: false,
22 | globals: {
23 | 'chart.js': 'Chart'
24 | }
25 | },
26 | external: [
27 | 'chart.js'
28 | ]
29 | },
30 | {
31 | input: 'src/index.js',
32 | output: {
33 | file: `dist/${pkg.name}.min.js`,
34 | format: 'umd',
35 | indent: false,
36 | globals: {
37 | 'chart.js': 'Chart'
38 | }
39 | },
40 | plugins: [
41 | terser({
42 | output: {
43 | preamble: banner
44 | }
45 | })
46 | ],
47 | external: [
48 | 'chart.js'
49 | ]
50 | }
51 | ];
52 |
--------------------------------------------------------------------------------
/scripts/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | if [ "$TRAVIS_BRANCH" != "release" ]; then
6 | echo "Skipping release because this is not the 'release' branch"
7 | exit 0
8 | fi
9 |
10 | # Travis executes this script from the repository root, so at the same level than package.json
11 | VERSION=$(node -p -e "require('./package.json').version")
12 |
13 | # Make sure that the associated tag doesn't already exist
14 | GITTAG=$(git ls-remote origin refs/tags/v$VERSION)
15 | if [ "$GITTAG" != "" ]; then
16 | echo "Tag for package.json version already exists, aborting release"
17 | exit 1
18 | fi
19 |
20 | git remote add auth-origin https://$GITHUB_AUTH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git
21 | git config --global user.email "$GITHUB_AUTH_EMAIL"
22 | git config --global user.name "Chart.js"
23 | git checkout --detach --quiet
24 | git add -f dist/*.js bower.json
25 | git commit -m "Release $VERSION"
26 | git tag -a "v$VERSION" -m "Version $VERSION"
27 | git push -q auth-origin refs/tags/v$VERSION 2>/dev/null
28 | git remote rm auth-origin
29 | git checkout -f @{-1}
30 |
--------------------------------------------------------------------------------
/test/specs/scale.spec.js:
--------------------------------------------------------------------------------
1 | import Chart from 'chart.js';
2 | import Scale, {defaults} from '../../src/scale.js';
3 |
4 | describe('Smith Scale', () => {
5 | it('should get the correct value for points', () => {
6 | // Create a mock scale for now
7 | const scaleConfig = Chart.helpers.clone(defaults);
8 | const scale = new Scale({
9 | options: scaleConfig,
10 | });
11 |
12 | scale.left = 100;
13 | scale.right = 300;
14 | scale.top = 100;
15 | scale.bottom = 300;
16 | scale.update(200, 200);
17 |
18 | expect(scale.getPointPosition(1, 0)).toEqual({
19 | x: 200,
20 | y: 200
21 | });
22 | expect(scale.getPointPosition(0, 0)).toEqual({
23 | x: 100,
24 | y: 200
25 | });
26 | expect(scale.getPointPosition(0, -1)).toEqual({
27 | x: 200,
28 | y: 300
29 | });
30 | expect(scale.getPointPosition(1, -1)).toEqual({
31 | x: 220,
32 | y: 240
33 | });
34 | expect(scale.getPointPosition(0, 1)).toEqual({
35 | x: 200,
36 | y: 100.00000000000001,
37 | });
38 | expect(scale.getPointPosition(1, 1)).toEqual({
39 | x: 220,
40 | y: 160,
41 | });
42 | expect(scale.getPointPosition(1000, 0)).toEqual({
43 | x: 299.80019980019983,
44 | y: 200
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/samples/smith.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Smith Chart
5 |
6 |
7 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name-template: 'v$RESOLVED_VERSION'
2 | tag-template: 'v$RESOLVED_VERSION'
3 | categories:
4 | - title: 'Breaking Changes'
5 | labels:
6 | - 'breaking change'
7 | - title: 'Enhancements'
8 | labels:
9 | - 'type: enhancement'
10 | - title: 'Performance'
11 | labels:
12 | - 'type: performance'
13 | - title: 'Bugs Fixed'
14 | labels:
15 | - 'type: bug'
16 | - title: 'Types'
17 | labels:
18 | - 'type: types'
19 | - title: 'Documentation'
20 | labels:
21 | - 'type: documentation'
22 | - title: 'Development'
23 | labels:
24 | - 'type: chore'
25 | exclude-labels:
26 | - 'type: infrastructure'
27 | change-template: '- #$NUMBER $TITLE'
28 | change-title-escapes: '\<*_&`#@'
29 | version-resolver:
30 | major:
31 | labels:
32 | - 'breaking change'
33 | minor:
34 | labels:
35 | - 'type: enhancement'
36 | patch:
37 | labels:
38 | - 'type: bug'
39 | - 'type: chore'
40 | - 'type: types'
41 | default: patch
42 | template: |
43 | # Essential Links
44 |
45 | * [npm](https://www.npmjs.com/package/chartjs-chart-smith)
46 | * [Docs](https://www.chartjs.org/chartjs-chart-smith/index)
47 |
48 | $CHANGES
49 |
50 | Thanks to $CONTRIBUTORS
51 |
--------------------------------------------------------------------------------
/.github/workflows/documentation.yml:
--------------------------------------------------------------------------------
1 | name: documentation
2 |
3 | on:
4 | pull_request:
5 | branches: [master]
6 | push:
7 | branches: [master]
8 |
9 | jobs:
10 | checks:
11 | if: github.event_name != 'push'
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v1
15 | - uses: actions/setup-node@v1
16 | with:
17 | node-version: '12.x'
18 | run: |
19 | cd docs
20 | npm ci
21 | npm run build
22 | gh-release:
23 | if: github.event_name != 'pull_request'
24 | runs-on: ubuntu-latest
25 | steps:
26 | - uses: actions/checkout@v1
27 | - uses: actions/setup-node@v1
28 | with:
29 | node-version: '12.x'
30 | - name: Add key to allow access to repository
31 | env:
32 | SSH_AUTH_SOCK: /tmp/ssh_agent.sock
33 | run: |
34 | mkdir -p ~/.ssh
35 | ssh-keyscan github.com >> ~/.ssh/known_hosts
36 | echo "${{ secrets.GH_PAGES_DEPLOY }}" > ~/.ssh/id_rsa
37 | chmod 600 ~/.ssh/id_rsa
38 | cat <> ~/.ssh/config
39 | Host github.com
40 | HostName github.com
41 | IdentityFile ~/.ssh/id_rsa
42 | EOT
43 | - name: Release to GitHub Pages
44 | env:
45 | USE_SSH: true
46 | GIT_USER: git
47 | run: |
48 | git config --global user.email "actions@gihub.com"
49 | git config --global user.name "gh-actions"
50 | cd docs
51 | npm ci
52 | npx docusaurus deploy
53 |
--------------------------------------------------------------------------------
/docs/docusaurus.config.js:
--------------------------------------------------------------------------------
1 | // VERSION replaced by deploy script
2 | module.exports = {
3 | title: 'chartjs-chart-smith',
4 | tagline: 'Smith chart extension to Chart.js',
5 | url: 'https://chartjs.org',
6 | baseUrl: '/chartjs-chart-smith/',
7 | favicon: 'img/favicon.ico',
8 | organizationName: 'chartjs', // Usually your GitHub org/user name.
9 | projectName: 'chartjs-chart-smith', // Usually your repo name.
10 | plugins: [],
11 | scripts: [
12 | 'https://cdn.jsdelivr.net/npm/chart.js@2.8.0/dist/Chart.js',
13 | 'https://cdn.jsdelivr.net/npm/chartjs-chart-smith/dist/chartjs-chart-smith.min.js'
14 | ],
15 | themes: ['@docusaurus/theme-live-codeblock'],
16 | themeConfig: {
17 | disableDarkMode: true, // Would need to implement for Charts embedded in docs
18 | navbar: {
19 | title: 'Smith Chart - chartjs-chart-smith',
20 | },
21 | footer: {
22 | style: 'dark',
23 | links: [
24 | {
25 | title: 'Developers',
26 | items: [
27 | {
28 | label: 'GitHub',
29 | href: 'https://github.com/chartjs/chartjs-chart-smith',
30 | },
31 | ],
32 | },
33 | ],
34 | copyright: `Copyright © ${new Date().getFullYear()} Chart.js contributors.`,
35 | },
36 | },
37 | presets: [
38 | [
39 | '@docusaurus/preset-classic',
40 | {
41 | docs: {
42 | routeBasePath: '',
43 | editUrl: 'https://github.com/chartjs/chartjs-chart-smith/edit/master/docs/',
44 | sidebarPath: require.resolve('./sidebars.js'),
45 | },
46 | theme: {
47 | customCss: require.resolve('./src/css/custom.css'),
48 | },
49 | },
50 | ],
51 | ],
52 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chartjs-chart-smith",
3 | "author": {
4 | "name": "Evert Timberg"
5 | },
6 | "description": "Create Smith Chart with Chart.js",
7 | "version": "0.3.0",
8 | "license": "MIT",
9 | "homepage": "https://github.com/chartjs/chartjs-chart-smith",
10 | "jsdelivr": "dist/chartjs-chart-smith.min.js",
11 | "unpkg": "dist/chartjs-chart-smith.min.js",
12 | "main": "dist/chartjs-chart-smith.min.js",
13 | "keywords": [
14 | "chartjs",
15 | "smith chart"
16 | ],
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/chartjs/chartjs-chart-smith.git"
20 | },
21 | "files": [
22 | "bower.json",
23 | "dist/*.js"
24 | ],
25 | "scripts": {
26 | "docs": "cd docs && npm install && npm run build && mkdir -p ../dist && cp -r build ../dist/docs"
27 | },
28 | "devDependencies": {
29 | "@rollup/plugin-commonjs": "^11.1.0",
30 | "@rollup/plugin-node-resolve": "^7.1.3",
31 | "chart.js": "~2.9.3",
32 | "coveralls": "^3.1.0",
33 | "eslint-config-chartjs": "^0.2.0",
34 | "eslint-config-esnext": "^4.0.0",
35 | "gulp": "^4.0.2",
36 | "gulp-eslint": "^6.0.0",
37 | "gulp-file": "^0.4.0",
38 | "gulp-replace": "^1.0.0",
39 | "gulp-streamify": "^1.0.2",
40 | "gulp-zip": "^5.0.1",
41 | "jasmine-core": "^3.5.0",
42 | "karma": "^6.3.14",
43 | "karma-coverage": "^2.0.2",
44 | "karma-firefox-launcher": "^1.3.0",
45 | "karma-jasmine": "^3.1.1",
46 | "karma-jasmine-html-reporter": "^1.5.4",
47 | "karma-rollup-preprocessor": "^7.0.5",
48 | "karma-spec-reporter": "^0.0.32",
49 | "merge2": "^1.3.0",
50 | "pixelmatch": "^5.2.0",
51 | "rollup": "^2.8.2",
52 | "rollup-plugin-istanbul": "^2.0.1",
53 | "rollup-plugin-terser": "^5.3.0",
54 | "yargs": "^15.3.1"
55 | },
56 | "peerDependencies": {
57 | "chart.js": ">= 2.7.0 < 3"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-nodejs-modules, import/no-commonjs, no-use-before-define */
2 |
3 | const gulp = require('gulp');
4 | const eslint = require('gulp-eslint');
5 | const file = require('gulp-file');
6 | const karma = require('karma');
7 | const path = require('path');
8 | const {exec} = require('child_process');
9 | const pkg = require('./package.json');
10 |
11 | const argv = require('yargs')
12 | .option('output', {alias: 'o', default: 'dist'})
13 | .option('docs-dir', {default: 'docs'})
14 | .option('www-dir', {default: 'www'})
15 | .argv;
16 |
17 | function run(bin, args) {
18 | return new Promise((resolve, reject) => {
19 | const exe = '"' + process.execPath + '"';
20 | const src = require.resolve(bin);
21 | const ps = exec([exe, src].concat(args || []).join(' '));
22 |
23 | ps.stdout.pipe(process.stdout);
24 | ps.stderr.pipe(process.stderr);
25 | ps.on('close', (error) => {
26 | if (error) {
27 | reject(error);
28 | } else {
29 | resolve();
30 | }
31 | });
32 | });
33 | }
34 |
35 | gulp.task('build', () => run('rollup/dist/bin/rollup', ['-c', argv.watch ? '--watch' : '']));
36 |
37 | gulp.task('test', (done) => {
38 | new karma.Server({
39 | configFile: path.join(__dirname, 'karma.config.js'),
40 | singleRun: !argv.watch,
41 | args: {
42 | coverage: !!argv.coverage,
43 | inputs: (argv.inputs || 'test/specs/**/*.js').split(';'),
44 | watch: argv.watch
45 | }
46 | },
47 | (error) => {
48 | // https://github.com/karma-runner/gulp-karma/issues/18
49 | error = error ? new Error('Karma returned with the error code: ' + error) : undefined;
50 | done(error);
51 | }).start();
52 | });
53 |
54 | gulp.task('lint', () => {
55 | const files = [
56 | 'src/**/*.js',
57 | 'test/**/*.js',
58 | '*.js'
59 | ];
60 |
61 | return gulp.src(files)
62 | .pipe(eslint())
63 | .pipe(eslint.format())
64 | .pipe(eslint.failAfterError());
65 | });
66 |
67 | gulp.task('docs', () => {
68 | const mode = argv.watch ? 'dev' : 'build';
69 | const out = path.join(argv.output, argv.docsDir);
70 | const args = argv.watch ? '' : '--dest ' + out;
71 | return run('vuepress', [mode, 'docs', args]);
72 | });
73 |
74 | gulp.task('bower', () => {
75 | const json = JSON.stringify({
76 | name: pkg.name,
77 | description: pkg.description,
78 | homepage: pkg.homepage,
79 | license: pkg.license,
80 | version: pkg.version,
81 | main: argv.output + '/' + pkg.name + '.js'
82 | }, null, 2);
83 |
84 | return file('bower.json', json, {src: true})
85 | .pipe(gulp.dest('./'));
86 | });
87 |
88 | gulp.task('default', gulp.parallel('build'));
89 |
--------------------------------------------------------------------------------
/karma.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-commonjs */
2 |
3 | const commonjs = require('@rollup/plugin-commonjs');
4 | const istanbul = require('rollup-plugin-istanbul');
5 | const resolve = require('@rollup/plugin-node-resolve');
6 | const builds = require('./rollup.config');
7 |
8 | module.exports = function(karma) {
9 | const args = karma.args || {};
10 | const regex = args.watch ? /h\.js$/ : /h\.min\.js$/;
11 | const build = builds.filter((v) => v.output.file.match(regex))[0];
12 |
13 | if (args.watch) {
14 | build.output.sourcemap = 'inline';
15 | }
16 |
17 | karma.set({
18 | browsers: ['firefox'],
19 | frameworks: ['jasmine'],
20 | reporters: ['spec', 'kjhtml'],
21 | logLevel: karma.LOG_WARN,
22 |
23 | files: [
24 | {pattern: './test/fixtures/**/*.js', included: false},
25 | {pattern: './test/fixtures/**/*.png', included: false},
26 | 'node_modules/chart.js/dist/Chart.js',
27 | 'test/index.js',
28 | 'src/index.js'
29 | ].concat(args.inputs),
30 |
31 | // Explicitly disable hardware acceleration to make image
32 | // diff more stable when ran on Travis and dev machine.
33 | // https://github.com/chartjs/Chart.js/pull/5629
34 | customLaunchers: {
35 | firefox: {
36 | base: 'Firefox',
37 | prefs: {
38 | 'layers.acceleration.disabled': true
39 | }
40 | }
41 | },
42 |
43 | preprocessors: {
44 | 'test/fixtures/**/*.js': ['fixtures'],
45 | 'test/specs/**/*.js': ['rollup'],
46 | 'test/index.js': ['rollup'],
47 | 'src/index.js': ['sources']
48 | },
49 |
50 | rollupPreprocessor: {
51 | plugins: [
52 | resolve(),
53 | commonjs()
54 | ],
55 | external: [
56 | 'chart.js'
57 | ],
58 | output: {
59 | format: 'umd',
60 | globals: {
61 | 'chart.js': 'Chart'
62 | }
63 | }
64 | },
65 |
66 | customPreprocessors: {
67 | fixtures: {
68 | base: 'rollup',
69 | options: {
70 | output: {
71 | format: 'iife',
72 | name: 'fixture'
73 | }
74 | }
75 | },
76 | sources: {
77 | base: 'rollup',
78 | options: build
79 | }
80 | }
81 | });
82 |
83 | if (args.coverage) {
84 | karma.reporters.push('coverage');
85 | karma.coverageReporter = {
86 | dir: 'coverage/',
87 | reporters: [
88 | {type: 'html', subdir: 'html'},
89 | {type: 'lcovonly', subdir: '.'}
90 | ]
91 | };
92 | [
93 | karma.rollupPreprocessor,
94 | karma.customPreprocessors.sources.options
95 | ].forEach((v) => {
96 | (v.plugins || (v.plugins = [])).push(
97 | istanbul({
98 | include: 'src/**/*.js'
99 | }));
100 | });
101 | }
102 | };
103 |
--------------------------------------------------------------------------------
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3 |
4 | name: Node.js Package
5 |
6 | on:
7 | release:
8 | types: [published]
9 |
10 | jobs:
11 | setup:
12 | runs-on: ubuntu-latest
13 | outputs:
14 | version: ${{ steps.trim.outputs.version }}
15 | steps:
16 | - id: trim
17 | run: echo "::set-output name=version::${TAG:1}"
18 | env:
19 | TAG: ${{ github.event.release.tag_name }}
20 |
21 | test:
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v2
25 | - name: Use Node.js
26 | uses: actions/setup-node@v1
27 | - name: Test
28 | run: |
29 | npm ci
30 | xvfb-run --auto-servernum npm test
31 |
32 | publish-npm:
33 | needs: [test, setup]
34 | runs-on: ubuntu-latest
35 | steps:
36 | - uses: actions/checkout@v2
37 | - uses: actions/setup-node@v1
38 | with:
39 | node-version: 12
40 | registry-url: https://registry.npmjs.org/
41 | - name: Setup and build
42 | run: |
43 | npm ci
44 | npm install -g json
45 | json -I -f package.json -e "this.version=\"$VERSION\""
46 | json -I -f package-lock.json -e "this.version=\"$VERSION\""
47 | npm run build
48 | ./scripts/docs-config.sh "${VERSION}"
49 | npm run docs
50 | npm run typedoc
51 | npm pack
52 | env:
53 | VERSION: ${{ needs.setup.outputs.version }}
54 | - name: Publish to NPM
55 | run: ./scripts/publish.sh
56 | env:
57 | NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
58 | VERSION: ${{ needs.setup.outputs.version }}
59 | - name: Deploy Docs
60 | run: ./scripts/deploy-docs.sh "$VERSION"
61 | env:
62 | GITHUB_TOKEN: ${{ secrets.GH_AUTH_TOKEN }}
63 | GH_AUTH_EMAIL: ${{ secrets.GH_AUTH_EMAIL }}
64 | VERSION: ${{ needs.setup.outputs.version }}
65 | - name: Upload NPM package file
66 | id: upload-npm-package-file
67 | uses: actions/upload-release-asset@v1
68 | env:
69 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70 | VERSION: ${{ needs.setup.outputs.version }}
71 | with:
72 | upload_url: ${{ github.event.release.upload_url }}
73 | asset_path: ${{ format('chartjs-chart-smith-{0}.tgz', needs.setup.outputs.version) }}
74 | asset_name: ${{ format('chartjs-chart-smith-{0}.tgz', needs.setup.outputs.version) }}
75 | asset_content_type: application/gzip
76 |
--------------------------------------------------------------------------------
/src/controller.js:
--------------------------------------------------------------------------------
1 | import Chart from 'chart.js';
2 |
3 | const helpers = Chart.helpers;
4 | const resolve = helpers.options.resolve;
5 | const valueOrDefault = helpers.valueOrDefault;
6 |
7 | class Controller extends Chart.controllers.line {
8 | // Not needed since there is only a single scale
9 | // eslint-disable-next-line class-methods-use-this, no-empty-function
10 | linkScales() {}
11 |
12 | updateElement(point, index) {
13 | const me = this;
14 | const meta = me.getMeta();
15 | const custom = point.custom || {};
16 | const datasetIndex = me.index;
17 | const yScale = me.getScaleForId(meta.yAxisID);
18 | const xScale = me.getScaleForId(meta.xAxisID);
19 | const lineModel = meta.dataset._model;
20 |
21 | const options = me._resolvePointOptions(point, index);
22 | const {x, y} = me.calculatePointPosition(index);
23 |
24 | // Utility
25 | point._xScale = xScale;
26 | point._yScale = yScale;
27 | point._options = options;
28 | point._datasetIndex = datasetIndex;
29 | point._index = index;
30 |
31 | // Desired view properties
32 | point._model = {
33 | x,
34 | y,
35 | skip: custom.skip || isNaN(x) || isNaN(y),
36 | // Appearance
37 | radius: options.radius,
38 | pointStyle: options.pointStyle,
39 | rotation: options.rotation,
40 | backgroundColor: options.backgroundColor,
41 | borderColor: options.borderColor,
42 | borderWidth: options.borderWidth,
43 | tension: valueOrDefault(custom.tension, lineModel ? lineModel.tension : 0),
44 | steppedLine: lineModel ? lineModel.steppedLine : false,
45 | // Tooltip
46 | hitRadius: options.hitRadius
47 | };
48 | }
49 |
50 | /**
51 | * @private
52 | */
53 | _resolvePointOptions(element, index) {
54 | const me = this;
55 | const chart = me.chart;
56 | const dataset = chart.data.datasets[me.index];
57 | const custom = element.custom || {};
58 | const options = chart.options.elements.point;
59 | const values = {};
60 | let i, ilen, key;
61 |
62 | // Scriptable options
63 | const context = {
64 | chart,
65 | dataIndex: index,
66 | dataset,
67 | datasetIndex: me.index
68 | };
69 |
70 | const ELEMENT_OPTIONS = {
71 | backgroundColor: 'pointBackgroundColor',
72 | borderColor: 'pointBorderColor',
73 | borderWidth: 'pointBorderWidth',
74 | hitRadius: 'pointHitRadius',
75 | hoverBackgroundColor: 'pointHoverBackgroundColor',
76 | hoverBorderColor: 'pointHoverBorderColor',
77 | hoverBorderWidth: 'pointHoverBorderWidth',
78 | hoverRadius: 'pointHoverRadius',
79 | pointStyle: 'pointStyle',
80 | radius: 'pointRadius',
81 | rotation: 'pointRotation'
82 | };
83 | const keys = Object.keys(ELEMENT_OPTIONS);
84 |
85 | for (i = 0, ilen = keys.length; i < ilen; ++i) {
86 | key = keys[i];
87 | values[key] = resolve([
88 | custom[key],
89 | dataset[ELEMENT_OPTIONS[key]],
90 | dataset[key],
91 | options[key]
92 | ], context, index);
93 | }
94 |
95 | return values;
96 | }
97 |
98 | calculatePointPosition(dataIndex) {
99 | const scale = this.chart.scale;
100 | const data = this.getDataset().data[dataIndex];
101 | return scale.getPointPosition(data.real, data.imag);
102 | }
103 | }
104 |
105 | export default Controller;
106 |
--------------------------------------------------------------------------------
/docs/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: index
3 | title: Smith Charts with Chart.js
4 | sidebar: index
5 | ---
6 |
7 | ## Installation
8 |
9 | ```bash
10 | > npm install chartjs-chart-smith
11 | ```
12 |
13 | To create a Smith Chart, include `chartjs-chart-smith.js` after `Chart.js` and then create the chart by setting the `type` attribute to `'smith'`.
14 |
15 | ```javascript
16 | var mySmithChart = new Chart({
17 | type: 'smith',
18 | data: dataObject
19 | });
20 | ```
21 |
22 | ### Data Representation
23 |
24 | The smith chart can graph multiple datasets at once. The data for each dataset is in the form of complex numbers.
25 |
26 | ```javascript
27 | var smithChartData = {
28 | datasets: [{
29 | label: 'Dataset 1',
30 | data: [{
31 | real: 0,
32 | imag: 1
33 | }, {
34 | real: 1,
35 | imag: 1
36 | }]
37 | }]
38 | };
39 | ```
40 |
41 | ### Scale Configuration
42 | The smith chart scale can be configured by placing options into the config that is passed to the chart upon creation.
43 |
44 | ```javascript
45 | new Chart({
46 | config: {
47 | scale: {
48 | display: true, // setting false will hide the scale
49 | gridLines: {
50 | // setting false will hide the grid lines
51 | display: true,
52 |
53 | // the color of the grid lines
54 | color: rgba(0, 0, 0, 0.1),
55 |
56 | // thickness of grid lines
57 | lineWidth: 1,
58 | },
59 | ticks: {
60 | // The color of the scale label text
61 | fontColor: 'black',
62 |
63 | // The font family used to render labels
64 | fontFamily: 'Helvetica',
65 |
66 | // The font size in px
67 | fontSize: 12,
68 |
69 | // Style of font
70 | fontStyle: 'normal'
71 |
72 | // Function used to convert real valued ticks to strings
73 | rCallback: function(tick, index, ticks) {}
74 |
75 | // Function used to convert imaginary valued ticks to strings
76 | xCallback: function(tick, index, ticks) {}
77 | }
78 | }
79 | }
80 | });
81 | ```
82 |
83 | ### Dataset Configuration
84 |
85 | The datasets for smith charts support many of the same options as the line chart
86 |
87 | ```javascript
88 | {
89 | // Bezier Curve tension. Set to 0 for straight lines
90 | tension: 0,
91 |
92 | // Fill color for dataset
93 | backgroundColor: 'rgba(0, 0, 0, 0.1)',
94 |
95 | // Width of line
96 | borderWidth: 1,
97 |
98 | // Line color
99 | borderColor: 'rgba(0, 0, 0, 0.1)',
100 |
101 | // Line ending style
102 | borderCapStyle: 'butt',
103 |
104 | // Line dash style
105 | borderDash: [],
106 |
107 | // Dash offset. Used in conjunction with borderDash property
108 | borderDashOffset: 0,
109 |
110 | // Line join style
111 | borderJoinStyle: 'miter',
112 |
113 | // Do we fill the line?
114 | fill: true,
115 |
116 | // Point radius
117 | radius: 3,
118 |
119 | // Point style (circle, cross, etc)
120 | pointStyle: 'circle',
121 |
122 | // Point fill color
123 | pointBackgroundColor: 'rgba(0, 0, 0, 0.1)',
124 |
125 | // Point stroke color
126 | pointBorderColor: 'rgba(0, 0, 0, 0.1)',
127 |
128 | // Point stroke width
129 | pointBorderWidth: 1,
130 |
131 | // Used for hit detection
132 | hitRadius: 3
133 | }
134 | ```
135 |
136 | ## License
137 |
138 | chartjs-chart-smith is available under the [MIT license](http://opensource.org/licenses/MIT).
139 |
140 | ## Bugs & issues
141 |
142 | When reporting bugs or issues, if you could include a link to a simple [jsbin](http://jsbin.com) or similar demonstrating the issue, that would be really helpful.
--------------------------------------------------------------------------------
/src/scale.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Defines the scale for the smith chart.
3 | * When built, Chart will be passed via the UMD header
4 | */
5 | import Chart from 'chart.js';
6 | const helpers = Chart.helpers;
7 |
8 | const defaults = {
9 | position: 'chartArea',
10 | display: true,
11 | ticks: {
12 | padding: 5,
13 | rCallback: (tick) => tick.toString(),
14 | xCallback: (tick) => tick.toString() + 'i',
15 | }
16 | };
17 |
18 | class SmithScale extends Chart.Scale {
19 | setDimensions() {
20 | this.height = this.maxHeight;
21 | this.width = this.maxWidth;
22 | this.xCenter = this.left + Math.round(this.width / 2);
23 | this.yCenter = this.top + Math.round(this.height / 2);
24 |
25 | this.paddingLeft = 0;
26 | this.paddingTop = 0;
27 | this.paddingRight = 0;
28 | this.paddingBottom = 0;
29 | }
30 |
31 | buildTicks() {
32 | this.rTicks = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 3.0, 4.0, 5.0, 10.0, 20.0, 50.0];
33 | this.xTicks = [-50.0, -20.0, -10.0, -5.0, -4.0, -3.0, -2.0, -1.0, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 20.0, 50.0];
34 |
35 | // Need to do this to make the core scale work
36 | return [];
37 | }
38 |
39 | convertTicksToLabels() {
40 | this.rLabels = this.rTicks.map(function(tick, index, ticks) {
41 | return this.options.ticks.rCallback.apply(this, [tick, index, ticks]);
42 | }, this);
43 |
44 | this.xLabels = this.xTicks.map(function(tick, index, ticks) {
45 | return this.options.ticks.xCallback.apply(this, [tick, index, ticks]);
46 | }, this);
47 |
48 | // Need to do this to make the core scale work
49 | return [];
50 | }
51 |
52 | // There is no tick rotation to calculate, so this needs to be overridden
53 | // eslint-disable-next-line class-methods-use-this, no-empty-function
54 | calculateTickRotation() {}
55 |
56 | // fit function similar to the radial linear scale
57 | fit() {
58 | const me = this;
59 | me.xCenter = (me.left + me.right) / 2;
60 | me.yCenter = (me.top + me.bottom) / 2;
61 | const fontSize = helpers.getValueOrDefault(me.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
62 |
63 | if (me.options.ticks.display) {
64 | const fontStyle = helpers.getValueOrDefault(me.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
65 | const fontFamily = helpers.getValueOrDefault(me.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
66 | const labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
67 | me.ctx.font = labelFont;
68 |
69 | const xLabelLengths = me.xLabels.map((tick) => me.ctx.measureText(tick).width);
70 |
71 | // Figure out where these points will go, and assuming they are drawn there, how much will it go outside of the chart area.
72 | // We use that to determine how much padding we nede on each side
73 | me.minDimension = Math.min(me.right - me.left, me.bottom - me.top);
74 |
75 | helpers.each(me.xTicks, (xTick, index) => {
76 | if (xTick !== 0) {
77 | const halfDimension = me.minDimension / 2;
78 | const labelStart = me.getPointPosition(0, xTick);
79 | const cosPhi = (labelStart.x - me.xCenter) / halfDimension;
80 | const sinPhi = (labelStart.y - me.yCenter) / halfDimension;
81 | const labelWidth = xLabelLengths[index] + me.options.ticks.padding;
82 | const pts = [{
83 | x: labelStart.x + (cosPhi * labelWidth) + (sinPhi * fontSize),
84 | y: labelStart.y + (sinPhi * labelWidth) - (cosPhi * fontSize)
85 | }, {
86 | x: labelStart.x + (cosPhi * labelWidth) - (sinPhi * fontSize),
87 | y: labelStart.y + (sinPhi * labelWidth) + (cosPhi * fontSize)
88 | }];
89 |
90 | helpers.each(pts, pt => {
91 | me.paddingLeft = Math.max(me.paddingLeft, me.left - pt.x);
92 | me.paddingTop = Math.max(me.paddingTop, me.top - pt.y);
93 | me.paddingRight = Math.max(me.paddingRight, pt.x - me.right);
94 | me.paddingBottom = Math.max(me.paddingBottom, pt.y - me.bottom);
95 | });
96 | }
97 | });
98 | }
99 |
100 | me.minDimension = Math.min(me.right - me.left - me.paddingLeft - me.paddingRight, me.bottom - me.top - me.paddingBottom - me.paddingTop);
101 |
102 | // Store data about the arcs that we will draw
103 | me.arcs = [];
104 | me.rLabelPoints = [];
105 | me.xLabelPoints = [];
106 |
107 | // How do we draw the circles? From http://care.iitd.ac.in/People/Faculty/bspanwar/crl713/smith_chart_basics.pdf
108 | // we have that constant resistance circles obey the following
109 | // Center { r / (1 + r), 0}, Radius = 1 / (1 + r)
110 | //
111 | // The center point and radius will need to be scaled based on the size of the canvas
112 | // Draw each of the circles
113 | helpers.each(me.rTicks, r => {
114 | const radius = 1 / (1 + r) * (me.minDimension / 2); // scale for the min dimension
115 | const x = me.xCenter + ((r / (1 + r)) * (me.minDimension / 2));
116 |
117 | me.arcs.push({
118 | x,
119 | y: me.yCenter,
120 | r: radius,
121 | s: 0,
122 | e: 2 * Math.PI,
123 | cc: false
124 | });
125 |
126 | me.rLabelPoints.push({
127 | x: x - radius,
128 | y: me.yCenter
129 | });
130 | });
131 |
132 | helpers.each(me.xTicks, x => {
133 | if (x !== 0) {
134 | const xRadius = (1 / Math.abs(x)) * (me.minDimension / 2);
135 | const xCoord = me.xCenter + (me.minDimension / 2); // far right side of the drawing area
136 | const yCoord = x > 0 ? me.yCenter - xRadius : me.yCenter + xRadius;
137 |
138 | // Ok, these circles are a pain. They need to only be drawn in the region that intersects the
139 | // resistance == 0 circle. This circle has a radius of 0.5 * this.minDimension and is centered
140 | // at (xCenter, yCenter). We will solve the intersection in polar coordinates and define the
141 | // center of our coordinate system as the center of the xCircle, ie (xCoord, yCoord)
142 |
143 | const r0 = Math.sqrt(Math.pow(xCoord - me.xCenter, 2) + Math.pow(yCoord - me.yCenter, 2));
144 | const phi0 = Math.atan2(me.yCenter - yCoord, me.xCenter - xCoord);
145 |
146 | // A circle with center location r0,phi0 with radius a is defined in polar coordinates by the equation
147 | // r = r0 * cos(phi - phi0) + sqrt(a^2 - ((r0^2) * sin^2(phi - phi0)))
148 | // Our xCircle is defined by r = xRadius because of where we defined the 0,0 point
149 | // Solving the intersection of these equations yields
150 | // phi = 0.5 * arccos((xRadius^2 - a^2) / (r0^2)) + phi0
151 | const arccos = Math.acos((Math.pow(xRadius, 2) - Math.pow(me.minDimension / 2, 2)) / Math.pow(r0, 2));
152 | const phi2 = ((x > 0 ? 0.5 : -0.5) * arccos) + phi0;
153 | const startAngle = x > 0 ? 0.5 * Math.PI : -0.5 * Math.PI;
154 |
155 | me.arcs.push({
156 | x: xCoord,
157 | y: yCoord,
158 | r: xRadius,
159 | s: startAngle,
160 | e: phi2,
161 | cc: x <= 0
162 | });
163 |
164 | me.xLabelPoints.push({
165 | x: xCoord + (Math.cos(phi2) * xRadius),
166 | y: yCoord + (Math.sin(phi2) * xRadius),
167 | });
168 | } else {
169 | me.xLabelPoints.push(null);
170 | }
171 | });
172 | }
173 |
174 | // Need a custom draw function here
175 | draw() {
176 | const me = this;
177 |
178 | if (me.options.display) {
179 | if (me.options.gridLines.display) {
180 | me.ctx.strokeStyle = me.options.gridLines.color;
181 | me.ctx.lineWidth = me.options.gridLines.lineWidth;
182 |
183 | // Draw horizontal line for x === 0
184 | me.ctx.beginPath();
185 | me.ctx.moveTo(me.xCenter - (me.minDimension / 2), me.yCenter);
186 | me.ctx.lineTo(me.xCenter + (me.minDimension / 2), me.yCenter);
187 | me.ctx.stroke();
188 |
189 | // Draw each of the arcs
190 | helpers.each(me.arcs, arc => {
191 | me.ctx.beginPath();
192 | me.ctx.arc(arc.x, arc.y, arc.r, arc.s, arc.e, arc.cc);
193 | me.ctx.stroke();
194 | });
195 | } else {
196 | // Simply draw a border line
197 | me.ctx.strokeStyle = me.options.gridLines.color;
198 | me.ctx.lineWidth = me.options.gridLines.lineWidth;
199 | me.ctx.beginPath();
200 | me.ctx.arc(me.xCenter, me.yCenter, me.minDimension / 2, 0, 2 * Math.PI, false);
201 | me.ctx.stroke();
202 | }
203 |
204 | if (me.options.ticks.display) {
205 | const fontSize = helpers.getValueOrDefault(me.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
206 | const fontStyle = helpers.getValueOrDefault(me.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
207 | const fontFamily = helpers.getValueOrDefault(me.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
208 |
209 | const labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
210 | me.ctx.font = labelFont;
211 |
212 | me.ctx.fillStyle = helpers.getValueOrDefault(me.options.ticks.fontColor, Chart.defaults.global.defaultFontColor);
213 |
214 | helpers.each(me.rLabels, (rLabel, index) => {
215 | const pt = me.rLabelPoints[index];
216 |
217 | me.ctx.save();
218 | me.ctx.translate(pt.x, pt.y);
219 | me.ctx.rotate(-0.5 * Math.PI);
220 | me.ctx.textBaseline = 'middle';
221 | me.ctx.textAlign = 'center';
222 | me.ctx.fillText(rLabel, 0, 0);
223 | me.ctx.restore();
224 | });
225 |
226 | helpers.each(me.xLabels, (xLabel, index) => {
227 | const pt = me.xLabelPoints[index];
228 |
229 | if (pt) {
230 | let align = 'left';
231 | let ang = Math.atan2(pt.y - me.yCenter, pt.x - me.xCenter);
232 | let textPadding = me.options.ticks.padding;
233 |
234 | if (pt.x < me.xCenter) {
235 | ang += Math.PI;
236 | align = 'right';
237 | textPadding *= -1;
238 | }
239 |
240 | me.ctx.save();
241 | me.ctx.translate(pt.x, pt.y);
242 | me.ctx.rotate(ang);
243 | me.ctx.textBaseline = 'middle';
244 | me.ctx.textAlign = align;
245 | me.ctx.fillText(xLabel, textPadding, 0);
246 | me.ctx.restore();
247 | }
248 | });
249 | }
250 | }
251 | }
252 | getPointPosition(real, imag) {
253 | // look for the intersection of the r circle and the x circle that is not the one along the right side of the canvas
254 | const realRadius = 1 / (1 + real) * (this.minDimension / 2); // scale for the minDimension size
255 | const realCenterX = this.xCenter + ((real / (1 + real)) * (this.minDimension / 2));
256 | const realCenterY = this.yCenter;
257 |
258 | const imagRadius = (1 / Math.abs(imag)) * (this.minDimension / 2);
259 | const imagCenterX = this.xCenter + (this.minDimension / 2); // far right side of the drawing area
260 | const imagCenterY = imag > 0 ? this.yCenter - imagRadius : this.yCenter + imagRadius;
261 |
262 | const r0 = Math.sqrt(Math.pow(imagCenterX - realCenterX, 2) + Math.pow(imagCenterY - realCenterY, 2));
263 | const angle = Math.atan2(realCenterY - imagCenterY, realCenterX - imagCenterX);
264 | const arccos = Math.acos((Math.pow(imagRadius, 2) - Math.pow(realRadius, 2)) / Math.pow(r0, 2));
265 | const phi = imag > 0 ? 0.5 * arccos + angle : -0.5 * arccos + angle;
266 |
267 | // We have an r and a phi from the point (imagCenterX, imagCenterY)
268 | // translate to an x and a undefined
269 | return {
270 | x: imag === 0 ? realCenterX - realRadius : (Math.cos(phi) * imagRadius) + imagCenterX,
271 | y: imag === 0 ? this.yCenter : (Math.sin(phi) * imagRadius) + imagCenterY
272 | };
273 | }
274 | getLabelForIndex(index, datasetIndex) {
275 | const d = this.chart.data.datasets[datasetIndex].data[index];
276 | return d.real + ' + ' + d.imag + 'i';
277 | }
278 | }
279 |
280 | export {defaults};
281 | export default SmithScale;
282 |
--------------------------------------------------------------------------------