Full Code of ProtoTeam/tcharts.js for AI

master adeedecd5f7c cached
41 files
49.8 KB
16.1k tokens
39 symbols
1 requests
Download .txt
Repository: ProtoTeam/tcharts.js
Branch: master
Commit: adeedecd5f7c
Files: 41
Total size: 49.8 KB

Directory structure:
gitextract_4qpz9orr/

├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── README.md
├── __tests__/
│   ├── axis.js
│   ├── bar.js
│   ├── box.js
│   ├── hbar.js
│   ├── index.js
│   ├── line.js
│   ├── point.js
│   ├── rect.js
│   ├── recttext.js
│   ├── table.js
│   ├── text.js
│   └── utils.js
├── package.json
├── src/
│   ├── charts/
│   │   ├── Bar.js
│   │   ├── Box.js
│   │   ├── Chart.js
│   │   ├── HBar.js
│   │   └── Table.js
│   ├── const.js
│   ├── core/
│   │   ├── Axis.js
│   │   ├── Element.js
│   │   ├── Layer.js
│   │   ├── Line.js
│   │   ├── Point.js
│   │   ├── Rect.js
│   │   ├── RectText.js
│   │   ├── Text.js
│   │   └── index.js
│   ├── index.js
│   └── utils/
│       ├── array.js
│       ├── invariant.js
│       ├── number.js
│       ├── string.js
│       └── types.js
└── webpack.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": ["es2015", "stage-0", "react"],
  "env": {
    "test": {
      "presets": ["es2015", "stage-0", "react"]
    }
  }
}

================================================
FILE: .eslintignore
================================================
benchmark
dist

================================================
FILE: .eslintrc
================================================
{
  "env": {
    "browser": true,
    "node": true,
    "jest": true,
    "es6": true
  },
  "parser": "babel-eslint",
  "parserOptions": {
    "sourceType": "module"
  },
  "extends": [
    "eslint-config-airbnb"
  ],
  "globals": {
  },
  "rules": {
    "comma-dangle": 0,
    "consistent-return": 0,
    "func-names": 0,
    "global-require": 0,
    "import/no-extraneous-dependencies": 0,
    "import/no-unresolved": 0,
    "indent": 0,
    "key-spacing": 0,
    "linebreak-style": 0,
    "new-cap": 0,
    "no-cond-assign": 0,
    "no-empty": 0,
    "no-fallthrough": 0,
    "no-loop-func": 0,
    "no-mixed-operators": 0,
    "no-new": 0,
    "no-param-reassign": 0,
    "no-restricted-syntax": 0,
    "no-return-assign": 0,
    "no-underscore-dangle": 0,
    "object-curly-spacing": 0,
    "prefer-rest-params": 0,
    "react/jsx-filename-extension": 0,
    "react/jsx-space-before-closing": 0,
    "strict": 0,
    "max-len": [
      2,
      120,
      4,
      {
        "ignoreUrls": true
      }
    ],
    "id-length": [
      2,
      {
        "min": 1
      }
    ],
    "class-methods-use-this": [
      1,
      {
        "exceptMethods": [
          "CLASSNAME"
        ]
      }
    ]
  }
}

================================================
FILE: .gitignore
================================================
.idea
coverage
node_modules
.coveralls.yml
npm-debug.*
l.js


================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
  - '4'
  - '4'
  - '5'
  - '6'
  - '7'
  - '8'
after_success:
  - npm run coveralls

================================================
FILE: README.md
================================================
# tcharts.js

