Skip to content
Merged
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
100 changes: 82 additions & 18 deletions src/core/PathProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,10 @@ export function normalizeArcAngles(angles: number[], anticlockwise: boolean): vo
// Make startAngle < endAngle when clockwise, otherwise endAngle < startAngle.
// The sweep angle can never been larger than P2.
else if (!anticlockwise && newStartAngle > newEndAngle) {
newEndAngle = newStartAngle +
(PI2 - modPI2(newStartAngle - newEndAngle));
newEndAngle = newStartAngle + (PI2 - modPI2(newStartAngle - newEndAngle));
}
else if (anticlockwise && newStartAngle < newEndAngle) {
newEndAngle = newStartAngle -
(PI2 - modPI2(newEndAngle - newStartAngle));
newEndAngle = newStartAngle - (PI2 - modPI2(newEndAngle - newStartAngle));
}

angles[0] = newStartAngle;
Expand All @@ -112,12 +110,26 @@ export default class PathProxy {
data: number[] | Float32Array

/**
* Version is for detecing if the path has been changed.
* Version is for tracking if the path has been changed.
*/
private _version = 0
private _version: number

/**
* If save path data.
*/
private _saveData: boolean

/**
* If the line segment is too small to draw. It will be added to the pending pt.
* It will be added if the subpath needs to be finished before stroke, fill, or starting a new subpath.
*/
private _pendingPtX: number;
private _pendingPtY: number;
// Distance of pending pt to previous point.
// 0 if there is no pending point.
// Only update the pending pt when distance is larger.
private _pendingPtDist: number;

private _ctx: ExtendedCanvasRenderingContext2D

private _xi = 0
Expand All @@ -135,6 +147,7 @@ export default class PathProxy {
private _ux: number
private _uy: number

// For dash shim.
private _lineDash: number[]
private _needsDash: boolean
private _dashOffset: number
Expand Down Expand Up @@ -219,6 +232,9 @@ export default class PathProxy {
}

moveTo(x: number, y: number) {
// Add pending point for previous path.
this._drawPendingPt();

this.addData(CMD.M, x, y);
this._ctx && this._ctx.moveTo(x, y);

Expand All @@ -236,10 +252,9 @@ export default class PathProxy {
}

lineTo(x: number, y: number) {
const exceedUnit = mathAbs(x - this._xi) > this._ux
|| mathAbs(y - this._yi) > this._uy
// Force draw the first segment
|| this._len < 5;
const dx = mathAbs(x - this._xi);
const dy = mathAbs(y - this._yi);
const exceedUnit = dx > this._ux || dy > this._uy;

this.addData(CMD.L, x, y);

Expand All @@ -250,6 +265,16 @@ export default class PathProxy {
if (exceedUnit) {
this._xi = x;
this._yi = y;
this._pendingPtDist = 0;
}
else {
const d2 = dx * dx + dy * dy;
// Only use the farthest pending point.
if (d2 > this._pendingPtDist) {
this._pendingPtX = x;
this._pendingPtY = y;
this._pendingPtDist = d2;
}
}

return this;
Expand Down Expand Up @@ -313,10 +338,10 @@ export default class PathProxy {
return this;
}

/**
* @return {module:zrender/core/PathProxy}
*/
closePath() {
// Add pending point for previous path.
this._drawPendingPt();

this.addData(CMD.Z);

const ctx = this._ctx;
Expand Down Expand Up @@ -449,7 +474,14 @@ export default class PathProxy {
}
}

_expandData() {
private _drawPendingPt() {
if (this._pendingPtDist > 0) {
this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY);
this._pendingPtDist = 0;
}
}

private _expandData() {
// Only if data is Float32Array
if (!(this.data instanceof Array)) {
const newData = [];
Expand All @@ -460,7 +492,7 @@ export default class PathProxy {
}
}

_dashedLineTo(x1: number, y1: number) {
private _dashedLineTo(x1: number, y1: number) {
const dashSum = this._dashSum;
const lineDash = this._lineDash;
const ctx = this._ctx;
Expand Down Expand Up @@ -510,7 +542,7 @@ export default class PathProxy {
}

// Not accurate dashed line to
_dashedBezierTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) {
private _dashedBezierTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) {
const ctx = this._ctx;

let dashSum = this._dashSum;
Expand Down Expand Up @@ -577,7 +609,7 @@ export default class PathProxy {
this._dashOffset = -mathSqrt(dx * dx + dy * dy);
}

_dashedQuadraticTo(x1: number, y1: number, x2: number, y2: number) {
private _dashedQuadraticTo(x1: number, y1: number, x2: number, y2: number) {
// Convert quadratic to cubic using degree elevation
const x3 = x2;
const y3 = y2;
Expand All @@ -601,6 +633,9 @@ export default class PathProxy {
if (!this._saveData) {
return;
}

this._drawPendingPt();

const data = this.data;
if (data instanceof Array) {
data.length = this._len;
Expand Down Expand Up @@ -879,6 +914,12 @@ export default class PathProxy {
let accumLength = 0;
let segCount = 0;
let displayedLength;

let pendingPtDist = 0;
let pendingPtX: number;
let pendingPtY: number;


if (drawPart) {
if (!this._pathSegLen) {
this._calculateLength();
Expand Down Expand Up @@ -908,15 +949,21 @@ export default class PathProxy {
}
switch (cmd) {
case CMD.M:
if (pendingPtDist > 0) {
ctx.lineTo(pendingPtX, pendingPtY);
pendingPtDist = 0;
}
x0 = xi = d[i++];
y0 = yi = d[i++];
ctx.moveTo(xi, yi);
break;
case CMD.L: {
x = d[i++];
y = d[i++];
const dx = mathAbs(x - xi);
const dy = mathAbs(y - yi);
// Not draw too small seg between
if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len - 1) {
if (dx > ux || dy > uy) {
if (drawPart) {
const l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
Expand All @@ -930,6 +977,16 @@ export default class PathProxy {
ctx.lineTo(x, y);
xi = x;
yi = y;
pendingPtDist = 0;
}
else {
const d2 = dx * dx + dy * dy;
// Only use the farthest pending point.
if (d2 > pendingPtDist) {
pendingPtX = x;
pendingPtY = y;
pendingPtDist = d2;
}
}
break;
}
Expand Down Expand Up @@ -1058,6 +1115,11 @@ export default class PathProxy {
ctx.rect(x, y, width, height);
break;
case CMD.Z:
if (pendingPtDist > 0) {
ctx.lineTo(pendingPtX, pendingPtY);
pendingPtDist = 0;
}

if (drawPart) {
const l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
Expand All @@ -1084,6 +1146,8 @@ export default class PathProxy {
proto._dashSum = 0;
proto._ux = 0;
proto._uy = 0;
proto._pendingPtDist = 0;
proto._version = 0;
})()
}

Expand Down
2 changes: 1 addition & 1 deletion src/graphic/Path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Displayable, { DisplayableProps,
import Element, { ElementAnimateConfig } from '../Element';
import PathProxy from '../core/PathProxy';
import * as pathContain from '../contain/path';
import Pattern, { PatternObject } from './Pattern';
import { PatternObject } from './Pattern';
import { Dictionary, PropType, MapToType } from '../core/types';
import BoundingRect from '../core/BoundingRect';
import { LinearGradientObject } from './LinearGradient';
Expand Down
80 changes: 80 additions & 0 deletions test/segmentIgnoreThreshold.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/dat.gui@0.7.6/build/dat.gui.js"></script>
<script src="../dist/zrender.js"></script>
<script src="lib/config.js"></script>
</head>
<body>
<style>
html, body, #main {
width: 100%;
height: 100%;
margin: 0;
}
</style>
<div id="main"></div>
<script type="text/javascript">
const zr = zrender.init(document.getElementById('main'), {
renderer: window.__ZRENDER__DEFAULT__RENDERER__
});

let r = 100;
let cx = r;
let cy = r + 10;
let compoundPath = new zrender.CompoundPath({
segmentIgnoreThreshold: 20,
shape: {
paths: []
},
style: {
fill: null,
stroke: '#000',
lineWidth: 2
}
})
zr.add(compoundPath);
for (let i = 0; i < 30; i++) {
const points = [];
for (let i = 0; i < 100; i++) {
let rad = Math.PI * 2 * i / 100;
let x = Math.cos(rad) * r + cx;
let y = Math.sin(rad) * r + cy;
points.push([x, y]);
}
const poly = new zrender.Polygon({
shape: {
points
}
});
compoundPath.shape.paths.push(poly);
const text = new zrender.Text({
style: {
text: `R: ${+r.toPrecision(2)}`,
align: 'left'
},
x: cx,
y: cy + 100,
rotation: -Math.PI / 2
});
zr.add(text);

cx += Math.max(r * 2, 15);
r /= 1.3;

}

function update() {
compoundPath.attr('segmentIgnoreThreshold', config.segmentIgnoreThreshold);
}

const config = {
segmentIgnoreThreshold: 20
};

const gui = new dat.GUI();
gui.add(config, 'segmentIgnoreThreshold', 0, 100).onChange(update);
</script>
</body>
</html>