Skip to content

Commit

Permalink
feat(word-cloud): 支持自定义random (#1791)
Browse files Browse the repository at this point in the history
* feat(word-cloud): 通过添加random选项以支持每次渲染的单词布局一致

* chore(word-cloud): update test

* refactor: 优化getRotate方法

* feat(word-cloud): add customPlacement opiton

* chore(word-cloud): add test for customPlacement

* docs(word-cloud): update doc

* docs(word-cloud): update demo

* refactor(word-cloud): 优化变量命名

* docs(word-cloud): 新增对placementStrategy返回值的描述

* chore: 修复单测报错
  • Loading branch information
zhangzhonghe authored Oct 27, 2020
1 parent 6bdd5dd commit d9af79d
Show file tree
Hide file tree
Showing 16 changed files with 508 additions and 171 deletions.
8 changes: 4 additions & 4 deletions __tests__/unit/plots/word-cloud/index-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ describe('word-cloud', () => {
weightField: 'GDP',
};

it('x*y', () => {
const cloud = new WordCloud(createDiv('x*y'), options);
it('basic', () => {
const cloud = new WordCloud(createDiv(), options);
cloud.render();

const geometry = cloud.chart.geometries[0];
Expand All @@ -35,8 +35,8 @@ describe('word-cloud', () => {
const options2 = deepMix({}, options, {
imageMask: 'ssss', // 无效值
});
const cloud1 = new WordCloud(createDiv('x*y'), options1);
const cloud2 = new WordCloud(createDiv('x*y'), options2);
const cloud1 = new WordCloud(createDiv(), options1);
const cloud2 = new WordCloud(createDiv(), options2);
expect(() => {
cloud1.render();
cloud2.render();
Expand Down
108 changes: 71 additions & 37 deletions __tests__/unit/plots/word-cloud/utils-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,20 @@ describe('word-cloud utils', () => {
fontSize: [20, 60],
rotation: [0, 90],
rotationSteps: 2,
rotateRatio: 0.5,
},
},
};

it('utils: transform', () => {
const rows = transform(params).filter((v) => v.hasText);
const result = transform(params).filter((v) => v.hasText);

expect(CountryEconomy.some((v) => v.Country === rows[0].text)).toBe(true);
expect(CountryEconomy.some((v) => v.GDP === rows[0].value)).toBe(true);
expect(rows[0].font).toBe('Verdana');
expect(rows[0].style).toBe('normal');
expect(rows[0].weight).toBe('normal');
expect(rows.every((row) => row.size >= 20 && row.size <= 60)).toBe(true);
expect(rows.every((row) => row.rotate === 0 || row.rotate === 90)).toBe(true);
expect(CountryEconomy.some((v) => v.Country === result[0].text)).toBe(true);
expect(CountryEconomy.some((v) => v.GDP === result[0].value)).toBe(true);
expect(result[0].font).toBe('Verdana');
expect(result[0].style).toBe('normal');
expect(result[0].weight).toBe('normal');
expect(result.every((row) => row.size >= 20 && row.size <= 60)).toBe(true);
expect(result.every((row) => row.rotate === 0 || row.rotate === 90)).toBe(true);
});

it('utils: transform, no data', () => {
Expand Down Expand Up @@ -72,8 +71,8 @@ describe('word-cloud utils', () => {
},
});

const rows = transform(p).filter((v) => v.hasText);
expect(rows.length).toBe(0);
const result = transform(p).filter((v) => v.hasText);
expect(result.length).toBe(0);
});

it('utils: padding is Array', () => {
Expand Down Expand Up @@ -102,11 +101,18 @@ describe('word-cloud utils', () => {
padding: [],
},
});
expect(transform(p1).length > 0).toBe(true);
expect(transform(p2).length > 0).toBe(true);
expect(transform(p3).length > 0).toBe(true);
expect(transform(p4).length > 0).toBe(true);
expect(transform(p5).length > 0).toBe(true);

const r1 = transform(p1).filter((v) => v.hasText);
const r2 = transform(p2).filter((v) => v.hasText);
const r3 = transform(p3).filter((v) => v.hasText);
const r4 = transform(p4).filter((v) => v.hasText);
const r5 = transform(p5).filter((v) => v.hasText);

expect(r1.length > 0).toBe(true);
expect(r2.length > 0).toBe(true);
expect(r3.length > 0).toBe(true);
expect(r4.length > 0).toBe(true);
expect(r5.length > 0).toBe(true);
});

it('utils: padding is String', () => {
Expand All @@ -127,8 +133,8 @@ describe('word-cloud utils', () => {
},
});

const rows = transform(p1).filter((v) => v.hasText);
expect(rows.every((row) => row.size === 20)).toBe(true);
const result = transform(p1).filter((v) => v.hasText);
expect(result.every((row) => row.size === 20)).toBe(true);
});

it('utils: fontSize is a number', () => {
Expand All @@ -140,57 +146,60 @@ describe('word-cloud utils', () => {
},
});

const rows = transform(p1).filter((v) => v.hasText);
expect(rows.every((row) => row.size === 20)).toBe(true);
const result = transform(p1).filter((v) => v.hasText);
expect(result.every((row) => row.size === 20)).toBe(true);
});

it('utils: rotation is not a Array', () => {
it('utils: rotation is number', () => {
const p1 = deepMix({}, params, {
options: {
wordStyle: {
rotation: 10, // 非数组时,自动转换成默认值 [0, 90]
rotation: 10,
},
},
});

const rows = transform(p1).filter((v) => v.hasText);
expect(rows.every((row) => row.rotate === 0 || row.rotate === 90)).toBe(true);
const result = transform(p1).filter((v) => v.hasText);
expect(result.every((row) => row.rotate === 10)).toBe(true);
});

it('utils: rotationSteps is 0', () => {
it('utils: rotation is function', () => {
const p1 = deepMix({}, params, {
options: {
wordStyle: {
rotationSteps: 0, // 等于 0 时,自动转换成默认值 1
rotation: () => 10,
},
},
});

const rows = transform(p1).filter((v) => v.hasText);
expect(rows.every((row) => row.rotate === 0)).toBe(true);
const result = transform(p1).filter((v) => v.hasText);
expect(result.every((row) => row.rotate === 10)).toBe(true);
});

it('utils: rotateRatio', () => {
it('utils: rotation is array', () => {
const p1 = deepMix({}, params, {
options: {
wordStyle: {
rotateRatio: -1, // 小于 0 时,自动转换成 0
rotation: [10, 10],
},
},
});
const p2 = deepMix({}, params, {

const result = transform(p1).filter((v) => v.hasText);
expect(result.every((row) => row.rotate === 10)).toBe(true);
});

it('utils: rotationSteps is 0', () => {
const p1 = deepMix({}, params, {
options: {
wordStyle: {
rotateRatio: 2, // 大于 2 时,自动转换成 1
rotationSteps: 0, // 等于 0 时,自动转换成默认值 1
},
},
});

const rows1 = transform(p1).filter((v) => v.hasText);
const rows2 = transform(p2).filter((v) => v.hasText);

expect(rows1.every((row) => row.rotate === 0)).toBe(true);
expect(rows2.every((row) => row.rotate === 90)).toBe(true);
const result = transform(p1).filter((v) => v.hasText);
expect(result.every((row) => row.rotate === 0)).toBe(true);
});

it('utils: processImageMask, HTMLImageElement', async () => {
Expand Down Expand Up @@ -233,4 +242,29 @@ describe('word-cloud utils', () => {
const img = await processImageMask(base64);
expect(img instanceof HTMLImageElement).toBe(true);
});

it('option: placementStrategy', () => {
const p = deepMix({}, params, {
options: {
placementStrategy: () => ({ x: 100, y: 100 }),
wordStyle: {
fontFamily: '字体',
fontWeight: '字重',
fontSize: 66,
rotation: 99,
},
},
});

const result = transform(p);
expect(result.length).toBe(CountryEconomy.length);
result.forEach((item) => {
expect(item.font).toBe('字体');
expect(item.weight).toBe('字重');
expect(item.size).toBe(66);
expect(item.rotate).toBe(99);
expect(item.x).toBe(100);
expect(item.y).toBe(100);
});
});
});
10 changes: 10 additions & 0 deletions __tests__/unit/utils/transform/word-cloud-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ describe('word-cloud', () => {
const result = wordCloud(data as Word[], { spiral: 'rectangular' });
basicCommon(result[0]);
});

it('random is number', () => {
const result = wordCloud(data as Word[], { random: 0.5 });
basicCommon(result[0]);
});

it('random is function', () => {
const result = wordCloud(data as Word[], { random: () => 0.5 });
basicCommon(result[0]);
});
});

describe('transform', () => {
Expand Down
76 changes: 60 additions & 16 deletions docs/manual/plots/word-cloud.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,41 @@ order: 0

默认配置: 无

#### random

**可选**, _number | function_

功能描述: 自定义所使用的随机函数,其值可以是一个 [0, 1) 区间中的值,也可以是一个返回该值的函数,当该值是一个固定的值时,每次渲染相同数据的词云图时,其对应的每个单词的布局坐标一致

默认配置: 默认使用的是浏览器内置的 `Math.random`,也就是每次渲染,单词的位置都不一样

#### spiral

**可选**, _'archimedean' | 'rectangular'_

功能描述:当设置为 `archimedean` 时,整个词云图接近于`椭圆`的形状,当设置为 `rectangular` 时,整个词云图接近于`矩形`的形状

默认配置: _'archimedean'_

#### placementStrategy

**可选**, _function_

功能描述: 自定义每个词语的坐标,返回值必须包含 x 和 y 属性,其余的可选。也可以在 `wordStyle` 中的选项中设置

默认配置: 无

其返回值的类型如下:

| 细分配置 | 类型 | 功能描述 |
| -------- | ------------------ | ------------------ |
| x | _number_ | 当前文本的横向坐标 |
| y | _number_ | 当前文本的纵向坐标 |
| font | _string_ | 文本的字体 |
| weight | _number \| string_ | 文本的字重 |
| size | _numberg_ | 文本的字体大小 |
| rotate | _numberg_ | 文本的旋转角度 |

#### timeInterval

**可选**, _number_
Expand Down Expand Up @@ -75,22 +110,31 @@ order: 0

默认配置: 无

| 细分配置 | 类型 | 默认值 | 功能描述 |
| ------------- | ------------------------------ | --------- | --------------------------------------------------------------- |
| fontFamily | _string \| function_ | 'Verdana' | 词云的字体 |
| fontWeight | _string \| number \| function_ | 'normal' | 设置字体的粗细 |
| padding | _number \| function_ | 1 | 每个单词所占的盒子的内边距,默认为 1。 越大单词之间的间隔越大。 |
| fontSize | _number[] \| function_ | [20, 60] | 字体的大小范围,比如 [10, 20] 表示最小字体是 10,最大 20 |
| rotation | _number[]_ | [0, 90] | 旋转的最小角度和最大角度 默认 [0, 90] |
| rotationSteps | _number_ | 2 | 旋转实际的步数,越大可能旋转角度越小, 默认是 2 |
| rotateRatio | _number_ | 0.5 | 旋转的比率 [0, 1],默认是 0.5 也就是 50%可能发生旋转 |

以上,某些属性可以设置为一个函数,其函数的参数是一个`object`,其属性如下

| 细分配置 | 类型 | 功能描述 |
| -------- | -------- | -------------- |
| text | _string_ | 文本内容 |
| value | _number_ | 该文本所占权重 |
| 细分配置 | 类型 | 默认值 | 功能描述 |
| ------------- | -------------------------------- | --------- | --------------------------------------------------------------- |
| fontFamily | _string \| function_ | 'Verdana' | 词云的字体 |
| fontWeight | _string \| number \| function_ | 'normal' | 设置字体的粗细 |
| padding | _number \| function_ | 1 | 每个单词所占的盒子的内边距,默认为 1。 越大单词之间的间隔越大。 |
| fontSize | _number[] \| number \| function_ | [20, 60] | 字体的大小范围,比如 [10, 20] 表示最小字体是 10,最大 20 |
| rotation | _number[] \| number \| function_ | [0, 90] | 旋转的最小角度和最大角度 默认 [0, 90] |
| rotationSteps | _number_ | 2 | 旋转实际的步数,越大可能旋转角度越小, 默认是 2 |

以上,某些属性可以设置为一个函数,其函数的参数如下:

| 细分配置 | 类型 | 功能描述 |
| -------- | -------- | ------------------------------ |
| word | _Word_ | 每个文本的数据对象 |
| index | _number_ | 当前文本对象在总数据中的索引值 |
| words | _Word[]_ | 总的文本数据,是一个数组 |

类型`Word`的配置如下:

| 细分配置 | 类型 | 功能描述 |
| -------- | -------- | ---------------------- |
| text | _string_ | 文本内容 |
| value | _number_ | 文本权重 |
| color | _any_ | 进行颜色映射的值 |
| datum | _object_ | 存储的所对应的原始数据 |

`markdown:docs/common/color.en.md`

Expand Down
Loading

0 comments on commit d9af79d

Please sign in to comment.