diff --git a/OHBM/build-profile.json5 b/OHBM/build-profile.json5 index 1b02a4eac717c16da94816d8b9392ca0e3088b2f..58057edad66ed9d0d1ad2b7fa5ea5e96b6e24420 100644 --- a/OHBM/build-profile.json5 +++ b/OHBM/build-profile.json5 @@ -23,6 +23,8 @@ "compileSdkVersion": 12, "compatibleSdkVersion": 12, "runtimeOS": "OpenHarmony", + //"compatibleSdkVersion": "5.0.0(12)", + //"runtimeOS": "HarmonyOS", "buildOption": { "strictMode": { "caseSensitiveCheck": true, diff --git a/OHBM/entry/src/main/ets/cases/BenchmarkMeasure.ts b/OHBM/entry/src/main/ets/cases/BenchmarkMeasure.ts index 227a69bcd4b7fdac5027f222bba1f0ce99de8929..cb1df45beccf4390271c37a673a80cb8b1702333 100644 --- a/OHBM/entry/src/main/ets/cases/BenchmarkMeasure.ts +++ b/OHBM/entry/src/main/ets/cases/BenchmarkMeasure.ts @@ -13,6 +13,8 @@ * limitations under the License. */ import { ThreadWorkerGlobalScope } from '@kit.ArkTS'; +import { B2Vec2 } from './performance/box2d/Common/b2Math'; +import { B2Array } from './performance/box2d/Dynamics/b2TimeStep'; const TAG = 'Benchmark' @@ -47,6 +49,9 @@ class WorkerMessage { r: number; g: number; b: number; + vertices_x:number[]; + vertices_y:number[]; + radius:number; constructor(name: string, type: number, time: number, latency: number, x: number, y: number, r: number, g: number, b: number) { @@ -119,7 +124,7 @@ class Benchmark { } if (!this.data) { - this.measure(workerPort); + //this.measure(workerPort); this.data = { runs: 0, elapsed: 0 }; } else { this.measure(workerPort); @@ -137,12 +142,12 @@ class Benchmark { measure(workerPort: ThreadWorkerGlobalScope): void { let elapsed: number = 0; let start: number = new Date().getTime(); - let i = 0 for (; (this.doDeterministic ? i < this.minIterations : elapsed < 1000); i++) { + workerPort.postMessage(new WorkerMessage(this.name+ `第${i + 1}次测试`, 0, 0, 0, 0, 0, 0, 0, 0)) this.run(workerPort); elapsed = new Date().getTime() - start; - workerPort.postMessage(new WorkerMessage(this.name+`(第${i + 1}次)`, 0, elapsed, 0, 0, 0, 0, 0, 0)) + workerPort.postMessage(new WorkerMessage(this.name+`第${i + 1}次`, 0, elapsed, 0, 0, 0, 0, 0, 0)) } if (this.data != null) { this.data.runs += i; @@ -168,7 +173,7 @@ class BenchmarkRun { BenchmarkRNG.resetRNG() this.benchmark.runSetup(result, workerPort) this.printResult(result) - workerPort.postMessage(new WorkerMessage(this.name+'(完成)', 0, result.time, result.latency, 0, 0, 0, 0, 0)) + workerPort.postMessage(new WorkerMessage(this.name+'完成', 0, result.time, result.latency, 0, 0, 0, 0, 0)) } catch (error) { this.printErrorMessage(JSON.stringify(error)) } diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/Shapes/b2Shape.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/Shapes/b2Shape.ts new file mode 100644 index 0000000000000000000000000000000000000000..37c70138bcf395f73b49402c4e70b48c3392e10a --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/Shapes/b2Shape.ts @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Transform } from '../../Common/b2Math'; +import { B2Vec2, add, b2MulR2, subtract, b2Dot22, b2Sqrt, multM, b2MulT2, b2Min, b2Max, addEqual, mulEqual, b2Cross } from '../../Common/b2Math'; +import { half, indexTwo } from '../../Common/b2Settings'; +import { float025 } from '../../Common/b2Settings'; +import { float3 } from '../../Common/b2Settings'; +import { b2Epsilon, b2PI, b2PolygonRadius } from '../../Common/b2Settings'; +import { B2Array } from '../../Dynamics/b2TimeStep'; +import { B2RayCastInput, B2RayCastOutput, B2AaBb } from '../b2Collision'; + +export class B2MassData { + public mass: number = 0; + public center = new B2Vec2(); + public aI: number = 0; +} + +enum B2ShapeType { + CIRCLE, + EDGE, + POLYGON, + CHAIN, + TYPECOUNT +} + +export class B2Shape { + constructor() { + this.mType = B2ShapeType.CIRCLE; + this.mRadius = 0; + } + + public clone(): B2Shape { + throw new Error('must override'); + } + + public type(): B2ShapeType { + return this.mType; + } + + public childCount(): number { + throw new Error('must override'); + } + + public testPoint(transform: B2Transform, point: B2Vec2): boolean { + throw new Error('must override'); + } + + public rayCast(output: B2RayCastOutput, input: B2RayCastInput, transform: B2Transform, childIndex: number): boolean { + throw new Error('must override'); + } + + public computeAaBb(aabb: B2AaBb, transform: B2Transform, childIndex: number): void { + throw new Error('must override'); + } + + public computeMass(density: number): B2MassData { + throw new Error('must override'); + } + + public mType: B2ShapeType; + public mRadius: number; + get radius(): number { + return this.mRadius; + } + set radius(newValue) { + this.mRadius = newValue; + } +} + +export class B2CircleShape extends B2Shape { + constructor() { + super(); + this.mType = B2ShapeType.CIRCLE; + this.mRadius = 0.0; + this.mPoint = new B2Vec2(0.0, 0.0); + } + + public clone(): B2Shape { + let clone = new B2CircleShape(); + clone.mRadius = this.mRadius; + clone.mPoint = this.mPoint; + return clone; + } + + public childCount(): number { + return 1; + } + + public testPoint(transform: B2Transform, point: B2Vec2): boolean { + let center = add(transform.p, b2MulR2(transform.q, this.mPoint)); + let d = subtract(this.p, center); + return b2Dot22(d, d) <= this.mRadius * this.mRadius; + } + + public rayCast(output: B2RayCastOutput, input: B2RayCastInput, transform: B2Transform, childIndex: number): boolean { + let position = add(transform.p, b2MulR2(transform.q, this.mPoint)); + let s = subtract(input.p1, position); + let b = b2Dot22(s, s) - this.mRadius * this.mRadius; + let r = subtract(input.p2, input.p1); + let c = b2Dot22(s, r); + let rr = b2Dot22(r, r); + let sigma = c * c - rr * b; + if (sigma < 0.0 || rr < b2Epsilon) { + return false; + } + let a = -(c + b2Sqrt(sigma)); + if (0.0 <= a && a <= input.maxFraction * rr) { + a /= rr; + output.fraction = a; + output.normal = add(s, multM(r, a)); + output.normal.normalize(); + return true; + } + return false; + } + + public computeAaBb(aabb: B2AaBb, transform: B2Transform, childIndex: number): void { + let p = add(transform.p, b2MulR2(transform.q, this.mPoint)); + aabb.lowerBound.set(p.x - this.mRadius, p.y - this.mRadius); + aabb.upperBound.set(p.x + this.mRadius, p.y + this.mRadius); + } + + public computeMass(density: number): B2MassData { + let massData = new B2MassData(); + massData.mass = density * b2PI * this.mRadius * this.mRadius; + massData.center = this.mPoint; + massData.aI = massData.mass * (half * this.mRadius * this.mRadius + b2Dot22(this.mPoint, this.mPoint)); + return massData; + } + + public getSupport(direction: B2Vec2): number { + return 0; + } + + public getSupportVertex(direction: B2Vec2): B2Vec2 { + return this.mPoint; + } + + public vertexCount(): number { + return 1; + } + + public vertex(index: number): B2Vec2 { + return this.mPoint; + } + + get p(): B2Vec2 { + return this.mp.get(0); + } + set p(newValue: B2Vec2) { + this.mp.set(0, newValue); + } + mp = new B2Array(1, new B2Vec2()); + get mPoint(): B2Vec2 { + return this.mp.get(0); + } + set mPoint(newValue: B2Vec2) { + this.mp.set(0, newValue); + } +} + +export class B2EdgeShape extends B2Shape { + constructor() { + super(); + this.mVertex0 = new B2Vec2(0.0, 0.0); + this.mVertex3 = new B2Vec2(0.0, 0.0); + this.mHasVertex0 = false; + this.mHasVertex3 = false; + this.mType = B2ShapeType.EDGE; + this.mRadius = b2PolygonRadius; + } + + public set(v1: B2Vec2, v2: B2Vec2): void { + this.mVertex1 = v1; + this.mVertex2 = v2; + this.mHasVertex0 = false; + this.mHasVertex3 = false; + } + + public clone(): B2Shape { + let clone = new B2EdgeShape(); + clone.mRadius = this.mRadius; + clone.mVertices = this.mVertices.clone(); + clone.mVertex0 = this.mVertex0; + clone.mVertex3 = this.mVertex3; + clone.mHasVertex0 = this.mHasVertex0; + clone.mHasVertex3 = this.mHasVertex3; + return clone; + } + + public childCount(): number { + return 1; + } + + public testPoint(transform: B2Transform, point: B2Vec2): boolean { + return false; + } + + public computeAaBb(aabb: B2AaBb, transform: B2Transform, childIndex: number): void { + let v1 = b2MulT2(transform, this.mVertex1); + let v2 = b2MulT2(transform, this.mVertex2); + let lower = b2Min(v1, v2); + let upper = b2Max(v1, v2); + let r = new B2Vec2(this.mRadius, this.mRadius); + aabb.lowerBound = subtract(lower, r); + aabb.upperBound = add(upper, r); + } + + public computeMass(density: number): B2MassData { + let massData = new B2MassData(); + massData.mass = 0.0; + massData.center = multM(add(this.mVertex1, this.mVertex2), half); + massData.aI = 0.0; + return massData; + } + + get vertex1(): B2Vec2 { + return this.mVertices.get(0); + } + set vertex1(newValue: B2Vec2) { + this.mVertices.set(0, newValue); + } + + get vertex2(): B2Vec2 { + return this.mVertices.get(1); + } + set vertex2(newValue: B2Vec2) { + this.mVertices.set(1, newValue); + } + + get vertex0(): B2Vec2 { + return this.mVertex0; + } + set vertex0(newValue) { + this.mVertex0 = newValue; + } + + get vertex3(): B2Vec2 { + return this.mVertex3; + } + set vertex3(newValue) { + this.mVertex3 = newValue; + } + + get hasVertex0(): boolean { + return this.mHasVertex0; + } + set hasVertex0(newValue) { + this.mHasVertex0 = newValue; + } + + get hasVertex3(): boolean { + return this.mHasVertex3; + } + set hasVertex3(newValue) { + this.mHasVertex3 = newValue; + } + + public mVertices = new B2Array(indexTwo, new B2Vec2()); + get mVertex1(): B2Vec2 { + return this.mVertices.get(0); + } + set mVertex1(newValue: B2Vec2) { + this.mVertices.set(0, newValue); + } + get mVertex2(): B2Vec2 { + return this.mVertices.get(1); + } + set mVertex2(newValue: B2Vec2) { + this.mVertices.set(1, newValue); + } + + public mVertex0: B2Vec2; + public mVertex3: B2Vec2; + public mHasVertex0: boolean; + public mHasVertex3: boolean; +} + +export class B2PolygonShape extends B2Shape { + constructor() { + super(); + this.mCentroid = new B2Vec2(0.0, 0.0); + this.mType = B2ShapeType.POLYGON; + this.mRadius = b2PolygonRadius; + } + + public clone(): B2Shape { + let clone = new B2PolygonShape(); + clone.mCentroid = this.mCentroid; + clone.mVertices = this.mVertices.clone(); + clone.mNormals = this.mNormals.clone(); + clone.mRadius = this.mRadius; + return clone; + } + + public childCount(): number { + return 1; + } + + public setAsEdge(v1: B2Vec2, v2: B2Vec2): void { + this.mVertices.append(v1); + this.mVertices.append(v2); + this.mCentroid.x = half * (v1.x + v2.x); + this.mCentroid.y = half * (v1.y + v2.y); + this.mNormals.append(this.crossVF(this.subtractVV(v2, v1), 1.0)); + let mn0 = this.mNormals.get(0); + mn0.normalize(); + this.mNormals.append(new B2Vec2(-this.mNormals.get(0).x, -this.mNormals.get(0).y)); + } + + public setAsBox(hx: number, hy: number): void { + this.mVertices.removeAll(); + this.mVertices.append(new B2Vec2(-hx, -hy)); + this.mVertices.append(new B2Vec2(hx, -hy)); + this.mVertices.append(new B2Vec2(hx, hy)); + this.mVertices.append(new B2Vec2(-hx, hy)); + this.mNormals.removeAll(); + this.mNormals.append(new B2Vec2(0.0, -1.0)); + this.mNormals.append(new B2Vec2(1.0, 0.0)); + this.mNormals.append(new B2Vec2(0.0, 1.0)); + this.mNormals.append(new B2Vec2(-1.0, 0.0)); + this.mCentroid.setZero(); + } + + public subtractVV(a: B2Vec2, b: B2Vec2): B2Vec2 { + let v = new B2Vec2(a.x - b.x, a.y - b.y); + return v; + } + + public crossVV(a: B2Vec2, b: B2Vec2): number { + return a.x * b.y - a.y * b.x; + } + + public crossVF(a: B2Vec2, s: number): B2Vec2 { + let v = new B2Vec2(s * a.y, -s * a.x); + return v; + } + + public computeAaBb(aabb: B2AaBb, transform: B2Transform, childIndex: number): void { + let lower = b2MulT2(transform, this.mVertices.get(0)); + let upper = lower; + for (let i = 1; i < this.mCount; i++) { + let v = b2MulT2(transform, this.mVertices.get(i)); + lower = b2Min(lower, v); + upper = b2Max(upper, v); + } + let r = new B2Vec2(this.mRadius, this.mRadius); + aabb.lowerBound = subtract(lower, r); + aabb.upperBound = add(r, upper); + } + + public computeMass(density: number): B2MassData { + let center = new B2Vec2(0.0, 0.0); + let area: number = 0.0; + let mI: number = 0.0; + let s = new B2Vec2(0.0, 0.0); + for (let i = 0; i < this.mCount; i++) { + addEqual(s, this.mVertices.get(i)); + } + mulEqual(s, 1.0 / this.mVertices.count); + let inv3: number = 1.0 / float3; + for (let i = 0; i < this.mCount; i++) { + let e1: B2Vec2 = subtract(this.mVertices.get(i), s); + let e2: B2Vec2 = i + 1 < this.mCount ? subtract(this.mVertices.get(i + 1), s) : subtract(this.mVertices.get(0), s); + let aD = b2Cross(e1, e2); + let triangleArea = half * aD; + area += triangleArea; + addEqual(center, multM(add(e1, e2), inv3 * triangleArea)); + let ex1 = e1.x; + let ey1 = e1.y; + let ex2 = e2.x; + let ey2 = e2.y; + let intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2; + let inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2; + mI += float025 * inv3 * aD * (intx2 + inty2); + } + let massData = new B2MassData(); + massData.mass = density * area; + mulEqual(center, 1.0 / area); + massData.center = add(center, s); + massData.aI = density * mI; + massData.aI += massData.mass * (b2Dot22(massData.center, massData.center) - b2Dot22(center, center)); + return massData; + } + + public vertexCount(): number { + return this.mCount; + } + + public vertex(index: number): B2Vec2 { + return this.mVertices.get(index); + } + public validate(): boolean { + for (let i = 0; i < this.mVertices.count; i++) { + let i1 = i; + let i2 = i < this.mVertices.count - 1 ? i1 + 1 : 0; + let p: B2Vec2 = this.mVertices.get(i1); + let e: B2Vec2 = subtract(this.mVertices.get(i2), p); + for (let j = 0; j < this.mCount; j++) { + if (j === i1 || j === i2) { + continue; + } + let v: B2Vec2 = subtract(this.mVertices.get(j), p); + let c = b2Cross(e, v); + if (c < 0.0) { + return false; + } + } + } + return true; + } + + get vertices(): B2Array { + return this.mVertices; + } + + get count(): number { + return this.mCount; + } + + mCentroid = new B2Vec2(); + mVertices = new B2Array(); + mNormals = new B2Array(); + get mCount(): number { + return this.mVertices.count; + } +} + +export { B2ShapeType }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2BroadPhase.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2BroadPhase.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc1fe55458a18c876ed25ddca4ff8ad5473571e1 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2BroadPhase.ts @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Vec2 } from '../Common/b2Math'; +import { B2RayCastInput, B2AaBb } from './b2Collision'; +import { b2TestOverlap2 } from './b2Collision'; +import { B2QueryWrapper, B2BroadPhaseWrapper, B2RayCastWrapper } from '../Common/b2Wrappers'; +import { B2FixtureProxy } from '../Dynamics/b2Fixture'; +import { B2DynamicTree } from './b2DynamicTree'; +export class B2Pair { + public proxyIdA: number = -1; + public proxyIdB: number = -1; + constructor(proxyIdA: number = -1, proxyIdB: number = -1) { + this.proxyIdA = proxyIdA; + this.proxyIdB = proxyIdB; + } +} + +export class B2BroadPhase implements B2QueryWrapper { + public static nullProxy = -1; + public mTree = new B2DynamicTree(); + public mProxyCount: number = 0; + public mMoveBuffer: number[] = []; + public mPairBuffer: B2Pair[] = []; + public mQueryProxyId: number = 0; + + constructor() { + this.mProxyCount = 0; + this.mPairBuffer = []; + this.mMoveBuffer = []; + } + + public createProxy(aabb: B2AaBb, userData: B2FixtureProxy): number { + let proxyId = this.mTree.createProxy(aabb, userData); + this.mProxyCount += 1; + console.info('box2d','createProxy:'+proxyId) + this.bufferMove(proxyId); + return proxyId; + } + + public destroyProxy(proxyId: number): void { + this.unBufferMove(proxyId); + this.mProxyCount -= 1; + this.mTree.destroyProxy(proxyId); + } + + public moveProxy(proxyId: number, aabb: B2AaBb, displacement: B2Vec2): void { + let buffer = this.mTree.moveProxy(proxyId, aabb, displacement); + if (buffer) { + this.bufferMove(proxyId); + } + } + + public touchProxy(proxyId: number): void { + console.info('box2d','touchProxy') + this.bufferMove(proxyId); + } + + public getFatAaBb(proxyId: number): B2AaBb { + return this.mTree.getFatAaBb(proxyId); + } + + public getUserData(proxyId: number): B2FixtureProxy | null { + return this.mTree.getUserData(proxyId); + } + + public testOverlap(proxyIdA: number, proxyIdB: number): boolean { + let aabbA = this.mTree.getFatAaBb(proxyIdA); + let aabbB = this.mTree.getFatAaBb(proxyIdB); + return b2TestOverlap2(aabbA, aabbB); + } + + public getProxyCount(): number { + return this.mProxyCount; + } + + public updatePairs(callback: T): void { + this.mPairBuffer.splice(0, this.mPairBuffer.length); + console.info('box2d','022') + console.info('box2d mMoveBuffer length:',this.mMoveBuffer.length) + for (let i = 0; i < this.mMoveBuffer.length; i++) { + this.mQueryProxyId = this.mMoveBuffer[i]; + console.info('box2d','this.mQueryProxyId '+this.mQueryProxyId) + if (this.mQueryProxyId === B2BroadPhase.nullProxy) { + continue; + } + let fatAaBb = this.mTree.getFatAaBb(this.mQueryProxyId); + this.mTree.query(this, fatAaBb); + } + this.mMoveBuffer.splice(0, this.mMoveBuffer.length); + console.info('box2d','066') + this.mPairBuffer.sort((a, b) => { + console.info('box2d','055') + if (a.proxyIdA === b.proxyIdA) { + return a.proxyIdB - b.proxyIdB; + } else { + return a.proxyIdA - b.proxyIdA; + } + }); + console.info('box2d mPairBuffer.length:',this.mPairBuffer.length) + let i = 0; + while (i < this.mPairBuffer.length) { + let primaryPair = this.mPairBuffer[i]; + let userDataA = this.mTree.getUserData(primaryPair.proxyIdA)!; + let userDataB = this.mTree.getUserData(primaryPair.proxyIdB)!; + callback.addPair(userDataA, userDataB); + i += 1; + while (i < this.mPairBuffer.length) { + let pair = this.mPairBuffer[i]; + if (pair.proxyIdA !== primaryPair.proxyIdA || pair.proxyIdB !== primaryPair.proxyIdB) { + break; + } + i += 1; + } + } + } + + public query(callback: T, aabb: B2AaBb): void { + this.mTree.query(callback, aabb); + } + + public rayCast(callback: T, input: B2RayCastInput): void { + this.mTree.rayCast(callback, input); + } + + public getTreeHeight(): number { + return this.mTree.getHeight(); + } + + public getTreeBalance(): number { + return this.mTree.getMaxBalance(); + } + + public getTreeQuality(): number { + return this.mTree.getAreaRatio(); + } + + public shiftOrigin(newOrigin: B2Vec2): void { + this.mTree.shiftOrigin(newOrigin); + } + + bufferMove(proxyId: number): void { + this.mMoveBuffer.push(proxyId); + } + + unBufferMove(proxyId: number): void { + for (let i = 0; i < this.mMoveBuffer.length; i++) { + if (this.mMoveBuffer[i] === proxyId) { + this.mMoveBuffer[i] = B2BroadPhase.nullProxy; + } + } + } + + public queryCallback(proxyId: number): boolean { + console.info('box2d','queryCallback:'+proxyId+' ,this.mQueryProxyId:'+this.mQueryProxyId) + if (proxyId === this.mQueryProxyId) { + return true; + } + console.info('box2d','B2Pair') + let pair = new B2Pair(Math.min(proxyId, this.mQueryProxyId), Math.max(proxyId, this.mQueryProxyId)); + this.mPairBuffer.push(pair); + return true; + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollideCircles.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollideCircles.ts new file mode 100644 index 0000000000000000000000000000000000000000..8b968e477251f4039bfeee969af7414a9dea5e28 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollideCircles.ts @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Vec2, B2Transform } from '../Common/b2Math'; +import { b2DistanceSquared, b2MulT2, b2Dot22, add, subtract, multM, b2MulTT2 } from '../Common/b2Math'; +import { B2ManifoldType, B2ManifoldPoint } from './b2Collision'; +import { B2Manifold } from './b2Collision'; +import { B2CircleShape, B2PolygonShape } from './Shapes/b2Shape'; +import { b2Epsilon, b2MaxFloat, half } from '../Common/b2Settings'; + +function b2CollideCircles(manifold: B2Manifold, circleA: B2CircleShape, xfA: B2Transform, circleB: B2CircleShape, xfB: B2Transform): void { + manifold.points.splice(0, manifold.points.length); + let pA = b2MulT2(xfA, circleA.mPoint); + let pB = b2MulT2(xfB, circleB.mPoint); + let d = subtract(pB, pA); + let distSqr = b2Dot22(d, d); + let rA = circleA.mRadius; + let rB = circleB.mRadius; + let radius = rA + rB; + if (distSqr > radius * radius) { + return; + } + manifold.type = B2ManifoldType.CIRCLES; + manifold.localPoint = circleA.mPoint; + manifold.localNormal.setZero(); + let cp = new B2ManifoldPoint(); + cp.localPoint = circleB.mPoint; + cp.id.setZero(); + manifold.points.push(cp); +} + +function b2CollidePolygonAndCircle(manifold: B2Manifold, polygonA: B2PolygonShape, xfA: B2Transform, circleB: B2CircleShape, xfB: B2Transform): void { + manifold.points.splice(0, manifold.points.length); + let c = b2MulT2(xfB, circleB.mPoint); + let cLocal = b2MulTT2(xfA, c); + let normalIndex = 0; + let separation = -b2MaxFloat; + let radius = polygonA.mRadius + circleB.mRadius; + let vertexCount = polygonA.mCount; + let vertices = polygonA.mVertices; + let normals = polygonA.mNormals; + for (let i = 0; i < vertexCount; i++) { + let n = subtract(cLocal, vertices.get(i)); + let s = b2Dot22(normals.get(i), n); + if (s > radius) { + return; + } + if (s > separation) { + separation = s; + normalIndex = i; + } + } + let vertIndex1 = normalIndex; + let vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0; + let v1: B2Vec2 = vertices.get(vertIndex1); + let v2: B2Vec2 = vertices.get(vertIndex2); + if (separation < b2Epsilon) { + manifold.type = B2ManifoldType.FACEA; + manifold.localNormal = normals.get(normalIndex); + manifold.localPoint = multM(add(v1, v2), half); + let cp = new B2ManifoldPoint(); + cp.localPoint = circleB.mPoint; + cp.id.setZero(); + manifold.points.push(cp); + return; + } + let u1 = b2Dot22(subtract(cLocal, v1), subtract(v2, v1)); + let u2 = b2Dot22(subtract(cLocal, v2), subtract(v1, v2)); + if (u1 <= 0.0) { + if (b2DistanceSquared(cLocal, v1) > radius * radius) { + return; + } + manifold.type = B2ManifoldType.FACEA; + manifold.localNormal = subtract(cLocal, v1); + manifold.localNormal.normalize(); + manifold.localPoint = v1; + let cp = new B2ManifoldPoint(); + cp.localPoint = circleB.mPoint; + cp.id.setZero(); + manifold.points.push(cp); + } else if (u2 <= 0.0) { + if (b2DistanceSquared(cLocal, v2) > radius * radius) { + return; + } + manifold.type = B2ManifoldType.FACEA; + manifold.localNormal = subtract(cLocal, v2); + manifold.localNormal.normalize(); + manifold.localPoint = v2; + let cp = new B2ManifoldPoint(); + cp.localPoint = circleB.mPoint; + cp.id.setZero(); + manifold.points.push(cp); + } else { + let inVec = normals.get(vertIndex1); + let faceCenter = multM(add(v1, v2), half); + let separation = b2Dot22(subtract(cLocal, faceCenter), inVec); + if (separation > radius) { + return; + } + manifold.type = B2ManifoldType.FACEA; + manifold.localNormal = inVec; + manifold.localPoint = faceCenter; + let cp = new B2ManifoldPoint(); + cp.localPoint = circleB.mPoint; + cp.id.setZero(); + manifold.points.push(cp); + } + return; +} + +export { b2CollideCircles, b2CollidePolygonAndCircle }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollideEdge.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollideEdge.ts new file mode 100644 index 0000000000000000000000000000000000000000..2634c04d794faa7b8c15cc1f1a7711b94a1af5de --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollideEdge.ts @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Transform, B2Vec2, b2MulT, b2MulT2, subtract, b2Dot22, multM, b2Cross, b2MulR2, b2MulTT2, add, minus } from '../Common/b2Math'; +import { B2Manifold, B2ContactFeature, B2ContactFeatureType, B2ManifoldType, B2ManifoldPoint, B2ClipVertex, b2ClipSegmentToLine } from './b2Collision'; +import { B2EdgeShape, B2CircleShape, B2PolygonShape } from './Shapes/b2Shape'; +import { b2MaxPolygonVertices, b2PolygonRadius, b2MaxManifoldPoints, b2AngularSlop, b2MaxFloat, float2, indexTwo } from '../Common/b2Settings'; +function b2CollideEdgeAndCircle(manifold: B2Manifold, edgeA: B2EdgeShape, xfA: B2Transform, circleB: B2CircleShape, xfB: B2Transform): void { + manifold.points.splice(0, manifold.points.length); + let mQ = b2MulTT2(xfA, b2MulT2(xfB, circleB.mPoint)); + let eA: B2Vec2 = edgeA.mVertex1; + let eB: B2Vec2 = edgeA.mVertex2; + let e = subtract(eB, eA); + let u = b2Dot22(e, subtract(eB, mQ)); + let v = b2Dot22(e, subtract(mQ, eA)); + let radius = edgeA.mRadius + circleB.mRadius; + let cf = new B2ContactFeature(); + cf.indexB = 0; + cf.typeB = B2ContactFeatureType.VERTEX; + if (v <= 0.0) { + let mP = eA; + let d = subtract(mQ, mP); + let dd = b2Dot22(d, d); + if (dd > radius * radius) { + return; + } + if (edgeA.mHasVertex0) { + let aA1 = edgeA.mVertex0; + let aB1 = eA; + let e1 = subtract(aB1, aA1); + let u1 = b2Dot22(e1, subtract(aB1, mQ)); + + if (u1 > 0.0) { + return; + } + } + cf.indexA = 0; + cf.typeA = B2ContactFeatureType.VERTEX; + manifold.type = B2ManifoldType.CIRCLES; + manifold.localNormal.setZero(); + manifold.localPoint = mP; + let cp = new B2ManifoldPoint(); + cp.id.setZero(); + cp.id = cf; + cp.localPoint = circleB.mPoint; + manifold.points.push(cp); + return; + } + if (u <= 0.0) { + let mP = eB; + let d = subtract(mQ, mP); + let dd = b2Dot22(d, d); + if (dd > radius * radius) { + return; + } + if (edgeA.mHasVertex3) { + let vB2 = edgeA.mVertex3; + let vA2 = eB; + let e2 = subtract(vB2, vA2); + let v2 = b2Dot22(e2, subtract(mQ, vA2)); + if (v2 > 0.0) { + return; + } + } + cf.indexA = 1; + cf.typeA = B2ContactFeatureType.VERTEX; + manifold.type = B2ManifoldType.CIRCLES; + manifold.localNormal.setZero(); + manifold.localPoint = mP; + let cp = new B2ManifoldPoint(); + cp.id.setZero(); + cp.id = cf; + cp.localPoint = circleB.mPoint; + manifold.points.push(cp); + return; + } + let den = b2Dot22(e, e); + let vP = multM(add(multM(eA, u), multM(eB, v)), 1.0 / den); + let d = subtract(mQ, vP); + let dd = b2Dot22(d, d); + if (dd > radius * radius) { + return; + } + let n = new B2Vec2(-e.y, e.x); + if (b2Dot22(n, subtract(mQ, eA)) < 0.0) { + n.set(-n.x, -n.y); + } + n.normalize(); + cf.indexA = 0; + cf.typeA = B2ContactFeatureType.FACE; + manifold.type = B2ManifoldType.FACEA; + manifold.localNormal = n; + manifold.localPoint = eA; + let cp = new B2ManifoldPoint(); + cp.id.setZero(); + cp.id = cf; + cp.localPoint = circleB.mPoint; + manifold.points.push(cp); + return; +} + +enum B2EpAxisType { + UNKNOWN, + EDGEA, + EDGEB +} + +export class B2EpAxis { + type = B2EpAxisType.UNKNOWN; + index = 0; + separation: number = 0; +} + +export class B2TempPolygon { + vertices: B2Vec2[] = []; + normals: B2Vec2[] = []; + count = 0; + + constructor() { + for (let i = 0; i < b2MaxPolygonVertices; i++) { + this.vertices.push(new B2Vec2()); + this.normals.push(new B2Vec2()); + } + } +} + +export class B2ReferenceFace { + i1 = 0; + i2 = 0; + v1 = new B2Vec2(); + v2 = new B2Vec2(); + normal = new B2Vec2(); + sideNormal1 = new B2Vec2(); + sideOffset1: number = 0; + sideNormal2 = new B2Vec2(); + sideOffset2: number = 0; +} +export class B2EpCollider { + mPolygonB = new B2TempPolygon(); + mxf = new B2Transform(); + mCentroidB = new B2Vec2(); + mv0 = new B2Vec2(); + mv1 = new B2Vec2(); + mv2 = new B2Vec2(); + mv3 = new B2Vec2(); + mNormal0 = new B2Vec2(); + mNormal1 = new B2Vec2(); + mNormal2 = new B2Vec2(); + mNormal = new B2Vec2(); + mType1 = VertexType.ISOLATED; + mType2 = VertexType.ISOLATED; + mLowerLimit = new B2Vec2(); + mUpperLimit = new B2Vec2(); + mRadius: number = 0; + mFront = false; + + public collide(edgeA: B2EdgeShape, xfA: B2Transform, polygonB: B2PolygonShape, xfB: B2Transform): B2Manifold { + let manifold = new B2Manifold(); + this.mxf = b2MulT(xfA, xfB); + this.mCentroidB = b2MulT2(this.mxf, polygonB.mCentroid); + this.mv0 = edgeA.mVertex0; + this.mv1 = edgeA.mVertex1; + this.mv2 = edgeA.mVertex2; + this.mv3 = edgeA.mVertex3; + let hasVertex0 = edgeA.mHasVertex0; + let hasVertex3 = edgeA.mHasVertex3; + let edge1 = subtract(this.mv2, this.mv1); + edge1.normalize(); + this.mNormal1.set(edge1.y, -edge1.x); + let offset1 = b2Dot22(this.mNormal1, subtract(this.mCentroidB, this.mv1)); + let offset0: number = 0.0; + let offset2: number = 0.0; + let convex1 = false; + let convex2 = false; + if (hasVertex0) { + let edge0 = subtract(this.mv1, this.mv0); + edge0.normalize(); + this.mNormal0.set(edge0.y, -edge0.x); + convex1 = b2Cross(edge0, edge1) >= 0.0; + offset0 = b2Dot22(this.mNormal0, subtract(this.mCentroidB, this.mv0)); + } + if (hasVertex3) { + let edge2 = subtract(this.mv3, this.mv2); + edge2.normalize(); + this.mNormal2.set(edge2.y, -edge2.x); + convex2 = b2Cross(edge1, edge2) > 0.0; + offset2 = b2Dot22(this.mNormal2, subtract(this.mCentroidB, this.mv2)); + } + if (hasVertex0 && hasVertex3) { + if (convex1 && convex2) { + this.mFront = offset0 >= 0.0 || offset1 >= 0.0 || offset2 >= 0.0; + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = this.mNormal0; + this.mUpperLimit = this.mNormal2; + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = minus(this.mNormal1); + this.mUpperLimit = minus(this.mNormal1); + } + } else if (convex1) { + this.mFront = offset0 >= 0.0 || (offset1 >= 0.0 && offset2 >= 0.0); + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = this.mNormal0; + this.mUpperLimit = this.mNormal1; + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = minus(this.mNormal2); + this.mUpperLimit = minus(this.mNormal1); + } + } else if (convex2) { + this.mFront = offset2 >= 0.0 || (offset0 >= 0.0 && offset1 >= 0.0); + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = this.mNormal1; + this.mUpperLimit = this.mNormal2; + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = minus(this.mNormal1); + this.mUpperLimit = minus(this.mNormal0); + } + } else { + this.mFront = offset0 >= 0.0 && offset1 >= 0.0 && offset2 >= 0.0; + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = this.mNormal1; + this.mUpperLimit = this.mNormal1; + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = minus(this.mNormal2); + this.mUpperLimit = minus(this.mNormal0); + } + } + } else if (hasVertex0) { + if (convex1) { + this.mFront = offset0 >= 0.0 || offset1 >= 0.0; + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = this.mNormal0; + this.mUpperLimit = minus(this.mNormal1); + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = this.mNormal1; + this.mUpperLimit = minus(this.mNormal1); + } + } else { + this.mFront = offset0 >= 0.0 && offset1 >= 0.0; + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = this.mNormal1; + this.mUpperLimit = minus(this.mNormal1); + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = this.mNormal1; + this.mUpperLimit = minus(this.mNormal0); + } + } + } else if (hasVertex3) { + if (convex2) { + this.mFront = offset1 >= 0.0 || offset2 >= 0.0; + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = minus(this.mNormal1); + this.mUpperLimit = this.mNormal2; + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = minus(this.mNormal1); + this.mUpperLimit = this.mNormal1; + } + } else { + this.mFront = offset1 >= 0.0 && offset2 >= 0.0; + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = minus(this.mNormal1); + this.mUpperLimit = this.mNormal1; + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = minus(this.mNormal2); + this.mUpperLimit = this.mNormal1; + } + } + } else { + this.mFront = offset1 >= 0.0; + if (this.mFront) { + this.mNormal = this.mNormal1; + this.mLowerLimit = minus(this.mNormal1); + this.mUpperLimit = minus(this.mNormal1); + } else { + this.mNormal = minus(this.mNormal1); + this.mLowerLimit = this.mNormal1; + this.mUpperLimit = this.mNormal1; + } + } + this.mPolygonB.count = polygonB.mCount; + for (let i = 0; i < polygonB.mCount; i++) { + this.mPolygonB.vertices[i] = b2MulT2(this.mxf, polygonB.mVertices.get(i)); + this.mPolygonB.normals[i] = b2MulR2(this.mxf.q, polygonB.mNormals.get(i)); + } + this.mRadius = float2 * b2PolygonRadius; + manifold.points.splice(0, manifold.points.length); + let edgeAxis = this.computeEdgeSeparation(); + if (edgeAxis.type === B2EpAxisType.UNKNOWN) { + return manifold; + } + if (edgeAxis.separation > this.mRadius) { + return manifold; + } + let polygonAxis = this.computePolygonSeparation(); + if (polygonAxis.type !== B2EpAxisType.UNKNOWN && polygonAxis.separation > this.mRadius) { + return manifold; + } + let relativeTol: number = 0.98; + let absoluteTol: number = 0.001; + let primaryAxis: B2EpAxis; + if (polygonAxis.type === B2EpAxisType.UNKNOWN) { + primaryAxis = edgeAxis; + } else if (polygonAxis.separation > relativeTol * edgeAxis.separation + absoluteTol) { + primaryAxis = polygonAxis; + } else { + primaryAxis = edgeAxis; + } + let ie: B2ClipVertex[] = []; + for (let i = 0; i < indexTwo; i++) { + ie.push(new B2ClipVertex()); + } + let rf = new B2ReferenceFace(); + if (primaryAxis.type === B2EpAxisType.EDGEA) { + manifold.type = B2ManifoldType.FACEA; + let bestIndex = 0; + let bestValue = b2Dot22(this.mNormal, this.mPolygonB.normals[0]); + for (let i = 1; i < this.mPolygonB.count; i++) { + let value = b2Dot22(this.mNormal, this.mPolygonB.normals[i]); + if (value < bestValue) { + bestValue = value; + bestIndex = i; + } + } + let i1 = bestIndex; + let i2 = i1 + 1 < this.mPolygonB.count ? i1 + 1 : 0; + ie[0].v = this.mPolygonB.vertices[i1]; + ie[0].id.indexA = 0; + ie[0].id.indexB = i1; + ie[0].id.typeA = B2ContactFeatureType.FACE; + ie[0].id.typeB = B2ContactFeatureType.VERTEX; + ie[1].v = this.mPolygonB.vertices[i2]; + ie[1].id.indexA = 0; + ie[1].id.indexB = i2; + ie[1].id.typeA = B2ContactFeatureType.FACE; + ie[1].id.typeB = B2ContactFeatureType.VERTEX; + if (this.mFront) { + rf.i1 = 0; + rf.i2 = 1; + rf.v1 = this.mv1; + rf.v2 = this.mv2; + rf.normal = this.mNormal1; + } else { + rf.i1 = 1; + rf.i2 = 0; + rf.v1 = this.mv2; + rf.v2 = this.mv1; + rf.normal = minus(this.mNormal1); + } + } else { + manifold.type = B2ManifoldType.FACEB; + ie[0].v = this.mv1; + ie[0].id.indexA = 0; + ie[0].id.indexB = primaryAxis.index; + ie[0].id.typeA = B2ContactFeatureType.VERTEX; + ie[0].id.typeB = B2ContactFeatureType.FACE; + ie[1].v = this.mv2; + ie[1].id.indexA = 0; + ie[1].id.indexB = primaryAxis.index; + ie[1].id.typeA = B2ContactFeatureType.VERTEX; + ie[1].id.typeB = B2ContactFeatureType.FACE; + rf.i1 = primaryAxis.index; + rf.i2 = rf.i1 + 1 < this.mPolygonB.count ? rf.i1 + 1 : 0; + rf.v1 = this.mPolygonB.vertices[rf.i1]; + rf.v2 = this.mPolygonB.vertices[rf.i2]; + rf.normal = this.mPolygonB.normals[rf.i1]; + } + rf.sideNormal1.set(rf.normal.y, -rf.normal.x); + rf.sideNormal2 = minus(rf.sideNormal1); + rf.sideOffset1 = b2Dot22(rf.sideNormal1, rf.v1); + rf.sideOffset2 = b2Dot22(rf.sideNormal2, rf.v2); + let clipPoints1 = b2ClipSegmentToLine(ie, rf.sideNormal1, rf.sideOffset1, rf.i1); + if (clipPoints1.length < b2MaxManifoldPoints) { + return manifold; + } + let clipPoints2 = b2ClipSegmentToLine(clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2); + if (clipPoints2.length < b2MaxManifoldPoints) { + return manifold; + } + if (primaryAxis.type === B2EpAxisType.EDGEA) { + manifold.localNormal = rf.normal; + manifold.localPoint = rf.v1; + } else { + manifold.localNormal = polygonB.mNormals.get(rf.i1); + manifold.localPoint = polygonB.mVertices.get(rf.i1); + } + for (let i = 0; i < b2MaxManifoldPoints; i++) { + let separation = b2Dot22(rf.normal, subtract(clipPoints2[i].v, rf.v1)); + if (separation <= this.mRadius) { + let cp = new B2ManifoldPoint(); + if (primaryAxis.type === B2EpAxisType.EDGEA) { + cp.localPoint = b2MulTT2(this.mxf, clipPoints2[i].v); + cp.id = clipPoints2[i].id; + } else { + cp.localPoint = clipPoints2[i].v; + cp.id.typeA = clipPoints2[i].id.typeB; + cp.id.typeB = clipPoints2[i].id.typeA; + cp.id.indexA = clipPoints2[i].id.indexB; + cp.id.indexB = clipPoints2[i].id.indexA; + } + manifold.points.push(cp); + } + } + return manifold; + } + public computeEdgeSeparation(): B2EpAxis { + let axis = new B2EpAxis(); + axis.type = B2EpAxisType.EDGEA; + axis.index = this.mFront ? 0 : 1; + axis.separation = b2MaxFloat; + for (let i = 0; i < this.mPolygonB.count; i++) { + let s = b2Dot22(this.mNormal, subtract(this.mPolygonB.vertices[i], this.mv1)); + if (s < axis.separation) { + axis.separation = s; + } + } + return axis; + } + + public computePolygonSeparation(): B2EpAxis { + let axis = new B2EpAxis(); + axis.type = B2EpAxisType.UNKNOWN; + axis.index = -1; + axis.separation = -b2MaxFloat; + let perp = new B2Vec2(-this.mNormal.y, this.mNormal.x); + for (let i = 0; i < this.mPolygonB.count; i++) { + let n = minus(this.mPolygonB.normals[i]); + let s1 = b2Dot22(n, subtract(this.mPolygonB.vertices[i], this.mv1)); + let s2 = b2Dot22(n, subtract(this.mPolygonB.vertices[i], this.mv2)); + let s = Math.min(s1, s2); + if (s > this.mRadius) { + axis.type = B2EpAxisType.EDGEB; + axis.index = i; + axis.separation = s; + return axis; + } + if (b2Dot22(n, perp) >= 0.0) { + if (b2Dot22(subtract(n, this.mUpperLimit), this.mNormal) < -b2AngularSlop) { + continue; + } + } else { + if (b2Dot22(subtract(n, this.mLowerLimit), this.mNormal) < -b2AngularSlop) { + continue; + } + } + if (s > axis.separation) { + axis.type = B2EpAxisType.EDGEB; + axis.index = i; + axis.separation = s; + } + } + return axis; + } +} + +enum VertexType { + ISOLATED, + CONCAVE, + CONVEX +} + +function b2CollideEdgeAndPolygon(manifold: B2Manifold, edgeA: B2EdgeShape, xfA: B2Transform, polygonB: B2PolygonShape, xfB: B2Transform): void { + let collider = new B2EpCollider(); + manifold = collider.collide(edgeA, xfA, polygonB, xfB); +} + +export { b2CollideEdgeAndCircle, b2CollideEdgeAndPolygon }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollidePolygon.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollidePolygon.ts new file mode 100644 index 0000000000000000000000000000000000000000..f016c301b5f90b2de9ffe1538f0e01d92795b196 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2CollidePolygon.ts @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Transform, B2Vec2 } from '../Common/b2Math'; +import { b2MulT, b2MulR2, b2MulT2, b2MulTR2, b2Dot22, subtract, add, b2Cross12, multM, b2MulTT2, minus } from '../Common/b2Math'; +import { B2PolygonShape } from './Shapes/b2Shape'; +import { B2Manifold } from './b2Collision'; +import { B2ClipVertex, B2ContactFeatureType, b2ClipSegmentToLine, B2ManifoldPoint, B2ManifoldType } from './b2Collision'; +import { b2MaxFloat, b2LinearSlop, b2MaxManifoldPoints, float2, half, indexTwo } from '../Common/b2Settings'; + +function b2FindMaxSeparation(poly1: B2PolygonShape, xf1: B2Transform, poly2: B2PolygonShape, xf2: B2Transform): [edgeIndex: number, maxSeparation: number] { + let count1 = poly1.mCount; + let count2 = poly2.mCount; + let n1s = poly1.mNormals; + let v1s = poly1.mVertices; + let v2s = poly2.mVertices; + let xf = b2MulT(xf2, xf1); + let bestIndex = 0; + let maxSeparation = -b2MaxFloat; + for (let i = 0; i < count1; i++) { + let n = b2MulR2(xf.q, n1s.get(i)); + let v1 = b2MulT2(xf, v1s.get(i)); + let si = b2MaxFloat; + for (let j = 0; j < count2; j++) { + let sij = b2Dot22(n, subtract(v2s.get(j), v1)); + if (sij < si) { + si = sij; + } + } + if (si > maxSeparation) { + maxSeparation = si; + bestIndex = i; + } + } + return [bestIndex, maxSeparation]; +} + +function b2FindIncidentEdge(poly1: B2PolygonShape, xf1: B2Transform, edge1: number, poly2: B2PolygonShape, xf2: B2Transform): B2ClipVertex[] { + let normals1 = poly1.mNormals; + let count2 = poly2.mCount; + let vertices2 = poly2.mVertices; + let normals2 = poly2.mNormals; + let normal1 = b2MulTR2(xf2.q, b2MulR2(xf1.q, normals1.get(edge1))); + let index = 0; + let minDot = b2MaxFloat; + for (let i = 0; i < count2; i++) { + let dot = b2Dot22(normal1, normals2.get(i)); + if (dot < minDot) { + minDot = dot; + index = i; + } + } + let i1 = index; + let i2 = i1 + 1 < count2 ? i1 + 1 : 0; + let c: B2ClipVertex[] = []; + for (let i = 0; i < float2; i++) { + c.push(new B2ClipVertex()); + } + c[0].v = b2MulT2(xf2, vertices2.get(i1)); + c[0].id.indexA = edge1; + c[0].id.indexB = i1; + c[0].id.typeA = B2ContactFeatureType.FACE; + c[0].id.typeB = B2ContactFeatureType.VERTEX; + c[1].v = b2MulT2(xf2, vertices2.get(i2)); + c[1].id.indexA = edge1; + c[1].id.indexB = i2; + c[1].id.typeA = B2ContactFeatureType.FACE; + c[1].id.typeB = B2ContactFeatureType.VERTEX; + return c; +} + +function b2CollidePolygons(manifold: B2Manifold, polyA: B2PolygonShape, xfA: B2Transform, polyB: B2PolygonShape, xfB: B2Transform): void { + manifold.points.splice(0, manifold.points.length); + let totalRadius = polyA.mRadius + polyB.mRadius; + let fm = b2FindMaxSeparation(polyA, xfA, polyB, xfB); + let edgeA = fm[0]; + let separationA = fm[1]; + if (separationA > totalRadius) { + return; + } + let fmb = b2FindMaxSeparation(polyB, xfB, polyA, xfA); + let edgeB = fmb[0]; + let separationB = fmb[1]; + if (separationB > totalRadius) { + return; + } + let ratio: number = 0.1; + let kTol: number = ratio * b2LinearSlop; + let speAb = separationB > separationA + kTol; + let poly1: B2PolygonShape = speAb ? polyB : polyA; + let poly2: B2PolygonShape = speAb ? polyA : polyB; + let xf1: B2Transform = speAb ? xfB : xfA; + let xf2: B2Transform = speAb ? xfA : xfB; + let edge1: number = speAb ? edgeB : edgeA; + let flip: boolean = speAb ? true : false; + manifold.type = speAb ? B2ManifoldType.FACEB : B2ManifoldType.FACEA; + let incidentEdge = b2FindIncidentEdge(poly1, xf1, edge1, poly2, xf2); + let vertices1 = poly1.mVertices; + let iv2 = edge1 + 1 < poly1.mCount ? edge1 + 1 : 0; + let v11: B2Vec2 = vertices1.get(edge1); + let v12: B2Vec2 = vertices1.get(iv2); + let localTangent = subtract(v12, v11); + localTangent.normalize(); + let localNormal = b2Cross12(localTangent, 1.0); + let planePoint = multM(add(v11, v12), half); + let tangent = b2MulR2(xf1.q, localTangent); + let normal = b2Cross12(tangent, 1.0); + v11 = b2MulT2(xf1, v11); + v12 = b2MulT2(xf1, v12); + let frontOffset = b2Dot22(normal, v11); + let sideOffset1 = -b2Dot22(tangent, v11) + totalRadius; + let sideOffset2 = b2Dot22(tangent, v12) + totalRadius; + let clipPoints1 = b2ClipSegmentToLine(incidentEdge, minus(tangent), sideOffset1, edge1); + if (clipPoints1.length < indexTwo) { + return; + } + let clipPoints2 = b2ClipSegmentToLine(clipPoints1, tangent, sideOffset2, iv2); + if (clipPoints2.length < indexTwo) { + return; + } + manifold.localNormal = localNormal; + manifold.localPoint = planePoint; + for (let i = 0; i < b2MaxManifoldPoints; i++) { + let separation = b2Dot22(normal, clipPoints2[i].v) - frontOffset; + if (separation <= totalRadius) { + let cp = new B2ManifoldPoint(); + cp.localPoint = b2MulTT2(xf2, clipPoints2[i].v); + cp.id = clipPoints2[i].id; + if (flip) { + let cf = cp.id; + cp.id.indexA = cf.indexB; + cp.id.indexB = cf.indexA; + cp.id.typeA = cf.typeB; + cp.id.typeB = cf.typeA; + } + manifold.points.push(cp); + } + } + return; +} +export { b2CollidePolygons }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2Collision.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2Collision.ts new file mode 100644 index 0000000000000000000000000000000000000000..a86c7857f2b691c17cb8c4524b65c76858706825 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2Collision.ts @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Transform } from '../Common/b2Math'; +import { B2Vec2, b2MulT2, b2DistanceSquared, b2Dot22, b2MulR2, b2Min, b2Max, b2Abs2, add, subtract, multM, minus } from '../Common/b2Math'; +import { B2Shape } from './Shapes/b2Shape'; +import { B2DistanceInput, B2SimplexCache, B2DistanceOutput, b2DistanceM } from './b2Distance'; +import { b2MaxManifoldPoints, b2MinFloat, b2MaxFloat, b2Epsilon, indexTwo } from '../Common/b2Settings'; +import { float2 } from '../Common/b2Settings'; +import { half } from '../Common/b2Settings'; +import { float10 } from '../Common/b2Settings'; +enum B2ContactFeatureType { + VERTEX = 0, + FACE = 1 +} + +export class B2ContactFeature { + public indexA: number = 0; + public indexB: number = 0; + public typeA: B2ContactFeatureType = B2ContactFeatureType.VERTEX; + public typeB: B2ContactFeatureType = B2ContactFeatureType.VERTEX; + constructor() {} + public setZero(): void { + this.indexA = 0; + this.indexB = 0; + this.typeA = B2ContactFeatureType.VERTEX; + this.typeB = B2ContactFeatureType.VERTEX; + } +} + +export class B2ManifoldPoint { + localPoint = new B2Vec2(); + public normalImpulse: number = 0.0; + public tangentImpulse: number = 0.0; + public id = new B2ContactFeature(); + constructor(copyFrom: B2ManifoldPoint | null = null) { + if (copyFrom !== null) { + this.localPoint = copyFrom.localPoint; + this.normalImpulse = copyFrom.normalImpulse; + this.tangentImpulse = copyFrom.tangentImpulse; + this.id = copyFrom.id; + } + } +} + +enum B2ManifoldType { + CIRCLES, + FACEA, + FACEB +} + +export class B2Manifold { + public points: B2ManifoldPoint[] = []; + public localNormal = new B2Vec2(); + public localPoint = new B2Vec2(); + public type = B2ManifoldType.CIRCLES; + public get pointCount(): number { + return this.points.length; + } + constructor(copyFrom: B2Manifold | null = null) { + if (copyFrom !== null) { + this.points = []; + for (let i = 0; i < copyFrom.points.length; i++) { + this.points.push(new B2ManifoldPoint(copyFrom.points[i])); + } + this.localNormal = copyFrom.localNormal; + this.localPoint = copyFrom.localPoint; + this.type = copyFrom.type; + } + } +} + +export class B2WorldManifold { + public normal = new B2Vec2(); + public points: B2Vec2[]; + public separations: number[]; + constructor() { + this.points = []; + this.separations = []; + for (let i = 0; i < b2MaxManifoldPoints; i++) { + this.points.push(new B2Vec2(0, 0)); + this.separations.push(0); + } + } + public initialize(manifold: B2Manifold, xfA: B2Transform, radiusA: number, xfB: B2Transform, radiusB: number): void { + if (manifold.pointCount === 0) { + return; + } + switch (manifold.type) { + case B2ManifoldType.CIRCLES: { + this.normal.set(1.0, 0.0); + let pointA = b2MulT2(xfA, manifold.localPoint); + let pointB = b2MulT2(xfB, manifold.points[0].localPoint); + if (b2DistanceSquared(pointA, pointB) > b2Epsilon * b2Epsilon) { + this.normal = subtract(pointB, pointA); + this.normal.normalize(); + } + let cA = add(pointA, multM(this.normal, radiusA)); + let cB = subtract(pointB, multM(this.normal, radiusB)); + this.points[0] = multM(add(cA, cB), half); + this.separations[0] = b2Dot22(subtract(cB, cA), this.normal); + break; + } + case B2ManifoldType.FACEA: { + this.normal = b2MulR2(xfA.q, manifold.localNormal); + let planePointA = b2MulT2(xfA, manifold.localPoint); + for (let i = 0; i < manifold.pointCount; i++) { + let clipPoint = b2MulT2(xfB, manifold.points[i].localPoint); + let ra = radiusA - b2Dot22(subtract(clipPoint, planePointA), this.normal); + let cA = add(clipPoint, multM(this.normal, ra)); + let cB = subtract(clipPoint, multM(this.normal, radiusB)); + this.points[i] = multM(add(cA, cB), half); + this.separations[i] = b2Dot22(subtract(cB, cA), this.normal); + } + break; + } + case B2ManifoldType.FACEB: { + this.normal = b2MulR2(xfB.q, manifold.localNormal); + let planePointB = b2MulT2(xfB, manifold.localPoint); + for (let i = 0; i < manifold.pointCount; i++) { + let clipPoint = b2MulT2(xfA, manifold.points[i].localPoint); + let rb = radiusB - b2Dot22(subtract(clipPoint, planePointB), this.normal); + let cB = add(clipPoint, multM(this.normal, rb)); + let cA = subtract(clipPoint, multM(this.normal, radiusA)); + this.points[i] = multM(add(cA, cB), half); + this.separations[i] = b2Dot22(subtract(cA, cB), this.normal); + } + this.normal = minus(this.normal); + break; + } + default: + break; + } + } +} + +export class B2ClipVertex { + constructor() {} + public v = new B2Vec2(); + public id = new B2ContactFeature(); +} + +export class B2RayCastInput { + constructor() {} + public p1 = new B2Vec2(); + public p2 = new B2Vec2(); + public maxFraction: number = 0; +} + +export class B2RayCastOutput { + constructor() {} + public normal = new B2Vec2(); + public fraction: number = 0; +} + +export class B2AaBb { + public lowerBound = new B2Vec2(); + public upperBound = new B2Vec2(); + constructor(lowerBound: B2Vec2 | null = null, upperBound: B2Vec2 | null = null) { + if (lowerBound !== null && upperBound !== null) { + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + } + + public isValid(): boolean { + let d = subtract(this.upperBound, this.lowerBound); + let valid = d.x >= 0.0 && d.y >= 0.0; + valid = valid && this.lowerBound.isValid() && this.upperBound.isValid(); + return valid; + } + + get center(): B2Vec2 { + return multM(add(this.lowerBound, this.upperBound), half); + } + + get extents(): B2Vec2 { + return multM(subtract(this.upperBound, this.lowerBound), half); + } + + get perimeter(): number { + let wx = this.upperBound.x - this.lowerBound.x; + let wy = this.upperBound.y - this.lowerBound.y; + return float2 * (wx + wy); + } + + public combine(aabb1: B2AaBb, aabb2: B2AaBb): void { + this.lowerBound = b2Min(aabb1.lowerBound, aabb2.lowerBound); + this.upperBound = b2Max(aabb1.upperBound, aabb2.upperBound); + } + + public contains(aabb: B2AaBb): boolean { + let result = true; + result = result && this.lowerBound.x <= aabb.lowerBound.x; + result = result && this.lowerBound.y <= aabb.lowerBound.y; + result = result && aabb.upperBound.x <= this.upperBound.x; + result = result && aabb.upperBound.y <= this.upperBound.y; + return result; + } + + public rayCast(input: B2RayCastInput): B2RayCastOutput | null { + let tmin = b2MinFloat; + let tmax = b2MaxFloat; + let p = input.p1; + let d = subtract(input.p2, input.p1); + let absD: B2Vec2 = b2Abs2(d); + let normal = new B2Vec2(); + for (let i = 0; i < indexTwo; i++) { + if (absD.getSubscript(i) < b2Epsilon) { + if (p.getSubscript(i) < this.lowerBound.getSubscript(i) || this.upperBound.getSubscript(i) < p.getSubscript(i)) { + return null; + } + } else { + let invd = 1.0 / d.getSubscript(i); + let t1 = (this.lowerBound.getSubscript(i) - p.getSubscript(i)) * invd; + let t2 = (this.upperBound.getSubscript(i) - p.getSubscript(i)) * invd; + let s: number = -1.0; + if (t1 > t2) { + t1 = t2; + t2 = t1; + s = 1.0; + } + if (t1 > tmin) { + normal.setZero(); + normal.setSubscript(i, s); + tmin = t1; + } + tmax = Math.min(tmax, t2); + if (tmin > tmax) { + return null; + } + } + } + if (tmin < 0.0 || input.maxFraction < tmin) { + return null; + } + let output = new B2RayCastOutput(); + output.fraction = tmin; + output.normal = normal; + return output; + } +} + +function b2ClipSegmentToLine(vIn: B2ClipVertex[], normal: B2Vec2, offset: number, vertexIndexA: number): B2ClipVertex[] { + let vOut: B2ClipVertex[] = []; + let distance0 = b2Dot22(normal, vIn[0].v) - offset; + let distance1 = b2Dot22(normal, vIn[1].v) - offset; + if (distance0 <= 0.0) { + vOut.push(vIn[0]); + } + if (distance1 <= 0.0) { + vOut.push(vIn[1]); + } + if (distance0 * distance1 < 0.0) { + let interp = distance0 / (distance0 - distance1); + let v = new B2ClipVertex(); + v.v = add(vIn[0].v, multM(subtract(vIn[1].v, vIn[0].v), interp)); + v.id.indexA = vertexIndexA; + v.id.indexB = vIn[0].id.indexB; + v.id.typeA = B2ContactFeatureType.VERTEX; + v.id.typeB = B2ContactFeatureType.FACE; + vOut.push(v); + } + return vOut; +} + +function b2TestOverlap(shapeA: B2Shape, shapeB: B2Shape, xfA: B2Transform, xfB: B2Transform): boolean { + let input = new B2DistanceInput(); + input.proxyA.set(shapeA); + input.proxyB.set(shapeB); + input.transformA = xfA; + input.transformB = xfB; + input.useRadii = true; + let cache = new B2SimplexCache(); + cache.count = 0; + let output = new B2DistanceOutput(); + b2DistanceM(output, cache, input); + return output.distance < float10 * b2Epsilon; +} + +function b2TestOverlap2(a: B2AaBb, b: B2AaBb): boolean { + let d1 = subtract(b.lowerBound, a.upperBound); + let d2 = subtract(a.lowerBound, b.upperBound); + if (d1.x > 0.0 || d1.y > 0.0) { + return false; + } + if (d2.x > 0.0 || d2.y > 0.0) { + return false; + } + return true; +} + +export { B2ManifoldType, b2TestOverlap, b2ClipSegmentToLine, b2TestOverlap2, B2ContactFeatureType }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2Distance.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2Distance.ts new file mode 100644 index 0000000000000000000000000000000000000000..e98073b635bbb0f95d61a778545ca24de8047833 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2Distance.ts @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + B2Vec2, + B2Transform, + b2Dot22, + subtractEqual, + b2Cross, + b2Distance, + b2Vec2Zero, + b2Cross21, + b2Cross12, + b2MulT2, + b2MulTR2, + multM, + add, + subtract, + addEqual, + minus +} from '../Common/b2Math'; +import { B2Shape, B2CircleShape, B2PolygonShape, B2EdgeShape } from './Shapes/b2Shape'; +import { b2Epsilon, b2MaxFloat, b2MaxManifoldPoints, indexThr, indexTwo } from '../Common/b2Settings'; +import { B2Array } from '../Dynamics/b2TimeStep'; +import { float2 } from '../Common/b2Settings'; +import { half } from '../Common/b2Settings'; + +export class B2DistanceProxy { + public mBuffer: B2Array = new B2Array(float2, new B2Vec2()); + public mVertices: B2Array = new B2Array(1, new B2Vec2()); + public mCount = 0; + public mRadius: number = 0; + public constructor(shape: B2Shape | null = null) { + if (shape !== null) { + this.set(shape); + } + } + + public set(shape: B2Shape): void { + switch (shape.type.name) { + case 'CIRCLE': { + let circle = shape as B2CircleShape; + this.mVertices = circle.mp; + this.mCount = 1; + this.mRadius = circle.mRadius; + break; + } + case 'POLYGON': { + let polygon = shape as B2PolygonShape; + this.mVertices = polygon.mVertices; + this.mCount = polygon.mCount; + this.mRadius = polygon.mRadius; + break; + } + case 'EDGE': { + let edge = shape as B2EdgeShape; + this.mVertices = edge.mVertices; + this.mCount = b2MaxManifoldPoints; + this.mRadius = edge.mRadius; + break; + } + default: + break; + } + } + + public getSupport(d: B2Vec2): number { + let bestIndex = 0; + let bestValue = b2Dot22(this.mVertices.get(0), d); + for (let i = 1; i < this.mCount; i++) { + let value = b2Dot22(this.mVertices!.get(i), d); + if (value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return bestIndex; + } + + public getSupportVertex(d: B2Vec2): B2Vec2 { + let bestIndex = 0; + let bestValue = b2Dot22(this.mVertices.get(0), d); + for (let i = 1; i < this.mCount; i++) { + let value = b2Dot22(this.mVertices!.get(i), d); + if (value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return this.mVertices!.get(bestIndex); + } + + public get getVertexCount(): number { + return this.mCount; + } + + public getVertex(index: number): B2Vec2 { + return this.mVertices.get(index); + } +} + +export class B2SimplexVertex { + wA = new B2Vec2(); + wB = new B2Vec2(); + w = new B2Vec2(); + a: number = 0; + indexA: number = 0; + indexB: number = 0; + set(other: B2SimplexVertex): void { + this.wA = other.wA; + this.wB = other.wB; + this.w = other.w; + this.a = other.a; + this.indexA = other.indexA; + this.indexB = other.indexB; + } +} + +export class B2Simplex { + mCount: number = 0; + constructor() { + for (let i = 0; i < indexThr; i++) { + this.mv.push(new B2SimplexVertex()); + } + } + + readCache(cache: B2SimplexCache, proxyA: B2DistanceProxy, transformA: B2Transform, proxyB: B2DistanceProxy, transformB: B2Transform): void { + this.mCount = cache.count; + for (let i = 0; i < this.mCount; i++) { + let v = this.mv[i]; + v.indexA = cache.indexA[i]; + v.indexB = cache.indexB[i]; + let wALocal = proxyA.getVertex(v.indexA); + let wBLocal = proxyB.getVertex(v.indexB); + v.wA = b2MulT2(transformA, wALocal); + v.wB = b2MulT2(transformB, wBLocal); + v.w = subtract(v.wB, v.wA); + v.a = 0.0; + } + if (this.mCount > 1) { + let metric1 = cache.metric; + let metric2 = this.getMetric(); + if (metric2 < half * metric1 || float2 * metric1 < metric2 || metric2 < b2Epsilon) { + this.mCount = 0; + } + } + if (this.mCount === 0) { + let v = this.mv[0]; + v.indexA = 0; + v.indexB = 0; + let wALocal = proxyA.getVertex(0); + let wBLocal = proxyB.getVertex(0); + v.wA = b2MulT2(transformA, wALocal); + v.wB = b2MulT2(transformB, wBLocal); + v.w = subtract(v.wB, v.wA); + v.a = 1.0; + this.mCount = 1; + } + } + + writeCache(cache: B2SimplexCache): void { + cache.metric = this.getMetric(); + cache.count = this.mCount; + for (let i = 0; i < this.mCount; i++) { + cache.indexA[i] = this.mv[i].indexA; + cache.indexB[i] = this.mv[i].indexB; + } + } + + getSearchDirection(): B2Vec2 { + switch (this.mCount) { + case 1: + return minus(this.mv1.w); + case indexTwo: { + let e12 = subtract(this.mv2.w, this.mv1.w); + let sgn = b2Cross(e12, minus(this.mv1.w)); + if (sgn > 0.0) { + return b2Cross21(1.0, e12); + } else { + return b2Cross12(e12, 1.0); + } + } + default: + return b2Vec2Zero; + } + } + + getClosestPoint(): B2Vec2 { + switch (this.mCount) { + case 0: + return b2Vec2Zero; + case 1: + return this.mv1.w; + case indexTwo: + return add(multM(this.mv1.w, this.mv1.a), multM(this.mv2.w, this.mv2.a)); + case indexThr: + return b2Vec2Zero; + default: + return b2Vec2Zero; + } + } + + getWitnessPoints(): [B2Vec2, B2Vec2] { + let pA = new B2Vec2(); + let pB = new B2Vec2(); + switch (this.mCount) { + case 0: + throw new Error('illegal state'); + case 1: + pA = this.mv1.wA; + pB = this.mv1.wB; + break; + case indexTwo: + pA = add(multM(this.mv1.wA, this.mv1.a), multM(this.mv2.wA, this.mv2.a)); + pB = add(multM(this.mv1.wB, this.mv1.a), multM(this.mv2.wB, this.mv2.a)); + break; + case indexThr: + pA = add(add(multM(this.mv1.wA, this.mv1.a), multM(this.mv2.wA, this.mv2.a)), multM(this.mv3.wA, this.mv3.a)); + pB = pA; + break; + default: + throw new Error('illegal state'); + } + return [pA, pB]; + } + + getMetric(): number { + switch (this.mCount) { + case 0: + return 0.0; + case 1: + return 0.0; + case indexTwo: + return b2Distance(this.mv1.w, this.mv2.w); + case indexThr: + return b2Cross(subtract(this.mv2.w, this.mv1.w), subtract(this.mv3.w, this.mv1.w)); + default: + return 0.0; + } + } + + solve2(): void { + let w1 = this.mv1.w; + let w2 = this.mv2.w; + let e12 = subtract(w2, w1); + let d122 = -b2Dot22(w1, e12); + if (d122 <= 0.0) { + this.mv1.a = 1.0; + this.mCount = 1; + return; + } + let d121 = b2Dot22(w2, e12); + if (d121 <= 0.0) { + this.mv2.a = 1.0; + this.mCount = 1; + this.mv1 = this.mv2; + return; + } + let invd12 = 1.0 / (d121 + d122); + this.mv1.a = d121 * invd12; + this.mv2.a = d122 * invd12; + this.mCount = indexTwo; + } + + solve3(): void { + let w1 = this.mv1.w; + let w2 = this.mv2.w; + let w3 = this.mv3.w; + let e12 = subtract(w2, w1); + let w1e12 = b2Dot22(w1, e12); + let w2e12 = b2Dot22(w2, e12); + let d121 = w2e12; + let d122 = -w1e12; + let e13 = subtract(w3, w1); + let w1e13 = b2Dot22(w1, e13); + let w3e13 = b2Dot22(w3, e13); + let d131 = w3e13; + let d132 = -w1e13; + let e23 = subtract(w3, w2); + let w2e23 = b2Dot22(w2, e23); + let w3e23 = b2Dot22(w3, e23); + let d231 = w3e23; + let d232 = -w2e23; + let n123 = b2Cross(e12, e13); + let d1231 = n123 * b2Cross(w2, w3); + let d1232 = n123 * b2Cross(w3, w1); + let d1233 = n123 * b2Cross(w1, w2); + if (d122 <= 0.0 && d132 <= 0.0) { + this.mv1.a = 1.0; + this.mCount = 1; + return; + } + if (d121 > 0.0 && d122 > 0.0 && d1233 <= 0.0) { + let invd12 = 1.0 / (d121 + d122); + this.mv1.a = d121 * invd12; + this.mv2.a = d122 * invd12; + this.mCount = indexTwo; + return; + } + if (d131 > 0.0 && d132 > 0.0 && d1232 <= 0.0) { + let invd13 = 1.0 / (d131 + d132); + this.mv1.a = d131 * invd13; + this.mv3.a = d132 * invd13; + this.mCount = indexTwo; + this.mv2 = this.mv3; + return; + } + if (d121 <= 0.0 && d232 <= 0.0) { + this.mv2.a = 1.0; + this.mCount = 1; + this.mv1 = this.mv2; + return; + } + if (d131 <= 0.0 && d231 <= 0.0) { + this.mv3.a = 1.0; + this.mCount = 1; + this.mv1 = this.mv3; + return; + } + if (d231 > 0.0 && d232 > 0.0 && d1231 <= 0.0) { + let invd23 = 1.0 / (d231 + d232); + this.mv2.a = d231 * invd23; + this.mv3.a = d232 * invd23; + this.mCount = indexTwo; + this.mv1 = this.mv3; + return; + } + let invd123 = 1.0 / (d1231 + d1232 + d1233); + this.mv1.a = d1231 * invd123; + this.mv2.a = d1232 * invd123; + this.mv3.a = d1233 * invd123; + this.mCount = indexThr; + } + + mv: B2SimplexVertex[] = []; + get mv1(): B2SimplexVertex { + return this.mv[0]; + } + + set mv1(newValue) { + this.mv[0].set(newValue); + } + + get mv2(): B2SimplexVertex { + return this.mv[1]; + } + + set mv2(newValue) { + this.mv[1].set(newValue); + } + + get mv3(): B2SimplexVertex { + return this.mv[float2]; + } + + set mv3(newValue) { + this.mv[float2].set(newValue); + } +} + +export class B2SimplexCache { + public metric: number; + public count: number; + public indexA: number[]; + public indexB: number[]; + public constructor() { + this.metric = 0; + this.count = 0; + this.indexA = [0, 0, 0]; + this.indexB = [0, 0, 0]; + } +} + +export class B2DistanceInput { + public proxyA: B2DistanceProxy; + public proxyB: B2DistanceProxy; + public transformA: B2Transform; + public transformB: B2Transform; + public useRadii: boolean; + public constructor() { + this.proxyA = new B2DistanceProxy(); + this.proxyB = new B2DistanceProxy(); + this.transformA = new B2Transform(); + this.transformB = new B2Transform(); + this.useRadii = false; + } +} + +export class B2DistanceOutput { + public pointA: B2Vec2; + public pointB: B2Vec2; + public distance: number; + public iterations: number; + public constructor() { + this.pointA = new B2Vec2(); + this.pointB = new B2Vec2(); + this.distance = 0; + this.iterations = 0; + } +} + +function b2DistanceM(output: B2DistanceOutput, cache: B2SimplexCache, input: B2DistanceInput): void { + let proxyA = input.proxyA; + let proxyB = input.proxyB; + let transformA = input.transformA; + let transformB = input.transformB; + let simplex = new B2Simplex(); + simplex.readCache(cache, proxyA, transformA, proxyB, transformB); + let kMaxIters = 20; + let saveA = [0, 0, 0]; + let saveB = [0, 0, 0]; + let saveCount = 0; + let distanceSqr1 = b2MaxFloat; + let distanceSqr2 = distanceSqr1; + let iter = 0; + while (iter < kMaxIters) { + saveCount = simplex.mCount; + for (let i = 0; i < saveCount; i++) { + saveA[i] = simplex.mv[i].indexA; + saveB[i] = simplex.mv[i].indexB; + } + switch (simplex.mCount) { + case 1: + break; + case indexTwo: + simplex.solve2(); + break; + case indexThr: + simplex.solve3(); + break; + default: + break; + } + if (simplex.mCount === indexThr) { + break; + } + let p = simplex.getClosestPoint(); + distanceSqr2 = p.lengthSquared(); + distanceSqr1 = distanceSqr2; + let d = simplex.getSearchDirection(); + if (d.lengthSquared() < b2Epsilon * b2Epsilon) { + break; + } + let vertex = simplex.mv[simplex.mCount]; + vertex.indexA = proxyA.getSupport(b2MulTR2(transformA.q, minus(d))); + vertex.wA = b2MulT2(transformA, proxyA.getVertex(vertex.indexA)); + vertex.indexB = proxyB.getSupport(b2MulTR2(transformB.q, d)); + vertex.wB = b2MulT2(transformB, proxyB.getVertex(vertex.indexB)); + vertex.w = subtract(vertex.wB, vertex.wA); + iter += 1; + let duplicate = false; + for (let i = 0; i < saveCount; i++) { + if (vertex.indexA === saveA[i] && vertex.indexB === saveB[i]) { + duplicate = true; + break; + } + } + if (duplicate) { + break; + } + simplex.mCount += 1; + } + b2GjkMaxIters = Math.max(b2GjkMaxIters, iter); + let sget = simplex.getWitnessPoints(); + output.pointA = sget[0]; + output.pointB = sget[1]; + output.distance = b2Distance(output.pointA, output.pointB); + output.iterations = iter; + simplex.writeCache(cache); + if (input.useRadii) { + let rA = proxyA.mRadius; + let rB = proxyB.mRadius; + if (output.distance > rA + rB && output.distance > b2Epsilon) { + output.distance -= rA + rB; + let normal = subtract(output.pointB, output.pointA); + normal.normalize(); + addEqual(output.pointA, multM(normal, rA)); + subtractEqual(output.pointB, multM(normal, rB)); + } else { + let p = multM(add(output.pointA, output.pointB), half); + output.pointA = p; + output.pointB = p; + output.distance = 0.0; + } + } + return; +} + +let b2GjkMaxIters: number = 0; + +export { b2DistanceM }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2DynamicTree.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2DynamicTree.ts new file mode 100644 index 0000000000000000000000000000000000000000..ceea3bdfae16d198c2d84bd5804678df2ddff607 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2DynamicTree.ts @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2AaBb, B2RayCastInput, b2TestOverlap2 } from './b2Collision'; +import { B2Vec2, subtract, add, multM, b2Cross21, b2Abs2, b2Min, b2Max, b2Dot22, subtractEqual } from '../Common/b2Math'; +import { B2QueryWrapper, B2RayCastWrapper } from '../Common/b2Wrappers'; +import { b2AaBbExtension, b2AaBbMultiplier, b2MaxFloat, indexTwo } from '../Common/b2Settings'; +import { float2 } from '../Common/b2Settings'; + +let b2NullNode = -1; +export class B2TreeNode { + public aabb = new B2AaBb(); + public userData: T | null = null; + parentOrNext: number = b2NullNode; + child1: number = b2NullNode; + child2: number = b2NullNode; + height: number = -1; + isLeaf(): boolean { + return this.child1 === b2NullNode; + } +} + +export class B2DynamicTree { + mRoot: number; + mNodes: B2TreeNode[]; + mNodeCount: number; + mNodeCapacity: number; + mFreeList: number; + mInsertionCount: number; + constructor() { + this.mRoot = b2NullNode; + this.mNodeCapacity = 16; + this.mNodeCount = 0; + this.mNodes = new Array>(); + for (let i = 0; i < this.mNodeCapacity - 1; i++) { + this.mNodes.push(new B2TreeNode()); + this.mNodes[this.mNodes.length - 1].parentOrNext = i + 1; + this.mNodes[this.mNodes.length - 1].height = -1; + } + this.mNodes.push(new B2TreeNode()); + this.mNodes[this.mNodes.length - 1].parentOrNext = b2NullNode; + this.mNodes[this.mNodes.length - 1].height = -1; + this.mFreeList = 0; + this.mInsertionCount = 0; + } + + public createProxy(aabb: B2AaBb, userData: T): number { + let proxyId = this.allocateNode(); + let r = new B2Vec2(b2AaBbExtension, b2AaBbExtension); + this.mNodes[proxyId].aabb.lowerBound = subtract(aabb.lowerBound, r); + this.mNodes[proxyId].aabb.upperBound = add(aabb.upperBound, r); + this.mNodes[proxyId].userData = userData; + this.mNodes[proxyId].height = 0; + this.insertLeaf(proxyId); + console.info('box2d','insertLeaf:'+proxyId) + return proxyId; + } + + public destroyProxy(proxyId: number): void { + this.removeLeaf(proxyId); + this.freeNode(proxyId); + } + + public moveProxy(proxyId: number, aabb: B2AaBb, displacement: B2Vec2): boolean { + //console.info('box2d','x1:'+aabb.lowerBound.x+'y1:'+aabb.lowerBound.y) + //console.info('box2d','x2:'+aabb.upperBound.x+'y2:'+aabb.upperBound.y) + //console.info('box2d','x1:'+this.mNodes[proxyId].aabb.lowerBound.x+'y1:'+this.mNodes[proxyId].aabb.lowerBound.y) + //console.info('box2d','x2:'+this.mNodes[proxyId].aabb.upperBound.x+'y2:'+this.mNodes[proxyId].aabb.upperBound.y) + if (this.mNodes[proxyId].aabb.contains(aabb)) { + return false; + } + this.removeLeaf(proxyId); + let b = aabb; + let r = new B2Vec2(b2AaBbExtension, b2AaBbExtension); + b.lowerBound = subtract(b.lowerBound, r); + b.upperBound = add(b.upperBound, r); + let d = multM(displacement, b2AaBbMultiplier); + if (d.x < 0.0) { + b.lowerBound.x += d.x; + } else { + b.upperBound.x += d.x; + } + if (d.y < 0.0) { + b.lowerBound.y += d.y; + } else { + b.upperBound.y += d.y; + } + this.mNodes[proxyId].aabb = b; + this.insertLeaf(proxyId); + return true; + } + + public getUserData(proxyId: number): T | null { + return this.mNodes[proxyId].userData; + } + + public getFatAaBb(proxyId: number): B2AaBb { + return this.mNodes[proxyId].aabb; + } + public query(callback: T, aabb: B2AaBb): void { + let stack: number[] = []; + stack.push(this.mRoot); + console.info('box2d','query stack '+stack.length) + while (stack.length > 0) { + let nodeId = stack.pop(); + console.info('box2d','stack nodeId: '+nodeId) + if (nodeId === b2NullNode) { + continue; + } + let node = this.mNodes[nodeId!]; + console.info('box2d','stack node: '+node.aabb.lowerBound.x+','+node.aabb.lowerBound.y) + console.info('box2d','stack node: '+node.aabb.upperBound.x+','+node.aabb.upperBound.y) + console.info('box2d','node: '+aabb.lowerBound.x+','+aabb.lowerBound.y) + console.info('box2d','node: '+aabb.upperBound.x+','+aabb.upperBound.y) + if (b2TestOverlap2(node.aabb, aabb)) { + console.info('box2d','b2TestOverlap2: '+nodeId) + if (node.isLeaf()) { + console.info('box2d','isLeaf: '+nodeId) + let proceed = callback.queryCallback(nodeId!); + if (proceed === false) { + return; + } + } else { + stack.push(node.child1); + stack.push(node.child2); + } + } + } + } + + rayCast(callback: T, input: B2RayCastInput): void { + let p1 = input.p1; + let p2 = input.p2; + let r = subtract(p2, p1); + r.normalize(); + let v = b2Cross21(1.0, r); + let absv = b2Abs2(v); + let maxFraction = input.maxFraction; + let segmentAaBb = new B2AaBb(); + let t = add(p1, multM(subtract(p2, p1), maxFraction)); + segmentAaBb.lowerBound = b2Min(p1, t); + segmentAaBb.upperBound = b2Max(p1, t); + let stack: number[] = []; + stack.push(this.mRoot); + while (stack.length > 0) { + let nodeId = stack.pop(); + if (nodeId === b2NullNode) { + continue; + } + let node = this.mNodes[nodeId!]; + if (b2TestOverlap2(node.aabb, segmentAaBb) === false) { + continue; + } + let c = node.aabb.center; + let h = node.aabb.extents; + let separation = Math.abs(b2Dot22(v, subtract(p1, c))) - b2Dot22(absv, h); + if (separation > 0.0) { + continue; + } + if (node.isLeaf()) { + let subInput = new B2RayCastInput(); + subInput.p1 = input.p1; + subInput.p2 = input.p2; + subInput.maxFraction = maxFraction; + let value = callback.rayCastCallback(subInput, nodeId!); + if (value === 0.0) { + return; + } + if (value > 0.0) { + maxFraction = value; + let t = add(p1, multM(subtract(p2, p1), maxFraction)); + segmentAaBb.lowerBound = b2Min(p1, t); + segmentAaBb.upperBound = b2Max(p1, t); + } + } else { + stack.push(node.child1); + stack.push(node.child2); + } + } + } + + public validate(): void { + this.validateStructure(this.mRoot); + this.validateMetrics(this.mRoot); + let freeIndex = this.mFreeList; + while (freeIndex !== b2NullNode) { + freeIndex = this.mNodes[freeIndex].parentOrNext; + } + } + + public getHeight(): number { + if (this.mRoot === b2NullNode) { + return 0; + } + return this.mNodes[this.mRoot].height; + } + + public getMaxBalance(): number { + let maxBalance = 0; + for (let i = 0; i < this.mNodes.length; i++) { + let node = this.mNodes[i]; + if (node.height <= 1) { + continue; + } + let child1 = node.child1; + let child2 = node.child2; + let balance = Math.abs(this.mNodes[child2].height - this.mNodes[child1].height); + maxBalance = Math.max(maxBalance, balance); + } + return maxBalance; + } + + public getAreaRatio(): number { + if (this.mRoot === b2NullNode) { + return 0.0; + } + let root = this.mNodes[this.mRoot]; + let rootArea = root.aabb.perimeter; + let totalArea: number = 0.0; + for (let i = 0; i < this.mNodes.length; i++) { + let node = this.mNodes[i]; + if (node.height < 0) { + continue; + } + totalArea += node.aabb.perimeter; + } + return totalArea / rootArea; + } + + public rebuildBottomUp(): void { + let nodes: number[] = []; + for (let i = 0; i < this.mNodes.length; i++) { + nodes.push(0); + } + let count = 0; + for (let i = 0; i < this.mNodes.length; i++) { + if (this.mNodes[i].height < 0) { + continue; + } + if (this.mNodes[i].isLeaf()) { + this.mNodes[i].parentOrNext = b2NullNode; + nodes[count] = i; + count += 1; + } else { + this.freeNode(i); + } + } + while (count > 1) { + let minCost = b2MaxFloat; + let iMin = -1; + let jMin = -1; + for (let i = 0; i < count; i++) { + let aabbi = this.mNodes[nodes[i]].aabb; + for (let j = i + 1; j < count; j++) { + let aabbj = this.mNodes[nodes[j]].aabb; + let b = new B2AaBb(); + b.combine(aabbi, aabbj); + let cost = b.perimeter; + if (cost < minCost) { + iMin = i; + jMin = j; + minCost = cost; + } + } + } + let index1 = nodes[iMin]; + let index2 = nodes[jMin]; + let child1 = this.mNodes[index1]; + let child2 = this.mNodes[index2]; + let parentIndex = this.allocateNode(); + let parent = this.mNodes[parentIndex]; + parent.child1 = index1; + parent.child2 = index2; + parent.height = 1 + Math.max(child1.height, child2.height); + parent.aabb.combine(child1.aabb, child2.aabb); + parent.parentOrNext = b2NullNode; + child1.parentOrNext = parentIndex; + child2.parentOrNext = parentIndex; + nodes[jMin] = nodes[count - 1]; + nodes[iMin] = parentIndex; + count -= 1; + } + this.mRoot = nodes[0]; + this.validate(); + } + + public shiftOrigin(newOrigin: B2Vec2): void { + for (let i = 0; i < this.mNodes.length; i++) { + subtractEqual(this.mNodes[i].aabb.lowerBound, newOrigin); + subtractEqual(this.mNodes[i].aabb.upperBound, newOrigin); + } + } + + allocateNode(): number { + if (this.mFreeList === b2NullNode) { + let node = new B2TreeNode(); + node.parentOrNext = b2NullNode; + node.height = -1; + this.mNodes.push(node); + this.mFreeList = this.mNodes.length - 1; + } + let nodeId = this.mFreeList; + this.mFreeList = this.mNodes[nodeId].parentOrNext; + this.mNodes[nodeId].parentOrNext = b2NullNode; + this.mNodes[nodeId].child1 = b2NullNode; + this.mNodes[nodeId].child2 = b2NullNode; + this.mNodes[nodeId].height = 0; + this.mNodes[nodeId].userData = null; + return nodeId; + } + + freeNode(nodeId: number): void { + this.mNodes[nodeId].parentOrNext = this.mFreeList; + this.mNodes[nodeId].height = -1; + this.mFreeList = nodeId; + } + + insertLeaf(leaf: number): void { + this.mInsertionCount += 1; + if (this.mRoot === b2NullNode) { + this.mRoot = leaf; + this.mNodes[this.mRoot].parentOrNext = b2NullNode; + return; + } + let leafAaBb = this.mNodes[leaf].aabb; + let index = this.mRoot; + while (this.mNodes[index].isLeaf() === false) { + let child1 = this.mNodes[index].child1; + let child2 = this.mNodes[index].child2; + let area = this.mNodes[index].aabb.perimeter; + let combinedAaBb = new B2AaBb(); + combinedAaBb.combine(this.mNodes[index].aabb, leafAaBb); + let combinedArea = combinedAaBb.perimeter; + let cost = float2 * combinedArea; + let inheritanceCost = float2 * (combinedArea - area); + let cost1: number; + if (this.mNodes[child1].isLeaf()) { + let aabb = new B2AaBb(); + aabb.combine(leafAaBb, this.mNodes[child1].aabb); + cost1 = aabb.perimeter + inheritanceCost; + } else { + let aabb = new B2AaBb(); + aabb.combine(leafAaBb, this.mNodes[child1].aabb); + let oldArea = this.mNodes[child1].aabb.perimeter; + let newArea = aabb.perimeter; + cost1 = newArea - oldArea + inheritanceCost; + } + let cost2: number; + if (this.mNodes[child2].isLeaf()) { + let aabb = new B2AaBb(); + aabb.combine(leafAaBb, this.mNodes[child2].aabb); + cost2 = aabb.perimeter + inheritanceCost; + } else { + let aabb = new B2AaBb(); + aabb.combine(leafAaBb, this.mNodes[child2].aabb); + let oldArea = this.mNodes[child2].aabb.perimeter; + let newArea = aabb.perimeter; + cost2 = newArea - oldArea + inheritanceCost; + } + if (cost < cost1 && cost < cost2) { + break; + } + if (cost1 < cost2) { + index = child1; + } else { + index = child2; + } + } + let sibling = index; + let oldParent = this.mNodes[sibling].parentOrNext; + let newParent = this.allocateNode(); + this.mNodes[newParent].parentOrNext = oldParent; + this.mNodes[newParent].userData = null; + this.mNodes[newParent].aabb.combine(leafAaBb, this.mNodes[sibling].aabb); + this.mNodes[newParent].height = this.mNodes[sibling].height + 1; + if (oldParent !== b2NullNode) { + if (this.mNodes[oldParent].child1 === sibling) { + this.mNodes[oldParent].child1 = newParent; + } else { + this.mNodes[oldParent].child2 = newParent; + } + this.mNodes[newParent].child1 = sibling; + this.mNodes[newParent].child2 = leaf; + this.mNodes[sibling].parentOrNext = newParent; + this.mNodes[leaf].parentOrNext = newParent; + } else { + this.mNodes[newParent].child1 = sibling; + this.mNodes[newParent].child2 = leaf; + this.mNodes[sibling].parentOrNext = newParent; + this.mNodes[leaf].parentOrNext = newParent; + this.mRoot = newParent; + } + index = this.mNodes[leaf].parentOrNext; + while (index !== b2NullNode) { + index = this.balance(index); + let child1 = this.mNodes[index].child1; + let child2 = this.mNodes[index].child2; + this.mNodes[index].height = 1 + Math.max(this.mNodes[child1].height, this.mNodes[child2].height); + this.mNodes[index].aabb.combine(this.mNodes[child1].aabb, this.mNodes[child2].aabb); + index = this.mNodes[index].parentOrNext; + } + } + + removeLeaf(leaf: number): void { + if (leaf === this.mRoot) { + this.mRoot = b2NullNode; + return; + } + let parent = this.mNodes[leaf].parentOrNext; + let grandParent = this.mNodes[parent].parentOrNext; + let sibling: number; + if (this.mNodes[parent].child1 === leaf) { + sibling = this.mNodes[parent].child2; + } else { + sibling = this.mNodes[parent].child1; + } + if (grandParent !== b2NullNode) { + if (this.mNodes[grandParent].child1 === parent) { + this.mNodes[grandParent].child1 = sibling; + } else { + this.mNodes[grandParent].child2 = sibling; + } + this.mNodes[sibling].parentOrNext = grandParent; + this.freeNode(parent); + let index = grandParent; + while (index !== b2NullNode) { + index = this.balance(index); + let child1 = this.mNodes[index].child1; + let child2 = this.mNodes[index].child2; + this.mNodes[index].aabb.combine(this.mNodes[child1].aabb, this.mNodes[child2].aabb); + this.mNodes[index].height = 1 + Math.max(this.mNodes[child1].height, this.mNodes[child2].height); + index = this.mNodes[index].parentOrNext; + } + } else { + this.mRoot = sibling; + this.mNodes[sibling].parentOrNext = b2NullNode; + this.freeNode(parent); + } + } + + balance(iA: number): number { + let mA = this.mNodes[iA]; + if (mA.isLeaf() || mA.height < indexTwo) { + return iA; + } + let iB = mA.child1; + let iC = mA.child2; + let mB = this.mNodes[iB]; + let mC = this.mNodes[iC]; + let balance = mC.height - mB.height; + if (balance > 1) { + let iF = mC.child1; + let iG = mC.child2; + let mF = this.mNodes[iF]; + let mG = this.mNodes[iG]; + mC.child1 = iA; + mC.parentOrNext = mA.parentOrNext; + mA.parentOrNext = iC; + if (mC.parentOrNext !== b2NullNode) { + if (this.mNodes[mC.parentOrNext].child1 === iA) { + this.mNodes[mC.parentOrNext].child1 = iC; + } else { + this.mNodes[mC.parentOrNext].child2 = iC; + } + } else { + this.mRoot = iC; + } + if (mF.height > mG.height) { + mC.child2 = iF; + mA.child2 = iG; + mG.parentOrNext = iA; + mA.aabb.combine(mB.aabb, mG.aabb); + mC.aabb.combine(mA.aabb, mF.aabb); + mA.height = 1 + Math.max(mB.height, mG.height); + mC.height = 1 + Math.max(mA.height, mF.height); + } else { + mC.child2 = iG; + mA.child2 = iF; + mF.parentOrNext = iA; + mA.aabb.combine(mB.aabb, mF.aabb); + mC.aabb.combine(mA.aabb, mG.aabb); + mA.height = 1 + Math.max(mB.height, mF.height); + mC.height = 1 + Math.max(mA.height, mG.height); + } + return iC; + } + if (balance < -1) { + let iD = mB.child1; + let iE = mB.child2; + let mD = this.mNodes[iD]; + let mE = this.mNodes[iE]; + mB.child1 = iA; + mB.parentOrNext = mA.parentOrNext; + mA.parentOrNext = iB; + if (mB.parentOrNext !== b2NullNode) { + if (this.mNodes[mB.parentOrNext].child1 === iA) { + this.mNodes[mB.parentOrNext].child1 = iB; + } else { + this.mNodes[mB.parentOrNext].child2 = iB; + } + } else { + this.mRoot = iB; + } + if (mD.height > mE.height) { + mB.child2 = iD; + mA.child1 = iE; + mE.parentOrNext = iA; + mA.aabb.combine(mC.aabb, mE.aabb); + mB.aabb.combine(mA.aabb, mD.aabb); + mA.height = 1 + Math.max(mC.height, mE.height); + mB.height = 1 + Math.max(mA.height, mD.height); + } else { + mB.child2 = iE; + mA.child1 = iD; + mD.parentOrNext = iA; + mA.aabb.combine(mC.aabb, mD.aabb); + mB.aabb.combine(mA.aabb, mE.aabb); + mA.height = 1 + Math.max(mC.height, mD.height); + mB.height = 1 + Math.max(mA.height, mE.height); + } + return iB; + } + return iA; + } + + validateStructure(index: number): void { + if (index === b2NullNode) { + return; + } + let node = this.mNodes[index]; + let child1 = node.child1; + let child2 = node.child2; + if (node.isLeaf()) { + return; + } + this.validateStructure(child1); + this.validateStructure(child2); + } + validateMetrics(index: number): void { + if (index === b2NullNode) { + return; + } + let node = this.mNodes[index]; + let child1 = node.child1; + let child2 = node.child2; + if (node.isLeaf()) { + return; + } + let aabb = new B2AaBb(); + aabb.combine(this.mNodes[child1].aabb, this.mNodes[child2].aabb); + this.validateMetrics(child1); + this.validateMetrics(child2); + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2TimeOfImpact.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2TimeOfImpact.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ca14a115f03dd67c3de93a0517f6824535b8cee --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Collision/b2TimeOfImpact.ts @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2DistanceProxy, B2SimplexCache, B2DistanceInput, B2DistanceOutput, b2DistanceM } from './b2Distance'; +import { B2Vec2, subtract, B2Sweep, b2Cross12, b2MulTR2, b2MulT2, b2Dot22, b2MulR2, add, multM, minus } from '../Common/b2Math'; +import { b2LinearSlop, b2MaxPolygonVertices, float025, half, float3, indexTwo, floopCount } from '../Common/b2Settings'; + +export class B2ToiInput { + constructor() {} + public proxyA = new B2DistanceProxy(); + public proxyB = new B2DistanceProxy(); + public sweepA = new B2Sweep(); + public sweepB = new B2Sweep(); + public tMax: number = 0.0; +} + +export enum State { + UNKNOWN, + FAILED, + OVERLAPPED, + TOUCHING, + SEPARATED +} + +export class B2ToiOutput { + constructor() {} + public state = State.UNKNOWN; + public t: number = 0; +} + +export function b2TimeOfImpact(output: B2ToiOutput, input: B2ToiInput): void { + output.state = State.UNKNOWN; + output.t = input.tMax; + let proxyA = input.proxyA; + let proxyB = input.proxyB; + let sweepA = input.sweepA; + let sweepB = input.sweepB; + sweepA.normalize(); + sweepB.normalize(); + let tMax = input.tMax; + let totalRadius = proxyA.mRadius + proxyB.mRadius; + let target = Math.max(b2LinearSlop, totalRadius - float3 * b2LinearSlop); + let tolerance = float025 * b2LinearSlop; + let t1: number = 0.0; + let kMaxIterations = 20; + let iter = 0; + let cache = new B2SimplexCache(); + cache.count = 0; + let distanceInput = new B2DistanceInput(); + distanceInput.proxyA = input.proxyA; + distanceInput.proxyB = input.proxyB; + distanceInput.useRadii = false; + while (true) { + let xfA = sweepA.getTransform(t1); + let xfB = sweepB.getTransform(t1); + distanceInput.transformA = xfA; + distanceInput.transformB = xfB; + let distanceOutput = new B2DistanceOutput(); + b2DistanceM(distanceOutput, cache, distanceInput); + if (distanceOutput.distance <= 0.0) { + output.state = State.OVERLAPPED; + output.t = 0.0; + break; + } + if (distanceOutput.distance < target + tolerance) { + output.state = State.TOUCHING; + output.t = t1; + break; + } + let fcn = new B2SeparationFunction(); + fcn.initialize(cache, proxyA, sweepA, proxyB, sweepB, t1); + let done = false; + let t2 = tMax; + let pushBackIter = 0; + while (true) { + let fms = fcn.findMinSeparation(t2); + let s2 = fms[0]; + let indexA = fms[1]; + let indexB = fms[indexTwo]; + if (s2 > target + tolerance) { + output.state = State.SEPARATED; + output.t = tMax; + done = true; + break; + } + if (s2 > target - tolerance) { + t1 = t2; + break; + } + let s1 = fcn.evaluate(indexA, indexB, t1); + if (s1 < target - tolerance) { + output.state = State.FAILED; + output.t = t1; + done = true; + break; + } + if (s1 <= target + tolerance) { + output.state = State.TOUCHING; + output.t = t1; + done = true; + break; + } + let rootIterCount = 0; + let a1 = t1; + let a2 = t2; + while (true) { + let t: number; + if ((rootIterCount & 1) !== 0) { + t = a1 + ((target - s1) * (a2 - a1)) / (s2 - s1); + } else { + t = half * (a1 + a2); + } + rootIterCount += 1; + let s = fcn.evaluate(indexA, indexB, t); + if (Math.abs(s - target) < tolerance) { + t2 = t; + break; + } + if (s > target) { + a1 = t; + s1 = s; + } else { + a2 = t; + s2 = s; + } + if (rootIterCount === floopCount) { + break; + } + } + b2ToiMaxRootIters = Math.max(b2ToiMaxRootIters, rootIterCount); + pushBackIter += 1; + if (pushBackIter === b2MaxPolygonVertices) { + break; + } + } + iter += 1; + if (done) { + break; + } + if (iter === kMaxIterations) { + output.state = State.FAILED; + output.t = t1; + break; + } + } + b2ToiMaxIters = Math.max(b2ToiMaxIters, iter); +} +let b2ToiMaxIters = 0; +let b2ToiMaxRootIters = 0; + +enum TYPE { + POINTS, + FACEA, + FACEB +} + +export class B2SeparationFunction { + initialize(cache: B2SimplexCache, proxyA: B2DistanceProxy, sweepA: B2Sweep, proxyB: B2DistanceProxy, sweepB: B2Sweep, t1: number): number { + this.mProxyA = proxyA; + this.mProxyB = proxyB; + let count = cache.count; + this.mSweepA = sweepA; + this.mSweepB = sweepB; + let xfA = this.mSweepA.getTransform(t1); + let xfB = this.mSweepB.getTransform(t1); + if (count === 1) { + this.mType = TYPE.POINTS; + let localPointA = this.mProxyA.getVertex(cache.indexA[0]); + let localPointB = this.mProxyB.getVertex(cache.indexB[0]); + let pointA = b2MulT2(xfA, localPointA); + let pointB = b2MulT2(xfB, localPointB); + this.mAxis = subtract(pointB, pointA); + let s = this.mAxis.normalize(); + return s; + } else if (cache.indexA[0] === cache.indexA[1]) { + this.mType = TYPE.FACEB; + let localPointB1 = proxyB.getVertex(cache.indexB[0]); + let localPointB2 = proxyB.getVertex(cache.indexB[1]); + this.mAxis = b2Cross12(subtract(localPointB2, localPointB1), 1.0); + this.mAxis.normalize(); + let normal = b2MulR2(xfB.q, this.mAxis); + this.mLocalPoint = multM(add(localPointB1, localPointB2), half); + let pointB = b2MulT2(xfB, this.mLocalPoint); + let localPointA = proxyA.getVertex(cache.indexA[0]); + let pointA = b2MulT2(xfA, localPointA); + let s = b2Dot22(subtract(pointA, pointB), normal); + if (s < 0.0) { + this.mAxis = minus(this.mAxis); + s = -s; + } + return s; + } else { + this.mType = TYPE.FACEA; + let localPointA1 = this.mProxyA.getVertex(cache.indexA[0]); + let localPointA2 = this.mProxyA.getVertex(cache.indexA[1]); + this.mAxis = b2Cross12(subtract(localPointA2, localPointA1), 1.0); + this.mAxis.normalize(); + let normal = b2MulR2(xfA.q, this.mAxis); + this.mLocalPoint = multM(add(localPointA1, localPointA2), half); + let pointA = b2MulT2(xfA, this.mLocalPoint); + let localPointB = this.mProxyB.getVertex(cache.indexB[0]); + let pointB = b2MulT2(xfB, localPointB); + let s = b2Dot22(subtract(pointB, pointA), normal); + if (s < 0.0) { + this.mAxis = minus(this.mAxis); + s = -s; + } + return s; + } + } + + findMinSeparation(t: number): [separation: number, indexA: number, indexB: number] { + let indexA: number; + let indexB: number; + let xfA = this.mSweepA.getTransform(t); + let xfB = this.mSweepB.getTransform(t); + switch (this.mType) { + case TYPE.POINTS: { + let axisA1 = b2MulTR2(xfA.q, this.mAxis); + let axisB1 = b2MulTR2(xfB.q, minus(this.mAxis)); + indexA = this.mProxyA.getSupport(axisA1); + indexB = this.mProxyB.getSupport(axisB1); + let localPointA1 = this.mProxyA.getVertex(indexA); + let localPointB1 = this.mProxyB.getVertex(indexB); + let pointA1 = b2MulT2(xfA, localPointA1); + let pointB1 = b2MulT2(xfB, localPointB1); + let separation1 = b2Dot22(subtract(pointB1, pointA1), this.mAxis); + return [separation1, indexA, indexB]; + } + case TYPE.FACEA: { + let normal2 = b2MulR2(xfA.q, this.mAxis); + let pointA2 = b2MulT2(xfA, this.mLocalPoint); + let axisB = b2MulTR2(xfB.q, minus(normal2)); + indexA = -1; + indexB = this.mProxyB.getSupport(axisB); + let localPointB = this.mProxyB.getVertex(indexB); + let pointB2 = b2MulT2(xfB, localPointB); + let separation2 = b2Dot22(subtract(pointB2, pointA2), normal2); + return [separation2, indexA, indexB]; + } + case TYPE.FACEB: { + let normal = b2MulR2(xfB.q, this.mAxis); + let pointB = b2MulT2(xfB, this.mLocalPoint); + let axisA = b2MulTR2(xfA.q, minus(normal)); + indexB = -1; + indexA = this.mProxyA.getSupport(axisA); + let localPointA = this.mProxyA.getVertex(indexA); + let pointA = b2MulT2(xfA, localPointA); + let separation = b2Dot22(subtract(pointA, pointB), normal); + return [separation, indexA, indexB]; + } + default: + break; + } + return [0, 0, 0]; + } + + evaluate(indexA: number, indexB: number, t: number): number { + let xfA = this.mSweepA.getTransform(t); + let xfB = this.mSweepB.getTransform(t); + switch (this.mType) { + case TYPE.POINTS: { + let localPointA1 = this.mProxyA.getVertex(indexA); + let localPointB1 = this.mProxyB.getVertex(indexB); + let pointA1 = b2MulT2(xfA, localPointA1); + let pointB1 = b2MulT2(xfB, localPointB1); + let separation1 = b2Dot22(subtract(pointB1, pointA1), this.mAxis); + return separation1; + } + case TYPE.FACEA: { + let normal2 = b2MulR2(xfA.q, this.mAxis); + let pointA2 = b2MulT2(xfA, this.mLocalPoint); + let localPointB = this.mProxyB.getVertex(indexB); + let pointB2 = b2MulT2(xfB, localPointB); + let separation2 = b2Dot22(subtract(pointB2, pointA2), normal2); + return separation2; + } + case TYPE.FACEB: { + let normal = b2MulR2(xfB.q, this.mAxis); + let pointB = b2MulT2(xfB, this.mLocalPoint); + let localPointA = this.mProxyA.getVertex(indexA); + let pointA = b2MulT2(xfA, localPointA); + let separation = b2Dot22(subtract(pointA, pointB), normal); + return separation; + } + default: + break; + } + return 0; + } + + mProxyA = new B2DistanceProxy(); + mProxyB = new B2DistanceProxy(); + mSweepA = new B2Sweep(); + mSweepB = new B2Sweep(); + mType = TYPE.POINTS; + mLocalPoint = new B2Vec2(); + mAxis = new B2Vec2(); +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Math.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Math.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4cf35047e2e6218e8cd91a4ae775d130225ad44 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Math.ts @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { int4, b2Epsilon, b2PI, float2, int16, indexTwo, b2MaxSubSteps } from './b2Settings'; + +function b2IsValid(x: number): boolean { + return Number.isNaN(x) === false && Number.isFinite(x) === false; +} + +function b2Sqrt(x: number): number { + return Math.sqrt(x); +} + +function b2Atan2(y: number, x: number): number { + return Math.atan2(y, x); +} + +export class B2Vec2 { + public x: number; + public y: number; + constructor(x0: number = 0, y0: number = 0) { + if (x0 !== null && y0 !== null) { + this.x = x0; + this.y = y0; + } else { + this.x = 0; + this.y = 0; + } + } + + public setZero(): void { + this.x = 0.0; + this.y = 0.0; + } + + public set(x0: number, y0: number): void { + this.x = x0; + this.y = y0; + } + getSubscript(index: number): number { + if (index === 0) { + return this.x; + } else { + return this.y; + } + } + setSubscript(index: number, newValue: number): void { + if (index === 0) { + this.x = newValue; + } else { + this.y = newValue; + } + } + public length(): number { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + public lengthSquared(): number { + return this.x * this.x + this.y * this.y; + } + public normalize(): number { + let length = this.length(); + if (length < b2Epsilon) { + return 0.0; + } + let invLength = 1.0 / length; + this.x *= invLength; + this.y *= invLength; + return length; + } + public isValid(): boolean { + return b2IsValid(this.x) && b2IsValid(this.y); + } + public skew(): B2Vec2 { + return new B2Vec2(-this.y, this.x); + } +} + +export function minus(v: B2Vec2): B2Vec2 { + let mv = new B2Vec2(-v.x, -v.y); + return mv; +} + +export function addEqual(a: B2Vec2, b: B2Vec2): void { + a.x += b.x; + a.y += b.y; +} + +export function subtractEqual(a: B2Vec2, b: B2Vec2): void { + a.x -= b.x; + a.y -= b.y; +} + +export function mulEqual(a: B2Vec2, b: number): void { + a.x *= b; + a.y *= b; +} + +export class B2Vec3 { + x: number; + y: number; + z: number; + constructor(x1: number = 0, y1: number = 0, z1: number = 0) { + if (x1 !== null && y1 !== null && z1 !== null) { + this.x = x1; + this.y = y1; + this.z = z1; + } else { + this.x = 0.0; + this.y = 0.0; + this.z = 0.0; + } + } + + setZero(): void { + this.x = 0.0; + this.y = 0.0; + } + + set(x1: number, y1: number, z1: number): void { + this.x = x1; + this.y = y1; + this.z = z1; + } +} + +export function minus3(v: B2Vec3): B2Vec3 { + let mv = new B2Vec3(-v.x, -v.y, -v.z); + return mv; +} + +export function addEqual3(a: B2Vec3, b: B2Vec3): void { + a.x += b.x; + a.y += b.y; + a.z += b.z; +} + +export function mulMEqual3(a: B2Vec3, b: number): void { + a.x *= b; + a.y *= b; + a.z *= b; +} + +export class B2Mat22 { + public ex: B2Vec2; + public ey: B2Vec2; + public setMat(angle: number): void { + let c = Math.cos(angle); + let s = Math.sin(angle); + this.ex.set(c, s); + this.ey.set(-s, c); + } + + constructor(c1: B2Vec2 | null = null, c2: B2Vec2 | null = null) { + if (c1 !== null && c2 !== null) { + this.ex = c1; + this.ey = c2; + } else { + this.ex = new B2Vec2(0.0, 0.0); + this.ey = new B2Vec2(0.0, 0.0); + } + } + + public set(c1: B2Vec2, c2: B2Vec2): void { + this.ex = c1; + this.ey = c2; + } + public setIdentity(): void { + this.ex.x = 1.0; + this.ey.x = 0.0; + this.ex.y = 0.0; + this.ey.y = 1.0; + } + public setZero(): void { + this.ex.x = 0.0; + this.ey.x = 0.0; + this.ex.y = 0.0; + this.ey.y = 0.0; + } + public getInverse(): B2Mat22 { + let a = this.ex.x; + let b = this.ey.x; + let c = this.ex.y; + let d = this.ey.y; + let mB = new B2Mat22(); + let det = a * d - b * c; + if (det !== 0.0) { + det = 1.0 / det; + } + mB.ex.x = det * d; + mB.ey.x = -det * b; + mB.ex.y = -det * c; + mB.ey.y = det * a; + return mB; + } + public solve(b: B2Vec2): B2Vec2 { + let a11 = this.ex.x; + let a12 = this.ey.x; + let a21 = this.ex.y; + let a22 = this.ey.y; + let det = a11 * a22 - a12 * a21; + if (det !== 0.0) { + det = 1.0 / det; + } + let x = new B2Vec2(); + x.x = det * (a22 * b.x - a12 * b.y); + x.y = det * (a11 * b.y - a21 * b.x); + return x; + } +} + +export class B2Mat33 { + public ex: B2Vec3; + public ey: B2Vec3; + public ez: B2Vec3; + constructor(c1: B2Vec3 | null = null, c2: B2Vec3 | null = null, c3: B2Vec3 | null = null) { + if (c1 !== null && c2 !== null && c3 !== null) { + this.ex = c1; + this.ey = c2; + this.ez = c3; + } else { + this.ex = new B2Vec3(); + this.ey = new B2Vec3(); + this.ez = new B2Vec3(); + } + } + public setZero(): void { + this.ex.setZero(); + this.ey.setZero(); + this.ez.setZero(); + } + public solve33(b: B2Vec3): B2Vec3 { + let det = b2Dot33(this.ex, b2Cross33(this.ey, this.ez)); + if (det !== 0.0) { + det = 1.0 / det; + } + let x = new B2Vec3(); + x.x = det * b2Dot33(b, b2Cross33(this.ey, this.ez)); + x.y = det * b2Dot33(this.ex, b2Cross33(b, this.ez)); + x.z = det * b2Dot33(this.ex, b2Cross33(this.ey, b)); + return x; + } + public solve22(b: B2Vec2): B2Vec2 { + let a11 = this.ex.x; + let a12 = this.ey.x; + let a21 = this.ex.y; + let a22 = this.ey.y; + let det = a11 * a22 - a12 * a21; + if (det !== 0.0) { + det = 1.0 / det; + } + let x = new B2Vec2(); + x.x = det * (a22 * b.x - a12 * b.y); + x.y = det * (a11 * b.y - a21 * b.x); + return x; + } + public getInverse22(): B2Mat33 { + let a = this.ex.x; + let b = this.ey.x; + let c = this.ex.y; + let d = this.ey.y; + let det = a * d - b * c; + if (det !== 0.0) { + det = 1.0 / det; + } + let mM = new B2Mat33(); + mM.ex.x = det * d; + mM.ey.x = -det * b; + mM.ex.z = 0.0; + mM.ex.y = -det * c; + mM.ey.y = det * a; + mM.ey.z = 0.0; + mM.ez.x = 0.0; + mM.ez.y = 0.0; + mM.ez.z = 0.0; + return mM; + } + + public getSymInverse33(): B2Mat33 { + let det = b2Dot33(this.ex, b2Cross33(this.ey, this.ez)); + if (det !== 0.0) { + det = 1.0 / det; + } + let a11 = this.ex.x; + let a12 = this.ey.x; + let a13 = this.ez.x; + let a22 = this.ey.y; + let a23 = this.ez.y; + let a33 = this.ez.z; + let mM = new B2Mat33(); + mM.ex.x = det * (a22 * a33 - a23 * a23); + mM.ex.y = det * (a13 * a23 - a12 * a33); + mM.ex.z = det * (a12 * a23 - a13 * a22); + mM.ey.x = mM.ex.y; + mM.ey.y = det * (a11 * a33 - a13 * a13); + mM.ey.z = det * (a13 * a12 - a11 * a23); + mM.ez.x = mM.ex.z; + mM.ez.y = mM.ey.z; + mM.ez.z = det * (a11 * a22 - a12 * a12); + return mM; + } +} + +export class B2Rot { + public s: number; + public c: number; + constructor(angle: number | null = null) { + if (angle !== null) { + this.s = Math.sin(angle); + this.c = Math.cos(angle); + } else { + this.s = 0.0; + this.c = 0.0; + } + } + + public set(angle: number): void { + this.s = Math.sin(angle); + this.c = Math.cos(angle); + } + public setIdentity(): void { + this.s = 0.0; + this.c = 1.0; + } + public angle(): number { + return b2Atan2(this.s, this.c); + } + public xAxis(): B2Vec2 { + return new B2Vec2(this.c, this.s); + } + public yAxis(): B2Vec2 { + return new B2Vec2(-this.s, this.c); + } +} + +export class B2Transform { + public p: B2Vec2; + public q: B2Rot; + constructor(position: B2Vec2 | null = null, rotation: B2Rot | null = null) { + if (position !== null && rotation !== null) { + this.p = position; + this.q = rotation; + } else { + this.p = new B2Vec2(); + this.q = new B2Rot(); + } + } + public setIdentity(): void { + this.p.setZero(); + this.q.setIdentity(); + } + public set(position: B2Vec2, angle: number): void { + this.p = position; + this.q.set(angle); + } +} + +export class B2Sweep { + public localCenter = new B2Vec2(); + public mc0 = new B2Vec2(); + get c0(): B2Vec2 { + return this.mc0; + } + set c0(value: B2Vec2) { + this.mc0 = value; + } + public c = new B2Vec2(); + public a0: number = 0; + public a: number = 0; + public alpha0: number = 0; + constructor() {} + public getTransform(beta: number): B2Transform { + let xf = new B2Transform(); + xf.p = add(multM(this.c0, 1.0 - beta), multM(this.c, beta)); + let angle = (1.0 - beta) * this.a0 + beta * this.a; + xf.q.set(angle); + subtractEqual(xf.p, b2MulR2(xf.q, this.localCenter)); + return xf; + } + public advance(alpha: number): void { + let beta = (alpha - this.alpha0) / (1.0 - this.alpha0); + addEqual(this.c0, multM(subtract(this.c, this.c0), beta)); + this.a0 += beta * (this.a - this.a0); + this.alpha0 = alpha; + } + public normalize(): void { + let twoPi = float2 * b2PI; + let d = twoPi * Math.floor(this.a0 / twoPi); + this.a0 -= d; + this.a -= d; + } +} + +let b2Vec2Zero = new B2Vec2(0.0, 0.0); +function b2Dot22(a: B2Vec2, b: B2Vec2): number { + return a.x * b.x + a.y * b.y; +} + +function b2Cross(a: B2Vec2, b: B2Vec2): number { + return a.x * b.y - a.y * b.x; +} + +function b2Cross12(a: B2Vec2, s: number): B2Vec2 { + return new B2Vec2(s * a.y, -s * a.x); +} + +function b2Cross21(s: number, a: B2Vec2): B2Vec2 { + return new B2Vec2(-s * a.y, s * a.x); +} + +function b2Mul22(a: B2Mat22, v: B2Vec2): B2Vec2 { + return new B2Vec2(b2Dot22(v, a.ex), b2Dot22(v, a.ey)); +} + +function b2MulTM2(a: B2Mat22, v: B2Vec2): B2Vec2 { + return new B2Vec2(b2Dot22(v, a.ex), b2Dot22(v, a.ey)); +} + +function add(a: B2Vec2, b: B2Vec2): B2Vec2 { + return new B2Vec2(a.x + b.x, a.y + b.y); +} + +function subtract(a: B2Vec2, b: B2Vec2): B2Vec2 { + return new B2Vec2(a.x - b.x, a.y - b.y); +} + +function multM(a: B2Vec2, b: number): B2Vec2 { + return new B2Vec2(a.x * b, a.y * b); +} + +function b2Distance(a: B2Vec2, b: B2Vec2): number { + let c = new B2Vec2(a.x - b.x, a.y - b.y); + return c.length(); +} + +function b2DistanceSquared(a: B2Vec2, b: B2Vec2): number { + let c = new B2Vec2(a.x - b.x, a.y - b.y); + return b2Dot22(c, c); +} + +function multM3(s: number, a: B2Vec3): B2Vec3 { + return new B2Vec3(s * a.x, s * a.y, s * a.z); +} + +function add3(a: B2Vec3, b: B2Vec3): B2Vec3 { + return new B2Vec3(a.x + b.x, a.y + b.y, a.z + b.z); +} + +function subtract3(a: B2Vec3, b: B2Vec3): B2Vec3 { + return new B2Vec3(a.x - b.x, a.y - b.y, a.z - b.z); +} + +function b2Dot33(a: B2Vec3, b: B2Vec3): number { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +function b2Cross33(a: B2Vec3, b: B2Vec3): B2Vec3 { + return new B2Vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); +} + +function b2Mul(a: B2Mat22, b: B2Mat22): B2Mat22 { + return new B2Mat22(b2Mul22(a, b.ex), b2Mul22(a, b.ey)); +} + +function b2Mul33(a: B2Mat33, v: B2Vec3): B2Vec3 { + return add3(add3(multM3(v.x, a.ex), multM3(v.y, a.ey)), multM3(v.z, a.ez)); +} + +function b2Mul32(a: B2Mat33, v: B2Vec2): B2Vec2 { + return new B2Vec2(a.ex.x * v.x + a.ey.x * v.y, a.ex.y * v.x + a.ey.y * v.y); +} + +function b2MulTRR(q: B2Rot, r: B2Rot): B2Rot { + let qr = new B2Rot(); + qr.s = q.c * r.s - q.s * r.c; + qr.c = q.c * r.c + q.s * r.s; + return qr; +} + +function b2MulR2(q: B2Rot, v: B2Vec2): B2Vec2 { + return new B2Vec2(q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y); +} + +function b2MulTR2(q: B2Rot, v: B2Vec2): B2Vec2 { + return new B2Vec2(q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y); +} + +function b2MulT2(t: B2Transform, v: B2Vec2): B2Vec2 { + let x = t.q.c * v.x - t.q.s * v.y + t.p.x; + let y = t.q.s * v.x + t.q.c * v.y + t.p.y; + return new B2Vec2(x, y); +} + +function b2MulTT2(t: B2Transform, v: B2Vec2): B2Vec2 { + let px = v.x - t.p.x; + let py = v.y - t.p.y; + let x = t.q.c * px + t.q.s * py; + let y = -t.q.s * px + t.q.c * py; + return new B2Vec2(x, y); +} + +function b2MulT(a: B2Transform, b: B2Transform): B2Transform { + let mC = new B2Transform(); + mC.q = b2MulTRR(a.q, b.q); + let ma = new B2Vec2(b.p.x - a.p.x, b.p.y - a.p.y); + mC.p = b2MulTR2(a.q, ma); + return mC; +} + +function b2Abs2(a: B2Vec2): B2Vec2 { + return new B2Vec2(Math.abs(a.x), Math.abs(a.y)); +} + +function b2Min(a: B2Vec2, b: B2Vec2): B2Vec2 { + return new B2Vec2(Math.min(a.x, b.x), Math.min(a.y, b.y)); +} + +function b2Max(a: B2Vec2, b: B2Vec2): B2Vec2 { + return new B2Vec2(Math.max(a.x, b.x), Math.max(a.y, b.y)); +} + +function b2ClampF(a: number, low: number, high: number): number { + return Math.max(low, Math.min(a, high)); +} + +export function b2NextPowerOfTwo(x0: number): number { + let x = x0; + x = x | (x >> 1); + x = x | (x >> indexTwo); + x = x | (x >> int4); + x = x | (x >> b2MaxSubSteps); + x = x | (x >> int16); + return x + 1; +} + +export { b2MulT, add, add3, b2Mul32, b2Mul33, subtract, multM, b2MulTT2, multM3, subtract3, b2MulTM2 }; +export { b2Vec2Zero, b2DistanceSquared, b2Min, b2Max, b2Abs2, b2ClampF, b2Mul22, b2Atan2 }; +export { b2Mul, b2MulR2, b2Dot22, b2Sqrt, b2Cross, b2MulT2, b2Distance, b2MulTR2, b2Cross21, b2Cross12 }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Settings.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Settings.ts new file mode 100644 index 0000000000000000000000000000000000000000..251e28d712762e8882eb8f4142743e2d13959edd --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Settings.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export let float2: number = 2.0; +export let angle180: number = 180.0; +export let half: number = 0.5; +export let float8: number = 8.0; +export let float10: number = 10.0; +export let int4: number = 4; +export let int16: number = 16; +export let float3: number = 3.0; +export let float025: number = 0.25; +export let indexTwo: number = 2; +export let indexThr: number = 3; +export let posIter: number = 20; +export let floopCount: number = 50; +export let b2MinFloat = Number.MIN_VALUE; +export let b2MaxFloat = Number.MAX_SAFE_INTEGER; +export let b2Epsilon = Number.EPSILON; +export let b2PI: number = Math.PI; +export let b2MaxManifoldPoints = 2; +export let b2MaxPolygonVertices = 8; +export let b2AaBbExtension: number = 0.1; +export let b2AaBbMultiplier: number = 2.0; +export let b2LinearSlop: number = 0.005; +export let b2AngularSlop: number = (float2 / angle180) * b2PI; +export let b2PolygonRadius: number = float2 * b2LinearSlop; +export let b2MaxSubSteps = 8; +export let b2MaxTOIContacts = 32; +export let b2VelocityThreshold: number = 1.0; +export let b2MaxLinearCorrection: number = 0.2; +export let b2MaxAngularCorrection: number = (float8 / angle180) * b2PI; +export let b2MaxTranslation: number = 2.0; +export let b2MaxTranslationSquared: number = b2MaxTranslation * b2MaxTranslation; +export let b2MaxRotation: number = half * b2PI; +export let b2MaxRotationSquared: number = b2MaxRotation * b2MaxRotation; +export let b2Baumgarte: number = 0.2; +export let b2ToiBaugarte: number = 0.75; +export let b2TimeToSleep: number = 0.5; +export let b2LinearSleepTolerance: number = 0.01; +export let b2AngularSleepTolerance: number = (float2 / angle180) * b2PI; + +export let hex1: number = 0x0001; +export let hex2: number = 0x0002; +export let hex4: number = 0x0004; +export let hex8: number = 0x0008; +export let hex10: number = 0x0010; +export let hex20: number = 0x0020; +export let hex40: number = 0x0040; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Wrappers.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Wrappers.ts new file mode 100644 index 0000000000000000000000000000000000000000..23a35c747544f4d53388c5f30fefb06a7177d13f --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Common/b2Wrappers.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2RayCastInput } from '../Collision/b2Collision'; +import { B2FixtureProxy } from '../Dynamics/b2Fixture'; +interface B2QueryWrapper { + queryCallback(proxyId: number): boolean; +} +interface B2RayCastWrapper { + rayCastCallback(input: B2RayCastInput, proxyId: number): number; +} +interface B2BroadPhaseWrapper { + addPair(proxyUserDataA: B2FixtureProxy, proxyUserDataB: B2FixtureProxy); +} + +export { B2QueryWrapper, B2BroadPhaseWrapper, B2RayCastWrapper }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Contacts/b2Contact.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Contacts/b2Contact.ts new file mode 100644 index 0000000000000000000000000000000000000000..de9ce0b8434d31762aa077040fa134c27f62502d --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Contacts/b2Contact.ts @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2CircleShape, B2EdgeShape, B2PolygonShape } from '../../Collision/Shapes/b2Shape'; +import { B2ShapeType } from '../../Collision/Shapes/b2Shape'; +import { B2Transform } from '../../Common/b2Math'; +import { B2ContactFeature } from '../../Collision/b2Collision'; +import { B2WorldManifold, B2Manifold, b2TestOverlap } from '../../Collision/b2Collision'; +import { B2ContactListener } from '../b2WorldCallbacks'; +import { B2Body } from '../b2Body'; +import { B2Fixture } from '../b2Fixture'; +import { b2CollidePolygons } from '../../Collision/b2CollidePolygon'; +import { b2CollideCircles, b2CollidePolygonAndCircle } from '../../Collision/b2CollideCircles'; +import { b2CollideEdgeAndCircle, b2CollideEdgeAndPolygon } from '../../Collision/b2CollideEdge'; + +function b2MixFriction(friction1: number, friction2: number): number { + return Math.sqrt(friction1 * friction2); +} + +function b2MixRestitution(restitution1: number, restitution2: number): number { + return restitution1 > restitution2 ? restitution1 : restitution2; +} + +type B2ContactCreateFcn = (fixtureA: B2Fixture, indexA: number, fixtureB: B2Fixture, indexB: number) => B2Contact; +type B2ContactDestroyFcn = (contact: B2Contact) => void; + +export class B2ContactRegister { + createFcn: B2ContactCreateFcn | null = null; + destroyFcn: B2ContactDestroyFcn | null = null; + primary: boolean = false; +} + +export class B2ContactRegisters { + rows: B2ShapeType; + columns: B2ShapeType; + grid: B2ContactRegister[] = []; + constructor(rows: B2ShapeType, columns: B2ShapeType) { + this.rows = rows; + this.columns = columns; + for (let i = 0; i < rows * columns; i++) { + this.grid.push(new B2ContactRegister()); + } + } + + getItem(row: B2ShapeType, column: B2ShapeType): B2ContactRegister { + return this.grid[row * this.columns + column]; + } + + setItem(row: B2ShapeType, column: B2ShapeType, value: B2ContactRegister): void { + this.grid[row * this.columns + column] = value; + } +} + +export class B2ContactEdge { + other: B2Body | null = null; + contact: B2Contact; + prev: B2ContactEdge | null = null; + next: B2ContactEdge | null = null; + constructor(contact: B2Contact) { + this.contact = contact; + } +} + +export class B2Contact { + mFlags: number = 0; + mPrev: B2Contact | null = null; + mNext: B2Contact | null = null; + mNodeA: B2ContactEdge; + mNodeB: B2ContactEdge; + mFixtureA: B2Fixture; + mFixtureB: B2Fixture; + mIndexA: number = 0; + mIndexB: number = 0; + mManifold = new B2Manifold(); + mToiCount: number = 0; + mToi: number = 0; + mFriction: number = 0; + mRestitution: number = 0; + mTangentSpeed: number = 0; + get manifold(): B2Manifold { + return this.mManifold; + } + + get worldManifold(): B2WorldManifold { + let bodyA = this.mFixtureA.body; + let bodyB = this.mFixtureB.body; + let shapeA = this.mFixtureA.shape; + let shapeB = this.mFixtureB.shape; + let worldManifold = new B2WorldManifold(); + worldManifold.initialize(this.mManifold, bodyA.transform, shapeA!.mRadius, bodyB.transform, shapeB!.mRadius); + return worldManifold; + } + + get isTouching(): boolean { + return (this.mFlags & FlagContacts.TOUCHINGFLAG) === FlagContacts.TOUCHINGFLAG; + } + + setEnabled(flag: boolean): void { + if (flag) { + this.mFlags |= FlagContacts.ENABLEDFLAG; + } else { + this.mFlags &= ~FlagContacts.ENABLEDFLAG; + } + } + + get isEnabled(): boolean { + return (this.mFlags & FlagContacts.ENABLEDFLAG) === FlagContacts.ENABLEDFLAG; + } + + getNext(): B2Contact | null { + return this.mNext; + } + + get fixtureA(): B2Fixture { + return this.mFixtureA; + } + + get childIndexA(): number { + return this.mIndexA; + } + + get fixtureB(): B2Fixture { + return this.mFixtureB; + } + + get childIndexB(): number { + return this.mIndexB; + } + + get friction(): number { + return this.mFriction; + } + + set setFriction(friction: number) { + this.mFriction = friction; + } + + resetFriction(): void { + this.mFriction = b2MixFriction(this.mFixtureA.mFriction, this.mFixtureB.mFriction); + } + + get restitution(): number { + return this.mRestitution; + } + + set setRestitution(restitution: number) { + this.mRestitution = restitution; + } + + resetRestitution(): void { + this.mRestitution = b2MixRestitution(this.mFixtureA.mRestitution, this.mFixtureB.mRestitution); + } + + setTangentSpeed(speed: number): void { + this.mTangentSpeed = speed; + } + + get tangentSpeed(): number { + return this.mTangentSpeed; + } + + evaluate(manifold: B2Manifold, xfA: B2Transform, xfB: B2Transform): void { + throw new Error('must override'); + } + + flagForFiltering(): void { + throw new Error('must override'); + } + + static addType(createFcn: B2ContactCreateFcn, destroyFcn: B2ContactDestroyFcn, type1: B2ShapeType, type2: B2ShapeType): void { + StaticVars.sRegisters.getItem(type1, type2).createFcn = createFcn; + StaticVars.sRegisters.getItem(type1, type2).destroyFcn = destroyFcn; + StaticVars.sRegisters.getItem(type1, type2).primary = true; + if (type1 !== type2) { + StaticVars.sRegisters.getItem(type2, type1).createFcn = createFcn; + StaticVars.sRegisters.getItem(type2, type1).destroyFcn = destroyFcn; + StaticVars.sRegisters.getItem(type2, type1).primary = false; + } + } + + static initializeRegisters(): void { + B2Contact.addType(B2PolygonContact.create, B2PolygonContact.destroy, B2ShapeType.POLYGON, B2ShapeType.POLYGON); + } + + static create(fixtureA: B2Fixture, indexA: number, fixtureB: B2Fixture, indexB: number): B2Contact | null { + if (StaticVars.sInitialized === false) { + B2Contact.initializeRegisters(); + StaticVars.sInitialized = true; + } + let type1 = fixtureA!.fixtureType; + let type2 = fixtureB!.fixtureType; + console.info('box2d','create:1') + let createFcn = StaticVars.sRegisters.getItem(type1!, type2!).createFcn; + if (createFcn !== null) { + console.info('box2d','create:2') + if (StaticVars.sRegisters.getItem(type1!, type2!).primary) { + console.info('box2d','create:3') + return createFcn(fixtureA, indexA, fixtureB, indexB); + } else { + console.info('box2d','create:4') + return createFcn(fixtureB, indexB, fixtureA, indexA); + } + } else { + return null; + } + } + + static destroy(contact: B2Contact): void { + let fixtureA = contact.mFixtureA; + let fixtureB = contact.mFixtureB; + if (contact.mManifold.pointCount > 0 && fixtureA?.isSensor === false && fixtureB?.isSensor === false) { + fixtureA?.body.setAwake(true); + fixtureB?.body.setAwake(true); + } + let typeA = fixtureA?.fixtureType; + let typeB = fixtureB?.fixtureType; + let destroyFcn = StaticVars.sRegisters.getItem(typeA!, typeB!).destroyFcn; + destroyFcn!(contact); + } + + constructor(fixtureA: B2Fixture, indexA: number, fixtureB: B2Fixture, indexB: number) { + this.mFlags = FlagContacts.ENABLEDFLAG; + this.mFixtureA = fixtureA; + this.mFixtureB = fixtureB; + this.mIndexA = indexA; + this.mIndexB = indexB; + this.mManifold.points.splice(0, this.mManifold.points.length); + this.mPrev = null; + this.mNext = null; + this.mNodeA = new B2ContactEdge(this); + this.mNodeA.prev = null; + this.mNodeA.next = null; + this.mNodeA.other = null; + this.mNodeB = new B2ContactEdge(this); + this.mNodeB.prev = null; + this.mNodeB.next = null; + this.mNodeB.other = null; + this.mToiCount = 0; + this.mFriction = b2MixFriction(this.mFixtureA.mFriction, this.mFixtureB.mFriction); + this.mRestitution = b2MixRestitution(this.mFixtureA.mRestitution, this.mFixtureB.mRestitution); + this.mTangentSpeed = 0.0; + } + + update(listener: B2ContactListener): void { + let oldManifold = new B2Manifold(this.mManifold); + this.mFlags |= FlagContacts.ENABLEDFLAG; + let touching = false; + let wasTouching = (this.mFlags & FlagContacts.TOUCHINGFLAG) === FlagContacts.TOUCHINGFLAG; + let sensorA = this.mFixtureA.isSensor; + let sensorB = this.mFixtureB.isSensor; + let sensor = sensorA || sensorB; + let bodyA = this.mFixtureA.body; + let bodyB = this.mFixtureB.body; + let xfA = bodyA.transform; + let xfB = bodyB.transform; + if (sensor) { + let shapeA = this.mFixtureA.shape; + let shapeB = this.mFixtureB.shape; + touching = b2TestOverlap(shapeA!, shapeB!, xfA, xfB); + this.mManifold.points.splice(0, this.mManifold.points.length); + } else { + this.evaluate(this.mManifold, xfA, xfB); + touching = this.mManifold.pointCount > 0; + for (let i = 0; i < this.mManifold.pointCount; i++) { + let mp2 = this.mManifold.points[i]; + mp2.normalImpulse = 0.0; + mp2.tangentImpulse = 0.0; + let id2 = mp2.id; + for (let j = 0; j < oldManifold.pointCount; j++) { + let mp1 = oldManifold.points[j]; + if (equalEqual(mp1.id, id2)) { + mp2.normalImpulse = mp1.normalImpulse; + mp2.tangentImpulse = mp1.tangentImpulse; + break; + } + } + } + if (touching !== wasTouching) { + bodyA.setAwake(true); + bodyB.setAwake(true); + } + } + } +} + +export class StaticVars { + static sRegisters = new B2ContactRegisters(B2ShapeType.TYPECOUNT, B2ShapeType.TYPECOUNT); + static sInitialized: boolean = false; +} + +const hex0001: number = 0x0001; +const hex0002: number = 0x0002; +const hex0004: number = 0x0004; +const hex0008: number = 0x0008; +const hex0010: number = 0x0010; +const hex0020: number = 0x0020; + +enum FlagContacts { + ISLANDFLAG = hex0001, + TOUCHINGFLAG = hex0002, + ENABLEDFLAG = hex0004, + FILTERFLAG = hex0008, + BULLETHITFLAG = hex0010, + TOIFLAG = hex0020 +} + +export class B2PolygonContact extends B2Contact { + static create(fixtureA: B2Fixture, indexA: number, fixtureB: B2Fixture, indexB: number): B2Contact { + return new B2PolygonContact(fixtureA, fixtureB); + } + + static destroy(contact: B2Contact): void {} + + constructor(fixtureA: B2Fixture, fixtureB: B2Fixture) { + super(fixtureA, 0, fixtureB, 0); + } + + evaluate(manifold: B2Manifold, xfA: B2Transform, xfB: B2Transform): void { + b2CollidePolygons(manifold, this.mFixtureA.shape as B2PolygonShape, xfA, this.mFixtureB.shape as B2PolygonShape, xfB); + } +} + +export class B2CircleContact extends B2Contact { + static create(fixtureA: B2Fixture, indexA: number, fixtureB: B2Fixture, indexB: number): B2Contact { + return new B2CircleContact(fixtureA, fixtureB); + } + + static destroy(contact: B2Contact): void {} + + constructor(fixtureA: B2Fixture, fixtureB: B2Fixture) { + super(fixtureA, 0, fixtureB, 0); + } + + evaluate(manifold: B2Manifold, xfA: B2Transform, xfB: B2Transform): void { + b2CollideCircles(manifold, this.mFixtureA.shape as B2CircleShape, xfA, this.mFixtureB.shape as B2CircleShape, xfB); + } +} + +export class B2EdgeAndCircleContact extends B2Contact { + static create(fixtureA: B2Fixture, indexA: number, fixtureB: B2Fixture, indexB: number): B2Contact { + return new B2EdgeAndCircleContact(fixtureA, fixtureB); + } + + static destroy(contact: B2Contact): void {} + + constructor(fixtureA: B2Fixture, fixtureB: B2Fixture) { + super(fixtureA, 0, fixtureB, 0); + } + + evaluate(manifold: B2Manifold, xfA: B2Transform, xfB: B2Transform): void { + b2CollideEdgeAndCircle(manifold, this.mFixtureA.shape as B2EdgeShape, xfA, this.mFixtureB.shape as B2CircleShape, xfB); + } +} + +export class B2EdgeAndPolygonContact extends B2Contact { + static create(fixtureA: B2Fixture, indexA: number, fixtureB: B2Fixture, indexB: number): B2Contact { + return new B2EdgeAndPolygonContact(fixtureA, fixtureB); + } + + static destroy(contact: B2Contact): void {} + + constructor(fixtureA: B2Fixture, fixtureB: B2Fixture) { + super(fixtureA, 0, fixtureB, 0); + } + + evaluate(manifold: B2Manifold, xfA: B2Transform, xfB: B2Transform): void { + b2CollideEdgeAndPolygon(manifold, this.mFixtureA.shape as B2EdgeShape, xfA, this.mFixtureB.shape as B2PolygonShape, xfB); + } +} + +export class B2PolygonAndCircleContact extends B2Contact { + static create(fixtureA: B2Fixture, indexA: number, fixtureB: B2Fixture, indexB: number): B2Contact { + return new B2PolygonAndCircleContact(fixtureA, fixtureB); + } + + static destroy(contact: B2Contact): void {} + + constructor(fixtureA: B2Fixture, fixtureB: B2Fixture) { + super(fixtureA, 0, fixtureB, 0); + } + + evaluate(manifold: B2Manifold, xfA: B2Transform, xfB: B2Transform): void { + b2CollidePolygonAndCircle(manifold, this.mFixtureA.shape as B2PolygonShape, xfA, this.mFixtureB.shape as B2CircleShape, xfB); + } +} + +export function equalEqual(lhs: B2ContactFeature, rhs: B2ContactFeature): Boolean { + return lhs.indexA === rhs.indexA && lhs.indexB === rhs.indexB && lhs.typeA === rhs.typeA && lhs.typeB === rhs.typeB; +} + +export { FlagContacts }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Contacts/b2ContactSolver.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Contacts/b2ContactSolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..4ea67e96cf281798bc8b646ebe144de08e09ffd5 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Contacts/b2ContactSolver.ts @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + B2Vec2, + B2Transform, + b2Dot22, + b2Cross, + b2ClampF, + b2MulT2, + b2Cross21, + b2Cross12, + b2Mul22, + b2MulR2, + B2Mat22, + addEqual, + multM, + add, + subtract, + subtractEqual, + minus +} from '../../Common/b2Math'; +import { B2Contact } from './b2Contact'; +import { B2ManifoldType, B2WorldManifold } from '../../Collision/b2Collision'; +import { B2Position, B2Velocity } from '../b2TimeStep'; +import { B2Array, B2TimeStep } from '../b2TimeStep'; +import { + b2Baumgarte, + b2LinearSlop, + b2MaxLinearCorrection, + b2MaxManifoldPoints, + b2ToiBaugarte, + b2VelocityThreshold, + half, + indexTwo +} from '../../Common/b2Settings'; + +export class B2VelocityConstraintPoint { + rA = new B2Vec2(); + rB = new B2Vec2(); + normalImpulse: number = 0.0; + tangentImpulse: number = 0.0; + normalMass: number = 0.0; + tangentMass: number = 0.0; + velocityBias: number = 0.0; +} + +export class B2ContactVelocityConstraint { + points = new Array(); + normal = new B2Vec2(); + normalMass = new B2Mat22(); + mK = new B2Mat22(); + indexA: number = 0; + indexB: number = 0; + invMassA: number = 0.0; + invMassB: number = 0.0; + invIA: number = 0.0; + invIB: number = 0.0; + friction: number = 0.0; + restitution: number = 0.0; + tangentSpeed: number = 0.0; + pointCount: number = 0; + contactIndex: number = 0; +} + +export class B2ContactSolverDef { + step = new B2TimeStep(); + contacts = new Array(); + count: number = 0; + positions = new B2Array(); + velocities = new B2Array(); +} + +export class B2ContactSolver { + constructor(def: B2ContactSolverDef) { + this.mStep = def.step; + this.mCount = def.count; + this.mPositionConstraints = new Array(); + this.mVelocityConstraints = new Array(); + this.mPositions = def.positions; + this.mVelocities = def.velocities; + this.mContacts = def.contacts; + for (let i = 0; i < this.mCount; i++) { + let contact = this.mContacts[i]; + let fixtureA = contact.mFixtureA; + let fixtureB = contact.mFixtureB; + let shapeA = fixtureA?.shape; + let shapeB = fixtureB?.shape; + let radiusA = shapeA?.mRadius; + let radiusB = shapeB?.mRadius; + let bodyA = fixtureA?.body; + let bodyB = fixtureB?.body; + let manifold = contact.manifold; + let pointCount = manifold.pointCount; + let vc = new B2ContactVelocityConstraint(); + vc.friction = contact.mFriction; + vc.restitution = contact.mRestitution; + vc.tangentSpeed = contact.mTangentSpeed; + vc.indexA = bodyA?.mIslandIndex!; + vc.indexB = bodyB?.mIslandIndex!; + vc.invMassA = bodyA?.mInvMass!; + vc.invMassB = bodyB?.mInvMass!; + vc.invIA = bodyA?.mInvI!; + vc.invIB = bodyB?.mInvI!; + vc.contactIndex = i; + vc.pointCount = pointCount; + vc.mK.setZero(); + vc.normalMass.setZero(); + this.mVelocityConstraints.push(vc); + let pc = new B2ContactPositionConstraint(); + pc.indexA = bodyA?.mIslandIndex!; + pc.indexB = bodyB?.mIslandIndex!; + pc.invMassA = bodyA?.mInvMass!; + pc.invMassB = bodyB?.mInvMass!; + pc.localCenterA = bodyA?.mSweep.localCenter!; + pc.localCenterB = bodyB?.mSweep.localCenter!; + pc.invIA = bodyA?.mInvI!; + pc.invIB = bodyB?.mInvI!; + pc.localNormal = manifold.localNormal; + pc.localPoint = manifold.localPoint; + pc.pointCount = pointCount; + pc.radiusA = radiusA!; + pc.radiusB = radiusB!; + pc.type = manifold.type; + this.mPositionConstraints.push(pc); + for (let j = 0; j < pointCount; j++) { + let cp = manifold.points[j]; + let vcp = new B2VelocityConstraintPoint(); + if (this.mStep.warmStarting) { + vcp.normalImpulse = this.mStep.dtRatio * cp.normalImpulse; + vcp.tangentImpulse = this.mStep.dtRatio * cp.tangentImpulse; + } else { + vcp.normalImpulse = 0.0; + vcp.tangentImpulse = 0.0; + } + vcp.rA.setZero(); + vcp.rB.setZero(); + vcp.normalMass = 0.0; + vcp.tangentMass = 0.0; + vcp.velocityBias = 0.0; + vc.points.push(vcp); + pc.localPoints[j] = cp.localPoint; + } + } + } + + initializeVelocityConstraints(): void { + for (let i = 0; i < this.mCount; i++) { + let vc = this.mVelocityConstraints[i]; + let pc = this.mPositionConstraints[i]; + let radiusA = pc.radiusA; + let radiusB = pc.radiusB; + let manifold = this.mContacts[vc.contactIndex].manifold; + let indexA = vc.indexA; + let indexB = vc.indexB; + let mA = vc.invMassA; + let mB = vc.invMassB; + let iA = vc.invIA; + let iB = vc.invIB; + let localCenterA = pc.localCenterA; + let localCenterB = pc.localCenterB; + let cA: B2Vec2 = this.mPositions.get(indexA).c; + let aA: number = this.mPositions.get(indexA).a; + let vA: B2Vec2 = this.mVelocities.get(indexA).v; + let wA: number = this.mVelocities.get(indexA).w; + let cB: B2Vec2 = this.mPositions.get(indexB).c; + let aB: number = this.mPositions.get(indexB).a; + let vB: B2Vec2 = this.mVelocities.get(indexB).v; + let wB: number = this.mVelocities.get(indexB).w; + let xfA = new B2Transform(); + let xfB = new B2Transform(); + xfA.q.set(aA); + xfB.q.set(aB); + xfA.p = subtract(cA, b2MulR2(xfA.q, localCenterA)); + xfB.p = subtract(cB, b2MulR2(xfB.q, localCenterB)); + let worldManifold = new B2WorldManifold(); + worldManifold.initialize(manifold, xfA, radiusA, xfB, radiusB); + vc.normal = worldManifold.normal; + let pointCount = vc.pointCount; + for (let j = 0; j < pointCount; j++) { + let vcp = vc.points[j]; + vcp.rA = subtract(worldManifold.points[j], cA); + vcp.rB = subtract(worldManifold.points[j], cB); + let rnA = b2Cross(vcp.rA, vc.normal); + let rnB = b2Cross(vcp.rB, vc.normal); + let kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB; + vcp.normalMass = kNormal > 0.0 ? 1.0 / kNormal : 0.0; + let tangent = b2Cross12(vc.normal, 1.0); + let rtA = b2Cross(vcp.rA, tangent); + let rtB = b2Cross(vcp.rB, tangent); + let kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB; + vcp.tangentMass = kTangent > 0.0 ? 1.0 / kTangent : 0.0; + vcp.velocityBias = 0.0; + let vRel = b2Dot22(vc.normal, subtract(subtract(add(vB, b2Cross21(wB, vcp.rB)), vA), b2Cross21(wA, vcp.rA))); + if (vRel < -b2VelocityThreshold) { + vcp.velocityBias = -vc.restitution * vRel; + } + } + if (vc.pointCount === indexTwo) { + let vcp1 = vc.points[0]; + let vcp2 = vc.points[1]; + let rn1A = b2Cross(vcp1.rA, vc.normal); + let rn1B = b2Cross(vcp1.rB, vc.normal); + let rn2A = b2Cross(vcp2.rA, vc.normal); + let rn2B = b2Cross(vcp2.rB, vc.normal); + let k11 = mA + mB + iA * rn1A * rn1A + iB * rn1B * rn1B; + let k22 = mA + mB + iA * rn2A * rn2A + iB * rn2B * rn2B; + let k12 = mA + mB + iA * rn1A * rn2A + iB * rn1B * rn2B; + let kMaxConditionNumber: number = 1000.0; + if (k11 * k11 < kMaxConditionNumber * (k11 * k22 - k12 * k12)) { + vc.mK.ex.set(k11, k12); + vc.mK.ey.set(k12, k22); + vc.normalMass = vc.mK.getInverse(); + } else { + vc.pointCount = 1; + } + } + } + } + + warmStart(): void { + for (let i = 0; i < this.mCount; i++) { + let vc = this.mVelocityConstraints[i]; + let indexA = vc.indexA; + let indexB = vc.indexB; + let mA = vc.invMassA; + let iA = vc.invIA; + let mB = vc.invMassB; + let iB = vc.invIB; + let pointCount = vc.pointCount; + let vA: B2Vec2 = this.mVelocities.get(indexA).v; + let wA: number = this.mVelocities.get(indexA).w; + let vB: B2Vec2 = this.mVelocities.get(indexB).v; + let wB: number = this.mVelocities.get(indexB).w; + let normal = vc.normal; + let tangent = b2Cross12(normal, 1.0); + for (let j = 0; j < pointCount; j++) { + let vcp = vc.points[j]; + let mP = add(multM(normal, vcp.normalImpulse), multM(tangent, vcp.tangentImpulse)); + wA -= iA * b2Cross(vcp.rA, mP); + subtractEqual(vA, multM(mP, mA)); + wB += iB * b2Cross(vcp.rB, mP); + addEqual(vB, multM(mP, mB)); + } + this.mVelocities.get(indexA).v = vA; + this.mVelocities.get(indexA).w = wA; + this.mVelocities.get(indexB).v = vB; + this.mVelocities.get(indexB).w = wB; + } + } + + solveVelocityConstraints(): void { + for (let i = 0; i < this.mCount; i++) { + let vc = this.mVelocityConstraints[i]; + let indexA = vc.indexA; + let indexB = vc.indexB; + let mA = vc.invMassA; + let iA = vc.invIA; + let mB = vc.invMassB; + let iB = vc.invIB; + let pointCount = vc.pointCount; + let vA: B2Vec2 = this.mVelocities.get(indexA).v; + let wA: number = this.mVelocities.get(indexA).w; + let vB: B2Vec2 = this.mVelocities.get(indexB).v; + let wB: number = this.mVelocities.get(indexB).w; + let normal = vc.normal; + let tangent = b2Cross12(normal, 1.0); + let friction = vc.friction; + for (let j = 0; j < pointCount; j++) { + let vcp = vc.points[j]; + let dv = subtract(subtract(add(vB, b2Cross21(wB, vcp.rB)), vA), b2Cross21(wA, vcp.rA)); + let vt = b2Dot22(dv, tangent) - vc.tangentSpeed; + let lambda = vcp.tangentMass * -vt; + let maxFriction = friction * vcp.normalImpulse; + let newImpulse = b2ClampF(vcp.tangentImpulse + lambda, -maxFriction, maxFriction); + lambda = newImpulse - vcp.tangentImpulse; + vcp.tangentImpulse = newImpulse; + let mP = multM(tangent, lambda); + subtractEqual(vA, multM(mP, mA)); + wA -= iA * b2Cross(vcp.rA, mP); + addEqual(vB, multM(mP, mB)); + wB += iB * b2Cross(vcp.rB, mP); + } + if (vc.pointCount === 1) { + let vcp = vc.points[0]; + let dv = subtract(subtract(add(vB, b2Cross21(wB, vcp.rB)), vA), b2Cross21(wA, vcp.rA)); + let vn = b2Dot22(dv, normal); + let lambda = -vcp.normalMass * (vn - vcp.velocityBias); + let newImpulse = Math.max(vcp.normalImpulse + lambda, 0.0); + lambda = newImpulse - vcp.normalImpulse; + vcp.normalImpulse = newImpulse; + let mP = multM(normal, lambda); + subtractEqual(vA, multM(mP, mA)); + wA -= iA * b2Cross(vcp.rA, mP); + addEqual(vB, multM(mP, mB)); + wB += iB * b2Cross(vcp.rB, mP); + } else { + let cp1 = vc.points[0]; + let cp2 = vc.points[1]; + let a = new B2Vec2(cp1.normalImpulse, cp2.normalImpulse); + let dv1 = subtract(subtract(add(vB, b2Cross21(wB, cp1.rB)), vA), b2Cross21(wA, cp1.rA)); + let dv2 = subtract(subtract(add(vB, b2Cross21(wB, cp2.rB)), vA), b2Cross21(wA, cp2.rA)); + let vn1 = b2Dot22(dv1, normal); + let vn2 = b2Dot22(dv2, normal); + let b = new B2Vec2(); + b.x = vn1 - cp1.velocityBias; + b.y = vn2 - cp2.velocityBias; + subtractEqual(b, b2Mul22(vc.mK, a)); + while (true) { + let x = minus(b2Mul22(vc.normalMass, b)); + if (x.x >= 0.0 && x.y >= 0.0) { + let d = subtract(x, a); + let mP1 = multM(normal, d.x); + let mP2 = multM(normal, d.y); + subtractEqual(vA, multM(add(mP1, mP2), mA)); + wA -= iA * (b2Cross(cp1.rA, mP1) + b2Cross(cp2.rA, mP2)); + addEqual(vB, multM(add(mP1, mP2), mB)); + wB += iB * (b2Cross(cp1.rB, mP1) + b2Cross(cp2.rB, mP2)); + cp1.normalImpulse = x.x; + cp2.normalImpulse = x.y; + dv1 = subtract(subtract(add(vB, b2Cross21(wB, cp1.rB)), vA), b2Cross21(wA, cp1.rA)); + dv2 = subtract(subtract(add(vB, b2Cross21(wB, cp2.rB)), vA), b2Cross21(wA, cp2.rA)); + vn1 = b2Dot22(dv1, normal); + vn2 = b2Dot22(dv2, normal); + break; + } + x.x = -cp1.normalMass * b.x; + x.y = 0.0; + vn1 = 0.0; + vn2 = vc.mK.ex.y * x.x + b.y; + if (x.x >= 0.0 && vn2 >= 0.0) { + let d = subtract(x, a); + let mP1 = multM(normal, d.x); + let mP2 = multM(normal, d.y); + subtractEqual(vA, multM(add(mP1, mP2), mA)); + wA -= iA * (b2Cross(cp1.rA, mP1) + b2Cross(cp2.rA, mP2)); + addEqual(vB, multM(add(mP1, mP2), mB)); + wB += iB * (b2Cross(cp1.rB, mP1) + b2Cross(cp2.rB, mP2)); + cp1.normalImpulse = x.x; + cp2.normalImpulse = x.y; + dv1 = subtract(subtract(add(vB, b2Cross21(wB, cp1.rB)), vA), b2Cross21(wA, cp1.rA)); + vn1 = b2Dot22(dv1, normal); + break; + } + x.x = 0.0; + x.y = -cp2.normalMass * b.y; + vn1 = vc.mK.ey.x * x.y + b.x; + vn2 = 0.0; + if (x.y >= 0.0 && vn1 >= 0.0) { + let d = subtract(x, a); + let mP1 = multM(normal, d.x); + let mP2 = multM(normal, d.y); + subtractEqual(vA, multM(add(mP1, mP2), mA)); + wA -= iA * (b2Cross(cp1.rA, mP1) + b2Cross(cp2.rA, mP2)); + addEqual(vB, multM(add(mP1, mP2), mB)); + wB += iB * (b2Cross(cp1.rB, mP1) + b2Cross(cp2.rB, mP2)); + cp1.normalImpulse = x.x; + cp2.normalImpulse = x.y; + dv2 = subtract(subtract(add(vB, b2Cross21(wB, cp2.rB)), vA), b2Cross21(wA, cp2.rA)); + vn2 = b2Dot22(dv2, normal); + break; + } + x.x = 0.0; + x.y = 0.0; + vn1 = b.x; + vn2 = b.y; + if (vn1 >= 0.0 && vn2 >= 0.0) { + let d = subtract(x, a); + let mP1 = multM(normal, d.x); + let mP2 = multM(normal, d.y); + subtractEqual(vA, multM(add(mP1, mP2), mA)); + wA -= iA * (b2Cross(cp1.rA, mP1) + b2Cross(cp2.rA, mP2)); + addEqual(vB, multM(add(mP1, mP2), mB)); + wB += iB * (b2Cross(cp1.rB, mP1) + b2Cross(cp2.rB, mP2)); + cp1.normalImpulse = x.x; + cp2.normalImpulse = x.y; + break; + } + break; + } + } + this.mVelocities.get(indexA).v = vA; + this.mVelocities.get(indexA).w = wA; + this.mVelocities.get(indexB).v = vB; + this.mVelocities.get(indexB).w = wB; + } + } + + storeImpulses(): void { + for (let i = 0; i < this.mCount; i++) { + let vc = this.mVelocityConstraints[i]; + let manifold = this.mContacts[vc.contactIndex].manifold; + for (let j = 0; j < vc.pointCount; j++) { + manifold.points[j].normalImpulse = vc.points[j].normalImpulse; + manifold.points[j].tangentImpulse = vc.points[j].tangentImpulse; + } + } + } + + solvePositionConstraints(): boolean { + let minSeparation: number = 0.0; + let ratio: number = -3.0; + for (let i = 0; i < this.mCount; i++) { + let pc = this.mPositionConstraints[i]; + let indexA = pc.indexA; + let indexB = pc.indexB; + let localCenterA = pc.localCenterA; + let mA = pc.invMassA; + let iA = pc.invIA; + let localCenterB = pc.localCenterB; + let mB = pc.invMassB; + let iB = pc.invIB; + let pointCount = pc.pointCount; + let cA: B2Vec2 = this.mPositions.get(indexA).c; + let aA: number = this.mPositions.get(indexA).a; + let cB: B2Vec2 = this.mPositions.get(indexB).c; + let aB: number = this.mPositions.get(indexB).a; + for (let j = 0; j < pointCount; j++) { + let xfA = new B2Transform(); + let xfB = new B2Transform(); + xfA.q.set(aA); + xfB.q.set(aB); + xfA.p = subtract(cA, b2MulR2(xfA.q, localCenterA)); + xfB.p = subtract(cB, b2MulR2(xfB.q, localCenterB)); + let psm = new B2PositionSolverManifold(); + psm.initialize(pc, xfA, xfB, j); + let normal = psm.normal; + let point = psm.point; + let separation = psm.separation; + let rA = subtract(point, cA); + let rB = subtract(point, cB); + minSeparation = Math.min(minSeparation, separation); + let mC = b2ClampF(b2Baumgarte * (separation + b2LinearSlop), -b2MaxLinearCorrection, 0.0); + let rnA = b2Cross(rA, normal); + let rnB = b2Cross(rB, normal); + let mK = mA + mB + iA * rnA * rnA + iB * rnB * rnB; + let impulse = mK > 0.0 ? -mC / mK : 0.0; + let mP = multM(normal, impulse); + subtractEqual(cA, multM(mP, mA)); + aA -= iA * b2Cross(rA, mP); + addEqual(cB, multM(mP, mB)); + aB += iB * b2Cross(rB, mP); + } + this.mPositions.get(indexA).c = cA; + this.mPositions.get(indexA).a = aA; + this.mPositions.get(indexB).c = cB; + this.mPositions.get(indexB).a = aB; + } + return minSeparation >= ratio * b2LinearSlop; + } + + solveToiPositionConstraints(toiIndexA: number, toiIndexB: number): boolean { + let ratio: number = -1.5; + let minSeparation: number = 0.0; + for (let i = 0; i < this.mCount; i++) { + let pc = this.mPositionConstraints[i]; + let indexA = pc.indexA; + let indexB = pc.indexB; + let localCenterA = pc.localCenterA; + let localCenterB = pc.localCenterB; + let pointCount = pc.pointCount; + let mA: number = 0.0; + let iA: number = 0.0; + if (indexA === toiIndexA || indexA === toiIndexB) { + mA = pc.invMassA; + iA = pc.invIA; + } + let mB: number = 0.0; + let iB: number = 0.0; + if (indexB === toiIndexA || indexB === toiIndexB) { + mB = pc.invMassB; + iB = pc.invIB; + } + let cA: B2Vec2 = this.mPositions.get(indexA).c; + let aA: number = this.mPositions.get(indexA).a; + let cB: B2Vec2 = this.mPositions.get(indexB).c; + let aB: number = this.mPositions.get(indexB).a; + for (let j = 0; j < pointCount; j++) { + let xfA = new B2Transform(); + let xfB = new B2Transform(); + xfA.q.set(aA); + xfB.q.set(aB); + xfA.p = subtract(cA, b2MulR2(xfA.q, localCenterA)); + xfB.p = subtract(cB, b2MulR2(xfB.q, localCenterB)); + let psm = new B2PositionSolverManifold(); + psm.initialize(pc, xfA, xfB, j); + let normal = psm.normal; + let point = psm.point; + let separation = psm.separation; + let rA = subtract(point, cA); + let rB = subtract(point, cB); + minSeparation = Math.min(minSeparation, separation); + let mC = b2ClampF(b2ToiBaugarte * (separation + b2LinearSlop), -b2MaxLinearCorrection, 0.0); + let rnA = b2Cross(rA, normal); + let rnB = b2Cross(rB, normal); + let mK = mA + mB + iA * rnA * rnA + iB * rnB * rnB; + let impulse = mK > 0.0 ? -mC / mK : 0.0; + let mP = multM(normal, impulse); + subtractEqual(cA, multM(mP, mA)); + aA -= iA * b2Cross(rA, mP); + addEqual(cB, multM(mP, mB)); + aB += iB * b2Cross(rB, mP); + } + this.mPositions.get(indexA).c = cA; + this.mPositions.get(indexA).a = aA; + this.mPositions.get(indexB).c = cB; + this.mPositions.get(indexB).a = aB; + } + return minSeparation >= ratio * b2LinearSlop; + } + + mStep: B2TimeStep; + mPositions: B2Array; + mVelocities: B2Array; + mPositionConstraints: Array; + mVelocityConstraints: Array; + mContacts: Array; + mCount: number; +} + +export class B2ContactPositionConstraint { + localPoints: B2Vec2[]; + localNormal = new B2Vec2(); + localPoint = new B2Vec2(); + indexA = 0; + indexB = 0; + invMassA: number = 0.0; + invMassB: number = 0.0; + localCenterA = new B2Vec2(); + localCenterB = new B2Vec2(); + invIA: number = 0; + invIB: number = 0; + type: B2ManifoldType = B2ManifoldType.CIRCLES; + radiusA: number = 0; + radiusB: number = 0; + pointCount = 0; + + constructor() { + this.localPoints = []; + for (let i = 0; i < b2MaxManifoldPoints; i++) { + this.localPoints.push(new B2Vec2()); + } + } +} + +export class B2PositionSolverManifold { + initialize(pc: B2ContactPositionConstraint, xfA: B2Transform, xfB: B2Transform, index: number): void { + switch (pc.type) { + case B2ManifoldType.CIRCLES: { + let pointA = b2MulT2(xfA, pc.localPoint); + let pointB = b2MulT2(xfB, pc.localPoints[0]); + this.normal = subtract(pointB, pointA); + this.normal.normalize(); + this.point = multM(add(pointA, pointB), half); + this.separation = b2Dot22(subtract(pointB, pointA), this.normal) - pc.radiusA - pc.radiusB; + break; + } + case B2ManifoldType.FACEA: { + this.normal = b2MulR2(xfA.q, pc.localNormal); + let planePoint0 = b2MulT2(xfA, pc.localPoint); + let clipPoint0 = b2MulT2(xfB, pc.localPoints[index]); + this.separation = b2Dot22(subtract(clipPoint0, planePoint0), this.normal) - pc.radiusA - pc.radiusB; + this.point = clipPoint0; + break; + } + case B2ManifoldType.FACEB: { + this.normal = b2MulR2(xfB.q, pc.localNormal); + let planePoint = b2MulT2(xfB, pc.localPoint); + let clipPoint = b2MulT2(xfA, pc.localPoints[index]); + this.separation = b2Dot22(subtract(clipPoint, planePoint), this.normal) - pc.radiusA - pc.radiusB; + this.point = clipPoint; + this.normal = minus(this.normal); + break; + } + default: + break; + } + } + normal = new B2Vec2(); + point = new B2Vec2(); + separation: number = 0.0; +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Joints/b2Joint.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Joints/b2Joint.ts new file mode 100644 index 0000000000000000000000000000000000000000..490346b2189b64df8f10fbe4f73dccba3f1ab387 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/Joints/b2Joint.ts @@ -0,0 +1,2516 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Body } from '../b2Body'; +import { + add, + addEqual, + addEqual3, + b2ClampF, + b2Cross, + b2Cross21, + b2Dot22, + B2Mat22, + B2Mat33, + b2Mul22, + b2Mul32, + b2Mul33, + b2MulR2, + b2MulTR2, + b2MulTT2, + B2Rot, + B2Vec2, + B2Vec3, + minus, + minus3, + mulEqual, + mulMEqual3, + multM, + subtract, + subtract3, + subtractEqual +} from '../../Common/b2Math'; +import { B2SolverData } from '../b2TimeStep'; +import { b2AngularSlop, b2LinearSlop, b2MaxAngularCorrection, b2MaxLinearCorrection, b2PI, float10, float2 } from '../../Common/b2Settings'; + +enum B2JointType { + UNKNOWNJOINT, + REVOLUTEJOINT, + PRISMATICJOINT, + DISTANCEJOINT, + PULLEYJOINT, + MOUSEJOINT, + GEARJOINT, + WHEELJOINT, + WELDJOINT, + FRICTIONJOINT, + ROPEJOINT, + MOTORJOINT +} + +enum B2LimitState { + INACTIVELIMIT, + ATLOWERLIMIT, + ATUPPERLIMIT, + EQUALLIMITS +} + +export class B2JointEdge { + constructor(joint: B2Joint) { + this.joint = joint; + } + other: B2Body | null = null; + joint: B2Joint; + prev: B2JointEdge | null = null; + next: B2JointEdge | null = null; +} + +export class B2JointDef { + constructor() { + this.type = B2JointType.UNKNOWNJOINT; + this.userData = null; + this.bodyA = null; + this.bodyB = null; + this.collideConnected = false; + } + public type: B2JointType; + public userData: Object | null; + public bodyA: B2Body | null; + public bodyB: B2Body | null; + public collideConnected: boolean; +} + +export class B2Joint { + mType: B2JointType = B2JointType.UNKNOWNJOINT; + mPrev: B2Joint | null = null; + mNext: B2Joint | null = null; + mEdgeA: B2JointEdge; + mEdgeB: B2JointEdge; + mBodyA: B2Body | null; + mBodyB: B2Body | null; + mIndex: number = 0; + mIslandFlag: boolean = false; + mCollideConnected: boolean = false; + mUserData: Object | null = null; + public get typeJoint(): B2JointType { + return this.mType; + } + public get bodyA(): B2Body | null { + return this.mBodyA; + } + public get bodyB(): B2Body | null { + return this.mBodyB; + } + public get anchorA(): B2Vec2 { + throw new Error('must override'); + } + public get anchorB(): B2Vec2 { + throw new Error('must override'); + } + constructor(def: B2JointDef) { + this.mType = def.type; + this.mPrev = null; + this.mNext = null; + this.mBodyA = def.bodyA; + this.mBodyB = def.bodyB; + this.mIndex = 0; + this.mCollideConnected = def.collideConnected; + this.mIslandFlag = false; + this.mUserData = def.userData; + this.mEdgeA = new B2JointEdge(this); + this.mEdgeA.other = null; + this.mEdgeA.prev = null; + this.mEdgeA.next = null; + this.mEdgeB = new B2JointEdge(this); + this.mEdgeB.other = null; + this.mEdgeB.prev = null; + this.mEdgeB.next = null; + } + public getReactionForce(invdt: number): B2Vec2 { + throw new Error('must override'); + } + public getReactionTorque(invdt: number): number { + throw new Error('must override'); + } + public getNext(): B2Joint | null { + return this.mNext; + } + public get userData(): Object | null { + return this.mUserData; + } + public set userData(value: Object | null) { + this.setUserData(value); + } + setUserData(data: Object | null): void { + this.mUserData = data; + } + get isActive(): boolean { + return this.mBodyA!.isActive && this.mBodyB!.isActive; + } + collideConnected(): boolean { + return this.mCollideConnected; + } + public shiftOrigin(newOrigin: B2Vec2): void {} + static create(def: B2JointDef): B2Joint { + let joint: B2Joint; + switch (def.type) { + case B2JointType.DISTANCEJOINT: + joint = new B2DistanceJoint(def as B2DistanceJointDef); + break; + case B2JointType.MOUSEJOINT: + joint = new B2MouseJoint(def as B2MouseJointDef); + break; + case B2JointType.PRISMATICJOINT: + joint = new B2PrismaticJoint(def as B2PrismaticJointDef); + break; + case B2JointType.REVOLUTEJOINT: + joint = new B2RevoluteJoint(def as B2RevoluteJointDef); + break; + case B2JointType.PULLEYJOINT: + joint = new B2PulleyJoint(def as B2PulleyJointDef); + break; + case B2JointType.GEARJOINT: + joint = new B2GearJoint(def as B2GearJointDef); + break; + case B2JointType.WELDJOINT: + joint = new B2WeldJoint(def as B2WeldJointDef); + break; + case B2JointType.FRICTIONJOINT: + joint = new B2FrictionJoint(def as B2FrictionJointDef); + break; + default: + throw new Error('Unknown joint type'); + } + return joint; + } + static destroy(joint: B2Joint): void {} + initVelocityConstraints(data: B2SolverData): void { + throw new Error('must override'); + } + solveVelocityConstraints(data: B2SolverData): void { + throw new Error('must override'); + } + solvePositionConstraints(data: B2SolverData): boolean { + throw new Error('must override'); + } +} + +export class B2WeldJointDef extends B2JointDef { + public constructor() { + super(); + this.localAnchorA = new B2Vec2(0.0, 0.0); + this.localAnchorB = new B2Vec2(0.0, 0.0); + this.referenceAngle = 0.0; + this.frequencyHz = 0.0; + this.dampingRatio = 0.0; + this.type = B2JointType.WELDJOINT; + } + + public initialize(bodyA: B2Body, bodyB: B2Body, anchor: B2Vec2): void { + this.bodyA = bodyA; + this.bodyB = bodyB; + this.localAnchorA = bodyA.getLocalPoint(anchor); + this.localAnchorB = bodyB.getLocalPoint(anchor); + this.referenceAngle = bodyB.angle - bodyA.angle; + } + public localAnchorA: B2Vec2; + public localAnchorB: B2Vec2; + public referenceAngle: number; + public frequencyHz: number; + public dampingRatio: number; +} + +export class B2WeldJoint extends B2Joint { + mFrequencyHz: number = 0.0; + mDampingRatio: number = 0.0; + mBias: number = 0.0; + mLocalAnchorA = new B2Vec2(); + mLocalAnchorB = new B2Vec2(); + mReferenceAngle: number = 0.0; + mGamma: number = 0.0; + mImpulse = new B2Vec3(); + mIndexA: number = 0; + mIndexB: number = 0; + mrA = new B2Vec2(); + mrB = new B2Vec2(); + mLocalCenterA = new B2Vec2(); + mLocalCenterB = new B2Vec2(); + mInvMassA: number = 0.0; + mInvMassB: number = 0.0; + mInvIA: number = 0.0; + mInvIB: number = 0.0; + mMass = new B2Mat33(); + public get anchorA(): B2Vec2 { + return this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + } + public get anchorB(): B2Vec2 { + return this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + } + public getReactionForce(invdt: number): B2Vec2 { + let mP = new B2Vec2(this.mImpulse.x, this.mImpulse.y); + return multM(mP, invdt); + } + public getReactionTorque(invdt: number): number { + return invdt * this.mImpulse.z; + } + public get localAnchorA(): B2Vec2 { + return this.mLocalAnchorA; + } + public get localAnchorB(): B2Vec2 { + return this.mLocalAnchorB; + } + public get referenceAngle(): number { + return this.mReferenceAngle; + } + public setFrequency(hz: number): void { + this.mFrequencyHz = hz; + } + public get frequency(): number { + return this.mFrequencyHz; + } + public setDampingRatio(ratio: number): void { + this.mDampingRatio = ratio; + } + public get dampingRatio(): number { + return this.mDampingRatio; + } + constructor(def: B2WeldJointDef) { + super(def); + this.mLocalAnchorA = def.localAnchorA; + this.mLocalAnchorB = def.localAnchorB; + this.mReferenceAngle = def.referenceAngle; + this.mFrequencyHz = def.frequencyHz; + this.mDampingRatio = def.dampingRatio; + this.mImpulse = new B2Vec3(0.0, 0.0, 0.0); + } + + initVelocityConstraints(data: B2SolverData): void { + this.mIndexA = this.mBodyA!.mIslandIndex; + this.mIndexB = this.mBodyB!.mIslandIndex; + this.mLocalCenterA = this.mBodyA!.mSweep.localCenter; + this.mLocalCenterB = this.mBodyB!.mSweep.localCenter; + this.mInvMassA = this.mBodyA!.mInvMass; + this.mInvMassB = this.mBodyB!.mInvMass; + this.mInvIA = this.mBodyA!.mInvI; + this.mInvIB = this.mBodyB!.mInvI; + let aA: number = data.positions.get(this.mIndexA).a; + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let aB: number = data.positions.get(this.mIndexB).a; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + this.mrA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + this.mrB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + let mat = new B2Mat33(); + mat.ex.x = mA + mB + this.mrA.y * this.mrA.y * iA + this.mrB.y * this.mrB.y * iB; + mat.ey.x = -this.mrA.y * this.mrA.x * iA - this.mrB.y * this.mrB.x * iB; + mat.ez.x = -this.mrA.y * iA - this.mrB.y * iB; + mat.ex.y = mat.ey.x; + mat.ey.y = mA + mB + this.mrA.x * this.mrA.x * iA + this.mrB.x * this.mrB.x * iB; + mat.ez.y = this.mrA.x * iA + this.mrB.x * iB; + mat.ex.z = mat.ez.x; + mat.ey.z = mat.ez.y; + mat.ez.z = iA + iB; + if (this.mFrequencyHz > 0.0) { + this.mMass = mat.getInverse22(); + let invM = iA + iB; + let m = invM > 0.0 ? 1.0 / invM : 0.0; + let c = aB - aA - this.mReferenceAngle; + let omega = float2 * b2PI * this.mFrequencyHz; + let d = float2 * m * this.mDampingRatio * omega; + let k = m * omega * omega; + let h = data.step.dt; + this.mGamma = h * (d + h * k); + this.mGamma = this.mGamma !== 0.0 ? 1.0 / this.mGamma : 0.0; + this.mBias = c * h * k * this.mGamma; + invM += this.mGamma; + this.mMass.ez.z = invM !== 0.0 ? 1.0 / invM : 0.0; + } else { + this.mMass = mat.getSymInverse33(); + this.mGamma = 0.0; + this.mBias = 0.0; + } + if (data.step.warmStarting) { + mulMEqual3(this.mImpulse, data.step.dtRatio); + let mP = new B2Vec2(this.mImpulse.x, this.mImpulse.y); + subtractEqual(vA, multM(mP, mA)); + wA -= iA * (b2Cross(this.mrA, mP) + this.mImpulse.z); + addEqual(vB, multM(mP, mB)); + wB += iB * (b2Cross(this.mrB, mP) + this.mImpulse.z); + } else { + this.mImpulse.setZero(); + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solveVelocityConstraints(data: B2SolverData): void { + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + if (this.mFrequencyHz > 0.0) { + let cdot2 = wB - wA; + let impulse2 = -this.mMass.ez.z * (cdot2 + this.mBias + this.mGamma * this.mImpulse.z); + this.mImpulse.z += impulse2; + wA -= iA * impulse2; + wB += iB * impulse2; + let cdot1 = subtract(subtract(add(vB, b2Cross21(wB, this.mrB)), vA), b2Cross21(wA, this.mrA)); + let impulse1 = minus(b2Mul32(this.mMass, cdot1)); + this.mImpulse.x += impulse1.x; + this.mImpulse.y += impulse1.y; + let mP = impulse1; + subtractEqual(vA, multM(mP, mA)); + wA -= iA * b2Cross(this.mrA, mP); + addEqual(vB, multM(mP, mB)); + wB += iB * b2Cross(this.mrB, mP); + } else { + let cdot1 = subtract(subtract(add(vB, b2Cross21(wB, this.mrB)), vA), b2Cross21(wA, this.mrA)); + let cdot2 = wB - wA; + let cdot = new B2Vec3(cdot1.x, cdot1.y, cdot2); + let impulse = minus3(b2Mul33(this.mMass, cdot)); + addEqual3(this.mImpulse, impulse); + let mP = new B2Vec2(impulse.x, impulse.y); + subtractEqual(vA, multM(mP, mA)); + wA -= iA * (b2Cross(this.mrA, mP) + impulse.z); + addEqual(vB, multM(mP, mB)); + wB += iB * (b2Cross(this.mrB, mP) + impulse.z); + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solvePositionConstraints(data: B2SolverData): boolean { + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + let rA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + let rB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let positionError: number; + let angularError: number; + let mK = new B2Mat33(); + mK.ex.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB; + mK.ey.x = -rA.y * rA.x * iA - rB.y * rB.x * iB; + mK.ez.x = -rA.y * iA - rB.y * iB; + mK.ex.y = mK.ey.x; + mK.ey.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB; + mK.ez.y = rA.x * iA + rB.x * iB; + mK.ex.z = mK.ez.x; + mK.ey.z = mK.ez.y; + mK.ez.z = iA + iB; + if (this.mFrequencyHz > 0.0) { + let mC1 = subtract(subtract(add(cB, rB), cA), rA); + positionError = mC1.length(); + angularError = 0.0; + let mP = minus(mK.solve22(mC1)); + subtractEqual(cA, multM(mP, mA)); + aA -= iA * b2Cross(rA, mP); + addEqual(cB, multM(mP, mB)); + aB += iB * b2Cross(rB, mP); + } else { + let mC1 = subtract(subtract(add(cB, rB), cA), rA); + let mC2 = aB - aA - this.mReferenceAngle; + positionError = mC1.length(); + angularError = Math.abs(mC2); + let mC = new B2Vec3(mC1.x, mC1.y, mC2); + let impulse = minus3(mK.solve33(mC)); + let mP = new B2Vec2(impulse.x, impulse.y); + subtractEqual(cA, multM(mP, mA)); + aA -= iA * (b2Cross(rA, mP) + impulse.z); + addEqual(cB, multM(mP, mB)); + aB += iB * (b2Cross(rB, mP) + impulse.z); + } + data.positions.get(this.mIndexA).c = cA; + data.positions.get(this.mIndexA).a = aA; + data.positions.get(this.mIndexB).c = cB; + data.positions.get(this.mIndexB).a = aB; + return positionError <= b2LinearSlop && angularError <= b2AngularSlop; + } +} + +export class B2RevoluteJointDef extends B2JointDef { + public constructor() { + super(); + this.localAnchorA = new B2Vec2(0.0, 0.0); + this.localAnchorB = new B2Vec2(0.0, 0.0); + this.referenceAngle = 0.0; + this.lowerAngle = 0.0; + this.upperAngle = 0.0; + this.maxMotorTorque = 0.0; + this.motorSpeed = 0.0; + this.enableLimit = false; + this.enableMotor = false; + this.type = B2JointType.REVOLUTEJOINT; + } + + public initialize(bodyA: B2Body, bodyB: B2Body, anchor: B2Vec2): void { + this.bodyA = bodyA; + this.bodyB = bodyB; + this.localAnchorA = bodyA.getLocalPoint(anchor); + this.localAnchorB = bodyB.getLocalPoint(anchor); + this.referenceAngle = bodyB.angle - bodyA.angle; + } + public localAnchorA: B2Vec2; + public localAnchorB: B2Vec2; + public referenceAngle: number; + public maxMotorTorque: number; + public motorSpeed: number; + public lowerAngle: number; + public upperAngle: number; + public enableLimit: boolean; + public enableMotor: boolean; +} + +export class B2RevoluteJoint extends B2Joint { + public get anchorA(): B2Vec2 { + return this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + } + public get anchorB(): B2Vec2 { + return this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + } + public get localAnchorA(): B2Vec2 { + return this.mLocalAnchorA; + } + public get localAnchorB(): B2Vec2 { + return this.mLocalAnchorB; + } + public get referenceAngle(): number { + return this.mReferenceAngle; + } + public get jointAngle(): number { + let bA = this.mBodyA; + let bB = this.mBodyB; + return bB!.mSweep.a - bA!.mSweep.a - this.mReferenceAngle; + } + + public get jointSpeed(): number { + let bA = this.mBodyA; + let bB = this.mBodyB; + return bB!.mAngularVelocity - bA!.mAngularVelocity; + } + + public get isLimitEnabled(): boolean { + return this.mEnableLimit; + } + public set isLimitEnabled(newValue: boolean) { + this.enableLimit(newValue); + } + + public enableLimit(flag: boolean): void { + if (flag !== this.mEnableLimit) { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mEnableLimit = flag; + this.mImpulse.z = 0.0; + } + } + + public get lowerLimit(): number { + return this.mLowerAngle; + } + + public get upperLimit(): number { + return this.mUpperAngle; + } + + public setLimits(lower: number, upper: number): void { + if (lower !== this.mLowerAngle || upper !== this.mUpperAngle) { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mImpulse.z = 0.0; + this.mLowerAngle = lower; + this.mUpperAngle = upper; + } + } + + public get isMotorEnabled(): boolean { + return this.mEnableMotor; + } + + public enableMotor(flag: boolean): void { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mEnableMotor = flag; + } + + public setMotorSpeed(speed: number): void { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mMotorSpeed = speed; + } + + public get motorSpeed(): number { + return this.mMotorSpeed; + } + public set motorSpeed(newValue: number) { + this.setMotorSpeed(newValue); + } + + public setMaxMotorTorque(torque: number): void { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mMaxMotorTorque = torque; + } + public get maxMotorTorque(): number { + return this.mMaxMotorTorque; + } + public set maxMotorTorque(newValue: number) { + this.setMaxMotorTorque(newValue); + } + public getReactionForce(invdt: number): B2Vec2 { + let mP = new B2Vec2(this.mImpulse.x, this.mImpulse.y); + return multM(mP, invdt); + } + + public getReactionTorque(invdt: number): number { + return invdt * this.mImpulse.z; + } + + public getMotorTorque(invdt: number): number { + return invdt * this.mMotorImpulse; + } + + constructor(def: B2RevoluteJointDef) { + super(def); + this.mLocalAnchorA = def.localAnchorA; + this.mLocalAnchorB = def.localAnchorB; + this.mReferenceAngle = def.referenceAngle; + this.mImpulse = new B2Vec3(0.0, 0.0, 0.0); + this.mMotorImpulse = 0.0; + this.mLowerAngle = def.lowerAngle; + this.mUpperAngle = def.upperAngle; + this.mMaxMotorTorque = def.maxMotorTorque; + this.mMotorSpeed = def.motorSpeed; + this.mEnableLimit = def.enableLimit; + this.mEnableMotor = def.enableMotor; + this.mLimitState = B2LimitState.INACTIVELIMIT; + } + + initVelocityConstraints(data: B2SolverData): void { + this.mIndexA = this.mBodyA!.mIslandIndex; + this.mIndexB = this.mBodyB!.mIslandIndex; + this.mLocalCenterA = this.mBodyA!.mSweep.localCenter; + this.mLocalCenterB = this.mBodyB!.mSweep.localCenter; + this.mInvMassA = this.mBodyA!.mInvMass; + this.mInvMassB = this.mBodyB!.mInvMass; + this.mInvIA = this.mBodyA!.mInvI; + this.mInvIB = this.mBodyB!.mInvI; + let aA: number = data.positions.get(this.mIndexA).a; + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let aB: number = data.positions.get(this.mIndexB).a; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + this.mrA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + this.mrB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + let fixedRotation = iA + iB === 0.0; + this.mMass.ex.x = mA + mB + this.mrA.y * this.mrA.y * iA + this.mrB.y * this.mrB.y * iB; + this.mMass.ey.x = -this.mrA.y * this.mrA.x * iA - this.mrB.y * this.mrB.x * iB; + this.mMass.ez.x = -this.mrA.y * iA - this.mrB.y * iB; + this.mMass.ex.y = this.mMass.ey.x; + this.mMass.ey.y = mA + mB + this.mrA.x * this.mrA.x * iA + this.mrB.x * this.mrB.x * iB; + this.mMass.ez.y = this.mrA.x * iA + this.mrB.x * iB; + this.mMass.ex.z = this.mMass.ez.x; + this.mMass.ey.z = this.mMass.ez.y; + this.mMass.ez.z = iA + iB; + this.mMotorMass = iA + iB; + if (this.mMotorMass > 0.0) { + this.mMotorMass = 1.0 / this.mMotorMass; + } + if (this.mEnableMotor === false || fixedRotation) { + this.mMotorImpulse = 0.0; + } + if (this.mEnableLimit && fixedRotation === false) { + let jointAngle = aB - aA - this.mReferenceAngle; + if (Math.abs(this.mUpperAngle - this.mLowerAngle) < float2 * b2AngularSlop) { + this.mLimitState = B2LimitState.EQUALLIMITS; + } else if (jointAngle <= this.mLowerAngle) { + if (this.mLimitState !== B2LimitState.ATLOWERLIMIT) { + this.mImpulse.z = 0.0; + } + this.mLimitState = B2LimitState.ATLOWERLIMIT; + } else if (jointAngle >= this.mUpperAngle) { + if (this.mLimitState !== B2LimitState.ATUPPERLIMIT) { + this.mImpulse.z = 0.0; + } + this.mLimitState = B2LimitState.ATUPPERLIMIT; + } else { + this.mLimitState = B2LimitState.INACTIVELIMIT; + this.mImpulse.z = 0.0; + } + } else { + this.mLimitState = B2LimitState.INACTIVELIMIT; + } + if (data.step.warmStarting) { + mulMEqual3(this.mImpulse, data.step.dtRatio); + this.mMotorImpulse *= data.step.dtRatio; + let mP = new B2Vec2(this.mImpulse.x, this.mImpulse.y); + subtractEqual(vA, multM(mP, mA)); + wA -= iA * (b2Cross(this.mrA, mP) + this.mMotorImpulse + this.mImpulse.z); + addEqual(vB, multM(mP, mB)); + wB += iB * (b2Cross(this.mrB, mP) + this.mMotorImpulse + this.mImpulse.z); + } else { + this.mImpulse.setZero(); + this.mMotorImpulse = 0.0; + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solveVelocityConstraints(data: B2SolverData): void { + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + let fixedRotation = iA + iB === 0.0; + if (this.mEnableMotor && this.mLimitState !== B2LimitState.EQUALLIMITS && fixedRotation === false) { + let cdot = wB - wA - this.mMotorSpeed; + let impulse = -this.mMotorMass * cdot; + let oldImpulse = this.mMotorImpulse; + let maxImpulse = data.step.dt * this.mMaxMotorTorque; + this.mMotorImpulse = b2ClampF(this.mMotorImpulse + impulse, -maxImpulse, maxImpulse); + impulse = this.mMotorImpulse - oldImpulse; + wA -= iA * impulse; + wB += iB * impulse; + } + + if (this.mEnableLimit && this.mLimitState !== B2LimitState.INACTIVELIMIT && fixedRotation === false) { + let cdot1 = subtract(subtract(add(vB, b2Cross21(wB, this.mrB)), vA), b2Cross21(wA, this.mrA)); + let cdot2 = wB - wA; + let cdot = new B2Vec3(cdot1.x, cdot1.y, cdot2); + let impulse = minus3(this.mMass.solve33(cdot)); + if (this.mLimitState === B2LimitState.EQUALLIMITS) { + addEqual3(this.mImpulse, impulse); + } else if (this.mLimitState === B2LimitState.ATLOWERLIMIT) { + let newImpulse = this.mImpulse.z + impulse.z; + if (newImpulse < 0.0) { + let rhs = add(minus(cdot1), multM(new B2Vec2(this.mMass.ez.x, this.mMass.ez.y), this.mImpulse.z)); + let reduced = this.mMass.solve22(rhs); + impulse.x = reduced.x; + impulse.y = reduced.y; + impulse.z = -this.mImpulse.z; + this.mImpulse.x += reduced.x; + this.mImpulse.y += reduced.y; + this.mImpulse.z = 0.0; + } else { + addEqual3(this.mImpulse, impulse); + } + } else if (this.mLimitState === B2LimitState.ATUPPERLIMIT) { + let newImpulse = this.mImpulse.z + impulse.z; + if (newImpulse > 0.0) { + let mass = new B2Vec2(this.mMass.ez.x, this.mMass.ez.y); + let rhs = add(minus(cdot1), multM(mass, this.mImpulse.z)); + let reduced = this.mMass.solve22(rhs); + impulse.x = reduced.x; + impulse.y = reduced.y; + impulse.z = -this.mImpulse.z; + this.mImpulse.x += reduced.x; + this.mImpulse.y += reduced.y; + this.mImpulse.z = 0.0; + } else { + addEqual3(this.mImpulse, impulse); + } + } + let mP = new B2Vec2(impulse.x, impulse.y); + subtractEqual(vA, multM(mP, mA)); + wA -= iA * (b2Cross(this.mrA, mP) + impulse.z); + addEqual(vB, multM(mP, mB)); + wB += iB * (b2Cross(this.mrB, mP) + impulse.z); + } else { + let cdot = subtract(subtract(add(vB, b2Cross21(wB, this.mrB)), vA), b2Cross21(wA, this.mrA)); + let impulse = this.mMass.solve22(minus(cdot)); + this.mImpulse.x += impulse.x; + this.mImpulse.y += impulse.y; + subtractEqual(vA, multM(impulse, mA)); + wA -= iA * b2Cross(this.mrA, impulse); + addEqual(vB, multM(impulse, mB)); + wB += iB * b2Cross(this.mrB, impulse); + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solvePositionConstraints(data: B2SolverData): boolean { + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + let angularError: number = 0.0; + let positionError: number = 0.0; + let fixedRotation = this.mInvIA + this.mInvIB === 0.0; + if (this.mEnableLimit && this.mLimitState !== B2LimitState.INACTIVELIMIT && fixedRotation === false) { + let angle = aB - aA - this.mReferenceAngle; + let limitImpulse: number = 0.0; + if (this.mLimitState === B2LimitState.EQUALLIMITS) { + let mC = b2ClampF(angle - this.mLowerAngle, -b2MaxAngularCorrection, b2MaxAngularCorrection); + limitImpulse = -this.mMotorMass * mC; + angularError = Math.abs(mC); + } else if (this.mLimitState === B2LimitState.ATLOWERLIMIT) { + let mC = angle - this.mLowerAngle; + angularError = -mC; + mC = b2ClampF(mC + b2AngularSlop, -b2MaxAngularCorrection, 0.0); + limitImpulse = -this.mMotorMass * mC; + } else if (this.mLimitState === B2LimitState.ATUPPERLIMIT) { + let mC = angle - this.mUpperAngle; + angularError = mC; + mC = b2ClampF(mC - b2AngularSlop, 0.0, b2MaxAngularCorrection); + limitImpulse = -this.mMotorMass * mC; + } + aA -= this.mInvIA * limitImpulse; + aB += this.mInvIB * limitImpulse; + } + qA.set(aA); + qB.set(aB); + let rA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + let rB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let cC = subtract(subtract(add(cB, rB), cA), rA); + positionError = cC.length(); + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + let mK = new B2Mat22(); + mK.ex.x = mA + mB + iA * rA.y * rA.y + iB * rB.y * rB.y; + mK.ex.y = -iA * rA.x * rA.y - iB * rB.x * rB.y; + mK.ey.x = mK.ex.y; + mK.ey.y = mA + mB + iA * rA.x * rA.x + iB * rB.x * rB.x; + let impulse = minus(mK.solve(cC)); + subtractEqual(cA, multM(impulse, mA)); + aA -= iA * b2Cross(rA, impulse); + addEqual(cB, multM(impulse, mB)); + aB += iB * b2Cross(rB, impulse); + data.positions.get(this.mIndexA).c = cA; + data.positions.get(this.mIndexA).a = aA; + data.positions.get(this.mIndexB).c = cB; + data.positions.get(this.mIndexB).a = aB; + return positionError <= b2LinearSlop && angularError <= b2AngularSlop; + } + mLocalAnchorA: B2Vec2; + mLocalAnchorB: B2Vec2; + mImpulse: B2Vec3; + mMotorImpulse: number; + mEnableMotor: boolean; + mMaxMotorTorque: number; + mMotorSpeed: number; + mEnableLimit: boolean; + mReferenceAngle: number; + mLowerAngle: number; + mUpperAngle: number; + mIndexA: number = 0; + mIndexB: number = 0; + mrA = new B2Vec2(); + mrB = new B2Vec2(); + mLocalCenterA = new B2Vec2(); + mLocalCenterB = new B2Vec2(); + mInvMassA: number = 0.0; + mInvMassB: number = 0.0; + mInvIA: number = 0.0; + mInvIB: number = 0.0; + mMass = new B2Mat33(); + mMotorMass: number = 0.0; + mLimitState = B2LimitState.INACTIVELIMIT; +} + +export class B2PulleyJointDef extends B2JointDef { + constructor() { + super(); + this.groundAnchorA = new B2Vec2(-1.0, 1.0); + this.groundAnchorB = new B2Vec2(1.0, 1.0); + this.localAnchorA = new B2Vec2(-1.0, 0.0); + this.localAnchorB = new B2Vec2(1.0, 0.0); + this.lengthA = 0.0; + this.lengthB = 0.0; + this.ratio = 1.0; + this.type = B2JointType.PULLEYJOINT; + this.collideConnected = true; + } + + public initialize(bodyA: B2Body, bodyB: B2Body, groundAnchorA: B2Vec2, groundAnchorB: B2Vec2, anchorA: B2Vec2, anchorB: B2Vec2, ratio: number): void { + this.bodyA = bodyA; + this.bodyB = bodyB; + this.groundAnchorA = groundAnchorA; + this.groundAnchorB = groundAnchorB; + this.localAnchorA = this.bodyA.getLocalPoint(anchorA); + this.localAnchorB = this.bodyB.getLocalPoint(anchorB); + let dA = subtract(anchorA, groundAnchorA); + this.lengthA = dA.length(); + let dB = subtract(anchorB, groundAnchorB); + this.lengthB = dB.length(); + this.ratio = ratio; + } + + groundAnchorA: B2Vec2; + groundAnchorB: B2Vec2; + localAnchorA: B2Vec2; + localAnchorB: B2Vec2; + lengthA: number; + lengthB: number; + ratio: number; +} + +export class B2PulleyJoint extends B2Joint { + mGroundAnchorA: B2Vec2; + mGroundAnchorB: B2Vec2; + mLengthA: number; + mLengthB: number; + mLocalAnchorA: B2Vec2; + mLocalAnchorB: B2Vec2; + mConstant: number; + mRatio: number; + mImpulse: number; + mIndexA: number = 0; + mIndexB: number = 0; + muA = new B2Vec2(); + muB = new B2Vec2(); + mrA = new B2Vec2(); + mrB = new B2Vec2(); + mLocalCenterA = new B2Vec2(); + mLocalCenterB = new B2Vec2(); + mInvMassA: number = 0.0; + mInvMassB: number = 0.0; + mInvIA: number = 0.0; + mInvIB: number = 0.0; + mMass: number = 0.0; + public get anchorA(): B2Vec2 { + return this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + } + public get anchorB(): B2Vec2 { + return this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + } + public getReactionForce(invdt: number): B2Vec2 { + let mP = multM(this.muB, this.mImpulse); + return multM(mP, invdt); + } + public getReactionTorque(invdt: number): number { + return 0.0; + } + public get groundAnchorA(): B2Vec2 { + return this.mGroundAnchorA; + } + public get groundAnchorB(): B2Vec2 { + return this.mGroundAnchorB; + } + public get lengthA(): number { + return this.mLengthA; + } + public get lengthB(): number { + return this.mLengthB; + } + public get ratio(): number { + return this.mRatio; + } + public get currentLengthA(): number { + let p = this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + let s = this.mGroundAnchorA; + let d = subtract(p, s); + return d.length(); + } + public get currentLengthB(): number { + let p = this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + let s = this.mGroundAnchorB; + let d = subtract(p, s); + return d.length(); + } + public shiftOrigin(newOrigin: B2Vec2): void { + subtractEqual(this.mGroundAnchorA, newOrigin); + subtractEqual(this.mGroundAnchorB, newOrigin); + } + constructor(def: B2PulleyJointDef) { + super(def); + this.mGroundAnchorA = def.groundAnchorA; + this.mGroundAnchorB = def.groundAnchorB; + this.mLocalAnchorA = def.localAnchorA; + this.mLocalAnchorB = def.localAnchorB; + this.mLengthA = def.lengthA; + this.mLengthB = def.lengthB; + this.mRatio = def.ratio; + this.mConstant = def.lengthA + this.mRatio * def.lengthB; + this.mImpulse = 0.0; + } + + initVelocityConstraints(data: B2SolverData): void { + this.mIndexA = this.mBodyA!.mIslandIndex; + this.mIndexB = this.mBodyB!.mIslandIndex; + this.mLocalCenterA = this.mBodyA!.mSweep.localCenter; + this.mLocalCenterB = this.mBodyB!.mSweep.localCenter; + this.mInvMassA = this.mBodyA!.mInvMass; + this.mInvMassB = this.mBodyB!.mInvMass; + this.mInvIA = this.mBodyA!.mInvI; + this.mInvIB = this.mBodyB!.mInvI; + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + this.mrA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + this.mrB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + this.muA = subtract(add(cA, this.mrA), this.mGroundAnchorA); + this.muB = subtract(add(cB, this.mrB), this.mGroundAnchorB); + let lengthA = this.muA.length(); + let lengthB = this.muB.length(); + if (lengthA > float10 * b2LinearSlop) { + mulEqual(this.muA, 1.0 / lengthA); + } else { + this.muA.setZero(); + } + if (lengthB > float10 * b2LinearSlop) { + mulEqual(this.muB, 1.0 / lengthB); + } else { + this.muB.setZero(); + } + let ruA = b2Cross(this.mrA, this.muA); + let ruB = b2Cross(this.mrB, this.muB); + let mA = this.mInvMassA + this.mInvIA * ruA * ruA; + let mB = this.mInvMassB + this.mInvIB * ruB * ruB; + this.mMass = mA + this.mRatio * this.mRatio * mB; + this.mMass = this.mMass > 0.0 ? 1.0 / this.mMass : this.mMass; + if (data.step.warmStarting) { + this.mImpulse *= data.step.dtRatio; + let mPA = multM(this.muA, -this.mImpulse); + let mPB = multM(this.muB, -this.mRatio * this.mImpulse); + addEqual(vA, multM(mPA, this.mInvMassA)); + wA += this.mInvIA * b2Cross(this.mrA, mPA); + addEqual(vB, multM(mPB, this.mInvMassB)); + wB += this.mInvIB * b2Cross(this.mrB, mPB); + } else { + this.mImpulse = 0.0; + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solveVelocityConstraints(data: B2SolverData): void { + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let vpA = add(vA, b2Cross21(wA, this.mrA)); + let vpB = add(vB, b2Cross21(wB, this.mrB)); + let cdot = -b2Dot22(this.muA, vpA) - this.mRatio * b2Dot22(this.muB, vpB); + let impulse = -this.mMass * cdot; + this.mImpulse += impulse; + let mPA = multM(this.muA, -impulse); + let mPB = multM(this.muB, -this.mRatio * impulse); + addEqual(vA, multM(mPA, this.mInvMassA)); + wA += this.mInvIA * b2Cross(this.mrA, mPA); + addEqual(vB, multM(mPB, this.mInvMassB)); + wB += this.mInvIB * b2Cross(this.mrB, mPB); + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solvePositionConstraints(data: B2SolverData): boolean { + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + let rA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + let rB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let uA = subtract(add(cA, rA), this.mGroundAnchorA); + let uB = subtract(add(cB, rB), this.mGroundAnchorB); + let lengthA = uA.length(); + let lengthB = uB.length(); + if (lengthA > float10 * b2LinearSlop) { + mulEqual(uA, 1.0 / lengthA); + } else { + uA.setZero(); + } + if (lengthB > float10 * b2LinearSlop) { + mulEqual(uB, 1.0 / lengthB); + } else { + uB.setZero(); + } + let ruA = b2Cross(rA, uA); + let ruB = b2Cross(rB, uB); + let mA = this.mInvMassA + this.mInvIA * ruA * ruA; + let mB = this.mInvMassB + this.mInvIB * ruB * ruB; + let mass = mA + this.mRatio * this.mRatio * mB; + if (mass > 0.0) { + mass = 1.0 / mass; + } + let mC = this.mConstant - lengthA - this.mRatio * lengthB; + let linearError = Math.abs(mC); + let impulse = -mass * mC; + let mPA = multM(uA, impulse); + let mPB = multM(uB, -this.mRatio * impulse); + addEqual(cA, multM(mPA, this.mInvMassA)); + aA += this.mInvIA * b2Cross(rA, mPA); + addEqual(cB, multM(mPB, this.mInvMassB)); + aB += this.mInvIB * b2Cross(rB, mPB); + data.positions.get(this.mIndexA).c = cA; + data.positions.get(this.mIndexA).a = aA; + data.positions.get(this.mIndexB).c = cB; + data.positions.get(this.mIndexB).a = aB; + return linearError < b2LinearSlop; + } +} + +export class B2PrismaticJointDef extends B2JointDef { + constructor() { + super(); + this.localAnchorA = new B2Vec2(); + this.localAnchorB = new B2Vec2(); + this.localAxisA = new B2Vec2(1.0, 0.0); + this.referenceAngle = 0.0; + this.enableLimit = false; + this.lowerTranslation = 0.0; + this.upperTranslation = 0.0; + this.enableMotor = false; + this.maxMotorForce = 0.0; + this.motorSpeed = 0.0; + this.type = B2JointType.PRISMATICJOINT; + } + + public initialize(bA: B2Body, bB: B2Body, anchor: B2Vec2, axis: B2Vec2): void { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.getLocalPoint(anchor); + this.localAnchorB = this.bodyB.getLocalPoint(anchor); + this.localAxisA = this.bodyA.getLocalVector(axis); + this.referenceAngle = this.bodyB.angle - this.bodyA.angle; + } + localAnchorA: B2Vec2; + localAnchorB: B2Vec2; + localAxisA: B2Vec2; + referenceAngle: number; + enableLimit: boolean; + lowerTranslation: number; + upperTranslation: number; + enableMotor: boolean; + maxMotorForce: number; + motorSpeed: number; +} + +export class B2PrismaticJoint extends B2Joint { + public get anchorA(): B2Vec2 { + return this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + } + + public get anchorB(): B2Vec2 { + return this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + } + + public getReactionForce(invdt: number): B2Vec2 { + return multM(add(multM(this.mPerp, this.mImpulse.x), multM(this.mAxis, this.mMotorImpulse + this.mImpulse.z)), invdt); + } + + public getReactionTorque(invdt: number): number { + return invdt * this.mImpulse.y; + } + + public get localAnchorA(): B2Vec2 { + return this.mLocalAnchorA; + } + + public get localAnchorB(): B2Vec2 { + return this.mLocalAnchorB; + } + + public get localAxisA(): B2Vec2 { + return this.mLocalXAxisA; + } + + public get referenceAngle(): number { + return this.mReferenceAngle; + } + + public jointTranslation(): number { + let pA = this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + let pB = this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + let d = subtract(pB, pA); + let axis = this.mBodyA!.getWorldVector(this.mLocalXAxisA); + let translation = b2Dot22(d, axis); + return translation; + } + + public jointSpeed(): number { + let bA = this.mBodyA; + let bB = this.mBodyB; + let rA = b2MulR2(bA!.mXF.q, subtract(this.mLocalAnchorA, bA!.mSweep.localCenter)); + let rB = b2MulR2(bB!.mXF.q, subtract(this.mLocalAnchorB, bB!.mSweep.localCenter)); + let p1 = add(bA!.mSweep.c, rA); + let p2 = add(bB!.mSweep.c, rB); + let d = subtract(p2, p1); + let axis = b2MulR2(bA!.mXF.q, this.mLocalXAxisA); + let vA = bA!.mLinearVelocity; + let vB = bB!.mLinearVelocity; + let wA = bA!.mAngularVelocity; + let wB = bB!.mAngularVelocity; + let sub = subtract(subtract(add(vB, b2Cross21(wB, rB)), vA), b2Cross21(wA, rA)); + let speed = b2Dot22(d, b2Cross21(wA, axis)) + b2Dot22(axis, sub); + return speed; + } + + get isLimitEnabled(): boolean { + return this.mEnableLimit; + } + setisLimitEnabled(newValue: boolean): void { + this.enableLimit(newValue); + } + + public enableLimit(flag: boolean): void { + if (flag !== this.mEnableLimit) { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mEnableLimit = flag; + this.mImpulse.z = 0.0; + } + } + + public get lowerLimit(): number { + return this.mLowerTranslation; + } + + public get upperLimit(): number { + return this.mUpperTranslation; + } + + public setLimits(lower: number, upper: number): void { + if (lower !== this.mLowerTranslation || upper !== this.mUpperTranslation) { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mLowerTranslation = lower; + this.mUpperTranslation = upper; + this.mImpulse.z = 0.0; + } + } + + get isMotorEnabled(): boolean { + return this.mEnableMotor; + } + setisMotorEnabled(newValue: boolean): void { + this.enableMotor(newValue); + } + + public enableMotor(flag: boolean): void { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mEnableMotor = flag; + } + + setMotorSpeed(speed: number): void { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mMotorSpeed = speed; + } + get motorSpeed(): number { + return this.mMotorSpeed; + } + set motorSpeed(newValue: number) { + this.setMotorSpeed(newValue); + } + + setMaxMotorForce(force: number): void { + this.mBodyA!.setAwake(true); + this.mBodyB!.setAwake(true); + this.mMaxMotorForce = force; + } + + get maxMotorForce(): number { + return this.mMaxMotorForce; + } + set maxMotorForce(newValue: number) { + this.setMaxMotorForce(newValue); + } + + getMotorForce(invdt: number): number { + return invdt * this.mMotorImpulse; + } + + constructor(def: B2PrismaticJointDef) { + super(def); + this.mLocalAnchorA = def.localAnchorA; + this.mLocalAnchorB = def.localAnchorB; + this.mLocalXAxisA = def.localAxisA; + this.mLocalXAxisA.normalize(); + this.mLocalYAxisA = b2Cross21(1.0, this.mLocalXAxisA); + this.mReferenceAngle = def.referenceAngle; + this.mImpulse = new B2Vec3(0.0, 0.0, 0.0); + this.mMotorMass = 0.0; + this.mMotorImpulse = 0.0; + this.mLowerTranslation = def.lowerTranslation; + this.mUpperTranslation = def.upperTranslation; + this.mMaxMotorForce = def.maxMotorForce; + this.mMotorSpeed = def.motorSpeed; + this.mEnableLimit = def.enableLimit; + this.mEnableMotor = def.enableMotor; + this.mLimitState = B2LimitState.INACTIVELIMIT; + this.mAxis = new B2Vec2(0.0, 0.0); + this.mPerp = new B2Vec2(0.0, 0.0); + } + + initVelocityConstraints(data: B2SolverData): void { + this.mIndexA = this.mBodyA!.mIslandIndex; + this.mIndexB = this.mBodyB!.mIslandIndex; + this.mLocalCenterA = this.mBodyA!.mSweep.localCenter; + this.mLocalCenterB = this.mBodyB!.mSweep.localCenter; + this.mInvMassA = this.mBodyA!.mInvMass; + this.mInvMassB = this.mBodyB!.mInvMass; + this.mInvIA = this.mBodyA!.mInvI; + this.mInvIB = this.mBodyB!.mInvI; + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + let rA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + let rB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let d = subtract(add(subtract(cB, cA), rB), rA); + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + this.mAxis = b2MulR2(qA, this.mLocalXAxisA); + this.ma1 = b2Cross(add(d, rA), this.mAxis); + this.ma2 = b2Cross(rB, this.mAxis); + this.mMotorMass = mA + mB + iA * this.ma1 * this.ma1 + iB * this.ma2 * this.ma2; + if (this.mMotorMass > 0.0) { + this.mMotorMass = 1.0 / this.mMotorMass; + } + this.mPerp = b2MulR2(qA, this.mLocalYAxisA); + this.ms1 = b2Cross(add(d, rA), this.mPerp); + this.ms2 = b2Cross(rB, this.mPerp); + let k11 = mA + mB + iA * this.ms1 * this.ms1 + iB * this.ms2 * this.ms2; + let k12 = iA * this.ms1 + iB * this.ms2; + let k13 = iA * this.ms1 * this.ma1 + iB * this.ms2 * this.ma2; + let k22 = iA + iB; + if (k22 === 0.0) { + k22 = 1.0; + } + let k23 = iA * this.ma1 + iB * this.ma2; + let k33 = mA + mB + iA * this.ma1 * this.ma1 + iB * this.ma2 * this.ma2; + this.matK.ex.set(k11, k12, k13); + this.matK.ey.set(k12, k22, k23); + this.matK.ez.set(k13, k23, k33); + if (this.mEnableLimit) { + let jointTranslation = b2Dot22(this.mAxis, d); + if (Math.abs(this.mUpperTranslation - this.mLowerTranslation) < float2 * b2LinearSlop) { + this.mLimitState = B2LimitState.EQUALLIMITS; + } else if (jointTranslation <= this.mLowerTranslation) { + if (this.mLimitState !== B2LimitState.ATLOWERLIMIT) { + this.mLimitState = B2LimitState.ATLOWERLIMIT; + this.mImpulse.z = 0.0; + } + } else if (jointTranslation >= this.mUpperTranslation) { + if (this.mLimitState !== B2LimitState.ATUPPERLIMIT) { + this.mLimitState = B2LimitState.ATUPPERLIMIT; + this.mImpulse.z = 0.0; + } + } else { + this.mLimitState = B2LimitState.INACTIVELIMIT; + this.mImpulse.z = 0.0; + } + } else { + this.mLimitState = B2LimitState.INACTIVELIMIT; + this.mImpulse.z = 0.0; + } + if (this.mEnableMotor === false) { + this.mMotorImpulse = 0.0; + } + if (data.step.warmStarting) { + mulMEqual3(this.mImpulse, data.step.dtRatio); + this.mMotorImpulse *= data.step.dtRatio; + let mP = add(multM(this.mPerp, this.mImpulse.x), multM(this.mAxis, this.mMotorImpulse + this.mImpulse.z)); + let mLA = this.mImpulse.x * this.ms1 + this.mImpulse.y + (this.mMotorImpulse + this.mImpulse.z) * this.ma1; + let mLB = this.mImpulse.x * this.ms2 + this.mImpulse.y + (this.mMotorImpulse + this.mImpulse.z) * this.ma2; + subtractEqual(vA, multM(mP, mA)); + wA -= iA * mLA; + addEqual(vB, multM(mP, mB)); + wB += iB * mLB; + } else { + this.mImpulse.setZero(); + this.mMotorImpulse = 0.0; + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solveVelocityConstraints(data: B2SolverData): void { + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + if (this.mEnableMotor && this.mLimitState !== B2LimitState.EQUALLIMITS) { + let cdot = b2Dot22(this.mAxis, subtract(vB, vA)) + this.ma2 * wB - this.ma1 * wA; + let impulse = this.mMotorMass * (this.mMotorSpeed - cdot); + let oldImpulse = this.mMotorImpulse; + let maxImpulse = data.step.dt * this.mMaxMotorForce; + this.mMotorImpulse = b2ClampF(this.mMotorImpulse + impulse, -maxImpulse, maxImpulse); + impulse = this.mMotorImpulse - oldImpulse; + let mP = multM(this.mAxis, impulse); + let lA = impulse * this.ma1; + let lB = impulse * this.ma2; + subtractEqual(vA, multM(mP, mA)); + wA -= iA * lA; + addEqual(vB, multM(mP, mB)); + wB += iB * lB; + } + let cdot1 = new B2Vec2(); + cdot1.x = b2Dot22(this.mPerp, subtract(vB, vA)) + this.ms2 * wB - this.ms1 * wA; + cdot1.y = wB - wA; + if (this.mEnableLimit && this.mLimitState !== B2LimitState.INACTIVELIMIT) { + let cdot2 = b2Dot22(this.mAxis, subtract(vB, vA)) + this.ma2 * wB - this.ma1 * wA; + let cdot = new B2Vec3(cdot1.x, cdot1.y, cdot2); + let f1 = this.mImpulse; + let df = this.matK.solve33(minus3(cdot)); + addEqual3(this.mImpulse, df); + if (this.mLimitState === B2LimitState.ATLOWERLIMIT) { + this.mImpulse.z = Math.max(this.mImpulse.z, 0.0); + } else if (this.mLimitState === B2LimitState.ATUPPERLIMIT) { + this.mImpulse.z = Math.min(this.mImpulse.z, 0.0); + } + let mkk = new B2Vec2(this.matK.ez.x, this.matK.ez.y); + let b = subtract(minus(cdot1), multM(mkk, this.mImpulse.z - f1.z)); + let ffx = new B2Vec2(f1.x, f1.y); + let f2r = add(this.matK.solve22(b), ffx); + this.mImpulse.x = f2r.x; + this.mImpulse.y = f2r.y; + df = subtract3(this.mImpulse, f1); + let p = add(multM(this.mPerp, df.x), multM(this.mAxis, df.z)); + let lA = df.x * this.ms1 + df.y + df.z * this.ma1; + let lB = df.x * this.ms2 + df.y + df.z * this.ma2; + subtractEqual(vA, multM(p, mA)); + wA -= iA * lA; + addEqual(vB, multM(p, mB)); + wB += iB * lB; + } else { + let df = this.matK.solve22(minus(cdot1)); + this.mImpulse.x += df.x; + this.mImpulse.y += df.y; + let mP = multM(this.mPerp, df.x); + let mLA = df.x * this.ms1 + df.y; + let mLB = df.x * this.ms2 + df.y; + subtractEqual(vA, multM(mP, mA)); + wA -= iA * mLA; + addEqual(vB, multM(mP, mB)); + wB += iB * mLB; + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solvePositionConstraints(data: B2SolverData): boolean { + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + let rA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + let rB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let d = subtract(subtract(add(cB, rB), cA), rA); + let axis = b2MulR2(qA, this.mLocalXAxisA); + let a1 = b2Cross(add(d, rA), axis); + let a2 = b2Cross(rB, axis); + let perp = b2MulR2(qA, this.mLocalYAxisA); + let s1 = b2Cross(add(d, rA), perp); + let s2 = b2Cross(rB, perp); + let impulse = new B2Vec3(); + let c1 = new B2Vec2(); + c1.x = b2Dot22(perp, d); + c1.y = aB - aA - this.mReferenceAngle; + let linearError = Math.abs(c1.x); + let angularError = Math.abs(c1.y); + let active = false; + let c2: number = 0.0; + if (this.mEnableLimit) { + let translation = b2Dot22(axis, d); + if (Math.abs(this.mUpperTranslation - this.mLowerTranslation) < float2 * b2LinearSlop) { + c2 = b2ClampF(translation, -b2MaxLinearCorrection, b2MaxLinearCorrection); + linearError = Math.max(linearError, Math.abs(translation)); + active = true; + } else if (translation <= this.mLowerTranslation) { + c2 = b2ClampF(translation - this.mLowerTranslation + b2LinearSlop, -b2MaxLinearCorrection, 0.0); + linearError = Math.max(linearError, this.mLowerTranslation - translation); + active = true; + } else if (translation >= this.mUpperTranslation) { + c2 = b2ClampF(translation - this.mUpperTranslation - b2LinearSlop, 0.0, b2MaxLinearCorrection); + linearError = Math.max(linearError, translation - this.mUpperTranslation); + active = true; + } + } + if (active) { + let k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; + let k12 = iA * s1 + iB * s2; + let k13 = iA * s1 * a1 + iB * s2 * a2; + let k22 = iA + iB; + if (k22 === 0.0) { + k22 = 1.0; + } + let k23 = iA * a1 + iB * a2; + let k33 = mA + mB + iA * a1 * a1 + iB * a2 * a2; + let mK = new B2Mat33(); + mK.ex.set(k11, k12, k13); + mK.ey.set(k12, k22, k23); + mK.ez.set(k13, k23, k33); + let cC = new B2Vec3(); + cC.x = c1.x; + cC.y = c1.y; + cC.z = c2; + impulse = mK.solve33(minus3(cC)); + } else { + let k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; + let k12 = iA * s1 + iB * s2; + let k22 = iA + iB; + if (k22 === 0.0) { + k22 = 1.0; + } + let mK = new B2Mat22(); + mK.ex.set(k11, k12); + mK.ey.set(k12, k22); + let impulse1 = mK.solve(minus(c1)); + impulse.x = impulse1.x; + impulse.y = impulse1.y; + impulse.z = 0.0; + } + let p = add(multM(perp, impulse.x), multM(axis, impulse.z)); + let lA = impulse.x * s1 + impulse.y + impulse.z * a1; + let lB = impulse.x * s2 + impulse.y + impulse.z * a2; + subtractEqual(cA, multM(p, mA)); + aA -= iA * lA; + addEqual(cB, multM(p, mB)); + aB += iB * lB; + data.positions.get(this.mIndexA).c = cA; + data.positions.get(this.mIndexA).a = aA; + data.positions.get(this.mIndexB).c = cB; + data.positions.get(this.mIndexB).a = aB; + return linearError <= b2LinearSlop && angularError <= b2AngularSlop; + } + + mLocalAnchorA: B2Vec2; + mLocalAnchorB: B2Vec2; + mLocalXAxisA: B2Vec2; + mLocalYAxisA: B2Vec2; + mReferenceAngle: number; + mImpulse: B2Vec3; + mMotorImpulse: number; + mLowerTranslation: number; + mUpperTranslation: number; + mMaxMotorForce: number; + mMotorSpeed: number; + mEnableLimit: boolean; + mEnableMotor: boolean; + mLimitState: B2LimitState; + mIndexA: number = 0; + mIndexB: number = 0; + mLocalCenterA = new B2Vec2(); + mLocalCenterB = new B2Vec2(); + mInvMassA: number = 0.0; + mInvMassB: number = 0.0; + mInvIA: number = 0.0; + mInvIB: number = 0.0; + mAxis = new B2Vec2(); + mPerp = new B2Vec2(); + ms1: number = 0.0; + ms2: number = 0.0; + ma1: number = 0.0; + ma2: number = 0.0; + matK = new B2Mat33(); + mMotorMass: number = 0.0; +} + +export class B2MouseJointDef extends B2JointDef { + public constructor() { + super(); + this.target = new B2Vec2(); + this.maxForce = 0.0; + this.frequencyHz = 5.0; + this.dampingRatio = 0.7; + this.type = B2JointType.MOUSEJOINT; + } + public target: B2Vec2; + public maxForce: number; + public frequencyHz: number; + public dampingRatio: number; +} + +export class B2MouseJoint extends B2Joint { + public get anchorA(): B2Vec2 { + return this.mTargetA; + } + public get anchorB(): B2Vec2 { + return this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + } + public getReactionForce(invdt: number): B2Vec2 { + return multM(this.mImpulse, invdt); + } + public getReactionTorque(invdt: number): number { + return invdt * 0.0; + } + public setTarget(target: B2Vec2): void { + if (this.mBodyB!.isAwake === false) { + this.mBodyB!.setAwake(true); + } + this.mTargetA = target; + } + get target(): B2Vec2 { + return this.mTargetA; + } + set target(newValue: B2Vec2) { + this.setTarget(newValue); + } + public setMaxForce(force: number): void { + this.mMaxForce = force; + } + get maxForce(): number { + return this.mMaxForce; + } + set force(newValue: number) { + this.setMaxForce(newValue); + } + public setFrequency(hz: number): void { + this.mFrequencyHz = hz; + } + get hz(): number { + return this.mFrequencyHz; + } + set hz(newValue: number) { + this.setFrequency(newValue); + } + public setDampingRatio(ratio: number): void { + this.mDampingRatio = ratio; + } + get dampingRatio(): number { + return this.mDampingRatio; + } + set dampingRatio(newValue: number) { + this.setDampingRatio(newValue); + } + public shiftOrigin(newOrigin: B2Vec2): void { + subtractEqual(this.mTargetA, newOrigin); + } + + constructor(def: B2MouseJointDef) { + super(def); + this.mTargetA = def.target; + this.mMaxForce = def.maxForce; + this.mImpulse = new B2Vec2(0.0, 0.0); + this.mFrequencyHz = def.frequencyHz; + this.mDampingRatio = def.dampingRatio; + this.mBeta = 0.0; + this.mGamma = 0.0; + this.mLocalAnchorB = b2MulTT2(this.mBodyB!.transform, this.mTargetA); + } + + initVelocityConstraints(data: B2SolverData): void { + let multiFactor: number = 0.98; + this.mIndexB = this.mBodyB!.mIslandIndex; + this.mLocalCenterB = this.mBodyB!.mSweep.localCenter; + this.mInvMassB = this.mBodyB!.mInvMass; + this.mInvIB = this.mBodyB!.mInvI; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let qB = new B2Rot(aB); + let mass = this.mBodyB!.mass; + let omega = float2 * b2PI * this.mFrequencyHz; + let d = float2 * mass * this.mDampingRatio * omega; + let k = mass * (omega * omega); + let h = data.step.dt; + this.mGamma = h * (d + h * k); + if (this.mGamma !== 0.0) { + this.mGamma = 1.0 / this.mGamma; + } + this.mBeta = h * k * this.mGamma; + this.mrB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let mK = new B2Mat22(); + mK.ex.x = this.mInvMassB + this.mInvIB * this.mrB.y * this.mrB.y + this.mGamma; + mK.ex.y = -this.mInvIB * this.mrB.x * this.mrB.y; + mK.ey.x = mK.ex.y; + mK.ey.y = this.mInvMassB + this.mInvIB * this.mrB.x * this.mrB.x + this.mGamma; + this.mMass = mK.getInverse(); + this.mC = subtract(add(cB, this.mrB), this.mTargetA); + mulEqual(this.mC, this.mBeta); + wB *= multiFactor; + if (data.step.warmStarting) { + mulEqual(this.mImpulse, data.step.dtRatio); + addEqual(vB, multM(this.mImpulse, this.mInvMassB)); + wB += this.mInvIB * b2Cross(this.mrB, this.mImpulse); + } else { + this.mImpulse.setZero(); + } + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solveVelocityConstraints(data: B2SolverData): void { + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let cdot = add(vB, b2Cross21(wB, this.mrB)); + let impulse = b2Mul22(this.mMass, minus(add(add(cdot, this.mC), multM(this.mImpulse, this.mGamma)))); + let oldImpulse = this.mImpulse; + addEqual(this.mImpulse, impulse); + let maxImpulse = data.step.dt * this.mMaxForce; + if (this.mImpulse.lengthSquared() > maxImpulse * maxImpulse) { + mulEqual(this.mImpulse, maxImpulse / this.mImpulse.length()); + } + impulse = subtract(this.mImpulse, oldImpulse); + addEqual(vB, multM(impulse, this.mInvMassB)); + wB += this.mInvIB * b2Cross(this.mrB, impulse); + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solvePositionConstraints(data: B2SolverData): boolean { + return true; + } + mLocalAnchorB: B2Vec2; + mTargetA: B2Vec2; + mFrequencyHz: number; + mDampingRatio: number; + mBeta: number; + mImpulse: B2Vec2; + mMaxForce: number; + mGamma: number; + mIndexA: number = 0; + mIndexB: number = 0; + mrB = new B2Vec2(); + mLocalCenterB = new B2Vec2(); + mInvMassB: number = 0.0; + mInvIB: number = 0.0; + mMass = new B2Mat22(); + mC = new B2Vec2(); +} + +export class B2GearJointDef extends B2JointDef { + public constructor() { + super(); + this.joint1 = null; + this.joint2 = null; + this.ratio = 1.0; + this.type = B2JointType.GEARJOINT; + } + joint1: B2Joint | null = null; + joint2: B2Joint | null = null; + ratio: number = 1.0; +} + +export class B2GearJoint extends B2Joint { + public get anchorA(): B2Vec2 { + return this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + } + public get anchorB(): B2Vec2 { + return this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + } + + public getReactionForce(invdt: number): B2Vec2 { + let mP = multM(this.mJvAC, this.mImpulse); + return multM(mP, invdt); + } + + public getReactionTorque(invdt: number): number { + let mL = this.mImpulse * this.mJwA; + return invdt * mL; + } + + public get joint1(): B2Joint | null { + return this.mJoint1; + } + + public get joint2(): B2Joint | null { + return this.mJoint2; + } + + public setRatio(ratio: number): void { + this.mRatio = ratio; + } + + public get ratio(): number { + return this.mRatio; + } + public set ratio(newValue: number) { + this.setRatio(newValue); + } + + constructor(def: B2GearJointDef) { + super(def); + this.mJoint1 = def.joint1; + this.mJoint2 = def.joint2; + this.mTypeA = this.mJoint1!.typeJoint; + this.mTypeB = this.mJoint2!.typeJoint; + let coordinateA: number; + let coordinateB: number; + this.mBodyC = this.mJoint1!.bodyA!; + this.mBodyA = this.mJoint1!.bodyB!; + let xfA = this.mBodyA!.mXF; + let aA = this.mBodyA!.mSweep.a; + let xfC = this.mBodyC.mXF; + let aC = this.mBodyC.mSweep.a; + if (this.mTypeA === B2JointType.REVOLUTEJOINT) { + let revolute = def.joint1 as B2RevoluteJoint; + this.mLocalAnchorC = revolute.mLocalAnchorA; + this.mLocalAnchorA = revolute.mLocalAnchorB; + this.mReferenceAngleA = revolute.mReferenceAngle; + this.mLocalAxisC.setZero(); + coordinateA = aA - aC - this.mReferenceAngleA; + } else { + let prismatic = def.joint1 as B2PrismaticJoint; + this.mLocalAnchorC = prismatic.mLocalAnchorA; + this.mLocalAnchorA = prismatic.mLocalAnchorB; + this.mReferenceAngleA = prismatic.mReferenceAngle; + this.mLocalAxisC = prismatic.mLocalXAxisA; + let pC = this.mLocalAnchorC; + let pA = b2MulTR2(xfC.q, add(b2MulR2(xfA.q, this.mLocalAnchorA), subtract(xfA.p, xfC.p))); + coordinateA = b2Dot22(subtract(pA, pC), this.mLocalAxisC); + } + this.mBodyD = this.mJoint2!.bodyA!; + this.mBodyB = this.mJoint2!.bodyB; + let xfB = this.mBodyB!.mXF; + let aB = this.mBodyB!.mSweep.a; + let xfD = this.mBodyD.mXF; + let aD = this.mBodyD.mSweep.a; + if (this.mTypeB === B2JointType.REVOLUTEJOINT) { + let revolute = def.joint2 as B2RevoluteJoint; + this.mLocalAnchorD = revolute.mLocalAnchorA; + this.mLocalAnchorB = revolute.mLocalAnchorB; + this.mReferenceAngleB = revolute.mReferenceAngle; + this.mLocalAxisD.setZero(); + coordinateB = aB - aD - this.mReferenceAngleB; + } else { + let prismatic = def.joint2 as B2PrismaticJoint; + this.mLocalAnchorD = prismatic.mLocalAnchorA; + this.mLocalAnchorB = prismatic.mLocalAnchorB; + this.mReferenceAngleB = prismatic.mReferenceAngle; + this.mLocalAxisD = prismatic.mLocalXAxisA; + let pD = this.mLocalAnchorD; + let pB = b2MulTR2(xfD.q, add(b2MulR2(xfB.q, this.mLocalAnchorB), subtract(xfB.p, xfD.p))); + coordinateB = b2Dot22(subtract(pB, pD), this.mLocalAxisD); + } + this.mRatio = def.ratio; + this.mConstant = coordinateA + this.mRatio * coordinateB; + this.mImpulse = 0.0; + } + + initVelocityConstraints(data: B2SolverData): void { + this.mIndexA = this.mBodyA!.mIslandIndex; + this.mIndexB = this.mBodyB!.mIslandIndex; + this.mIndexC = this.mBodyC.mIslandIndex; + this.mIndexD = this.mBodyD.mIslandIndex; + this.mLcA = this.mBodyA!.mSweep.localCenter; + this.mLcB = this.mBodyB!.mSweep.localCenter; + this.mLcC = this.mBodyC.mSweep.localCenter; + this.mLcD = this.mBodyD.mSweep.localCenter; + this.mmA = this.mBodyA!.mInvMass; + this.mmB = this.mBodyB!.mInvMass; + this.mmC = this.mBodyC.mInvMass; + this.mmD = this.mBodyD.mInvMass; + this.miA = this.mBodyA!.mInvI; + this.miB = this.mBodyB!.mInvI; + this.miC = this.mBodyC.mInvI; + this.miD = this.mBodyD.mInvI; + let aA: number = data.positions.get(this.mIndexA).a; + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let aB: number = data.positions.get(this.mIndexB).a; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let aC: number = data.positions.get(this.mIndexC).a; + let vC: B2Vec2 = data.velocities.get(this.mIndexC).v; + let wC: number = data.velocities.get(this.mIndexC).w; + let aD: number = data.positions.get(this.mIndexD).a; + let vD: B2Vec2 = data.velocities.get(this.mIndexD).v; + let wD: number = data.velocities.get(this.mIndexD).w; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + let qC = new B2Rot(aC); + let qD = new B2Rot(aD); + this.mMass = 0.0; + if (this.mTypeA === B2JointType.REVOLUTEJOINT) { + this.mJvAC.setZero(); + this.mJwA = 1.0; + this.mJwC = 1.0; + this.mMass += this.miA + this.miC; + } else { + let u = b2MulR2(qC, this.mLocalAxisC); + let rC = b2MulR2(qC, subtract(this.mLocalAnchorC, this.mLcC)); + let rA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLcA)); + this.mJvAC = u; + this.mJwC = b2Cross(rC, u); + this.mJwA = b2Cross(rA, u); + this.mMass += this.mmC + this.mmA + this.miC * this.mJwC * this.mJwC + this.miA * this.mJwA * this.mJwA; + } + if (this.mTypeB === B2JointType.REVOLUTEJOINT) { + this.mJvBD.setZero(); + this.mJwB = this.mRatio; + this.mJwD = this.mRatio; + this.mMass += this.mRatio * this.mRatio * (this.miB + this.miD); + } else { + let u = b2MulR2(qD, this.mLocalAxisD); + let rD = b2MulR2(qD, subtract(this.mLocalAnchorD, this.mLcD)); + let rB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLcB)); + this.mJvBD = multM(u, this.mRatio); + this.mJwD = this.mRatio * b2Cross(rD, u); + this.mJwB = this.mRatio * b2Cross(rB, u); + let mass1 = this.mRatio * this.mRatio * (this.mmD + this.mmB); + let mass2 = this.miD * this.mJwD * this.mJwD + this.miB * this.mJwB * this.mJwB; + let mass = mass1 + mass2; + this.mMass += mass; + } + this.mMass = this.mMass > 0.0 ? 1.0 / this.mMass : 0.0; + if (data.step.warmStarting) { + addEqual(vA, multM(this.mJvAC, this.mmA * this.mImpulse)); + wA += this.miA * this.mImpulse * this.mJwA; + addEqual(vB, multM(this.mJvBD, this.mmB * this.mImpulse)); + wB += this.miB * this.mImpulse * this.mJwB; + subtractEqual(vC, multM(this.mJvAC, this.mmC * this.mImpulse)); + wC -= this.miC * this.mImpulse * this.mJwC; + subtractEqual(vD, multM(this.mJvBD, this.mmD * this.mImpulse)); + wD -= this.miD * this.mImpulse * this.mJwD; + } else { + this.mImpulse = 0.0; + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + data.velocities.get(this.mIndexC).v = vC; + data.velocities.get(this.mIndexC).w = wC; + data.velocities.get(this.mIndexD).v = vD; + data.velocities.get(this.mIndexD).w = wD; + } + + solveVelocityConstraints(data: B2SolverData): void { + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let vC: B2Vec2 = data.velocities.get(this.mIndexC).v; + let wC: number = data.velocities.get(this.mIndexC).w; + let vD: B2Vec2 = data.velocities.get(this.mIndexD).v; + let wD: number = data.velocities.get(this.mIndexD).w; + let cdot = b2Dot22(this.mJvAC, subtract(vA, vC)) + b2Dot22(this.mJvBD, subtract(vB, vD)); + cdot += this.mJwA * wA - this.mJwC * wC + (this.mJwB * wB - this.mJwD * wD); + let impulse = -this.mMass * cdot; + this.mImpulse += impulse; + addEqual(vA, multM(this.mJvAC, this.mmA * impulse)); + wA += this.miA * impulse * this.mJwA; + addEqual(vB, multM(this.mJvBD, this.mmB * impulse)); + wB += this.miB * impulse * this.mJwB; + subtractEqual(vC, multM(this.mJvAC, this.mmC * impulse)); + wC -= this.miC * impulse * this.mJwC; + subtractEqual(vD, multM(this.mJvBD, this.mmD * impulse)); + wD -= this.miD * impulse * this.mJwD; + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + data.velocities.get(this.mIndexC).v = vC; + data.velocities.get(this.mIndexC).w = wC; + data.velocities.get(this.mIndexD).v = vD; + data.velocities.get(this.mIndexD).w = wD; + } + + solvePositionConstraints(data: B2SolverData): boolean { + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let cC: B2Vec2 = data.positions.get(this.mIndexC).c; + let aC: number = data.positions.get(this.mIndexC).a; + let cD: B2Vec2 = data.positions.get(this.mIndexD).c; + let aD: number = data.positions.get(this.mIndexD).a; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + let qC = new B2Rot(aC); + let qD = new B2Rot(aD); + let linearError: number = 0.0; + let coordinateA: number = 0; + let coordinateB: number = 0; + let mJvAC = new B2Vec2(); + let mJvBD = new B2Vec2(); + let mJwA: number = 0; + let mJwB: number = 0; + let mJwC: number = 0; + let mJwD: number = 0; + let mass: number = 0.0; + if (this.mTypeA === B2JointType.REVOLUTEJOINT) { + mJvAC.setZero(); + mJwA = 1.0; + mJwC = 1.0; + mass += this.miA + this.miC; + coordinateA = aA - aC - this.mReferenceAngleA; + } else { + let u = b2MulR2(qC, this.mLocalAxisC); + let rC = b2MulR2(qC, subtract(this.mLocalAnchorC, this.mLcC)); + let rA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLcA)); + mJvAC = u; + mJwC = b2Cross(rC, u); + mJwA = b2Cross(rA, u); + mass += this.mmC + this.mmA + this.miC * mJwC * mJwC + this.miA * mJwA * mJwA; + let pC = subtract(this.mLocalAnchorC, this.mLcC); + let pA = b2MulTR2(qC, add(rA, subtract(cA, cC))); + coordinateA = b2Dot22(subtract(pA, pC), this.mLocalAxisC); + } + if (this.mTypeB === B2JointType.REVOLUTEJOINT) { + mJvBD.setZero(); + mJwB = this.mRatio; + mJwD = this.mRatio; + mass += this.mRatio * this.mRatio * (this.miB + this.miD); + coordinateB = aB - aD - this.mReferenceAngleB; + } else { + let u = b2MulR2(qD, this.mLocalAxisD); + let rD = b2MulR2(qD, subtract(this.mLocalAnchorD, this.mLcD)); + let rB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLcB)); + mJvBD = multM(u, this.mRatio); + mJwD = this.mRatio * b2Cross(rD, u); + mJwB = this.mRatio * b2Cross(rB, u); + mass += this.mRatio * this.mRatio * (this.mmD + this.mmB) + this.miD * mJwD * mJwD + this.miB * mJwB * mJwB; + let pD = subtract(this.mLocalAnchorD, this.mLcD); + let pB = b2MulTR2(qD, add(rB, subtract(cB, cD))); + coordinateB = b2Dot22(subtract(pB, pD), this.mLocalAxisD); + } + let mC = coordinateA + this.mRatio * coordinateB - this.mConstant; + let impulse: number = 0.0; + if (mass > 0.0) { + impulse = -mC / mass; + } + addEqual(cA, multM(mJvAC, this.mmA * impulse)); + aA += this.miA * impulse * mJwA; + addEqual(cB, multM(mJvBD, this.mmB * impulse)); + aB += this.miB * impulse * mJwB; + subtractEqual(cC, multM(mJvAC, this.mmC * impulse)); + aC -= this.miC * impulse * mJwC; + subtractEqual(cD, multM(mJvBD, this.mmD * impulse)); + aD -= this.miD * impulse * mJwD; + data.positions.get(this.mIndexA).c = cA; + data.positions.get(this.mIndexA).a = aA; + data.positions.get(this.mIndexB).c = cB; + data.positions.get(this.mIndexB).a = aB; + data.positions.get(this.mIndexC).c = cC; + data.positions.get(this.mIndexC).a = aC; + data.positions.get(this.mIndexD).c = cD; + data.positions.get(this.mIndexD).a = aD; + return linearError < b2LinearSlop; + } + mJoint1: B2Joint | null; + mJoint2: B2Joint | null; + mTypeA: B2JointType; + mTypeB: B2JointType; + mBodyC: B2Body; + mBodyD: B2Body; + mLocalAnchorA = new B2Vec2(); + mLocalAnchorB = new B2Vec2(); + mLocalAnchorC = new B2Vec2(); + mLocalAnchorD = new B2Vec2(); + mLocalAxisC = new B2Vec2(); + mLocalAxisD = new B2Vec2(); + mReferenceAngleA: number = 0.0; + mReferenceAngleB: number = 0.0; + mConstant: number = 0.0; + mRatio: number = 0.0; + mImpulse: number = 0.0; + mIndexA: number = 0; + mIndexB: number = 0; + mIndexC: number = 0; + mIndexD: number = 0; + mLcA = new B2Vec2(); + mLcB = new B2Vec2(); + mLcC = new B2Vec2(); + mLcD = new B2Vec2(); + mmA: number = 0.0; + mmB: number = 0.0; + mmC: number = 0.0; + mmD: number = 0.0; + miA: number = 0.0; + miB: number = 0.0; + miC: number = 0.0; + miD: number = 0.0; + mJvAC = new B2Vec2(); + mJvBD = new B2Vec2(); + mJwA: number = 0.0; + mJwB: number = 0.0; + mJwC: number = 0.0; + mJwD: number = 0.0; + mMass: number = 0.0; +} + +export class B2FrictionJointDef extends B2JointDef { + constructor() { + super(); + this.localAnchorA = new B2Vec2(); + this.localAnchorB = new B2Vec2(); + this.maxForce = 0.0; + this.maxTorque = 0.0; + this.type = B2JointType.FRICTIONJOINT; + } + + public initialize(bA: B2Body, bB: B2Body, anchor: B2Vec2): void { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.getLocalPoint(anchor); + this.localAnchorB = this.bodyB.getLocalPoint(anchor); + } + + public localAnchorA: B2Vec2; + public localAnchorB: B2Vec2; + public maxForce: number; + public maxTorque: number; +} + +export class B2FrictionJoint extends B2Joint { + mLocalAnchorA: B2Vec2; + mLocalAnchorB: B2Vec2; + mLinearImpulse: B2Vec2; + mAngularImpulse: number; + mMaxForce: number; + mMaxTorque: number; + mIndexA: number = 0; + mIndexB: number = 0; + mrA = new B2Vec2(); + mrB = new B2Vec2(); + mLocalCenterA = new B2Vec2(); + mLocalCenterB = new B2Vec2(); + mInvMassA: number = 0.0; + mInvMassB: number = 0.0; + mInvIA: number = 0.0; + mInvIB: number = 0.0; + mLinearMass = new B2Mat22(); + mAngularMass: number = 0.0; + public get anchorA(): B2Vec2 { + return this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + } + + public get anchorB(): B2Vec2 { + return this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + } + + public getReactionForce(invdt: number): B2Vec2 { + return multM(this.mLinearImpulse, invdt); + } + + public getReactionTorque(invdt: number): number { + return invdt * this.mAngularImpulse; + } + + public get localAnchorA(): B2Vec2 { + return this.mLocalAnchorA; + } + + public get localAnchorB(): B2Vec2 { + return this.mLocalAnchorB; + } + + public setMaxForce(force: number): void { + this.mMaxForce = force; + } + + public get maxForce(): number { + return this.mMaxForce; + } + + public setMaxTorque(torque: number): void { + this.mMaxTorque = torque; + } + + public get maxTorque(): number { + return this.mMaxTorque; + } + + constructor(def: B2FrictionJointDef) { + super(def); + this.mLocalAnchorA = def.localAnchorA; + this.mLocalAnchorB = def.localAnchorB; + this.mLinearImpulse = new B2Vec2(0.0, 0.0); + this.mAngularImpulse = 0.0; + this.mMaxForce = def.maxForce; + this.mMaxTorque = def.maxTorque; + } + + initVelocityConstraints(data: B2SolverData): void { + this.mIndexA = this.mBodyA!.mIslandIndex; + this.mIndexB = this.mBodyB!.mIslandIndex; + this.mLocalCenterA = this.mBodyA!.mSweep.localCenter; + this.mLocalCenterB = this.mBodyB!.mSweep.localCenter; + this.mInvMassA = this.mBodyA!.mInvMass; + this.mInvMassB = this.mBodyB!.mInvMass; + this.mInvIA = this.mBodyA!.mInvI; + this.mInvIB = this.mBodyB!.mInvI; + let aA: number = data.positions.get(this.mIndexA).a; + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let aB: number = data.positions.get(this.mIndexB).a; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + this.mrA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + this.mrB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + let mK = new B2Mat22(); + mK.ex.x = mA + mB + iA * this.mrA.y * this.mrA.y + iB * this.mrB.y * this.mrB.y; + mK.ex.y = -iA * this.mrA.x * this.mrA.y - iB * this.mrB.x * this.mrB.y; + mK.ey.x = mK.ex.y; + mK.ey.y = mA + mB + iA * this.mrA.x * this.mrA.x + iB * this.mrB.x * this.mrB.x; + this.mLinearMass = mK.getInverse(); + this.mAngularMass = iA + iB; + if (this.mAngularMass > 0.0) { + this.mAngularMass = 1.0 / this.mAngularMass; + } + if (data.step.warmStarting) { + mulEqual(this.mLinearImpulse, data.step.dtRatio); + this.mAngularImpulse *= data.step.dtRatio; + let mP = new B2Vec2(this.mLinearImpulse.x, this.mLinearImpulse.y); + subtractEqual(vA, multM(mP, mA)); + wA -= iA * (b2Cross(this.mrA, mP) + this.mAngularImpulse); + addEqual(vB, multM(mP, mB)); + wB += iB * (b2Cross(this.mrB, mP) + this.mAngularImpulse); + } else { + this.mLinearImpulse.setZero(); + this.mAngularImpulse = 0.0; + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solveVelocityConstraints(data: B2SolverData): void { + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let mA = this.mInvMassA; + let mB = this.mInvMassB; + let iA = this.mInvIA; + let iB = this.mInvIB; + let h = data.step.dt; + let cdot = wB - wA; + let impulse = -this.mAngularMass * cdot; + let oldImpulse = this.mAngularImpulse; + let maxImpulse = h * this.mMaxTorque; + this.mAngularImpulse = b2ClampF(this.mAngularImpulse + impulse, -maxImpulse, maxImpulse); + impulse = this.mAngularImpulse - oldImpulse; + wA -= iA * impulse; + wB += iB * impulse; + let locCdot = subtract(subtract(add(vB, b2Cross21(wB, this.mrB)), vA), b2Cross21(wA, this.mrA)); + let locImpulse = minus(b2Mul22(this.mLinearMass, locCdot)); + let locOldImpulse = this.mLinearImpulse; + addEqual(this.mLinearImpulse, locImpulse); + let locMaxImpulse = h * this.mMaxForce; + if (this.mLinearImpulse.lengthSquared() > locMaxImpulse * locMaxImpulse) { + this.mLinearImpulse.normalize(); + mulEqual(this.mLinearImpulse, locMaxImpulse); + } + locImpulse = subtract(this.mLinearImpulse, locOldImpulse); + subtractEqual(vA, multM(locImpulse, mA)); + wA -= iA * b2Cross(this.mrA, locImpulse); + addEqual(vB, multM(locImpulse, mB)); + wB += iB * b2Cross(this.mrB, locImpulse); + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + solvePositionConstraints(data: B2SolverData): boolean { + return true; + } +} + +export class B2DistanceJointDef extends B2JointDef { + constructor(bodyA?: B2Body, bodyB?: B2Body, anchorA?: B2Vec2, anchorB?: B2Vec2) { + super(); + if (bodyA !== null && bodyB !== null && anchorA !== null && anchorB !== null) { + this.initialize(bodyA!, bodyB!, anchorA!, anchorB!); + } else { + this.localAnchorA = new B2Vec2(0.0, 0.0); + this.localAnchorB = new B2Vec2(0.0, 0.0); + this.length = 1.0; + this.frequencyHz = 0.0; + this.dampingRatio = 0.0; + this.type = B2JointType.DISTANCEJOINT; + } + } + + initialize(bA: B2Body, bB: B2Body, anchorA: B2Vec2, anchorB: B2Vec2): void { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.getLocalPoint(anchorA); + this.localAnchorB = this.bodyB.getLocalPoint(anchorB); + let d = subtract(anchorB, anchorA); + this.length = d.length(); + } + + public localAnchorA: B2Vec2 = new B2Vec2(); + public localAnchorB: B2Vec2 = new B2Vec2(); + public length: number = 1.0; + public frequencyHz: number = 0.0; + public dampingRatio: number = 0.0; +} + +export class B2DistanceJoint extends B2Joint { + mFrequencyHz: number; + mDampingRatio: number; + mBias: number; + mLocalAnchorA: B2Vec2; + mLocalAnchorB: B2Vec2; + mGamma: number; + mImpulse: number; + mLength: number; + mIndexA: number = 0; + mIndexB: number = 0; + mu = new B2Vec2(); + mrA = new B2Vec2(); + mrB = new B2Vec2(); + mLocalCenterA = new B2Vec2(); + mLocalCenterB = new B2Vec2(); + mInvMassA: number = 0.0; + mInvMassB: number = 0.0; + mInvIA: number = 0.0; + mInvIB: number = 0.0; + mMass: number = 0.0; + public get anchorA(): B2Vec2 { + return this.mBodyA!.getWorldPoint(this.mLocalAnchorA); + } + + public get anchorB(): B2Vec2 { + return this.mBodyB!.getWorldPoint(this.mLocalAnchorB); + } + + public getReactionForce(invdt: number): B2Vec2 { + let mF = multM(this.mu, invdt * this.mImpulse); + return mF; + } + + public getReactionTorque(invdt: number): number { + return 0.0; + } + + public get localAnchorA(): B2Vec2 { + return this.mLocalAnchorA; + } + + public get localAnchorB(): B2Vec2 { + return this.mLocalAnchorB; + } + + setLength(length: number): void { + this.mLength = length; + } + + public get length(): number { + return this.mLength; + } + + setFrequency(hz: number): void { + this.mFrequencyHz = hz; + } + + public get frequency(): number { + return this.mFrequencyHz; + } + + setDampingRatio(ratio: number): void { + this.mDampingRatio = ratio; + } + + public get dampingRatio(): number { + return this.mDampingRatio; + } + + constructor(def: B2DistanceJointDef) { + super(def); + this.mLocalAnchorA = def.localAnchorA; + this.mLocalAnchorB = def.localAnchorB; + this.mLength = def.length; + this.mFrequencyHz = def.frequencyHz; + this.mDampingRatio = def.dampingRatio; + this.mImpulse = 0.0; + this.mGamma = 0.0; + this.mBias = 0.0; + } + + public initVelocityConstraints(data: B2SolverData): void { + this.mIndexA = this.mBodyA!.mIslandIndex; + this.mIndexB = this.mBodyB!.mIslandIndex; + this.mLocalCenterA = this.mBodyA!.mSweep.localCenter; + this.mLocalCenterB = this.mBodyB!.mSweep.localCenter; + this.mInvMassA = this.mBodyA!.mInvMass; + this.mInvMassB = this.mBodyB!.mInvMass; + this.mInvIA = this.mBodyA!.mInvI; + this.mInvIB = this.mBodyB!.mInvI; + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + this.mrA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + this.mrB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + this.mu = subtract(subtract(add(cB, this.mrB), cA), this.mrA); + let length = this.mu.length(); + if (length > b2LinearSlop) { + mulEqual(this.mu, 1.0 / length); + } else { + this.mu.set(0.0, 0.0); + } + let crAu = b2Cross(this.mrA, this.mu); + let crBu = b2Cross(this.mrB, this.mu); + let invMass = this.mInvMassA + this.mInvIA * crAu * crAu + this.mInvMassB + this.mInvIB * crBu * crBu; + this.mMass = invMass !== 0.0 ? 1.0 / invMass : 0.0; + if (this.mFrequencyHz > 0.0) { + let mC = length - this.mLength; + let omega = float2 * b2PI * this.mFrequencyHz; + let d = float2 * this.mMass * this.mDampingRatio * omega; + let k = this.mMass * omega * omega; + let h = data.step.dt; + this.mGamma = h * (d + h * k); + this.mGamma = this.mGamma !== 0.0 ? 1.0 / this.mGamma : 0.0; + this.mBias = mC * h * k * this.mGamma; + invMass += this.mGamma; + this.mMass = invMass !== 0.0 ? 1.0 / invMass : 0.0; + } else { + this.mGamma = 0.0; + this.mBias = 0.0; + } + if (data.step.warmStarting) { + this.mImpulse *= data.step.dtRatio; + let mP = multM(this.mu, this.mImpulse); + subtractEqual(vA, multM(mP, this.mInvMassA)); + wA -= this.mInvIA * b2Cross(this.mrA, mP); + addEqual(vB, multM(mP, this.mInvMassB)); + wB += this.mInvIB * b2Cross(this.mrB, mP); + } else { + this.mImpulse = 0.0; + } + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + public solveVelocityConstraints(data: B2SolverData): void { + let vA: B2Vec2 = data.velocities.get(this.mIndexA).v; + let wA: number = data.velocities.get(this.mIndexA).w; + let vB: B2Vec2 = data.velocities.get(this.mIndexB).v; + let wB: number = data.velocities.get(this.mIndexB).w; + let vpA = add(vA, b2Cross21(wA, this.mrA)); + let vpB = add(vB, b2Cross21(wB, this.mrB)); + let cdot = b2Dot22(this.mu, subtract(vpB, vpA)); + let impulse = -this.mMass * (cdot + this.mBias + this.mGamma * this.mImpulse); + this.mImpulse += impulse; + let mP = multM(this.mu, impulse); + subtractEqual(vA, multM(mP, this.mInvMassA)); + wA -= this.mInvIA * b2Cross(this.mrA, mP); + addEqual(vB, multM(mP, this.mInvMassB)); + wB += this.mInvIB * b2Cross(this.mrB, mP); + data.velocities.get(this.mIndexA).v = vA; + data.velocities.get(this.mIndexA).w = wA; + data.velocities.get(this.mIndexB).v = vB; + data.velocities.get(this.mIndexB).w = wB; + } + + public solvePositionConstraints(data: B2SolverData): boolean { + if (this.mFrequencyHz > 0.0) { + return true; + } + let cA: B2Vec2 = data.positions.get(this.mIndexA).c; + let aA: number = data.positions.get(this.mIndexA).a; + let cB: B2Vec2 = data.positions.get(this.mIndexB).c; + let aB: number = data.positions.get(this.mIndexB).a; + let qA = new B2Rot(aA); + let qB = new B2Rot(aB); + let rA = b2MulR2(qA, subtract(this.mLocalAnchorA, this.mLocalCenterA)); + let rB = b2MulR2(qB, subtract(this.mLocalAnchorB, this.mLocalCenterB)); + let u = subtract(subtract(add(cB, rB), cA), rA); + let length = u.normalize(); + let mC = length - this.mLength; + mC = b2ClampF(mC, -b2MaxLinearCorrection, b2MaxLinearCorrection); + let impulse = -this.mMass * mC; + let mP = multM(u, impulse); + subtractEqual(cA, multM(mP, this.mInvMassA)); + aA -= this.mInvIA * b2Cross(rA, mP); + addEqual(cB, multM(mP, this.mInvMassB)); + aB += this.mInvIB * b2Cross(rB, mP); + data.positions.get(this.mIndexA).c = cA; + data.positions.get(this.mIndexA).a = aA; + data.positions.get(this.mIndexB).c = cB; + data.positions.get(this.mIndexB).a = aB; + return Math.abs(mC) < b2LinearSlop; + } +} +export { B2JointType, B2LimitState }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Body.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Body.ts new file mode 100644 index 0000000000000000000000000000000000000000..a41cf1583e32e6c8e289d84823c9a69cddcb645d --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Body.ts @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + B2Vec2, + b2MulTT2, + b2MulTR2, + B2Mat22, + B2Transform, + b2Vec2Zero, + subtract, + multM, + add, + b2Cross, + b2Cross21, + b2MulT2, + b2MulR2, + B2Sweep, + b2Dot22, + addEqual, + mulEqual +} from '../Common/b2Math'; +import { B2FixtureDef, B2Fixture } from './b2Fixture'; +import { B2Shape } from '../Collision/Shapes/b2Shape'; +import { B2MassData } from '../Collision/Shapes/b2Shape'; +import { B2World } from './b2World'; +import { FlagsW } from './b2World'; +import { B2ContactEdge } from './Contacts/b2Contact'; +import { B2JointEdge } from './Joints/b2Joint'; +import { hex1, hex10, hex2, hex20, hex4, hex40, hex8 } from '../Common/b2Settings'; +enum B2BodyType { + STATICBODY = 0, + KINEMATICBODY, + DYNAMICBODY +} +export class B2BodyDef { + type = B2BodyType.STATICBODY; + position = new B2Vec2(); + angle: number = 0.0; + linearVelocity = new B2Vec2(); + angularVelocity: number = 0.0; + linearDamping: number = 0.0; + angularDamping: number = 0.0; + allowSleep = true; + awake = true; + fixedRotation = false; + bullet = false; + active = true; + userData?: object | null = null; + gravityScale: number = 1.0; + constructor() {} +} +export class B2Body { + mType: B2BodyType; + mFlags: number = 0; + mIslandIndex = 0; + mXF: B2Transform; + mSweep: B2Sweep; + mLinearVelocity: B2Vec2; + mAngularVelocity: number; + mForce: B2Vec2; + mTorque: number; + mWorld: B2World; + mPrev: B2Body | null = null; + mNext: B2Body | null = null; + mFixtureList: B2Fixture | null = null; + mFixtureCount: number = 0; + mJointList: B2JointEdge | null = null; + mContactList: B2ContactEdge | null = null; + mMass: number; + mInvMass: number; + mI: number; + mInvI: number; + mLinearDamping: number; + mAngularDamping: number; + mGravityScale: number; + mSleepTime: number; + mUserData: object | null; + createFixture(def: B2FixtureDef): B2Fixture { + if (this.mWorld.isLocked === true) { + throw new Error('world is locked'); + } + let fixture = new B2Fixture(this, def); + if ((this.mFlags & Flags.activeFlag) !== 0) { + let broadPhase = this.mWorld.mContactManager.mBroadPhase; + fixture.createProxies(broadPhase, this.mXF); + } + fixture.mNext = this.mFixtureList; + this.mFixtureList = fixture; + this.mFixtureCount += 1; + fixture.mBody = this; + if (fixture.mDensity > 0.0) { + this.resetMassData(); + } + this.mWorld.mFlags |= FlagsW.newFixture; + return fixture; + } + + createFixture2(shape: B2Shape, density: number): B2Fixture { + let def = new B2FixtureDef(); + def.shape = shape; + def.density = density; + return this.createFixture(def); + } + + destroyFixture(fixture: B2Fixture): void { + if (this.mWorld.isLocked === true) { + return; + } + let prev: B2Fixture | null = null; + let f = this.mFixtureList; + while (f !== null) { + if (f === fixture) { + if (prev !== null) { + prev.mNext = f!.mNext; + } else { + this.mFixtureList = f.mNext; + } + } + prev = f; + f = f.mNext; + } + + let edge: B2ContactEdge | null = this.mContactList; + while (edge !== null) { + let c = edge!.contact; + edge = edge!.next; + let fixtureA = c.fixtureA; + let fixtureB = c.fixtureB; + if (fixture === fixtureA || fixture === fixtureB) { + this.mWorld.mContactManager.destroy(c); + } + } + if ((this.mFlags & Flags.activeFlag) !== 0) { + let broadPhase = this.mWorld.mContactManager.mBroadPhase; + fixture.destroyProxies(broadPhase); + } + fixture.destroy(); + fixture.mNext = null; + this.mFixtureCount -= 1; + this.resetMassData(); + } + + setTransform(position: B2Vec2, angle: number): void { + if (this.mWorld.isLocked === true) { + return; + } + this.mXF.q.set(angle); + this.mXF.p = position; + this.mSweep.c = b2MulT2(this.mXF, this.mSweep.localCenter); + this.mSweep.a = angle; + this.mSweep.c0 = this.mSweep.c; + this.mSweep.a0 = angle; + let broadPhase = this.mWorld.mContactManager.mBroadPhase; + let f: B2Fixture | null = this.mFixtureList; + while (f !== null) { + f.synchronize(broadPhase, this.mXF, this.mXF); + f = f!.mNext; + } + } + + get transform(): B2Transform { + return this.mXF; + } + + get position(): B2Vec2 { + return this.mXF.p; + } + + get angle(): number { + return this.mSweep.a; + } + + get worldCenter(): B2Vec2 { + return this.mSweep.c; + } + + get localCenter(): B2Vec2 { + return this.mSweep.localCenter; + } + + setLinearVelocity(v: B2Vec2): void { + if (this.mType === B2BodyType.STATICBODY) { + return; + } + if (b2Dot22(v, v) > 0.0) { + this.setAwake(true); + } + this.mLinearVelocity = v; + } + + get linearVelocity(): B2Vec2 { + return this.mLinearVelocity; + } + + set linearVelocity(newValue) { + this.setLinearVelocity(newValue); + } + + setAngularVelocity(omega: number): void { + if (this.mType === B2BodyType.STATICBODY) { + return; + } + if (omega * omega > 0.0) { + this.setAwake(true); + } + this.mAngularVelocity = omega; + } + + get angularVelocity(): number { + return this.mAngularVelocity; + } + + set angularVelocity(newValue) { + this.setAngularVelocity(newValue); + } + + applyForce(force: B2Vec2, point: B2Vec2, wake: boolean): void { + if (this.mType !== B2BodyType.DYNAMICBODY) { + return; + } + if (wake && (this.mFlags & Flags.awakeFlag) === 0) { + this.setAwake(true); + } + if ((this.mFlags & Flags.awakeFlag) !== 0) { + addEqual(this.mForce, force); + this.mTorque += b2Cross(subtract(point, this.mSweep.c), force); + } + } + + applyForceToCenter(force: B2Vec2, wake: boolean): void { + if (this.mType !== B2BodyType.DYNAMICBODY) { + return; + } + if (wake && (this.mFlags & Flags.awakeFlag) === 0) { + this.setAwake(true); + } + if ((this.mFlags & Flags.awakeFlag) !== 0) { + addEqual(this.mForce, force); + } + } + + applyTorque(torque: number, wake: boolean): void { + if (this.mType !== B2BodyType.DYNAMICBODY) { + return; + } + if (wake && (this.mFlags & Flags.awakeFlag) === 0) { + this.setAwake(true); + } + if ((this.mFlags & Flags.awakeFlag) !== 0) { + this.mTorque += torque; + } + } + + applyLinearImpulse(impulse: B2Vec2, point: B2Vec2, wake: boolean): void { + if (this.mType !== B2BodyType.DYNAMICBODY) { + return; + } + if (wake && (this.mFlags & Flags.awakeFlag) === 0) { + this.setAwake(true); + } + if ((this.mFlags & Flags.awakeFlag) !== 0) { + addEqual(this.mLinearVelocity, multM(impulse, this.mInvMass)); + this.mAngularVelocity += this.mInvI * b2Cross(subtract(point, this.mSweep.c), impulse); + } + } + + applyAngularImpulse(impulse: number, wake: boolean): void { + if (this.mType !== B2BodyType.DYNAMICBODY) { + return; + } + if (wake && (this.mFlags & Flags.awakeFlag) === 0) { + this.setAwake(true); + } + if ((this.mFlags & Flags.awakeFlag) !== 0) { + this.mAngularVelocity += this.mInvI * impulse; + } + } + get mass(): number { + return this.mMass; + } + get inertia(): number { + return this.mI + this.mMass * b2Dot22(this.mSweep.localCenter, this.mSweep.localCenter); + } + get massData(): B2MassData { + let data = new B2MassData(); + data.mass = this.mMass; + data.aI = this.mI + this.mMass * b2Dot22(this.mSweep.localCenter, this.mSweep.localCenter); + data.center = this.mSweep.localCenter; + return data; + } + + set massData(newValue) { + this.setMassData(newValue); + } + + setMassData(massData: B2MassData): void { + if (this.mWorld.isLocked === true) { + return; + } + if (this.mType !== B2BodyType.DYNAMICBODY) { + return; + } + this.mInvMass = 0.0; + this.mI = 0.0; + this.mInvI = 0.0; + this.mMass = massData.mass; + if (this.mMass <= 0.0) { + this.mMass = 1.0; + } + this.mInvMass = 1.0 / this.mMass; + if (massData.aI > 0.0 && (this.mFlags & Flags.fixedRotationFlag) === 0) { + this.mI = massData.aI - this.mMass * b2Dot22(massData.center, massData.center); + this.mInvI = 1.0 / this.mI; + } + let oldCenter = this.mSweep.c; + this.mSweep.localCenter = massData.center; + this.mSweep.c0 = b2MulT2(this.mXF, this.mSweep.localCenter); + this.mSweep.c = this.mSweep.c0; + addEqual(this.mLinearVelocity, b2Cross21(this.mAngularVelocity, subtract(this.mSweep.c, oldCenter))); + } + + resetMassData(): void { + this.mMass = 0.0; + this.mInvMass = 0.0; + this.mI = 0.0; + this.mInvI = 0.0; + this.mSweep.localCenter.setZero(); + if (this.mType === B2BodyType.STATICBODY || this.mType === B2BodyType.KINEMATICBODY) { + this.mSweep.c0 = this.mXF.p; + this.mSweep.c = this.mXF.p; + this.mSweep.a0 = this.mSweep.a; + return; + } + let localCenter = b2Vec2Zero; + let f: B2Fixture | null = this.mFixtureList; + while (f !== null) { + if (f.mDensity === 0.0) { + f = f!.mNext; + continue; + } + let massData = f.massData; + this.mMass += massData.mass; + addEqual(localCenter, multM(massData.center, massData.mass)); + this.mI += massData.aI; + f = f!.mNext; + } + if (this.mMass > 0.0) { + this.mInvMass = 1.0 / this.mMass; + mulEqual(localCenter, this.mInvMass); + } else { + this.mMass = 1.0; + this.mInvMass = 1.0; + } + if (this.mI > 0.0 && (this.mFlags & Flags.fixedRotationFlag) === 0) { + this.mI -= this.mMass * b2Dot22(localCenter, localCenter); + this.mInvI = 1.0 / this.mI; + } else { + this.mI = 0.0; + this.mInvI = 0.0; + } + let oldCenter = this.mSweep.c; + this.mSweep.localCenter = localCenter; + this.mSweep.c0 = b2MulT2(this.mXF, this.mSweep.localCenter); + this.mSweep.c = this.mSweep.c0; + addEqual(this.mLinearVelocity, b2Cross21(this.mAngularVelocity, subtract(this.mSweep.c, oldCenter))); + } + + getWorldPoint(localPoint: B2Vec2): B2Vec2 { + return b2MulT2(this.mXF, localPoint); + } + + getWorldVector(localVector: B2Vec2): B2Vec2 { + return b2MulR2(this.mXF.q, localVector); + } + + getLocalPoint(worldPoint: B2Vec2): B2Vec2 { + return b2MulTT2(this.mXF, worldPoint); + } + + getLocalVector(worldVector: B2Vec2): B2Vec2 { + return b2MulTR2(this.mXF.q, worldVector); + } + + getLinearVelocityFromWorldPoint(worldPoint: B2Vec2): B2Vec2 { + return add(this.mLinearVelocity, b2Cross21(this.mAngularVelocity, subtract(worldPoint, this.mSweep.c))); + } + + getLinearVelocityFromLocalPoint(localPoint: B2Vec2): B2Vec2 { + return this.getLinearVelocityFromWorldPoint(this.getWorldPoint(localPoint)); + } + + get linearDamping(): number { + return this.mLinearDamping; + } + set linearDamping(newValue) { + this.setLinearDamping(newValue); + } + + setLinearDamping(linearDamping: number): void { + this.mLinearDamping = linearDamping; + } + + get angularDamping(): number { + return this.mGravityScale; + } + set angularDamping(newValue) { + this.setAngularDamping(newValue); + } + + setAngularDamping(angularDamping: number): void { + this.mAngularDamping = angularDamping; + } + + get gravityScale(): number { + return this.mGravityScale; + } + set gravityScale(newValue) { + this.setGravityScale(newValue); + } + + setGravityScale(scale: number): void { + this.mGravityScale = scale; + } + + setType(type: B2BodyType): void { + if (this.mWorld.isLocked === true) { + return; + } + if (this.mType === type) { + return; + } + this.mType = type; + this.resetMassData(); + if (this.mType === B2BodyType.STATICBODY) { + this.mLinearVelocity.setZero(); + this.mAngularVelocity = 0.0; + this.mSweep.a0 = this.mSweep.a; + this.mSweep.c0 = this.mSweep.c; + this.synchronizeFixtures(); + } + this.setAwake(true); + this.mForce.setZero(); + this.mTorque = 0.0; + let ce: B2ContactEdge | null = this.mContactList; + while (ce !== null) { + let ce0 = ce!; + ce = ce!.next; + this.mWorld.mContactManager.destroy(ce0.contact); + } + this.mContactList = null; + let broadPhase = this.mWorld.mContactManager.mBroadPhase; + let f: B2Fixture | null = this.mFixtureList; + while (f !== null) { + let proxyCount = f!.mProxyCount; + for (let i = 0; i < proxyCount; i++) { + broadPhase.touchProxy(f!.mProxies[i].proxyId); + } + f = f!.mNext; + } + } + + get typeBody(): B2BodyType { + return this.mType; + } + set typeBody(newValue) { + this.setType(newValue); + } + + setBullet(flag: boolean): void { + if (flag) { + this.mFlags |= Flags.bulletFlag; + } else { + this.mFlags &= ~Flags.bulletFlag; + } + } + + get isBullet(): boolean { + return (this.mFlags & Flags.bulletFlag) === Flags.bulletFlag; + } + + set isBullet(newValue) { + this.setBullet(newValue); + } + + setSleepingAllowed(flag: boolean): void { + if (flag) { + this.mFlags |= Flags.autoSleepFlag; + } else { + this.mFlags &= ~Flags.autoSleepFlag; + this.setAwake(true); + } + } + + get isSleepingAllowed(): boolean { + return (this.mFlags & Flags.autoSleepFlag) === Flags.autoSleepFlag; + } + + set isSleepingAllowed(newValue) { + this.setSleepingAllowed(newValue); + } + + setAwake(flag: boolean): void { + if (flag) { + if ((this.mFlags & Flags.awakeFlag) === 0) { + this.mFlags |= Flags.awakeFlag; + this.mSleepTime = 0.0; + } + } else { + this.mFlags &= ~Flags.awakeFlag; + this.mSleepTime = 0.0; + this.mLinearVelocity.setZero(); + this.mAngularVelocity = 0.0; + this.mForce.setZero(); + this.mTorque = 0.0; + } + } + + get isAwake(): boolean { + return (this.mFlags & Flags.awakeFlag) === Flags.awakeFlag; + } + + setActive(flag: boolean): void { + if (flag === this.isActive) { + return; + } + if (flag) { + this.mFlags |= Flags.activeFlag; + let broadPhase = this.mWorld.mContactManager.mBroadPhase; + let f: B2Fixture | null = this.mFixtureList; + while (f !== null) { + f!.createProxies(broadPhase, this.mXF); + f = f!.mNext; + } + } else { + this.mFlags &= ~Flags.activeFlag; + let broadPhase = this.mWorld.mContactManager.mBroadPhase; + let f: B2Fixture | null = this.mFixtureList; + while (f !== null) { + f!.destroyProxies(broadPhase); + if (f!.mNext !== null) { + f = f!.mNext; + } else { + break; + } + } + let ce = this.mContactList; + while (ce !== null) { + let ce0 = ce!; + ce = ce!.next; + this.mWorld.mContactManager.destroy(ce0.contact); + } + this.mContactList = null; + } + } + get isActive(): boolean { + return (this.mFlags & Flags.activeFlag) === Flags.activeFlag; + } + + setFixedRotation(flag: boolean): void { + let status = (this.mFlags & Flags.fixedRotationFlag) === Flags.fixedRotationFlag; + if (status === flag) { + return; + } + if (flag) { + this.mFlags |= Flags.fixedRotationFlag; + } else { + this.mFlags &= ~Flags.fixedRotationFlag; + } + this.mAngularVelocity = 0.0; + this.resetMassData(); + } + + public isFixedRotation(): boolean { + return (this.mFlags & Flags.fixedRotationFlag) === Flags.fixedRotationFlag; + } + + public getFixtureList(): B2Fixture | null { + return this.mFixtureList; + } + + public getJointList(): B2JointEdge | null { + return this.mJointList; + } + + public getContactList(): B2ContactEdge | null { + return this.mContactList; + } + + public getNext(): B2Body | null { + return this.mNext; + } + + get userData(): object | null { + return this.mUserData; + } + + set userData(newValue) { + this.setUserData(newValue); + } + + public setUserData(data: object | null): void { + this.mUserData = data; + } + + get world(): B2World | null { + return this.mWorld; + } + + constructor(def: B2BodyDef, world: B2World) { + this.mFlags = 0; + if (def.bullet) { + this.mFlags |= Flags.bulletFlag; + } + if (def.fixedRotation) { + this.mFlags |= Flags.fixedRotationFlag; + } + if (def.allowSleep) { + this.mFlags |= Flags.autoSleepFlag; + } + if (def.awake) { + this.mFlags |= Flags.awakeFlag; + } + if (def.active) { + this.mFlags |= Flags.activeFlag; + } + this.mWorld = world; + this.mXF = new B2Transform(); + this.mXF.p = def.position; + this.mXF.q.set(def.angle); + this.mSweep = new B2Sweep(); + this.mSweep.localCenter = new B2Vec2(0.0, 0.0); + this.mSweep.c0 = this.mXF.p; + this.mSweep.c = this.mXF.p; + this.mSweep.a0 = def.angle; + this.mSweep.a = def.angle; + this.mSweep.alpha0 = 0.0; + this.mPrev = null; + this.mNext = null; + this.mLinearVelocity = def.linearVelocity; + this.mAngularVelocity = def.angularVelocity; + this.mLinearDamping = def.linearDamping; + this.mAngularDamping = def.angularDamping; + this.mGravityScale = def.gravityScale; + this.mForce = new B2Vec2(0.0, 0.0); + this.mTorque = 0.0; + this.mSleepTime = 0.0; + this.mType = def.type; + if (this.mType === B2BodyType.DYNAMICBODY) { + this.mMass = 1.0; + this.mInvMass = 1.0; + } else { + this.mMass = 0.0; + this.mInvMass = 0.0; + } + this.mI = 0.0; + this.mInvI = 0.0; + this.mUserData = def.userData!; + this.mFixtureList = null; + this.mFixtureCount = 0; + } + + synchronizeFixtures(): void { + let xf1 = new B2Transform(); + xf1.q.set(this.mSweep.a0); + xf1.p = subtract(this.mSweep.c0, b2MulR2(xf1.q, this.mSweep.localCenter)); + let broadPhase = this.mWorld.mContactManager.mBroadPhase; + let f: B2Fixture | null = this.mFixtureList; + while (f !== null) { + f!.synchronize(broadPhase, xf1, this.mXF); + f = f!.mNext; + } + } + + synchronizeTransform(): void { + this.mXF.q.set(this.mSweep.a); + this.mXF.p = subtract(this.mSweep.c, b2MulR2(this.mXF.q, this.mSweep.localCenter)); + let tVec = this.mSweep.localCenter; + let tMat = new B2Mat22(); + tMat.setMat(this.mSweep.a); + this.mXF.p.y = this.mSweep.c.y - (tMat.ex.y * tVec.x + tMat.ey.y * tVec.y); + } + + shouldCollide(other: B2Body): boolean { + if (this.mType !== B2BodyType.DYNAMICBODY && other.mType !== B2BodyType.DYNAMICBODY) { + return false; + } + let jn: B2JointEdge | null = this.mJointList; + while (jn !== null) { + if (jn!.other === other) { + if (jn!.joint.mCollideConnected === false) { + return false; + } + } + jn = jn!.next; + } + return true; + } + advance(alpha: number): void { + this.mSweep.advance(alpha); + this.mSweep.c = this.mSweep.c0; + this.mSweep.a = this.mSweep.a0; + this.mXF.q.set(this.mSweep.a); + this.mXF.p = subtract(this.mSweep.c, b2MulR2(this.mXF.q, this.mSweep.localCenter)); + } +} +export class Flags { + static islandFlag = hex1; + static awakeFlag = hex2; + static autoSleepFlag = hex4; + static bulletFlag = hex8; + static fixedRotationFlag = hex10; + static activeFlag = hex20; + static toiFlag = hex40; +} +export { B2BodyType }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2ContactManager.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2ContactManager.ts new file mode 100644 index 0000000000000000000000000000000000000000..b860614f0364363f1af0a9fde92bf67dd69cd0f5 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2ContactManager.ts @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2ContactListener } from './b2WorldCallbacks'; +import { B2ContactFilter, B2DefaultContactListener } from './b2WorldCallbacks'; +import { B2Contact, FlagContacts } from './Contacts/b2Contact'; +import { B2BroadPhaseWrapper } from '../Common/b2Wrappers'; +import { B2FixtureProxy } from './b2Fixture'; +import { B2BroadPhase } from '../Collision/b2BroadPhase'; +import { B2BodyType } from './b2Body'; + +let b2DefaultFilter = new B2ContactFilter(); +let b2DefaultListener = new B2DefaultContactListener(); + +export class B2ContactManager implements B2BroadPhaseWrapper { + mContactList: B2Contact | null = null; + mContactCount: number = 0; + mContactFilter: B2ContactFilter | null = b2DefaultFilter; + mContactListener: B2ContactListener | null = b2DefaultListener; + constructor() { + this.mContactList = null; + this.mContactCount = 0; + this.mContactFilter = b2DefaultFilter; + this.mContactListener = b2DefaultListener; + } + + public addPair(proxyUserDataA: B2FixtureProxy, proxyUserDataB: B2FixtureProxy): void { + console.info('box2d','900') + let proxyA = proxyUserDataA; + let proxyB = proxyUserDataB; + let fixtureA = proxyA.fixture; + let fixtureB = proxyB.fixture; + let indexA = proxyA.childIndex; + let indexB = proxyB.childIndex; + let bodyA = fixtureA.body; + let bodyB = fixtureB.body; + if (bodyA === bodyB) { + return; + } + let edge = bodyB.getContactList(); + console.info('box2d','edge:'+edge) + while (edge !== null) { + if (edge.other === bodyA) { + let fA = edge.contact.fixtureA; + let fB = edge.contact.fixtureB; + let iA = edge.contact.childIndexA; + let iB = edge.contact.childIndexB; + if (fA === fixtureA && fB === fixtureB && iA === indexA && iB === indexB) { + return; + } + if (fA === fixtureB && fB === fixtureA && iA === indexB && iB === indexA) { + return; + } + } + edge = edge!.next; + } + console.info('box2d','shouldCollide:1') + if (bodyB.shouldCollide(bodyA) === false) { + return; + } + console.info('box2d','shouldCollide:2') + if (this.mContactFilter !== null && this.mContactFilter.shouldCollide(fixtureA, fixtureB) === false) { + return; + } + console.info('box2d','shouldCollide:3') + let c = B2Contact.create(fixtureA, indexA, fixtureB, indexB); + if (c === null) { + return; + } + console.info('box2d','shouldCollide:4') + fixtureA = c.fixtureA; + fixtureB = c.fixtureB; + indexA = c.childIndexA; + indexB = c.childIndexB; + bodyA = fixtureA.body; + bodyB = fixtureB.body; + c.mPrev = null; + c.mNext = this.mContactList; + if (this.mContactList !== null) { + this.mContactList.mPrev = c; + } + this.mContactList = c; + c.mNodeA.contact = c!; + c.mNodeA.other = bodyB; + c.mNodeA.prev = null; + c.mNodeA.next = bodyA.mContactList; + if (bodyA.mContactList !== null) { + bodyA.mContactList.prev = c.mNodeA; + } + bodyA.mContactList = c.mNodeA; + c.mNodeB.contact = c!; + c.mNodeB.other = bodyA; + c.mNodeB.prev = null; + c.mNodeB.next = bodyB.mContactList; + if (bodyB.mContactList !== null) { + bodyB.mContactList.prev = c.mNodeB; + } + bodyB.mContactList = c.mNodeB; + if (fixtureA.isSensor === false && fixtureB.isSensor === false) { + bodyA.setAwake(true); + bodyB.setAwake(true); + } + this.mContactCount += 1; + } + + findNewContacts(): void { + this.mBroadPhase.updatePairs(this); + } + + destroy(c: B2Contact): void { + let fixtureA = c.fixtureA; + let fixtureB = c.fixtureB; + let bodyA = fixtureA.body; + let bodyB = fixtureB.body; + if (this.mContactListener !== null && c.isTouching) { + this.mContactListener.endContact(c); + } + if (c.mPrev !== null) { + c.mPrev.mNext = c.mNext; + } + if (c.mNext !== null) { + c.mNext.mPrev = c.mPrev; + } + if (c === this.mContactList) { + this.mContactList = c.mNext; + } + if (c.mNodeA.prev !== null) { + c.mNodeA.prev.next = c.mNodeA.next; + } + if (c.mNodeA.next !== null) { + c.mNodeA.next.prev = c.mNodeA.prev; + } + if (c.mNodeA === bodyA.mContactList) { + bodyA.mContactList = c.mNodeA.next; + } + if (c.mNodeB.prev !== null) { + c.mNodeB.prev.next = c.mNodeB.next; + } + if (c.mNodeB.next !== null) { + c.mNodeB.next.prev = c.mNodeB.prev; + } + if (c.mNodeB === bodyB.mContactList) { + bodyB.mContactList = c.mNodeB.next; + } + B2Contact.destroy(c); + this.mContactCount -= 1; + } + + collide(): void { + let c = this.mContactList; + console.info('box2d','000') + while (c !== null) { + console.info('box2d','111') + let fixtureA = c.fixtureA; + let fixtureB = c.fixtureB; + let indexA = c.childIndexA; + let indexB = c.childIndexB; + let bodyA = fixtureA.body; + let bodyB = fixtureB.body; + if ((c.mFlags & FlagContacts.FILTERFLAG) !== 0) { + console.info('box2d','222') + if (bodyB.shouldCollide(bodyA) === false) { + console.info('box2d','333') + let cNuke = c!; + c = cNuke.getNext(); + this.destroy(cNuke); + continue; + } + if (this.mContactFilter !== null && this.mContactFilter.shouldCollide(fixtureA, fixtureB) === false) { + console.info('box2d','444') + let cNuke = c!; + c = cNuke.getNext(); + this.destroy(cNuke); + continue; + } + c.mFlags &= ~FlagContacts.FILTERFLAG; + } + console.info('box2d','555') + let activeA = bodyA.isAwake && bodyA.mType !== B2BodyType.STATICBODY; + let activeB = bodyB.isAwake && bodyB.mType !== B2BodyType.STATICBODY; + if (activeA === false && activeB === false) { + console.info('box2d','666') + c = c.getNext(); + continue; + } + console.info('box2d','777') + let proxyIdA = fixtureA.mProxies[indexA].proxyId; + let proxyIdB = fixtureB.mProxies[indexB].proxyId; + let overlap = this.mBroadPhase.testOverlap(proxyIdA, proxyIdB); + if (overlap === false) { + console.info('box2d','888') + let cNuke = c!; + c = cNuke.getNext(); + this.destroy(cNuke); + continue; + } + console.info('box2d','999') + c.update(this.mContactListener!); + c = c.getNext(); + } + } + mBroadPhase = new B2BroadPhase(); + public broadPhase(): B2BroadPhase { + return this.mBroadPhase; + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Fixture.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Fixture.ts new file mode 100644 index 0000000000000000000000000000000000000000..93a858cc773f33e63b798c2e1a68e7d372e2f795 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Fixture.ts @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Shape } from '../Collision/Shapes/b2Shape'; +import { B2RayCastOutput, B2RayCastInput } from '../Collision/b2Collision'; +import { B2AaBb } from '../Collision/b2Collision'; +import { B2ShapeType, B2MassData } from '../Collision/Shapes/b2Shape'; +import { B2Vec2, B2Transform } from '../Common/b2Math'; +import { subtract } from '../Common/b2Math'; +import { B2BroadPhase } from '../Collision/b2BroadPhase'; +import { B2Body } from './b2Body'; + +export class B2Filter { + constructor() { + this.categoryBits = 0x0001; + this.maskBits = 0xffff; + this.groupIndex = 0; + } + public categoryBits: number; + public maskBits: number; + public groupIndex: number; +} +export class B2FixtureDef { + constructor() { + this.shape = null; + this.userData = null; + this.friction = 0.2; + this.restitution = 0.0; + this.density = 0.0; + this.isSensor = false; + this.filter = new B2Filter(); + } + shape: B2Shape | null; + userData?: object | null; + friction: number; + restitution: number; + density: number; + isSensor: boolean; + filter: B2Filter; +} + +export class B2FixtureProxy { + constructor(fixture: B2Fixture) { + this.fixture = fixture; + } + aabb = new B2AaBb(); + fixture: B2Fixture; + childIndex = 0; + proxyId = 0; +} + +export class B2Fixture { + mDensity: number = 0.0; + mNext: B2Fixture | null = null; + mBody: B2Body; + mShape: B2Shape | null = null; + mFriction: number = 0.0; + mRestitution: number = 0.0; + mProxies: B2FixtureProxy[] = []; + mFilter = new B2Filter(); + mIsSensor: boolean = false; + mUserData?: object | null = null; + + constructor(body: B2Body, def: B2FixtureDef) { + this.mUserData = def.userData; + this.mFriction = def.friction; + this.mRestitution = def.restitution; + this.mBody = body; + this.mNext = null; + this.mFilter = def.filter; + this.mIsSensor = def.isSensor; + this.mShape = def.shape!.clone(); + this.mProxies = []; + this.mDensity = def.density; + } + + get mProxyCount(): number { + return this.mProxies.length; + } + + get fixtureType(): B2ShapeType { + return this.mShape!.type(); + } + + get shape(): B2Shape { + return this.mShape!; + } + + get isSensor(): boolean { + return this.mIsSensor; + } + + set isSensor(newValue) { + this.setSensor(newValue); + } + + setSensor(sensor: boolean): void { + if (sensor !== this.mIsSensor) { + this.mBody.setAwake(true); + this.mIsSensor = sensor; + } + } + + get filterData(): B2Filter { + return this.mFilter; + } + + set filterData(newValue) { + this.setFilterData(newValue); + } + + setFilterData(filter: B2Filter): void { + this.mFilter = filter; + this.refilter(); + } + + refilter(): void { + let edge = this.mBody.getContactList(); + while (edge !== null) { + let contact = edge!.contact; + let fixtureA = contact.fixtureA; + let fixtureB = contact.fixtureB; + if (fixtureA === this || fixtureB === this) { + contact.flagForFiltering(); + } + edge = edge!.next; + } + let world = this.mBody.world; + if (world === null) { + return; + } + let broadPhase = world.mContactManager.mBroadPhase; + for (let i = 0; i < this.mProxyCount; i++) { + broadPhase.touchProxy(this.mProxies[i].proxyId); + } + } + + get body(): B2Body { + return this.mBody; + } + + getNext(): B2Fixture | undefined { + if (this.mNext !== null) { + return this.mNext; + } + return undefined; + } + + get userData(): object | undefined { + if (this.mUserData !== null) { + return this.mUserData; + } + return undefined; + } + + set userData(newValue) { + this.setUserData(newValue); + } + + setUserData(data?: object): void { + this.mUserData = data; + } + + testPoint(p: B2Vec2): boolean { + return this.mShape!.testPoint(this.mBody.transform, p); + } + + rayCast(output: B2RayCastOutput, input: B2RayCastInput, childIndex: number): boolean { + return this.mShape!.rayCast(output, input, this.mBody.transform, childIndex); + } + + get massData(): B2MassData { + return this.mShape!.computeMass(this.mDensity); + } + + get density(): number { + return this.mDensity; + } + + set density(newValue) { + this.setDensity(newValue); + } + + setDensity(density: number): void { + this.mDensity = density; + } + + get friction(): number { + return this.mFriction; + } + + set friction(newValue) { + this.setFriction(newValue); + } + + setFriction(friction: number): void { + this.mFriction = friction; + } + + get restitution(): number { + return this.mRestitution; + } + + set restitution(newValue) { + this.setRestitution(newValue); + } + + setRestitution(restitution: number): void { + this.mRestitution = restitution; + } + + getAaBb(childIndex: number): B2AaBb { + return this.mProxies[childIndex].aabb; + } + + destroy(): void { + this.mProxies.splice(0, this.mProxies.length); + this.mShape = null; + } + + createProxies(broadPhase: B2BroadPhase, xf: B2Transform): void { + if (this.mShape !== null) { + let proxyCount = this.mShape.childCount(); + for (let i = 0; i < proxyCount; i++) { + let proxy = new B2FixtureProxy(this); + this.mShape!.computeAaBb(proxy.aabb, xf, i); + proxy.childIndex = i; + proxy.proxyId = broadPhase.createProxy(proxy.aabb, proxy); + this.mProxies.push(proxy); + } + } + } + + destroyProxies(broadPhase: B2BroadPhase): void { + for (let i = 0; i < this.mProxyCount; i++) { + let proxy = this.mProxies[i]; + broadPhase.destroyProxy(proxy.proxyId); + } + this.mProxies.splice(0, this.mProxies.length); + } + + synchronize(broadPhase: B2BroadPhase, transform1: B2Transform, transform2: B2Transform): void { + if (this.mProxyCount === 0) { + return; + } + for (let i = 0; i < this.mProxyCount; i++) { + let proxy = this.mProxies[i]; + let aabb1 = new B2AaBb(); + let aabb2 = new B2AaBb(); + this.mShape!.computeAaBb(aabb1, transform1, proxy.childIndex); + this.mShape!.computeAaBb(aabb2, transform2, proxy.childIndex); + //proxy.aabb.combine(aabb1, aabb2); + let combinedAabb = new B2AaBb(); // 创建一个新的 AABB 对象 + combinedAabb.combine(aabb1, aabb2); // 计算组合后的 AABB + let displacement = subtract(transform2.p, transform1.p); + broadPhase.moveProxy(proxy.proxyId, combinedAabb, displacement); + } + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Island.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Island.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c2d74f19fb083898a5d274a73039df35bd5a79e --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2Island.ts @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Vec2 } from '../Common/b2Math'; +import { b2Dot22, multM, add, addEqual, mulEqual } from '../Common/b2Math'; +import { B2Contact } from './Contacts/b2Contact'; +import { B2ContactVelocityConstraint } from './Contacts/b2ContactSolver'; +import { B2ContactSolver, B2ContactSolverDef } from './Contacts/b2ContactSolver'; +import { B2Joint } from './Joints/b2Joint'; +import { B2Body } from './b2Body'; +import { B2BodyType, Flags } from './b2Body'; +import { B2TimeStep } from './b2TimeStep'; +import { B2Array, B2Position, B2SolverData, B2Velocity } from './b2TimeStep'; +import { B2ContactListener } from './b2WorldCallbacks'; +import { B2ContactImpulse } from './b2WorldCallbacks'; +import { + b2AngularSleepTolerance, + b2LinearSleepTolerance, + b2MaxFloat, + b2MaxRotation, + b2MaxRotationSquared, + b2MaxTranslation, + b2MaxTranslationSquared, + b2TimeToSleep +} from '../Common/b2Settings'; + +export class B2Island { + mListener: B2ContactListener; + mBodies: Array; + mContacts: Array; + mJoints: Array; + mPositions: B2Array; + mVelocities: B2Array; + get mBodyCount(): number { + return this.mBodies.length; + } + get mJointCount(): number { + return this.mJoints.length; + } + get mContactCount(): number { + return this.mContacts.length; + } + mBodyCapacity: number; + mContactCapacity: number; + mJointCapacity: number; + constructor(bodyCapacity: number, contactCapacity: number, jointCapacity: number, listener: B2ContactListener) { + this.mBodyCapacity = bodyCapacity; + this.mContactCapacity = contactCapacity; + this.mJointCapacity = jointCapacity; + this.mListener = listener; + this.mBodies = Array(); + this.mContacts = Array(); + this.mJoints = Array(); + this.mVelocities = new B2Array(); + this.mPositions = new B2Array(); + } + + reset(bodyCapacity: number, contactCapacity: number, jointCapacity: number, listener: B2ContactListener): void { + this.mBodyCapacity = bodyCapacity; + this.mContactCapacity = contactCapacity; + this.mJointCapacity = jointCapacity; + this.mListener = listener; + this.mBodies.splice(0, this.mBodies.length); + this.mContacts.splice(0, this.mContacts.length); + this.mJoints.splice(0, this.mJoints.length); + this.mVelocities.removeAll(); + this.mPositions.removeAll(); + } + + clear(): void { + this.mBodies.splice(0, this.mBodies.length); + this.mContacts.splice(0, this.mContacts.length); + this.mJoints.splice(0, this.mJoints.length); + } + + solve(step: B2TimeStep, gravity: B2Vec2, allowSleep: boolean): void { + let h = step.dt; + this.mPositions.removeAll(); + this.mVelocities.removeAll(); + for (let i = 0; i < this.mBodyCount; i++) { + let b = this.mBodies[i]; + let c = b.mSweep.c; + let a = b.mSweep.a; + let v = b.mLinearVelocity; + let w = b.mAngularVelocity; + b.mSweep.c0 = b.mSweep.c; + b.mSweep.a0 = b.mSweep.a; + if (b.mType === B2BodyType.DYNAMICBODY) { + addEqual(v, multM(add(multM(gravity, b.mGravityScale), multM(b.mForce, b.mInvMass)), h)); + w += h * b.mInvI * b.mTorque; + mulEqual(v, 1.0 / (1.0 + h * b.mLinearDamping)); + w *= 1.0 / (1.0 + h * b.mAngularDamping); + } + this.mPositions.append(new B2Position(c, a)); + this.mVelocities.append(new B2Velocity(v, w)); + } + let solverData = new B2SolverData(); + solverData.step = step; + solverData.positions = this.mPositions; + solverData.velocities = this.mVelocities; + let contactSolverDef = new B2ContactSolverDef(); + contactSolverDef.step = step; + contactSolverDef.contacts = this.mContacts; + contactSolverDef.count = this.mContactCount; + contactSolverDef.positions = this.mPositions; + contactSolverDef.velocities = this.mVelocities; + let contactSolver = new B2ContactSolver(contactSolverDef); + contactSolver.initializeVelocityConstraints(); + if (step.warmStarting) { + contactSolver.warmStart(); + } + for (let i = 0; i < this.mJointCount; i++) { + this.mJoints[i].initVelocityConstraints(solverData); + } + for (let i = 0; i < step.velocityIterations; i++) { + for (let j = 0; j < this.mJointCount; j++) { + this.mJoints[j].solveVelocityConstraints(solverData); + } + contactSolver.solveVelocityConstraints(); + } + contactSolver.storeImpulses(); + for (let i = 0; i < this.mBodyCount; i++) { + let c: B2Vec2 = this.mPositions.get(i).c; + let a: number = this.mPositions.get(i).a; + let v: B2Vec2 = this.mVelocities.get(i).v; + let w: number = this.mVelocities.get(i).w; + let translation = multM(v, h); + if (b2Dot22(translation, translation) > b2MaxTranslationSquared) { + let ratio = b2MaxTranslation / translation.length(); + mulEqual(v, ratio); + } + let rotation = h * w; + if (rotation * rotation > b2MaxRotationSquared) { + let ratio = b2MaxRotation / Math.abs(rotation); + w *= ratio; + } + c = add(c, multM(v, h)); + a += h * w; + this.mPositions.get(i).c = c; + this.mPositions.get(i).a = a; + this.mVelocities.get(i).v = v; + this.mVelocities.get(i).w = w; + let b = this.mBodies[i]; + b.mSweep.c0.set(b.mSweep.c.x, b.mSweep.c.y); + b.mSweep.a0 = b.mSweep.a; + b.mSweep.c.x += step.dt * b.mLinearVelocity.x; + b.mSweep.c.y += step.dt * b.linearVelocity.y; + b.mSweep.a += step.dt * b.angularVelocity; + b.synchronizeTransform(); + } + let positionSolved = false; + for (let i = 0; i < step.positionIterations; i++) { + let contactsOkay = contactSolver.solvePositionConstraints(); + let jointsOkay = true; + for (let i2 = 0; i2 < this.mJointCount; i2++) { + let jointOkay = this.mJoints[i2].solvePositionConstraints(solverData); + jointsOkay = jointsOkay && jointOkay; + } + if (contactsOkay && jointsOkay) { + positionSolved = true; + break; + } + } + for (let i = 0; i < this.mBodyCount; i++) { + let body = this.mBodies[i]; + body.mSweep.c = this.mPositions.get(i).c; + body.mSweep.a = this.mPositions.get(i).a; + body.mLinearVelocity = this.mVelocities.get(i).v; + body.mAngularVelocity = this.mVelocities.get(i).w; + body.synchronizeTransform(); + } + this.report(contactSolver.mVelocityConstraints); + if (allowSleep) { + let minSleepTime = b2MaxFloat; + let linTolSqr = b2LinearSleepTolerance * b2LinearSleepTolerance; + let angTolSqr = b2AngularSleepTolerance * b2AngularSleepTolerance; + for (let i = 0; i < this.mBodyCount; i++) { + let b = this.mBodies[i]; + if (b.typeBody === B2BodyType.STATICBODY) { + continue; + } + let ats = b.mAngularVelocity * b.mAngularVelocity > angTolSqr; + let lts = b2Dot22(b.mLinearVelocity, b.mLinearVelocity) > linTolSqr; + if ((b.mFlags & Flags.autoSleepFlag) === 0 || ats || lts) { + b.mSleepTime = 0.0; + minSleepTime = 0.0; + } else { + b.mSleepTime += h; + minSleepTime = Math.min(minSleepTime, b.mSleepTime); + } + } + if (minSleepTime >= b2TimeToSleep && positionSolved) { + for (let i = 0; i < this.mBodyCount; i++) { + let b = this.mBodies[i]; + b.setAwake(false); + } + } + } + } + + solveToi(subStep: B2TimeStep, toiIndexA: number, toiIndexB: number): void { + this.mPositions.removeAll(); + this.mVelocities.removeAll(); + for (let i = 0; i < this.mBodyCount; i++) { + let b = this.mBodies[i]; + this.mPositions.append(new B2Position(b.mSweep.c, b.mSweep.a)); + this.mVelocities.append(new B2Velocity(b.mLinearVelocity, b.mAngularVelocity)); + } + let contactSolverDef = new B2ContactSolverDef(); + contactSolverDef.contacts = this.mContacts; + contactSolverDef.count = this.mContactCount; + contactSolverDef.step = subStep; + contactSolverDef.positions = this.mPositions; + contactSolverDef.velocities = this.mVelocities; + let contactSolver = new B2ContactSolver(contactSolverDef); + for (let i = 0; i < subStep.positionIterations; i++) { + let contactsOkay = contactSolver.solveToiPositionConstraints(toiIndexA, toiIndexB); + if (contactsOkay) { + break; + } + } + this.mBodies[toiIndexA].mSweep.c0 = this.mPositions.get(toiIndexA).c; + this.mBodies[toiIndexA].mSweep.a0 = this.mPositions.get(toiIndexA).a; + this.mBodies[toiIndexB].mSweep.c0 = this.mPositions.get(toiIndexB).c; + this.mBodies[toiIndexB].mSweep.a0 = this.mPositions.get(toiIndexB).a; + contactSolver.initializeVelocityConstraints(); + for (let i = 0; i < subStep.velocityIterations; i++) { + contactSolver.solveVelocityConstraints(); + } + let h = subStep.dt; + for (let i = 0; i < this.mBodyCount; i++) { + let c: B2Vec2 = this.mPositions.get(i).c; + let a: number = this.mPositions.get(i).a; + let v: B2Vec2 = this.mVelocities.get(i).v; + let w: number = this.mVelocities.get(i).w; + let translation = multM(v, h); + if (b2Dot22(translation, translation) > b2MaxTranslationSquared) { + let ratio = b2MaxTranslation / translation.length(); + mulEqual(v, ratio); + } + let rotation = h * w; + if (rotation * rotation > b2MaxRotationSquared) { + let ratio = b2MaxRotation / Math.abs(rotation); + w *= ratio; + } + addEqual(c, multM(v, h)); + a += h * w; + this.mPositions.get(i).c = c; + this.mPositions.get(i).a = a; + this.mVelocities.get(i).v = v; + this.mVelocities.get(i).w = w; + let body = this.mBodies[i]; + body.mSweep.c = c; + body.mSweep.a = a; + body.mLinearVelocity = v; + body.mAngularVelocity = w; + body.synchronizeTransform(); + } + this.report(contactSolver.mVelocityConstraints); + } + + addB(body: B2Body): void { + body.mIslandIndex = this.mBodyCount; + this.mBodies.push(body); + } + + addC(contact: B2Contact): void { + this.mContacts.push(contact); + } + + addJ(joint: B2Joint): void { + this.mJoints.push(joint); + } + report(constraints: Array): void { + if (this.mListener === null) { + return; + } + for (let i = 0; i < this.mContactCount; i++) { + let c = this.mContacts[i]; + let vc = constraints[i]; + let impulse = new B2ContactImpulse(); + impulse.count = vc.pointCount; + for (let j = 0; j < vc.pointCount; j++) { + impulse.normalImpulses[j] = vc.points[j].normalImpulse; + impulse.tangentImpulses[j] = vc.points[j].tangentImpulse; + } + this.mListener?.postSolve(c, impulse); + } + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2TimeStep.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2TimeStep.ts new file mode 100644 index 0000000000000000000000000000000000000000..478b687c517747431fd95f1be02ca5908a67bdf7 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2TimeStep.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Vec2 } from '../Common/b2Math'; + +export class B2Profile { + public step: number = 0.0; + public collide: number = 0.0; + public solve: number = 0.0; + public solveInit: number = 0.0; + public solveVelocity: number = 0.0; + public solvePosition: number = 0.0; + public broadPhase: number = 0.0; + public solveToi: number = 0.0; + constructor() {} +} + +export class B2TimeStep { + public dt: number = 0.0; + public invDt: number = 0.0; + public dtRatio: number = 0.0; + public velocityIterations: number = 0; + public positionIterations: number = 0; + public warmStarting: boolean = false; +} + +export class B2Array { + array: T[] = []; + constructor(count: number | null = null, repeatedValue: T | null = null) { + if (count !== null && repeatedValue !== null) { + for (let i = 0; i < count; i++) { + this.array.push(repeatedValue); + } + } + } + public append(newElement: T): void { + this.array.push(newElement); + } + public insert(newElement: T, i: number): void { + this.array.splice(i, 0, newElement); + } + public removeAtIndex(index: number): T[] { + return this.array.splice(index, 1); + } + public removeLast(): void { + this.array.pop(); + } + public removeAll(): void { + this.array.splice(0, this.array.length); + } + public get(index: number): T { + return this.array[index]; + } + public set(index: number, newValue: T): void { + this.array[index] = newValue; + } + public clone(): B2Array { + let clone = new B2Array(); + for (let e of this.array) { + clone.array.push(e); + } + return clone; + } + public get count(): number { + return this.array.length; + } +} + +export class B2Position { + constructor(c: B2Vec2, a: number) { + this.c = c; + this.a = a; + } + c: B2Vec2; + a: number; +} +export class B2Velocity { + constructor(v: B2Vec2, w: number) { + this.v = v; + this.w = w; + } + v: B2Vec2; + w: number; +} +export class B2SolverData { + step = new B2TimeStep(); + positions = new B2Array(); + velocities = new B2Array(); +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2World.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2World.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ed8e57703927b090cc9c7aab3b53780d488cb27 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2World.ts @@ -0,0 +1,745 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2BroadPhase } from '../Collision/b2BroadPhase'; +import { B2AaBb } from '../Collision/b2Collision'; +import { B2RayCastInput, B2RayCastOutput } from '../Collision/b2Collision'; +import { B2ToiInput, B2ToiOutput, b2TimeOfImpact, State } from '../Collision/b2TimeOfImpact'; +import { B2Vec2 } from '../Common/b2Math'; +import { multM, add, subtractEqual } from '../Common/b2Math'; +import { B2QueryWrapper, B2RayCastWrapper } from '../Common/b2Wrappers'; +import { B2Island } from './b2Island'; +import { B2BodyDef } from './b2Body'; +import { Flags, B2Body, B2BodyType } from './b2Body'; +import { B2Joint } from './Joints/b2Joint'; +import { B2TimeStep } from './b2TimeStep'; +import { + B2QueryCallbackFunction, + B2ContactFilter, + B2RayCastCallback, + B2DestructionListener, + B2RayCastCallbackFunction, + B2ContactListener, + B2QueryCallback +} from './b2WorldCallbacks'; +import { B2QueryCallbackProxy, B2RayCastCallbackProxy } from './b2WorldCallbacks'; +import { B2Contact } from './Contacts/b2Contact'; +import { FlagContacts } from './Contacts/b2Contact'; +import { B2Profile } from './b2TimeStep'; +import { B2ContactManager } from './b2ContactManager'; +import { b2Epsilon, b2MaxSubSteps, b2MaxTOIContacts, hex2, hex4, float10, indexTwo, posIter, hex1 } from '../Common/b2Settings'; + +export class B2World { + mIsland: B2Island | null = null; + mToiIsland: B2Island | null = null; + mFlags = FlagsW.clearForces; + mContactManager: B2ContactManager = new B2ContactManager(); + mBodyList: B2Body | null = null; + mJointList: B2Joint | null = null; + mBodyCount: number = 0; + mJointCount: number = 0; + mGravity: B2Vec2; + mAllowSleep: boolean = true; + mDestructionListener: B2DestructionListener | null = null; + mInvDt0: number = 0.0; + mWarmStarting: boolean = true; + mContinuousPhysics: boolean = true; + mSubStepping: boolean = false; + mStepComplete: boolean = true; + mProfile: B2Profile = new B2Profile(); + constructor(gravity: B2Vec2) { + this.mDestructionListener = null; + this.mBodyList = null; + this.mJointList = null; + this.mBodyCount = 0; + this.mJointCount = 0; + this.mWarmStarting = true; + this.mContinuousPhysics = true; + this.mSubStepping = false; + this.mStepComplete = true; + this.mAllowSleep = true; + this.mGravity = gravity; + this.mFlags = FlagsW.clearForces; + this.mInvDt0 = 0.0; + this.mContactManager = new B2ContactManager(); + } + + setDestructionListener(listener: B2DestructionListener): void { + this.mDestructionListener = listener; + } + + setContactFilter(filter: B2ContactFilter): void { + this.mContactManager.mContactFilter = filter; + } + + setContactListener(listener: B2ContactListener): void { + this.mContactManager.mContactListener = listener; + } + + public createBody(def: B2BodyDef): B2Body { + if (this.isLocked) { + throw new Error('world is locked'); + } + let b = new B2Body(def, this); + b.mPrev = null; + b.mNext = this.mBodyList; + if (this.mBodyList !== null) { + this.mBodyList.mPrev = b; + } + this.mBodyList = b; + this.mBodyCount += 1; + return b; + } + + public destroyBody(b: B2Body): void { + if (this.isLocked) { + return; + } + let je = b.mJointList; + while (je !== null) { + let je0 = je!; + je = je.next; + if (this.mDestructionListener !== null) { + this.mDestructionListener.sayGoodbye(je0.joint); + } + b.mJointList = je; + } + b.mJointList = null; + let ce = b.mContactList; + while (ce !== null) { + let ce0 = ce!; + ce = ce.next; + this.mContactManager.destroy(ce0.contact); + } + b.mContactList = null; + let f = b.mFixtureList; + while (f !== null) { + let f0 = f!; + f = f.mNext!; + if (this.mDestructionListener !== null) { + this.mDestructionListener.sayGoodbye(f0); + } + f0.destroyProxies(this.mContactManager.mBroadPhase); + f0.destroy(); + b.mFixtureList = f; + b.mFixtureCount -= 1; + } + b.mFixtureList = null; + b.mFixtureCount = 0; + if (b.mPrev !== null) { + b.mPrev.mNext = b.mNext; + } + if (b.mNext !== null) { + b.mNext.mPrev = b.mPrev; + } + if (b === this.mBodyList) { + this.mBodyList = b.mNext; + } + this.mBodyCount -= 1; + } + + public step(dt: number, velocityIterations: number, positionIterations: number): void { + if ((this.mFlags & FlagsW.newFixture) !== 0) { + console.info('box2d','011') + this.mContactManager.findNewContacts(); + this.mFlags &= ~FlagsW.newFixture; + } + this.mFlags |= FlagsW.locked; + let step = new B2TimeStep(); + step.dt = dt; + step.velocityIterations = velocityIterations; + step.positionIterations = positionIterations; + if (dt > 0.0) { + step.invDt = 1.0 / dt; + } else { + step.invDt = 0.0; + } + step.dtRatio = this.mInvDt0 * dt; + step.warmStarting = this.mWarmStarting; + this.mContactManager.collide(); + if (this.mStepComplete && step.dt > 0.0) { + this.solve(step); + } + if (this.mContinuousPhysics && step.dt > 0.0) { + this.solveToi(step); + } + if (step.dt > 0.0) { + this.mInvDt0 = step.invDt; + } + if ((this.mFlags & FlagsW.clearForces) !== 0) { + this.clearForces(); + } + this.mFlags &= ~FlagsW.locked; + } + + public clearForces(): void { + let body = this.mBodyList; + while (body !== null) { + body.mForce.setZero(); + body.mTorque = 0.0; + body = body.getNext(); + } + } + + public queryAaBb(callback: B2QueryCallback, aabb: B2AaBb): void { + let wrapper = new B2WorldQueryWrapper(); + wrapper.broadPhase = this.mContactManager.mBroadPhase; + wrapper.callback = callback; + this.mContactManager.mBroadPhase.query(wrapper, aabb); + } + + public queryAaBb2(aabb: B2AaBb, callback: B2QueryCallbackFunction): void { + this.queryAaBb(new B2QueryCallbackProxy(callback), aabb); + } + + public rayCast(callback: B2RayCastCallback, point1: B2Vec2, point2: B2Vec2): void { + let wrapper = new B2WorldRayCastWrapper(); + wrapper.broadPhase = this.mContactManager.mBroadPhase; + wrapper.callback = callback; + let input = new B2RayCastInput(); + input.maxFraction = 1.0; + input.p1 = point1; + input.p2 = point2; + this.mContactManager.mBroadPhase.rayCast(wrapper, input); + } + + public rayCast2(point1: B2Vec2, point2: B2Vec2, callback: B2RayCastCallbackFunction): void { + this.rayCast(new B2RayCastCallbackProxy(callback), point1, point2); + } + + public getBodyList(): B2Body | null { + return this.mBodyList; + } + + public getJointList(): B2Joint | null { + return this.mJointList; + } + + public getContactList(): B2Contact | null { + return this.mContactManager.mContactList; + } + + public setAllowSleeping(flag: boolean): void { + if (flag === this.mAllowSleep) { + return; + } + this.mAllowSleep = flag; + if (this.mAllowSleep === false) { + let b = this.mBodyList; + while (b !== null) { + b.setAwake(true); + b = b.mNext; + } + } + } + + public get allowSleeping(): boolean { + return this.mAllowSleep; + } + + public set allowSleeping(newValue: boolean) { + this.setAllowSleeping(newValue); + } + + public setWarmStarting(flag: boolean): void { + this.mWarmStarting = flag; + } + + public get warmStarting(): boolean { + return this.mWarmStarting; + } + + public set warmStarting(newValue: boolean) { + this.setWarmStarting(newValue); + } + + public setContinuousPhysics(flag: boolean): void { + this.mContinuousPhysics = flag; + } + + public get continuousPhysics(): boolean { + return this.mContinuousPhysics; + } + + public set continuousPhysics(newValue: boolean) { + this.setContinuousPhysics(newValue); + } + + public setSubStepping(flag: boolean): void { + this.mSubStepping = flag; + } + + public get subStepping(): boolean { + return this.mSubStepping; + } + + public set subStepping(newValue: boolean) { + this.setSubStepping(newValue); + } + + public get proxyCount(): number { + return this.mContactManager.mBroadPhase.getProxyCount(); + } + + public get bodyCount(): number { + return this.mBodyCount; + } + + public get jointCount(): number { + return this.mJointCount; + } + + public get contactCount(): number { + return this.mContactManager.mContactCount; + } + + public get treeHeight(): number { + return this.mContactManager.mBroadPhase.getTreeHeight(); + } + + public get treeBalance(): number { + return this.mContactManager.mBroadPhase.getTreeBalance(); + } + + public get treeQuality(): number { + return this.mContactManager.mBroadPhase.getTreeQuality(); + } + + public setGravity(gravity: B2Vec2): void { + this.mGravity = gravity; + } + + public get gravity(): B2Vec2 { + return this.mGravity; + } + + public set gravity(newValue: B2Vec2) { + this.setGravity(newValue); + } + + public get isLocked(): boolean { + return (this.mFlags & FlagsW.locked) === FlagsW.locked; + } + + public setAutoClearForces(flag: boolean): void { + if (flag) { + this.mFlags |= FlagsW.clearForces; + } else { + this.mFlags &= ~FlagsW.clearForces; + } + } + + public get autoClearForces(): boolean { + return (this.mFlags & FlagsW.clearForces) === FlagsW.clearForces; + } + + public shiftOrigin(newOrigin: B2Vec2): void { + if ((this.mFlags & FlagsW.locked) === FlagsW.locked) { + return; + } + let b = this.mBodyList; + while (b !== null) { + subtractEqual(b.mXF.p, newOrigin); + subtractEqual(b.mSweep.c0, newOrigin); + subtractEqual(b.mSweep.c, newOrigin); + b = b.mNext; + } + let j = this.mJointList; + while (j !== null) { + j.shiftOrigin(newOrigin); + j = j.mNext; + } + this.mContactManager.mBroadPhase.shiftOrigin(newOrigin); + } + + public get contactManager(): B2ContactManager { + return this.mContactManager; + } + + public get profile(): B2Profile { + return this.mProfile; + } + + solve(step: B2TimeStep): void { + this.mProfile.solveInit = 0.0; + this.mProfile.solveVelocity = 0.0; + this.mProfile.solvePosition = 0.0; + + if (this.mIsland === null) { + this.mIsland = new B2Island(this.mBodyCount, this.mContactManager.mContactCount, this.mJointCount, this.mContactManager.mContactListener!); + } else { + this.mIsland.reset(this.mBodyCount, this.mContactManager.mContactCount, this.mJointCount, this.mContactManager.mContactListener!); + } + let island = this.mIsland; + let b = this.mBodyList; + while (b !== null) { + b.mFlags &= ~Flags.islandFlag; + b = b.mNext; + } + let c = this.mContactManager.mContactList; + while (c !== null) { + c.mFlags &= ~FlagContacts.ISLANDFLAG; + c = c.mNext; + } + let j = this.mJointList; + while (j !== null) { + j.mIslandFlag = false; + j = j.mNext; + } + let stack = new Array(); + let seed = this.mBodyList; + while (seed !== null) { + if ((seed.mFlags & Flags.islandFlag) !== 0) { + seed = seed.mNext; + continue; + } + if (seed!.isAwake === false || seed!.isActive === false) { + seed = seed!.mNext; + continue; + } + if (seed!.typeBody === B2BodyType.STATICBODY) { + seed = seed!.mNext; + continue; + } + island.clear(); + stack.push(seed!); + seed!.mFlags |= Flags.islandFlag; + while (stack.length > 0) { + let b = stack.pop(); + island.addB(b!); + b!.setAwake(true); + if (b!.typeBody === B2BodyType.STATICBODY) { + continue; + } + let ce = b!.mContactList; + while (ce !== null) { + let contact = ce!.contact; + if ((contact.mFlags & FlagContacts.ISLANDFLAG) !== 0) { + ce = ce!.next; + continue; + } + if (contact.isEnabled === false || contact.isTouching === false) { + ce = ce!.next; + continue; + } + let sensorA = contact.mFixtureA.mIsSensor; + let sensorB = contact.mFixtureB.mIsSensor; + if (sensorA || sensorB) { + ce = ce!.next; + continue; + } + island?.addC(contact); + contact.mFlags |= FlagContacts.ISLANDFLAG; + let other = ce!.other; + if ((other !== null) && (other.mFlags & Flags.islandFlag) !== 0) { + ce = ce!.next; + continue; + } + stack.push(other!); + if (other !== null) { + other.mFlags |= Flags.islandFlag; + } + ce = ce!.next; + } + let je = b!.mJointList; + while (je !== null) { + if (je!.joint.mIslandFlag === true) { + je = je!.next; + continue; + } + let other: B2Body = je.other!; + if (other.isActive === false) { + je = je.next; + continue; + } + island?.addJ(je.joint); + je!.joint.mIslandFlag = true; + if ((other.mFlags & Flags.islandFlag) !== 0) { + je = je!.next; + continue; + } + stack.push(other); + other.mFlags |= Flags.islandFlag; + } + } + island.solve(step, this.mGravity, this.mAllowSleep); + for (let i = 0; i < island.mBodyCount; i++) { + let b = island.mBodies[i]; + if (b.typeBody === B2BodyType.STATICBODY) { + b.mFlags &= ~Flags.islandFlag; + } + } + seed = seed!.mNext; + } + stack.splice(0, stack.length); + let locB = this.mBodyList; + while (locB !== null) { + if ((locB!.mFlags & Flags.islandFlag) === 0) { + locB = locB!.getNext(); + continue; + } + if (locB!.typeBody === B2BodyType.STATICBODY) { + locB = locB!.getNext(); + continue; + } + locB!.synchronizeFixtures(); + locB = locB!.getNext(); + } + this.mContactManager.findNewContacts(); + } + + solveToi(step: B2TimeStep): void { + if (this.mToiIsland === null) { + this.mToiIsland = new B2Island(indexTwo * b2MaxTOIContacts, b2MaxTOIContacts, 0, this.mContactManager.mContactListener!); + } else { + this.mToiIsland.reset(indexTwo * b2MaxTOIContacts, b2MaxTOIContacts, 0, this.mContactManager.mContactListener!); + } + let island = this.mToiIsland!; + if (this.mStepComplete) { + let b = this.mBodyList; + while (b !== null) { + b.mFlags &= ~Flags.islandFlag; + b.mSweep.alpha0 = 0.0; + b = b.mNext; + } + let c = this.mContactManager.mContactList; + while (c !== null) { + c.mFlags &= ~(FlagContacts.TOIFLAG | FlagContacts.ISLANDFLAG); + c.mToiCount = 0; + c.mToi = 1.0; + c = c.mNext; + } + } + while (true) { + let minContact: B2Contact | null = null; + let minAlpha: number = 1.0; + let c = this.mContactManager.mContactList; + while (c !== null) { + if (c.isEnabled === false) { + c = c!.mNext; + continue; + } + if (c.mToiCount > b2MaxSubSteps) { + c = c!.mNext; + continue; + } + let alpha: number = 1.0; + if ((c!.mFlags & FlagContacts.TOIFLAG) !== 0) { + alpha = c!.mToi; + } else { + let fA = c!.fixtureA; + let fB = c!.fixtureB; + if (fA.isSensor || fB.isSensor) { + c = c!.mNext; + continue; + } + let bA = fA.body; + let bB = fB.body; + let typeA = bA.mType; + let typeB = bB.mType; + let activeA = bA.isAwake && typeA !== B2BodyType.STATICBODY; + let activeB = bB.isAwake && typeB !== B2BodyType.STATICBODY; + if (activeA === false && activeB === false) { + c = c!.mNext; + continue; + } + let collideA = bA.isBullet || typeA !== B2BodyType.DYNAMICBODY; + let collideB = bB.isBullet || typeB !== B2BodyType.DYNAMICBODY; + if (collideA === false && collideB === false) { + c = c!.mNext; + continue; + } + let alpha0 = bA.mSweep.alpha0; + if (bA.mSweep.alpha0 < bB.mSweep.alpha0) { + alpha0 = bB.mSweep.alpha0; + bA.mSweep.advance(alpha0); + } else if (bB.mSweep.alpha0 < bA.mSweep.alpha0) { + alpha0 = bA.mSweep.alpha0; + bB.mSweep.advance(alpha0); + } + let input = new B2ToiInput(); + input.proxyA.set(fA.shape!); + input.proxyB.set(fB.shape!); + input.sweepA = bA.mSweep; + input.sweepB = bB.mSweep; + input.tMax = 1.0; + let output = new B2ToiOutput(); + b2TimeOfImpact(output, input); + let beta = output.t; + if (output.state === State.TOUCHING) { + alpha = Math.min(alpha0 + (1.0 - alpha0) * beta, 1.0); + } else { + alpha = 1.0; + } + c!.mToi = alpha; + c!.mFlags |= FlagContacts.TOIFLAG; + } + if (alpha < minAlpha) { + minContact = c; + minAlpha = alpha; + } + c = c!.mNext; + } + if (minContact === null || 1.0 - float10 * b2Epsilon < minAlpha) { + this.mStepComplete = true; + break; + } + let fA = minContact!.fixtureA; + let fB = minContact!.fixtureB; + let bA = fA.body; + let bB = fB.body; + let backup1 = bA.mSweep; + let backup2 = bB.mSweep; + bA.advance(minAlpha); + bB.advance(minAlpha); + minContact!.update(this.mContactManager.mContactListener!); + minContact!.mFlags &= ~FlagContacts.TOIFLAG; + minContact!.mToiCount += 1; + if (minContact.isEnabled === false || minContact.isTouching === false) { + minContact!.setEnabled(false); + bA.mSweep = backup1; + bB.mSweep = backup2; + bA.synchronizeTransform(); + bB.synchronizeTransform(); + continue; + } + bA.setAwake(true); + bB.setAwake(true); + island.clear(); + island.addB(bA); + island.addB(bB); + island.addC(minContact); + bA.mFlags |= Flags.islandFlag; + bB.mFlags |= Flags.islandFlag; + minContact.mFlags |= FlagContacts.ISLANDFLAG; + let bodies = [bA, bB]; + for (let i = 0; i < indexTwo; i++) { + let body = bodies[i]; + if (body.mType === B2BodyType.DYNAMICBODY) { + let ce = body.mContactList; + while (ce !== null) { + if (island.mBodyCount === island.mBodyCapacity) { + break; + } + if (island.mContactCount === island.mContactCapacity) { + break; + } + let contact = ce!.contact; + if ((contact.mFlags & FlagContacts.ISLANDFLAG) !== 0) { + ce = ce!.next; + continue; + } + let other = ce!.other!; + if (other.mType === B2BodyType.DYNAMICBODY && body.isBullet === false && other.isBullet === false) { + ce = ce!.next; + continue; + } + let sensorA = contact.mFixtureA.mIsSensor; + let sensorB = contact.mFixtureB.mIsSensor; + if (sensorA || sensorB) { + ce = ce!.next; + continue; + } + let backup = other.mSweep; + if ((other.mFlags & Flags.islandFlag) === 0) { + other.advance(minAlpha); + } + contact.update(this.mContactManager.mContactListener!); + if (contact.isEnabled === false) { + other.mSweep = backup; + other.synchronizeTransform(); + ce = ce!.next; + continue; + } + if (contact.isTouching === false) { + other.mSweep = backup; + other.synchronizeTransform(); + ce = ce!.next; + continue; + } + contact.mFlags |= FlagContacts.ISLANDFLAG; + island.addC(contact); + if ((other.mFlags & Flags.islandFlag) !== 0) { + ce = ce!.next; + continue; + } + other.mFlags |= Flags.islandFlag; + if (other.mType !== B2BodyType.STATICBODY) { + other.setAwake(true); + } + island.addB(other); + ce = ce!.next; + } + } + } + let subStep = new B2TimeStep(); + subStep.dt = (1.0 - minAlpha) * step.dt; + subStep.invDt = 1.0 / subStep.dt; + subStep.dtRatio = 1.0; + subStep.positionIterations = posIter; + subStep.velocityIterations = step.velocityIterations; + subStep.warmStarting = false; + island.solveToi(subStep, bA.mIslandIndex, bB.mIslandIndex); + for (let i = 0; i < island.mBodyCount; i++) { + let body = island.mBodies[i]; + body.mFlags &= ~Flags.islandFlag; + if (body.mType !== B2BodyType.DYNAMICBODY) { + continue; + } + body.synchronizeFixtures(); + let ce = body.mContactList; + while (ce !== null) { + ce!.contact.mFlags &= ~(FlagContacts.TOIFLAG | FlagContacts.ISLANDFLAG); + ce = ce!.next; + } + } + this.mContactManager.findNewContacts(); + if (this.mSubStepping) { + this.mStepComplete = false; + break; + } + } + } +} + +export class FlagsW { + static newFixture: number = hex1; + static locked: number = hex2; + static clearForces: number = hex4; +} +export class B2WorldQueryWrapper implements B2QueryWrapper { + broadPhase: B2BroadPhase | null = null; + callback: B2QueryCallback | null = null; + queryCallback(proxyId: number): boolean { + let proxy = this.broadPhase?.getUserData(proxyId)!; + return this.callback!.reportFixture(proxy.fixture); + } +} +export class B2WorldRayCastWrapper implements B2RayCastWrapper { + broadPhase: B2BroadPhase | null = null; + callback: B2RayCastCallback | null = null; + rayCastCallback(input: B2RayCastInput, proxyId: number): number { + let proxy = this.broadPhase?.getUserData(proxyId)!; + let fixture = proxy.fixture; + let index = proxy.childIndex; + let output = new B2RayCastOutput(); + let hit = fixture.rayCast(output, input, index); + if (hit) { + let fraction = output.fraction; + let point = add(multM(input.p1, 1.0 - fraction), multM(input.p2, fraction)); + return this.callback!.reportFixture(fixture, point, output.normal, fraction); + } + return input.maxFraction; + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2WorldCallbacks.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2WorldCallbacks.ts new file mode 100644 index 0000000000000000000000000000000000000000..e14bb74ab25e57aaa7b97d9c8f40b0404756dce3 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Dynamics/b2WorldCallbacks.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Vec2 } from '../Common/b2Math'; +import { B2Joint } from './Joints/b2Joint'; +import { B2Manifold } from '../Collision/b2Collision'; +import { B2Contact } from './Contacts/b2Contact'; +import { B2Fixture } from './b2Fixture'; +import { b2MaxManifoldPoints } from '../Common/b2Settings'; + +interface B2DestructionListener { + sayGoodbye(joint: B2Joint): void; + sayGoodbye(fixture: B2Fixture): void; +} + +export class B2ContactFilter { + shouldCollide(fixtureA: B2Fixture, fixtureB: B2Fixture): boolean { + let filterA = fixtureA.filterData; + let filterB = fixtureB.filterData; + if (filterA.groupIndex === filterB.groupIndex && filterA.groupIndex !== 0) { + return filterA.groupIndex > 0; + } + let collide = (filterA.maskBits & filterB.categoryBits) !== 0 && (filterA.categoryBits & filterB.maskBits) !== 0; + return collide; + } +} + +export class B2ContactImpulse { + public normalImpulses: Array; + public tangentImpulses: Array; + public count: number = 0; + + constructor() { + this.normalImpulses = []; + this.tangentImpulses = []; + for (let i = 0; i < b2MaxManifoldPoints; i++) { + this.normalImpulses.push(0); + this.tangentImpulses.push(0); + } + } +} + +interface B2ContactListener { + beginContact(contact: B2Contact): void; + endContact(contact: B2Contact): void; + preSolve(contact: B2Contact, oldManifold: B2Manifold): void; + postSolve(contact: B2Contact, impulse: B2ContactImpulse): void; +} + +export class B2DefaultContactListener implements B2ContactListener { + beginContact(contact: B2Contact):void {} + endContact(contact: B2Contact):void {} + preSolve(contact: B2Contact, oldManifold: B2Manifold):void {} + postSolve(contact: B2Contact, impulse: B2ContactImpulse):void {} +} + +interface B2QueryCallback { + reportFixture(fixture: B2Fixture): boolean; +} + +interface B2QueryCallbackFunction { + queryBack(fixture: B2Fixture): boolean; +} + +export class B2QueryCallbackProxy implements B2QueryCallback { + callback: B2QueryCallbackFunction; + constructor(callback: B2QueryCallbackFunction) { + this.callback = callback; + } + reportFixture(fixture: B2Fixture): boolean { + return this.callback.queryBack(fixture); + } +} + +interface B2RayCastCallback { + reportFixture(fixture: B2Fixture, point: B2Vec2, normal: B2Vec2, fraction: number): number; +} + +interface B2RayCastCallbackFunction { + rayBack(fixture: B2Fixture, point: B2Vec2, normal: B2Vec2, fraction: number): number; +} + +export class B2RayCastCallbackProxy implements B2RayCastCallback { + callback: B2RayCastCallbackFunction; + constructor(callback: B2RayCastCallbackFunction) { + this.callback = callback; + } + reportFixture(fixture: B2Fixture, point: B2Vec2, normal: B2Vec2, fraction: number): number { + return this.callback.rayBack(fixture, point, normal, fraction); + } +} +export { B2RayCastCallbackFunction, B2RayCastCallback, B2QueryCallbackFunction, B2QueryCallback, B2ContactListener, B2DestructionListener }; diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/Rope/b2Rope.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/Rope/b2Rope.ts new file mode 100644 index 0000000000000000000000000000000000000000..712b9dfc04c6ddb27839a63f6c2b06e8d19a7628 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/Rope/b2Rope.ts @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2Vec2, b2Atan2, b2Cross, b2Dot22, b2Distance, subtract, multM, addEqual, mulEqual, subtractEqual, minus } from '../Common/b2Math'; +import { b2PI, float2, indexTwo } from '../Common/b2Settings'; +class B2RopeDef { + vertices: B2Vec2[] | null; + count: number; + masses: number[] | null; + gravity: B2Vec2 = new B2Vec2(); + damping: number; + k2: number; + k3: number; + constructor() { + this.vertices = null; + this.count = 0; + this.masses = null; + this.gravity = new B2Vec2(); + this.damping = 0.1; + this.k2 = 0.9; + this.k3 = 0.1; + } +} + +export class B2Rope { + mCount: number; + mps: B2Vec2[] | null = null; + mp0s: B2Vec2[] | null = null; + mvs: B2Vec2[] | null = null; + mims: number[] | null = null; + mLs: number[] | null = null; + mas: number[] | null = null; + mGravity: B2Vec2; + mDamping: number; + mk2: number; + mk3: number; + constructor() { + this.mCount = 0; + this.mps = []; + this.mp0s = []; + this.mvs = []; + this.mims = []; + this.mLs = []; + this.mas = []; + this.mGravity = new B2Vec2(0.0, 0.0); + this.mDamping = 0.1; + this.mk2 = 1.0; + this.mk3 = 0.1; + } + + initialize(def: B2RopeDef): void { + this.mCount = def.count; + this.mps = []; + this.mp0s = []; + this.mvs = []; + this.mims = []; + for (let i = 0; i < this.mCount; i++) { + this.mps.push(new B2Vec2()); + this.mp0s.push(new B2Vec2()); + this.mvs.push(new B2Vec2()); + this.mims.push(0.0); + } + for (let i = 0; i < this.mCount; i++) { + this.mps[i] = def.vertices![i]; + this.mp0s[i] = def.vertices![i]; + this.mvs[i].setZero(); + let m = def.masses![i]; + if (m > 0.0) { + this.mims[i] = 1.0 / m; + } else { + this.mims[i] = 0.0; + } + } + let count2 = this.mCount - 1; + let count3 = this.mCount - indexTwo; + this.mLs = []; + for (let i = 0; i < count2; i++) { + this.mLs.push(0.0); + } + this.mas = []; + for (let j = 0; j < count3; j++) { + this.mas.push(0.0); + } + for (let i = 0; i < count2; i++) { + let p1 = this.mps[i]; + let p2 = this.mps[i + 1]; + this.mLs[i] = b2Distance(p1, p2); + } + for (let i = 0; i < count3; i++) { + let p1 = this.mps[i]; + let p2 = this.mps[i + 1]; + let p3 = this.mps[i + indexTwo]; + let d1 = subtract(p2, p1); + let d2 = subtract(p3, p2); + let a = b2Cross(d1, d2); + let b = b2Dot22(d1, d2); + this.mas[i] = b2Atan2(a, b); + } + this.mGravity = def.gravity; + this.mDamping = def.damping; + this.mk2 = def.k2; + this.mk3 = def.k3; + } + + step(h: number, iterations: number): void { + if (h === 0.0) { + return; + } + let d = Math.exp(-h * this.mDamping); + for (let i = 0; i < this.mCount; i++) { + this.mp0s![i] = this.mps![i]; + if (this.mims![i] > 0.0) { + addEqual(this.mvs![i], multM(this.mGravity, h)); + } + mulEqual(this.mvs![i], d); + addEqual(this.mps![i], multM(this.mvs![i], h)); + } + for (let i = 0; i < iterations; i++) { + this.solveC2(); + this.solveC3(); + this.solveC2(); + } + let invh = 1.0 / h; + for (let i = 0; i < this.mCount; i++) { + this.mvs![i] = multM(subtract(this.mps![i], this.mp0s![i]), invh); + } + } + + get vertexCount(): number { + return this.mCount; + } + + get vertices(): B2Vec2[] { + return this.mps!; + } + + setAngle(angle: number): void { + let count3 = this.mCount - indexTwo; + for (let i = 0; i < count3; i++) { + this.mas![i] = angle; + } + } + + solveC2(): void { + let count2 = this.mCount - 1; + for (let i = 0; i < count2; i++) { + let p1 = this.mps![i]; + let p2 = this.mps![i + 1]; + let d = subtract(p2, p1); + let L = d.normalize(); + let im1 = this.mims![i]; + let im2 = this.mims![i + 1]; + if (im1 + im2 === 0.0) { + continue; + } + let s1 = im1 / (im1 + im2); + let s2 = im2 / (im1 + im2); + subtractEqual(p1, multM(d, this.mk2 * s1 * (this.mLs![i] - L))); + addEqual(p2, multM(d, this.mk2 * s2 * (this.mLs![i] - L))); + this.mps![i] = p1; + this.mps![i + 1] = p2; + } + } + + solveC3(): void { + let count3 = this.mCount - indexTwo; + for (let i = 0; i < count3; i++) { + let p1 = this.mps![i]; + let p2 = this.mps![i + 1]; + let p3 = this.mps![i + indexTwo]; + let m1 = this.mims![i]; + let m2 = this.mims![i + 1]; + let m3 = this.mims![i + indexTwo]; + let d1 = subtract(p2, p1); + let d2 = subtract(p3, p2); + let l1sqr = d1.lengthSquared(); + let l2sqr = d2.lengthSquared(); + if (l1sqr * l2sqr === 0.0) { + continue; + } + let a = b2Cross(d1, d2); + let b = b2Dot22(d1, d2); + let angle = b2Atan2(a, b); + let jd1 = multM(d1.skew(), -1.0 / l1sqr); + let jd2 = multM(d2.skew(), 1.0 / l2sqr); + let j1 = minus(jd1); + let j2 = subtract(jd1, jd2); + let j3 = jd2; + let mass = m1 * b2Dot22(j1, j1) + m2 * b2Dot22(j2, j2) + m3 * b2Dot22(j3, j3); + if (mass === 0.0) { + continue; + } + mass = 1.0 / mass; + let c = angle - this.mas![i]; + while (c > b2PI) { + angle -= float2 * b2PI; + c = angle - this.mas![i]; + } + while (c < -b2PI) { + angle += float2 * b2PI; + c = angle - this.mas![i]; + } + let impulse = -this.mk3 * mass * c; + addEqual(p1, multM(j1, m1 * impulse)); + addEqual(p2, multM(j2, m2 * impulse)); + addEqual(p3, multM(j3, m3 * impulse)); + this.mps![i] = p1; + this.mps![i + 1] = p2; + this.mps![i + indexTwo] = p3; + } + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/box2d.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/box2d.ts new file mode 100644 index 0000000000000000000000000000000000000000..53c4c999bf6b5251c7a3cae6cb2132a700b79bb1 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/box2d.ts @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2CircleShape, B2PolygonShape, B2Shape, B2ShapeType } from './Collision/Shapes/b2Shape'; +import { addEqual, B2Vec2 } from './Common/b2Math'; +import { B2BodyDef, B2BodyType } from './Dynamics/b2Body'; +import { B2FixtureDef } from './Dynamics/b2Fixture'; +import { B2World } from './Dynamics/b2World'; +import { ThreadWorkerGlobalScope } from '@kit.ArkTS'; +import { WorkerMessage } from '../../BenchmarkMeasure'; +// 首先,确保导入了所有需要的接触类型类 +import { + B2CircleContact, + B2Contact, + B2EdgeAndCircleContact, + B2EdgeAndPolygonContact, + B2PolygonAndCircleContact, + B2PolygonContact +} from './Dynamics/Contacts/b2Contact'; + +// 然后,在初始化物理世界时,注册所有接触类型 +function initializeContactTypes() { + // 注册多边形与多边形接触 + B2Contact.addType(B2PolygonContact.create, B2PolygonContact.destroy, B2ShapeType.POLYGON, B2ShapeType.POLYGON); + // 注册圆形与圆形接触 + B2Contact.addType(B2CircleContact.create, B2CircleContact.destroy, B2ShapeType.CIRCLE, B2ShapeType.CIRCLE); + // 注册边缘与圆形接触 + B2Contact.addType(B2EdgeAndCircleContact.create, B2EdgeAndCircleContact.destroy, B2ShapeType.EDGE, + B2ShapeType.CIRCLE); + // 注册边缘与多边形接触 + B2Contact.addType(B2EdgeAndPolygonContact.create, B2EdgeAndPolygonContact.destroy, B2ShapeType.EDGE, + B2ShapeType.POLYGON); + // 注册多边形与圆形接触 + B2Contact.addType(B2PolygonAndCircleContact.create, B2PolygonAndCircleContact.destroy, B2ShapeType.POLYGON, + B2ShapeType.CIRCLE); +} + +function runBox2D(workerPort: ThreadWorkerGlobalScope): void { + let loop20: number = 600; + // 在物理世界初始化时调用此函数 + initializeContactTypes(); + let world = makeNewWorld(); + const velocityIterations = 10; + const positionIterations = 5; + let message = new WorkerMessage('Box2d', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + drawWorld(world, workerPort) + for (let i = 0; i < loop20; i++) { + world.step(1 / 60, velocityIterations, positionIterations); + let message = new WorkerMessage('Box2d', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + drawWorld(world, workerPort) + } +} + +function drawWorld(world: B2World, workerPort: ThreadWorkerGlobalScope) { + let bodies = world.getBodyList(); + let i = 0 + while (bodies) { + i++ + const body = bodies; + let fixtures = body.getFixtureList(); + let j = 0 + while (fixtures) { + j++ + const fixture = fixtures; + const shape = fixture.shape; + const pos = body.position; + const angle = body.angle; + drawShape(shape, pos, angle, workerPort); + fixtures = fixtures.getNext(); + } + console.log('body:' + i.toString() + ' fixture: ' + j.toString()) + bodies = bodies.getNext(); + } +} + +function drawShape(shape: B2Shape, position: B2Vec2, angle: number, workerPort: ThreadWorkerGlobalScope) { + let message = new WorkerMessage('Box2d', 2, 0, 0, position.x, position.y, 0, 0, 0) + switch (shape.type()) { + case B2ShapeType.POLYGON: + let vertices = (shape as B2PolygonShape).mVertices + let x: number[] = [] + let y: number[] = [] + for (let index = 0; index < vertices.count; index++) { + x.push(vertices.get(index).x) + y.push(vertices.get(index).y) + } + message.vertices_x = x + message.vertices_y = y + workerPort.postMessage(message); + break; + case B2ShapeType.CIRCLE: + message.type = 3 + message.radius = (shape as B2CircleShape).radius + workerPort.postMessage(message); + break; + // 其他形状... + } +} + +/* + * @Setup + */ +function makeNewWorld(): B2World { + let density: number = 1.0; + let gravityY: number = -10.0; + let zero: number = 0.0; + let gravity = new B2Vec2(zero, gravityY); + let world = new B2World(gravity); + + //地面 + let edgeV1X: number = -100.0; + let edgeV2X: number = 300.0; + let shape = new B2PolygonShape(); + shape.setAsEdge(new B2Vec2(edgeV1X, zero), new B2Vec2(edgeV2X, zero)); + shape.radius = 0; + let fdg = new B2FixtureDef(); + fdg.density = density; + fdg.shape = shape; + fdg.restitution = 0.8; + fdg.friction = 0.5; + let bdg = new B2BodyDef(); + bdg.type = B2BodyType.STATICBODY; + bdg.allowSleep = false; + let ground = world.createBody(bdg); + ground.createFixture(fdg); + + // 创建球体 + const ballShape = new B2CircleShape(); + ballShape.radius = 5; + const ballFd = new B2FixtureDef(); + ballFd.shape = ballShape; + ballFd.density = density; + ballFd.restitution = 0.8; + const ballBd = new B2BodyDef(); + ballBd.type = B2BodyType.DYNAMICBODY; + ballBd.position.set(200, 400); + ballBd.allowSleep = false; + let ballBody = world.createBody(ballBd); + ballBody.createFixture(ballFd); + + return world; +} + +export { runBox2D } \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/box2d1.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/box2d1.ts new file mode 100644 index 0000000000000000000000000000000000000000..b94c993ac1e09cf819b40733bc0ea5e91349f4af --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/box2d1.ts @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2CircleShape, B2PolygonShape, B2Shape, B2ShapeType } from './Collision/Shapes/b2Shape'; +import { addEqual, B2Vec2 } from './Common/b2Math'; +import { B2BodyDef, B2BodyType } from './Dynamics/b2Body'; +import { B2FixtureDef } from './Dynamics/b2Fixture'; +import { B2World } from './Dynamics/b2World'; +import { ThreadWorkerGlobalScope } from '@kit.ArkTS'; +import { WorkerMessage } from '../../BenchmarkMeasure'; +// 首先,确保导入了所有需要的接触类型类 +import { + B2CircleContact, + B2Contact, + B2EdgeAndCircleContact, + B2EdgeAndPolygonContact, + B2PolygonAndCircleContact, + B2PolygonContact +} from './Dynamics/Contacts/b2Contact'; + +// 然后,在初始化物理世界时,注册所有接触类型 +function initializeContactTypes() { + // 注册多边形与多边形接触 + B2Contact.addType(B2PolygonContact.create, B2PolygonContact.destroy, B2ShapeType.POLYGON, B2ShapeType.POLYGON); + // 注册圆形与圆形接触 + B2Contact.addType(B2CircleContact.create, B2CircleContact.destroy, B2ShapeType.CIRCLE, B2ShapeType.CIRCLE); + // 注册边缘与圆形接触 + B2Contact.addType(B2EdgeAndCircleContact.create, B2EdgeAndCircleContact.destroy, B2ShapeType.EDGE, + B2ShapeType.CIRCLE); + // 注册边缘与多边形接触 + B2Contact.addType(B2EdgeAndPolygonContact.create, B2EdgeAndPolygonContact.destroy, B2ShapeType.EDGE, + B2ShapeType.POLYGON); + // 注册多边形与圆形接触 + B2Contact.addType(B2PolygonAndCircleContact.create, B2PolygonAndCircleContact.destroy, B2ShapeType.POLYGON, + B2ShapeType.CIRCLE); +} + +function runBox2D1(workerPort: ThreadWorkerGlobalScope): void { + let loop20: number = 200; + // 在物理世界初始化时调用此函数 + initializeContactTypes(); + let world = makeNewWorld(); + const velocityIterations = 10; + const positionIterations = 5; + let message = new WorkerMessage('Box2d', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + drawWorld(world, workerPort) + for (let i = 0; i < loop20; i++) { + world.step(1 / 30, velocityIterations, positionIterations); + let message = new WorkerMessage('Box2d', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + drawWorld(world, workerPort) + } +} + +function drawWorld(world: B2World, workerPort: ThreadWorkerGlobalScope) { + let bodies = world.getBodyList(); + let i = 0 + while (bodies) { + i++ + const body = bodies; + let fixtures = body.getFixtureList(); + let j = 0 + while (fixtures) { + j++ + const fixture = fixtures; + const shape = fixture.shape; + const pos = body.position; + const angle = body.angle; + drawShape(shape, pos, angle, workerPort); + fixtures = fixtures.getNext(); + } + console.log('body:' + i.toString() + ' fixture: ' + j.toString()) + bodies = bodies.getNext(); + } +} + +function drawShape(shape: B2Shape, position: B2Vec2, angle: number, workerPort: ThreadWorkerGlobalScope) { + let message = new WorkerMessage('Box2d', 2, 0, 0, position.x, position.y, 0, 0, 0) + switch (shape.type()) { + case B2ShapeType.POLYGON: + let vertices = (shape as B2PolygonShape).mVertices + let x: number[] = [] + let y: number[] = [] + for (let index = 0; index < vertices.count; index++) { + x.push(vertices.get(index).x) + y.push(vertices.get(index).y) + } + message.vertices_x = x + message.vertices_y = y + workerPort.postMessage(message); + break; + case B2ShapeType.CIRCLE: + message.type = 3 + message.radius = (shape as B2CircleShape).radius + workerPort.postMessage(message); + break; + // 其他形状... + } +} + +/* + * @Setup + */ +function makeNewWorld(): B2World { + let density: number = 1.0; + let gravityY: number = -10.0; + let zero: number = 0.0; + let gravity = new B2Vec2(zero, gravityY); + let world = new B2World(gravity); + + //地面 + let edgeV1X: number = -100.0; + let edgeV2X: number = 300.0; + let shape = new B2PolygonShape(); + shape.setAsEdge(new B2Vec2(edgeV1X, zero), new B2Vec2(edgeV2X, zero)); + shape.radius = 0; + let fdg = new B2FixtureDef(); + fdg.density = density; + fdg.shape = shape; + fdg.restitution = 0.8; + fdg.friction = 0.5; + let bdg = new B2BodyDef(); + bdg.type = B2BodyType.STATICBODY; + bdg.allowSleep = false; + let ground = world.createBody(bdg); + ground.createFixture(fdg); + + // 创建球体 + const ballShape = new B2CircleShape(); + ballShape.radius = 5; + const ballFd = new B2FixtureDef(); + ballFd.shape = ballShape; + ballFd.density = density; + ballFd.restitution = 0.8; + const ballBd = new B2BodyDef(); + ballBd.type = B2BodyType.DYNAMICBODY; + ballBd.position.set(200, 300); + ballBd.allowSleep = false; + let ballBody = world.createBody(ballBd); + ballBody.createFixture(ballFd); + + //方块群 + let a: number = 5; + let shapeBox = new B2PolygonShape(); + shapeBox.setAsBox(a, a); + shapeBox.radius = 0; + let xX: number = 2.0; + let xY: number = 10; + let x = new B2Vec2(xX, xY); + let y = new B2Vec2(); + let deltaXX: number = 10; + let deltaXY: number = 20; + let deltaYX: number = 20; + let loop5: number = 2; + let loop10: number = 2; + let deltaX = new B2Vec2(deltaXX, deltaXY); + let deltaY = new B2Vec2(deltaYX, zero); + for (let i = 0; i < loop10; i++) { + y.set(x.x, x.y); + for (let j = 0; j < loop5; j++) { + let fd = new B2FixtureDef(); + fd.density = density; + fd.restitution = 0.8; + fd.friction = 0.5; + fd.shape = shapeBox; + let bd = new B2BodyDef(); + bd.type = B2BodyType.DYNAMICBODY; + bd.allowSleep = false; + bd.bullet = true; + bd.position.set(xX + j * 20, xY + i * 20 + 100); + let body = world.createBody(bd); + body.createFixture(fd); + if (j > 0) { + body.applyForceToCenter(new B2Vec2(-(i + 1) * (j + 1) * 500, 0), true); + } else { + body.applyForceToCenter(new B2Vec2((i + 1) * (j + 1) * 2000, 0), false); + } + if (i > 0) { + body.applyForceToCenter(new B2Vec2(0, -(i + 1) * (j + 1) * 500), true); + } else { + body.applyForceToCenter(new B2Vec2(0, (i + 1) * (j + 1) * 50000), false); + } + addEqual(y, deltaY); + } + addEqual(x, deltaX); + } + return world; +} + +export { runBox2D1 } \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/box2d2.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/box2d2.ts new file mode 100644 index 0000000000000000000000000000000000000000..585f1bd2ee45add92c8c2a077b0de68159a37914 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/box2d2.ts @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2CircleShape, B2PolygonShape, B2Shape, B2ShapeType } from './Collision/Shapes/b2Shape'; +import { addEqual, B2Vec2 } from './Common/b2Math'; +import { B2BodyDef, B2BodyType } from './Dynamics/b2Body'; +import { B2FixtureDef } from './Dynamics/b2Fixture'; +import { B2World } from './Dynamics/b2World'; +import { ThreadWorkerGlobalScope } from '@kit.ArkTS'; +import { WorkerMessage } from '../../BenchmarkMeasure'; +// 首先,确保导入了所有需要的接触类型类 +import { + B2CircleContact, + B2Contact, + B2EdgeAndCircleContact, + B2EdgeAndPolygonContact, + B2PolygonAndCircleContact, + B2PolygonContact +} from './Dynamics/Contacts/b2Contact'; + +// 然后,在初始化物理世界时,注册所有接触类型 +function initializeContactTypes() { + // 注册多边形与多边形接触 + B2Contact.addType(B2PolygonContact.create, B2PolygonContact.destroy, B2ShapeType.POLYGON, B2ShapeType.POLYGON); + // 注册圆形与圆形接触 + B2Contact.addType(B2CircleContact.create, B2CircleContact.destroy, B2ShapeType.CIRCLE, B2ShapeType.CIRCLE); + // 注册边缘与圆形接触 + B2Contact.addType(B2EdgeAndCircleContact.create, B2EdgeAndCircleContact.destroy, B2ShapeType.EDGE, + B2ShapeType.CIRCLE); + // 注册边缘与多边形接触 + B2Contact.addType(B2EdgeAndPolygonContact.create, B2EdgeAndPolygonContact.destroy, B2ShapeType.EDGE, + B2ShapeType.POLYGON); + // 注册多边形与圆形接触 + B2Contact.addType(B2PolygonAndCircleContact.create, B2PolygonAndCircleContact.destroy, B2ShapeType.POLYGON, + B2ShapeType.CIRCLE); +} + +function runBox2D2(workerPort: ThreadWorkerGlobalScope): void { + let loop20: number = 600; + // 在物理世界初始化时调用此函数 + initializeContactTypes(); + let world = makeNewWorld(); + const velocityIterations = 10; + const positionIterations = 5; + let message = new WorkerMessage('Box2d', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + drawWorld(world, workerPort) + for (let i = 0; i < loop20; i++) { + world.step(1 / 60, velocityIterations, positionIterations); + let message = new WorkerMessage('Box2d', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + drawWorld(world, workerPort) + } +} + +function drawWorld(world: B2World, workerPort: ThreadWorkerGlobalScope) { + let bodies = world.getBodyList(); + let i = 0 + while (bodies) { + i++ + const body = bodies; + let fixtures = body.getFixtureList(); + let j = 0 + while (fixtures) { + j++ + const fixture = fixtures; + const shape = fixture.shape; + const pos = body.position; + const angle = body.angle; + drawShape(shape, pos, angle, workerPort); + fixtures = fixtures.getNext(); + } + console.log('body:' + i.toString() + ' fixture: ' + j.toString()) + bodies = bodies.getNext(); + } +} + +function drawShape(shape: B2Shape, position: B2Vec2, angle: number, workerPort: ThreadWorkerGlobalScope) { + let message = new WorkerMessage('Box2d', 2, 0, 0, position.x, position.y, 0, 0, 0) + switch (shape.type()) { + case B2ShapeType.POLYGON: + let vertices = (shape as B2PolygonShape).mVertices + let x: number[] = [] + let y: number[] = [] + for (let index = 0; index < vertices.count; index++) { + x.push(vertices.get(index).x) + y.push(vertices.get(index).y) + } + message.vertices_x = x + message.vertices_y = y + workerPort.postMessage(message); + break; + case B2ShapeType.CIRCLE: + message.type = 3 + message.radius = (shape as B2CircleShape).radius + workerPort.postMessage(message); + break; + // 其他形状... + } +} + +/* + * @Setup + */ +function makeNewWorld(): B2World { + let density: number = 1.0; + let gravityY: number = -10.0; + let zero: number = 0.0; + let gravity = new B2Vec2(zero, gravityY); + let world = new B2World(gravity); + + //地面 + let edgeV1X: number = -100.0; + let edgeV2X: number = 300.0; + let shape = new B2PolygonShape(); + shape.setAsEdge(new B2Vec2(edgeV1X, zero), new B2Vec2(edgeV2X, zero)); + shape.radius = 0; + let fdg = new B2FixtureDef(); + fdg.density = density; + fdg.shape = shape; + fdg.restitution = 0.8; + fdg.friction = 0.5; + let bdg = new B2BodyDef(); + bdg.type = B2BodyType.STATICBODY; + bdg.allowSleep = false; + let ground = world.createBody(bdg); + ground.createFixture(fdg); + + let shapeB = new B2CircleShape(); + shapeB.radius = 10; + let fd1 = new B2FixtureDef(); + fd1.density = density; + fd1.shape = shapeB; + fd1.restitution = 0.8; + fd1.friction = 0.5; + let bd1 = new B2BodyDef(); + bd1.type = B2BodyType.DYNAMICBODY; + bd1.allowSleep = false + bd1.bullet = true; + bd1.position.set(-40, 350); + let body1 = world.createBody(bd1); + body1.createFixture(fd1); + body1.applyForceToCenter(new B2Vec2(500000, 0), false); + + return world; +} + +export { runBox2D2 } \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/cases/performance/box2d/box2d3.ts b/OHBM/entry/src/main/ets/cases/performance/box2d/box2d3.ts new file mode 100644 index 0000000000000000000000000000000000000000..19af8836afe85c22feeaedeccfe02759000bab67 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/box2d/box2d3.ts @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { B2CircleShape, B2PolygonShape, B2Shape, B2ShapeType } from './Collision/Shapes/b2Shape'; +import { addEqual, B2Vec2 } from './Common/b2Math'; +import { B2BodyDef, B2BodyType } from './Dynamics/b2Body'; +import { B2FixtureDef } from './Dynamics/b2Fixture'; +import { B2World } from './Dynamics/b2World'; +import { ThreadWorkerGlobalScope } from '@kit.ArkTS'; +import { WorkerMessage } from '../../BenchmarkMeasure'; +// 首先,确保导入了所有需要的接触类型类 +import { + B2CircleContact, + B2Contact, + B2EdgeAndCircleContact, + B2EdgeAndPolygonContact, + B2PolygonAndCircleContact, + B2PolygonContact +} from './Dynamics/Contacts/b2Contact'; + +// 然后,在初始化物理世界时,注册所有接触类型 +function initializeContactTypes() { + // 注册多边形与多边形接触 + B2Contact.addType(B2PolygonContact.create, B2PolygonContact.destroy, B2ShapeType.POLYGON, B2ShapeType.POLYGON); + // 注册圆形与圆形接触 + B2Contact.addType(B2CircleContact.create, B2CircleContact.destroy, B2ShapeType.CIRCLE, B2ShapeType.CIRCLE); + // 注册边缘与圆形接触 + B2Contact.addType(B2EdgeAndCircleContact.create, B2EdgeAndCircleContact.destroy, B2ShapeType.EDGE, + B2ShapeType.CIRCLE); + // 注册边缘与多边形接触 + B2Contact.addType(B2EdgeAndPolygonContact.create, B2EdgeAndPolygonContact.destroy, B2ShapeType.EDGE, + B2ShapeType.POLYGON); + // 注册多边形与圆形接触 + B2Contact.addType(B2PolygonAndCircleContact.create, B2PolygonAndCircleContact.destroy, B2ShapeType.POLYGON, + B2ShapeType.CIRCLE); +} + +function runBox2D3(workerPort: ThreadWorkerGlobalScope): void { + let loop20: number = 200; + // 在物理世界初始化时调用此函数 + initializeContactTypes(); + let world = makeNewWorld(); + const velocityIterations = 10; + const positionIterations = 5; + let message = new WorkerMessage('Box2d', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + drawWorld(world, workerPort) + for (let i = 0; i < loop20; i++) { + world.step(1 / 30, velocityIterations, positionIterations); + let message = new WorkerMessage('Box2d', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + drawWorld(world, workerPort) + } +} + +function drawWorld(world: B2World, workerPort: ThreadWorkerGlobalScope) { + let bodies = world.getBodyList(); + let i = 0 + while (bodies) { + i++ + const body = bodies; + let fixtures = body.getFixtureList(); + let j = 0 + while (fixtures) { + j++ + const fixture = fixtures; + const shape = fixture.shape; + const pos = body.position; + const angle = body.angle; + drawShape(shape, pos, angle, workerPort); + fixtures = fixtures.getNext(); + } + console.log('body:' + i.toString() + ' fixture: ' + j.toString()) + bodies = bodies.getNext(); + } +} + +function drawShape(shape: B2Shape, position: B2Vec2, angle: number, workerPort: ThreadWorkerGlobalScope) { + let message = new WorkerMessage('Box2d', 2, 0, 0, position.x, position.y, 0, 0, 0) + switch (shape.type()) { + case B2ShapeType.POLYGON: + let vertices = (shape as B2PolygonShape).mVertices + let x: number[] = [] + let y: number[] = [] + for (let index = 0; index < vertices.count; index++) { + x.push(vertices.get(index).x) + y.push(vertices.get(index).y) + } + message.vertices_x = x + message.vertices_y = y + workerPort.postMessage(message); + break; + case B2ShapeType.CIRCLE: + message.type = 3 + message.radius = (shape as B2CircleShape).radius + workerPort.postMessage(message); + break; + // 其他形状... + } +} + +/* + * @Setup + */ +function makeNewWorld(): B2World { + let density: number = 1.0; + let gravityY: number = -10.0; + let zero: number = 0.0; + let gravity = new B2Vec2(zero, gravityY); + let world = new B2World(gravity); + + //地面 + let edgeV1X: number = -100.0; + let edgeV2X: number = 300.0; + let shape = new B2PolygonShape(); + shape.setAsEdge(new B2Vec2(edgeV1X, zero), new B2Vec2(edgeV2X, zero)); + shape.radius = 0; + let fdg = new B2FixtureDef(); + fdg.density = density; + fdg.shape = shape; + fdg.restitution = 0.8; + fdg.friction = 0.5; + let bdg = new B2BodyDef(); + bdg.type = B2BodyType.STATICBODY; + bdg.allowSleep = false; + let ground = world.createBody(bdg); + ground.createFixture(fdg); + + // 创建球体 + const ballShape = new B2CircleShape(); + ballShape.radius = 5; + const ballFd = new B2FixtureDef(); + ballFd.shape = ballShape; + ballFd.density = density; + ballFd.restitution = 0.8; + const ballBd = new B2BodyDef(); + ballBd.type = B2BodyType.DYNAMICBODY; + ballBd.position.set(200, 300); + ballBd.allowSleep = false; + let ballBody = world.createBody(ballBd); + ballBody.createFixture(ballFd); + + //方块群 + let a: number = 5; + let shapeBox = new B2PolygonShape(); + shapeBox.setAsBox(a, a); + shapeBox.radius = 0; + let xX: number = 2.0; + let xY: number = 10; + let x = new B2Vec2(xX, xY); + let y = new B2Vec2(); + let deltaXX: number = 10; + let deltaXY: number = 20; + let deltaYX: number = 20; + let loop5: number = 5; + let loop10: number = 5; + let deltaX = new B2Vec2(deltaXX, deltaXY); + let deltaY = new B2Vec2(deltaYX, zero); + for (let i = 0; i < loop10; i++) { + y.set(x.x, x.y); + for (let j = 0; j < loop5; j++) { + let fd = new B2FixtureDef(); + fd.density = density; + fd.restitution = 0.8; + fd.friction = 0.5; + fd.shape = shapeBox; + let bd = new B2BodyDef(); + bd.type = B2BodyType.DYNAMICBODY; + bd.allowSleep = false; + bd.bullet = true; + bd.position.set(xX + j * 20, xY + i * 20 + 100); + let body = world.createBody(bd); + body.createFixture(fd); + if (j > 2) { + body.applyForceToCenter(new B2Vec2(-(i + 1) * (j + 1) * 500, 0), true); + } else { + body.applyForceToCenter(new B2Vec2((i + 1) * (j + 1) * 2000, 0), true); + } + if (i > 2) { + body.applyForceToCenter(new B2Vec2(0, -(i + 1) * (j + 1) * 500), false); + } else { + body.applyForceToCenter(new B2Vec2(0, (i + 1) * (j + 1) * 5000), true); + } + addEqual(y, deltaY); + } + addEqual(x, deltaX); + } + return world; +} + +export { runBox2D3 } \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/cases/performance/n-body.ts b/OHBM/entry/src/main/ets/cases/performance/n-body.ts new file mode 100644 index 0000000000000000000000000000000000000000..7860a2ac8e9fa58c1aa089795f67e2f93fd92e4f --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/n-body.ts @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ThreadWorkerGlobalScope } from '@kit.ArkTS'; +import { WorkerMessage } from '../BenchmarkMeasure'; + +const COMMON_NUMBER_FOUR: number = 4; +const CURRENT_LOOP_STEP: number = 2; +const CURRENT_LOOP_COUNT: number = 10000; +const CURRENT_LOOP_OPERATE_NUMBER: number = 100; +const FUNCTION_ADVANCE_ARGUMENT_DT: number = 0.01; +const FUNCTION_ENERGY_OPERATE_COEFFICIENT: number = 0.5; +const MAX_LOOP_COUNT: number = 8; +const MS_CONVERSION_RATIO: number = 1000; + +const INSTANCE_BODY_ONE_X: number = 4.8414314424647209; +const INSTANCE_BODY_ONE_Y: number = -1.16032004402742839; +const INSTANCE_BODY_ONE_Z: number = -1.03622044471123109e-1; +const INSTANCE_BODY_ONE_VX: number = 1.66007664274403694e-3; +const INSTANCE_BODY_ONE_VY: number = 7.69901118419740425e-3; +const INSTANCE_BODY_ONE_VZ: number = -6.90460016972063023e-5; +const INSTANCE_BODY_ONE_MASS: number = 9.54791938424326609e-4; + +const INSTANCE_BODY_TWO_X: number = 8.34336671824457987; +const INSTANCE_BODY_TWO_Y: number = 4.12479856412430479; +const INSTANCE_BODY_TWO_Z: number = -4.03523417114321381e-1; +const INSTANCE_BODY_TWO_VX: number = -2.76742510726862411e-3; +const INSTANCE_BODY_TWO_VY: number = 4.99852801234917238e-3; +const INSTANCE_BODY_TWO_VZ: number = 2.30417297573763929e-5; +const INSTANCE_BODY_TWO_MASS: number = 2.85885980666130812e-4; + +const INSTANCE_BODY_THREE_X: number = 1.2894369562139131e1; +const INSTANCE_BODY_THREE_Y: number = -1.51111514016986312e1; +const INSTANCE_BODY_THREE_Z: number = -2.23307578892655734e-1; +const INSTANCE_BODY_THREE_VX: number = 2.96460137564761618e-3; +const INSTANCE_BODY_THREE_VY: number = 2.3784717395948095e-3; +const INSTANCE_BODY_THREE_VZ: number = -2.96589568540237556e-5; +const INSTANCE_BODY_THREE_MASS: number = 4.36624404335156298e-5; + +const INSTANCE_BODY_FOUR_X: number = 1.53796971148509165e1; +const INSTANCE_BODY_FOUR_Y: number = -2.59193146099879641e1; +const INSTANCE_BODY_FOUR_Z: number = 1.79258772950371181e-1; +const INSTANCE_BODY_FOUR_VX: number = 2.68067772490389322e-3; +const INSTANCE_BODY_FOUR_VY: number = 1.62824170038242295e-3; +const INSTANCE_BODY_FOUR_VZ: number = -9.5159225451971587e-5; +const INSTANCE_BODY_FOUR_MASS: number = 5.15138902046611451e-5; + +let PI: number = 3.141592653589793; +let SOLAR_MASS: number = COMMON_NUMBER_FOUR * PI * PI; +let DAYS_PER_YEAR: number = 365.24; + +class Body { + x: number; + y: number; + z: number; + vx: number; + vy: number; + vz: number; + mass: number; + + constructor(x1: number, y1: number, z1: number, vx: number, vy: number, vz: number, mass: number) { + this.x = x1; + this.vx = vx; + this.y = y1; + this.vy = vy; + this.z = z1; + this.vz = vz; + this.mass = mass; + } + + offsetMomentum(px: number, py: number, pz: number): Body { + this.vx = -px / SOLAR_MASS; + this.vy = -py / SOLAR_MASS; + this.vz = -pz / SOLAR_MASS; + return this; + } +} + +function jupiter(): Body { + return new Body( + INSTANCE_BODY_ONE_X, + INSTANCE_BODY_ONE_Y, + INSTANCE_BODY_ONE_Z, + INSTANCE_BODY_ONE_VX * DAYS_PER_YEAR, + INSTANCE_BODY_ONE_VY * DAYS_PER_YEAR, + INSTANCE_BODY_ONE_VZ * DAYS_PER_YEAR, + INSTANCE_BODY_ONE_MASS * SOLAR_MASS + ); +} + +function saturn(): Body { + return new Body( + INSTANCE_BODY_TWO_X, + INSTANCE_BODY_TWO_Y, + INSTANCE_BODY_TWO_Z, + INSTANCE_BODY_TWO_VX * DAYS_PER_YEAR, + INSTANCE_BODY_TWO_VY * DAYS_PER_YEAR, + INSTANCE_BODY_TWO_VZ * DAYS_PER_YEAR, + INSTANCE_BODY_TWO_MASS * SOLAR_MASS + ); +} + +function uranus(): Body { + return new Body( + INSTANCE_BODY_THREE_X, + INSTANCE_BODY_THREE_Y, + INSTANCE_BODY_THREE_Z, + INSTANCE_BODY_THREE_VX * DAYS_PER_YEAR, + INSTANCE_BODY_THREE_VY * DAYS_PER_YEAR, + INSTANCE_BODY_THREE_VZ * DAYS_PER_YEAR, + INSTANCE_BODY_THREE_MASS * SOLAR_MASS + ); +} + +function neptune(): Body { + return new Body( + INSTANCE_BODY_FOUR_X, + INSTANCE_BODY_FOUR_Y, + INSTANCE_BODY_FOUR_Z, + INSTANCE_BODY_FOUR_VX * DAYS_PER_YEAR, + INSTANCE_BODY_FOUR_VY * DAYS_PER_YEAR, + INSTANCE_BODY_FOUR_VZ * DAYS_PER_YEAR, + INSTANCE_BODY_FOUR_MASS * SOLAR_MASS + ); +} + +function sun(): Body { + return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS); +} + +class NBodySystem { + bodies: Body[]; + + constructor(bodies: Body[]) { + this.bodies = bodies; + let px = 0.0; + let py = 0.0; + let pz = 0.0; + let size = this.bodies.length; + for (let i = 0; i < size; i++) { + let b = this.bodies[i]; + let m = b.mass; + px += b.vx * m; + py += b.vy * m; + pz += b.vz * m; + } + + this.bodies[0].offsetMomentum(px, py, pz); + } + + advance(dt: number, workerPort: ThreadWorkerGlobalScope): void { + let dx: number; + let dy: number; + let dz: number; + let distance: number; + let mag: number; + let size = this.bodies.length; + + for (let i = 0; i < size; i++) { + let bodyI = this.bodies[i]; + for (let j = i + 1; j < size; j++) { + let bodyJ = this.bodies[j]; + dx = bodyI.x - bodyJ.x; + dy = bodyI.y - bodyJ.y; + dz = bodyI.z - bodyJ.z; + + distance = Math.sqrt(dx * dx + dy * dy + dz * dz); + mag = dt / (distance * distance * distance); + + bodyI.vx -= dx * bodyJ.mass * mag; + bodyI.vy -= dy * bodyJ.mass * mag; + bodyI.vz -= dz * bodyJ.mass * mag; + + bodyJ.vx += dx * bodyI.mass * mag; + bodyJ.vy += dy * bodyI.mass * mag; + bodyJ.vz += dz * bodyI.mass * mag; + } + } + for (let i = 0; i < size; i++) { + let body = this.bodies[i]; + body.x += dt * body.vx; + body.y += dt * body.vy; + body.z += dt * body.vz; + } + } + + energy(): number { + let dx: number; + let dy: number; + let dz: number; + let distance: number; + let e = 0.0; + let size = this.bodies.length; + + for (let i = 0; i < size; i++) { + let bodyI = this.bodies[i]; + + e += FUNCTION_ENERGY_OPERATE_COEFFICIENT * bodyI.mass * + (bodyI.vx * bodyI.vx + bodyI.vy * bodyI.vy + bodyI.vz * bodyI.vz); + + for (let j = i + 1; j < size; j++) { + let bodyJ = this.bodies[j]; + dx = bodyI.x - bodyJ.x; + dy = bodyI.y - bodyJ.y; + dz = bodyI.z - bodyJ.z; + distance = Math.sqrt(dx * dx + dy * dy + dz * dz); + e -= (bodyI.mass * bodyJ.mass) / distance; + } + } + return e; + } +} + +declare interface ArkTools { + timeInUs(args: number): number; +} + +function runNBody(workerPort: ThreadWorkerGlobalScope): void { + let ret = 0; + let n = 3; + let message = new WorkerMessage('n-body', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message); + while (n <= CURRENT_LOOP_COUNT) { + let planets = [sun(), jupiter(), saturn(), uranus(), neptune()]; + let bodies = new NBodySystem(planets); + let max = n * CURRENT_LOOP_OPERATE_NUMBER; + ret += bodies.energy(); + for (let i = 0; i < max; i++) { + bodies.advance(FUNCTION_ADVANCE_ARGUMENT_DT, workerPort); + } + let message1 = new WorkerMessage('n-body', -1, 0, 0, 0, 0, 0, 0, 0) + workerPort.postMessage(message1); + for (let index = 0; index < bodies.bodies.length; index++) { + let message = + new WorkerMessage('Planet', 4, 100, bodies.bodies[index].z, + bodies.bodies[index].x, + bodies.bodies[index].y, 0, 0, 0) + switch (bodies.bodies[index].mass) { + case SOLAR_MASS: + message.name = '太阳'; + message.r = 255; + break; + case INSTANCE_BODY_ONE_MASS * SOLAR_MASS: + message.name = '木星'; + message.time = 60; + message.r = 218; + message.g = 165; + message.b = 32; + break; + case INSTANCE_BODY_TWO_MASS * SOLAR_MASS: + message.name = '土星'; + message.time = 50; + message.r = 255; + message.g = 215; + break; + case INSTANCE_BODY_THREE_MASS * SOLAR_MASS: + message.name = '天王星'; + message.time = 20; + message.r = 135; + message.g = 206; + message.b = 250; + break; + case INSTANCE_BODY_FOUR_MASS * SOLAR_MASS: + message.name = '海王星' + message.time = 20; + message.b = 139; + break; + } + workerPort.postMessage(message); + } + ret += bodies.energy(); + console.log('energy ' + n + ':' + ret); + n *= CURRENT_LOOP_STEP; + } + + let expected = -1.3524862408537381; + if (ret !== expected) { + console.log('ERROR: bad result: expected' + expected + 'but got' + ret); + } +} + +export { runNBody } diff --git a/OHBM/entry/src/main/ets/cases/performance/richards.ts b/OHBM/entry/src/main/ets/cases/performance/richards.ts index 175fbe548575be8b4efef83bc71403d888caa160..709b9cce67c8b6e600f2a7d79fc83407c6fbf563 100644 --- a/OHBM/entry/src/main/ets/cases/performance/richards.ts +++ b/OHBM/entry/src/main/ets/cases/performance/richards.ts @@ -18,91 +18,95 @@ import { BenchmarkRun } from "../BenchmarkMeasure"; /**************************source code********************************/ class Scheduler { - queueCount: number; - holdCount: number; - blocks: (TaskControlBlock | undefined)[]; - list: TaskControlBlock | undefined; - currentTcb: TaskControlBlock | undefined; - currentId: number | undefined; - - constructor() { - this.queueCount = 0; - this.holdCount = 0; - this.blocks = Array(NUMBER_OF_IDS).fill(undefined); - this.list = undefined; - this.currentTcb = undefined; - this.currentId = undefined; - } - - addIdleTask(id: number, priority: number, queue: Packet | undefined, count: number): void { - this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count)); - } - - addWorkerTask(id: number, priority: number, queue: Packet): void { - this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0)); - } - - addHandlerTask(id: number, priority: number, queue: Packet): void { - this.addTask(id, priority, queue, new HandlerTask(this)); - } - - addDeviceTask(id: number, priority: number, queue: Packet | undefined): void { - this.addTask(id, priority, queue, new DeviceTask(this)); - } - - addRunningTask(id: number, priority: number, queue: Packet | undefined, task: Task): void { - this.addTask(id, priority, queue, task); - this.currentTcb!.setRunning(); - } - - addTask(id: number, priority: number, queue: Packet | undefined, task: Task): void { - this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task); - this.list = this.currentTcb; - this.blocks[id] = this.currentTcb; - } - - schedule(): void { - this.currentTcb = this.list; - while (this.currentTcb !== undefined) { - if (this.currentTcb.isHeldOrSuspended()) { - this.currentTcb = this.currentTcb.link; - } else { - this.currentId = this.currentTcb.id; - this.currentTcb = this.currentTcb.run(); - } - } - } - - release(id: number): TaskControlBlock | undefined { - const tcb = this.blocks[id]; - if (tcb === undefined) return tcb; - tcb.markAsNotHeld(); - if (tcb.priority > this.currentTcb!.priority) { - return tcb; - } else { - return this.currentTcb; - } - } - - holdCurrent(): TaskControlBlock | undefined { - this.holdCount += 1; - this.currentTcb?.markAsHeld(); - return this.currentTcb?.link; - } - - suspendCurrent(): TaskControlBlock | undefined { - this.currentTcb?.markAsSuspended(); - return this.currentTcb; - } - - queue(packet: Packet): TaskControlBlock | undefined { - const t = this.blocks[packet.id]; - if (t === undefined) return t; - this.queueCount += 1; - packet.link = undefined; - packet.id = this.currentId!; - return t.checkPriorityAdd(this.currentTcb!, packet); - } + queueCount: number; + holdCount: number; + blocks: (TaskControlBlock | undefined)[]; + list: TaskControlBlock | undefined; + currentTcb: TaskControlBlock | undefined; + currentId: number | undefined; + + constructor() { + this.queueCount = 0; + this.holdCount = 0; + this.blocks = Array(NUMBER_OF_IDS).fill(undefined); + this.list = undefined; + this.currentTcb = undefined; + this.currentId = undefined; + } + + addIdleTask(id: number, priority: number, queue: Packet | undefined, count: number): void { + this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count)); + } + + addWorkerTask(id: number, priority: number, queue: Packet): void { + this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0)); + } + + addHandlerTask(id: number, priority: number, queue: Packet): void { + this.addTask(id, priority, queue, new HandlerTask(this)); + } + + addDeviceTask(id: number, priority: number, queue: Packet | undefined): void { + this.addTask(id, priority, queue, new DeviceTask(this)); + } + + addRunningTask(id: number, priority: number, queue: Packet | undefined, task: Task): void { + this.addTask(id, priority, queue, task); + this.currentTcb!.setRunning(); + } + + addTask(id: number, priority: number, queue: Packet | undefined, task: Task): void { + this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task); + this.list = this.currentTcb; + this.blocks[id] = this.currentTcb; + } + + schedule(): void { + this.currentTcb = this.list; + while (this.currentTcb !== undefined) { + if (this.currentTcb.isHeldOrSuspended()) { + this.currentTcb = this.currentTcb.link; + } else { + this.currentId = this.currentTcb.id; + this.currentTcb = this.currentTcb.run(); + } + } + } + + release(id: number): TaskControlBlock | undefined { + const tcb = this.blocks[id]; + if (tcb === undefined) { + return tcb; + } + tcb.markAsNotHeld(); + if (tcb.priority > this.currentTcb!.priority) { + return tcb; + } else { + return this.currentTcb; + } + } + + holdCurrent(): TaskControlBlock | undefined { + this.holdCount += 1; + this.currentTcb?.markAsHeld(); + return this.currentTcb?.link; + } + + suspendCurrent(): TaskControlBlock | undefined { + this.currentTcb?.markAsSuspended(); + return this.currentTcb; + } + + queue(packet: Packet): TaskControlBlock | undefined { + const t = this.blocks[packet.id]; + if (t === undefined) { + return t; + } + this.queueCount += 1; + packet.link = undefined; + packet.id = this.currentId!; + return t.checkPriorityAdd(this.currentTcb!, packet); + } } const ID_IDLE = 0; @@ -117,82 +121,82 @@ const KIND_DEVICE = 0; const KIND_WORK = 1; class TaskControlBlock { - link: TaskControlBlock | undefined; - id: number; - priority: number; - queue: Packet | undefined; - task: Task; - state: number; - - constructor(link: TaskControlBlock | undefined, id: number, priority: number, queue: Packet | undefined, task: Task) { - this.link = link; - this.id = id; - this.priority = priority; - this.queue = queue; - this.task = task; - if (queue === undefined) { - this.state = STATE_SUSPENDED; - } else { - this.state = STATE_SUSPENDED_RUNNABLE; - } - } - - setRunning(): void { + link: TaskControlBlock | undefined; + id: number; + priority: number; + queue: Packet | undefined; + task: Task; + state: number; + + constructor(link: TaskControlBlock | undefined, id: number, priority: number, queue: Packet | undefined, task: Task) { + this.link = link; + this.id = id; + this.priority = priority; + this.queue = queue; + this.task = task; + if (queue === undefined) { + this.state = STATE_SUSPENDED; + } else { + this.state = STATE_SUSPENDED_RUNNABLE; + } + } + + setRunning(): void { + this.state = STATE_RUNNING; + } + + markAsNotHeld(): void { + this.state = this.state & STATE_NOT_HELD; + } + + markAsHeld(): void { + this.state = this.state | STATE_HELD; + } + + isHeldOrSuspended(): boolean { + return (this.state & STATE_HELD) !== 0 || (this.state === STATE_SUSPENDED); + } + + markAsSuspended(): void { + this.state = this.state | STATE_SUSPENDED; + } + + markAsRunnable(): void { + this.state = this.state | STATE_RUNNABLE; + } + + run(): TaskControlBlock | undefined { + let packet: Packet | undefined; + if (this.state === STATE_SUSPENDED_RUNNABLE) { + packet = this.queue; + this.queue = packet?.link; + if (this.queue === undefined) { this.state = STATE_RUNNING; - } - - markAsNotHeld(): void { - this.state = this.state & STATE_NOT_HELD; - } - - markAsHeld(): void { - this.state = this.state | STATE_HELD; - } - - isHeldOrSuspended(): boolean { - return (this.state & STATE_HELD) !== 0 || (this.state === STATE_SUSPENDED); - } - - markAsSuspended(): void { - this.state = this.state | STATE_SUSPENDED; - } - - markAsRunnable(): void { - this.state = this.state | STATE_RUNNABLE; - } - - run(): TaskControlBlock | undefined { - let packet: Packet | undefined; - if (this.state === STATE_SUSPENDED_RUNNABLE) { - packet = this.queue; - this.queue = packet?.link; - if (this.queue === undefined) { - this.state = STATE_RUNNING; - } else { - this.state = STATE_RUNNABLE; - } - } else { - packet = undefined; - } - return this.task.run(packet); - } - - checkPriorityAdd(taskControlBlock: TaskControlBlock, packet: Packet): TaskControlBlock | undefined { - if (this.queue === undefined) { - this.queue = packet; - this.markAsRunnable(); - if (this.priority > taskControlBlock.priority) { - return this; - } - } else { - this.queue = packet.addTo(this.queue); - } - return taskControlBlock; - } - - toString(): string { - return `tcb { ${this.task}@${this.state} }` ; - } + } else { + this.state = STATE_RUNNABLE; + } + } else { + packet = undefined; + } + return this.task.run(packet); + } + + checkPriorityAdd(taskControlBlock: TaskControlBlock, packet: Packet): TaskControlBlock | undefined { + if (this.queue === undefined) { + this.queue = packet; + this.markAsRunnable(); + if (this.priority > taskControlBlock.priority) { + return this; + } + } else { + this.queue = packet.addTo(this.queue); + } + return taskControlBlock; + } + + toString(): string { + return `tcb { ${this.task}@${this.state} }`; + } } const STATE_RUNNING = 0; @@ -203,191 +207,191 @@ const STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE; const STATE_NOT_HELD = ~STATE_HELD; interface Task { - run(packet: Packet|undefined): TaskControlBlock | undefined; + run(packet: Packet | undefined): TaskControlBlock | undefined; } class IdleTask implements Task { - scheduler: Scheduler; - v1: number; - count: number; - - constructor(scheduler: Scheduler, v1: number, count: number) { - this.scheduler = scheduler; - this.v1 = v1; - this.count = count; - } - - run(packet: Packet|undefined): TaskControlBlock | undefined { - this.count -= 1; - if (this.count === 0) { - return this.scheduler.holdCurrent(); - } - if ((this.v1 & 1) === 0) { - this.v1 = this.v1 >> 1; - return this.scheduler.release(ID_DEVICE_A); - } else { - this.v1 = (this.v1 >> 1) ^ 0xD008; - return this.scheduler.release(ID_DEVICE_B); - } - } - - toString(): string { - return "IdleTask"; - } + scheduler: Scheduler; + v1: number; + count: number; + + constructor(scheduler: Scheduler, v1: number, count: number) { + this.scheduler = scheduler; + this.v1 = v1; + this.count = count; + } + + run(packet: Packet | undefined): TaskControlBlock | undefined { + this.count -= 1; + if (this.count === 0) { + return this.scheduler.holdCurrent(); + } + if ((this.v1 & 1) === 0) { + this.v1 = this.v1 >> 1; + return this.scheduler.release(ID_DEVICE_A); + } else { + this.v1 = (this.v1 >> 1) ^ 0xD008; + return this.scheduler.release(ID_DEVICE_B); + } + } + + toString(): string { + return "IdleTask"; + } } class DeviceTask implements Task { - scheduler: Scheduler; - v1: Packet | undefined; - - constructor(scheduler: Scheduler) { - this.scheduler = scheduler; - this.v1 = undefined; - } - - run(packet: Packet|undefined): TaskControlBlock | undefined { - if (packet === undefined) { - if (this.v1 === undefined) { - return this.scheduler.suspendCurrent(); - } - const v = this.v1; - this.v1 = undefined; - return this.scheduler.queue(v); - } else { - this.v1 = packet; - return this.scheduler.holdCurrent(); - } - } - - toString(): string { - return "DeviceTask"; - } + scheduler: Scheduler; + v1: Packet | undefined; + + constructor(scheduler: Scheduler) { + this.scheduler = scheduler; + this.v1 = undefined; + } + + run(packet: Packet | undefined): TaskControlBlock | undefined { + if (packet === undefined) { + if (this.v1 === undefined) { + return this.scheduler.suspendCurrent(); + } + const v = this.v1; + this.v1 = undefined; + return this.scheduler.queue(v); + } else { + this.v1 = packet; + return this.scheduler.holdCurrent(); + } + } + + toString(): string { + return "DeviceTask"; + } } class WorkerTask implements Task { - scheduler: Scheduler; - v1: number; - v2: number; - - constructor(scheduler: Scheduler, v1: number, v2: number) { - this.scheduler = scheduler; - this.v1 = v1; - this.v2 = v2; - } - - run(packet: Packet|undefined): TaskControlBlock | undefined { - if (packet === undefined) { - return this.scheduler.suspendCurrent(); - } else { - if (this.v1 === ID_HANDLER_A) { - this.v1 = ID_HANDLER_B; - } else { - this.v1 = ID_HANDLER_A; - } - packet.id = this.v1; - packet.a1 = 0; - for (let i = 0; i < DATA_SIZE; i++) { - this.v2 += 1; - if (this.v2 > 26) { - this.v2 = 1; - } - packet.a2[i] = this.v2; - } - return this.scheduler.queue(packet); + scheduler: Scheduler; + v1: number; + v2: number; + + constructor(scheduler: Scheduler, v1: number, v2: number) { + this.scheduler = scheduler; + this.v1 = v1; + this.v2 = v2; + } + + run(packet: Packet | undefined): TaskControlBlock | undefined { + if (packet === undefined) { + return this.scheduler.suspendCurrent(); + } else { + if (this.v1 === ID_HANDLER_A) { + this.v1 = ID_HANDLER_B; + } else { + this.v1 = ID_HANDLER_A; + } + packet.id = this.v1; + packet.a1 = 0; + for (let i = 0; i < DATA_SIZE; i++) { + this.v2 += 1; + if (this.v2 > 26) { + this.v2 = 1; } + packet.a2[i] = this.v2; + } + return this.scheduler.queue(packet); } - - toString(): string { - return "WorkerTask"; - } + } + + toString(): string { + return "WorkerTask"; + } } class HandlerTask implements Task { - scheduler: Scheduler; - v1: Packet | undefined; - v2: Packet | undefined; - - constructor(scheduler: Scheduler) { - this.scheduler = scheduler; - this.v1 = undefined; - this.v2 = undefined; + scheduler: Scheduler; + v1: Packet | undefined; + v2: Packet | undefined; + + constructor(scheduler: Scheduler) { + this.scheduler = scheduler; + this.v1 = undefined; + this.v2 = undefined; + } + + run(packet: Packet | undefined): TaskControlBlock | undefined { + if (packet !== undefined) { + if (packet.kind === KIND_WORK) { + this.v1 = packet.addTo(this.v1); + } else { + this.v2 = packet.addTo(this.v2); + } } - - run(packet: Packet|undefined): TaskControlBlock | undefined { - if (packet !== undefined) { - if (packet.kind === KIND_WORK) { - this.v1 = packet.addTo(this.v1); - } else { - this.v2 = packet.addTo(this.v2); - } - } - - if (this.v1 !== undefined) { - const count = this.v1.a1; - let v: Packet | undefined; - if (count < DATA_SIZE) { - if (this.v2 !== undefined) { - v = this.v2; - this.v2 = this.v2.link; - v.a1 = this.v1.a2[count]; - this.v1.a1 = count + 1; - return this.scheduler.queue(v); - } - } else { - v = this.v1; - this.v1 = this.v1.link; - return this.scheduler.queue(v); - } + + if (this.v1 !== undefined) { + const count = this.v1.a1; + let v: Packet | undefined; + if (count < DATA_SIZE) { + if (this.v2 !== undefined) { + v = this.v2; + this.v2 = this.v2.link; + v.a1 = this.v1.a2[count]; + this.v1.a1 = count + 1; + return this.scheduler.queue(v); } - - return this.scheduler.suspendCurrent(); - } - - toString(): string { - return "HandlerTask"; + } else { + v = this.v1; + this.v1 = this.v1.link; + return this.scheduler.queue(v); + } } + + return this.scheduler.suspendCurrent(); + } + + toString(): string { + return "HandlerTask"; + } } -const DATA_SIZE:number = 4; +const DATA_SIZE: number = 4; class Packet { - link: Packet | undefined; - id: number; - kind: number; - a1: number; - a2: number[]; - - constructor(link: Packet | undefined, id: number, kind: number) { - this.link = link; - this.id = id; - this.kind = kind; - this.a1 = 0; - this.a2 = Array(DATA_SIZE).fill(0); - } - - addTo(queue: Packet | undefined): Packet { - this.link = undefined; - if (queue === undefined) { - return this; - } - let peek: Packet | undefined; - let next = queue; - while (next !== undefined) { - peek = next.link; - if (peek === undefined) { - break; - } - next = peek; - } - if (next !== undefined) { - next.link = this; - } - return queue; - } - - toString(): string { - return "Packet"; - } + link: Packet | undefined; + id: number; + kind: number; + a1: number; + a2: number[]; + + constructor(link: Packet | undefined, id: number, kind: number) { + this.link = link; + this.id = id; + this.kind = kind; + this.a1 = 0; + this.a2 = Array(DATA_SIZE).fill(0); + } + + addTo(queue: Packet | undefined): Packet { + this.link = undefined; + if (queue === undefined) { + return this; + } + let peek: Packet | undefined; + let next = queue; + while (next !== undefined) { + peek = next.link; + if (peek === undefined) { + break; + } + next = peek; + } + if (next !== undefined) { + next.link = this; + } + return queue; + } + + toString(): string { + return "Packet"; + } } const COUNT = 1000; @@ -395,36 +399,36 @@ const EXPECTED_QUEUE_COUNT = 2322; const EXPECTED_HOLD_COUNT = 928; function runRichards(): void { - const scheduler = new Scheduler(); - scheduler.addIdleTask(ID_IDLE, 0, undefined, COUNT); - - let queue = new Packet(undefined, ID_WORKER, KIND_WORK); - queue = new Packet(queue, ID_WORKER, KIND_WORK); - scheduler.addWorkerTask(ID_WORKER, 1000, queue); - - queue = new Packet(undefined, ID_DEVICE_A, KIND_DEVICE); - queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); - queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); - scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue); - - queue = new Packet(undefined, ID_DEVICE_B, KIND_DEVICE); - queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); - queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); - scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue); - - scheduler.addDeviceTask(ID_DEVICE_A, 4000, undefined); - - scheduler.addDeviceTask(ID_DEVICE_B, 5000, undefined); - - scheduler.schedule(); - - if (scheduler.queueCount !== EXPECTED_QUEUE_COUNT || - scheduler.holdCount !== EXPECTED_HOLD_COUNT) { - const msg = `Error during execution: queueCount = ${scheduler.queueCount}, holdCount = ${scheduler.holdCount}.` ; - throw Error(msg); - } + const scheduler = new Scheduler(); + scheduler.addIdleTask(ID_IDLE, 0, undefined, COUNT); + + let queue = new Packet(undefined, ID_WORKER, KIND_WORK); + queue = new Packet(queue, ID_WORKER, KIND_WORK); + scheduler.addWorkerTask(ID_WORKER, 1000, queue); + + queue = new Packet(undefined, ID_DEVICE_A, KIND_DEVICE); + queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); + queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); + scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue); + + queue = new Packet(undefined, ID_DEVICE_B, KIND_DEVICE); + queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); + queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); + scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue); + + scheduler.addDeviceTask(ID_DEVICE_A, 4000, undefined); + + scheduler.addDeviceTask(ID_DEVICE_B, 5000, undefined); + + scheduler.schedule(); + + if (scheduler.queueCount !== EXPECTED_QUEUE_COUNT || + scheduler.holdCount !== EXPECTED_HOLD_COUNT) { + const msg = `Error during execution: queueCount = ${scheduler.queueCount}, holdCount = ${scheduler.holdCount}.`; + throw Error(msg); + } } /**************************configure and run benchmark********************************/ -export {runRichards} +export { runRichards } diff --git a/OHBM/entry/src/main/ets/cases/performance/zlib/bitstream.ts b/OHBM/entry/src/main/ets/cases/performance/zlib/bitstream.ts new file mode 100644 index 0000000000000000000000000000000000000000..b6560c2076115d645becf007202feb80f8c7cca1 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/zlib/bitstream.ts @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function replaceNumberCount(count: number, element: number = 0): number[] { + let result = new Array(count).fill(element); + + return result; +} +const Number_2 = 2; +const Number_8 = 8; +const Number_16 = 16; +const Number_24 = 24; +const Number_32 = 32; +const Number_256 = 256; + +export function replace8NumberCount(count: number, element: number = 0): Uint8Array { + let result = new Uint8Array(count).fill(element); + + return result; +} + +export function replace16NumberCount(count: number, element: number = 0): Uint16Array { + let result = new Uint16Array(count).fill(element); + + return result; +} + +export function replace32NumberCount(count: number, element: number = 0): Uint32Array { + let result = new Uint32Array(count).fill(element); + + return result; +} + + +export class BitStream { + public static DefaultBlockSize = 0x8000; + private index: number; + private bitindex: number; + private buffer: Uint8Array; + public static ReverseTable: Uint8Array = new Uint8Array(0); + + constructor(buffer: Uint8Array, bufferPosition: number) { + BitStream.ReverseTable = this.getReverseTable(); + this.index = bufferPosition; + this.bitindex = 0; + this.buffer = buffer; + + if (this.buffer.length * Number_2 <= this.index) { + throw new Error('invalid index'); + } else if (this.buffer.length <= this.index) { + this.expandBuffer(); + } + } + + public expandBuffer(): Uint8Array { + let oldbuf: Uint8Array = this.buffer; + let il: number = oldbuf.length; + // copy buffer + let uint8Buffer: Uint8Array = replace8NumberCount(il << 1); + uint8Buffer.set(oldbuf); + this.buffer = uint8Buffer; + return this.buffer; + } + + public writeBits(number: number, n: number, reverse: boolean = false): void { + let numberTemp: number = number; + let buffer: Uint8Array = this.buffer; + let index: number = this.index; + let bitindex: number = this.bitindex; + + let current: number = buffer[index]; + + const rev32_ = (num: number): number => { + let A = BitStream.ReverseTable[num & 0xff] << Number_24; + let B = BitStream.ReverseTable[(num >>> Number_8) & 0xff] << Number_16; + let C = BitStream.ReverseTable[(num >>> Number_16) & 0xff] << Number_8; + let D = BitStream.ReverseTable[(num >>> Number_24) & 0xff]; + return A | B | C | D; + }; + + if (reverse && n > 1) { + numberTemp = + n > Number_8 ? rev32_(numberTemp) >> (Number_32 - n) : BitStream.ReverseTable[numberTemp] >> (Number_8 - n); + } + + if (n + bitindex < Number_8) { + current = (current << n) | numberTemp; + bitindex += n; + } else { + for (let i = 0; i < n; ++i) { + current = (current << 1) | ((numberTemp >> (n - i - 1)) & 1); + + // next byte + bitindex += 1; + if (bitindex === Number_8) { + bitindex = 0; + buffer[index] = BitStream.ReverseTable[current]; + index += 1; + current = 0; + + // expand + if (index === buffer.length) { + buffer = this.expandBuffer(); + } + } + } + } + buffer[index] = current; + this.buffer = buffer; + this.bitindex = bitindex; + this.index = index; + } + + public finish(): Uint8Array { + let buffer: Uint8Array = this.buffer; + let index: number = this.index; + let output: Uint8Array; + + if (this.bitindex > 0) { + buffer[index] <<= Number_8 - this.bitindex; + buffer[index] = BitStream.ReverseTable[buffer[index]]; + index += 1; + } + // array truncation + + output = buffer.subarray(0, index); + + return output; + } + + private getReverseTable(): Uint8Array { + let table = replace8NumberCount(Number_256); + + for (let i = 0; i < Number_256; ++i) { + table[i] = ((n): number => { + let N = n; + let r = n; + let s = 7; + N >>>= 1; + while (N != 0) { + r <<= 1; + r |= N & 1; + s -= 1; + N >>>= 1; + } + return ((r << s) & 0xff) >>> 0; + })(i); + } + return table; + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/zlib/heap.ts b/OHBM/entry/src/main/ets/cases/performance/zlib/heap.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee10c5f5c9f5b5a75d9ca9ee7043bf7766c522e9 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/zlib/heap.ts @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { replace16NumberCount } from './bitstream'; + +export class PopObject { + index: number; + value: number; + length: number; +} +const Number_2 = 2; +const Number_4 = 4; + +export class Heap { + public buffer: Uint16Array; + public length: number; + + constructor(length: number) { + this.buffer = replace16NumberCount(length * Number_2); + this.length = 0; + } + + public getParent(index: number): number { + return (((index - Number_2) / Number_4) | 0) * Number_2; + } + + public getChild(index: number): number { + return Number_2 * index + Number_2; + } + + public push(index: number, value: number): number { + let current: number = this.length; + let parent: number; + let heap = this.buffer; + let swap: number; + + heap[this.length] = value; + heap[this.length + 1] = index; + this.length += 2; + + while (current > 0) { + parent = this.getParent(current); + // check heap + if (heap[current] > heap[parent]) { + swap = heap[current]; + heap[current] = heap[parent]; + heap[parent] = swap; + // switch heap + swap = heap[current + 1]; + heap[current + 1] = heap[parent + 1]; + heap[parent + 1] = swap; + // set current + current = parent; + } else { + break; + } + } + // return + return this.length; + } + + public pop(): PopObject { + let parent: number = 0; + let current: number = 0; + let index: number = 0; + let swap: number = 0; + let value: number = 0; + + let heap: Uint16Array = this.buffer; + value = heap[0]; + index = heap[1]; + // set heap + this.length -= 2; + heap[0] = heap[this.length]; + heap[1] = heap[this.length + 1]; + // reset parent + parent = 0; + while (true) { + current = this.getChild(parent); + // check length + if (current >= this.length) { + break; + } + + if (current + Number_2 < this.length && heap[current + Number_2] > heap[current]) { + current += Number_2; + } + //// check heap + if (heap[current] > heap[parent]) { + swap = heap[parent]; + heap[parent] = heap[current]; + heap[current] = swap; + // switch heap + swap = heap[parent + 1]; + heap[parent + 1] = heap[current + 1]; + heap[current + 1] = swap; + } else { + // break + break; + } + // reset + parent = current; + } + + return { index: index, value: value, length: this.length }; + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/zlib/huffman.ts b/OHBM/entry/src/main/ets/cases/performance/zlib/huffman.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3cb1c381217cef0683b0f00911ef5719798eb23 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/zlib/huffman.ts @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { replace32NumberCount } from './bitstream'; +const Number_16 = 16; + +export class Huffman { + constructor() {} + + public static buildHuffmanTable(lengths: Uint8Array): [Uint32Array, number, number] { + let listSize: number = lengths.length; + let maxCodeLength: number = 0; + let minCodeLength: number = Number.POSITIVE_INFINITY; + let size: number; + let table: Uint32Array; + let bitLength: number = 1; + let code: number = 0; + let skip: number = 2; + let reversed: number; + let rtemp: number; + let j: number; + let value: number; + + for (let i = 0; i < listSize; ++i) { + if (lengths[i] > maxCodeLength) { + maxCodeLength = lengths[i]; + } + if (lengths[i] < minCodeLength) { + minCodeLength = lengths[i]; + } + } + + size = 1 << maxCodeLength; + table = replace32NumberCount(size); + while (bitLength <= maxCodeLength) { + let i = 0; + while (i < listSize) { + if (lengths[i] === bitLength) { + reversed = 0; + rtemp = code; + j = 0; + + while (j < bitLength) { + reversed = (reversed << 1) | (rtemp & 1); + rtemp >>= 1; + j += 1; + } + + value = (bitLength << Number_16) | i; + j = reversed; + + while (j < size) { + table[j] = value; + j += skip; + } + + code += 1; + } + i += 1; + } + + bitLength += 1; + code <<= 1; + skip <<= 1; + } + + return [table, maxCodeLength, minCodeLength]; + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/zlib/rawdeflate.ts b/OHBM/entry/src/main/ets/cases/performance/zlib/rawdeflate.ts new file mode 100644 index 0000000000000000000000000000000000000000..cde2bb337ffdab1f777a7cc87ddf6c175a2954fe --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/zlib/rawdeflate.ts @@ -0,0 +1,1057 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + BitStream, + replaceNumberCount, + replace8NumberCount, + replace16NumberCount, + replace32NumberCount +} from './bitstream'; +import { Heap, PopObject } from './heap'; + +const Number_2 = 2; +const Number_3 = 3; +const Number_4 = 4; +const Number_5 = 5; +const Number_6 = 6; +const Number_7 = 7; +const Number_8 = 8; +const Number_9 = 9; +const Number_10 = 10; +const Number_11 = 11; +const Number_12 = 12; +const Number_14 = 14; +const Number_15 = 15; +const Number_16 = 16; +const Number_17 = 17; +const Number_18 = 18; +const Number_19 = 19; +const Number_22 = 22; +const Number_24 = 24; +const Number_26 = 26; +const Number_30 = 30; +const Number_32 = 32; +const Number_34 = 34; +const Number_42 = 42; +const Number_48 = 48; +const Number_50 = 50; +const Number_58 = 58; +const Number_64 = 64; +const Number_66 = 66; +const Number_82 = 82; +const Number_96 = 96; +const Number_98 = 98; +const Number_114 = 114; +const Number_128 = 128; +const Number_130 = 130; +const Number_138 = 138; +const Number_143 = 143; +const Number_162 = 162; +const Number_192 = 192; +const Number_194 = 194; +const Number_226 = 226; +const Number_255 = 255; +const Number_256 = 256; +const Number_257 = 257; +const Number_258 = 258; +const Number_279 = 279; +const Number_286 = 286; +const Number_287 = 287; +const Number_288 = 288; +const Number_384 = 384; +const Number_512 = 512; +const Number_768 = 768; +const Number_1024 = 1024; +const Number_1536 = 1536; +const Number_2048 = 2048; +const Number_3072 = 3072; +const Number_4096 = 4096; +const Number_6144 = 6144; +const Number_8192 = 8192; +const Number_12288 = 12288; +const Number_16384 = 16384; +const Number_24576 = 24576; +const Number_32768 = 32768; + +export function replacePopObjectArray(count: number, element: PopObject): Array { + let result: PopObject[] = new Array(); + for (let i = 0; i < count; i++) { + result.push(element); + } + return result; +} + +export class OptionParams { + lazy?: number; + compressionType?: CompressionType; + outputBuffer?: Uint8Array; + outputIndex?: number; +} + +interface TreeSymbols { + codes: Uint32Array; + freqs: Uint8Array; +} + +export enum CompressionType { + NONE = 0, + FIXED = 1, + DYNAMIC = Number_2, + RESERVED = Number_3 +} + +export class Lz77Match { + public length: number; + public backwardDistance: number; + + constructor(length: number, backwardDistance: number) { + this.length = length; + this.backwardDistance = backwardDistance; + } + + static get LengthCodeTable(): Uint32Array { + const code = (length: number): number[] => { + switch (true) { + case length === Number_3: + return [257, length - 3, 0]; + case length === Number_4: + return [258, length - 4, 0]; + case length === Number_5: + return [259, length - 5, 0]; + case length === Number_6: + return [260, length - 6, 0]; + case length === Number_7: + return [261, length - 7, 0]; + case length === Number_8: + return [262, length - 8, 0]; + case length === Number_9: + return [263, length - 9, 0]; + case length === Number_10: + return [264, length - 10, 0]; + case length <= Number_12: + return [265, length - 11, 1]; + case length <= Number_14: + return [266, length - 13, 1]; + case length <= Number_16: + return [267, length - 15, 1]; + case length <= Number_18: + return [268, length - 17, 1]; + case length <= Number_22: + return [269, length - 19, 2]; + case length <= Number_26: + return [270, length - 23, 2]; + case length <= Number_30: + return [271, length - 27, 2]; + case length <= Number_34: + return [272, length - 31, 2]; + case length <= Number_42: + return [273, length - 35, 3]; + case length <= Number_50: + return [274, length - 43, 3]; + case length <= Number_58: + return [275, length - 51, 3]; + case length <= Number_66: + return [276, length - 59, 3]; + case length <= Number_82: + return [277, length - 67, 4]; + case length <= Number_98: + return [278, length - 83, 4]; + case length <= Number_114: + return [279, length - 99, 4]; + case length <= Number_130: + return [280, length - 115, 4]; + case length <= Number_162: + return [281, length - 131, 5]; + case length <= Number_194: + return [282, length - 163, 5]; + case length <= Number_226: + return [283, length - 195, 5]; + case length <= Number_257: + return [284, length - 227, 5]; + case length === Number_258: + return [285, length - 258, 0]; + default: + throw new Error(`invalid length: ${length}`); + } + }; + + let table: Uint32Array = replace32NumberCount(Number_258); + let c: number[] = []; + + for (let i = 3; i <= Number_258; i++) { + c = code(i); + table[i] = (c[Number_2] << Number_24) | (c[1] << Number_16) | c[0]; + } + + return table; + } + + public getDistanceCode_(dist: number): number[] { + /** @type {!Array.} distance code table. */ + let r: number[]; + switch (true) { + case dist === 1: + r = [0, dist - 1, 0]; + break; + case dist === Number_2: + r = [1, dist - 2, 0]; + break; + case dist === Number_3: + r = [2, dist - 3, 0]; + break; + case dist === Number_4: + r = [3, dist - 4, 0]; + break; + case dist <= Number_6: + r = [4, dist - 5, 1]; + break; + case dist <= Number_8: + r = [5, dist - 7, 1]; + break; + case dist <= Number_12: + r = [6, dist - 9, 2]; + break; + case dist <= Number_16: + r = [7, dist - 13, 2]; + break; + case dist <= Number_24: + r = [8, dist - 17, 3]; + break; + case dist <= Number_32: + r = [9, dist - 25, 3]; + break; + case dist <= Number_48: + r = [10, dist - 33, 4]; + break; + case dist <= Number_64: + r = [11, dist - 49, 4]; + break; + case dist <= Number_96: + r = [12, dist - 65, 5]; + break; + case dist <= Number_128: + r = [13, dist - 97, 5]; + break; + case dist <= Number_192: + r = [14, dist - 129, 6]; + break; + case dist <= Number_256: + r = [15, dist - 193, 6]; + break; + case dist <= Number_384: + r = [16, dist - 257, 7]; + break; + case dist <= Number_512: + r = [17, dist - 385, 7]; + break; + case dist <= Number_768: + r = [18, dist - 513, 8]; + break; + case dist <= Number_1024: + r = [19, dist - 769, 8]; + break; + case dist <= Number_1536: + r = [20, dist - 1025, 9]; + break; + case dist <= Number_2048: + r = [21, dist - 1537, 9]; + break; + case dist <= Number_3072: + r = [22, dist - 2049, 10]; + break; + case dist <= Number_4096: + r = [23, dist - 3073, 10]; + break; + case dist <= Number_6144: + r = [24, dist - 4097, 11]; + break; + case dist <= Number_8192: + r = [25, dist - 6145, 11]; + break; + case dist <= Number_12288: + r = [26, dist - 8193, 12]; + break; + case dist <= Number_16384: + r = [27, dist - 12289, 12]; + break; + case dist <= Number_24576: + r = [28, dist - 16385, 13]; + break; + case dist <= Number_32768: + r = [29, dist - 24577, 13]; + break; + default: + throw new Error('invalid distance'); + } + return r; + } + + public toLz77Array(): number[] { + let length: number = this.length; + let dist: number = this.backwardDistance; + let codeArray: number[] = []; + let pos: number = 0; + let code: number; + + // length + code = Lz77Match.LengthCodeTable[length]; + codeArray[pos] = code & 0xffff; + codeArray[pos + 1] = (code >> Number_16) & 0xff; + codeArray[pos + Number_2] = code >> Number_24; + pos += Number_3; + // distance + let distCode = this.getDistanceCode_(dist); + codeArray[pos] = distCode[0]; + codeArray[pos + 1] = distCode[1]; + codeArray[pos + Number_2] = distCode[Number_2]; + + return codeArray; + } +} + +/** + * @Generator + */ +export class RawDeflate { + public compressionType: CompressionType; + public lazy: number = 0; + public freqsLitLen: Uint32Array = new Uint32Array(0); + public freqsDist: Uint32Array = new Uint32Array(0); + public input: Uint8Array = new Uint8Array(0); + public output: Uint8Array = new Uint8Array(0); + public op: number = 0; + public length: number = 0; + public backwardDistance: number; + public static Lz77MaxLength: number = Number_258; + public static WindowSize: number = 0x8000; + public static MaxCodeLength: number = Number_16; + public static HUFMAX: number = Number_286; + public static Lz77MinLength: number = Number_3; + + constructor(input: Uint8Array, opt_params?: OptionParams) { + this.compressionType = CompressionType.DYNAMIC; + this.lazy = 0; + this.length = 0; + this.backwardDistance = 0; + this.input = input; + this.op = 0; + // option parameters + if (opt_params != null) { + if (opt_params.lazy != null) { + this.lazy = opt_params.lazy; + } + if (typeof opt_params.compressionType === 'number') { + this.compressionType = opt_params.compressionType; + } + if (opt_params.outputBuffer != null) { + this.output = opt_params.outputBuffer; + } + if (typeof opt_params.outputIndex === 'number') { + this.op = opt_params.outputIndex; + } + } + + this.output = replace8NumberCount(0x8000); + } + + public static get FixedHuffmanTable(): number[][] { + let table: number[][] = []; + + for (let i = 0; i < Number_288; i++) { + switch (true) { + case i <= Number_143: + table.push([i + 0x030, 8]); + break; + case i <= Number_255: + table.push([i - 144 + 0x190, 9]); + break; + case i <= Number_279: + table.push([i - 256 + 0x000, 7]); + break; + case i <= Number_287: + table.push([i - 280 + 0x0c0, 8]); + break; + default: + throw new Error(`invalid literal: ${i}`); + } + } + return table; + } + + public compress(): Uint8Array { + let blockArray: Uint8Array; + let input: Uint8Array = this.input; + + switch (this.compressionType) { + case CompressionType.NONE: + // each 65535-Byte (length header: 16-bit) + let position: number = 0; + let length: number = input.length; + while (position < length) { + blockArray = input.subarray(position, position + 0xffff); + position += blockArray.length; + this.makeNocompressBlock(blockArray, position === length); + } + break; + case CompressionType.FIXED: + this.output = this.makeFixedHuffmanBlock(input, true); + this.op = this.output.length; + break; + case CompressionType.DYNAMIC: + this.output = this.makeDynamicHuffmanBlock(input, true); + this.op = this.output.length; + break; + default: + throw new Error('invalid compression type'); + } + return this.output; + } + + public makeNocompressBlock(blockArray: Uint8Array, isFinalBlock: boolean): Uint8Array { + let bfinal: number; + let btype: CompressionType; + let len: number; + let nlen: number; + + let output: Uint8Array = this.output; + let op: number = this.op; + + // expand buffer + + output = new Uint8Array(this.output.buffer); + while (output.length <= op + blockArray.length + Number_5) { + output = replace8NumberCount(output.length << 1); + } + output.set(this.output); + + // header + bfinal = isFinalBlock ? 1 : 0; + btype = CompressionType.NONE; + output[op] = bfinal | (btype << 1); + + // length + len = blockArray.length; + nlen = (~len + 0x10000) & 0xffff; + output[op + 1] = len & 0xff; + output[op + Number_2] = (len >>> Number_8) & 0xff; + output[op + Number_3] = nlen & 0xff; + output[op + Number_4] = (nlen >>> Number_8) & 0xff; + op += Number_5; + // copy buffer + + output.set(blockArray, op); + op += blockArray.length; + output = output.subarray(0, op); + + this.op = op; + this.output = output; + + return output; + } + + public makeFixedHuffmanBlock(blockArray: Uint8Array, isFinalBlock: boolean): Uint8Array { + let stream: BitStream = new BitStream(new Uint8Array(this.output.buffer), this.op); + let bfinal: number = 0; + let btype: CompressionType = CompressionType.FIXED; + let data: Uint16Array; + + // header + bfinal = isFinalBlock ? 1 : 0; + + stream.writeBits(bfinal, 1, true); + stream.writeBits(btype, Number_2, true); + + data = this.lz77(blockArray); + this.fixedHuffman(data, stream); + + return stream.finish(); + } + + public makeDynamicHuffmanBlock(blockArray: Uint8Array, isFinalBlock: boolean): Uint8Array { + let stream = new BitStream(this.output, this.op); + let bfinal: number; + let btype: CompressionType; + let data: Uint16Array; + let hlit: number = 0; + let hdist: number = 0; + let hclen: number = 0; + let hclenOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + let litLenLengths: Uint8Array; + let litLenCodes: Uint16Array; + let distLengths: Uint8Array; + let distCodes: Uint16Array; + let treeSymbols: TreeSymbols; + let treeLengths: Uint8Array; + let transLengths: number[] = replaceNumberCount(Number_19); + let treeCodes: Uint16Array; + let code: number; + let bitlen: number; + + // header + bfinal = isFinalBlock ? 1 : 0; + btype = CompressionType.DYNAMIC; + + stream.writeBits(bfinal, 1, true); + stream.writeBits(btype, Number_2, true); + + data = this.lz77(blockArray); + + litLenLengths = this.getLengths_(this.freqsLitLen, Number_15); + litLenCodes = this.getCodesFromLengths_(litLenLengths); + distLengths = this.getLengths_(this.freqsDist, Number_7); + distCodes = this.getCodesFromLengths_(distLengths); + + for (hlit = Number_286; hlit > Number_257; hlit--) { + if (litLenLengths[hlit - 1] === 0) { + break; + } + } + for (hdist = Number_30; hdist > 1; hdist--) { + if (distLengths[hdist - 1] === 0) { + break; + } + } + + // HCLEN + treeSymbols = this.getTreeSymbols_(hlit, litLenLengths, hdist, distLengths); + treeLengths = this.getLengths_(treeSymbols.freqs, Number_7); + for (let i = 0; i < Number_19; i++) { + transLengths[i] = treeLengths[hclenOrder[i]]; + } + for (hclen = Number_19; hclen > Number_4; hclen--) { + if (transLengths[hclen - 1] === 0) { + break; + } + } + + treeCodes = this.getCodesFromLengths_(treeLengths); + + stream.writeBits(hlit - Number_257, Number_5, true); + stream.writeBits(hdist - 1, Number_5, true); + stream.writeBits(hclen - Number_4, Number_4, true); + for (let i = 0; i < hclen; i++) { + stream.writeBits(transLengths[i], Number_3, true); + } + + let i: number = 0; + let il: number = treeSymbols.codes.length; + while (i < il) { + code = treeSymbols.codes[i]; + + stream.writeBits(treeCodes[code], treeLengths[code], true); + // extra bits + if (code >= Number_16) { + i += 1; + switch (code) { + case Number_16: + bitlen = Number_2; + break; + case Number_17: + bitlen = Number_3; + break; + case Number_18: + bitlen = Number_7; + break; + default: + throw new Error(`invalid code: ${code}`); + } + + stream.writeBits(treeSymbols.codes[i], bitlen, true); + } + + i += 1; + } + + this.dynamicHuffman(data, [litLenCodes, litLenLengths], [distCodes, distLengths], stream); + + return stream.finish(); + } + + public dynamicHuffman( + dataArray: Uint16Array, + litLen: [Uint16Array, Uint8Array], + dist: [Uint16Array, Uint8Array], + stream: BitStream + ): BitStream { + let index: number = 0; + let literal: number; + let code: number; + let litLenCodes: Uint16Array = litLen[0]; + let litLenLengths: Uint8Array = litLen[1]; + let distCodes: Uint16Array = dist[0]; + let distLengths: Uint8Array = dist[1]; + + // write symbols to BitStream + while (index < dataArray.length) { + literal = dataArray[index]; + + // literal or length + stream.writeBits(litLenCodes[literal], litLenLengths[literal], true); + + // length/distance symbol + if (literal > Number_256) { + // length extra + stream.writeBits(dataArray[index + 1], dataArray[index + Number_2], true); + // distance + code = dataArray[index + Number_3]; + stream.writeBits(distCodes[code], distLengths[code], true); + // distance extra + stream.writeBits(dataArray[index + Number_4], dataArray[index + Number_5], true); + // end marker + index += Number_5; + } else if (literal === Number_256) { + break; + } + index += 1; + } + + return stream; + } + + public fixedHuffman(dataArray: Uint16Array, stream: BitStream): BitStream { + let index: number = 0; + let literal: number; + while (index < dataArray.length) { + literal = dataArray[index]; + stream.writeBits(RawDeflate.FixedHuffmanTable[literal][0], RawDeflate.FixedHuffmanTable[literal][1]); + if (literal > 0x100) { + stream.writeBits(dataArray[index + 1], dataArray[index + Number_2], true); + stream.writeBits(dataArray[index + Number_3], Number_5); + stream.writeBits(dataArray[index + Number_4], dataArray[index + Number_5], true); + index += Number_5; + } else if (literal === 0x100) { + break; + } + } + return stream; + } + + public lz77(dataArray: Uint8Array): Uint16Array { + let matchKey: number = 0; + let table: Record = {}; + let windowSize: number = RawDeflate.WindowSize; + let matchsList: number[] = []; + let longestMatch: Lz77Match; + let prevMatch: Lz77Match | null = null; + let lz77buf: Uint16Array = replace16NumberCount(dataArray.length * Number_2); + let pos: number = 0; + let skipLength: number = 0; + let freqsLitLen: Uint32Array = replace32NumberCount(Number_286); + let freqsDist: Uint32Array = replace32NumberCount(Number_30); + let lazy: number = this.lazy; + let tmp: number; + + freqsLitLen[Number_256] = 1; + const writeMatch = (match: Lz77Match, offset: number): void => { + let lz77Array = match.toLz77Array(); + + for (let i = 0; i < lz77Array.length; ++i) { + lz77buf[pos] = lz77Array[i]; + pos += 1; + } + + freqsLitLen[lz77Array[0]] += 1; + freqsDist[lz77Array[Number_3]] += 1; + skipLength = match.length + offset - 1; + prevMatch = null; + }; + + for (let position = 0; position < dataArray.length; ++position) { + for (let i = 0; i < RawDeflate.Lz77MinLength; ++i) { + if (position + i === dataArray.length) { + break; + } + matchKey = (matchKey << Number_8) | dataArray[position + i]; + } + + if (table[matchKey] != null) { + matchsList = table[matchKey]; + } else { + matchsList = []; + table[matchKey] = matchsList; + } + + if (skipLength > 0) { + skipLength -= 1; + matchsList.push(position); + continue; + } + + while (matchsList.length > 0 && position - matchsList[0] > windowSize) { + matchsList.shift(); + } + + if (position + RawDeflate.Lz77MinLength >= dataArray.length) { + if (prevMatch != null) { + writeMatch(prevMatch, -1); + } + + for (let i = position; i < dataArray.length; ++i) { + tmp = dataArray[i]; + lz77buf[pos] = tmp; + pos += 1; + freqsLitLen[tmp] += 1; + } + + break; + } + + if (matchsList.length > 0) { + longestMatch = this.searchLongestMatch_(dataArray, position, matchsList); + if (prevMatch != null) { + if (prevMatch.length < longestMatch.length) { + // write previous literal + tmp = dataArray[position - 1]; + lz77buf[pos] = tmp; + pos += 1; + freqsLitLen[tmp] += 1; + + // write current match + writeMatch(longestMatch, 0); + } else { + // write previous match + writeMatch(prevMatch, -1); + } + } else if (longestMatch.length < lazy) { + prevMatch = longestMatch; + } else { + writeMatch(longestMatch, 0); + } + } else if (prevMatch != null) { + writeMatch(prevMatch, -1); + } else { + tmp = dataArray[position]; + lz77buf[pos] = tmp; + pos += 1; + freqsLitLen[tmp] += 1; + } + + matchsList.push(position); + } + + lz77buf[pos] = 256; + pos += 1; + freqsLitLen[Number_256] += 1; + this.freqsLitLen = freqsLitLen; + this.freqsDist = freqsDist; + /** @type {!(Uint16Array|Array.)} */ + return lz77buf.subarray(0, pos); + } + + public searchLongestMatch_(data: Uint8Array, position: number, matchList: number[]): Lz77Match { + let match: number; + let currentMatch: number | null = null; + let matchMax: number = 0; + let matchLength: number; + let dl: number = data.length; + + permatch: for (let i = 0; i < matchList.length; i++) { + match = matchList[matchList.length - i - 1]; + matchLength = RawDeflate.Lz77MinLength; + + if (matchMax > RawDeflate.Lz77MinLength) { + for (let j = matchMax; j > RawDeflate.Lz77MinLength; j--) { + if (data[match + j - 1] !== data[position + j - 1]) { + continue permatch; + } + } + matchLength = matchMax; + } + + while ( + matchLength < RawDeflate.Lz77MaxLength && + position + matchLength < dl && + data[match + matchLength] === data[position + matchLength] + ) { + matchLength += 1; + } + + if (matchLength > matchMax) { + currentMatch = match; + matchMax = matchLength; + } + + if (matchLength === RawDeflate.Lz77MaxLength) { + break; + } + } + return new Lz77Match(matchMax, position - (currentMatch ?? 0)); + } + + public getTreeSymbols_(hlit: number, litlenLengths: Uint8Array, hdist: number, distLengths: Uint8Array): TreeSymbols { + let src: Uint32Array = replace32NumberCount(hlit + hdist); + let j: number = 0; + let runLength: number; + let result = replace32NumberCount(Number_286 + Number_30); + let nResult: number; + let rpt: number; + let freqs: Uint8Array = replace8NumberCount(Number_19); + + for (let i = 0; i < hlit; i++) { + src[j] = litlenLengths[i]; + j += 1; + } + for (let i = 0; i < hdist; i++) { + src[j] = distLengths[i]; + j += 1; + } + + nResult = 0; + let i: number = 0; + while (i < src.length) { + j = 1; + while (i + j < src.length && src[i + j] === src[i]) { + j += 1; + } + + runLength = j; + + if (src[i] === 0) { + if (runLength < Number_3) { + while (runLength > 0) { + result[nResult] = 0; + freqs[0] += 1; + nResult += 1; + runLength -= 1; + } + } else { + while (runLength > 0) { + rpt = Math.min(runLength, Number_138); + + if (rpt > runLength - Number_3 && rpt < runLength) { + rpt = runLength - Number_3; + } + + if (rpt <= Number_10) { + result[nResult] = 17; + result[nResult + 1] = rpt - Number_3; + freqs[Number_17] += 1; + nResult += Number_2; + } else { + result[nResult] = 18; + result[nResult + 1] = rpt - Number_11; + freqs[Number_18] += 1; + nResult += Number_2; + } + + runLength -= rpt; + } + } + } else { + result[nResult] = src[i]; + nResult += 1; + freqs[src[i]] += 1; + runLength -= 1; + + if (runLength < Number_3) { + while (runLength > 0) { + result[nResult] = src[i]; + freqs[src[i]] += 1; + nResult += 1; + runLength -= 1; + } + } else { + while (runLength > 0) { + rpt = Math.min(runLength, Number_6); + + if (rpt > runLength - Number_3 && rpt < runLength) { + rpt = runLength - Number_3; + } + + result[nResult] = 16; + result[nResult + 1] = rpt - Number_3; + freqs[Number_16] += 1; + nResult += Number_2; + runLength -= rpt; + } + } + } + i += j; + } + return { codes: result.subarray(0, nResult), freqs: freqs }; + } + + public getLengths_(freqs: Uint8Array | Uint32Array, limit: number): Uint8Array { + let nSymbols: number = freqs.length; + let heap: Heap = new Heap(Number_2 * RawDeflate.HUFMAX); + let length: Uint8Array = replace8NumberCount(nSymbols); + let nodes: PopObject[]; + let values: Uint32Array; + let codeLength: Uint8Array; + + for (let i = 0; i < nSymbols; ++i) { + let intfreqs: number = freqs[i]; + if (intfreqs > 0) { + heap.push(i, intfreqs); + } + } + nodes = replacePopObjectArray(heap.length / Number_2, { + index: 0, + value: 0, + length: 0 + }); + values = replace32NumberCount(heap.length / Number_2); + + if (nodes.length === 1) { + length[heap.pop().index] = 1; + return length; + } + + for (let i = 0; i < heap.length / Number_2; ++i) { + nodes[i] = heap.pop(); + values[i] = nodes[i].value; + } + codeLength = this.reversePackageMerge_(values, values.length, limit); + + for (let i = 0; i < nodes.length; ++i) { + length[nodes[i].index] = codeLength[i]; + } + + return length; + } + + public reversePackageMerge_(freqs: Array | Uint32Array, symbols: number, limit: number): Uint8Array { + let minimumCost: Uint16Array = replace16NumberCount(limit); + let flag: Uint8Array = replace8NumberCount(limit); + let codeLength: Uint8Array = replace8NumberCount(symbols); + let value: number[][] = new Array(limit); + let type: number[][] = new Array(limit); + let currentPosition: number[] = replaceNumberCount(limit); + + let excess: number = (1 << limit) - symbols; + let half: number = 1 << (limit - 1); + let weight: number = 0; + let next: number = 0; + + const takePackage = (index: number): void => { + let x: number = type[index][currentPosition[index]]; + + if (x === symbols) { + takePackage(index + 1); + takePackage(index + 1); + } else { + codeLength[x] -= 1; + } + + currentPosition[index] += 1; + }; + + minimumCost[limit - 1] = symbols; + + for (let j = 0; j < limit; ++j) { + if (excess < half) { + flag[j] = 0; + } else { + flag[j] = 1; + excess -= half; + } + excess <<= 1; + minimumCost[limit - Number_2 - j] = ((minimumCost[limit - 1 - j] / Number_2) | 0) + symbols; + } + minimumCost[0] = flag[0]; + + value[0] = replaceNumberCount(minimumCost[0]); + type[0] = replaceNumberCount(minimumCost[0]); + for (let j = 1; j < limit; ++j) { + if (minimumCost[j] > Number_2 * minimumCost[j - 1] + flag[j]) { + minimumCost[j] = Number_2 * minimumCost[j - 1] + flag[j]; + } + value[j] = replaceNumberCount(minimumCost[j]); + type[j] = replaceNumberCount(minimumCost[j]); + } + + for (let i = 0; i < symbols; ++i) { + codeLength[i] = limit; + } + + for (let t = 0; t < minimumCost[limit - 1]; ++t) { + value[limit - 1][t] = freqs[t]; + type[limit - 1][t] = t; + } + + for (let i = 0; i < limit; ++i) { + currentPosition[i] = 0; + } + + if (flag[limit - 1] === 1) { + codeLength[0] -= 1; + currentPosition[limit - 1] += 1; + } + + let j: number = limit - Number_2; + while (j >= 0) { + let i: number = 0; + weight = 0; + next = currentPosition[j + 1]; + + let t: number = 0; + while (t < minimumCost[j]) { + weight = value[j + 1][next] + value[j + 1][next + 1]; + + if (weight > freqs[i]) { + value[j][t] = weight; + type[j][t] = symbols; + next += Number_2; + } else { + value[j][t] = freqs[i]; + type[j][t] = i; + i += 1; + } + t += 1; + } + + currentPosition[j] = 0; + if (flag[j] === 1) { + takePackage(j); + } + j -= 1; + } + + return codeLength; + } + + public getCodesFromLengths_(lengths: Uint8Array): Uint16Array { + let codes: Uint16Array = replace16NumberCount(lengths.length); + let count: number[] = []; + let startCode: number[] = []; + let code: number = 0; + + // Count the codes of each length. + for (let i = 0; i < lengths.length; i++) { + count[lengths[i]] = (count[lengths[i]] | 0) + 1; + } + + // Determine the starting code for each length block. + for (let i = 1; i <= RawDeflate.MaxCodeLength; i++) { + startCode[i] = code; + code += count[i] | 0; + code <<= 1; + } + + // Determine the code for each symbol. Mirrored, of course. + for (let i = 0; i < lengths.length; i++) { + code = startCode[lengths[i]]; + startCode[lengths[i]] += 1; + codes[i] = 0; + + for (let j = 0; j < lengths[i]; j++) { + codes[i] = (codes[i] << 1) | (code & 1); + code >>>= 1; + } + } + return codes; + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/zlib/rawinflate.ts b/OHBM/entry/src/main/ets/cases/performance/zlib/rawinflate.ts new file mode 100644 index 0000000000000000000000000000000000000000..75fc1a3be51761cef20ae45965afba4d36032bad --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/zlib/rawinflate.ts @@ -0,0 +1,697 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Huffman } from './huffman'; +import { replace8NumberCount } from './bitstream'; + +const Number_2 = 2; +const Number_3 = 3; +const Number_4 = 4; +const Number_5 = 5; +const Number_7 = 7; +const Number_8 = 8; +const Number_9 = 9; +const Number_11 = 11; +const Number_16 = 16; +const Number_17 = 17; +const Number_18 = 18; +const Number_30 = 30; +const Number_143 = 143; +const Number_255 = 255; +const Number_256 = 256; +const Number_257 = 257; +const Number_258 = 258; +const Number_279 = 279; +const Number_288 = 288; +const Number_32768 = 32768; +interface InOptionParams { + index: number; + bufferSize: number; + bufferType: number; + resize: boolean; +} + +interface ExpandOption { + fixRatio?: number; + addRatio?: number; +} + +enum BufferType { + BLOCK, + ADAPTIVE +} + +/* + *@Generator + */ +export class RawInflate { + public static ZLIB_RAW_INFLATE_BUFFER_SIZE: number = 0x8000; + public static buildHuffmanTable: (lengths: Uint8Array) => [Uint32Array, number, number] = Huffman.buildHuffmanTable; + public static MaxBackwardLength: number = Number_32768; + public static MaxCopyLength: number = Number_258; + public currentLitlenTable: Uint16Array | [Uint32Array, number, number] | null = null; + + public static order: Uint16Array = ((): Uint16Array => { + const table = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + return new Uint16Array(table); + })(); + + public static LengthCodeTable: Uint16Array = ((table: number[]): Uint16Array => { + return new Uint16Array(table); + })([ + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000d, 0x000f, 0x0011, 0x0013, 0x0017, + 0x001b, 0x001f, 0x0023, 0x002b, 0x0033, 0x003b, 0x0043, 0x0053, 0x0063, 0x0073, 0x0083, 0x00a3, 0x00c3, 0x00e3, + 0x0102, 0x0102, 0x0102 + ]); + + public static LengthExtraTable: Uint8Array = ((table: number[]): Uint8Array => { + return new Uint8Array(table); + })([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0]); + + public static DistCodeTable: Uint16Array = ((table: number[]): Uint16Array => { + return new Uint16Array(table); + })([ + 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d, 0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, + 0x0081, 0x00c1, 0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01, 0x1001, 0x1801, 0x2001, 0x3001, + 0x4001, 0x6001 + ]); + + public static DistExtraTable: Uint8Array = ((): Uint8Array => { + const table = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]; + return new Uint8Array(table); + })(); + + public static FixedLiteralLengthTable: [Uint32Array, number, number] = ((): [Uint32Array, number, number] => { + let lengths = new Uint8Array(replace8NumberCount(Number_288)); + + for (let i = 0; i < lengths.length; ++i) { + lengths[i] = i <= Number_143 ? Number_8 : i <= Number_255 ? Number_9 : i <= Number_279 ? Number_7 : Number_8; + } + return RawInflate.buildHuffmanTable(lengths); + })(); + + public static FixedDistanceTable: [Uint32Array, number, number] = ((): [Uint32Array, number, number] => { + let lengths = new Uint8Array(replace8NumberCount(Number_30)); + + for (let i = 0; i < lengths.length; ++i) { + lengths[i] = 5; + } + + return RawInflate.buildHuffmanTable(lengths); + })(); + public buffer: Uint8Array | null = null; + public blocks: Array; + public bufferSize: number; + public totalpos: number; + public ip: number; + public bitsbuf: number; + public bitsbuflen: number; + public input: Uint8Array; + public output: Uint8Array; + public op: number; + public bfinal: boolean = false; + public bufferType = BufferType.ADAPTIVE; + public resize: boolean = false; + + constructor(input: Uint8Array, opt_params?: InOptionParams) { + this.blocks = []; + this.bufferSize = RawInflate.ZLIB_RAW_INFLATE_BUFFER_SIZE; + this.totalpos = 0; + this.ip = 0; + this.bitsbuf = 0; + this.bitsbuflen = 0; + this.input = new Uint8Array(input); + this.bfinal = false; + this.bufferType = BufferType.ADAPTIVE; + this.resize = false; + + // option parameters + if (opt_params != null) { + if (opt_params.index) { + this.ip = opt_params.index; + } + if (opt_params.bufferSize) { + this.bufferSize = opt_params.bufferSize; + } + if (opt_params.bufferType) { + this.bufferType = opt_params.bufferType; + } + if (opt_params.resize) { + this.resize = opt_params.resize; + } + } + + // initialize + switch (this.bufferType) { + case BufferType.BLOCK: + this.op = RawInflate.MaxBackwardLength; + this.output = replace8NumberCount(RawInflate.MaxBackwardLength + this.bufferSize + RawInflate.MaxCopyLength); + break; + case BufferType.ADAPTIVE: + this.op = 0; + this.output = replace8NumberCount(this.bufferSize); + break; + default: + throw new Error('invalid inflate mode'); + } + } + + public decompress(): Uint8Array { + while (!this.bfinal) { + this.parseBlock(); + } + switch (this.bufferType) { + case BufferType.BLOCK: + return this.concatBufferBlock(); + case BufferType.ADAPTIVE: + return this.concatBufferDynamic(); + default: + throw new Error('invalid inflate mode'); + } + } + + public parseBlock() { + let hdr: number = this.readBits(Number_3); + + // bfinal + if ((hdr & 0x1) != 0) { + this.bfinal = true; + } + + // hdr btype + hdr >>>= 1; + switch (hdr) { + // case uncompressed + case 0: + this.parseUncompressedBlock(); + break; + // case fixed huffman + case 1: + this.parseFixedHuffmanBlock(); + break; + // case dynamic huffman + case Number_2: + this.parseDynamicHuffmanBlock(); + break; + // case reserved or other + default: + throw new Error(`unknown BTYPE: ${hdr}`); + } + } + + public readBits(length: number): number { + let bitsbuf: number = this.bitsbuf; + let bitsbuflen: number = this.bitsbuflen; + let input: Uint8Array = this.input; + let ip: number = this.ip; + + /** @type {number} */ + let inputLength = input.length; + /** @type {number} input and output byte. */ + let octet: number; + + // input byte tet + if (ip + ((length - bitsbuflen + Number_7) >> Number_3) >= inputLength) { + throw new Error('input buffer is broken'); + } + + // not enough buffer + while (bitsbuflen < length) { + bitsbuf |= input[ip] << bitsbuflen; + ip += 1; + bitsbuflen += Number_8; + } + + // output byte tet + octet = bitsbuf & ((1 << length) - 1); + bitsbuf >>>= length; + bitsbuflen -= length; + // set bitsbuf,bitsbuflen and ip + this.bitsbuf = bitsbuf; + this.bitsbuflen = bitsbuflen; + this.ip = ip; + + return octet; + } + + public readCodeByTable(table: Uint16Array | Uint8Array | [Uint32Array, number, number]): number { + let bitsbuf: number = this.bitsbuf; + let bitsbuflen: number = this.bitsbuflen; + let input: Uint8Array = this.input; + let ip: number = this.ip; + + let inputLength: number = input.length; + let codeTable: number | Uint32Array = table[0]; + let maxCodeLength: number = table[1]; + let codeWithLength: number; + let codeLength: number; + + // not enough buffer + while (bitsbuflen < maxCodeLength) { + if (ip >= inputLength) { + break; + } + bitsbuf |= input[ip] << bitsbuflen; + ip += 1; + bitsbuflen += Number_8; + } + + // read max length + codeWithLength = codeTable[bitsbuf & ((1 << maxCodeLength) - 1)]; + codeLength = codeWithLength >>> Number_16; + + if (codeLength > bitsbuflen) { + throw new Error(`invalid code length: ${codeLength}`); + } + + bitsbuf >>= codeLength; + bitsbuflen -= codeLength; + ip += 1; + + this.bitsbuf = bitsbuf; + this.bitsbuflen = bitsbuflen; + this.ip = ip; + + return codeWithLength & 0xffff; + } + + public parseUncompressedBlock(): void { + let input: Uint8Array = this.input; + let ip: number = this.ip; + let output: Uint8Array = this.output; + let op: number = this.op; + + let inputLength: number = input.length; + let len: number; + let nlen: number; + let olength: number = output.length; + let preCopy: number; + + // skip buffered header bits and set 0 + this.bitsbuf = 0; + this.bitsbuflen = 0; + + // check len + if (ip + 1 >= inputLength) { + throw new Error('invalid uncompressed block header: LEN'); + } + len = input[ip] | (input[ip + 1] << Number_8); + ip += Number_2; + + // nlen + if (ip + 1 >= inputLength) { + throw new Error('invalid uncompressed block header: NLEN'); + } + nlen = input[ip] | (input[ip + 1] << Number_8); + ip += Number_2; + + // check len & nlen + if (len === ~nlen) { + throw new Error('invalid uncompressed block header: length verify'); + } + + // check size + if (ip + len > input.length) { + throw new Error('input buffer is broken'); + } + + // expand buffer + switch (this.bufferType) { + case BufferType.BLOCK: + + while (op + len > output.length) { + preCopy = olength - op; + len -= preCopy; + // set input to output + output.set(input.subarray(ip, ip + preCopy), op); + op += preCopy; + ip += preCopy; + // set op + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + break; + // ADAPTIVE + case BufferType.ADAPTIVE: + while (op + len > output.length) { + output = this.expandBufferAdaptive({ fixRatio: 2 }); + } + break; + default: + // Error + throw new Error('invalid inflate mode'); + } + + // copy input.subarray + + output.set(input.subarray(ip, ip + len), op); + op += len; + ip += len; + // reset ip,op,output + this.ip = ip; + this.op = op; + this.output = output; + } + + public parseFixedHuffmanBlock(): void { + switch (this.bufferType) { + case BufferType.ADAPTIVE: + this.decodeHuffmanAdaptive(RawInflate.FixedLiteralLengthTable, RawInflate.FixedDistanceTable); + break; + case BufferType.BLOCK: + this.decodeHuffmanBlock(RawInflate.FixedLiteralLengthTable, RawInflate.FixedDistanceTable); + break; + default: + throw new Error('invalid inflate mode'); + } + } + + public parseDynamicHuffmanBlock(): void { + let hlit: number = this.readBits(Number_5) + Number_257; + let hdist: number = this.readBits(Number_5) + 1; + let hclen: number = this.readBits(Number_4) + Number_4; + let codeLengths: Uint8Array = replace8NumberCount(RawInflate.order.length); + let codeLengthsTable: [Uint32Array, number, number]; + let litlenTable: Uint8Array | [Uint32Array, number, number]; + let distTable: Uint8Array | [Uint32Array, number, number]; + let lengthTable: Uint8Array; + let code: number; + let prev: number | null = null; + let repeats: number; + + // decode code lengths + for (let i = 0; i < hclen; ++i) { + codeLengths[RawInflate.order[i]] = this.readBits(Number_3); + } + + // decode length table + codeLengthsTable = RawInflate.buildHuffmanTable(codeLengths); + lengthTable = replace8NumberCount(hlit + hdist); + let i = 0; + while (i < hlit + hdist) { + code = this.readCodeByTable(codeLengthsTable); + switch (code) { + case Number_16: + repeats = Number_3 + this.readBits(Number_2); + while (repeats > 0) { + lengthTable[i] = prev ?? 0; + i += 1; + repeats -= 1; + } + break; + case Number_17: + repeats = Number_3 + this.readBits(Number_3); + while (repeats > 0) { + lengthTable[i] = 0; + i += 1; + repeats -= 1; + } + prev = 0; + break; + case Number_18: + repeats = Number_11 + this.readBits(Number_7); + while (repeats > 0) { + lengthTable[i] = 0; + + repeats -= 1; + i += 1; + } + prev = 0; + break; + default: + lengthTable[i] = code; + prev = code; + i += 1; + break; + } + } + + litlenTable = RawInflate.buildHuffmanTable(lengthTable.subarray(0, hlit)); + + distTable = RawInflate.buildHuffmanTable(lengthTable.subarray(hlit)); + + switch (this.bufferType) { + case BufferType.ADAPTIVE: + this.decodeHuffmanAdaptive(litlenTable, distTable); + break; + case BufferType.BLOCK: + this.decodeHuffmanBlock(litlenTable, distTable); + break; + default: + throw new Error('invalid inflate mode'); + } + } + + public decodeHuffmanBlock( + litlen: Uint16Array | [Uint32Array, number, number], + dist: Uint8Array | [Uint32Array, number, number] + ): void { + let output: Uint8Array = this.output; + let op: number = this.op; + + this.currentLitlenTable = litlen; + + let olength: number = output.length - RawInflate.MaxCopyLength; + let code: number; + let ti: number; + let codeDist: number; + let codeLength: number; + + let lengthCodeTable: Uint16Array = RawInflate.LengthCodeTable; + let lengthExtraTable: Uint8Array = RawInflate.LengthExtraTable; + let distCodeTable: Uint16Array = RawInflate.DistCodeTable; + let distExtraTable: Uint8Array = RawInflate.DistExtraTable; + + code = this.readCodeByTable(litlen); + while (code !== Number_256) { + // literal + if (code < Number_256) { + if (op >= olength) { + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + output[op] = code; + op += 1; + continue; + } + + // code length + ti = code - Number_257; + codeLength = lengthCodeTable[ti]; + if (lengthExtraTable[ti] > 0) { + codeLength += this.readBits(lengthExtraTable[ti]); + } + // code dist + code = this.readCodeByTable(dist); + codeDist = distCodeTable[code]; + if (distExtraTable[code] > 0) { + codeDist += this.readBits(distExtraTable[code]); + } + // decode lz77 + if (op >= olength) { + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + while (codeLength > 0) { + output[op] = output[op - codeDist]; + op += 1; + codeLength -= 1; + } + code = this.readCodeByTable(litlen); + } + + while (this.bitsbuflen >= Number_8) { + this.bitsbuflen -= 8; + this.ip -= 1; + } + this.op = op; + } + + public decodeHuffmanAdaptive( + litlen: Uint16Array | [Uint32Array, number, number], + dist: Uint8Array | [Uint32Array, number, number] + ): void { + let output: Uint8Array = this.output; + let op: number = this.op; + + this.currentLitlenTable = litlen; + + let olength: number = output.length; + let code: number; + let ti: number; + let codeDist: number; + let codeLength: number; + + let lengthCodeTable: Uint16Array = RawInflate.LengthCodeTable; + let lengthExtraTable: Uint8Array = RawInflate.LengthExtraTable; + let distCodeTable: Uint16Array = RawInflate.DistCodeTable; + let distExtraTable: Uint8Array = RawInflate.DistExtraTable; + + code = this.readCodeByTable(litlen); + while (code !== Number_256) { + // literal + if (code < Number_256) { + if (op >= olength) { + output = this.expandBufferAdaptive(); + olength = output.length; + } + output[op] = code; + op += 1; + continue; + } + // code length + ti = code - Number_257; + codeLength = lengthCodeTable[ti]; + if (lengthExtraTable[ti] > 0) { + codeLength += this.readBits(lengthExtraTable[ti]); + } + // code dist + code = this.readCodeByTable(dist); + codeDist = distCodeTable[code]; + if (distExtraTable[code] > 0) { + codeDist += this.readBits(distExtraTable[code]); + } + + // decode lz77 + if (op + codeLength > olength) { + output = this.expandBufferAdaptive(); + olength = output.length; + } + while (codeLength > 0) { + output[op] = output[op - codeDist]; + op += 1; + codeLength -= 1; + } + code = this.readCodeByTable(litlen); + } + + while (this.bitsbuflen >= Number_8) { + this.bitsbuflen -= 8; + this.ip -= 1; + } + this.op = op; + } + + public expandBufferBlock(): Uint8Array { + let buffer = replace8NumberCount(this.op - RawInflate.MaxBackwardLength); + let backward: number = this.op - RawInflate.MaxBackwardLength; + + let output: Uint8Array = this.output; + + // copy to output buffer + + buffer.set(output.subarray(RawInflate.MaxBackwardLength, buffer.length)); + + this.blocks.push(buffer); + this.totalpos += buffer.length; + + // copy to backward buffer + + output.set(output.subarray(backward, backward + RawInflate.MaxBackwardLength)); + + this.op = RawInflate.MaxBackwardLength; + + return output; + } + + public expandBufferAdaptive(opt_param?: ExpandOption): Uint8Array { + let buffer: Uint8Array; + let ratio: number = (this.input.length / this.ip + 1) | 0; + let maxHuffCode: number; + let newSize: number; + let maxInflateSize: number; + + let input: Uint8Array = this.input; + let output: Uint8Array = this.output; + + if (opt_param != null) { + if (opt_param.fixRatio) { + ratio = opt_param.fixRatio; + } + if (opt_param.addRatio) { + ratio += opt_param.addRatio; + } + } + + // calculate new buffer size + if (ratio < Number_2) { + maxHuffCode = (input.length - this.ip) / this.currentLitlenTable![Number_2]; + maxInflateSize = ((maxHuffCode / Number_2) * Number_258) | 0; + newSize = maxInflateSize < output.length ? output.length + maxInflateSize : output.length << 1; + } else { + newSize = output.length * ratio; + } + + // buffer expantion + buffer = replace8NumberCount(newSize); + buffer.set(output); + + this.output = buffer; + + return this.output; + } + + public concatBufferBlock(): Uint8Array { + let pos: number = 0; + let limit: number = this.totalpos + (this.op - RawInflate.MaxBackwardLength); + let output: Uint8Array = this.output; + let blocks: Uint8Array[] = this.blocks; + let buffer: Uint8Array = replace8NumberCount(limit); + + // single buffer + if (blocks.length === 0) { + return this.output.subarray(RawInflate.MaxBackwardLength, this.op); + } + + // copy to buffer + for (let i = 0; i < blocks.length; ++i) { + let block: Uint8Array = blocks[i]; + for (let j = 0; j < block.length; ++j) { + buffer[pos] = block[j]; + pos += 1; + } + } + + // current buffer + for (let i = RawInflate.MaxBackwardLength; i < this.op; ++i) { + buffer[pos] = output[i]; + pos += 1; + } + + this.blocks = []; + this.buffer = buffer; + + return this.buffer ?? new Uint8Array(0); + } + + public concatBufferDynamic(): Uint8Array { + let buffer: Uint8Array; + let op: number = this.op; + + if (this.resize) { + buffer = replace8NumberCount(op); + buffer.set(this.output.subarray(0, op)); + } else { + buffer = this.output.subarray(0, op); + } + + this.buffer = buffer; + + return this.buffer ?? new Uint8Array(0); + } +} diff --git a/OHBM/entry/src/main/ets/cases/performance/zlib/zlib.ts b/OHBM/entry/src/main/ets/cases/performance/zlib/zlib.ts new file mode 100644 index 0000000000000000000000000000000000000000..eca250268949894d5708b4d112a6ebbf18a08485 --- /dev/null +++ b/OHBM/entry/src/main/ets/cases/performance/zlib/zlib.ts @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RawDeflate, OptionParams, CompressionType } from './rawdeflate'; +import { RawInflate } from './rawinflate'; + +const Number_15 = 15; + +declare interface ArkTools { + timeInUs(args: number): number; +} + +const arr1: number[] = [ + 111, 107, 46, 0, 0, 0, 0, 0, 12, 0, 8, 0, 140, 0, 8, 0, 76, 0, 8, 0, 204, 0, 8, 0, 44, 0, 8, 0, 172, 0, 8, 0, 108, 0, + 8, 0, 236, 0, 8, 0, 28, 0, 8, 0, 156, 0, 8, 0, 92, 0, 8, 0, 220, 0, 8, 0, 60, 0, 8, 0, 188, 0, 8, 0, 124, 0, 8, 0, + 252, 0, 8, 0, 2, 0, 8, 0, 130, 0, 8, 0, 66, 0, 8, 0, 194, 0, 8, 0, 34, 0, 8, 0, 162, 0, 8, 0, 98, 0, 8, 0, 226, 0, 8, + 0, 18, 0, 8, 0, 146, 0, 8, 0, 82, 0, 8, 0, 210, 0, 8, 0, 50, 0, 8, 0, 178, 0, 8, 0, 114, 0, 8, 0, 242, 0, 8, 0, 10, 0, + 8, 0, 138, 0, 8, 0, 74, 0, 8, 0, 202, 0, 8, 0, 42, 0, 8, 0, 170, 0, 8, 0, 106, 0, 8, 0, 234, 0, 8, 0, 26, 0, 8, 0, + 154, 0, 8, 0, 90, 0, 8, 0, 218, 0, 8, 0, 58, 0, 8, 0, 186, 0, 8, 0, 122, 0, 8, 0, 250, 0, 8, 0, 6, 0, 8, 0, 134, 0, 8, + 0, 70, 0, 8, 0, 198, 0, 8, 0, 38, 0, 8, 0, 166, 0, 8, 0, 102, 0, 8, 0, 230, 0, 8, 0, 22, 0, 8, 0, 150, 0, 8, 0, 86, 0, + 8, 0, 214, 0, 8, 0, 54, 0, 8, 0, 182, 0, 8, 0, 118, 0, 8, 0, 246, 0, 8, 0, 14, 0, 8, 0, 142, 0, 8, 0, 78, 0, 8, 0, + 206, 0, 8, 0, 46, 0, 8, 0, 174, 0, 8, 0, 110, 0, 8, 0, 238, 0, 8, 0, 30, 0, 8, 0, 158, 0, 8, 0, 94, 0, 8, 0, 222, 0, + 8, 0, 62, 0, 8, 0, 190, 0, 8, 0, 126, 0, 8, 0, 254, 0, 8, 0, 1, 0, 8, 0, 129, 0, 8, 0, 65, 0, 8, 0, 193, 0, 8, 0, 33, + 0, 8, 0, 161, 0, 8, 0, 97, 0, 8, 0, 225, 0, 8, 0, 17, 0, 8, 0, 145, 0, 8, 0, 81, 0, 8, 0, 209, 0, 8, 0, 49, 0, 8, 0, + 177, 0, 8, 0, 113, 0, 8, 0, 241, 0, 8, 0, 9, 0, 8, 0, 137, 0, 8, 0, 73, 0, 8, 0, 201, 0, 8, 0, 41, 0, 8, 0, 169, 0, 8, + 0, 105, 0, 8, 0, 233, 0, 8, 0, 25, 0, 8, 0, 153, 0, 8, 0, 89, 0, 8, 0, 217, 0, 8, 0, 57, 0, 8, 0, 185, 0, 8, 0, 121, + 0, 8, 0, 249, 0, 8, 0, 5, 0, 8, 0, 133, 0, 8, 0, 69, 0, 8, 0, 197, 0, 8, 0, 37, 0, 8, 0, 165, 0, 8, 0, 101, 0, 8, 0, + 229, 0, 8, 0, 21, 0, 8, 0, 149, 0, 8, 0, 85, 0, 8, 0, 213, 0, 8, 0, 53, 0, 8, 0, 181, 0, 8, 0, 117, 0, 8, 0, 245, 0, + 8, 0, 13, 0, 8, 0, 141, 0, 8, 0, 77, 0, 8, 0, 205, 0, 8, 0, 45, 0, 8, 0, 173, 0, 8, 0, 109, 0, 8, 0, 237, 0, 8, 0, 29, + 0, 8, 0, 157, 0, 8, 0, 93, 0, 8, 0, 221, 0, 8, 0, 61, 0, 8, 0, 189, 0, 8, 0, 125, 0, 8, 0, 253, 0, 8, 0, 19, 0, 9, 0, + 19, 1, 9, 0, 147, 0, 9, 0, 147, 1, 9, 0, 83, 0, 9, 0, 83, 1, 9, 0, 211, 0, 9, 0, 211, 1, 9, 0, 51, 0, 9, 0, 51, 1, 9, + 0, 179, 0, 9, 0, 179, 1, 9, 0, 115, 0, 9, 0, 115, 1, 9, 0, 243, 0, 9, 0, 243, 1, 9, 0, 11, 0, 9, 0, 11, 1, 9, 0, 139, + 0, 9, 0, 139, 1, 9, 0, 75, 0, 9, 0, 75, 1, 9, 0, 203, 0, 9, 0, 203, 1, 9, 0, 43, 0, 9, 0, 43, 1, 9, 0, 171, 0, 9, 0, + 171, 1, 9, 0, 107, 0, 9, 0, 107, 1, 9, 0, 235, 0, 9, 0, 235, 1, 9, 0, 27, 0, 9, 0, 27, 1, 9, 0, 155, 0, 9, 0, 155, 1, + 9, 0, 91, 0, 9, 0, 91, 1, 9, 0, 219, 0, 9, 0, 219, 1, 9, 0, 59, 0, 9, 0, 59, 1, 9, 0, 187, 0, 9, 0, 187, 1, 9, 0, 123, + 0, 9, 0, 123, 1, 9, 0, 251, 0, 9, 0, 251, 1, 9, 0, 7, 0, 9, 0, 7, 1, 9, 0, 135, 0, 9, 0, 135, 1, 9, 0, 71, 0, 9, 0, + 71, 1, 9, 0, 199, 0, 9, 0, 199, 1, 9, 0, 39, 0, 9, 0, 39, 1, 9, 0, 167, 0, 9, 0, 167, 1, 9, 0, 103, 0, 9, 0, 103, 1, + 9, 0, 231, 0, 9, 0, 231, 1, 9, 0, 23, 0, 9, 0, 23, 1, 9, 0, 151, 0, 9, 0, 151, 1, 9, 0, 87, 0, 9, 0, 87, 1, 9, 0, 215, + 0, 9, 0, 215, 1, 9, 0, 55, 0, 9, 0, 55, 1, 9, 0, 183, 0, 9, 0, 183, 1, 9, 0, 119, 0, 9, 0, 119, 1, 9, 0, 247, 0, 9, 0, + 247, 1, 9, 0, 15, 0, 9, 0, 15, 1, 9, 0, 143, 0, 9, 0, 143, 1, 9, 0, 79, 0, 9, 0, 79, 1, 9, 0, 207, 0, 9, 0, 207, 1, 9, + 0, 47, 0, 9, 0, 47, 1, 9, 0, 175, 0, 9, 0, 175, 1, 9, 0, 111, 0, 9, 0, 111, 1, 9, 0, 239, 0, 9, 0, 239, 1, 9, 0, 31, + 0, 9, 0, 31, 1, 9, 0, 159, 0, 9, 0, 159, 1, 9, 0, 95, 0, 9, 0, 95, 1, 9, 0, 223, 0, 9, 0, 223, 1, 9, 0, 63, 0, 9, 0, + 63, 1, 9, 0, 191, 0, 9, 0, 191, 1, 9, 0, 127, 0, 9, 0, 127, 1, 9, 0, 255, 0, 9, 0, 255, 1, 9, 0, 0, 0, 7, 0, 64, 0, 7, + 0, 32, 0, 7, 0, 96, 0, 7, 0, 16, 0, 7, 0, 80, 0, 7, 0, 48, 0, 7, 0, 112, 0, 7, 0, 8, 0, 7, 0, 72, 0, 7, 0, 40, 0, 7, + 0, 104, 0, 7, 0, 24, 0, 7, 0, 88, 0, 7, 0, 56, 0, 7, 0, 120, 0, 7, 0, 4, 0, 7, 0, 68, 0, 7, 0, 36, 0, 7, 0, 100, 0, 7, + 0, 20, 0, 7, 0, 84, 0, 7, 0, 52, 0, 7, 0, 116, 0, 7, 0, 3, 0, 8, 0, 131, 0, 8, 0, 67, 0, 8, 0, 195, 0, 8, 0, 35, 0, 8, + 0, 163, 0, 8, 0, 99, 0, 8, 0, 227, 0, 8, 0, 16, 0, 0, 0, 16, 15, 0, 0, 1, 1, 0, 0, 30, 1, 0, 0, 15, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 0, 16, 0, 5, 0, 8, 0, 5, 0, 24, 0, 5, 0, 4, 0, 5, 0, 20, 0, 5, 0, 12, 0, 5, 0, 28, 0, 5, 0, 2, 0, 5, 0, + 18, 0, 5, 0, 10, 0, 5, 0, 26, 0, 5, 0, 6, 0, 5, 0, 22, 0, 5, 0, 14, 0, 5, 0, 30, 0, 5, 0, 1, 0, 5, 0, 17, 0, 5, 0, 9, + 0, 5, 0, 25, 0, 5, 0, 5, 0, 5, 0, 21, 0, 5, 0, 13, 0, 5, 0, 29, 0, 5, 0, 3, 0, 5, 0, 19, 0, 5, 0, 11, 0, 5, 0, 27, 0, + 5, 0, 7, 0, 5, 0, 23, 0, 5, 0, 168, 4, 0, 0, 136, 15, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 17, 0, 17, 0, 17, 0, 17, 0, 18, 0, 18, 0, + 18, 0, 18, 0, 19, 0, 19, 0, 19, 0, 19, 0, 20, 0, 20, 0, 20, 0, 20, 0, 21, 0, 21, 0, 21, 0, 21, 0, 16, 0, 73, 0, 195, + 0, 0, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 13, 0, 15, 0, 17, 0, 19, 0, 23, 0, 27, 0, 31, 0, 35, + 0, 43, 0, 51, 0, 59, 0, 67, 0, 83, 0, 99, 0, 115, 0, 131, 0, 163, 0, 195, 0, 227, 0, 2, 1, 0, 0, 0, 0, 0, 0, 16, 0, + 16, 0, 16, 0, 16, 0, 17, 0, 17, 0, 18, 0, 18, 0, 19, 0, 19, 0, 20, 0, 20, 0, 21, 0, 21, 0, 22, 0, 22, 0, 23, 0, 23, 0, + 24, 0, 24, 0, 25, 0, 25, 0, 26, 0, 26, 0, 27, 0, 27, 0, 28, 0, 28, 0, 29, 0, 29, 0, 64, 0, 64, 0, 1, 0, 2, 0, 3, 0, 4, + 0, 5, 0, 7, 0, 9, 0, 13, 0, 17, 0, 25, 0, 33, 0, 49, 0, 65, 0, 97, 0, 129, 0, 193, 0, 1, 1, 129, 1, 1, 2, 1, 3, 1, 4, + 1, 6, 1, 8, 1, 12, 1, 16, 1, 24, 1, 32, 1, 48, 1, 64, 1, 96, 0, 0, 0, 0, 16, 0, 17, 0, 18, 0, 0, 0, 8, 0, 7, 0, 9, 0, + 6, 0, 10, 0, 5, 0, 11, 0, 4, 0, 12, 0, 3, 0, 13, 0, 2, 0, 14, 0, 1, 0, 15, 0, 0, 0, 96, 7, 0, 0, 0, 8, 80, 0, 0, 8, + 16, 0, 20, 8, 115, 0, 18, 7, 31, 0, 0, 8, 112, 0, 0, 8, 48, 0, 0, 9, 192, 0, 16, 7, 10, 0, 0, 8, 96, 0, 0, 8, 32, 0, + 0, 9, 160, 0, 0, 8, 0, 0, 0, 8, 128, 0, 0, 8, 64, 0, 0, 9, 224, 0, 16, 7, 6, 0, 0, 8, 88, 0, 0, 8, 24, 0, 0, 9, 144, + 0, 19, 7, 59, 0, 0, 8, 120, 0, 0, 8, 56, 0, 0, 9, 208, 0, 17, 7, 17, 0, 0, 8, 104, 0, 0, 8, 40, 0, 0, 9, 176, 0, 0, 8, + 8, 0, 0, 8, 136, 0, 0, 8, 72, 0, 0, 9, 240, 0, 16, 7, 4, 0, 0, 8, 84, 0, 0, 8, 20, 0, 21, 8, 227, 0, 19, 7, 43, 0, 0, + 8, 116, 0, 0, 8, 52, 0, 0, 9, 200, 0, 17, 7, 13, 0, 0, 8, 100, 0, 0, 8, 36, 0, 0, 9, 168, 0, 0, 8, 4, 0, 0, 8, 132, 0, + 0, 8, 68, 0, 0, 9, 232, 0, 16, 7, 8, 0, 0, 8, 92, 0, 0, 8, 28, 0, 0, 9, 152, 0, 20, 7, 83, 0, 0, 8, 124, 0, 0, 8, 60, + 0, 0, 9, 216, 0, 18, 7, 23, 0, 0, 8, 108, 0, 0, 8, 44, 0, 0, 9, 184, 0, 0, 8, 12, 0, 0, 8, 140, 0, 0, 8, 76, 0, 0, 9, + 248, 0, 16, 7, 3, 0, 0, 8, 82, 0, 0, 8, 18, 0, 21, 8, 163, 0, 19, 7, 35, 0, 0, 8, 114, 0, 0, 8, 50, 0, 0, 9, 196, 0, + 17, 7, 11, 0, 0, 8, 98, 0, 0, 8, 34, 0, 0, 9, 164, 0, 0, 8, 2, 0, 0, 8, 130, 0, 0, 8, 66, 0, 0, 9, 228, 0, 16, 7, 7, + 0, 0, 8, 90, 0, 0, 8, 26, 0, 0, 9, 148, 0, 20, 7, 67, 0, 0, 8, 122, 0, 0, 8, 58, 0, 0, 9, 212, 0, 18, 7, 19, 0, 0, 8, + 106, 0, 0, 8, 42, 0, 0, 9, 180, 0, 0, 8, 10, 0, 0, 8, 138, 0, 0, 8, 74, 0, 0, 9, 244, 0, 16, 7, 5, 0, 0, 8, 86, 0, 0, + 8, 22, 0, 64, 8, 0, 0, 19, 7, 51, 0, 0, 8, 118, 0, 0, 8, 54, 0, 0, 9, 204, 0, 17, 7, 15, 0, 0, 8, 102, 0, 0, 8, 38, 0, + 0, 9, 172, 0, 0, 8, 6, 0, 0, 8, 134, 0, 0, 8, 70, 0, 0, 9, 236, 0, 16, 7, 9, 0, 0, 8, 94, 0, 0, 8, 30, 0, 0, 9, 156, + 0, 20, 7, 99, 0, 0, 8, 126, 0, 0, 8, 62, 0, 0, 9, 220, 0, 18, 7, 27, 0, 0, 8, 110, 0, 0, 8, 46, 0, 0, 9, 188, 0, 0, 8, + 14, 0, 0, 8, 142, 0, 0, 8, 78, 0, 0, 9, 252, 0, 96, 7, 0, 0, 0, 8, 81, 0, 0, 8, 17, 0, 21, 8, 131, 0, 18, 7, 31, 0, 0, + 8, 113, 0, 0, 8, 49, 0, 0, 9, 194, 0, 16, 7, 10, 0, 0, 8, 97, 0, 0, 8, 33, 0, 0, 9, 162, 0, 0, 8, 1, 0, 0, 8, 129, 0, + 0, 8, 65, 0, 0, 9, 226, 0, 16, 7, 6, 0, 0, 8, 89, 0, 0, 8, 25, 0, 0, 9, 146, 0, 19, 7, 59, 0, 0, 8, 121, 0, 0, 8, 57, + 0, 0, 9, 210, 0, 17, 7, 17, 0, 0, 8, 105, 0, 0, 8, 41, 0, 0, 9, 178, 0, 0, 8, 9, 0, 0, 8, 137, 0, 0, 8, 73, 0, 0, 9, + 242, 0, 16, 7, 4, 0, 0, 8, 85, 0, 0, 8, 21, 0, 16, 8, 2, 1, 19, 7, 43, 0, 0, 8, 117, 0, 0, 8, 53, 0, 0, 9, 202, 0, 17, + 7, 13, 0, 0, 8, 101, 0, 0, 8, 37, 0, 0, 9, 170, 0, 0, 8, 5, 0, 0, 8, 133, 0, 0, 8, 69, 0, 0, 9, 234, 0, 16, 7, 8, 0, + 0, 8, 93, 0, 0, 8, 29, 0, 0, 9, 154, 0, 20, 7, 83, 0, 0, 8, 125, 0, 0, 8, 61, 0, 0, 9, 218, 0, 18, 7, 23, 0, 0, 8, + 109, 0, 0, 8, 45, 0, 0, 9, 186, 0, 0, 8, 13, 0, 0, 8, 141, 0, 0, 8, 77, 0, 0, 9, 250, 0, 16, 7, 3, 0, 0, 8, 83, 0, 0, + 8, 19, 0, 21, 8, 195, 0, 19, 7, 35, 0, 0, 8, 115, 0, 0, 8, 51, 0, 0, 9, 198, 0, 17, 7, 11, 0, 0, 8, 99, 0, 0, 8, 35, + 0, 0, 9, 166, 0, 0, 8, 3, 0, 0, 8, 131, 0, 0, 8, 67, 0, 0, 9, 230, 0, 16, 7, 7, 0, 0, 8, 91, 0, 0, 8, 27, 0, 0, 9, + 150, 0, 20, 7, 67, 0, 0, 8, 123, 0, 0, 8, 59, 0, 0, 9, 214, 0, 18, 7, 19, 0, 0, 8, 107, 0, 0, 8, 43, 0, 0, 9, 182, 0, + 0, 8, 11, 0, 0, 8, 139, 0, 0, 8, 75, 0, 0, 9, 246, 0, 16, 7, 5, 0, 0, 8, 87, 0, 0, 8, 23, 0, 64, 8, 0, 0, 19, 7, 51, + 0, 0, 8, 119, 0, 0, 8, 55, 0, 0, 9, 206, 0, 17, 7, 15, 0, 0, 8, 103, 0, 0, 8, 39, 0, 0, 9, 174, 0, 0, 8, 7, 0, 0, 8, + 135, 0, 0, 8, 71, 0, 0, 9, 238, 0, 16, 7, 9, 0, 0, 8, 95, 0, 0, 8, 31, 0, 0, 9, 158, 0, 20, 7, 99, 0, 0, 8, 127, 0, 0, + 8, 63, 0, 0, 9, 222, 0, 18, 7, 27, 0, 0, 8, 111, 0, 0, 8, 47, 0, 0, 9, 190, 0, 0, 8, 15, 0, 0, 8, 143, 0, 0, 8, 79, 0, + 0, 9, 254, 0, 96, 7, 0, 0, 0, 8, 80, 0, 0, 8, 16, 0, 20, 8, 115, 0, 18, 7, 31, 0, 0, 8, 112, 0, 0, 8, 48, 0, 0, 9, + 193, 0, 16, 7, 10, 0, 0, 8, 96, 0, 0, 8, 32, 0, 0, 9, 161, 0, 0, 8, 0, 0, 0, 8, 128, 0, 0, 8, 64, 0, 0, 9, 225, 0, 16, + 7, 6, 0, 0, 8, 88, 0, 0, 8, 24, 0, 0, 9, 145, 0, 19, 7, 59, 0, 0, 8, 120, 0, 0, 8, 56, 0, 0, 9, 209, 0, 17, 7, 17, 0, + 0, 8, 104, 0, 0, 8, 40, 0, 0, 9, 177, 0, 0, 8, 8, 0, 0, 8, 136, 0, 0, 8, 72, 0, 0, 9, 241, 0, 16, 7, 4, 0, 0, 8, 84, + 0, 0, 8, 20, 0, 21, 8, 227, 0, 19, 7, 43, 0, 0, 8, 116, 0, 0, 8, 52, 0, 0, 9, 201, 0, 17, 7, 13, 0, 0, 8, 100, 0, 0, + 8, 36, 0, 0, 9, 169, 0, 0, 8, 4, 0, 0, 8, 132, 0, 0, 8, 68, 0, 0, 9, 233, 0, 16, 7, 8, 0, 0, 8, 92, 0, 0, 8, 28, 0, 0, + 9, 153, 0, 20, 7, 83, 0, 0, 8, 124, 0, 0, 8, 60, 0, 0, 9, 217, 0, 18, 7, 23, 0, 0, 8, 108, 0, 0, 8, 44, 0, 0, 9, 185, + 0, 0, 8, 12, 0, 0, 8, 140, 0, 0, 8, 76, 0, 0, 9, 249, 0, 16, 7, 3, 0, 0, 8, 82, 0, 0, 8, 18, 0, 21, 8, 163, 0, 19, 7, + 35, 0, 0, 8, 114, 0, 0, 8, 50, 0, 0, 9, 197, 0, 17, 7, 11, 0, 0, 8, 98, 0, 0, 8, 34, 0, 0, 9, 165, 0, 0, 8, 2, 0, 0, + 8, 130, 0, 0, 8, 66, 0, 0, 9, 229, 0, 16, 7, 7, 0, 0, 8, 90, 0, 0, 8, 26, 0, 0, 9, 149, 0, 20, 7, 67, 0, 0, 8, 122, 0, + 0, 8, 58, 0, 0, 9, 213, 0, 18, 7, 19, 0, 0, 8, 106, 0, 0, 8, 42, 0, 0, 9, 181, 0, 0, 8, 10, 0, 0, 8, 138, 0, 0, 8, 74, + 0, 0, 9, 245, 0, 16, 7, 5, 0, 0, 8, 86, 0, 0, 8, 22, 0, 64, 8, 0, 0, 19, 7, 51, 0, 0, 8, 118, 0, 0, 8, 54, 0, 0, 9, + 205, 0, 17, 7, 15, 0, 0, 8, 102, 0, 0, 8, 38, 0, 0, 9, 173, 0, 0, 8, 6, 0, 0, 8, 134, 0, 0, 8, 70, 0, 0, 9, 237, 0, + 16, 7, 9, 0, 0, 8, 94, 0, 0, 8, 30, 0, 0, 9, 157, 0, 20, 7, 99, 0, 0, 8, 126, 0, 0, 8, 62, 0, 0, 9, 221, 0, 18, 7, 27, + 0, 0, 8, 110, 0, 0, 8, 46, 0, 0, 9, 189, 0, 0, 8, 14, 0, 0, 8, 142, 0, 0, 8, 78, 0, 0, 9, 253, 0, 96, 7, 0, 0, 0, 8, + 81, 0, 0, 8, 17, 0, 21, 8, 131, 0, 18, 7, 31, 0, 0, 8, 113, 0, 0, 8, 49, 0, 0, 9, 195, 0, 16, 7, 10, 0, 0, 8, 97, 0, + 0, 8, 33, 0, 0, 9, 163, 0, 0, 8, 1, 0, 0, 8, 129, 0, 0, 8, 65, 0, 0, 9, 227, 0, 16, 7, 6, 0, 0, 8, 89, 0, 0, 8, 25, 0, + 0, 9, 147, 0, 19, 7, 59, 0, 0, 8, 121, 0, 0, 8, 57, 0, 0, 9, 211, 0, 17, 7, 17, 0, 0, 8, 105, 0, 0, 8, 41, 0, 0, 9, + 179, 0, 0, 8, 9, 0, 0, 8, 137, 0, 0, 8, 73, 0, 0, 9, 243, 0, 16, 7, 4, 0, 0, 8, 85, 0, 0, 8, 21, 0, 16, 8, 2, 1, 19, + 7, 43, 0, 0, 8, 117, 0, 0, 8, 53, 0, 0, 9, 203, 0, 17, 7, 13, 0, 0, 8, 101, 0, 0, 8, 37, 0, 0, 9, 171, 0, 0, 8, 5, 0, + 0, 8, 133, 0, 0, 8, 69, 0, 0, 9, 235, 0, 16, 7, 8, 0, 0, 8, 93, 0, 0, 8, 29, 0, 0, 9, 155, 0, 20, 7, 83, 0, 0, 8, 125, + 0, 0, 8, 61, 0, 0, 9, 219, 0, 18, 7, 23, 0, 0, 8, 109, 0, 0, 8, 45, 0, 0, 9, 187, 0, 0, 8, 13, 0, 0, 8, 141, 0, 0, 8, + 77, 0, 0, 9, 251, 0, 16, 7, 3, 0, 0, 8, 83, 0, 0, 8, 19, 0, 21, 8, 195, 0, 19, 7, 35, 0, 0, 8, 115, 0, 0, 8, 51, 0, 0, + 9, 199, 0, 17, 7, 11, 0, 0, 8, 99, 0, 0, 8, 35, 0, 0, 9, 167, 0, 0, 8, 3, 0, 0, 8, 131, 0, 0, 8, 67, 0, 0, 9, 231, 0, + 16, 7, 7, 0, 0, 8, 91, 0, 0, 8, 27, 0, 0, 9, 151, 0, 20, 7, 67, 0, 0, 8, 123, 0, 0, 8, 59, 0, 0, 9, 215, 0, 18, 7, 19, + 0, 0, 8, 107, 0, 0, 8, 43, 0, 0, 9, 183, 0, 0, 8, 11, 0, 0, 8, 139, 0, 0, 8, 75, 0, 0, 9, 247, 0, 16, 7, 5, 0, 0, 8, + 87, 0, 0, 8, 23, 0, 64, 8, 0, 0, 19, 7, 51, 0, 0, 8, 119, 0, 0, 8, 55, 0, 0, 9, 207, 0, 17, 7, 15, 0, 0, 8, 103, 0, 0, + 8, 39, 0, 0, 9, 175, 0, 0, 8, 7, 0, 0, 8, 135, 0, 0, 8, 71, 0, 0, 9, 239, 0, 16, 7, 9, 0, 0, 8, 95, 0, 0, 8, 31, 0, 0, + 9, 159, 0, 20, 7, 99, 0, 0, 8, 127, 0, 0, 8, 63, 0, 0, 9, 223, 0, 18, 7, 27, 0, 0, 8, 111, 0, 0, 8, 47, 0, 0, 9, 191, + 0, 0, 8, 15, 0, 0, 8, 143, 0, 0, 8, 79, 0, 0, 9, 255, 0, 16, 5, 1, 0, 23, 5, 1, 1, 19, 5, 17, 0, 27, 5, 1, 16, 17, 5, + 5, 0, 25, 5, 1, 4, 21, 5, 65, 0, 29, 5, 1, 64, 16, 5, 3, 0, 24, 5, 1, 2, 20, 5, 33, 0, 28, 5, 1, 32, 18, 5, 9, 0, 26, + 5, 1, 8, 22, 5, 129, 0, 64, 5, 0, 0, 16, 5, 2, 0, 23, 5, 129, 1, 19, 5, 25, 0, 27, 5, 1, 24, 17, 5, 7, 0, 25, 5, 1, 6, + 21, 5, 97, 0, 29, 5, 1, 96, 16, 5, 4, 0, 24, 5, 1, 3, 20, 5, 49, 0, 28, 5, 1, 48, 18, 5, 13, 0, 26, 5, 1, 12, 22, 5, + 193, 0, 64, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, + 0, 3, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, + 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, + 7, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 12, 0, + 0, 0, 12, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, + 48, 7, 119, 44, 97, 14, 238, 186, 81, 9, 153, 25, 196, 109, 7, 143, 244, 106, 112, 53, 165, 99, 233, 163, 149, 100, + 158, 50, 136, 219, 14, 164, 184, 220, 121, 30, 233, 213, 224, 136, 217, 210, 151, 43, 76, 182, 9, 189, 124, 177, 126, + 7, 45, 184, 231, 145, 29, 191, 144, 100, 16, 183, 29, 242, 32, 176, 106, 72, 113, 185, 243, 222, 65, 190, 132, 125, + 212, 218, 26, 235, 228, 221, 109, 81, 181, 212, 244, 199, 133, 211, 131, 86, 152, 108, 19, 192, 168, 107, 100, 122, + 249, 98, 253, 236, 201, 101, 138, 79, 92, 1, 20, 217, 108, 6, 99, 99, 61, 15, 250, 245, 13, 8, 141, 200, 32, 110, 59, + 94, 16, 105, 76, 228, 65, 96, 213, 114, 113, 103, 162, 209, 228, 3, 60, 71, 212, 4, 75, 253, 133, 13, 210, 107, 181, + 10, 165, 250, 168, 181, 53, 108, 152, 178, 66, 214, 201, 187, 219, 64, 249, 188, 172, 227, 108, 216, 50, 117, 92, 223, + 69, 207, 13, 214, 220, 89, 61, 209, 171, 172, 48, 217, 38, 58, 0, 222, 81, 128, 81, 215, 200, 22, 97, 208, 191, 181, + 244, 180, 33, 35, 196, 179, 86, 153, 149, 186, 207, 15, 165, 189, 184, 158, 184, 2, 40, 8, 136, 5, 95, 178, 217, 12, + 198, 36, 233, 11, 177, 135, 124, 111, 47, 17, 76, 104, 88, 171, 29, 97, 193, 61, 45, 102, 182, 144, 65, 220, 118, 6, + 113, 219, 1, 188, 32, 210, 152, 42, 16, 213, 239, 137, 133, 177, 113, 31, 181, 182, 6, 165, 228, 191, 159, 51, 212, + 184, 232, 162, 201, 7, 120, 52, 249, 0, 15, 142, 168, 9, 150, 24, 152, 14, 225, 187, 13, 106, 127, 45, 61, 109, 8, + 151, 108, 100, 145, 1, 92, 99, 230, 244, 81, 107, 107, 98, 97, 108, 28, 216, 48, 101, 133, 78, 0, 98, 242, 237, 149, + 6, 108, 123, 165, 1, 27, 193, 244, 8, 130, 87, 196, 15, 245, 198, 217, 176, 101, 80, 233, 183, 18, 234, 184, 190, 139, + 124, 136, 185, 252, 223, 29, 221, 98, 73, 45, 218, 21, 243, 124, 211, 140, 101, 76, 212, 251, 88, 97, 178, 77, 206, + 81, 181, 58, 116, 0, 188, 163, 226, 48, 187, 212, 65, 165, 223, 74, 215, 149, 216, 61, 109, 196, 209, 164, 251, 244, + 214, 211, 106, 233, 105, 67, 252, 217, 110, 52, 70, 136, 103, 173, 208, 184, 96, 218, 115, 45, 4, 68, 229, 29, 3, 51, + 95, 76, 10, 170, 201, 124, 13, 221, 60, 113, 5, 80, 170, 65, 2, 39, 16, 16, 11, 190, 134, 32, 12, 201, 37, 181, 104, + 87, 179, 133, 111, 32, 9, 212, 102, 185, 159, 228, 97, 206, 14, 249, 222, 94, 152, 201, 217, 41, 34, 152, 208, 176, + 180, 168, 215, 199, 23, 61, 179, 89, 129, 13, 180, 46, 59, 92, 189, 183, 173, 108, 186, 192, 32, 131, 184, 237, 182, + 179, 191, 154, 12, 226, 182, 3, 154, 210, 177, 116, 57, 71, 213, 234, 175, 119, 210, 157, 21, 38, 219, 4, 131, 22, + 220, 115, 18, 11, 99, 227, 132, 59, 100, 148, 62, 106, 109, 13, 168, 90, 106, 122, 11, 207, 14, 228, 157, 255, 9, 147, + 39, 174, 0, 10, 177, 158, 7, 125, 68, 147, 15, 240, 210, 163, 8, 135, 104, 242, 1, 30, 254, 194, 6, 105, 93, 87, 98, + 247, 203, 103, 101, 128, 113, 54, 108, 25, 231, 6, 107, 110, 118, 27, 212, 254, 224, 43, 211, 137, 90, 122, 218, 16, + 204, 74, 221, 103, 111, 223, 185, 249, 249, 239, 190, 142, 67, 190, 183, 23, 213, 142, 176, 96, 232, 163, 214, 214, + 126, 147, 209, 161, 196, 194, 216, 56, 82, 242, 223, 79, 241, 103, 187, 209, 103, 87, 188, 166, 221, 6, 181, 63, 75, + 54, 178, 72, 218, 43, 13, 216, 76, 27, 10, 175, 246, 74, 3, 54, 96, 122, 4, 65, 195, 239, 96, 223, 85, 223, 103, 168, + 239, 142, 110, 49, 121, 190, 105, 70, 140, 179, 97, 203, 26, 131, 102, 188, 160, 210, 111, 37, 54, 226, 104, 82, 149, + 119, 12, 204, 3, 71, 11, 187, 185, 22, 2, 34, 47, 38, 5, 85, 190, 59, 186, 197, 40, 11, 189, 178, 146, 90, 180, 43, 4, + 106, 179, 92, 167, 255, 215, 194, 49, 207, 208, 181, 139, 158, 217, 44, 29, 174, 222, 91, 176, 194, 100, 155, 38, 242, + 99, 236, 156, 163, 106, 117, 10, 147, 109, 2, 169, 6, 9, 156, 63, 54, 14, 235, 133, 103, 7, 114, 19, 87, 0, 5, 130, + 74, 191, 149, 20, 122, 184, 226, 174, 43, 177, 123, 56, 27, 182, 12, 155, 142, 210, 146, 13, 190, 213, 229, 183, 239, + 220, 124, 33, 223, 219, 11, 212, 210, 211, 134, 66, 226, 212, 241, 248, 179, 221, 104, 110, 131, 218, 31, 205, 22, + 190, 129, 91, 38, 185, 246, 225, 119, 176, 111, 119, 71, 183, 24, 230, 90, 8, 136, 112, 106, 15, 255, 202, 59, 6, 102, + 92, 11, 1, 17, 255, 158, 101, 143, 105, 174, 98, 248, 211, 255, 107, 97, 69, 207, 108, 22, 120, 226, 10, 160, 238, + 210, 13, 215, 84, 131, 4, 78, 194, 179, 3, 57, 97, 38, 103, 167, 247, 22, 96, 208, 77, 71, 105, 73, 219, 119, 110, 62, + 74, 106, 209, 174, 220, 90, 214, 217, 102, 11, 223, 64, 240, 59, 216, 55, 83, 174, 188, 169, 197, 158, 187, 222, 127, + 207, 178, 71, 233, 255, 181, 48, 28, 242, 189, 189, 138, 194, 186, 202, 48, 147, 179, 83, 166, 163, 180, 36, 5, 54, + 208, 186, 147, 6, 215, 205, 41, 87, 222, 84, 191, 103, 217, 35, 46, 122, 102, 179, 184, 74, 97, 196, 2, 27, 104, 93, + 148, 43, 111, 42, 55, 190, 11, 180, 161, 142, 12, 195, 27, 223, 5, 90, 141, 239, 2, 45, 0, 0, 0, 0, 65, 49, 27, 25, + 130, 98, 54, 50, 195, 83, 45, 43, 4, 197, 108, 100, 69, 244, 119, 125, 134, 167, 90, 86, 199, 150, 65, 79, 8, 138, + 217, 200, 73, 187, 194, 209, 138, 232, 239, 250, 203, 217, 244, 227, 12, 79, 181, 172, 77, 126, 174, 181, 142, 45, + 131, 158, 207, 28, 152, 135, 81, 18, 194, 74, 16, 35, 217, 83, 211, 112, 244, 120, 146, 65, 239, 97, 85, 215, 174, 46, + 20, 230, 181, 55, 215, 181, 152, 28, 150, 132, 131, 5, 89, 152, 27, 130, 24, 169, 0, 155, 219, 250, 45, 176, 154, 203, + 54, 169, 93, 93, 119, 230, 28, 108, 108, 255, 223, 63, 65, 212, 158, 14, 90, 205, 162, 36, 132, 149, 227, 21, 159, + 140, 32, 70, 178, 167, 97, 119, 169, 190, 166, 225, 232, 241, 231, 208, 243, 232, 36, 131, 222, 195, 101, 178, 197, + 218, 170, 174, 93, 93, 235, 159, 70, 68, 40, 204, 107, 111, 105, 253, 112, 118, 174, 107, 49, 57, 239, 90, 42, 32, 44, + 9, 7, 11, 109, 56, 28, 18, 243, 54, 70, 223, 178, 7, 93, 198, 113, 84, 112, 237, 48, 101, 107, 244, 247, 243, 42, 187, + 182, 194, 49, 162, 117, 145, 28, 137, 52, 160, 7, 144, 251, 188, 159, 23, 186, 141, 132, 14, 121, 222, 169, 37, 56, + 239, 178, 60, 255, 121, 243, 115, 190, 72, 232, 106, 125, 27, 197, 65, 60, 42, 222, 88, 5, 79, 121, 240, 68, 126, 98, + 233, 135, 45, 79, 194, 198, 28, 84, 219, 1, 138, 21, 148, 64, 187, 14, 141, 131, 232, 35, 166, 194, 217, 56, 191, 13, + 197, 160, 56, 76, 244, 187, 33, 143, 167, 150, 10, 206, 150, 141, 19, 9, 0, 204, 92, 72, 49, 215, 69, 139, 98, 250, + 110, 202, 83, 225, 119, 84, 93, 187, 186, 21, 108, 160, 163, 214, 63, 141, 136, 151, 14, 150, 145, 80, 152, 215, 222, + 17, 169, 204, 199, 210, 250, 225, 236, 147, 203, 250, 245, 92, 215, 98, 114, 29, 230, 121, 107, 222, 181, 84, 64, 159, + 132, 79, 89, 88, 18, 14, 22, 25, 35, 21, 15, 218, 112, 56, 36, 155, 65, 35, 61, 167, 107, 253, 101, 230, 90, 230, 124, + 37, 9, 203, 87, 100, 56, 208, 78, 163, 174, 145, 1, 226, 159, 138, 24, 33, 204, 167, 51, 96, 253, 188, 42, 175, 225, + 36, 173, 238, 208, 63, 180, 45, 131, 18, 159, 108, 178, 9, 134, 171, 36, 72, 201, 234, 21, 83, 208, 41, 70, 126, 251, + 104, 119, 101, 226, 246, 121, 63, 47, 183, 72, 36, 54, 116, 27, 9, 29, 53, 42, 18, 4, 242, 188, 83, 75, 179, 141, 72, + 82, 112, 222, 101, 121, 49, 239, 126, 96, 254, 243, 230, 231, 191, 194, 253, 254, 124, 145, 208, 213, 61, 160, 203, + 204, 250, 54, 138, 131, 187, 7, 145, 154, 120, 84, 188, 177, 57, 101, 167, 168, 75, 152, 131, 59, 10, 169, 152, 34, + 201, 250, 181, 9, 136, 203, 174, 16, 79, 93, 239, 95, 14, 108, 244, 70, 205, 63, 217, 109, 140, 14, 194, 116, 67, 18, + 90, 243, 2, 35, 65, 234, 193, 112, 108, 193, 128, 65, 119, 216, 71, 215, 54, 151, 6, 230, 45, 142, 197, 181, 0, 165, + 132, 132, 27, 188, 26, 138, 65, 113, 91, 187, 90, 104, 152, 232, 119, 67, 217, 217, 108, 90, 30, 79, 45, 21, 95, 126, + 54, 12, 156, 45, 27, 39, 221, 28, 0, 62, 18, 0, 152, 185, 83, 49, 131, 160, 144, 98, 174, 139, 209, 83, 181, 146, 22, + 197, 244, 221, 87, 244, 239, 196, 148, 167, 194, 239, 213, 150, 217, 246, 233, 188, 7, 174, 168, 141, 28, 183, 107, + 222, 49, 156, 42, 239, 42, 133, 237, 121, 107, 202, 172, 72, 112, 211, 111, 27, 93, 248, 46, 42, 70, 225, 225, 54, + 222, 102, 160, 7, 197, 127, 99, 84, 232, 84, 34, 101, 243, 77, 229, 243, 178, 2, 164, 194, 169, 27, 103, 145, 132, 48, + 38, 160, 159, 41, 184, 174, 197, 228, 249, 159, 222, 253, 58, 204, 243, 214, 123, 253, 232, 207, 188, 107, 169, 128, + 253, 90, 178, 153, 62, 9, 159, 178, 127, 56, 132, 171, 176, 36, 28, 44, 241, 21, 7, 53, 50, 70, 42, 30, 115, 119, 49, + 7, 180, 225, 112, 72, 245, 208, 107, 81, 54, 131, 70, 122, 119, 178, 93, 99, 78, 215, 250, 203, 15, 230, 225, 210, + 204, 181, 204, 249, 141, 132, 215, 224, 74, 18, 150, 175, 11, 35, 141, 182, 200, 112, 160, 157, 137, 65, 187, 132, 70, + 93, 35, 3, 7, 108, 56, 26, 196, 63, 21, 49, 133, 14, 14, 40, 66, 152, 79, 103, 3, 169, 84, 126, 192, 250, 121, 85, + 129, 203, 98, 76, 31, 197, 56, 129, 94, 244, 35, 152, 157, 167, 14, 179, 220, 150, 21, 170, 27, 0, 84, 229, 90, 49, + 79, 252, 153, 98, 98, 215, 216, 83, 121, 206, 23, 79, 225, 73, 86, 126, 250, 80, 149, 45, 215, 123, 212, 28, 204, 98, + 19, 138, 141, 45, 82, 187, 150, 52, 145, 232, 187, 31, 208, 217, 160, 6, 236, 243, 126, 94, 173, 194, 101, 71, 110, + 145, 72, 108, 47, 160, 83, 117, 232, 54, 18, 58, 169, 7, 9, 35, 106, 84, 36, 8, 43, 101, 63, 17, 228, 121, 167, 150, + 165, 72, 188, 143, 102, 27, 145, 164, 39, 42, 138, 189, 224, 188, 203, 242, 161, 141, 208, 235, 98, 222, 253, 192, 35, + 239, 230, 217, 189, 225, 188, 20, 252, 208, 167, 13, 63, 131, 138, 38, 126, 178, 145, 63, 185, 36, 208, 112, 248, 21, + 203, 105, 59, 70, 230, 66, 122, 119, 253, 91, 181, 107, 101, 220, 244, 90, 126, 197, 55, 9, 83, 238, 118, 56, 72, 247, + 177, 174, 9, 184, 240, 159, 18, 161, 51, 204, 63, 138, 114, 253, 36, 147, 0, 0, 0, 0, 55, 106, 194, 1, 110, 212, 132, + 3, 89, 190, 70, 2, 220, 168, 9, 7, 235, 194, 203, 6, 178, 124, 141, 4, 133, 22, 79, 5, 184, 81, 19, 14, 143, 59, 209, + 15, 214, 133, 151, 13, 225, 239, 85, 12, 100, 249, 26, 9, 83, 147, 216, 8, 10, 45, 158, 10, 61, 71, 92, 11, 112, 163, + 38, 28, 71, 201, 228, 29, 30, 119, 162, 31, 41, 29, 96, 30, 172, 11, 47, 27, 155, 97, 237, 26, 194, 223, 171, 24, 245, + 181, 105, 25, 200, 242, 53, 18, 255, 152, 247, 19, 166, 38, 177, 17, 145, 76, 115, 16, 20, 90, 60, 21, 35, 48, 254, + 20, 122, 142, 184, 22, 77, 228, 122, 23, 224, 70, 77, 56, 215, 44, 143, 57, 142, 146, 201, 59, 185, 248, 11, 58, 60, + 238, 68, 63, 11, 132, 134, 62, 82, 58, 192, 60, 101, 80, 2, 61, 88, 23, 94, 54, 111, 125, 156, 55, 54, 195, 218, 53, + 1, 169, 24, 52, 132, 191, 87, 49, 179, 213, 149, 48, 234, 107, 211, 50, 221, 1, 17, 51, 144, 229, 107, 36, 167, 143, + 169, 37, 254, 49, 239, 39, 201, 91, 45, 38, 76, 77, 98, 35, 123, 39, 160, 34, 34, 153, 230, 32, 21, 243, 36, 33, 40, + 180, 120, 42, 31, 222, 186, 43, 70, 96, 252, 41, 113, 10, 62, 40, 244, 28, 113, 45, 195, 118, 179, 44, 154, 200, 245, + 46, 173, 162, 55, 47, 192, 141, 154, 112, 247, 231, 88, 113, 174, 89, 30, 115, 153, 51, 220, 114, 28, 37, 147, 119, + 43, 79, 81, 118, 114, 241, 23, 116, 69, 155, 213, 117, 120, 220, 137, 126, 79, 182, 75, 127, 22, 8, 13, 125, 33, 98, + 207, 124, 164, 116, 128, 121, 147, 30, 66, 120, 202, 160, 4, 122, 253, 202, 198, 123, 176, 46, 188, 108, 135, 68, 126, + 109, 222, 250, 56, 111, 233, 144, 250, 110, 108, 134, 181, 107, 91, 236, 119, 106, 2, 82, 49, 104, 53, 56, 243, 105, + 8, 127, 175, 98, 63, 21, 109, 99, 102, 171, 43, 97, 81, 193, 233, 96, 212, 215, 166, 101, 227, 189, 100, 100, 186, 3, + 34, 102, 141, 105, 224, 103, 32, 203, 215, 72, 23, 161, 21, 73, 78, 31, 83, 75, 121, 117, 145, 74, 252, 99, 222, 79, + 203, 9, 28, 78, 146, 183, 90, 76, 165, 221, 152, 77, 152, 154, 196, 70, 175, 240, 6, 71, 246, 78, 64, 69, 193, 36, + 130, 68, 68, 50, 205, 65, 115, 88, 15, 64, 42, 230, 73, 66, 29, 140, 139, 67, 80, 104, 241, 84, 103, 2, 51, 85, 62, + 188, 117, 87, 9, 214, 183, 86, 140, 192, 248, 83, 187, 170, 58, 82, 226, 20, 124, 80, 213, 126, 190, 81, 232, 57, 226, + 90, 223, 83, 32, 91, 134, 237, 102, 89, 177, 135, 164, 88, 52, 145, 235, 93, 3, 251, 41, 92, 90, 69, 111, 94, 109, 47, + 173, 95, 128, 27, 53, 225, 183, 113, 247, 224, 238, 207, 177, 226, 217, 165, 115, 227, 92, 179, 60, 230, 107, 217, + 254, 231, 50, 103, 184, 229, 5, 13, 122, 228, 56, 74, 38, 239, 15, 32, 228, 238, 86, 158, 162, 236, 97, 244, 96, 237, + 228, 226, 47, 232, 211, 136, 237, 233, 138, 54, 171, 235, 189, 92, 105, 234, 240, 184, 19, 253, 199, 210, 209, 252, + 158, 108, 151, 254, 169, 6, 85, 255, 44, 16, 26, 250, 27, 122, 216, 251, 66, 196, 158, 249, 117, 174, 92, 248, 72, + 233, 0, 243, 127, 131, 194, 242, 38, 61, 132, 240, 17, 87, 70, 241, 148, 65, 9, 244, 163, 43, 203, 245, 250, 149, 141, + 247, 205, 255, 79, 246, 96, 93, 120, 217, 87, 55, 186, 216, 14, 137, 252, 218, 57, 227, 62, 219, 188, 245, 113, 222, + 139, 159, 179, 223, 210, 33, 245, 221, 229, 75, 55, 220, 216, 12, 107, 215, 239, 102, 169, 214, 182, 216, 239, 212, + 129, 178, 45, 213, 4, 164, 98, 208, 51, 206, 160, 209, 106, 112, 230, 211, 93, 26, 36, 210, 16, 254, 94, 197, 39, 148, + 156, 196, 126, 42, 218, 198, 73, 64, 24, 199, 204, 86, 87, 194, 251, 60, 149, 195, 162, 130, 211, 193, 149, 232, 17, + 192, 168, 175, 77, 203, 159, 197, 143, 202, 198, 123, 201, 200, 241, 17, 11, 201, 116, 7, 68, 204, 67, 109, 134, 205, + 26, 211, 192, 207, 45, 185, 2, 206, 64, 150, 175, 145, 119, 252, 109, 144, 46, 66, 43, 146, 25, 40, 233, 147, 156, 62, + 166, 150, 171, 84, 100, 151, 242, 234, 34, 149, 197, 128, 224, 148, 248, 199, 188, 159, 207, 173, 126, 158, 150, 19, + 56, 156, 161, 121, 250, 157, 36, 111, 181, 152, 19, 5, 119, 153, 74, 187, 49, 155, 125, 209, 243, 154, 48, 53, 137, + 141, 7, 95, 75, 140, 94, 225, 13, 142, 105, 139, 207, 143, 236, 157, 128, 138, 219, 247, 66, 139, 130, 73, 4, 137, + 181, 35, 198, 136, 136, 100, 154, 131, 191, 14, 88, 130, 230, 176, 30, 128, 209, 218, 220, 129, 84, 204, 147, 132, 99, + 166, 81, 133, 58, 24, 23, 135, 13, 114, 213, 134, 160, 208, 226, 169, 151, 186, 32, 168, 206, 4, 102, 170, 249, 110, + 164, 171, 124, 120, 235, 174, 75, 18, 41, 175, 18, 172, 111, 173, 37, 198, 173, 172, 24, 129, 241, 167, 47, 235, 51, + 166, 118, 85, 117, 164, 65, 63, 183, 165, 196, 41, 248, 160, 243, 67, 58, 161, 170, 253, 124, 163, 157, 151, 190, 162, + 208, 115, 196, 181, 231, 25, 6, 180, 190, 167, 64, 182, 137, 205, 130, 183, 12, 219, 205, 178, 59, 177, 15, 179, 98, + 15, 73, 177, 85, 101, 139, 176, 104, 34, 215, 187, 95, 72, 21, 186, 6, 246, 83, 184, 49, 156, 145, 185, 180, 138, 222, + 188, 131, 224, 28, 189, 218, 94, 90, 191, 237, 52, 152, 190, 0, 0, 0, 0, 101, 103, 188, 184, 139, 200, 9, 170, 238, + 175, 181, 18, 87, 151, 98, 143, 50, 240, 222, 55, 220, 95, 107, 37, 185, 56, 215, 157, 239, 40, 180, 197, 138, 79, 8, + 125, 100, 224, 189, 111, 1, 135, 1, 215, 184, 191, 214, 74, 221, 216, 106, 242, 51, 119, 223, 224, 86, 16, 99, 88, + 159, 87, 25, 80, 250, 48, 165, 232, 20, 159, 16, 250, 113, 248, 172, 66, 200, 192, 123, 223, 173, 167, 199, 103, 67, + 8, 114, 117, 38, 111, 206, 205, 112, 127, 173, 149, 21, 24, 17, 45, 251, 183, 164, 63, 158, 208, 24, 135, 39, 232, + 207, 26, 66, 143, 115, 162, 172, 32, 198, 176, 201, 71, 122, 8, 62, 175, 50, 160, 91, 200, 142, 24, 181, 103, 59, 10, + 208, 0, 135, 178, 105, 56, 80, 47, 12, 95, 236, 151, 226, 240, 89, 133, 135, 151, 229, 61, 209, 135, 134, 101, 180, + 224, 58, 221, 90, 79, 143, 207, 63, 40, 51, 119, 134, 16, 228, 234, 227, 119, 88, 82, 13, 216, 237, 64, 104, 191, 81, + 248, 161, 248, 43, 240, 196, 159, 151, 72, 42, 48, 34, 90, 79, 87, 158, 226, 246, 111, 73, 127, 147, 8, 245, 199, 125, + 167, 64, 213, 24, 192, 252, 109, 78, 208, 159, 53, 43, 183, 35, 141, 197, 24, 150, 159, 160, 127, 42, 39, 25, 71, 253, + 186, 124, 32, 65, 2, 146, 143, 244, 16, 247, 232, 72, 168, 61, 88, 20, 155, 88, 63, 168, 35, 182, 144, 29, 49, 211, + 247, 161, 137, 106, 207, 118, 20, 15, 168, 202, 172, 225, 7, 127, 190, 132, 96, 195, 6, 210, 112, 160, 94, 183, 23, + 28, 230, 89, 184, 169, 244, 60, 223, 21, 76, 133, 231, 194, 209, 224, 128, 126, 105, 14, 47, 203, 123, 107, 72, 119, + 195, 162, 15, 13, 203, 199, 104, 177, 115, 41, 199, 4, 97, 76, 160, 184, 217, 245, 152, 111, 68, 144, 255, 211, 252, + 126, 80, 102, 238, 27, 55, 218, 86, 77, 39, 185, 14, 40, 64, 5, 182, 198, 239, 176, 164, 163, 136, 12, 28, 26, 176, + 219, 129, 127, 215, 103, 57, 145, 120, 210, 43, 244, 31, 110, 147, 3, 247, 38, 59, 102, 144, 154, 131, 136, 63, 47, + 145, 237, 88, 147, 41, 84, 96, 68, 180, 49, 7, 248, 12, 223, 168, 77, 30, 186, 207, 241, 166, 236, 223, 146, 254, 137, + 184, 46, 70, 103, 23, 155, 84, 2, 112, 39, 236, 187, 72, 240, 113, 222, 47, 76, 201, 48, 128, 249, 219, 85, 231, 69, + 99, 156, 160, 63, 107, 249, 199, 131, 211, 23, 104, 54, 193, 114, 15, 138, 121, 203, 55, 93, 228, 174, 80, 225, 92, + 64, 255, 84, 78, 37, 152, 232, 246, 115, 136, 139, 174, 22, 239, 55, 22, 248, 64, 130, 4, 157, 39, 62, 188, 36, 31, + 233, 33, 65, 120, 85, 153, 175, 215, 224, 139, 202, 176, 92, 51, 59, 182, 89, 237, 94, 209, 229, 85, 176, 126, 80, 71, + 213, 25, 236, 255, 108, 33, 59, 98, 9, 70, 135, 218, 231, 233, 50, 200, 130, 142, 142, 112, 212, 158, 237, 40, 177, + 249, 81, 144, 95, 86, 228, 130, 58, 49, 88, 58, 131, 9, 143, 167, 230, 110, 51, 31, 8, 193, 134, 13, 109, 166, 58, + 181, 164, 225, 64, 189, 193, 134, 252, 5, 47, 41, 73, 23, 74, 78, 245, 175, 243, 118, 34, 50, 150, 17, 158, 138, 120, + 190, 43, 152, 29, 217, 151, 32, 75, 201, 244, 120, 46, 174, 72, 192, 192, 1, 253, 210, 165, 102, 65, 106, 28, 94, 150, + 247, 121, 57, 42, 79, 151, 150, 159, 93, 242, 241, 35, 229, 5, 25, 107, 77, 96, 126, 215, 245, 142, 209, 98, 231, 235, + 182, 222, 95, 82, 142, 9, 194, 55, 233, 181, 122, 217, 70, 0, 104, 188, 33, 188, 208, 234, 49, 223, 136, 143, 86, 99, + 48, 97, 249, 214, 34, 4, 158, 106, 154, 189, 166, 189, 7, 216, 193, 1, 191, 54, 110, 180, 173, 83, 9, 8, 21, 154, 78, + 114, 29, 255, 41, 206, 165, 17, 134, 123, 183, 116, 225, 199, 15, 205, 217, 16, 146, 168, 190, 172, 42, 70, 17, 25, + 56, 35, 118, 165, 128, 117, 102, 198, 216, 16, 1, 122, 96, 254, 174, 207, 114, 155, 201, 115, 202, 34, 241, 164, 87, + 71, 150, 24, 239, 169, 57, 173, 253, 204, 94, 17, 69, 6, 238, 77, 118, 99, 137, 241, 206, 141, 38, 68, 220, 232, 65, + 248, 100, 81, 121, 47, 249, 52, 30, 147, 65, 218, 177, 38, 83, 191, 214, 154, 235, 233, 198, 249, 179, 140, 161, 69, + 11, 98, 14, 240, 25, 7, 105, 76, 161, 190, 81, 155, 60, 219, 54, 39, 132, 53, 153, 146, 150, 80, 254, 46, 46, 153, + 185, 84, 38, 252, 222, 232, 158, 18, 113, 93, 140, 119, 22, 225, 52, 206, 46, 54, 169, 171, 73, 138, 17, 69, 230, 63, + 3, 32, 129, 131, 187, 118, 145, 224, 227, 19, 246, 92, 91, 253, 89, 233, 73, 152, 62, 85, 241, 33, 6, 130, 108, 68, + 97, 62, 212, 170, 206, 139, 198, 207, 169, 55, 126, 56, 65, 127, 214, 93, 38, 195, 110, 179, 137, 118, 124, 214, 238, + 202, 196, 111, 214, 29, 89, 10, 177, 161, 225, 228, 30, 20, 243, 129, 121, 168, 75, 215, 105, 203, 19, 178, 14, 119, + 171, 92, 161, 194, 185, 57, 198, 126, 1, 128, 254, 169, 156, 229, 153, 21, 36, 11, 54, 160, 54, 110, 81, 28, 142, 167, + 22, 102, 134, 194, 113, 218, 62, 44, 222, 111, 44, 73, 185, 211, 148, 240, 129, 4, 9, 149, 230, 184, 177, 123, 73, 13, + 163, 30, 46, 177, 27, 72, 62, 210, 67, 45, 89, 110, 251, 195, 246, 219, 233, 166, 145, 103, 81, 31, 169, 176, 204, + 122, 206, 12, 116, 148, 97, 185, 102, 241, 6, 5, 222, 0, 0, 0, 0, 119, 7, 48, 150, 238, 14, 97, 44, 153, 9, 81, 186, + 7, 109, 196, 25, 112, 106, 244, 143, 233, 99, 165, 53, 158, 100, 149, 163, 14, 219, 136, 50, 121, 220, 184, 164, 224, + 213, 233, 30, 151, 210, 217, 136, 9, 182, 76, 43, 126, 177, 124, 189, 231, 184, 45, 7, 144, 191, 29, 145, 29, 183, 16, + 100, 106, 176, 32, 242, 243, 185, 113, 72, 132, 190, 65, 222, 26, 218, 212, 125, 109, 221, 228, 235, 244, 212, 181, + 81, 131, 211, 133, 199, 19, 108, 152, 86, 100, 107, 168, 192, 253, 98, 249, 122, 138, 101, 201, 236, 20, 1, 92, 79, + 99, 6, 108, 217, 250, 15, 61, 99, 141, 8, 13, 245, 59, 110, 32, 200, 76, 105, 16, 94, 213, 96, 65, 228, 162, 103, 113, + 114, 60, 3, 228, 209, 75, 4, 212, 71, 210, 13, 133, 253, 165, 10, 181, 107, 53, 181, 168, 250, 66, 178, 152, 108, 219, + 187, 201, 214, 172, 188, 249, 64, 50, 216, 108, 227, 69, 223, 92, 117, 220, 214, 13, 207, 171, 209, 61, 89, 38, 217, + 48, 172, 81, 222, 0, 58, 200, 215, 81, 128, 191, 208, 97, 22, 33, 180, 244, 181, 86, 179, 196, 35, 207, 186, 149, 153, + 184, 189, 165, 15, 40, 2, 184, 158, 95, 5, 136, 8, 198, 12, 217, 178, 177, 11, 233, 36, 47, 111, 124, 135, 88, 104, + 76, 17, 193, 97, 29, 171, 182, 102, 45, 61, 118, 220, 65, 144, 1, 219, 113, 6, 152, 210, 32, 188, 239, 213, 16, 42, + 113, 177, 133, 137, 6, 182, 181, 31, 159, 191, 228, 165, 232, 184, 212, 51, 120, 7, 201, 162, 15, 0, 249, 52, 150, 9, + 168, 142, 225, 14, 152, 24, 127, 106, 13, 187, 8, 109, 61, 45, 145, 100, 108, 151, 230, 99, 92, 1, 107, 107, 81, 244, + 28, 108, 97, 98, 133, 101, 48, 216, 242, 98, 0, 78, 108, 6, 149, 237, 27, 1, 165, 123, 130, 8, 244, 193, 245, 15, 196, + 87, 101, 176, 217, 198, 18, 183, 233, 80, 139, 190, 184, 234, 252, 185, 136, 124, 98, 221, 29, 223, 21, 218, 45, 73, + 140, 211, 124, 243, 251, 212, 76, 101, 77, 178, 97, 88, 58, 181, 81, 206, 163, 188, 0, 116, 212, 187, 48, 226, 74, + 223, 165, 65, 61, 216, 149, 215, 164, 209, 196, 109, 211, 214, 244, 251, 67, 105, 233, 106, 52, 110, 217, 252, 173, + 103, 136, 70, 218, 96, 184, 208, 68, 4, 45, 115, 51, 3, 29, 229, 170, 10, 76, 95, 221, 13, 124, 201, 80, 5, 113, 60, + 39, 2, 65, 170, 190, 11, 16, 16, 201, 12, 32, 134, 87, 104, 181, 37, 32, 111, 133, 179, 185, 102, 212, 9, 206, 97, + 228, 159, 94, 222, 249, 14, 41, 217, 201, 152, 176, 208, 152, 34, 199, 215, 168, 180, 89, 179, 61, 23, 46, 180, 13, + 129, 183, 189, 92, 59, 192, 186, 108, 173, 237, 184, 131, 32, 154, 191, 179, 182, 3, 182, 226, 12, 116, 177, 210, 154, + 234, 213, 71, 57, 157, 210, 119, 175, 4, 219, 38, 21, 115, 220, 22, 131, 227, 99, 11, 18, 148, 100, 59, 132, 13, 109, + 106, 62, 122, 106, 90, 168, 228, 14, 207, 11, 147, 9, 255, 157, 10, 0, 174, 39, 125, 7, 158, 177, 240, 15, 147, 68, + 135, 8, 163, 210, 30, 1, 242, 104, 105, 6, 194, 254, 247, 98, 87, 93, 128, 101, 103, 203, 25, 108, 54, 113, 110, 107, + 6, 231, 254, 212, 27, 118, 137, 211, 43, 224, 16, 218, 122, 90, 103, 221, 74, 204, 249, 185, 223, 111, 142, 190, 239, + 249, 23, 183, 190, 67, 96, 176, 142, 213, 214, 214, 163, 232, 161, 209, 147, 126, 56, 216, 194, 196, 79, 223, 242, 82, + 209, 187, 103, 241, 166, 188, 87, 103, 63, 181, 6, 221, 72, 178, 54, 75, 216, 13, 43, 218, 175, 10, 27, 76, 54, 3, 74, + 246, 65, 4, 122, 96, 223, 96, 239, 195, 168, 103, 223, 85, 49, 110, 142, 239, 70, 105, 190, 121, 203, 97, 179, 140, + 188, 102, 131, 26, 37, 111, 210, 160, 82, 104, 226, 54, 204, 12, 119, 149, 187, 11, 71, 3, 34, 2, 22, 185, 85, 5, 38, + 47, 197, 186, 59, 190, 178, 189, 11, 40, 43, 180, 90, 146, 92, 179, 106, 4, 194, 215, 255, 167, 181, 208, 207, 49, 44, + 217, 158, 139, 91, 222, 174, 29, 155, 100, 194, 176, 236, 99, 242, 38, 117, 106, 163, 156, 2, 109, 147, 10, 156, 9, 6, + 169, 235, 14, 54, 63, 114, 7, 103, 133, 5, 0, 87, 19, 149, 191, 74, 130, 226, 184, 122, 20, 123, 177, 43, 174, 12, + 182, 27, 56, 146, 210, 142, 155, 229, 213, 190, 13, 124, 220, 239, 183, 11, 219, 223, 33, 134, 211, 210, 212, 241, + 212, 226, 66, 104, 221, 179, 248, 31, 218, 131, 110, 129, 190, 22, 205, 246, 185, 38, 91, 111, 176, 119, 225, 24, 183, + 71, 119, 136, 8, 90, 230, 255, 15, 106, 112, 102, 6, 59, 202, 17, 1, 11, 92, 143, 101, 158, 255, 248, 98, 174, 105, + 97, 107, 255, 211, 22, 108, 207, 69, 160, 10, 226, 120, 215, 13, 210, 238, 78, 4, 131, 84, 57, 3, 179, 194, 167, 103, + 38, 97, 208, 96, 22, 247, 73, 105, 71, 77, 62, 110, 119, 219, 174, 209, 106, 74, 217, 214, 90, 220, 64, 223, 11, 102, + 55, 216, 59, 240, 169, 188, 174, 83, 222, 187, 158, 197, 71, 178, 207, 127, 48, 181, 255, 233, 189, 189, 242, 28, 202, + 186, 194, 138, 83, 179, 147, 48, 36, 180, 163, 166, 186, 208, 54, 5, 205, 215, 6, 147, 84, 222, 87, 41, 35, 217, 103, + 191, 179, 102, 122, 46, 196, 97, 74, 184, 93, 104, 27, 2, 42, 111, 43, 148, 180, 11, 190, 55, 195, 12, 142, 161, 90, + 5, 223, 27, 45, 2, 239, 141, 0, 0, 0, 0, 25, 27, 49, 65, 50, 54, 98, 130, 43, 45, 83, 195, 100, 108, 197, 4, 125, 119, + 244, 69, 86, 90, 167, 134, 79, 65, 150, 199, 200, 217, 138, 8, 209, 194, 187, 73, 250, 239, 232, 138, 227, 244, 217, + 203, 172, 181, 79, 12, 181, 174, 126, 77, 158, 131, 45, 142, 135, 152, 28, 207, 74, 194, 18, 81, 83, 217, 35, 16, 120, + 244, 112, 211, 97, 239, 65, 146, 46, 174, 215, 85, 55, 181, 230, 20, 28, 152, 181, 215, 5, 131, 132, 150, 130, 27, + 152, 89, 155, 0, 169, 24, 176, 45, 250, 219, 169, 54, 203, 154, 230, 119, 93, 93, 255, 108, 108, 28, 212, 65, 63, 223, + 205, 90, 14, 158, 149, 132, 36, 162, 140, 159, 21, 227, 167, 178, 70, 32, 190, 169, 119, 97, 241, 232, 225, 166, 232, + 243, 208, 231, 195, 222, 131, 36, 218, 197, 178, 101, 93, 93, 174, 170, 68, 70, 159, 235, 111, 107, 204, 40, 118, 112, + 253, 105, 57, 49, 107, 174, 32, 42, 90, 239, 11, 7, 9, 44, 18, 28, 56, 109, 223, 70, 54, 243, 198, 93, 7, 178, 237, + 112, 84, 113, 244, 107, 101, 48, 187, 42, 243, 247, 162, 49, 194, 182, 137, 28, 145, 117, 144, 7, 160, 52, 23, 159, + 188, 251, 14, 132, 141, 186, 37, 169, 222, 121, 60, 178, 239, 56, 115, 243, 121, 255, 106, 232, 72, 190, 65, 197, 27, + 125, 88, 222, 42, 60, 240, 121, 79, 5, 233, 98, 126, 68, 194, 79, 45, 135, 219, 84, 28, 198, 148, 21, 138, 1, 141, 14, + 187, 64, 166, 35, 232, 131, 191, 56, 217, 194, 56, 160, 197, 13, 33, 187, 244, 76, 10, 150, 167, 143, 19, 141, 150, + 206, 92, 204, 0, 9, 69, 215, 49, 72, 110, 250, 98, 139, 119, 225, 83, 202, 186, 187, 93, 84, 163, 160, 108, 21, 136, + 141, 63, 214, 145, 150, 14, 151, 222, 215, 152, 80, 199, 204, 169, 17, 236, 225, 250, 210, 245, 250, 203, 147, 114, + 98, 215, 92, 107, 121, 230, 29, 64, 84, 181, 222, 89, 79, 132, 159, 22, 14, 18, 88, 15, 21, 35, 25, 36, 56, 112, 218, + 61, 35, 65, 155, 101, 253, 107, 167, 124, 230, 90, 230, 87, 203, 9, 37, 78, 208, 56, 100, 1, 145, 174, 163, 24, 138, + 159, 226, 51, 167, 204, 33, 42, 188, 253, 96, 173, 36, 225, 175, 180, 63, 208, 238, 159, 18, 131, 45, 134, 9, 178, + 108, 201, 72, 36, 171, 208, 83, 21, 234, 251, 126, 70, 41, 226, 101, 119, 104, 47, 63, 121, 246, 54, 36, 72, 183, 29, + 9, 27, 116, 4, 18, 42, 53, 75, 83, 188, 242, 82, 72, 141, 179, 121, 101, 222, 112, 96, 126, 239, 49, 231, 230, 243, + 254, 254, 253, 194, 191, 213, 208, 145, 124, 204, 203, 160, 61, 131, 138, 54, 250, 154, 145, 7, 187, 177, 188, 84, + 120, 168, 167, 101, 57, 59, 131, 152, 75, 34, 152, 169, 10, 9, 181, 250, 201, 16, 174, 203, 136, 95, 239, 93, 79, 70, + 244, 108, 14, 109, 217, 63, 205, 116, 194, 14, 140, 243, 90, 18, 67, 234, 65, 35, 2, 193, 108, 112, 193, 216, 119, 65, + 128, 151, 54, 215, 71, 142, 45, 230, 6, 165, 0, 181, 197, 188, 27, 132, 132, 113, 65, 138, 26, 104, 90, 187, 91, 67, + 119, 232, 152, 90, 108, 217, 217, 21, 45, 79, 30, 12, 54, 126, 95, 39, 27, 45, 156, 62, 0, 28, 221, 185, 152, 0, 18, + 160, 131, 49, 83, 139, 174, 98, 144, 146, 181, 83, 209, 221, 244, 197, 22, 196, 239, 244, 87, 239, 194, 167, 148, 246, + 217, 150, 213, 174, 7, 188, 233, 183, 28, 141, 168, 156, 49, 222, 107, 133, 42, 239, 42, 202, 107, 121, 237, 211, 112, + 72, 172, 248, 93, 27, 111, 225, 70, 42, 46, 102, 222, 54, 225, 127, 197, 7, 160, 84, 232, 84, 99, 77, 243, 101, 34, 2, + 178, 243, 229, 27, 169, 194, 164, 48, 132, 145, 103, 41, 159, 160, 38, 228, 197, 174, 184, 253, 222, 159, 249, 214, + 243, 204, 58, 207, 232, 253, 123, 128, 169, 107, 188, 153, 178, 90, 253, 178, 159, 9, 62, 171, 132, 56, 127, 44, 28, + 36, 176, 53, 7, 21, 241, 30, 42, 70, 50, 7, 49, 119, 115, 72, 112, 225, 180, 81, 107, 208, 245, 122, 70, 131, 54, 99, + 93, 178, 119, 203, 250, 215, 78, 210, 225, 230, 15, 249, 204, 181, 204, 224, 215, 132, 141, 175, 150, 18, 74, 182, + 141, 35, 11, 157, 160, 112, 200, 132, 187, 65, 137, 3, 35, 93, 70, 26, 56, 108, 7, 49, 21, 63, 196, 40, 14, 14, 133, + 103, 79, 152, 66, 126, 84, 169, 3, 85, 121, 250, 192, 76, 98, 203, 129, 129, 56, 197, 31, 152, 35, 244, 94, 179, 14, + 167, 157, 170, 21, 150, 220, 229, 84, 0, 27, 252, 79, 49, 90, 215, 98, 98, 153, 206, 121, 83, 216, 73, 225, 79, 23, + 80, 250, 126, 86, 123, 215, 45, 149, 98, 204, 28, 212, 45, 141, 138, 19, 52, 150, 187, 82, 31, 187, 232, 145, 6, 160, + 217, 208, 94, 126, 243, 236, 71, 101, 194, 173, 108, 72, 145, 110, 117, 83, 160, 47, 58, 18, 54, 232, 35, 9, 7, 169, + 8, 36, 84, 106, 17, 63, 101, 43, 150, 167, 121, 228, 143, 188, 72, 165 +]; +const arr2: number[] = [ + 164, 145, 27, 102, 189, 138, 42, 39, 242, 203, 188, 224, 235, 208, 141, 161, 192, 253, 222, 98, 217, 230, 239, 35, 20, + 188, 225, 189, 13, 167, 208, 252, 38, 138, 131, 63, 63, 145, 178, 126, 112, 208, 36, 185, 105, 203, 21, 248, 66, 230, + 70, 59, 91, 253, 119, 122, 220, 101, 107, 181, 197, 126, 90, 244, 238, 83, 9, 55, 247, 72, 56, 118, 184, 9, 174, 177, + 161, 18, 159, 240, 138, 63, 204, 51, 147, 36, 253, 114, 0, 0, 0, 0, 1, 194, 106, 55, 3, 132, 212, 110, 2, 70, 190, 89, + 7, 9, 168, 220, 6, 203, 194, 235, 4, 141, 124, 178, 5, 79, 22, 133, 14, 19, 81, 184, 15, 209, 59, 143, 13, 151, 133, + 214, 12, 85, 239, 225, 9, 26, 249, 100, 8, 216, 147, 83, 10, 158, 45, 10, 11, 92, 71, 61, 28, 38, 163, 112, 29, 228, + 201, 71, 31, 162, 119, 30, 30, 96, 29, 41, 27, 47, 11, 172, 26, 237, 97, 155, 24, 171, 223, 194, 25, 105, 181, 245, + 18, 53, 242, 200, 19, 247, 152, 255, 17, 177, 38, 166, 16, 115, 76, 145, 21, 60, 90, 20, 20, 254, 48, 35, 22, 184, + 142, 122, 23, 122, 228, 77, 56, 77, 70, 224, 57, 143, 44, 215, 59, 201, 146, 142, 58, 11, 248, 185, 63, 68, 238, 60, + 62, 134, 132, 11, 60, 192, 58, 82, 61, 2, 80, 101, 54, 94, 23, 88, 55, 156, 125, 111, 53, 218, 195, 54, 52, 24, 169, + 1, 49, 87, 191, 132, 48, 149, 213, 179, 50, 211, 107, 234, 51, 17, 1, 221, 36, 107, 229, 144, 37, 169, 143, 167, 39, + 239, 49, 254, 38, 45, 91, 201, 35, 98, 77, 76, 34, 160, 39, 123, 32, 230, 153, 34, 33, 36, 243, 21, 42, 120, 180, 40, + 43, 186, 222, 31, 41, 252, 96, 70, 40, 62, 10, 113, 45, 113, 28, 244, 44, 179, 118, 195, 46, 245, 200, 154, 47, 55, + 162, 173, 112, 154, 141, 192, 113, 88, 231, 247, 115, 30, 89, 174, 114, 220, 51, 153, 119, 147, 37, 28, 118, 81, 79, + 43, 116, 23, 241, 114, 117, 213, 155, 69, 126, 137, 220, 120, 127, 75, 182, 79, 125, 13, 8, 22, 124, 207, 98, 33, 121, + 128, 116, 164, 120, 66, 30, 147, 122, 4, 160, 202, 123, 198, 202, 253, 108, 188, 46, 176, 109, 126, 68, 135, 111, 56, + 250, 222, 110, 250, 144, 233, 107, 181, 134, 108, 106, 119, 236, 91, 104, 49, 82, 2, 105, 243, 56, 53, 98, 175, 127, + 8, 99, 109, 21, 63, 97, 43, 171, 102, 96, 233, 193, 81, 101, 166, 215, 212, 100, 100, 189, 227, 102, 34, 3, 186, 103, + 224, 105, 141, 72, 215, 203, 32, 73, 21, 161, 23, 75, 83, 31, 78, 74, 145, 117, 121, 79, 222, 99, 252, 78, 28, 9, 203, + 76, 90, 183, 146, 77, 152, 221, 165, 70, 196, 154, 152, 71, 6, 240, 175, 69, 64, 78, 246, 68, 130, 36, 193, 65, 205, + 50, 68, 64, 15, 88, 115, 66, 73, 230, 42, 67, 139, 140, 29, 84, 241, 104, 80, 85, 51, 2, 103, 87, 117, 188, 62, 86, + 183, 214, 9, 83, 248, 192, 140, 82, 58, 170, 187, 80, 124, 20, 226, 81, 190, 126, 213, 90, 226, 57, 232, 91, 32, 83, + 223, 89, 102, 237, 134, 88, 164, 135, 177, 93, 235, 145, 52, 92, 41, 251, 3, 94, 111, 69, 90, 95, 173, 47, 109, 225, + 53, 27, 128, 224, 247, 113, 183, 226, 177, 207, 238, 227, 115, 165, 217, 230, 60, 179, 92, 231, 254, 217, 107, 229, + 184, 103, 50, 228, 122, 13, 5, 239, 38, 74, 56, 238, 228, 32, 15, 236, 162, 158, 86, 237, 96, 244, 97, 232, 47, 226, + 228, 233, 237, 136, 211, 235, 171, 54, 138, 234, 105, 92, 189, 253, 19, 184, 240, 252, 209, 210, 199, 254, 151, 108, + 158, 255, 85, 6, 169, 250, 26, 16, 44, 251, 216, 122, 27, 249, 158, 196, 66, 248, 92, 174, 117, 243, 0, 233, 72, 242, + 194, 131, 127, 240, 132, 61, 38, 241, 70, 87, 17, 244, 9, 65, 148, 245, 203, 43, 163, 247, 141, 149, 250, 246, 79, + 255, 205, 217, 120, 93, 96, 216, 186, 55, 87, 218, 252, 137, 14, 219, 62, 227, 57, 222, 113, 245, 188, 223, 179, 159, + 139, 221, 245, 33, 210, 220, 55, 75, 229, 215, 107, 12, 216, 214, 169, 102, 239, 212, 239, 216, 182, 213, 45, 178, + 129, 208, 98, 164, 4, 209, 160, 206, 51, 211, 230, 112, 106, 210, 36, 26, 93, 197, 94, 254, 16, 196, 156, 148, 39, + 198, 218, 42, 126, 199, 24, 64, 73, 194, 87, 86, 204, 195, 149, 60, 251, 193, 211, 130, 162, 192, 17, 232, 149, 203, + 77, 175, 168, 202, 143, 197, 159, 200, 201, 123, 198, 201, 11, 17, 241, 204, 68, 7, 116, 205, 134, 109, 67, 207, 192, + 211, 26, 206, 2, 185, 45, 145, 175, 150, 64, 144, 109, 252, 119, 146, 43, 66, 46, 147, 233, 40, 25, 150, 166, 62, 156, + 151, 100, 84, 171, 149, 34, 234, 242, 148, 224, 128, 197, 159, 188, 199, 248, 158, 126, 173, 207, 156, 56, 19, 150, + 157, 250, 121, 161, 152, 181, 111, 36, 153, 119, 5, 19, 155, 49, 187, 74, 154, 243, 209, 125, 141, 137, 53, 48, 140, + 75, 95, 7, 142, 13, 225, 94, 143, 207, 139, 105, 138, 128, 157, 236, 139, 66, 247, 219, 137, 4, 73, 130, 136, 198, 35, + 181, 131, 154, 100, 136, 130, 88, 14, 191, 128, 30, 176, 230, 129, 220, 218, 209, 132, 147, 204, 84, 133, 81, 166, 99, + 135, 23, 24, 58, 134, 213, 114, 13, 169, 226, 208, 160, 168, 32, 186, 151, 170, 102, 4, 206, 171, 164, 110, 249, 174, + 235, 120, 124, 175, 41, 18, 75, 173, 111, 172, 18, 172, 173, 198, 37, 167, 241, 129, 24, 166, 51, 235, 47, 164, 117, + 85, 118, 165, 183, 63, 65, 160, 248, 41, 196, 161, 58, 67, 243, 163, 124, 253, 170, 162, 190, 151, 157, 181, 196, 115, + 208, 180, 6, 25, 231, 182, 64, 167, 190, 183, 130, 205, 137, 178, 205, 219, 12, 179, 15, 177, 59, 177, 73, 15, 98, + 176, 139, 101, 85, 187, 215, 34, 104, 186, 21, 72, 95, 184, 83, 246, 6, 185, 145, 156, 49, 188, 222, 138, 180, 189, + 28, 224, 131, 191, 90, 94, 218, 190, 152, 52, 237, 0, 0, 0, 0, 184, 188, 103, 101, 170, 9, 200, 139, 18, 181, 175, + 238, 143, 98, 151, 87, 55, 222, 240, 50, 37, 107, 95, 220, 157, 215, 56, 185, 197, 180, 40, 239, 125, 8, 79, 138, 111, + 189, 224, 100, 215, 1, 135, 1, 74, 214, 191, 184, 242, 106, 216, 221, 224, 223, 119, 51, 88, 99, 16, 86, 80, 25, 87, + 159, 232, 165, 48, 250, 250, 16, 159, 20, 66, 172, 248, 113, 223, 123, 192, 200, 103, 199, 167, 173, 117, 114, 8, 67, + 205, 206, 111, 38, 149, 173, 127, 112, 45, 17, 24, 21, 63, 164, 183, 251, 135, 24, 208, 158, 26, 207, 232, 39, 162, + 115, 143, 66, 176, 198, 32, 172, 8, 122, 71, 201, 160, 50, 175, 62, 24, 142, 200, 91, 10, 59, 103, 181, 178, 135, 0, + 208, 47, 80, 56, 105, 151, 236, 95, 12, 133, 89, 240, 226, 61, 229, 151, 135, 101, 134, 135, 209, 221, 58, 224, 180, + 207, 143, 79, 90, 119, 51, 40, 63, 234, 228, 16, 134, 82, 88, 119, 227, 64, 237, 216, 13, 248, 81, 191, 104, 240, 43, + 248, 161, 72, 151, 159, 196, 90, 34, 48, 42, 226, 158, 87, 79, 127, 73, 111, 246, 199, 245, 8, 147, 213, 64, 167, 125, + 109, 252, 192, 24, 53, 159, 208, 78, 141, 35, 183, 43, 159, 150, 24, 197, 39, 42, 127, 160, 186, 253, 71, 25, 2, 65, + 32, 124, 16, 244, 143, 146, 168, 72, 232, 247, 155, 20, 88, 61, 35, 168, 63, 88, 49, 29, 144, 182, 137, 161, 247, 211, + 20, 118, 207, 106, 172, 202, 168, 15, 190, 127, 7, 225, 6, 195, 96, 132, 94, 160, 112, 210, 230, 28, 23, 183, 244, + 169, 184, 89, 76, 21, 223, 60, 209, 194, 231, 133, 105, 126, 128, 224, 123, 203, 47, 14, 195, 119, 72, 107, 203, 13, + 15, 162, 115, 177, 104, 199, 97, 4, 199, 41, 217, 184, 160, 76, 68, 111, 152, 245, 252, 211, 255, 144, 238, 102, 80, + 126, 86, 218, 55, 27, 14, 185, 39, 77, 182, 5, 64, 40, 164, 176, 239, 198, 28, 12, 136, 163, 129, 219, 176, 26, 57, + 103, 215, 127, 43, 210, 120, 145, 147, 110, 31, 244, 59, 38, 247, 3, 131, 154, 144, 102, 145, 47, 63, 136, 41, 147, + 88, 237, 180, 68, 96, 84, 12, 248, 7, 49, 30, 77, 168, 223, 166, 241, 207, 186, 254, 146, 223, 236, 70, 46, 184, 137, + 84, 155, 23, 103, 236, 39, 112, 2, 113, 240, 72, 187, 201, 76, 47, 222, 219, 249, 128, 48, 99, 69, 231, 85, 107, 63, + 160, 156, 211, 131, 199, 249, 193, 54, 104, 23, 121, 138, 15, 114, 228, 93, 55, 203, 92, 225, 80, 174, 78, 84, 255, + 64, 246, 232, 152, 37, 174, 139, 136, 115, 22, 55, 239, 22, 4, 130, 64, 248, 188, 62, 39, 157, 33, 233, 31, 36, 153, + 85, 120, 65, 139, 224, 215, 175, 51, 92, 176, 202, 237, 89, 182, 59, 85, 229, 209, 94, 71, 80, 126, 176, 255, 236, 25, + 213, 98, 59, 33, 108, 218, 135, 70, 9, 200, 50, 233, 231, 112, 142, 142, 130, 40, 237, 158, 212, 144, 81, 249, 177, + 130, 228, 86, 95, 58, 88, 49, 58, 167, 143, 9, 131, 31, 51, 110, 230, 13, 134, 193, 8, 181, 58, 166, 109, 189, 64, + 225, 164, 5, 252, 134, 193, 23, 73, 41, 47, 175, 245, 78, 74, 50, 34, 118, 243, 138, 158, 17, 150, 152, 43, 190, 120, + 32, 151, 217, 29, 120, 244, 201, 75, 192, 72, 174, 46, 210, 253, 1, 192, 106, 65, 102, 165, 247, 150, 94, 28, 79, 42, + 57, 121, 93, 159, 150, 151, 229, 35, 241, 242, 77, 107, 25, 5, 245, 215, 126, 96, 231, 98, 209, 142, 95, 222, 182, + 235, 194, 9, 142, 82, 122, 181, 233, 55, 104, 0, 70, 217, 208, 188, 33, 188, 136, 223, 49, 234, 48, 99, 86, 143, 34, + 214, 249, 97, 154, 106, 158, 4, 7, 189, 166, 189, 191, 1, 193, 216, 173, 180, 110, 54, 21, 8, 9, 83, 29, 114, 78, 154, + 165, 206, 41, 255, 183, 123, 134, 17, 15, 199, 225, 116, 146, 16, 217, 205, 42, 172, 190, 168, 56, 25, 17, 70, 128, + 165, 118, 35, 216, 198, 102, 117, 96, 122, 1, 16, 114, 207, 174, 254, 202, 115, 201, 155, 87, 164, 241, 34, 239, 24, + 150, 71, 253, 173, 57, 169, 69, 17, 94, 204, 118, 77, 238, 6, 206, 241, 137, 99, 220, 68, 38, 141, 100, 248, 65, 232, + 249, 47, 121, 81, 65, 147, 30, 52, 83, 38, 177, 218, 235, 154, 214, 191, 179, 249, 198, 233, 11, 69, 161, 140, 25, + 240, 14, 98, 161, 76, 105, 7, 60, 155, 81, 190, 132, 39, 54, 219, 150, 146, 153, 53, 46, 46, 254, 80, 38, 84, 185, + 153, 158, 232, 222, 252, 140, 93, 113, 18, 52, 225, 22, 119, 169, 54, 46, 206, 17, 138, 73, 171, 3, 63, 230, 69, 187, + 131, 129, 32, 227, 224, 145, 118, 91, 92, 246, 19, 73, 233, 89, 253, 241, 85, 62, 152, 108, 130, 6, 33, 212, 62, 97, + 68, 198, 139, 206, 170, 126, 55, 169, 207, 214, 127, 65, 56, 110, 195, 38, 93, 124, 118, 137, 179, 196, 202, 238, 214, + 89, 29, 214, 111, 225, 161, 177, 10, 243, 20, 30, 228, 75, 168, 121, 129, 19, 203, 105, 215, 171, 119, 14, 178, 185, + 194, 161, 92, 1, 126, 198, 57, 156, 169, 254, 128, 36, 21, 153, 229, 54, 160, 54, 11, 142, 28, 81, 110, 134, 102, 22, + 167, 62, 218, 113, 194, 44, 111, 222, 44, 148, 211, 185, 73, 9, 4, 129, 240, 177, 184, 230, 149, 163, 13, 73, 123, 27, + 177, 46, 30, 67, 210, 62, 72, 251, 110, 89, 45, 233, 219, 246, 195, 81, 103, 145, 166, 204, 176, 169, 31, 116, 12, + 206, 122, 102, 185, 97, 148, 222, 5, 6, 241, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 4, 0, 4, 0, 8, 0, 4, 0, 2, 0, 0, 0, + 4, 0, 5, 0, 16, 0, 8, 0, 2, 0, 0, 0, 4, 0, 6, 0, 32, 0, 32, 0, 2, 0, 0, 0, 4, 0, 4, 0, 16, 0, 16, 0, 4, 0, 0, 0, 8, 0, + 16, 0, 32, 0, 32, 0, 4, 0, 0, 0, 8, 0, 16, 0, 128, 0, 128, 0, 4, 0, 0, 0, 8, 0, 32, 0, 128, 0, 0, 1, 4, 0, 0, 0, 32, + 0, 128, 0, 2, 1, 0, 4, 4, 0, 0, 0, 32, 0, 2, 1, 2, 1, 0, 16, 4, 0, 0, 0, 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, + 3, 13, 2, 14, 1, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, + 0, 7, 0, 0, 0, 8, 0, 0, 0, 10, 0, 0, 0, 12, 0, 0, 0, 14, 0, 0, 0, 16, 0, 0, 0, 20, 0, 0, 0, 24, 0, 0, 0, 28, 0, 0, 0, + 32, 0, 0, 0, 40, 0, 0, 0, 48, 0, 0, 0, 56, 0, 0, 0, 64, 0, 0, 0, 80, 0, 0, 0, 96, 0, 0, 0, 112, 0, 0, 0, 128, 0, 0, 0, + 160, 0, 0, 0, 192, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, + 0, 0, 0, 6, 0, 0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 32, 0, 0, 0, 48, 0, 0, 0, 64, 0, 0, 0, 96, 0, + 0, 0, 128, 0, 0, 0, 192, 0, 0, 0, 0, 1, 0, 0, 128, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 8, 0, + 0, 0, 12, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 32, 0, 0, 0, 48, 0, 0, 0, 64, 0, 0, 0, 96, 0, 0, 98, 117, 102, 102, 101, + 114, 32, 101, 114, 114, 111, 114, 0, 0, 0, 0, 105, 110, 115, 117, 102, 102, 105, 99, 105, 101, 110, 116, 32, 109, 101, + 109, 111, 114, 121, 0, 0, 0, 0, 0, 115, 116, 114, 101, 97, 109, 32, 101, 114, 114, 111, 114, 0, 0, 0, 0, 101, 114, + 114, 111, 114, 58, 32, 37, 100, 92, 110, 0, 0, 0, 0, 0, 115, 116, 114, 99, 109, 112, 40, 98, 117, 102, 102, 101, 114, + 44, 32, 98, 117, 102, 102, 101, 114, 51, 41, 32, 61, 61, 32, 48, 0, 0, 0, 0, 100, 101, 99, 111, 109, 112, 114, 101, + 115, 115, 101, 100, 83, 105, 122, 101, 32, 61, 61, 32, 115, 105, 122, 101, 0, 0, 0, 0, 0, 0, 0, 0, 47, 116, 109, 112, + 47, 101, 109, 115, 99, 114, 105, 112, 116, 101, 110, 95, 116, 101, 109, 112, 47, 122, 108, 105, 98, 46, 99, 0, 0, 0, + 0, 0, 115, 105, 122, 101, 115, 58, 32, 37, 100, 44, 37, 100, 10, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, + 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, + 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 100, 111, 105, 116, 0, 0, 0, 0 +]; +const dataOrigin: number[] = arr1.concat(arr2); +let showDebugLog: boolean = false; + +function runZlib() { + const opt: OptionParams = { compressionType: CompressionType.NONE }; + // compress + const deflate: RawDeflate = new RawDeflate(new Uint8Array(dataOrigin), opt); + const rawCompress = deflate.compress(); + if (showDebugLog) { + console.log('rawCompress_0_100:', rawCompress); + } + + // decompress + const rawInflate: RawInflate = new RawInflate(rawCompress); + const rawPlain = rawInflate.decompress(); + + if (showDebugLog) { + console.log('rawPlain_0_100:', rawPlain); + } +} + +export { runZlib } \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/models/PerformanceTestItemData.ets b/OHBM/entry/src/main/ets/models/PerformanceTestItemData.ets new file mode 100644 index 0000000000000000000000000000000000000000..81d0b188648c65d3962ac662f1ab98276b164b56 --- /dev/null +++ b/OHBM/entry/src/main/ets/models/PerformanceTestItemData.ets @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * List item data entity. + */ +@Observed +export default class PerformanceTestItemData { + title: string; + subTitle: string; + result: string = '未测试'; + isSelect: boolean = true; + + constructor(title: string, subTitle: string) { + this.title = title; + this.subTitle = subTitle; + } +} \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/models/TestItemData.ets b/OHBM/entry/src/main/ets/models/TestItemData.ets index cdbbd6de0cb56f6e2d5645e9cc3a8089cc44d4c4..66492cbc8aaa25d43b492e2cda26315a0f21456a 100644 --- a/OHBM/entry/src/main/ets/models/TestItemData.ets +++ b/OHBM/entry/src/main/ets/models/TestItemData.ets @@ -15,6 +15,7 @@ /** * List item data entity. */ +import { promptAction } from '@kit.ArkUI'; type OnItemClick = () => void; @@ -24,7 +25,12 @@ export default class TestItemData { img: Resource; subTitle: string; - constructor(title: string, img: Resource, subTitle: string, onItemClick: OnItemClick) { + constructor(title: string, img: Resource, subTitle: string, onItemClick: OnItemClick = () => { + promptAction.showToast({ + message: '敬请期待', + duration: 2000 + }); + }) { this.title = title; this.img = img; this.subTitle = subTitle; diff --git a/OHBM/entry/src/main/ets/pages/BasicInfo.ets b/OHBM/entry/src/main/ets/pages/BasicInfo.ets index 6cc29a999b99ee2774f47300e068330d15806161..2fb413dc6831052cdd181777ff1e38397dee6ced 100644 --- a/OHBM/entry/src/main/ets/pages/BasicInfo.ets +++ b/OHBM/entry/src/main/ets/pages/BasicInfo.ets @@ -23,6 +23,7 @@ import { inputDevice } from '@kit.InputKit'; import Item from '../models/BasicInfoItemData'; import BasicInfoArray from '../views/BasicInfoAarry'; import { display } from '@kit.ArkUI'; +import HeadBar from '../views/HeadBar'; @Entry @@ -46,11 +47,7 @@ struct BasicInfo { build() { Column() { - Text('验机') - .fontSize(32) - .width('100%') - .textAlign(TextAlign.Center) - .margin(16) + HeadBar({ title: '验机', isSetting: false }) Scroll() { Column() { BasicInfoArray({title:'设备信息',infoArray:this.deviceInfoArray}) diff --git a/OHBM/entry/src/main/ets/pages/Index.ets b/OHBM/entry/src/main/ets/pages/Index.ets index 2a2ab2e42e7d820f0995fabb1b478c0015093fda..5d02d612493a3f7d991afb7035af06fa1ea6ff0b 100644 --- a/OHBM/entry/src/main/ets/pages/Index.ets +++ b/OHBM/entry/src/main/ets/pages/Index.ets @@ -16,15 +16,21 @@ import TestItemData from '../models/TestItemData'; import TestGridItem from '../views/TestGridItem'; import router from '@ohos.router'; import { BusinessError } from '@ohos.base'; +import TestListItem from '../views/TestListItem'; +import HeadBar from '../views/HeadBar'; +import { batteryInfo } from '@kit.BasicServicesKit'; @Entry @Component struct Index { @State message: string = '开鸿测评(OpenHarmony Benchmarks)'; @State items: TestItemData[] = []; + @State extraItems: TestItemData[] = []; + @State batteryTemp: number = batteryInfo.batteryTemperature / 10; + private queryHandleId: number = -1; aboutToAppear(): void { - this.items.push(new TestItemData('验机', $r('app.media.app_icon'), '待测试', () => { + this.items.push(new TestItemData('验机', $r('app.media.rectangle_portrait_rotate'), '基本信息检测', () => { try { router.pushUrl({ url: 'pages/BasicInfo', @@ -32,8 +38,8 @@ struct Index { } catch (err) { console.error(`pushUrl failed, code is ${(err as BusinessError).code}, message is ${(err as BusinessError).message}`); } - })) - this.items.push(new TestItemData('性能评分', $r('app.media.app_icon'), '待测试', () => { + })); + this.items.push(new TestItemData('性能评分', $r('app.media.timer'), '综合性能测试', () => { try { router.pushUrl({ url: 'pages/Performance', @@ -41,32 +47,35 @@ struct Index { } catch (err) { console.error(`pushUrl failed, code is ${(err as BusinessError).code}, message is ${(err as BusinessError).message}`); } - })) - this.items.push(new TestItemData('压力', $r('app.media.app_icon'), '待测试', () => { - })) - this.items.push(new TestItemData('网络', $r('app.media.app_icon'), '待测试', () => { - })) - this.items.push(new TestItemData('屏幕', $r('app.media.app_icon'), '待测试', () => { - })) - this.items.push(new TestItemData('电池', $r('app.media.app_icon'), '待测试', () => { - })) + })); + this.items.push(new TestItemData('存储', $r('app.media.externaldrive'), '读写性能测试')); + this.items.push(new TestItemData('传输', $r('app.media.mobiledata'), '网络性能测试')); + this.items.push(new TestItemData('屏幕', $r('app.media.hand_tap'), '显示触屏检测')); + this.items.push(new TestItemData('续航', $r('app.media.bolt_filled_on_circle'), '功耗检测')); + this.extraItems.push(new TestItemData('WLAN', $r('app.media.wifi'), '无线局域网传输')); + this.extraItems.push(new TestItemData('蓝牙', $r('app.media.bluetooth'), '协议支持及传输')); + this.extraItems.push(new TestItemData('相机', $r('app.media.circle_viewfinder'), '拍照视频')); + this.extraItems.push(new TestItemData('定位', $r('app.media.satellite_map'), 'GPS北斗')); + this.extraItems.push(new TestItemData('安全', $r('app.media.checkmark_shield'), '系统安全')); + this.extraItems.push(new TestItemData('触控', $r('app.media.hand_draw'), '输入输出')); + this.extraItems.push(new TestItemData('图形', $r('app.media.picture_2'), '显示性能测试')); + this.extraItems.push(new TestItemData('NFC', $r('app.media.nfc'), '近距离通信')); + this.extraItems.push(new TestItemData('电池', $r('app.media.battery_75percent'), '电池健康检查')); + this.queryHandleId = setInterval(() => { + this.batteryTemp = batteryInfo.batteryTemperature / 10; + }, 1000); + } + aboutToDisappear(): void { + if (this.queryHandleId !== -1) { + clearInterval(this.queryHandleId); + this.queryHandleId = -1; + } } build() { Column() { - Row() { - Text('开鸿测评v0.0.1') - .fontSize(16) - .fontColor(Color.White) - .margin(8) - Image($r('app.media.app_icon')) - .height(36) - .margin(8) - } - .backgroundColor('#3333FF') - .width('100%') - .justifyContent(FlexAlign.SpaceBetween) + HeadBar() Scroll() { Column() { @@ -74,31 +83,42 @@ struct Index { Text('开始测评') .fontSize(42) .fontColor(Color.White) - .borderRadius(42) + .borderRadius(16) .padding(16) - .backgroundColor('#11FFFFFF') + .backgroundColor(Color.Orange) + .shadow({ + radius: 10, + color: Color.Gray, + offsetX: 20, + offsetY: 20 + }) + .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 }) .alignSelf(ItemAlign.Center) .alignRules({ bottom: { anchor: '__container__', align: VerticalAlign.Center }, middle: { anchor: '__container__', align: HorizontalAlign.Center } }) + .onClick(() => { + try { + router.pushUrl({ + url: 'pages/Performance', + }) + } catch (err) { + console.error(`pushUrl failed, code is ${(err as BusinessError).code}, message is ${(err as BusinessError).message}`); + } + }) Row() { - Text('电池温度:28.5℃') + Text('电池温度:' + this.batteryTemp.toString() + ' ℃') .fontSize(16) .fontColor(Color.White) .margin(8) - Text('CPU温度:31.5℃') - .fontSize(16) - .fontColor(Color.White) - .margin(8) - Blank() - Text('查看详情 >') + Text('CPU温度:-- ℃') .fontSize(16) .fontColor(Color.White) .margin(8) } .width('100%') - .backgroundBlurStyle(BlurStyle.COMPONENT_THIN) + .backgroundBlurStyle(BlurStyle.Thin) .alignRules({ bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, middle: { anchor: '__container__', align: HorizontalAlign.Center } @@ -106,7 +126,7 @@ struct Index { } .width('100%') .height('40%') - .backgroundColor('#3333FF') + .backgroundColor('#66AAFF') Column() { Grid() { @@ -121,46 +141,28 @@ struct Index { .columnsTemplate('1fr 1fr 1fr') .columnsGap(10) .rowsGap(10) - .margin({ bottom: 16 }) - .padding({ top: 16 }) + .padding({ top: 16, bottom: 16 }) .backgroundColor(Color.White) .width('100%') - .height(272) } .width('100%') .backgroundColor('#66CCCCCC') Column() { List({ space: 10 }) { - ListItem() { - Text('其他测试1') - } - .height(48) - - ListItem() { - Text('其他测试2') - } - .height(48) - - ListItem() { - Text('其他测试3') - } - .height(48) - - ListItem() { - Text('其他测试4') - } - .height(48) - - ListItem() { - Text('其他测试5') - } - .height(48) + ForEach(this.extraItems, (item: TestItemData) => { + ListItem() { + TestListItem({ item }) + } + }) } .divider({ strokeWidth: 1, color: '#CCCCCC' }) + .width('100%') + .backgroundColor(Color.White) .margin(16) - } + .backgroundColor('#66CCCCCC') + .margin({ bottom: 32 }) } }.scrollBar(BarState.Off) } diff --git a/OHBM/entry/src/main/ets/pages/Performance.ets b/OHBM/entry/src/main/ets/pages/Performance.ets index f44e8a4144081f507e75e005a0106eabdd822167..9fa44d1462c3b8076f4727ed32c5c4482f2d23a2 100644 --- a/OHBM/entry/src/main/ets/pages/Performance.ets +++ b/OHBM/entry/src/main/ets/pages/Performance.ets @@ -14,43 +14,88 @@ */ import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS'; import { WorkerMessage } from '../cases/BenchmarkMeasure'; +import PerformanceTestItemData from '../models/PerformanceTestItemData'; import Logger from '../utils/Logger'; +import HeadBar from '../views/HeadBar'; +import PerformanceTestItem from '../views/PerformanceTestItem'; @Entry @Component struct Performance { @State output: string = '' @State isRunning: boolean = false; - @State testResult: string = '' - @State totalResult: string = '' + @State testResult: string = '正在测试...' private TAG: string = 'Performance'; private performanceWorker: worker.ThreadWorker | undefined = undefined; - private settings: RenderingContextSettings = new RenderingContextSettings(true) - private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) + private settings: RenderingContextSettings = new RenderingContextSettings(true); + private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); + private canvas_x: number = 400; + private canvas_y: number = 400; + private canvas_delta_x: number = 100; + private canvas_delta_y: number = 100; + private canvas_scale: number = 1; + private raytrace_canvas_scale: number = 20; + private fps: number = 0; + @State fpsText: string = '' + private fpsTimerId: number = 0; + @State totalScore: string = '评分:未测试'; + private totalScoreValue: number = 0.00; + private d: number = 200; + @State items: PerformanceTestItemData[] = []; + @State isTotal: boolean = true; + + aboutToAppear(): void { + this.initItems(); + } + + aboutToDisappear(): void { + if (this.performanceWorker !== undefined) { + this.performanceWorker.terminate(); + } + } build() { Column() { + HeadBar({ title: '性能测试', isSetting: false }) Scroll() { Column() { - Text(this.isRunning ? '正在测试……' : '性能评分') - .fontSize(18) + Text(this.isRunning ? this.testResult : this.totalScore) + .fontSize(24) + .fontColor(Color.Orange) .margin(8) - Row() { - Canvas(this.context) - .width(200) - .height(200) - .backgroundColor('#ffff00') + .maxLines(1) + .textAlign(TextAlign.Center) + .width('100%') + Stack() { + Row() { + Canvas(this.context) + .width(400) + .height(400) + .backgroundColor('#ffff00') + } + + Text(this.fpsText) + .fontSize(16) + .margin(8) } + .shadow({ + radius: 2, + color: Color.Gray + }) + .alignContent(Alignment.TopEnd) - Text(this.testResult) - .fontColor(Color.Red) - .fontSize(20) - .margin(8) Button(this.isRunning ? '停止' : '开始') - .height(100) + .height(60) .width(200) - .fontSize(20) + .fontSize(24) .margin(8) + .shadow({ + radius: 10, + color: Color.Gray, + offsetX: 5, + offsetY: 5 + }) + .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 }) .onClick(() => { if (this.isRunning) { if (this.performanceWorker !== undefined) { @@ -60,65 +105,269 @@ struct Performance { } } } else { - if (this.performanceWorker === undefined) { - this.performanceWorker = new worker.ThreadWorker('entry/ets/workers/PerformanceWorker.ets'); - } - this.context.clearRect(0, 0, 200, 200); - this.totalResult = ''; - this.performanceWorker.postMessage('Start'); - this.performanceWorker.onmessage = (e: MessageEvents): void => { - let data: WorkerMessage = e.data; - switch (data.type) { - case 0: - let result = `${data.name}:time = ${data.time},latency = ${data.latency}`; - Logger.info(this.TAG, result); - this.context.clearRect(0, 0, 200, 200); - if (data.name.indexOf('完成') !== -1) { - this.totalResult = this.totalResult +'\n'+ result; - } else { - this.testResult = result; - } - break; - case 1: - let red = Math.floor(data.r * 255); - let green = Math.floor(data.g * 255); - let blue = Math.floor(data.b * 255); - Logger.info(this.TAG, - `RayTrace pixel: (x:${data.x},y:${data.y}),rgb(${red},${green},${blue})`); - this.context.fillStyle = `rgb(${red},${green},${blue})` - this.context.fillRect(data.x * 10, data.y * 10, 10, 10) - break; - default: - if (data.name.indexOf('全部完成') !== -1) { - this.isRunning = false; - } - break; - } - } - - this.performanceWorker.onexit = (code) => { - Logger.info(this.TAG, "main thread terminate"); - this.performanceWorker === undefined; - this.isRunning = false; + this.start(); + } + }) + Column() { + List({ space: 10 }) { + ListItem() { + Row() { + Text('测试项') + .fontSize(20) + .maxLines(1) + .textOverflow({ overflow: TextOverflow.MARQUEE }) + .width(120) + .fontWeight(FontWeight.Bold) + .textAlign(TextAlign.Center) + Blank() + Text('评分') + .fontWeight(FontWeight.Bold) + .fontSize(20) + .margin(4) } + .alignItems(VerticalAlign.Center) + .height(48) + .width('90%') + .justifyContent(FlexAlign.SpaceBetween) + } + .height(48) - this.performanceWorker.onerror = (err: ErrorEvent) => { - Logger.info(this.TAG, "main error message " + err.message); - this.isRunning = false; + ForEach(this.items, (item: PerformanceTestItemData) => { + ListItem() { + PerformanceTestItem({ item: item, isRunning: this.isRunning }) } - this.isRunning = true; - } - }) - Text(this.totalResult) - .fontSize(18) - .margin(8) + .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 }) + .onClick(() => { + if (!this.isRunning) { + item.isSelect = !item.isSelect; + } + }) + .width('90%') + }) + } + .divider({ strokeWidth: 1, color: '#CCCCCC' }) + .margin(16) + } } .justifyContent(FlexAlign.Center) .constraintSize({ minHeight: '100%' }) .width('100%') } .height('100%') - .padding({ left: 12, right: 12 }) + .width('90%') + .scrollBar(BarState.Off) + .margin({ bottom: 32 }) + } + .width('100%') + .height('100%') + } + + initItems() { + this.items.push(new PerformanceTestItemData('n-body', + '模拟多个物体在相互之间的万有引力作用下的运动,评估物理算法的性能,特别是在模拟大规模天体系统时')) + this.items.push(new PerformanceTestItemData('box2d', + '单个小球自由落体运动测试(Box2D是一个2D物理引擎,用于游戏开发和模拟,评估2D物理模拟的性能,包括碰撞检测、刚体动力学等)')) + this.items.push(new PerformanceTestItemData('box2d1', '5个物体的受力运动测试')) + this.items.push(new PerformanceTestItemData('box2d2', '单个小球抛物线运动测试')) + this.items.push(new PerformanceTestItemData('box2d3', '26个物体的受力运动测试')) + this.items.push(new PerformanceTestItemData('raytrace', + '光线追踪是一种用于生成逼真图像的渲染技术,评估渲染算法的性能,特别是在处理复杂光照和阴影效果时')) + this.items.push(new PerformanceTestItemData('richards', + '并发程序的基准测试,模拟一个任务调度系统,评估操作系统的并发处理能力和调度效率')) + this.items.push(new PerformanceTestItemData('deltablue', + '约束求解算法的实现,用于解决一组约束条件的最优解,评估算法性能和计算效率')) + this.items.push(new PerformanceTestItemData('navier-stokes', + '纳维-斯托克斯方程是流体动力学中描述流体运动的基本方程,评估流体动力学模拟的性能')) + this.items.push(new PerformanceTestItemData('base64', + 'Base64编码方法,用于将二进制数据转换为ASCII字符串,评估数据编码和解码的性能')) + this.items.push(new PerformanceTestItemData('crypto-aes', + 'AES(高级加密标准)对称加密算法,评估AES加密和解密的性能')) + this.items.push(new PerformanceTestItemData('crypto-md5', + 'MD5哈希函数,用于数据完整性验证,评估MD5哈希算法的性能')) + this.items.push(new PerformanceTestItemData('crypto-sha1', + 'SHA-1哈希函数,用于确保数据的完整性,评估SHA-1哈希算法的性能')) + this.items.push(new PerformanceTestItemData('zlib', 'Zlib压缩,评估数据压缩和解压缩的性能')) + } + + start() { + if (this.performanceWorker === undefined) { + this.performanceWorker = new worker.ThreadWorker('entry/ets/workers/PerformanceWorker.ets'); } + this.context.clearRect(0, 0, this.canvas_x, this.canvas_y); + this.totalScoreValue = 0; + let selectedItems: string[] = this.items + .filter(item => item.isSelect === true) + .map(item => item.title); + this.performanceWorker.postMessage(selectedItems); + this.fpsTimerId = setInterval(() => { + this.fpsText = 'fps:' + this.fps.toString(); + this.fps = 0; + }, 1000) + this.performanceWorker.onmessage = (e: MessageEvents): void => { + let data: WorkerMessage = e.data; + switch (data.type) { + case -1: + this.context.clearRect(0, 0, this.canvas_x, this.canvas_y); + break; + case 0: + Logger.info(this.TAG, data.name); + if (data.name.indexOf('测试') !== -1) { + const itemIndex = this.items.findIndex(item => item.title === data.name.split('第')[0]); + if (itemIndex !== -1) { + this.items[itemIndex].result = '测试中'; + } + this.testResult = data.name; + this.context.clearRect(0, 0, this.canvas_x, this.canvas_y); + } else if (data.name.indexOf('完成') !== -1) { + let score = 0; + switch (data.name.replace('完成', '')) { + case 'zlib': + score = Math.round(100000000 / data.time) / 100; + break; + default: + score = Math.round(10000000000 / data.time) / 100; + break; + } + this.totalScoreValue = this.totalScoreValue + score; + const itemIndex = this.items.findIndex(item => item.title === data.name.replace('完成', '')); + if (itemIndex !== -1) { + this.items[itemIndex].result = score.toString(); + } + } else { + let result = `${data.name}:耗时 ${data.time},延迟 ${data.latency}`; + Logger.info(this.TAG, result); + } + break; + case 1: + let red = Math.floor(data.r * 255); + let green = Math.floor(data.g * 255); + let blue = Math.floor(data.b * 255); + Logger.info(this.TAG, + `RayTrace pixel: (x:${data.x},y:${data.y}),rgb(${red},${green},${blue})`); + this.context.fillStyle = `rgb(${red},${green},${blue})` + this.context.fillRect(data.x * this.raytrace_canvas_scale, data.y * this.raytrace_canvas_scale, + this.raytrace_canvas_scale, + this.raytrace_canvas_scale) + break; + case 2: + const vertices_x: number[] = data.vertices_x; + const vertices_y: number[] = data.vertices_y; + + this.context.beginPath(); + this.context.moveTo(data.x + vertices_x[0] * this.canvas_scale + this.canvas_delta_x, + this.canvas_y - (data.y + vertices_y[0] * this.canvas_scale)); + for (let i = 1; i < vertices_x.length; i++) { + this.context.lineTo(data.x + vertices_x[i] * this.canvas_scale + this.canvas_delta_x, + this.canvas_y - (data.y + vertices_y[i] * this.canvas_scale)); + } + this.context.closePath(); + this.context.stroke(); + break; + case 3: + this.context.beginPath(); + this.context.arc(data.x + this.canvas_delta_x, this.canvas_y - data.y, + data.radius * this.canvas_scale, 0, 2 * Math.PI); + this.context.fill() + this.context.closePath(); + this.context.stroke(); + this.fps++; + break; + case 4: + // 定义球体的中心和半径 + const center = new DBody(data.x * 15, data.y * 15, data.latency * 15); + console.log(`body radius: ${data.time} ,x ${data.x},y ${data.y},z ${data.latency}`); + let radius: number = data.time; + + // 绘制球体的近似方法 + const slices = 20; + const stacks = 20; + this.context.beginPath(); + for (let i = 0; i < slices; i++) { + for (let j = 0; j < stacks; j++) { + const theta = (i / slices) * Math.PI; + const phi = (j / stacks) * 2 * Math.PI; + const x = center.x + radius * Math.sin(theta) * Math.cos(phi); + const y = center.y + radius * Math.sin(theta) * Math.sin(phi); + const z = center.z + radius * Math.cos(theta); + const p = this.project(x, y, z); + this.context.arc(p.x, p.y, radius / (z + this.d), 0, 2 * Math.PI); + } + } + this.context.strokeStyle = this.rgbToHex(data.r, data.g, data.b); + this.context.stroke(); + const text = data.name; + const textPosition = this.project(center.x + 1.5 * radius, center.y, center.z); + const textDistance = Math.abs(center.z + 1.5 * radius); // 文字与视点的距离 + const textSize = Math.max(10, 20 * (this.d / (textDistance + this.d))); // 动态调整文字大小 + this.context.font = `${textSize}px Arial`; + this.context.fillStyle = '#000'; + this.context.fillText(text, textPosition.x, textPosition.y); + if (data.name === '太阳') { + this.fps++; + } + break; + default: + if (data.name.indexOf('全部完成') !== -1) { + if (this.performanceWorker !== undefined) { + try { + this.performanceWorker.terminate(); + } catch (e) { + } + } + } + break; + } + } + + this.performanceWorker.onexit = (code) => { + Logger.info(this.TAG, "main thread terminate"); + if (this.fpsTimerId !== 0) { + clearInterval(this.fpsTimerId); + } + this.fpsText = '' + this.totalScore = '性能评分:' + (Math.round(this.totalScoreValue * 100) / 100).toString(); + this.performanceWorker = undefined; + this.isRunning = false; + } + + this.performanceWorker.onerror = (err: ErrorEvent) => { + Logger.info(this.TAG, "main error message " + err.message); + this.performanceWorker = undefined; + this.isRunning = false; + this.fpsText = '' + if (this.fpsTimerId !== 0) { + clearInterval(this.fpsTimerId); + } + } + this.isRunning = true; + } + + project(x: number, y: number, z: number): DBody { + return new DBody( + (x / (z + this.d)) * 100 + this.canvas_x / 2, // 转换为屏幕坐标 + (y / (z + this.d)) * 100 + this.canvas_y / 2, + 0 + ); + } + + toHex(value: number) { + const hex = value.toString(16); + return hex.length === 1 ? '0' + hex : hex; + }; + + rgbToHex(r: number, g: number, b: number): string { + return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; + } +} + +class DBody { + x: number; + y: number; + z: number; + + constructor(x1: number, y1: number, z1: number) { + this.x = x1; + this.y = y1; + this.z = z1; } } \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/views/BasicInfoAarry.ets b/OHBM/entry/src/main/ets/views/BasicInfoAarry.ets index 48db0a0937cdd5fe63db0c2451515e60eba983f8..4431b34e1aafe5d6b1403c1c3997e8a4a5f748d2 100644 --- a/OHBM/entry/src/main/ets/views/BasicInfoAarry.ets +++ b/OHBM/entry/src/main/ets/views/BasicInfoAarry.ets @@ -38,10 +38,10 @@ export default struct BasicInfoArray { .height(36) .rotate({ angle: this.isFold ? 0 : 180 }) } + .clickEffect({ level: ClickEffectLevel.LIGHT}) .justifyContent(FlexAlign.SpaceBetween) - .backgroundColor('#CCCCCC') + .backgroundColor('#33CCCCCC') .width('100%') - .borderWidth(1) .onClick(() => { this.isFold = !this.isFold; }) @@ -59,13 +59,19 @@ export default struct BasicInfoArray { .margin({ top: 4, bottom: 4 }) .justifyContent(FlexAlign.SpaceBetween) .width('100%') - }.backgroundColor(index % 2 === 0 ? '#FFFFFF' : '#EEEEEE') + }.backgroundColor(index % 2 === 0 ? '#FFFFFF' : '#33EEEEEE') }) } .transition(TransitionEffect.OPACITY.animation({ duration: 1000, curve: Curve.Ease })) - .divider({ strokeWidth: 1, color: '#CCCCCC' }) .width('90%') } } + .margin(8) + .shadow({ + radius: 10, + color: Color.Gray, + offsetX: 5, + offsetY: 5 + }) } } \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/views/HeadBar.ets b/OHBM/entry/src/main/ets/views/HeadBar.ets new file mode 100644 index 0000000000000000000000000000000000000000..6356b1c3b159a9b50dd362064602f42ed18358cc --- /dev/null +++ b/OHBM/entry/src/main/ets/views/HeadBar.ets @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { promptAction } from '@kit.ArkUI'; +/** + * List item information component. + */ +@Component +export default struct HeadBar { + private title: string = 'OpenHarmony BenchMarks' + private isSetting: boolean = true; + + build() { + Row() { + if (!this.isSetting) { + Navigator({ target: 'pages/Index', type: NavigationType.Back }) { + Image($r('app.media.chevron_down')) + .height(36) + .margin(8) + .rotate({ angle: 90 }) + } + } + if (this.isSetting){ + Text(){ + Span('O').fontSize(20).fontColor(Color.Orange) + Span('pen').fontSize(20).fontColor(Color.White) + Span('H').fontSize(20).fontColor(Color.Orange) + Span('armony ').fontSize(20).fontColor(Color.White) + Span('B').fontSize(20).fontColor(Color.Orange) + Span('ench').fontSize(20).fontColor(Color.White) + Span('M').fontSize(20).fontColor(Color.Orange) + Span('arks').fontSize(20).fontColor(Color.White) + }.margin(8) + }else{ + Text(this.title) + .fontSize( 24) + .margin(8) + .fontColor(Color.White) + } + + Image($r('app.media.gearshape')) + .height(36) + .margin(8) + .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 }) + .onClick(()=>{ + promptAction.showToast({ + message: 'Developed by sunlian@kaihong.com', + duration: 5000 + }); + }) + } + .backgroundColor('#66aaff') + .width('100%') + .justifyContent(FlexAlign.SpaceBetween) + } +} \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/views/PerformanceTestItem.ets b/OHBM/entry/src/main/ets/views/PerformanceTestItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..3bebf824a1e73bd184e5589e8ff06ea9ee70ea71 --- /dev/null +++ b/OHBM/entry/src/main/ets/views/PerformanceTestItem.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import PerformanceTestItemData from '../models/PerformanceTestItemData'; + +/** + * List item information component. + */ +@Component +export default struct PerformanceTestItem { + @ObjectLink item: PerformanceTestItemData; + @Prop isRunning:boolean; + + aboutToAppear() { + } + + build() { + Row() { + Toggle({ type: ToggleType.Checkbox, isOn: this.item.isSelect }) + .size({ width: 20, height: 20 }) + .selectedColor('#007DFF') + .enabled(!this.isRunning) + Column() { + Text(this.item.title) + .fontSize(20) + .maxLines(1) + .textOverflow({ overflow: TextOverflow.MARQUEE }) + .textAlign(TextAlign.Center) + Text(this.item.subTitle) + .fontSize(16) + .fontColor(Color.Gray) + } + .width('70%') + .alignItems(HorizontalAlign.Start) + .margin(4) + + Blank() + Text(this.item.result) + .fontSize(20) + .margin(4) + .fontColor(this.item.result === '未测试' ? Color.Black : + this.item.result === '测试中' ? Color.Orange : Color.Blue) + } + .width('100%') + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.SpaceBetween) + } +} \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/views/TestGridItem.ets b/OHBM/entry/src/main/ets/views/TestGridItem.ets index 11df4cd3b71576098b9cb33f4f3a37b5b9232f5f..e618209bf6b5390636715f015ffeeac994795870 100644 --- a/OHBM/entry/src/main/ets/views/TestGridItem.ets +++ b/OHBM/entry/src/main/ets/views/TestGridItem.ets @@ -33,7 +33,6 @@ export default struct TestGridItem { .borderRadius(12) .backgroundImage(this.item.img) .backgroundImageSize(ImageSize.Cover) - .shadow({ radius: 1, color: Color.Gray }) .width(64) .height(64) .justifyContent(FlexAlign.SpaceBetween) @@ -48,9 +47,17 @@ export default struct TestGridItem { Text(this.item.subTitle) .fontSize(16) .margin(4) + .maxLines(1) .fontColor(Color.Gray) } .alignItems(HorizontalAlign.Center) + .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 }) + .shadow({ + radius: 2, + color: Color.Gray, + offsetX: 1, + offsetY: 1 + }) .onClick(() => { this.item.onItemClick(); }) diff --git a/OHBM/entry/src/main/ets/views/TestListItem.ets b/OHBM/entry/src/main/ets/views/TestListItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..c6017118a9c4fe02f5be007b1ebcd4cdc0616317 --- /dev/null +++ b/OHBM/entry/src/main/ets/views/TestListItem.ets @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import ItemData from '../models/TestItemData'; + +/** + * List item information component. + */ +@Component +export default struct TestListItem { + item: ItemData = new ItemData('', $r('app.media.app_icon'), '', () => { + }); + + aboutToAppear() { + } + + build() { + Row() { + Column() { + } + .backgroundImage(this.item.img) + .backgroundImageSize(ImageSize.Cover) + .width(32) + .height(32) + .justifyContent(FlexAlign.SpaceBetween) + .margin(8) + + Column() { + Text(this.item.title) + .fontSize(20) + .maxLines(1) + .margin(4) + .textOverflow({ overflow: TextOverflow.MARQUEE }) + Text(this.item.subTitle) + .fontSize(16) + .margin(4) + .fontColor(Color.Gray) + }.alignItems(HorizontalAlign.Start) + Blank() + Image($r('app.media.chevron_down')) + .height(32) + .width(32) + .margin(8) + .rotate({angle:'-90'}) + } + .width('100%') + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.SpaceBetween) + .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 }) + .onClick(() => { + this.item.onItemClick(); + }) + } +} \ No newline at end of file diff --git a/OHBM/entry/src/main/ets/workers/PerformanceWorker.ets b/OHBM/entry/src/main/ets/workers/PerformanceWorker.ets index dca87bb53af55947e278e681ea58b5b6bf46b353..2d5cf01a6422ade31dc5a83d1aa91e5f7c415b1c 100644 --- a/OHBM/entry/src/main/ets/workers/PerformanceWorker.ets +++ b/OHBM/entry/src/main/ets/workers/PerformanceWorker.ets @@ -16,16 +16,41 @@ import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; import { BenchmarkRun, WorkerMessage } from "../cases/BenchmarkMeasure"; import { Base64Run } from '../cases/performance/base64'; +import { runBox2D } from '../cases/performance/box2d/box2d'; +import { runBox2D1 } from '../cases/performance/box2d/box2d1'; +import { runBox2D2 } from '../cases/performance/box2d/box2d2'; +import { runBox2D3 } from '../cases/performance/box2d/box2d3'; import { AESRun } from '../cases/performance/crypto-aes'; import { Md5Run } from '../cases/performance/crypto-md5'; import { Sha1Run } from '../cases/performance/crypto-sha1'; import { DeltablueRun } from '../cases/performance/deltablue'; +import { runNBody } from '../cases/performance/n-body'; import { NavierstokesRun, NavierStokesSetup } from '../cases/performance/navier-stoke'; import { RaytraceRun } from "../cases/performance/RayTrace"; import { runRichards } from '../cases/performance/richards'; -import Logger from '../utils/Logger'; +import { runZlib } from '../cases/performance/zlib/zlib'; const workerPort: ThreadWorkerGlobalScope = worker.workerPort; +const benchmarks: BenchmarkRun[] = [ + new BenchmarkRun('n-body', true, true, runNBody, undefined, undefined, undefined, 5), + new BenchmarkRun('box2d', true, true, runBox2D, undefined, undefined, undefined, 10), + new BenchmarkRun('box2d1', true, true, runBox2D1, undefined, undefined, undefined, 5), + new BenchmarkRun('box2d2', true, true, runBox2D2, undefined, undefined, undefined, 10), + new BenchmarkRun('box2d3', true, true, runBox2D3, undefined, undefined, undefined, 3), + new BenchmarkRun('raytrace', true, true, RaytraceRun, undefined, undefined, undefined, 20), + new BenchmarkRun('richards', true, true, runRichards, undefined, undefined, undefined, 100), + new BenchmarkRun('deltablue', true, true, DeltablueRun, undefined, undefined, undefined, 20), + new BenchmarkRun('navier-stokes', true, true, NavierstokesRun, NavierStokesSetup, undefined, undefined, 20), + new BenchmarkRun('base64', true, true, Base64Run, undefined, undefined, undefined, 10), + new BenchmarkRun('crypto-aes', true, true, AESRun, undefined, undefined, undefined, 10), + new BenchmarkRun('crypto-md5', true, true, Md5Run, undefined, undefined, undefined, 20), + new BenchmarkRun('crypto-sha1', true, true, Sha1Run, undefined, undefined, undefined, 20), + new BenchmarkRun('zlib', true, true, runZlib, undefined, undefined, undefined, 10000), +]; + +function runBenchmark(benchmark: BenchmarkRun, workerPort: ThreadWorkerGlobalScope) { + benchmark.run(workerPort); +} /** * Defines the event handler to be called when the worker thread receives a message sent by the host thread. @@ -34,33 +59,14 @@ const workerPort: ThreadWorkerGlobalScope = worker.workerPort; * @param e message data */ workerPort.onmessage = (e: MessageEvents) => { - let data: string = e.data; - if (data === 'Start') { - let benchmarkRunRaytrace = - new BenchmarkRun('raytrace', true, true, RaytraceRun, undefined, undefined, undefined, 20); - benchmarkRunRaytrace.run(workerPort); - let benchmarkRunRichards = - new BenchmarkRun('richards', true, true, runRichards, undefined, undefined, undefined, 100) - benchmarkRunRichards.run(workerPort) - let benchmarkRunDeltablue = - new BenchmarkRun('deltablue', true, true, DeltablueRun, undefined, undefined, undefined, 20) - benchmarkRunDeltablue.run(workerPort) - let benchmarkRunNavierstokes = - new BenchmarkRun('navier-stokes', true, true, NavierstokesRun, NavierStokesSetup, undefined, undefined, 20) - benchmarkRunNavierstokes.run(workerPort) - - let benchmarkRunBase64 = new BenchmarkRun('base64', true, true, Base64Run, undefined, undefined, undefined, 10) - benchmarkRunBase64.run(workerPort); - let benchmarkRunAES = new BenchmarkRun('crypto-aes', true, true, AESRun, undefined, undefined, undefined, 10) - benchmarkRunAES.run(workerPort) - let benchmarkRunMd5 = new BenchmarkRun('crypto-md5', true, true, Md5Run, undefined, undefined, undefined, 20) - benchmarkRunMd5.run(workerPort) - let benchmarkRunSha1 = new BenchmarkRun('crypto-sha1', true, true, Sha1Run, undefined, undefined, undefined, 20) - benchmarkRunSha1.run(workerPort) - //const benchmarkRunBox2d = new BenchmarkRun('box2d', true, true, runBox2D, undefined, tearDownBox2D, undefined, 32) - //benchmarkRunBox2d.run(workerPort) - workerPort.postMessage(new WorkerMessage(`全部完成`, -1, 0, 0, 0, 0, 0, 0, 0)) - } + let data: string[] = e.data; + data.forEach(testName => { + const benchmark = benchmarks.find(b => b.name === testName); + if (benchmark) { + runBenchmark(benchmark, workerPort); + } + }); + workerPort.postMessage(new WorkerMessage(`全部完成`, -9, 0, 0, 0, 0, 0, 0, 0)) } /** @@ -79,4 +85,4 @@ workerPort.onmessageerror = (e: MessageEvents) => { * @param e error message */ workerPort.onerror = (e: ErrorEvent) => { -} \ No newline at end of file +} diff --git a/OHBM/entry/src/main/resources/base/media/battery_75percent.png b/OHBM/entry/src/main/resources/base/media/battery_75percent.png new file mode 100644 index 0000000000000000000000000000000000000000..317d95438d07638af6bcde80487e818f09fc6be4 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/battery_75percent.png differ diff --git a/OHBM/entry/src/main/resources/base/media/bluetooth.png b/OHBM/entry/src/main/resources/base/media/bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..e34734607820051ff2fd3a5f8b77d979df3158ca Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/bluetooth.png differ diff --git a/OHBM/entry/src/main/resources/base/media/bolt_filled_on_circle.png b/OHBM/entry/src/main/resources/base/media/bolt_filled_on_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..20dca8efbec2178bddbe6ea2b121c2e959070c8e Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/bolt_filled_on_circle.png differ diff --git a/OHBM/entry/src/main/resources/base/media/checkmark_shield.png b/OHBM/entry/src/main/resources/base/media/checkmark_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..39390e109056f0e3c801d2324f978ddd8185cd97 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/checkmark_shield.png differ diff --git a/OHBM/entry/src/main/resources/base/media/circle_viewfinder.png b/OHBM/entry/src/main/resources/base/media/circle_viewfinder.png new file mode 100644 index 0000000000000000000000000000000000000000..72cc0c832cb18522cf03ddac0618a2a69949d094 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/circle_viewfinder.png differ diff --git a/OHBM/entry/src/main/resources/base/media/externaldrive.png b/OHBM/entry/src/main/resources/base/media/externaldrive.png new file mode 100644 index 0000000000000000000000000000000000000000..bbe295ff3bb955940ce1bffbde3c2b28568dc370 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/externaldrive.png differ diff --git a/OHBM/entry/src/main/resources/base/media/gearshape.png b/OHBM/entry/src/main/resources/base/media/gearshape.png new file mode 100644 index 0000000000000000000000000000000000000000..563e2185b7cf705a4f93119b78c73cca8774c8d6 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/gearshape.png differ diff --git a/OHBM/entry/src/main/resources/base/media/hand_draw.png b/OHBM/entry/src/main/resources/base/media/hand_draw.png new file mode 100644 index 0000000000000000000000000000000000000000..ef9024d7a5dd85c8ff0c3cea73e3bb809db7e184 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/hand_draw.png differ diff --git a/OHBM/entry/src/main/resources/base/media/hand_tap.png b/OHBM/entry/src/main/resources/base/media/hand_tap.png new file mode 100644 index 0000000000000000000000000000000000000000..6220f48061f3c05fd008ba44ccd8b8b02cd712a5 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/hand_tap.png differ diff --git a/OHBM/entry/src/main/resources/base/media/mobiledata.png b/OHBM/entry/src/main/resources/base/media/mobiledata.png new file mode 100644 index 0000000000000000000000000000000000000000..35526b9b19430ae654efff86f494c9f48fc2d9ed Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/mobiledata.png differ diff --git a/OHBM/entry/src/main/resources/base/media/nfc.png b/OHBM/entry/src/main/resources/base/media/nfc.png new file mode 100644 index 0000000000000000000000000000000000000000..207ab2487ab05dd185e80eafd6dbf115f4106244 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/nfc.png differ diff --git a/OHBM/entry/src/main/resources/base/media/picture_2.png b/OHBM/entry/src/main/resources/base/media/picture_2.png new file mode 100644 index 0000000000000000000000000000000000000000..a88e3cbb6a9408405268596b08346e13f24c1b4e Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/picture_2.png differ diff --git a/OHBM/entry/src/main/resources/base/media/rectangle_portrait_rotate.png b/OHBM/entry/src/main/resources/base/media/rectangle_portrait_rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..620730241660c240e94ffe0f1e6800e3313f37f3 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/rectangle_portrait_rotate.png differ diff --git a/OHBM/entry/src/main/resources/base/media/satellite_map.png b/OHBM/entry/src/main/resources/base/media/satellite_map.png new file mode 100644 index 0000000000000000000000000000000000000000..aa48faa058b68d5df62d57a3a107c89e7a75de3e Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/satellite_map.png differ diff --git a/OHBM/entry/src/main/resources/base/media/timer.png b/OHBM/entry/src/main/resources/base/media/timer.png new file mode 100644 index 0000000000000000000000000000000000000000..2822e636c379792898300b547569d7241f6d86b8 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/timer.png differ diff --git a/OHBM/entry/src/main/resources/base/media/wifi.png b/OHBM/entry/src/main/resources/base/media/wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..79774180bf8715a62dd49c010a77c59353403fa8 Binary files /dev/null and b/OHBM/entry/src/main/resources/base/media/wifi.png differ