Skip to content

Commit

Permalink
feat: add arrow for lineX, lineY (#4905)
Browse files Browse the repository at this point in the history
* feat: add arrow for lineX, lineY

* chore: update demo, show arrow
  • Loading branch information
hustcc committed Apr 23, 2023
1 parent 40f59fe commit 84430a5
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 51 deletions.
Binary file modified __tests__/integration/snapshots/static/intakePointAnnotation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions __tests__/plots/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export { disastersPointBubble } from './disasters-point-bubble';
export { cars2PointJitterY } from './cars2-point-jitterY';
export { peoplePointStacked } from './people-point-stacked';
export { intakePointAnnotation } from './intake-point-annotation';
export { intakePointAnnotationWithArrow } from './intake-point-annotation-with-arrow';
export { diamondPointJitterPolar } from './diamond-point-jitter-polar';
export { seattleWeatherPoint1d } from './seattle-weather-point-1d';
export { flarePointCirclePackDefault } from './flare-point-circle-pack-default';
Expand Down
75 changes: 75 additions & 0 deletions __tests__/plots/static/intake-point-annotation-with-arrow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { G2Spec } from '../../../src';
import { intake } from '../../data/intake';

export function intakePointAnnotationWithArrow(): G2Spec {
return {
type: 'view',
children: [
{
type: 'point',
data: intake,
encode: {
x: 'x',
y: 'y',
color: '#1890ff',
size: 'z',
shape: 'point',
},
scale: {
x: { nice: true },
y: { nice: true, domainMax: 165, zero: true },
size: { range: [10, 40] },
},
legend: { size: false },
style: {
stroke: '#1890ff',
fillOpacity: 0.3,
},
labels: [
{
text: 'name',
position: 'inside',
style: {
fill: '#1890ff',
stroke: '#fff',
},
},
],
},
{
type: 'lineY',
data: [50],
style: {
stroke: '#545454',
arrow: true,
arrowStroke: 'red',
arrowFill: 'red',
},
labels: [
{
text: 'Safe sugar intake 50g/day',
position: 'right',
textBaseline: 'bottom',
},
],
},
{
type: 'lineX',
data: [65],
style: {
stroke: '#545454',
arrow: true,
},
labels: [
{
text: 'Safe fat intake 65g/day',
position: 'top-left',
style: {
textBaseline: 'bottom',
},
},
],
},
],
};
}
4 changes: 2 additions & 2 deletions __tests__/plots/static/intake-point-annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function intakePointAnnotation(): G2Spec {
type: 'lineY',
data: [50],
style: {
stroke: '#54545',
stroke: '#545454',
},
labels: [
{
Expand All @@ -54,7 +54,7 @@ export function intakePointAnnotation(): G2Spec {
type: 'lineX',
data: [65],
style: {
stroke: '#54545',
stroke: '#545454',
},
labels: [
{
Expand Down
1 change: 1 addition & 0 deletions __tests__/plots/static/metros-link-trending.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function metrosLinkTrending(): G2Spec {
x: { type: 'log' },
},
style: {
arrow: true,
arrowSize: 6,
},
axis: {
Expand Down
1 change: 1 addition & 0 deletions site/examples/annotation/line/demo/interval-threshold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ chart
.data([300])
.style('stroke', '#F4664A')
.style('lineDash', [3, 3])
.style('arrow', true)
.label({
text: 'hazardous',
position: 'right',
Expand Down
1 change: 1 addition & 0 deletions site/examples/general/link/demo/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ chart
.encode('y', ['R90_10_1980', 'R90_10_2015'])
.encode('color', (d) => d.R90_10_2015 - d.R90_10_1980)
.scale('x', { type: 'log' })
.style('arrow', true)
.style('arrowSize', 6)
.axis('x', {
labelFormatter: '~s',
Expand Down
47 changes: 45 additions & 2 deletions src/shape/lineXY/line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,44 @@ import { arc, line } from 'd3-shape';
import { isPolar } from '../../utils/coordinate';
import { select } from '../../utils/selection';
import { dist } from '../../utils/vector';
import { subObject } from '../../utils/helper';
import { Primitive, ShapeComponent as SC, Vector2 } from '../../runtime';
import { applyStyle, getShapeTheme } from '../utils';

export type LineOptions = Record<string, any>;
export type LineOptions = {
/**
* Whether show arrow.
*/
arrow?: boolean;
/**
* Arrow size(px), default is 4px.
*/
arrowSize?: number;
/**
* Fill color of arrow.
*/
arrowFill?: string;
/**
* Stroke color of arrow.
*/
arrowStroke?: string;
/**
* Others.
*/
[key: string]: any;
};

function getArrowMarker(arrowSize: number, arrowStyle: any) {
const arrowMarker = new Path({
style: {
path: `M ${arrowSize},${arrowSize} L -${arrowSize},0 L ${arrowSize},-${arrowSize} L 0,0 Z`,
anchor: '0.5 0.5',
transformOrigin: 'center',
...arrowStyle,
},
});
return arrowMarker;
}

function getPath(points: Vector2[], coordinate: Coordinate) {
if (!isPolar(coordinate))
Expand All @@ -32,7 +66,7 @@ function getTransform(coordinate: Coordinate, transform?: Primitive) {
}

export const Line: SC<LineOptions> = (options) => {
const { ...style } = options;
const { arrow, arrowSize = 4, ...style } = options;
return (points, value, coordinate, theme) => {
const { mark, shape, defaultShape } = value;
const { defaultColor, lineWidth, ...shapeTheme } = getShapeTheme(
Expand All @@ -43,6 +77,14 @@ export const Line: SC<LineOptions> = (options) => {
);
const { color = defaultColor, size = lineWidth } = value;

const arrowMarker = arrow
? getArrowMarker(arrowSize, {
fill: style.stroke || color,
stroke: style.stroke || color,
...subObject(style, 'arrow'),
})
: null;

const path = getPath(points, coordinate);
const transform = getTransform(coordinate, value.transform);

Expand All @@ -52,6 +94,7 @@ export const Line: SC<LineOptions> = (options) => {
.style('stroke', color)
.style('lineWidth', size)
.style('transform', transform)
.style('markerEnd', arrowMarker)
.call(applyStyle, style)
.node();
};
Expand Down
44 changes: 6 additions & 38 deletions src/shape/link/link.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,17 @@
import { Path } from '@antv/g';
import { path as d3path } from 'd3-path';
import { applyStyle, arrowPoints, ArrowOptions, getShapeTheme } from '../utils';
import { select } from '../../utils/selection';
import { ArrowOptions } from '../utils';
import { ShapeComponent as SC } from '../../runtime';
import { Vector } from '../../shape/vector/vector';

export type LinkOptions = ArrowOptions;

/**
* Connect 2 points with a single line.
* Connect 2 points with a single line with arrow.
* ----->
*/
export const Link: SC<LinkOptions> = (options) => {
const { arrowSize, ...style } = options;
const { arrow = false } = options;
return (points, value, coordinate, theme) => {
const { mark, shape, defaultShape } = value;
const { defaultColor, ...shapeTheme } = getShapeTheme(
theme,
mark,
shape,
defaultShape,
);
const { color = defaultColor, transform } = value;
const [from, to] = points;

// Draw line
const path = d3path();
path.moveTo(...from);
path.lineTo(...to);

// Draw 2 arrows.
if (arrowSize) {
// Calculate arrow end point.
const [arrow1, arrow2] = arrowPoints(from, to, { arrowSize });
path.moveTo(...to);
path.lineTo(...arrow1);
path.moveTo(...to);
path.lineTo(...arrow2);
}

return select(new Path())
.call(applyStyle, shapeTheme)
.style('d', path.toString())
.style('stroke', color)
.style('transform', transform)
.call(applyStyle, style)
.node();
return Vector({ ...options, arrow })(points, value, coordinate, theme);
};
};

Expand Down
4 changes: 4 additions & 0 deletions src/shape/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export function appendPolygon(path: D3Path, points: Vector2[]) {
}

export type ArrowOptions = {
/**
* Whether show arrow of line.
*/
arrow?: boolean;
/**
* Arrow size, can be a px number, or a percentage string. Default: '40%'
*/
Expand Down
21 changes: 12 additions & 9 deletions src/shape/vector/vector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,36 @@ import { ShapeComponent as SC } from '../../runtime';
export type VectorOptions = ArrowOptions;

/**
* Connect 2 points with a single line with arrow.
* ----->
*/
export const Vector: SC<VectorOptions> = (options) => {
const { arrowSize = '40%', ...style } = options;
const { arrow = true, arrowSize = '40%', ...style } = options;
return (points, value, coordinate, theme) => {
const { mark, shape, defaultShape, color, transform } = value;
const { mark, shape, defaultShape, transform } = value;
const { defaultColor, ...shapeTheme } = getShapeTheme(
theme,
mark,
shape,
defaultShape,
);
const { color = defaultColor } = value;
const [from, to] = points;

// Calculate arrow end point.
const [arrow1, arrow2] = arrowPoints(from, to, { arrowSize });

// Draw line
const path = d3path();
path.moveTo(...from);
path.lineTo(...to);

// Draw 2 arrows.
path.moveTo(...to);
path.lineTo(...arrow1);
path.moveTo(...to);
path.lineTo(...arrow2);
if (arrow) {
// Calculate arrow end point.
const [arrow1, arrow2] = arrowPoints(from, to, { arrowSize });
path.moveTo(...to);
path.lineTo(...arrow1);
path.moveTo(...to);
path.lineTo(...arrow2);
}

return select(new Path())
.call(applyStyle, shapeTheme)
Expand Down

0 comments on commit 84430a5

Please sign in to comment.