diff --git a/packages/display/src/DisplayObject.ts b/packages/display/src/DisplayObject.ts index ad900988..676c40e5 100644 --- a/packages/display/src/DisplayObject.ts +++ b/packages/display/src/DisplayObject.ts @@ -37,6 +37,7 @@ import { execute as displayObjectLocalToGlobalService } from "./DisplayObject/se import { execute as displayObjectGlobalToLocalService } from "./DisplayObject/service/DisplayObjectGlobalToLocalService"; import { execute as displayObjectGetHeightUseCase } from "./DisplayObject/usecase/DisplayObjectGetHeightUseCase"; import { execute as displayObjectSetHeightUseCase } from "./DisplayObject/usecase/DisplayObjectSetHeightUseCase"; +import { execute as displayObjectRemoveService } from "./DisplayObject/service/DisplayObjectRemoveService"; import { $getInstanceId, $parentMap, @@ -1114,78 +1115,16 @@ export class DisplayObject extends EventDispatcher return $variables.clear(); } - // /** - // * @param {Float32Array} multi_matrix - // * @returns {object} - // * @private - // */ - // _$getLayerBounds (multi_matrix: Float32Array): BoundsImpl - // { - // const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - // ? this._$getBounds() as BoundsImpl - // : $getBoundsObject(); - - // const bounds: BoundsImpl = $boundsMatrix(baseBounds, multi_matrix); - // $poolBoundsObject(baseBounds); - - // const filters: FilterArrayImpl = this._$filters || this.filters; - // if (!filters.length) { - // return bounds; - // } - - // let filterBounds: BoundsImpl = $getBoundsObject( - // 0, - // $Math.abs(bounds.xMax - bounds.xMin), - // 0, - // $Math.abs(bounds.yMax - bounds.yMin) - // ); - // $poolBoundsObject(bounds); - - // let xScale: number = +$Math.sqrt( - // multi_matrix[0] * multi_matrix[0] - // + multi_matrix[1] * multi_matrix[1] - // ); - // let yScale: number = +$Math.sqrt( - // multi_matrix[2] * multi_matrix[2] - // + multi_matrix[3] * multi_matrix[3] - // ); - - // xScale /= $devicePixelRatio; - // yScale /= $devicePixelRatio; - - // xScale *= 2; - // yScale *= 2; - - // for (let idx: number = 0; idx < filters.length; ++idx) { - // filterBounds = filters[idx] - // ._$generateFilterRect(filterBounds, xScale, yScale); - // } - - // return filterBounds; - // } - - // /** - // * @description AnimationToolのシンボルと同期 - // * Synchronize with AnimationTool symbol - // * - // * @param {number} character_id - // * @param {object} character - // * @param {LoaderInfo} loaderInfo - // * @return {void} - // * @method - // * @protected - // */ - // _$sync ( - // character_id: number, - // character: ICharacter, - // loaderInfo: LoaderInfo - // ): void { - - // // setup - // this.characterId = character_id; - // $loaderInfoMap.set(this, loaderInfo); - - // // build - // this._$buildCharacter(character); - // } + /** + * @description 親子関係を解除します。 + * Removes the parent-child relationship. + * + * @return {void} + * @method + * @public + */ + remove (): void + { + displayObjectRemoveService(this); + } } \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectRemoveService.ts b/packages/display/src/DisplayObject/service/DisplayObjectRemoveService.ts new file mode 100644 index 00000000..44c13af2 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectRemoveService.ts @@ -0,0 +1,19 @@ +import type { DisplayObject } from "../../DisplayObject"; + +/** + * @description 親子関係を解除する。 + * Break the parent-child relationship. + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + const parent = display_object.parent; + if (!parent) { + return ; + } + parent.removeChild(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer.ts b/packages/display/src/DisplayObjectContainer.ts index 251eff47..55fdb598 100644 --- a/packages/display/src/DisplayObjectContainer.ts +++ b/packages/display/src/DisplayObjectContainer.ts @@ -3,7 +3,11 @@ import type { IDisplayObject } from "./interface/IDisplayObject"; import { execute as displayObjectApplyChangesService } from "./DisplayObject/service/DisplayObjectApplyChangesService"; import { execute as displayObjectContainerAddChildUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerAddChildUseCase"; import { execute as displayObjectContainerRemoveChildUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase"; +import { execute as displayObjectContainerRemoveChildAtUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase"; import { execute as displayObjectContainerGetChildAtService } from "./DisplayObjectContainer/service/DisplayObjectContainerGetChildAtService"; +import { execute as displayObjectContainerContainsService } from "./DisplayObjectContainer/service/DisplayObjectContainerContainsService"; +import { execute as displayObjectContainerGetChildByNameService } from "./DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService"; +import { execute as displayObjectContainerRemoveChildrenUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase"; import { $getArray } from "./DisplayObjectUtil"; import { InteractiveObject } from "./InteractiveObject"; @@ -159,43 +163,20 @@ export class DisplayObjectContainer extends InteractiveObject } /** - * @description 指定された表示オブジェクトが、DisplayObjectContainer インスタンスの子であるか - * インスタンス自体であるかを指定します。 - * Determines whether the specified display object is a child - * of the DisplayObjectContainer instance or the instance itself. + * @description 指定された DisplayObject が、DisplayObjectContainer インスタンスの子孫であるか + * もしくは、インスタンス自体であるかを指定します。 + * Whether the specified DisplayObject is a descendant of a DisplayObjectContainer instance. + * or the instance itself. * - * @param {DisplayObject} child + * @param {DisplayObject} display_object * @return {boolean} * @method * @public */ - // contains (child: DisplayObjectImpl): boolean - // { - // if (this._$instanceId === child._$instanceId) { - // return true; - // } - - // const children: DisplayObjectImpl[] = this._$getChildren(); - // for (let idx: number = 0; idx < children.length; ++idx) { - - // const instance: DisplayObjectImpl = children[idx]; - - // if (instance._$instanceId === child._$instanceId) { - // return true; - // } - - // if (instance instanceof DisplayObjectContainer) { - - // if (instance.contains(child)) { - // return true; - // } - - // } - - // } - - // return false; - // } + contains (display_object: D): boolean + { + return displayObjectContainerContainsService(this, display_object); + } /** * @description 指定のインデックス位置にある子表示オブジェクトインスタンスを返します。 @@ -220,26 +201,10 @@ export class DisplayObjectContainer extends InteractiveObject * @method * @public */ - // getChildByName (name: string): DisplayObjectImpl | null - // { - // if (!name) { - // return null; - // } - - // // fixed logic - // const children: DisplayObjectImpl[] = this._$getChildren(); - // for (let idx: number = 0; idx < children.length; ++idx) { - - // const child: DisplayObjectImpl = children[idx]; - // if (child.name !== name) { - // continue; - // } - - // return child; - // } - - // return null; - // } + getChildByName (name: string): D | null + { + return displayObjectContainerGetChildByNameService(this, name); + } /** * @description 子 DisplayObject インスタンスのインデックス位置を返します。 @@ -283,41 +248,22 @@ export class DisplayObjectContainer extends InteractiveObject */ removeChildAt (index: number): void { - const child = this.getChildAt(index); - if (child) { - this.removeChild(child); - } + displayObjectContainerRemoveChildAtUseCase(this, index); } /** - * @description DisplayObjectContainer インスタンスの子リストから - * すべての child DisplayObject インスタンスを削除します。 - * Removes all child DisplayObject instances from - * the child list of the DisplayObjectContainer instance. + * @description 配列で指定されたインデックスの子をコンテナから削除します + * Removes children of the index specified in the array from the container * - * @param {number} [begin_index=0] - * @param {number} [end_index=0x7fffffff] + * @param {number[]} indexes * @return {void} * @method * @public */ - // removeChildren ( - // begin_index: number = 0, - // end_index: number = 0x7fffffff - // ): void { - - // const children: DisplayObjectImpl[] = this._$getChildren(); - // if (!children.length) { - // return ; - // } - - // begin_index = $clamp(begin_index, 0, 0x7ffffffe, 0) - 1; - // end_index = $clamp(end_index, 1, 0x7ffffff, 0x7ffffff); - - // for (let idx: number = $Math.min(end_index, children.length - 1); idx > begin_index; --idx) { - // this._$remove(children[idx]); - // } - // } + removeChildren (...indexes: number[]): void + { + displayObjectContainerRemoveChildrenUseCase(this, indexes); + } /** * @description 表示オブジェクトコンテナの既存の子の位置を変更します。 diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.test.ts new file mode 100644 index 00000000..f1dbe03e --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.test.ts @@ -0,0 +1,31 @@ +import { execute } from "./DisplayObjectContainerContainsService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerContainsService.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape = container.addChild(new Shape()); + + const sprite1 = container.addChild(new DisplayObjectContainer()); + const textField = sprite1.addChild(new TextField()); + + const sprite2 = sprite1.addChild(new DisplayObjectContainer()); + const video = sprite2.addChild(new Video(100, 300)); + + expect(execute(container, shape)).toBe(true); + expect(execute(container, textField)).toBe(true); + expect(execute(container, video)).toBe(true); + expect(execute(container, container)).toBe(true); + expect(execute(container, sprite1)).toBe(true); + expect(execute(container, sprite2)).toBe(true); + + expect(execute(sprite1, shape)).toBe(false); + expect(execute(sprite2, textField)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.ts new file mode 100644 index 00000000..3705f779 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.ts @@ -0,0 +1,45 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; + +/** + * @description DisplayObjectContainer とその子孫が指定の DisplayObject を含むかどうか + * Whether DisplayObjectContainer and its descendants contain the specified DisplayObject + * + * @param {DisplayObjectContainer} display_object_container + * @param {DisplayObject} display_object + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + display_object: D +): boolean => { + + if (display_object_container.instanceId === display_object.instanceId) { + return true; + } + + const children = display_object_container.children; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx]; + if (!child) { + continue; + } + + if (child.instanceId === display_object.instanceId) { + return true; + } + + if (!child.isContainerEnabled) { + continue; + } + + if ((child as DisplayObjectContainer).contains(display_object)) { + return true; + } + } + + return false; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.test.ts new file mode 100644 index 00000000..e978cf97 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.test.ts @@ -0,0 +1,27 @@ +import { execute } from "./DisplayObjectContainerGetChildByNameService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerGetChildByNameService.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape = container.addChild(new Shape()); + shape.name = "shape"; + + const textField = container.addChild(new TextField()); + textField.name = "textField"; + + const video = container.addChild(new Video(100, 300)); + video.name = "video"; + + expect(execute(container, "")).toBe(null); + expect(execute(container, "shape")).toBe(shape); + expect(execute(container, "textField")).toBe(textField); + expect(execute(container, "video")).toBe(video); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.ts new file mode 100644 index 00000000..bc445a2a --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.ts @@ -0,0 +1,35 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; + +/** + * @description 指定した名前の子要素を取得します + * Gets the child element with the specified name. + * + * @param {DisplayObjectContainer} display_object_container + * @param {string} name + * @return {DisplayObject | null} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + name: string +): D | null => { + + if (!name) { + return null; + } + + const children = display_object_container.children; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx]; + if (!child.name || child.name !== name) { + continue; + } + + return child; + } + + return null; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.test.ts new file mode 100644 index 00000000..db2691df --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./DisplayObjectContainerRemoveChildAtUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerRemoveChildAtUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + container.addChild(new Shape()); + container.addChild(new Shape()); + container.addChild(new Shape()); + + const shape4 = container.addChild(new Shape()); + const shape5 = container.addChild(new Shape()); + expect(container.children.length).toBe(5); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape4.parent).toBe(container); + execute(container, 3); + expect(shape4.parent).toBe(null); + expect(container.children.length).toBe(4); + + expect(shape5.parent).toBe(container); + execute(container, container.numChildren - 1); + expect(container.children.length).toBe(3); + expect(shape5.parent).toBe(null); + + expect(container.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.ts new file mode 100644 index 00000000..1b679807 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.ts @@ -0,0 +1,31 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectContainerGetChildAtService } from "../service/DisplayObjectContainerGetChildAtService"; +import { execute as displayObjectContainerRemoveChildUseCase } from "./DisplayObjectContainerRemoveChildUseCase"; + +/** + * @description 指定されたインデックスから子を削除する。 + * Remove a child from the specified index. + * + * @param {DisplayObjectContainer} display_object_container + * @param {number} index + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + index: number +): void => { + + const displayObject = displayObjectContainerGetChildAtService( + display_object_container, index + ); + + if (!displayObject) { + return ; + } + + displayObjectContainerRemoveChildUseCase( + display_object_container, displayObject + ); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.test.ts index 7d605f35..7e5cd359 100644 --- a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.test.ts +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.test.ts @@ -11,6 +11,9 @@ describe("DisplayObjectContainerRemoveChildUseCase.js test", () => const shape1 = container.addChild(new Shape()); const shape2 = container.addChild(new Shape()); + container.changed = false; + expect(container.changed).toBe(false); + expect(shape1.$added).toBe(true); expect(shape1.parent?.instanceId).toBe(container.instanceId); @@ -29,6 +32,8 @@ describe("DisplayObjectContainerRemoveChildUseCase.js test", () => expect(container.children.length).toBe(0); expect(shape2.$added).toBe(false); expect(shape2.parent).toBe(null); + + expect(container.changed).toBe(true); }); }); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.test.ts new file mode 100644 index 00000000..2fbddcbf --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.test.ts @@ -0,0 +1,30 @@ +import { execute } from "./DisplayObjectContainerRemoveChildrenUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerRemoveChildrenUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + container.addChild(new Shape()); + container.addChild(new Shape()); + const shape3 = container.addChild(new Shape()); + const shape4 = container.addChild(new Shape()); + container.addChild(new Shape()); + expect(container.children.length).toBe(5); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape3.parent).toBe(container); + expect(shape4.parent).toBe(container); + execute(container, [2, 3]); + expect(shape3.parent).toBe(null); + expect(shape4.parent).toBe(null); + expect(container.children.length).toBe(3); + + expect(container.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.ts new file mode 100644 index 00000000..a854b72b --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.ts @@ -0,0 +1,37 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectContainerRemoveChildUseCase } from "./DisplayObjectContainerRemoveChildUseCase"; + +/** + * @description 配列で指定されたインデックスの子をコンテナから削除します + * Removes children of the index specified in the array from the container + * + * @param {DisplayObjectContainer} display_object_container + * @param {number[]} indexes + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + indexes: number[] +): void => { + + const children = display_object_container.children.slice(); + if (!children.length) { + return ; + } + + for (let idx = 0; idx < indexes.length; idx++) { + + const index = indexes[idx]; + + const child = children[index]; + if (!child) { + continue; + } + + displayObjectContainerRemoveChildUseCase( + display_object_container, child + ); + } +}; \ No newline at end of file