File: | home/maarten/src/libreoffice/core/extensions/source/scanner/grid.cxx |
Warning: | line 365, column 32 The left operand of '-' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |||
2 | /* | |||
3 | * This file is part of the LibreOffice project. | |||
4 | * | |||
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/. | |||
8 | * | |||
9 | * This file incorporates work covered by the following license notice: | |||
10 | * | |||
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 . | |||
18 | */ | |||
19 | ||||
20 | #include <sal/config.h> | |||
21 | #include <osl/thread.h> | |||
22 | #include <cstdio> | |||
23 | #include <math.h> | |||
24 | #include <boost/math/special_functions/expm1.hpp> | |||
25 | ||||
26 | #include <bitmaps.hlst> | |||
27 | #include <cmath> | |||
28 | ||||
29 | #include "grid.hxx" | |||
30 | #include <vcl/bitmapex.hxx> | |||
31 | #include <vcl/customweld.hxx> | |||
32 | #include <vcl/event.hxx> | |||
33 | #include <vcl/settings.hxx> | |||
34 | #include <vcl/svapp.hxx> | |||
35 | ||||
36 | #include <algorithm> | |||
37 | #include <limits> | |||
38 | #include <memory> | |||
39 | ||||
40 | class GridWindow : public weld::CustomWidgetController | |||
41 | { | |||
42 | // helper class for handles | |||
43 | struct impHandle | |||
44 | { | |||
45 | Point maPos; | |||
46 | sal_uInt16 mnOffX; | |||
47 | sal_uInt16 mnOffY; | |||
48 | ||||
49 | impHandle(const Point& rPos, sal_uInt16 nX, sal_uInt16 nY) | |||
50 | : maPos(rPos), mnOffX(nX), mnOffY(nY) | |||
51 | { | |||
52 | } | |||
53 | ||||
54 | bool operator<(const impHandle& rComp) const | |||
55 | { | |||
56 | return (maPos.X() < rComp.maPos.X()); | |||
57 | } | |||
58 | ||||
59 | void draw(vcl::RenderContext& rRenderContext, const BitmapEx& rBitmapEx) | |||
60 | { | |||
61 | const Point aOffset(rRenderContext.PixelToLogic(Point(mnOffX, mnOffY))); | |||
62 | rRenderContext.DrawBitmapEx(maPos - aOffset, rBitmapEx); | |||
63 | } | |||
64 | ||||
65 | bool isHit(OutputDevice const & rWin, const Point& rPos) | |||
66 | { | |||
67 | const Point aOffset(rWin.PixelToLogic(Point(mnOffX, mnOffY))); | |||
68 | const tools::Rectangle aTarget(maPos - aOffset, maPos + aOffset); | |||
69 | return aTarget.IsInside(rPos); | |||
70 | } | |||
71 | }; | |||
72 | ||||
73 | tools::Rectangle m_aGridArea; | |||
74 | ||||
75 | double m_fMinX; | |||
76 | double m_fMinY; | |||
77 | double m_fMaxX; | |||
78 | double m_fMaxY; | |||
79 | ||||
80 | double m_fChunkX; | |||
81 | double m_fMinChunkX; | |||
82 | double m_fChunkY; | |||
83 | double m_fMinChunkY; | |||
84 | ||||
85 | double* m_pXValues; | |||
86 | double* m_pOrigYValues; | |||
87 | int m_nValues; | |||
88 | std::unique_ptr<double[]> m_pNewYValues; | |||
89 | ||||
90 | sal_uInt16 m_BmOffX; | |||
91 | sal_uInt16 m_BmOffY; | |||
92 | ||||
93 | bool m_bCutValues; | |||
94 | ||||
95 | // stuff for handles | |||
96 | using Handles = std::vector<impHandle>; | |||
97 | static constexpr auto npos = std::numeric_limits<Handles::size_type>::max(); | |||
98 | Handles m_aHandles; | |||
99 | Handles::size_type m_nDragIndex; | |||
100 | ||||
101 | BitmapEx m_aMarkerBitmap; | |||
102 | ||||
103 | Point transform( double x, double y ); | |||
104 | void transform( const Point& rOriginal, double& x, double& y ); | |||
105 | ||||
106 | double findMinX(); | |||
107 | double findMinY(); | |||
108 | double findMaxX(); | |||
109 | double findMaxY(); | |||
110 | ||||
111 | void drawGrid(vcl::RenderContext& rRenderContext); | |||
112 | void drawOriginal(vcl::RenderContext& rRenderContext); | |||
113 | void drawNew(vcl::RenderContext& rRenderContext); | |||
114 | void drawHandles(vcl::RenderContext& rRenderContext); | |||
115 | ||||
116 | void computeExtremes(); | |||
117 | static void computeChunk( double fMin, double fMax, double& fChunkOut, double& fMinChunkOut ); | |||
118 | void computeNew(); | |||
119 | static double interpolate( double x, double const * pNodeX, double const * pNodeY, int nNodes ); | |||
120 | ||||
121 | virtual bool MouseMove( const MouseEvent& ) override; | |||
122 | virtual bool MouseButtonDown( const MouseEvent& ) override; | |||
123 | virtual bool MouseButtonUp( const MouseEvent& ) override; | |||
124 | void onResize(); | |||
125 | virtual void Resize() override; | |||
126 | virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; | |||
127 | void drawLine(vcl::RenderContext& rRenderContext, double x1, double y1, double x2, double y2); | |||
128 | public: | |||
129 | GridWindow(); | |||
130 | void Init(double* pXValues, double* pYValues, int nValues, bool bCutValues, const BitmapEx &rMarkerBitmap); | |||
131 | virtual ~GridWindow() override; | |||
132 | ||||
133 | void setBoundings( double fMinX, double fMinY, double fMaxX, double fMaxY ); | |||
134 | ||||
135 | double* getNewYValues() { return m_pNewYValues.get(); } | |||
136 | ||||
137 | void ChangeMode(ResetType nType); | |||
138 | ||||
139 | virtual void Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect ) override; | |||
140 | }; | |||
141 | ||||
142 | GridWindow::GridWindow() | |||
143 | : m_aGridArea(50, 15, 100, 100) | |||
144 | , m_fMinX(0.0) | |||
145 | , m_fMinY(0.0) | |||
146 | , m_fMaxX(0.0) | |||
147 | , m_fMaxY(0.0) | |||
148 | , m_fChunkX(0.0) | |||
149 | , m_fMinChunkX(0.0) | |||
150 | , m_fChunkY(0.0) | |||
151 | , m_fMinChunkY(0.0) | |||
152 | , m_pXValues(nullptr) | |||
153 | , m_pOrigYValues(nullptr) | |||
154 | , m_nValues(0) | |||
155 | , m_BmOffX(0) | |||
156 | , m_BmOffY(0) | |||
157 | , m_bCutValues(false) | |||
158 | , m_aHandles() | |||
159 | , m_nDragIndex(npos) | |||
160 | { | |||
161 | } | |||
162 | ||||
163 | void GridWindow::Init(double* pXValues, double* pYValues, int nValues, bool bCutValues, const BitmapEx &rMarkerBitmap) | |||
164 | { | |||
165 | m_aMarkerBitmap = rMarkerBitmap; | |||
166 | m_pXValues = pXValues; | |||
167 | m_pOrigYValues = pYValues; | |||
168 | m_nValues = nValues; | |||
169 | m_bCutValues = bCutValues; | |||
170 | ||||
171 | onResize(); | |||
172 | ||||
173 | if (m_pOrigYValues && m_nValues) | |||
174 | { | |||
175 | m_pNewYValues.reset(new double[ m_nValues ]); | |||
176 | memcpy( m_pNewYValues.get(), m_pOrigYValues, sizeof( double ) * m_nValues ); | |||
177 | } | |||
178 | ||||
179 | setBoundings( 0, 0, 1023, 1023 ); | |||
180 | computeExtremes(); | |||
181 | ||||
182 | // create left and right marker as first and last entry | |||
183 | m_BmOffX = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Width() >> 1); | |||
184 | m_BmOffY = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Height() >> 1); | |||
185 | m_aHandles.push_back(impHandle(transform(findMinX(), findMinY()), m_BmOffX, m_BmOffY)); | |||
186 | m_aHandles.push_back(impHandle(transform(findMaxX(), findMaxY()), m_BmOffX, m_BmOffY)); | |||
187 | } | |||
188 | ||||
189 | void GridWindow::Resize() | |||
190 | { | |||
191 | onResize(); | |||
192 | } | |||
193 | ||||
194 | void GridWindow::onResize() | |||
195 | { | |||
196 | Size aSize = GetOutputSizePixel(); | |||
197 | m_aGridArea.setWidth( aSize.Width() - 80 ); | |||
198 | m_aGridArea.setHeight( aSize.Height() - 40 ); | |||
199 | } | |||
200 | ||||
201 | void GridWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) | |||
202 | { | |||
203 | Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(240, 200), MapMode(MapUnit::MapAppFont))); | |||
204 | pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); | |||
205 | CustomWidgetController::SetDrawingArea(pDrawingArea); | |||
206 | SetOutputSizePixel(aSize); | |||
207 | } | |||
208 | ||||
209 | GridDialog::GridDialog(weld::Window* pParent, double* pXValues, double* pYValues, int nValues) | |||
210 | : GenericDialogController(pParent, "modules/scanner/ui/griddialog.ui", "GridDialog") | |||
211 | , m_xOKButton(m_xBuilder->weld_button("ok")) | |||
212 | , m_xResetTypeBox(m_xBuilder->weld_combo_box("resetTypeCombobox")) | |||
213 | , m_xResetButton(m_xBuilder->weld_button("resetButton")) | |||
214 | , m_xGridWindow(new GridWindow) | |||
215 | , m_xGridWindowWND(new weld::CustomWeld(*m_xBuilder, "gridwindow", *m_xGridWindow)) | |||
216 | { | |||
217 | m_xGridWindow->Init(pXValues, pYValues, nValues, true/*bCutValues*/, BitmapEx(RID_SCANNER_HANDLE"extensions/res/scanner/handle.png")); | |||
218 | m_xResetTypeBox->set_active(0); | |||
219 | m_xResetButton->connect_clicked( LINK( this, GridDialog, ClickButtonHdl )::tools::detail::makeLink( ::tools::detail::castTo<GridDialog *>(this), &GridDialog::LinkStubClickButtonHdl) ); | |||
220 | } | |||
221 | ||||
222 | GridDialog::~GridDialog() | |||
223 | { | |||
224 | } | |||
225 | ||||
226 | GridWindow::~GridWindow() | |||
227 | { | |||
228 | m_pNewYValues.reset(); | |||
229 | } | |||
230 | ||||
231 | double GridWindow::findMinX() | |||
232 | { | |||
233 | if( ! m_pXValues ) | |||
234 | return 0.0; | |||
235 | double fMin = m_pXValues[0]; | |||
236 | for( int i = 1; i < m_nValues; i++ ) | |||
237 | if( m_pXValues[ i ] < fMin ) | |||
238 | fMin = m_pXValues[ i ]; | |||
239 | return fMin; | |||
240 | } | |||
241 | ||||
242 | double GridWindow::findMinY() | |||
243 | { | |||
244 | if( ! m_pNewYValues ) | |||
245 | return 0.0; | |||
246 | double fMin = m_pNewYValues[0]; | |||
247 | for( int i = 1; i < m_nValues; i++ ) | |||
248 | if( m_pNewYValues[ i ] < fMin ) | |||
249 | fMin = m_pNewYValues[ i ]; | |||
250 | return fMin; | |||
251 | } | |||
252 | ||||
253 | ||||
254 | double GridWindow::findMaxX() | |||
255 | { | |||
256 | if( ! m_pXValues ) | |||
257 | return 0.0; | |||
258 | double fMax = m_pXValues[0]; | |||
259 | for( int i = 1; i < m_nValues; i++ ) | |||
260 | if( m_pXValues[ i ] > fMax ) | |||
261 | fMax = m_pXValues[ i ]; | |||
262 | return fMax; | |||
263 | } | |||
264 | ||||
265 | ||||
266 | double GridWindow::findMaxY() | |||
267 | { | |||
268 | if( ! m_pNewYValues ) | |||
269 | return 0.0; | |||
270 | double fMax = m_pNewYValues[0]; | |||
271 | for( int i = 1; i < m_nValues; i++ ) | |||
272 | if( m_pNewYValues[ i ] > fMax ) | |||
273 | fMax = m_pNewYValues[ i ]; | |||
274 | return fMax; | |||
275 | } | |||
276 | ||||
277 | ||||
278 | void GridWindow::computeExtremes() | |||
279 | { | |||
280 | if( !(m_nValues && m_pXValues && m_pOrigYValues) ) | |||
281 | return; | |||
282 | ||||
283 | m_fMaxX = m_fMinX = m_pXValues[0]; | |||
284 | m_fMaxY = m_fMinY = m_pOrigYValues[0]; | |||
285 | for( int i = 1; i < m_nValues; i++ ) | |||
286 | { | |||
287 | if( m_pXValues[ i ] > m_fMaxX ) | |||
288 | m_fMaxX = m_pXValues[ i ]; | |||
289 | else if( m_pXValues[ i ] < m_fMinX ) | |||
290 | m_fMinX = m_pXValues[ i ]; | |||
291 | if( m_pOrigYValues[ i ] > m_fMaxY ) | |||
292 | m_fMaxY = m_pOrigYValues[ i ]; | |||
293 | else if( m_pOrigYValues[ i ] < m_fMinY ) | |||
294 | m_fMinY = m_pOrigYValues[ i ]; | |||
295 | } | |||
296 | setBoundings( m_fMinX, m_fMinY, m_fMaxX, m_fMaxY ); | |||
297 | } | |||
298 | ||||
299 | ||||
300 | Point GridWindow::transform( double x, double y ) | |||
301 | { | |||
302 | Point aRet; | |||
303 | ||||
304 | aRet.setX( static_cast<long>( ( x - m_fMinX ) * | |||
305 | static_cast<double>(m_aGridArea.GetWidth()) / ( m_fMaxX - m_fMinX ) | |||
306 | + m_aGridArea.Left() ) ); | |||
307 | aRet.setY( static_cast<long>( | |||
308 | m_aGridArea.Bottom() - | |||
309 | ( y - m_fMinY ) * | |||
310 | static_cast<double>(m_aGridArea.GetHeight()) / ( m_fMaxY - m_fMinY ) ) ); | |||
311 | return aRet; | |||
312 | } | |||
313 | ||||
314 | void GridWindow::transform( const Point& rOriginal, double& x, double& y ) | |||
315 | { | |||
316 | const long nWidth = m_aGridArea.GetWidth(); | |||
317 | const long nHeight = m_aGridArea.GetHeight(); | |||
318 | if (!nWidth
| |||
319 | return; | |||
320 | x = ( rOriginal.X() - m_aGridArea.Left() ) * (m_fMaxX - m_fMinX) / static_cast<double>(nWidth) + m_fMinX; | |||
321 | y = ( m_aGridArea.Bottom() - rOriginal.Y() ) * (m_fMaxY - m_fMinY) / static_cast<double>(nHeight) + m_fMinY; | |||
322 | } | |||
323 | ||||
324 | void GridWindow::drawLine(vcl::RenderContext& rRenderContext, double x1, double y1, double x2, double y2 ) | |||
325 | { | |||
326 | rRenderContext.DrawLine(transform(x1, y1), transform(x2, y2)); | |||
327 | } | |||
328 | ||||
329 | void GridWindow::computeChunk( double fMin, double fMax, double& fChunkOut, double& fMinChunkOut ) | |||
330 | { | |||
331 | // get a nice chunk size like 10, 100, 25 or such | |||
332 | fChunkOut = ( fMax - fMin ) / 6.0; | |||
333 | int logchunk = static_cast<int>(std::log10( fChunkOut )); | |||
334 | int nChunk = static_cast<int>( fChunkOut / std::exp( static_cast<double>(logchunk-1) * M_LN102.30258509299404568402 ) ); | |||
335 | if( nChunk >= 75 ) | |||
336 | nChunk = 100; | |||
337 | else if( nChunk >= 35 ) | |||
338 | nChunk = 50; | |||
339 | else if ( nChunk > 20 ) | |||
340 | nChunk = 25; | |||
341 | else if ( nChunk >= 13 ) | |||
342 | nChunk = 20; | |||
343 | else if( nChunk > 5 ) | |||
344 | nChunk = 10; | |||
345 | else | |||
346 | nChunk = 5; | |||
347 | fChunkOut = static_cast<double>(nChunk) * exp( static_cast<double>(logchunk-1) * M_LN102.30258509299404568402 ); | |||
348 | // compute whole chunks fitting into fMin | |||
349 | nChunk = static_cast<int>( fMin / fChunkOut ); | |||
350 | fMinChunkOut = static_cast<double>(nChunk) * fChunkOut; | |||
351 | while( fMinChunkOut < fMin ) | |||
352 | fMinChunkOut += fChunkOut; | |||
353 | } | |||
354 | ||||
355 | ||||
356 | void GridWindow::computeNew() | |||
357 | { | |||
358 | if(2 == m_aHandles.size()) | |||
359 | { | |||
360 | // special case: only left and right markers | |||
361 | double xleft, yleft; | |||
362 | double xright, yright; | |||
363 | transform(m_aHandles[0].maPos, xleft, yleft); | |||
364 | transform(m_aHandles[1].maPos, xright, yright ); | |||
365 | double factor = (yright-yleft)/(xright-xleft); | |||
| ||||
366 | for( int i = 0; i < m_nValues; i++ ) | |||
367 | { | |||
368 | m_pNewYValues[ i ] = yleft + ( m_pXValues[ i ] - xleft )*factor; | |||
369 | } | |||
370 | } | |||
371 | else | |||
372 | { | |||
373 | // sort markers | |||
374 | std::sort(m_aHandles.begin(), m_aHandles.end()); | |||
375 | const int nSorted = m_aHandles.size(); | |||
376 | int i; | |||
377 | ||||
378 | // get node arrays | |||
379 | std::unique_ptr<double[]> nodex(new double[ nSorted ]); | |||
380 | std::unique_ptr<double[]> nodey(new double[ nSorted ]); | |||
381 | ||||
382 | for( i = 0; i < nSorted; i++ ) | |||
383 | transform( m_aHandles[i].maPos, nodex[ i ], nodey[ i ] ); | |||
384 | ||||
385 | for( i = 0; i < m_nValues; i++ ) | |||
386 | { | |||
387 | double x = m_pXValues[ i ]; | |||
388 | m_pNewYValues[ i ] = interpolate( x, nodex.get(), nodey.get(), nSorted ); | |||
389 | if( m_bCutValues ) | |||
390 | { | |||
391 | if( m_pNewYValues[ i ] > m_fMaxY ) | |||
392 | m_pNewYValues[ i ] = m_fMaxY; | |||
393 | else if( m_pNewYValues[ i ] < m_fMinY ) | |||
394 | m_pNewYValues[ i ] = m_fMinY; | |||
395 | } | |||
396 | } | |||
397 | } | |||
398 | } | |||
399 | ||||
400 | ||||
401 | double GridWindow::interpolate( | |||
402 | double x, | |||
403 | double const * pNodeX, | |||
404 | double const * pNodeY, | |||
405 | int nNodes ) | |||
406 | { | |||
407 | // compute Lagrange interpolation | |||
408 | double ret = 0; | |||
409 | for( int i = 0; i < nNodes; i++ ) | |||
410 | { | |||
411 | double sum = pNodeY[ i ]; | |||
412 | for( int n = 0; n < nNodes; n++ ) | |||
413 | { | |||
414 | if( n != i ) | |||
415 | { | |||
416 | sum *= x - pNodeX[ n ]; | |||
417 | sum /= pNodeX[ i ] - pNodeX[ n ]; | |||
418 | } | |||
419 | } | |||
420 | ret += sum; | |||
421 | } | |||
422 | return ret; | |||
423 | } | |||
424 | ||||
425 | void GridDialog::setBoundings(double fMinX, double fMinY, double fMaxX, double fMaxY) | |||
426 | { | |||
427 | m_xGridWindow->setBoundings(fMinX, fMinY, fMaxX, fMaxY); | |||
428 | } | |||
429 | ||||
430 | void GridWindow::setBoundings(double fMinX, double fMinY, double fMaxX, double fMaxY) | |||
431 | { | |||
432 | m_fMinX = fMinX; | |||
433 | m_fMinY = fMinY; | |||
434 | m_fMaxX = fMaxX; | |||
435 | m_fMaxY = fMaxY; | |||
436 | ||||
437 | computeChunk( m_fMinX, m_fMaxX, m_fChunkX, m_fMinChunkX ); | |||
438 | computeChunk( m_fMinY, m_fMaxY, m_fChunkY, m_fMinChunkY ); | |||
439 | } | |||
440 | ||||
441 | void GridWindow::drawGrid(vcl::RenderContext& rRenderContext) | |||
442 | { | |||
443 | char pBuf[256]; | |||
444 | rRenderContext.SetLineColor(COL_BLACK); | |||
445 | // draw vertical lines | |||
446 | for (double fX = m_fMinChunkX; fX < m_fMaxX; fX += m_fChunkX) | |||
447 | { | |||
448 | drawLine(rRenderContext, fX, m_fMinY, fX, m_fMaxY); | |||
449 | // draw tickmarks | |||
450 | Point aPt = transform(fX, m_fMinY); | |||
451 | std::sprintf(pBuf, "%g", fX); | |||
452 | OUString aMark(pBuf, strlen(pBuf), osl_getThreadTextEncoding()); | |||
453 | Size aTextSize(rRenderContext.GetTextWidth(aMark), rRenderContext.GetTextHeight()); | |||
454 | aPt.AdjustX( -(aTextSize.Width() / 2) ); | |||
455 | aPt.AdjustY(aTextSize.Height() / 2 ); | |||
456 | rRenderContext.DrawText(aPt, aMark); | |||
457 | } | |||
458 | // draw horizontal lines | |||
459 | for (double fY = m_fMinChunkY; fY < m_fMaxY; fY += m_fChunkY) | |||
460 | { | |||
461 | drawLine(rRenderContext, m_fMinX, fY, m_fMaxX, fY); | |||
462 | // draw tickmarks | |||
463 | Point aPt = transform(m_fMinX, fY); | |||
464 | std::sprintf(pBuf, "%g", fY); | |||
465 | OUString aMark(pBuf, strlen(pBuf), osl_getThreadTextEncoding()); | |||
466 | Size aTextSize(rRenderContext.GetTextWidth(aMark), rRenderContext.GetTextHeight()); | |||
467 | aPt.AdjustX( -(aTextSize.Width() + 2) ); | |||
468 | aPt.AdjustY( -(aTextSize.Height() / 2) ); | |||
469 | rRenderContext.DrawText(aPt, aMark); | |||
470 | } | |||
471 | ||||
472 | // draw boundings | |||
473 | drawLine(rRenderContext, m_fMinX, m_fMinY, m_fMaxX, m_fMinY); | |||
474 | drawLine(rRenderContext, m_fMinX, m_fMaxY, m_fMaxX, m_fMaxY); | |||
475 | drawLine(rRenderContext, m_fMinX, m_fMinY, m_fMinX, m_fMaxY); | |||
476 | drawLine(rRenderContext, m_fMaxX, m_fMinY, m_fMaxX, m_fMaxY); | |||
477 | } | |||
478 | ||||
479 | void GridWindow::drawOriginal(vcl::RenderContext& rRenderContext) | |||
480 | { | |||
481 | if (m_nValues && m_pXValues && m_pOrigYValues) | |||
482 | { | |||
483 | rRenderContext.SetLineColor(COL_RED); | |||
484 | for (int i = 0; i < m_nValues - 1; i++) | |||
485 | { | |||
486 | drawLine(rRenderContext, | |||
487 | m_pXValues[i], m_pOrigYValues[i], | |||
488 | m_pXValues[i + 1], m_pOrigYValues[i + 1]); | |||
489 | } | |||
490 | } | |||
491 | } | |||
492 | ||||
493 | void GridWindow::drawNew(vcl::RenderContext& rRenderContext) | |||
494 | { | |||
495 | if (m_nValues && m_pXValues && m_pNewYValues) | |||
496 | { | |||
497 | rRenderContext.SetClipRegion(vcl::Region(m_aGridArea)); | |||
498 | rRenderContext.SetLineColor(COL_YELLOW); | |||
499 | for (int i = 0; i < m_nValues - 1; i++) | |||
500 | { | |||
501 | drawLine(rRenderContext, | |||
502 | m_pXValues[i], m_pNewYValues[i], | |||
503 | m_pXValues[i + 1], m_pNewYValues[i + 1]); | |||
504 | } | |||
505 | rRenderContext.SetClipRegion(); | |||
506 | } | |||
507 | } | |||
508 | ||||
509 | void GridWindow::drawHandles(vcl::RenderContext& rRenderContext) | |||
510 | { | |||
511 | for(impHandle & rHandle : m_aHandles) | |||
512 | { | |||
513 | rHandle.draw(rRenderContext, m_aMarkerBitmap); | |||
514 | } | |||
515 | } | |||
516 | ||||
517 | void GridWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) | |||
518 | { | |||
519 | rRenderContext.SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetDialogColor())); | |||
520 | drawGrid(rRenderContext); | |||
521 | drawOriginal(rRenderContext); | |||
522 | drawNew(rRenderContext); | |||
523 | drawHandles(rRenderContext); | |||
524 | } | |||
525 | ||||
526 | bool GridWindow::MouseMove( const MouseEvent& rEvt ) | |||
527 | { | |||
528 | if( rEvt.GetButtons() == MOUSE_LEFT(sal_uInt16(0x0001)) && m_nDragIndex != npos ) | |||
529 | { | |||
530 | Point aPoint( rEvt.GetPosPixel() ); | |||
531 | ||||
532 | if( m_nDragIndex == 0 || m_nDragIndex == m_aHandles.size() - 1) | |||
533 | { | |||
534 | aPoint.setX( m_aHandles[m_nDragIndex].maPos.X() ); | |||
535 | } | |||
536 | else | |||
537 | { | |||
538 | if(aPoint.X() < m_aGridArea.Left()) | |||
539 | aPoint.setX( m_aGridArea.Left() ); | |||
540 | else if(aPoint.X() > m_aGridArea.Right()) | |||
541 | aPoint.setX( m_aGridArea.Right() ); | |||
542 | } | |||
543 | ||||
544 | if( aPoint.Y() < m_aGridArea.Top() ) | |||
545 | aPoint.setY( m_aGridArea.Top() ); | |||
546 | else if( aPoint.Y() > m_aGridArea.Bottom() ) | |||
547 | aPoint.setY( m_aGridArea.Bottom() ); | |||
548 | ||||
549 | if( aPoint != m_aHandles[m_nDragIndex].maPos ) | |||
550 | { | |||
551 | m_aHandles[m_nDragIndex].maPos = aPoint; | |||
552 | Invalidate( m_aGridArea ); | |||
553 | } | |||
554 | } | |||
555 | ||||
556 | return false; | |||
557 | } | |||
558 | ||||
559 | bool GridWindow::MouseButtonUp( const MouseEvent& rEvt ) | |||
560 | { | |||
561 | if( rEvt.GetButtons() == MOUSE_LEFT(sal_uInt16(0x0001)) ) | |||
562 | { | |||
563 | if( m_nDragIndex != npos ) | |||
564 | { | |||
565 | m_nDragIndex = npos; | |||
566 | computeNew(); | |||
567 | Invalidate(m_aGridArea); | |||
568 | } | |||
569 | } | |||
570 | ||||
571 | return false; | |||
572 | } | |||
573 | ||||
574 | bool GridWindow::MouseButtonDown( const MouseEvent& rEvt ) | |||
575 | { | |||
576 | Point aPoint( rEvt.GetPosPixel() ); | |||
577 | Handles::size_type nMarkerIndex = npos; | |||
578 | ||||
579 | for(Handles::size_type a(0); nMarkerIndex
| |||
| ||||
580 | { | |||
581 | if(m_aHandles[a].isHit(GetDrawingArea()->get_ref_device(), aPoint)) | |||
582 | { | |||
583 | nMarkerIndex = a; | |||
584 | } | |||
585 | } | |||
586 | ||||
587 | if( rEvt.GetButtons() == MOUSE_LEFT(sal_uInt16(0x0001)) ) | |||
588 | { | |||
589 | // user wants to drag a button | |||
590 | if( nMarkerIndex != npos ) | |||
591 | { | |||
592 | m_nDragIndex = nMarkerIndex; | |||
593 | } | |||
594 | } | |||
595 | else if( rEvt.GetButtons() == MOUSE_RIGHT(sal_uInt16(0x0004)) ) | |||
596 | { | |||
597 | // user wants to add/delete a button | |||
598 | if( nMarkerIndex
| |||
599 | { | |||
600 | if( nMarkerIndex != 0 && nMarkerIndex != m_aHandles.size() - 1) | |||
601 | { | |||
602 | // delete marker under mouse | |||
603 | if( m_nDragIndex == nMarkerIndex ) | |||
604 | m_nDragIndex = npos; | |||
605 | ||||
606 | m_aHandles.erase(m_aHandles.begin() + nMarkerIndex); | |||
607 | } | |||
608 | } | |||
609 | else | |||
610 | { | |||
611 | m_BmOffX = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Width() >> 1); | |||
612 | m_BmOffY = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Height() >> 1); | |||
613 | m_aHandles.push_back(impHandle(aPoint, m_BmOffX, m_BmOffY)); | |||
614 | } | |||
615 | ||||
616 | computeNew(); | |||
617 | Invalidate(m_aGridArea); | |||
618 | } | |||
619 | ||||
620 | return false; | |||
621 | } | |||
622 | ||||
623 | void GridWindow::ChangeMode(ResetType nType) | |||
624 | { | |||
625 | switch( nType ) | |||
626 | { | |||
627 | case ResetType::LINEAR_ASCENDING: | |||
628 | { | |||
629 | for( int i = 0; i < m_nValues; i++ ) | |||
630 | { | |||
631 | m_pNewYValues[ i ] = m_fMinY + (m_fMaxY-m_fMinY)/(m_fMaxX-m_fMinX)*(m_pXValues[i]-m_fMinX); | |||
632 | } | |||
633 | } | |||
634 | break; | |||
635 | case ResetType::LINEAR_DESCENDING: | |||
636 | { | |||
637 | for( int i = 0; i < m_nValues; i++ ) | |||
638 | { | |||
639 | m_pNewYValues[ i ] = m_fMaxY - (m_fMaxY-m_fMinY)/(m_fMaxX-m_fMinX)*(m_pXValues[i]-m_fMinX); | |||
640 | } | |||
641 | } | |||
642 | break; | |||
643 | case ResetType::RESET: | |||
644 | { | |||
645 | if( m_pOrigYValues && m_pNewYValues && m_nValues ) | |||
646 | memcpy( m_pNewYValues.get(), m_pOrigYValues, m_nValues*sizeof(double) ); | |||
647 | } | |||
648 | break; | |||
649 | case ResetType::EXPONENTIAL: | |||
650 | { | |||
651 | for( int i = 0; i < m_nValues; i++ ) | |||
652 | { | |||
653 | m_pNewYValues[ i ] = m_fMinY + (m_fMaxY-m_fMinY)*(boost::math::expm1((m_pXValues[i]-m_fMinX)/(m_fMaxX-m_fMinX)))/(M_E2.7182818284590452354-1.0); | |||
654 | } | |||
655 | } | |||
656 | break; | |||
657 | ||||
658 | default: | |||
659 | break; | |||
660 | } | |||
661 | ||||
662 | if (m_pNewYValues) | |||
663 | { | |||
664 | for(size_t i(0); i < m_aHandles.size(); i++) | |||
665 | { | |||
666 | // find nearest xvalue | |||
667 | double x, y; | |||
668 | transform( m_aHandles[i].maPos, x, y ); | |||
669 | int nIndex = 0; | |||
670 | double delta = std::fabs( x-m_pXValues[0] ); | |||
671 | for( int n = 1; n < m_nValues; n++ ) | |||
672 | { | |||
673 | if( delta > std::fabs( x - m_pXValues[ n ] ) ) | |||
674 | { | |||
675 | delta = std::fabs( x - m_pXValues[ n ] ); | |||
676 | nIndex = n; | |||
677 | } | |||
678 | } | |||
679 | if( 0 == i ) | |||
680 | m_aHandles[i].maPos = transform( m_fMinX, m_pNewYValues[ nIndex ] ); | |||
681 | else if( m_aHandles.size() - 1 == i ) | |||
682 | m_aHandles[i].maPos = transform( m_fMaxX, m_pNewYValues[ nIndex ] ); | |||
683 | else | |||
684 | m_aHandles[i].maPos = transform( m_pXValues[ nIndex ], m_pNewYValues[ nIndex ] ); | |||
685 | } | |||
686 | } | |||
687 | ||||
688 | Invalidate(); | |||
689 | } | |||
690 | ||||
691 | IMPL_LINK_NOARG(GridDialog, ClickButtonHdl, weld::Button&, void)void GridDialog::LinkStubClickButtonHdl(void * instance, weld ::Button& data) { return static_cast<GridDialog *>( instance)->ClickButtonHdl(data); } void GridDialog::ClickButtonHdl (__attribute__ ((unused)) weld::Button&) | |||
692 | { | |||
693 | int nType = m_xResetTypeBox->get_active(); | |||
694 | m_xGridWindow->ChangeMode(static_cast<ResetType>(nType)); | |||
695 | } | |||
696 | ||||
697 | double* GridDialog::getNewYValues() | |||
698 | { | |||
699 | return m_xGridWindow->getNewYValues(); | |||
700 | } | |||
701 | ||||
702 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |