// Scripting by Oleg Okhotnikov, contact - svoboda200786@gmail.com let legend_back; let active_obj; let move_flag = false; let drawing_object = false; let deltaX = 0; let deltaY = 0; let borders; let point; let min_val = 0.95; let shape_change_flag = false; let katet_min_length = 10; let stick_array = []; var text_editing; // показывает, есть ли активный текстовый элемент внутри составного объекта, // иначе клик по полю снимает активность текстового элемента // "Составные" объекты состоят из нескольких примитивов и в отличие от групп позволяют // перемещать подобъекты по отдельности, при этом сохраняя целостность фигуры // Создаем составные объекты, линия (line), размерность (size), выноска (cap), розетка (socket) и т.д.: let btn_stick = document.querySelector("#stick"); let arrow_middle_pointX = 16; function add_line(e, obj, ispart=false, x1=old_arrow_X, y1=old_arrow_Y, x2=canvas.getPointer(e.e).x, y2=canvas.getPointer(e.e).y, flag=true, name="line", stroke = fieldcolor, line_type="line"){ var line, arrow, arrow2; if(!ispart){ // не подобъект, как например, стрелки у розетки. У каждого составного объекта // есть родительский подобъект, обычно линия, // (object.parent), который записывается в массив всех фигур (shape) index++; if(obj != undefined){ for(let i in obj){ obj[i].groupName = /[a-zA-Z\_]+/.exec(obj[i].groupName) + index; // перезаписываем groupName, добавляя новый индекс } } } if(obj != undefined){ // значит мы записываем в объект line линию, которую вернул канвас в start.js line = obj.line; // первый элемент составного объекта line.index = index; } else{ // создаем новый объект line line = drawArrowLine([x1, y1, x2, y2], stroke, index, name, calcLineLength({x1: x1, y1: y1, x2: x2, y2: y2}), line_type); } if(!ispart){ // если стрелка не часть составного объекта вроде socket, то shape[index] = line; // делаем элемент line ключевым - при его (line) перемещении, // все части составного объекта arrow двигаются за ним (то есть за line) } var centerX = (line.x1 + line.x2) / 2, centerY = (line.y1 + line.y2) / 2; deltaX = line.left - centerX, deltaY = line.top - centerY; if(obj != undefined){ arrow = obj.arrow; arrow.index = index; } else{ switch(name){ case "line": arrow = drawArrowCircle(line, deltaX, deltaY, flag, index, name, "arrow_start"); break; case "arrow": case "size": arrow = drawArrowPoly(x2, y2, x1, y1, deltaX, deltaY, line, stroke, index, name, "arrow_start"); break; case "socket": arrow = drawArrowPoly(x1, y1, x2, y2, deltaX, deltaY, line, stroke, index, name, "arrow_start"); break; } } if(!ispart){ if(obj != undefined){ arrow2 = obj.arrow2; arrow2.index = index; } else{ switch(name){ case "line": case "arrow": arrow2 = drawArrowCircle(line, deltaX, deltaY, flag, index, name, "arrow_end"); break; case "size": arrow2 = drawArrowPoly(x1, y1, x2, y2, deltaX, deltaY, line, stroke, index, name, "arrow_end", -1); break; } } } arrow.line = line.line = line; line.arrow = arrow.arrow = arrow; // свойство parent обозначает родительский подобъект или, иначе говоря, объект, // к которому привязаны остальные подобъекты составного объекта line.parent = arrow.parent = line; line.parent.objects = ['line', 'arrow', 'arrow2']; if(!ispart){ // у линий внутри составных объектов нет стрелок arrow2.line = arrow.line; arrow2.arrow = arrow.arrow; line.arrow2 = arrow.arrow2 = arrow2.arrow2 = arrow2; arrow2.parent = arrow.parent; arrow2.perPixelTargetFind = false; if(name == "line"){ arrow.stroke_colorable = false; } arrow2.stroke_colorable = false; } arrow.perPixelTargetFind = false; if(!window.add_geometry_flag && name != "socket"){ // stick(e, line.arrow2); } if(obj != undefined){ line.parent.added = true; } if(obj){ var groupType = /[a-zA-Z\_]+/.exec(obj.line.groupName); let key = obj.line.groupName; if(!stick_array[key]){ stick_array[key] = {}; if(obj.arrow2 && groupType != "geometry"){ // let param = text2Angle(obj.arrow.get('left'), obj.arrow.get('top'), obj.arrow2.get('left'), obj.arrow2.get('top')); // stick_array[key].x0 = param.x; // stick_array[key].y0 = param.y; // stick_array[key].x1 = obj.arrow.get('left'); // stick_array[key].y1 = obj.arrow.get('top'); // stick_array[key].x2 = obj.arrow2.get('left'); // stick_array[key].y2 = obj.arrow2.get('top'); // console.log(groupType); moveEnd({pointer: {x: obj.line.parent.arrow.left, y: obj.line.parent.arrow.top}}, obj.line.parent.arrow); // moveEnd({pointer: {x: obj.line.parent.arrow2.left, y: obj.line.parent.arrow2.top}}, obj.line.parent.arrow2); } else{ if(groupType == "geometry"){ setTimeout(function(){ // свойство parent поменятеся, поэтоу вызываем таймаут obj.parent = shape[obj.line.index]; let param = text2Angle(obj.line.get('x1'), obj.line.get('y1'), obj.line.get('x2'), obj.line.get('y2')); x0 = stick_array[key].x0 = param.x; y0 = stick_array[key].y0 = param.y; x1 = stick_array[key].x1 = obj.line.get('x1'); y1 = stick_array[key].y1 = obj.line.get('y1'); x2 = stick_array[key].x2 = obj.line.get('x2'); y2 = stick_array[key].y2 = obj.line.get('y2'); x3 = stick_array[key].x3 = obj.parent.line_part1.get('x1'); y3 = stick_array[key].y3 = obj.parent.line_part1.get('y1'); x4 = stick_array[key].x4 = obj.parent.line_part2.get('x1'); y4 = stick_array[key].y4 = obj.parent.line_part2.get('y1'); param = text2Angle(obj.parent.line_part1.get('x1'), obj.parent.line_part1.get('y1'), obj.parent.line.get('x1'), obj.parent.line.get('y1')); x5 = stick_array[key].x5 = param.x; y5 = stick_array[key].y5 = param.y; param = text2Angle(obj.parent.line_part2.get('x1'), obj.parent.line_part2.get('y1'), obj.parent.line.get('x2'), obj.parent.line.get('y2')); x6 = stick_array[key].x6 = param.x; y6 = stick_array[key].y6 = param.y; // var t = canvas.viewportTransform; // canvas.contextTop.fillRect(t[0]*x0+t[4], t[3]*y0+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x1+t[4], t[3]*y1+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x2+t[4], t[3]*y2+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x3+t[4], t[3]*y3+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x4+t[4], t[3]*y4+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x5+t[4], t[3]*y5+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x6+t[4], t[3]*y6+t[5], 10, 10) }, 10); } else{ setTimeout(function(){ moveEnd({pointer: {x: obj.line.parent.arrow_part1.left, y: obj.line.parent.arrow_part1.top}}, obj.line.parent.arrow_part1); moveEnd({pointer: {x: obj.line.parent.arrow_part2.left, y: obj.line.parent.arrow_part2.top}}, obj.line.parent.arrow_part2); }, 10); } } } } line.on('mousedown', function (e) { if(this.name != "line_part1" && this.name != "line_part2" || this.groupType == "geometry"){ if(this.groupType == "size"){ this.line.x1 = this.arrow.left; this.line.y1 = this.arrow.top; this.line.x2 = this.arrow2.left; this.line.y2 = this.arrow2.top; } this.first_center = { x: line.x1 + (line.x2 - line.x1) / 2, y: line.y1 + (line.y2 - line.y1) / 2 }; this.first_click = { x: canvas.getPointer(e.e).x, y: canvas.getPointer(e.e).y }; this.delta_between_arrow_and_click = { x: this.parent.arrow.get("left") - canvas.getPointer(e.e).x, y: this.parent.arrow.get("top") - canvas.getPointer(e.e).y }; } }) arrow.on('mousedown', function (e) { shape_change_flag = true; hideLens(); if(this.parent.line_part1 != undefined){ this.parent.line_part1.first_click = { x: canvas.getPointer(e.e).x, y: canvas.getPointer(e.e).y }; } }) if(arrow2 != undefined){ shape_change_flag = true; arrow2.on('mousedown', function (e) { hideLens(); shape_change_flag = true; if(this.parent.line_part2 != undefined){ this.parent.line_part2.first_click = { x: canvas.getPointer(e.e).x, y: canvas.getPointer(e.e).y }; } }) } if(obj === undefined){ if(!ispart){ canvas.add(line, arrow, arrow2); canvas.setActiveObject(arrow); } } canvas.bringToFront(arrow).renderAll(); line.on('mouseover', function () { canvas.bringToFront(arrow).renderAll(); }) // canvasEvents(); setTimeout(function(){ arrow.setCoords(); if(arrow2){ arrow2.setCoords(); } }, 10); return {line: line, arrow: arrow, arrow2: arrow2}; } function canvasEvents(){ canvas.on('mouse:down', function (e) { let obj = canvas.getActiveObject(); if(obj && obj.parent && obj.parent.groupType == "geometry"){ obj.parent.line_part1.x1 = obj.parent.arrow_part1.get('left'); obj.parent.line_part1.y1 = obj.parent.arrow_part1.get('top'); obj.parent.line_part1.x2 = obj.parent.arrow.get('left'); obj.parent.line_part1.y2 = obj.parent.arrow.get('top'); obj.parent.line_part2.x1 = obj.parent.arrow_part2.get('left'); obj.parent.line_part2.y1 = obj.parent.arrow_part2.get('top'); obj.parent.line_part2.x2 = obj.parent.arrow2.get('left'); obj.parent.line_part2.y2 = obj.parent.arrow2.get('top'); obj.parent.line.x1 = obj.parent.arrow.get('left'); obj.parent.line.y1 = obj.parent.arrow.get('top'); obj.parent.line.x2 = obj.parent.arrow2.get('left'); obj.parent.line.y2 = obj.parent.arrow2.get('top'); obj.oldX = obj.parent.line_part1.x2 obj.oldY = obj.parent.line_part1.y2 } }) canvas.on('mouse:move', function (e) { let obj = canvas.getActiveObject(); if(obj && obj.parent){ if(obj != undefined && obj.type != "activeSelection" && obj.parent.name != "rect"){ if(obj.pointType == "arrow_start" || obj.pointType == "arrow_end"){ obj.parent.line.length = calcLineLength({x1: obj.parent.arrow.left, y1: obj.parent.arrow.top, x2: obj.parent.arrow2.left, y2: obj.parent.arrow2.top}); if (obj.name == "arrow_part1" || obj.name == "arrow_part2"){ obj.parent.line.length = calcLineLength({x1: obj.parent.arrow_part1.left, y1: obj.parent.arrow_part1.top, x2: obj.parent.arrow_part2.left, y2: obj.parent.arrow_part2.top}); } } if (obj.name == "line_part1" || obj.name == "line_part2"){ obj.parent.line.length = calcLineLength({x1: obj.parent.line_part1.left, y1: obj.parent.line_part1.top, x2: obj.parent.line_part2.left, y2: obj.parent.line_part2.top}); } if(obj.parent.added && obj.parent.arrow){ if(obj.parent.groupType != "cap"){ line_min_length = obj.parent.arrow.width * obj.parent.arrow.scaleX; } else{ line_min_length = 10; } } if(obj.parent.line != undefined){ if(obj.parent.line.length >= line_min_length || obj.parent.groupType == "geometry"){ if(obj.pointType == "arrow_start" && shape_change_flag){ moveEnd(e, obj); } if(obj.pointType == "arrow_end" && shape_change_flag){ moveEnd(e, obj); } if(obj.type == "line" && !obj.ispart && (geometry_obj || mouse_down_flag) ){ moveLine(e, obj); window.geometry_selected = false; } } else{ if(obj.pointType == "arrow_start"){ if (obj.name == "arrow_part1"){ obj.set({ left: obj.parent.line_part1.x1, top: obj.parent.line_part1.y1, }) } else if (obj.name == "arrow_part2"){ obj.set({ left: obj.parent.line_part2.x1, top: obj.parent.line_part2.y1, }) } else{ obj.set({ left: obj.parent.line.x1, top: obj.parent.line.y1, }) } } if(obj.pointType == "arrow_end"){ obj.set({ left: obj.parent.line.x2, top: obj.parent.line.y2, }) } if(obj.type == "line"){ if(obj.name == "line_part1"){ obj.set({ x1: obj.parent.arrow_part1.left, y1: obj.parent.arrow_part1.top, x2: obj.parent.line.x1, y2: obj.parent.line.y1 }) } if(obj.name == "line_part2"){ obj.set({ x1: obj.parent.arrow_part2.left, y1: obj.parent.arrow_part2.top, x2: obj.parent.line.x2, y2: obj.parent.line.y2 }) } } // canvas.renderAll(); } } } else if(obj != undefined && obj.parent != undefined && obj.parent.name == "rect" && shape_change_flag){ if (obj.name == "arrow_part1"){ obj.parent.line_part1.length = calcLineLength({x1: obj.parent.arrow_part1.left, y1: obj.parent.arrow_part1.top, x2: obj.parent.line_part1.x2, y2: obj.parent.line_part1.y2}); } if (obj.name == "arrow_part2"){ obj.parent.line_part2.length = calcLineLength({x1: obj.parent.arrow_part2.left, y1: obj.parent.arrow_part2.top, x2: obj.parent.line_part2.x2, y2: obj.parent.line_part2.y2}); } if(obj.pointType == "arrow_start"){ moveEnd(e, obj); } // if(obj.pointType == "arrow_end"){ // moveEnd(e, obj); // } } } }) } function drawArrowLine(array, stroke, index, name, length, line_type){ let line = new fabric.Line(array, { stroke: stroke, selectable: true, hasBorders: false, hasControls: false, fill: 'rgba(0,0,0,0)', originX: 'center', originY: 'center', lockScalingX: true, lockScalingY: true, perPixelTargetFind: true, length: length, name: 'line', groupName: name+index, index: index }); if(line_type == "geometry_line_part1" || line_type == "geometry_line_part2"){ setTimeout(function(){ line.strokeWidth = selectStrokeWidth("geometry", line_type); }, 10); } else{ line.strokeWidth = selectStrokeWidth(name, line_type); } return line; } function drawArrowPoly(x1, y1, x2, y2, deltaX, deltaY, line, stroke, index, name, pointType, direction = 1){ if(direction == 1){ var arrow = new fabric.Path('M 0,2.5 13,-6.31861e-7 9.19811,2.5 13,5 Z'); } else{ var arrow = new fabric.Path('M 13,2.5 0,-6.31861e-7 3.80189,2.5 0,5 Z'); } arrow.set({ left: x1 + deltaX, top: y1 + deltaY, angle: calcArrowAngle(x1, y1, x2, y2), originX: 'left', // стрелка изначально имеет горизонтальную ориентацию, поэтому острие слева посередине originY: 'center', hasBorders: false, hasControls: false, lockScalingX: true, lockScalingY: true, centeredRotation: true, centeredScaling: true, centeredScaling: true, fill: fieldcolor, strokeUniform: true, stroke: stroke, strokeWidth: 0, pointType: pointType, name: 'arrow', groupName: name + index, index: index }); scaleArrow(arrow); if(pointType == "arrow_end"){ arrow.set({ name: 'arrow2', originX: 'right', angle: calcArrowAngle(x2, y2, x1, y1), }); } return arrow; } function drawArrowCircle(line, deltaX, deltaY, flag, index, name, pointType){ let arrow = new fabric.Circle({ left: line.get('x1'), top: line.get('y1'), radius: 5, stroke: 'rgba(0, 0, 0, 0)', strokeWidth: 1, selectable: flag, originX: 'center', originY: 'center', hasBorders: false, hasControls: false, lockScalingX: true, lockScalingY: true, lockRotation: true, pointType: pointType, fill: 'rgba(0, 0, 0, 0)', perPixelTargetFind: true, name: 'arrow', groupName: name + index, index: index }) if(pointType == "arrow_end"){ arrow.name = "arrow2"; } return arrow; } function drawArrowText(text, left, top, angle, originX, originY, index, name, groupName, flag){ let text2 = new fabric.IText(text, { fontSize: text_dimension[zoom_size], // fontWeight: 100 + 100 * stroke_width, originX: originX, originY: originY, fontFamily: "Golos Text", fill: "black", backgroundColor: 'rgba(0,0,0,0)', hasBorders: false, hasControls: false, lockScalingX: true, lockScalingY: true, lockRotation: true, lockMovementX: flag, lockMovementY: flag, left: left, top: top, angle: angle, fill: fieldcolor, textAlign: 'center', name: name, groupName: groupName+index, index: index }) return text2; } function drawSocketRect(x, y, index, name){ var rect = new fabric.Path("M 0 0 L 0 18.484375 L 18.160156 18.484375 L 18.160156 0 L 0 0 z M 9.0800781 2.4316406 A 6.8098435 6.8098435 0 0 1 15.888672 9.2421875 A 6.8098435 6.8098435 0 0 1 9.0800781 16.052734 A 6.8098435 6.8098435 0 0 1 2.2695312 9.2421875 A 6.8098435 6.8098435 0 0 1 9.0800781 2.4316406 z "); rect.set({ left: x, top: y, originX: 'center', originY: 'center', hasBorders: false, hasControls: false, lockScalingX: true, lockScalingY: true, centeredRotation: true, centeredScaling: true, centeredScaling: true, fill: fieldcolor, strokeUniform: true, strokeWidth: 0, name: "rect_back", groupName: name + index, index: index }); return rect; } function add_arrow(e, obj, ispart=false, x1=old_arrow_X, y1=old_arrow_Y, x2=canvas.getPointer(e.e).x, y2=canvas.getPointer(e.e).y, flag=true, name="arrow", stroke = fieldcolor){ return add_line(e, obj, ispart, x1, y1, x2, y2, flag, name, stroke); } //////////////////////////// Size ////////////////////////////// function add_size(e, obj, ispart=false, x1=old_arrow_X, y1=old_arrow_Y, x2=canvas.getPointer(e.e).x, y2=canvas.getPointer(e.e).y, flag=true, name="size", stroke = fieldcolor){ let temp = add_line(e, obj, ispart, x1, y1, x2, y2, flag, name, stroke); let line = temp.line; let arrow = temp.arrow; let arrow2 = temp.arrow2; if(obj != undefined){ text2 = obj.text2; text2.index = index; } else{ let left = text2Angle(line.x1, line.y1, line.x2, line.y2).x - 20; let top = text2Angle(line.x1, line.y1, line.x2, line.y2).y; let angle = text2Angle(line.x1, line.y1, line.x2, line.y2).ang; text2 = drawArrowText("000", left, top, angle, 'left', 'bottom', index, "text2", name, true); } line.text2 = arrow.text2 = text2.text2 = text2; line.text2.selectable = false; // свойство parent обозначает родительский подобъект или, иначе говоря, объект, // к которому привязаны остальные подобъекты составного объекта line.parent = arrow.parent = text2.parent = line; if(!ispart){ arrow2.text2 = arrow.text2; arrow2.parent = arrow.parent; } if(obj != undefined){ setTimeout(function(){ changeTextWidth(text2); }, 100); text2.on('changed', function () { changeTextWidth(text2); }); } text2.on('changed', function () { if(this.width <= 2){ this.width = 33; } if(this.name == "text2_part1"){ var parent = this.parent.line_part1; } if(this.name == "text2_part2"){ var parent = this.parent.line_part2; } if(this.name == "text2"){ var parent = this.parent.line; } // центрирование текста textRotate(parent); this.setCoords(); }); text2.on('editing:entered', function () { text_editing = true; }) text2.on('editing:exited', function () { if(!editor_command){ element_cursor(true); } mouse_down_flag = false; this.initialized = true; if(this.name == "text2_part1"){ if(!this.parent.line_part2.text2.initialized){ this.parent.line_part2.text2.enterEditing(); this.parent.line_part2.text2.hiddenTextarea.focus(); this.parent.line_part2.text2.selectLine(1); let text_obj = this.parent.line_part2.text2; text_editing = true; setTimeout(function(){canvas.setActiveObject(text_obj);}, 10); } else{ text_editing = false; } } if(this.name == "text2_part2"){ if(!this.parent.line_part1.text2.initialized){ this.parent.line_part1.text2.enterEditing(); this.parent.line_part1.text2.hiddenTextarea.focus(); this.parent.line_part1.text2.selectLine(1); let text_obj = this.parent.line_part1.text2; text_editing = true; setTimeout(function(){canvas.setActiveObject(text_obj);}, 10); } else{ text_editing = false; } } if(this.name == "text2"){ text_editing = false; } }); text2.on('mousedown:before', function (e) { this.first_click = { x: canvas.getPointer(e.e).x, y: canvas.getPointer(e.e).y }; mouse_down_flag = true; }); text2.on('mouseup', function (e) { mouse_down_flag = false; canvas.setActiveObject(this); if(!this.was_moved){ this.enterEditing(); } this.was_moved = false; canvas.defaultCursor = null; canvas.defaultCursor_old = null; }); text2.on('mousemove', function (e) { if(mouse_down_flag){ this.exitEditing(); this.was_moved = true; } if(!this.isEditing){ if(this.parent.arrow != undefined){ var turn_angle = this.parent.arrow.angle; } else if(this == this.parent.text2){ var turn_angle = this.parent.arrow_part2.angle; } else{ var turn_angle = this.parent.arrow_part1.angle; } if((turn_angle >= 90 && turn_angle <= 135) || (turn_angle >= -90 && turn_angle <= -45)) { if(mouse_down_flag && this.first_click != undefined && canvas.getPointer(e.e).x > this.first_click.x){ this.set('originY', 'top'); } if(mouse_down_flag && this.first_click != undefined && canvas.getPointer(e.e).x < this.first_click.x){ this.set('originY', 'bottom'); } } else if((turn_angle >= 45 && turn_angle <= 90) || turn_angle == 270 || (turn_angle >= -135 && turn_angle <= -90)) { if(mouse_down_flag && this.first_click != undefined && canvas.getPointer(e.e).x > this.first_click.x){ this.set('originY', 'bottom'); } if(mouse_down_flag && this.first_click != undefined && canvas.getPointer(e.e).x < this.first_click.x){ this.set('originY', 'top'); } } else if(Math.ceil(turn_angle) == 0){ if(mouse_down_flag && this.first_click != undefined && canvas.getPointer(e.e).y > this.first_click.y){ this.set('originY', 'bottom'); } if(mouse_down_flag && this.first_click != undefined && canvas.getPointer(e.e).y < this.first_click.y){ this.set('originY', 'top'); } } else{ if(mouse_down_flag && this.first_click != undefined && canvas.getPointer(e.e).y > this.first_click.y){ this.set('originY', 'top'); } if(mouse_down_flag && this.first_click != undefined && canvas.getPointer(e.e).y < this.first_click.y){ this.set('originY', 'bottom'); } } canvas.renderAll(); } }) if(obj === undefined && !ispart){ canvas.add(text2); canvas.setActiveObject(arrow); } line.parent.objects.push("text2"); line.setCoords(); arrow.setCoords(); if(!ispart){ arrow2.setCoords(); return {line: line, arrow: arrow, arrow2: arrow2, text2: text2}; } else{ return {line: line, arrow: arrow, text2: text2}; } } //////////////////////////// Geometry ////////////////////////////// function add_geometry(e, obj, ispart=false, x1=old_arrow_X, y1=old_arrow_Y, x2=canvas.getPointer(e.e).x, y2=canvas.getPointer(e.e).y, flag=true, name="size", stroke = fieldcolor){ window.add_geometry_flag = true; let line_main, line_part1, line_part2; if(obj != undefined){ // значит мы создаем составной объект при загрузке документа из сохраненного файла obj1 = { line: obj.line, arrow: obj.arrow, arrow2: obj.arrow2, text2: obj.text2 }; line_main = editor_obj.add_size(e, obj1, false); obj2 = { line: obj.line_part1, arrow: obj.arrow_part1 }; line_part1 = editor_obj.add_line(e, obj2, true); obj3 = { line: obj.line_part2, arrow: obj.arrow_part2 }; line_part2 = editor_obj.add_line(e, obj3, true); // Меняем координаты на глобальные line_main.line.x1 = line_main.line.left + line_main.line.x1; line_main.line.y1 = line_main.line.top + line_main.line.y1; line_main.line.x2 = line_main.line.left + line_main.line.x2; line_main.line.y2 = line_main.line.top+ line_main.line.y2; line_part1.line.x1 = line_part1.line.left + line_part1.line.x1; line_part1.line.y1 = line_part1.line.top + line_part1.line.y1; line_part1.line.x2 = line_part1.line.left + line_part1.line.x2; line_part1.line.y2 = line_part1.line.top+ line_part1.line.y2; line_part2.line.x1 = line_part2.line.left + line_part2.line.x1; line_part2.line.y1 = line_part2.line.top + line_part2.line.y1; line_part2.line.x2 = line_part2.line.left + line_part2.line.x2; line_part2.line.y2 = line_part2.line.top+ line_part2.line.y2; line_main.line.setCoords(); line_part1.line.setCoords(); line_part2.line.setCoords(); } else{ line_main = editor_obj.add_size(e, undefined, false, x1, y1, x2, y2, false, "size"); line_part1 = editor_obj.add_line(e, obj, true, line_main.arrow.left, line_main.arrow.top, line_main.arrow.left, line_main.arrow.top, flag, "line", fieldcolor, "geometry_line_part1"); line_part2 = editor_obj.add_line(e, obj, true, line_main.arrow2.left, line_main.arrow2.top, line_main.arrow2.left, line_main.arrow2.top, flag, "line", fieldcolor, "geometry_line_part2"); } // var rect = editor_obj.add_rect(null, true, canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, 18, 18, "rgba(0, 0, 0, 0)", fieldcolor, "socket", true); line_main.line.objects = ["arrow_part1", "line_part1", "arrow_part2", "line_part2", "line", "arrow", "arrow2", "text2"]; line_part1.ispart = true; line_part2.ispart = true; line_main.line.line_part1 = line_part1.line; line_main.line.line_part2 = line_part2.line; line_main.line.arrow_part1 = line_part1.arrow; line_main.line.arrow_part2 = line_part2.arrow; line_main.line.arrow_part1.scale(0); // в текущей реализации верхушки боковых линий объекта не используются line_main.line.arrow_part2.scale(0); // в текущей реализации верхушки боковых линий объекта не используются // свойство parent обозначает родительский подобъект или, иначе говоря, объект, // к которому привязаны остальные подобъекты составного объекта line_main.line.parent = line_main.arrow.parent = line_main.text2.parent = line_main.line.line_part1.parent = line_main.line.line_part2.parent = line_main.line.arrow_part1.parent = line_main.line.line_part1.parent = line_main.line.arrow_part2.parent = line_main.line.line_part2.parent = line_main.line; line_main.line.parent.groupType = "geometry"; if(obj != undefined){ line_main.line.parent.added = true; } line_main.line.line_part1.name = "line_part1"; line_main.line.arrow_part1.name = "arrow_part1"; line_main.line.line_part2.name = "line_part2"; line_main.line.arrow_part2.name = "arrow_part2"; for (i of line_main.line.parent.objects){ line_main.line.parent[i].groupName = "geometry" + index; } shape[index] = line_main.line.line; if(obj == undefined){ canvas.add(line_part1.line, line_part2.line); } // line_main.rect = rect; // window.rect2 = rect; // canvas.add(line_main.rect); // console.log(line_main.rect); line_main.line.line_part1.lockMovementX = true; line_main.line.line_part1.lockMovementY = true; line_main.line.line_part2.lockMovementX = true; line_main.line.line_part2.lockMovementY = true; line_part1.line.strokeWidth = 1; line_part2.line.strokeWidth = 1; line_main.line.arrow_part1.on('mousedown', function (e) { this.parent.line_part1.first_click = { x: canvas.getPointer(e.e).x, y: canvas.getPointer(e.e).y }; }) line_main.line.arrow_part2.on('mousedown', function (e) { this.parent.line_part2.first_click = { x: canvas.getPointer(e.e).x, y: canvas.getPointer(e.e).y }; }) window.add_geometry_flag = false; } //////////////////////////// Caption ////////////////////////// function add_cap(e, obj, x1=old_arrow_X, y1=old_arrow_Y, x2=canvas.getPointer(e.e).x, y2=canvas.getPointer(e.e).y, stroke = fieldcolor, name="cap"){ index++; if(obj != undefined){ for(let i in obj){ obj[i].groupName = /[a-zA-Z\_]+/.exec(obj[i].groupName) + index; // перезаписываем groupName, добавляя новый индекс } } var line, arrow, arrow2; if(obj != undefined){ line = obj.line; line.index = index; } else{ line = drawArrowLine([x2, y2, x1, y1], stroke, index, name, calcLineLength({x1: x1, y1: y1, x2: x2, y2: y2})); } line.objects = ['line', 'arrow', 'arrow2']; shape[index] = line; var centerX = (line.x1 + line.x2) / 2, centerY = (line.y1 + line.y2) / 2; deltaX = line.left - centerX, deltaY = line.top - centerY; if(obj != undefined){ arrow = obj.arrow; arrow.index = index; } else{ arrow = drawArrowText("000", line.x2, line.y2, 0, 'right', 'bottom', index, "arrow", name, false); } if(obj != undefined){ setTimeout(function(){ changeTextWidth(arrow); }, 100); arrow.on('changed', function () { changeTextWidth(arrow); }); } arrow.pointType = 'arrow_start'; arrow.line = line; // arrow.line.strokeLineCap = 'round'; if(obj != undefined){ arrow2 = obj.arrow2; arrow2.index = index; } else{ var diameter = line.strokeWidth * 3 + 1; arrow2 = new fabric.Circle({ left: line.get('x2') + deltaX, top: line.get('y2') + deltaY, radius: diameter / 2, stroke: stroke, strokeWidth: line.strokeWidth, originX: 'center', originY: 'center', hasBorders: true, hasControls: false, lockScalingX: true, lockScalingY: true, lockRotation: true, pointType: 'arrow_end', fill: 'rgba(255, 255, 255, 0)', perPixelTargetFind: true, name: "arrow2", groupName: name + index, index: index }); } arrow2.line = line; arrow2.hasBorders = false; arrow2.colorable = false; line.line = arrow.line = arrow2.line = line; line.arrow = arrow.arrow = arrow2.arrow = arrow; line.arrow2 = arrow.arrow2 = arrow2.arrow2 = arrow2; line.text2 = arrow.text2 = arrow2.text2 = arrow; // свойство parent обозначает родительский подобъект или, иначе говоря, объект, // к которому привязаны остальные подобъекты составного объекта line.parent = arrow2.parent = arrow.parent = line; stick(e, arrow2); if(obj != undefined){ line.parent.added = true; moveEnd({pointer: {x: obj.line.parent.arrow.left, y: obj.line.parent.arrow.top}}, obj.line.parent.arrow); } if(obj == undefined){ canvas.add(line, arrow, arrow2); canvas.setActiveObject(arrow); } shape_change_flag = true; arrow.on('mousedown', function (e) { shape_change_flag = true; hideLens() }) arrow2.on('mousedown', function () { shape_change_flag = true; hideLens() }); line.on('moving', function (e) { moveLine(e, line); }); } //////////////////////////// Socket //////////////////////////// function add_socket(e, obj){ // circle, small_circle1, small_circle2, let rect, rect_back, line_part1, line_part2; index++; if(obj != undefined){ for(let i in obj){ obj[i].groupName = /[a-zA-Z\_]+/.exec(obj[i].groupName) + index; // перезаписываем groupName, добавляя новый индекс } } if(obj != undefined){ // значит мы создаем составной объект при загрузке документа из сохраненного файла obj1 = { line: obj.line_part1, arrow: obj.arrow_part1, text2: obj.text2_part1 }; line_part1 = editor_obj.add_size(e, obj1, true); obj2 = { line: obj.line_part2, arrow: obj.arrow_part2, text2: obj.text2_part2 }; line_part2 = editor_obj.add_size(e, obj2, true); rect_back = obj.rect_back; // circle = obj.circle; // small_circle1 = obj.small_circle1; // small_circle2 = obj.small_circle2; rect = obj.rect; } else{ line_part1 = editor_obj.add_size(null, undefined, true, canvas.getPointer(e.e).x, canvas.getPointer(e.e).y+100, canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, false, "socket"); line_part2 = editor_obj.add_size(null, undefined, true, canvas.getPointer(e.e).x+100, canvas.getPointer(e.e).y, canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, false, "socket"); // rect_back = editor_obj.add_rect(null, false, canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, 18, 18, fieldcolor, fieldcolor, "socket", true); rect_back = drawSocketRect(canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, index, "socket"); // circle = editor_obj.add_circle(null, true, canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, 8, "rgba(255, 255, 255)", fieldcolor, "socket", true); // small_circle1 = editor_obj.add_circle(null, true, canvas.getPointer(e.e).x - 4, canvas.getPointer(e.e).y, 1, fieldcolor, fieldcolor, "socket", true); // small_circle2 = editor_obj.add_circle(null, true, canvas.getPointer(e.e).x + 4, canvas.getPointer(e.e).y, 1, fieldcolor, fieldcolor, "socket", true); rect = editor_obj.add_rect(null, true, canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, 18, 18, "rgba(0, 0, 0, 0)", fieldcolor, "socket", true); } // circle.strokeWidth = 1; // small_circle1.strokeWidth = 1; // small_circle2.strokeWidth = 1; rect.strokeWidth = 1; rect.index = index; rect.groupName = "socket" + index; rect.groupType = "socket"; rect.lockScalingX = true; rect.lockScalingY = true; rect.hasControls = false; // "circle", "small_circle1", "small_circle2", rect.objects = ["rect_back", "arrow_part1", "text2_part1", "line_part1", "arrow_part2", "text2_part2", "line_part2", "rect"]; rect.perPixelTargetFind = false; rect_back.ispart = true; // circle.ispart = true; // small_circle1.ispart = true; // small_circle2.ispart = true; line_part1.line.ispart = true; line_part2.line.ispart = true; line_part1.arrow.ispart = true; line_part2.arrow.ispart = true; line_part1.text2.ispart = true; line_part2.text2.ispart = true; rect_back.colorable = true; // small_circle1.colorable = true; // small_circle2.colorable = true; rect.colorable = false; rect.rect = rect; rect.rect_back = rect_back; rect.line_part1 = line_part1; rect.line_part2 = line_part2; rect.arrow_part1 = line_part1.arrow; rect.text2_part1 = line_part1.text2; rect.line_part1 = line_part1.line; rect.arrow_part2 = line_part2.arrow; rect.text2_part2 = line_part2.text2; rect.text2 = rect.text2_part2; rect.line_part2 = line_part2.line; // rect.circle = circle; // rect.small_circle1 = small_circle1; // rect.small_circle2 = small_circle2; rect.arrow_part1.canvas = canvas; // для правильного расчета координат в getViewportTransform fabric.js rect.arrow_part2.canvas = canvas; // для правильного расчета координат в getViewportTransform fabric.js // свойство parent обозначает родительский подобъект или, иначе говоря, объект, // к которому привязаны остальные подобъекты составного объекта // = rect.circle.parent = rect.small_circle1.parent = rect.small_circle2.parent rect.rect.parent = rect.rect_back.parent = rect.line_part1.parent = rect.line_part2.parent = rect.arrow_part1.parent = rect.line_part1.parent = rect.text2_part1.parent = rect.arrow_part2.parent = rect.text2_part2.parent = rect.line_part2.parent = rect; if(obj != undefined){ rect.parent.added = true; } rect.rect.name = "rect"; // rect.rect_back.name = "rect_back"; // rect.circle.name = "circle"; // rect.small_circle1.name = "small_circle1"; // rect.small_circle2.name = "small_circle2"; rect.line_part1.name = "line_part1"; rect.arrow_part1.name = "arrow_part1"; rect.text2_part1.name = "text2_part1"; rect.line_part2.name = "line_part2"; rect.arrow_part2.name = "arrow_part2"; rect.text2_part2.name = "text2_part2"; for(var i of rect.objects){ rect[i].hasBorders = false; rect[i].hasControls = false; } rect.line_part1.selectable = false; rect.line_part2.selectable = false; rect.text2_part1.selectable = false; rect.text2_part2.selectable = false; shape[index] = rect.rect; if(obj == undefined){ canvas.setActiveObject(shape[index]); moveEnd(e, line_part1.arrow); moveEnd(e, line_part2.arrow); canvas.add(line_part1.text2, line_part1.line, line_part2.text2, line_part2.line, rect.rect_back, rect); } // canvas.bringToFront(circle).renderAll(); // canvas.bringToFront(small_circle1).renderAll(); // canvas.bringToFront(small_circle2).renderAll(); canvas.bringToFront(rect).renderAll(); if(obj == undefined){ scaleSocket(shape[index]); } rect.on('moving', function () { let deltaX = rect.left - rect.oldX; let deltaY = rect.top - rect.oldY; rect.oldX = rect.left; rect.oldY = rect.top; rect_back.left += deltaX; rect_back.top += deltaY; // circle.left += deltaX; // circle.top += deltaY; // small_circle1.left += deltaX; // small_circle2.left += deltaX; // small_circle1.top += deltaY; // small_circle2.top += deltaY; line_part1.line.left += deltaX; line_part1.line.top += deltaY; line_part2.line.left += deltaX; line_part2.line.top += deltaY; moveLine(e, line_part1.line); moveLine(e, line_part2.line); }) rect.on('mousedown', function () { rect.oldX = rect.left; rect.oldY = rect.top; }) rect.line_part1.on('mouseover', function () { this.hoverCursor = "no-drop"; }) rect.line_part2.on('mouseover', function () { this.hoverCursor = "no-drop"; }) // эти три обработчика нужны для копирования, потому что круги копируются поверх квадрата // circle.on('mousedown:before', function () { // canvas.bringToFront(this.parent.rect).renderAll(); // }) // small_circle1.on('mousedown:before', function () { // canvas.bringToFront(this.parent.rect).renderAll(); // }) // small_circle2.on('mousedown:before', function () { // canvas.bringToFront(this.parent.rect).renderAll(); // }) } //////////////////////////// Legend //////////////////////////// function add_legend_back(obj){ let rect; if(obj != undefined){ rect = obj; } else{ rect = editor_obj.add_rect(null, false, canvas.width - 210, canvas.height - 160, 200, 150, "#eee", "gray", "back", true); canvas.add(rect); rect.rx = 10; rect.ry = 10; } rect.originX = 'left'; rect.originY = 'top'; rect.oldX = rect.left; rect.oldY = rect.top; rect.selectable = false; rect.hasControls = true; rect.lockRotation = true; rect.parent = rect; canvas.on('mouse:down', reCalculate); rect.on('moving', reCalculate); rect.on('scaling', reCalculate); function reCalculate(e) { // не даем уменьшить легенду, если ее ширина меньше самого длинного айтема // if(rect.width * rect.scaleX < (legend_back.margin + biggest_legend_item_width) || canvas.getPointer(e.e).x < rect.left && // (legend_back.__corner == "mr" || legend_back.__corner == "tr" || legend_back.__corner == "br" ) ){ //////////// top - bottom /////////////// if( (rect.height * rect.scaleY < text_height || canvas.getPointer(e.e).y > rect.top + text_height) && (legend_back.__corner == "mt" || legend_back.__corner == "tl" || legend_back.__corner == "tr" ) ){ var t = canvas.viewportTransform; rect.scaleY = text_height / legend_back.height; rect.top = Math.round((rect.oldrY - t[5]) / t[3]) - text_height - rect.strokeWidth; } if( (rect.height * rect.scaleY < text_height || canvas.getPointer(e.e).y < rect.top) && (legend_back.__corner == "mb" || legend_back.__corner == "bl" || legend_back.__corner == "br" ) ){ rect.scaleY = text_height / legend_back.height; rect.top = rect.oldY; } //////////// left - right //////////// if( (rect.width * rect.scaleX < (legend_back.margin + biggest_legend_item_width) || canvas.getPointer(e.e).x > rect.left + biggest_legend_item_width) && (legend_back.__corner == "ml" || legend_back.__corner == "tl" || legend_back.__corner == "bl" ) ){ var t = canvas.viewportTransform; rect.scaleX = 1; rect.left = Math.round((rect.oldrX - t[4]) / t[0]) - rect.oldWidth - rect.strokeWidth; canvas.renderAll(); } if( (rect.width * rect.scaleX < (legend_back.margin + biggest_legend_item_width) || canvas.getPointer(e.e).x < rect.left) && (legend_back.__corner == "mr" || legend_back.__corner == "tr" || legend_back.__corner == "br" ) ){ rect.scaleX = 1; rect.left = rect.oldX; canvas.renderAll(); } canvas.bringToFront(rect).renderAll(); let deltaX = rect.left - rect.oldX; let deltaY = rect.top - rect.oldY; rect.oldX = rect.left; rect.oldrX = rect.oCoords.tr.x; rect.oldY = rect.top; rect.oldrY = rect.oCoords.br.y; rect.oldWidth = rect.width; let array = canvas.getObjects(); for(var i in array){ let shape_type = /legend.*/.test(array[i].groupName); if(shape_type){ array[i].left += deltaX; array[i].top += deltaY; canvas.bringToFront(array[i]); } } rect.rx = rect.border_radius * (1 / rect.scaleX); rect.ry = rect.border_radius * (1 / rect.scaleY); } rect.on('mousedown', function (e) { canvas.preserveObjectStacking = true; rect.hasControls = true; canvas.setActiveObject(rect); }) rect.on('mouseover', function () { rect.selectable = true; }) rect.on('mouseout', function () { editor_obj.add_legend(); }) canvas.on('mouse:down:before', function (e) { canvas.preserveObjectStacking = true; let array = canvas.getObjects(); for(var i in array){ let shape_type = /legend.*/.test(array[i].groupName); if(shape_type && array[i].type == "i-text" && canvas.getPointer(e.e).x > array[i].left && canvas.getPointer(e.e).x < array[i].left + array[i].width && canvas.getPointer(e.e).y > array[i].top && canvas.getPointer(e.e).y < array[i].top + array[i].height){ canvas.setActiveObject(array[i]); array[i].enterEditing(); } } }) legend_back = rect; legend_back.border_radius = 10; } // Общие функции function stick(e, obj){ for(key in stick_array){ if(obj.groupName != key){ for(var n = 0; n <= Object.keys(stick_array[key]).length / 2; n++){ if(obj.name != "line_part1" && obj.name != "line_part2"){ obj_x = obj.get('left'); obj_y = obj.get('top'); } else{ obj_x = obj.get('x1'); obj_y = obj.get('y1'); } let stick_distance_line = { x1: obj_x, y1: obj_y, x2: stick_array[key]["x" + n], y2: stick_array[key]["y" + n], }; if(Math.abs(calcLineLength(stick_distance_line)) < 20){ var stick_x = stick_distance_line["x2"]; var stick_y = stick_distance_line["y2"]; } } } } if(obj.name != "line_part1" && obj.name != "line_part2" ){ if(obj.parent.name == "rect"){ var opposite_point =obj.parent; } else{ if(obj.pointType == "arrow_start"){ var opposite_point = obj.parent.arrow2; } if(obj.pointType == "arrow_end"){ var opposite_point = obj.parent.arrow; } } var angle = calcArrowAngle(canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, opposite_point.left, opposite_point.top, false); var gap = 5; var katet = obj.line.length; var cross = false; if(angle <= 0 + gap && angle >= 0 || angle >= 360 - gap && angle <= 360){ cross = "horizontal"; var stick_x = opposite_point.left - katet * Math.cos(0 * Math.PI / 180); var stick_y = opposite_point.top; } if(angle <= 180 + gap && angle >= 180 - gap){ cross = "horizontal"; var stick_x = opposite_point.left - katet * Math.cos(180 * Math.PI / 180); var stick_y = opposite_point.top; } if(angle <= 90 + gap && angle >= 90 - gap){ cross = "vertical"; var stick_x = opposite_point.left; var stick_y = opposite_point.top - katet * Math.sin(90 * Math.PI / 180); } if(angle <= 270 + gap && angle >= 270 - gap){ cross = "vertical"; var stick_x = opposite_point.left; var stick_y = opposite_point.top - katet * Math.sin(270 * Math.PI / 180); } var ctx = canvas.contextTop; ctx.strokeStyle = "lime"; ctx.beginPath(); ctx.setLineDash([5, 5]); if(cross == "horizontal"){ var transform = fabric.util.transformPoint(new fabric.Point(canvas.vptCoords.tr.x, stick_y), canvas.viewportTransform); ctx.moveTo(transform.x, transform.y); ctx.lineTo(0, transform.y); ctx.stroke(); } else if(cross == "vertical"){ var transform = fabric.util.transformPoint(new fabric.Point(stick_x, canvas.vptCoords.br.y), canvas.viewportTransform); ctx.moveTo(transform.x, transform.y); ctx.lineTo(transform.x, 0); ctx.stroke(); } else{ ctx.clearRect(0, 0, canvas.width, canvas.height); } } if(typeof stick_x == "number"){ obj.set('left', stick_x); obj.set('top', stick_y); if(obj.pointType == "arrow_start"){ obj.line.set('x1', stick_x); obj.line.set('y1', stick_y); } } } function moveEnd(e, obj, cap_flag = false) { if(btn_stick.classList.contains("top-menu-button-selected")){ stick(e, obj); var stick_flag = true; } else{ var stick_flag = false; } if(obj.name == "arrow" || obj.name == "arrow_part1" || obj.name == "line_part1"){ obj.line.oldX = obj.line.x1; obj.line.oldY = obj.line.y1; if(obj.parent.groupType == "geometry"){ obj.line.old_fake_X = obj.parent.line_part1.x1; obj.line.old_fake_Y = obj.parent.line_part1.y1; } } else{ // obj.fill = "blue"; obj.line.oldX = obj.line.x2; obj.line.oldY = obj.line.y2; if(obj.parent.groupType == "geometry"){ obj.line.old_fake_X = obj.parent.line_part2.x1; obj.line.old_fake_Y = obj.parent.line_part2.y1; } } if(obj.name == "arrow" || obj.name == "arrow2" || obj.name == "arrow_part1" || obj.name == "arrow_part2"){ if(!obj.parent.border){ obj.line.set('x1', obj.arrow.get('left')); obj.line.set('y1', obj.arrow.get('top')); if(obj.arrow2 != undefined){ obj.line.set('x2', obj.arrow2.get('left')); obj.line.set('y2', obj.arrow2.get('top')); } else{ obj.line.set('x2', obj.parent.get('left')); obj.line.set('y2', obj.parent.get('top')); } } textRotate(obj); var x1, y1, x2, y2; var side_line1, side_line2; if(obj.name == "arrow"){ side_line1 = obj.parent.line_part1; side_line2 = obj.parent.line_part2; var param1 = "1"; var param2 = "2"; var param3 = "2"; } else if(obj.name == "arrow2"){ side_line1 = obj.parent.line_part2; side_line2 = obj.parent.line_part1; var param1 = "2"; var param2 = "1"; var param3 = ""; } if(side_line2 != undefined){ // объект geometry var diag_angle_new = calcArrowAngle(canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, side_line2.x1, side_line2.y1, true); ////////// var r2 = obj.parent.diag = calcLineLength({x1: side_line2.x1, y1: side_line2.y1, x2: canvas.getPointer(e.e).x, y2: canvas.getPointer(e.e).y}); obj.parent.old_diag_angle = diag_angle_new; var katet2 = side_line2.length; // угол между курсором (стрелкой основной линии) и нижней точкой боковой линии var temp_angle = calcArrowAngle(canvas.getPointer(e.e).x, canvas.getPointer(e.e).y, side_line1.x1, side_line1.y1, true); var rotate_angle = Math.PI/2 - temp_angle + diag_angle_new; // угол, нак который поворачивается // координатная плоскость, угол между диагональю и линией, // проходящей мужду двух нижних точек боковых линий (фактически основание прямоугольника фигуры geometry) // x3 показывает, слева или справа находится x в координатной плоскости центром которой является точка, за которую тянем, // если x3 слева от x4, то мы поворачивали фигуру, а не перетаскивали курсор так, чтобы она зеркально отразилась // иначе говоря, x3 это проекция точки курсора на линию, проходящую мужду двух нижних точек боковых линий, которая становится осью Х var luft = 50; if(obj.parent.opposite == false){ luft = -50; } let x3 = side_line2.x1 - r2 * Math.cos(rotate_angle) + luft; let y3 = side_line2.y1 + r2 * Math.cos(Math.PI - rotate_angle); let x4 = side_line2.x1; let y4 = side_line2.y1; var ctx = canvas.upperCanvasEl.getContext('2d'); ctx.fillStyle = "lime"; // var ctx = canvas.upperCanvasEl.getContext('2d'); // ctx.fillStyle = "orange"; // ctx.clearRect(0, 0, canvas.width, canvas.height); // var transform = fabric.util.transformPoint(new fabric.Point(x3, 300), canvas.viewportTransform); // ctx.fillRect(transform.x, transform.y, 10, 10); // obj.parent.opposite — если true, это означает, что мы переместили край, за который тянем, за точку, // из которой начали рисовать var hyp = Math.sqrt(Math.pow(r2, 2) - Math.pow(katet2, 2)); if(hyp <= 150 || isNaN(hyp)){ if(!obj.parent.border){ side_line1.start = { x: obj.left, y: obj.top } } obj.parent.border = true; // console.log(obj.parent.border); } else{ obj.parent.border = false; } if(x3 < x4 && y3 < y4){ obj.parent.opposite = true; // console.log("слева"); } else{ obj.parent.opposite = false; // console.log("справа"); } if(obj.parent.border){ let beta = calcArrowAngle(obj.line.get("x" + param2), obj.line.get("y" + param2), side_line1.start.x, side_line1.start.y, true); // console.log(beta * 180 / Math.PI); var x0 = side_line1.start.x; var y0 = side_line1.start.y; var xr = canvas.getPointer(e.e).x; var yr = y0 - (x0 - xr) * Math.tan(beta); obj.left = xr; obj.top = yr; obj.line.set("x" + param1, obj.get('left')); obj.line.set("y" + param1, obj.get('top')); } // в некотором диапазоне от начальной точки меняем формулу, но не меняем obj.parent.opposite. Также меняем // формулу, если obj.parent.opposite равен true if(!obj.parent.opposite || (!obj.parent.opposite && !obj.parent.added)){ var signX = 1; var signY = 1; var ugol2 = diag_angle_new - Math.acos(katet2 / r2); } else{ var signX = -1; var signY = 1; var ugol2 = Math.PI - (diag_angle_new + Math.acos(katet2 / r2)); } // если больше определенной длины линии между курсором и верхней точкой боковой линии if(Math.sqrt(Math.pow(r2, 2) - Math.pow(katet2, 2)) >= 5){ // side_line2 изменяется и определен выше // тут рисуем точку основной линии, противоположной той, со стороны которй находится стрелка, за которую тянем // x1 — расчетная нижняя точка боковой линии // x2 — расчетная верхняя точка боковой линии x2 = side_line2.x1 - signX * katet2 * Math.cos(ugol2); y2 = side_line2.y1 - signY * katet2 * Math.sin(ugol2); if(!obj.parent.border){ obj.line.set("x" + param2, x2); obj.line.set("y" + param2, y2); } obj.lockMovementX = false; obj.lockMovementY = false; } else{ obj.lockMovementX = true; obj.lockMovementY = true; obj.line.set("x" + param1, obj.line["lastX" + param1]); obj.line.set("y" + param1, obj.line["lastY" + param1]); obj.set('left', obj.line.get("x" + param1)); obj.set('top', obj.line.get("y" + param1)); } } else{ // если не объект geometry var coords; if(obj.arrow2){ var angle = calcArrowAngle(obj.arrow.get('left'), obj.arrow.get('top'), obj.arrow2.get('left'), obj.arrow2.get('top'), true); } else{ var angle = calcArrowAngle(obj.arrow.get('left'), obj.arrow.get('top'), obj.line.get('x2'), obj.line.get('y2'), true); } } obj.line._setWidthHeight(); var key = obj.groupName; stick_array[key] = {}; let param = text2Angle(obj.line.get('x1'), obj.line.get('y1'), obj.line.get('x2'), obj.line.get('y2')); x0 = stick_array[key].x0 = param.x; x0 = stick_array[key].y0 = param.y; x1 = stick_array[key].x1 = obj.line.get('x1'); y1 = stick_array[key].y1 = obj.line.get('y1'); x2 = stick_array[key].x2 = obj.line.get('x2'); y2 = stick_array[key].y2 = obj.line.get('y2'); // var t = canvas.viewportTransform; // canvas.contextTop.fillRect(t[0]*x0+t[4], t[3]*y0+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x1+t[4], t[3]*y1+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x2+t[4], t[3]*y2+t[5], 10, 10) if(obj.parent.groupType == "geometry"){ x3 = stick_array[key].x3 = obj.parent.line_part1.get('x1'); y3 = stick_array[key].y3 = obj.parent.line_part1.get('y1'); x4 = stick_array[key].x4 = obj.parent.line_part2.get('x1'); y4 = stick_array[key].y4 = obj.parent.line_part2.get('y1'); param = text2Angle(obj.parent.line_part1.get('x1'), obj.parent.line_part1.get('y1'), obj.parent.line.get('x1'), obj.parent.line.get('y1')); x5 = stick_array[key].x5 = param.x; y5 = stick_array[key].y5 = param.y; param = text2Angle(obj.parent.line_part2.get('x1'), obj.parent.line_part2.get('y1'), obj.parent.line.get('x2'), obj.parent.line.get('y2')); x6 = stick_array[key].x6 = param.x; y6 = stick_array[key].y6 = param.y; // var t = canvas.viewportTransform; // canvas.contextTop.fillRect(t[0]*x0+t[4], t[3]*y0+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x1+t[4], t[3]*y1+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x2+t[4], t[3]*y2+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x3+t[4], t[3]*y3+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x4+t[4], t[3]*y4+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x5+t[4], t[3]*y5+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x6+t[4], t[3]*y6+t[5], 10, 10) } if(obj.parent.groupType == "socket"){ if(obj.name == "arrow_part1"){ param = text2Angle(obj.parent.line_part2.get('x1'), obj.parent.line_part2.get('y1'), obj.parent.line_part2.get('x2'), obj.parent.line_part2.get('y2')); x3 = stick_array[key].x3 = param.x; y3 = stick_array[key].y3 = param.y; x4 = stick_array[key].x4 = obj.parent.line_part2.get('x1'); y4 = stick_array[key].y4 = obj.parent.line_part2.get('y1'); } else{ param = text2Angle(obj.parent.line_part1.get('x1'), obj.parent.line_part1.get('y1'), obj.parent.line_part1.get('x2'), obj.parent.line_part1.get('y2')); x3 = stick_array[key].x3 = param.x; y3 = stick_array[key].y3 = param.y; x4 = stick_array[key].x4 = obj.parent.line_part1.get('x1'); y4 = stick_array[key].y4 = obj.parent.line_part1.get('y1'); } // var t = canvas.viewportTransform; // canvas.contextTop.fillRect(t[0]*x0+t[4], t[3]*y0+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x1+t[4], t[3]*y1+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x2+t[4], t[3]*y2+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x3+t[4], t[3]*y3+t[5], 10, 10) // canvas.contextTop.fillRect(t[0]*x4+t[4], t[3]*y4+t[5], 10, 10) } if(typeof angle == "undefined"){ var angle = calcArrowAngle(x1, y1, x2, y2); } else{ angle = angle * 180 / Math.PI; } if(obj.parent.groupType != "cap"){ obj.arrow.set('angle', angle); } if(obj.arrow2 != undefined && obj.type != "circle"){ obj.arrow2.set('angle', angle); } obj.line.setCoords(); if(obj.parent.groupType == "cap" && !cap_flag){ if(Math.sign(Math.cos(angle * Math.PI/180))>0){ obj.arrow.originX = 'right'; } else{ obj.arrow.originX = 'left'; } } if(obj.parent.groupType == "geometry" && ugol2 != NaN){ let lines_coords = calcLineAngle(obj, e); if(obj.name == "arrow" || obj.name == "arrow2"){ let rotate_obj = obj.parent.line_part2; if(!obj.parent.added){ // фигура geometry не создана, растягивается бъект parent r2 = rotate_obj.length; x2 = rotate_obj.x1 - 1 * r2 * Math.cos(ugol2); y2 = rotate_obj.y1 - 1 * r2 * Math.sin(ugol2); obj.parent.line_part1.set('x1', lines_coords.x1); obj.parent.line_part1.set('y1', lines_coords.y1); obj.parent.line_part2.set('x2', x2); obj.parent.line_part2.set('y2', y2); obj.parent.arrow2.set('left', x2); obj.parent.arrow2.set('top', y2); obj.parent.arrow_part1.set('left', lines_coords.x1); obj.parent.arrow_part1.set('top', lines_coords.y1); obj.parent.arrow_part2.set('left', lines_coords.x2); obj.parent.arrow_part2.set('top', lines_coords.y2); obj.parent.arrow_part1.setCoords(); obj.parent.arrow_part2.setCoords(); obj.parent.line_part1.length = obj.parent.line_part2.length = calcLineLength({x1: obj.parent.line_part2.x1, y1: obj.parent.line_part2.y1, x2: obj.parent.line_part2.x2, y2: obj.parent.line_part2.y2}); } else{ // нижняя точка боковой линии side_line1.set('x1', lines_coords["x" + param1]); side_line1.set('y1', lines_coords["y" + param1]); // элемент arrow (тут пустой круг), нижняя точка боковой линии obj.parent.arrow_part1.set('left', lines_coords.x1); obj.parent.arrow_part1.set('top', lines_coords.y1); // элемент arrow (тут пустой круг), нижняя точка боковой линии obj.parent.arrow_part2.set('left', lines_coords.x2); obj.parent.arrow_part2.set('top', lines_coords.y2); } } textRotate(obj.parent); // Если тянем за стрелки if(canvas.getActiveObject()){ var active_obj = canvas.getActiveObject().name; if(active_obj != "line_part1" && active_obj != "line_part2"){ // верхняя точка боковой линии у той стрелки, за которую тянем side_line1.set('x2', obj.parent["x" + param1]); side_line1.set('y2', obj.parent["y" + param1]); // верхняя точка противоположной боковой линии var side_ang = calcArrowAngle(side_line1.x1, side_line1.y1, side_line1.x2, side_line1.y2, true) var side_top_x = side_line2.x1 + 1 * side_line1.length * Math.cos(side_ang); var side_top_y = side_line2.y1 + 1 * side_line1.length * Math.sin(side_ang); side_line2.set('x2', side_top_x); side_line2.set('y2', side_top_y); if(obj.parent.groupType == "geometry"){ if(!obj.parent.border && obj.parent.added){ obj.line.set("x" + param2, side_top_x); obj.line.set("y" + param2, side_top_y); } obj.parent["arrow" + param3].set('left', obj.parent["x" + param2]); obj.parent["arrow" + param3].set('top', obj.parent["y" + param2]); } } // if(stick_flag){ // // stick(e, side_line1); // // stick(e, side_line2); // } } if(testgo){ side_line1.set('x2', obj.parent["x" + param1]); side_line1.set('y2', obj.parent["y" + param1]); side_line2.set('x2', obj.parent["x" + param2]); side_line2.set('y2', obj.parent["y" + param2]); } } } obj.line.lastX1 = obj.line.x1; obj.line.lastX2 = obj.line.x2; obj.line.lastY1 = obj.line.y1; obj.line.lastY2 = obj.line.y2; if(!obj.parent.added){ //иначе при создании стрелки рисуются с опозданием canvas.renderAll(); } } function moveLine(e, line) { if(line.parent.arrow_part1 != undefined && line.parent.groupType != "socket"){ // Объет geometry if(line.name != "line_part1" && line.name != "line_part2"){ line.parent.line_part1.length = line.parent.line_part2.length = calcLineLength({x1: line.parent.line_part1.x1, y1: line.parent.line_part1.y1, x2: line.parent.line_part1.x2, y2: line.parent.line_part1.y2}); } let x0 = line.first_click.x; let y0 = line.first_click.y; let beta = calcArrowAngle(line.parent.line.x1, line.parent.line.y1, line.parent.line.x2, line.parent.line.y2, true); // let alpha = beta + Math.PI/2; if ((beta > Math.PI / 2 - 0.1 && beta < Math.PI / 2 + 0.1) || (beta > Math.PI * 3 / 2 - 0.1 && beta < Math.PI * 3 / 2 + 0.1)){ var x = canvas.getPointer(e.e).x; var y = y0 - (x - x0) * 1 / Math.tan(beta); } else{ var y = canvas.getPointer(e.e).y; var x = x0 - (y - y0) * Math.tan(beta); } var delta_first_clickX = x - x0; var delta_first_clickY = y - y0; if(line.name != "line_part1" && line.name != "line_part2"){ line.set({ 'left': line.first_center.x + delta_first_clickX, 'top': line.first_center.y + delta_first_clickY }); // canvas.renderAll(); } if(line == line.parent){ if(Math.sign(line.parent.line_part1.y2 - line.parent.line_part1.y1) < 0){ line.parent.text2.originY = 'bottom'; if(beta * 180 / Math.PI >= 90 && beta * 180 / Math.PI <= 270){ line.parent.direction = 1; // устанавливаем направление относительно начального положения — требуется, // чтобы понять, с какой стороны от основной линии рисовать боковые линии } else{ line.parent.direction = -1; // устанавливаем направлени относительно начального положенияе — требуется, // чтобы понять, с какой стороны от основной линии рисовать боковые линии } } else{ line.parent.text2.originY = 'top'; if(beta * 180 / Math.PI >= 90 && beta * 180 / Math.PI <= 270){ line.parent.direction = -1; } else{ line.parent.direction = 1; } } } if(line.name != "line_part2"){ line.parent.line_part2.set('x2', line.parent.x2); line.parent.line_part2.set('y2', line.parent.y2); line.parent.line_part2.setCoords(); } if(line.name != "line_part1"){ line.parent.line_part1.set('x2', line.parent.x1); line.parent.line_part1.set('y2', line.parent.y1); line.parent.line_part1.setCoords(); } if(line.name == "line_part1"){ line.parent.arrow.set('left', line.parent.line_part1.x2); line.parent.arrow.set('top', line.parent.line_part1.y2); line.parent.arrow_part1.set('left', line.parent.line_part1.x1); line.parent.arrow_part1.set('top', line.parent.line_part1.y1); } if(line.name == "line_part2"){ line.parent.arrow2.set('left', line.parent.line_part2.x2); line.parent.arrow2.set('top', line.parent.line_part2.y2); line.parent.arrow_part2.set('left', line.parent.line_part2.x1); line.parent.arrow_part2.set('top', line.parent.line_part2.y1); } if(line.name != "line_part1" && line.name != "line_part2"){ line.parent.diag_length = calcLineLength({x1: line.parent.line_part2.x1, y1: line.parent.line_part2.y1, x2: line.line.x1, y2: line.line.y1}); } } line.setCoords(); angle = calcArrowAngle(line.x1, line.y1, line.x2, line.y2); var oldCenterX = (line.x1 + line.x2) / 2, oldCenterY = (line.y1 + line.y2) / 2, deltaX = Math.round(line.left - oldCenterX), deltaY = Math.round(line.top - oldCenterY); line.arrow.set({ 'left': line.x1 + deltaX, 'top': line.y1 + deltaY }).setCoords(); if(line.arrow2 != undefined){ line.arrow2.set({ 'left': line.x2 + deltaX, 'top': line.y2 + deltaY }).setCoords(); } line.set({ 'x1': line.x1 + deltaX, 'y1': line.y1 + deltaY, 'x2': line.x2 + deltaX, 'y2': line.y2 + deltaY }); line.set({ 'left': (line.x1 + line.x2) / 2, 'top': (line.y1 + line.y2) / 2 }); if(line.text2 != undefined && line.parent.groupType != "cap"){ textRotate(line); } var key = line.groupName; if(stick_array[key]){ stick_array[key].x1 = line.get('x1'); stick_array[key].y1 = line.get('y1'); stick_array[key].x2 = line.get('x2'); stick_array[key].y2 = line.get('y2'); } if(line.parent.groupType == "geometry"){ // Иначе стрелки рисуются с опозданием canvas.renderAll(); } } function calcLineLength(line){ let a = line.x2 - line.x1; let b = line.y2 - line.y1; let length = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)); return length; } function calcLineAngle(obj, e){ // здесь высчитываются координаты концов боковых линий в зависимости от угла основной линии к горизонтали if(obj.angleDelta == undefined){ obj.angleDelta = 0; } var beta = calcArrowAngle(obj.parent.x1, obj.parent.y1, obj.parent.x2, obj.parent.y2, true); if(obj.name == "arrow" || obj.name == "arrow_part1" || obj.name == "line_part1"){ var line_main = { x1: obj.parent.line.x1, y1: obj.parent.line.y1, x2: obj.parent.line.x2, y2: obj.parent.line.y2 } } else{ var line_main = { x1: obj.parent.line.x2, y1: obj.parent.line.y2, x2: obj.parent.line.x1, y2: obj.parent.line.y1 } } var beta_deg = beta * 180 / Math.PI; if((beta_deg > 135 && beta_deg < 225) || (beta_deg > 315 && beta_deg < 360) || (beta_deg < 45)){ //console.log("Под 45!"); if(Math.sign(line_main.x2 - obj.parent.line.oldX) == -Math.sign(line_main.x2 - line_main.x1)){ //console.log("Сменили знак!"); if(obj.angleDelta == 0){ obj.angleDelta = Math.PI; } else{ obj.angleDelta = 0; } } } else{ if(Math.sign(line_main.y2 - obj.parent.line.oldY) == -Math.sign(line_main.y2 - line_main.y1)){ if(obj.angleDelta == 0){ obj.angleDelta = Math.PI; } else{ obj.angleDelta = 0; } beta += Math.PI; } } beta += obj.angleDelta; // угол основной линии //console.log(beta * 180 / Math.PI); // боковая линия рисуется под углом девянсото градусов к основной с учетом направления: if(obj.name == "arrow" || obj.name == "line_part1"){ // в зависимости от того, за какую часть деформируем if(obj.parent.direction == -1){ var alpha = beta + Math.PI/2; } else{ var alpha = beta - Math.PI/2; } } if(obj.name == "arrow2" || obj.name == "line_part2"){ // в зависимости от того, за какую часть деформируем if(obj.parent.direction == 1){ var alpha = beta - Math.PI/2; } else{ var alpha = beta + Math.PI/2; } } let r2 = obj.parent.line_part2.length; // длина боковой линии if(obj.name == "arrow" || obj.name == "arrow2"){ sign = 1; } else{ sign = -1; } if(obj.name == "arrow" || obj.name == "arrow2"){ var x0 = obj.parent.x1; var y0 = obj.parent.y1; } else{ var x0 = obj.parent.line_part1.x1; var y0 = obj.parent.line_part1.y1; } // нижняя точка первой линии let x1 = x0 + sign * r2 * Math.cos(alpha); let y1 = y0 + sign * r2 * Math.sin(alpha); if(obj.name == "arrow" || obj.name == "arrow2"){ var x0 = obj.parent.x2; var y0 = obj.parent.y2; } else{ var x0 = obj.parent.line_part2.x1; var y0 = obj.parent.line_part2.y1; } // нижняя точка второй линии let x2 = x0 + sign * r2 * Math.cos(alpha); let y2 = y0 + sign * r2 * Math.sin(alpha); return {x1: x1, y1: y1, x2: x2, y2: y2, parent_ang: beta_deg}; } function calcArrowAngle(x1, y1, x2, y2, rad = false) { let r = calcLineLength({x1: x1, y1: y1, x2: x2, y2: y2}); // длинна линии let beta = Math.acos((x2 - x1) / r); // угол между линией и осью абсцисс beta = beta + (Math.PI - beta) * 2 * -1 * Math.sign(-1 + Math.sign(y2 - y1)); let angle = beta * 180 / Math.PI; if(rad){ return beta; } else{ return angle; } } function textRotate(obj){ if(obj.text2 != undefined && obj.parent.groupType != "cap"){ let param = text2Angle(obj.line.x1, obj.line.y1, obj.line.x2, obj.line.y2); let alpha = param.ang; if(alpha != 360){ obj.text2.angle = alpha - 180*Math.floor(Math.cos(alpha*Math.PI/180)); } else{ obj.text2.angle = alpha; } obj.text2.setCoords(); // центрирование текста obj.text2.left = param.x - (obj.text2.width/2)*Math.cos(alpha*Math.PI/180)*Math.sign(Math.cos(alpha*Math.PI/180)); obj.text2.top = param.y - (obj.text2.width/2)*Math.sin(alpha*Math.PI/180)*Math.sign(Math.cos(alpha*Math.PI/180)); } } function text2Angle(x1, y1, x2, y2){ let ang = calcArrowAngle(x1, y1, x2, y2); let x3 = (x2 - x1) / 2 + x1; let y3 = (y2 - y1) / 2 + y1; let coords = { ang: ang, x: x3, y: y3 }; return coords; } function getRadiusCoords(x0 = 0, y0 = 0, radius = 10, angle){ // x = Math.ceil(x0 + radius * Math.cos(angle)); // y = Math.ceil(y0 + radius * Math.sin(angle)); var x = x0 + radius * Math.cos(angle); var y = y0 + radius * Math.sin(angle); return {x: x, y: y, angle: angle} } function gof(){ // shape[0].line.set("y1", shape[0].line.y2); shape[0].line.set("x2", 300); shape[0].line.set("y2", 200); shape[0].line.set("x1", 600); shape[0].line.set("y1", 200); shape[0].line_part1.set("y1", shape[0].line.y1); shape[0].line_part1.set("x1", shape[0].line.x1); shape[0].line_part1.set("x2", shape[0].line.x1); shape[0].line_part1.set("y2", shape[0].line.y1 + 100); shape[0].line_part2.set("y1", shape[0].line.y2); shape[0].line_part2.set("x1", shape[0].line.x2); shape[0].line_part2.set("x2", shape[0].line.x2); shape[0].line_part2.set("y2", shape[0].line.y2 + 100); shape[0].arrow.set("left", shape[0].line.x1); shape[0].arrow2.set("left", shape[0].line.x2); shape[0].arrow.set("top", shape[0].line.y1); shape[0].arrow2.set("top", shape[0].line.y2); canvas.renderAll(); } var shag = -60; var testgo = false; function go(){ testgo = true; shape[0].line.arrow2.set("left", shape[0].line.arrow2.left - shag); moveEnd({e: {clientX: shape[0].line.arrow2.left, clientY: shape[0].line.arrow2.top}}, shape[0].line.arrow2); // testgo = true; // shape[1].line_part1.arrow.set("left", shape[1].line_part1.arrow.left - shag); // moveEnd({pointer: {x: shape[1].line_part1.arrow.left, y: shape[1].line_part1.arrow.top}}, shape[1].line_part1.arrow); canvas.renderAll() } function moveByStep(stepX, stepY){ item = canvas.getActiveObject(); var move_obj; if(item){ if(group.cloneOne){ // __onMouseDown в fabric.js, там установлен getActiveObject().setCoords() let item = canvas.getActiveObject(); editor_obj.setClone(item); canvas.renderAll(); element_cursor(); } else{ oldParam(item); } if( (item.parent.objects || item.parent._objects != undefined) ){ // если не примтив, //проходимся по всем частям составного объекта if(item.parent.objects != undefined){ for(let i of item.parent.objects){ move_obj = item.parent[i]; move_obj.left += stepX; move_obj.top += stepY; } } if(item.parent._objects != undefined){ // выделение или группа и не часть другого объекта for(let obj of item.parent._objects){ move_obj = obj; move_obj.left += stepX; move_obj.top += stepY; } } }else{ move_obj = item; move_obj.left += stepX; move_obj.top += stepY; } changes(item, "12"); poupup.classList.add("mfp-hide"); var ctx = canvas.contextTop; ctx.clearRect(0, 0, canvas.width, canvas.height); canvas.renderAll(); } }