From 1001aa058660fc88c5640ee891cb07a5111fe1f4 Mon Sep 17 00:00:00 2001 From: ienaga Date: Wed, 4 Dec 2024 23:51:28 +0900 Subject: [PATCH] =?UTF-8?q?#154=20DisplayObject=E3=81=AE=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E3=82=92=E5=AE=9F=E8=A3=85(WIP)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 34 ++- packages/display/src/DisplayObject.ts | 224 ++++-------------- .../DisplayObjectGetBoundsUseCase.test.ts | 34 +++ .../usecase/DisplayObjectGetBoundsUseCase.ts | 62 +++++ .../DisplayObjectHitTestObjectUseCase.test.ts | 35 +++ .../DisplayObjectHitTestObjectUseCase.ts | 55 +++++ .../DisplayObjectHitTestPointUseCase.ts | 144 +++++++++++ .../src/interface/IPlayerHitObject copy.ts | 8 - 8 files changed, 408 insertions(+), 188 deletions(-) create mode 100644 packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.test.ts create mode 100644 packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.ts create mode 100644 packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.test.ts create mode 100644 packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.ts create mode 100644 packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.ts delete mode 100644 packages/display/src/interface/IPlayerHitObject copy.ts diff --git a/index.html b/index.html index 75444bc4..35b1670f 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,39 @@ diff --git a/packages/display/src/DisplayObject.ts b/packages/display/src/DisplayObject.ts index 676c40e5..168b7c4e 100644 --- a/packages/display/src/DisplayObject.ts +++ b/packages/display/src/DisplayObject.ts @@ -38,13 +38,17 @@ import { execute as displayObjectGlobalToLocalService } from "./DisplayObject/se 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 { execute as displayObjectGetBoundsUseCase } from "./DisplayObject/usecase/DisplayObjectGetBoundsUseCase"; +import { execute as displayObjectHitTestObjectUseCase } from "./DisplayObject/usecase/DisplayObjectHitTestObjectUseCase"; +import { execute as displayObjectHitTestPointUseCase } from "./DisplayObject/usecase/DisplayObjectHitTestPointUseCase"; import { $getInstanceId, $parentMap, $loaderInfoMap, $rootMap, $variables, - $getDraggingDisplayObject + $getDraggingDisplayObject, + $pointer } from "./DisplayObjectUtil"; /** @@ -554,36 +558,32 @@ export class DisplayObject extends EventDispatcher } /** - * @description マウスまたはユーザー入力デバイスの x 軸の位置をピクセルで示します。 - * Indicates the x coordinate of the mouse or user input device position, in pixels. + * @description 対象のDisplayObjectの基準点からの x 軸の位置をピクセルで示します。 + * Indicates the x coordinate of the DisplayObject instance relative to the local coordinates of the parent DisplayObjectContainer. * * @member {number} * @default 0 * @readonly * @public */ - // get mouseX (): number - // { - // return $getEvent() - // ? this.globalToLocal($currentMousePoint()).x - // : 0; - // } - - // /** - // * @description マウスまたはユーザー入力デバイスの y 軸の位置をピクセルで示します。 - // * Indicates the y coordinate of the mouse or user input device position, in pixels. - // * - // * @member {number} - // * @default 0 - // * @readonly - // * @public - // */ - // get mouseY (): number - // { - // return $getEvent() - // ? this.globalToLocal($currentMousePoint()).y - // : 0; - // } + get mouseX (): number + { + return this.globalToLocal($pointer).x; + } + + /** + * @description 対象のDisplayObjectの基準点からの y 軸の位置をピクセルで示します。 + * Indicates the y coordinate of the DisplayObject instance relative to the local coordinates of the parent DisplayObjectContainer. + * + * @member {number} + * @default 0 + * @readonly + * @public + */ + get mouseY (): number + { + return this.globalToLocal($pointer).y; + } /** * @description このDisplayObjectの親のDisplayObjectContainerを返却します。 @@ -762,68 +762,16 @@ export class DisplayObject extends EventDispatcher } /** - * @description targetCoordinateSpace オブジェクトの座標系を基準にして、 - * 表示オブジェクトの領域を定義する矩形を返します。 - * Returns a rectangle that defines the area - * of the display object relative to the coordinate system - * of the targetCoordinateSpace object. + * @description 指定したDisplayObject の座標系を基準にして、表示オブジェクトの領域を定義する矩形を返します。 + * Returns a rectangle that defines the area of the display object relative to the coordinate system of the targetDisplayObject. * - * @param {DisplayObject} [target=null] + * @param {DisplayObject} [target_display_object=null] * @return {Rectangle} */ - // getBounds (target: DisplayObjectImpl | null = null): Rectangle - // { - // const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - // ? this._$getBounds() as BoundsImpl - // : $getBoundsObject(); - - // const matrix: Matrix = this._$transform.concatenatedMatrix; - - // // to global - // const bounds: BoundsImpl = $boundsMatrix(baseBounds, matrix._$matrix); - - // // pool - // $poolMatrix(matrix); - // $poolBoundsObject(baseBounds); - - // // create bounds object - // const targetBaseBounds: BoundsImpl = $getBoundsObject( - // bounds.xMin, - // bounds.xMax, - // bounds.yMin, - // bounds.yMax - // ); - - // // pool - // $poolBoundsObject(bounds); - - // if (!target) { - // target = this; - // } - - // const targetMatrix: Matrix = target._$transform.concatenatedMatrix; - // targetMatrix.invert(); - - // const resultBounds: BoundsImpl = $boundsMatrix( - // targetBaseBounds, targetMatrix._$matrix - // ); - // $poolBoundsObject(targetBaseBounds); - // $poolMatrix(targetMatrix); - - // const xMin: number = resultBounds.xMin; - // const yMin: number = resultBounds.yMin; - // const xMax: number = resultBounds.xMax; - // const yMax: number = resultBounds.yMax; - - // // pool - // $poolBoundsObject(resultBounds); - - // return new Rectangle( - // xMin, yMin, - // $Math.abs(xMax - xMin), - // $Math.abs(yMax - yMin) - // ); - // } + getBounds (target_display_object: D | null = null): Rectangle + { + return displayObjectGetBoundsUseCase(this as unknown as D, target_display_object); + } /** * @description point オブジェクトをステージ(グローバル)座標から @@ -841,48 +789,17 @@ export class DisplayObject extends EventDispatcher } /** - * @description 表示オブジェクトの境界ボックスを評価して、 - * obj 表示オブジェクトの境界ボックスと重複または交差するかどうかを調べます。 - * Evaluates the bounding box of the display object to see - * if it overlaps or intersects with the bounding box of the obj display object. + * @description DisplayObject の描画範囲を評価して、重複または交差するかどうかを調べます。 + * Evaluates a DisplayObject's drawing range to see if it overlaps or intersects. * - * @param {DisplayObject} object + * @param {DisplayObject} target_display_object * @returns {boolean} * @public */ - // hitTestObject (object: DisplayObjectImpl): boolean - // { - // const baseBounds1: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - // ? this._$getBounds() as BoundsImpl - // : $getBoundsObject(); - - // const matrix1: Matrix = this._$transform.concatenatedMatrix; - // const bounds1: BoundsImpl = $boundsMatrix(baseBounds1, matrix1._$matrix); - - // // pool - // $poolMatrix(matrix1); - // $poolBoundsObject(baseBounds1); - - // const baseBounds2: BoundsImpl = object._$getBounds(null); - // const matrix2: Matrix = object._$transform.concatenatedMatrix; - // const bounds2: BoundsImpl = $boundsMatrix(baseBounds2, matrix2._$matrix); - - // // pool - // $poolMatrix(matrix2); - // $poolBoundsObject(baseBounds2); - - // // calc - // const sx: number = $Math.max(bounds1.xMin, bounds2.xMin); - // const sy: number = $Math.max(bounds1.yMin, bounds2.yMin); - // const ex: number = $Math.min(bounds1.xMax, bounds2.xMax); - // const ey: number = $Math.min(bounds1.yMax, bounds2.yMax); - - // // pool - // $poolBoundsObject(bounds1); - // $poolBoundsObject(bounds2); - - // return ex - sx >= 0 && ey - sy >= 0; - // } + hitTestObject (target_display_object: D): boolean + { + return displayObjectHitTestObjectUseCase(this as unknown as DisplayObject, target_display_object); + } /** * @description 表示オブジェクトを評価して、x および y パラメーターで指定された @@ -896,63 +813,12 @@ export class DisplayObject extends EventDispatcher * @returns {boolean} * @public */ - // hitTestPoint ( - // x: number, y: number, - // shape_flag: boolean = false - // ): boolean { - - // if (shape_flag) { - - // let matrix: Float32Array = $MATRIX_ARRAY_IDENTITY; - - // let parent: ParentImpl | null = this._$parent; - // while (parent) { - - // matrix = $multiplicationMatrix( - // parent._$transform._$rawMatrix(), - // matrix - // ); - - // parent = parent._$parent; - // } - - // $hitContext.setTransform(1, 0, 0, 1, 0, 0); - // $hitContext.beginPath(); - - // let result: boolean = false; - // if ("_$hit" in this && typeof this._$hit === "function") { - // result = this._$hit($hitContext, matrix, { "x": x, "y": y }, true); - // } - - // if (matrix !== $MATRIX_ARRAY_IDENTITY) { - // $poolFloat32Array6(matrix); - // } - - // return result; - // } - - // const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - // ? this._$getBounds() as BoundsImpl - // : $getBoundsObject(); - - // const bounds: BoundsImpl = $boundsMatrix(baseBounds, this._$transform._$rawMatrix()); - // $poolBoundsObject(baseBounds); - - // const rectangle: Rectangle = new Rectangle( - // bounds.xMin, bounds.yMin, - // bounds.xMax - bounds.xMin, - // bounds.yMax - bounds.yMin - // ); - - // // pool - // $poolBoundsObject(bounds); - - // const point: Point = this._$parent - // ? this._$parent.globalToLocal(new Point(x, y)) - // : new Point(x, y); - - // return rectangle.containsPoint(point); - // } + hitTestPoint ( + x: number, y: number, + shape_flag: boolean = false + ): boolean { + return displayObjectHitTestPointUseCase(this, x, y, shape_flag); + } /** * @description point オブジェクトを表示オブジェクトの(ローカル)座標からステージ(グローバル)座標に変換します。 diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.test.ts new file mode 100644 index 00000000..ac8d637a --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./DisplayObjectGetBoundsUseCase"; +import type { DisplayObject } from "../../DisplayObject"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Shape } from "../../Shape"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectGetBoundsUseCase.js test", () => +{ + it("execute test case1", () => + { + const sprite = new DisplayObjectContainer(); + + const container = new DisplayObjectContainer(); + container.x = 100; + container.y = 100; + sprite.addChild(container); + + const contents = new Shape(); + contents.graphics.drawCircle(0, 0, 100); + container.addChild(contents); + + const bounds1 = execute(contents, container as unknown as DisplayObject); + const bounds2 = execute(contents, sprite as unknown as DisplayObject); + + expect(bounds1.x).toBe(-100); + expect(bounds1.y).toBe(-100); + expect(bounds1.width).toBe(200); + expect(bounds1.height).toBe(200); + expect(bounds2.x).toBe(0); + expect(bounds2.y).toBe(0); + expect(bounds2.width).toBe(200); + expect(bounds2.height).toBe(200); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.ts new file mode 100644 index 00000000..58f3342d --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.ts @@ -0,0 +1,62 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawBoundsUseCase } from "./DisplayObjectGetRawBoundsUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../service/DisplayObjectCalcBoundsMatrixService"; +import { Rectangle, Matrix } from "@next2d/geom"; +import { $poolArray } from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectのmatrixを考慮した描画範囲を計算 + * Calculate the drawing range considering the matrix of DisplayObject + * + * @param {DisplayObject} display_object + * @param {DisplayObject} [target_display_object=null] + * @return {Rectangle} + * @method + * @protected + */ +export const execute = ( + display_object: D, + target_display_object: D | null = null +): Rectangle => { + + const matrix = display_object.concatenatedMatrix; + const rawBounds = displayObjectGetRawBoundsUseCase(display_object); + + // to global + const bounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + matrix.rawData + ); + + // pool + $poolArray(rawBounds); + Matrix.release(matrix.rawData); + + if (!target_display_object) { + target_display_object = display_object; + } + + const targetMatrix: Matrix = target_display_object.concatenatedMatrix; + targetMatrix.invert(); + + const targetBounds = displayObjectCalcBoundsMatrixService( + bounds[0], bounds[1], + bounds[2], bounds[3], + targetMatrix.rawData + ); + + // pool + $poolArray(bounds); + Matrix.release(targetMatrix.rawData); + + const rectangle = new Rectangle( + targetBounds[0], targetBounds[1], + Math.abs(targetBounds[2] - targetBounds[0]), + Math.abs(targetBounds[3] - targetBounds[1]) + ); + + $poolArray(targetBounds); + + return rectangle; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.test.ts new file mode 100644 index 00000000..76785a83 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.test.ts @@ -0,0 +1,35 @@ +import { execute } from "./DisplayObjectHitTestObjectUseCase"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Shape } from "../../Shape"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectHitTestObjectUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + + const shape1 = container.addChild(new Shape()); + shape1 + .graphics + .beginFill() + .drawRect(0, 0, 32, 32); + + shape1.x = 32; + shape1.y = 32; + + const shape2 = container.addChild(new Shape()); + shape2 + .graphics + .beginFill() + .drawRect(0, 0, 32, 32); + + shape2.x = 65; + shape2.y = 65; + expect(execute(shape1, shape2)).toBe(false); + + shape2.x = 64; + shape2.y = 64; + expect(execute(shape1, shape2)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.ts new file mode 100644 index 00000000..81f53a72 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.ts @@ -0,0 +1,55 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawBoundsUseCase } from "./DisplayObjectGetRawBoundsUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../service/DisplayObjectCalcBoundsMatrixService"; +import { Matrix } from "@next2d/geom"; +import { $poolArray } from "../../DisplayObjectUtil"; + +/** + * @description + * @param {DisplayObject} display_object + * @param {DisplayObject} target_display_object + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + display_object: D, + target_display_object: D +): boolean => { + + const rawBounds1 = displayObjectGetRawBoundsUseCase(display_object); + const matrix1 = display_object.concatenatedMatrix; + const bounds1 = displayObjectCalcBoundsMatrixService( + rawBounds1[0], rawBounds1[1], + rawBounds1[2], rawBounds1[3], + matrix1.rawData + ); + + // pool + $poolArray(rawBounds1); + Matrix.release(matrix1.rawData); + + const rawBounds2 = displayObjectGetRawBoundsUseCase(target_display_object); + const matrix2 = target_display_object.concatenatedMatrix; + const bounds2 = displayObjectCalcBoundsMatrixService( + rawBounds2[0], rawBounds2[1], + rawBounds2[2], rawBounds2[3], + matrix2.rawData + ); + + // pool + $poolArray(rawBounds2); + Matrix.release(matrix2.rawData); + + // calc + const sx = Math.max(bounds1[0], bounds2[0]); + const sy = Math.max(bounds1[1], bounds2[1]); + const ex = Math.min(bounds1[2], bounds2[2]); + const ey = Math.min(bounds1[3], bounds2[3]); + + // pool + $poolArray(bounds1); + $poolArray(bounds2); + + return ex - sx >= 0 && ey - sy >= 0; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.ts new file mode 100644 index 00000000..5167594b --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.ts @@ -0,0 +1,144 @@ +import type { IPlayerHitObject } from "../../interface/IPlayerHitObject"; +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Shape } from "../../Shape"; +import type { TextField } from "@next2d/text"; +import type { Video } from "@next2d/media"; +import { execute as displayObjectGetRawBoundsUseCase } from "./DisplayObjectGetRawBoundsUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../service/DisplayObjectCalcBoundsMatrixService"; +import { execute as displayObjectGetRawMatrixUseCase } from "./DisplayObjectGetRawMatrixUseCase"; +import { execute as displayObjectContainerMouseHitUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase"; +import { execute as shapeHitTestUseCase } from "../../Shape/usecase/ShapeHitTestUseCase"; +import { execute as textFieldHitTestUseCase } from "../../TextField/usecase/TextFieldHitTestUseCase"; +import { execute as videoHitTestUseCase } from "../../Video/usecase/VideoHitTestUseCase"; +import { + Rectangle, + Point +} from "@next2d/geom"; +import { + $MATRIX_ARRAY_IDENTITY, + $poolArray +} from "../../DisplayObjectUtil"; + +const canvas = document.createElement("canvas"); +canvas.width = 1; +canvas.height = 1; + +/** + * @type {CanvasRenderingContext2D} + * @private + */ +const $hitContext: CanvasRenderingContext2D = canvas.getContext("2d") as CanvasRenderingContext2D; + +/** + * @type {IPlayerHitObject} + * @private + */ +const $hitObject: IPlayerHitObject = { + "x": 0, + "y": 0, + "pointer": "", + "hit": null +}; + +/** + * @description 指定された DisplayObject が指定された座標にヒットしているかどうかを返します + * Returns whether the specified DisplayObject hits the specified coordinates + * + * @param {DisplayObject} display_object + * @param {number} x + * @param {number} y + * @param {boolean} [shape_flag=false] + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + display_object: D, + x: number, + y: number, + shape_flag: boolean = false +): boolean => { + + if (shape_flag) { + + const parent = display_object.parent; + + const matrix = parent + ? parent.concatenatedMatrix.rawData + : $MATRIX_ARRAY_IDENTITY; + + $hitContext.setTransform(1, 0, 0, 1, 0, 0); + $hitContext.beginPath(); + + $hitObject.x = x; + $hitObject.y = y; + $hitObject.hit = null; + + switch (true) { + + case display_object.isContainerEnabled: + return displayObjectContainerMouseHitUseCase( + display_object as unknown as DisplayObjectContainer, + $hitContext, + matrix, + $hitObject + ); + + case display_object.isShape: + return shapeHitTestUseCase( + display_object as unknown as Shape, + $hitContext, + matrix, + $hitObject + ); + + case display_object.isText: + return textFieldHitTestUseCase( + display_object as unknown as TextField, + $hitContext, + matrix, + $hitObject + ); + + case display_object.isVideo: + return videoHitTestUseCase( + display_object as unknown as Video, + $hitContext, + matrix, + $hitObject + ); + + default: + return false; + + } + } + + const rawBounds = displayObjectGetRawBoundsUseCase(display_object); + const martix = displayObjectGetRawMatrixUseCase(display_object); + const bounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + martix ? martix : $MATRIX_ARRAY_IDENTITY + ); + + // pool + $poolArray(rawBounds); + + const rectangle = new Rectangle( + bounds[0], bounds[1], + Math.abs(bounds[2] - bounds[0]), + Math.abs(bounds[3] - bounds[1]) + ); + + // pool + $poolArray(bounds); + + const parent = display_object.parent; + const point = parent + ? parent.globalToLocal(new Point(x, y)) + : new Point(x, y); + + return rectangle.containsPoint(point); +}; \ No newline at end of file diff --git a/packages/display/src/interface/IPlayerHitObject copy.ts b/packages/display/src/interface/IPlayerHitObject copy.ts deleted file mode 100644 index 67b691fe..00000000 --- a/packages/display/src/interface/IPlayerHitObject copy.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { IDisplayObject } from "./IDisplayObject"; - -export interface IPlayerHitObject { - x: number; - y: number; - pointer: string; - hit: IDisplayObject | null; -} \ No newline at end of file