Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vrender-core",
"comment": "fix: fix issue with segment connect area",
"type": "none"
}
],
"packageName": "@visactor/vrender-core"
}
49 changes: 48 additions & 1 deletion packages/vrender-core/src/common/seg-context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { IPoint, IPointLike } from '@visactor/vutils';
import { abs, Point } from '@visactor/vutils';
import type { ICubicBezierCurve, ICurve, ICurveType, IDirection, ILineCurve, ISegPath2D } from '../interface';
import type { ICubicBezierCurve, ICurve, ICurveType, IDirection, ILineCurve, ISegment, ISegPath2D } from '../interface';
import { Direction } from './enums';
import { CubicBezierCurve } from './segment/curve/cubic-bezier';
import { LineCurve } from './segment/curve/line';
Expand Down Expand Up @@ -158,6 +158,53 @@ export class SegContext implements ISegPath2D {
this.length = this.curves.reduce((l, c) => l + c.getLength(), 0);
return this.length;
}

reverse() {
this.curves.reverse();
[this._startX, this._lastX] = [this._lastX, this._startX];
[this._startY, this._lastY] = [this._lastY, this._startY];
[this._startOriginP, this._lastOriginP] = [this._lastOriginP, this._startOriginP];
this.curves.forEach(c => {
c.reverse();
});
}

splitBySegments(segments: ISegment[]) {
if (segments.length === 1) {
return [this];
}
const res: SegContext[] = [];
let curveIdx = 0;
segments.forEach(seg => {
const lastP = seg.points[seg.points.length - 1];
const ctx = new SegContext(this.curveType, this.direction);
res.push(ctx);
for (; curveIdx < this.curves.length; curveIdx++) {
let curve = this.curves[curveIdx];
if (curve.originP2 === lastP) {
ctx.curves.push(curve);
curveIdx++;
for (; curveIdx < this.curves.length; curveIdx++) {
curve = this.curves[curveIdx];
if (curve.originP2 === lastP) {
ctx.curves.push(curve);
} else {
break;
}
}
ctx._lastX = lastP.x;
ctx._lastY = lastP.y;
ctx._lastOriginP = lastP;
ctx._startX = ctx.curves[0].p0.x;
ctx._startY = ctx.curves[0].p0.y;
ctx._startOriginP = ctx.curves[0].p0;
break;
}
ctx.curves.push(curve);
}
});
return res;
}
}

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/vrender-core/src/common/segment/curve/arc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ export class ArcCurve extends Curve implements IArcCurve {
includeX(x: number): boolean {
throw new Error('ArcCurve暂不支持includeX');
}

reverse() {
throw new Error('ArcCurve暂不支持reverse');
}
}
1 change: 1 addition & 0 deletions packages/vrender-core/src/common/segment/curve/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ export abstract class Curve implements ICurve<IPoint> {
protected abstract calcLength(): number;
protected abstract calcProjLength(direction: IDirection): number;
abstract draw(path: IPath2D, x: number, y: number, sx: number, sy: number, percent: number): void;
abstract reverse(): void;
}
13 changes: 9 additions & 4 deletions packages/vrender-core/src/common/segment/curve/cubic-bezier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ export class CubicBezierCurve extends Curve implements ICubicBezierCurve {
type: number = CurveTypeEnum.CubicBezierCurve;
declare originP1?: IPointLike;
declare originP2?: IPointLike;
declare readonly p0: IPoint;
declare readonly p1: IPoint;
declare readonly p2: IPoint;
declare readonly p3: IPoint;
declare p0: IPoint;
declare p1: IPoint;
declare p2: IPoint;
declare p3: IPoint;
constructor(p0: IPoint, p1: IPoint, p2: IPoint, p3: IPoint) {
super();
this.p0 = p0;
Expand Down Expand Up @@ -153,4 +153,9 @@ export class CubicBezierCurve extends Curve implements ICubicBezierCurve {
const t = (x - minX) / (maxX - minX);
return this.getPointAt(t).y;
}

reverse() {
[this.p0, this.p1, this.p2, this.p3] = [this.p3, this.p2, this.p1, this.p0];
[this.originP1, this.originP2] = [this.originP2, this.originP1];
}
}
4 changes: 4 additions & 0 deletions packages/vrender-core/src/common/segment/curve/ellipse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,8 @@ export class EllipseCurve extends Curve implements IEllipseCurve {
includeX(x: number): boolean {
throw new Error('QuadraticBezierCurve暂不支持includeX');
}

reverse() {
throw new Error('暂不支持');
}
}
5 changes: 5 additions & 0 deletions packages/vrender-core/src/common/segment/curve/line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,9 @@ export class LineCurve extends Curve implements ILineCurve {
}
return Infinity;
}

