# freehand line

This commit is contained in:
FDD 2017-12-24 21:15:48 +08:00
parent 609bb765ac
commit ab00a0a3e3
13 changed files with 1506 additions and 694 deletions

567
dist/maptalks.plot.js vendored
View File

@ -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);
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -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: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <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>

View File

@ -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: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <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 () {

View File

@ -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)
}
}
}

View 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

View File

@ -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'])

View File

@ -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)

View File

@ -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)

View 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
}

View 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
}

View File

@ -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

View File

@ -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