bump product version to 4.1.6.2
[LibreOffice.git] / extensions / source / scanner / grid.cxx
blobdc017f0c3628b82d625edaa8e29d5985e02ca252
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "sal/config.h"
22 #include <grid.hrc>
23 #include <cstdio>
24 #include <math.h> // for M_LN10 and M_E
26 #include <cmath>
28 #include <grid.hxx>
30 // for ::std::sort
31 #include <algorithm>
33 ResId SaneResId( sal_uInt32 );
35 /***********************************************************************
37 * GridWindow
39 ***********************************************************************/
41 // ---------------------------------------------------------------------
43 GridWindow::GridWindow(double* pXValues, double* pYValues, int nValues, Window* pParent, sal_Bool bCutValues )
44 : ModalDialog( pParent, SaneResId( GRID_DIALOG ) ),
45 m_aGridArea( 50, 15, 100, 100 ),
46 m_pXValues( pXValues ),
47 m_pOrigYValues( pYValues ),
48 m_nValues( nValues ),
49 m_pNewYValues( NULL ),
50 m_bCutValues( bCutValues ),
51 m_aHandles(),
52 m_nDragIndex( 0xffffffff ),
53 m_aMarkerBitmap( Bitmap( SaneResId( GRID_DIALOG_HANDLE_BMP ) ), Color( 255, 255, 255 ) ),
54 m_aOKButton( this, SaneResId( GRID_DIALOG_OK_BTN ) ),
55 m_aCancelButton( this, SaneResId( GRID_DIALOG_CANCEL_BTN ) ),
56 m_aResetTypeBox( this, SaneResId( GRID_DIALOG_TYPE_BOX ) ),
57 m_aResetButton( this, SaneResId( GRID_DIALOG_RESET_BTN ) )
59 sal_uInt16 nPos = m_aResetTypeBox.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_ASCENDING ) ) );
60 m_aResetTypeBox.SetEntryData( nPos, (void *)RESET_TYPE_LINEAR_ASCENDING );
62 nPos = m_aResetTypeBox.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_DESCENDING ) ) );
63 m_aResetTypeBox.SetEntryData( nPos, (void *)RESET_TYPE_LINEAR_DESCENDING );
65 nPos = m_aResetTypeBox.InsertEntry( String( SaneResId( RESET_TYPE_RESET ) ) );
66 m_aResetTypeBox.SetEntryData( nPos, (void *)RESET_TYPE_RESET );
68 nPos = m_aResetTypeBox.InsertEntry( String( SaneResId( RESET_TYPE_EXPONENTIAL ) ) );
69 m_aResetTypeBox.SetEntryData( nPos, (void *)RESET_TYPE_EXPONENTIAL );
71 m_aResetTypeBox.SelectEntryPos( 0 );
73 m_aResetButton.SetClickHdl( LINK( this, GridWindow, ClickButtonHdl ) );
75 SetMapMode( MapMode( MAP_PIXEL ) );
76 Size aSize = GetOutputSizePixel();
77 Size aBtnSize = m_aOKButton.GetOutputSizePixel();
78 m_aGridArea.setWidth( aSize.Width() - aBtnSize.Width() - 80 );
79 m_aGridArea.setHeight( aSize.Height() - 40 );
81 if( m_pOrigYValues && m_nValues )
83 m_pNewYValues = new double[ m_nValues ];
84 memcpy( m_pNewYValues, m_pOrigYValues, sizeof( double ) * m_nValues );
87 setBoundings( 0, 0, 1023, 1023 );
88 computeExtremes();
90 // create left and right marker as first and last entry
91 m_BmOffX = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Width() >> 1);
92 m_BmOffY = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Height() >> 1);
93 m_aHandles.push_back(impHandle(transform(findMinX(), findMinY()), m_BmOffX, m_BmOffY));
94 m_aHandles.push_back(impHandle(transform(findMaxX(), findMaxY()), m_BmOffX, m_BmOffY));
96 FreeResource();
99 // ---------------------------------------------------------------------
101 GridWindow::~GridWindow()
103 if( m_pNewYValues )
104 delete [] m_pNewYValues;
107 // ---------------------------------------------------------------------
109 double GridWindow::findMinX()
111 if( ! m_pXValues )
112 return 0.0;
113 double fMin = m_pXValues[0];
114 for( int i = 1; i < m_nValues; i++ )
115 if( m_pXValues[ i ] < fMin )
116 fMin = m_pXValues[ i ];
117 return fMin;
120 // ---------------------------------------------------------------------
122 double GridWindow::findMinY()
124 if( ! m_pNewYValues )
125 return 0.0;
126 double fMin = m_pNewYValues[0];
127 for( int i = 1; i < m_nValues; i++ )
128 if( m_pNewYValues[ i ] < fMin )
129 fMin = m_pNewYValues[ i ];
130 return fMin;
133 // ---------------------------------------------------------------------
135 double GridWindow::findMaxX()
137 if( ! m_pXValues )
138 return 0.0;
139 double fMax = m_pXValues[0];
140 for( int i = 1; i < m_nValues; i++ )
141 if( m_pXValues[ i ] > fMax )
142 fMax = m_pXValues[ i ];
143 return fMax;
146 // ---------------------------------------------------------------------
148 double GridWindow::findMaxY()
150 if( ! m_pNewYValues )
151 return 0.0;
152 double fMax = m_pNewYValues[0];
153 for( int i = 1; i < m_nValues; i++ )
154 if( m_pNewYValues[ i ] > fMax )
155 fMax = m_pNewYValues[ i ];
156 return fMax;
159 // ---------------------------------------------------------------------
161 void GridWindow::computeExtremes()
163 if( m_nValues && m_pXValues && m_pOrigYValues )
165 m_fMaxX = m_fMinX = m_pXValues[0];
166 m_fMaxY = m_fMinY = m_pOrigYValues[0];
167 for( int i = 1; i < m_nValues; i++ )
169 if( m_pXValues[ i ] > m_fMaxX )
170 m_fMaxX = m_pXValues[ i ];
171 else if( m_pXValues[ i ] < m_fMinX )
172 m_fMinX = m_pXValues[ i ];
173 if( m_pOrigYValues[ i ] > m_fMaxY )
174 m_fMaxY = m_pOrigYValues[ i ];
175 else if( m_pOrigYValues[ i ] < m_fMinY )
176 m_fMinY = m_pOrigYValues[ i ];
178 setBoundings( m_fMinX, m_fMinY, m_fMaxX, m_fMaxY );
182 // ---------------------------------------------------------------------
184 Point GridWindow::transform( double x, double y )
186 Point aRet;
188 aRet.X() = (long)( ( x - m_fMinX ) *
189 (double)m_aGridArea.GetWidth() / ( m_fMaxX - m_fMinX )
190 + m_aGridArea.Left() );
191 aRet.Y() = (long)(
192 m_aGridArea.Bottom() -
193 ( y - m_fMinY ) *
194 (double)m_aGridArea.GetHeight() / ( m_fMaxY - m_fMinY ) );
195 return aRet;
198 // ---------------------------------------------------------------------
200 void GridWindow::transform( const Point& rOriginal, double& x, double& y )
202 x = ( rOriginal.X() - m_aGridArea.Left() ) * (m_fMaxX - m_fMinX) / (double)m_aGridArea.GetWidth() + m_fMinX;
203 y = ( m_aGridArea.Bottom() - rOriginal.Y() ) * (m_fMaxY - m_fMinY) / (double)m_aGridArea.GetHeight() + m_fMinY;
206 // ---------------------------------------------------------------------
208 void GridWindow::drawLine( double x1, double y1, double x2, double y2 )
210 DrawLine( transform( x1, y1 ), transform( x2, y2 ) );
213 // ---------------------------------------------------------------------
215 void GridWindow::computeChunk( double fMin, double fMax, double& fChunkOut, double& fMinChunkOut )
217 // get a nice chunk size like 10, 100, 25 or such
218 fChunkOut = ( fMax - fMin ) / 6.0;
219 int logchunk = (int)std::log10( fChunkOut );
220 int nChunk = (int)( fChunkOut / std::exp( (double)(logchunk-1) * M_LN10 ) );
221 if( nChunk >= 75 )
222 nChunk = 100;
223 else if( nChunk >= 35 )
224 nChunk = 50;
225 else if ( nChunk > 20 )
226 nChunk = 25;
227 else if ( nChunk >= 13 )
228 nChunk = 20;
229 else if( nChunk > 5 )
230 nChunk = 10;
231 else
232 nChunk = 5;
233 fChunkOut = (double) nChunk * exp( (double)(logchunk-1) * M_LN10 );
234 // compute whole chunks fitting into fMin
235 nChunk = (int)( fMin / fChunkOut );
236 fMinChunkOut = (double)nChunk * fChunkOut;
237 while( fMinChunkOut < fMin )
238 fMinChunkOut += fChunkOut;
241 // ---------------------------------------------------------------------
243 void GridWindow::computeNew()
245 if(2L == m_aHandles.size())
247 // special case: only left and right markers
248 double xleft, yleft;
249 double xright, yright;
250 transform(m_aHandles[0L].maPos, xleft, yleft);
251 transform(m_aHandles[1L].maPos, xright, yright );
252 double factor = (yright-yleft)/(xright-xleft);
253 for( int i = 0; i < m_nValues; i++ )
255 m_pNewYValues[ i ] = yleft + ( m_pXValues[ i ] - xleft )*factor;
258 else
260 // sort markers
261 std::sort(m_aHandles.begin(), m_aHandles.end());
262 const int nSorted = m_aHandles.size();
263 int i;
265 // get node arrays
266 double* nodex = new double[ nSorted ];
267 double* nodey = new double[ nSorted ];
269 for( i = 0L; i < nSorted; i++ )
270 transform( m_aHandles[i].maPos, nodex[ i ], nodey[ i ] );
272 for( i = 0; i < m_nValues; i++ )
274 double x = m_pXValues[ i ];
275 m_pNewYValues[ i ] = interpolate( x, nodex, nodey, nSorted );
276 if( m_bCutValues )
278 if( m_pNewYValues[ i ] > m_fMaxY )
279 m_pNewYValues[ i ] = m_fMaxY;
280 else if( m_pNewYValues[ i ] < m_fMinY )
281 m_pNewYValues[ i ] = m_fMinY;
285 delete [] nodex;
286 delete [] nodey;
290 // ---------------------------------------------------------------------
292 double GridWindow::interpolate(
293 double x,
294 double* pNodeX,
295 double* pNodeY,
296 int nNodes )
298 // compute Lagrange interpolation
299 double ret = 0;
300 for( int i = 0; i < nNodes; i++ )
302 double sum = pNodeY[ i ];
303 for( int n = 0; n < nNodes; n++ )
305 if( n != i )
307 sum *= x - pNodeX[ n ];
308 sum /= pNodeX[ i ] - pNodeX[ n ];
311 ret += sum;
313 return ret;
316 // ---------------------------------------------------------------------
318 void GridWindow::setBoundings( double fMinX, double fMinY, double fMaxX, double fMaxY )
320 m_fMinX = fMinX;
321 m_fMinY = fMinY;
322 m_fMaxX = fMaxX;
323 m_fMaxY = fMaxY;
325 computeChunk( m_fMinX, m_fMaxX, m_fChunkX, m_fMinChunkX );
326 computeChunk( m_fMinY, m_fMaxY, m_fChunkY, m_fMinChunkY );
329 // ---------------------------------------------------------------------
331 void GridWindow::drawGrid()
333 char pBuf[256];
334 SetLineColor( Color( COL_BLACK ) );
335 // draw vertical lines
336 for( double fX = m_fMinChunkX; fX < m_fMaxX; fX += m_fChunkX )
338 drawLine( fX, m_fMinY, fX, m_fMaxY );
339 // draw tickmarks
340 Point aPt = transform( fX, m_fMinY );
341 std::sprintf( pBuf, "%g", fX );
342 String aMark( pBuf, osl_getThreadTextEncoding() );
343 Size aTextSize( GetTextWidth( aMark ), GetTextHeight() );
344 aPt.X() -= aTextSize.Width()/2;
345 aPt.Y() += aTextSize.Height()/2;
346 DrawText( aPt, aMark );
348 // draw horizontal lines
349 for( double fY = m_fMinChunkY; fY < m_fMaxY; fY += m_fChunkY )
351 drawLine( m_fMinX, fY, m_fMaxX, fY );
352 // draw tickmarks
353 Point aPt = transform( m_fMinX, fY );
354 std::sprintf( pBuf, "%g", fY );
355 String aMark( pBuf, osl_getThreadTextEncoding() );
356 Size aTextSize( GetTextWidth( aMark ), GetTextHeight() );
357 aPt.X() -= aTextSize.Width() + 2;
358 aPt.Y() -= aTextSize.Height()/2;
359 DrawText( aPt, aMark );
362 // draw boundings
363 drawLine( m_fMinX, m_fMinY, m_fMaxX, m_fMinY );
364 drawLine( m_fMinX, m_fMaxY, m_fMaxX, m_fMaxY );
365 drawLine( m_fMinX, m_fMinY, m_fMinX, m_fMaxY );
366 drawLine( m_fMaxX, m_fMinY, m_fMaxX, m_fMaxY );
369 // ---------------------------------------------------------------------
371 void GridWindow::drawOriginal()
373 if( m_nValues && m_pXValues && m_pOrigYValues )
375 SetLineColor( Color( COL_RED ) );
376 for( int i = 0; i < m_nValues-1; i++ )
378 drawLine( m_pXValues[ i ], m_pOrigYValues[ i ],
379 m_pXValues[ i+1 ], m_pOrigYValues[ i+1 ] );
384 // ---------------------------------------------------------------------
386 void GridWindow::drawNew()
388 if( m_nValues && m_pXValues && m_pNewYValues )
390 SetClipRegion(Region(m_aGridArea));
391 SetLineColor( Color( COL_YELLOW ) );
392 for( int i = 0; i < m_nValues-1; i++ )
394 drawLine( m_pXValues[ i ], m_pNewYValues[ i ],
395 m_pXValues[ i+1 ], m_pNewYValues[ i+1 ] );
397 SetClipRegion();
401 // ---------------------------------------------------------------------
403 void GridWindow::drawHandles()
405 for(sal_uInt32 i(0L); i < m_aHandles.size(); i++)
407 m_aHandles[i].draw(*this, m_aMarkerBitmap);
411 // ---------------------------------------------------------------------
413 void GridWindow::Paint( const Rectangle& rRect )
415 ModalDialog::Paint( rRect );
416 drawGrid();
417 drawOriginal();
418 drawNew();
419 drawHandles();
422 // ---------------------------------------------------------------------
424 void GridWindow::MouseMove( const MouseEvent& rEvt )
426 if( rEvt.GetButtons() == MOUSE_LEFT && m_nDragIndex != 0xffffffff )
428 Point aPoint( rEvt.GetPosPixel() );
430 if( m_nDragIndex == 0L || m_nDragIndex == m_aHandles.size() - 1L)
432 aPoint.X() = m_aHandles[m_nDragIndex].maPos.X();
434 else
436 if(aPoint.X() < m_aGridArea.Left())
437 aPoint.X() = m_aGridArea.Left();
438 else if(aPoint.X() > m_aGridArea.Right())
439 aPoint.X() = m_aGridArea.Right();
442 if( aPoint.Y() < m_aGridArea.Top() )
443 aPoint.Y() = m_aGridArea.Top();
444 else if( aPoint.Y() > m_aGridArea.Bottom() )
445 aPoint.Y() = m_aGridArea.Bottom();
447 if( aPoint != m_aHandles[m_nDragIndex].maPos )
449 m_aHandles[m_nDragIndex].maPos = aPoint;
450 Invalidate( m_aGridArea );
454 ModalDialog::MouseMove( rEvt );
457 // ---------------------------------------------------------------------
459 void GridWindow::MouseButtonUp( const MouseEvent& rEvt )
461 if( rEvt.GetButtons() == MOUSE_LEFT )
463 if( m_nDragIndex != 0xffffffff )
465 m_nDragIndex = 0xffffffff;
466 computeNew();
467 Invalidate( m_aGridArea );
468 Paint( m_aGridArea );
472 ModalDialog::MouseButtonUp( rEvt );
475 // ---------------------------------------------------------------------
477 void GridWindow::MouseButtonDown( const MouseEvent& rEvt )
479 Point aPoint( rEvt.GetPosPixel() );
480 sal_uInt32 nMarkerIndex = 0xffffffff;
482 for(sal_uInt32 a(0L); nMarkerIndex == 0xffffffff && a < m_aHandles.size(); a++)
484 if(m_aHandles[a].isHit(*this, aPoint))
486 nMarkerIndex = a;
490 if( rEvt.GetButtons() == MOUSE_LEFT )
492 // user wants to drag a button
493 if( nMarkerIndex != 0xffffffff )
495 m_nDragIndex = nMarkerIndex;
498 else if( rEvt.GetButtons() == MOUSE_RIGHT )
500 // user wants to add/delete a button
501 if( nMarkerIndex != 0xffffffff )
503 if( nMarkerIndex != 0L && nMarkerIndex != m_aHandles.size() - 1L)
505 // delete marker under mouse
506 if( m_nDragIndex == nMarkerIndex )
507 m_nDragIndex = 0xffffffff;
509 m_aHandles.erase(m_aHandles.begin() + nMarkerIndex);
512 else
514 m_BmOffX = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Width() >> 1);
515 m_BmOffY = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Height() >> 1);
516 m_aHandles.push_back(impHandle(aPoint, m_BmOffX, m_BmOffY));
519 computeNew();
520 Invalidate( m_aGridArea );
521 Paint( m_aGridArea );
524 ModalDialog::MouseButtonDown( rEvt );
527 // ---------------------------------------------------------------------
529 IMPL_LINK( GridWindow, ClickButtonHdl, Button*, pButton )
531 if( pButton == &m_aResetButton )
533 int nType = (int)(sal_IntPtr)m_aResetTypeBox.GetEntryData( m_aResetTypeBox.GetSelectEntryPos() );
534 switch( nType )
536 case RESET_TYPE_LINEAR_ASCENDING:
538 for( int i = 0; i < m_nValues; i++ )
540 m_pNewYValues[ i ] = m_fMinY + (m_fMaxY-m_fMinY)/(m_fMaxX-m_fMinX)*(m_pXValues[i]-m_fMinX);
543 break;
544 case RESET_TYPE_LINEAR_DESCENDING:
546 for( int i = 0; i < m_nValues; i++ )
548 m_pNewYValues[ i ] = m_fMaxY - (m_fMaxY-m_fMinY)/(m_fMaxX-m_fMinX)*(m_pXValues[i]-m_fMinX);
551 break;
552 case RESET_TYPE_RESET:
554 if( m_pOrigYValues && m_pNewYValues && m_nValues )
555 memcpy( m_pNewYValues, m_pOrigYValues, m_nValues*sizeof(double) );
557 break;
558 case RESET_TYPE_EXPONENTIAL:
560 for( int i = 0; i < m_nValues; i++ )
562 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);
565 break;
567 default:
568 break;
571 for(sal_uInt32 i(0L); i < m_aHandles.size(); i++)
573 // find nearest xvalue
574 double x, y;
575 transform( m_aHandles[i].maPos, x, y );
576 int nIndex = 0;
577 double delta = std::fabs( x-m_pXValues[0] );
578 for( int n = 1; n < m_nValues; n++ )
580 if( delta > std::fabs( x - m_pXValues[ n ] ) )
582 delta = std::fabs( x - m_pXValues[ n ] );
583 nIndex = n;
586 if( 0 == i )
587 m_aHandles[i].maPos = transform( m_fMinX, m_pNewYValues[ nIndex ] );
588 else if( m_aHandles.size() - 1L == i )
589 m_aHandles[i].maPos = transform( m_fMaxX, m_pNewYValues[ nIndex ] );
590 else
591 m_aHandles[i].maPos = transform( m_pXValues[ nIndex ], m_pNewYValues[ nIndex ] );
594 Invalidate( m_aGridArea );
595 Paint(Rectangle());
597 return 0;
600 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */