@edv4h/usketch-plugin-free-position 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +46 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +33 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +43 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EdV4H
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# @edv4h/usketch-plugin-free-position
|
|
2
|
+
|
|
3
|
+
指定した位置から**最も近い、既存 shape と被らない位置**を求める機能を提供するプラグイン(Issue #581)。
|
|
4
|
+
貼り付け/複製/ドロップ/AI 生成などで新規 shape を重ねずに配置したいときに使う。UI は持たない。
|
|
5
|
+
|
|
6
|
+
## 使い方
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { createFreePositionPlugin } from "@edv4h/usketch-plugin-free-position";
|
|
10
|
+
|
|
11
|
+
const plugins = [
|
|
12
|
+
// ...
|
|
13
|
+
createFreePositionPlugin({ strategy: "ring" }), // "ring"(既定)| "push"
|
|
14
|
+
];
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
登録すると、`free-position:find` イベントで問い合わせできる(snap の `snap:get-settings` と同じ
|
|
18
|
+
同期コールバック方式):
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import type { BoundingBox } from "@edv4h/usketch-shared";
|
|
22
|
+
|
|
23
|
+
let result: BoundingBox | undefined;
|
|
24
|
+
ctx.events.emit("free-position:find", {
|
|
25
|
+
desired: { x, y, width, height }, // 置きたい位置・サイズ
|
|
26
|
+
excludeIds: ["shape-1"], // 任意: 衝突判定から除外(移動対象自身など)
|
|
27
|
+
strategy: "push", // 任意: このリクエストだけ戦略を上書き
|
|
28
|
+
onResult: (free) => { result = free; },
|
|
29
|
+
});
|
|
30
|
+
// result が「最も近い空き位置」(同サイズ)。プラグイン未登録なら onResult は呼ばれない。
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 探索戦略
|
|
34
|
+
|
|
35
|
+
- **ring**(既定): desired 中心から同心リングを外へ広げ、衝突しない最近傍を返す。
|
|
36
|
+
- **push**: desired を起点に、重なる相手から最小重なり軸方向へ押し出して分離する。
|
|
37
|
+
|
|
38
|
+
回転した shape は `getRotatedAABB` で外接矩形にして衝突判定する。
|
|
39
|
+
|
|
40
|
+
## 連携
|
|
41
|
+
|
|
42
|
+
`@edv4h/usketch-plugin-keyboard-shortcuts` の paste/duplicate はこのイベントを使い、複数 shape を
|
|
43
|
+
相対配置を保ったままグループ単位で空き位置へずらす(本プラグイン未登録時は従来の +20 オフセット)。
|
|
44
|
+
drop など他の配置経路も同じイベントを呼べば利用できる。
|
|
45
|
+
|
|
46
|
+
純ロジックは `@edv4h/usketch-shape-utils` の `findFreePosition` / `overlapsAny` として単体でも使える。
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACxB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,wBAAwB,GAGxB,MAAM,aAAa,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type FreePositionStrategy } from "@edv4h/usketch-shape-utils";
|
|
2
|
+
import { type BoundingBox, type UsketchPlugin } from "@edv4h/usketch-shared";
|
|
3
|
+
export interface FreePositionConfig {
|
|
4
|
+
/** 既定の探索戦略("ring" | "push"、既定 "ring")。 */
|
|
5
|
+
strategy?: FreePositionStrategy;
|
|
6
|
+
/** ring の半径刻み(world px)。 */
|
|
7
|
+
step?: number;
|
|
8
|
+
/** ring の探索上限距離(world px)。 */
|
|
9
|
+
maxDistance?: number;
|
|
10
|
+
/** push の反復上限。 */
|
|
11
|
+
maxIterations?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* `free-position:find` リクエストのペイロード。
|
|
15
|
+
* snap の `snap:get-settings` と同様、同期コールバック(`onResult`)で結果を返す。
|
|
16
|
+
*/
|
|
17
|
+
export interface FreePositionRequest {
|
|
18
|
+
/** 置きたい位置・サイズ(回転 shape は回転後 AABB を渡す)。 */
|
|
19
|
+
desired: BoundingBox;
|
|
20
|
+
/** 衝突判定から除外する shape ID(移動対象自身など)。 */
|
|
21
|
+
excludeIds?: string[];
|
|
22
|
+
/** この問い合わせ限定の戦略上書き。 */
|
|
23
|
+
strategy?: FreePositionStrategy;
|
|
24
|
+
/** 結果(同サイズの空き位置)を受け取るコールバック。 */
|
|
25
|
+
onResult: (free: BoundingBox) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 指定位置から最も近い「被らない位置」を求める機能を提供するプラグイン(#581)。
|
|
29
|
+
* EventBus の `free-position:find` で問い合わせると、現在のボード上の shape(回転考慮 AABB)を
|
|
30
|
+
* 避けた最近傍の空き位置を返す。UI は持たない。
|
|
31
|
+
*/
|
|
32
|
+
export declare function createFreePositionPlugin(config?: FreePositionConfig): UsketchPlugin;
|
|
33
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,oBAAoB,EAAoB,MAAM,4BAA4B,CAAC;AACzF,OAAO,EACN,KAAK,WAAW,EAIhB,KAAK,aAAa,EAClB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,kBAAkB;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IACnC,0CAA0C;IAC1C,OAAO,EAAE,WAAW,CAAC;IACrB,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,gCAAgC;IAChC,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,GAAE,kBAAuB,GAAG,aAAa,CAoCvF"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { findFreePosition } from "@edv4h/usketch-shape-utils";
|
|
2
|
+
import { getRotatedAABB, safeRotation, } from "@edv4h/usketch-shared";
|
|
3
|
+
/**
|
|
4
|
+
* 指定位置から最も近い「被らない位置」を求める機能を提供するプラグイン(#581)。
|
|
5
|
+
* EventBus の `free-position:find` で問い合わせると、現在のボード上の shape(回転考慮 AABB)を
|
|
6
|
+
* 避けた最近傍の空き位置を返す。UI は持たない。
|
|
7
|
+
*/
|
|
8
|
+
export function createFreePositionPlugin(config = {}) {
|
|
9
|
+
return {
|
|
10
|
+
id: "usketch-plugin-free-position",
|
|
11
|
+
name: "Free Position",
|
|
12
|
+
setup(ctx) {
|
|
13
|
+
function collectOccupied(exclude) {
|
|
14
|
+
const ex = exclude && exclude.length > 0 ? new Set(exclude) : null;
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const [id, shape] of ctx.store.getShapes()) {
|
|
17
|
+
if (ex?.has(id))
|
|
18
|
+
continue;
|
|
19
|
+
const def = ctx.shapes.get(shape.type);
|
|
20
|
+
const bounds = def
|
|
21
|
+
? def.getBounds(shape)
|
|
22
|
+
: { x: shape.x, y: shape.y, width: shape.width, height: shape.height };
|
|
23
|
+
const rotation = safeRotation(shape.rotation);
|
|
24
|
+
out.push(rotation ? getRotatedAABB(bounds, rotation) : bounds);
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
const off = ctx.events.on("free-position:find", (req) => {
|
|
29
|
+
const free = findFreePosition({
|
|
30
|
+
desired: req.desired,
|
|
31
|
+
occupied: collectOccupied(req.excludeIds),
|
|
32
|
+
strategy: req.strategy ?? config.strategy,
|
|
33
|
+
step: config.step,
|
|
34
|
+
maxDistance: config.maxDistance,
|
|
35
|
+
maxIterations: config.maxIterations,
|
|
36
|
+
});
|
|
37
|
+
req.onResult(free);
|
|
38
|
+
});
|
|
39
|
+
return () => off();
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAEN,cAAc,EAEd,YAAY,GAEZ,MAAM,uBAAuB,CAAC;AA4B/B;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAA6B,EAAE;IACvE,OAAO;QACN,EAAE,EAAE,8BAA8B;QAClC,IAAI,EAAE,eAAe;QAErB,KAAK,CAAC,GAAkB;YACvB,SAAS,eAAe,CAAC,OAAkB;gBAC1C,MAAM,EAAE,GAAG,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBACnE,MAAM,GAAG,GAAkB,EAAE,CAAC;gBAC9B,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;oBACjD,IAAI,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;wBAAE,SAAS;oBAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACvC,MAAM,MAAM,GAAG,GAAG;wBACjB,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC;wBACtB,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;oBACxE,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAC9C,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBAChE,CAAC;gBACD,OAAO,GAAG,CAAC;YACZ,CAAC;YAED,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAsB,oBAAoB,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC5E,MAAM,IAAI,GAAG,gBAAgB,CAAC;oBAC7B,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC;oBACzC,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;oBACzC,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,aAAa,EAAE,MAAM,CAAC,aAAa;iBACnC,CAAC,CAAC;gBACH,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC,CAAC,CAAC;YAEH,OAAO,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC;KACD,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@edv4h/usketch-plugin-free-position",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"source": "./src/index.ts",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@edv4h/usketch-shape-utils": "2.1.0",
|
|
16
|
+
"@edv4h/usketch-shared": "4.2.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"typescript": "5.9.3",
|
|
20
|
+
"vitest": "3.2.4"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/EdV4H/usketch.git",
|
|
33
|
+
"directory": "plugins/usketch-plugin-free-position"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/EdV4H/usketch#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/EdV4H/usketch/issues"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc -p tsconfig.json",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run --passWithNoTests",
|
|
43
|
+
"clean": "rm -rf dist"
|
|
44
|
+
}
|
|
45
|
+
}
|