> [TCharts.js](http://tcharts.org) is a Lightweight and fast terminal ASCII charts for nodejs and browser.

[![Ver](https://img.shields.io/npm/v/tcharts.js.svg)](https://www.npmjs.com/package/tcharts.js) [![Build Status](https://travis-ci.org/ProtoTeam/tcharts.js.svg?branch=master)](https://travis-ci.org/ProtoTeam/tcharts.js) [![Coverage Status](https://coveralls.io/repos/github/ProtoTeam/tcharts.js/badge.svg?branch=master)](https://coveralls.io/github/ProtoTeam/tcharts.js)

```
    +--------------+----------------------+---------------------+
    |              |                      |                     |
    |              |                      |                     |
    |              |                      |                     |
    |              |                      |                     |
    |              |                      |                     |
    |              |                      |                     |
    |              |        C:25%         |      Hello:25%      |
    |              |                      |                     |
    |              |                      |                     |
    |    A:25%     |                      |                     |
    |              |                      |                     |
    |              |                      |                     |
    |              +----------------------+---------------------+
    |              |                                            |
    |              |                                            |
    |              |                                            |
    |              |                   B:25%                    |
    |              |                                            |
    |              |                                            |
    +--------------+--------------------------------------------+

```


## 1. Install & Usage

> npm i -S tcharts.js


### Table

```js
const TCharts = require('tcharts.js');
const { Table } = TCharts;

const table = new Table(0.2); // set gap rate = 0.2
table.setData([
  ['標識符', '名字', '生日'],
  ['#1', '圖靈', 24],
  ['#2', '潘金蓮', false],
  ['#3', '西門慶', null],
  ['#4', '明日花绮罗'],
]);
console.log(table.string());
```

### Bar

```js
const TCharts = require('tcharts.js');
const { Bar } = TCharts;

const bar = new Bar();
bar.setData([
  {value:100, name:'A'},
  {value:45, name:'B'},
  {value:70, name:'C'},
  {value:30, name:'D'},
]);
console.log(bar.string());
```

### HBar

```js
const TCharts = require('tcharts.js');
const { HBar } = TCharts;

const hbar = new HBar();
hbar.setData([
  {value: 100, name: 'A'},
  {value: 45, name: 'B'},
  {value: 70, name: 'C'},
  {value: 30, name: 'D'},
]);
console.log(hbar.string());

```

### Box

```js
const TCharts = require('tcharts.js');
const { Box } = TCharts;

const box = new Box(60, 20); // width, height
box.setData([
  {value:100, name:'A'},
  {value:100, name:'B'},
  {value:100, name:'C'},
  {value:100, name:'Hello'},
]);
console.log(box.string());
```


## 2. Supported charts

 - `Bar`: bar chart, with x, y.
 - `HBar`: horizontal bar chart.
 - `Box`: box chart showing with a square.
 - `Table`: data table in terminal.

How to use them, you can see the testcases in `__tests__` folder.


## 3. Build & Test

```
npm i

npm run build

npm test
```

Then you can see the result of test cases.


## 4. License

ISC@[ProtoTeam](https://github.com/ProtoTeam).




================================================
FILE: __tests__/axis.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

module.exports = (Point, Axis) => {
  test('1. draw a axis element.', () => {
    const point0 = new Point(0, 0);
    const pointX = new Point(20, 0);
    const pointY = new Point(0, 5);
    const axis = new Axis(point0, pointX, pointY);
    console.log(axis.toString());
    const axisLayer = axis.draw();

    expect(axisLayer.box).toEqual({
      x1: 0,
      y1: 0,
      x2: 20,
      y2: 5,
    });
    expect(axisLayer.array()).toEqual([
      '^                    '.split(''),
      '|                    '.split(''),
      '|                    '.split(''),
      '|                    '.split(''),
      '|                    '.split(''),
      '+------------------->'.split(''),
    ]);
    expect(axis.clone().draw().array()).toEqual([
      '^                    '.split(''),
      '|                    '.split(''),
      '|                    '.split(''),
      '|                    '.split(''),
      '|                    '.split(''),
      '+------------------->'.split(''),
    ]);
    console.log(axisLayer.string());
    expect(axis.CLASSNAME).toBe('Axis');
    expect(axisLayer.CLASSNAME).toBe('Layer');
  });
};


================================================
FILE: __tests__/bar.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

module.exports = (Bar) => {
  test('1. draw a bar chart.', () => {
    let bar = new Bar(20);
    bar.setData([
      {value:100, name:'A'},
      {value:45, name:'B'},
      {value:70, name:'C'},
      {value:30, name:'D'},
    ]);
    const r = `
^                           
|  A:100                    
|  +--+                     
|  |  |                     
|  |  |        C:70         
|  |  |        +--+         
|  |  |  B:45  |  |         
|  |  |  +--+  |  |  D:30   
|  |  |  |  |  |  |  +--+   
|  |  |  |  |  |  |  |  |   
|  |  |  |  |  |  |  |  |   
+--+--+--+--+--+--+--+--+-->`.trim();
    expect(bar.string()).toBe(r);

    bar = new Bar(20, 0.1);
    bar.setData([
      {value:100, name:'A'},
      {value:45, name:'B'},
      {value:70, name:'C'},
      {value:30, name:'D'},
    ]);
    console.log(bar.string());

    bar = new Bar();
    bar.setData([
      {value:100, name:'A'},
      {value:45, name:'B'},
      {value:70, name:'C'},
      {value:30, name:'D'},
    ]);
    bar.array();
    console.log(bar.string());
  });
};


================================================
FILE: __tests__/box.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

module.exports = (Box) => {
  test('1. draw a box chart.', () => {
    let box = new Box(60, 20);
    box.setData([
      {value:100, name:'A'},
      {value:100, name:'B'},
      {value:100, name:'C'},
      {value:100, name:'Hello'},
    ]);
    const r = `
+--------------+----------------------+---------------------+
|              |                      |                     |
|              |                      |                     |
|              |                      |                     |
|              |                      |                     |
|              |                      |                     |
|              |                      |                     |
|              |        C:25%         |      Hello:25%      |
|              |                      |                     |
|              |                      |                     |
|    A:25%     |                      |                     |
|              |                      |                     |
|              |                      |                     |
|              +----------------------+---------------------+
|              |                                            |
|              |                                            |
|              |                                            |
|              |                   B:25%                    |
|              |                                            |
|              |                                            |
+--------------+--------------------------------------------+`.trim();
    expect(box.string()).toBe(r);

    box = new Box();
    box.setData([
      {value:100, name:'A'},
      {value:100, name:'B'},
      {value:100, name:'C'},
      {value:100, name:'Hello'},
    ]);
    console.log(box.string());
  });
};


================================================
FILE: __tests__/hbar.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

module.exports = (HBar) => {
  test('1. draw a hbar chart.', () => {
    const hbar = new HBar();
    hbar.setData([
      {value: 100, name: 'A'},
      {value: 45, name: 'B'},
      {value: 70, name: 'C'},
      {value: 30, name: 'D'},
    ]);
    console.log(hbar.string());
    const r = `
^                                                      
|                                                      
+---------------+                                      
|           D:30|                                      
+---------------+                                      
|                                                      
+------------------------------------+                 
|                                C:70|                 
+------------------------------------+                 
|                                                      
+-----------------------+                              
|                   B:45|                              
+-----------------------+                              
|                                                      
+----------------------------------------------------+ 
|                                               A:100| 
+----------------------------------------------------+ 
|                                                      
+----------------------------------------------------->
`.trim();
    expect(hbar.string()).toBe(r);
  });
};


================================================
FILE: __tests__/index.js
================================================
/**
 * Created by hustcc on 17/6/21.
 */

// test for library and test
// eslint-disable-next-line
const { Box, Bar, HBar, Table } = require(process.env.NODE_ENV !== 'production' ? '../src/' : '../');
const { Axis, Line, Point, Rect, Text, RectText } = require('../src/core/');
const StringUtils = require('../src/utils/string');
const NumberUtils = require('../src/utils/number');


describe('Testcases of tcharts.js', () => {
  describe('1. Point element.', () => {
    require('./point')(Point);
  });

  describe('2. Line element.', () => {
    require('./line')(Point, Line);
  });

  describe('3. Text element.', () => {
    require('./text')(Point, Text);
  });

  describe('4. Rect element.', () => {
    require('./rect')(Point, Rect);
  });

  describe('5. RectText element.', () => {
    require('./recttext')(Point, RectText);
  });

  describe('6. Axis element.', () => {
    require('./axis')(Point, Axis);
  });

  describe('7. Box chart.', () => {
    require('./box')(Box);
  });

  describe('8. Bar chart.', () => {
    require('./bar')(Bar);
  });

  describe('9. HBar chart.', () => {
    require('./hbar')(HBar);
  });

  describe('10. Table chart.', () => {
    require('./table')(Table);
  });

  describe('11. utils.', () => {
    require('./utils')(StringUtils, NumberUtils);
  });
});


================================================
FILE: __tests__/line.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */


module.exports = (Point, Line) => {
  test('1. draw a line element.', () => {
    let start = new Point(0, 0);
    let end = new Point(0, 50);
    const line = new Line(start, end);
    let lineLayer = line.draw();

    expect(lineLayer.box).toEqual({
      x1: 0,
      y1: 0,
      x2: 0,
      y2: 50,
    });
    expect(lineLayer.array()).toEqual(new Array(51).fill(new Array(1).fill('|')));

    start = new Point(0, 0);
    end = new Point(50, 0);
    lineLayer = new Line(start, end).draw();

    expect(lineLayer.box).toEqual({
      x1: 0,
      y1: 0,
      x2: 50,
      y2: 0,
    });
    expect(lineLayer.array()).toEqual(new Array(1).fill(new Array(51).fill('-')));

    expect(line.clone().CLASSNAME).toBe('Line');
    expect(line.toString()).toBe('Line(Point(0, 0), Point(0, 50))');
  });
};


================================================
FILE: __tests__/point.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */


module.exports = (Point) => {
  test('1. draw a point element.', () => {
    const pointLayer = new Point(50, 50).draw();
    expect(pointLayer.box).toEqual({
      x1: 50,
      y1: 50,
      x2: 50,
      y2: 50,
    });
    expect(pointLayer.array()).toEqual([['+']]);
  });
};


================================================
FILE: __tests__/rect.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */


module.exports = (Point, Rect) => {
  test('1. draw a rect element.', () => {
    const start = new Point(0, 0);
    const end = new Point(4, 3);
    const rect = new Rect(start, end);
    const rectLayer = rect.draw();

    expect(rectLayer.box).toEqual({
      x1: 0,
      y1: 0,
      x2: 4,
      y2: 3,
    });
    expect(rectLayer.array()).toEqual([
      '+---+'.split(''),
      '|   |'.split(''),
      '|   |'.split(''),
      '+---+'.split(''),
    ]);

    expect(rect.clone().CLASSNAME).toBe('Rect');
    rect.toString();
  });
};


================================================
FILE: __tests__/recttext.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

module.exports = (Point, RectText) => {
  test('1. draw a rect-text element.', () => {
    const start = new Point(0, 0);
    const end = new Point(10, 5);
    const reactText = new RectText(start, end, 'TC');
    const rectTextLayer = reactText.draw();

    expect(rectTextLayer.box).toEqual({
      x1: 0,
      y1: 0,
      x2: 10,
      y2: 5,
    });
    expect(rectTextLayer.array()).toEqual([
      '+---------+'.split(''),
      '|         |'.split(''),
      '|         |'.split(''),
      ['|', ' ', ' ', ' ', ' ', 'TC', '', ' ', ' ', ' ', '|'],
      '|         |'.split(''),
      '+---------+'.split(''),
    ]);

    expect(reactText.clone().CLASSNAME).toBe('RectText');
    reactText.toString();
  });
};


================================================
FILE: __tests__/table.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */


module.exports = (Table) => {
  test('1. draw a table chart.', () => {
    let table = new Table();
    table.setData([
      ['id', 'name', 'birthday'],
      ['#1', 'xiaowei', '1992-08-01'],
      ['#2', 'hello', '1992-09-20'],
      ['#3', 'tcharts', '2017-06-27'],
      ['#4', 'world'],
    ]);
    const r = `
+--+-------+----------+
|id|  name | birthday |
+--+-------+----------+
|#1|xiaowei|1992-08-01|
+--+-------+----------+
|#2| hello |1992-09-20|
+--+-------+----------+
|#3|tcharts|2017-06-27|
+--+-------+----------+
|#4| world |          |
+--+-------+----------+`.trim();
    expect(table.string()).toBe(r);

    table = new Table(0.5); // set gap rate = 0.5
    table.setData([
      ['id', 'name', 'birthday'],
      ['#1', 'xiaowei', '1992-08-01'],
      ['#2', 'hello', '1992-09-20'],
      ['#3', 'tcharts', '2017-06-27'],
      ['#4', 'world'],
    ]);
    console.log(table.string());
  });

    test('2. draw a table chart, contains chinese.', () => {
        let table = new Table();
        table.setData([
            ['標識符', '名字', '生日'],
            ['#1', '圖靈', '1992-08-01'],
            ['#2', '潘金蓮', '1992-09-20'],
            ['#3', '西門慶', '2017-06-27'],
            ['#4', '明日花绮罗'],
        ]);
        const r = `
+------+----------+----------+
|標識符|   名字   |   生日   |
+------+----------+----------+
|  #1  |   圖靈   |1992-08-01|
+------+----------+----------+
|  #2  |  潘金蓮  |1992-09-20|
+------+----------+----------+
|  #3  |  西門慶  |2017-06-27|
+------+----------+----------+
|  #4  |明日花绮罗|          |
+------+----------+----------+`.trim();
        expect(table.string()).toBe(r);

        table = new Table(0.5); // set gap rate = 0.5
        table.setData([
            ['標識符', '名字', '生日'],
            ['#1', '圖靈', '1992-08-01'],
            ['#2', '潘金蓮', '1992-09-20'],
            ['#3', '西門慶', '2017-06-27'],
            ['#4', '明日花绮罗'],
        ]);
        console.log(table.string());

      table = new Table(0.5); // set gap rate = 0.5
      table.setData([
        ['標識符', '名字', '生日'],
        ['#1', '圖靈', 24],
        ['#2', '潘金蓮', false],
        ['#3', '西門慶', null],
        ['#4', '明日花绮罗'],
      ]);
      console.log(table.string());
    });
};


================================================
FILE: __tests__/text.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

module.exports = (Point, Text) => {
  test('1. draw a text element.', () => {
    const text = 'Hello world.';
    const p = new Point(40, 50);
    const textE = new Text(p, text);
    const textLayer = textE.draw();
    expect(textLayer.box).toEqual({
      x1: 35,
      y1: 50,
      x2: 46,
      y2: 50,
    });
    const r = new Array(12).fill('');
    r[0] = text;

    expect(textLayer.array()).toEqual([
      r
    ]);

    expect(textE.clone().CLASSNAME).toBe('Text');
    textE.toString();
  });
};


================================================
FILE: __tests__/utils.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

module.exports = (StringUtils, NumberUtils) => {
  test('1. toString.', () => {
    expect(StringUtils.toString(null)).toBe('null');
    expect(StringUtils.toString(undefined)).toBe('undefined');
    expect(StringUtils.toString(false)).toBe('false');
    expect(StringUtils.toString('')).toBe('');
    expect(StringUtils.toString(123)).toBe('123');
    expect(StringUtils.toString('hustcc')).toBe('hustcc');

    expect(StringUtils.toString([1, '2'])).toBe('[1,"2"]');
    expect(StringUtils.toString({a: 1, b: '2'})).toBe('{"a":1,"b":"2"}');
    expect(typeof StringUtils.toString(new Date())).toBe('string');
  });

  test('2. toPercent.', () => {
    expect(NumberUtils.toPercent(123)).toBe('12300%');
    expect(NumberUtils.toPercent(0.234, 0)).toBe('23%');
  });
};


================================================
FILE: package.json
================================================
{
  "name": "tcharts.js",
  "version": "0.0.4",
  "description": "Lightweight and fast terminal ASCII charts for nodejs and browser.",
  "main": "dist/tcharts.min.js",
  "files": [
    "dist"
  ],
  "scripts": {
    "lint": "eslint src && eslint __tests__",
    "cover": "jest --coverage",
    "test": "npm run lint && npm run cover",
    "coveralls": "cat ./coverage/lcov.info | coveralls",
    "build": "webpack && cross-env NODE_ENV=production jest --coverage"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/hustcc/tcharts.js.git"
  },
  "keywords": [
    "terminal",
    "console",
    "charts",
    "ascii",
    "terminal-charts",
    "tcharts",
    "tcharts.js"
  ],
  "author": "hustcc",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/hustcc/tcharts.js/issues"
  },
  "homepage": "https://github.com/hustcc/tcharts.js#readme",
  "devDependencies": {
    "babel-cli": "^6.24.1",
    "babel-core": "^6.24.1",
    "babel-eslint": "^7.2.3",
    "babel-jest": "^20.0.3",
    "babel-loader": "^7.1.0",
    "babel-preset-env": "^1.5.2",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "coveralls": "^2.13.1",
    "cross-env": "^5.0.5",
    "eslint": "^3.19.0",
    "eslint-config-airbnb": "^15.0.1",
    "eslint-config-airbnb-base": "^11.2.0",
    "eslint-plugin-import": "^2.3.0",
    "eslint-plugin-jsx-a11y": "^5.0.3",
    "eslint-plugin-react": "^7.0.1",
    "jest": "^20.0.4",
    "webpack": "^3.0.0"
  },
  "dependencies": {
    "area-divide": "^0.0.1",
    "evenly": "^1.0.2",
    "fixed-round": "^0.0.1",
    "invariant": "^2.2.2",
    "variable-type": "^0.2.0",
    "what.js": "^1.0.1",
    "word-width": "^1.0.1"
  },
  "jest": {
    "testRegex": "(/__tests__/index)\\.(ts|tsx|js)$",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "json"
    ],
    "collectCoverage": true,
    "collectCoverageFrom": [
      "src/*.{js,jsx}",
      "src/*/*.{js,jsx}",
      "!**/node_modules/**",
      "!**/vendor/**"
    ],
    "moduleDirectories": [
      "node_modules",
      "src"
    ],
    "transform": {
      "^.+\\.js?$": "babel-jest"
    }
  }
}


================================================
FILE: src/charts/Bar.js
================================================
/**
 * Created by hustcc.
 */

const Chart = require('./Chart');
const invariant = require('../utils/invariant');
const { round } = require('../utils/number');
const Axis = require('../core/Axis');
const Point = require('../core/Point');
const Text = require('../core/Text');
const Rect = require('../core/Rect');
const { BAR_DATA_TYPE } = require('../const');

/**
 * 柱形图
 *
 * ^
 * |    A:70
 * |   +---+
 * |   |   |
 * |   |   |              C:50
 * |   |   |             +---+
 * |   |   |             |   |
 * |   |   |             |   |
 * |   |   |     B:30    |   |
 * |   |   |    +---+    |   |
 * |   |   |    |   |    |   |
 * |   |   |    |   |    |   |
 * |   |   |    |   |    |   |
 * +---+---+----+---+----+---+----->
 *
 *
 * data 结构:
 [
   {value:335, name:'A'},
   {value:310, name:'B'},
   {value:274, name:'C'},
   {value:235, name:'D'},
   {value:400, name:'E'},
 ]
 *
 */
class Bar extends Chart {
  constructor(minWidth = 20, heightRate = 0.4) {
    super(0, 0);
    this.minWidth = minWidth;
    this.heightRate = heightRate; // 高度 / 宽度比例
    this.barWidth = 2;
  }

  setData = (data) => {
    invariant(
      BAR_DATA_TYPE.check(data),
      'TCharts: data of `Bar` chart should be type of Array.'
    );
    this.data = data;
    // 计算宽高
    let width = (this.data.length * 2 + 1) * (this.barWidth + 1);
    width = Math.max(width, this.minWidth);
    const height = round(width * this.heightRate);
    this.resetSize(width, height);
    this.generateLayer();
  };

  /**
   * 具体图表的实现
   */
  generateLayer = () => {
    // 1. 构造 text 键值
    let maxValue = 0;
    this.data.forEach((d) => {
      d.text = `${d.name}:${d.value}`;
      maxValue = Math.max(maxValue, d.value);
    });

    // 2. 计算每个数值的柱形图高度d
    this.data.forEach((d) => {
      d.height = round((d.value / maxValue) * (this.height - 2)); // -2 是为了显示文本
    });

    // 3. 绘制坐标轴图层
    const axisLayer = new Axis(
      new Point(0, 0),
      new Point(this.width, 0),
      new Point(0, this.height)
    ).draw();

    // 4. 绘制矩形图层
    const widthStep = round(this.width / (this.data.length * 2 + 1));
    const rectLayers = this.data.map((d, i) => new Rect(
      new Point(widthStep * (i * 2 + 1), 0),
      new Point(widthStep * (i * 2 + 2), d.height)).draw()
    );

    // 5. 绘制文字图层(局左对齐)
    const textLayers = this.data.map((d, i) => new Text(
      new Point(widthStep * (i * 2 + 1), d.height + 1), // 文本高度为 1
      d.text,
      'left').draw()
    );

    // 6. 合并图层
    let layers = [];
    layers.push(axisLayer);
    layers = layers.concat(rectLayers);
    layers = layers.concat(textLayers);

    this.layer.mergeArray(layers);
  }
}

module.exports = Bar;


================================================
FILE: src/charts/Box.js
================================================
/**
 * Created by hustcc.
 */

const areaDivide = require('area-divide');
const Chart = require('./Chart');
const { toPercent } = require('../utils/number');
const RectText = require('../core/RectText');
const Point = require('../core/Point');
const invariant = require('../utils/invariant');
const { BOX_DATA_TYPE } = require('../const');

/**
 * 面积区域占比图
 *
 *  +-------------------+------------+
 *  |                   |    B:10%   |
 *  |                   |            |
 *  |                   |------------+
 *  |       A:75%       |            |
 *  |                   |    C:20%   |
 *  |                   |            |
 *  +-------------------+------------+
 *
 * data 结构:
 [
   {value:335, name:'A'},
   {value:310, name:'B'},
   {value:274, name:'C'},
   {value:235, name:'D'},
   {value:400, name:'E'},
 ]
 *
 */
class Box extends Chart {
  constructor(width = 60, height = 20) {
    super(width, height);
  }

  setData = (data) => {
    invariant(
      BOX_DATA_TYPE.check(data),
      'TCharts: data of `Box` chart should be type of Array.'
    );
    this.data = data;
    this.generateLayer();
  };

  /**
   * 具体图表的实现
   */
  generateLayer = () => {
    // 1. 计算总数
    let total = 0;
    this.data.forEach((d) => {
      total += d.value;
    });
    // 2. 计算占比
    this.data.forEach((d) => {
      d.percent = d.value / total;
      d.text = `${d.name}:${toPercent(d.percent, 0)}`;
    });
    // 3. 降序排序
    this.data.sort((x, y) => y.value - x.value);
    // 4. 瓜分面积
    // 获得比率,然后调用方法瓜分算法
    const percents = this.data.map(e => e.percent);
    const plan = areaDivide(this.width, this.height, percents);

    const layers = plan.map((p, index) => new RectText(
      new Point(p.x1, p.y1),
      new Point(p.x2, p.y2),
      this.data[index].text).draw()
    );

    this.layer.mergeArray(layers);
  };
}

module.exports = Box;


================================================
FILE: src/charts/Chart.js
================================================
/**
 * Created by hustcc.
 */

const Layer = require('../core/Layer');

/**
 * 图表的基类
 * 1. 拥有画布的宽高属性
 * 2. 可以设置图形的 data 配置
 * 3. 拥有将图层变成字符串的能力 string() / array();
 * 4. 拥有一个图层,这个是最最终图层
 */
class Chart {
  constructor(width, height) {
    this.resetSize(width, height);
    this.data = [];
  }

  resetSize = (width, height) => {
    this.width = width;
    this.height = height;
    // 创建一个空的图层
    this.layer = Layer.emptyInstance({
      x1: 0,
      y1: 0,
      x2: this.width,
      y2: this.height,
    });
  };

  // 返回图表的文本,可以直接用脑输出
  string = () => this.layer.string();

  // 返回图表的文本数组,逐行打印就可以显示了
  array = () => this.layer.array();
}

module.exports = Chart;


================================================
FILE: src/charts/HBar.js
================================================
/**
 * Created by hustcc.
 */

const Chart = require('./Chart');
const invariant = require('../utils/invariant');
const { round } = require('../utils/number');
const Axis = require('../core/Axis');
const Point = require('../core/Point');
const Rect = require('../core/Rect');
const Text = require('../core/Text');
const { HBAR_DATA_TYPE } = require('../const');

/**
 * 横向柱形图
 *
 * ^
 * |
 * +--------------------------+
 * |           A:70           |
 * +--------------------------+
 * |
 * |
 * +-----------------+
 * |      B:30       |
 * +-----------------+
 * |
 * |
 * +---------------------+
 * |         C:50        |
 * +---------------------+
 * |
 * +---+---+----+---+----+---+----->
 *
 */
class HBar extends Chart {
  constructor(minHeight = 8, widthRate = 3) {
    super(0, 0);
    this.minHeight = minHeight;
    this.widthRate = widthRate; // 高度 / 宽度比例
    this.barHeight = 1;
  }

  setData = (data) => {
    invariant(
      HBAR_DATA_TYPE.check(data),
      'TCharts: data of `HBar` chart should be type of Array.'
    );
    this.data = data;
    // 计算宽高
    let height = (this.data.length * 2 + 1) * (this.barHeight + 1);
    height = Math.max(height, this.minHeight);
    const width = round(height * this.widthRate);
    this.resetSize(width, height);
    this.generateLayer();
  };

  /**
   * 具体图表的实现
   */
  generateLayer = () => {
    // 1. 构造 text 键值
    let maxValue = 0;
    this.data.forEach((d) => {
      d.text = `${d.name}:${d.value}`;
      maxValue = Math.max(maxValue, d.value);
    });

    // 2. 计算每个数值的柱形图宽度
    this.data.forEach((d) => {
      d.width = round((d.value / maxValue) * (this.width - 1)); // -1 图形美观
    });

    // 3. 绘制坐标轴图层
    const axisLayer = new Axis(
      new Point(0, 0),
      new Point(this.width, 0),
      new Point(0, this.height)
    ).draw();

    // 4. 绘制矩形图层
    const heightStep = round(this.height / (this.data.length * 2 + 1));
    const rectLayers = this.data.map((d, i) => new Rect(
      new Point(0, heightStep * (i * 2 + 1)),
      new Point(d.width, heightStep * (i * 2 + 2))).draw()
    );

    // 5. 绘制文字图层(局左对齐)
    const textLayers = this.data.map((d, i) => {
      if (d.text.length >= d.width) {
        // 文本长度超过柱子长度,在在柱形右边显示
        return new Text(
          new Point(d.width + 1, heightStep * (i * 2 + 1) + 1),
          d.text).draw();
      }
      // 居右显示
      return new Text(
        new Point(d.width - 1, heightStep * (i * 2 + 1) + 1),
        d.text,
        'right').draw();
    });

    // 6. 合并图层
    let layers = [];
    layers.push(axisLayer);
    layers = layers.concat(rectLayers);
    layers = layers.concat(textLayers);

    this.layer.mergeArray(layers);
  }
}

module.exports = HBar;


================================================
FILE: src/charts/Table.js
================================================
/**
 * Created by hustcc.
 */

const Chart = require('./Chart');
const invariant = require('../utils/invariant');
const RectText = require('../core/RectText');
const Point = require('../core/Point');
const { arrayClone } = require('../utils/array');
const { wordWidth } = require('../utils/string');
const { round } = require('../utils/number');
const { TABLE_DATA_TYPE } = require('../const');

/**
 * 表格
 *
 *
 * +----+----------+----------------+
 * | id |   name   |    birthday    |
 * +----+----------+----------------+
 * | #1 |  xiaowei |   1992-08-01   |
 * +----+----------+----------------+
 * | #2 |  hello   |   1992-09-20   |
 * +----+----------+----------------+
 * | #3 | tcharts  |   2017-06-27   |
 * +----+----------+----------------+
 * | #4 |     d    |                |
 * +----+----------+----------------+
 *
 *
 *
 */
class Table extends Chart {
  constructor(rate = 0) {
    super(0, 0); // table 的宽高有内容自动伸缩
    this.rate = rate; // 比例,比如文字宽度为10, rate = 0.1,则表格 cell 宽度为 12
  }

  // 通过内容计算每一列的宽高
  _calColSizes = (data, row, col) => {
    let sizes = new Array(col).fill(0);
    data.forEach((d) => {
      sizes = sizes.map((s, i) => Math.max(wordWidth(d[i]), s));
    });
    // 乘以 rate
    sizes = sizes.map(s => s + round(s * this.rate) * 2);
    return sizes;
  };

  _getRowAndCol = (data) => {
    const row = data.length;
    let col = 0;
    data.forEach((d) => {
      col = Math.max(col, d.length);
    });

    // 数据不能为零长度
    invariant(
      row !== 0 && col !== 0,
      `TCharts: data of \`Table\` chart should be type of matrix Array, 
      and can not be zero row or column. Got row: %s, column: %s.`,
      row,
      col
    );
    return {
      row,
      col,
    };
  };

  // 填充数据(对于有空缺的数据)
  _fullFillData = (data, row, col) => {
    const cloneData = arrayClone(data);
    // 遍历来填充数据
    data.forEach((d, index) => {
      if (col > d.length) {
        // 补充一些空的文本
        cloneData[index].splice(cloneData[index].length, 0, ...new Array(col - d.length).fill(''));
      }
    });
    return cloneData;
  };

  _calTableSizes = (colSizes, row, col) => {
    // width height 是从 0 开始计数的,所以这里的 width height 比实际的 - 1
    const height = row * 2;
    const width = col + colSizes.reduce((r, ele) => r + ele);
    return {
      width,
      height,
    };
  };

  setData = (data) => {
    invariant(
      TABLE_DATA_TYPE.check(data),
      'TCharts: data of `Table` chart should be type of matrix Array.'
    );
    const { row, col } = this._getRowAndCol(data);
    this.data = this._fullFillData(data, row, col);

    const colSizes = this._calColSizes(this.data, row, col);
    const { width, height } = this._calTableSizes(colSizes, row, col);

    this.resetSize(width, height);
    this.generateLayer(colSizes, row, col);
  };

  /**
   * 具体 table 的实现
   */
  generateLayer = (colSizes, row, col) => {
    const rectTexts = []; // 非常多有的 rectText 实例
    let startX = 0;
    let startY = 0;

    for (let i = 0; i < row; i += 1) {
      startX = 0;
      for (let j = 0; j < col; j += 1) {
        rectTexts.push(new RectText(
          new Point(startX, startY),
          new Point(startX + colSizes[j] + 1, startY + 2),
          this.data[row - i - 1][j]).draw());

        startX += (colSizes[j] + 1);
      }
      startY += 2;
    }
    return this.layer.mergeArray(rectTexts);
  }
}

module.exports = Table;


================================================
FILE: src/const.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

const VT = require('variable-type');

const COMMON_TYPE = VT.arrayOf(
  VT.shape({
    name: VT.or([
      VT.number,
      VT.string,
    ]),
    value: VT.number,
  })
);

const BAR_DATA_TYPE = COMMON_TYPE;

const BOX_DATA_TYPE = COMMON_TYPE;

const HBAR_DATA_TYPE = COMMON_TYPE;

const TABLE_DATA_TYPE = VT.arrayOf(
  VT.arrayOf(
    VT.any,
  ),
);

// const TREE_DATA_TYPE = VT.shape({
//   name: VT.or([
//     VT.number,
//     VT.string,
//   ]),
//   // TODO children 一个递归结构的数组
//   children: VT.arrayOf(
//     VT.recursive
//   ),
// });

module.exports = {
  BAR_DATA_TYPE,
  BOX_DATA_TYPE,
  HBAR_DATA_TYPE,
  TABLE_DATA_TYPE,
  // TREE_DATA_TYPE,
};


================================================
FILE: src/core/Axis.js
================================================
/**
 * Created by hustcc.
 */

const invariant = require('../utils/invariant');
const types = require('../utils/types');
const Element = require('./Element');
const Line = require('./Line');

/**
 * 一个坐标轴系统,不带 x,y 文字
 *
 * ^
 * |
 * |
 * |
 * |
 * |
 * |
 * |
 * |
 * |
 * +------------------------------>
 *
 */
class Axis extends Element {
  /**
   * 构造方法
   * @param point0 零点
   * @param pointX 横向轴
   * @param pointY 纵向轴
   */
  constructor(point0, pointX, pointY) {
    super();
    invariant(
      types.isPoint(point0) && types.isPoint(pointX) && types.isPoint(pointY),
      'TCharts: constructor props of Axis should be (Point, Point, Point), got (%s, %s, %s).',
      types.typeOf(point0),
      types.typeOf(pointX),
      types.typeOf(pointY));

    invariant(
      point0.y === pointX.y,
      'TCharts: constructor props `pointX` of Axis should be an Horizontal line with `point0`.');

    invariant(
      point0.x === pointY.x,
      'TCharts: constructor props `pointY` of Axis should be an Horizontal line with `point0`.');

    this.point0 = point0;
    this.pointX = pointX;
    this.pointY = pointY;
    this.box = {
      x1: this.point0.x,
      y1: this.point0.y,
      x2: this.pointX.x,
      y2: this.pointY.y,
    };

    this.initLayer();
  }

  clone = () => new Axis(this.point0, this.pointX, this.pointY);

  toString = () => `Axis(${this.point0}, ${this.pointX}, ${this.pointY})`;

  draw = () => {
    // X 轴图层
    const lineXLayer = new Line(this.point0, this.pointX).draw();
    // Y 轴图层
    const lineYLayer = new Line(this.point0, this.pointY).draw();

    // 后合并点,可以提高性能,同时不会导致层覆盖问题
    return this.layer.merge(
      lineXLayer,                      // x 轴
      lineYLayer,                      // y 轴
      this.point0.draw({ fill: '+' }), // 左边圆点
      this.pointX.draw({ fill: '>' }), // x 轴箭头
      this.pointY.draw({ fill: '^' })  // y 轴箭头
    );
  };

  get CLASSNAME() {
    return 'Axis';
  }
}

module.exports = Axis;


================================================
FILE: src/core/Element.js
================================================
/**
 * Created by hustcc.
 */
const { floorCenter } = require('../utils/number');
const Layer = require('./Layer');
/**
 * 元素基类,像积木一样一层一层的搭建组件元素
 * 每隔元素都可以通过 draw 方法获得一个图层
 * 上层的元素是利用下层元素组合出来的,然后形成新的图层
 * 一层一层,直到形成最终的图表 charts
 *
 * 元素都有一个 box 结构(盒模型?)限定了元素的起始位置
 * 另外具有一个 z-index 属性,可以设置元素显示的层级
 *
 * 元素的 clone 可以直接获得一个相同的元素,引用不同
 */
class Element {
  constructor(zIndex = 0) {
    this.zIndex = zIndex; // TODO 用于控制渲染层级,后期可以用于重构渲染性能
    // box 限制了元素的起始结束点坐标,以
    this.box = { x1: 0, y1: 0, x2: 0, y2: 0 };
  }

  /**
   * 初始化空的 layer
   */
  initLayer = () => {
    this.layer = Layer.emptyInstance(this.box, this.zIndex);
  };

  // setZIndex = (zIndex = 0) => {
  //   this.zIndex = zIndex;
  // };

  /**
   * 获得中心点的坐标
   * @returns {{x: number, y: number}}
   */
  center = () => ({
    x: floorCenter(this.box.x1, this.box.x2),
    y: floorCenter(this.box.y1, this.box.y2),
  });

  /**
   * 获得元素的大小
   * @returns {{width: number, height: number}}
   */
  size = () => {
    const width = this.box.x2 - this.box.x1 + 1;
    const height = this.box.y2 - this.box.y1 + 1;
    return {
      width,
      height,
    };
  };

  // clone = () => new Element();
  //
  // toString = () => {
  //   invariant(false, 'TCharts: Element\'s method `toString` should be implemented by children Class.');
  // };
  //
  //
  // /**
  //  * 绘制图形,产生图层,图层将用于最终的渲染
  //  * @param options { fill: '*', line: '-' }
  //  */
  // draw = () => {
  //   invariant(false, 'TCharts: Element\'s method `draw` should be implemented by children Class.');
  // };
  //
  // get CLASSNAME() {
  //   invariant(false, 'TCharts: Element\'s method `CLASSNAME` should be implemented by children Class.');
  // }
}

module.exports = Element;


================================================
FILE: src/core/Layer.js
================================================
/**
 * Created by hustcc.
 */

const invariant = require('../utils/invariant');
const types = require('../utils/types');
const { fillMatrix } = require('../utils/array');

/**
 * 定义一个图层,图层包括一个范围和填充的内容
 * 图层是最终将用于显示的部分,图层可以通过 merge 合并
 * 最终显示在控制台的仅仅只有一个图层
 * 这个图层是有很多的小元素图层合并而来
 *
 * 如何设计图层,更好划分图层,将提高 merge 性能
 *
 * 图层也就是 Element 元素 draw 方法的返回值
 */
class Layer {
  constructor(box = { x1: 0, y1: 0, x2: 0, y2: 0 }, ascii = [], zIndex = 0) {
    invariant(
      types.isObject(box) && types.isArray(ascii) && types.isNumber(zIndex),
      'TCharts: constructor props of Layer should be (object, array, number), got (%s, %s, %s).',
      types.typeOf(box),
      types.typeOf(ascii),
      types.typeOf(zIndex));

    this.box = box;
    this.ascii = ascii;
    this.zIndex = zIndex;
  }

  static emptyInstance(box, zIndex = 0) {
    const width = box.x2 - box.x1 + 1;
    const height = box.y2 - box.y1 + 1;

    const ascii = fillMatrix(height, width, ' ');
    return new Layer(box, ascii, zIndex);
  }

  // box 盒模型是不是大于
  // gt = (layer, includeEqual = true) => {
  //    if (includeEqual) {
  //      if (
  //        this.box.x1 <= layer.box.x1 &&
  //        this.box.y1 <= layer.box.y1 &&
  //        this.box.x2 >= layer.box.x2 &&
  //        this.box.y2 >= layer.box.y2) {
  //        return true;
  //      }
  //      return false;
  //    }
  //   if (
  //     this.box.x1 < layer.box.x1 &&
  //     this.box.y1 < layer.box.y1 &&
  //     this.box.x2 > layer.box.x2 &&
  //     this.box.y2 > layer.box.y2) {
  //     return true;
  //   }
  //    return false;
  // };

  /**
   * 按顺序合并另外的 layers
   * @param layer
   */
  merge = (...layers) => this.mergeArray(layers);

  /**
   * 合并一个 layer
   * @param layer
   */
  mergeOne = (layer) => {
    // 校验:前提条件是,this 的范围肯定大于 layer
    // invariant(
    //   this.gt(layer),
    //   'TCharts: layer\'box should be greater then layer which will be merged.');

    // 算法
    const { x1, y1, x2, y2 } = layer.box;
    const { x1: thisX1, y1: thisY1, x2: thisX2, y2: thisY2 } = this.box;
    const rangeX1 = Math.max(x1, thisX1);
    const rangeY1 = Math.max(y1, thisY1);
    const rangeX2 = Math.min(x2, thisX2);
    const rangeY2 = Math.min(y2, thisY2);
    // box 的偏移量
    let i1;
    let i2;
    for (let i = rangeX1; i <= rangeX2; i += 1) {
      // 减少计算量
      i1 = i - thisX1;
      i2 = i - x1;
      for (let j = rangeY1; j <= rangeY2; j += 1) {
        this.ascii[j - thisY1][i1] = layer.ascii[j - y1][i2];
      }
    }
    return this;
  };

  /**
   * 合并一个 layer
   * @param layer
   */
  mergeArray = (layers) => {
    // 首先先按照 zIndex 升序排列,越大越在上层
    // chrome 下 sort 方法可能为不稳定,这点需要注意。
    // layers.sort((x, y) => x - y); // TODO :暂时不要 zIndex 排序特性
    // 排序之后遍历,先绘制底层的,后使用上层覆盖
    layers.forEach((layer) => {
      this.mergeOne(layer);
    });
    return this;
  };

  /**
   * 返回图表的文本,可以直接用脑输出
   * @returns {string}
   */
  string = () => {
    const asciiArray = this.ascii;
    // 逆向遍历
    const rst = [];
    for (let i = asciiArray.length - 1; i >= 0; i -= 1) {
      rst.push(`${asciiArray[i].join('')}`);
    }
    return rst.join('\n');
  };

  /**
   * 返回图表的文本数组,逐行打印就可以显示了
   */
  array = () => this.ascii.slice().reverse();

  get CLASSNAME() {
    return 'Layer';
  }
}

module.exports = Layer;


================================================
FILE: src/core/Line.js
================================================
/**
 * Created by hustcc.
 */

const invariant = require('../utils/invariant');
const types = require('../utils/types');
const Element = require('./Element');
const { fillMatrix } = require('../utils/array');
// const Layer = require('./Layer');

/**
 * 一个线段,包括横线和竖线
 *
 * 横线
 * -------------------
 *
 * 竖线
 * |
 * |
 * |
 * |
 * |
 * |
 *
 */
class Line extends Element {
  constructor(start, end) {
    super();
    // 参数必须是 point 类型的
    invariant(
      types.isPoint(start) && types.isPoint(end),
      'TCharts: constructor props of Line should be (Point, Point), got (%s, %s).',
      types.typeOf(start),
      types.typeOf(end));
    // 控制台模式下,仅仅只能输入横线或者竖线
    invariant(
      start.x === end.x || start.y === end.y,
      'TCharts: we can only draw Horizontal / Vertical line in terminal. got(%s, %s).',
      start,
      end);
    // 校验通过,赋值属性
    this.start = start;
    this.end = end;
    this.box = {
      x1: Math.min(this.start.x, this.end.x),
      y1: Math.min(this.start.y, this.end.y),
      x2: Math.max(this.start.x, this.end.x),
      y2: Math.max(this.start.y, this.end.y),
    };

    this.initLayer();
  }

  clone = () => new Line(this.start.clone(), this.end.clone());

  toString = () => `Line(${this.start}, ${this.end})`;

  draw = () => {
    const { width, height } = this.size();
    const isHorizontal = this.box.y1 === this.box.y2;

    let ascii = null;
    if (isHorizontal) {
      // 横向线
      ascii = fillMatrix(1, width, '-');
    } else {
      // 纵向线
      ascii = fillMatrix(height, 1, '|');
    }
    this.layer.ascii = ascii;
    return this.layer;
  };

  get CLASSNAME() {
    return 'Line';
  }
}

module.exports = Line;


================================================
FILE: src/core/Point.js
================================================
/**
 * Created by hustcc.
 */

const invariant = require('../utils/invariant');
const types = require('../utils/types');
const Element = require('./Element');

/**
 * 一个点
 *
 * +
 *
 */
class Point extends Element {
  constructor(x, y) {
    super();
    invariant(
      types.isNumber(x) && types.isNumber(y),
      'TCharts: constructor props of Point should be (number, number), got (%s, %s).',
      types.typeOf(x),
      types.typeOf(y));

    this.x = x;
    this.y = y;
    this.box = {
      x1: this.x,
      y1: this.y,
      x2: this.x,
      y2: this.y,
    };

    this.initLayer();
  }

  clone = () => new Point(this.x, this.y);

  toString = () => `Point(${this.x}, ${this.y})`;

  draw = (options = { fill: '+' }) => {
    this.layer.ascii = [[options.fill]];
    return this.layer;
  };

  get CLASSNAME() {
    return 'Point';
  }
}

module.exports = Point;


================================================
FILE: src/core/Rect.js
================================================
/**
 * Created by hustcc.
 */

const invariant = require('../utils/invariant');
const types = require('../utils/types');
const Element = require('./Element');
const Point = require('./Point');
const Line = require('./Line');
// const Layer = require('./Layer');

/**
 *  带边框的填充矩形,在控制台下面,仅仅只能绘制四边形
 *
 *  +---------------------------+
 *  |                           |
 *  |                           |
 *  |                           |
 *  |                           |
 *  +---------------------------+
 *
 */
class Rect extends Element {
  constructor(start, end) {
    super();
    // 参数必须是 point 类型的
    invariant(
      types.isPoint(start) && types.isPoint(end),
      'TCharts: constructor props of Rect should be (Point, Point), got (%s, %s).',
      types.typeOf(start),
      types.typeOf(end));
    // 校验通过,赋值属性
    this.start = start;
    this.end = end;
    this.box = {
      x1: Math.min(this.start.x, this.end.x),
      y1: Math.min(this.start.y, this.end.y),
      x2: Math.max(this.start.x, this.end.x),
      y2: Math.max(this.start.y, this.end.y),
    };

    this.initLayer();
  }

  clone = () => new Rect(this.start.clone(), this.end.clone());

  toString = () => `Rect(${this.start}, ${this.end})`;

  draw = () => {
    // 四个顶点
    const points = [
      new Point(this.box.x1, this.box.y1),
      new Point(this.box.x2, this.box.y1),
      new Point(this.box.x2, this.box.y2),
      new Point(this.box.x1, this.box.y2),
    ];
    const corners = points.map(p => p.draw());

    // 四条边
    const lines = [
      new Line(points[0], points[1]).draw(),
      new Line(points[1], points[2]).draw(),
      new Line(points[2], points[3]).draw(),
      new Line(points[3], points[0]).draw(),
    ];

    // 合并图层部分
    return this.layer.mergeArray(lines.concat(corners));
  };
  get CLASSNAME() {
    return 'Rect';
  }
}

module.exports = Rect;


================================================
FILE: src/core/RectText.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

const invariant = require('../utils/invariant');
const types = require('../utils/types');
const Element = require('./Element');
const Point = require('./Point');
const Rect = require('./Rect');
const Text = require('./Text');

/**
 *  带边框的填充矩形,并且显示说明文本,在控制台下面,仅仅只能绘制四边形
 *
 *  +---------------------------+
 *  |                           |
 *  |            ABC            |
 *  |                           |
 *  |                           |
 *  +---------------------------+
 *
 */
class RectText extends Element {
  constructor(start, end, text) {
    super();
    // 参数必须是 point 类型的
    invariant(
      types.isPoint(start) && types.isPoint(end),
      'TCharts: constructor props of RectText should be (Point, Point, any), got (%s, %s, %s).',
      types.typeOf(start),
      types.typeOf(end),
      types.typeOf(text));
    // 校验通过,赋值属性
    this.start = start;
    this.end = end;
    this.text = text;
    this.box = {
      x1: Math.min(this.start.x, this.end.x),
      y1: Math.min(this.start.y, this.end.y),
      x2: Math.max(this.start.x, this.end.x),
      y2: Math.max(this.start.y, this.end.y),
    };

    this.initLayer();
  }

  clone = () => new RectText(this.start.clone(), this.end.clone(), this.text);

  toString = () => `RectText(${this.start}, ${this.end}, ${this.text})`;

  draw = () => {
    // 四个顶点
    const rect = new Rect(this.start, this.end);
    const center = this.center(); // 居中位置
    const text = new Text(new Point(center.x, center.y), this.text);

    // 合并图层
    return this.layer.merge(rect.draw(), text.draw());
  };

  get CLASSNAME() {
    return 'RectText';
  }
}

module.exports = RectText;


================================================
FILE: src/core/Text.js
================================================
/**
 * Created by hustcc.
 */

const evenly = require('evenly');
const { wordWidth, toString } = require('../utils/string');
const invariant = require('../utils/invariant');
const types = require('../utils/types');
const { fillMatrix } = require('../utils/array');
const Element = require('./Element');

/**
 * 一段文本(目前仅支持横向)
 *
 * hello world,这个是一个控制台图标库
 */
class Text extends Element {
  constructor(p, text, align = 'center') {
    super();
    invariant(
      types.isPoint(p),
      'TCharts: constructor props of Text should be (Point, any), got (%s, %s).',
      types.typeOf(p),
      types.typeOf(text));

    invariant(
      ['center', 'left', 'right'].indexOf(align) >= 0,
      'TCharts: constructor props `align` of Text should be one of [\'center\', \'left\', \'right\'], got %s.',
      align);
    this.p = p;
    this.text = toString(text);
    this.align = align;


    const textWidth = wordWidth(this.text);

    if (this.align === 'center') {
      // 居中显示
      const _width = evenly(textWidth, 2, 0);
      this.box = {
        x1: p.x - _width[0] + 1, // 因为当前 p 位置还可以存一个字符串
        y1: p.y,
        x2: p.x + _width[1],
        y2: p.y,
      };
    } else if (this.align === 'left') {
      // 局左显示
      this.box = {
        x1: p.x,
        y1: p.y,
        x2: p.x + textWidth - 1,
        y2: p.y,
      };
    } else {
      // 居右显示
      this.box = {
        x1: p.x - textWidth + 1,
        y1: p.y,
        x2: p.x,
        y2: p.y,
      };
    }

    this.initLayer();
  }

  clone = () => new Text(this.p, this.text);

  toString = () => `Text(${this.p}, ${this.text})`;

  draw = () => {
    const { width } = this.size();

    this.layer.ascii = fillMatrix(1, width, '');
    this.layer.ascii[0][0] = this.text;
    return this.layer;
  };

  get CLASSNAME() {
    return 'Text';
  }
}

module.exports = Text;


================================================
FILE: src/core/index.js
================================================
/**
 * Created by hustcc.
 */

const Axis = require('./Axis');
const Line = require('./Line');
const Point = require('./Point');
const Rect = require('./Rect');
const Text = require('./Text');
const RectText = require('./RectText');

module.exports = {
  Axis,
  Line,
  Point,
  Rect,
  Text,
  RectText,
};


================================================
FILE: src/index.js
================================================
/**
 * Created by hustcc.
 */

const Bar = require('./charts/Bar');
const Box = require('./charts/Box');
const HBar = require('./charts/HBar');
const Table = require('./charts/Table');

module.exports = {
  Box,
  Bar,
  HBar,
  Table,
};


================================================
FILE: src/utils/array.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

'use strict';


/**
 * fastest way to fill a array
 * here: https://jsperf.com/zeroarrayjs/10
 * @param len
 * @param val
 * @returns {Array}
 */
const fillArray = (len, val) => {
  const res = new Array(len);
  for (let i = 0; i < len; i += 1) {
    res[i] = val;
  }
  return res;
};

/**
 * Return a of val
 *
 * @param  {Number} `row` number of rows
 * @param  {Number} `col` number of columns
 * @param  {*} `val`
 * @return {*[][]} `matrix` of val
 */
const fillMatrix = (row, col, val) => {
  const res = new Array(row);
  for (let i = 0; i < row; i += 1) {
    res[i] = fillArray(col, val);
  }
  return res;
};

/**
 * deep clone array
 * @param arr
 * @returns {*}
 */
const arrayClone = (arr) => {
  let i;
  let copy;
  if (Array.isArray(arr)) {
    copy = arr.slice(0);
    for (i = 0; i < copy.length; i += 1) {
      copy[i] = arrayClone(copy[i]);
    }
    return copy;
  }
  return arr;
};

module.exports = {
  fillArray,
  fillMatrix,
  arrayClone,
};


================================================
FILE: src/utils/invariant.js
================================================
module.exports = require('invariant');


================================================
FILE: src/utils/number.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

const round = require('fixed-round');

const center = (x1, x2) => round((x1 + x2) / 2);

const floorCenter = (x1, x2) => Math.floor((x1 + x2) / 2);

const toPercent = (number, fixed = 2) => `${round(number * 100, fixed)}%`;


module.exports = {
  round,
  center,
  floorCenter,
  toPercent,
};


================================================
FILE: src/utils/string.js
================================================
/**
 * Created by hustcc.
 * Contract: i@hust.cc
 */

const WordWidth = require('word-width');
const types = require('./types');

const toString = (v) => {
  if (v === null) return 'null';
  if (v === undefined) return 'undefined';
  if (types.isNumber(v) || types.isString(v) || types.isBool(v)) return `${v}`;
  if (types.isArray(v) || types.isObject(v)) return JSON.stringify(v);
  return Object({}).toString.call(v);
};

const wordWidth = s => WordWidth(toString(s));

module.exports = {
  wordWidth,
  toString,
};


================================================
FILE: src/utils/types.js
================================================
/**
 * Created by hustcc.
 */

const VT = require('variable-type');
const what = require('what.js');


const isNumber = v => VT.number.check(v);

const isString = v => VT.string.check(v);

const isArray = v => VT.array.check(v);

const isObject = v => VT.object.check(v);

const isBool = v => VT.bool.check(v);

const isEmpty = v => VT.or([
  VT.null,
  VT.undefined
]).check(v);

const isPoint = v => v.CLASSNAME === 'Point';

const typeOf = (v) => {
  if (v && v.CLASSNAME) return v.CLASSNAME;
  return what(v);
};

module.exports = {
  isNumber,
  isString,
  isArray,
  isObject,
  isBool,
  isEmpty,
  isPoint,
  typeOf,
};


================================================
FILE: webpack.config.js
================================================
/**
 * Copyright (c) 2017 hustcc
 * License: ISC
 * GitHub: https://github.com/hustcc/tcharts.js
 **/

var path = require('path');
var webpack = require('webpack');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'tcharts.min.js',
    path: path.resolve(__dirname, 'dist'),
    library: 'TCharts',
    libraryTarget: 'umd',
    umdNamedDefine: true
  },
  module: {
    loaders: [{
      test: /.js$/,
      loader: 'babel-loader'
    }]
  },
  devtool: 'source-map',
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      output: { comments: false },
      compress: { warnings: false }
    })
  ]
};
Download .txt
gitextract_4qpz9orr/

