mirror of
https://github.com/sakitam-fdd/maptalks.plot.git
synced 2025-12-08 20:35:49 +00:00
# freehand line
This commit is contained in:
parent
609bb765ac
commit
ab00a0a3e3
567
dist/maptalks.plot.js
vendored
567
dist/maptalks.plot.js
vendored
@ -68,8 +68,268 @@ var PlotTypes = Object.freeze({
|
||||
CURVEFLAG: CURVEFLAG
|
||||
});
|
||||
|
||||
var FITTING_COUNT = 100;
|
||||
|
||||
var ZERO_TOLERANCE = 0.0001;
|
||||
|
||||
var BASE_LAYERNAME = 'maptalks-plot-vector-layer';
|
||||
|
||||
var MathDistance = function MathDistance(pnt1, pnt2) {
|
||||
return Math.sqrt(Math.pow(pnt1[0] - pnt2[0], 2) + Math.pow(pnt1[1] - pnt2[1], 2));
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var Mid = function Mid(point1, point2) {
|
||||
return [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2];
|
||||
};
|
||||
|
||||
var getCircleCenterOfThreePoints = function getCircleCenterOfThreePoints(point1, point2, point3) {
|
||||
var pntA = [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2];
|
||||
var pntB = [pntA[0] - point1[1] + point2[1], pntA[1] + point1[0] - point2[0]];
|
||||
var pntC = [(point1[0] + point3[0]) / 2, (point1[1] + point3[1]) / 2];
|
||||
var pntD = [pntC[0] - point1[1] + point3[1], pntC[1] + point1[0] - point3[0]];
|
||||
return getIntersectPoint(pntA, pntB, pntC, pntD);
|
||||
};
|
||||
|
||||
var getIntersectPoint = function getIntersectPoint(pntA, pntB, pntC, pntD) {
|
||||
if (pntA[1] === pntB[1]) {
|
||||
var _f = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1]);
|
||||
var _x = _f * (pntA[1] - pntC[1]) + pntC[0];
|
||||
var _y = pntA[1];
|
||||
return [_x, _y];
|
||||
}
|
||||
if (pntC[1] === pntD[1]) {
|
||||
var _e = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1]);
|
||||
var _x2 = _e * (pntC[1] - pntA[1]) + pntA[0];
|
||||
var _y2 = pntC[1];
|
||||
return [_x2, _y2];
|
||||
}
|
||||
var e = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1]);
|
||||
var f = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1]);
|
||||
var y = (e * pntA[1] - pntA[0] - f * pntC[1] + pntC[0]) / (e - f);
|
||||
var x = e * y - e * pntA[1] + pntA[0];
|
||||
return [x, y];
|
||||
};
|
||||
|
||||
var getAzimuth = function getAzimuth(startPoint, endPoint) {
|
||||
var azimuth = void 0;
|
||||
var angle = Math.asin(Math.abs(endPoint[1] - startPoint[1]) / MathDistance(startPoint, endPoint));
|
||||
if (endPoint[1] >= startPoint[1] && endPoint[0] >= startPoint[0]) {
|
||||
azimuth = angle + Math.PI;
|
||||
} else if (endPoint[1] >= startPoint[1] && endPoint[0] < startPoint[0]) {
|
||||
azimuth = Math.PI * 2 - angle;
|
||||
} else if (endPoint[1] < startPoint[1] && endPoint[0] < startPoint[0]) {
|
||||
azimuth = angle;
|
||||
} else if (endPoint[1] < startPoint[1] && endPoint[0] >= startPoint[0]) {
|
||||
azimuth = Math.PI - angle;
|
||||
}
|
||||
return azimuth;
|
||||
};
|
||||
|
||||
|
||||
|
||||
var isClockWise = function isClockWise(pnt1, pnt2, pnt3) {
|
||||
return (pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) > (pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
var getCubicValue = function getCubicValue(t, startPnt, cPnt1, cPnt2, endPnt) {
|
||||
t = Math.max(Math.min(t, 1), 0);
|
||||
var tp = 1 - t,
|
||||
t2 = t * t;
|
||||
|
||||
var t3 = t2 * t;
|
||||
var tp2 = tp * tp;
|
||||
var tp3 = tp2 * tp;
|
||||
var x = tp3 * startPnt[0] + 3 * tp2 * t * cPnt1[0] + 3 * tp * t2 * cPnt2[0] + t3 * endPnt[0];
|
||||
var y = tp3 * startPnt[1] + 3 * tp2 * t * cPnt1[1] + 3 * tp * t2 * cPnt2[1] + t3 * endPnt[1];
|
||||
return [x, y];
|
||||
};
|
||||
|
||||
|
||||
|
||||
var getArcPoints = function getArcPoints(center, radius, startAngle, endAngle) {
|
||||
var x = null,
|
||||
y = null,
|
||||
pnts = [],
|
||||
angleDiff = endAngle - startAngle;
|
||||
|
||||
angleDiff = angleDiff < 0 ? angleDiff + Math.PI * 2 : angleDiff;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
var angle = startAngle + angleDiff * i / 100;
|
||||
x = center[0] + radius * Math.cos(angle);
|
||||
y = center[1] + radius * Math.sin(angle);
|
||||
pnts.push([x, y]);
|
||||
}
|
||||
return pnts;
|
||||
};
|
||||
|
||||
var getBisectorNormals = function getBisectorNormals(t, pnt1, pnt2, pnt3) {
|
||||
var normal = getNormal(pnt1, pnt2, pnt3);
|
||||
var bisectorNormalRight = null,
|
||||
bisectorNormalLeft = null,
|
||||
dt = null,
|
||||
x = null,
|
||||
y = null;
|
||||
|
||||
var dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]);
|
||||
var uX = normal[0] / dist;
|
||||
var uY = normal[1] / dist;
|
||||
var d1 = MathDistance(pnt1, pnt2);
|
||||
var d2 = MathDistance(pnt2, pnt3);
|
||||
if (dist > ZERO_TOLERANCE) {
|
||||
if (isClockWise(pnt1, pnt2, pnt3)) {
|
||||
dt = t * d1;
|
||||
x = pnt2[0] - dt * uY;
|
||||
y = pnt2[1] + dt * uX;
|
||||
bisectorNormalRight = [x, y];
|
||||
dt = t * d2;
|
||||
x = pnt2[0] + dt * uY;
|
||||
y = pnt2[1] - dt * uX;
|
||||
bisectorNormalLeft = [x, y];
|
||||
} else {
|
||||
dt = t * d1;
|
||||
x = pnt2[0] + dt * uY;
|
||||
y = pnt2[1] - dt * uX;
|
||||
bisectorNormalRight = [x, y];
|
||||
dt = t * d2;
|
||||
x = pnt2[0] - dt * uY;
|
||||
y = pnt2[1] + dt * uX;
|
||||
bisectorNormalLeft = [x, y];
|
||||
}
|
||||
} else {
|
||||
x = pnt2[0] + t * (pnt1[0] - pnt2[0]);
|
||||
y = pnt2[1] + t * (pnt1[1] - pnt2[1]);
|
||||
bisectorNormalRight = [x, y];
|
||||
x = pnt2[0] + t * (pnt3[0] - pnt2[0]);
|
||||
y = pnt2[1] + t * (pnt3[1] - pnt2[1]);
|
||||
bisectorNormalLeft = [x, y];
|
||||
}
|
||||
return [bisectorNormalRight, bisectorNormalLeft];
|
||||
};
|
||||
|
||||
var getNormal = function getNormal(pnt1, pnt2, pnt3) {
|
||||
var dX1 = pnt1[0] - pnt2[0];
|
||||
var dY1 = pnt1[1] - pnt2[1];
|
||||
var d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1);
|
||||
dX1 /= d1;
|
||||
dY1 /= d1;
|
||||
var dX2 = pnt3[0] - pnt2[0];
|
||||
var dY2 = pnt3[1] - pnt2[1];
|
||||
var d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2);
|
||||
dX2 /= d2;
|
||||
dY2 /= d2;
|
||||
var uX = dX1 + dX2;
|
||||
var uY = dY1 + dY2;
|
||||
return [uX, uY];
|
||||
};
|
||||
|
||||
var getLeftMostControlPoint = function getLeftMostControlPoint(controlPoints, t) {
|
||||
var _ref = [controlPoints[0], controlPoints[1], controlPoints[2], null, null],
|
||||
pnt1 = _ref[0],
|
||||
pnt2 = _ref[1],
|
||||
pnt3 = _ref[2],
|
||||
controlX = _ref[3],
|
||||
controlY = _ref[4];
|
||||
|
||||
var pnts = getBisectorNormals(0, pnt1, pnt2, pnt3);
|
||||
var normalRight = pnts[0];
|
||||
var normal = getNormal(pnt1, pnt2, pnt3);
|
||||
var dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]);
|
||||
if (dist > ZERO_TOLERANCE) {
|
||||
var mid = Mid(pnt1, pnt2);
|
||||
var pX = pnt1[0] - mid[0];
|
||||
var pY = pnt1[1] - mid[1];
|
||||
var d1 = MathDistance(pnt1, pnt2);
|
||||
var n = 2.0 / d1;
|
||||
var nX = -n * pY;
|
||||
var nY = n * pX;
|
||||
var a11 = nX * nX - nY * nY;
|
||||
var a12 = 2 * nX * nY;
|
||||
var a22 = nY * nY - nX * nX;
|
||||
var dX = normalRight[0] - mid[0];
|
||||
var dY = normalRight[1] - mid[1];
|
||||
controlX = mid[0] + a11 * dX + a12 * dY;
|
||||
controlY = mid[1] + a12 * dX + a22 * dY;
|
||||
} else {
|
||||
controlX = pnt1[0] + t * (pnt2[0] - pnt1[0]);
|
||||
controlY = pnt1[1] + t * (pnt2[1] - pnt1[1]);
|
||||
}
|
||||
return [controlX, controlY];
|
||||
};
|
||||
|
||||
var getRightMostControlPoint = function getRightMostControlPoint(controlPoints, t) {
|
||||
var count = controlPoints.length;
|
||||
var pnt1 = controlPoints[count - 3];
|
||||
var pnt2 = controlPoints[count - 2];
|
||||
var pnt3 = controlPoints[count - 1];
|
||||
var pnts = getBisectorNormals(0, pnt1, pnt2, pnt3);
|
||||
var normalLeft = pnts[1];
|
||||
var normal = getNormal(pnt1, pnt2, pnt3);
|
||||
var dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]);
|
||||
var controlX = null,
|
||||
controlY = null;
|
||||
|
||||
if (dist > ZERO_TOLERANCE) {
|
||||
var mid = Mid(pnt2, pnt3);
|
||||
var pX = pnt3[0] - mid[0];
|
||||
var pY = pnt3[1] - mid[1];
|
||||
var d1 = MathDistance(pnt2, pnt3);
|
||||
var n = 2.0 / d1;
|
||||
var nX = -n * pY;
|
||||
var nY = n * pX;
|
||||
var a11 = nX * nX - nY * nY;
|
||||
var a12 = 2 * nX * nY;
|
||||
var a22 = nY * nY - nX * nX;
|
||||
var dX = normalLeft[0] - mid[0];
|
||||
var dY = normalLeft[1] - mid[1];
|
||||
controlX = mid[0] + a11 * dX + a12 * dY;
|
||||
controlY = mid[1] + a12 * dX + a22 * dY;
|
||||
} else {
|
||||
controlX = pnt3[0] + t * (pnt2[0] - pnt3[0]);
|
||||
controlY = pnt3[1] + t * (pnt2[1] - pnt3[1]);
|
||||
}
|
||||
return [controlX, controlY];
|
||||
};
|
||||
|
||||
var getCurvePoints = function getCurvePoints(t, controlPoints) {
|
||||
var leftControl = getLeftMostControlPoint(controlPoints, t);
|
||||
var pnt1 = null,
|
||||
pnt2 = null,
|
||||
pnt3 = null,
|
||||
normals = [leftControl],
|
||||
points = [];
|
||||
|
||||
for (var i = 0; i < controlPoints.length - 2; i++) {
|
||||
var _ref2 = [controlPoints[i], controlPoints[i + 1], controlPoints[i + 2]];
|
||||
pnt1 = _ref2[0];
|
||||
pnt2 = _ref2[1];
|
||||
pnt3 = _ref2[2];
|
||||
|
||||
var normalPoints = getBisectorNormals(t, pnt1, pnt2, pnt3);
|
||||
normals = normals.concat(normalPoints);
|
||||
}
|
||||
var rightControl = getRightMostControlPoint(controlPoints, t);
|
||||
if (rightControl) {
|
||||
normals.push(rightControl);
|
||||
}
|
||||
for (var _i = 0; _i < controlPoints.length - 1; _i++) {
|
||||
pnt1 = controlPoints[_i];
|
||||
pnt2 = controlPoints[_i + 1];
|
||||
points.push(pnt1);
|
||||
for (var _t = 0; _t < FITTING_COUNT; _t++) {
|
||||
var pnt = getCubicValue(_t / FITTING_COUNT, pnt1, normals[_i * 2], normals[_i * 2 + 1], pnt2);
|
||||
points.push(pnt);
|
||||
}
|
||||
points.push(pnt2);
|
||||
}
|
||||
return points;
|
||||
};
|
||||
|
||||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
|
||||
return typeof obj;
|
||||
} : function (obj) {
|
||||
@ -247,52 +507,158 @@ var possibleConstructorReturn = function (self, call) {
|
||||
return call && (typeof call === "object" || typeof call === "function") ? call : self;
|
||||
};
|
||||
|
||||
var Canvas2d = maptalks.Canvas;
|
||||
var Coordinate$1 = maptalks.Coordinate;
|
||||
var options = {
|
||||
'arcDegree': 90
|
||||
'arrowStyle': null,
|
||||
'arrowPlacement': 'vertex-last',
|
||||
'clipToPaint': true
|
||||
};
|
||||
|
||||
var Arc = function (_maptalks$LineString) {
|
||||
inherits(Arc, _maptalks$LineString);
|
||||
|
||||
function Arc(coordinates) {
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
classCallCheck(this, Arc);
|
||||
|
||||
var _this = possibleConstructorReturn(this, _maptalks$LineString.call(this, options));
|
||||
|
||||
_this.type = 'Arc';
|
||||
_this._coordinates = [];
|
||||
if (coordinates) {
|
||||
_this.setPoints(coordinates);
|
||||
}
|
||||
return _this;
|
||||
}
|
||||
|
||||
Arc.prototype._generate = function _generate() {
|
||||
var count = this._coordinates.length;
|
||||
if (count < 2) return;
|
||||
if (count === 2) {
|
||||
this.setCoordinates(this._coordinates);
|
||||
} else {
|
||||
var _ref = [this._coordinates[0], this._coordinates[1], this._coordinates[2], null, null],
|
||||
pnt1 = _ref[0],
|
||||
pnt2 = _ref[1],
|
||||
pnt3 = _ref[2],
|
||||
startAngle = _ref[3],
|
||||
endAngle = _ref[4];
|
||||
|
||||
var center = getCircleCenterOfThreePoints([pnt1['x'], pnt1['y']], [pnt2['x'], pnt2['y']], [pnt3['x'], pnt3['y']]);
|
||||
var radius = MathDistance([pnt1['x'], pnt1['y']], center);
|
||||
var angle1 = getAzimuth([pnt1['x'], pnt1['y']], center);
|
||||
var angle2 = getAzimuth([pnt2['x'], pnt2['y']], center);
|
||||
if (isClockWise([pnt1['x'], pnt1['y']], [pnt2['x'], pnt2['y']], [pnt3['x'], pnt3['y']])) {
|
||||
startAngle = angle2;
|
||||
endAngle = angle1;
|
||||
} else {
|
||||
startAngle = angle1;
|
||||
endAngle = angle2;
|
||||
}
|
||||
var points = getArcPoints(center, radius, startAngle, endAngle);
|
||||
if (Array.isArray(points)) {
|
||||
var _points = points.map(function (_item) {
|
||||
if (Array.isArray(_item)) {
|
||||
if (!isNaN(_item[0]) && !isNaN(_item[1])) {
|
||||
return new Coordinate$1(_item[0], _item[1]);
|
||||
}
|
||||
} else {
|
||||
return _item;
|
||||
}
|
||||
});
|
||||
this.setCoordinates(_points);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Arc.prototype.setPoints = function setPoints(coordinates) {
|
||||
this._coordinates = !coordinates ? [] : coordinates;
|
||||
if (this._coordinates.length >= 1) {
|
||||
this._generate();
|
||||
}
|
||||
};
|
||||
|
||||
Arc.prototype._exportGeoJSONGeometry = function _exportGeoJSONGeometry() {
|
||||
var points = this.getCoordinates();
|
||||
var coordinates = Coordinate$1.toNumberArrays(points);
|
||||
return {
|
||||
'type': 'LineString',
|
||||
'coordinates': coordinates
|
||||
};
|
||||
};
|
||||
|
||||
Arc.prototype._toJSON = function _toJSON(options) {
|
||||
return {
|
||||
'feature': this.toGeoJSON(options)
|
||||
};
|
||||
};
|
||||
|
||||
return Arc;
|
||||
}(maptalks.LineString);
|
||||
|
||||
Arc.mergeOptions(options);
|
||||
|
||||
Arc.registerJSONType('Arc');
|
||||
|
||||
var Coordinate$2 = maptalks.Coordinate;
|
||||
var options$1 = {
|
||||
'arrowStyle': null,
|
||||
'arrowPlacement': 'vertex-last',
|
||||
'clipToPaint': true
|
||||
};
|
||||
|
||||
var Curve = function (_maptalks$LineString) {
|
||||
inherits(Curve, _maptalks$LineString);
|
||||
|
||||
function Curve() {
|
||||
function Curve(coordinates) {
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
classCallCheck(this, Curve);
|
||||
return possibleConstructorReturn(this, _maptalks$LineString.apply(this, arguments));
|
||||
|
||||
var _this = possibleConstructorReturn(this, _maptalks$LineString.call(this, options));
|
||||
|
||||
_this.type = 'Curve';
|
||||
_this._coordinates = [];
|
||||
if (coordinates) {
|
||||
_this.setPoints(coordinates);
|
||||
}
|
||||
return _this;
|
||||
}
|
||||
|
||||
Curve.prototype._arc = function _arc(ctx, points, lineOpacity) {
|
||||
var degree = this.options['arcDegree'] * Math.PI / 180;
|
||||
for (var i = 1, l = points.length; i < l; i++) {
|
||||
Canvas2d._arcBetween(ctx, points[i - 1], points[i], degree);
|
||||
Canvas2d._stroke(ctx, lineOpacity);
|
||||
}
|
||||
};
|
||||
|
||||
Curve.prototype._quadraticCurve = function _quadraticCurve(ctx, points) {
|
||||
if (points.length <= 2) {
|
||||
Canvas2d._path(ctx, points);
|
||||
return;
|
||||
}
|
||||
Canvas2d.quadraticCurve(ctx, points);
|
||||
};
|
||||
|
||||
Curve.prototype._bezierCurve = function _bezierCurve(ctx, points) {
|
||||
if (points.length <= 3) {
|
||||
Canvas2d._path(ctx, points);
|
||||
return;
|
||||
}
|
||||
var i = void 0,
|
||||
l = void 0;
|
||||
for (i = 1, l = points.length; i + 2 < l; i += 3) {
|
||||
ctx.bezierCurveTo(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y, points[i + 2].x, points[i + 2].y);
|
||||
}
|
||||
if (i < l) {
|
||||
for (; i < l; i++) {
|
||||
ctx.lineTo(points[i].x, points[i].y);
|
||||
Curve.prototype._generate = function _generate() {
|
||||
var count = this._coordinates.length;
|
||||
if (count < 2) {
|
||||
return false;
|
||||
} else if (count === 2) {
|
||||
this.setCoordinates(this._coordinates);
|
||||
} else {
|
||||
var _coordinates = this._coordinates.map(function (_item) {
|
||||
if (_item && _item.hasOwnProperty('x')) {
|
||||
return [_item['x'], _item['y']];
|
||||
} else if (Array.isArray(_item)) {
|
||||
return _item;
|
||||
}
|
||||
});
|
||||
var points = getCurvePoints(0.3, _coordinates);
|
||||
if (Array.isArray(points)) {
|
||||
var _points = points.map(function (_item) {
|
||||
if (Array.isArray(_item)) {
|
||||
return new Coordinate$2(_item[0], _item[1]);
|
||||
} else {
|
||||
return _item;
|
||||
}
|
||||
});
|
||||
this.setCoordinates(_points);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Curve.prototype.setPoints = function setPoints(coordinates) {
|
||||
this._coordinates = !coordinates ? [] : coordinates;
|
||||
if (this._coordinates.length >= 1) {
|
||||
this._generate();
|
||||
}
|
||||
};
|
||||
|
||||
Curve.prototype._toJSON = function _toJSON(options) {
|
||||
return {
|
||||
'feature': this.toGeoJSON(options),
|
||||
@ -300,13 +666,6 @@ var Curve = function (_maptalks$LineString) {
|
||||
};
|
||||
};
|
||||
|
||||
Curve.prototype._paintOn = function _paintOn(ctx, points, lineOpacity) {
|
||||
ctx.beginPath();
|
||||
this._arc(ctx, points, lineOpacity);
|
||||
Canvas2d._stroke(ctx, lineOpacity);
|
||||
this._paintArrow(ctx, points, lineOpacity);
|
||||
};
|
||||
|
||||
Curve.fromJSON = function fromJSON(json) {
|
||||
var feature = json['feature'];
|
||||
var arc = new Curve(feature['geometry']['coordinates'], json['options']);
|
||||
@ -318,10 +677,10 @@ var Curve = function (_maptalks$LineString) {
|
||||
}(maptalks.LineString);
|
||||
|
||||
Curve.registerJSONType('Curve');
|
||||
Curve.mergeOptions(options);
|
||||
Curve.mergeOptions(options$1);
|
||||
|
||||
var Coordinate$1 = maptalks.Coordinate;
|
||||
var options$1 = {
|
||||
var Coordinate$3 = maptalks.Coordinate;
|
||||
var options$2 = {
|
||||
'arrowStyle': null,
|
||||
'arrowPlacement': 'vertex-last',
|
||||
'clipToPaint': true
|
||||
@ -343,9 +702,15 @@ var Polyline = function (_maptalks$LineString) {
|
||||
return _this;
|
||||
}
|
||||
|
||||
Polyline.prototype.setPoints = function setPoints(coordinates) {
|
||||
if (coordinates) {
|
||||
this.setCoordinates(coordinates);
|
||||
}
|
||||
};
|
||||
|
||||
Polyline.prototype._exportGeoJSONGeometry = function _exportGeoJSONGeometry() {
|
||||
var points = this.getCoordinates();
|
||||
var coordinates = Coordinate$1.toNumberArrays(points);
|
||||
var coordinates = Coordinate$3.toNumberArrays(points);
|
||||
return {
|
||||
'type': 'LineString',
|
||||
'coordinates': coordinates
|
||||
@ -361,12 +726,12 @@ var Polyline = function (_maptalks$LineString) {
|
||||
return Polyline;
|
||||
}(maptalks.LineString);
|
||||
|
||||
Polyline.mergeOptions(options$1);
|
||||
Polyline.mergeOptions(options$2);
|
||||
|
||||
Polyline.registerJSONType('Polyline');
|
||||
|
||||
var Coordinate$2 = maptalks.Coordinate;
|
||||
var options$2 = {
|
||||
var Coordinate$4 = maptalks.Coordinate;
|
||||
var options$3 = {
|
||||
'arrowStyle': null,
|
||||
'arrowPlacement': 'vertex-last',
|
||||
'clipToPaint': true
|
||||
@ -388,9 +753,15 @@ var FreeLine = function (_maptalks$LineString) {
|
||||
return _this;
|
||||
}
|
||||
|
||||
FreeLine.prototype.setPoints = function setPoints(coordinates) {
|
||||
if (coordinates) {
|
||||
this.setCoordinates(coordinates);
|
||||
}
|
||||
};
|
||||
|
||||
FreeLine.prototype._exportGeoJSONGeometry = function _exportGeoJSONGeometry() {
|
||||
var points = this.getCoordinates();
|
||||
var coordinates = Coordinate$2.toNumberArrays(points);
|
||||
var coordinates = Coordinate$4.toNumberArrays(points);
|
||||
return {
|
||||
'type': 'LineString',
|
||||
'coordinates': coordinates
|
||||
@ -406,42 +777,59 @@ var FreeLine = function (_maptalks$LineString) {
|
||||
return FreeLine;
|
||||
}(maptalks.LineString);
|
||||
|
||||
FreeLine.mergeOptions(options$2);
|
||||
FreeLine.mergeOptions(options$3);
|
||||
|
||||
FreeLine.registerJSONType('FreeLine');
|
||||
|
||||
var RegisterModes = {};
|
||||
RegisterModes[ARC] = {
|
||||
'freehand': false,
|
||||
'limitClickCount': 3,
|
||||
'action': ['click', 'mousemove'],
|
||||
'create': function create(path) {
|
||||
return new Arc(path);
|
||||
},
|
||||
'update': function update(path, geometry) {
|
||||
geometry.setPoints(path);
|
||||
},
|
||||
'generate': function generate(geometry) {
|
||||
return geometry;
|
||||
}
|
||||
};
|
||||
RegisterModes[CURVE] = {
|
||||
'action': ['click', 'click', 'dbclick'],
|
||||
'freehand': false,
|
||||
'action': ['click', 'mousemove', 'dblclick'],
|
||||
'create': function create(path) {
|
||||
return new Curve(path);
|
||||
},
|
||||
'update': function update(path, geometry) {
|
||||
geometry.setCoordinates(path);
|
||||
geometry.setPoints(path);
|
||||
},
|
||||
'generate': function generate(geometry) {
|
||||
return geometry;
|
||||
}
|
||||
};
|
||||
RegisterModes[POLYLINE] = {
|
||||
'action': ['click', 'mousemove', 'dbclick'],
|
||||
'freehand': false,
|
||||
'action': ['click', 'mousemove', 'dblclick'],
|
||||
'create': function create(path) {
|
||||
return new Polyline(path);
|
||||
},
|
||||
'update': function update(path, geometry) {
|
||||
geometry.setCoordinates(path);
|
||||
geometry.setPoints(path);
|
||||
},
|
||||
'generate': function generate(geometry) {
|
||||
return geometry;
|
||||
}
|
||||
};
|
||||
RegisterModes[FREE_LINE] = {
|
||||
'freehand': true,
|
||||
'action': ['mousedown', 'drag', 'mouseup'],
|
||||
'create': function create(path) {
|
||||
return new FreeLine(path);
|
||||
},
|
||||
'update': function update(path, geometry) {
|
||||
geometry.setCoordinates(path);
|
||||
geometry.setPoints(path);
|
||||
},
|
||||
'generate': function generate(geometry) {
|
||||
return geometry;
|
||||
@ -504,6 +892,15 @@ var PlotDraw = function (_maptalks$MapTool) {
|
||||
_this.layerName = _this.options && _this.options['layerName'] ? _this.options['layerName'] : BASE_LAYERNAME;
|
||||
|
||||
_this.drawLayer = null;
|
||||
|
||||
_this.events = {
|
||||
'click': _this._firstClickHandler,
|
||||
'mousemove': _this._mouseMoveHandler,
|
||||
'dblclick': _this._doubleClickHandler,
|
||||
'mousedown': _this._mouseDownHandler,
|
||||
'mouseup': _this._mouseUpHandler,
|
||||
'drag': _this._mouseMoveHandler
|
||||
};
|
||||
return _this;
|
||||
}
|
||||
|
||||
@ -565,17 +962,49 @@ var PlotDraw = function (_maptalks$MapTool) {
|
||||
|
||||
PlotDraw.prototype.getEvents = function getEvents() {
|
||||
var action = this._getRegisterMode()['action'];
|
||||
var _events = {};
|
||||
if (Array.isArray(action)) {
|
||||
return {
|
||||
'click': this._firstClickHandler,
|
||||
'mousemove': this._mouseMoveHandler,
|
||||
'dblclick': this._doubleClickHandler
|
||||
};
|
||||
for (var i = 0; i < action.length; i++) {
|
||||
if (action[i] === 'drag') {
|
||||
_events['mousemove'] = this.events[action[i]];
|
||||
} else {
|
||||
_events[action[i]] = this.events[action[i]];
|
||||
}
|
||||
}
|
||||
return _events;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
PlotDraw.prototype._mouseDownHandler = function _mouseDownHandler(event) {
|
||||
this._createGeometry(event);
|
||||
};
|
||||
|
||||
PlotDraw.prototype._mouseUpHandler = function _mouseUpHandler(event) {
|
||||
this.endDraw(event);
|
||||
};
|
||||
|
||||
PlotDraw.prototype._firstClickHandler = function _firstClickHandler(event) {
|
||||
this._createGeometry(event);
|
||||
var registerMode = this._getRegisterMode();
|
||||
var coordinate = event['coordinate'];
|
||||
if (this._geometry) {
|
||||
if (!(this._historyPointer === null)) {
|
||||
this._clickCoords = this._clickCoords.slice(0, this._historyPointer);
|
||||
}
|
||||
this._clickCoords.push(coordinate);
|
||||
this._historyPointer = this._clickCoords.length;
|
||||
if (registerMode['limitClickCount'] && registerMode['limitClickCount'] === this._historyPointer) {
|
||||
registerMode['update'](this._clickCoords, this._geometry, event);
|
||||
this.endDraw(event);
|
||||
} else {
|
||||
registerMode['update'](this._clickCoords, this._geometry, event);
|
||||
}
|
||||
this._fireEvent('drawvertex', event);
|
||||
}
|
||||
};
|
||||
|
||||
PlotDraw.prototype._createGeometry = function _createGeometry(event) {
|
||||
var registerMode = this._getRegisterMode();
|
||||
var coordinate = event['coordinate'];
|
||||
var symbol = this.getSymbol();
|
||||
@ -587,14 +1016,6 @@ var PlotDraw = function (_maptalks$MapTool) {
|
||||
}
|
||||
this._addGeometryToStage(this._geometry);
|
||||
this._fireEvent('drawstart', event);
|
||||
} else {
|
||||
if (!(this._historyPointer === null)) {
|
||||
this._clickCoords = this._clickCoords.slice(0, this._historyPointer);
|
||||
}
|
||||
this._clickCoords.push(coordinate);
|
||||
this._historyPointer = this._clickCoords.length;
|
||||
registerMode['update'](this._clickCoords, this._geometry, event);
|
||||
this._fireEvent('drawvertex', event);
|
||||
}
|
||||
};
|
||||
|
||||
@ -613,7 +1034,16 @@ var PlotDraw = function (_maptalks$MapTool) {
|
||||
if (path && path.length > 0 && coordinate.equals(path[path.length - 1])) {
|
||||
return;
|
||||
}
|
||||
registerMode['update'](path.concat([coordinate]), this._geometry, event);
|
||||
if (!registerMode.freehand) {
|
||||
registerMode['update'](path.concat([coordinate]), this._geometry, event);
|
||||
} else {
|
||||
if (!(this._historyPointer === null)) {
|
||||
this._clickCoords = this._clickCoords.slice(0, this._historyPointer);
|
||||
}
|
||||
this._clickCoords.push(coordinate);
|
||||
this._historyPointer = this._clickCoords.length;
|
||||
registerMode['update'](this._clickCoords, this._geometry, event);
|
||||
}
|
||||
this._fireEvent('mousemove', event);
|
||||
};
|
||||
|
||||
@ -771,7 +1201,6 @@ var PlotDraw = function (_maptalks$MapTool) {
|
||||
var desc = Object.getOwnPropertyDescriptor(modes, key);
|
||||
var _key = key.toLowerCase();
|
||||
Object.defineProperty(registeredMode, _key, desc);
|
||||
console.log(registeredMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
dist/maptalks.plot.js.map
vendored
2
dist/maptalks.plot.js.map
vendored
File diff suppressed because one or more lines are too long
@ -1,69 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Geometry - Arc and Bezier Curves</title>
|
||||
<link rel="stylesheet" href="../node_modules/maptalks/dist/maptalks.css">
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<body>
|
||||
<div id="map" class="container"></div>
|
||||
<script src="../node_modules/maptalks/dist/maptalks.js"></script>
|
||||
<script type="text/javascript">
|
||||
var map = new maptalks.Map('map', {
|
||||
center: [-0.113049,51.498568],
|
||||
zoom: 14,
|
||||
attribution: {
|
||||
content: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>'
|
||||
},
|
||||
baseLayer: new maptalks.TileLayer('base', {
|
||||
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
|
||||
subdomains: ['a','b','c','d']
|
||||
})
|
||||
});
|
||||
|
||||
var layer = new maptalks.VectorLayer('vector').addTo(map);
|
||||
|
||||
var drawTool = new maptalks.DrawTool({
|
||||
mode: 'Point'
|
||||
}).addTo(map).disable();
|
||||
|
||||
drawTool.on('drawend', function (param) {
|
||||
console.log(param.geometry);
|
||||
layer.addGeometry(param.geometry);
|
||||
});
|
||||
|
||||
var items = ['Point', 'LineString', 'Polygon', 'Circle', 'Ellipse', 'Rectangle'].map(function (value) {
|
||||
return {
|
||||
item: value,
|
||||
click: function () {
|
||||
drawTool.setMode(value).enable();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
new maptalks.control.Toolbar({
|
||||
items: [
|
||||
{
|
||||
item: 'Shape',
|
||||
children: items
|
||||
},
|
||||
{
|
||||
item: 'Disable',
|
||||
click: function () {
|
||||
drawTool.disable();
|
||||
}
|
||||
},
|
||||
{
|
||||
item: 'Clear',
|
||||
click: function () {
|
||||
layer.clear();
|
||||
}
|
||||
}
|
||||
]
|
||||
}).addTo(map);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -2,11 +2,19 @@
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Geometry - Arc and Bezier Curves</title>
|
||||
<title>maptalks-plot</title>
|
||||
<link rel="stylesheet" href="../node_modules/maptalks/dist/maptalks.css">
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
html, body {
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="map" class="container"></div>
|
||||
@ -14,14 +22,14 @@
|
||||
<script src="../dist/maptalks.plot.js"></script>
|
||||
<script type="text/javascript">
|
||||
var map = new maptalks.Map('map', {
|
||||
center: [-0.113049,51.498568],
|
||||
center: [-0.113049, 51.498568],
|
||||
zoom: 14,
|
||||
attribution: {
|
||||
content: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>'
|
||||
},
|
||||
baseLayer: new maptalks.TileLayer('base', {
|
||||
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
|
||||
subdomains: ['a','b','c','d']
|
||||
subdomains: ['a', 'b', 'c', 'd']
|
||||
})
|
||||
});
|
||||
|
||||
@ -33,11 +41,8 @@
|
||||
drawTool.on('drawend', function (param) {
|
||||
console.log(param.geometry);
|
||||
layer.addGeometry(param.geometry);
|
||||
setTimeout(function () {
|
||||
param.geometry.startEdit()
|
||||
}, 500)
|
||||
});
|
||||
var items = ['Polyline', 'Curve', 'FreeLine'].map(function (value) {
|
||||
var items = ['Polyline', 'Curve', 'Arc', 'FreeLine'].map(function (value) {
|
||||
return {
|
||||
item: value,
|
||||
click: function () {
|
||||
@ -47,6 +47,19 @@ class PlotDraw extends maptalks.MapTool {
|
||||
* @type {*}
|
||||
*/
|
||||
this.drawLayer = null
|
||||
|
||||
/**
|
||||
* events
|
||||
* @type {{click: PlotDraw._firstClickHandler, mousemove: PlotDraw._mouseMoveHandler, dblclick: PlotDraw._doubleClickHandler}}
|
||||
*/
|
||||
this.events = {
|
||||
'click': this._firstClickHandler,
|
||||
'mousemove': this._mouseMoveHandler,
|
||||
'dblclick': this._doubleClickHandler,
|
||||
'mousedown': this._mouseDownHandler,
|
||||
'mouseup': this._mouseUpHandler,
|
||||
'drag': this._mouseMoveHandler
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,22 +140,75 @@ class PlotDraw extends maptalks.MapTool {
|
||||
}
|
||||
|
||||
/**
|
||||
* get register events
|
||||
* 注册地图事件
|
||||
* @returns {*}
|
||||
*/
|
||||
getEvents () {
|
||||
const action = this._getRegisterMode()['action']
|
||||
const _events = {}
|
||||
if (Array.isArray(action)) {
|
||||
return {
|
||||
'click': this._firstClickHandler,
|
||||
'mousemove': this._mouseMoveHandler,
|
||||
'dblclick': this._doubleClickHandler
|
||||
for (let i = 0; i < action.length; i++) {
|
||||
if (action[i] === 'drag') {
|
||||
_events['mousemove'] = this.events[action[i]]
|
||||
} else {
|
||||
_events[action[i]] = this.events[action[i]]
|
||||
}
|
||||
}
|
||||
return _events
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标按下事件
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_mouseDownHandler (event) {
|
||||
this._createGeometry(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标抬起事件
|
||||
* @param event
|
||||
* @returns {PlotDraw}
|
||||
* @private
|
||||
*/
|
||||
_mouseUpHandler (event) {
|
||||
this.endDraw(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标第一次点击事件处理
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_firstClickHandler (event) {
|
||||
this._createGeometry(event)
|
||||
const registerMode = this._getRegisterMode()
|
||||
const coordinate = event['coordinate']
|
||||
if (this._geometry) {
|
||||
if (!(this._historyPointer === null)) {
|
||||
this._clickCoords = this._clickCoords.slice(0, this._historyPointer)
|
||||
}
|
||||
this._clickCoords.push(coordinate)
|
||||
this._historyPointer = this._clickCoords.length
|
||||
if (registerMode['limitClickCount'] && registerMode['limitClickCount'] === this._historyPointer) {
|
||||
registerMode['update'](this._clickCoords, this._geometry, event)
|
||||
this.endDraw(event)
|
||||
} else {
|
||||
registerMode['update'](this._clickCoords, this._geometry, event)
|
||||
}
|
||||
this._fireEvent('drawvertex', event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第一次事件创建相关geometry
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_createGeometry (event) {
|
||||
const registerMode = this._getRegisterMode()
|
||||
const coordinate = event['coordinate']
|
||||
const symbol = this.getSymbol()
|
||||
@ -154,19 +220,13 @@ class PlotDraw extends maptalks.MapTool {
|
||||
}
|
||||
this._addGeometryToStage(this._geometry)
|
||||
this._fireEvent('drawstart', event)
|
||||
} else {
|
||||
if (!(this._historyPointer === null)) {
|
||||
this._clickCoords = this._clickCoords.slice(0, this._historyPointer)
|
||||
}
|
||||
this._clickCoords.push(coordinate)
|
||||
this._historyPointer = this._clickCoords.length
|
||||
registerMode['update'](this._clickCoords, this._geometry, event)
|
||||
this._fireEvent('drawvertex', event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标移动事件处理
|
||||
* FIXME 当为freehand模式时,鼠标按下松开而不移动会造成
|
||||
* 构造的geometry不完整
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
@ -185,7 +245,16 @@ class PlotDraw extends maptalks.MapTool {
|
||||
if (path && path.length > 0 && coordinate.equals(path[path.length - 1])) {
|
||||
return
|
||||
}
|
||||
registerMode['update'](path.concat([coordinate]), this._geometry, event)
|
||||
if (!registerMode.freehand) {
|
||||
registerMode['update'](path.concat([coordinate]), this._geometry, event)
|
||||
} else {
|
||||
if (!(this._historyPointer === null)) {
|
||||
this._clickCoords = this._clickCoords.slice(0, this._historyPointer)
|
||||
}
|
||||
this._clickCoords.push(coordinate)
|
||||
this._historyPointer = this._clickCoords.length
|
||||
registerMode['update'](this._clickCoords, this._geometry, event)
|
||||
}
|
||||
this._fireEvent('mousemove', event)
|
||||
}
|
||||
|
||||
@ -198,6 +267,12 @@ class PlotDraw extends maptalks.MapTool {
|
||||
this.endDraw(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* get point
|
||||
* @param event
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
_getMouseContainerPoint (event) {
|
||||
const action = this._getRegisterMode()['action']
|
||||
if (action.indexOf('drag') > -1) {
|
||||
@ -206,6 +281,12 @@ class PlotDraw extends maptalks.MapTool {
|
||||
return event['containerPoint']
|
||||
}
|
||||
|
||||
/**
|
||||
* is valid point
|
||||
* @param containerPoint
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
_isValidContainerPoint (containerPoint) {
|
||||
const mapSize = this._map.getSize()
|
||||
const w = mapSize['width']
|
||||
@ -354,6 +435,10 @@ class PlotDraw extends maptalks.MapTool {
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载资源
|
||||
* @private
|
||||
*/
|
||||
_loadResources () {
|
||||
const symbol = this.getSymbol()
|
||||
const resources = maptalks.Util.getExternalResources(symbol)
|
||||
@ -391,7 +476,6 @@ class PlotDraw extends maptalks.MapTool {
|
||||
let desc = Object.getOwnPropertyDescriptor(modes, key)
|
||||
let _key = key.toLowerCase()
|
||||
Object.defineProperty(registeredMode, _key, desc)
|
||||
console.log(registeredMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
94
src/geometry/Polyline/Arc.js
Normal file
94
src/geometry/Polyline/Arc.js
Normal file
@ -0,0 +1,94 @@
|
||||
import * as maptalks from 'maptalks'
|
||||
import {
|
||||
getArcPoints,
|
||||
MathDistance,
|
||||
getAzimuth,
|
||||
isClockWise,
|
||||
getCircleCenterOfThreePoints
|
||||
} from '../helper/index'
|
||||
const Coordinate = maptalks.Coordinate
|
||||
const options = {
|
||||
'arrowStyle': null,
|
||||
'arrowPlacement': 'vertex-last', // vertex-first, vertex-last, vertex-firstlast, point
|
||||
'clipToPaint': true
|
||||
}
|
||||
|
||||
class Arc extends maptalks.LineString {
|
||||
constructor (coordinates, options = {}) {
|
||||
super(options)
|
||||
this.type = 'Arc'
|
||||
this._coordinates = []
|
||||
if (coordinates) {
|
||||
this.setPoints(coordinates)
|
||||
}
|
||||
}
|
||||
|
||||
_generate () {
|
||||
let count = this._coordinates.length
|
||||
if (count < 2) return
|
||||
if (count === 2) {
|
||||
this.setCoordinates(this._coordinates)
|
||||
} else {
|
||||
let [
|
||||
pnt1, pnt2,
|
||||
pnt3, startAngle,
|
||||
endAngle
|
||||
] = [
|
||||
this._coordinates[0], this._coordinates[1],
|
||||
this._coordinates[2], null, null
|
||||
]
|
||||
let center = getCircleCenterOfThreePoints([pnt1['x'], pnt1['y']], [pnt2['x'], pnt2['y']], [pnt3['x'], pnt3['y']])
|
||||
let radius = MathDistance([pnt1['x'], pnt1['y']], center)
|
||||
let angle1 = getAzimuth([pnt1['x'], pnt1['y']], center)
|
||||
let angle2 = getAzimuth([pnt2['x'], pnt2['y']], center)
|
||||
if (isClockWise([pnt1['x'], pnt1['y']], [pnt2['x'], pnt2['y']], [pnt3['x'], pnt3['y']])) {
|
||||
startAngle = angle2
|
||||
endAngle = angle1
|
||||
} else {
|
||||
startAngle = angle1
|
||||
endAngle = angle2
|
||||
}
|
||||
let points = getArcPoints(center, radius, startAngle, endAngle)
|
||||
if (Array.isArray(points)) {
|
||||
let _points = points.map(_item => {
|
||||
if (Array.isArray(_item)) {
|
||||
if (!isNaN(_item[0]) && !isNaN(_item[1])) {
|
||||
return new Coordinate(_item[0], _item[1])
|
||||
}
|
||||
} else {
|
||||
return _item
|
||||
}
|
||||
})
|
||||
this.setCoordinates(_points)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setPoints (coordinates) {
|
||||
this._coordinates = !coordinates ? [] : coordinates
|
||||
if (this._coordinates.length >= 1) {
|
||||
this._generate()
|
||||
}
|
||||
}
|
||||
|
||||
_exportGeoJSONGeometry () {
|
||||
const points = this.getCoordinates()
|
||||
const coordinates = Coordinate.toNumberArrays(points)
|
||||
return {
|
||||
'type': 'LineString',
|
||||
'coordinates': coordinates
|
||||
}
|
||||
}
|
||||
|
||||
_toJSON (options) {
|
||||
return {
|
||||
'feature': this.toGeoJSON(options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Arc.mergeOptions(options)
|
||||
|
||||
Arc.registerJSONType('Arc')
|
||||
|
||||
export default Arc
|
||||
@ -4,43 +4,58 @@
|
||||
*/
|
||||
|
||||
import * as maptalks from 'maptalks'
|
||||
const Canvas2d = maptalks.Canvas
|
||||
import { getCurvePoints } from '../helper/index'
|
||||
const Coordinate = maptalks.Coordinate
|
||||
const options = {
|
||||
'arcDegree': 90
|
||||
'arrowStyle': null,
|
||||
'arrowPlacement': 'vertex-last', // vertex-first, vertex-last, vertex-firstlast, point
|
||||
'clipToPaint': true
|
||||
}
|
||||
class Curve extends maptalks.LineString {
|
||||
_arc (ctx, points, lineOpacity) {
|
||||
const degree = this.options['arcDegree'] * Math.PI / 180
|
||||
for (let i = 1, l = points.length; i < l; i++) {
|
||||
Canvas2d._arcBetween(ctx, points[i - 1], points[i], degree)
|
||||
Canvas2d._stroke(ctx, lineOpacity)
|
||||
constructor (coordinates, options = {}) {
|
||||
super(options)
|
||||
this.type = 'Curve'
|
||||
this._coordinates = []
|
||||
if (coordinates) {
|
||||
this.setPoints(coordinates)
|
||||
}
|
||||
}
|
||||
|
||||
_quadraticCurve (ctx, points) {
|
||||
if (points.length <= 2) {
|
||||
Canvas2d._path(ctx, points)
|
||||
return
|
||||
}
|
||||
Canvas2d.quadraticCurve(ctx, points)
|
||||
}
|
||||
|
||||
_bezierCurve (ctx, points) {
|
||||
if (points.length <= 3) {
|
||||
Canvas2d._path(ctx, points)
|
||||
return
|
||||
}
|
||||
let i, l
|
||||
for (i = 1, l = points.length; i + 2 < l; i += 3) {
|
||||
ctx.bezierCurveTo(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y, points[i + 2].x, points[i + 2].y)
|
||||
}
|
||||
if (i < l) {
|
||||
for (; i < l; i++) {
|
||||
ctx.lineTo(points[i].x, points[i].y)
|
||||
_generate () {
|
||||
let count = this._coordinates.length
|
||||
if (count < 2) {
|
||||
return false
|
||||
} else if (count === 2) {
|
||||
this.setCoordinates(this._coordinates)
|
||||
} else {
|
||||
let _coordinates = this._coordinates.map(_item => {
|
||||
if (_item && _item.hasOwnProperty('x')) {
|
||||
return [_item['x'], _item['y']]
|
||||
} else if (Array.isArray(_item)) {
|
||||
return _item
|
||||
}
|
||||
})
|
||||
let points = getCurvePoints(0.3, _coordinates)
|
||||
if (Array.isArray(points)) {
|
||||
let _points = points.map(_item => {
|
||||
if (Array.isArray(_item)) {
|
||||
return new Coordinate(_item[0], _item[1])
|
||||
} else {
|
||||
return _item
|
||||
}
|
||||
})
|
||||
this.setCoordinates(_points)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setPoints (coordinates) {
|
||||
this._coordinates = !coordinates ? [] : coordinates
|
||||
if (this._coordinates.length >= 1) {
|
||||
this._generate()
|
||||
}
|
||||
}
|
||||
|
||||
_toJSON (options) {
|
||||
return {
|
||||
'feature': this.toGeoJSON(options),
|
||||
@ -48,14 +63,6 @@ class Curve extends maptalks.LineString {
|
||||
}
|
||||
}
|
||||
|
||||
// paint method on canvas
|
||||
_paintOn (ctx, points, lineOpacity) {
|
||||
ctx.beginPath()
|
||||
this._arc(ctx, points, lineOpacity)
|
||||
Canvas2d._stroke(ctx, lineOpacity)
|
||||
this._paintArrow(ctx, points, lineOpacity)
|
||||
}
|
||||
|
||||
static fromJSON (json) {
|
||||
const feature = json['feature']
|
||||
const arc = new Curve(feature['geometry']['coordinates'], json['options'])
|
||||
|
||||
@ -20,6 +20,12 @@ class FreeLine extends maptalks.LineString {
|
||||
}
|
||||
}
|
||||
|
||||
setPoints (coordinates) {
|
||||
if (coordinates) {
|
||||
this.setCoordinates(coordinates)
|
||||
}
|
||||
}
|
||||
|
||||
_exportGeoJSONGeometry () {
|
||||
const points = this.getCoordinates()
|
||||
const coordinates = Coordinate.toNumberArrays(points)
|
||||
|
||||
@ -15,6 +15,12 @@ class Polyline extends maptalks.LineString {
|
||||
}
|
||||
}
|
||||
|
||||
setPoints (coordinates) {
|
||||
if (coordinates) {
|
||||
this.setCoordinates(coordinates)
|
||||
}
|
||||
}
|
||||
|
||||
_exportGeoJSONGeometry () {
|
||||
const points = this.getCoordinates()
|
||||
const coordinates = Coordinate.toNumberArrays(points)
|
||||
|
||||
233
src/geometry/helper/common.js
Normal file
233
src/geometry/helper/common.js
Normal file
@ -0,0 +1,233 @@
|
||||
import * as Constants from '../../Constants'
|
||||
// import * as maptalks from 'maptalks'
|
||||
// const Coordinate = maptalks.Coordinate
|
||||
|
||||
/**
|
||||
* 计算两个坐标之间的距离
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @returns {number}
|
||||
* @constructor
|
||||
*/
|
||||
const mathDistance = (point1, point2) => {
|
||||
return (Math.sqrt(Math.pow((point1['x'] - point2['x']), 2) + Math.pow((point1['y'] - point2['y']), 2)))
|
||||
}
|
||||
|
||||
/**
|
||||
* 求取两个坐标的中间值
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @returns {[*,*]}
|
||||
* @constructor
|
||||
*/
|
||||
const getMiddlePoint = (point1, point2) => {
|
||||
return [(point1['x'] + point2['x']) / 2, (point1['y'] + point2['y']) / 2]
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是顺时针
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @param point3
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isClockWise = (point1, point2, point3) => {
|
||||
return ((point3['y'] - point1['y']) * (point2['x'] - point1['x']) > (point2['y'] - point1['y']) * (point3['x'] - point1['x']))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取立方值
|
||||
* @param t
|
||||
* @param startPnt
|
||||
* @param cPnt1
|
||||
* @param cPnt2
|
||||
* @param endPnt
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
const getCubicValue = (t, startPnt, cPnt1, cPnt2, endPnt) => {
|
||||
t = Math.max(Math.min(t, 1), 0)
|
||||
let [tp, t2] = [(1 - t), (t * t)]
|
||||
let t3 = t2 * t
|
||||
let tp2 = tp * tp
|
||||
let tp3 = tp2 * tp
|
||||
let x = (tp3 * startPnt['x']) + (3 * tp2 * t * cPnt1['x']) + (3 * tp * t2 * cPnt2['x']) + (t3 * endPnt['x'])
|
||||
let y = (tp3 * startPnt['y']) + (3 * tp2 * t * cPnt1['y']) + (3 * tp * t2 * cPnt2['y']) + (t3 * endPnt['y'])
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
/**
|
||||
* getBisectorNormals
|
||||
* @param t
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @param point3
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
const getBisectorNormals = (t, point1, point2, point3) => {
|
||||
let normal = getNormal(point1, point2, point3)
|
||||
let [bisectorNormalRight, bisectorNormalLeft, dt, x1, y1, x2, y2] = [null, null, null, null, null, null, null]
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
let uX = normal[0] / dist
|
||||
let uY = normal[1] / dist
|
||||
let d1 = mathDistance(point1, point2)
|
||||
let d2 = mathDistance(point2, point3)
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
if (isClockWise(point1, point2, point3)) {
|
||||
dt = t * d1
|
||||
x1 = point2['x'] - dt * uY
|
||||
y1 = point2['y'] + dt * uX
|
||||
bisectorNormalRight = [x1, y1]
|
||||
dt = t * d2
|
||||
x2 = point2['x'] + dt * uY
|
||||
y2 = point2['y'] - dt * uX
|
||||
bisectorNormalLeft = [x2, y2]
|
||||
} else {
|
||||
dt = t * d1
|
||||
x1 = point2['x'] + dt * uY
|
||||
y1 = point2['y'] - dt * uX
|
||||
bisectorNormalRight = [x1, y1]
|
||||
dt = t * d2
|
||||
x2 = point2['x'] - dt * uY
|
||||
y2 = point2['y'] + dt * uX
|
||||
bisectorNormalLeft = [x2, y2]
|
||||
}
|
||||
} else {
|
||||
x1 = point2['x'] + t * (point1['x'] - point2['x'])
|
||||
y1 = point2['y'] + t * (point1['y'] - point2['y'])
|
||||
bisectorNormalRight = [x1, y1]
|
||||
x2 = point2['x'] + t * (point3['x'] - point2['x'])
|
||||
y2 = point2['y'] + t * (point3['y'] - point2['y'])
|
||||
bisectorNormalLeft = [x2, y2]
|
||||
}
|
||||
return [bisectorNormalRight, bisectorNormalLeft]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认三点的内切圆
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @param point3
|
||||
* @returns {*[]}
|
||||
*/
|
||||
const getNormal = (point1, point2, point3) => {
|
||||
let dX1 = point1['x'] - point2['x']
|
||||
let dY1 = point1['y'] - point2['y']
|
||||
let d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1)
|
||||
dX1 /= d1
|
||||
dY1 /= d1
|
||||
let dX2 = point3['x'] - point2['x']
|
||||
let dY2 = point3['y'] - point2['y']
|
||||
let d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2)
|
||||
dX2 /= d2
|
||||
dY2 /= d2
|
||||
let uX = dX1 + dX2
|
||||
let uY = dY1 + dY2
|
||||
return [uX, uY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取左边控制点
|
||||
* @param controlPoints
|
||||
* @param offset
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
const getLeftMostControlPoint = (controlPoints, offset) => {
|
||||
let [point1, point2, point3, controlX, controlY] = [controlPoints[0], controlPoints[1], controlPoints[2], null, null]
|
||||
let points = getBisectorNormals(0, point1, point2, point3)
|
||||
let normalRight = points[0]
|
||||
let normal = getNormal(point1, point2, point3)
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
let mid = getMiddlePoint(point1, point2)
|
||||
let pX = point1[0] - mid[0]
|
||||
let pY = point1[1] - mid[1]
|
||||
let d1 = mathDistance(point1, point2)
|
||||
let n = 2.0 / d1
|
||||
let nX = -n * pY
|
||||
let nY = n * pX
|
||||
let a11 = nX * nX - nY * nY
|
||||
let a12 = 2 * nX * nY
|
||||
let a22 = nY * nY - nX * nX
|
||||
let dX = normalRight[0] - mid[0]
|
||||
let dY = normalRight[1] - mid[1]
|
||||
controlX = mid[0] + a11 * dX + a12 * dY
|
||||
controlY = mid[1] + a12 * dX + a22 * dY
|
||||
} else {
|
||||
controlX = point1[0] + offset * (point2[0] - point1[0])
|
||||
controlY = point1[1] + offset * (point2[1] - point1[1])
|
||||
}
|
||||
return [controlX, controlY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取右边控制点
|
||||
* @param controlPoints
|
||||
* @param t
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
const getRightMostControlPoint = (controlPoints, t) => {
|
||||
let count = controlPoints.length
|
||||
let point1 = controlPoints[count - 3]
|
||||
let point2 = controlPoints[count - 2]
|
||||
let point3 = controlPoints[count - 1]
|
||||
let pnts = getBisectorNormals(0, point1, point2, point3)
|
||||
let normalLeft = pnts[1]
|
||||
let normal = getNormal(point1, point2, point3)
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
let [controlX, controlY] = [null, null]
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
let mid = getMiddlePoint(point2, point3)
|
||||
let pX = point3[0] - mid[0]
|
||||
let pY = point3[1] - mid[1]
|
||||
let d1 = mathDistance(point2, point3)
|
||||
let n = 2.0 / d1
|
||||
let nX = -n * pY
|
||||
let nY = n * pX
|
||||
let a11 = nX * nX - nY * nY
|
||||
let a12 = 2 * nX * nY
|
||||
let a22 = nY * nY - nX * nX
|
||||
let dX = normalLeft[0] - mid[0]
|
||||
let dY = normalLeft[1] - mid[1]
|
||||
controlX = mid[0] + a11 * dX + a12 * dY
|
||||
controlY = mid[1] + a12 * dX + a22 * dY
|
||||
} else {
|
||||
controlX = point3[0] + t * (point2[0] - point3[0])
|
||||
controlY = point3[1] + t * (point2[1] - point3[1])
|
||||
}
|
||||
return [controlX, controlY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 插值曲线点
|
||||
* @param offset
|
||||
* @param controlPoints
|
||||
* @returns {null}
|
||||
*/
|
||||
const getCurvePoints = (offset, controlPoints) => {
|
||||
let leftControl = getLeftMostControlPoint(controlPoints, offset)
|
||||
let [point1, point2, point3, normals, points] = [null, null, null, [leftControl], []]
|
||||
for (let i = 0; i < controlPoints.length - 2; i++) {
|
||||
[point1, point2, point3] = [controlPoints[i], controlPoints[i + 1], controlPoints[i + 2]]
|
||||
let normalPoints = getBisectorNormals(offset, point1, point2, point3)
|
||||
normals = normals.concat(normalPoints)
|
||||
}
|
||||
let rightControl = getRightMostControlPoint(controlPoints, offset)
|
||||
if (rightControl) {
|
||||
normals.push(rightControl)
|
||||
}
|
||||
for (let i = 0; i < controlPoints.length - 1; i++) {
|
||||
point1 = controlPoints[i]
|
||||
point2 = controlPoints[i + 1]
|
||||
points.push(point1)
|
||||
for (let t = 0; t < Constants.FITTING_COUNT; t++) {
|
||||
let pnt = getCubicValue(t / Constants.FITTING_COUNT, point1, normals[i * 2], normals[i * 2 + 1], point2)
|
||||
points.push(pnt)
|
||||
}
|
||||
points.push(point2)
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
export {
|
||||
getCurvePoints
|
||||
}
|
||||
490
src/geometry/helper/index.js
Normal file
490
src/geometry/helper/index.js
Normal file
@ -0,0 +1,490 @@
|
||||
import * as Constants from '../../Constants'
|
||||
/**
|
||||
* 计算两个坐标之间的距离
|
||||
* @param pnt1
|
||||
* @param pnt2
|
||||
* @returns {number}
|
||||
* @constructor
|
||||
*/
|
||||
export const MathDistance = (pnt1, pnt2) => {
|
||||
return (Math.sqrt(Math.pow((pnt1[0] - pnt2[0]), 2) + Math.pow((pnt1[1] - pnt2[1]), 2)))
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算点集合的总距离
|
||||
* @param points
|
||||
* @returns {number}
|
||||
*/
|
||||
export const wholeDistance = (points) => {
|
||||
let distance = 0
|
||||
if (points && Array.isArray(points) && points.length > 0) {
|
||||
points.forEach((item, index) => {
|
||||
if (index < points.length - 1) {
|
||||
distance += (MathDistance(item, points[index + 1]))
|
||||
}
|
||||
})
|
||||
}
|
||||
return distance
|
||||
}
|
||||
/**
|
||||
* 获取基础长度
|
||||
* @param points
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getBaseLength = (points) => {
|
||||
return Math.pow(wholeDistance(points), 0.99)
|
||||
}
|
||||
|
||||
/**
|
||||
* 求取两个坐标的中间值
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @returns {[*,*]}
|
||||
* @constructor
|
||||
*/
|
||||
export const Mid = (point1, point2) => {
|
||||
return [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2]
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过三个点确定一个圆的中心点
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @param point3
|
||||
*/
|
||||
export const getCircleCenterOfThreePoints = (point1, point2, point3) => {
|
||||
let pntA = [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2]
|
||||
let pntB = [pntA[0] - point1[1] + point2[1], pntA[1] + point1[0] - point2[0]]
|
||||
let pntC = [(point1[0] + point3[0]) / 2, (point1[1] + point3[1]) / 2]
|
||||
let pntD = [pntC[0] - point1[1] + point3[1], pntC[1] + point1[0] - point3[0]]
|
||||
return getIntersectPoint(pntA, pntB, pntC, pntD)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取交集的点
|
||||
* @param pntA
|
||||
* @param pntB
|
||||
* @param pntC
|
||||
* @param pntD
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getIntersectPoint = (pntA, pntB, pntC, pntD) => {
|
||||
if (pntA[1] === pntB[1]) {
|
||||
let f = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1])
|
||||
let x = f * (pntA[1] - pntC[1]) + pntC[0]
|
||||
let y = pntA[1]
|
||||
return [x, y]
|
||||
}
|
||||
if (pntC[1] === pntD[1]) {
|
||||
let e = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1])
|
||||
let x = e * (pntC[1] - pntA[1]) + pntA[0]
|
||||
let y = pntC[1]
|
||||
return [x, y]
|
||||
}
|
||||
let e = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1])
|
||||
let f = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1])
|
||||
let y = (e * pntA[1] - pntA[0] - f * pntC[1] + pntC[0]) / (e - f)
|
||||
let x = e * y - e * pntA[1] + pntA[0]
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方位角(地平经度)
|
||||
* @param startPoint
|
||||
* @param endPoint
|
||||
* @returns {*}
|
||||
*/
|
||||
export const getAzimuth = (startPoint, endPoint) => {
|
||||
let azimuth
|
||||
let angle = Math.asin(Math.abs(endPoint[1] - startPoint[1]) / (MathDistance(startPoint, endPoint)))
|
||||
if (endPoint[1] >= startPoint[1] && endPoint[0] >= startPoint[0]) {
|
||||
azimuth = angle + Math.PI
|
||||
} else if (endPoint[1] >= startPoint[1] && endPoint[0] < startPoint[0]) {
|
||||
azimuth = Math.PI * 2 - angle
|
||||
} else if (endPoint[1] < startPoint[1] && endPoint[0] < startPoint[0]) {
|
||||
azimuth = angle
|
||||
} else if (endPoint[1] < startPoint[1] && endPoint[0] >= startPoint[0]) {
|
||||
azimuth = Math.PI - angle
|
||||
}
|
||||
return azimuth
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过三个点获取方位角
|
||||
* @param pntA
|
||||
* @param pntB
|
||||
* @param pntC
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getAngleOfThreePoints = (pntA, pntB, pntC) => {
|
||||
let angle = getAzimuth(pntB, pntA) - getAzimuth(pntB, pntC)
|
||||
return ((angle < 0) ? (angle + Math.PI * 2) : angle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是顺时针
|
||||
* @param pnt1
|
||||
* @param pnt2
|
||||
* @param pnt3
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isClockWise = (pnt1, pnt2, pnt3) => {
|
||||
return ((pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) > (pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0]))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取线上的点
|
||||
* @param t
|
||||
* @param startPnt
|
||||
* @param endPnt
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getPointOnLine = (t, startPnt, endPnt) => {
|
||||
let x = startPnt[0] + (t * (endPnt[0] - startPnt[0]))
|
||||
let y = startPnt[1] + (t * (endPnt[1] - startPnt[1]))
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取立方值
|
||||
* @param t
|
||||
* @param startPnt
|
||||
* @param cPnt1
|
||||
* @param cPnt2
|
||||
* @param endPnt
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getCubicValue = (t, startPnt, cPnt1, cPnt2, endPnt) => {
|
||||
t = Math.max(Math.min(t, 1), 0)
|
||||
let [tp, t2] = [(1 - t), (t * t)]
|
||||
let t3 = t2 * t
|
||||
let tp2 = tp * tp
|
||||
let tp3 = tp2 * tp
|
||||
let x = (tp3 * startPnt[0]) + (3 * tp2 * t * cPnt1[0]) + (3 * tp * t2 * cPnt2[0]) + (t3 * endPnt[0])
|
||||
let y = (tp3 * startPnt[1]) + (3 * tp2 * t * cPnt1[1]) + (3 * tp * t2 * cPnt2[1]) + (t3 * endPnt[1])
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据起止点和旋转方向求取第三个点
|
||||
* @param startPnt
|
||||
* @param endPnt
|
||||
* @param angle
|
||||
* @param distance
|
||||
* @param clockWise
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getThirdPoint = (startPnt, endPnt, angle, distance, clockWise) => {
|
||||
let azimuth = getAzimuth(startPnt, endPnt)
|
||||
let alpha = clockWise ? (azimuth + angle) : (azimuth - angle)
|
||||
let dx = distance * Math.cos(alpha)
|
||||
let dy = distance * Math.sin(alpha)
|
||||
return ([endPnt[0] + dx, endPnt[1] + dy])
|
||||
}
|
||||
|
||||
/**
|
||||
* 插值弓形线段点
|
||||
* @param center
|
||||
* @param radius
|
||||
* @param startAngle
|
||||
* @param endAngle
|
||||
* @returns {null}
|
||||
*/
|
||||
export const getArcPoints = (center, radius, startAngle, endAngle) => {
|
||||
let [x, y, pnts, angleDiff] = [null, null, [], (endAngle - startAngle)]
|
||||
angleDiff = ((angleDiff < 0) ? (angleDiff + (Math.PI * 2)) : angleDiff)
|
||||
for (let i = 0; i < 100; i++) {
|
||||
let angle = startAngle + angleDiff * i / 100
|
||||
x = center[0] + radius * Math.cos(angle)
|
||||
y = center[1] + radius * Math.sin(angle)
|
||||
pnts.push([x, y])
|
||||
}
|
||||
return pnts
|
||||
}
|
||||
|
||||
/**
|
||||
* getBisectorNormals
|
||||
* @param t
|
||||
* @param pnt1
|
||||
* @param pnt2
|
||||
* @param pnt3
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getBisectorNormals = (t, pnt1, pnt2, pnt3) => {
|
||||
let normal = getNormal(pnt1, pnt2, pnt3)
|
||||
let [bisectorNormalRight, bisectorNormalLeft, dt, x, y] = [null, null, null, null, null]
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
let uX = normal[0] / dist
|
||||
let uY = normal[1] / dist
|
||||
let d1 = MathDistance(pnt1, pnt2)
|
||||
let d2 = MathDistance(pnt2, pnt3)
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
if (isClockWise(pnt1, pnt2, pnt3)) {
|
||||
dt = t * d1
|
||||
x = pnt2[0] - dt * uY
|
||||
y = pnt2[1] + dt * uX
|
||||
bisectorNormalRight = [x, y]
|
||||
dt = t * d2
|
||||
x = pnt2[0] + dt * uY
|
||||
y = pnt2[1] - dt * uX
|
||||
bisectorNormalLeft = [x, y]
|
||||
} else {
|
||||
dt = t * d1
|
||||
x = pnt2[0] + dt * uY
|
||||
y = pnt2[1] - dt * uX
|
||||
bisectorNormalRight = [x, y]
|
||||
dt = t * d2
|
||||
x = pnt2[0] - dt * uY
|
||||
y = pnt2[1] + dt * uX
|
||||
bisectorNormalLeft = [x, y]
|
||||
}
|
||||
} else {
|
||||
x = pnt2[0] + t * (pnt1[0] - pnt2[0])
|
||||
y = pnt2[1] + t * (pnt1[1] - pnt2[1])
|
||||
bisectorNormalRight = [x, y]
|
||||
x = pnt2[0] + t * (pnt3[0] - pnt2[0])
|
||||
y = pnt2[1] + t * (pnt3[1] - pnt2[1])
|
||||
bisectorNormalLeft = [x, y]
|
||||
}
|
||||
return [bisectorNormalRight, bisectorNormalLeft]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认三点的内切圆
|
||||
* @param pnt1
|
||||
* @param pnt2
|
||||
* @param pnt3
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getNormal = (pnt1, pnt2, pnt3) => {
|
||||
let dX1 = pnt1[0] - pnt2[0]
|
||||
let dY1 = pnt1[1] - pnt2[1]
|
||||
let d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1)
|
||||
dX1 /= d1
|
||||
dY1 /= d1
|
||||
let dX2 = pnt3[0] - pnt2[0]
|
||||
let dY2 = pnt3[1] - pnt2[1]
|
||||
let d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2)
|
||||
dX2 /= d2
|
||||
dY2 /= d2
|
||||
let uX = dX1 + dX2
|
||||
let uY = dY1 + dY2
|
||||
return [uX, uY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取左边控制点
|
||||
* @param controlPoints
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getLeftMostControlPoint = (controlPoints, t) => {
|
||||
let [pnt1, pnt2, pnt3, controlX, controlY] = [controlPoints[0], controlPoints[1], controlPoints[2], null, null]
|
||||
let pnts = getBisectorNormals(0, pnt1, pnt2, pnt3)
|
||||
let normalRight = pnts[0]
|
||||
let normal = getNormal(pnt1, pnt2, pnt3)
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
let mid = Mid(pnt1, pnt2)
|
||||
let pX = pnt1[0] - mid[0]
|
||||
let pY = pnt1[1] - mid[1]
|
||||
let d1 = MathDistance(pnt1, pnt2)
|
||||
let n = 2.0 / d1
|
||||
let nX = -n * pY
|
||||
let nY = n * pX
|
||||
let a11 = nX * nX - nY * nY
|
||||
let a12 = 2 * nX * nY
|
||||
let a22 = nY * nY - nX * nX
|
||||
let dX = normalRight[0] - mid[0]
|
||||
let dY = normalRight[1] - mid[1]
|
||||
controlX = mid[0] + a11 * dX + a12 * dY
|
||||
controlY = mid[1] + a12 * dX + a22 * dY
|
||||
} else {
|
||||
controlX = pnt1[0] + t * (pnt2[0] - pnt1[0])
|
||||
controlY = pnt1[1] + t * (pnt2[1] - pnt1[1])
|
||||
}
|
||||
return [controlX, controlY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取右边控制点
|
||||
* @param controlPoints
|
||||
* @param t
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getRightMostControlPoint = (controlPoints, t) => {
|
||||
let count = controlPoints.length
|
||||
let pnt1 = controlPoints[count - 3]
|
||||
let pnt2 = controlPoints[count - 2]
|
||||
let pnt3 = controlPoints[count - 1]
|
||||
let pnts = getBisectorNormals(0, pnt1, pnt2, pnt3)
|
||||
let normalLeft = pnts[1]
|
||||
let normal = getNormal(pnt1, pnt2, pnt3)
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
let [controlX, controlY] = [null, null]
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
let mid = Mid(pnt2, pnt3)
|
||||
let pX = pnt3[0] - mid[0]
|
||||
let pY = pnt3[1] - mid[1]
|
||||
let d1 = MathDistance(pnt2, pnt3)
|
||||
let n = 2.0 / d1
|
||||
let nX = -n * pY
|
||||
let nY = n * pX
|
||||
let a11 = nX * nX - nY * nY
|
||||
let a12 = 2 * nX * nY
|
||||
let a22 = nY * nY - nX * nX
|
||||
let dX = normalLeft[0] - mid[0]
|
||||
let dY = normalLeft[1] - mid[1]
|
||||
controlX = mid[0] + a11 * dX + a12 * dY
|
||||
controlY = mid[1] + a12 * dX + a22 * dY
|
||||
} else {
|
||||
controlX = pnt3[0] + t * (pnt2[0] - pnt3[0])
|
||||
controlY = pnt3[1] + t * (pnt2[1] - pnt3[1])
|
||||
}
|
||||
return [controlX, controlY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 插值曲线点
|
||||
* @param t
|
||||
* @param controlPoints
|
||||
* @returns {null}
|
||||
*/
|
||||
export const getCurvePoints = (t, controlPoints) => {
|
||||
let leftControl = getLeftMostControlPoint(controlPoints, t)
|
||||
let [pnt1, pnt2, pnt3, normals, points] = [null, null, null, [leftControl], []]
|
||||
for (let i = 0; i < controlPoints.length - 2; i++) {
|
||||
[pnt1, pnt2, pnt3] = [controlPoints[i], controlPoints[i + 1], controlPoints[i + 2]]
|
||||
let normalPoints = getBisectorNormals(t, pnt1, pnt2, pnt3)
|
||||
normals = normals.concat(normalPoints)
|
||||
}
|
||||
let rightControl = getRightMostControlPoint(controlPoints, t)
|
||||
if (rightControl) {
|
||||
normals.push(rightControl)
|
||||
}
|
||||
for (let i = 0; i < controlPoints.length - 1; i++) {
|
||||
pnt1 = controlPoints[i]
|
||||
pnt2 = controlPoints[i + 1]
|
||||
points.push(pnt1)
|
||||
for (let t = 0; t < Constants.FITTING_COUNT; t++) {
|
||||
let pnt = getCubicValue(t / Constants.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2)
|
||||
points.push(pnt)
|
||||
}
|
||||
points.push(pnt2)
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
/**
|
||||
* 贝塞尔曲线
|
||||
* @param points
|
||||
* @returns {*}
|
||||
*/
|
||||
export const getBezierPoints = function (points) {
|
||||
if (points.length <= 2) {
|
||||
return points
|
||||
} else {
|
||||
let bezierPoints = []
|
||||
let n = points.length - 1
|
||||
for (let t = 0; t <= 1; t += 0.01) {
|
||||
let [x, y] = [0, 0]
|
||||
for (let index = 0; index <= n; index++) {
|
||||
let factor = getBinomialFactor(n, index)
|
||||
let a = Math.pow(t, index)
|
||||
let b = Math.pow((1 - t), (n - index))
|
||||
x += factor * a * b * points[index][0]
|
||||
y += factor * a * b * points[index][1]
|
||||
}
|
||||
bezierPoints.push([x, y])
|
||||
}
|
||||
bezierPoints.push(points[n])
|
||||
return bezierPoints
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取阶乘数据
|
||||
* @param n
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getFactorial = (n) => {
|
||||
let result = 1
|
||||
switch (n) {
|
||||
case (n <= 1):
|
||||
result = 1
|
||||
break
|
||||
case (n === 2):
|
||||
result = 2
|
||||
break
|
||||
case (n === 3):
|
||||
result = 6
|
||||
break
|
||||
case (n === 24):
|
||||
result = 24
|
||||
break
|
||||
case (n === 5):
|
||||
result = 120
|
||||
break
|
||||
default:
|
||||
for (let i = 1; i <= n; i++) {
|
||||
result *= i
|
||||
}
|
||||
break
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二项分布
|
||||
* @param n
|
||||
* @param index
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getBinomialFactor = (n, index) => {
|
||||
return (getFactorial(n) / (getFactorial(index) * getFactorial(n - index)))
|
||||
}
|
||||
|
||||
/**
|
||||
* 插值线性点
|
||||
* @param points
|
||||
* @returns {*}
|
||||
*/
|
||||
export const getQBSplinePoints = points => {
|
||||
if (points.length <= 2) {
|
||||
return points
|
||||
} else {
|
||||
let [n, bSplinePoints] = [2, []]
|
||||
let m = points.length - n - 1
|
||||
bSplinePoints.push(points[0])
|
||||
for (let i = 0; i <= m; i++) {
|
||||
for (let t = 0; t <= 1; t += 0.05) {
|
||||
let [x, y] = [0, 0]
|
||||
for (let k = 0; k <= n; k++) {
|
||||
let factor = getQuadricBSplineFactor(k, t)
|
||||
x += factor * points[i + k][0]
|
||||
y += factor * points[i + k][1]
|
||||
}
|
||||
bSplinePoints.push([x, y])
|
||||
}
|
||||
}
|
||||
bSplinePoints.push(points[points.length - 1])
|
||||
return bSplinePoints
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到二次线性因子
|
||||
* @param k
|
||||
* @param t
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getQuadricBSplineFactor = (k, t) => {
|
||||
let res = 0
|
||||
if (k === 0) {
|
||||
res = Math.pow(t - 1, 2) / 2
|
||||
} else if (k === 1) {
|
||||
res = (-2 * Math.pow(t, 2) + 2 * t + 1) / 2
|
||||
} else if (k === 2) {
|
||||
res = Math.pow(t, 2) / 2
|
||||
}
|
||||
return res
|
||||
}
|
||||
@ -3,42 +3,60 @@
|
||||
* @desc 标绘图形构造类
|
||||
*/
|
||||
|
||||
import Arc from './Polyline/Arc'
|
||||
import Curve from './Polyline/Curve'
|
||||
import Polyline from './Polyline/Polyline'
|
||||
import FreeLine from './Polyline/FreeLine'
|
||||
import * as PlotTypes from '../core/PlotTypes'
|
||||
const RegisterModes = {}
|
||||
RegisterModes[PlotTypes.ARC] = {
|
||||
'freehand': false,
|
||||
'limitClickCount': 3,
|
||||
'action': ['click', 'mousemove'],
|
||||
'create': function (path) {
|
||||
return new Arc(path)
|
||||
},
|
||||
'update': function (path, geometry) {
|
||||
geometry.setPoints(path)
|
||||
},
|
||||
'generate': function (geometry) {
|
||||
return geometry
|
||||
}
|
||||
}
|
||||
RegisterModes[PlotTypes.CURVE] = {
|
||||
'action': ['click', 'click', 'dbclick'],
|
||||
'freehand': false,
|
||||
'action': ['click', 'mousemove', 'dblclick'],
|
||||
'create': function (path) {
|
||||
return new Curve(path)
|
||||
},
|
||||
'update': function (path, geometry) {
|
||||
geometry.setCoordinates(path)
|
||||
geometry.setPoints(path)
|
||||
},
|
||||
'generate': function (geometry) {
|
||||
return geometry
|
||||
}
|
||||
}
|
||||
RegisterModes[PlotTypes.POLYLINE] = {
|
||||
'action': ['click', 'mousemove', 'dbclick'],
|
||||
'freehand': false,
|
||||
'action': ['click', 'mousemove', 'dblclick'],
|
||||
'create': function (path) {
|
||||
return new Polyline(path)
|
||||
},
|
||||
'update': function (path, geometry) {
|
||||
geometry.setCoordinates(path)
|
||||
geometry.setPoints(path)
|
||||
},
|
||||
'generate': function (geometry) {
|
||||
return geometry
|
||||
}
|
||||
}
|
||||
RegisterModes[PlotTypes.FREE_LINE] = {
|
||||
'freehand': true,
|
||||
'action': ['mousedown', 'drag', 'mouseup'],
|
||||
'create': function (path) {
|
||||
return new FreeLine(path)
|
||||
},
|
||||
'update': function (path, geometry) {
|
||||
geometry.setCoordinates(path)
|
||||
geometry.setPoints(path)
|
||||
},
|
||||
'generate': function (geometry) {
|
||||
return geometry
|
||||
|
||||
@ -1,494 +1,3 @@
|
||||
import * as Constants from '../Constants'
|
||||
/**
|
||||
* 计算两个坐标之间的距离
|
||||
* @param pnt1
|
||||
* @param pnt2
|
||||
* @returns {number}
|
||||
* @constructor
|
||||
*/
|
||||
export const MathDistance = (pnt1, pnt2) => {
|
||||
return (Math.sqrt(Math.pow((pnt1[0] - pnt2[0]), 2) + Math.pow((pnt1[1] - pnt2[1]), 2)))
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算点集合的总距离
|
||||
* @param points
|
||||
* @returns {number}
|
||||
*/
|
||||
export const wholeDistance = (points) => {
|
||||
let distance = 0
|
||||
if (points && Array.isArray(points) && points.length > 0) {
|
||||
points.forEach((item, index) => {
|
||||
if (index < points.length - 1) {
|
||||
distance += (MathDistance(item, points[index + 1]))
|
||||
}
|
||||
})
|
||||
}
|
||||
return distance
|
||||
}
|
||||
/**
|
||||
* 获取基础长度
|
||||
* @param points
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getBaseLength = (points) => {
|
||||
return Math.pow(wholeDistance(points), 0.99)
|
||||
}
|
||||
|
||||
/**
|
||||
* 求取两个坐标的中间值
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @returns {[*,*]}
|
||||
* @constructor
|
||||
*/
|
||||
export const Mid = (point1, point2) => {
|
||||
return [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2]
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过三个点确定一个圆的中心点
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @param point3
|
||||
*/
|
||||
export const getCircleCenterOfThreePoints = (point1, point2, point3) => {
|
||||
let pntA = [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2]
|
||||
let pntB = [pntA[0] - point1[1] + point2[1], pntA[1] + point1[0] - point2[0]]
|
||||
let pntC = [(point1[0] + point3[0]) / 2, (point1[1] + point3[1]) / 2]
|
||||
let pntD = [pntC[0] - point1[1] + point3[1], pntC[1] + point1[0] - point3[0]]
|
||||
return getIntersectPoint(pntA, pntB, pntC, pntD)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取交集的点
|
||||
* @param pntA
|
||||
* @param pntB
|
||||
* @param pntC
|
||||
* @param pntD
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getIntersectPoint = (pntA, pntB, pntC, pntD) => {
|
||||
if (pntA[1] === pntB[1]) {
|
||||
let f = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1])
|
||||
let x = f * (pntA[1] - pntC[1]) + pntC[0]
|
||||
let y = pntA[1]
|
||||
return [x, y]
|
||||
}
|
||||
if (pntC[1] === pntD[1]) {
|
||||
let e = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1])
|
||||
let x = e * (pntC[1] - pntA[1]) + pntA[0]
|
||||
let y = pntC[1]
|
||||
return [x, y]
|
||||
}
|
||||
let e = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1])
|
||||
let f = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1])
|
||||
let y = (e * pntA[1] - pntA[0] - f * pntC[1] + pntC[0]) / (e - f)
|
||||
let x = e * y - e * pntA[1] + pntA[0]
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方位角(地平经度)
|
||||
* @param startPoint
|
||||
* @param endPoint
|
||||
* @returns {*}
|
||||
*/
|
||||
export const getAzimuth = (startPoint, endPoint) => {
|
||||
let azimuth
|
||||
let angle = Math.asin(Math.abs(endPoint[1] - startPoint[1]) / (MathDistance(startPoint, endPoint)))
|
||||
if (endPoint[1] >= startPoint[1] && endPoint[0] >= startPoint[0]) {
|
||||
azimuth = angle + Math.PI
|
||||
} else if (endPoint[1] >= startPoint[1] && endPoint[0] < startPoint[0]) {
|
||||
azimuth = Math.PI * 2 - angle
|
||||
} else if (endPoint[1] < startPoint[1] && endPoint[0] < startPoint[0]) {
|
||||
azimuth = angle
|
||||
} else if (endPoint[1] < startPoint[1] && endPoint[0] >= startPoint[0]) {
|
||||
azimuth = Math.PI - angle
|
||||
}
|
||||
return azimuth
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过三个点获取方位角
|
||||
* @param pntA
|
||||
* @param pntB
|
||||
* @param pntC
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getAngleOfThreePoints = (pntA, pntB, pntC) => {
|
||||
let angle = getAzimuth(pntB, pntA) - getAzimuth(pntB, pntC)
|
||||
return ((angle < 0) ? (angle + Math.PI * 2) : angle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是顺时针
|
||||
* @param pnt1
|
||||
* @param pnt2
|
||||
* @param pnt3
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isClockWise = (pnt1, pnt2, pnt3) => {
|
||||
return ((pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) > (pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0]))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取线上的点
|
||||
* @param t
|
||||
* @param startPnt
|
||||
* @param endPnt
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getPointOnLine = (t, startPnt, endPnt) => {
|
||||
let x = startPnt[0] + (t * (endPnt[0] - startPnt[0]))
|
||||
let y = startPnt[1] + (t * (endPnt[1] - startPnt[1]))
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取立方值
|
||||
* @param t
|
||||
* @param startPnt
|
||||
* @param cPnt1
|
||||
* @param cPnt2
|
||||
* @param endPnt
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getCubicValue = (t, startPnt, cPnt1, cPnt2, endPnt) => {
|
||||
t = Math.max(Math.min(t, 1), 0)
|
||||
let [tp, t2] = [(1 - t), (t * t)]
|
||||
let t3 = t2 * t
|
||||
let tp2 = tp * tp
|
||||
let tp3 = tp2 * tp
|
||||
let x = (tp3 * startPnt[0]) + (3 * tp2 * t * cPnt1[0]) + (3 * tp * t2 * cPnt2[0]) + (t3 * endPnt[0])
|
||||
let y = (tp3 * startPnt[1]) + (3 * tp2 * t * cPnt1[1]) + (3 * tp * t2 * cPnt2[1]) + (t3 * endPnt[1])
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据起止点和旋转方向求取第三个点
|
||||
* @param startPnt
|
||||
* @param endPnt
|
||||
* @param angle
|
||||
* @param distance
|
||||
* @param clockWise
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getThirdPoint = (startPnt, endPnt, angle, distance, clockWise) => {
|
||||
let azimuth = getAzimuth(startPnt, endPnt)
|
||||
let alpha = clockWise ? (azimuth + angle) : (azimuth - angle)
|
||||
let dx = distance * Math.cos(alpha)
|
||||
let dy = distance * Math.sin(alpha)
|
||||
return ([endPnt[0] + dx, endPnt[1] + dy])
|
||||
}
|
||||
|
||||
/**
|
||||
* 插值弓形线段点
|
||||
* @param center
|
||||
* @param radius
|
||||
* @param startAngle
|
||||
* @param endAngle
|
||||
* @returns {null}
|
||||
*/
|
||||
export const getArcPoints = (center, radius, startAngle, endAngle) => {
|
||||
let [x, y, pnts, angleDiff] = [null, null, [], (endAngle - startAngle)]
|
||||
angleDiff = ((angleDiff < 0) ? (angleDiff + (Math.PI * 2)) : angleDiff)
|
||||
for (let i = 0; i <= 100; i++) {
|
||||
let angle = startAngle + angleDiff * i / 100
|
||||
x = center[0] + radius * Math.cos(angle)
|
||||
y = center[1] + radius * Math.sin(angle)
|
||||
pnts.push([x, y])
|
||||
}
|
||||
return pnts
|
||||
}
|
||||
|
||||
/**
|
||||
* getBisectorNormals
|
||||
* @param t
|
||||
* @param pnt1
|
||||
* @param pnt2
|
||||
* @param pnt3
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getBisectorNormals = (t, pnt1, pnt2, pnt3) => {
|
||||
let normal = getNormal(pnt1, pnt2, pnt3)
|
||||
let [bisectorNormalRight, bisectorNormalLeft, dt, x, y] = [null, null, null, null, null]
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
let uX = normal[0] / dist
|
||||
let uY = normal[1] / dist
|
||||
let d1 = MathDistance(pnt1, pnt2)
|
||||
let d2 = MathDistance(pnt2, pnt3)
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
if (isClockWise(pnt1, pnt2, pnt3)) {
|
||||
dt = t * d1
|
||||
x = pnt2[0] - dt * uY
|
||||
y = pnt2[1] + dt * uX
|
||||
bisectorNormalRight = [x, y]
|
||||
dt = t * d2
|
||||
x = pnt2[0] + dt * uY
|
||||
y = pnt2[1] - dt * uX
|
||||
bisectorNormalLeft = [x, y]
|
||||
} else {
|
||||
dt = t * d1
|
||||
x = pnt2[0] + dt * uY
|
||||
y = pnt2[1] - dt * uX
|
||||
bisectorNormalRight = [x, y]
|
||||
dt = t * d2
|
||||
x = pnt2[0] - dt * uY
|
||||
y = pnt2[1] + dt * uX
|
||||
bisectorNormalLeft = [x, y]
|
||||
}
|
||||
} else {
|
||||
x = pnt2[0] + t * (pnt1[0] - pnt2[0])
|
||||
y = pnt2[1] + t * (pnt1[1] - pnt2[1])
|
||||
bisectorNormalRight = [x, y]
|
||||
x = pnt2[0] + t * (pnt3[0] - pnt2[0])
|
||||
y = pnt2[1] + t * (pnt3[1] - pnt2[1])
|
||||
bisectorNormalLeft = [x, y]
|
||||
}
|
||||
return [bisectorNormalRight, bisectorNormalLeft]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认三点的内切圆
|
||||
* @param pnt1
|
||||
* @param pnt2
|
||||
* @param pnt3
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getNormal = (pnt1, pnt2, pnt3) => {
|
||||
let dX1 = pnt1[0] - pnt2[0]
|
||||
let dY1 = pnt1[1] - pnt2[1]
|
||||
let d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1)
|
||||
dX1 /= d1
|
||||
dY1 /= d1
|
||||
let dX2 = pnt3[0] - pnt2[0]
|
||||
let dY2 = pnt3[1] - pnt2[1]
|
||||
let d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2)
|
||||
dX2 /= d2
|
||||
dY2 /= d2
|
||||
let uX = dX1 + dX2
|
||||
let uY = dY1 + dY2
|
||||
return [uX, uY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取左边控制点
|
||||
* @param controlPoints
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getLeftMostControlPoint = (controlPoints, t) => {
|
||||
let [pnt1, pnt2, pnt3, controlX, controlY] = [controlPoints[0], controlPoints[1], controlPoints[2], null, null]
|
||||
let pnts = getBisectorNormals(0, pnt1, pnt2, pnt3)
|
||||
let normalRight = pnts[0]
|
||||
let normal = getNormal(pnt1, pnt2, pnt3)
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
let mid = Mid(pnt1, pnt2)
|
||||
let pX = pnt1[0] - mid[0]
|
||||
let pY = pnt1[1] - mid[1]
|
||||
let d1 = MathDistance(pnt1, pnt2)
|
||||
let n = 2.0 / d1
|
||||
let nX = -n * pY
|
||||
let nY = n * pX
|
||||
let a11 = nX * nX - nY * nY
|
||||
let a12 = 2 * nX * nY
|
||||
let a22 = nY * nY - nX * nX
|
||||
let dX = normalRight[0] - mid[0]
|
||||
let dY = normalRight[1] - mid[1]
|
||||
controlX = mid[0] + a11 * dX + a12 * dY
|
||||
controlY = mid[1] + a12 * dX + a22 * dY
|
||||
} else {
|
||||
controlX = pnt1[0] + t * (pnt2[0] - pnt1[0])
|
||||
controlY = pnt1[1] + t * (pnt2[1] - pnt1[1])
|
||||
}
|
||||
return [controlX, controlY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取右边控制点
|
||||
* @param controlPoints
|
||||
* @param t
|
||||
* @returns {[*,*]}
|
||||
*/
|
||||
export const getRightMostControlPoint = (controlPoints, t) => {
|
||||
let count = controlPoints.length
|
||||
let pnt1 = controlPoints[count - 3]
|
||||
let pnt2 = controlPoints[count - 2]
|
||||
let pnt3 = controlPoints[count - 1]
|
||||
let pnts = getBisectorNormals(0, pnt1, pnt2, pnt3)
|
||||
let normalLeft = pnts[1]
|
||||
let normal = getNormal(pnt1, pnt2, pnt3)
|
||||
let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
|
||||
let [controlX, controlY] = [null, null]
|
||||
if (dist > Constants.ZERO_TOLERANCE) {
|
||||
let mid = Mid(pnt2, pnt3)
|
||||
let pX = pnt3[0] - mid[0]
|
||||
let pY = pnt3[1] - mid[1]
|
||||
let d1 = MathDistance(pnt2, pnt3)
|
||||
let n = 2.0 / d1
|
||||
let nX = -n * pY
|
||||
let nY = n * pX
|
||||
let a11 = nX * nX - nY * nY
|
||||
let a12 = 2 * nX * nY
|
||||
let a22 = nY * nY - nX * nX
|
||||
let dX = normalLeft[0] - mid[0]
|
||||
let dY = normalLeft[1] - mid[1]
|
||||
controlX = mid[0] + a11 * dX + a12 * dY
|
||||
controlY = mid[1] + a12 * dX + a22 * dY
|
||||
} else {
|
||||
controlX = pnt3[0] + t * (pnt2[0] - pnt3[0])
|
||||
controlY = pnt3[1] + t * (pnt2[1] - pnt3[1])
|
||||
}
|
||||
return [controlX, controlY]
|
||||
}
|
||||
|
||||
/**
|
||||
* 插值曲线点
|
||||
* @param t
|
||||
* @param controlPoints
|
||||
* @returns {null}
|
||||
*/
|
||||
export const getCurvePoints = (t, controlPoints) => {
|
||||
let leftControl = getLeftMostControlPoint(controlPoints, t)
|
||||
let [pnt1, pnt2, pnt3, normals, points] = [null, null, null, [leftControl], []]
|
||||
for (let i = 0; i < controlPoints.length - 2; i++) {
|
||||
[pnt1, pnt2, pnt3] = [controlPoints[i], controlPoints[i + 1], controlPoints[i + 2]]
|
||||
let normalPoints = getBisectorNormals(t, pnt1, pnt2, pnt3)
|
||||
normals = normals.concat(normalPoints)
|
||||
}
|
||||
let rightControl = getRightMostControlPoint(controlPoints, t)
|
||||
if (rightControl) {
|
||||
normals.push(rightControl)
|
||||
}
|
||||
for (let i = 0; i < controlPoints.length - 1; i++) {
|
||||
pnt1 = controlPoints[i]
|
||||
pnt2 = controlPoints[i + 1]
|
||||
points.push(pnt1)
|
||||
for (let t = 0; t < Constants.FITTING_COUNT; t++) {
|
||||
let pnt = getCubicValue(t / Constants.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2)
|
||||
points.push(pnt)
|
||||
}
|
||||
points.push(pnt2)
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
/**
|
||||
* 贝塞尔曲线
|
||||
* @param points
|
||||
* @returns {*}
|
||||
*/
|
||||
export const getBezierPoints = function (points) {
|
||||
if (points.length <= 2) {
|
||||
return points
|
||||
} else {
|
||||
let bezierPoints = []
|
||||
let n = points.length - 1
|
||||
for (let t = 0; t <= 1; t += 0.01) {
|
||||
let [x, y] = [0, 0]
|
||||
for (let index = 0; index <= n; index++) {
|
||||
let factor = getBinomialFactor(n, index)
|
||||
let a = Math.pow(t, index)
|
||||
let b = Math.pow((1 - t), (n - index))
|
||||
x += factor * a * b * points[index][0]
|
||||
y += factor * a * b * points[index][1]
|
||||
}
|
||||
bezierPoints.push([x, y])
|
||||
}
|
||||
bezierPoints.push(points[n])
|
||||
return bezierPoints
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取阶乘数据
|
||||
* @param n
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getFactorial = (n) => {
|
||||
let result = 1
|
||||
switch (n) {
|
||||
case (n <= 1):
|
||||
result = 1
|
||||
break
|
||||
case (n === 2):
|
||||
result = 2
|
||||
break
|
||||
case (n === 3):
|
||||
result = 6
|
||||
break
|
||||
case (n === 24):
|
||||
result = 24
|
||||
break
|
||||
case (n === 5):
|
||||
result = 120
|
||||
break
|
||||
default:
|
||||
for (let i = 1; i <= n; i++) {
|
||||
result *= i
|
||||
}
|
||||
break
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二项分布
|
||||
* @param n
|
||||
* @param index
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getBinomialFactor = (n, index) => {
|
||||
return (getFactorial(n) / (getFactorial(index) * getFactorial(n - index)))
|
||||
}
|
||||
|
||||
/**
|
||||
* 插值线性点
|
||||
* @param points
|
||||
* @returns {*}
|
||||
*/
|
||||
export const getQBSplinePoints = points => {
|
||||
if (points.length <= 2) {
|
||||
return points
|
||||
} else {
|
||||
let [n, bSplinePoints] = [2, []]
|
||||
let m = points.length - n - 1
|
||||
bSplinePoints.push(points[0])
|
||||
for (let i = 0; i <= m; i++) {
|
||||
for (let t = 0; t <= 1; t += 0.05) {
|
||||
let [x, y] = [0, 0]
|
||||
for (let k = 0; k <= n; k++) {
|
||||
let factor = getQuadricBSplineFactor(k, t)
|
||||
x += factor * points[i + k][0]
|
||||
y += factor * points[i + k][1]
|
||||
}
|
||||
bSplinePoints.push([x, y])
|
||||
}
|
||||
}
|
||||
bSplinePoints.push(points[points.length - 1])
|
||||
return bSplinePoints
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到二次线性因子
|
||||
* @param k
|
||||
* @param t
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getQuadricBSplineFactor = (k, t) => {
|
||||
let res = 0
|
||||
if (k === 0) {
|
||||
res = Math.pow(t - 1, 2) / 2
|
||||
} else if (k === 1) {
|
||||
res = (-2 * Math.pow(t, 2) + 2 * t + 1) / 2
|
||||
} else if (k === 2) {
|
||||
res = Math.pow(t, 2) / 2
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为对象
|
||||
* @param value
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user