自适应步长Bezier曲线扫描
花了两个小时把这个东西做好了。虽然不及[LIEN87 ; SHAN87 ; SHAN89]论文厉害,不过自己弄的这个写起来倒是相当容易的。在这里贴出效果图和代码。效果图中,我先使用蓝色画笔,用PolyBezier绘制曲线,然后使用红色像素使用自己的算法绘制曲线。可以看见有一点点误差,不过效果还是可以接受的。代码仍然使用自己的那套库开发,不过曲线扫描的方法不受库的限制。这个算法保证非交叉部分的点不会被重复绘制。
有了这个算法之后我就可以把贝塞尔曲线转换成密度刚好的折线了。这才是最终目的。
1
#include
"
..\..\..\..\Library\Windows\VL_WinMain.h
"
2 #include " ..\..\..\..\Library\Windows\VL_WinGDI.h "
3
4 using namespace vl;
5 using namespace vl::windows;
6
7 class MyForm : public VL_WinForm
8 {
9 protected :
10 VL_WinDIB * FBuffer;
11 VL_WinDC * FBufferDC;
12 VL_WinDC * FFormDC;
13 VL_WinPen::Ptr FGDIPen;
14 VL_WinPen::Ptr FMyPen;
15
16 POINT FControlPoints[ 4 ];
17 VInt FMouseIndex;
18
19 void Clear()
20 {
21 FBufferDC -> FillRect( 0 , 0 , 800 , 600 );
22 }
23
24 void GetD(VDouble T , VDouble & dX , VDouble & dY)
25 {
26 dX = FControlPoints[ 0 ].x * ( - 3 * ( 1 - T) * ( 1 - T)) +
27 FControlPoints[ 1 ].x * ( - 6 * T * ( 1 - T) + 3 * ( 1 - T) * ( 1 - T)) +
28 FControlPoints[ 2 ].x * ( - 3 * T * T + 6 * T * ( 1 - T)) +
29 FControlPoints[ 3 ].x * 3 * T * T;
30
31 dY = FControlPoints[ 0 ].y * ( - 3 * ( 1 - T) * ( 1 - T)) +
32 FControlPoints[ 1 ].y * ( - 6 * T * ( 1 - T) + 3 * ( 1 - T) * ( 1 - T)) +
33 FControlPoints[ 2 ].y * ( - 3 * T * T + 6 * T * ( 1 - T)) +
34 FControlPoints[ 3 ].y * 3 * T * T;
35 }
36
37 void GetP(VDouble T , VDouble & X , VDouble & Y)
38 {
39 X = FControlPoints[ 0 ].x * ( 1 - T) * ( 1 - T) * ( 1 - T) +
40 FControlPoints[ 1 ].x * 3 * T * ( 1 - T) * ( 1 - T) +
41 FControlPoints[ 2 ].x * 3 * T * T * ( 1 - T) +
42 FControlPoints[ 3 ].x * T * T * T;
43
44 Y = FControlPoints[ 0 ].y * ( 1 - T) * ( 1 - T) * ( 1 - T) +
45 FControlPoints[ 1 ].y * 3 * T * ( 1 - T) * ( 1 - T) +
46 FControlPoints[ 2 ].y * 3 * T * T * ( 1 - T) +
47 FControlPoints[ 3 ].y * T * T * T;
48 }
49
50 void SetColor(VInt X , VInt Y , VInt Color)
51 {
52 VByte R = Color % 256 ;
53 VByte G = (Color >> 8 ) % 256 ;
54 VByte B = (Color >> 16 ) % 256 ;
55 if (X >= 0 && X < FBuffer -> GetWidth() && Y >= 0 && Y < FBuffer -> GetHeight())
56 {
57 ((VInt32u * )(FBuffer -> ScanLines[Y]))[X] = (R << 16 ) + (G << 8 ) + B;
58 }
59 }
60
61 void DrawCurve()
62 {
63 FBufferDC -> MoveTo(FControlPoints[ 0 ].x,FControlPoints[ 0 ].y);
64 VDouble T = 0.0001 ;
65 VBool Finished = false ;
66 VDouble OldX = FControlPoints[ 0 ].x;
67 VDouble OldY = FControlPoints[ 0 ].y;
68 VDouble X1,Y1;
69 VDouble X2 = FControlPoints[ 3 ].x;
70 VDouble Y2 = FControlPoints[ 3 ].y;
71
72 VInt OldX1 = FControlPoints[ 0 ].x;
73 VInt OldY1 = FControlPoints[ 0 ].y;
74 VInt OldX2 = 0 ;
75 VInt OldY2 = 0 ;
76 VBool Old2Available = false ;
77 SetColor(OldX1,OldY1,RGB( 255 , 0 , 0 ));
78 while ( ! Finished)
79 {
80 /* 初始步长为当前t到结束点 */
81 VDouble dT = ( 1 - T);
82 VBool AtTail = true ;
83 while ( true )
84 {
85 /* 取半步,检查目标点与当前点的长度 */
86 VDouble dTHalf = dT / 2 ;
87 if (dTHalf < 0.00001 )
88 {
89 dTHalf = dTHalf;
90 }
91 GetP(T + dT,X1,Y1);
92 GetP(T + dTHalf,X2,Y2);
93 VDouble Length = sqrt((X1 - OldX) * (X1 - OldX) + (Y1 - OldY) * (Y1 - OldY));
94 VDouble LengthHalf = sqrt((X2 - OldX) * (X2 - OldX) + (Y2 - OldY) * (Y2 - OldY));
95 /* 如果半步长大于一步则取半步,继续迭代 */
96 if (LengthHalf >= Length)
97 {
98 dT = dTHalf;
99 }
100 else
101 {
102 /* 否则,如果全步长产生的距离<=1则绘制 */
103 if (Length <= 1 )
104 {
105 if (AtTail)
106 {
107 Finished = true ;
108 }
109 break ;
110 }
111 /* 否则进行近似测量 */
112 else
113 {
114 dT *= 1 / Length;
115 GetP(T + dT,X1,Y1);
116 }
117 }
118 AtTail = false ;
119 }
120 /* 计算像素位置 */
121 VInt X = Round(X1);
122 VInt Y = Round(Y1);
123 VInt dX_I = (OldX1 - X) * (OldX1 - X);
124 VInt dY_I = (OldY1 - Y) * (OldY1 - Y);
125 /* 让线条长度保持1 */
126 if (Old2Available)
127 {
128 if (dX_I == 1 && dY_I == 1 )
129 {
130 OldX1 = X;
131 OldY1 = Y;
132 SetColor(OldX1,OldY1,RGB( 255 , 0 , 0 ));
133 }
134 else
135 {
136 OldX1 = X;
137 OldY1 = Y;
138 SetColor(OldX1,OldY1,RGB( 255 , 0 , 0 ));
139 SetColor(OldX2,OldY2,RGB( 255 , 0 , 0 ));
140 }
141 Old2Available = false ;
142 }
143 else
144 {
145 if (dX_I == 1 && dY_I == 1 )
146 {
147 OldX1 = X;
148 OldY1 = Y;
149 SetColor(OldX1,OldY1,RGB( 255 , 0 , 0 ));
150 }
151 else if (dX_I == 1 || dY_I == 1 )
152 {
153 OldX2 = X;
154 OldY2 = Y;
155 Old2Available = true ;
156 }
157 }
158 OldX = X1;
159 OldY = Y1;
160 T += dT;
161 }
162 if (Old2Available)
163 {
164 SetColor(OldX2,OldY2,RGB( 255 , 0 , 0 ));
165 }
166 }
167
168 void DrawHandles()
169 {
170 for (VInt i = 0 ;i < 4 ;i ++ )
171 {
172 POINT P = FControlPoints[i];
173 FBufferDC -> Ellipse(P.x - 2 ,P.y - 2 ,P.x + 2 ,P.y + 2 );
174 }
175 }
176
177 void Draw()
178 {
179 Clear();
180 FBufferDC -> SetPen(FGDIPen);
181 FBufferDC -> PolyBezier(FControlPoints, 4 );
182 FBufferDC -> SetPen(FMyPen);
183 DrawCurve();
184 DrawHandles();
185 FFormDC -> Draw( 0 , 0 ,FBuffer);
186 }
187 public :
188 MyForm():VL_WinForm( true )
189 {
190 FBuffer = new VL_WinDIB( 800 , 600 );
191 FBufferDC =& FBuffer -> DC;
192 FBufferDC -> SetBackTransparent( true );
193 FFormDC = new VL_WinControlDC(GetHandle());
194 FGDIPen = new VL_WinPen(PS_SOLID, 1 ,RGB( 0 , 0 , 255 ));
195 FMyPen = new VL_WinPen(PS_SOLID, 1 ,RGB( 255 , 0 , 0 ));
196 FMouseIndex =- 1 ;
197
198 FControlPoints[ 0 ].x = 200 ;
199 FControlPoints[ 0 ].y = 400 ;
200 FControlPoints[ 1 ].x = 200 ;
201 FControlPoints[ 1 ].y = 200 ;
202 FControlPoints[ 2 ].x = 400 ;
203 FControlPoints[ 2 ].y = 200 ;
204 FControlPoints[ 3 ].x = 400 ;
205 FControlPoints[ 3 ].y = 400 ;
206
207 OnPaint.Bind( this , & MyForm::Form_OnPaint);
208 OnLeftButtonDown.Bind( this , & MyForm::Form_OnMouseDown);
209 OnMouseMove.Bind( this , & MyForm::Form_OnMouseMove);
210 OnLeftButtonUp.Bind( this , & MyForm::Form_OnMouseUp);
211
212 SetMaximizeBox( false );
213 SetMinimizeBox( false );
214 SetBorder(vwfbSingle);
215 SetClientWidth( 800 );
216 SetClientHeight( 600 );
217 SetText(L " Vczh Bezier " );
218 MoveCenter();
219 Draw();
220 Show();
221 }
222
223 ~ MyForm()
224 {
225 delete FFormDC;
226 delete FBuffer;
227 }
228
229 void Form_OnPaint(VL_Base * Sender)
230 {
231 FFormDC -> Draw( 0 , 0 ,FBuffer);
232 }
233
234 void Form_OnMouseDown(VL_Base * Sender,VLS_MouseStruct MouseStruct)
235 {
236 /* 计算出在鼠标控制范围内(6像素)的控制点 */
237 for (VInt i = 0 ;i < 4 ;i ++ )
238 {
239 POINT P = FControlPoints[i];
240 if ((P.x - MouseStruct.X) * (P.x - MouseStruct.X) + (P.y - MouseStruct.Y) * (P.y - MouseStruct.Y) <= 36 )
241 {
242 FMouseIndex = i;
243 break ;
244 }
245 }
246 Form_OnMouseMove(Sender,MouseStruct);
247 }
248
249 void Form_OnMouseMove(VL_Base * Sender,VLS_MouseStruct MouseStruct)
250 {
251 /* 抓住有效控制点的话则移动 */
252 if (FMouseIndex !=- 1 )
253 {
254 FControlPoints[FMouseIndex].x = MouseStruct.X;
255 FControlPoints[FMouseIndex].y = MouseStruct.Y;
256 Draw();
257 }
258 }
259
260 void Form_OnMouseUp(VL_Base * Sender,VLS_MouseStruct MouseStruct)
261 {
262 /* 接触控制 */
263 FMouseIndex =- 1 ;
264 }
265 };
266
267 void main()
268 {
269 new MyForm();
270 GetApplication() -> Run();
271 }
2 #include " ..\..\..\..\Library\Windows\VL_WinGDI.h "
3
4 using namespace vl;
5 using namespace vl::windows;
6
7 class MyForm : public VL_WinForm
8 {
9 protected :
10 VL_WinDIB * FBuffer;
11 VL_WinDC * FBufferDC;
12 VL_WinDC * FFormDC;
13 VL_WinPen::Ptr FGDIPen;
14 VL_WinPen::Ptr FMyPen;
15
16 POINT FControlPoints[ 4 ];
17 VInt FMouseIndex;
18
19 void Clear()
20 {
21 FBufferDC -> FillRect( 0 , 0 , 800 , 600 );
22 }
23
24 void GetD(VDouble T , VDouble & dX , VDouble & dY)
25 {
26 dX = FControlPoints[ 0 ].x * ( - 3 * ( 1 - T) * ( 1 - T)) +
27 FControlPoints[ 1 ].x * ( - 6 * T * ( 1 - T) + 3 * ( 1 - T) * ( 1 - T)) +
28 FControlPoints[ 2 ].x * ( - 3 * T * T + 6 * T * ( 1 - T)) +
29 FControlPoints[ 3 ].x * 3 * T * T;
30
31 dY = FControlPoints[ 0 ].y * ( - 3 * ( 1 - T) * ( 1 - T)) +
32 FControlPoints[ 1 ].y * ( - 6 * T * ( 1 - T) + 3 * ( 1 - T) * ( 1 - T)) +
33 FControlPoints[ 2 ].y * ( - 3 * T * T + 6 * T * ( 1 - T)) +
34 FControlPoints[ 3 ].y * 3 * T * T;
35 }
36
37 void GetP(VDouble T , VDouble & X , VDouble & Y)
38 {
39 X = FControlPoints[ 0 ].x * ( 1 - T) * ( 1 - T) * ( 1 - T) +
40 FControlPoints[ 1 ].x * 3 * T * ( 1 - T) * ( 1 - T) +
41 FControlPoints[ 2 ].x * 3 * T * T * ( 1 - T) +
42 FControlPoints[ 3 ].x * T * T * T;
43
44 Y = FControlPoints[ 0 ].y * ( 1 - T) * ( 1 - T) * ( 1 - T) +
45 FControlPoints[ 1 ].y * 3 * T * ( 1 - T) * ( 1 - T) +
46 FControlPoints[ 2 ].y * 3 * T * T * ( 1 - T) +
47 FControlPoints[ 3 ].y * T * T * T;
48 }
49
50 void SetColor(VInt X , VInt Y , VInt Color)
51 {
52 VByte R = Color % 256 ;
53 VByte G = (Color >> 8 ) % 256 ;
54 VByte B = (Color >> 16 ) % 256 ;
55 if (X >= 0 && X < FBuffer -> GetWidth() && Y >= 0 && Y < FBuffer -> GetHeight())
56 {
57 ((VInt32u * )(FBuffer -> ScanLines[Y]))[X] = (R << 16 ) + (G << 8 ) + B;
58 }
59 }
60
61 void DrawCurve()
62 {
63 FBufferDC -> MoveTo(FControlPoints[ 0 ].x,FControlPoints[ 0 ].y);
64 VDouble T = 0.0001 ;
65 VBool Finished = false ;
66 VDouble OldX = FControlPoints[ 0 ].x;
67 VDouble OldY = FControlPoints[ 0 ].y;
68 VDouble X1,Y1;
69 VDouble X2 = FControlPoints[ 3 ].x;
70 VDouble Y2 = FControlPoints[ 3 ].y;
71
72 VInt OldX1 = FControlPoints[ 0 ].x;
73 VInt OldY1 = FControlPoints[ 0 ].y;
74 VInt OldX2 = 0 ;
75 VInt OldY2 = 0 ;
76 VBool Old2Available = false ;
77 SetColor(OldX1,OldY1,RGB( 255 , 0 , 0 ));
78 while ( ! Finished)
79 {
80 /* 初始步长为当前t到结束点 */
81 VDouble dT = ( 1 - T);
82 VBool AtTail = true ;
83 while ( true )
84 {
85 /* 取半步,检查目标点与当前点的长度 */
86 VDouble dTHalf = dT / 2 ;
87 if (dTHalf < 0.00001 )
88 {
89 dTHalf = dTHalf;
90 }
91 GetP(T + dT,X1,Y1);
92 GetP(T + dTHalf,X2,Y2);
93 VDouble Length = sqrt((X1 - OldX) * (X1 - OldX) + (Y1 - OldY) * (Y1 - OldY));
94 VDouble LengthHalf = sqrt((X2 - OldX) * (X2 - OldX) + (Y2 - OldY) * (Y2 - OldY));
95 /* 如果半步长大于一步则取半步,继续迭代 */
96 if (LengthHalf >= Length)
97 {
98 dT = dTHalf;
99 }
100 else
101 {
102 /* 否则,如果全步长产生的距离<=1则绘制 */
103 if (Length <= 1 )
104 {
105 if (AtTail)
106 {
107 Finished = true ;
108 }
109 break ;
110 }
111 /* 否则进行近似测量 */
112 else
113 {
114 dT *= 1 / Length;
115 GetP(T + dT,X1,Y1);
116 }
117 }
118 AtTail = false ;
119 }
120 /* 计算像素位置 */
121 VInt X = Round(X1);
122 VInt Y = Round(Y1);
123 VInt dX_I = (OldX1 - X) * (OldX1 - X);
124 VInt dY_I = (OldY1 - Y) * (OldY1 - Y);
125 /* 让线条长度保持1 */
126 if (Old2Available)
127 {
128 if (dX_I == 1 && dY_I == 1 )
129 {
130 OldX1 = X;
131 OldY1 = Y;
132 SetColor(OldX1,OldY1,RGB( 255 , 0 , 0 ));
133 }
134 else
135 {
136 OldX1 = X;
137 OldY1 = Y;
138 SetColor(OldX1,OldY1,RGB( 255 , 0 , 0 ));
139 SetColor(OldX2,OldY2,RGB( 255 , 0 , 0 ));
140 }
141 Old2Available = false ;
142 }
143 else
144 {
145 if (dX_I == 1 && dY_I == 1 )
146 {
147 OldX1 = X;
148 OldY1 = Y;
149 SetColor(OldX1,OldY1,RGB( 255 , 0 , 0 ));
150 }
151 else if (dX_I == 1 || dY_I == 1 )
152 {
153 OldX2 = X;
154 OldY2 = Y;
155 Old2Available = true ;
156 }
157 }
158 OldX = X1;
159 OldY = Y1;
160 T += dT;
161 }
162 if (Old2Available)
163 {
164 SetColor(OldX2,OldY2,RGB( 255 , 0 , 0 ));
165 }
166 }
167
168 void DrawHandles()
169 {
170 for (VInt i = 0 ;i < 4 ;i ++ )
171 {
172 POINT P = FControlPoints[i];
173 FBufferDC -> Ellipse(P.x - 2 ,P.y - 2 ,P.x + 2 ,P.y + 2 );
174 }
175 }
176
177 void Draw()
178 {
179 Clear();
180 FBufferDC -> SetPen(FGDIPen);
181 FBufferDC -> PolyBezier(FControlPoints, 4 );
182 FBufferDC -> SetPen(FMyPen);
183 DrawCurve();
184 DrawHandles();
185 FFormDC -> Draw( 0 , 0 ,FBuffer);
186 }
187 public :
188 MyForm():VL_WinForm( true )
189 {
190 FBuffer = new VL_WinDIB( 800 , 600 );
191 FBufferDC =& FBuffer -> DC;
192 FBufferDC -> SetBackTransparent( true );
193 FFormDC = new VL_WinControlDC(GetHandle());
194 FGDIPen = new VL_WinPen(PS_SOLID, 1 ,RGB( 0 , 0 , 255 ));
195 FMyPen = new VL_WinPen(PS_SOLID, 1 ,RGB( 255 , 0 , 0 ));
196 FMouseIndex =- 1 ;
197
198 FControlPoints[ 0 ].x = 200 ;
199 FControlPoints[ 0 ].y = 400 ;
200 FControlPoints[ 1 ].x = 200 ;
201 FControlPoints[ 1 ].y = 200 ;
202 FControlPoints[ 2 ].x = 400 ;
203 FControlPoints[ 2 ].y = 200 ;
204 FControlPoints[ 3 ].x = 400 ;
205 FControlPoints[ 3 ].y = 400 ;
206
207 OnPaint.Bind( this , & MyForm::Form_OnPaint);
208 OnLeftButtonDown.Bind( this , & MyForm::Form_OnMouseDown);
209 OnMouseMove.Bind( this , & MyForm::Form_OnMouseMove);
210 OnLeftButtonUp.Bind( this , & MyForm::Form_OnMouseUp);
211
212 SetMaximizeBox( false );
213 SetMinimizeBox( false );
214 SetBorder(vwfbSingle);
215 SetClientWidth( 800 );
216 SetClientHeight( 600 );
217 SetText(L " Vczh Bezier " );
218 MoveCenter();
219 Draw();
220 Show();
221 }
222
223 ~ MyForm()
224 {
225 delete FFormDC;
226 delete FBuffer;
227 }
228
229 void Form_OnPaint(VL_Base * Sender)
230 {
231 FFormDC -> Draw( 0 , 0 ,FBuffer);
232 }
233
234 void Form_OnMouseDown(VL_Base * Sender,VLS_MouseStruct MouseStruct)
235 {
236 /* 计算出在鼠标控制范围内(6像素)的控制点 */
237 for (VInt i = 0 ;i < 4 ;i ++ )
238 {
239 POINT P = FControlPoints[i];
240 if ((P.x - MouseStruct.X) * (P.x - MouseStruct.X) + (P.y - MouseStruct.Y) * (P.y - MouseStruct.Y) <= 36 )
241 {
242 FMouseIndex = i;
243 break ;
244 }
245 }
246 Form_OnMouseMove(Sender,MouseStruct);
247 }
248
249 void Form_OnMouseMove(VL_Base * Sender,VLS_MouseStruct MouseStruct)
250 {
251 /* 抓住有效控制点的话则移动 */
252 if (FMouseIndex !=- 1 )
253 {
254 FControlPoints[FMouseIndex].x = MouseStruct.X;
255 FControlPoints[FMouseIndex].y = MouseStruct.Y;
256 Draw();
257 }
258 }
259
260 void Form_OnMouseUp(VL_Base * Sender,VLS_MouseStruct MouseStruct)
261 {
262 /* 接触控制 */
263 FMouseIndex =- 1 ;
264 }
265 };
266
267 void main()
268 {
269 new MyForm();
270 GetApplication() -> Run();
271 }