reverse() {
[this.p0, this.p1] = [this.p1, this.p0];
[this.originP1, this.originP2] = [this.originP2, this.originP1];
}
}
4 changes: 4 additions & 0 deletions packages/vrender-core/src/common/segment/curve/move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ export class MoveCurve extends Curve implements IMoveCurve {
getYAt(x: number): number {
return Infinity;
}

reverse() {
[this.p0, this.p1] = [this.p1, this.p0];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ export class QuadraticBezierCurve extends Curve implements IQuadraticBezierCurve
declare originP1?: IPointLike;
declare originP2?: IPointLike;

declare readonly p0: IPoint;
declare readonly p1: IPoint;
declare readonly p2: IPoint;
declare p0: IPoint;
declare p1: IPoint;
declare p2: IPoint;
constructor(p0: IPoint, p1: IPoint, p2: IPoint) {
super();
this.p0 = p0;
Expand Down Expand Up @@ -70,4 +70,9 @@ export class QuadraticBezierCurve extends Curve implements IQuadraticBezierCurve
includeX(x: number): boolean {
throw new Error('QuadraticBezierCurve暂不支持includeX');
}

reverse() {
[this.p0, this.p1, this.p2] = [this.p2, this.p1, this.p0];
[this.originP1, this.originP2] = [this.originP2, this.originP1];
}
}
12 changes: 11 additions & 1 deletion packages/vrender-core/src/common/segment/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IPointLike } from '@visactor/vutils';
import type { ICurveType, IGenSegmentParams, ISegPath2D } from '../../interface';
import type { ICurveType, ISegment, ISegPath2D } from '../../interface';
import { genLinearSegments } from './linear';
import { genBasisSegments } from './basis';
import { genMonotoneXSegments, genMonotoneYSegments } from './monotone';
Expand Down Expand Up @@ -45,3 +45,13 @@ export function calcLineCache(
return genLinearSegments(points, params);
}
}

export function calcSegLineCache(
segments: ISegment[],
points: IPointLike[],
curveType: ICurveType,
params?: { startPoint?: IPointLike; curveTension?: number }
): ISegPath2D[] {
const cache = calcLineCache(points, curveType, params);
return (cache as any).splitBySegments(segments);
}
1 change: 1 addition & 0 deletions packages/vrender-core/src/interface/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface ICurve<T> {
draw: (path: IPath2D, x: number, y: number, sx: number, sy: number, percent: number) => void;
getYAt: (x: number) => number;
includeX: (x: number) => boolean;
reverse: () => void;
}

