Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: connectors resize #6704

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ import type {
import { NoteBlockModel } from '../../../../note-block/note-model.js';
import { normalizeTextBound } from '../../../../surface-block/canvas-renderer/element-renderer/text/utils.js';
import { TextElementModel } from '../../../../surface-block/element-model/text.js';
import type { ElementModel } from '../../../../surface-block/index.js';
import type {
ElementModel,
PointLocation,
} from '../../../../surface-block/index.js';
import {
CanvasElementType,
deserializeXYWH,
Expand Down Expand Up @@ -619,13 +622,15 @@ export class EdgelessSelectedRect extends WithDisposable(LitElement) {
string,
{
bound: Bound;
matrix?: DOMMatrix;
path?: PointLocation[];
}
>,
direction: HandleDirection
) => {
const { edgeless } = this;

newBounds.forEach(({ bound }, id) => {
newBounds.forEach(({ bound, matrix, path }, id) => {
const element = edgeless.service.getElementById(id);
if (!element) return;

Expand Down Expand Up @@ -755,9 +760,15 @@ export class EdgelessSelectedRect extends WithDisposable(LitElement) {
});
}
} else {
if (element instanceof ConnectorElementModel && path && matrix) {
ConnectorElementModel.resize(element, bound, path, matrix);
return;
}

if (element instanceof ShapeElementModel) {
bound = normalizeShapeBound(element, bound);
}

edgeless.service.updateElement(id, {
xywh: bound.serialize(),
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { assertExists } from '@blocksuite/global/utils';

import type { IPoint } from '../../../../_common/types.js';
import type { PointLocation } from '../../../../surface-block/index.js';
import {
Bound,
getQuadBoundsWithRotation,
rotatePoints,
} from '../../../../surface-block/index.js';
import { NOTE_MIN_WIDTH } from '../../utils/consts.js';
import type { SelectableBounds } from '../../utils/query.js';
import { HandleDirection, type ResizeMode } from './resize-handles.js';

// 15deg
Expand All @@ -20,6 +22,7 @@ type ResizeMoveHandler = (
string,
{
bound: Bound;
matrix?: DOMMatrix;
}
>,
direction: HandleDirection
Expand Down Expand Up @@ -62,6 +65,7 @@ export class HandleResizeManager {
{
bound: Bound;
rotate: number;
path?: PointLocation[];
}
>();

Expand Down Expand Up @@ -143,15 +147,7 @@ export class HandleResizeManager {
return this._originalRect;
}

updateBounds(
bounds: Map<
string,
{
bound: Bound;
rotate: number;
}
>
) {
updateBounds(bounds: Map<string, SelectableBounds>) {
this._bounds = bounds;
}

Expand Down Expand Up @@ -446,10 +442,12 @@ export class HandleResizeManager {
string,
{
bound: Bound;
matrix?: DOMMatrix;
path?: PointLocation[];
}
>();

let process: (value: { bound: Bound; rotate: number }, key: string) => void;
let process: (value: SelectableBounds, key: string) => void;

if (isCorner || isAll || isEdgeAndCorner) {
if (this._bounds.size === 1) {
Expand All @@ -470,7 +468,7 @@ export class HandleResizeManager {
.translateSelf(-fp.x, -fp.y);

// TODO: on same rotate
process = ({ bound: { x, y, w, h } }, id) => {
process = ({ bound: { x, y, w, h }, path }, id) => {
const cx = x + w / 2;
const cy = y + h / 2;
const center = new DOMPoint(cx, cy).matrixTransform(m2);
Expand All @@ -484,6 +482,8 @@ export class HandleResizeManager {
newWidth,
newHeight
),
matrix: m2,
path,
});
};
}
Expand All @@ -497,7 +497,7 @@ export class HandleResizeManager {
fixedPoint.y,
0
);
process = ({ bound: { x, y, w, h }, rotate = 0 }, id) => {
process = ({ bound: { x, y, w, h }, rotate = 0, path }, id) => {
const cx = x + w / 2;
const cy = y + h / 2;

Expand Down Expand Up @@ -539,6 +539,8 @@ export class HandleResizeManager {
newWidth,
newHeight
),
matrix: m2,
path,
});
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
type CanvasElement,
ConnectorElementModel,
type IVec,
type PointLocation,
} from '../../../../surface-block/index.js';
import { isConnectorAndBindingsAllSelected } from '../../../../surface-block/managers/connector-manager.js';
import type {
Expand Down Expand Up @@ -82,6 +83,7 @@ export class DefaultToolController extends EdgelessToolController<DefaultTool> {
private _isDoubleClickedOnMask = false;
private _alignBound = new Bound();
private _selectedBounds: Bound[] = [];
private _selectedPaths = new Map<string, PointLocation[]>();
private _toBeMoved: EdgelessModel[] = [];
private _autoPanTimer: number | null = null;
private _dragging = false;
Expand Down Expand Up @@ -258,7 +260,10 @@ export class DefaultToolController extends EdgelessToolController<DefaultTool> {
}

if (selected instanceof ConnectorElementModel) {
const path = this._selectedPaths.get(selected.id)!;
selected.moveTo(bound);
ConnectorElementModel.move(selected, bound, path, [delta[0], delta[1]]);
return;
}

this._service.updateElement(selected.id, {
Expand Down Expand Up @@ -601,9 +606,15 @@ export class DefaultToolController extends EdgelessToolController<DefaultTool> {
this._dragStartModelCoord = [startX, startY];
this._dragLastModelCoord = [startX, startY];

this._selectedBounds = this._toBeMoved.map(element =>
Bound.deserialize(element.xywh)
);
this._selectedBounds = this._toBeMoved.map(element => {
if (element instanceof ConnectorElementModel) {
this._selectedPaths.set(
element.id,
element.absolutePath.map(p => p.clone())
);
}
return Bound.deserialize(element.xywh);
});

this._alignBound = this._edgeless.service.snap.setupAlignables(
this._toBeMoved
Expand Down Expand Up @@ -721,6 +732,7 @@ export class DefaultToolController extends EdgelessToolController<DefaultTool> {
this._dragStartPos = [0, 0];
this._dragLastPos = [0, 0];
this._selectedBounds = [];
this._selectedPaths.clear();
this._edgeless.service.snap.cleanupAlignables();
surface.overlays.frame.clear();
this._toBeMoved = [];
Expand Down
37 changes: 20 additions & 17 deletions packages/blocks/src/root-block/edgeless/utils/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import {
type CanvasElement,
type CanvasElementWithText,
clamp,
ConnectorElementModel,
deserializeXYWH,
getQuadBoundsWithRotation,
GRID_GAP_MAX,
GRID_GAP_MIN,
type PointLocation,
ShapeElementModel,
TextElementModel,
} from '../../../surface-block/index.js';
Expand Down Expand Up @@ -261,27 +263,28 @@ export function getSelectedRect(selected: Selectable[]): DOMRect {
);
}

export function getSelectableBounds(selected: Selectable[]): Map<
string,
{
bound: Bound;
rotate: number;
}
> {
const bounds = new Map<
string,
{
bound: Bound;
rotate: number;
}
>();
export interface SelectableBounds {
bound: Bound;
rotate: number;
path?: PointLocation[];
}

export function getSelectableBounds(
selected: Selectable[]
): Map<string, SelectableBounds> {
const bounds = new Map<string, SelectableBounds>();
getElementsWithoutGroup(selected).forEach(ele => {
const bound = Bound.deserialize(ele.xywh);

bounds.set(ele.id, {
const props: SelectableBounds = {
bound,
rotate: ele.rotate,
});
};

if (ele instanceof ConnectorElementModel) {
props.path = ele.absolutePath.map(p => p.clone());
}

bounds.set(ele.id, props);
});

return bounds;
Expand Down
73 changes: 73 additions & 0 deletions packages/blocks/src/surface-block/element-model/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,79 @@ export class ConnectorElementModel extends ElementModel<ConnectorElementProps> {
}
}

static move(
connector: ConnectorElementModel,
bounds: Bound,
originalPath: PointLocation[],
delta: IVec2
) {
const { mode } = connector;
const offset = [bounds.x, bounds.y];

connector.updatingPath = true;
connector.xywh = bounds.serialize();
if (mode === ConnectorMode.Curve) {
connector.path = originalPath.map(point => {
const [p, t, absIn, absOut] = [
point,
point.tangent,
point.absIn,
point.absOut,
]
.map(p => Vec.add([p[0], p[1]], delta))
.map(p => Vec.sub([p[0], p[1]], offset));
const ip = Vec.sub(absIn, p);
const op = Vec.sub(absOut, p);
return new PointLocation(p, t, ip, op);
});
} else {
connector.path = originalPath.map(point => {
const d = Vec.add([point[0], point[1]], delta);
const o = Vec.sub(d, offset);
return PointLocation.fromVec(o);
});
}

connector.updatingPath = false;
}

static resize(
connector: ConnectorElementModel,
bounds: Bound,
originalPath: PointLocation[],
matrix: DOMMatrix
) {
const { mode } = connector;

connector.updatingPath = true;
connector.xywh = bounds.serialize();

const offset = [bounds.x, bounds.y];
if (mode === ConnectorMode.Curve) {
connector.path = originalPath.map(point => {
const [p, t, absIn, absOut] = [
point,
point.tangent,
point.absIn,
point.absOut,
]
.map(p => new DOMPoint(...p).matrixTransform(matrix))
.map(p => Vec.sub([p.x, p.y], offset));
const ip = Vec.sub(absIn, p);
const op = Vec.sub(absOut, p);
return new PointLocation(p, t, ip, op);
});
} else {
connector.path = originalPath.map(point => {
const { x, y } = new DOMPoint(...point).matrixTransform(matrix);
const p = Vec.sub([x, y], offset);
return PointLocation.fromVec(p);
});
}

connector.updatingPath = false;
}

override hitTest(
x: number,
y: number,
Expand Down
Loading