├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── README.md
├── __tests__/
│   ├── axis.js
│   ├── bar.js
│   ├── box.js
│   ├── hbar.js
│   ├── index.js
│   ├── line.js
│   ├── point.js
│   ├── rect.js
│   ├── recttext.js
│   ├── table.js
│   ├── text.js
│   └── utils.js
├── package.json
├── src/
│   ├── charts/
│   │   ├── Bar.js
│   │   ├── Box.js
│   │   ├── Chart.js
│   │   ├── HBar.js
│   │   └── Table.js
│   ├── const.js
│   ├── core/
│   │   ├── Axis.js
│   │   ├── Element.js
│   │   ├── Layer.js
│   │   ├── Line.js
│   │   ├── Point.js
│   │   ├── Rect.js
│   │   ├── RectText.js
│   │   ├── Text.js
│   │   └── index.js
│   ├── index.js
│   └── utils/
│       ├── array.js
│       ├── invariant.js
│       ├── number.js
│       ├── string.js
│       └── types.js
└── webpack.config.js
Download .txt
SYMBOL INDEX (39 symbols across 14 files)

FILE: src/charts/Bar.js
  class Bar (line 43) | class Bar extends Chart {
    method constructor (line 44) | constructor(minWidth = 20, heightRate = 0.4) {

FILE: src/charts/Box.js
  class Box (line 35) | class Box extends Chart {
    method constructor (line 36) | constructor(width = 60, height = 20) {

FILE: src/charts/Chart.js
  class Chart (line 14) | class Chart {
    method constructor (line 15) | constructor(width, height) {

FILE: src/charts/HBar.js
  class HBar (line 36) | class HBar extends Chart {
    method constructor (line 37) | constructor(minHeight = 8, widthRate = 3) {

FILE: src/charts/Table.js
  class Table (line 33) | class Table extends Chart {
    method constructor (line 34) | constructor(rate = 0) {

FILE: src/const.js
  constant COMMON_TYPE (line 8) | const COMMON_TYPE = VT.arrayOf(
  constant BAR_DATA_TYPE (line 18) | const BAR_DATA_TYPE = COMMON_TYPE;
  constant BOX_DATA_TYPE (line 20) | const BOX_DATA_TYPE = COMMON_TYPE;
  constant HBAR_DATA_TYPE (line 22) | const HBAR_DATA_TYPE = COMMON_TYPE;
  constant TABLE_DATA_TYPE (line 24) | const TABLE_DATA_TYPE = VT.arrayOf(

FILE: src/core/Axis.js
  class Axis (line 26) | class Axis extends Element {
    method constructor (line 33) | constructor(point0, pointX, pointY) {
    method CLASSNAME (line 83) | get CLASSNAME() {

FILE: src/core/Element.js
  class Element (line 17) | class Element {
    method constructor (line 18) | constructor(zIndex = 0) {

FILE: src/core/Layer.js
  class Layer (line 19) | class Layer {
    method constructor (line 20) | constructor(box = { x1: 0, y1: 0, x2: 0, y2: 0 }, ascii = [], zIndex =...
    method emptyInstance (line 33) | static emptyInstance(box, zIndex = 0) {
    method CLASSNAME (line 134) | get CLASSNAME() {

FILE: src/core/Line.js
  class Line (line 26) | class Line extends Element {
    method constructor (line 27) | constructor(start, end) {
    method CLASSNAME (line 74) | get CLASSNAME() {

FILE: src/core/Point.js
  class Point (line 15) | class Point extends Element {
    method constructor (line 16) | constructor(x, y) {
    method CLASSNAME (line 45) | get CLASSNAME() {

FILE: src/core/Rect.js
  class Rect (line 23) | class Rect extends Element {
    method constructor (line 24) | constructor(start, end) {
    method CLASSNAME (line 70) | get CLASSNAME() {

FILE: src/core/RectText.js
  class RectText (line 24) | class RectText extends Element {
    method constructor (line 25) | constructor(start, end, text) {
    method CLASSNAME (line 62) | get CLASSNAME() {

FILE: src/core/Text.js
  class Text (line 17) | class Text extends Element {
    method constructor (line 18) | constructor(p, text, align = 'center') {
    method CLASSNAME (line 79) | get CLASSNAME() {
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (58K chars).
[
  {
    "path": ".babelrc",
    "chars": 131,
    "preview": "{\n  \"presets\": [\"es2015\", \"stage-0\", \"react\"],\n  \"env\": {\n    \"test\": {\n      \"presets\": [\"es2015\", \"stage-0\", \"react\"]\n"
  },
  {
    "path": ".eslintignore",
    "chars": 14,
    "preview": "benchmark\ndist"
  },
  {
    "path": ".eslintrc",
    "chars": 1210,
    "preview": "{\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"jest\": true,\n    \"es6\": true\n  },\n  \"parser\": \"babel-eslint\",\n "
  },
  {
    "path": ".gitignore",
    "chars": 60,
    "preview": ".idea\ncoverage\nnode_modules\n.coveralls.yml\nnpm-debug.*\nl.js\n"
  },
  {
    "path": ".travis.yml",
    "chars": 111,
    "preview": "language: node_js\nnode_js:\n  - '4'\n  - '4'\n  - '5'\n  - '6'\n  - '7'\n  - '8'\nafter_success:\n  - npm run coveralls"
  },
  {
    "path": "README.md",
    "chars": 3439,
    "preview": "# tcharts.js\n\n> [TCharts.js](http://tcharts.org) is a Lightweight and fast terminal ASCII charts for nodejs and browser."
  },
  {
    "path": "__tests__/axis.js",
    "chars": 1191,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nmodule.exports = (Point, Axis) => {\n  test('1. draw a axis element"
  },
  {
    "path": "__tests__/bar.js",
    "chars": 1111,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nmodule.exports = (Bar) => {\n  test('1. draw a bar chart.', () => {"
  },
  {
    "path": "__tests__/box.js",
    "chars": 1867,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nmodule.exports = (Box) => {\n  test('1. draw a box chart.', () => {"
  },
  {
    "path": "__tests__/hbar.js",
    "chars": 1466,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nmodule.exports = (HBar) => {\n  test('1. draw a hbar chart.', () =>"
  },
  {
    "path": "__tests__/index.js",
    "chars": 1311,
    "preview": "/**\n * Created by hustcc on 17/6/21.\n */\n\n// test for library and test\n// eslint-disable-next-line\nconst { Box, Bar, HBa"
  },
  {
    "path": "__tests__/line.js",
    "chars": 863,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\n\nmodule.exports = (Point, Line) => {\n  test('1. draw a line elemen"
  },
  {
    "path": "__tests__/point.js",
    "chars": 336,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\n\nmodule.exports = (Point) => {\n  test('1. draw a point element.', "
  },
  {
    "path": "__tests__/rect.js",
    "chars": 600,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\n\nmodule.exports = (Point, Rect) => {\n  test('1. draw a rect elemen"
  },
  {
    "path": "__tests__/recttext.js",
    "chars": 774,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nmodule.exports = (Point, RectText) => {\n  test('1. draw a rect-tex"
  },
  {
    "path": "__tests__/table.js",
    "chars": 2257,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\n\nmodule.exports = (Table) => {\n  test('1. draw a table chart.', ()"
  },
  {
    "path": "__tests__/text.js",
    "chars": 565,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nmodule.exports = (Point, Text) => {\n  test('1. draw a text element"
  },
  {
    "path": "__tests__/utils.js",
    "chars": 825,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nmodule.exports = (StringUtils, NumberUtils) => {\n  test('1. toStri"
  },
  {
    "path": "package.json",
    "chars": 2195,
    "preview": "{\n  \"name\": \"tcharts.js\",\n  \"version\": \"0.0.4\",\n  \"description\": \"Lightweight and fast terminal ASCII charts for nodejs "
  },
  {
    "path": "src/charts/Bar.js",
    "chars": 2667,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst Chart = require('./Chart');\nconst invariant = require('../utils/invariant');\nconst "
  },
  {
    "path": "src/charts/Box.js",
    "chars": 1856,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst areaDivide = require('area-divide');\nconst Chart = require('./Chart');\nconst { toPe"
  },
  {
    "path": "src/charts/Chart.js",
    "chars": 669,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst Layer = require('../core/Layer');\n\n/**\n * 图表的基类\n * 1. 拥有画布的宽高属性\n * 2. 可以设置图形的 data "
  },
  {
    "path": "src/charts/HBar.js",
    "chars": 2700,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst Chart = require('./Chart');\nconst invariant = require('../utils/invariant');\nconst "
  },
  {
    "path": "src/charts/Table.js",
    "chars": 3371,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst Chart = require('./Chart');\nconst invariant = require('../utils/invariant');\nconst "
  },
  {
    "path": "src/const.js",
    "chars": 718,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nconst VT = require('variable-type');\n\nconst COMMON_TYPE = VT.array"
  },
  {
    "path": "src/core/Axis.js",
    "chars": 1971,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst invariant = require('../utils/invariant');\nconst types = require('../utils/types');"
  },
  {
    "path": "src/core/Element.js",
    "chars": 1717,
    "preview": "/**\n * Created by hustcc.\n */\nconst { floorCenter } = require('../utils/number');\nconst Layer = require('./Layer');\n/**\n"
  },
  {
    "path": "src/core/Layer.js",
    "chars": 3282,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst invariant = require('../utils/invariant');\nconst types = require('../utils/types');"
  },
  {
    "path": "src/core/Line.js",
    "chars": 1676,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst invariant = require('../utils/invariant');\nconst types = require('../utils/types');"
  },
  {
    "path": "src/core/Point.js",
    "chars": 879,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst invariant = require('../utils/invariant');\nconst types = require('../utils/types');"
  },
  {
    "path": "src/core/Rect.js",
    "chars": 1865,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst invariant = require('../utils/invariant');\nconst types = require('../utils/types');"
  },
  {
    "path": "src/core/RectText.js",
    "chars": 1695,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nconst invariant = require('../utils/invariant');\nconst types = req"
  },
  {
    "path": "src/core/Text.js",
    "chars": 1850,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst evenly = require('evenly');\nconst { wordWidth, toString } = require('../utils/strin"
  },
  {
    "path": "src/core/index.js",
    "chars": 309,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst Axis = require('./Axis');\nconst Line = require('./Line');\nconst Point = require('./"
  },
  {
    "path": "src/index.js",
    "chars": 239,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst Bar = require('./charts/Bar');\nconst Box = require('./charts/Box');\nconst HBar = re"
  },
  {
    "path": "src/utils/array.js",
    "chars": 1025,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\n'use strict';\n\n\n/**\n * fastest way to fill a array\n * here: https:"
  },
  {
    "path": "src/utils/invariant.js",
    "chars": 39,
    "preview": "module.exports = require('invariant');\n"
  },
  {
    "path": "src/utils/number.js",
    "chars": 349,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nconst round = require('fixed-round');\n\nconst center = (x1, x2) => "
  },
  {
    "path": "src/utils/string.js",
    "chars": 520,
    "preview": "/**\n * Created by hustcc.\n * Contract: i@hust.cc\n */\n\nconst WordWidth = require('word-width');\nconst types = require('./"
  },
  {
    "path": "src/utils/types.js",
    "chars": 629,
    "preview": "/**\n * Created by hustcc.\n */\n\nconst VT = require('variable-type');\nconst what = require('what.js');\n\n\nconst isNumber = "
  },
  {
    "path": "webpack.config.js",
    "chars": 632,
    "preview": "/**\n * Copyright (c) 2017 hustcc\n * License: ISC\n * GitHub: https://github.com/hustcc/tcharts.js\n **/\n\nvar path = requir"
  }
]

About this extraction

This page contains the full source code of the ProtoTeam/tcharts.js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (49.8 KB), approximately 16.1k tokens, and a symbol index with 39 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.

Copied to clipboard!