1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: grid.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_extensions.hxx"
35 #include <math.h> // for M_LN10 and M_E
37 #define _USE_MATH_DEFINES
39 #undef _USE_MATH_DEFINES
46 ResId
SaneResId( sal_uInt32
);
48 /***********************************************************************
52 ***********************************************************************/
54 // ---------------------------------------------------------------------
56 GridWindow::GridWindow(double* pXValues
, double* pYValues
, int nValues
, Window
* pParent
, BOOL bCutValues
)
57 : ModalDialog( pParent
, SaneResId( GRID_DIALOG
) ),
58 m_aGridArea( 50, 15, 100, 100 ),
59 m_pXValues( pXValues
),
60 m_pOrigYValues( pYValues
),
62 m_pNewYValues( NULL
),
63 m_bCutValues( bCutValues
),
65 m_nDragIndex( 0xffffffff ),
66 m_aMarkerBitmap( Bitmap( SaneResId( GRID_DIALOG_HANDLE_BMP
) ), Color( 255, 255, 255 ) ),
67 m_aOKButton( this, SaneResId( GRID_DIALOG_OK_BTN
) ),
68 m_aCancelButton( this, SaneResId( GRID_DIALOG_CANCEL_BTN
) ),
69 m_aResetTypeBox( this, SaneResId( GRID_DIALOG_TYPE_BOX
) ),
70 m_aResetButton( this, SaneResId( GRID_DIALOG_RESET_BTN
) )
72 USHORT nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_ASCENDING
) ) );
73 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_LINEAR_ASCENDING
);
75 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_DESCENDING
) ) );
76 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_LINEAR_DESCENDING
);
78 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_RESET
) ) );
79 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_RESET
);
81 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_EXPONENTIAL
) ) );
82 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_EXPONENTIAL
);
84 m_aResetTypeBox
.SelectEntryPos( 0 );
86 m_aResetButton
.SetClickHdl( LINK( this, GridWindow
, ClickButtonHdl
) );
88 SetMapMode( MapMode( MAP_PIXEL
) );
89 Size aSize
= GetOutputSizePixel();
90 Size aBtnSize
= m_aOKButton
.GetOutputSizePixel();
91 m_aGridArea
.setWidth( aSize
.Width() - aBtnSize
.Width() - 80 );
92 m_aGridArea
.setHeight( aSize
.Height() - 40 );
94 if( m_pOrigYValues
&& m_nValues
)
96 m_pNewYValues
= new double[ m_nValues
];
97 memcpy( m_pNewYValues
, m_pOrigYValues
, sizeof( double ) * m_nValues
);
100 setBoundings( 0, 0, 1023, 1023 );
103 // create left and right marker as first and last entry
104 m_BmOffX
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Width() >> 1);
105 m_BmOffY
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Height() >> 1);
106 m_aHandles
.push_back(impHandle(transform(findMinX(), findMinY()), m_BmOffX
, m_BmOffY
));
107 m_aHandles
.push_back(impHandle(transform(findMaxX(), findMaxY()), m_BmOffX
, m_BmOffY
));
112 // ---------------------------------------------------------------------
114 GridWindow::~GridWindow()
117 delete [] m_pNewYValues
;
120 // ---------------------------------------------------------------------
122 double GridWindow::findMinX()
126 double fMin
= m_pXValues
[0];
127 for( int i
= 1; i
< m_nValues
; i
++ )
128 if( m_pXValues
[ i
] < fMin
)
129 fMin
= m_pXValues
[ i
];
133 // ---------------------------------------------------------------------
135 double GridWindow::findMinY()
137 if( ! m_pNewYValues
)
139 double fMin
= m_pNewYValues
[0];
140 for( int i
= 1; i
< m_nValues
; i
++ )
141 if( m_pNewYValues
[ i
] < fMin
)
142 fMin
= m_pNewYValues
[ i
];
146 // ---------------------------------------------------------------------
148 double GridWindow::findMaxX()
152 double fMax
= m_pXValues
[0];
153 for( int i
= 1; i
< m_nValues
; i
++ )
154 if( m_pXValues
[ i
] > fMax
)
155 fMax
= m_pXValues
[ i
];
159 // ---------------------------------------------------------------------
161 double GridWindow::findMaxY()
163 if( ! m_pNewYValues
)
165 double fMax
= m_pNewYValues
[0];
166 for( int i
= 1; i
< m_nValues
; i
++ )
167 if( m_pNewYValues
[ i
] > fMax
)
168 fMax
= m_pNewYValues
[ i
];
172 // ---------------------------------------------------------------------
174 void GridWindow::computeExtremes()
176 if( m_nValues
&& m_pXValues
&& m_pOrigYValues
)
178 m_fMaxX
= m_fMinX
= m_pXValues
[0];
179 m_fMaxY
= m_fMinY
= m_pOrigYValues
[0];
180 for( int i
= 1; i
< m_nValues
; i
++ )
182 if( m_pXValues
[ i
] > m_fMaxX
)
183 m_fMaxX
= m_pXValues
[ i
];
184 else if( m_pXValues
[ i
] < m_fMinX
)
185 m_fMinX
= m_pXValues
[ i
];
186 if( m_pOrigYValues
[ i
] > m_fMaxY
)
187 m_fMaxY
= m_pOrigYValues
[ i
];
188 else if( m_pOrigYValues
[ i
] < m_fMinY
)
189 m_fMinY
= m_pOrigYValues
[ i
];
191 setBoundings( m_fMinX
, m_fMinY
, m_fMaxX
, m_fMaxY
);
195 // ---------------------------------------------------------------------
197 Point
GridWindow::transform( double x
, double y
)
201 aRet
.X() = (long)( ( x
- m_fMinX
) *
202 (double)m_aGridArea
.GetWidth() / ( m_fMaxX
- m_fMinX
)
203 + m_aGridArea
.Left() );
205 m_aGridArea
.Bottom() -
207 (double)m_aGridArea
.GetHeight() / ( m_fMaxY
- m_fMinY
) );
211 // ---------------------------------------------------------------------
213 void GridWindow::transform( const Point
& rOriginal
, double& x
, double& y
)
215 x
= ( rOriginal
.X() - m_aGridArea
.Left() ) * (m_fMaxX
- m_fMinX
) / (double)m_aGridArea
.GetWidth() + m_fMinX
;
216 y
= ( m_aGridArea
.Bottom() - rOriginal
.Y() ) * (m_fMaxY
- m_fMinY
) / (double)m_aGridArea
.GetHeight() + m_fMinY
;
219 // ---------------------------------------------------------------------
221 void GridWindow::drawLine( double x1
, double y1
, double x2
, double y2
)
223 DrawLine( transform( x1
, y1
), transform( x2
, y2
) );
226 // ---------------------------------------------------------------------
228 void GridWindow::computeChunk( double fMin
, double fMax
, double& fChunkOut
, double& fMinChunkOut
)
230 // get a nice chunk size like 10, 100, 25 or such
231 fChunkOut
= ( fMax
- fMin
) / 6.0;
232 int logchunk
= (int)std::log10( fChunkOut
);
233 int nChunk
= (int)( fChunkOut
/ std::exp( (double)(logchunk
-1) * M_LN10
) );
236 else if( nChunk
>= 35 )
238 else if ( nChunk
> 20 )
240 else if ( nChunk
>= 13 )
242 else if( nChunk
> 5 )
246 fChunkOut
= (double) nChunk
* exp( (double)(logchunk
-1) * M_LN10
);
247 // compute whole chunks fitting into fMin
248 nChunk
= (int)( fMin
/ fChunkOut
);
249 fMinChunkOut
= (double)nChunk
* fChunkOut
;
250 while( fMinChunkOut
< fMin
)
251 fMinChunkOut
+= fChunkOut
;
254 // ---------------------------------------------------------------------
256 void GridWindow::computeNew()
258 if(2L == m_aHandles
.size())
260 // special case: only left and right markers
262 double xright
, yright
;
263 transform(m_aHandles
[0L].maPos
, xleft
, yleft
);
264 transform(m_aHandles
[1L].maPos
, xright
, yright
);
265 double factor
= (yright
-yleft
)/(xright
-xleft
);
266 for( int i
= 0; i
< m_nValues
; i
++ )
268 m_pNewYValues
[ i
] = yleft
+ ( m_pXValues
[ i
] - xleft
)*factor
;
274 std::sort(m_aHandles
.begin(), m_aHandles
.end());
275 const int nSorted
= m_aHandles
.size();
279 double* nodex
= new double[ nSorted
];
280 double* nodey
= new double[ nSorted
];
282 for( i
= 0L; i
< nSorted
; i
++ )
283 transform( m_aHandles
[i
].maPos
, nodex
[ i
], nodey
[ i
] );
285 for( i
= 0; i
< m_nValues
; i
++ )
287 double x
= m_pXValues
[ i
];
288 m_pNewYValues
[ i
] = interpolate( x
, nodex
, nodey
, nSorted
);
291 if( m_pNewYValues
[ i
] > m_fMaxY
)
292 m_pNewYValues
[ i
] = m_fMaxY
;
293 else if( m_pNewYValues
[ i
] < m_fMinY
)
294 m_pNewYValues
[ i
] = m_fMinY
;
303 // ---------------------------------------------------------------------
305 double GridWindow::interpolate(
311 // compute Lagrange interpolation
313 for( int i
= 0; i
< nNodes
; i
++ )
315 double sum
= pNodeY
[ i
];
316 for( int n
= 0; n
< nNodes
; n
++ )
320 sum
*= x
- pNodeX
[ n
];
321 sum
/= pNodeX
[ i
] - pNodeX
[ n
];
329 // ---------------------------------------------------------------------
331 void GridWindow::setBoundings( double fMinX
, double fMinY
, double fMaxX
, double fMaxY
)
338 computeChunk( m_fMinX
, m_fMaxX
, m_fChunkX
, m_fMinChunkX
);
339 computeChunk( m_fMinY
, m_fMaxY
, m_fChunkY
, m_fMinChunkY
);
342 // ---------------------------------------------------------------------
344 void GridWindow::drawGrid()
347 SetLineColor( Color( COL_BLACK
) );
348 // draw vertical lines
349 for( double fX
= m_fMinChunkX
; fX
< m_fMaxX
; fX
+= m_fChunkX
)
351 drawLine( fX
, m_fMinY
, fX
, m_fMaxY
);
353 Point aPt
= transform( fX
, m_fMinY
);
354 std::sprintf( pBuf
, "%g", fX
);
355 String
aMark( pBuf
, gsl_getSystemTextEncoding() );
356 Size
aTextSize( GetTextWidth( aMark
), GetTextHeight() );
357 aPt
.X() -= aTextSize
.Width()/2;
358 aPt
.Y() += aTextSize
.Height()/2;
359 DrawText( aPt
, aMark
);
361 // draw horizontal lines
362 for( double fY
= m_fMinChunkY
; fY
< m_fMaxY
; fY
+= m_fChunkY
)
364 drawLine( m_fMinX
, fY
, m_fMaxX
, fY
);
366 Point aPt
= transform( m_fMinX
, fY
);
367 std::sprintf( pBuf
, "%g", fY
);
368 String
aMark( pBuf
, gsl_getSystemTextEncoding() );
369 Size
aTextSize( GetTextWidth( aMark
), GetTextHeight() );
370 aPt
.X() -= aTextSize
.Width() + 2;
371 aPt
.Y() -= aTextSize
.Height()/2;
372 DrawText( aPt
, aMark
);
376 drawLine( m_fMinX
, m_fMinY
, m_fMaxX
, m_fMinY
);
377 drawLine( m_fMinX
, m_fMaxY
, m_fMaxX
, m_fMaxY
);
378 drawLine( m_fMinX
, m_fMinY
, m_fMinX
, m_fMaxY
);
379 drawLine( m_fMaxX
, m_fMinY
, m_fMaxX
, m_fMaxY
);
382 // ---------------------------------------------------------------------
384 void GridWindow::drawOriginal()
386 if( m_nValues
&& m_pXValues
&& m_pOrigYValues
)
388 SetLineColor( Color( COL_RED
) );
389 for( int i
= 0; i
< m_nValues
-1; i
++ )
391 drawLine( m_pXValues
[ i
], m_pOrigYValues
[ i
],
392 m_pXValues
[ i
+1 ], m_pOrigYValues
[ i
+1 ] );
397 // ---------------------------------------------------------------------
399 void GridWindow::drawNew()
401 if( m_nValues
&& m_pXValues
&& m_pNewYValues
)
403 SetClipRegion( m_aGridArea
);
404 SetLineColor( Color( COL_YELLOW
) );
405 for( int i
= 0; i
< m_nValues
-1; i
++ )
407 drawLine( m_pXValues
[ i
], m_pNewYValues
[ i
],
408 m_pXValues
[ i
+1 ], m_pNewYValues
[ i
+1 ] );
414 // ---------------------------------------------------------------------
416 void GridWindow::drawHandles()
418 for(sal_uInt32
i(0L); i
< m_aHandles
.size(); i
++)
420 m_aHandles
[i
].draw(*this, m_aMarkerBitmap
);
424 // ---------------------------------------------------------------------
426 void GridWindow::Paint( const Rectangle
& rRect
)
428 ModalDialog::Paint( rRect
);
435 // ---------------------------------------------------------------------
437 void GridWindow::MouseMove( const MouseEvent
& rEvt
)
439 if( rEvt
.GetButtons() == MOUSE_LEFT
&& m_nDragIndex
!= 0xffffffff )
441 Point
aPoint( rEvt
.GetPosPixel() );
443 if( m_nDragIndex
== 0L || m_nDragIndex
== m_aHandles
.size() - 1L)
445 aPoint
.X() = m_aHandles
[m_nDragIndex
].maPos
.X();
449 if(aPoint
.X() < m_aGridArea
.Left())
450 aPoint
.X() = m_aGridArea
.Left();
451 else if(aPoint
.X() > m_aGridArea
.Right())
452 aPoint
.X() = m_aGridArea
.Right();
455 if( aPoint
.Y() < m_aGridArea
.Top() )
456 aPoint
.Y() = m_aGridArea
.Top();
457 else if( aPoint
.Y() > m_aGridArea
.Bottom() )
458 aPoint
.Y() = m_aGridArea
.Bottom();
460 if( aPoint
!= m_aHandles
[m_nDragIndex
].maPos
)
462 m_aHandles
[m_nDragIndex
].maPos
= aPoint
;
463 Invalidate( m_aGridArea
);
467 ModalDialog::MouseMove( rEvt
);
470 // ---------------------------------------------------------------------
472 void GridWindow::MouseButtonUp( const MouseEvent
& rEvt
)
474 if( rEvt
.GetButtons() == MOUSE_LEFT
)
476 if( m_nDragIndex
!= 0xffffffff )
478 m_nDragIndex
= 0xffffffff;
480 Invalidate( m_aGridArea
);
481 Paint( m_aGridArea
);
485 ModalDialog::MouseButtonUp( rEvt
);
488 // ---------------------------------------------------------------------
490 void GridWindow::MouseButtonDown( const MouseEvent
& rEvt
)
492 Point
aPoint( rEvt
.GetPosPixel() );
493 sal_uInt32 nMarkerIndex
= 0xffffffff;
495 for(sal_uInt32
a(0L); nMarkerIndex
== 0xffffffff && a
< m_aHandles
.size(); a
++)
497 if(m_aHandles
[a
].isHit(*this, aPoint
))
503 if( rEvt
.GetButtons() == MOUSE_LEFT
)
505 // user wants to drag a button
506 if( nMarkerIndex
!= 0xffffffff )
508 m_nDragIndex
= nMarkerIndex
;
511 else if( rEvt
.GetButtons() == MOUSE_RIGHT
)
513 // user wants to add/delete a button
514 if( nMarkerIndex
!= 0xffffffff )
516 if( nMarkerIndex
!= 0L && nMarkerIndex
!= m_aHandles
.size() - 1L)
518 // delete marker under mouse
519 if( m_nDragIndex
== nMarkerIndex
)
520 m_nDragIndex
= 0xffffffff;
522 m_aHandles
.erase(m_aHandles
.begin() + nMarkerIndex
);
527 m_BmOffX
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Width() >> 1);
528 m_BmOffY
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Height() >> 1);
529 m_aHandles
.push_back(impHandle(aPoint
, m_BmOffX
, m_BmOffY
));
533 Invalidate( m_aGridArea
);
534 Paint( m_aGridArea
);
537 ModalDialog::MouseButtonDown( rEvt
);
540 // ---------------------------------------------------------------------
542 IMPL_LINK( GridWindow
, ClickButtonHdl
, Button
*, pButton
)
544 if( pButton
== &m_aResetButton
)
546 int nType
= (int)(sal_IntPtr
)m_aResetTypeBox
.GetEntryData( m_aResetTypeBox
.GetSelectEntryPos() );
549 case RESET_TYPE_LINEAR_ASCENDING
:
551 for( int i
= 0; i
< m_nValues
; i
++ )
553 m_pNewYValues
[ i
] = m_fMinY
+ (m_fMaxY
-m_fMinY
)/(m_fMaxX
-m_fMinX
)*(m_pXValues
[i
]-m_fMinX
);
557 case RESET_TYPE_LINEAR_DESCENDING
:
559 for( int i
= 0; i
< m_nValues
; i
++ )
561 m_pNewYValues
[ i
] = m_fMaxY
- (m_fMaxY
-m_fMinY
)/(m_fMaxX
-m_fMinX
)*(m_pXValues
[i
]-m_fMinX
);
565 case RESET_TYPE_RESET
:
567 if( m_pOrigYValues
&& m_pNewYValues
&& m_nValues
)
568 memcpy( m_pNewYValues
, m_pOrigYValues
, m_nValues
*sizeof(double) );
571 case RESET_TYPE_EXPONENTIAL
:
573 for( int i
= 0; i
< m_nValues
; i
++ )
575 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);
584 for(sal_uInt32
i(0L); i
< m_aHandles
.size(); i
++)
586 // find nearest xvalue
588 transform( m_aHandles
[i
].maPos
, x
, y
);
590 double delta
= std::fabs( x
-m_pXValues
[0] );
591 for( int n
= 1; n
< m_nValues
; n
++ )
593 if( delta
> std::fabs( x
- m_pXValues
[ n
] ) )
595 delta
= std::fabs( x
- m_pXValues
[ n
] );
600 m_aHandles
[i
].maPos
= transform( m_fMinX
, m_pNewYValues
[ nIndex
] );
601 else if( m_aHandles
.size() - 1L == i
)
602 m_aHandles
[i
].maPos
= transform( m_fMaxX
, m_pNewYValues
[ nIndex
] );
604 m_aHandles
[i
].maPos
= transform( m_pXValues
[ nIndex
], m_pNewYValues
[ nIndex
] );
607 Invalidate( m_aGridArea
);