Merge remote-tracking branch 'upstream/main'

This commit is contained in:
2026-08-26 09:31:34 +08:00
parent 9b8c4bfab6
commit 394eb0285d
34137 changed files with 3589424 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
/**
* 圆弧
*/
import Path, { PathProps } from '../Path';
export class ArcShape {
cx = 0;
cy = 0;
r = 0;
startAngle = 0;
endAngle = Math.PI * 2
clockwise? = true
}
export interface ArcProps extends PathProps {
shape?: Partial<ArcShape>
}
class Arc extends Path<ArcProps> {
shape: ArcShape
constructor(opts?: ArcProps) {
super(opts);
}
getDefaultStyle() {
return {
stroke: '#000',
fill: null as string
};
}
getDefaultShape() {
return new ArcShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: ArcShape) {
const x = shape.cx;
const y = shape.cy;
const r = Math.max(shape.r, 0);
const startAngle = shape.startAngle;
const endAngle = shape.endAngle;
const clockwise = shape.clockwise;
const unitX = Math.cos(startAngle);
const unitY = Math.sin(startAngle);
ctx.moveTo(unitX * r + x, unitY * r + y);
ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
}
}
Arc.prototype.type = 'arc';
export default Arc;
+138
View File
@@ -0,0 +1,138 @@
/**
* 贝塞尔曲线
*/
import Path, { PathProps } from '../Path';
import * as vec2 from '../../core/vector';
import {
quadraticSubdivide,
cubicSubdivide,
quadraticAt,
cubicAt,
quadraticDerivativeAt,
cubicDerivativeAt
} from '../../core/curve';
const out: number[] = [];
export class BezierCurveShape {
x1 = 0
y1 = 0
x2 = 0
y2 = 0
cpx1 = 0
cpy1 = 0
cpx2?: number
cpy2?: number
// Curve show percent, for animating
percent = 1
}
function someVectorAt(shape: BezierCurveShape, t: number, isTangent: boolean) {
const cpx2 = shape.cpx2;
const cpy2 = shape.cpy2;
if (cpx2 != null || cpy2 != null) {
return [
(isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),
(isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)
];
}
else {
return [
(isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),
(isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)
];
}
}
export interface BezierCurveProps extends PathProps {
shape?: Partial<BezierCurveShape>
}
class BezierCurve extends Path<BezierCurveProps> {
shape: BezierCurveShape
constructor(opts?: BezierCurveProps) {
super(opts);
}
getDefaultStyle() {
return {
stroke: '#000',
fill: null as string
};
}
getDefaultShape() {
return new BezierCurveShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: BezierCurveShape) {
let x1 = shape.x1;
let y1 = shape.y1;
let x2 = shape.x2;
let y2 = shape.y2;
let cpx1 = shape.cpx1;
let cpy1 = shape.cpy1;
let cpx2 = shape.cpx2;
let cpy2 = shape.cpy2;
let percent = shape.percent;
if (percent === 0) {
return;
}
ctx.moveTo(x1, y1);
if (cpx2 == null || cpy2 == null) {
if (percent < 1) {
quadraticSubdivide(x1, cpx1, x2, percent, out);
cpx1 = out[1];
x2 = out[2];
quadraticSubdivide(y1, cpy1, y2, percent, out);
cpy1 = out[1];
y2 = out[2];
}
ctx.quadraticCurveTo(
cpx1, cpy1,
x2, y2
);
}
else {
if (percent < 1) {
cubicSubdivide(x1, cpx1, cpx2, x2, percent, out);
cpx1 = out[1];
cpx2 = out[2];
x2 = out[3];
cubicSubdivide(y1, cpy1, cpy2, y2, percent, out);
cpy1 = out[1];
cpy2 = out[2];
y2 = out[3];
}
ctx.bezierCurveTo(
cpx1, cpy1,
cpx2, cpy2,
x2, y2
);
}
}
/**
* Get point at percent
*/
pointAt(t: number) {
return someVectorAt(this.shape, t, false);
}
/**
* Get tangent at percent
*/
tangentAt(t: number) {
const p = someVectorAt(this.shape, t, true);
return vec2.normalize(p, p);
}
};
BezierCurve.prototype.type = 'bezier-curve';
export default BezierCurve;
+38
View File
@@ -0,0 +1,38 @@
/**
* 圆形
*/
import Path, { PathProps } from '../Path';
export class CircleShape {
cx = 0
cy = 0
r = 0
}
export interface CircleProps extends PathProps {
shape?: Partial<CircleShape>
}
class Circle extends Path<CircleProps> {
shape: CircleShape
constructor(opts?: CircleProps) {
super(opts);
}
getDefaultShape() {
return new CircleShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: CircleShape) {
// Use moveTo to start a new sub path.
// Or it will be connected to other subpaths when in CompoundPath
ctx.moveTo(shape.cx + shape.r, shape.cy);
ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2);
}
};
Circle.prototype.type = 'circle';
export default Circle;
+58
View File
@@ -0,0 +1,58 @@
/**
* 水滴形状
*/
import Path, { PathProps } from '../Path';
export class DropletShape {
cx = 0
cy = 0
width = 0
height = 0
}
export interface DropletProps extends PathProps {
shape?: Partial<DropletShape>
}
class Droplet extends Path<DropletProps> {
shape: DropletShape
constructor(opts?: DropletProps) {
super(opts);
}
getDefaultShape() {
return new DropletShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: DropletShape) {
const x = shape.cx;
const y = shape.cy;
const a = shape.width;
const b = shape.height;
ctx.moveTo(x, y + a);
ctx.bezierCurveTo(
x + a,
y + a,
x + a * 3 / 2,
y - a / 3,
x,
y - b
);
ctx.bezierCurveTo(
x - a * 3 / 2,
y - a / 3,
x - a,
y + a,
x,
y + a
);
ctx.closePath();
}
}
Droplet.prototype.type = 'droplet';
export default Droplet;
+49
View File
@@ -0,0 +1,49 @@
/**
* 椭圆形状
*/
import Path, { PathProps } from '../Path';
export class EllipseShape {
cx = 0
cy = 0
rx = 0
ry = 0
}
export interface EllipseProps extends PathProps {
shape?: Partial<EllipseShape>
}
class Ellipse extends Path<EllipseProps> {
shape: EllipseShape
constructor(opts?: EllipseProps) {
super(opts);
}
getDefaultShape() {
return new EllipseShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: EllipseShape) {
const k = 0.5522848;
const x = shape.cx;
const y = shape.cy;
const a = shape.rx;
const b = shape.ry;
const ox = a * k; // 水平控制点偏移量
const oy = b * k; // 垂直控制点偏移量
// 从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线
ctx.moveTo(x - a, y);
ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);
ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);
ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);
ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);
ctx.closePath();
}
}
Ellipse.prototype.type = 'ellipse';
export default Ellipse;
+51
View File
@@ -0,0 +1,51 @@
/**
* 心形
*/
import Path, { PathProps } from '../Path';
export class HeartShape {
cx = 0
cy = 0
width = 0
height = 0
}
export interface HeartProps extends PathProps {
shape?: Partial<HeartShape>
}
class Heart extends Path<HeartProps> {
shape: HeartShape
constructor(opts?: HeartProps) {
super(opts);
}
getDefaultShape() {
return new HeartShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: HeartShape) {
const x = shape.cx;
const y = shape.cy;
const a = shape.width;
const b = shape.height;
ctx.moveTo(x, y);
ctx.bezierCurveTo(
x + a / 2, y - b * 2 / 3,
x + a * 2, y + b / 3,
x, y + b
);
ctx.bezierCurveTo(
x - a * 2, y + b / 3,
x - a / 2, y - b * 2 / 3,
x, y
);
}
}
Heart.prototype.type = 'heart';
export default Heart;
+60
View File
@@ -0,0 +1,60 @@
/**
* 正多边形
*/
import Path, { PathProps } from '../Path';
const PI = Math.PI;
const sin = Math.sin;
const cos = Math.cos;
export class IsogonShape {
x = 0
y = 0
r = 0
n = 0
}
export interface IsogonProps extends PathProps {
shape?: Partial<IsogonShape>
}
class Isogon extends Path<IsogonProps> {
shape: IsogonShape
constructor(opts?: IsogonProps) {
super(opts);
}
getDefaultShape() {
return new IsogonShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: IsogonShape) {
const n = shape.n;
if (!n || n < 2) {
return;
}
const x = shape.x;
const y = shape.y;
const r = shape.r;
const dStep = 2 * PI / n;
let deg = -PI / 2;
ctx.moveTo(x + r * cos(deg), y + r * sin(deg));
for (let i = 0, end = n - 1; i < end; i++) {
deg += dStep;
ctx.lineTo(x + r * cos(deg), y + r * sin(deg));
}
ctx.closePath();
return;
}
}
Isogon.prototype.type = 'isogon';
export default Isogon;
+96
View File
@@ -0,0 +1,96 @@
/**
* 直线
* @module zrender/graphic/shape/Line
*/
import Path, { PathProps } from '../Path';
import {subPixelOptimizeLine} from '../helper/subPixelOptimize';
import { VectorArray } from '../../core/vector';
// Avoid create repeatly.
const subPixelOptimizeOutputShape = {};
export class LineShape {
// Start point
x1 = 0
y1 = 0
// End point
x2 = 0
y2 = 0
percent = 1
}
export interface LineProps extends PathProps {
shape?: Partial<LineShape>
}
class Line extends Path<LineProps> {
shape: LineShape
constructor(opts?: LineProps) {
super(opts);
}
getDefaultStyle() {
return {
stroke: '#000',
fill: null as string
};
}
getDefaultShape() {
return new LineShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: LineShape) {
let x1;
let y1;
let x2;
let y2;
if (this.subPixelOptimize) {
const optimizedShape = subPixelOptimizeLine(
subPixelOptimizeOutputShape, shape, this.style
);
x1 = optimizedShape.x1;
y1 = optimizedShape.y1;
x2 = optimizedShape.x2;
y2 = optimizedShape.y2;
}
else {
x1 = shape.x1;
y1 = shape.y1;
x2 = shape.x2;
y2 = shape.y2;
}
const percent = shape.percent;
if (percent === 0) {
return;
}
ctx.moveTo(x1, y1);
if (percent < 1) {
x2 = x1 * (1 - percent) + x2 * percent;
y2 = y1 * (1 - percent) + y2 * percent;
}
ctx.lineTo(x2, y2);
}
/**
* Get point at percent
*/
pointAt(p: number): VectorArray {
const shape = this.shape;
return [
shape.x1 * (1 - p) + shape.x2 * p,
shape.y1 * (1 - p) + shape.y2 * p
];
}
}
Line.prototype.type = 'line';
export default Line;
+38
View File
@@ -0,0 +1,38 @@
/**
* 多边形
* @module zrender/shape/Polygon
*/
import Path, { PathProps } from '../Path';
import * as polyHelper from '../helper/poly';
import { VectorArray } from '../../core/vector';
export class PolygonShape {
points: VectorArray[] = null
smooth?: number = 0
smoothConstraint?: VectorArray[] = null
}
export interface PolygonProps extends PathProps {
shape?: Partial<PolygonShape>
}
class Polygon extends Path<PolygonProps> {
shape: PolygonShape
constructor(opts?: PolygonProps) {
super(opts);
}
getDefaultShape() {
return new PolygonShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: PolygonShape) {
polyHelper.buildPath(ctx, shape, true);
}
};
Polygon.prototype.type = 'polygon';
export default Polygon;
+45
View File
@@ -0,0 +1,45 @@
/**
* @module zrender/graphic/shape/Polyline
*/
import Path, { PathProps } from '../Path';
import * as polyHelper from '../helper/poly';
import { VectorArray } from '../../core/vector';
export class PolylineShape {
points: VectorArray[] = null
// Percent of displayed polyline. For animating purpose
percent?: number = 1
smooth?: number = 0
smoothConstraint?: VectorArray[] = null
}
export interface PolylineProps extends PathProps {
shape?: Partial<PolylineShape>
}
class Polyline extends Path<PolylineProps> {
shape: PolylineShape
constructor(opts?: PolylineProps) {
super(opts);
}
getDefaultStyle() {
return {
stroke: '#000',
fill: null as string
};
}
getDefaultShape() {
return new PolylineShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: PolylineShape) {
polyHelper.buildPath(ctx, shape, false);
}
}
Polyline.prototype.type = 'polyline';
export default Polyline;
+79
View File
@@ -0,0 +1,79 @@
/**
* 矩形
* @module zrender/graphic/shape/Rect
*/
import Path, { PathProps } from '../Path';
import * as roundRectHelper from '../helper/roundRect';
import {subPixelOptimizeRect} from '../helper/subPixelOptimize';
export class RectShape {
// 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4
// r缩写为1 相当于 [1, 1, 1, 1]
// r缩写为[1] 相当于 [1, 1, 1, 1]
// r缩写为[1, 2] 相当于 [1, 2, 1, 2]
// r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2]
r?: number | number[]
x = 0
y = 0
width = 0
height = 0
}
export interface RectProps extends PathProps {
shape?: Partial<RectShape>
}
// Avoid create repeatly.
const subPixelOptimizeOutputShape = {};
class Rect extends Path<RectProps> {
shape: RectShape
constructor(opts?: RectProps) {
super(opts);
}
getDefaultShape() {
return new RectShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: RectShape) {
let x: number;
let y: number;
let width: number;
let height: number;
if (this.subPixelOptimize) {
const optimizedShape = subPixelOptimizeRect(subPixelOptimizeOutputShape, shape, this.style);
x = optimizedShape.x;
y = optimizedShape.y;
width = optimizedShape.width;
height = optimizedShape.height;
optimizedShape.r = shape.r;
shape = optimizedShape;
}
else {
x = shape.x;
y = shape.y;
width = shape.width;
height = shape.height;
}
if (!shape.r) {
ctx.rect(x, y, width, height);
}
else {
roundRectHelper.buildPath(ctx, shape);
}
}
isZeroArea() {
return !this.shape.width || !this.shape.height;
}
}
Rect.prototype.type = 'rect';
export default Rect;
+41
View File
@@ -0,0 +1,41 @@
/**
* 圆环
*/
import Path, { PathProps } from '../Path';
export class RingShape {
cx = 0
cy = 0
r = 0
r0 = 0
}
export interface RingProps extends PathProps {
shape?: Partial<RingShape>
}
class Ring extends Path<RingProps> {
shape: RingShape
constructor(opts?: RingProps) {
super(opts);
}
getDefaultShape() {
return new RingShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: RingShape) {
const x = shape.cx;
const y = shape.cy;
const PI2 = Math.PI * 2;
ctx.moveTo(x + shape.r, y);
ctx.arc(x, y, shape.r, 0, PI2, false);
ctx.moveTo(x + shape.r0, y);
ctx.arc(x, y, shape.r0, 0, PI2, true);
}
}
Ring.prototype.type = 'ring';
export default Ring;
+76
View File
@@ -0,0 +1,76 @@
/**
* 玫瑰线
* @module zrender/graphic/shape/Rose
*/
import Path, { PathProps } from '../Path';
const sin = Math.sin;
const cos = Math.cos;
const radian = Math.PI / 180;
export class RoseShape {
cx = 0
cy = 0
r: number[] = []
k = 0
n = 1
}
export interface RoseProps extends PathProps {
shape?: Partial<RoseShape>
}
class Rose extends Path<RoseProps> {
shape: RoseShape
constructor(opts?: RoseProps) {
super(opts);
}
getDefaultStyle() {
return {
stroke: '#000',
fill: null as string
};
}
getDefaultShape() {
return new RoseShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: RoseShape) {
const R = shape.r;
const k = shape.k;
const n = shape.n;
const x0 = shape.cx;
const y0 = shape.cy;
let x;
let y;
let r;
ctx.moveTo(x0, y0);
for (let i = 0, len = R.length; i < len; i++) {
r = R[i];
for (let j = 0; j <= 360 * n; j++) {
x = r
* sin(k / n * j % 360 * radian)
* cos(j * radian)
+ x0;
y = r
* sin(k / n * j % 360 * radian)
* sin(j * radian)
+ y0;
ctx.lineTo(x, y);
}
}
}
}
Rose.prototype.type = 'rose';
export default Rose;
+56
View File
@@ -0,0 +1,56 @@
import Path, { PathProps } from '../Path';
import * as roundSectorHelper from '../helper/roundSector';
export class SectorShape {
cx = 0
cy = 0
r0 = 0
r = 0
startAngle = 0
endAngle = Math.PI * 2
clockwise = true
/**
* Corner radius of sector
*
* clockwise, from inside to outside, four corners are
* inner start -> inner end
* outer start -> outer end
*
* 5 => [5, 5, 5, 5]
* [5] => [5, 5, 0, 0]
* [5, 10] => [5, 5, 10, 10]
* [5, 10, 15] => [5, 10, 15, 15]
* [5, 10, 15, 20] => [5, 10, 15, 20]
*/
cornerRadius: number | number[] = 0
}
export interface SectorProps extends PathProps {
shape?: Partial<SectorShape>
}
class Sector extends Path<SectorProps> {
shape: SectorShape
constructor(opts?: SectorProps) {
super(opts);
}
getDefaultShape() {
return new SectorShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: SectorShape) {
roundSectorHelper.buildPath(ctx, shape);
}
isZeroArea() {
return this.shape.startAngle === this.shape.endAngle
|| this.shape.r === this.shape.r0;
}
}
Sector.prototype.type = 'sector';
export default Sector;
+76
View File
@@ -0,0 +1,76 @@
/**
* n角星(n>3)
* @module zrender/graphic/shape/Star
*/
import Path, { PathProps } from '../Path';
const PI = Math.PI;
const cos = Math.cos;
const sin = Math.sin;
export class StarShape {
cx = 0
cy = 0
n = 3
r0: number
r = 0
}
export interface StarProps extends PathProps {
shape?: Partial<StarShape>
}
class Star extends Path<StarProps> {
shape: StarShape
constructor(opts?: StarProps) {
super(opts);
}
getDefaultShape() {
return new StarShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: StarShape) {
const n = shape.n;
if (!n || n < 2) {
return;
}
const x = shape.cx;
const y = shape.cy;
const r = shape.r;
let r0 = shape.r0;
// 如果未指定内部顶点外接圆半径,则自动计算
if (r0 == null) {
r0 = n > 4
// 相隔的外部顶点的连线的交点,
// 被取为内部交点,以此计算r0
? r * cos(2 * PI / n) / cos(PI / n)
// 二三四角星的特殊处理
: r / 3;
}
const dStep = PI / n;
let deg = -PI / 2;
const xStart = x + r * cos(deg);
const yStart = y + r * sin(deg);
deg += dStep;
// 记录边界点,用于判断inside
ctx.moveTo(xStart, yStart);
for (let i = 0, end = n * 2 - 1, ri; i < end; i++) {
ri = i % 2 === 0 ? r0 : r;
ctx.lineTo(x + ri * cos(deg), y + ri * sin(deg));
deg += dStep;
}
ctx.closePath();
}
}
Star.prototype.type = 'star';
export default Star;
+92
View File
@@ -0,0 +1,92 @@
/**
* 内外旋轮曲线
* @module zrender/graphic/shape/Trochold
*/
import Path, { PathProps } from '../Path';
const cos = Math.cos;
const sin = Math.sin;
export class TrochoidShape {
cx = 0
cy = 0
r = 0
r0 = 0
d = 0
location = 'out'
}
export interface TrochoidProps extends PathProps {
shape?: Partial<TrochoidShape>
}
class Trochoid extends Path<TrochoidProps> {
shape: TrochoidShape
constructor(opts?: TrochoidProps) {
super(opts);
}
getDefaultStyle() {
return {
stroke: '#000',
fill: null as string
};
}
getDefaultShape() {
return new TrochoidShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: TrochoidShape) {
const R = shape.r;
const r = shape.r0;
const d = shape.d;
const offsetX = shape.cx;
const offsetY = shape.cy;
const delta = shape.location === 'out' ? 1 : -1;
let x1;
let y1;
let x2;
let y2;
if (shape.location && R <= r) {
return;
}
let num = 0;
let i = 1;
let theta;
x1 = (R + delta * r) * cos(0)
- delta * d * cos(0) + offsetX;
y1 = (R + delta * r) * sin(0)
- d * sin(0) + offsetY;
ctx.moveTo(x1, y1);
// 计算结束时的i
do {
num++;
}
while ((r * num) % (R + delta * r) !== 0);
do {
theta = Math.PI / 180 * i;
x2 = (R + delta * r) * cos(theta)
- delta * d * cos((R / r + delta) * theta)
+ offsetX;
y2 = (R + delta * r) * sin(theta)
- d * sin((R / r + delta) * theta)
+ offsetY;
ctx.lineTo(x2, y2);
i++;
}
while (i <= (r * num) / (R + delta * r) * 360);
}
}
Trochoid.prototype.type = 'trochoid';
export default Trochoid;