export interface ICubicBezierCurve extends ICurve<IPoint> {
Expand Down
144 changes: 94 additions & 50 deletions packages/vrender-core/src/render/contributions/render/area-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import type {
} from '../../../interface';
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import { ContributionProvider } from '../../../common/contribution-provider';
import { calcLineCache } from '../../../common/segment';
import { calcLineCache, calcSegLineCache } from '../../../common/segment';

import { getTheme } from '../../../graphic/theme';
import { AreaRenderContribution } from './contributions/constants';
Expand Down Expand Up @@ -240,62 +240,106 @@ export class DefaultCanvasAreaRender extends BaseRender<IArea> implements IGraph
// 更新cache
if (area.shouldUpdateShape()) {
if (segments && segments.length) {
let startPoint: IPointLike;
let lastTopSeg: { endX: number; endY: number };
const topCaches = segments
.map((seg, index) => {
if (seg.points.length <= 1) {
// 第一个点的话,直接设置lastTopSeg
if (index === 0) {
seg.points[0] && (lastTopSeg = { endX: seg.points[0].x, endY: seg.points[0].y });
return null;
}
}
// 添加上一个segment结束的点作为这个segment的起始点
if (index === 1) {
startPoint = { x: lastTopSeg.endX, y: lastTopSeg.endY };
} else if (index > 1) {
startPoint.x = lastTopSeg.endX;
startPoint.y = lastTopSeg.endY;
}
const data = calcLineCache(parsePoint(seg.points, connectedType), curveType, {
startPoint,
curveTension
});
lastTopSeg = data;
return data;
})
.filter(item => !!item);
let lastBottomSeg: ISegPath2D;
const bottomCaches = [];
for (let i = segments.length - 1; i >= 0; i--) {
const points = segments[i].points;
// let startPoint: IPointLike;
// let lastTopSeg: { endX: number; endY: number };
// 检测segment是否有定义curveType
const points: IPointLike[] = [];
segments.forEach(seg => {
parsePoint(seg.points, connectedType).forEach(p => {
points.push(p);
});
});
const topCaches = calcSegLineCache(segments, points, curveType, { curveTension });
// const topCaches = segments
// .map((seg, index) => {
// if (seg.points.length <= 1) {
// // 第一个点的话,直接设置lastTopSeg
// if (index === 0) {
// seg.points[0] && (lastTopSeg = { endX: seg.points[0].x, endY: seg.points[0].y });
// return null;
// }
// }
// // 添加上一个segment结束的点作为这个segment的起始点
// if (index === 1) {
// startPoint = { x: lastTopSeg.endX, y: lastTopSeg.endY };
// } else if (index > 1) {
// startPoint.x = lastTopSeg.endX;
// startPoint.y = lastTopSeg.endY;
// }
// const data = calcLineCache(parsePoint(seg.points, connectedType), curveType, {
// startPoint,
// curveTension
// });
// lastTopSeg = data;
// return data;
// })
// .filter(item => !!item);
const bottomSegments = [];
for (let i = 0; i < segments.length; i++) {
const points = parsePoint(segments[i].points, connectedType);
const bottomPoints: IPointLike[] = [];
for (let i = points.length - 1; i >= 0; i--) {
for (let i = 0; i < points.length; i++) {
bottomPoints.push({
x: points[i].x1 ?? points[i].x,
y: points[i].y1 ?? points[i].y
});
}
// 处理一下bottom的segments,bottom的segments需要手动添加endPoints
if (i !== 0) {
const lastSegmentPoints = segments[i - 1].points;
const endPoint = lastSegmentPoints[lastSegmentPoints.length - 1];
endPoint &&
bottomPoints.push({
x: endPoint.x1 ?? endPoint.x,
y: endPoint.y1 ?? endPoint.y
});
}
if (bottomPoints.length > 1) {
lastBottomSeg = calcLineCache(
parsePoint(bottomPoints, connectedType),
curveType === 'stepBefore' ? 'stepAfter' : curveType === 'stepAfter' ? 'stepBefore' : curveType,
{ curveTension }
);
bottomCaches.unshift(lastBottomSeg);
}
bottomSegments.push({
points: bottomPoints
});
}
// const bottomSegments = segments.map(seg => ({...seg, points: seg.points.map(item => ({
// x: item.x1 ?? item.x,
// y: item.y1 ?? item.y
// }))}));
const bottomPoints: IPointLike[] = [];
bottomSegments.forEach(seg => {
parsePoint(seg.points, connectedType).forEach(p => {
bottomPoints.push(p);
});
});
const bottomCaches = calcSegLineCache(
bottomSegments,
bottomPoints,
curveType === 'stepBefore' ? 'stepAfter' : curveType === 'stepAfter' ? 'stepBefore' : curveType,
{ curveTension }
) as any[];
// // 翻转点
bottomCaches.forEach(cache => {
cache.reverse && cache.reverse();
});
// let lastBottomSeg: ISegPath2D;
// console.log(bottomCaches);
// bottomCaches = [];
// console.log(bottomCaches);
// for (let i = segments.length - 1; i >= 0; i--) {
// const points = segments[i].points;
// const bottomPoints: IPointLike[] = [];
// for (let i = points.length - 1; i >= 0; i--) {
// bottomPoints.push({
// x: points[i].x1 ?? points[i].x,
// y: points[i].y1 ?? points[i].y
// });
// }
// // 处理一下bottom的segments,bottom的segments需要手动添加endPoints
// if (i !== 0) {
// const lastSegmentPoints = segments[i - 1].points;
// const endPoint = lastSegmentPoints[lastSegmentPoints.length - 1];
// endPoint &&
// bottomPoints.push({
// x: endPoint.x1 ?? endPoint.x,
// y: endPoint.y1 ?? endPoint.y
// });
// }
// if (bottomPoints.length > 1) {
// lastBottomSeg = calcLineCache(
// parsePoint(bottomPoints, connectedType),
// curveType === 'stepBefore' ? 'stepAfter' : curveType === 'stepAfter' ? 'stepBefore' : curveType,
// { curveTension }
// );
// bottomCaches.unshift(lastBottomSeg);
// }
// }
area.cacheArea = bottomCaches.map((item, index) => ({
top: topCaches[index],
bottom: item
Expand Down
Loading