手写签名

This commit is contained in:
itgaojian 2023-04-11 14:19:44 +08:00
parent 24ce192f76
commit 08288d205d
11 changed files with 1534 additions and 6 deletions

View File

@ -4,6 +4,9 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.MotionEvent;
import com.tenlionsoft.baselib.constant.Constant;
import com.tenlionsoft.baselib.utils.LogUtils;
import java.util.ArrayList;
@ -12,7 +15,7 @@ public abstract class BasePen {
/**
* 绘制计算的次数数值越小计算的次数越多
*/
public static final int STEP_FACTOR = 20;
public static final int STEP_FACTOR = 10;
protected ArrayList<ControllerPoint> mHWPointList = new ArrayList<>();
protected ControllerPoint mLastPoint = new ControllerPoint(0, 0);
@ -51,7 +54,7 @@ public abstract class BasePen {
// event会被下一次事件重用这里必须生成新的否则会有问题
int action = event.getAction() & event.getActionMasked();
MotionEvent event2 = MotionEvent.obtain(event);
LogUtils.e("绘制"+action);
switch (action) {
case MotionEvent.ACTION_DOWN:
lastId = event2.getPointerId(0);
@ -197,6 +200,15 @@ public abstract class BasePen {
mHWPointList.clear();
}
public void onDo() {
LogUtils.e(mHWPointList.size());
if(mHWPointList.size()>0){
LogUtils.e(mHWPointList.size());
mHWPointList.remove(mHWPointList.size() - 1);
}
}
/**
* 绘制
* 当现在的点和触摸点的位置在一起的时候不用去绘制

View File

@ -96,7 +96,7 @@ public class PaintView extends View {
@Override
public boolean onTouchEvent(MotionEvent event) {
//通过压感值来设置画笔的粗细
setPaintPressure(event.getPressure());
setPaintPressure(event.getPressure(), event.getToolType(event.getActionIndex()));
//获取触摸的工具
toolType = event.getToolType(event.getActionIndex());
// FINGER手指 STYLUS手写笔 MOUSE鼠标
@ -133,8 +133,9 @@ public class PaintView extends View {
/**
* 构建Bitmap用来保存绘制的图
* @isCrop 是否清除边界空白
*
* @return 所绘制的bitmap
* @isCrop 是否清除边界空白
*/
public Bitmap buildAreaBitmap(boolean isCrop) {
Bitmap result;
@ -150,11 +151,16 @@ public class PaintView extends View {
/**
* 设置压力传感值
*
* @param pressure
*/
private void setPaintPressure(float pressure) {
private void setPaintPressure(float pressure, int type) {
if (mPaint != null) {
mPaint.setStrokeWidth(strokeWidth*pressure);
if (type == 2) {
mPaint.setStrokeWidth(strokeWidth * pressure * 5);
} else {
mPaint.setStrokeWidth(strokeWidth * pressure);
}
mStokeBrushPen.setPaint(mPaint);
invalidate();
}
@ -162,6 +168,7 @@ public class PaintView extends View {
/**
* 设置画笔大小
*
* @param width 大小
*/
public void setPaintWidth(int width) {
@ -176,6 +183,7 @@ public class PaintView extends View {
/**
* 设置画笔颜色
*
* @param color 颜色
*/
public void setPaintColor(int color) {
@ -200,6 +208,15 @@ public class PaintView extends View {
invalidate();
}
public void onDo(){
mStokeBrushPen.onDo();
if (mStepOperation != null) {
mStepOperation.reset();
mStepOperation.addBitmap(mBitmap);
}
invalidate();
}
/**
* 释放

View File

@ -4,6 +4,8 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import com.tenlionsoft.baselib.utils.LogUtils;
public class SteelPen extends BasePen {
@Override
@ -23,6 +25,7 @@ public class SteelPen extends BasePen {
ControllerPoint point = mBezier.getPoint(t);
mHWPointList.add(point);
}
LogUtils.e("doMove==" + mHWPointList.size());
}
@Override

View File

@ -0,0 +1,459 @@
package com.tenlionsoft.baselib.core.widget.pens;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import java.util.Vector;
/**
* Author:Double
* Time:2019/4/22
* Description:This is DrawingStrokes
*/
public class DrawingStrokes {
private final static String TAG = DrawingStrokes.class.getSimpleName();
public Paint mPaint;
public Vector<TimePoint> myPoints = new Vector<>();
public Path strokesPath = null;
public float lastLineX;
public float lastLineY;
public float mLastX;
public float mLastY;
public float mLLastX;
public float mLLastY;
public Bitmap myBitmap;
public Canvas myCanvas;
public Vector<TimePoint> mPoint;
public TimePoint lastTop= new TimePoint();
public TimePoint lastBottom=new TimePoint();
public boolean isDown = false,isUp = false;
public float mLastK = 0;
public View strokeView;
public Strokes strokes = null;
//抬笔和操作笔划时的绘图
public Canvas canvasStroke;
public Bitmap bitmapStroke;
public boolean isUnDo = false;
public SplineCurveStrategy splineCurveStrategy;
public int state = -1;
public final static int X_ADD_Y_ADD = 0X00;
public final static int X_ADD_Y_DEC = 0X01;
public final static int X_ADD_Y_SAM = 0X02;
public final static int X_DEC_Y_ADD = 0X03;
public final static int X_DEC_Y_DEC = 0X04;
public final static int X_DEC_Y_SAM = 0X05;
public final static int X_SAM_Y_ADD = 0X06;
public final static int X_SAM_Y_DEC = 0X07;
public final static int X_SAM_Y_SAM = 0X08;
public boolean debug = false;
public float mLastWidth ;
private float width, height;
private float maxWidth;
private PenType penType;
public DrawingStrokes(View strokeView, Strokes strokes){
this.strokes = strokes;
this.strokeView = strokeView;
this.strokesPath = new Path();
mPoint = new Vector<>();
}
public void setSize(float width,float height,Paint mPaint){
if (myBitmap != null) return;
this.width = width;
this.height = height;
initBitmap();
initBitmapStroke();
if(this.mPaint == null) {
this.mPaint = mPaint;
}
}
public void setPenType(PenType penType) {
this.penType = penType;
}
private void initBitmap(){
if(myBitmap == null){
myBitmap = Bitmap.createBitmap((int)width,(int)height, Bitmap.Config.ARGB_8888);
myCanvas = new Canvas(myBitmap);
}
}
private void initBitmapStroke(){
if(bitmapStroke == null){
bitmapStroke = Bitmap.createBitmap((int)width,(int)height, Bitmap.Config.ARGB_8888);
canvasStroke = new Canvas(bitmapStroke);
}
}
public float strokeWidth(float press,float widthDelta){
float width = Math.min(maxWidth , (0.1f * (1 + press * (maxWidth * 10 - 1) ))) * 0.9f + mLastWidth * 0.1f;
if(width>mLastWidth)
return Math.min(width , mLastWidth + widthDelta);
else
return Math.max(width , mLastWidth - widthDelta);
}
public void setMaxWidth(float maxWidth){
this.maxWidth = maxWidth;
Log.i(TAG, "maxWidth " + maxWidth);
}
public float getMaxWidth() {
return maxWidth;
}
public void addPoint(TimePoint timePoint,float pressure){
mPoint.add(timePoint);
if(mPoint.size() > 3){
SplineCurve splineCurve = new SplineCurve(mPoint.get(0),
mPoint.get(1), mPoint.get(2), mPoint.get(3));
float velocity = splineCurve.point3.velocityFrom(splineCurve.point2);
float widthDelta = 0;
float newWidth;
if (false) {
if (velocity > 3) {
splineCurve.steps = 4;
widthDelta = 0.8f;
} else if (velocity > 2) {
splineCurve.steps = 3;
widthDelta = 0.7f;
} else if (velocity > 1) {
splineCurve.steps = 3;
widthDelta = 0.6f;
} else if (velocity > 0.5) {
splineCurve.steps = 2;
widthDelta = 0.5f;
} else if (velocity > 0.2) {
splineCurve.steps = 2;
widthDelta = 0.4f;
} else if (velocity > 0.1) {
splineCurve.steps = 1;
widthDelta = 0.3f;
} else {
splineCurve.steps = 1;
widthDelta = 0.2f;
}
Log.i(TAG, "pressure: " + pressure);
if (pressure < 0.4)
newWidth = strokeWidth(pressure, 1 - pressure);
else
newWidth = strokeWidth(pressure, widthDelta);
} else {
if (velocity > 3) {
splineCurve.steps = 4;
widthDelta = 3.0f;
} else if (velocity > 2) {
splineCurve.steps = 3;
widthDelta = 2.0f;
} else if (velocity > 1) {
splineCurve.steps = 3;
widthDelta = 1.0f;
} else if (velocity > 0.5) {
splineCurve.steps = 2;
widthDelta = 0.8f;
} else if (velocity > 0.2) {
splineCurve.steps = 2;
widthDelta = 0.6f;
} else if (velocity > 0.1) {
splineCurve.steps = 1;
widthDelta = 0.3f;
} else {
splineCurve.steps = 1;
widthDelta = 0.2f;
}
newWidth = strokeWidth(pressure, widthDelta) ;
}
newWidth = Float.isNaN(newWidth) ? mLastWidth : newWidth;
Log.i(TAG, "newWidth" + newWidth);
if(strokes.getMyPathSize() >= 1) {
strokes.getMyPathList().elementAt(strokes.getMyPathSize() - 1).addOriginPoint(new TimePoint(timePoint.x, timePoint.y));
strokes.getMyPathList().elementAt(strokes.getMyPathSize() - 1).addOriginWidth(newWidth);
}
if(isUp){
strokes.getMyPathList().elementAt(strokes.getMyPathSize() - 1).addOriginPoint(new TimePoint(timePoint.x, timePoint.y));
strokes.getMyPathList().elementAt(strokes.getMyPathSize() - 1).addOriginWidth(newWidth);
}
if (splineCurveStrategy == null) {
splineCurveStrategy = new SplineCurveStrategy(splineCurve, mLastWidth, newWidth, myCanvas, mPaint);
splineCurveStrategy.initLastPoint(lastTop, lastBottom);
} else {
splineCurveStrategy.updateData(mLastWidth, newWidth, splineCurve);
}
Log.i("penType", penType.getPenType() + "");
switch (penType.getPenType()) {
case PenType.PEN:
splineCurveStrategy.drawPen(this);//钢笔
break;
case PenType.BRUSH:
splineCurveStrategy.drawBrushPen(this);//毛笔
break;
}
mPoint.remove(0);
}
}
public double mult(float x1,float y1,float x2,float y2,float x3,float y3){
return (x1 - x3)*(y2 - y3) - (x2 - x3)*(y1 - y3);
}
public boolean intersect(float x1,float y1,float x2,float y2,float x3,float y3,
float x4,float y4){
if(Math.max(x1,x2)<Math.min(x3,x4)){
return false;
}
if(Math.max(y1,y2)<Math.min(y3,y4)){
return false;
}
if(Math.max(x3,x4)<Math.min(x1,x2)){
return false;
}
if(Math.max(y3,y4)<Math.min(y1,y2)){
return false;
}
if(mult(x3,y3,x2,y2,x1,y1)*mult(x2,y2,x4,y4,x1,y1)<0){
return false;
}
if(mult(x1,y1,x4,y4,x3,y3)*mult(x4,y4,x2,y2,x3,y3)<0){
return false;
}
return true;
}
public float calculateDegree(float x1,float y1,float x2,float y2,float x3,float y3){
float b = (float)Math.sqrt((x1 - x2)*( x1 - x2) + (y1 - y2) * (y1 - y2));
float c = (float)Math.sqrt((x2 - x3)*( x2 - x3) +
(y2 - y3) * (y2 - y3));
float a = (float)Math.sqrt((x1 - x3)*( x1 - x3) +
(y1 - y3) * (y1 - y3));
if(c==0||b==0) return 0;
float sum = (b * b + c * c - a * a)/(2*b*c);
float degree =(float) Math.acos(sum) * 180 / (float)Math.PI;
Log.i(TAG, "degree : " + degree);
if(Float.isNaN(degree)) degree = 0;
return degree;
}
public Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//当所有笔划都画到bitmapStroke上时
//清除myBitmap上的笔划 这步很重要
if(myCanvas!=null) {
myCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
myCanvas.drawBitmap(bitmapStroke, 0, 0, mPaint);
strokeView.invalidate();
}
}
};
public void updatePathToCanvas(){
new Thread(new Runnable() {
@Override
public void run() {
canvasStroke.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
strokes.draw(canvasStroke,mPaint);
myHandler.sendEmptyMessage(0);
}
}).start();
}
public void moveTo(float x,float y,float pressure){
strokesPath.reset();
myPoints.clear();
mPoint.clear();
isDown = true;
isUp = false;
mLLastX = x;
mLLastY = y;
mLastX = x;
mLastY = y;
mLastWidth = Math.min(maxWidth , 0.1f * (1 + (pressure+0.2f) * (maxWidth * 10 - 1) ));
mLastK = 0;
strokes.addMyPath(strokes.getMyPathSize(), strokes.getMyPathSize() + strokes.getRecycleStrokesListSize());
addPoint(new TimePoint(x, y), pressure);
addPoint(new TimePoint(x, y), pressure);
strokes.getMyPathList().elementAt(strokes.getMyPathSize()-1).addOriginPoint(new TimePoint(x, y));
strokes.getMyPathList().elementAt(strokes.getMyPathSize()-1).addOriginPoint(new TimePoint(x,y));
strokes.getMyPathList().elementAt(strokes.getMyPathSize()-1).addOriginWidth(mLastWidth);
strokes.getMyPathList().elementAt(strokes.getMyPathSize()-1).addOriginWidth(mLastWidth);
}
public void lineTo(float x,float y,float pressure,boolean isUp){
if (isUp) {
addPoint(new TimePoint(x, y), pressure);
this.isUp = true;
addPoint(new TimePoint(x, y), pressure);
for(int i = myPoints.size() - 1;i>=0;i--){
strokesPath.lineTo(myPoints.elementAt(i).getX(),myPoints.elementAt(i).getY());
strokes.getMyPathList().get(strokes.getMyPathSize()-1).addPoint(new TimePoint(myPoints.elementAt(i).getX(),myPoints.elementAt(i).getY()));
}
myPoints.clear();
strokes.getMyPathList().elementAt(strokes.getMyPathSize()-1).setStroke(strokesPath);
setUnDo(false);
Log.i(TAG, "拟合前的点数量" + strokes.getMyPathList().elementAt(strokes.getMyPathSize() - 1).getOriginPoints().size());
Log.i(TAG,"拟合后的点数量" + strokes.getMyPathList().elementAt(strokes.getMyPathSize() - 1).getPoints().size());
} else {
addPoint(new TimePoint(x, y), pressure);
}
}
public void draw(Canvas canvas,Paint mPaint) {
// TODO Auto-generated method stub
canvas.drawBitmap(myBitmap, 0, 0, mPaint);
}
//撤销
public void unDo(){
Log.i(TAG, "unDo");
setUnDo(true);
//首先判断两个vector中笔划的优先级
int recycleVectorPriority = -1;
int myVectorPriority = -1;
int myPicturePriority = -1;
int recyclePicturePriority = -1;
if(strokes.getMyPathSize() > 0){
myVectorPriority = strokes.getMyPathList().elementAt(strokes.getMyPathSize()-1).getPriority();
}
if(strokes.getRecycleStrokesListSize() > 0){
recycleVectorPriority = strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).getPriority();
}
//说明有可以撤销的笔划
if(recycleVectorPriority != -1 || myVectorPriority != -1||myPicturePriority!=-1||recyclePicturePriority!=-1){
if(myVectorPriority >= recycleVectorPriority && myVectorPriority >= myPicturePriority &&myVectorPriority>=recyclePicturePriority){
//将笔划压入撤销栈
strokes.addUnDoStrokes(strokes.getMyPathList().elementAt(strokes.getMyPathSize()-1));
//移除myPath最后一个
strokes.deleteMyPath(strokes.getMyPathSize()-1);
}
if(recycleVectorPriority>=myPicturePriority&&recycleVectorPriority>=myVectorPriority&&recycleVectorPriority>=recyclePicturePriority){//可能一次会撤销很多笔划 因为当初可能一次删除多个笔划 所以要循环
int priority = -1;
do {
//降低回收栈中笔划的优先级
strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).setPriority(
strokes.getMyPathList().elementAt(strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).getLocation()).getPriority());
//设置撤销栈中笔划的优先级继承回收站的 方便恢复多个笔划
strokes.getMyPathList().elementAt(strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).getLocation()).setPriority(
recycleVectorPriority);
//将myPath对应位置的笔划压入撤销栈
Log.i(TAG,strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).getLocation()+" ");
strokes.addUnDoStrokes(strokes.getMyPathList().elementAt(strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).getLocation()));
//移除myPath对应位置的笔划
strokes.deleteMyPath(strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).getLocation());
strokes.getMyPathList().add(strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).getLocation(),
strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1));
strokes.deleteRecycleStrokesList(strokes.getRecycleStrokesListSize() - 1);
//继续判断下一个笔划的优先级
priority = -1;
if(strokes.getRecycleStrokesListSize() > 0)
priority = strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).getPriority();
}while(priority == recycleVectorPriority);
}
updatePathToCanvas();
}
}
public void reDo(){
Log.i(TAG, "reDo");
//对三个栈都做清空
if(isUnDo) setUnDo(false);
strokes.getMyPathList().clear();
strokes.getRecycleStrokesList().clear();
updatePathToCanvas();
}
public void clear(){
//对三个栈都做清空
if(isUnDo) setUnDo(false);
strokes.getMyPathList().clear();
strokes.getRecycleStrokesList().clear();
}
public void recover(){
//上一步必须是撤销
if(isUnDo){
Log.i(TAG, "recover");
int strokePriority = -1;
int strokeSize = strokes.getUnDoStrokesList().size();
if(strokeSize>0)
strokePriority = strokes.getUnDoStrokesList().elementAt(strokeSize - 1).getPriority();
int picturePriority = -1;
Log.i(TAG,strokePriority+" "+picturePriority);
if(strokePriority!=-1||picturePriority!=-1) {
int addPriority = strokes.getMyPathSize() + strokes.getRecycleStrokesListSize();
if ((picturePriority!=-1&&strokePriority <= picturePriority && strokePriority!=-1)||
(picturePriority==-1&&strokePriority!=-1)) {
//可能是恢复多个
int priority = -1;
int finalPriority = strokePriority;
do {
//恢复的位置是插入
if (strokes.getUnDoStrokesList().elementAt(strokeSize - 1).getLocation() < strokes.getMyPathSize()) {
//降低撤销栈中笔划的优先级
strokes.getUnDoStrokesList().elementAt(strokeSize - 1).setPriority(
strokes.getMyPathList().elementAt(strokes.getUnDoStrokesList().elementAt(strokeSize - 1).getLocation()).getPriority()
);
//将myPath对应位置上的压入回收栈
strokes.getRecycleStrokesList().add(strokes.getMyPathList().elementAt(strokes.getUnDoStrokesList().elementAt(strokeSize - 1).getLocation()));
//增加优先级
strokes.getRecycleStrokesList().elementAt(strokes.getRecycleStrokesListSize() - 1).setPriority(addPriority);
//删除myPath对应的笔划
strokes.deleteMyPath(strokes.getUnDoStrokesList().elementAt(strokeSize - 1).getLocation());
}
//将撤销栈中最后一个压入myPath对应的位置上
strokes.getMyPathList().add(strokes.getUnDoStrokesList().elementAt(strokeSize - 1).getLocation(),
strokes.getUnDoStrokesList().elementAt(strokeSize - 1));
//删除撤销栈最后一个
strokes.deleteUnDoStrokesList(strokeSize - 1);
//继续判断下一个笔划的优先级
priority = -1;
strokeSize = strokes.getUnDoStrokesList().size();
if (strokeSize > 0)
priority = strokes.getUnDoStrokesList().elementAt(strokeSize - 1).getPriority();
} while (priority == finalPriority);
updatePathToCanvas();
}
if ((strokePriority!=-1&&strokePriority >= picturePriority&&picturePriority!=-1)||
(strokePriority==-1&&picturePriority!=-1)) {
//可能是恢复多个
int priority = -1;
int finalPriority = picturePriority;
updatePathToCanvas();
}
}
}
}
public boolean isUnDo() {
return isUnDo;
}
public void setUnDo(boolean unDo) {
isUnDo = unDo;
if(!unDo){
strokes.clearUnDoStrokesList();
}
}
public void onDestroy(){
if(myBitmap!=null&&!myBitmap.isRecycled()){
myCanvas = null;
myBitmap.recycle();
myBitmap = null;
}
if(bitmapStroke != null && !bitmapStroke.isRecycled()){
canvasStroke = null;
bitmapStroke.recycle();
bitmapStroke = null;
}
setUnDo(false);
clear();
myPoints.clear();
}
}

