Skip to content

Commit

Permalink
feat: Add tag dom rendering / adapt to Polygonal curve
Browse files Browse the repository at this point in the history
  • Loading branch information
Kerwin-L committed Jan 5, 2022
1 parent 1b125d2 commit ec0bfcc
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 90 deletions.
132 changes: 49 additions & 83 deletions packages/lb-annotation/src/core/toolOperation/ViewOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,72 +10,19 @@ import RectUtils from '@/utils/tool/RectUtils';
import PolygonUtils from '@/utils/tool/PolygonUtils';
import { BasicToolOperation, IBasicToolOperationProps } from './basicToolOperation';
import MathUtils from '@/utils/MathUtils';
import { DEFAULT_FONT } from '@/constant/tool';
import RenderDomClass from '@/utils/tool/RenderDomClass';
import { DEFAULT_FONT, ELineTypes, SEGMENT_NUMBER } from '@/constant/tool';
import { DEFAULT_TEXT_SHADOW, DEFAULT_TEXT_OFFSET, TEXT_ATTRIBUTE_OFFSET } from '@/constant/annotation';

const newScope = 3;

interface IBasicStyle {
stroke?: string; // 边框颜色
fill?: string; // 填充颜色
thickness?: number; // 当前图形宽度
}

interface IGraphicsBasicConfig extends IBasicStyle {
hiddenText?: boolean; // 是否隐藏文本
isReference?: boolean; // 是否进行的参考显示
}

interface IAnnotationData {
type: 'rect' | 'polygon' | 'line' | 'point' | 'text';
annotation: IBasicRect & IBasicPolygon & IBasicLine & IPoint & IBasicText;
}

interface IBasicRect extends IGraphicsBasicConfig {
id: string;
x: number;
y: number;
width: number;
height: number;
}

interface IBasicPolygon extends IGraphicsBasicConfig {
id: string;
pointList: IPoint[];
showDirection?: boolean;
specialPoint?: boolean; // 顶点是否特殊点
specialEdge?: boolean; // 顶点与其下一个顶点连成的边是否为特殊边
}

type IBasicLine = IBasicPolygon;

interface IBasicText {
x: number;
y: number;
text: string; // Use \n for line feed
position: 'rt';
textMaxWidth?: number;

color?: string;
background?: string;
lineHeight?: number;
font?: string; // canvas-font https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/font
}

interface IPoint extends IGraphicsBasicConfig {
x: number;
y: number;
radius?: number;
}
const DEFAULT_RADIUS = 3;
const DEFAULT_STROKE_COLOR = '#6371FF';

type IViewOperationProps = {
style: IBasicStyle;
annotations: IAnnotationData[];
} & IBasicToolOperationProps;

const DEFAULT_RADIUS = 3;
const DEFAULT_STROKE_COLOR = '#6371FF';

