Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:sgratzl/chartjs-chart-wordcloud into…
Browse files Browse the repository at this point in the history
… dev
  • Loading branch information
sgratzl committed Mar 14, 2024
2 parents b893df6 + 65e61f4 commit 90a509f
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 9 deletions.
50 changes: 42 additions & 8 deletions src/controllers/WordCloudController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ export class WordCloudController extends DatasetController<'wordCloud', WordElem
/**
* @hidden
*/
static readonly defaults = {
static readonly defaults = /* #__PURE__ */ {
datasets: {
fit: true,
animation: {
colors: {
properties: ['color', 'strokeStyle'],
Expand All @@ -57,7 +56,7 @@ export class WordCloudController extends DatasetController<'wordCloud', WordElem
/**
* @hidden
*/
static readonly overrides = {
static readonly overrides = /* #__PURE__ */ {
scales: {
x: {
type: 'linear',
Expand Down Expand Up @@ -93,7 +92,8 @@ export class WordCloudController extends DatasetController<'wordCloud', WordElem
*/
update(mode: UpdateMode): void {
super.update(mode);
this.rand = rnd(this.chart.id);
const dsOptions = (this as any).options as IWordCloudControllerDatasetOptions;
this.rand = rnd(dsOptions.randomRotationSeed ?? this.chart.id);
const meta = this._cachedMeta;

const elems = (meta.data || []) as unknown as WordElement[];
Expand All @@ -105,13 +105,21 @@ export class WordCloudController extends DatasetController<'wordCloud', WordElem
*/
updateElements(elems: WordElement[], start: number, count: number, mode: UpdateMode): void {
this.wordLayout.stop();
const dsOptions = (this as any).options as IWordCloudControllerDatasetOptions;
const xScale = this._cachedMeta.xScale as { left: number; right: number };
const yScale = this._cachedMeta.yScale as { top: number; bottom: number };

const w = xScale.right - xScale.left;
const h = yScale.bottom - yScale.top;
const labels = this.chart.data.labels as string[];

const growOptions: IAutoGrowOptions = {
maxTries: 3,
scalingFactor: 1.2,
};
// update with configured options
Object.assign(growOptions, dsOptions?.autoGrow ?? {});

const words: (ICloudWord & Record<string, unknown>)[] = [];
for (let i = start; i < start + count; i += 1) {
const o = this.resolveDataElementOptions(i, mode) as unknown as IWordElementOptions;
Expand Down Expand Up @@ -139,14 +147,18 @@ export class WordCloudController extends DatasetController<'wordCloud', WordElem
// syncish since no time limit is set
this.wordLayout.random(this.rand).words(words);

const run = (factor = 1, tries = 3): void => {
const run = (factor = 1, tries = growOptions.maxTries): void => {
this.wordLayout
.size([w * factor, h * factor])
.on('end', (tags, bounds) => {
if (tags.length < labels.length) {
if (tries > 0) {
// try again with a factor of 1.2
run(factor * 1.2, tries - 1);
const f =
typeof growOptions.scalingFactor === 'function'
? growOptions.scalingFactor(factor, tags, labels.length)
: factor * growOptions.scalingFactor;
run(f, tries - 1);
return;
}
// eslint-disable-next-line no-console
Expand All @@ -155,7 +167,6 @@ export class WordCloudController extends DatasetController<'wordCloud', WordElem
const wb = bounds[1].x - bounds[0].x;
const hb = bounds[1].y - bounds[0].y;

const dsOptions = (this as any).options as IWordCloudControllerDatasetOptions;
const scale = dsOptions.fit ? Math.min(w / wb, h / hb) : 1;
const indices = new Set(labels.map((_, i) => i));
tags.forEach((tag) => {
Expand Down Expand Up @@ -203,16 +214,39 @@ export class WordCloudController extends DatasetController<'wordCloud', WordElem
}
}

export interface IAutoGrowOptions {
/**
* @default 3
*/
maxTries: number;
/**
* @default 1.2
*/
scalingFactor: number | ((currentFactor: number, fitted: ICloudWord[], total: number) => number);
}

export interface IWordCloudControllerDatasetOptions
extends ControllerDatasetOptions,
ScriptableAndArrayOptions<IWordElementOptions, ScriptableContext<'wordCloud'>>,
ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'wordCloud'>>,
AnimationOptions<'wordCloud'> {
/**
* whether to fit the word cloud to the map, by scaling to the actual bounds
* @default true
* @default false
*/
fit: boolean;

/**
* configures the automatic growing of the canvas in case not all words can be fitted onto the screen
* @default { maxTries: 3, scalingFactor: 1.2}
*/
autoGrow: IAutoGrowOptions;

/**
* specifies the random seed that should be used for randomly rotating words if needed
* @default the current chart id
*/
randomRotationSeed: string;
}

declare module 'chart.js' {
Expand Down
2 changes: 1 addition & 1 deletion src/elements/WordElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class WordElement extends Element<IWordElementProps, IWordElementOptions>
/**
* @hidden
*/
static readonly defaultRoutes = {
static readonly defaultRoutes = /* #__PURE__ */ {
color: 'color',
family: 'font.family',
style: 'font.style',
Expand Down

0 comments on commit 90a509f

Please sign in to comment.