View File

@ -0,0 +1,19 @@
package com.tenlionsoft.baselib.core.widget.pens;
/**
* Created by BJ-00314 on 2019/5/20.
*/
public class PenType {
/**笔的类型*/
public static final int PEN = 0x00;
public static final int BRUSH = 0x01;
/**记录当前笔类型 默认刚开始都是钢笔*/
private int penType = PEN;
public void setPenType(int penType) {
this.penType = penType;
}
public int getPenType() {
return penType;
}
}

View File

@ -0,0 +1,51 @@
package com.tenlionsoft.baselib.core.widget.pens;
/**
* Author:Double
* Time:2019/4/22
* Description:This is SplineCurve
*/
public class SplineCurve {
public TimePoint point1;
public TimePoint point2;//起点
public TimePoint point3;//结束点
public TimePoint point4;
public int steps = 10;//设定在曲线上取的点数
public SplineCurve(TimePoint point1, TimePoint point2,
TimePoint point3, TimePoint point4){
this.point1 = point1;
this.point2 = point2;
this.point3 = point3;
this.point4 = point4;
}
//获得贝塞尔曲线中取得的10个点的相邻点距离和
public float length(){
int length = 0;
float perStep;
double cx, cy, px = 0, py = 0, xdiff, ydiff;
for(int i = 0;i <= steps; i++){
perStep = (float)i / steps;
cx = point(perStep, this.point1.x, this.point2.x, this.point3.x,
this.point4.x);
cy = point(perStep, this.point1.y, this.point2.y, this.point3.y,
this.point4.y);
if(i > 0){//计算与上一个点的距离
xdiff = cx - px;
ydiff = cy - py;
length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
}
px = cx;
py = cy;
}
return length;
}
//通过贝塞尔算法返回每个点的x或者y值
public double point(float perStep, float point1, float point2, float point3, float point4){
return point1 * (1.0 - perStep) * (1.0 - perStep) * (1.0 - perStep) / 6.0
+ point2 * (3 * perStep * perStep * perStep - 6 * perStep * perStep + 4) / 6.0
+ point3 * (-3 * perStep * perStep * perStep + 3 * perStep * perStep + 3 * perStep + 1)/6.0
+point4 * perStep * perStep * perStep / 6.0;
}
}

