nux-1.16.0
|
00001 /* 00002 * Copyright 2010 Inalogic® Inc. 00003 * 00004 * This program is free software: you can redistribute it and/or modify it 00005 * under the terms of the GNU Lesser General Public License, as 00006 * published by the Free Software Foundation; either version 2.1 or 3.0 00007 * of the License. 00008 * 00009 * This program is distributed in the hope that it will be useful, but 00010 * WITHOUT ANY WARRANTY; without even the implied warranties of 00011 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 00012 * PURPOSE. See the applicable version of the GNU Lesser General Public 00013 * License for more details. 00014 * 00015 * You should have received a copy of both the GNU Lesser General Public 00016 * License along with this program. If not, see <http://www.gnu.org/licenses/> 00017 * 00018 * Authored by: Jay Taoko <jaytaoko@inalogic.com> 00019 * 00020 */ 00021 00022 00023 #include "Nux.h" 00024 00025 #include "NuxCore/Math/Spline.h" 00026 00027 #include "NuxGraphics/GpuDevice.h" 00028 #include "NuxGraphics/GLDeviceObjects.h" 00029 #include "NuxGraphics/GLSh_DrawFunction.h" 00030 00031 #include "SplineCurveEditor.h" 00032 00033 namespace nux 00034 { 00035 00036 static const int KNOT_SIZE = 2; 00037 static const int KNOT_HIT_TEST = 4; 00038 static const int GRAPH_MARGIN = 2; 00039 00040 unsigned long CTRL_KEY = 0; 00041 // todo InitWidget 00042 SplineCurveEditor::SplineCurveEditor (NUX_FILE_LINE_DECL) 00043 : View (NUX_FILE_LINE_PARAM) 00044 { 00045 m_minX = 0.0f; 00046 m_minY = 0.0f; 00047 m_maxX = 1.0f; 00048 m_maxY = 1.0f; 00049 m_FunctionCallback = 0; 00050 m_Background = 0; 00051 00052 InitializeLayout(); 00053 InitializeWidgets(); 00054 00055 m_control_knot.Reset(); 00056 m_CubicSpline.Set (m_control_knot.GetNumKnot(), m_control_knot.GetXArray(), m_control_knot.GetYArray() ); 00057 00058 00059 m_Texture = GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableDeviceTexture (256, 4, 0, BITFMT_R8G8B8A8); 00060 m_DrawFunctionShader = new GLSh_DrawFunction(); 00061 00062 NTextureData image; 00063 MakeCheckBoardImage (image.GetSurface (0), 64, 64, Color (0xff323232), Color (0xff535353), 8, 8); 00064 BaseTexture* CheckboardPattern = GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture (); 00065 CheckboardPattern->Update(&image); 00066 00067 TexCoordXForm texxform; 00068 texxform.SetTexCoordType (TexCoordXForm::OFFSET_COORD); 00069 texxform.SetWrap (TEXWRAP_REPEAT, TEXWRAP_REPEAT); 00070 m_Background = new TextureLayer (CheckboardPattern->GetDeviceTexture(), texxform, color::White); 00071 00072 CheckboardPattern->UnReference ();; 00073 // m_Background = PaintLayer(m_CheckboardPattern); 00074 // m_Background.SetTileTexture(true); 00075 } 00076 00077 SplineCurveEditor::~SplineCurveEditor() 00078 { 00079 NUX_SAFE_DELETE (m_DrawFunctionShader); 00080 NUX_SAFE_DELETE (m_Background); 00081 } 00082 00083 void SplineCurveEditor::InitializeWidgets() 00084 { 00085 00086 } 00087 00088 void SplineCurveEditor::InitializeLayout() 00089 { 00090 SetMinimumSize (200, 200); 00091 mouse_down.connect (sigc::mem_fun (this, &SplineCurveEditor::RecvMouseDown) ); 00092 mouse_up.connect (sigc::mem_fun (this, &SplineCurveEditor::RecvMouseUp) ); 00093 mouse_drag.connect (sigc::mem_fun (this, &SplineCurveEditor::RecvMouseDrag) ); 00094 key_down.connect (sigc::mem_fun (this, &SplineCurveEditor::RecvKeyEvent) ); 00095 } 00096 00097 void SplineCurveEditor::SetControlPoints (const SplineKnot &splineKnot) 00098 { 00099 m_control_knot.Reset(); 00100 m_control_knot = splineKnot; 00101 m_CubicSpline.Set (m_control_knot.GetNumKnot(), m_control_knot.GetXArray(), m_control_knot.GetYArray() ); 00102 } 00103 00104 const SplineKnot &SplineCurveEditor::GetControlPoints() const 00105 { 00106 return m_control_knot; 00107 } 00108 00109 void SplineCurveEditor::AddKnot (double x, double y, bool selected) 00110 { 00111 m_control_knot.AddKnot (x, y, selected); 00112 m_CubicSpline.Set (m_control_knot.GetNumKnot(), m_control_knot.GetXArray(), m_control_knot.GetYArray() ); 00113 sigCurveChange.emit (this); 00114 QueueDraw(); 00115 } 00116 00117 void SplineCurveEditor::Reset() 00118 { 00119 m_control_knot.Reset(); 00120 } 00121 00122 double SplineCurveEditor::Eval (double t) 00123 { 00124 double val; 00125 int nbKnot = m_control_knot.GetNumKnot(); 00126 00127 if (nbKnot <= 1) 00128 return 0.0; 00129 00130 if (t < m_control_knot[0].GetX() ) 00131 val = m_control_knot[0].GetY(); 00132 else if (t > m_control_knot[nbKnot-1].GetX() ) 00133 val = m_control_knot[nbKnot-1].GetY(); 00134 else 00135 val = m_CubicSpline.Eval (t); 00136 00137 if (val > m_maxY) 00138 val = m_maxY; 00139 00140 if (val < m_minY) 00141 val = m_minY; 00142 00143 return val; 00144 } 00145 00146 long SplineCurveEditor::ProcessEvent (IEvent &ievent, long TraverseInfo, long ProcessEventInfo) 00147 { 00148 long ret = TraverseInfo; 00149 00150 if (ievent.e_event == NUX_MOUSE_PRESSED) 00151 { 00152 if (!GetGeometry().IsPointInside (ievent.e_x, ievent.e_y) ) 00153 { 00154 //return TraverseInfo; 00155 } 00156 } 00157 00158 CTRL_KEY = ievent.GetVirtualKeyState (NUX_VK_LCONTROL); 00159 ret = PostProcessEvent2 (ievent, ret, ProcessEventInfo); 00160 return ret; 00161 } 00162 00163 00164 void SplineCurveEditor::Draw (GraphicsEngine &GfxContext, bool force_draw) 00165 { 00166 Geometry base = GetGeometry(); 00167 00168 GetPainter().PaintBackground (GfxContext, base); 00169 GetPainter().Paint2DQuadColor (GfxContext, base, Color (COLOR_BACKGROUND_PRIMARY) ); 00170 00171 base.OffsetPosition (GRAPH_MARGIN, GRAPH_MARGIN); 00172 base.OffsetSize (-2 * GRAPH_MARGIN, -2 * GRAPH_MARGIN); 00173 00174 int i; 00175 int nsample = base.GetWidth(); 00176 int nbKnot = m_control_knot.GetNumKnot(); 00177 00178 if (1) 00179 { 00180 double *t = 0; 00181 double *y = 0; 00182 00183 if (nbKnot > 1) 00184 { 00185 t = new double[nbKnot]; 00186 y = new double[nbKnot]; 00187 } 00188 00189 std::vector<double> a; 00190 std::vector<double> b; 00191 00192 if (nbKnot > 1) 00193 { 00194 for (i = 0; i < nbKnot; i++) 00195 { 00196 t[i] = m_control_knot[i].GetX(); 00197 y[i] = m_control_knot[i].GetY(); 00198 a.push_back (m_control_knot[i].GetX() ); 00199 b.push_back (m_control_knot[i].GetY() ); 00200 } 00201 } 00202 00203 int W = GetBaseWidth() - 2 * GRAPH_MARGIN; 00204 int H = GetBaseHeight() - 2 * GRAPH_MARGIN; 00205 int X = GetBaseX() + GRAPH_MARGIN; 00206 int Y = GetBaseY() + GRAPH_MARGIN; 00207 00208 double tval_prev, val_prev; 00209 double tval, val; 00210 tval_prev = 0.0; 00211 00212 val_prev = Eval (tval_prev); 00213 00214 GetPainter().PushDrawLayer (GfxContext, base, m_Background); 00215 GetPainter().PopBackground(); 00216 00217 GfxContext.PushClippingRectangle (base); 00218 00219 GfxContext.GetRenderStates().EnableLineSmooth (TRUE, 1, GL_FASTEST); 00220 GfxContext.GetRenderStates().SetBlend (TRUE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00221 GfxContext.GetRenderStates().SetColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); 00222 00223 if (nbKnot <= 1) 00224 GetPainter().Draw2DLine (GfxContext, X, Y + H - 1, X + W, Y + H - 1, Color (0xFFFFFFFF) ); 00225 else 00226 { 00227 float tex_dx = (m_maxX - m_minX) / m_Texture->GetWidth(); 00228 SURFACE_LOCKED_RECT lockrect; 00229 m_Texture->LockRect (0, &lockrect, 0); 00230 BYTE *dest = (BYTE *) lockrect.pBits; 00231 00232 for (t_s32 i = 0; i < m_Texture->GetWidth(); i++) 00233 { 00234 float y = m_CubicSpline.Eval (m_minX + i * tex_dx); 00235 y = (y - m_minY) / (m_maxY - m_minY); 00236 00237 for (t_s32 j = 0; j < m_Texture->GetHeight(); j++) 00238 { 00239 dest[4*i + 0 + j *lockrect.Pitch] = 255 * Clamp<float> (y, 0.0f, 1.0f); 00240 dest[4*i + 1 + j *lockrect.Pitch] = 255 * Clamp<float> (y, 0.0f, 1.0f); 00241 dest[4*i + 2 + j *lockrect.Pitch] = 255 * Clamp<float> (y, 0.0f, 1.0f); 00242 dest[4*i + 3 + j *lockrect.Pitch] = 255 * Clamp<float> (y, 0.0f, 1.0f); 00243 } 00244 } 00245 00246 m_Texture->UnlockRect (0); 00247 00248 m_DrawFunctionShader->SetTextureFunction (m_Texture); 00249 m_DrawFunctionShader->SetBackgroundColor (Color(0.1f, 0.1f, 0.1f, 0.6f)); 00250 m_DrawFunctionShader->Render (X, Y, 0, W, H, GfxContext.GetWindowWidth(), GfxContext.GetWindowHeight() ); 00251 //m_DrawFunctionShader->End(); 00252 00253 for ( int i = 1; i < nsample; i++ ) 00254 { 00255 tval = ( double ) ( i ) / ( double ) ( nsample - 1 ); 00256 val = m_CubicSpline.Eval (tval); 00257 00258 if (val > m_maxY) 00259 val = m_maxY; 00260 00261 if (val <= m_minY) 00262 val = m_minY + 1.0f / H; 00263 00264 int X0, Y0, X1, Y1; 00265 X0 = X + W * (tval_prev - m_minX) / (m_maxX - m_minX); 00266 Y0 = Y + H * ( 1 - (val_prev - m_minY) / (m_maxY - m_minY) ); 00267 X1 = X + W * (tval - m_minX) / (m_maxX - m_minX); 00268 Y1 = Y + H * ( 1 - (val - m_minY) / (m_maxY - m_minY) ); 00269 00270 GetPainter().Draw2DLine (GfxContext, X0, Y0, X1, Y1, Color (0xFFFFFFFF) ); 00271 00272 tval_prev = tval; 00273 val_prev = val; 00274 } 00275 00276 } 00277 00278 GfxContext.GetRenderStates().EnableLineSmooth (GL_FALSE); 00279 GfxContext.GetRenderStates().SetBlend (GL_FALSE); 00280 GfxContext.GetRenderStates().SetColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 00281 00282 for (i = 0; i < nbKnot; i++) 00283 { 00284 int X0, Y0; 00285 X0 = X + W * (m_control_knot[i].GetX() - m_minX) / (m_maxX - m_minX); 00286 Y0 = Y + H * ( 1 - (m_control_knot[i].GetY() - m_minY) / (m_maxY - m_minY) ); 00287 00288 Geometry ShapeGeo = GetTheme().GetImageGeometry (eDOT6x6); 00289 00290 if (m_control_knot.isKnotSelected (i) ) 00291 { 00292 GetPainter().PaintShape (GfxContext, 00293 Geometry (X0 - ShapeGeo.GetWidth() / 2, Y0 - ShapeGeo.GetHeight() / 2, ShapeGeo.GetWidth(), ShapeGeo.GetHeight() ), 00294 Color (0xFF44FF44), eDOT6x6); 00295 } 00296 else 00297 { 00298 GetPainter().PaintShape (GfxContext, 00299 Geometry (X0 - ShapeGeo.GetWidth() / 2, Y0 - ShapeGeo.GetHeight() / 2, ShapeGeo.GetWidth(), ShapeGeo.GetHeight() ), 00300 Color (0xFFFFFFFF), eDOT6x6); 00301 00302 } 00303 } 00304 00305 GfxContext.PopClippingRectangle(); 00306 00307 delete[] t; 00308 delete[] y; 00309 } 00310 00311 // We do some ajustment here because when a knot is at one of the border, we still want to see the entire knot and not have the wireframe 00312 // square draw itself over it. 00313 GetPainter().Paint2DQuadWireframe (GfxContext, Geometry (base.x - KNOT_SIZE, 00314 base.y - KNOT_SIZE, 00315 base.GetWidth() + 2 * KNOT_SIZE, 00316 base.GetHeight() + 2 * KNOT_SIZE), Color (0xFF000000) ); 00317 } 00318 00319 00320 void SplineCurveEditor::DrawContent (GraphicsEngine &GfxContext, bool force_draw) 00321 { 00322 00323 } 00324 00325 void SplineCurveEditor::PostDraw (GraphicsEngine &GfxContext, bool force_draw) 00326 { 00327 00328 } 00329 00330 00331 void SplineCurveEditor::SetXAxisBounds (float minX, float maxX) 00332 { 00333 m_minX = minX; 00334 m_maxX = maxX; 00335 QueueDraw(); 00336 } 00337 00338 void SplineCurveEditor::SetYAxisBounds (float minY, float maxY) 00339 { 00340 m_minY = minY; 00341 m_maxY = maxY; 00342 QueueDraw(); 00343 } 00344 00345 void SplineCurveEditor::SetFunctionCallback (SplineFunctionCallback f) 00346 { 00347 m_FunctionCallback = f; 00348 QueueDraw(); 00349 } 00350 00351 float SplineCurveEditor::EvalFunction (float x) 00352 { 00353 if (m_FunctionCallback != 0) 00354 return (*m_FunctionCallback) (x); 00355 00356 return 0; 00357 } 00358 00359 void SplineCurveEditor::UpdateGraph() 00360 { 00361 QueueDraw(); 00362 } 00363 00364 00365 // check if a value lies within a closed interval 00366 #ifndef INSIDE_BOUNDS 00367 #define INSIDE_BOUNDS( x, lo, hi ) ( (x) >= (lo) && (x) <= (hi) ) 00368 #endif 00369 00370 //check if a 2D point lies within a 2D box 00371 #ifndef PT_INSIDE_BOX 00372 #define PT_INSIDE_BOX( x, y, lo_x, hi_x, lo_y, hi_y ) ( INSIDE_BOUNDS(x,lo_x,hi_x) && INSIDE_BOUNDS(y,lo_y,hi_y) ) 00373 #endif 00374 00375 void SplineCurveEditor::RecvMouseUp (int x, int y, unsigned long button_flags, unsigned long key_flags) 00376 { 00377 QueueDraw(); 00378 00379 if (m_control_knot.GetNumSelectedKnot() > 0) 00380 sigCurveChange.emit (this); 00381 } 00382 00383 void SplineCurveEditor::RecvMouseDown (int x, int y, unsigned long button_flags, unsigned long key_flags) 00384 { 00385 int nbKnot = m_control_knot.GetNumKnot(); 00386 00387 if (CTRL_KEY == 0) 00388 { 00389 m_control_knot.UnSelectAllKnot(); 00390 } 00391 00392 int W = GetBaseWidth() - 2 * GRAPH_MARGIN; 00393 int H = GetBaseHeight() - 2 * GRAPH_MARGIN; 00394 int X = GetBaseX() + GRAPH_MARGIN; 00395 int Y = GetBaseY() + GRAPH_MARGIN; 00396 00397 bool b = PT_INSIDE_BOX (X - GRAPH_MARGIN + x, Y - GRAPH_MARGIN + y, X - GRAPH_MARGIN, X + W + 2 * GRAPH_MARGIN, Y - GRAPH_MARGIN, Y + H + 2 * GRAPH_MARGIN); 00398 00399 if (b == false) 00400 return; 00401 00402 X = GetBaseX(); 00403 Y = GetBaseY(); 00404 00405 double new_x, new_y; 00406 new_x = (double) (x - GRAPH_MARGIN) / (double) W; 00407 new_y = 1.0 - (double) (y - GRAPH_MARGIN) / (double) H; 00408 00409 hit_point_dx = 0; 00410 hit_point_dy = 0; 00411 00412 for (int i = 0; i < nbKnot; i++) 00413 { 00414 int Xp, Yp; 00415 Xp = X + W * (m_control_knot[i].GetX() - m_minX) / (m_maxX - m_minX); 00416 Yp = Y + H * ( 1 - (m_control_knot[i].GetY() - m_minY) / (m_maxY - m_minY) ); 00417 00418 int tx, ty; 00419 tx = X + x - GRAPH_MARGIN; 00420 ty = Y + y - GRAPH_MARGIN; 00421 00422 if (PT_INSIDE_BOX (tx, ty, Xp - KNOT_HIT_TEST, Xp + KNOT_HIT_TEST, Yp - KNOT_HIT_TEST, Yp + KNOT_HIT_TEST) ) 00423 { 00424 hit_point_dx = (tx) - Xp; 00425 hit_point_dy = Yp - (ty); 00426 00427 m_control_knot.SelectKnot (i, TRUE); 00428 m_CubicSpline.Set (m_control_knot.GetNumKnot(), m_control_knot.GetXArray(), m_control_knot.GetYArray() ); 00429 QueueDraw(); 00430 return; 00431 } 00432 } 00433 00434 double eval = Eval (new_x); 00435 00436 if (new_y >= eval - 4.0 / H && (new_y <= eval + 4.0 / H) ) 00437 { 00438 m_control_knot.AddKnot (new_x, new_y, TRUE); 00439 m_CubicSpline.Set (m_control_knot.GetNumKnot(), m_control_knot.GetXArray(), m_control_knot.GetYArray() ); 00440 sigCurveChange.emit (this); 00441 QueueDraw(); 00442 } 00443 } 00444 00445 void SplineCurveEditor::RecvMouseDrag (int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) 00446 { 00447 float xp, yp; 00448 int W = GetBaseWidth() - 2 * GRAPH_MARGIN; 00449 int H = GetBaseHeight() - 2 * GRAPH_MARGIN; 00450 int X = GetBaseX() + GRAPH_MARGIN; 00451 int Y = GetBaseY() + GRAPH_MARGIN; 00452 int nbKnot = m_control_knot.GetNumKnot(); 00453 00454 if (nbKnot <= 1) 00455 return; // no drag when there is not enough knots. 00456 00457 xp = m_minX + (m_maxX - m_minX) * dx / W; 00458 yp = m_minY + (m_maxY - m_minY) * dy / H; 00459 00460 00461 00462 for (int i = 0; i < nbKnot; i++) 00463 { 00464 if (m_control_knot.isKnotSelected (i) ) 00465 { 00466 int tempx, tempy; 00467 tempx = X + x - GRAPH_MARGIN; 00468 tempy = Y + y - GRAPH_MARGIN; 00469 00470 int Xp, Yp; 00471 Xp = X + W * (m_control_knot[i].GetX() - m_minX) / (m_maxX - m_minX); 00472 Yp = Y + H * ( 1 - (m_control_knot[i].GetY() - m_minY) / (m_maxY - m_minY) ); 00473 00474 if (dx > 0) 00475 { 00476 if (tempx > Xp + hit_point_dx) 00477 { 00478 xp = /*m_minX +*/ (m_maxX - m_minX) * (tempx - Xp - hit_point_dx) / W; 00479 } 00480 else 00481 { 00482 xp = 0; 00483 } 00484 } 00485 else if (dx < 0) 00486 { 00487 if (tempx < Xp + hit_point_dx) 00488 { 00489 xp = /*m_minX +*/ (m_maxX - m_minX) * (tempx - Xp - hit_point_dx) / W; 00490 } 00491 else 00492 { 00493 xp = 0; 00494 } 00495 } 00496 00497 if (dy > 0) 00498 { 00499 if (tempy > Yp - hit_point_dy) 00500 { 00501 yp = /*m_minY +*/ (m_maxY - m_minY) * (tempy - Yp + hit_point_dy) / H; 00502 } 00503 else 00504 { 00505 yp = 0; 00506 } 00507 } 00508 else if (dy < 0) 00509 { 00510 if (tempy < Yp - hit_point_dy) 00511 { 00512 yp = /*m_minY +*/ (m_maxY - m_minY) * (tempy - Yp + hit_point_dy) / H; 00513 } 00514 else 00515 { 00516 yp = 0; 00517 } 00518 } 00519 00520 m_control_knot[i].SetX (m_control_knot[i].GetX() + xp); 00521 m_control_knot[i].SetY (m_control_knot[i].GetY() - yp); 00522 00523 if (m_control_knot[i].GetX() < m_minX) 00524 { 00525 m_control_knot[i].SetX (m_minX); 00526 } 00527 00528 if (m_control_knot[i].GetX() > m_maxX) 00529 { 00530 m_control_knot[i].SetX (m_maxX); 00531 } 00532 00533 if (m_control_knot[i].GetY() < m_minY) 00534 { 00535 m_control_knot[i].SetY (m_minY); 00536 } 00537 00538 if (m_control_knot[i].GetY() > m_maxY) 00539 { 00540 m_control_knot[i].SetY (m_maxY); 00541 } 00542 00543 00544 if (i == 0) 00545 { 00546 if ( m_control_knot[i].GetX() > m_control_knot[i + 1].GetX() - 0.01) 00547 m_control_knot[i].SetX (m_control_knot[i + 1].GetX() - 0.01); 00548 } 00549 else if (i == nbKnot - 1) 00550 { 00551 if (m_control_knot[i].GetX() < m_control_knot[i - 1].GetX() + 0.01) 00552 { 00553 m_control_knot[i].SetX (m_control_knot[i - 1].GetX() + 0.01); 00554 } 00555 } 00556 else 00557 { 00558 if (m_control_knot[i].GetX() > m_control_knot[i + 1].GetX() - 0.01) 00559 { 00560 m_control_knot[i].SetX (m_control_knot[i + 1].GetX() - 0.01); 00561 } 00562 00563 if (m_control_knot[i].GetX() < m_control_knot[i - 1].GetX() + 0.01) 00564 { 00565 m_control_knot[i].SetX (m_control_knot[i - 1].GetX() + 0.01); 00566 } 00567 } 00568 00569 break; 00570 } 00571 } 00572 00573 m_CubicSpline.Set (m_control_knot.GetNumKnot(), m_control_knot.GetXArray(), m_control_knot.GetYArray() ); 00574 QueueDraw(); 00575 } 00576 00577 void SplineCurveEditor::RecvKeyEvent ( 00578 unsigned long eventType , /*event type*/ 00579 unsigned long keysym , /*event keysym*/ 00580 unsigned long state , /*event state*/ 00581 const TCHAR* character , /*character*/ 00582 unsigned short keyCount /*key repeat count*/ 00583 ) 00584 { 00585 if (!HasKeyboardFocus() ) 00586 { 00587 return; 00588 } 00589 00590 if ( (keysym == NUX_VK_DELETE) || (keysym == NUX_VK_DELETE) || (keysym == NUX_VK_BACKSPACE) ) 00591 { 00592 00593 int kn = m_control_knot.GetNumSelectedKnot(); 00594 00595 m_control_knot.EraseSelectedKnot(); 00596 m_CubicSpline.Set (m_control_knot.GetNumKnot(), m_control_knot.GetXArray(), m_control_knot.GetYArray() ); 00597 00598 if (kn > 0) 00599 sigCurveChange.emit (this); 00600 00601 QueueDraw(); 00602 } 00603 } 00604 00605 00606 }