Commit 172686cc by jlc

update:节点可旋转

parent fff3af96
/*
* Copyright (C) 1998-2024 by Northwoods Software Corporation. All Rights Reserved.
*/
import * as go from 'gojs';
/**
* The RotateMultipleTool class lets the user rotate multiple objects at a time.
* When more than one part is selected, rotates all parts, revolving them about their collective center.
* If the control key is held down during rotation, rotates all parts individually.
*
* Caution: this only works for Groups that do *not* have a Placeholder.
*
* If you want to experiment with this extension, try the <a href="../../samples/RotateMultiple.html">Rotate Multiple</a> sample.
* @category Tool Extension
*/
export class RotateMultipleTool extends go.RotatingTool {
/**
* Holds references to all selected non-Link Parts and their offset & angles
*/
private _initialInfo: go.Map<go.Part, PartInfo> | null;
/**
* Initial angle when rotating as a whole
*/
private _initialAngle: number;
/**
* Rotation point of selection
*/
private _centerPoint: go.Point;
/**
* Constructs a RotateMultipleTool and sets the name for the tool.
*/
constructor(init?: Partial<RotateMultipleTool>) {
super();
this.name = 'RotateMultiple';
this._initialInfo = null;
this._initialAngle = 0;
this._centerPoint = new go.Point();
if (init) Object.assign(this, init);
}
/**
* Calls {@link go.RotatingTool.doActivate}, and then remembers the center point of the collection,
* and the initial distances and angles of selected parts to the center.
*/
override doActivate(): void {
super.doActivate();
const diagram = this.diagram;
// center point of the collection
this._centerPoint = diagram.computePartsBounds(diagram.selection).center;
// remember the angle relative to the center point when rotating the whole collection
this._initialAngle = this._centerPoint.directionPoint(diagram.lastInput.documentPoint);
// remember initial angle and distance for each Part
const infos = new go.Map<go.Part, PartInfo>();
const tool = this;
diagram.selection.each((part) => {
tool.walkTree(part, infos);
});
this._initialInfo = infos;
// forget the rotationPoint since we use _centerPoint instead
this.rotationPoint = new go.Point(NaN, NaN);
}
/**
* @hidden @internal
*/
private walkTree(part: go.Part, infos: go.Map<go.Part, PartInfo>): void {
if (part === null || part instanceof go.Link) return;
// distance from _centerPoint to locationSpot of part
const dist = Math.sqrt(this._centerPoint.distanceSquaredPoint(part.location));
// calculate initial relative angle
const dir = this._centerPoint.directionPoint(part.location);
// saves part-angle combination in array
infos.add(part, new PartInfo(dir, dist, part.rotateObject.angle));
// recurse into Groups
if (part instanceof go.Group) {
const it = part.memberParts.iterator;
while (it.next()) this.walkTree(it.value, infos);
}
}
/**
* Clean up any references to Parts.
*/
override doDeactivate(): void {
this._initialInfo = null;
super.doDeactivate();
}
/**
* Rotate all selected objects about their collective center.
* When the control key is held down while rotating, all selected objects are rotated individually.
*/
override rotate(newangle: number): void {
const diagram = this.diagram;
if (this._initialInfo === null) return;
const node = this.adornedObject !== null ? this.adornedObject.part : null;
if (node === null) return;
const e = diagram.lastInput;
// when rotating individual parts, remember the original angle difference
const angleDiff = newangle - node.rotateObject.angle;
const tool = this;
this._initialInfo.each((kvp) => {
const part = kvp.key;
if (part instanceof go.Link) return; // only Nodes and simple Parts
const partInfo = kvp.value;
// rotate every selected non-Link Part
// find information about the part set in RotateMultipleTool._initialInformation
if (e.control || e.meta) {
if (node === part) {
part.rotateObject.angle = newangle;
} else {
part.rotateObject.angle += angleDiff;
}
} else {
const radAngle = newangle * (Math.PI / 180); // converts the angle traveled from degrees to radians
// calculate the part's x-y location relative to the central rotation point
const offsetX = partInfo.distance * Math.cos(radAngle + partInfo.placementAngle);
const offsetY = partInfo.distance * Math.sin(radAngle + partInfo.placementAngle);
// move part
part.location = new go.Point(tool._centerPoint.x + offsetX, tool._centerPoint.y + offsetY);
// rotate part
part.rotateObject.angle = partInfo.rotationAngle + newangle;
}
});
}
/**
* Calculate the desired angle with different rotation points,
* depending on whether we are rotating the whole selection as one, or Parts individually.
* @param newPoint - in document coordinates
*/
override computeRotate(newPoint: go.Point): number {
const diagram = this.diagram;
if (this.adornedObject === null) return 0.0;
let angle = 0.0;
const e = diagram.lastInput;
if (e.control || e.meta) {
// relative to the center of the Node whose handle we are rotating
const part = this.adornedObject.part;
if (part !== null) {
const rotationPoint = part.getDocumentPoint(part.locationSpot);
angle = rotationPoint.directionPoint(newPoint);
}
} else {
// relative to the center of the whole selection
angle = this._centerPoint.directionPoint(newPoint) - this._initialAngle;
}
if (angle >= 360) angle -= 360;
else if (angle < 0) angle += 360;
const interval = Math.min(Math.abs(this.snapAngleMultiple), 180);
const epsilon = Math.min(Math.abs(this.snapAngleEpsilon), interval / 2);
// if it's close to a multiple of INTERVAL degrees, make it exactly so
if (!diagram.lastInput.shift && interval > 0 && epsilon > 0) {
if (angle % interval < epsilon) {
angle = Math.floor(angle / interval) * interval;
} else if (angle % interval > interval - epsilon) {
angle = (Math.floor(angle / interval) + 1) * interval;
}
if (angle >= 360) angle -= 360;
else if (angle < 0) angle += 360;
}
return angle;
}
}
/**
* Internal class to remember a Part's offset and angle.
*/
class PartInfo {
placementAngle: number;
distance: number;
rotationAngle: number;
constructor(placementAngle: number, distance: number, rotationAngle: number) {
this.placementAngle = placementAngle * (Math.PI / 180); // in radians
this.distance = distance;
this.rotationAngle = rotationAngle; // in degrees
}
}
......@@ -24,6 +24,7 @@ import { ResizeMultipleTool } from './extensions/ResizeMultipleTool';
import { LinkLabelDraggingTool } from './extensions/LinkLabelDraggingTools';
import { NodeLabelDraggingTool } from './extensions/NodeLabelDraggingTool';
import { GuidedDraggingTool } from './extensions/GuidedDraggingTool';
import { RotateMultipleTool } from './extensions/RotateMultipleTool';
const $ = go.GraphObject.make;
var myDiagram: any;
......@@ -40,6 +41,8 @@ function initDiagram() {
"draggingTool.verticalGuidelineColor": "blue", // 垂直两侧的辅助线条为蓝色
"draggingTool.centerGuidelineColor": "green", // 节点中心的水平和垂直对齐的辅助线条为绿色
"draggingTool.guidelineWidth": 1, // 设置辅助对齐的线条的粗细
// 旋转节点
rotatingTool: new RotateMultipleTool(),
});
myDiagram.toolManager.mouseMoveTools.insertAt(0, new PortShiftingTool()); // 设置端口移动
......@@ -49,7 +52,8 @@ function initDiagram() {
myDiagram.nodeTemplate =
$(go.Node, "Spot",
{
resizable: true
resizable: true,
rotatable: true
},
new go.Binding("location", "loc"),
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify), // 进行元素位置信息的绑定
......@@ -129,7 +133,8 @@ function initDiagram() {
myDiagram.nodeTemplateMap.add('zhaChi',
$(go.Node, "Spot",
{
resizable: true
resizable: true,
rotatable: true
},
new go.Binding("location", "loc"),
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify), // 进行元素位置信息的绑定
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment