xref: /AOO41X/main/extensions/source/scanner/grid.cxx (revision 2a97ec55f1442d65917e8c8b82a55ab76c9ff676)
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