diff --git a/lib/web_ui/lib/src/engine/canvas_pool.dart b/lib/web_ui/lib/src/engine/canvas_pool.dart index ea6cc11303af7..c07801a72513d 100644 --- a/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -1128,9 +1128,6 @@ class ContextStateHandle { /// Provides save stack tracking functionality to implementations of /// [EngineCanvas]. class _SaveStackTracking { - // !Warning: this vector should not be mutated. - static final Vector3 _unitZ = Vector3(0.0, 0.0, 1.0); - final List _saveStack = []; /// The stack that maintains clipping operations used when text is painted @@ -1189,7 +1186,7 @@ class _SaveStackTracking { /// Rotates the [currentTransform] matrix. @mustCallSuper void rotate(double radians) { - _currentTransform.rotate(_unitZ, radians); + _currentTransform.rotate(kUnitZ, radians); } /// Skews the [currentTransform] matrix. diff --git a/lib/web_ui/lib/src/engine/engine_canvas.dart b/lib/web_ui/lib/src/engine/engine_canvas.dart index 297319a082371..7819ba4da17ec 100644 --- a/lib/web_ui/lib/src/engine/engine_canvas.dart +++ b/lib/web_ui/lib/src/engine/engine_canvas.dart @@ -134,8 +134,6 @@ class SaveClipEntry { /// Provides save stack tracking functionality to implementations of /// [EngineCanvas]. mixin SaveStackTracking on EngineCanvas { - static final Vector3 _unitZ = Vector3(0.0, 0.0, 1.0); - final List _saveStack = []; /// The stack that maintains clipping operations used when text is painted @@ -206,7 +204,7 @@ mixin SaveStackTracking on EngineCanvas { /// Classes that override this method must call `super.rotate()`. @override void rotate(double radians) { - _currentTransform.rotate(_unitZ, radians); + _currentTransform.rotate(kUnitZ, radians); } /// Skews the [currentTransform] matrix. @@ -288,8 +286,6 @@ class _SaveElementStackEntry { /// Provides save stack tracking functionality to implementations of /// [EngineCanvas]. mixin SaveElementStackTracking on EngineCanvas { - static final Vector3 _unitZ = Vector3(0.0, 0.0, 1.0); - final List<_SaveElementStackEntry> _saveStack = <_SaveElementStackEntry>[]; /// The element at the top of the element stack, or [rootElement] if the stack @@ -373,7 +369,7 @@ mixin SaveElementStackTracking on EngineCanvas { /// Classes that override this method must call `super.rotate()`. @override void rotate(double radians) { - _currentTransform.rotate(_unitZ, radians); + _currentTransform.rotate(kUnitZ, radians); } /// Skews the [currentTransform] matrix. diff --git a/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart index 4ebde4e3ab78e..b9c73950ff083 100644 --- a/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart @@ -1081,11 +1081,11 @@ class BitmapCanvas extends EngineCanvas { final double dpr = ui.window.devicePixelRatio; final double width = ui.window.physicalSize.width * dpr; final double height = ui.window.physicalSize.height * dpr; - final Vector3 topLeft = inverted.perspectiveTransform(Vector3(0, 0, 0)); - final Vector3 topRight = inverted.perspectiveTransform(Vector3(width, 0, 0)); + final Vector3 topLeft = inverted.perspectiveTransform(x: 0, y: 0, z: 0); + final Vector3 topRight = inverted.perspectiveTransform(x: width, y: 0, z: 0); final Vector3 bottomRight = - inverted.perspectiveTransform(Vector3(width, height, 0)); - final Vector3 bottomLeft = inverted.perspectiveTransform(Vector3(0, height, 0)); + inverted.perspectiveTransform(x: width, y: height, z: 0); + final Vector3 bottomLeft = inverted.perspectiveTransform(x: 0, y: height, z: 0); return ui.Rect.fromLTRB( math.min(topLeft.x, math.min(topRight.x, math.min(bottomRight.x, bottomLeft.x))), diff --git a/lib/web_ui/lib/src/engine/vector_math.dart b/lib/web_ui/lib/src/engine/vector_math.dart index 5e6cfe0461410..c50354d217272 100644 --- a/lib/web_ui/lib/src/engine/vector_math.dart +++ b/lib/web_ui/lib/src/engine/vector_math.dart @@ -379,30 +379,34 @@ class Matrix4 { /// Transform [arg] of type [Vector3] using the perspective transformation /// defined by [this]. - Vector3 perspectiveTransform(Vector3 arg) { - final Float32List argStorage = arg._v3storage; - - final double x = (_m4storage[0] * argStorage[0]) + - (_m4storage[4] * argStorage[1]) + - (_m4storage[8] * argStorage[2]) + + Vector3 perspectiveTransform({ + required double x, + required double y, + required double z, + }) { + final double transformedX = (_m4storage[0] * x) + + (_m4storage[4] * y) + + (_m4storage[8] * z) + _m4storage[12]; - final double y = (_m4storage[1] * argStorage[0]) + - (_m4storage[5] * argStorage[1]) + - (_m4storage[9] * argStorage[2]) + + final double transformedY = (_m4storage[1] * x) + + (_m4storage[5] * y) + + (_m4storage[9] * z) + _m4storage[13]; - final double z = (_m4storage[2] * argStorage[0]) + - (_m4storage[6] * argStorage[1]) + - (_m4storage[10] * argStorage[2]) + + final double transformedZ = (_m4storage[2] * x) + + (_m4storage[6] * y) + + (_m4storage[10] * z) + _m4storage[14]; final double w = 1.0 / - ((_m4storage[3] * argStorage[0]) + - (_m4storage[7] * argStorage[1]) + - (_m4storage[11] * argStorage[2]) + + ((_m4storage[3] * x) + + (_m4storage[7] * y) + + (_m4storage[11] * z) + _m4storage[15]); - argStorage[0] = x * w; - argStorage[1] = y * w; - argStorage[2] = z * w; - return arg; + + return ( + x: transformedX * w, + y: transformedY * w, + z: transformedZ * w, + ); } bool isIdentity() => @@ -443,18 +447,18 @@ class Matrix4 { /// Returns the translation vector from this homogeneous transformation matrix. Vector3 getTranslation() { - final double z = _m4storage[14]; - final double y = _m4storage[13]; - final double x = _m4storage[12]; - return Vector3(x, y, z); + return ( + x: _m4storage[12], + y: _m4storage[13], + z: _m4storage[14], + ); } void rotate(Vector3 axis, double angle) { final double len = axis.length; - final Float32List axisStorage = axis._v3storage; - final double x = axisStorage[0] / len; - final double y = axisStorage[1] / len; - final double z = axisStorage[2] / len; + final double x = axis.x / len; + final double y = axis.y / len; + final double z = axis.z / len; final double c = math.cos(angle); final double s = math.sin(angle); final double C = 1.0 - c; @@ -528,13 +532,9 @@ class Matrix4 { /// Sets the translation vector in this homogeneous transformation matrix. void setTranslation(Vector3 t) { - final Float32List tStorage = t._v3storage; - final double z = tStorage[2]; - final double y = tStorage[1]; - final double x = tStorage[0]; - _m4storage[14] = z; - _m4storage[13] = y; - _m4storage[12] = x; + _m4storage[14] = t.z; + _m4storage[13] = t.y; + _m4storage[12] = t.x; } /// Sets the translation vector in this homogeneous transformation matrix. @@ -953,35 +953,6 @@ class Matrix4 { (m33 * argStorage[15]); } - /// Rotate [arg] of type [Vector3] using the rotation defined by [this]. - Vector3 rotate3(Vector3 arg) { - final Float32List argStorage = arg._v3storage; - final double x = (_m4storage[0] * argStorage[0]) + - (_m4storage[4] * argStorage[1]) + - (_m4storage[8] * argStorage[2]); - final double y = (_m4storage[1] * argStorage[0]) + - (_m4storage[5] * argStorage[1]) + - (_m4storage[9] * argStorage[2]); - final double z = (_m4storage[2] * argStorage[0]) + - (_m4storage[6] * argStorage[1]) + - (_m4storage[10] * argStorage[2]); - argStorage[0] = x; - argStorage[1] = y; - argStorage[2] = z; - return arg; - } - - /// Rotate a copy of [arg] of type [Vector3] using the rotation defined by - /// [this]. If a [out] parameter is supplied, the copy is stored in [out]. - Vector3 rotated3(Vector3 arg, [Vector3? out]) { - if (out == null) { - out = Vector3.copy(arg); - } else { - out.setFrom(arg); - } - return rotate3(out); - } - /// Transforms a 3-component vector in-place. void transform3(Float32List vector) { final double x = (_m4storage[0] * vector[0]) + @@ -1091,315 +1062,21 @@ class Matrix4 { } } -/// 3D column vector. -class Vector3 { - /// Construct a new vector with the specified values. - factory Vector3(double x, double y, double z) => - Vector3.zero()..setValues(x, y, z); - - /// Zero vector. - Vector3.zero() : _v3storage = Float32List(3); - - /// Splat [value] into all lanes of the vector. - factory Vector3.all(double value) => Vector3.zero()..splat(value); - - /// Copy of [other]. - factory Vector3.copy(Vector3 other) => Vector3.zero()..setFrom(other); - - /// Constructs Vector3 with given Float32List as [storage]. - Vector3.fromFloat32List(this._v3storage); - - /// Constructs Vector3 with a [storage] that views given [buffer] starting at - /// [offset]. [offset] has to be multiple of [Float32List.bytesPerElement]. - Vector3.fromBuffer(ByteBuffer buffer, int offset) - : _v3storage = Float32List.view(buffer, offset, 3); - - /// Generate random vector in the range (0, 0, 0) to (1, 1, 1). You can - /// optionally pass your own random number generator. - factory Vector3.random([math.Random? rng]) { - rng ??= math.Random(); - return Vector3(rng.nextDouble(), rng.nextDouble(), rng.nextDouble()); - } - - final Float32List _v3storage; +const Vector3 kUnitX = (x: 1.0, y: 0.0, z: 0.0); +const Vector3 kUnitY = (x: 0.0, y: 1.0, z: 0.0); +const Vector3 kUnitZ = (x: 0.0, y: 0.0, z: 1.0); - /// The components of the vector. - Float32List get storage => _v3storage; - - /// Set the values of [result] to the minimum of [a] and [b] for each line. - static void min(Vector3 a, Vector3 b, Vector3 result) { - result - ..x = math.min(a.x, b.x) - ..y = math.min(a.y, b.y) - ..z = math.min(a.z, b.z); - } - - /// Set the values of [result] to the maximum of [a] and [b] for each line. - static void max(Vector3 a, Vector3 b, Vector3 result) { - result - ..x = math.max(a.x, b.x) - ..y = math.max(a.y, b.y) - ..z = math.max(a.z, b.z); - } - - /// Interpolate between [min] and [max] with the amount of [a] using a linear - /// interpolation and store the values in [result]. - static void mix(Vector3 min, Vector3 max, double a, Vector3 result) { - result - ..x = min.x + a * (max.x - min.x) - ..y = min.y + a * (max.y - min.y) - ..z = min.z + a * (max.z - min.z); - } - - /// Set the values of the vector. - void setValues(double x, double y, double z) { - _v3storage[0] = x; - _v3storage[1] = y; - _v3storage[2] = z; - } - - /// Zero vector. - void setZero() { - _v3storage[2] = 0.0; - _v3storage[1] = 0.0; - _v3storage[0] = 0.0; - } - - /// Set the values by copying them from [other]. - void setFrom(Vector3 other) { - final Float32List otherStorage = other._v3storage; - _v3storage[0] = otherStorage[0]; - _v3storage[1] = otherStorage[1]; - _v3storage[2] = otherStorage[2]; - } - - /// Splat [arg] into all lanes of the vector. - void splat(double arg) { - _v3storage[2] = arg; - _v3storage[1] = arg; - _v3storage[0] = arg; - } - - /// Access the component of the vector at the index [i]. - double operator [](int i) => _v3storage[i]; - - /// Set the component of the vector at the index [i]. - void operator []=(int i, double v) { - _v3storage[i] = v; - } - - /// Set the length of the vector. A negative [value] will change the vectors - /// orientation and a [value] of zero will set the vector to zero. - set length(double value) { - if (value == 0.0) { - setZero(); - } else { - double l = length; - if (l == 0.0) { - return; - } - l = value / l; - _v3storage[0] *= l; - _v3storage[1] *= l; - _v3storage[2] *= l; - } - } +/// 3D column vector. +typedef Vector3 = ({double x, double y, double z}); +extension Vector3Extension on Vector3 { /// Length. double get length => math.sqrt(length2); /// Length squared. double get length2 { - double sum; - sum = _v3storage[0] * _v3storage[0]; - sum += _v3storage[1] * _v3storage[1]; - sum += _v3storage[2] * _v3storage[2]; - return sum; - } - - /// Normalizes [this]. - double normalize() { - final double l = length; - if (l == 0.0) { - return 0.0; - } - final double d = 1.0 / l; - _v3storage[0] *= d; - _v3storage[1] *= d; - _v3storage[2] *= d; - return l; - } - - /// Normalizes copy of [this]. - Vector3 normalized() => Vector3.copy(this)..normalize(); - - /// Normalize vector into [out]. - Vector3 normalizeInto(Vector3 out) { - out - ..setFrom(this) - ..normalize(); - return out; - } - - /// Distance from [this] to [arg] - double distanceTo(Vector3 arg) => math.sqrt(distanceToSquared(arg)); - - /// Squared distance from [this] to [arg] - double distanceToSquared(Vector3 arg) { - final Float32List argStorage = arg._v3storage; - final double dx = _v3storage[0] - argStorage[0]; - final double dy = _v3storage[1] - argStorage[1]; - final double dz = _v3storage[2] - argStorage[2]; - - return dx * dx + dy * dy + dz * dz; - } - - /// Returns the angle between [this] vector and [other] in radians. - double angleTo(Vector3 other) { - final Float32List otherStorage = other._v3storage; - if (_v3storage[0] == otherStorage[0] && - _v3storage[1] == otherStorage[1] && - _v3storage[2] == otherStorage[2]) { - return 0.0; - } - - final double d = dot(other) / (length * other.length); - - return math.acos(d.clamp(-1.0, 1.0)); - } - - /// Inner product. - double dot(Vector3 other) { - final Float32List otherStorage = other._v3storage; - double sum; - sum = _v3storage[0] * otherStorage[0]; - sum += _v3storage[1] * otherStorage[1]; - sum += _v3storage[2] * otherStorage[2]; - return sum; - } - - /// Projects [this] using the projection matrix [arg] - void applyProjection(Matrix4 arg) { - final Float32List argStorage = arg.storage; - final double x = _v3storage[0]; - final double y = _v3storage[1]; - final double z = _v3storage[2]; - final double d = 1.0 / - (argStorage[3] * x + - argStorage[7] * y + - argStorage[11] * z + - argStorage[15]); - _v3storage[0] = (argStorage[0] * x + - argStorage[4] * y + - argStorage[8] * z + - argStorage[12]) * - d; - _v3storage[1] = (argStorage[1] * x + - argStorage[5] * y + - argStorage[9] * z + - argStorage[13]) * - d; - _v3storage[2] = (argStorage[2] * x + - argStorage[6] * y + - argStorage[10] * z + - argStorage[14]) * - d; - } - - /// True if any component is infinite. - bool get isInfinite { - bool isInfinite = false; - isInfinite = isInfinite || _v3storage[0].isInfinite; - isInfinite = isInfinite || _v3storage[1].isInfinite; - isInfinite = isInfinite || _v3storage[2].isInfinite; - return isInfinite; + return (x * x) + (y * y) + (z * z); } - - /// True if any component is NaN. - bool get isNaN { - bool isNan = false; - isNan = isNan || _v3storage[0].isNaN; - isNan = isNan || _v3storage[1].isNaN; - isNan = isNan || _v3storage[2].isNaN; - return isNan; - } - - /// Add [arg] to [this]. - void add(Vector3 arg) { - final Float32List argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] + argStorage[0]; - _v3storage[1] = _v3storage[1] + argStorage[1]; - _v3storage[2] = _v3storage[2] + argStorage[2]; - } - - /// Add [arg] scaled by [factor] to [this]. - void addScaled(Vector3 arg, double factor) { - final Float32List argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] + argStorage[0] * factor; - _v3storage[1] = _v3storage[1] + argStorage[1] * factor; - _v3storage[2] = _v3storage[2] + argStorage[2] * factor; - } - - /// Subtract [arg] from [this]. - void sub(Vector3 arg) { - final Float32List argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] - argStorage[0]; - _v3storage[1] = _v3storage[1] - argStorage[1]; - _v3storage[2] = _v3storage[2] - argStorage[2]; - } - - /// Multiply entries in [this] with entries in [arg]. - void multiply(Vector3 arg) { - final Float32List argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] * argStorage[0]; - _v3storage[1] = _v3storage[1] * argStorage[1]; - _v3storage[2] = _v3storage[2] * argStorage[2]; - } - - /// Divide entries in [this] with entries in [arg]. - void divide(Vector3 arg) { - final Float32List argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] / argStorage[0]; - _v3storage[1] = _v3storage[1] / argStorage[1]; - _v3storage[2] = _v3storage[2] / argStorage[2]; - } - - /// Scale [this]. - void scale(double arg) { - _v3storage[2] = _v3storage[2] * arg; - _v3storage[1] = _v3storage[1] * arg; - _v3storage[0] = _v3storage[0] * arg; - } - - /// Create a copy of [this] and scale it by [arg]. - Vector3 scaled(double arg) => clone()..scale(arg); - - /// Clone of [this]. - Vector3 clone() => Vector3.copy(this); - - /// Copy [this] into [arg]. - Vector3 copyInto(Vector3 arg) { - final Float32List argStorage = arg._v3storage; - argStorage[0] = _v3storage[0]; - argStorage[1] = _v3storage[1]; - argStorage[2] = _v3storage[2]; - return arg; - } - - /// Copies [this] into [array] starting at [offset]. - void copyIntoArray(List array, [int offset = 0]) { - array[offset + 2] = _v3storage[2]; - array[offset + 1] = _v3storage[1]; - array[offset + 0] = _v3storage[0]; - } - - set x(double arg) => _v3storage[0] = arg; - set y(double arg) => _v3storage[1] = arg; - set z(double arg) => _v3storage[2] = arg; - - double get x => _v3storage[0]; - double get y => _v3storage[1]; - double get z => _v3storage[2]; } /// Converts a matrix represented using [Float64List] to one represented using diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index ba2c3ea8c163b..d6a7b9238f217 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -754,8 +754,8 @@ void _matrix4x4CompositionTests() { paint(canvas, (ui.Canvas canvas) { final Matrix4 matrix = Matrix4.identity(); matrix.setEntry(3, 2, 0.001); - matrix.rotate(Vector3(1, 0, 0), rotateAroundX); - matrix.rotate(Vector3(0, 1, 0), rotateAroundY); + matrix.rotate(kUnitX, rotateAroundX); + matrix.rotate(kUnitY, rotateAroundY); canvas.transform(matrix.toFloat64()); }); }, width, height); diff --git a/lib/web_ui/test/ui/scene_builder_test.dart b/lib/web_ui/test/ui/scene_builder_test.dart index b8f59b67f3522..90359c7289932 100644 --- a/lib/web_ui/test/ui/scene_builder_test.dart +++ b/lib/web_ui/test/ui/scene_builder_test.dart @@ -48,7 +48,7 @@ Future testMain() async { // matrix for the device pixel ratio, so just push the identity matrix. sceneBuilder.pushTransform(transform.toFloat64()); transform.translate(150, 150); - transform.rotate(Vector3(0, 0, 1), math.pi / 3); + transform.rotate(kUnitZ, math.pi / 3); sceneBuilder.pushTransform(transform.toFloat64()); sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { canvas.drawRRect(