下面列出了android.graphics.Path#transform ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private void computePath(Rect bounds) {
final float currentScale = mCurrentScale;
final Path path = mPath;
final RectF rect = mRect;
final Matrix matrix = mMatrix;
path.reset();
int totalSize = Math.min(bounds.width(), bounds.height());
float initial = mClosedStateSize;
float destination = totalSize;
float currentSize = initial + (destination - initial) * currentScale;
float halfSize = currentSize / 2f;
float inverseScale = 1f - currentScale;
float cornerSize = halfSize * inverseScale;
float[] corners = new float[]{halfSize, halfSize, halfSize, halfSize, halfSize, halfSize, cornerSize, cornerSize};
rect.set(bounds.left, bounds.top, bounds.left + currentSize, bounds.top + currentSize);
path.addRoundRect(rect, corners, Path.Direction.CCW);
matrix.reset();
matrix.postRotate(-45, bounds.left + halfSize, bounds.top + halfSize);
matrix.postTranslate((bounds.width() - currentSize) / 2, 0);
float hDiff = (bounds.bottom - currentSize - mExternalOffset) * inverseScale;
matrix.postTranslate(0, hDiff);
path.transform(matrix);
}
private void computePath(Rect bounds) {
final float currentScale = mCurrentScale;
final Path path = mPath;
final RectF rect = mRect;
final Matrix matrix = mMatrix;
path.reset();
int totalSize = Math.min(bounds.width(), bounds.height());
float initial = mClosedStateSize;
float destination = totalSize;
float currentSize = initial + (destination - initial) * currentScale;
float halfSize = currentSize / 2f;
float inverseScale = 1f - currentScale;
float cornerSize = halfSize * inverseScale;
float[] corners = new float[]{halfSize, halfSize, halfSize, halfSize, halfSize, halfSize, cornerSize, cornerSize};
rect.set(bounds.left, bounds.top, bounds.left + currentSize, bounds.top + currentSize);
path.addRoundRect(rect, corners, Path.Direction.CCW);
matrix.reset();
matrix.postRotate(-45, bounds.left + halfSize, bounds.top + halfSize);
matrix.postTranslate((bounds.width() - currentSize) / 2, 0);
float hDiff = (bounds.bottom - currentSize - mExternalOffset) * inverseScale;
matrix.postTranslate(0, hDiff);
path.transform(matrix);
}
@Override
public boolean doTextContainer(TextContainer obj)
{
if (obj instanceof SVG.TextPath)
{
// Since we cheat a bit with our textPath rendering, we need
// to cheat a bit with our bbox calculation.
SVG.TextPath tpath = (SVG.TextPath) obj;
SVG.SvgObject ref = obj.document.resolveIRI(tpath.href);
if (ref == null) {
error("TextPath path reference '%s' not found", tpath.href);
return false;
}
SVG.Path pathObj = (SVG.Path) ref;
Path path = (new PathConverter(pathObj.d)).getPath();
if (pathObj.transform != null)
path.transform(pathObj.transform);
RectF pathBounds = new RectF();
path.computeBounds(pathBounds, true);
bbox.union(pathBounds);
return false;
}
return true;
}
private void computePath(Rect bounds) {
final float currentScale = mCurrentScale;
final Path path = mPath;
final RectF rect = mRect;
final Matrix matrix = mMatrix;
path.reset();
int totalSize = Math.min(bounds.width(), bounds.height());
float initial = mClosedStateSize;
float destination = totalSize;
float currentSize = initial + (destination - initial) * currentScale;
float halfSize = currentSize / 2f;
float inverseScale = 1f - currentScale;
float cornerSize = halfSize * inverseScale;
float[] corners = new float[]{halfSize, halfSize, halfSize, halfSize, halfSize, halfSize, cornerSize, cornerSize};
rect.set(bounds.left, bounds.top, bounds.left + currentSize, bounds.top + currentSize);
path.addRoundRect(rect, corners, Path.Direction.CCW);
matrix.reset();
matrix.postRotate(-45, bounds.left + halfSize, bounds.top + halfSize);
matrix.postTranslate((bounds.width() - currentSize) / 2, 0);
float hDiff = (bounds.bottom - currentSize - mExternalOffset) * inverseScale;
matrix.postTranslate(0, hDiff);
path.transform(matrix);
}
@Override
public boolean hitTestForSelection(Path path) {
if (!isSelectionEnabled()) {
return false;
}
RectF box = new RectF();
if (path.isRect(box)) {
// Quick check if path is rectangle
return box.contains(getOuterBoundingBox());
} else {
path.transform(getInvertedDisplayMatrix());
Region r1 = ShapeUtils.createRegionFromPath(path);
Region r2 = ShapeUtils.createRegionFromPath(getTouchableArea());
if (r1.quickReject(r2)) {
// Quick check for not intersect case
return false;
}
return !r2.op(r1, Region.Op.DIFFERENCE);
}
}
@Override
public boolean doTextContainer(TextContainer obj)
{
if (obj instanceof SVG.TextPath)
{
// Since we cheat a bit with our textPath rendering, we need
// to cheat a bit with our bbox calculation.
SVG.TextPath tpath = (SVG.TextPath) obj;
SVG.SvgObject ref = obj.document.resolveIRI(tpath.href);
if (ref == null) {
error("TextPath path reference '%s' not found", tpath.href);
return false;
}
SVG.Path pathObj = (SVG.Path) ref;
Path path = (new PathConverter(pathObj.d)).getPath();
if (pathObj.transform != null)
path.transform(pathObj.transform);
RectF pathBounds = new RectF();
path.computeBounds(pathBounds, true);
bbox.union(pathBounds);
return false;
}
return true;
}
/**
* Sets the Path defining a pattern of motion between two coordinates.
* The pattern will be translated, rotated, and scaled to fit between the start and end points.
* The pattern must not be empty and must have the end point differ from the start point.
*
* @param patternPath A Path to be used as a pattern for two-dimensional motion.
* @attr ref android.R.styleable#PatternPathMotion_patternPathData
*/
public void setPatternPath(Path patternPath) {
PathMeasure pathMeasure = new PathMeasure(patternPath, false);
float length = pathMeasure.getLength();
float[] pos = new float[2];
pathMeasure.getPosTan(length, pos, null);
float endX = pos[0];
float endY = pos[1];
pathMeasure.getPosTan(0, pos, null);
float startX = pos[0];
float startY = pos[1];
if (startX == endX && startY == endY) {
throw new IllegalArgumentException("pattern must not end at the starting point");
}
mTempMatrix.setTranslate(-startX, -startY);
float dx = endX - startX;
float dy = endY - startY;
float distance = (float) Math.hypot(dx, dy);
float scale = 1 / distance;
mTempMatrix.postScale(scale, scale);
double angle = Math.atan2(dy, dx);
mTempMatrix.postRotate((float) Math.toDegrees(-angle));
patternPath.transform(mTempMatrix, mPatternPath);
mOriginalPatternPath = patternPath;
}
@Override
public void applyToPath(@NonNull Matrix transform, @NonNull Path path) {
Matrix inverse = matrix;
transform.invert(inverse);
path.transform(inverse);
path.cubicTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
path.transform(transform);
}
/**
* transform a path with all the given matrices VERY IMPORTANT: keep order
* to value-touch-offset
*
* @param path
*/
public void pathValueToPixel(Path path) {
path.transform(mMatrixValueToPx);
path.transform(mViewPortHandler.getMatrixTouch());
path.transform(mMatrixOffset);
}
@Override
public RectF getOuterBoundingBox() {
if (boundingPath != null) {
Path path = new Path(boundingPath);
path.transform(getDisplayMatrix());
RectF box = new RectF();
path.computeBounds(box, true);
return box;
}
return new RectF();
}
/**
* Sets the Path defining a pattern of motion between two coordinates.
* The pattern will be translated, rotated, and scaled to fit between the start and end points.
* The pattern must not be empty and must have the end point differ from the start point.
*
* @param patternPath A Path to be used as a pattern for two-dimensional motion.
* @attr ref android.R.styleable#PatternPathMotion_patternPathData
*/
public void setPatternPath(@Nullable Path patternPath) {
PathMeasure pathMeasure = new PathMeasure(patternPath, false);
float length = pathMeasure.getLength();
float[] pos = new float[2];
pathMeasure.getPosTan(length, pos, null);
float endX = pos[0];
float endY = pos[1];
pathMeasure.getPosTan(0, pos, null);
float startX = pos[0];
float startY = pos[1];
if (startX == endX && startY == endY) {
throw new IllegalArgumentException("pattern must not end at the starting point");
}
mTempMatrix.setTranslate(-startX, -startY);
float dx = endX - startX;
float dy = endY - startY;
float distance = (float) Math.hypot(dx, dy);
float scale = 1 / distance;
mTempMatrix.postScale(scale, scale);
double angle = Math.atan2(dy, dx);
mTempMatrix.postRotate((float) Math.toDegrees(-angle));
if (patternPath != null) {
patternPath.transform(mTempMatrix, mPatternPath);
}
mOriginalPatternPath = patternPath;
}
/**
* Returns if the shape of the icon is same as the path.
* For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds.
*/
private boolean isShape(Path maskPath) {
// Condition1:
// If width and height of the path not close to a square, then the icon shape is
// not same as the mask shape.
float iconRatio = ((float) mBounds.width()) / mBounds.height();
if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) {
return false;
}
// Condition 2:
// Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
// should generate transparent image, if the actual icon is equivalent to the shape.
mFileId = mRandom.nextInt();
mBitmapARGB.eraseColor(Color.TRANSPARENT);
mCanvasARGB.drawBitmap(mBitmap, 0, 0, mPaintIcon);
// Fit the shape within the icon's bounding box
mMatrix.reset();
mMatrix.setScale(mBounds.width(), mBounds.height());
mMatrix.postTranslate(mBounds.left, mBounds.top);
maskPath.transform(mMatrix);
// XOR operation
mCanvasARGB.drawPath(maskPath, mPaintMaskShape);
// DST_OUT operation around the mask path outline
mCanvasARGB.drawPath(maskPath, mPaintMaskShapeOutline);
boolean isTrans = isTransparentBitmap(mBitmapARGB);
// Check if the result is almost transparent
if (!isTrans) {
return false;
}
return true;
}
/**
* transform a path with all the given matrices VERY IMPORTANT: keep order
* to value-touch-offset
*
* @param path
*/
protected void transformPath(Path path) {
path.transform(mMatrixValueToPx);
path.transform(mMatrixTouch);
path.transform(mMatrixOffset);
}
@Override
public void applyToPath(@NonNull Matrix transform, @NonNull Path path) {
Matrix inverse = matrix;
transform.invert(inverse);
path.transform(inverse);
path.lineTo(x, y);
path.transform(transform);
}
/**
* transform a path with all the given matrices VERY IMPORTANT: keep order
* to value-touch-offset
*
* @param path
*/
protected void transformPath(Path path) {
path.transform(mMatrixValueToPx);
path.transform(mMatrixTouch);
path.transform(mMatrixOffset);
}
@Override
protected void calculateBoundingBox() {
Rect box = new Rect();
paint.getTextBounds(text, 0, text.length(), box);
originalBoundingBox = new RectF(box);
boundingPath = new Path();
boundingPath.addRect(originalBoundingBox, Path.Direction.CW);
boundingPath.transform(dataMatrix);
updateBoundingBox();
}
public void fillPath(Path p) {
paint.setStyle(Paint.Style.FILL);
if (getTransform().isIdentity()) {
canvas.drawPath(p, paint);
} else {
RectF bounds = new RectF();
p.computeBounds(bounds, false);
Path p2 = new Path();
p.transform(getTransformMatrix(), p2);
RectF bounds2 = new RectF();
p2.computeBounds(bounds2, false);
float ratio = Math.max(bounds2.width()/bounds.width(), bounds2.height()/bounds.height());
if (ratio > 2 && !isMutableImageGraphics) {
// If the canvas is hardware accelerated, then it will rasterize the path
// first, then apply the transform which leads to blurry paths if the transform does
// significant scaling.
// In such cases, we
Bitmap nativeBuffer = Bitmap.createBitmap(
(int)(bounds2.width()), (int)(bounds2.height()), Bitmap.Config.ARGB_8888);
//int restorePoint = canvas.saveLayer(bounds2, paint, Canvas.ALL_SAVE_FLAG);
Canvas c = new Canvas(nativeBuffer);
Matrix translateM = new Matrix();
translateM.set(getTransformMatrix());
translateM.postTranslate(-bounds2.left, -bounds2.top);
c.concat(translateM);
c.drawPath(p, paint);
canvas.drawBitmap(nativeBuffer, bounds2.left, bounds2.top, paint);
} else {
canvas.save();
applyTransform();
canvas.drawPath(p, paint);
unapplyTransform();
canvas.restore();
}
}
}
public Path getActualElementPath() {
Path path = new Path(elementPath);
path.transform(displayMatrix);
return path;
}
public static Path getTwoCircle_CutLineMergePath(Circle c1, Circle c2, double expandAngel) {
float startAngel = GeometryUtils.getP2AngleByX(c2.center, c1.center);
Matrix m = new Matrix();
float[] src = new float[]{c2.center.x, c2.center.y};
float[] dst = new float[2];//代替了c2.center的点
m.postRotate(-startAngel, c1.center.x, c1.center.y); //现在都在右边了
m.mapPoints(dst, src);
//现在为止 就是 c1,c2 x点都是相同了 并且 c2在c1右边
ZPointF pA, pB, pC, pD;//AD 在c1, BC在c2上
ZPointF pApDControl, pBpCControl;//AD 在c1, BC在c2上
pA = new ZPointF((float) Math.cos(Math.toRadians(expandAngel / 2)) * c1.r, -(float) Math.sin(Math.toRadians(expandAngel / 2)) * c1.r);
pA.offset(c1.center);
pD = new ZPointF((float) Math.cos(Math.toRadians(expandAngel / 2)) * c1.r, (float) Math.sin(Math.toRadians(expandAngel / 2)) * c1.r);
pD.offset(c1.center);
pApDControl = new ZPointF((float) (c1.r / Math.cos(Math.toRadians(expandAngel / 2))), 0);
pApDControl.offset(c1.center);
pB = new ZPointF(-(float) Math.cos(Math.toRadians(expandAngel / 2)) * c2.r, -(float) Math.sin(Math.toRadians(expandAngel / 2)) * c2.r);
pB.offset(dst[0], dst[1]);
pC = new ZPointF(-(float) Math.cos(Math.toRadians(expandAngel / 2)) * c2.r, (float) Math.sin(Math.toRadians(expandAngel / 2)) * c2.r);
pC.offset(dst[0], dst[1]);
pBpCControl = new ZPointF(-(float) (c2.r / Math.cos(Math.toRadians(expandAngel / 2))), 0);
pBpCControl.offset(dst[0], dst[1]);
Path resultPath = new Path();
resultPath.moveTo(pA.x, pA.y);
resultPath.cubicTo(pApDControl.x, pApDControl.y, pBpCControl.x, pBpCControl.y, pB.x, pB.y);
resultPath.lineTo(pC.x, pC.y);
resultPath.cubicTo(pBpCControl.x, pBpCControl.y, pApDControl.x, pApDControl.y, pD.x, pD.y);
resultPath.close();
Matrix m2 = new Matrix();
m2.postRotate(startAngel, c1.center.x, c1.center.y);
resultPath.transform(m2);
return resultPath;
}
protected void drawText(float radius, float size, GeoPoint pt, GISDisplay display) {
if (TextUtils.isEmpty(mText))
return;
Paint textPaint = new Paint();
textPaint.setColor(mTextColor);
textPaint.setAntiAlias(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setStrokeCap(Paint.Cap.ROUND);
float gap = (float) (1 / display.getScale());
float innerRadius = radius - gap;
float textSize = size * innerRadius; // initial text size
Rect textRect = new Rect();
textPaint.setTextSize(size);
textPaint.getTextBounds(mText, 0, mText.length(), textRect);
textPaint.setTextSize(textSize);
float halfW = textRect.width() * innerRadius / 2;
float halfH = textRect.height() * innerRadius / 2;
// float outerTextRadius = (float) Math.sqrt(halfH * halfH + halfW * halfW);
// float textScale = innerRadius / outerTextRadius;
float textX = (float) (pt.getX() - halfW - radius / 2);
float textY = (float) (pt.getY() + halfH - radius / 2);
switch (mTextAlignment) {
case ALIGN_TOP:
textY = textY - halfH * 2;
break;
case ALIGN_TOP_RIGHT:
textX = textX + halfW * 2 - halfW * .5f;
textY = textY - halfH * 2;
break;
case ALIGN_RIGHT:
textX = textX + halfW * 2 - halfW * 0.25f;
break;
case ALIGN_BOTTOM_RIGHT:
textX = textX + halfW * 2 - halfW * .5f;
textY = textY + halfH * 2;
break;
case ALIGN_BOTTOM:
textY = textY + halfH * 2;
break;
case ALIGN_BOTTOM_LEFT:
textX = textX - halfW * 2 + halfW * .5f;
textY = textY + halfH * 2;
break;
case ALIGN_LEFT:
textX = textX - halfW * 2 + halfW * 0.25f;
break;
case ALIGN_TOP_LEFT:
textX = textX - halfW * 2 + halfW * .5f;
textY = textY - halfH * 2;
break;
}
Path textPath = new Path();
textPaint.getTextPath(mText, 0, mText.length(), textX, textY, textPath);
textPath.close();
Matrix matrix = new Matrix();
matrix.reset();
matrix.setScale(1, -1, (float) pt.getX(), (float) pt.getY());
textPath.transform(matrix);
display.drawPath(textPath, textPaint);
}