View File

@ -0,0 +1,686 @@
package com.tenlionsoft.baselib.core.widget.pens;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.Log;
import java.util.Random;
/**
* Author:Double
* Time:2019/4/22
* Description:This is SplineCurveStrategy
*/
public class SplineCurveStrategy {
public int curveIndex = 2;
public float startWidth;
public float endWidth;
public SplineCurve splineCurve;
public Paint blurryPaint;
public Paint mMosaicPaint;
public Paint eraserPaint;
public final int eraserWidth = 50;
protected Canvas canvas;
protected Paint mPaint;
public TimePoint lastTop,lastBottom;
protected Path mPath;
public SplineCurveStrategy(SplineCurve splineCurve, float startWidth, float endWidth, Canvas canvas, Paint mPaint){
this.splineCurve = splineCurve;
this.startWidth = startWidth;
this.endWidth = endWidth;
this.blurryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.mMosaicPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.eraserPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.eraserPaint.setColor(Color.WHITE);
this.eraserPaint.setStrokeWidth(eraserWidth);
this.eraserPaint.setStrokeCap(Paint.Cap.ROUND);
this.eraserPaint.setStrokeJoin(Paint.Join.ROUND);
mMosaicPaint.setAlpha(80);
mMosaicPaint.setStrokeCap(Paint.Cap.BUTT);
mMosaicPaint.setStrokeJoin(Paint.Join.ROUND);
//mMosaicPaint.setStyle(Paint.Style.FILL);
mMosaicPaint.setStrokeWidth(12f);
this.canvas = canvas;
this.mPaint = mPaint;
this.mPath = new Path();
}
public void updateData(float startWidth,float endWidth, SplineCurve splineCurve){
this.splineCurve = splineCurve;
this.startWidth = startWidth;
this.endWidth = endWidth;
}
public double mult(float x1,float y1,float x2,float y2,float x3,float y3){
return (x1 - x3)*(y2 - y3) - (x2 - x3)*(y1 - y3);
}
public void initLastPoint(TimePoint lastTop,TimePoint lastBottom){
this.lastTop = lastTop;
this.lastBottom = lastBottom;
}
public void drawPen(DrawingStrokes drawingStrokes) {
if(drawingStrokes.debug)
mPaint.setStyle(Paint.Style.STROKE);
else mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLACK);
//获得笔在两点间不同宽度的差值
int drawSteps = (int)Math.floor(splineCurve.length());
if(drawingStrokes.isUp) {
//curveIndex = 1;
if(drawSteps > 2)
curveIndex = (drawSteps - 2)/2;
else curveIndex = 1;
if(curveIndex < 1) curveIndex = 1;
if(drawSteps == 0) drawSteps = 2;
}else if(drawingStrokes.isDown){
curveIndex = 1;
if(drawSteps == 0) drawSteps = 2;
}else{
if(drawSteps > 100) curveIndex = 40;
else if(drawSteps > 80) curveIndex = 35;
else if(drawSteps > 70) curveIndex = 30;
else if(drawSteps > 60) curveIndex = 25;
else if(drawSteps > 50) curveIndex = 20;
else if(drawSteps > 40) curveIndex = 15;
else if(drawSteps > 30) curveIndex = 13;
else if(drawSteps > 20) curveIndex = 9;
else if(drawSteps > 10) curveIndex = 7;
else if(drawSteps >= 4) curveIndex = 3;
else curveIndex = 1;
}
float widthDelta = endWidth - startWidth;
//两点间实际轨迹距离
float k = 0;
TimePoint myPointC,myPointD,myPointA,myPointB;
//危险
boolean modify = false;
if(drawSteps==0) {
drawSteps = 1;
modify = true;
}
Log.i("w_pen",drawSteps+" "+curveIndex);
// curveIndex = drawSteps/2;
// if(curveIndex==0) curveIndex=1;
for(int i = 0,num = 1; i < drawSteps; i+=curveIndex,num++){
mPath.reset();
float t = (float)(i) / drawSteps;
float tt = t * t;
float ttt = tt * t;
float u = 1 - t;
float uu = u * u;
float uuu = uu * u;
float x = uuu * splineCurve.point1.x / 6.0f;
x += (3*ttt-6*tt+4) * splineCurve.point2.x/ 6.0f;
x += (-3*ttt+3*tt+3*t+1)* splineCurve.point3.x/ 6.0f;
x += ttt * splineCurve.point4.x/ 6.0f;
float y = uuu * splineCurve.point1.y/ 6.0f;
y += (3*ttt-6*tt+4) * splineCurve.point2.y/ 6.0f;
y += (-3*ttt+3*tt+3*t+1) * splineCurve.point3.y/ 6.0f;
y += ttt * splineCurve.point4.y/ 6.0f;
float currentWidth = startWidth + t * widthDelta ;
if(!drawingStrokes.isUp)
if(Math.abs(t*widthDelta)>0.2f*num) {
if(t*widthDelta>0)
currentWidth = startWidth + 0.2f*num;
else currentWidth = startWidth - 0.2f*num;
}
int currentState = 0;
float numX = x - drawingStrokes.mLastX;
float numY = y - drawingStrokes.mLastY;
if(numX > 0 && numY >0) currentState = drawingStrokes.X_ADD_Y_ADD;
if(numX > 0 && numY < 0) currentState = drawingStrokes.X_ADD_Y_DEC;
if(numX > 0 && numY == 0) currentState = drawingStrokes.X_ADD_Y_SAM;
if(numX < 0 && numY > 0) currentState = drawingStrokes.X_DEC_Y_ADD;
if(numX < 0 && numY <0) currentState = drawingStrokes.X_DEC_Y_DEC;
if(numX < 0 && numY == 0) currentState = drawingStrokes.X_DEC_Y_SAM;
if(numX == 0 && numY > 0) currentState = drawingStrokes.X_SAM_Y_ADD;
if(numX == 0 && numY < 0) currentState = drawingStrokes.X_SAM_Y_DEC;
if(numX == 0 && numY == 0) currentState = drawingStrokes.X_SAM_Y_SAM;
// if(drawingStrokes.state!=currentState&&modify&&!drawingStrokes.isUp&&!drawingStrokes.isDown){//保证转弯处不恶意修改值
// break;
// }
if( x != drawingStrokes.mLastX){
k = (y - drawingStrokes.mLastY) / (x - drawingStrokes.mLastX);
//上个点的上下端点MyPointA,MyPointB
myPointA = new TimePoint( (drawingStrokes.mLastWidth / 2 )* (-k) / (float) Math.sqrt(k * k + 1) + drawingStrokes.mLastX,
(drawingStrokes.mLastWidth / 2 ) / (float) Math.sqrt(k * k + 1) + drawingStrokes.mLastY);
myPointB = new TimePoint( (-drawingStrokes.mLastWidth / 2 )* (-k) / (float) Math.sqrt(k * k + 1) + drawingStrokes.mLastX,
(-drawingStrokes.mLastWidth / 2 ) / (float) Math.sqrt(k * k + 1) + drawingStrokes.mLastY);
//当前点的上下端点MyPointC,MyPointD
myPointC = new TimePoint( (currentWidth / 2 )* (-k) / (float) Math.sqrt(k * k + 1) + x,
(currentWidth / 2 ) / (float) Math.sqrt(k * k + 1) + y);
myPointD = new TimePoint( (-currentWidth / 2 )* (-k) / (float) Math.sqrt(k * k + 1) + x,
(-currentWidth / 2 ) / (float) Math.sqrt(k * k + 1) + y);
}else{
myPointA = new TimePoint( drawingStrokes.mLastWidth / 2 + drawingStrokes.mLastX,
drawingStrokes.mLastY);
myPointB = new TimePoint( -drawingStrokes.mLastWidth / 2 + drawingStrokes.mLastX,
drawingStrokes.mLastY);
myPointC = new TimePoint( currentWidth / 2 + x,
y);
myPointD = new TimePoint( -currentWidth / 2 + x,
y);
}
if(drawingStrokes.isDown){//起点 需要算AB
//算出矩形的四个点
TimePoint A,B,C,D;
if( myPointA.x != myPointB.x){
k = (myPointA.y - myPointB.y) / (myPointA.x - myPointB.x);
A = new TimePoint( (drawingStrokes.mLastWidth )* (-k) / (float) Math.sqrt(k * k + 1) + myPointA.x,
(drawingStrokes.mLastWidth ) / (float) Math.sqrt(k * k + 1) + myPointA.y);
B = new TimePoint( (-drawingStrokes.mLastWidth )* (-k) / (float) Math.sqrt(k * k + 1) + myPointA.x,
(-drawingStrokes.mLastWidth ) / (float) Math.sqrt(k * k + 1) + myPointA.y);
//当前点的上下端点MyPointC,MyPointD
C = new TimePoint( (drawingStrokes.mLastWidth )* (-k) / (float) Math.sqrt(k * k + 1) + myPointB.x,
(drawingStrokes.mLastWidth ) / (float) Math.sqrt(k * k + 1) + myPointB.y);
D = new TimePoint( (-drawingStrokes.mLastWidth )* (-k) / (float) Math.sqrt(k * k + 1) + myPointB.x,
(-drawingStrokes.mLastWidth ) / (float) Math.sqrt(k * k + 1) + myPointB.y);
}else{
A = new TimePoint( drawingStrokes.mLastWidth + myPointA.x,
myPointA.y);
B = new TimePoint( -drawingStrokes.mLastWidth + myPointA.x,
myPointA.y);
C = new TimePoint( drawingStrokes.mLastWidth + myPointB.x,
myPointB.y);
D = new TimePoint( -drawingStrokes.mLastWidth / 2 + myPointB.x,
myPointB.y);
}
TimePoint centerAC = new TimePoint((A.x+C.x)/2,(A.y+C.y)/2);
TimePoint centerBD = new TimePoint((B.x+D.x)/2,(B.y+D.y)/2);
boolean isAC = true;
if( myPointA.x != myPointB.x){
float b = myPointA.y - k *myPointA.x;
if((centerAC.y - k* centerAC.x - b)*(drawingStrokes.mPoint.get(3).y - k*drawingStrokes.mPoint.get(3).x - b)<=0){
isAC = true;
}else {
isAC = false;
}
// if ((drawingStrokes.mLastY - centerAC.y) / (drawingStrokes.mLastX - centerAC.x) * k >0) {
// isAC = true;
// } else {
// isAC = false;
// }
}else{
if((centerAC.y-myPointA.y)*(drawingStrokes.mPoint.get(3).y - myPointA.y)<=0){
isAC = true;
}else {
isAC = false;
}
}
if(!drawingStrokes.debug) {
//填充
// mPath.moveTo(myPointB.x, myPointB.y);
// mPath.lineTo(myPointD.x, myPointD.y);
//// //**mPath.lineTo(myPointC.x, myPointC.y);
// mPath.lineTo(myPointC.x, myPointC.y);
// mPath.lineTo(myPointA.x, myPointA.y);
mPath.moveTo(myPointB.x, myPointB.y);
if(isAC)
mPath.quadTo(centerAC.x,centerAC.y,myPointA.x,myPointA.y);
else mPath.quadTo(centerBD.x,centerBD.y,myPointA.x,myPointA.y);
mPath.lineTo(myPointC.x, myPointC.y);
mPath.lineTo(myPointD.x, myPointD.y);
mPath.lineTo(myPointB.x, myPointB.y);
canvas.drawPath(mPath,mPaint);
mPath.reset();
}else {
//测试
// mPath.moveTo(myPointB.x, myPointB.y);
// mPath.lineTo(myPointD.x, myPointD.y);
// //**mPath.lineTo(myPointC.x, myPointC.y);
// mPath.moveTo(myPointC.x, myPointC.y);
// mPath.lineTo(myPointA.x, myPointA.y);
mPath.moveTo(myPointB.x, myPointB.y);
if(isAC)
mPath.quadTo(centerAC.x,centerAC.y,myPointA.x,myPointA.y);
else mPath.quadTo(centerBD.x,centerBD.y,myPointA.x,myPointA.y);
mPath.lineTo(myPointC.x, myPointC.y);
mPath.moveTo(myPointB.x, myPointB.y);
mPath.lineTo(myPointD.x, myPointD.y);
mPath.lineTo(myPointB.x, myPointB.y);
}
drawingStrokes.isDown = false;
drawingStrokes.strokesPath.moveTo(myPointB.x, myPointB.y);
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointB.x,myPointB.y));
if(isAC) {
drawingStrokes.strokesPath.quadTo(centerAC.x, centerAC.y, myPointA.x, myPointA.y);
drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new TimePoint(centerAC.x, centerAC.y));
}
else {
drawingStrokes.strokesPath.quadTo(centerBD.x, centerBD.y, myPointA.x, myPointA.y);
drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new TimePoint(centerBD.x, centerBD.y));
}
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointA.x, myPointA.y));
drawingStrokes.strokesPath.lineTo(myPointC.x, myPointC.y);
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointC.x, myPointC.y));
drawingStrokes.lastLineX = myPointC.x;
drawingStrokes.lastLineY = myPointC.y;
//drawingStrokes.myPoints.add(new MyPoints(myPointA.x,myPointA.y));
drawingStrokes.myPoints.add(new TimePoint(myPointB.x,myPointB.y));
drawingStrokes.myPoints.add(new TimePoint(myPointD.x,myPointD.y));
}else{
//相交为180
if((drawingStrokes.mLLastX == drawingStrokes.mLastX &&drawingStrokes.mLastX == x)
||(drawingStrokes.mLLastX != drawingStrokes.mLastX &&drawingStrokes.mLastX != x && (k == drawingStrokes.mLastK || -k == drawingStrokes.mLastK))){
if (-k == drawingStrokes.mLastK&&k!=0) {//特殊情况
Log.d("123","特殊来了"+k);
}
}else{
//判断外端点画弧
float degereeA = drawingStrokes.calculateDegree(drawingStrokes.mLastX,drawingStrokes.mLastY,
drawingStrokes.mLLastX, drawingStrokes.mLLastY, myPointA.x,myPointA.y);
float degereeB = drawingStrokes.calculateDegree(drawingStrokes.mLastX,drawingStrokes.mLastY,
drawingStrokes.mLLastX, drawingStrokes.mLLastY, myPointB.x,myPointB.y);
float degereeLT = drawingStrokes.calculateDegree(drawingStrokes.mLastX,drawingStrokes.mLastY,
x, y, lastTop.x,lastTop.y);
float degereeLB = drawingStrokes.calculateDegree(drawingStrokes.mLastX,drawingStrokes.mLastY,
x, y, lastBottom.x,lastBottom.y);
//谁大谁是外端点
if ((degereeA >= degereeB&&degereeLT >= degereeLB)||(degereeA <= degereeB&&degereeLT <= degereeLB)) {
if(!drawingStrokes.debug) {
//填充
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(lastTop.x, lastTop.y);
mPath.lineTo(lastBottom.x, lastBottom.y);
mPath.lineTo(myPointB.x, myPointB.y);
mPath.lineTo(myPointA.x, myPointA.y);
canvas.drawPath(mPath,mPaint);
mPath.reset();
}else {
//测试
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(lastTop.x, lastTop.y);
mPath.moveTo(lastBottom.x, lastBottom.y);
mPath.lineTo(myPointB.x, myPointB.y);
}
if(drawingStrokes.lastLineX == lastTop.x && drawingStrokes.lastLineY == lastTop.y){
drawingStrokes.strokesPath.lineTo(myPointA.x, myPointA.y);
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointA.x, myPointA.y));
drawingStrokes.myPoints.add(new TimePoint(myPointB.x,myPointB.y));
drawingStrokes.lastLineX = myPointA.x;
drawingStrokes.lastLineY = myPointA.y;
}else{
drawingStrokes.strokesPath.lineTo(myPointB.x, myPointB.y);
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointB.x, myPointB.y));
drawingStrokes.myPoints.add(new TimePoint(myPointA.x,myPointA.y));
drawingStrokes.lastLineX = myPointB.x;
drawingStrokes.lastLineY = myPointB.y;
}
} else {
//测试
if (drawingStrokes.intersect(myPointA.x, myPointA.y, lastBottom.x, lastBottom.y, x, y, drawingStrokes.mLastX, drawingStrokes.mLastY)
|| drawingStrokes.intersect(myPointA.x, myPointA.y, lastBottom.x, lastBottom.y, drawingStrokes.mLLastX, drawingStrokes.mLLastY,
drawingStrokes.mLastX, drawingStrokes.mLastY)) {
//转弯了
if(drawingStrokes.state!=-1&&drawingStrokes.state != currentState) {
if(!drawingStrokes.debug) {
//填充
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(lastBottom.x, lastBottom.y);
mPath.lineTo(lastTop.x, lastTop.y);
mPath.lineTo(myPointB.x, myPointB.y);
mPath.lineTo(myPointA.x, myPointA.y);
canvas.drawPath(mPath,mPaint);
mPath.reset();
}else {
//测试
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(lastBottom.x, lastBottom.y);
mPath.moveTo(lastTop.x, lastTop.y);
mPath.lineTo(myPointB.x, myPointB.y);
}
if(drawingStrokes.lastLineX == lastBottom.x && drawingStrokes.lastLineY == lastBottom.y){
drawingStrokes.strokesPath.lineTo(myPointA.x, myPointA.y);
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointA.x, myPointA.y));
drawingStrokes.myPoints.add(new TimePoint(myPointB.x,myPointB.y));
drawingStrokes.lastLineX = myPointA.x;
drawingStrokes.lastLineY = myPointA.y;
}else{
drawingStrokes.strokesPath.lineTo(myPointB.x, myPointB.y);
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointB.x, myPointB.y));
drawingStrokes.myPoints.add(new TimePoint(myPointA.x,myPointA.y));
drawingStrokes.lastLineX = myPointB.x;
drawingStrokes.lastLineY = myPointB.y;
}
}else{
if(!drawingStrokes.debug) {
//填充
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(lastTop.x, lastTop.y);
mPath.lineTo(lastBottom.x, lastBottom.y);
mPath.lineTo(myPointB.x, myPointB.y);
mPath.lineTo(myPointA.x, myPointA.y);
canvas.drawPath(mPath,mPaint);
mPath.reset();
}else {
//测试
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(lastTop.x, lastTop.y);
mPath.moveTo(lastBottom.x, lastBottom.y);
mPath.lineTo(myPointB.x, myPointB.y);
}
if(drawingStrokes.lastLineX == lastTop.x && drawingStrokes.lastLineY == lastTop.y){
drawingStrokes.strokesPath.lineTo(myPointA.x, myPointA.y);
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointA.x, myPointA.y));
drawingStrokes.myPoints.add(new TimePoint(myPointB.x,myPointB.y));
drawingStrokes.lastLineX = myPointA.x;
drawingStrokes.lastLineY = myPointA.y;
}else{
drawingStrokes.strokesPath.lineTo(myPointB.x, myPointB.y);
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointB.x, myPointB.y));
drawingStrokes.myPoints.add(new TimePoint(myPointA.x,myPointA.y));
drawingStrokes.lastLineX = myPointB.x;
drawingStrokes.lastLineY = myPointB.y;
}
}
}else{
if(!drawingStrokes.debug) {
//填充
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(lastBottom.x, lastBottom.y);
mPath.lineTo(lastTop.x, lastTop.y);
mPath.lineTo(myPointB.x, myPointB.y);
mPath.lineTo(myPointA.x, myPointA.y);
canvas.drawPath(mPath,mPaint);
mPath.reset();
}else {
//测试
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(lastBottom.x, lastBottom.y);
mPath.moveTo(lastTop.x, lastTop.y);
mPath.lineTo(myPointB.x, myPointB.y);
}
if(drawingStrokes.lastLineX == lastBottom.x && drawingStrokes.lastLineY == lastBottom.y){
drawingStrokes.strokesPath.lineTo(myPointA.x, myPointA.y);
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointA.x, myPointA.y));
drawingStrokes.myPoints.add(new TimePoint(myPointB.x,myPointB.y));
drawingStrokes.lastLineX = myPointA.x;
drawingStrokes.lastLineY = myPointA.y;
}else{
drawingStrokes.strokesPath.lineTo(myPointB.x, myPointB.y);
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointB.x, myPointB.y));
drawingStrokes.myPoints.add(new TimePoint(myPointA.x,myPointA.y));
drawingStrokes.lastLineX = myPointB.x;
drawingStrokes.lastLineY = myPointB.y;
}
}
}
}
if(!drawingStrokes.debug) {
//填充
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(myPointC.x, myPointC.y);
mPath.lineTo(myPointD.x, myPointD.y);
mPath.lineTo(myPointB.x, myPointB.y);
mPath.lineTo(myPointA.x, myPointA.y);
canvas.drawPath(mPath,mPaint);
mPath.reset();
}else {
//测试
mPath.moveTo(myPointA.x, myPointA.y);
mPath.lineTo(myPointC.x, myPointC.y);
mPath.moveTo(myPointD.x, myPointD.y);
mPath.lineTo(myPointB.x, myPointB.y);
}
if(drawingStrokes.lastLineX == myPointA.x && drawingStrokes.lastLineY == myPointA.y){
drawingStrokes.strokesPath.lineTo(myPointC.x, myPointC.y);
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointC.x, myPointC.y));
drawingStrokes.myPoints.add(new TimePoint(myPointD.x,myPointD.y));
drawingStrokes.lastLineX = myPointC.x;
drawingStrokes.lastLineY = myPointC.y;
}else{
drawingStrokes.strokesPath.lineTo(myPointD.x, myPointD.y);
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointD.x, myPointD.y));
drawingStrokes.myPoints.add(new TimePoint(myPointC.x,myPointC.y));
drawingStrokes.lastLineX = myPointD.x;
drawingStrokes.lastLineY = myPointD.y;
}
}
if(drawingStrokes.isUp && i >= drawSteps -curveIndex ){
// mPath.moveTo(myPointC.x, myPointC.y);
// //控制点+结束点
// mPath.lineTo(myPointD.x,myPointD.y);
drawingStrokes.isUp = false;
TimePoint A,B,C,D;
if( myPointC.x != myPointD.x){
k = (myPointC.y - myPointD.y) / (myPointC.x - myPointD.x);
A = new TimePoint( (currentWidth )* (-k) / (float) Math.sqrt(k * k + 1) + myPointC.x,
(currentWidth ) / (float) Math.sqrt(k * k + 1) + myPointC.y);
B = new TimePoint( (-currentWidth )* (-k) / (float) Math.sqrt(k * k + 1) + myPointD.x,
(-currentWidth ) / (float) Math.sqrt(k * k + 1) + myPointD.y);
//当前点的上下端点MyPointC,MyPointD
C = new TimePoint( (currentWidth )* (-k) / (float) Math.sqrt(k * k + 1) + myPointC.x,
(currentWidth ) / (float) Math.sqrt(k * k + 1) + myPointC.y);
D = new TimePoint( (-currentWidth )* (-k) / (float) Math.sqrt(k * k + 1) + myPointD.x,
(-currentWidth ) / (float) Math.sqrt(k * k + 1) + myPointD.y);
}else{
A = new TimePoint( currentWidth + myPointC.x,
myPointC.y);
B = new TimePoint( -currentWidth + myPointD.x,
myPointD.y);
C = new TimePoint( currentWidth + myPointC.x,
myPointC.y);
D = new TimePoint( -currentWidth + myPointD.x,
myPointD.y);
}
TimePoint centerAC = new TimePoint((A.x+C.x)/2,(A.y+C.y)/2);
TimePoint centerBD = new TimePoint((B.x+D.x)/2,(B.y+D.y)/2);
boolean isAC = true;
if( myPointC.x != myPointD.x){
float b = myPointC.y - k *myPointC.x;
if((centerAC.y - k* centerAC.x - b)*(drawingStrokes.mLastY - k*drawingStrokes.mLastX - b)<=0){
isAC = true;
}else {
isAC = false;
}
}else{
if((centerAC.y-myPointC.y)*(drawingStrokes.mLastY - myPointC.y)<=0){
isAC = true;
}else {
isAC = false;
}
}
mPath.moveTo(myPointC.x, myPointC.y);
if(isAC){
mPath.quadTo(centerAC.x,centerAC.y,myPointD.x,myPointD.y);
if(drawingStrokes.lastLineX == myPointC.x&&drawingStrokes.lastLineY == myPointC.y) {
drawingStrokes.strokesPath.quadTo(centerAC.x, centerAC.y, myPointD.x, myPointD.y);
//rawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(centerAC.x, centerAC.y));
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointD.x, myPointD.y));
}else{
drawingStrokes.strokesPath.quadTo(centerAC.x, centerAC.y, myPointC.x, myPointC.y);
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(centerAC.x, centerAC.y));
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointC.x, myPointC.y));
}
}else{
mPath.quadTo(centerBD.x,centerBD.y,myPointD.x,myPointD.y);
if(drawingStrokes.lastLineX == myPointC.x&&drawingStrokes.lastLineY == myPointC.y) {
drawingStrokes.strokesPath.quadTo(centerBD.x, centerBD.y, myPointD.x, myPointD.y);
// drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(centerBD.x, centerBD.y));
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointD.x, myPointD.y));
}else{
drawingStrokes.strokesPath.quadTo(centerBD.x, centerBD.y, myPointC.x, myPointC.y);
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(centerBD.x, centerBD.y));
//drawingStrokes.strokes.getMyPathList().get(drawingStrokes.strokes.getMyPathSize()-1).addPoint(new MyPoints(myPointC.x, myPointC.y));
}
}
mPath.lineTo(myPointC.x, myPointC.y);
canvas.drawPath(mPath,mPaint);
mPath.reset();
}
lastTop.x = myPointC.x;
lastTop.y = myPointC.y;
lastBottom.x = myPointD.x;
lastBottom.y = myPointD.y;
drawingStrokes.mLastWidth = currentWidth;
drawingStrokes.mLLastX = drawingStrokes.mLastX;
drawingStrokes.mLLastY = drawingStrokes.mLastY;
drawingStrokes.mLastX = x;
drawingStrokes.mLastY = y;
drawingStrokes.mLastK = k;
drawingStrokes.state = currentState;
}
}
/*
* 毛笔*/
public void drawBrushPen(DrawingStrokes drawingStrokes) {
// if(drawingStrokes.debug)
int[] colors = new int[] {
Color.parseColor("#66111111"),
Color.parseColor("#77111111"),
Color.parseColor("#88111111")};
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(1f);
blurryPaint.setStrokeWidth(1f);
blurryPaint.setColor(Color.RED);
//blurryPaint.setColor(drawingStrokes.context.getResources().getColor(R.color.pencil_nine));
// blurryPaint.setPathEffect(new DashPathEffect(new float[] {1f, 1f}, 0));
blurryPaint.setStyle(Paint.Style.STROKE);
//blurryPaint.setAlpha(100);
//mPaint.setAlpha(250);
// mPaint.setAntiAlias(false);
// mPaint.setPathEffect(new DashPathEffect(new float[] {2f, 2f}, 0));
//mPaint.setPathEffect(new DiscretePathEffect(3.0f, 5.0f));
//mPaint.setPathEffect(new CornerPathEffect(10));
// mPaint.setColor(drawingStrokes.context.getResources().getColor(R.color.pencil_two));
mPaint.setColor(Color.parseColor("#111111"));
//获得笔在两点间不同宽度的差值
int drawSteps = (int) Math.floor(splineCurve.length());
if (drawingStrokes.isUp) {
//curveIndex = 1;
if (drawSteps > 2)
curveIndex = (drawSteps - 2) / 2;
else curveIndex = 1;
if (curveIndex < 1) curveIndex = 1;
if (drawSteps == 0) drawSteps = 2;
} else if (drawingStrokes.isDown) {
curveIndex = 1;
if (drawSteps == 0) drawSteps = 2;
} else {
if (drawSteps > 100) curveIndex = 40;
else if (drawSteps > 80) curveIndex = 35;
else if (drawSteps > 70) curveIndex = 30;
else if (drawSteps > 60) curveIndex = 25;
else if (drawSteps > 50) curveIndex = 20;
else if (drawSteps > 40) curveIndex = 15;
else if (drawSteps > 30) curveIndex = 13;
else if (drawSteps > 20) curveIndex = 9;
else if (drawSteps > 10) curveIndex = 7;
else if (drawSteps >= 4) curveIndex = 3;
else curveIndex = 1;
}
float widthDelta = endWidth - startWidth;
//两点间实际轨迹距离
if (drawSteps == 0) {
drawSteps = 1;
}
Log.i("endWidth", " " + endWidth);
// curveIndex = drawSteps/2;
// if(curveIndex==0) curveIndex=1;
for (float i = 0, num = 1; i < drawSteps; i += 0.5f, num++) {
mPath.reset();
float t = (float) (i) / drawSteps;
float tt = t * t;
float ttt = tt * t;
float u = 1 - t;
float uu = u * u;
float uuu = uu * u;
float x = uuu * splineCurve.point1.x / 6.0f;
x += (3 * ttt - 6 * tt + 4) * splineCurve.point2.x / 6.0f;
x += (-3 * ttt + 3 * tt + 3 * t + 1) * splineCurve.point3.x / 6.0f;
x += ttt * splineCurve.point4.x / 6.0f;
float y = uuu * splineCurve.point1.y / 6.0f;
y += (3 * ttt - 6 * tt + 4) * splineCurve.point2.y / 6.0f;
y += (-3 * ttt + 3 * tt + 3 * t + 1) * splineCurve.point3.y / 6.0f;
y += ttt * splineCurve.point4.y / 6.0f;
float currentWidth = startWidth + t * widthDelta;
// if (!drawingStrokes.isUp)
// if (Math.abs(t * widthDelta) > 0.4f * num) {
// if (t * widthDelta > 0)
// currentWidth = startWidth + 0.4f * num;
// else currentWidth = startWidth - 0.4f * num;
// }
// mPaint.setMaskFilter(new BlurMaskFilter(currentWidth, BlurMaskFilter.Blur.SOLID));
// mPaint.setShadowLayer(currentWidth / 2, 0, 0, drawingStrokes.context.getResources().getColor(R.color.pencil_nine));
if (drawingStrokes.isDown) {
// canvas.drawLine(drawingStrokes.mLastX + currentWidth / 2, drawingStrokes.mLastY - currentWidth / 2, x + currentWidth / 2, y - currentWidth/ 2, blurryPaint);
// mPaint.setStrokeWidth(1f);
// mPaint.setColor(drawingStrokes.context.getResources().getColor(R.color.pencil_two));
//canvas.drawOval(new RectF(x - currentWidth / 2, y - currentWidth / 2, x + currentWidth / 2, y + currentWidth / 2), blurryPaint);
canvas.drawOval(new RectF(x - currentWidth / 2, y - currentWidth / 2, x + currentWidth / 2, y + currentWidth / 2), mPaint);
drawingStrokes.mLastWidth = currentWidth;
drawingStrokes.mLastX = x;
drawingStrokes.mLastY = y;
drawingStrokes.isDown = false;
} else {
// float width = currentWidth / 2;
// while (width < currentWidth) {
// canvas.drawLine(drawingStrokes.mLastX + width, drawingStrokes.mLastY - width , x + width, y - width, blurryPaint);
// width += 0.5f;
// }
LinearGradient linearGradient = new LinearGradient(x - currentWidth / 2, y - currentWidth / 2, x + currentWidth / 2, y + currentWidth / 2, colors, null, Shader.TileMode.REPEAT);
// RadialGradient radialGradient = new RadialGradient(x, y, currentWidth / 2, colors[1], colors[0], Shader.TileMode.MIRROR);
blurryPaint.setShader(linearGradient);
//mPaint.setAlpha(0);
float k;
if( x != drawingStrokes.mLastX) {
k = Math.abs((drawingStrokes.mLastY - y) / (drawingStrokes.mLastX - x));
Log.d("123","myk: " + k);
if(k < 0.005) {
canvas.drawLine(x , y - currentWidth, x , y - currentWidth / 4, blurryPaint);
} else if(k < 0.05) {
canvas.drawLine(x , y - currentWidth * 15 / 16, x , y - currentWidth / 4, blurryPaint);
} else if( k < 0.1) {
canvas.drawLine(x , y - currentWidth * 14 / 16, x , y - currentWidth / 4, blurryPaint);
} else if(k < 0.3) {
canvas.drawLine(x , y - currentWidth * 13 / 16, x , y - currentWidth / 4, blurryPaint);
} else if(k < 0.5) {
canvas.drawLine(x , y - currentWidth * 3 / 4, x , y - currentWidth / 4, blurryPaint);
} else if(k > 100) {
canvas.drawLine(x + currentWidth, y , x + currentWidth / 4, y, blurryPaint);
} else if(k > 50) {
canvas.drawLine(x + currentWidth * 15 / 16, y , x + currentWidth / 4, y, blurryPaint);
} else if(k > 20) {
canvas.drawLine(x + currentWidth * 14 / 16, y , x + currentWidth / 4, y, blurryPaint);
} else if(k > 10) {
canvas.drawLine(x + currentWidth * 13 / 16, y , x + currentWidth / 4, y, blurryPaint);
} else if(k > 5) {
canvas.drawLine(x + currentWidth * 3 / 4, y , x + currentWidth / 4, y, blurryPaint);
} else {
k = (drawingStrokes.mLastY - y) / (drawingStrokes.mLastX - x);
//根据斜率计算另一点
TimePoint myPointC = new TimePoint( (currentWidth * 11 / 16 )* (-k) / (float) Math.sqrt(k * k + 1) + x,
(currentWidth * 11 / 16 ) / (float) Math.sqrt(k * k + 1) + y);
TimePoint myPointD = new TimePoint( (-currentWidth * 11 / 16 )* (-k) / (float) Math.sqrt(k * k + 1) + x,
(-currentWidth * 11 / 16 ) / (float) Math.sqrt(k * k + 1) + y);
canvas.drawLine(myPointC.x, myPointC.y, x, y, blurryPaint);
canvas.drawLine(myPointD.x, myPointD.y, x, y, blurryPaint);
}
} else {
canvas.drawLine(x , y - currentWidth, x , y - currentWidth / 4, blurryPaint);
}
//canvas.drawOval(new RectF(x + currentWidth , y - currentWidth , x , y ), blurryPaint);
//mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvas.drawOval(new RectF(x - currentWidth / 2, y - currentWidth / 2, x + currentWidth / 2, y + currentWidth / 2), mPaint);
drawingStrokes.mLastWidth = currentWidth;
drawingStrokes.mLastX = x;
drawingStrokes.mLastY = y;
}
}
}
}