export default class ViewOperation extends BasicToolOperation {
public style: IBasicStyle = {};

Expand All @@ -85,11 +32,17 @@ export default class ViewOperation extends BasicToolOperation {

private loading: boolean; // 加载图片时不渲染图形

private renderDomInstance: RenderDomClass;

constructor(props: IViewOperationProps) {
super({ ...props, showDefaultCursor: true });
this.style = props.style ?? { stroke: DEFAULT_STROKE_COLOR, thickness: 3 };
this.annotations = props.annotations;
this.loading = false;
this.renderDomInstance = new RenderDomClass({
container: this.container,
height: this.canvas.height,
});
}

public setLoading(loading: boolean) {
Expand Down Expand Up @@ -264,6 +217,9 @@ export default class ViewOperation extends BasicToolOperation {
if (this.loading === true) {
return;
}
this.renderDomInstance.render(
this.annotations.filter((v) => v.type === 'text' && v.annotation.position === 'rt').map((v) => v.annotation),
);

this.annotations.forEach((annotation) => {
switch (annotation.type) {
Expand Down Expand Up @@ -326,32 +282,37 @@ export default class ViewOperation extends BasicToolOperation {
}
case 'polygon': {
const polygon = annotation.annotation;
const { lineType = ELineTypes.Line } = polygon;
const renderPolygon = AxisUtils.changePointListByZoom(polygon?.pointList ?? [], this.zoom, this.currentPos);
const style = this.getSpecificStyle(polygon);
if (polygon.id === this.mouseHoverID || style.fill) {
const fillArr = rgba(style?.fill ?? style?.stroke ?? DEFAULT_STROKE_COLOR);
const fill = `rgba(${fillArr[0]}, ${fillArr[1]}, ${fillArr[2]},${fillArr[3] * 0.8})`;
DrawUtils.drawPolygonWithFill(this.canvas, renderPolygon, { color: fill });
DrawUtils.drawPolygonWithFill(this.canvas, renderPolygon, { color: fill, lineType });
}
DrawUtils.drawPolygon(this.canvas, renderPolygon, {
const newPointList = DrawUtils.drawPolygon(this.canvas, renderPolygon, {
...style,
isClose: true,
...this.getReferenceOptions(polygon?.isReference),
lineType,
});

const isShowDirection = polygon?.showDirection === true && polygon?.pointList?.length > 2;

// 是否展示方向
if (isShowDirection) {
DrawUtils.drawArrowByCanvas(
this.canvas,
renderPolygon[0],
MathUtils.getLineCenterPoint([renderPolygon[0], renderPolygon[1]]),
{
color: style.stroke,
thickness: style.thickness,
},
);
let startPoint = renderPolygon[0];
let endPoint = MathUtils.getLineCenterPoint([renderPolygon[0], renderPolygon[1]]);

if (lineType === ELineTypes.Curve) {
const pos = Math.floor(SEGMENT_NUMBER / 2);
startPoint = newPointList[pos];
endPoint = newPointList[pos + 1];
}
DrawUtils.drawArrowByCanvas(this.canvas, startPoint, endPoint, {
color: style.stroke,
thickness: style.thickness,
});
DrawUtils.drawCircleWithFill(this.canvas, renderPolygon[0], style.thickness + 2, {
color: style.stroke,
});
Expand Down Expand Up @@ -384,24 +345,32 @@ export default class ViewOperation extends BasicToolOperation {

case 'line': {
const line = annotation.annotation;
const { lineType = ELineTypes.Line } = line;

const renderLine = AxisUtils.changePointListByZoom(line.pointList as IPoint[], this.zoom, this.currentPos);
const style = this.getSpecificStyle(line);
DrawUtils.drawPolygon(this.canvas, renderLine, { ...style, ...this.getReferenceOptions(line?.isReference) });
const newPointList = DrawUtils.drawPolygon(this.canvas, renderLine, {
...style,
...this.getReferenceOptions(line?.isReference),
lineType,
});

const isShowDirection = line?.showDirection === true && line?.pointList?.length > 2;

// 是否展示方向
if (isShowDirection) {
DrawUtils.drawArrowByCanvas(
this.canvas,
renderLine[0],
MathUtils.getLineCenterPoint([renderLine[0], renderLine[1]]),
{
color: style.stroke,
thickness: style.thickness,
},
);
let startPoint = renderLine[0];
let endPoint = MathUtils.getLineCenterPoint([renderLine[0], renderLine[1]]);

if (lineType === ELineTypes.Curve) {
const pos = Math.floor(SEGMENT_NUMBER / 2);
startPoint = newPointList[pos];
endPoint = newPointList[pos + 1];
}
DrawUtils.drawArrowByCanvas(this.canvas, startPoint, endPoint, {
color: style.stroke,
thickness: style.thickness,
});
DrawUtils.drawCircleWithFill(this.canvas, renderLine[0], style.thickness + 2, {
color: style.stroke,
});
Expand Down Expand Up @@ -486,12 +455,9 @@ export default class ViewOperation extends BasicToolOperation {
fontHeight = 0,
} = MathUtils.getTextArea(this.canvas, textAnnotation.text, textMaxWidth, font, lineHeight);

// 定位在右上角
// 定位在右上角 - 以 dom 元素展现
if (position === 'rt') {
Object.assign(renderPoint, {
x: this.canvas.width - width - paddingLR * 2,
y: 0,
});
break;
}

// 字体背景
Expand Down
56 changes: 56 additions & 0 deletions packages/lb-annotation/src/types/tool/annotationView.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
declare interface IBasicStyle {
stroke?: string; // 边框颜色
fill?: string; // 填充颜色
thickness?: number; // 当前图形宽度
}

declare interface IGraphicsBasicConfig extends IBasicStyle {
hiddenText?: boolean; // 是否隐藏文本
isReference?: boolean; // 是否进行的参考显示
}

declare interface IAnnotationData {
type: 'rect' | 'polygon' | 'line' | 'point' | 'text';
annotation: IBasicRect & IBasicPolygon & IBasicLine & IPoint & IBasicText;
}

declare interface IBasicRect extends IGraphicsBasicConfig {
id: string;
x: number;
y: number;
width: number;
height: number;
}

declare interface IBasicPoint extends IGraphicsBasicConfig {
id: string;
x: number;
y: number;
radius?: number;
}

declare interface IBasicPolygon extends IGraphicsBasicConfig {
id: string;
pointList: IBasicPoint[];
showDirection?: boolean;
specialPoint?: boolean; // 顶点是否特殊点
specialEdge?: boolean; // 顶点与a其下一个顶点连成的边是否为特殊边

lineType?: ELineTypes;
}

declare type IBasicLine = IBasicPolygon;

declare interface IBasicText {
id: string;
x: number;
y: number;
text: string; // Use \n for line feed
position: 'rt';
textMaxWidth?: number;

color?: string;
background?: string;
lineHeight?: number;
font?: string; // canvas-font https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/font
}
23 changes: 22 additions & 1 deletion packages/lb-annotation/src/utils/tool/DrawUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,22 @@ export default class DrawUtils {
lineCap: CanvasLineCap;
lineType: ELineTypes;
lineDash: number[];
hoverEdgeIndex: number;
}> = {},
) {
if (pointList.length < 2) {
return;
}

const ctx: CanvasRenderingContext2D = canvas.getContext('2d')!;
const { color = DEFAULT_COLOR, thickness = 1, lineCap = 'round', lineType = ELineTypes.Line, lineDash } = options;
const {
color = DEFAULT_COLOR,
thickness = 1,
lineCap = 'round',
lineType = ELineTypes.Line,
lineDash,
hoverEdgeIndex,
} = options;
ctx.save();
const setStyle = () => {
ctx.strokeStyle = color;
Expand All @@ -233,7 +241,20 @@ export default class DrawUtils {
setStyle();

if (lineType === ELineTypes.Curve) {
// 适配 HoverEdge 的高亮形式,TODO
if (hoverEdgeIndex !== undefined && hoverEdgeIndex > -1) {
pointList.push(pointList[0]);
}

pointList = PolygonUtils.createSmoothCurvePointsFromPointList([...pointList], SEGMENT_NUMBER);

if (hoverEdgeIndex !== undefined && hoverEdgeIndex > -1) {
// 想要绘制第 hoverEdgeIndex 的边, 注意,现在发现这种闭合的初始化的点并不是从 点 0 开始,而是从 0的后一个点开始
pointList = pointList.slice((SEGMENT_NUMBER + 1) * hoverEdgeIndex, (SEGMENT_NUMBER + 1) * (hoverEdgeIndex + 1));
}
} else if (hoverEdgeIndex !== undefined && hoverEdgeIndex > -1) {
pointList = [...pointList, pointList[0]];
pointList = pointList.slice(hoverEdgeIndex, hoverEdgeIndex + 2);
}

ctx.beginPath();
Expand Down
23 changes: 21 additions & 2 deletions packages/lb-annotation/src/utils/tool/PolygonUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,36 @@ export default class PolygonUtils {
* @returns {Array<number>}
*/
static createSmoothCurvePointsFromPointList(
pointList: Array<{ x: number; y: number }>,
pointList: Array<{ x: number; y: number; [a: string]: any }>,
numberOfSegments: number = SEGMENT_NUMBER,
) {
return this.createSmoothCurvePoints(
const newPoints = this.createSmoothCurvePoints(
pointList.reduce((acc: number[], cur: { x: number; y: number }) => {
return [...acc, cur.x, cur.y];
}, []),
0.5,
false,
numberOfSegments,
);

// TODO 该部分后续可以优化,将原有点的信息嵌入
return newPoints.map((p, i) => {
const pos = i / (SEGMENT_NUMBER + 1);
const v = Math.floor(pos);
const data = pointList[v] ?? {};

if (v === pos) {
return {
...data,
...p,
};
}

return {
specialEdge: data.specialEdge,
...p,
};
});
}

static createSmoothCurvePoints(
Expand Down
Loading

0 comments on commit ec0bfcc

Please sign in to comment.