this.dragLinkPos = null;
this.resizeLinkPos = null;
+ this.movePolygonPointPos = null;
this.magnetValuesX = [];
this.magnetValuesY = [];
this.contextMenuPosition = null;
this.rectSelection = null;
- this.shapeMode = 'rectangle';
this.dropTypes = [4, 6, 7, 12, 15, 16, 17, 18, 25, 30, 31];
LinkeditorLinks.prototype = {
init: function () {
- this.updateShapeMode();
},
initEvents: function () {
$this.deselectAllLinks();
let link = $(this).closest('.link');
$this.selectLink(link);
- $this.startResizeLink($(this).attr('class'));
+ if ($(this).hasClass('poly')) {
+ $this.startMovePolygonPoint($(this).data('point'));
+ } else {
+ $this.startResizeLink($(this).attr('class'));
+ }
});
$(document).on('mousedown', '.link', function (e) {
});
this.linkeditor.hasChanged();
+ this.updatePolygonLinks();
this.updateLayers();
},
},
createLinkDrag: function () {
- var link = this.duplicateLinkDrag({width: 0, height: 0});
+ var link = this.duplicateLinkDrag({width: 0, height: 0, polygon: null});
$(link).addClass('pendingCreate').addClass('new');
this.deselectAllLinks();
this.selectLink($(link));
},
selectLink: function (l) {
+ console.log($(l).find('.corners'));
if ($(l).find('.corners').length === 0) {
$(l).append('<div class="corners"><div class="nw"></div><div class="n"></div><div class="ne"></div><div class="e"></div><div class="se"></div><div class="s"></div><div class="sw"></div><div class="w"></div></div>')
}
}
$this.addLink(link, false);
});
+ this.updatePolygonLinks();
this.updateLayers();
this.linkeditor.undo.initState();
},
this.deselectAllLinks();
this.selectLink($(link));
+ this.updatePolygonLinks();
this.linkeditor.form.updateFormData();
return $(link);
});
this.linkeditor.rulers.updateMagnetValues();
this.updateLayers();
+ this.updatePolygonLinks(false);
},
updateSelectionData: function (props) {
mouseUp: function () {
this.endRectSelection();
this.stopDragLink();
- this.endPolygonLine();
this.stopResizeLink();
+ this.stopMovePolygonPoint();
this.cleanPendingCreateLink();
},
mouseDown: function () {
- if (this.shapeMode === 'rectangle') {
- this.createLinkDrag();
- } else {
+ if ($('#linkeditor-main').hasClass('polygon')) {
this.beginPolygonLine();
+ } else {
+ this.createLinkDrag();
}
},
- toggleShape: function (shape) {
- if (this.shapeMode === shape) {
- return;
- }
- this.shapeMode = shape;
- this.updateShapeMode();
- },
-
- updateShapeMode: function () {
- $('[data-icon^="shape"]').removeClass('active');
- $('[data-icon="shape-' + this.shapeMode + '"]').addClass('active');
- },
-
beginPolygonLine: function () {
var link;
var pos = this.linkeditor.globalToFluidbook(this.linkeditor.mx, this.linkeditor.my, this.linkeditor.single);
+ let polygon;
if ($('.pendingPolygonCreate').length === 0) {
link = this.duplicateLinkDrag({width: 0, height: 0, left: pos.x, top: pos.y});
$(link).addClass('pendingPolygonCreate');
- $(link).data('points', [pos]);
+ polygon = [pos];
} else {
link = $('.pendingPolygonCreate');
- $(link).data('points').push(pos);
+ polygon = this.getOffsetPolygon(link);
+ polygon.push(pos);
}
+
this.selectLink($(link));
- this.updatePolygonLink(link);
+ this.updatePolygonLink(link, polygon);
this.linkeditor.hasChanged();
},
- updatePolygonLink: function (link) {
+ closePolygonShape: function () {
+ $('.pendingPolygonCreate').removeClass('pendingPolygonCreate');
+ },
+
+ getOffsetPolygon: function (link) {
+ let left = parseFloat($(link).attr('fb-left'));
+ let top = parseFloat($(link).attr('fb-top'));
+ try {
+ let points = JSON.parse($(link).attr('fb-polygon'));
+ let offset = [];
+ $.each(points, function (k, v) {
+ offset.push({x: parseFloat(v.x) + left, y: parseFloat(v.y) + top});
+ });
+ return offset;
+ } catch (e) {
+ return null;
+ }
+ },
+
+ updatePolygonLinks: function (updateData) {
+ if (updateData === undefined) {
+ updateData = true;
+ }
+ let $this = this;
+ $('.link[fb-polygon]').each(function () {
+ let polygon = $this.getOffsetPolygon($(this));
+ if (polygon !== null) {
+ $this.updatePolygonLink($(this), polygon, updateData);
+ } else {
+ $(this).find('svg,.corners').remove();
+ $(this).attr('fb-polygon', null);
+ }
+ });
+ },
+
+ updatePolygonLink: function (link, polygon, updateData) {
+ if (updateData === undefined) {
+ updateData = true;
+ }
+ let $this = this;
+
let minx = Number.MAX_VALUE;
let maxx = Number.MIN_VALUE;
let miny = Number.MAX_VALUE;
let maxy = Number.MIN_VALUE;
- $.each($(link).data('points'), function (k, pos) {
+ $.each(polygon, function (k, pos) {
minx = Math.min(minx, pos.x);
miny = Math.min(miny, pos.y);
maxx = Math.max(maxx, pos.x);
maxy = Math.max(maxy, pos.y);
});
+ let normalizedPolygon = [];
+
let w = maxx - minx;
let h = maxy - miny;
$(link).attr('fb-left', minx);
$(link).attr('fb-top', miny);
- let svg = '<svg preserveAspectRatio="none" width="' + w + '" height="' + h + '" style="width: 100%;height:100%;"><polygon points="';
-
- $.each($(link).data('points'), function (k, pos) {
- svg += (pos.x - minx) + ',' + (pos.y - miny) + ' ';
+ let svg = '<svg preserveAspectRatio="none" viewBox="0 0 ' + w + ' ' + h + '" width="' + w + '" height="' + h + '" style="width: 100%;height:100%;"><polygon points="';
+ let corners = $('<div class="corners"></div>');
+ let clippath = [];
+
+ $.each(polygon, function (k, pos) {
+ let point = {
+ x: $this.linkeditor.utils.roundDimension(pos.x - minx),
+ y: $this.linkeditor.utils.roundDimension(pos.y - miny)
+ };
+ let cx = (point.x / w) * 100;
+ let cy = (point.y / h) * 100;
+ svg += point.x + ',' + point.y + ' ';
+ normalizedPolygon.push(point);
+ $(corners).append('<div class="poly" data-point="' + k + '" style="left:calc(' + cx + '% - 4px);top:calc(' + cy + '% - 4px);"></div>');
+
+ let cs = 1.05;
+ let ccx = ((cx / 100) * 100 * cs) - ((cs * 100) - 100) / 2;
+ let ccy = ((cy / 100) * 100 * cs) - ((cs * 100) - 100) / 2;
+ clippath.push([ccx, ccy]);
});
- svg += '" style="fill:currentColor;stroke:currentColor;stroke-width:1;fill-opacity:0.25;"></svg>';
+
+
+ svg += '" fill="currentColor" stroke="currentColor" stroke-width="1.25" fill-opacity="0.25" vector-effect="non-scaling-stroke"></svg>';
+ $(link).css('clip-path', 'polygon(' + this.getConvexClipPath(clippath) + ')');
$(link).html(svg);
+ $(link).append(corners);
+ let jsonPolygon = JSON.stringify(normalizedPolygon);
+ $(link).attr('fb-polygon', jsonPolygon);
+ if (updateData) {
+ this.updateLinkData($(link).attr('fb-uid'), {
+ left: minx,
+ top: miny,
+ width: w,
+ height: h,
+ polygon: jsonPolygon
+ });
+ }
+ },
+ getConvexClipPath: function (points) {
+ const grahamscan = new GrahamScan();
+ grahamscan.setPoints(points);
+ let hull = grahamscan.getHull();
+ let res = [];
+ $.each(hull, function (k, p) {
+ res.push(p[0] + '% ' + p[1] + '%');
+ });
+ return res.join(',');
+ },
+
+ startMovePolygonPoint: function (idx) {
+ let link = this.getFirstLinkInSelection();
+ let polygon = this.getOffsetPolygon(link);
+ this.movePolygonPointPos = {
+ x: this.linkeditor.mx,
+ y: this.linkeditor.my,
+ ox: polygon[idx].x,
+ oy: polygon[idx].y,
+ index: idx
+ };
+ this.setDragOrigValues();
},
- endPolygonLine: function () {
+ stopMovePolygonPoint: function () {
+ if (this.movePolygonPointPos === null) {
+ return;
+ }
+ var $this = this;
+ this.linkeditor.form.updateLinkForm();
+ this.movePolygonPointPos = null;
+ this.updatePolygonLinks();
+ this.linkeditor.hasChanged();
+ },
+ movePolygonPoint: function () {
+ if (this.movePolygonPointPos === null) {
+ return;
+ }
+ let $this = this;
+ let f = 1 / (this.linkeditor.fs * this.linkeditor.zoom.zoom);
+ let dx = (this.linkeditor.mx - this.movePolygonPointPos.x) * f;
+ let dy = (this.linkeditor.my - this.movePolygonPointPos.y) * f;
+
+
+ let link = this.getFirstLinkInSelection();
+ let polygon = this.getOffsetPolygon(link);
+ polygon[this.movePolygonPointPos.index].x = this.movePolygonPointPos.ox + dx;
+ polygon[this.movePolygonPointPos.index].y = this.movePolygonPointPos.oy + dy;
+
+ this.updatePolygonLink(link, polygon);
+ $(link).attr('fb-update', '1');
+
+ this.linkeditor.updateFBElements(false);
+ this.linkeditor.save.hasChanged();
},
};