View File

@ -0,0 +1,141 @@
package com.tenlionsoft.baselib.core.widget.pens;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import java.util.Vector;
/**
* Author:Double
* Time:2019/4/22
* Description:This is Strokes
*/
public class Strokes {
//当前笔划列表
private Vector<Stroke> myPath = null;
//保存删除 移动 旋转等更新后回收的笔划
private Vector<Stroke> recycleStrokesList = null;
//恢复操作保存的笔划
private Vector<Stroke> unDoStrokesList = null;
public Strokes(){
myPath = new Vector<>();
recycleStrokesList = new Vector<>();
unDoStrokesList = new Vector<>();
}
public int getRecycleStrokesListSize(){
return recycleStrokesList.size();
}
public int getMyPathSize(){
return myPath.size();
}
public Vector<Stroke> getRecycleStrokesList(){
return recycleStrokesList;
}
public Vector<Stroke> getMyPathList(){
return myPath;
}
public Vector<Stroke> getUnDoStrokesList(){
return unDoStrokesList;
}
public void addMyPath(int location, int priority) {
myPath.add(new Stroke(location,priority));
}
public void draw(Canvas canvas,Paint paint){
paint.setStyle(Paint.Style.FILL);
for(int i = 0;i < myPath.size(); i++){
if(myPath.size() == 0) break;
Path stroke = myPath.elementAt(i).getStroke();
if(!stroke.isEmpty()){
canvas.drawPath(stroke,paint);
}
}
}
public void deleteMyPath(int index){
myPath.remove(index);
}
public void deleteRecycleStrokesList(int index){
recycleStrokesList.remove(index);
}
public void deleteUnDoStrokesList(int index){
unDoStrokesList.remove(index);
}
public void clearUnDoStrokesList(){
checkUnDoNull();
unDoStrokesList.clear();
}
public void checkUnDoNull(){
if(unDoStrokesList==null){
unDoStrokesList = new Vector<>();
}
}
public void addUnDoStrokes(Stroke stroke){
checkUnDoNull();
unDoStrokesList.add(stroke);
}
public class Stroke{
private Path stroke;
private int location;//删除笔划原来所在笔划列表中的位置
private int priority;//所有笔划执行撤销动作的优先级
private Vector<TimePoint> points; //拟合后的几个点
private Vector<TimePoint> originPoints;//最原始的几个点
private Vector<Float> originWidth;
public Stroke(int index,int priority){
this.stroke = new Path();
this.location = index;
this.priority = priority;
this.points = new Vector<>();
originWidth = new Vector<>();
originPoints = new Vector<>();
}
public Path getStroke(){
return stroke;
}
public void setStroke(Path path){
stroke = new Path(path);
}
public Vector<TimePoint> getOriginPoints(){
return originPoints;
}
public void setOriginPoints(Vector<TimePoint> timePoints){
this.originPoints = timePoints;
}
public void setOriginWidth(Vector<Float> originWidth){
this.originWidth = originWidth;
}
public void addOriginPoint(TimePoint myPoints){
originPoints.add(myPoints);
}
public Vector<Float> getOriginWidth(){
return originWidth;
}
public void addOriginWidth(float width){
originWidth.add(width);
}
public void addPoint(TimePoint point){
points.add(point);
}
public Vector<TimePoint> getPoints(){
return points;
}
public int getLocation() {
return location;
}
public void setLocation(int location) {
this.location = location;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
}
}

View File

@ -0,0 +1,89 @@
package com.tenlionsoft.baselib.core.widget.pens;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Author:Double
* Time:2019/4/22
* Description:This is StrokesView
*/
public class StrokesView extends View{
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//笔划
private Strokes strokes;
private DrawingStrokes mDrawing;
private PenType penType;
public StrokesView(Context context, AttributeSet attributeSet){
super(context,attributeSet);
strokes = new Strokes();
mDrawing = new DrawingStrokes(this, strokes);
mDrawing.setMaxWidth(2);//刚笔
penType = new PenType();
penType.setPenType(PenType.PEN);
mDrawing.setPenType(penType);
}
@Override
protected void onDraw(Canvas canvas) {
if(mDrawing != null) {
mDrawing.setSize(canvas.getWidth(),canvas.getHeight(),mPaint);
mDrawing.draw(canvas, mPaint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mDrawing.moveTo(event.getX(), event.getY(), event.getPressure());
} else if(action == MotionEvent.ACTION_MOVE){
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
//判断两点之间的距离是否太短
double distance = Math.sqrt((historicalX - mDrawing.mPoint.get(mDrawing.mPoint.size() - 1).getX())
* (historicalX - mDrawing.mPoint.get(mDrawing.mPoint.size() - 1).getX())
+ (historicalY - mDrawing.mPoint.get(mDrawing.mPoint.size() - 1).getY())
* (historicalY - mDrawing.mPoint.get(mDrawing.mPoint.size() - 1).getY()));
if(mDrawing.mPoint.size() > 0 && distance > 0.2)
mDrawing.lineTo(historicalX, historicalY, event.getHistoricalPressure(i),false);
}
}else if(action == MotionEvent.ACTION_UP) {
mDrawing.lineTo(event.getX(), event.getY(), event.getPressure(),true);
}
invalidate();
return true;
}
public void reDo() {
mDrawing.reDo();
}
public void unDo() {
mDrawing.unDo();
}
public void recover() {
mDrawing.recover();
}
public void setPenType(int penType) {
this.penType.setPenType(penType);
switch (penType) {
case PenType.BRUSH:
mDrawing.setMaxWidth(8);//毛笔
break;
case PenType.PEN:
mDrawing.setMaxWidth(2);//刚笔
break;
}
}
public void onDestroy() {
mDrawing.onDestroy();
}
}

View File

@ -0,0 +1,50 @@
package com.tenlionsoft.baselib.core.widget.pens;
/**
* Author:Double
* Time:2019/4/22
* Description:This is TimePoint
*/
public class TimePoint {
public float x;
public float y;
public long timestamp;
public TimePoint(){};
public TimePoint(float x, float y){
this.x = x;
this.y = y;
// this.timestamp = System.currentTimeMillis();
}
public TimePoint(float x, float y, long time){
this.x = x;
this.y = y;
this.timestamp = time;
}
public long getTimestamp() {
return timestamp;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
//获得两个点间的速度
public float velocityFrom(TimePoint start){
return distanceTo(start) / (this.timestamp - start.timestamp);
}
//获得两个点间的距离
public float distanceTo(TimePoint point){
return (float) Math.sqrt(Math.pow(point.x - this.x, 2) + Math.pow(point.y - this.y, 2));
}
}

View File

@ -23,6 +23,7 @@ import com.tenlionsoft.baselib.core.retrofit_net.conver.RxTransformer;
import com.tenlionsoft.baselib.core.widget.base.BaseFragment;
import com.tenlionsoft.baselib.core.widget.base.FragmentUtils;
import com.tenlionsoft.baselib.core.widget.base.FunctionTitleNumAdapter;
import com.tenlionsoft.baselib.core.widget.views.CustomHandWritingDialog;
import com.tenlionsoft.baselib.core.widget.views.CustomStateView;
import com.tenlionsoft.baselib.core.widget.views.ItemSplitDivider;
import com.tenlionsoft.baselib.utils.ExceptionHandler;