| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| package androidx.ui.graphics |
| |
| import androidx.ui.core.toFrameworkRect |
| import androidx.ui.geometry.Offset |
| import androidx.ui.geometry.RRect |
| import androidx.ui.geometry.Radius |
| import androidx.ui.geometry.Rect |
| import androidx.ui.graphics.vectormath.Matrix4 |
| import androidx.ui.graphics.vectormath.degrees |
| |
| class Path(private val internalPath: android.graphics.Path = android.graphics.Path()) { |
| |
| // Temporary value holders to reuse an object (not part of a state): |
| private val rectF = android.graphics.RectF() |
| private val radii = FloatArray(8) |
| private val mMatrix = android.graphics.Matrix() |
| |
| fun toFrameworkPath(): android.graphics.Path = internalPath |
| |
| private fun clone() = Path().apply { |
| internalPath.set(this@Path.internalPath) |
| } |
| |
| /** |
| * Determines how the interior of this path is calculated. |
| * |
| * Defaults to the non-zero winding rule, [PathFillType.nonZero]. |
| */ |
| fun getFillType(): PathFillType { |
| if (internalPath.fillType == android.graphics.Path.FillType.EVEN_ODD) { |
| return PathFillType.evenOdd |
| } else { |
| return PathFillType.nonZero |
| } |
| } |
| |
| fun setFillType(value: PathFillType) { |
| internalPath.fillType = |
| if (value == PathFillType.evenOdd) { |
| android.graphics.Path.FillType.EVEN_ODD |
| } else { |
| android.graphics.Path.FillType.WINDING |
| } |
| } |
| |
| /** Starts a new subpath at the given coordinate. */ |
| fun moveTo(dx: Float, dy: Float) { |
| internalPath.moveTo(dx, dy) |
| } |
| |
| /** Starts a new subpath at the given offset from the current point. */ |
| fun relativeMoveTo(dx: Float, dy: Float) { |
| internalPath.rMoveTo(dx, dy) |
| } |
| |
| /** |
| * Adds a straight line segment from the current point to the given |
| * point. |
| */ |
| fun lineTo(dx: Float, dy: Float) { |
| internalPath.lineTo(dx, dy) |
| } |
| |
| /** |
| * Adds a straight line segment from the current point to the point |
| * at the given offset from the current point. |
| */ |
| fun relativeLineTo(dx: Float, dy: Float) { |
| internalPath.rLineTo(dx, dy) |
| } |
| |
| /** |
| * Adds a quadratic bezier segment that curves from the current |
| * point to the given point (x2,y2), using the control point |
| * (x1,y1). |
| */ |
| fun quadraticBezierTo(x1: Float, y1: Float, x2: Float, y2: Float) { |
| internalPath.quadTo(x1, y1, x2, y2) |
| } |
| |
| /** |
| * Adds a quadratic bezier segment that curves from the current |
| * point to the point at the offset (x2,y2) from the current point, |
| * using the control point at the offset (x1,y1) from the current |
| * point. |
| */ |
| fun relativeQuadraticBezierTo(x1: Float, y1: Float, x2: Float, y2: Float) { |
| internalPath.rQuadTo(x1, y1, x2, y2) |
| } |
| |
| /** |
| * Adds a cubic bezier segment that curves from the current point |
| * to the given point (x3,y3), using the control points (x1,y1) and |
| * (x2,y2). |
| */ |
| fun cubicTo(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float) { |
| internalPath.cubicTo( |
| x1, y1, |
| x2, y2, |
| x3, y3 |
| ) |
| } |
| |
| /** |
| * Adds a cubic bezier segment that curves from the current point |
| * to the point at the offset (x3,y3) from the current point, using |
| * the control points at the offsets (x1,y1) and (x2,y2) from the |
| * current point. |
| */ |
| fun relativeCubicTo(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float) { |
| internalPath.rCubicTo( |
| x1, y1, |
| x2, y2, |
| x3, y3 |
| ) |
| } |
| |
| /** |
| * Adds a bezier segment that curves from the current point to the |
| * given point (x2,y2), using the control points (x1,y1) and the |
| * weight w. If the weight is greater than 1, then the curve is a |
| * hyperbola; if the weight equals 1, it's a parabola; and if it is |
| * less than 1, it is an ellipse. |
| * |
| * Throws [UnsupportedOperationException] as Android framework does not support this API |
| */ |
| @Suppress("UNUSED_PARAMETER") |
| fun conicTo(x1: Float, y1: Float, x2: Float, y2: Float, w: Float) { |
| // TODO(njawad) figure out how to handle unsupported framework Path operations |
| throw UnsupportedOperationException("conicTo not supported in framework Path") |
| } |
| |
| /** |
| * Adds a bezier segment that curves from the current point to the |
| * point at the offset (x2,y2) from the current point, using the |
| * control point at the offset (x1,y1) from the current point and |
| * the weight w. If the weight is greater than 1, then the curve is |
| * a hyperbola; if the weight equals 1, it's a parabola; and if it |
| * is less than 1, it is an ellipse. |
| * |
| * Throws [UnsupportedOperationException] as Android framework does not support this API |
| */ |
| @Suppress("UNUSED_PARAMETER") |
| fun relativeConicTo(x1: Float, y1: Float, x2: Float, y2: Float, w: Float) { |
| // TODO(njawad) figure out how to handle unsupported framework Path operations |
| throw UnsupportedOperationException("relativeConicTo not supported in framework Path") |
| } |
| |
| /** |
| * If the [forceMoveTo] argument is false, adds a straight line |
| * segment and an arc segment. |
| * |
| * If the [forceMoveTo] argument is true, starts a new subpath |
| * consisting of an arc segment. |
| * |
| * In either case, the arc segment consists of the arc that follows |
| * the edge of the oval bounded by the given rectangle, from |
| * startAngle radians around the oval up to startAngle + sweepAngle |
| * radians around the oval, with zero radians being the point on |
| * the right hand side of the oval that crosses the horizontal line |
| * that intersects the center of the rectangle and with positive |
| * angles going clockwise around the oval. |
| * |
| * The line segment added if `forceMoveTo` is false starts at the |
| * current point and ends at the start of the arc. |
| */ |
| fun arcToRad( |
| rect: Rect, |
| startAngleRadians: Float, |
| sweepAngleRadians: Float, |
| forceMoveTo: Boolean |
| ) { |
| arcTo(rect, degrees(startAngleRadians), degrees(sweepAngleRadians), forceMoveTo) |
| } |
| |
| /** |
| * If the [forceMoveTo] argument is false, adds a straight line |
| * segment and an arc segment. |
| * |
| * If the [forceMoveTo] argument is true, starts a new subpath |
| * consisting of an arc segment. |
| * |
| * In either case, the arc segment consists of the arc that follows |
| * the edge of the oval bounded by the given rectangle, from |
| * startAngle degrees around the oval up to startAngle + sweepAngle |
| * degrees around the oval, with zero degrees being the point on |
| * the right hand side of the oval that crosses the horizontal line |
| * that intersects the center of the rectangle and with positive |
| * angles going clockwise around the oval. |
| * |
| * The line segment added if `forceMoveTo` is false starts at the |
| * current point and ends at the start of the arc. |
| */ |
| fun arcTo( |
| rect: Rect, |
| startAngleDegrees: Float, |
| sweepAngleDegrees: Float, |
| forceMoveTo: Boolean |
| ) { |
| val left = rect.left |
| val top = rect.top |
| val right = rect.right |
| val bottom = rect.bottom |
| rectF.set(left, top, right, bottom) |
| internalPath.arcTo( |
| rectF, |
| startAngleDegrees, |
| sweepAngleDegrees, |
| forceMoveTo |
| ) |
| } |
| |
| /** |
| * Appends up to four conic curves weighted to describe an oval of `radius` |
| * and rotated by `rotation`. |
| * |
| * The first curve begins from the last point in the path and the last ends |
| * at `arcEnd`. The curves follow a path in a direction determined by |
| * `clockwise` and `largeArc` in such a way that the sweep angle |
| * is always less than 360 degrees. |
| * |
| * A simple line is appended if either either radii are zero or the last |
| * point in the path is `arcEnd`. The radii are scaled to fit the last path |
| * point if both are greater than zero but too small to describe an arc. |
| * |
| * Throws [UnsupportedOperationException] as Android framework does not support this API |
| */ |
| @Suppress("UNUSED_PARAMETER") |
| fun arcToPoint( |
| arcEnd: Offset, |
| radius: Radius = Radius.zero, |
| rotation: Float = 0.0f, |
| largeArc: Boolean = false, |
| clockwise: Boolean = true |
| ) { |
| // TODO(njawad) figure out how to handle unsupported framework Path operations |
| throw UnsupportedOperationException("arcToPoint not supported in framework Path") |
| } |
| |
| @Suppress("UNUSED_PARAMETER") |
| private fun _arcToPoint( |
| arcEndX: Float, |
| arcEndY: Float, |
| radius: Float, |
| radiusY: Float, |
| rotation: Float, |
| largeArc: Boolean, |
| clockwise: Boolean |
| ) { |
| // TODO(njawad): figure out how to handle unsupported framework Path operations) |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_arcToPoint' |
| } |
| |
| /** |
| * Appends up to four conic curves weighted to describe an oval of `radius` |
| * and rotated by `rotation`. |
| * |
| * The last path point is described by (px, py). |
| * |
| * The first curve begins from the last point in the path and the last ends |
| * at `arcEndDelta.dx + px` and `arcEndDelta.dy + py`. The curves follow a |
| * path in a direction determined by `clockwise` and `largeArc` |
| * in such a way that the sweep angle is always less than 360 degrees. |
| * |
| * A simple line is appended if either either radii are zero, or, both |
| * `arcEndDelta.dx` and `arcEndDelta.dy` are zero. The radii are scaled to |
| * fit the last path point if both are greater than zero but too small to |
| * describe an arc. |
| */ |
| fun relativeArcToPoint( |
| arcEndDelta: Offset, |
| radius: Radius = Radius.zero, |
| rotation: Float = 0.0f, |
| largeArc: Boolean = false, |
| clockwise: Boolean = true |
| ) { |
| _relativeArcToPoint( |
| arcEndDelta.dx, |
| arcEndDelta.dy, |
| radius.x, |
| radius.y, |
| rotation, |
| largeArc, |
| clockwise |
| ) |
| } |
| |
| @Suppress("UNUSED_PARAMETER") |
| private fun _relativeArcToPoint( |
| arcEndX: Float, |
| arcEndY: Float, |
| radius: Float, |
| radiusY: Float, |
| rotation: Float, |
| largeArc: Boolean, |
| clockwise: Boolean |
| ) { |
| // TODO(njawad): figure out how to handle unsupported framework Path operations) |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_relativeArcToPoint'; |
| } |
| |
| /** |
| * Adds a new subpath that consists of four lines that outline the |
| * given rectangle. |
| */ |
| fun addRect(rect: Rect) { |
| assert(_rectIsValid(rect)) |
| rectF.set(rect.toFrameworkRect()) |
| // TODO(njawad) figure out what to do with Path Direction, |
| // Flutter does not use it, Platform does |
| internalPath.addRect(rectF, android.graphics.Path.Direction.CCW) |
| } |
| |
| // Not necessary as wrapping platform Path |
| @Suppress("UNUSED_PARAMETER") |
| fun _addRect(left: Float, top: Float, right: Float, bottom: Float) { |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_addRect'; |
| } |
| |
| /** |
| * Adds a new subpath that consists of a curve that forms the |
| * ellipse that fills the given rectangle. |
| * |
| * To add a circle, pass an appropriate rectangle as `oval`. [Rect.fromCircle] |
| * can be used to easily describe the circle's center [Offset] and radius. |
| */ |
| fun addOval(oval: Rect) { |
| rectF.set(oval.toFrameworkRect()) |
| // TODO(njawad): figure out what to do with Path Direction, |
| // Flutter does not use it, Platform does) |
| internalPath.addOval(rectF, android.graphics.Path.Direction.CCW) |
| // _addOval(oval.left, oval.top, oval.right, oval.bottom); |
| } |
| |
| // Not necessary as wrapping platform Path |
| @Suppress("UNUSED_PARAMETER") |
| private fun _addOval(left: Float, top: Float, right: Float, bottom: Float) { |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_addOval'; |
| } |
| |
| /** |
| * Adds a new subpath with one arc segment that consists of the arc |
| * that follows the edge of the oval bounded by the given |
| * rectangle, from startAngle radians around the oval up to |
| * startAngle + sweepAngle radians around the oval, with zero |
| * radians being the point on the right hand side of the oval that |
| * crosses the horizontal line that intersects the center of the |
| * rectangle and with positive angles going clockwise around the |
| * oval. |
| */ |
| fun addArcRad(oval: Rect, startAngleRadians: Float, sweepAngleRadians: Float) { |
| addArc(oval, degrees(startAngleRadians), degrees(sweepAngleRadians)) |
| } |
| |
| /** |
| * Adds a new subpath with one arc segment that consists of the arc |
| * that follows the edge of the oval bounded by the given |
| * rectangle, from startAngle degrees around the oval up to |
| * startAngle + sweepAngle degrees around the oval, with zero |
| * degrees being the point on the right hand side of the oval that |
| * crosses the horizontal line that intersects the center of the |
| * rectangle and with positive angles going clockwise around the |
| * oval. |
| */ |
| fun addArc(oval: Rect, startAngleDegrees: Float, sweepAngleDegrees: Float) { |
| assert(_rectIsValid(oval)) |
| rectF.set(oval.toFrameworkRect()) |
| internalPath.addArc(rectF, startAngleDegrees, sweepAngleDegrees) |
| } |
| |
| // Not necessary as wrapping platform Path |
| @Suppress("UNUSED_PARAMETER") |
| private fun _addArc(left: Float, top: Float, right: Float, bottom: Float) { |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_addArc' |
| } |
| |
| /** |
| * Adds a new subpath with a sequence of line segments that connect the given |
| * points. |
| * |
| * If `close` is true, a final line segment will be added that connects the |
| * last point to the first point. |
| * |
| * The `points` argument is interpreted as offsets from the origin. |
| */ |
| @Suppress("UNUSED_PARAMETER") |
| fun addPolygon(points: List<Offset>, close: Boolean) { |
| // TODO(njawad) implement with sequence of "lineTo" calls |
| TODO() |
| } |
| |
| @Suppress("UNUSED_PARAMETER") |
| private fun _addPolygon(points: FloatArray, close: Boolean) { |
| // TODO(njawad): implement with sequence of "lineTo" calls) |
| TODO() |
| // Flutter calls into native code here |
| // native 'Path_addPolygon' |
| } |
| |
| fun addRRect(rrect: RRect) { |
| rectF.set(rrect.left, rrect.top, rrect.right, rrect.bottom) |
| radii[0] = rrect.topLeftRadiusX |
| radii[1] = rrect.topLeftRadiusY |
| |
| radii[2] = rrect.topRightRadiusX |
| radii[3] = rrect.topRightRadiusY |
| |
| radii[4] = rrect.bottomRightRadiusX |
| radii[5] = rrect.bottomRightRadiusY |
| |
| radii[6] = rrect.bottomLeftRadiusX |
| radii[7] = rrect.bottomLeftRadiusY |
| internalPath.addRoundRect(rectF, radii, android.graphics.Path.Direction.CCW) |
| } |
| |
| // Not necessary as wrapping platform Path |
| @Suppress("UNUSED_PARAMETER") |
| private fun _addRRect(rrect: FloatArray) { |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_addRRect'; |
| } |
| |
| /** |
| * Adds a new subpath that consists of the given `path` offset by the given |
| * `offset`. |
| * |
| * If `matrix4` is specified, the path will be transformed by this matrix |
| * after the matrix is translated by the given offset. The matrix is a 4x4 |
| * matrix stored in column major order. |
| */ |
| fun addPath(path: Path, offset: Offset = Offset.zero, matrix: Matrix4? = null) { |
| if (matrix != null) { |
| // TODO(njawad): update logic to convert Matrix4 -> framework |
| // Matrix when ported) |
| TODO("Refactor to convert Matrix4 to framework Matrix when Matrix4 is ported") |
| // internalPath.addPath(path.toFrameworkPath(), matrix); |
| } else { |
| internalPath.addPath(path.toFrameworkPath(), offset.dx, offset.dy) |
| } |
| } |
| |
| // Not necessary as wrapping platform Path |
| @Suppress("UNUSED_PARAMETER") |
| private fun _addPath(path: Path, dx: Float, dy: Float) { |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_addPath'; |
| } |
| |
| // Not necessary as wrapping platform Path |
| @Suppress("UNUSED_PARAMETER") |
| private fun _addPathWithMatrix(path: Path, dx: Float, dy: Float, matrix4: Matrix4) { |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_addPathWithMatrix'; |
| } |
| |
| fun extendWithPath(path: Path, offset: Offset, matrix: Matrix4) { |
| assert(Offset.isValid(offset)) |
| // if (matrix != null) { |
| assert(_matrixIsValid(matrix)) |
| _extendWithPathAndMatrix(path, offset.dx, offset.dy, matrix) |
| // } else { |
| // _extendWithPath(path, offset.dx, offset.dy) |
| // } |
| } |
| |
| @Suppress("UNUSED_PARAMETER") |
| private fun _extendWithPath(path: Path, dx: Float, dy: Float) { |
| // TODO(njawad): figure out how to handle unsupported framework Path operations) |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_extendWithPath'; |
| } |
| |
| @Suppress("UNUSED_PARAMETER") |
| private fun _extendWithPathAndMatrix(path: Path, dx: Float, dy: Float, matrix: Matrix4) { |
| // TODO(njawad): figure out how to handle unsupported framework Path operations) |
| TODO() |
| // Flutter calls into native Path logic here |
| // native 'Path_extendWithPathAndMatrix'; |
| } |
| |
| /** |
| * Closes the last subpath, as if a straight line had been drawn |
| * from the current point to the first point of the subpath. |
| */ |
| fun close() { |
| internalPath.close() |
| } |
| |
| /** |
| * Clears the [Path] object of all subpaths, returning it to the |
| * same state it had when it was created. The _current point_ is |
| * reset to the origin. |
| */ |
| fun reset() { |
| internalPath.reset() |
| } |
| |
| /** |
| * Tests to see if the given point is within the path. (That is, whether the |
| * point would be in the visible portion of the path if the path was used |
| * with [Canvas.clipPath].) |
| * |
| * The `point` argument is interpreted as an offset from the origin. |
| * |
| * Returns true if the point is in the path, and false otherwise. |
| */ |
| fun contains(offset: Offset): Boolean { |
| assert(Offset.isValid(offset)) |
| return _contains(offset) |
| } |
| |
| private fun _contains(offset: Offset): Boolean { |
| // TODO(njawad) framework Path implementation does not have a contains method") |
| // TODO(njawad): figure out how to handle unsupported framework Path operations) |
| |
| // TODO(Andrey): temporary non-efficient implementation) |
| val path = android.graphics.Path() |
| path.addRect( |
| offset.dx - 0.01f, |
| offset.dy - 0.01f, |
| offset.dx + 0.01f, |
| offset.dy + 0.01f, |
| android.graphics.Path.Direction.CW |
| ) |
| if (path.op(internalPath, android.graphics.Path.Op.INTERSECT)) { |
| return !path.isEmpty |
| } |
| return false |
| // Flutter calls into native code here |
| // native 'Path_contains'; |
| } |
| |
| /** |
| * Translates all the segments of every subpath by the given offset. |
| */ |
| fun shift(offset: Offset) { |
| mMatrix.reset() |
| mMatrix.setTranslate(offset.dx, offset.dy) |
| internalPath.transform(mMatrix) |
| } |
| |
| /** |
| * Returns a copy of the path with all the segments of every |
| * subpath transformed by the given matrix. |
| */ |
| @Suppress("UNUSED_PARAMETER") |
| fun transform(matrix: Matrix4): Path { |
| // TODO(njawad): Update implementation with Matrix4 -> android.graphics.Matrix) |
| TODO("Update implementation with Matrix4 -> android.graphics.Matrix conversion") |
| // internalPath.transform(matrix); |
| // return clone() |
| } |
| |
| // Not necessary as ported implementation with public transform method |
| @Suppress("UNUSED_PARAMETER") |
| private fun _transform(matrix: Matrix4) { |
| TODO() |
| // Flutter calls into native code here |
| // native 'Path_transform'; |
| } |
| |
| /** |
| * Computes the bounding rectangle for this path. |
| * |
| * A path containing only axis-aligned points on the same straight line will |
| * have no area, and therefore `Rect.isEmpty` will return true for such a |
| * path. Consider checking `rect.width + rect.height > 0.0` instead, or |
| * using the [computeMetrics] API to check the path length. |
| * |
| * For many more elaborate paths, the bounds may be inaccurate. For example, |
| * when a path contains a circle, the points used to compute the bounds are |
| * the circle's implied control points, which form a square around the circle; |
| * if the circle has a transformation applied using [transform] then that |
| * square is rotated, and the (axis-aligned, non-rotated) bounding box |
| * therefore ends up grossly overestimating the actual area covered by the |
| * circle. |
| */ |
| // see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds |
| fun getBounds(): Rect { |
| internalPath.computeBounds(rectF, true) |
| return Rect( |
| rectF.left, |
| rectF.top, |
| rectF.right, |
| rectF.bottom |
| ) |
| } |
| |
| // Not necessary as implemented with framework Path#computeBounds method |
| private fun _getBounds(): Rect { |
| TODO() |
| // Flutter calls into native code here |
| // native 'Path_getBounds'; |
| // return Rect(0.0f, 0.0f, 0.0f, 0.0f) |
| } |
| |
| companion object { |
| /** |
| * Combines the two paths according to the manner specified by the given |
| * `operation`. |
| * |
| * The resulting path will be constructed from non-overlapping contours. The |
| * curve order is reduced where possible so that cubics may be turned into |
| * quadratics, and quadratics maybe turned into lines. |
| * |
| * Throws [IllegalArgumentException] as Android framework does not support this API |
| */ |
| fun combine( |
| operation: PathOperation, |
| path1: Path, |
| path2: Path |
| ): Path { |
| val path = Path() |
| |
| if (path.op(path1, path2, operation)) { |
| return path |
| } |
| throw IllegalArgumentException("Path.combine() failed. This may be due an invalid " + |
| "path; in particular, check for NaN values.") |
| // TODO(njawad) where do we put Dart's StateError or equivalent? |
| // throw StateError('Path.combine() failed. This may be due an invalid path; |
| // in particular, check for NaN values.'); |
| } |
| } |
| |
| // Wrapper around _op method to avoid synthetic accessor in companion combine method |
| fun op( |
| path1: Path, |
| path2: Path, |
| operation: PathOperation |
| ): Boolean { |
| return _op(path1, path2, operation) |
| } |
| |
| private fun _op( |
| path1: Path, |
| path2: Path, |
| operation: PathOperation |
| ): Boolean { |
| // TODO(shepshapard): Our current min SDK is 21, so this check shouldn't be needed. |
| // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { |
| val op = when (operation) { |
| PathOperation.difference -> android.graphics.Path.Op.DIFFERENCE |
| PathOperation.intersect -> android.graphics.Path.Op.INTERSECT |
| PathOperation.reverseDifference -> android.graphics.Path.Op.REVERSE_DIFFERENCE |
| PathOperation.union -> android.graphics.Path.Op.UNION |
| else -> android.graphics.Path.Op.XOR |
| } |
| return internalPath.op(path1.toFrameworkPath(), path2.toFrameworkPath(), op) |
| // } else { |
| // return false |
| // } |
| } |
| |
| // TODO(njawad) figure out equivalent for PathMetrics for the framework based in Path |
| // |
| // // / Creates a [PathMetrics] object for this path. |
| // // / |
| // // / If `forceClosed` is set to true, the contours of the path will be measured |
| // // / as if they had been closed, even if they were not explicitly closed. |
| // PathMetrics computeMetrics({bool forceClosed: false}) { |
| // return new PathMetrics._(this, forceClosed); |
| // } |
| |
| /** |
| * Returns the path's convexity, as defined by the content of the path. |
| * |
| * A path is convex if it has a single contour, and only ever curves in a |
| * single direction. |
| * |
| * This function will calculate the convexity of the path from its control |
| * points, and cache the result. |
| */ |
| val isConvex: Boolean get() = internalPath.isConvex |
| |
| /** |
| * Returns true if the path is empty (contains no lines or curves) |
| */ |
| val isEmpty: Boolean get() = internalPath.isEmpty |
| |
| private fun _rectIsValid(rect: Rect): Boolean { |
| assert(Float.NaN != rect.left) { |
| "Rect.left is NaN" |
| } |
| assert(Float.NaN != rect.top) { |
| "Rect.top is NaN" |
| } |
| assert(Float.NaN != rect.right) { |
| "Rect.right is NaN" |
| } |
| assert(Float.NaN != rect.bottom) { |
| "Rect.bottom is NaN" |
| } |
| return true |
| } |
| |
| @Suppress("UNUSED_PARAMETER") |
| private fun _matrixIsValid(matrix: Matrix4): Boolean { |
| return true |
| } |
| } |