1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_extensions.hxx"
26 #include <grid.hrc>
27 #include <cstdio>
28 #include <math.h> // for M_LN10 and M_E
29
30 #define _USE_MATH_DEFINES
31 #include <cmath>
32 #undef _USE_MATH_DEFINES
33
34 #include <grid.hxx>
35
36 // for ::std::sort
37 #include <algorithm>
38
39 ResId SaneResId( sal_uInt32 );
40
41 /***********************************************************************
42 *
43 * GridWindow
44 *
45 ***********************************************************************/
46
47 // ---------------------------------------------------------------------
48
GridWindow(double * pXValues,double * pYValues,int nValues,Window * pParent,sal_Bool bCutValues)49 GridWindow::GridWindow(double* pXValues, double* pYValues, int nValues, Window* pParent, sal_Bool bCutValues )
50 : ModalDialog( pParent, SaneResId( GRID_DIALOG ) ),
51 m_aGridArea( 50, 15, 100, 100 ),
52 m_pXValues( pXValues ),
53 m_pOrigYValues( pYValues ),
54 m_nValues( nValues ),
55 m_pNewYValues( NULL ),
56 m_bCutValues( bCutValues ),
57 m_aHandles(),
58 m_nDragIndex( 0xffffffff ),
59 m_aMarkerBitmap( Bitmap( SaneResId( GRID_DIALOG_HANDLE_BMP ) ), Color( 255, 255, 255 ) ),
60 m_aOKButton( this, SaneResId( GRID_DIALOG_OK_BTN ) ),
61 m_aCancelButton( this, SaneResId( GRID_DIALOG_CANCEL_BTN ) ),
62 m_aResetTypeBox( this, SaneResId( GRID_DIALOG_TYPE_BOX ) ),
63 m_aResetButton( this, SaneResId( GRID_DIALOG_RESET_BTN ) )
64 {
65 sal_uInt16 nPos = m_aResetTypeBox.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_ASCENDING ) ) );
66 m_aResetTypeBox.SetEntryData( nPos, (void *)RESET_TYPE_LINEAR_ASCENDING );
67
68 nPos = m_aResetTypeBox.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_DESCENDING ) ) );
69 m_aResetTypeBox.SetEntryData( nPos, (void *)RESET_TYPE_LINEAR_DESCENDING );
70
71 nPos = m_aResetTypeBox.InsertEntry( String( SaneResId( RESET_TYPE_RESET ) ) );
72 m_aResetTypeBox.SetEntryData( nPos, (void *)RESET_TYPE_RESET );
73
74 nPos = m_aResetTypeBox.InsertEntry( String( SaneResId( RESET_TYPE_EXPONENTIAL ) ) );
75 m_aResetTypeBox.SetEntryData( nPos, (void *)RESET_TYPE_EXPONENTIAL );
76
77 m_aResetTypeBox.SelectEntryPos( 0 );
78
79 m_aResetButton.SetClickHdl( LINK( this, GridWindow, ClickButtonHdl ) );
80
81 SetMapMode( MapMode( MAP_PIXEL ) );
82 Size aSize = GetOutputSizePixel();
83 Size aBtnSize = m_aOKButton.GetOutputSizePixel();
84 m_aGridArea.setWidth( aSize.Width() - aBtnSize.Width() - 80 );
85 m_aGridArea.setHeight( aSize.Height() - 40 );
86
87 if( m_pOrigYValues && m_nValues )
88 {
89 m_pNewYValues = new double[ m_nValues ];
90 memcpy( m_pNewYValues, m_pOrigYValues, sizeof( double ) * m_nValues );
91 }
92
93 setBoundings( 0, 0, 1023, 1023 );
94 computeExtremes();
95
96 // create left and right marker as first and last entry
97 m_BmOffX = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Width() >> 1);
98 m_BmOffY = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Height() >> 1);
99 m_aHandles.push_back(impHandle(transform(findMinX(), findMinY()), m_BmOffX, m_BmOffY));
100 m_aHandles.push_back(impHandle(transform(findMaxX(), findMaxY()), m_BmOffX, m_BmOffY));
101
102 FreeResource();
103 }
104
105 // ---------------------------------------------------------------------
106
~GridWindow()107 GridWindow::~GridWindow()
108 {
109 if( m_pNewYValues )
110 delete [] m_pNewYValues;
111 }
112
113 // ---------------------------------------------------------------------
114
findMinX()115 double GridWindow::findMinX()
116 {
117 if( ! m_pXValues )
118 return 0.0;
119 double fMin = m_pXValues[0];
120 for( int i = 1; i < m_nValues; i++ )
121 if( m_pXValues[ i ] < fMin )
122 fMin = m_pXValues[ i ];
123 return fMin;
124 }
125
126 // ---------------------------------------------------------------------
127
findMinY()128 double GridWindow::findMinY()
129 {
130 if( ! m_pNewYValues )
131 return 0.0;
132 double fMin = m_pNewYValues[0];
133 for( int i = 1; i < m_nValues; i++ )
134 if( m_pNewYValues[ i ] < fMin )
135 fMin = m_pNewYValues[ i ];
136 return fMin;
137 }
138
139 // ---------------------------------------------------------------------
140
findMaxX()141 double GridWindow::findMaxX()
142 {
143 if( ! m_pXValues )
144 return 0.0;
145 double fMax = m_pXValues[0];
146 for( int i = 1; i < m_nValues; i++ )
147 if( m_pXValues[ i ] > fMax )
148 fMax = m_pXValues[ i ];
149 return fMax;
150 }
151
152 // ---------------------------------------------------------------------
153
findMaxY()154 double GridWindow::findMaxY()
155 {
156 if( ! m_pNewYValues )
157 return 0.0;
158 double fMax = m_pNewYValues[0];
159 for( int i = 1; i < m_nValues; i++ )
160 if( m_pNewYValues[ i ] > fMax )
161 fMax = m_pNewYValues[ i ];
162 return fMax;
163 }
164
165 // ---------------------------------------------------------------------
166
computeExtremes()167 void GridWindow::computeExtremes()
168 {
169 if( m_nValues && m_pXValues && m_pOrigYValues )
170 {
171 m_fMaxX = m_fMinX = m_pXValues[0];
172 m_fMaxY = m_fMinY = m_pOrigYValues[0];
173 for( int i = 1; i < m_nValues; i++ )
174 {
175 if( m_pXValues[ i ] > m_fMaxX )
176 m_fMaxX = m_pXValues[ i ];
177 else if( m_pXValues[ i ] < m_fMinX )
178 m_fMinX = m_pXValues[ i ];
179 if( m_pOrigYValues[ i ] > m_fMaxY )
180 m_fMaxY = m_pOrigYValues[ i ];
181 else if( m_pOrigYValues[ i ] < m_fMinY )
182 m_fMinY = m_pOrigYValues[ i ];
183 }
184 setBoundings( m_fMinX, m_fMinY, m_fMaxX, m_fMaxY );
185 }
186 }
187
188 // ---------------------------------------------------------------------
189
transform(double x,double y)190 Point GridWindow::transform( double x, double y )
191 {
192 Point aRet;
193
194 aRet.X() = (long)( ( x - m_fMinX ) *
195 (double)m_aGridArea.GetWidth() / ( m_fMaxX - m_fMinX )
196 + m_aGridArea.Left() );
197 aRet.Y() = (long)(
198 m_aGridArea.Bottom() -
199 ( y - m_fMinY ) *
200 (double)m_aGridArea.GetHeight() / ( m_fMaxY - m_fMinY ) );
201 return aRet;
202 }
203
204 // ---------------------------------------------------------------------
205
transform(const Point & rOriginal,double & x,double & y)206 void GridWindow::transform( const Point& rOriginal, double& x, double& y )
207 {
208 x = ( rOriginal.X() - m_aGridArea.Left() ) * (m_fMaxX - m_fMinX) / (double)m_aGridArea.GetWidth() + m_fMinX;
209 y = ( m_aGridArea.Bottom() - rOriginal.Y() ) * (m_fMaxY - m_fMinY) / (double)m_aGridArea.GetHeight() + m_fMinY;
210 }
211
212 // ---------------------------------------------------------------------
213
drawLine(double x1,double y1,double x2,double y2)214 void GridWindow::drawLine( double x1, double y1, double x2, double y2 )
215 {
216 DrawLine( transform( x1, y1 ), transform( x2, y2 ) );
217 }
218
219 // ---------------------------------------------------------------------
220
computeChunk(double fMin,double fMax,double & fChunkOut,double & fMinChunkOut)221 void GridWindow::computeChunk( double fMin, double fMax, double& fChunkOut, double& fMinChunkOut )
222 {
223 // get a nice chunk size like 10, 100, 25 or such
224 fChunkOut = ( fMax - fMin ) / 6.0;
225 int logchunk = (int)std::log10( fChunkOut );
226 int nChunk = (int)( fChunkOut / std::exp( (double)(logchunk-1) * M_LN10 ) );
227 if( nChunk >= 75 )
228 nChunk = 100;
229 else if( nChunk >= 35 )
230 nChunk = 50;
231 else if ( nChunk > 20 )
232 nChunk = 25;
233 else if ( nChunk >= 13 )
234 nChunk = 20;
235 else if( nChunk > 5 )
236 nChunk = 10;
237 else
238 nChunk = 5;
239 fChunkOut = (double) nChunk * exp( (double)(logchunk-1) * M_LN10 );
240 // compute whole chunks fitting into fMin
241 nChunk = (int)( fMin / fChunkOut );
242 fMinChunkOut = (double)nChunk * fChunkOut;
243 while( fMinChunkOut < fMin )
244 fMinChunkOut += fChunkOut;
245 }
246
247 // ---------------------------------------------------------------------
248
computeNew()249 void GridWindow::computeNew()
250 {
251 if(2L == m_aHandles.size())
252 {
253 // special case: only left and right markers
254 double xleft, yleft;
255 double xright, yright;
256 transform(m_aHandles[0L].maPos, xleft, yleft);
257 transform(m_aHandles[1L].maPos, xright, yright );
258 double factor = (yright-yleft)/(xright-xleft);
259 for( int i = 0; i < m_nValues; i++ )
260 {
261 m_pNewYValues[ i ] = yleft + ( m_pXValues[ i ] - xleft )*factor;
262 }
263 }
264 else
265 {
266 // sort markers
267 std::sort(m_aHandles.begin(), m_aHandles.end());
268 const int nSorted = m_aHandles.size();
269 int i;
270
271 // get node arrays
272 double* nodex = new double[ nSorted ];
273 double* nodey = new double[ nSorted ];
274
275 for( i = 0L; i < nSorted; i++ )
276 transform( m_aHandles[i].maPos, nodex[ i ], nodey[ i ] );
277
278 for( i = 0; i < m_nValues; i++ )
279 {
280 double x = m_pXValues[ i ];
281 m_pNewYValues[ i ] = interpolate( x, nodex, nodey, nSorted );
282 if( m_bCutValues )
283 {
284 if( m_pNewYValues[ i ] > m_fMaxY )
285 m_pNewYValues[ i ] = m_fMaxY;
286 else if( m_pNewYValues[ i ] < m_fMinY )
287 m_pNewYValues[ i ] = m_fMinY;
288 }
289 }
290
291 delete [] nodex;
292 delete [] nodey;
293 }
294 }
295
296 // ---------------------------------------------------------------------
297
interpolate(double x,double * pNodeX,double * pNodeY,int nNodes)298 double GridWindow::interpolate(
299 double x,
300 double* pNodeX,
301 double* pNodeY,
302 int nNodes )
303 {
304 // compute Lagrange interpolation
305 double ret = 0;
306 for( int i = 0; i < nNodes; i++ )
307 {
308 double sum = pNodeY[ i ];
309 for( int n = 0; n < nNodes; n++ )
310 {
311 if( n != i )
312 {
313 sum *= x - pNodeX[ n ];
314 sum /= pNodeX[ i ] - pNodeX[ n ];
315 }
316 }
317 ret += sum;
318 }
319 return ret;
320 }
321
322 // ---------------------------------------------------------------------
323
setBoundings(double fMinX,double fMinY,double fMaxX,double fMaxY)324 void GridWindow::setBoundings( double fMinX, double fMinY, double fMaxX, double fMaxY )
325 {
326 m_fMinX = fMinX;
327 m_fMinY = fMinY;
328 m_fMaxX = fMaxX;
329 m_fMaxY = fMaxY;
330
331 computeChunk( m_fMinX, m_fMaxX, m_fChunkX, m_fMinChunkX );
332 computeChunk( m_fMinY, m_fMaxY, m_fChunkY, m_fMinChunkY );
333 }
334
335 // ---------------------------------------------------------------------
336
drawGrid()337 void GridWindow::drawGrid()
338 {
339 char pBuf[256];
340 SetLineColor( Color( COL_BLACK ) );
341 // draw vertical lines
342 for( double fX = m_fMinChunkX; fX < m_fMaxX; fX += m_fChunkX )
343 {
344 drawLine( fX, m_fMinY, fX, m_fMaxY );
345 // draw tickmarks
346 Point aPt = transform( fX, m_fMinY );
347 std::sprintf( pBuf, "%g", fX );
348 String aMark( pBuf, gsl_getSystemTextEncoding() );
349 Size aTextSize( GetTextWidth( aMark ), GetTextHeight() );
350 aPt.X() -= aTextSize.Width()/2;
351 aPt.Y() += aTextSize.Height()/2;
352 DrawText( aPt, aMark );
353 }
354 // draw horizontal lines
355 for( double fY = m_fMinChunkY; fY < m_fMaxY; fY += m_fChunkY )
356 {
357 drawLine( m_fMinX, fY, m_fMaxX, fY );
358 // draw tickmarks
359 Point aPt = transform( m_fMinX, fY );
360 std::sprintf( pBuf, "%g", fY );
361 String aMark( pBuf, gsl_getSystemTextEncoding() );
362 Size aTextSize( GetTextWidth( aMark ), GetTextHeight() );
363 aPt.X() -= aTextSize.Width() + 2;
364 aPt.Y() -= aTextSize.Height()/2;
365 DrawText( aPt, aMark );
366 }
367
368 // draw boundings
369 drawLine( m_fMinX, m_fMinY, m_fMaxX, m_fMinY );
370 drawLine( m_fMinX, m_fMaxY, m_fMaxX, m_fMaxY );
371 drawLine( m_fMinX, m_fMinY, m_fMinX, m_fMaxY );
372 drawLine( m_fMaxX, m_fMinY, m_fMaxX, m_fMaxY );
373 }
374
375 // ---------------------------------------------------------------------
376
drawOriginal()377 void GridWindow::drawOriginal()
378 {
379 if( m_nValues && m_pXValues && m_pOrigYValues )
380 {
381 SetLineColor( Color( COL_RED ) );
382 for( int i = 0; i < m_nValues-1; i++ )
383 {
384 drawLine( m_pXValues[ i ], m_pOrigYValues[ i ],
385 m_pXValues[ i+1 ], m_pOrigYValues[ i+1 ] );
386 }
387 }
388 }
389
390 // ---------------------------------------------------------------------
391
drawNew()392 void GridWindow::drawNew()
393 {
394 if( m_nValues && m_pXValues && m_pNewYValues )
395 {
396 SetClipRegion( m_aGridArea );
397 SetLineColor( Color( COL_YELLOW ) );
398 for( int i = 0; i < m_nValues-1; i++ )
399 {
400 drawLine( m_pXValues[ i ], m_pNewYValues[ i ],
401 m_pXValues[ i+1 ], m_pNewYValues[ i+1 ] );
402 }
403 SetClipRegion();
404 }
405 }
406
407 // ---------------------------------------------------------------------
408
drawHandles()409 void GridWindow::drawHandles()
410 {
411 for(sal_uInt32 i(0L); i < m_aHandles.size(); i++)
412 {
413 m_aHandles[i].draw(*this, m_aMarkerBitmap);
414 }
415 }
416
417 // ---------------------------------------------------------------------
418
Paint(const Rectangle & rRect)419 void GridWindow::Paint( const Rectangle& rRect )
420 {
421 ModalDialog::Paint( rRect );
422 drawGrid();
423 drawOriginal();
424 drawNew();
425 drawHandles();
426 }
427
428 // ---------------------------------------------------------------------
429
MouseMove(const MouseEvent & rEvt)430 void GridWindow::MouseMove( const MouseEvent& rEvt )
431 {
432 if( rEvt.GetButtons() == MOUSE_LEFT && m_nDragIndex != 0xffffffff )
433 {
434 Point aPoint( rEvt.GetPosPixel() );
435
436 if( m_nDragIndex == 0L || m_nDragIndex == m_aHandles.size() - 1L)
437 {
438 aPoint.X() = m_aHandles[m_nDragIndex].maPos.X();
439 }
440 else
441 {
442 if(aPoint.X() < m_aGridArea.Left())
443 aPoint.X() = m_aGridArea.Left();
444 else if(aPoint.X() > m_aGridArea.Right())
445 aPoint.X() = m_aGridArea.Right();
446 }
447
448 if( aPoint.Y() < m_aGridArea.Top() )
449 aPoint.Y() = m_aGridArea.Top();
450 else if( aPoint.Y() > m_aGridArea.Bottom() )
451 aPoint.Y() = m_aGridArea.Bottom();
452
453 if( aPoint != m_aHandles[m_nDragIndex].maPos )
454 {
455 m_aHandles[m_nDragIndex].maPos = aPoint;
456 Invalidate( m_aGridArea );
457 }
458 }
459
460 ModalDialog::MouseMove( rEvt );
461 }
462
463 // ---------------------------------------------------------------------
464
MouseButtonUp(const MouseEvent & rEvt)465 void GridWindow::MouseButtonUp( const MouseEvent& rEvt )
466 {
467 if( rEvt.GetButtons() == MOUSE_LEFT )
468 {
469 if( m_nDragIndex != 0xffffffff )
470 {
471 m_nDragIndex = 0xffffffff;
472 computeNew();
473 Invalidate( m_aGridArea );
474 Paint( m_aGridArea );
475 }
476 }
477
478 ModalDialog::MouseButtonUp( rEvt );
479 }
480
481 // ---------------------------------------------------------------------
482
MouseButtonDown(const MouseEvent & rEvt)483 void GridWindow::MouseButtonDown( const MouseEvent& rEvt )
484 {
485 Point aPoint( rEvt.GetPosPixel() );
486 sal_uInt32 nMarkerIndex = 0xffffffff;
487
488 for(sal_uInt32 a(0L); nMarkerIndex == 0xffffffff && a < m_aHandles.size(); a++)
489 {
490 if(m_aHandles[a].isHit(*this, aPoint))
491 {
492 nMarkerIndex = a;
493 }
494 }
495
496 if( rEvt.GetButtons() == MOUSE_LEFT )
497 {
498 // user wants to drag a button
499 if( nMarkerIndex != 0xffffffff )
500 {
501 m_nDragIndex = nMarkerIndex;
502 }
503 }
504 else if( rEvt.GetButtons() == MOUSE_RIGHT )
505 {
506 // user wants to add/delete a button
507 if( nMarkerIndex != 0xffffffff )
508 {
509 if( nMarkerIndex != 0L && nMarkerIndex != m_aHandles.size() - 1L)
510 {
511 // delete marker under mouse
512 if( m_nDragIndex == nMarkerIndex )
513 m_nDragIndex = 0xffffffff;
514
515 m_aHandles.erase(m_aHandles.begin() + nMarkerIndex);
516 }
517 }
518 else
519 {
520 m_BmOffX = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Width() >> 1);
521 m_BmOffY = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Height() >> 1);
522 m_aHandles.push_back(impHandle(aPoint, m_BmOffX, m_BmOffY));
523 }
524
525 computeNew();
526 Invalidate( m_aGridArea );
527 Paint( m_aGridArea );
528 }
529
530 ModalDialog::MouseButtonDown( rEvt );
531 }
532
533 // ---------------------------------------------------------------------
534
IMPL_LINK(GridWindow,ClickButtonHdl,Button *,pButton)535 IMPL_LINK( GridWindow, ClickButtonHdl, Button*, pButton )
536 {
537 if( pButton == &m_aResetButton )
538 {
539 int nType = (int)(sal_IntPtr)m_aResetTypeBox.GetEntryData( m_aResetTypeBox.GetSelectEntryPos() );
540 switch( nType )
541 {
542 case RESET_TYPE_LINEAR_ASCENDING:
543 {
544 for( int i = 0; i < m_nValues; i++ )
545 {
546 m_pNewYValues[ i ] = m_fMinY + (m_fMaxY-m_fMinY)/(m_fMaxX-m_fMinX)*(m_pXValues[i]-m_fMinX);
547 }
548 }
549 break;
550 case RESET_TYPE_LINEAR_DESCENDING:
551 {
552 for( int i = 0; i < m_nValues; i++ )
553 {
554 m_pNewYValues[ i ] = m_fMaxY - (m_fMaxY-m_fMinY)/(m_fMaxX-m_fMinX)*(m_pXValues[i]-m_fMinX);
555 }
556 }
557 break;
558 case RESET_TYPE_RESET:
559 {
560 if( m_pOrigYValues && m_pNewYValues && m_nValues )
561 memcpy( m_pNewYValues, m_pOrigYValues, m_nValues*sizeof(double) );
562 }
563 break;
564 case RESET_TYPE_EXPONENTIAL:
565 {
566 for( int i = 0; i < m_nValues; i++ )
567 {
568 m_pNewYValues[ i ] = m_fMinY + (m_fMaxY-m_fMinY)*(std::exp((m_pXValues[i]-m_fMinX)/(m_fMaxX-m_fMinX))-1.0)/(M_E-1.0);
569 }
570 }
571 break;
572
573 default:
574 break;
575 }
576
577 for(sal_uInt32 i(0L); i < m_aHandles.size(); i++)
578 {
579 // find nearest xvalue
580 double x, y;
581 transform( m_aHandles[i].maPos, x, y );
582 int nIndex = 0;
583 double delta = std::fabs( x-m_pXValues[0] );
584 for( int n = 1; n < m_nValues; n++ )
585 {
586 if( delta > std::fabs( x - m_pXValues[ n ] ) )
587 {
588 delta = std::fabs( x - m_pXValues[ n ] );
589 nIndex = n;
590 }
591 }
592 if( 0 == i )
593 m_aHandles[i].maPos = transform( m_fMinX, m_pNewYValues[ nIndex ] );
594 else if( m_aHandles.size() - 1L == i )
595 m_aHandles[i].maPos = transform( m_fMaxX, m_pNewYValues[ nIndex ] );
596 else
597 m_aHandles[i].maPos = transform( m_pXValues[ nIndex ], m_pNewYValues[ nIndex ] );
598 }
599
600 Invalidate( m_aGridArea );
601 Paint(Rectangle());
602 }
603 return 0;
604 }
605