android画经过多点的曲线

参考链接
refer

“Spline Curves”

android自带的画曲线方法是不经过控制点的,而有时候又有这样的需要,
网上找了很多资料,是用三次函数实现的.

步骤是大概这样的

  1. 获取经过多点的函数曲线
  2. 在函数曲线上平均获取一些点,画短直线

具体实现不清楚,一句话

I do not know why, but it works.

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.pj011_cubics.view.entry;

/** this class represents a cubic polynomial */

public class Cubic {

float a,b,c,d; /* a + b*u + c*u^2 +d*u^3 */

public Cubic(float a, float b, float c, float d){
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}


/** evaluate cubic */
public float eval(float u) {
return (((d*u) + c)*u + b)*u + a;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/**
*
*/

package com.example.pj011_cubics.view;

import java.util.LinkedList;
import java.util.List;

import com.example.pj011_cubics.view.entry.Cubic;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
* 画图用View·
* @author 113108A006FEU
*
*/

public class MyView extends View {
/**
* 重要参数,两点之间分为几段描画,数字愈大分段越多,描画的曲线就越精细.
*/

private static final int STEPS = 12;
Paint paint;
Path linePath;
Path curvePath;
List<Point> points;
List<Integer> points_x;
List<Integer> points_y;

boolean drawLineFlag;
boolean drawCurveFlag;

/**
* struct.
*
* @param context
*/

public MyView(Context context) {
super(context);
initObj();
}

/**
* struct.
*
* @param context
* @param attrs
*/

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
initObj();
}

/**
* struct.
*
* @param context
* @param attrs
* @param defStyle
*/

public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initObj();
}

/**
* 计算曲线.
*
* @param x
* @return
*/

private List<Cubic> calculate(List<Integer> x) {
int n = x.size() - 1;
float[] gamma = new float[n + 1];
float[] delta = new float[n + 1];
float[] D = new float[n + 1];
int i;
/*
* We solve the equation [2 1 ] [D[0]] [3(x[1] - x[0]) ] |1 4 1 | |D[1]|
* |3(x[2] - x[0]) | | 1 4 1 | | . | = | . | | ..... | | . | | . | | 1 4
* 1| | . | |3(x[n] - x[n-2])| [ 1 2] [D[n]] [3(x[n] - x[n-1])]
*
* by using row operations to convert the matrix to upper triangular and
* then back sustitution. The D[i] are the derivatives at the knots.
*/


gamma[0] = 1.0f / 2.0f;
for (i = 1; i < n; i++) {
gamma[i] = 1 / (4 - gamma[i - 1]);
}
gamma[n] = 1 / (2 - gamma[n - 1]);

delta[0] = 3 * (x.get(1) - x.get(0)) * gamma[0];
for (i = 1; i < n; i++) {
delta[i] = (3 * (x.get(i + 1) - x.get(i - 1)) - delta[i - 1])
* gamma[i];
}
delta[n] = (3 * (x.get(n) - x.get(n - 1)) - delta[n - 1]) * gamma[n];

D[n] = delta[n];
for (i = n - 1; i >= 0; i--) {
D[i] = delta[i] - gamma[i] * D[i + 1];
}

/* now compute the coefficients of the cubics */
List<Cubic> cubics = new LinkedList<Cubic>();
for (i = 0; i < n; i++) {
Cubic c = new Cubic(x.get(i), D[i], 3 * (x.get(i + 1) - x.get(i))
- 2 * D[i] - D[i + 1], 2 * (x.get(i) - x.get(i + 1)) + D[i]
+ D[i + 1]);
cubics.add(c);
}
return cubics;
}

/**
* 清除屏幕上的点.
*/

public void clearPoints() {
points.clear();
invalidate();
}

/**
* 画曲线.
*
* @param canvas
*/

private void drawCurve(Canvas canvas) {
paint.setColor(Color.RED);
points_x.clear();
points_y.clear();
for (int i = 0; i < points.size(); i++) {
points_x.add(points.get(i).x);
points_y.add(points.get(i).y);
}

List<Cubic> calculate_x = calculate(points_x);
List<Cubic> calculate_y = calculate(points_y);
curvePath
.moveTo(calculate_x.get(0).eval(0), calculate_y.get(0).eval(0));

for (int i = 0; i < calculate_x.size(); i++) {
for (int j = 1; j <= STEPS; j++) {
float u = j / (float) STEPS;
curvePath.lineTo(calculate_x.get(i).eval(u), calculate_y.get(i)
.eval(u));
}
}
canvas.drawPath(curvePath, paint);
}

/**
* 画点.
*
* @param canvas
*/

private void drawPoints(Canvas canvas) {
for (int i = 0; i < points.size(); i++) {
Point p = points.get(i);
canvas.drawCircle(p.x, p.y, 5, paint);
}
}

/**
* 初始化.
*/

private void initObj() {
paint = new Paint();
linePath = new Path();
curvePath = new Path();
points = new LinkedList<Point>();
points_x = new LinkedList<Integer>();
points_y = new LinkedList<Integer>();
drawLineFlag = true;
drawCurveFlag = true;
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);

drawPoints(canvas);

linePath.reset();
curvePath.reset();

if (drawLineFlag && points.size() > 1) {
paint.setColor(Color.GREEN);
linePath.moveTo(points.get(0).x, points.get(0).y);
for (int i = 1; i < points.size(); i++) {
linePath.lineTo(points.get(i).x, points.get(i).y);
}
canvas.drawPath(linePath, paint);
}

if (points.size() > 2) {
drawCurve(canvas);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {

points.add(new Point((int) event.getX(), (int) event.getY()));
invalidate();
return super.onTouchEvent(event);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.example.pj011_cubics;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import com.example.pj011_cubics.view.MyView;

public class MainActivity extends Activity {
private MyView canvasView;
private Button btnClearPoints;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

getViews();
btnClearPoints.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
canvasView.clearPoints();
}
});
}

private void getViews() {
canvasView = (MyView) findViewById(R.id.canvasView);
btnClearPoints = (Button) findViewById(R.id.btnClearPoints);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >


<com.example.pj011_cubics.view.MyView
android:id="@+id/canvasView"
android:layout_width="match_parent"
android:layout_height="match_parent" />


<Button
android:id="@+id/btnClearPoints"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/canvasView"
android:layout_alignTop="@+id/canvasView"
android:text="ClearPoints" />


</RelativeLayout>
Fork me on GitHub