1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
21 #include <osl/thread.h>
24 #include <boost/math/special_functions/expm1.hpp>
29 #include <vcl/builderfactory.hxx>
32 #include <boost/scoped_array.hpp>
34 class GridWindow
: public vcl::Window
36 // helper class for handles
43 impHandle(const Point
& rPos
, sal_uInt16 nX
, sal_uInt16 nY
)
44 : maPos(rPos
), mnOffX(nX
), mnOffY(nY
)
48 bool operator<(const impHandle
& rComp
) const
50 return (maPos
.X() < rComp
.maPos
.X());
53 void draw(vcl::RenderContext
& rRenderContext
, const BitmapEx
& rBitmapEx
)
55 const Point
aOffset(rRenderContext
.PixelToLogic(Point(mnOffX
, mnOffY
)));
56 rRenderContext
.DrawBitmapEx(maPos
- aOffset
, rBitmapEx
);
59 bool isHit(vcl::Window
& rWin
, const Point
& rPos
)
61 const Point
aOffset(rWin
.PixelToLogic(Point(mnOffX
, mnOffY
)));
62 const Rectangle
aTarget(maPos
- aOffset
, maPos
+ aOffset
);
63 return aTarget
.IsInside(rPos
);
67 Rectangle m_aGridArea
;
80 double* m_pOrigYValues
;
82 double* m_pNewYValues
;
90 std::vector
< impHandle
> m_aHandles
;
91 sal_uInt32 m_nDragIndex
;
93 BitmapEx m_aMarkerBitmap
;
95 Point
transform( double x
, double y
);
96 void transform( const Point
& rOriginal
, double& x
, double& y
);
103 void drawGrid(vcl::RenderContext
& rRenderContext
);
104 void drawOriginal(vcl::RenderContext
& rRenderContext
);
105 void drawNew(vcl::RenderContext
& rRenderContext
);
106 void drawHandles(vcl::RenderContext
& rRenderContext
);
108 void computeExtremes();
109 static void computeChunk( double fMin
, double fMax
, double& fChunkOut
, double& fMinChunkOut
);
111 static double interpolate( double x
, double* pNodeX
, double* pNodeY
, int nNodes
);
113 virtual void MouseMove( const MouseEvent
& ) SAL_OVERRIDE
;
114 virtual void MouseButtonDown( const MouseEvent
& ) SAL_OVERRIDE
;
115 virtual void MouseButtonUp( const MouseEvent
& ) SAL_OVERRIDE
;
117 virtual void Resize() SAL_OVERRIDE
;
118 virtual Size
GetOptimalSize() const SAL_OVERRIDE
;
119 void drawLine(vcl::RenderContext
& rRenderContext
, double x1
, double y1
, double x2
, double y2
);
121 GridWindow(vcl::Window
* pParent
);
122 void Init(double* pXValues
, double* pYValues
, int nValues
, bool bCutValues
, const BitmapEx
&rMarkerBitmap
);
123 virtual ~GridWindow();
124 virtual void dispose() SAL_OVERRIDE
;
126 void setBoundings( double fMinX
, double fMinY
, double fMaxX
, double fMaxY
);
128 double* getNewYValues() { return m_pNewYValues
; }
130 void ChangeMode(int nType
);
132 virtual void Paint( vcl::RenderContext
& /*rRenderContext*/, const Rectangle
& rRect
) SAL_OVERRIDE
;
135 GridWindow::GridWindow(vcl::Window
* pParent
)
137 , m_aGridArea(50, 15, 100, 100)
147 , m_pOrigYValues(NULL
)
149 , m_pNewYValues(NULL
)
152 , m_bCutValues(false)
154 , m_nDragIndex(0xffffffff)
156 SetMapMode(MapMode(MAP_PIXEL
));
159 void GridWindow::Init(double* pXValues
, double* pYValues
, int nValues
, bool bCutValues
, const BitmapEx
&rMarkerBitmap
)
161 m_aMarkerBitmap
= rMarkerBitmap
;
162 m_pXValues
= pXValues
;
163 m_pOrigYValues
= pYValues
;
165 m_bCutValues
= bCutValues
;
167 SetSizePixel(GetOptimalSize());
170 if (m_pOrigYValues
&& m_nValues
)
172 m_pNewYValues
= new double[ m_nValues
];
173 memcpy( m_pNewYValues
, m_pOrigYValues
, sizeof( double ) * m_nValues
);
176 setBoundings( 0, 0, 1023, 1023 );
179 // create left and right marker as first and last entry
180 m_BmOffX
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Width() >> 1);
181 m_BmOffY
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Height() >> 1);
182 m_aHandles
.push_back(impHandle(transform(findMinX(), findMinY()), m_BmOffX
, m_BmOffY
));
183 m_aHandles
.push_back(impHandle(transform(findMaxX(), findMaxY()), m_BmOffX
, m_BmOffY
));
186 void GridWindow::Resize()
191 void GridWindow::onResize()
193 Size aSize
= GetSizePixel();
194 m_aGridArea
.setWidth( aSize
.Width() - 80 );
195 m_aGridArea
.setHeight( aSize
.Height() - 40 );
198 Size
GridWindow::GetOptimalSize() const
200 return LogicToPixel(Size(240, 200), MAP_APPFONT
);
203 GridDialog::GridDialog(double* pXValues
, double* pYValues
, int nValues
, vcl::Window
* pParent
, bool bCutValues
)
204 : ModalDialog(pParent
, "GridDialog", "modules/scanner/ui/griddialog.ui")
206 get(m_pOKButton
, "ok");
207 get(m_pResetTypeBox
, "resetTypeCombobox");
208 get(m_pResetButton
, "resetButton");
209 get(m_pGridWindow
, "gridwindow");
210 m_pGridWindow
->Init(pXValues
, pYValues
, nValues
, bCutValues
, get
<FixedImage
>("handle")->GetImage().GetBitmapEx());
212 m_pResetTypeBox
->SelectEntryPos( 0 );
214 m_pResetButton
->SetClickHdl( LINK( this, GridDialog
, ClickButtonHdl
) );
217 GridDialog::~GridDialog()
222 void GridDialog::dispose()
225 m_pResetTypeBox
.clear();
226 m_pResetButton
.clear();
227 m_pGridWindow
.clear();
228 ModalDialog::dispose();
231 GridWindow::~GridWindow()
236 void GridWindow::dispose()
238 delete [] m_pNewYValues
;
239 vcl::Window::dispose();
242 double GridWindow::findMinX()
246 double fMin
= m_pXValues
[0];
247 for( int i
= 1; i
< m_nValues
; i
++ )
248 if( m_pXValues
[ i
] < fMin
)
249 fMin
= m_pXValues
[ i
];
253 double GridWindow::findMinY()
255 if( ! m_pNewYValues
)
257 double fMin
= m_pNewYValues
[0];
258 for( int i
= 1; i
< m_nValues
; i
++ )
259 if( m_pNewYValues
[ i
] < fMin
)
260 fMin
= m_pNewYValues
[ i
];
266 double GridWindow::findMaxX()
270 double fMax
= m_pXValues
[0];
271 for( int i
= 1; i
< m_nValues
; i
++ )
272 if( m_pXValues
[ i
] > fMax
)
273 fMax
= m_pXValues
[ i
];
279 double GridWindow::findMaxY()
281 if( ! m_pNewYValues
)
283 double fMax
= m_pNewYValues
[0];
284 for( int i
= 1; i
< m_nValues
; i
++ )
285 if( m_pNewYValues
[ i
] > fMax
)
286 fMax
= m_pNewYValues
[ i
];
292 void GridWindow::computeExtremes()
294 if( m_nValues
&& m_pXValues
&& m_pOrigYValues
)
296 m_fMaxX
= m_fMinX
= m_pXValues
[0];
297 m_fMaxY
= m_fMinY
= m_pOrigYValues
[0];
298 for( int i
= 1; i
< m_nValues
; i
++ )
300 if( m_pXValues
[ i
] > m_fMaxX
)
301 m_fMaxX
= m_pXValues
[ i
];
302 else if( m_pXValues
[ i
] < m_fMinX
)
303 m_fMinX
= m_pXValues
[ i
];
304 if( m_pOrigYValues
[ i
] > m_fMaxY
)
305 m_fMaxY
= m_pOrigYValues
[ i
];
306 else if( m_pOrigYValues
[ i
] < m_fMinY
)
307 m_fMinY
= m_pOrigYValues
[ i
];
309 setBoundings( m_fMinX
, m_fMinY
, m_fMaxX
, m_fMaxY
);
315 Point
GridWindow::transform( double x
, double y
)
319 aRet
.X() = (long)( ( x
- m_fMinX
) *
320 (double)m_aGridArea
.GetWidth() / ( m_fMaxX
- m_fMinX
)
321 + m_aGridArea
.Left() );
323 m_aGridArea
.Bottom() -
325 (double)m_aGridArea
.GetHeight() / ( m_fMaxY
- m_fMinY
) );
329 void GridWindow::transform( const Point
& rOriginal
, double& x
, double& y
)
331 const long nWidth
= m_aGridArea
.GetWidth();
332 const long nHeight
= m_aGridArea
.GetHeight();
333 if (!nWidth
|| !nHeight
)
335 x
= ( rOriginal
.X() - m_aGridArea
.Left() ) * (m_fMaxX
- m_fMinX
) / (double)nWidth
+ m_fMinX
;
336 y
= ( m_aGridArea
.Bottom() - rOriginal
.Y() ) * (m_fMaxY
- m_fMinY
) / (double)nHeight
+ m_fMinY
;
339 void GridWindow::drawLine(vcl::RenderContext
& rRenderContext
, double x1
, double y1
, double x2
, double y2
)
341 rRenderContext
.DrawLine(transform(x1
, y1
), transform(x2
, y2
));
344 void GridWindow::computeChunk( double fMin
, double fMax
, double& fChunkOut
, double& fMinChunkOut
)
346 // get a nice chunk size like 10, 100, 25 or such
347 fChunkOut
= ( fMax
- fMin
) / 6.0;
348 int logchunk
= (int)std::log10( fChunkOut
);
349 int nChunk
= (int)( fChunkOut
/ std::exp( (double)(logchunk
-1) * M_LN10
) );
352 else if( nChunk
>= 35 )
354 else if ( nChunk
> 20 )
356 else if ( nChunk
>= 13 )
358 else if( nChunk
> 5 )
362 fChunkOut
= (double) nChunk
* exp( (double)(logchunk
-1) * M_LN10
);
363 // compute whole chunks fitting into fMin
364 nChunk
= (int)( fMin
/ fChunkOut
);
365 fMinChunkOut
= (double)nChunk
* fChunkOut
;
366 while( fMinChunkOut
< fMin
)
367 fMinChunkOut
+= fChunkOut
;
372 void GridWindow::computeNew()
374 if(2L == m_aHandles
.size())
376 // special case: only left and right markers
378 double xright
, yright
;
379 transform(m_aHandles
[0L].maPos
, xleft
, yleft
);
380 transform(m_aHandles
[1L].maPos
, xright
, yright
);
381 double factor
= (yright
-yleft
)/(xright
-xleft
);
382 for( int i
= 0; i
< m_nValues
; i
++ )
384 m_pNewYValues
[ i
] = yleft
+ ( m_pXValues
[ i
] - xleft
)*factor
;
390 std::sort(m_aHandles
.begin(), m_aHandles
.end());
391 const int nSorted
= m_aHandles
.size();
395 boost::scoped_array
<double> nodex(new double[ nSorted
]);
396 boost::scoped_array
<double> nodey(new double[ nSorted
]);
398 for( i
= 0L; i
< nSorted
; i
++ )
399 transform( m_aHandles
[i
].maPos
, nodex
[ i
], nodey
[ i
] );
401 for( i
= 0; i
< m_nValues
; i
++ )
403 double x
= m_pXValues
[ i
];
404 m_pNewYValues
[ i
] = interpolate( x
, nodex
.get(), nodey
.get(), nSorted
);
407 if( m_pNewYValues
[ i
] > m_fMaxY
)
408 m_pNewYValues
[ i
] = m_fMaxY
;
409 else if( m_pNewYValues
[ i
] < m_fMinY
)
410 m_pNewYValues
[ i
] = m_fMinY
;
418 double GridWindow::interpolate(
424 // compute Lagrange interpolation
426 for( int i
= 0; i
< nNodes
; i
++ )
428 double sum
= pNodeY
[ i
];
429 for( int n
= 0; n
< nNodes
; n
++ )
433 sum
*= x
- pNodeX
[ n
];
434 sum
/= pNodeX
[ i
] - pNodeX
[ n
];
442 void GridDialog::setBoundings(double fMinX
, double fMinY
, double fMaxX
, double fMaxY
)
444 m_pGridWindow
->setBoundings(fMinX
, fMinY
, fMaxX
, fMaxY
);
447 void GridWindow::setBoundings(double fMinX
, double fMinY
, double fMaxX
, double fMaxY
)
454 computeChunk( m_fMinX
, m_fMaxX
, m_fChunkX
, m_fMinChunkX
);
455 computeChunk( m_fMinY
, m_fMaxY
, m_fChunkY
, m_fMinChunkY
);
458 void GridWindow::drawGrid(vcl::RenderContext
& rRenderContext
)
461 rRenderContext
.SetLineColor(Color(COL_BLACK
));
462 // draw vertical lines
463 for (double fX
= m_fMinChunkX
; fX
< m_fMaxX
; fX
+= m_fChunkX
)
465 drawLine(rRenderContext
, fX
, m_fMinY
, fX
, m_fMaxY
);
467 Point aPt
= transform(fX
, m_fMinY
);
468 std::sprintf(pBuf
, "%g", fX
);
469 OUString
aMark(pBuf
, strlen(pBuf
), osl_getThreadTextEncoding());
470 Size
aTextSize(rRenderContext
.GetTextWidth(aMark
), rRenderContext
.GetTextHeight());
471 aPt
.X() -= aTextSize
.Width() / 2;
472 aPt
.Y() += aTextSize
.Height() / 2;
473 rRenderContext
.DrawText(aPt
, aMark
);
475 // draw horizontal lines
476 for (double fY
= m_fMinChunkY
; fY
< m_fMaxY
; fY
+= m_fChunkY
)
478 drawLine(rRenderContext
, m_fMinX
, fY
, m_fMaxX
, fY
);
480 Point aPt
= transform(m_fMinX
, fY
);
481 std::sprintf(pBuf
, "%g", fY
);
482 OUString
aMark(pBuf
, strlen(pBuf
), osl_getThreadTextEncoding());
483 Size
aTextSize(rRenderContext
.GetTextWidth(aMark
), rRenderContext
.GetTextHeight());
484 aPt
.X() -= aTextSize
.Width() + 2;
485 aPt
.Y() -= aTextSize
.Height() / 2;
486 rRenderContext
.DrawText(aPt
, aMark
);
490 drawLine(rRenderContext
, m_fMinX
, m_fMinY
, m_fMaxX
, m_fMinY
);
491 drawLine(rRenderContext
, m_fMinX
, m_fMaxY
, m_fMaxX
, m_fMaxY
);
492 drawLine(rRenderContext
, m_fMinX
, m_fMinY
, m_fMinX
, m_fMaxY
);
493 drawLine(rRenderContext
, m_fMaxX
, m_fMinY
, m_fMaxX
, m_fMaxY
);
496 void GridWindow::drawOriginal(vcl::RenderContext
& rRenderContext
)
498 if (m_nValues
&& m_pXValues
&& m_pOrigYValues
)
500 rRenderContext
.SetLineColor(Color(COL_RED
));
501 for (int i
= 0; i
< m_nValues
- 1; i
++)
503 drawLine(rRenderContext
,
504 m_pXValues
[i
], m_pOrigYValues
[i
],
505 m_pXValues
[i
+ 1], m_pOrigYValues
[i
+ 1]);
510 void GridWindow::drawNew(vcl::RenderContext
& rRenderContext
)
512 if (m_nValues
&& m_pXValues
&& m_pNewYValues
)
514 rRenderContext
.SetClipRegion(vcl::Region(m_aGridArea
));
515 rRenderContext
.SetLineColor(Color(COL_YELLOW
));
516 for (int i
= 0; i
< m_nValues
- 1; i
++)
518 drawLine(rRenderContext
,
519 m_pXValues
[i
], m_pNewYValues
[i
],
520 m_pXValues
[i
+ 1], m_pNewYValues
[i
+ 1]);
522 rRenderContext
.SetClipRegion();
526 void GridWindow::drawHandles(vcl::RenderContext
& rRenderContext
)
528 for(sal_uInt32
i(0L); i
< m_aHandles
.size(); i
++)
530 m_aHandles
[i
].draw(rRenderContext
, m_aMarkerBitmap
);
534 void GridWindow::Paint(vcl::RenderContext
& rRenderContext
, const Rectangle
& rRect
)
536 Window::Paint(rRenderContext
, rRect
);
537 drawGrid(rRenderContext
);
538 drawOriginal(rRenderContext
);
539 drawNew(rRenderContext
);
540 drawHandles(rRenderContext
);
543 void GridWindow::MouseMove( const MouseEvent
& rEvt
)
545 if( rEvt
.GetButtons() == MOUSE_LEFT
&& m_nDragIndex
!= 0xffffffff )
547 Point
aPoint( rEvt
.GetPosPixel() );
549 if( m_nDragIndex
== 0L || m_nDragIndex
== m_aHandles
.size() - 1L)
551 aPoint
.X() = m_aHandles
[m_nDragIndex
].maPos
.X();
555 if(aPoint
.X() < m_aGridArea
.Left())
556 aPoint
.X() = m_aGridArea
.Left();
557 else if(aPoint
.X() > m_aGridArea
.Right())
558 aPoint
.X() = m_aGridArea
.Right();
561 if( aPoint
.Y() < m_aGridArea
.Top() )
562 aPoint
.Y() = m_aGridArea
.Top();
563 else if( aPoint
.Y() > m_aGridArea
.Bottom() )
564 aPoint
.Y() = m_aGridArea
.Bottom();
566 if( aPoint
!= m_aHandles
[m_nDragIndex
].maPos
)
568 m_aHandles
[m_nDragIndex
].maPos
= aPoint
;
569 Invalidate( m_aGridArea
);
573 Window::MouseMove( rEvt
);
576 void GridWindow::MouseButtonUp( const MouseEvent
& rEvt
)
578 if( rEvt
.GetButtons() == MOUSE_LEFT
)
580 if( m_nDragIndex
!= 0xffffffff )
582 m_nDragIndex
= 0xffffffff;
584 Invalidate(m_aGridArea
);
588 Window::MouseButtonUp( rEvt
);
591 void GridWindow::MouseButtonDown( const MouseEvent
& rEvt
)
593 Point
aPoint( rEvt
.GetPosPixel() );
594 sal_uInt32 nMarkerIndex
= 0xffffffff;
596 for(sal_uInt32
a(0L); nMarkerIndex
== 0xffffffff && a
< m_aHandles
.size(); a
++)
598 if(m_aHandles
[a
].isHit(*this, aPoint
))
604 if( rEvt
.GetButtons() == MOUSE_LEFT
)
606 // user wants to drag a button
607 if( nMarkerIndex
!= 0xffffffff )
609 m_nDragIndex
= nMarkerIndex
;
612 else if( rEvt
.GetButtons() == MOUSE_RIGHT
)
614 // user wants to add/delete a button
615 if( nMarkerIndex
!= 0xffffffff )
617 if( nMarkerIndex
!= 0L && nMarkerIndex
!= m_aHandles
.size() - 1L)
619 // delete marker under mouse
620 if( m_nDragIndex
== nMarkerIndex
)
621 m_nDragIndex
= 0xffffffff;
623 m_aHandles
.erase(m_aHandles
.begin() + nMarkerIndex
);
628 m_BmOffX
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Width() >> 1);
629 m_BmOffY
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Height() >> 1);
630 m_aHandles
.push_back(impHandle(aPoint
, m_BmOffX
, m_BmOffY
));
634 Invalidate(m_aGridArea
);
637 Window::MouseButtonDown( rEvt
);
640 void GridWindow::ChangeMode(int nType
)
644 case LINEAR_ASCENDING
:
646 for( int i
= 0; i
< m_nValues
; i
++ )
648 m_pNewYValues
[ i
] = m_fMinY
+ (m_fMaxY
-m_fMinY
)/(m_fMaxX
-m_fMinX
)*(m_pXValues
[i
]-m_fMinX
);
652 case LINEAR_DESCENDING
:
654 for( int i
= 0; i
< m_nValues
; i
++ )
656 m_pNewYValues
[ i
] = m_fMaxY
- (m_fMaxY
-m_fMinY
)/(m_fMaxX
-m_fMinX
)*(m_pXValues
[i
]-m_fMinX
);
662 if( m_pOrigYValues
&& m_pNewYValues
&& m_nValues
)
663 memcpy( m_pNewYValues
, m_pOrigYValues
, m_nValues
*sizeof(double) );
668 for( int i
= 0; i
< m_nValues
; i
++ )
670 m_pNewYValues
[ i
] = m_fMinY
+ (m_fMaxY
-m_fMinY
)*(boost::math::expm1((m_pXValues
[i
]-m_fMinX
)/(m_fMaxX
-m_fMinX
)))/(M_E
-1.0);
681 for(sal_uInt32
i(0L); i
< m_aHandles
.size(); i
++)
683 // find nearest xvalue
685 transform( m_aHandles
[i
].maPos
, x
, y
);
687 double delta
= std::fabs( x
-m_pXValues
[0] );
688 for( int n
= 1; n
< m_nValues
; n
++ )
690 if( delta
> std::fabs( x
- m_pXValues
[ n
] ) )
692 delta
= std::fabs( x
- m_pXValues
[ n
] );
697 m_aHandles
[i
].maPos
= transform( m_fMinX
, m_pNewYValues
[ nIndex
] );
698 else if( m_aHandles
.size() - 1L == i
)
699 m_aHandles
[i
].maPos
= transform( m_fMaxX
, m_pNewYValues
[ nIndex
] );
701 m_aHandles
[i
].maPos
= transform( m_pXValues
[ nIndex
], m_pNewYValues
[ nIndex
] );
708 IMPL_LINK( GridDialog
, ClickButtonHdl
, Button
*, pButton
)
710 if (pButton
== m_pResetButton
)
712 int nType
= m_pResetTypeBox
->GetSelectEntryPos();
713 m_pGridWindow
->ChangeMode(nType
);
718 double* GridDialog::getNewYValues()
720 return m_pGridWindow
->getNewYValues();
723 VCL_BUILDER_FACTORY(GridWindow
)
725 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */