source: trunk/packages/vizservers/vtkvis/Renderer.cpp @ 4171

Last change on this file since 4171 was 4171, checked in by ldelgass, 10 years ago

Use bounds of only visible objects when computing scene bounds for axis bounds/
range and 2D camera offset. This fixes a problem with contour/heightmap
sequences, where non-visible datasets can be in 3D mode while the visible
dataset is in 2D mode.

  • Property svn:eol-style set to native
File size: 157.3 KB
RevLine 
[2100]1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
[3177]3 * Copyright (C) 2004-2012  HUBzero Foundation, LLC
[2100]4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
[2112]8#include <cfloat>
[2194]9#include <cstring>
10#include <cassert>
[2317]11#include <cmath>
[2112]12
[2260]13#include <GL/gl.h>
14
15#ifdef WANT_TRACE
16#include <sys/time.h>
17#endif
18
[3818]19#ifdef USE_FONT_CONFIG
20#include <vtkFreeTypeTools.h>
21#endif
[2214]22#include <vtkMath.h>
[2100]23#include <vtkCamera.h>
[2317]24#include <vtkLight.h>
[3683]25#include <vtkLightCollection.h>
[2100]26#include <vtkCoordinate.h>
[2112]27#include <vtkTransform.h>
[2100]28#include <vtkCharArray.h>
29#ifdef USE_CUSTOM_AXES
[3164]30#include "vtkRpCubeAxesActor.h"
[2100]31#else
[3164]32#include <vtkCubeAxesActor.h>
[2100]33#endif
34#include <vtkProperty.h>
[2115]35#include <vtkProperty2D.h>
[2100]36#include <vtkPointData.h>
37#include <vtkLookupTable.h>
38#include <vtkTextProperty.h>
[2260]39#include <vtkOpenGLRenderWindow.h>
[2533]40#include <vtkVersion.h>
[2100]41
[3621]42#include "Renderer.h"
43#include "RendererGraphicsObjs.h"
[3616]44#include "Math.h"
[2123]45#include "ColorMap.h"
[2100]46#include "Trace.h"
47
[3330]48#define MSECS_ELAPSED(t1, t2) \
49    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3) : \
50     (((t2).tv_sec - (t1).tv_sec))*1.0e+3 + (double)((t2).tv_usec - (t1).tv_usec)/1.0e+3)
[2260]51
[3615]52#define printCameraInfo(camera)                                         \
53do {                                                                    \
54    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g", \
55          (camera)->GetParallelScale(),                                 \
56          (camera)->GetViewAngle(),                                     \
57          (camera)->GetDistance(),                                      \
58          (camera)->GetPosition()[0],                                   \
59          (camera)->GetPosition()[1],                                   \
60          (camera)->GetPosition()[2],                                   \
61          (camera)->GetFocalPoint()[0],                                 \
62          (camera)->GetFocalPoint()[1],                                 \
63          (camera)->GetFocalPoint()[2],                                 \
64          (camera)->GetViewUp()[0],                                     \
65          (camera)->GetViewUp()[1],                                     \
66          (camera)->GetViewUp()[2],                                     \
67          (camera)->GetClippingRange()[0],                              \
68          (camera)->GetClippingRange()[1]);                             \
69} while(0)
[2100]70
[3615]71using namespace VtkVis;
72
[2100]73Renderer::Renderer() :
[2112]74    _needsRedraw(true),
[3330]75    _needsAxesReset(false),
76    _needsCameraClippingRangeReset(false),
77    _needsCameraReset(false),
[2423]78    _windowWidth(500),
79    _windowHeight(500),
[3164]80    _cameraMode(PERSPECTIVE),
[3330]81    _cameraAspect(ASPECT_NATIVE),
[2423]82    _imgCameraPlane(PLANE_XY),
83    _imgCameraOffset(0),
[2194]84    _cameraZoomRatio(1),
85    _useCumulativeRange(true),
[3164]86    _cumulativeRangeOnlyVisible(false)
[2100]87{
88    _bgColor[0] = 0;
89    _bgColor[1] = 0;
90    _bgColor[2] = 0;
[2194]91    _cameraPan[0] = 0;
92    _cameraPan[1] = 0;
[2351]93    _cameraOrientation[0] = 1.0;
94    _cameraOrientation[1] = 0.0;
95    _cameraOrientation[2] = 0.0;
96    _cameraOrientation[3] = 0.0;
[2100]97    // clipping planes to prevent overdrawing axes
[2270]98    _activeClipPlanes = vtkSmartPointer<vtkPlaneCollection>::New();
[2100]99    // bottom
[2317]100    _cameraClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
101    _cameraClipPlanes[0]->SetNormal(0, 1, 0);
102    _cameraClipPlanes[0]->SetOrigin(0, 0, 0);
[2270]103    if (_cameraMode == IMAGE)
[2317]104        _activeClipPlanes->AddItem(_cameraClipPlanes[0]);
[2100]105    // left
[2317]106    _cameraClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
107    _cameraClipPlanes[1]->SetNormal(1, 0, 0);
108    _cameraClipPlanes[1]->SetOrigin(0, 0, 0);
[2270]109    if (_cameraMode == IMAGE)
[2317]110        _activeClipPlanes->AddItem(_cameraClipPlanes[1]);
[2270]111    // top
[2317]112    _cameraClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
113    _cameraClipPlanes[2]->SetNormal(0, -1, 0);
114    _cameraClipPlanes[2]->SetOrigin(0, 1, 0);
[2270]115    if (_cameraMode == IMAGE)
[2317]116        _activeClipPlanes->AddItem(_cameraClipPlanes[2]);
[2100]117    // right
[2317]118    _cameraClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
119    _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
120    _cameraClipPlanes[3]->SetOrigin(1, 0, 0);
[2270]121    if (_cameraMode == IMAGE)
[2317]122        _activeClipPlanes->AddItem(_cameraClipPlanes[3]);
[3818]123
124#ifdef USE_FONT_CONFIG
125    vtkFreeTypeTools *typeTools = vtkFreeTypeTools::GetInstance();
126    typeTools->ForceCompiledFontsOff();
127    TRACE("FreeTypeTools impl: %s", typeTools->GetClassName());
128#endif
129
[2100]130    _renderer = vtkSmartPointer<vtkRenderer>::New();
[3330]131
132    // Global ambient (defaults to 1,1,1)
133    //_renderer->SetAmbient(.2, .2, .2);
134
[3683]135    _renderer->AutomaticLightCreationOff();
136
[2317]137    vtkSmartPointer<vtkLight> headlight = vtkSmartPointer<vtkLight>::New();
138    headlight->SetLightTypeToHeadlight();
139    headlight->PositionalOff();
[3330]140    // Light ambient color defaults to 0,0,0
[2317]141    //headlight->SetAmbientColor(1, 1, 1);
142    _renderer->AddLight(headlight);
[3683]143
[2317]144    vtkSmartPointer<vtkLight> skylight = vtkSmartPointer<vtkLight>::New();
145    skylight->SetLightTypeToCameraLight();
146    skylight->SetPosition(0, 1, 0);
147    skylight->SetFocalPoint(0, 0, 0);
148    skylight->PositionalOff();
[3330]149    // Light ambient color defaults to 0,0,0
[2317]150    //skylight->SetAmbientColor(1, 1, 1);
151    _renderer->AddLight(skylight);
[3683]152
[2112]153    _renderer->LightFollowCameraOn();
[2100]154    _renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
[2254]155#ifdef USE_OFFSCREEN_RENDERING
[2100]156    _renderWindow->DoubleBufferOff();
157    _renderWindow->OffScreenRenderingOn();
[2260]158#else
159    _renderWindow->SwapBuffersOff();
[2251]160#endif
[2254]161    _renderWindow->SetSize(_windowWidth, _windowHeight);
[2290]162    // Next 2 options needed to support depth peeling
163    _renderWindow->SetAlphaBitPlanes(1);
164    _renderWindow->SetMultiSamples(0);
165    _renderer->SetMaximumNumberOfPeels(100);
166    _renderer->SetUseDepthPeeling(1);
[3330]167    _renderer->SetTwoSidedLighting(1);
[2100]168    _renderWindow->AddRenderer(_renderer);
[2317]169    setViewAngle(_windowHeight);
170    initAxes();
[3993]171    //initOrientationMarkers();
[2317]172    initCamera();
[2261]173    addColorMap("default", ColorMap::getDefault());
[2404]174    addColorMap("grayDefault", ColorMap::getGrayDefault());
[2261]175    addColorMap("volumeDefault", ColorMap::getVolumeDefault());
[2320]176    addColorMap("elementDefault", ColorMap::getElementDefault());
[2100]177}
178
179Renderer::~Renderer()
180{
[2270]181    TRACE("Enter");
[3164]182
183    deleteAllGraphicsObjects<Arc>();
184    deleteAllGraphicsObjects<Arrow>();
185    deleteAllGraphicsObjects<Box>();
186    deleteAllGraphicsObjects<Cone>();
187    deleteAllGraphicsObjects<Contour2D>();
188    deleteAllGraphicsObjects<Contour3D>();
189    deleteAllGraphicsObjects<Cutplane>();
190    deleteAllGraphicsObjects<Cylinder>();
191    deleteAllGraphicsObjects<Disk>();
192    deleteAllGraphicsObjects<Glyphs>();
193    deleteAllGraphicsObjects<Group>();
194    deleteAllGraphicsObjects<HeightMap>();
[3818]195    deleteAllGraphicsObjects<Image>();
[4082]196    deleteAllGraphicsObjects<ImageCutplane>();
[3164]197    deleteAllGraphicsObjects<LIC>();
198    deleteAllGraphicsObjects<Line>();
199    deleteAllGraphicsObjects<Molecule>();
[3535]200    deleteAllGraphicsObjects<Outline>();
[3861]201    deleteAllGraphicsObjects<Parallelepiped>();
[3164]202    deleteAllGraphicsObjects<PolyData>();
203    deleteAllGraphicsObjects<Polygon>();
204    deleteAllGraphicsObjects<PseudoColor>();
205    deleteAllGraphicsObjects<Sphere>();
206    deleteAllGraphicsObjects<Streamlines>();
[3818]207    deleteAllGraphicsObjects<Text3D>();
[3164]208    deleteAllGraphicsObjects<Volume>();
209    deleteAllGraphicsObjects<Warp>();
210
[2270]211    TRACE("Deleting ColorMaps");
[2269]212    // Delete color maps and data sets last in case references still
213    // exist
214    for (ColorMapHashmap::iterator itr = _colorMaps.begin();
[2612]215         itr != _colorMaps.end(); ++itr) {
[2269]216        delete itr->second;
217    }
218    _colorMaps.clear();
[2270]219    TRACE("Deleting DataSets");
[2123]220    for (DataSetHashmap::iterator itr = _dataSets.begin();
[2612]221         itr != _dataSets.end(); ++itr) {
[2123]222        delete itr->second;
223    }
224    _dataSets.clear();
[2612]225
226    clearFieldRanges();
[3330]227    clearUserFieldRanges();
[2612]228
[2270]229    TRACE("Leave");
[2100]230}
231
232/**
233 * \brief Add a DataSet to this Renderer
234 *
235 * This just adds the DataSet to the Renderer's list of data sets.
[2290]236 * In order to render the data, a graphics object using the data
237 * set must be added to the Renderer.
[2100]238 */
[2123]239void Renderer::addDataSet(const DataSetId& id)
[2100]240{
[2112]241    if (getDataSet(id) != NULL) {
[2320]242        WARN("Replacing existing DataSet %s", id.c_str());
[2112]243        deleteDataSet(id);
244    }
245    _dataSets[id] = new DataSet(id);
[2100]246}
247
248/**
249 * \brief Remove the specified DataSet and associated rendering objects
250 *
[2260]251 * The underlying DataSet and any associated graphics
[2100]252 * objects are deleted, freeing the memory used.
253 */
[2123]254void Renderer::deleteDataSet(const DataSetId& id)
[2100]255{
[2123]256    DataSetHashmap::iterator itr;
257
258    bool doAll = false;
259
260    if (id.compare("all") == 0) {
261        itr = _dataSets.begin();
262        doAll = true;
263    } else {
264        itr = _dataSets.find(id);
265    }
[2112]266    if (itr == _dataSets.end()) {
267        ERROR("Unknown dataset %s", id.c_str());
268        return;
[2123]269    }
[2100]270
[2123]271    do {
272        TRACE("Deleting dataset %s", itr->second->getName().c_str());
273
[2612]274        deleteGraphicsObject<Contour2D>(itr->second->getName());
275        deleteGraphicsObject<Contour3D>(itr->second->getName());
276        deleteGraphicsObject<Cutplane>(itr->second->getName());
277        deleteGraphicsObject<Glyphs>(itr->second->getName());
278        deleteGraphicsObject<HeightMap>(itr->second->getName());
[3818]279        deleteGraphicsObject<Image>(itr->second->getName());
[4082]280        deleteGraphicsObject<ImageCutplane>(itr->second->getName());
[2612]281        deleteGraphicsObject<LIC>(itr->second->getName());
282        deleteGraphicsObject<Molecule>(itr->second->getName());
[3535]283        deleteGraphicsObject<Outline>(itr->second->getName());
[2612]284        deleteGraphicsObject<PolyData>(itr->second->getName());
285        deleteGraphicsObject<PseudoColor>(itr->second->getName());
286        deleteGraphicsObject<Streamlines>(itr->second->getName());
287        deleteGraphicsObject<Volume>(itr->second->getName());
[3126]288        deleteGraphicsObject<Warp>(itr->second->getName());
[2455]289
[2112]290        TRACE("After deleting graphics objects");
291
292        delete itr->second;
[2277]293        itr = _dataSets.erase(itr);
294    } while (doAll && itr != _dataSets.end());
[2123]295
[2194]296    // Update cumulative data range
[2612]297    initFieldRanges();
298    updateFieldRanges();
[2194]299
[3330]300    sceneBoundsChanged();
[2123]301    _needsRedraw = true;
[2100]302}
303
[2627]304/**
305 * \brief Get a list of DataSets this Renderer knows about
306 */
[2423]307void Renderer::getDataSetNames(std::vector<std::string>& names)
308{
309    names.clear();
310    for (DataSetHashmap::iterator itr = _dataSets.begin();
311         itr != _dataSets.end(); ++itr) {
312        names.push_back(itr->second->getName());
313    }
314}
315
[2100]316/**
317 * \brief Find the DataSet for the given DataSetId key
318 *
319 * \return A pointer to the DataSet, or NULL if not found
320 */
[2123]321DataSet *Renderer::getDataSet(const DataSetId& id)
[2100]322{
323    DataSetHashmap::iterator itr = _dataSets.find(id);
[2112]324    if (itr == _dataSets.end()) {
[2402]325#ifdef DEBUG
[2112]326        TRACE("DataSet not found: %s", id.c_str());
[2402]327#endif
[2100]328        return NULL;
[2112]329    } else
[2100]330        return itr->second;
331}
332
333/**
334 * \brief (Re-)load the data for the specified DataSet key from a file
335 */
[2123]336bool Renderer::setDataFile(const DataSetId& id, const char *filename)
[2100]337{
338    DataSet *ds = getDataSet(id);
339    if (ds) {
340        bool ret = ds->setDataFile(filename);
[2612]341        initFieldRanges();
342        updateFieldRanges();
[2100]343        _needsRedraw = true;
344        return ret;
345    } else
346        return false;
347}
348
349/**
350 * \brief (Re-)load the data for the specified DataSet key from a memory buffer
351 */
[2123]352bool Renderer::setData(const DataSetId& id, char *data, int nbytes)
[2100]353{
354    DataSet *ds = getDataSet(id);
355    if (ds) {
[2146]356        bool ret = ds->setData(data, nbytes);
[2612]357        initFieldRanges();
358        updateFieldRanges();
[2100]359        _needsRedraw = true;
[2146]360        return ret;
[2100]361    } else
362        return false;
363}
364
[2627]365/**
366 * \brief Set the active scalar field array by name for a DataSet
367 */
[2393]368bool Renderer::setDataSetActiveScalars(const DataSetId& id, const char *scalarName)
369{
370    DataSetHashmap::iterator itr;
371
372    bool doAll = false;
373
374    if (id.compare("all") == 0) {
375        itr = _dataSets.begin();
376        doAll = true;
377    } else {
378        itr = _dataSets.find(id);
379    }
380    if (itr == _dataSets.end()) {
381        ERROR("DataSet not found: %s", id.c_str());
382        return false;
383    }
384
385    bool ret = true;
[2597]386    bool needRangeUpdate = false;
[2393]387    do {
[3902]388        const char *name = itr->second->getActiveScalarsName();
389        if (name == NULL || (strcmp(name, scalarName) != 0)) {
[2597]390            if (!itr->second->setActiveScalars(scalarName)) {
391                ret = false;
392            } else {
393                needRangeUpdate = true;
394            }
[2393]395        }
396    } while (doAll && ++itr != _dataSets.end());
397
[2597]398    if (needRangeUpdate) {
[2612]399         updateFieldRanges();
[2393]400        _needsRedraw = true;
401    }
402
403    return ret;
404}
405
[2627]406/**
407 * \brief Set the active vector field array by name for a DataSet
408 */
[2393]409bool Renderer::setDataSetActiveVectors(const DataSetId& id, const char *vectorName)
410{
411    DataSetHashmap::iterator itr;
412
413    bool doAll = false;
414
415    if (id.compare("all") == 0) {
416        itr = _dataSets.begin();
417        doAll = true;
418    } else {
419        itr = _dataSets.find(id);
420    }
421    if (itr == _dataSets.end()) {
422        ERROR("DataSet not found: %s", id.c_str());
423        return false;
424    }
425
426    bool ret = true;
[2597]427    bool needRangeUpdate = false;
[2393]428    do {
[2597]429        if (strcmp(itr->second->getActiveVectorsName(), vectorName) != 0) {
430            if (!itr->second->setActiveVectors(vectorName)) {
431                ret = false;
432            } else {
433                needRangeUpdate = true;
434            }
[2393]435        }
436    } while (doAll && ++itr != _dataSets.end());
437
[2597]438    if (needRangeUpdate) {
[2612]439        updateFieldRanges();
[2393]440        _needsRedraw = true;
441    }
442
443    return ret;
444}
445
[2194]446/**
447 * \brief Control whether the cumulative data range of datasets is used for
448 * colormapping and contours
[3330]449 *
450 * NOTE: If using explicit range(s), setting cumulative ranges on will
451 * remove explicit ranges on all fields
[2194]452 */
453void Renderer::setUseCumulativeDataRange(bool state, bool onlyVisible)
454{
[3330]455    TRACE("use cumulative: %d, only visible: %d", (state ? 1 : 0), (onlyVisible ? 1 : 0));
456    _useCumulativeRange = state;
457    _cumulativeRangeOnlyVisible = onlyVisible;
458    clearUserFieldRanges();
459    updateFieldRanges();
460    resetAxes();
461    _needsRedraw = true;
[2194]462}
463
[2402]464void Renderer::resetAxes(double bounds[6])
[2112]465{
466    TRACE("Resetting axes");
[3330]467
468    if (_cubeAxesActor == NULL) {
[2112]469        initAxes();
470    }
[3330]471
[2112]472    if (_cameraMode == IMAGE) {
[3198]473        _cubeAxesActor->SetUse2DMode(1);
[2112]474    } else {
[3198]475        _cubeAxesActor->SetUse2DMode(0);
476    }
477
478    if (_cameraMode == IMAGE) {
479        return;
480    }
481
[3330]482    setAxesBounds(bounds);
483    setAxesRanges();
484}
485
486/**
487 * \brief Explicitly set world coordinate bounds of axes
488 *
489 * This determines the region in world coordinates around which
490 * the axes are drawn
491 */
492void Renderer::setAxesBounds(double min, double max)
493{
494    setAxisBounds(X_AXIS, min, max);
495    setAxisBounds(Y_AXIS, min, max);
496    setAxisBounds(Z_AXIS, min, max);
497}
498
499/**
500 * \brief Set axes bounds based on auto/explicit settings
501 */
502void Renderer::setAxesBounds(double boundsIn[6])
503{
504    double newBounds[6];
505    if (boundsIn != NULL) {
506        memcpy(&newBounds[0], &boundsIn[0], sizeof(double)*6);
[3198]507    } else {
[3330]508        if (_axesAutoBounds[X_AXIS] ||
509            _axesAutoBounds[Y_AXIS] ||
510            _axesAutoBounds[Z_AXIS]) {
[4171]511            collectBounds(newBounds, true);
[3330]512        }
513    }
514
515    double bounds[6];
516    if (_axesAutoBounds[X_AXIS]) {
517        memcpy(&bounds[0], &newBounds[0], sizeof(double)*2);
518    } else {
519        memcpy(&bounds[0], &_axesUserBounds[0], sizeof(double)*2);
520    }
521    if (_axesAutoBounds[Y_AXIS]) {
522        memcpy(&bounds[2], &newBounds[2], sizeof(double)*2);
523    } else {
524        memcpy(&bounds[2], &_axesUserBounds[2], sizeof(double)*2);
525    }
526    if (_axesAutoBounds[Z_AXIS]) {
527        memcpy(&bounds[4], &newBounds[4], sizeof(double)*2);
528    } else {
529        memcpy(&bounds[4], &_axesUserBounds[4], sizeof(double)*2);
530    }
531
532    _cubeAxesActor->SetBounds(bounds);
533
534    TRACE("Axis bounds: %g %g %g %g %g %g",
[3198]535              bounds[0],
536              bounds[1],
537              bounds[2],
538              bounds[3],
539              bounds[4],
540              bounds[5]);
[3330]541
542    _needsRedraw = true;
543}
544
545/**
546 * \brief Toggle automatic vs. explicit range setting on all axes
547 */
548void Renderer::setAxesAutoBounds(bool state)
549{
550    _axesAutoBounds[X_AXIS] = state;
551    _axesAutoBounds[Y_AXIS] = state;
552    _axesAutoBounds[Z_AXIS] = state;
553
554    TRACE("Set axes bounds to %s", (state ? "auto" : "explicit"));
555
556    setAxesBounds();
557
558    _needsRedraw = true;
559}
560
561/**
562 * \brief Toggle automatic vs. explicit range setting on all axes
563 */
564void Renderer::setAxisAutoBounds(Axis axis, bool state)
565{
566    _axesAutoBounds[axis] = state;
567
568    TRACE("Set axis %d bounds to %s", axis, (state ? "auto" : "explicit"));
569
570    setAxesBounds();
571
572    _needsRedraw = true;
573}
574
575/**
576 * \brief Toggle automatic vs. explicit range setting on all axes
577 */
578void Renderer::setAxesAutoRange(bool state)
579{
580    _axesRangeMode[X_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
581    _axesRangeMode[Y_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
582    _axesRangeMode[Z_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
583
584    TRACE("Set axes range to %s", (state ? "auto" : "explicit"));
585
586    setAxesRanges();
587
588    _needsRedraw = true;
589}
590
591/**
592 * \brief Explicitly set world coordinate bounds of axis
593 *
594 * This determines the region in world coordinates around which
595 * the axes are drawn
596 */
597void Renderer::setAxisBounds(Axis axis, double min, double max)
598{
599    double bounds[6];
600    _cubeAxesActor->GetBounds(bounds);
601
602    bounds[axis*2  ] = _axesUserBounds[axis*2  ] = min;
603    bounds[axis*2+1] = _axesUserBounds[axis*2+1] = max;
604
605    _cubeAxesActor->SetBounds(bounds);
606
607    _axesAutoBounds[axis] = false;
608
609    TRACE("Axis bounds: %g %g %g %g %g %g",
610              bounds[0],
611              bounds[1],
612              bounds[2],
613              bounds[3],
614              bounds[4],
615              bounds[5]);
616
617    _needsRedraw = true;
618}
619
620/**
621 * \brief Toggle automatic vs. explicit range setting on specific axis
622 */
623void Renderer::setAxisAutoRange(Axis axis, bool state)
624{
625    _axesRangeMode[axis] = state ? RANGE_AUTO : RANGE_EXPLICIT;
626
627    TRACE("Set axis %d range to %s", axis, (state ? "auto" : "explicit"));
628
629    setAxesRanges();
630
631    _needsRedraw = true;
632}
633
634/**
635 * \brief Set explicit range on axes and disable auto range
636 */
637void Renderer::setAxesRange(double min, double max)
638{
639    setAxisRange(X_AXIS, min, max);
640    setAxisRange(Y_AXIS, min, max);
641    setAxisRange(Z_AXIS, min, max);
642}
643
644/**
645 * \brief Explicitly set power multiplier for axes labels
646 *
647 * The exponent will appear in the axis title and be omitted from labels
648 */
649void Renderer::setAxesLabelPowerScaling(int xPow, int yPow, int zPow, bool useCustom)
650{
651    _cubeAxesActor->SetLabelScaling(!useCustom, xPow, yPow, zPow);
652
653    TRACE("Set axis label scaling: custom: %d, powers: %d,%d,%d", (int)useCustom, xPow, yPow, zPow);
654
655    _needsRedraw = true;
656}
657
658/**
659 * \brief Set explicit range on axis and disable auto range
660 */
661void Renderer::setAxisRange(Axis axis, double min, double max)
662{
663    switch (axis) {
664    case X_AXIS:
665        _axesRangeMode[X_AXIS] = RANGE_EXPLICIT;
666        _cubeAxesActor->SetXAxisRange(min, max);
667        break;
668    case Y_AXIS:
669        _axesRangeMode[Y_AXIS] = RANGE_EXPLICIT;
670        _cubeAxesActor->SetYAxisRange(min, max);
671        break;
672    case Z_AXIS:
673        _axesRangeMode[Z_AXIS] = RANGE_EXPLICIT;
674        _cubeAxesActor->SetZAxisRange(min, max);
675        break;
676    default:
677        break;
[2112]678    }
[3330]679
680    TRACE("Set axis %d range to: %g, %g", axis, min, max);
681
682     _needsRedraw = true;
[2112]683}
684
[2100]685/**
[3330]686 * \brief Set axes ranges based on bounds and range modes
687 */
688void Renderer::setAxesRanges()
689{
690    computeAxesScale();
691
692    double bounds[6];
693    _cubeAxesActor->GetBounds(bounds);
694
695    double ranges[6];
696    if (_axesRangeMode[X_AXIS] != RANGE_EXPLICIT) {
697        ranges[0] = bounds[0] * _axesScale[X_AXIS];
698        ranges[1] = bounds[1] * _axesScale[X_AXIS];
699        _cubeAxesActor->SetXAxisRange(&ranges[0]);
700    } else {
701        _cubeAxesActor->GetXAxisRange(&ranges[0]);
702    }
[3615]703
[3330]704    if (_axesRangeMode[Y_AXIS] != RANGE_EXPLICIT) {
705        ranges[2] = bounds[2] * _axesScale[Y_AXIS];
706        ranges[3] = bounds[3] * _axesScale[Y_AXIS];
707        _cubeAxesActor->SetYAxisRange(&ranges[2]);
708    } else {
709        _cubeAxesActor->GetYAxisRange(&ranges[2]);
710    }
711
712    if (_axesRangeMode[Z_AXIS] != RANGE_EXPLICIT) {
713        ranges[4] = bounds[4] * _axesScale[Z_AXIS];
714        ranges[5] = bounds[5] * _axesScale[Z_AXIS];
715        _cubeAxesActor->SetZAxisRange(&ranges[4]);
716    } else {
717        _cubeAxesActor->GetZAxisRange(&ranges[4]);
718    }
719
720    TRACE("Axis ranges: %g %g %g %g %g %g",
721          ranges[0],
722          ranges[1],
723          ranges[2],
724          ranges[3],
725          ranges[4],
726          ranges[5]);
727
728    _needsRedraw = true;
729}
730
731/**
732 * \brief Compute scaling from axes bounds to ranges
733 *
734 * Uses actor scaling to determine if world coordinates
735 * need to be scaled to undo actor scaling in range labels
736 */
737void Renderer::computeAxesScale()
738{
739    if (_axesRangeMode[X_AXIS] != RANGE_AUTO &&
740        _axesRangeMode[Y_AXIS] != RANGE_AUTO &&
741        _axesRangeMode[Z_AXIS] != RANGE_AUTO)
742        return;
743
744    double bounds[6];
745    if (_cameraMode == IMAGE) {
[4171]746        collectBounds(bounds, true);
[3330]747    } else
748        _cubeAxesActor->GetBounds(bounds);
749
750    double unscaledBounds[6];
[4171]751    collectUnscaledBounds(unscaledBounds, true);
[3330]752
753    if (_axesRangeMode[X_AXIS] == RANGE_AUTO) {
754        double sx = (bounds[1] == bounds[0]) ? 1.0 : (unscaledBounds[1] - unscaledBounds[0]) / (bounds[1] - bounds[0]);
755        _axesScale[X_AXIS] = sx;
756        TRACE("Setting X axis scale to: %g", sx);
757    }
758    if (_axesRangeMode[Y_AXIS] == RANGE_AUTO) {
759        double sy = (bounds[3] == bounds[2]) ? 1.0 : (unscaledBounds[3] - unscaledBounds[2]) / (bounds[3] - bounds[2]);
760        _axesScale[Y_AXIS] = sy;
761        TRACE("Setting Y axis scale to: %g", sy);
762    }
763    if (_axesRangeMode[Y_AXIS] == RANGE_AUTO) {
764        double sz = (bounds[5] == bounds[4]) ? 1.0 : (unscaledBounds[5] - unscaledBounds[4]) / (bounds[5] - bounds[4]);
765        _axesScale[Z_AXIS] = sz;
766        TRACE("Setting Z axis scale to: %g", sz);
767    }
768}
769
770/**
771 * \brief Set scaling factor to convert world coordinates to axis
772 * label values in X, Y, Z
773 */
774void Renderer::setAxesScale(double scale)
775{
776    _axesRangeMode[X_AXIS] = RANGE_SCALE_BOUNDS;
777    _axesRangeMode[Y_AXIS] = RANGE_SCALE_BOUNDS;
778    _axesRangeMode[Z_AXIS] = RANGE_SCALE_BOUNDS;
779
780    _axesScale[X_AXIS] = scale;
781    _axesScale[Y_AXIS] = scale;
782    _axesScale[Z_AXIS] = scale;
783
784    TRACE("Setting axes scale to: %g", scale);
785
786    setAxesRanges();
787}
788
789/**
790 * \brief Set scaling factor to convert world coordinates to axis label values
791 */
792void Renderer::setAxisScale(Axis axis, double scale)
793{
794    _axesRangeMode[axis] = RANGE_SCALE_BOUNDS;
795    _axesScale[axis] = scale;
796
797    TRACE("Setting axis %d scale to: %g", axis, scale);
798
799    setAxesRanges();
800}
801
802/**
803 * \brief Set an origin point within the axes bounds where the axes will intersect
804 *
805 * \param x X coordinate of origin
806 * \param y Y coordinate of origin
807 * \param z Z coordinate of origin
808 * \param useCustom Flag indicating if custom origin is to be used/enabled
809 */
810void Renderer::setAxesOrigin(double x, double y, double z, bool useCustom)
811{
812    _cubeAxesActor->SetAxisOrigin(x, y, z);
813    _cubeAxesActor->SetUseAxisOrigin((useCustom ? 1 : 0));
814
815    _needsRedraw = true;
816}
817
[3993]818
819void Renderer::initOrientationMarkers()
820{
821    if (_axesActor == NULL) {
822        _axesActor = vtkSmartPointer<vtkAxesActor>::New();
823    }
824    if (_annotatedCubeActor == NULL) {
825        _annotatedCubeActor = vtkSmartPointer<vtkAnnotatedCubeActor>::New();
826    }
827    _renderer->AddViewProp(_axesActor);
828}
829
[3330]830/**
[2100]831 * \brief Set inital properties on the 2D Axes
832 */
833void Renderer::initAxes()
834{
[2112]835    TRACE("Initializing axes");
[2865]836#ifdef USE_CUSTOM_AXES
[2112]837    if (_cubeAxesActor == NULL)
[2865]838        _cubeAxesActor = vtkSmartPointer<vtkRpCubeAxesActor>::New();
839#else
840    if (_cubeAxesActor == NULL)
[2112]841        _cubeAxesActor = vtkSmartPointer<vtkCubeAxesActor>::New();
[2865]842#endif
[2112]843    _cubeAxesActor->SetCamera(_renderer->GetActiveCamera());
[2317]844    _cubeAxesActor->GetProperty()->LightingOff();
[2112]845    // Don't offset labels at origin
846    _cubeAxesActor->SetCornerOffset(0);
[2521]847    _cubeAxesActor->SetFlyModeToStaticTriad();
[3330]848
[3193]849    _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
[3198]850    _cubeAxesActor->SetDrawXInnerGridlines(0);
851    _cubeAxesActor->SetDrawYInnerGridlines(0);
852    _cubeAxesActor->SetDrawZInnerGridlines(0);
853    _cubeAxesActor->SetDrawXGridpolys(0);
854    _cubeAxesActor->SetDrawYGridpolys(0);
855    _cubeAxesActor->SetDrawZGridpolys(0);
856    _cubeAxesActor->SetDistanceLODThreshold(1);
[3194]857    _cubeAxesActor->SetEnableDistanceLOD(0);
[3198]858    _cubeAxesActor->SetViewAngleLODThreshold(0);
[3194]859    _cubeAxesActor->SetEnableViewAngleLOD(0);
[3330]860    // Attempt to set font sizes for 2D (this currently doesn't work)
861    for (int i = 0; i < 3; i++) {
862        _cubeAxesActor->GetTitleTextProperty(i)->SetFontSize(14);
863        _cubeAxesActor->GetLabelTextProperty(i)->SetFontSize(12);
864    }
865    // Set font pixel size for 3D
866    _cubeAxesActor->SetScreenSize(8.0);
867
[3198]868    _cubeAxesActor->SetBounds(0, 1, 0, 1, 0, 1);
869    _cubeAxesActor->SetXAxisRange(0, 1);
870    _cubeAxesActor->SetYAxisRange(0, 1);
871    _cubeAxesActor->SetZAxisRange(0, 1);
[3330]872    for (int axis = 0; axis < 3; axis++) {
873        _axesAutoBounds[axis] = true;
874        _axesRangeMode[axis] = RANGE_AUTO;
875        _axesScale[axis] = 1.0;
876    }
[2112]877
[3198]878    double axesColor[] = {1,1,1};
879    setAxesColor(axesColor);
880
881    if (!_renderer->HasViewProp(_cubeAxesActor))
882        _renderer->AddViewProp(_cubeAxesActor);
883
884    if (_cameraMode == IMAGE) {
885        _cubeAxesActor->SetUse2DMode(1);
886    } else {
887        _cubeAxesActor->SetUse2DMode(0);
888    }
[2100]889}
890
891/**
[2230]892 * \brief Set Fly mode of axes
893 */
894void Renderer::setAxesFlyMode(AxesFlyMode mode)
895{
896    if (_cubeAxesActor == NULL)
[3198]897        return;
898
[2230]899    switch (mode) {
900    case FLY_STATIC_EDGES:
901        _cubeAxesActor->SetFlyModeToStaticEdges();
[3193]902        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
[2230]903        break;
904    case FLY_STATIC_TRIAD:
905        _cubeAxesActor->SetFlyModeToStaticTriad();
[3193]906        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
[2230]907        break;
908    case FLY_OUTER_EDGES:
909        _cubeAxesActor->SetFlyModeToOuterEdges();
[3193]910        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
[2230]911        break;
912    case FLY_FURTHEST_TRIAD:
913        _cubeAxesActor->SetFlyModeToFurthestTriad();
[3193]914        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_FURTHEST);
[2230]915        break;
916    case FLY_CLOSEST_TRIAD:
917    default:
918        _cubeAxesActor->SetFlyModeToClosestTriad();
[3193]919        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_CLOSEST);
[2230]920        break;
921    }
922    _needsRedraw = true;
923}
924
925/**
[2115]926 * \brief Set color of axes, ticks, labels, titles
927 */
[3330]928void Renderer::setAxesColor(double color[3], double opacity)
[2115]929{
[3330]930    setAxesTitleColor(color, opacity);
931    setAxesLabelColor(color, opacity);
932    setAxesLinesColor(color, opacity);
933    setAxesGridlinesColor(color, opacity);
934    setAxesInnerGridlinesColor(color, opacity);
935    setAxesGridpolysColor(color, opacity);
[2115]936}
937
938/**
[3330]939 * \brief Set color of axes title text
940 */
941void Renderer::setAxesTitleColor(double color[3], double opacity)
942{
943    setAxisTitleColor(X_AXIS, color, opacity);
944    setAxisTitleColor(Y_AXIS, color, opacity);
945    setAxisTitleColor(Z_AXIS, color, opacity);
946}
947
948/**
949 * \brief Set color of axes label text
950 */
951void Renderer::setAxesLabelColor(double color[3], double opacity)
952{
953    setAxisLabelColor(X_AXIS, color, opacity);
954    setAxisLabelColor(Y_AXIS, color, opacity);
955    setAxisLabelColor(Z_AXIS, color, opacity);
956}
957
958/**
959 * \brief Set color of main axes lines
960 */
961void Renderer::setAxesLinesColor(double color[3], double opacity)
962{
963    setAxisLinesColor(X_AXIS, color, opacity);
964    setAxisLinesColor(Y_AXIS, color, opacity);
965    setAxisLinesColor(Z_AXIS, color, opacity);
966}
967
968/**
969 * \brief Set color of axis gridlines
970 */
971void Renderer::setAxesGridlinesColor(double color[3], double opacity)
972{
973    setAxisGridlinesColor(X_AXIS, color, opacity);
974    setAxisGridlinesColor(Y_AXIS, color, opacity);
975    setAxisGridlinesColor(Z_AXIS, color, opacity);
976}
977
978/**
979 * \brief Set color of axes inner gridlines
980 */
981void Renderer::setAxesInnerGridlinesColor(double color[3], double opacity)
982{
983    setAxisInnerGridlinesColor(X_AXIS, color, opacity);
984    setAxisInnerGridlinesColor(Y_AXIS, color, opacity);
985    setAxisInnerGridlinesColor(Z_AXIS, color, opacity);
986}
987
988/**
989 * \brief Set color of axes inner grid polygons
990 */
991void Renderer::setAxesGridpolysColor(double color[3], double opacity)
992{
993    setAxisGridpolysColor(X_AXIS, color, opacity);
994    setAxisGridpolysColor(Y_AXIS, color, opacity);
995    setAxisGridpolysColor(Z_AXIS, color, opacity);
996}
997
998/**
[2434]999 * \brief Turn on/off rendering of all enabled axes
[2328]1000 */
1001void Renderer::setAxesVisibility(bool state)
1002{
1003    if (_cubeAxesActor != NULL) {
1004        _cubeAxesActor->SetVisibility((state ? 1 : 0));
1005        _needsRedraw = true;
1006    }
1007}
1008
1009/**
[2112]1010 * \brief Turn on/off rendering of all axes gridlines
1011 */
1012void Renderer::setAxesGridVisibility(bool state)
1013{
[2328]1014    setAxisGridVisibility(X_AXIS, state);
1015    setAxisGridVisibility(Y_AXIS, state);
1016    setAxisGridVisibility(Z_AXIS, state);
1017}
1018
1019/**
[3330]1020 * \brief Turn on/off rendering of all axes inner gridlines
1021 */
1022void Renderer::setAxesInnerGridVisibility(bool state)
1023{
1024    setAxisInnerGridVisibility(X_AXIS, state);
1025    setAxisInnerGridVisibility(Y_AXIS, state);
1026    setAxisInnerGridVisibility(Z_AXIS, state);
1027}
1028
1029/**
1030 * \brief Turn on/off rendering of all axes inner grid polygons
1031 */
1032void Renderer::setAxesGridpolysVisibility(bool state)
1033{
1034    setAxisGridpolysVisibility(X_AXIS, state);
1035    setAxisGridpolysVisibility(Y_AXIS, state);
1036    setAxisGridpolysVisibility(Z_AXIS, state);
1037}
1038
1039/**
[2328]1040 * \brief Turn on/off rendering of all axis labels
1041 */
1042void Renderer::setAxesLabelVisibility(bool state)
1043{
1044    setAxisLabelVisibility(X_AXIS, state);
1045    setAxisLabelVisibility(Y_AXIS, state);
1046    setAxisLabelVisibility(Z_AXIS, state);
1047}
1048
1049/**
1050 * \brief Turn on/off rendering of all axis ticks
1051 */
1052void Renderer::setAxesTickVisibility(bool state)
1053{
1054    setAxisTickVisibility(X_AXIS, state);
1055    setAxisTickVisibility(Y_AXIS, state);
1056    setAxisTickVisibility(Z_AXIS, state);
1057}
1058
1059/**
[3330]1060 * \brief Turn on/off rendering of all axis ticks
1061 */
1062void Renderer::setAxesMinorTickVisibility(bool state)
1063{
1064    setAxisMinorTickVisibility(X_AXIS, state);
1065    setAxisMinorTickVisibility(Y_AXIS, state);
1066    setAxisMinorTickVisibility(Z_AXIS, state);
1067}
1068
1069/**
[2329]1070 * \brief Control position of ticks on 3D axes
1071 */
1072void Renderer::setAxesTickPosition(AxesTickPosition pos)
1073{
1074    if (_cubeAxesActor == NULL)
1075        return;
1076
1077    switch (pos) {
1078    case TICKS_BOTH:
1079        _cubeAxesActor->SetTickLocationToBoth();
1080        break;
1081    case TICKS_OUTSIDE:
1082        _cubeAxesActor->SetTickLocationToOutside();
1083        break;
1084    case TICKS_INSIDE:
1085    default:
1086        _cubeAxesActor->SetTickLocationToInside();
1087        break;
1088    }
1089    _needsRedraw = true;
1090}
1091
1092/**
[3330]1093 * \brief Controls label scaling by power of 10
1094 */
1095void Renderer::setAxesLabelScaling(bool autoScale, int xpow, int ypow, int zpow)
1096{
1097    if (_cubeAxesActor != NULL) {
1098        _cubeAxesActor->SetLabelScaling(autoScale, xpow, ypow, zpow);
1099        _needsRedraw = true;
1100    }
1101}
1102
1103/**
1104 * \brief Set axes title/label font size in pixels
1105 */
1106void Renderer::setAxesPixelFontSize(double screenSize)
1107{
1108    if (_cubeAxesActor != NULL) {
1109        TRACE("Setting axes font size to: %g px", screenSize);
1110        _cubeAxesActor->SetScreenSize(screenSize);
1111        _needsRedraw = true;
1112    }
1113}
1114
1115/**
1116 * \brief Set axis title font family
1117 */
1118void Renderer::setAxesTitleFont(const char *fontName)
1119{
1120    setAxisTitleFont(X_AXIS, fontName);
1121    setAxisTitleFont(Y_AXIS, fontName);
1122    setAxisTitleFont(Z_AXIS, fontName);
1123}
1124
1125/**
1126 * \brief Set axis title font size
1127 */
1128void Renderer::setAxesTitleFontSize(int sz)
1129{
1130    setAxisTitleFontSize(X_AXIS, sz);
1131    setAxisTitleFontSize(Y_AXIS, sz);
1132    setAxisTitleFontSize(Z_AXIS, sz);
1133}
1134
1135/**
1136 * \brief Set orientation (as rotation in degrees) of axis title
1137 */
1138void Renderer::setAxesTitleOrientation(double orientation)
1139{
1140    setAxisTitleOrientation(X_AXIS, orientation);
1141    setAxisTitleOrientation(Y_AXIS, orientation);
1142    setAxisTitleOrientation(Z_AXIS, orientation);
1143}
1144
1145/**
1146 * \brief Set axis label font family
1147 */
1148void Renderer::setAxesLabelFont(const char *fontName)
1149{
1150    setAxisLabelFont(X_AXIS, fontName);
1151    setAxisLabelFont(Y_AXIS, fontName);
1152    setAxisLabelFont(Z_AXIS, fontName);
1153}
1154
1155/**
1156 * \brief Set axis label font size
1157 */
1158void Renderer::setAxesLabelFontSize(int sz)
1159{
1160    setAxisLabelFontSize(X_AXIS, sz);
1161    setAxisLabelFontSize(Y_AXIS, sz);
1162    setAxisLabelFontSize(Z_AXIS, sz);
1163}
1164
1165/**
1166 * \brief Set orientation (as rotation in degrees) of axis labels
1167 */
1168void Renderer::setAxesLabelOrientation(double orientation)
1169{
1170    setAxisLabelOrientation(X_AXIS, orientation);
1171    setAxisLabelOrientation(Y_AXIS, orientation);
1172    setAxisLabelOrientation(Z_AXIS, orientation);
1173}
1174
1175/**
1176 * \brief Set printf style label format string
1177 */
1178void Renderer::setAxesLabelFormat(const char *format)
1179{
1180    setAxisLabelFormat(X_AXIS, format);
1181    setAxisLabelFormat(Y_AXIS, format);
1182    setAxisLabelFormat(Z_AXIS, format);
1183}
1184
1185/**
[2328]1186 * \brief Turn on/off rendering of the specified axis
1187 */
1188void Renderer::setAxisVisibility(Axis axis, bool state)
1189{
[2112]1190    if (_cubeAxesActor != NULL) {
[2328]1191        if (axis == X_AXIS) {
1192            _cubeAxesActor->SetXAxisVisibility((state ? 1 : 0));
1193        } else if (axis == Y_AXIS) {
1194            _cubeAxesActor->SetYAxisVisibility((state ? 1 : 0));
1195        } else if (axis == Z_AXIS) {
1196            _cubeAxesActor->SetZAxisVisibility((state ? 1 : 0));
1197        }
[2112]1198        _needsRedraw = true;
1199    }
[3330]1200}
1201
1202/**
1203 * \brief Turn on/off rendering of single axis gridlines
1204 */
1205void Renderer::setAxisGridVisibility(Axis axis, bool state)
1206{
1207    if (_cubeAxesActor != NULL) {
[2328]1208        if (axis == X_AXIS) {
[3330]1209            _cubeAxesActor->SetDrawXGridlines((state ? 1 : 0));
[2328]1210        } else if (axis == Y_AXIS) {
[3330]1211            _cubeAxesActor->SetDrawYGridlines((state ? 1 : 0));
[2434]1212        } else if (axis == Z_AXIS) {
[3330]1213            _cubeAxesActor->SetDrawZGridlines((state ? 1 : 0));
[2328]1214        }
1215        _needsRedraw = true;
1216    }
[2112]1217}
1218
1219/**
1220 * \brief Turn on/off rendering of single axis gridlines
1221 */
[3330]1222void Renderer::setAxisInnerGridVisibility(Axis axis, bool state)
[2112]1223{
1224    if (_cubeAxesActor != NULL) {
1225        if (axis == X_AXIS) {
[3330]1226            _cubeAxesActor->SetDrawXInnerGridlines((state ? 1 : 0));
[2112]1227        } else if (axis == Y_AXIS) {
[3330]1228            _cubeAxesActor->SetDrawYInnerGridlines((state ? 1 : 0));
[2112]1229        } else if (axis == Z_AXIS) {
[3330]1230            _cubeAxesActor->SetDrawZInnerGridlines((state ? 1 : 0));
[2112]1231        }
1232        _needsRedraw = true;
1233    }
1234}
1235
1236/**
[3330]1237 * \brief Turn on/off rendering of single axis gridlines
[2100]1238 */
[3330]1239void Renderer::setAxisGridpolysVisibility(Axis axis, bool state)
[2100]1240{
[2112]1241    if (_cubeAxesActor != NULL) {
[2328]1242        if (axis == X_AXIS) {
[3330]1243            _cubeAxesActor->SetDrawXGridpolys((state ? 1 : 0));
[2328]1244        } else if (axis == Y_AXIS) {
[3330]1245            _cubeAxesActor->SetDrawYGridpolys((state ? 1 : 0));
[2328]1246        } else if (axis == Z_AXIS) {
[3330]1247            _cubeAxesActor->SetDrawZGridpolys((state ? 1 : 0));
[2328]1248        }
[2100]1249        _needsRedraw = true;
1250    }
[3330]1251}
1252
1253/**
1254 * \brief Toggle label visibility for the specified axis
1255 */
1256void Renderer::setAxisLabelVisibility(Axis axis, bool state)
1257{
1258    if (_cubeAxesActor != NULL) {
[2328]1259        if (axis == X_AXIS) {
[3330]1260            _cubeAxesActor->SetXAxisLabelVisibility((state ? 1 : 0));
[2328]1261        } else if (axis == Y_AXIS) {
[3330]1262            _cubeAxesActor->SetYAxisLabelVisibility((state ? 1 : 0));
[2434]1263        } else if (axis == Z_AXIS) {
[3330]1264            _cubeAxesActor->SetZAxisLabelVisibility((state ? 1 : 0));
[2328]1265        }
[2112]1266        _needsRedraw = true;
1267    }
[2100]1268}
1269
1270/**
[2328]1271 * \brief Toggle tick visibility for the specified axis
[2100]1272 */
[2328]1273void Renderer::setAxisTickVisibility(Axis axis, bool state)
[2100]1274{
[2112]1275    if (_cubeAxesActor != NULL) {
[2100]1276        if (axis == X_AXIS) {
[2328]1277            _cubeAxesActor->SetXAxisTickVisibility((state ? 1 : 0));
[2100]1278        } else if (axis == Y_AXIS) {
[2328]1279            _cubeAxesActor->SetYAxisTickVisibility((state ? 1 : 0));
[2100]1280        } else if (axis == Z_AXIS) {
[2328]1281            _cubeAxesActor->SetZAxisTickVisibility((state ? 1 : 0));
[2100]1282        }
1283        _needsRedraw = true;
1284    }
[3330]1285}
1286
1287/**
1288 * \brief Toggle tick visibility for the specified axis
1289 */
1290void Renderer::setAxisMinorTickVisibility(Axis axis, bool state)
1291{
1292    if (_cubeAxesActor != NULL) {
[2112]1293        if (axis == X_AXIS) {
[3330]1294            _cubeAxesActor->SetXAxisMinorTickVisibility((state ? 1 : 0));
[2112]1295        } else if (axis == Y_AXIS) {
[3330]1296            _cubeAxesActor->SetYAxisMinorTickVisibility((state ? 1 : 0));
[2434]1297        } else if (axis == Z_AXIS) {
[3330]1298            _cubeAxesActor->SetZAxisMinorTickVisibility((state ? 1 : 0));
[2112]1299        }
1300        _needsRedraw = true;
1301    }
[2100]1302}
1303
1304/**
[3330]1305 * \brief Set color of axis lines, ticks, labels, titles
1306 */
1307void Renderer::setAxisColor(Axis axis, double color[3], double opacity)
1308{
1309    setAxisTitleColor(axis, color, opacity);
1310    setAxisLabelColor(axis, color, opacity);
1311    setAxisLinesColor(axis, color, opacity);
1312    setAxisGridlinesColor(axis, color, opacity);
1313    setAxisInnerGridlinesColor(axis, color, opacity);
1314    setAxisGridpolysColor(axis, color, opacity);
1315}
1316
1317/**
1318 * \brief Set color of axis title text
1319 */
1320void Renderer::setAxisTitleColor(Axis axis, double color[3], double opacity)
1321{
1322    if (_cubeAxesActor != NULL) {
1323        _cubeAxesActor->GetTitleTextProperty(axis)->SetColor(color);
1324        _cubeAxesActor->GetTitleTextProperty(axis)->SetOpacity(opacity);
1325        _needsRedraw = true;
1326    }
1327}
1328
1329/**
1330 * \brief Set color of axis label text
1331 */
1332void Renderer::setAxisLabelColor(Axis axis, double color[3], double opacity)
1333{
1334    if (_cubeAxesActor != NULL) {
1335        _cubeAxesActor->GetLabelTextProperty(axis)->SetColor(color);
1336        _cubeAxesActor->GetLabelTextProperty(axis)->SetOpacity(opacity);
1337        _needsRedraw = true;
1338    }
1339}
1340
1341/**
1342 * \brief Set color of main axis line
1343 */
1344void Renderer::setAxisLinesColor(Axis axis, double color[3], double opacity)
1345{
1346    if (_cubeAxesActor != NULL) {
1347        switch (axis) {
1348        case X_AXIS:
1349            _cubeAxesActor->GetXAxesLinesProperty()->SetColor(color);
1350            _cubeAxesActor->GetXAxesLinesProperty()->SetOpacity(opacity);
1351            break;
1352        case Y_AXIS:
1353            _cubeAxesActor->GetYAxesLinesProperty()->SetColor(color);
1354            _cubeAxesActor->GetYAxesLinesProperty()->SetOpacity(opacity);
1355            break;
1356        case Z_AXIS:
1357            _cubeAxesActor->GetZAxesLinesProperty()->SetColor(color);
1358            _cubeAxesActor->GetZAxesLinesProperty()->SetOpacity(opacity);
1359            break;
1360        default:
1361            ;
1362        }
1363        _needsRedraw = true;
1364    }
1365}
1366
1367/**
1368 * \brief Set color of axis gridlines
1369 */
1370void Renderer::setAxisGridlinesColor(Axis axis, double color[3], double opacity)
1371{
1372    if (_cubeAxesActor != NULL) {
1373        switch (axis) {
1374        case X_AXIS:
1375            _cubeAxesActor->GetXAxesGridlinesProperty()->SetColor(color);
1376            _cubeAxesActor->GetXAxesGridlinesProperty()->SetOpacity(opacity);
1377            break;
1378        case Y_AXIS:
1379            _cubeAxesActor->GetYAxesGridlinesProperty()->SetColor(color);
1380            _cubeAxesActor->GetYAxesGridlinesProperty()->SetOpacity(opacity);
1381            break;
1382        case Z_AXIS:
1383            _cubeAxesActor->GetZAxesGridlinesProperty()->SetColor(color);
1384            _cubeAxesActor->GetZAxesGridlinesProperty()->SetOpacity(opacity);
1385            break;
1386        default:
1387            ;
1388        }
1389        _needsRedraw = true;
1390    }
1391}
1392
1393/**
1394 * \brief Set color of axis inner gridlines
1395 */
1396void Renderer::setAxisInnerGridlinesColor(Axis axis, double color[3], double opacity)
1397{
1398    if (_cubeAxesActor != NULL) {
1399        switch (axis) {
1400        case X_AXIS:
1401            _cubeAxesActor->GetXAxesInnerGridlinesProperty()->SetColor(color);
1402            _cubeAxesActor->GetXAxesInnerGridlinesProperty()->SetOpacity(opacity);
1403            break;
1404        case Y_AXIS:
1405            _cubeAxesActor->GetYAxesInnerGridlinesProperty()->SetColor(color);
1406            _cubeAxesActor->GetYAxesInnerGridlinesProperty()->SetOpacity(opacity);
1407            break;
1408        case Z_AXIS:
1409            _cubeAxesActor->GetZAxesInnerGridlinesProperty()->SetColor(color);
1410            _cubeAxesActor->GetZAxesInnerGridlinesProperty()->SetOpacity(opacity);
1411            break;
1412        default:
1413            ;
1414        }
1415        _needsRedraw = true;
1416    }
1417}
1418
1419/**
1420 * \brief Set color of axis inner grid polygons
1421 *
1422 * Default opacity is 0.6
1423 */
1424void Renderer::setAxisGridpolysColor(Axis axis, double color[3], double opacity)
1425{
1426    if (_cubeAxesActor != NULL) {
1427        switch (axis) {
1428        case X_AXIS:
1429            _cubeAxesActor->GetXAxesGridpolysProperty()->SetColor(color);
1430            _cubeAxesActor->GetXAxesGridpolysProperty()->SetOpacity(opacity);
1431            break;
1432        case Y_AXIS:
1433            _cubeAxesActor->GetYAxesGridpolysProperty()->SetColor(color);
1434            _cubeAxesActor->GetYAxesGridpolysProperty()->SetOpacity(opacity);
1435            break;
1436        case Z_AXIS:
1437            _cubeAxesActor->GetZAxesGridpolysProperty()->SetColor(color);
1438            _cubeAxesActor->GetZAxesGridpolysProperty()->SetOpacity(opacity);
1439            break;
1440        default:
1441            ;
1442        }
1443        _needsRedraw = true;
1444    }
1445}
1446
1447/**
[2112]1448 * \brief Set title of the specified axis
1449 */
1450void Renderer::setAxisTitle(Axis axis, const char *title)
1451{
1452    if (_cubeAxesActor != NULL) {
1453        if (axis == X_AXIS) {
1454            _cubeAxesActor->SetXTitle(title);
1455        } else if (axis == Y_AXIS) {
1456            _cubeAxesActor->SetYTitle(title);
1457        } else if (axis == Z_AXIS) {
1458            _cubeAxesActor->SetZTitle(title);
1459        }
1460        _needsRedraw = true;
1461    }
1462}
1463
1464/**
1465 * \brief Set units of the specified axis
1466 */
1467void Renderer::setAxisUnits(Axis axis, const char *units)
1468{
1469    if (_cubeAxesActor != NULL) {
1470        if (axis == X_AXIS) {
1471            _cubeAxesActor->SetXUnits(units);
1472        } else if (axis == Y_AXIS) {
1473            _cubeAxesActor->SetYUnits(units);
1474        } else if (axis == Z_AXIS) {
1475            _cubeAxesActor->SetZUnits(units);
1476        }
1477        _needsRedraw = true;
1478    }
[3330]1479}
1480
1481/**
1482 * \brief Set axis title font family
1483 */
1484void Renderer::setAxisTitleFont(Axis axis, const char *fontName)
1485{
1486    if (_cubeAxesActor != NULL) {
1487        TRACE("Setting axis %d title font to: '%s'", axis, fontName);
1488        _cubeAxesActor->GetTitleTextProperty(axis)->SetFontFamilyAsString(fontName);
1489        _needsRedraw = true;
1490    }
1491}
1492
1493/**
1494 * \brief Set axis title font size (and optionally pixel size)
1495 */
1496void Renderer::setAxisTitleFontSize(Axis axis, int sz)
1497{
1498    if (_cubeAxesActor != NULL) {
1499        TRACE("Setting axis %d title font size to: %d", axis, sz);
1500        _cubeAxesActor->GetTitleTextProperty(axis)->SetFontSize(sz);
1501        _needsRedraw = true;
1502    }
1503}
1504
1505/**
1506 * \brief Set orientation (as rotation in degrees) of axis titles
1507 */
1508void Renderer::setAxisTitleOrientation(Axis axis, double orientation)
1509{
1510    if (_cubeAxesActor != NULL) {
1511        TRACE("Setting axis %d title orientation to: %g", axis, orientation);
1512        _cubeAxesActor->GetTitleTextProperty(axis)->SetOrientation(orientation);
1513        _needsRedraw = true;
1514    }
1515}
1516
1517/**
1518 * \brief Set axis label font family
1519 */
1520void Renderer::setAxisLabelFont(Axis axis, const char *fontName)
1521{
1522    if (_cubeAxesActor != NULL) {
1523        TRACE("Setting axis %d label font to: '%s'", axis, fontName);
1524        _cubeAxesActor->GetLabelTextProperty(axis)->SetFontFamilyAsString(fontName);
1525        _needsRedraw = true;
1526    }
1527}
1528
1529/**
1530 * \brief Set axis label font size (and optionally pixel size)
1531 */
1532void Renderer::setAxisLabelFontSize(Axis axis, int sz)
1533{
1534    if (_cubeAxesActor != NULL) {
1535        TRACE("Setting axis %d label font size to: %d", axis, sz);
1536        _cubeAxesActor->GetLabelTextProperty(axis)->SetFontSize(sz);
1537        _needsRedraw = true;
1538    }
1539}
1540
1541/**
1542 * \brief Set orientation (as rotation in degrees) of axis labels
1543 */
1544void Renderer::setAxisLabelOrientation(Axis axis, double orientation)
1545{
1546    if (_cubeAxesActor != NULL) {
1547        TRACE("Setting axis %d label orientation to: %g", axis, orientation);
1548        _cubeAxesActor->GetLabelTextProperty(axis)->SetOrientation(orientation);
1549        _needsRedraw = true;
1550    }
1551}
1552
1553/**
1554 * \brief Set printf style label format string
1555 */
1556void Renderer::setAxisLabelFormat(Axis axis, const char *format)
1557{
1558    if (_cubeAxesActor != NULL) {
1559        TRACE("Setting axis %d label format to: '%s'", axis, format);
[2112]1560        if (axis == X_AXIS) {
[3330]1561            _cubeAxesActor->SetXLabelFormat(format);
[4141]1562#ifdef USE_CUSTOM_AXES
[3479]1563            _cubeAxesActor->XAutoLabelFormatOff();
[4141]1564#endif
[2112]1565        } else if (axis == Y_AXIS) {
[3330]1566            _cubeAxesActor->SetYLabelFormat(format);
[4141]1567#ifdef USE_CUSTOM_AXES
[3479]1568            _cubeAxesActor->YAutoLabelFormatOff();
[4141]1569#endif
[2434]1570        } else if (axis == Z_AXIS) {
[3330]1571            _cubeAxesActor->SetZLabelFormat(format);
[4141]1572#ifdef USE_CUSTOM_AXES
[3479]1573            _cubeAxesActor->ZAutoLabelFormatOff();
[4141]1574#endif
[2112]1575        }
1576        _needsRedraw = true;
1577    }
1578}
1579
1580/**
[2402]1581 * \brief Notify graphics objects that color map has changed
[2100]1582 */
[2402]1583void Renderer::updateColorMap(ColorMap *cmap)
1584{
[3330]1585    TRACE("%s", cmap->getName().c_str());
[3164]1586    updateGraphicsObjectColorMap<Contour2D>(cmap);
1587    updateGraphicsObjectColorMap<Contour3D>(cmap);
1588    updateGraphicsObjectColorMap<Cutplane>(cmap);
1589    updateGraphicsObjectColorMap<Glyphs>(cmap);
1590    updateGraphicsObjectColorMap<HeightMap>(cmap);
[3818]1591    updateGraphicsObjectColorMap<Image>(cmap);
[4082]1592    updateGraphicsObjectColorMap<ImageCutplane>(cmap);
[3164]1593    updateGraphicsObjectColorMap<LIC>(cmap);
1594    updateGraphicsObjectColorMap<Molecule>(cmap);
[3818]1595    updateGraphicsObjectColorMap<PolyData>(cmap);
[3164]1596    updateGraphicsObjectColorMap<PseudoColor>(cmap);
1597    updateGraphicsObjectColorMap<Streamlines>(cmap);
1598    updateGraphicsObjectColorMap<Volume>(cmap);
1599    updateGraphicsObjectColorMap<Warp>(cmap);
[3330]1600    TRACE("Leave");
[2402]1601}
1602
1603/**
1604 * \brief Check if a ColorMap is in use by graphics objects
1605 */
1606bool Renderer::colorMapUsed(ColorMap *cmap)
1607{
[3164]1608    if (graphicsObjectColorMapUsed<Contour2D>(cmap))
1609        return true;
1610    if (graphicsObjectColorMapUsed<Contour3D>(cmap))
1611        return true;
1612    if (graphicsObjectColorMapUsed<Cutplane>(cmap))
1613        return true;
1614    if (graphicsObjectColorMapUsed<Glyphs>(cmap))
1615        return true;
1616    if (graphicsObjectColorMapUsed<HeightMap>(cmap))
1617        return true;
[3818]1618    if (graphicsObjectColorMapUsed<Image>(cmap))
1619        return true;
[4082]1620    if (graphicsObjectColorMapUsed<ImageCutplane>(cmap))
1621        return true;
[3164]1622    if (graphicsObjectColorMapUsed<LIC>(cmap))
1623        return true;
1624    if (graphicsObjectColorMapUsed<Molecule>(cmap))
1625        return true;
[3818]1626    if (graphicsObjectColorMapUsed<PolyData>(cmap))
1627        return true;
[3164]1628    if (graphicsObjectColorMapUsed<PseudoColor>(cmap))
1629        return true;
1630    if (graphicsObjectColorMapUsed<Streamlines>(cmap))
1631        return true;
1632    if (graphicsObjectColorMapUsed<Volume>(cmap))
1633        return true;
1634    if (graphicsObjectColorMapUsed<Warp>(cmap))
1635        return true;
1636
[2402]1637    return false;
1638}
1639
1640/**
1641 * \brief Add/replace a ColorMap for use in the Renderer
1642 */
[2123]1643void Renderer::addColorMap(const ColorMapId& id, ColorMap *colorMap)
[2100]1644{
[2112]1645    if (colorMap != NULL) {
1646        colorMap->build();
1647        if (getColorMap(id) != NULL) {
[2402]1648            TRACE("Replacing existing ColorMap %s", id.c_str());
1649            // Copy to current colormap to avoid invalidating
1650            // pointers in graphics objects using the color map
1651            *_colorMaps[id] = *colorMap;
1652            delete colorMap;
1653            // Notify graphics objects of change
1654            updateColorMap(_colorMaps[id]);
1655        } else
1656            _colorMaps[id] = colorMap;
[2112]1657    } else {
1658        ERROR("NULL ColorMap");
[2100]1659    }
1660}
1661
1662/**
[2112]1663 * \brief Return the ColorMap associated with the colormap key given
[2100]1664 */
[2123]1665ColorMap *Renderer::getColorMap(const ColorMapId& id)
[2100]1666{
1667    ColorMapHashmap::iterator itr = _colorMaps.find(id);
1668
1669    if (itr == _colorMaps.end())
1670        return NULL;
1671    else
1672        return itr->second;
1673}
1674
1675/**
1676 * \brief Remove the colormap associated with the key given
1677 *
1678 * The underlying vtkLookupTable will be deleted if it is not referenced
1679 * by any other objects
1680 */
[2123]1681void Renderer::deleteColorMap(const ColorMapId& id)
[2100]1682{
[2124]1683    ColorMapHashmap::iterator itr;
1684
1685    bool doAll = false;
1686
1687    if (id.compare("all") == 0) {
1688        itr = _colorMaps.begin();
1689        doAll = true;
1690    } else {
1691        itr = _colorMaps.find(id);
1692    }
1693
1694    if (itr == _colorMaps.end()) {
1695        ERROR("Unknown ColorMap %s", id.c_str());
[2100]1696        return;
[2124]1697    }
[2100]1698
[2124]1699    do {
[2402]1700        if (itr->second->getName().compare("default") == 0 ||
[2404]1701            itr->second->getName().compare("grayDefault") == 0 ||
[2402]1702            itr->second->getName().compare("volumeDefault") == 0 ||
1703            itr->second->getName().compare("elementDefault") == 0) {
1704            if (id.compare("all") != 0) {
1705                WARN("Cannot delete a default color map");
1706            }
1707            continue;
1708        } else if (colorMapUsed(itr->second)) {
1709            WARN("Cannot delete color map '%s', it is in use", itr->second->getName().c_str());
1710            continue;
1711        }
1712
[2146]1713        TRACE("Deleting ColorMap %s", itr->second->getName().c_str());
1714
[2124]1715        delete itr->second;
[2277]1716        itr = _colorMaps.erase(itr);
1717    } while (doAll && itr != _colorMaps.end());
[2100]1718}
1719
[2627]1720/**
[3330]1721 * \brief Set the number of discrete colors used in the colormap's lookup table
1722 *
1723 * Note that the number of table entries is independent of the number of
1724 * control points in the color/alpha ramp
1725 */
1726void Renderer::setColorMapNumberOfTableEntries(const ColorMapId& id, int numEntries)
1727{
1728    ColorMapHashmap::iterator itr;
1729
1730    bool doAll = false;
1731
1732    if (id.compare("all") == 0) {
1733        itr = _colorMaps.begin();
1734        doAll = true;
1735    } else {
1736        itr = _colorMaps.find(id);
1737    }
1738
1739    if (itr == _colorMaps.end()) {
1740        ERROR("Unknown ColorMap %s", id.c_str());
1741        return;
1742    }
1743
1744    if (numEntries < 0) {
1745        numEntries = 256;
1746        TRACE("Setting numEntries to default value of %d", numEntries);
1747    }
1748
1749    do {
1750        if (itr->second->getName() == "elementDefault") {
1751            TRACE("Can't change number of table entries for default element color map");
1752        } else {
1753            itr->second->setNumberOfTableEntries(numEntries);
1754            updateColorMap(itr->second);
1755        }
1756    } while (doAll && ++itr != _colorMaps.end());
1757
1758    _needsRedraw = true;
1759}
1760
1761/**
[2627]1762 * \brief Render a labelled legend image for the given colormap
1763 *
1764 * The field is assumed to be the active scalar or vector field
1765 * based on the legendType.
1766 *
1767 * \param[in] id ColorMap name
1768 * \param[in] dataSetID DataSet name
1769 * \param[in] legendType scalar or vector field legend
1770 * \param[in,out] title If supplied, draw title ("#auto" means to
1771 * fill in field name and draw).  If blank, do not draw title. 
1772 * If title was blank or "#auto", will be filled with field name on
1773 * return
1774 * \param[in,out] range Data range to use in legend.  Set min > max to have
1775 * range computed, will be filled with valid min and max values
1776 * \param[in] width Pixel width of legend (aspect controls orientation)
1777 * \param[in] height Pixel height of legend (aspect controls orientation)
[2865]1778 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
[2627]1779 * \param[in] numLabels Number of labels to render (includes min/max)
1780 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1781 * will be resized if needed.
1782 * \return The image is rendered into the supplied array, false is
1783 * returned if the color map is not found
1784 */
[2612]1785bool Renderer::renderColorMap(const ColorMapId& id,
1786                              const DataSetId& dataSetID,
1787                              Renderer::LegendType legendType,
1788                              std::string& title,
1789                              double range[2],
1790                              int width, int height,
[2729]1791                              bool opaque,
[2612]1792                              int numLabels,
1793                              vtkUnsignedCharArray *imgData)
1794{
1795    DataSet *dataSet = NULL;
1796    if (dataSetID.compare("all") == 0) {
1797        if (_dataSets.empty()) {
1798            WARN("No DataSets exist, can't fill title or range");
1799            return renderColorMap(id, dataSetID, legendType,
1800                                  NULL,
1801                                  DataSet::POINT_DATA,
[2729]1802                                  title, range, width, height, opaque, numLabels, imgData);
[2612]1803        } else {
1804            dataSet = _dataSets.begin()->second;
1805        }
1806    } else {
1807        dataSet = getDataSet(dataSetID);
1808        if (dataSet == NULL) {
1809            ERROR("DataSet '%s' not found", dataSetID.c_str());
1810            return false;
1811        }
1812    }
1813
1814    if (legendType == LEGEND_SCALAR) {
1815        return renderColorMap(id, dataSetID, legendType,
1816                              dataSet->getActiveScalarsName(),
1817                              dataSet->getActiveScalarsType(),
[2729]1818                              title, range, width, height, opaque, numLabels, imgData);
[2612]1819    } else {
1820        return renderColorMap(id, dataSetID, legendType,
1821                              dataSet->getActiveVectorsName(),
1822                              dataSet->getActiveVectorsType(),
[2729]1823                              title, range, width, height, opaque, numLabels, imgData);
[2612]1824    }
1825}
1826
[2627]1827/**
1828 * \brief Render a labelled legend image for the given colormap
1829 *
1830 * The field is assumed to be point data, if the field is not found
1831 * as point data, cell data is used.
1832 *
1833 * \param[in] id ColorMap name
1834 * \param[in] dataSetID DataSet name
1835 * \param[in] legendType scalar or vector field legend
1836 * \param[in] fieldName Name of the field array this legend is for
1837 * \param[in,out] title If supplied, draw title ("#auto" means to
1838 * fill in field name and draw).  If blank, do not draw title. 
1839 * If title was blank or "#auto", will be filled with field name on
1840 * return
1841 * \param[in,out] range Data range to use in legend.  Set min > max to have
1842 * range computed, will be filled with valid min and max values
1843 * \param[in] width Pixel width of legend (aspect controls orientation)
1844 * \param[in] height Pixel height of legend (aspect controls orientation)
[2865]1845 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
[2627]1846 * \param[in] numLabels Number of labels to render (includes min/max)
1847 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1848 * will be resized if needed.
1849 * \return The image is rendered into the supplied array, false is
1850 * returned if the color map is not found
1851 */
[2612]1852bool Renderer::renderColorMap(const ColorMapId& id,
1853                              const DataSetId& dataSetID,
1854                              Renderer::LegendType legendType,
1855                              const char *fieldName,
1856                              std::string& title,
1857                              double range[2],
1858                              int width, int height,
[2729]1859                              bool opaque,
[2612]1860                              int numLabels,
1861                              vtkUnsignedCharArray *imgData)
1862{
1863    DataSet *dataSet = NULL;
1864    if (dataSetID.compare("all") == 0) {
1865        if (_dataSets.empty()) {
1866            WARN("No DataSets exist, can't fill title or range");
1867            return renderColorMap(id, dataSetID, legendType,
1868                                  NULL,
1869                                  DataSet::POINT_DATA,
[2729]1870                                  title, range, width, height, opaque, numLabels, imgData);
[2612]1871        } else {
1872            dataSet = _dataSets.begin()->second;
1873        }
1874    } else {
1875        dataSet = getDataSet(dataSetID);
1876        if (dataSet == NULL) {
1877            ERROR("DataSet '%s' not found", dataSetID.c_str());
1878            return false;
1879        }
1880    }
1881
1882    DataSet::DataAttributeType attrType;
1883    int numComponents;
1884
1885    dataSet->getFieldInfo(fieldName, &attrType, &numComponents);
1886
1887    return renderColorMap(id, dataSetID, legendType,
1888                          fieldName,
1889                          attrType,
[2729]1890                          title, range, width, height, opaque, numLabels, imgData);
[2612]1891}
1892
[2100]1893/**
1894 * \brief Render a labelled legend image for the given colormap
1895 *
[2471]1896 * \param[in] id ColorMap name
1897 * \param[in] dataSetID DataSet name
1898 * \param[in] legendType scalar or vector field legend
[2627]1899 * \param[in] fieldName Name of the field array this legend is for
1900 * \param[in] type DataAttributeType of the field
[2471]1901 * \param[in,out] title If supplied, draw title ("#auto" means to
1902 * fill in field name and draw).  If blank, do not draw title. 
1903 * If title was blank or "#auto", will be filled with field name on
1904 * return
[2627]1905 * \param[in,out] range Data range to use in legend.  Set min > max to have
1906 * range computed, will be filled with valid min and max values
[2471]1907 * \param[in] width Pixel width of legend (aspect controls orientation)
1908 * \param[in] height Pixel height of legend (aspect controls orientation)
[2865]1909 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
[2471]1910 * \param[in] numLabels Number of labels to render (includes min/max)
1911 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1912 * will be resized if needed.
[2123]1913 * \return The image is rendered into the supplied array, false is
1914 * returned if the color map is not found
[2100]1915 */
[2146]1916bool Renderer::renderColorMap(const ColorMapId& id,
1917                              const DataSetId& dataSetID,
[2453]1918                              Renderer::LegendType legendType,
[2612]1919                              const char *fieldName,
1920                              DataSet::DataAttributeType type,
[2471]1921                              std::string& title,
1922                              double range[2],
[2100]1923                              int width, int height,
[2729]1924                              bool opaque,
[2467]1925                              int numLabels,
[2100]1926                              vtkUnsignedCharArray *imgData)
1927{
[2471]1928    TRACE("Enter");
[2123]1929    ColorMap *colorMap = getColorMap(id);
1930    if (colorMap == NULL)
1931        return false;
[4073]1932#ifdef LEGEND_SOFTWARE_RENDER
1933    ColorMap::renderColorMap(colorMap, width, height, imgData, opaque, _bgColor,
1934#ifdef RENDER_TARGA
1935                             true, TARGA_BYTES_PER_PIXEL
1936#else
1937                             false
1938#endif
1939                             );
1940#else
[2100]1941    if (_legendRenderWindow == NULL) {
1942        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
[2596]1943        _legendRenderWindow->SetMultiSamples(0);
[2254]1944#ifdef USE_OFFSCREEN_RENDERING
[2100]1945        _legendRenderWindow->DoubleBufferOff();
1946        _legendRenderWindow->OffScreenRenderingOn();
[2402]1947#else
1948        _legendRenderWindow->DoubleBufferOn();
1949        _legendRenderWindow->SwapBuffersOff();
[2254]1950#endif
[2100]1951    }
1952
1953    _legendRenderWindow->SetSize(width, height);
1954
1955    if (_legendRenderer == NULL) {
1956        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
1957        _legendRenderWindow->AddRenderer(_legendRenderer);
1958    }
1959    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
1960
1961    if (_scalarBarActor == NULL) {
1962        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
[3455]1963        _scalarBarActor->DrawFrameOff();
1964        _scalarBarActor->DrawBackgroundOff();
[2260]1965        _legendRenderer->AddViewProp(_scalarBarActor);
[2100]1966    }
[2146]1967
[2729]1968    if (opaque) {
1969        _scalarBarActor->UseOpacityOff();
1970    } else {
1971        _scalarBarActor->UseOpacityOn();
1972    }
1973
[2478]1974    if (width > height) {
1975        _scalarBarActor->SetOrientationToHorizontal();
1976    } else {
1977        _scalarBarActor->SetOrientationToVertical();
1978    }
1979
1980    // Set viewport-relative width/height/pos
1981    if (title.empty() && numLabels == 0) {
[3639]1982#ifdef NEW_SCALAR_BAR
1983        _scalarBarActor->SetBarRatio(1);
[3797]1984        _scalarBarActor->SetTitleRatio(0);
[3639]1985#endif
[2478]1986        if (width > height) {
1987            // horizontal
[3639]1988#ifdef NEW_SCALAR_BAR
1989            _scalarBarActor->SetDisplayPosition(0, 0);
1990            _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
1991            _scalarBarActor->GetPosition2Coordinate()->SetValue(width+4, height);
1992#else
1993            _scalarBarActor->SetPosition(0, 0);
[3455]1994            _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.4); // VTK: floor(actorHeight * .4)
1995            _scalarBarActor->SetWidth(1); // VTK: actorWidth
[3639]1996#endif
[2478]1997        } else {
1998            // vertical
[3639]1999#ifdef NEW_SCALAR_BAR
2000            _scalarBarActor->SetDisplayPosition(0, -4);
2001            _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
2002            _scalarBarActor->GetPosition2Coordinate()->SetValue(width+1, height+5);
2003#else
2004            _scalarBarActor->SetPosition(0, 0);
[3455]2005            _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.86); // VTK: floor(actorHeight * .86)
[2478]2006            _scalarBarActor->SetWidth(((double)(width+5))/((double)width)); // VTK: actorWidth - 4 pixels
[3639]2007#endif
[2478]2008        }
2009    } else {
[3639]2010#ifdef NEW_SCALAR_BAR
2011        _scalarBarActor->SetBarRatio(0.375);
[3797]2012        _scalarBarActor->SetTitleRatio(0.5);
[3639]2013        _scalarBarActor->SetDisplayPosition(0, 0);
2014        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
2015        _scalarBarActor->GetPosition2Coordinate()->SetValue(width, height);
2016#else
[2478]2017        if (width > height) {
2018            // horizontal
2019            _scalarBarActor->SetPosition(.075, .1);
2020            _scalarBarActor->SetHeight(0.8);
2021            _scalarBarActor->SetWidth(0.85);
2022        } else {
2023            // vertical
2024            _scalarBarActor->SetPosition(.1, .05);
2025            _scalarBarActor->SetHeight(0.9);
2026            _scalarBarActor->SetWidth(0.8);
2027        }
[3639]2028#endif
[2478]2029    }
2030
[2146]2031    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
[2453]2032    DataSet *dataSet = NULL;
2033    bool cumulative = _useCumulativeRange;
[2146]2034    if (dataSetID.compare("all") == 0) {
[2471]2035        if (_dataSets.empty()) {
2036            WARN("No DataSets exist, can't fill title or range");
2037        } else {
2038            dataSet = _dataSets.begin()->second;
2039        }
[2453]2040        cumulative = true;
[2146]2041    } else {
[2453]2042        dataSet = getDataSet(dataSetID);
[2146]2043        if (dataSet == NULL) {
[2471]2044            ERROR("DataSet '%s' not found", dataSetID.c_str());
2045            return false;
[2453]2046        }
2047    }
2048
[2471]2049    bool drawTitle = false;
2050    if (!title.empty()) {
2051        drawTitle = true;
2052        if (title.compare("#auto") == 0) {
2053            title.clear();
2054        }
2055    }
2056
[2627]2057    bool needRange = false;
2058    if (range[0] > range[1]) {
2059        range[0] = 0.0;
2060        range[1] = 1.0;
2061        needRange = true;
2062    }
[2471]2063
[2453]2064    switch (legendType) {
[2612]2065    case LEGEND_VECTOR_MAGNITUDE:
[2627]2066        if (needRange) {
2067            if (cumulative) {
2068                getCumulativeDataRange(range, fieldName, type, 3);
2069            } else if (dataSet != NULL) {
2070                dataSet->getDataRange(range, fieldName, type);
2071            }
[2453]2072        }
[2612]2073
2074        lut->SetRange(range);
2075
[2471]2076        if (title.empty() && dataSet != NULL) {
[2612]2077            if (fieldName != NULL) {
2078                title = fieldName;
[2471]2079                title.append("(mag)");
2080            }
2081        }
[2453]2082        break;
[2612]2083    case LEGEND_VECTOR_X:
[2627]2084        if (needRange) {
2085            if (cumulative) {
2086                getCumulativeDataRange(range, fieldName, type, 3, 0);
2087            } else if (dataSet != NULL) {
2088                dataSet->getDataRange(range, fieldName, type, 0);
2089            }
[2453]2090        }
[2612]2091
2092        lut->SetRange(range);
2093
[2471]2094        if (title.empty() && dataSet != NULL) {
[2612]2095            if (fieldName != NULL) {
2096                title = fieldName;
[2471]2097                title.append("(x)");
2098            }
2099        }
[2453]2100        break;
[2612]2101    case LEGEND_VECTOR_Y:
[2627]2102        if (needRange) {
2103            if (cumulative) {
2104                getCumulativeDataRange(range, fieldName, type, 3, 1);
2105            } else if (dataSet != NULL) {
2106                dataSet->getDataRange(range, fieldName, type, 1);
2107            }
[2453]2108        }
[2612]2109
2110        lut->SetRange(range);
2111
[2471]2112        if (title.empty() && dataSet != NULL) {
[2612]2113            if (fieldName != NULL) {
2114                title = fieldName;
[2471]2115                title.append("(y)");
2116            }
2117        }
[2453]2118        break;
[2612]2119    case LEGEND_VECTOR_Z:
[2627]2120        if (needRange) {
2121            if (cumulative) {
2122                getCumulativeDataRange(range, fieldName, type, 3, 2);
2123            } else if (dataSet != NULL) {
2124                dataSet->getDataRange(range, fieldName, type, 1);
2125            }
[2453]2126        }
[2612]2127
2128        lut->SetRange(range);
2129
[2471]2130        if (title.empty() && dataSet != NULL) {
[2612]2131            if (fieldName != NULL) {
2132                title = fieldName;
[2471]2133                title.append("(z)");
2134            }
2135        }
[2453]2136        break;
[2612]2137    case LEGEND_SCALAR:
[2453]2138    default:
[2627]2139        if (needRange) {
2140            if (cumulative) {
2141                getCumulativeDataRange(range, fieldName, type, 1);
2142            } else if (dataSet != NULL) {
2143                dataSet->getDataRange(range, fieldName, type);
2144            }
2145        }
[2612]2146
2147        lut->SetRange(range);
2148
[2471]2149        if (title.empty() && dataSet != NULL) {
[2612]2150            if (fieldName != NULL)
2151                title = fieldName;
[2471]2152        }
[2453]2153        break;
[2146]2154    }
[2453]2155
[2146]2156    _scalarBarActor->SetLookupTable(lut);
[3455]2157    _scalarBarActor->SetMaximumNumberOfColors((width > height ? width : height));
[2146]2158
[2471]2159    if (drawTitle) {
2160        _scalarBarActor->SetTitle(title.c_str());
2161    } else {
2162        _scalarBarActor->SetTitle("");
2163    }
[3639]2164
2165    double color[3];
2166    color[0] = 1 - _bgColor[0];
2167    color[1] = 1 - _bgColor[1];
2168    color[2] = 1 - _bgColor[2];
2169
2170    _scalarBarActor->GetTitleTextProperty()->SetColor(color);
2171    _scalarBarActor->GetTitleTextProperty()->BoldOff();
[2100]2172    _scalarBarActor->GetTitleTextProperty()->ItalicOff();
[3639]2173    _scalarBarActor->GetTitleTextProperty()->ShadowOff();
[2471]2174    _scalarBarActor->SetNumberOfLabels(numLabels);
[3639]2175    _scalarBarActor->GetLabelTextProperty()->SetColor(color);
[2100]2176    _scalarBarActor->GetLabelTextProperty()->BoldOff();
2177    _scalarBarActor->GetLabelTextProperty()->ItalicOff();
2178    _scalarBarActor->GetLabelTextProperty()->ShadowOff();
[3639]2179#ifdef NEW_SCALAR_BAR
[3797]2180    if (!drawTitle && numLabels == 0) {
[3639]2181        _scalarBarActor->DrawAnnotationsOff();
2182        _scalarBarActor->SetAnnotationLeaderPadding(0);
2183        _scalarBarActor->SetTextPad(0);
2184    } else {
2185        _scalarBarActor->DrawAnnotationsOn();
2186        _scalarBarActor->SetAnnotationLeaderPadding(8);
2187        _scalarBarActor->SetTextPad(1);
2188    }
2189#endif
[2100]2190
2191    _legendRenderWindow->Render();
[3828]2192    int *sz = _legendRenderWindow->GetSize();
2193    if (sz[0] != width || sz[1] != height) {
2194        ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], width, height);
2195    }
[2100]2196
[2260]2197#ifdef RENDER_TARGA
2198    _legendRenderWindow->MakeCurrent();
2199    // Must clear previous errors first.
2200    while (glGetError() != GL_NO_ERROR){
2201        ;
2202    }
2203    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
2204    int size = bytesPerPixel * width * height;
2205
2206    if (imgData->GetMaxId() + 1 != size)
2207    {
2208        imgData->SetNumberOfComponents(bytesPerPixel);
2209        imgData->SetNumberOfValues(size);
2210    }
2211    glDisable(GL_TEXTURE_2D);
[2596]2212    if (_legendRenderWindow->GetDoubleBuffer()) {
2213        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetBackLeftBuffer()));
2214    } else {
2215        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetFrontLeftBuffer()));
2216    }
[2260]2217    glPixelStorei(GL_PACK_ALIGNMENT, 1);
2218    if (bytesPerPixel == 4) {
2219        glReadPixels(0, 0, width, height, GL_BGRA,
2220                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2221    } else {
2222        glReadPixels(0, 0, width, height, GL_BGR,
2223                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2224    }
2225    if (glGetError() != GL_NO_ERROR) {
2226        ERROR("glReadPixels");
2227    }
2228#else
[2254]2229    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
2230                                      !_legendRenderWindow->GetDoubleBuffer(),
2231                                      imgData);
[2260]2232#endif
[4073]2233#endif
[2471]2234    TRACE("Leave");
[2123]2235    return true;
[2100]2236}
2237
[3818]2238bool Renderer::renderColorMap(const ColorMapId& id,
2239                              int width, int height,
2240                              bool opaque,
2241                              vtkUnsignedCharArray *imgData)
2242{
2243    TRACE("Enter");
2244    ColorMap *colorMap = getColorMap(id);
2245    if (colorMap == NULL)
2246        return false;
[4073]2247#ifdef LEGEND_SOFTWARE_RENDER
2248    ColorMap::renderColorMap(colorMap, width, height, imgData, opaque, _bgColor,
2249#ifdef RENDER_TARGA
2250                             true, TARGA_BYTES_PER_PIXEL
2251#else
2252                             false
2253#endif
2254                             );
2255#else
[3818]2256    if (_legendRenderWindow == NULL) {
2257        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
2258        _legendRenderWindow->SetMultiSamples(0);
2259#ifdef USE_OFFSCREEN_RENDERING
2260        _legendRenderWindow->DoubleBufferOff();
2261        _legendRenderWindow->OffScreenRenderingOn();
2262#else
2263        _legendRenderWindow->DoubleBufferOn();
2264        _legendRenderWindow->SwapBuffersOff();
2265#endif
2266    }
2267
2268    _legendRenderWindow->SetSize(width, height);
2269
2270    if (_legendRenderer == NULL) {
2271        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
2272        _legendRenderWindow->AddRenderer(_legendRenderer);
2273    }
2274    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
2275
2276    if (_scalarBarActor == NULL) {
2277        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
2278        _scalarBarActor->DrawFrameOff();
2279        _scalarBarActor->DrawBackgroundOff();
2280        _scalarBarActor->DrawColorBarOn();
2281        _legendRenderer->AddViewProp(_scalarBarActor);
2282    }
2283
2284    if (opaque) {
2285        _scalarBarActor->UseOpacityOff();
2286    } else {
2287        _scalarBarActor->UseOpacityOn();
2288    }
2289
2290    if (width > height) {
2291        _scalarBarActor->SetOrientationToHorizontal();
2292    } else {
2293        _scalarBarActor->SetOrientationToVertical();
2294    }
2295
2296    // Set viewport-relative width/height/pos
2297#ifdef NEW_SCALAR_BAR
2298    _scalarBarActor->SetBarRatio(1);
2299    _scalarBarActor->SetTitleRatio(0);
2300#endif
2301    if (width > height) {
2302        // horizontal
2303#ifdef NEW_SCALAR_BAR
2304        _scalarBarActor->SetDisplayPosition(0, 0);
2305        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
2306        _scalarBarActor->GetPosition2Coordinate()->SetValue(width+4, height);
2307#else
2308        _scalarBarActor->SetPosition(0, 0);
2309        _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.4); // VTK: floor(actorHeight * .4)
2310        _scalarBarActor->SetWidth(1); // VTK: actorWidth
2311#endif
2312    } else {
2313        // vertical
2314#ifdef NEW_SCALAR_BAR
2315        _scalarBarActor->SetDisplayPosition(0, -4);
2316        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
2317        _scalarBarActor->GetPosition2Coordinate()->SetValue(width+1, height+5);
2318#else
2319        _scalarBarActor->SetPosition(0, 0);
2320        _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.86); // VTK: floor(actorHeight * .86)
2321        _scalarBarActor->SetWidth(((double)(width+5))/((double)width)); // VTK: actorWidth - 4 pixels
2322#endif
2323    }
2324
2325    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
2326
2327    double range[2];
2328    range[0] = 0.0;
2329    range[1] = 1.0;
2330
2331    lut->SetRange(range);
2332
2333    _scalarBarActor->SetLookupTable(lut);
2334    _scalarBarActor->SetMaximumNumberOfColors((width > height ? width : height));
2335    _scalarBarActor->SetTitle("");
2336    _scalarBarActor->SetNumberOfLabels(0);
2337#ifdef NEW_SCALAR_BAR
2338    _scalarBarActor->DrawAnnotationsOff();
2339    _scalarBarActor->SetAnnotationLeaderPadding(0);
2340    _scalarBarActor->SetTextPad(0);
2341#endif
2342
2343    _legendRenderWindow->Render();
[3828]2344    int *sz = _legendRenderWindow->GetSize();
2345    if (sz[0] != width || sz[1] != height) {
2346        ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], width, height);
2347    }
[3818]2348
2349#ifdef RENDER_TARGA
2350    _legendRenderWindow->MakeCurrent();
2351    // Must clear previous errors first.
2352    while (glGetError() != GL_NO_ERROR){
2353        ;
2354    }
2355    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
2356    int size = bytesPerPixel * width * height;
2357
2358    if (imgData->GetMaxId() + 1 != size)
2359    {
2360        imgData->SetNumberOfComponents(bytesPerPixel);
2361        imgData->SetNumberOfValues(size);
2362    }
2363    glDisable(GL_TEXTURE_2D);
2364    if (_legendRenderWindow->GetDoubleBuffer()) {
2365        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetBackLeftBuffer()));
2366    } else {
2367        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetFrontLeftBuffer()));
2368    }
2369    glPixelStorei(GL_PACK_ALIGNMENT, 1);
2370    if (bytesPerPixel == 4) {
2371        glReadPixels(0, 0, width, height, GL_BGRA,
2372                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2373    } else {
2374        glReadPixels(0, 0, width, height, GL_BGR,
2375                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2376    }
2377    if (glGetError() != GL_NO_ERROR) {
2378        ERROR("glReadPixels");
2379    }
2380#else
2381    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
2382                                      !_legendRenderWindow->GetDoubleBuffer(),
2383                                      imgData);
2384#endif
[4073]2385#endif
[3818]2386    TRACE("Leave");
2387    return true;
2388}
2389
[2100]2390/**
[2320]2391 * \brief Set camera FOV based on render window height
2392 *
2393 * Computes a field-of-view angle based on some assumptions about
2394 * viewer's distance to screen and pixel density
2395 */
[2317]2396void Renderer::setViewAngle(int height)
2397{
2398    // Distance of eyes from screen in inches
2399    double d = 20.0;
[2320]2400    // Assume 72 ppi screen
[2317]2401    double h = (double)height / 72.0;
2402
2403    double angle = vtkMath::DegreesFromRadians(2.0 * atan((h/2.0)/d));
2404    _renderer->GetActiveCamera()->SetViewAngle(angle);
2405
2406    TRACE("Setting view angle: %g", angle);
2407}
2408
[2261]2409/**
[2100]2410 * \brief Resize the render window (image size for renderings)
2411 */
2412void Renderer::setWindowSize(int width, int height)
2413{
[2423]2414    if (_windowWidth == width &&
[2759]2415        _windowHeight == height) {
2416        TRACE("No change");
[2423]2417        return;
[2759]2418    }
[2317]2419
[2759]2420    TRACE("Setting window size to %dx%d", width, height);
2421
[2423]2422    //setViewAngle(height);
2423
[2260]2424    // FIXME: Fix up panning on aspect change
2425#ifdef notdef
2426    if (_cameraPan[0] != 0.0) {
2427        _cameraPan[0] *= ((double)_windowWidth / width);
2428    }
2429    if (_cameraPan[1] != 0.0) {
2430        _cameraPan[1] *= ((double)_windowHeight / height);
2431    }
2432#endif
[2100]2433    _windowWidth = width;
2434    _windowHeight = height;
2435    _renderWindow->SetSize(_windowWidth, _windowHeight);
[3419]2436
[2194]2437    if (_cameraMode == IMAGE) {
[3419]2438        if (_cameraAspect == ASPECT_WINDOW) {
2439            double imgWindowAspect = getImageCameraAspect();
2440            TRACE("Setting object aspect to %g", imgWindowAspect);
2441            setObjectAspects(imgWindowAspect);
2442            initCamera();
2443        } else {
2444            if (_userImgWorldDims[0] > 0) {
2445                _setCameraZoomRegion(_userImgWorldOrigin[0],
2446                                     _userImgWorldOrigin[1],
2447                                     _userImgWorldDims[0],
2448                                     _userImgWorldDims[1]);
2449            } else {
2450                if (isCameraMaximized()) {
2451                    initCamera();
2452                } else {
2453                    _setCameraZoomRegion(_imgWorldOrigin[0],
2454                                         _imgWorldOrigin[1],
2455                                         _imgWorldDims[0],
2456                                         _imgWorldDims[1]);
2457                }
2458            }
2459        }
[2194]2460    }
[2100]2461    _needsRedraw = true;
2462}
2463
[3164]2464void Renderer::setObjectAspects(double aspectRatio)
2465{
2466    setGraphicsObjectAspect<Arc>(aspectRatio);
2467    setGraphicsObjectAspect<Arrow>(aspectRatio);
2468    setGraphicsObjectAspect<Box>(aspectRatio);
2469    setGraphicsObjectAspect<Cone>(aspectRatio);
2470    setGraphicsObjectAspect<Contour2D>(aspectRatio);
2471    setGraphicsObjectAspect<Contour3D>(aspectRatio);
2472    setGraphicsObjectAspect<Cutplane>(aspectRatio);
2473    setGraphicsObjectAspect<Cylinder>(aspectRatio);
2474    setGraphicsObjectAspect<Disk>(aspectRatio);
2475    setGraphicsObjectAspect<Glyphs>(aspectRatio);
2476    setGraphicsObjectAspect<HeightMap>(aspectRatio);
[3818]2477    setGraphicsObjectAspect<Image>(aspectRatio);
[4082]2478    setGraphicsObjectAspect<ImageCutplane>(aspectRatio);
[3164]2479    setGraphicsObjectAspect<LIC>(aspectRatio);
2480    setGraphicsObjectAspect<Line>(aspectRatio);
2481    setGraphicsObjectAspect<Molecule>(aspectRatio);
[3535]2482    setGraphicsObjectAspect<Outline>(aspectRatio);
[3861]2483    setGraphicsObjectAspect<Parallelepiped>(aspectRatio);
[3164]2484    setGraphicsObjectAspect<PolyData>(aspectRatio);
2485    setGraphicsObjectAspect<Polygon>(aspectRatio);
2486    setGraphicsObjectAspect<PseudoColor>(aspectRatio);
2487    setGraphicsObjectAspect<Sphere>(aspectRatio);
2488    setGraphicsObjectAspect<Streamlines>(aspectRatio);
[3818]2489    setGraphicsObjectAspect<Text3D>(aspectRatio);
[3164]2490    setGraphicsObjectAspect<Volume>(aspectRatio);
2491    setGraphicsObjectAspect<Warp>(aspectRatio);
2492}
2493
[3330]2494void Renderer::setCameraAspect(Aspect aspect)
2495{
2496    //if (_cameraAspect == aspect)
2497    //    return;
2498
2499    _cameraAspect = aspect;
2500
2501    double aspectRatio = 0.0;
2502    switch (aspect) {
2503    case ASPECT_SQUARE:
2504        aspectRatio = 1.0;
2505        break;
2506    case ASPECT_WINDOW:
2507        aspectRatio = 1.0;
2508        if (_cameraMode == IMAGE) {
[3419]2509            aspectRatio = getImageCameraAspect();
[3330]2510        }
2511        break;
2512    case ASPECT_NATIVE:
2513    default:
2514        aspectRatio = 0.0;
2515    }
2516
2517    setObjectAspects(aspectRatio);
2518
2519    if (_cameraMode == IMAGE)
2520        _needsCameraReset = true;
2521    _needsRedraw = true;
2522}
2523
[2100]2524/**
[2112]2525 * \brief Change the camera type: perspective, orthographic or image view
2526 *
2527 * Perspective mode is a normal 3D camera.
2528 *
2529 * Orthogrphic mode is parallel projection.
2530 *
2531 * Image mode is an orthographic camera with fixed axes and a clipping region
[3419]2532 * around the plot area, use _setCameraZoomRegion to control the displayed area
[2112]2533 *
2534 * \param[in] mode Enum specifying camera type
2535 */
2536void Renderer::setCameraMode(CameraMode mode)
2537{
2538    if (_cameraMode == mode) return;
2539
2540    CameraMode origMode = _cameraMode;
2541    _cameraMode = mode;
[2423]2542    resetAxes();
2543
[2112]2544    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2545    switch (mode) {
2546    case ORTHO: {
2547        TRACE("Set camera to Ortho mode");
2548        camera->ParallelProjectionOn();
2549        if (origMode == IMAGE) {
[2270]2550            resetCamera(true);
[2112]2551        }
2552        break;
2553    }
2554    case PERSPECTIVE: {
2555        TRACE("Set camera to Perspective mode");
2556        camera->ParallelProjectionOff();
2557        if (origMode == IMAGE) {
[2270]2558            resetCamera(true);
[2112]2559        }
2560        break;
2561    }
2562    case IMAGE: {
2563        camera->ParallelProjectionOn();
[3419]2564        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2565                             _imgWorldDims[0],_imgWorldDims[1]);
[2112]2566        TRACE("Set camera to Image mode");
2567        break;
2568    }
2569    default:
2570        ERROR("Unkown camera mode: %d", mode);
2571    }
2572    _needsRedraw = true;
2573}
2574
[2194]2575/**
2576 * \brief Get the current camera mode
2577 */
2578Renderer::CameraMode Renderer::getCameraMode() const
2579{
2580    return _cameraMode;
2581}
2582
[2236]2583/**
[2349]2584 * \brief Set the VTK camera parameters based on a 4x4 view matrix
2585 */
2586void Renderer::setCameraFromMatrix(vtkCamera *camera, vtkMatrix4x4& mat)
2587{
2588    double d = camera->GetDistance();
2589    double vu[3];
2590    vu[0] = mat[1][0];
2591    vu[1] = mat[1][1];
2592    vu[2] = mat[1][2];
2593    double trans[3];
2594    trans[0] = mat[0][3];
2595    trans[1] = mat[1][3];
2596    trans[2] = mat[2][3];
2597    mat[0][3] = 0;
2598    mat[1][3] = 0;
2599    mat[2][3] = 0;
2600    double vpn[3] = {mat[2][0], mat[2][1], mat[2][2]};
2601    double pos[3];
2602    // With translation removed, we have an orthogonal matrix,
2603    // so the inverse is the transpose
2604    mat.Transpose();
2605    mat.MultiplyPoint(trans, pos);
2606    pos[0] = -pos[0];
2607    pos[1] = -pos[1];
2608    pos[2] = -pos[2];
2609    double fp[3];
2610    fp[0] = pos[0] - vpn[0] * d;
2611    fp[1] = pos[1] - vpn[1] * d;
2612    fp[2] = pos[2] - vpn[2] * d;
2613    camera->SetPosition(pos);
2614    camera->SetFocalPoint(fp);
2615    camera->SetViewUp(vu);
2616}
2617
2618/**
[2236]2619 * \brief Set the orientation of the camera from a quaternion
[2352]2620 * rotation
[2236]2621 *
2622 * \param[in] quat A quaternion with scalar part first: w,x,y,z
[2349]2623 * \param[in] absolute Is rotation absolute or relative?
[2236]2624 */
[2393]2625void Renderer::setCameraOrientation(const double quat[4], bool absolute)
[2200]2626{
[2260]2627    if (_cameraMode == IMAGE)
2628        return;
[2213]2629    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
[2214]2630    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
[2352]2631    vtkSmartPointer<vtkMatrix4x4> rotMat = vtkSmartPointer<vtkMatrix4x4>::New();
[2349]2632
[2393]2633    double q[4];
2634    copyQuat(quat, q);
2635
[2349]2636    if (absolute) {
[2351]2637        double abs[4];
2638        // Save absolute rotation
[2393]2639        copyQuat(q, abs);
[2351]2640        // Compute relative rotation
[2393]2641        quatMult(quatReciprocal(_cameraOrientation), q, q);
[2351]2642        // Store absolute rotation
2643        copyQuat(abs, _cameraOrientation);
[2349]2644    } else {
[2351]2645        // Compute new absolute rotation
[2393]2646        quatMult(_cameraOrientation, q, _cameraOrientation);
[2351]2647    }
2648
[2393]2649    quaternionToTransposeMatrix4x4(q, *rotMat);
[2349]2650#ifdef DEBUG
[2352]2651    TRACE("Rotation matrix:\n %g %g %g\n %g %g %g\n %g %g %g",
2652          (*rotMat)[0][0], (*rotMat)[0][1], (*rotMat)[0][2],
2653          (*rotMat)[1][0], (*rotMat)[1][1], (*rotMat)[1][2],
2654          (*rotMat)[2][0], (*rotMat)[2][1], (*rotMat)[2][2]);
2655    vtkSmartPointer<vtkMatrix4x4> camMat = vtkSmartPointer<vtkMatrix4x4>::New();
2656    camMat->DeepCopy(camera->GetViewTransformMatrix());
2657    TRACE("Camera matrix:\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g",
2658          (*camMat)[0][0], (*camMat)[0][1], (*camMat)[0][2], (*camMat)[0][3],
2659          (*camMat)[1][0], (*camMat)[1][1], (*camMat)[1][2], (*camMat)[1][3],
2660          (*camMat)[2][0], (*camMat)[2][1], (*camMat)[2][2], (*camMat)[2][3],
2661          (*camMat)[3][0], (*camMat)[3][1], (*camMat)[3][2], (*camMat)[3][3]);
[2351]2662    printCameraInfo(camera);
[2349]2663#endif
[2351]2664    trans->Translate(0, 0, -camera->GetDistance());
[2352]2665    trans->Concatenate(rotMat);
[2351]2666    trans->Translate(0, 0, camera->GetDistance());
2667    trans->Concatenate(camera->GetViewTransformMatrix());
2668    setCameraFromMatrix(camera, *trans->GetMatrix());
2669
[3615]2670    resetCameraClippingRange();
[2222]2671    printCameraInfo(camera);
[2213]2672    _needsRedraw = true;
2673}
2674
[2236]2675/**
2676 * \brief Set the position and orientation of the camera
2677 *
2678 * \param[in] position x,y,z position of camera in world coordinates
2679 * \param[in] focalPoint x,y,z look-at point in world coordinates
2680 * \param[in] viewUp x,y,z up vector of camera
2681 */
[2393]2682void Renderer::setCameraOrientationAndPosition(const double position[3],
2683                                               const double focalPoint[3],
2684                                               const double viewUp[3])
[2213]2685{
[2351]2686    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2687    camera->SetPosition(position);
2688    camera->SetFocalPoint(focalPoint);
2689    camera->SetViewUp(viewUp);
[3615]2690    resetCameraClippingRange();
[2200]2691    _needsRedraw = true;
2692}
2693
[2236]2694/**
2695 * \brief Get the position and orientation of the camera
2696 *
2697 * \param[out] position x,y,z position of camera in world coordinates
2698 * \param[out] focalPoint x,y,z look-at point in world coordinates
2699 * \param[out] viewUp x,y,z up vector of camera
2700 */
[2213]2701void Renderer::getCameraOrientationAndPosition(double position[3],
2702                                               double focalPoint[3],
2703                                               double viewUp[3])
[2200]2704{
[2112]2705    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
[2351]2706    camera->GetPosition(position);
2707    camera->GetFocalPoint(focalPoint);
2708    camera->GetViewUp(viewUp);
[2112]2709}
2710
[3330]2711void Renderer::sceneBoundsChanged()
2712{
2713#ifdef RESET_CAMERA_ON_SCENE_CHANGE
2714    _needsCameraReset = true;
2715#else
2716    _needsAxesReset = true;
2717    _needsCameraClippingRangeReset = true;
2718#endif
2719
2720    _needsRedraw = true;
2721}
2722
[2222]2723/**
[2112]2724 * \brief Reset pan, zoom, clipping planes and optionally rotation
2725 *
2726 * \param[in] resetOrientation Reset the camera rotation/orientation also
2727 */
2728void Renderer::resetCamera(bool resetOrientation)
2729{
[3615]2730    TRACE("Enter: %d", resetOrientation ? 1 : 0);
[2222]2731    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
[2112]2732    if (_cameraMode == IMAGE) {
2733        initCamera();
2734    } else {
2735        if (resetOrientation) {
2736            camera->SetPosition(0, 0, 1);
2737            camera->SetFocalPoint(0, 0, 0);
2738            camera->SetViewUp(0, 1, 0);
[2351]2739            _cameraOrientation[0] = 1.0;
2740            _cameraOrientation[1] = 0.0;
2741            _cameraOrientation[2] = 0.0;
2742            _cameraOrientation[3] = 0.0;
[2112]2743        }
[2512]2744        //setViewAngle(_windowHeight);
[3330]2745        //double bounds[6];
2746        //collectBounds(bounds, false);
2747        //_renderer->ResetCamera(bounds);
[3399]2748        if (_needsAxesReset) {
2749            resetAxes();
2750            _needsAxesReset = false;
2751        }
[3615]2752        resetVtkCamera();
[2349]2753        //computeScreenWorldCoords();
[2112]2754    }
[2222]2755
2756    printCameraInfo(camera);
2757
[2194]2758    _cameraZoomRatio = 1;
2759    _cameraPan[0] = 0;
2760    _cameraPan[1] = 0;
[2200]2761
[2112]2762    _needsRedraw = true;
2763}
2764
[3419]2765void Renderer::resetVtkCamera(double *bounds)
2766{
[3615]2767    TRACE("Enter: bounds: %p", bounds);
[3419]2768    if (bounds != NULL)
2769        _renderer->ResetCamera(bounds);
2770    else
2771        _renderer->ResetCamera();
[3615]2772    printCameraInfo(_renderer->GetActiveCamera());
[3419]2773}
2774
[3330]2775/**
2776 * \brief Set the camera near/far clipping range based on current scene bounds
2777 */
[2393]2778void Renderer::resetCameraClippingRange()
2779{
2780    _renderer->ResetCameraClippingRange();
[3615]2781    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2782    //double dist = camera->GetClippingRange()[0] + (camera->GetClippingRange()[1] - camera->GetClippingRange()[0])/2.0;
2783    //camera->SetDistance(dist);
2784    printCameraInfo(camera);
[2393]2785}
2786
[2112]2787/**
2788 * \brief Perform a relative rotation to current camera orientation
2789 *
2790 * Angles are in degrees, rotation is about focal point
2791 */
2792void Renderer::rotateCamera(double yaw, double pitch, double roll)
2793{
[2114]2794    if (_cameraMode == IMAGE)
2795        return;
[2112]2796    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2797    camera->Azimuth(yaw); // Rotate about object
2798    //camera->SetYaw(yaw); // Rotate about camera
2799    camera->Elevation(pitch); // Rotate about object
2800    //camera->SetPitch(pitch); // Rotate about camera
2801    camera->Roll(roll); // Roll about camera view axis
[3615]2802    resetCameraClippingRange();
[2349]2803    //computeScreenWorldCoords();
[2112]2804    _needsRedraw = true;
2805}
2806
2807/**
[2194]2808 * \brief Perform a 2D translation of the camera
[2112]2809 *
[2200]2810 * x,y pan amount are specified as signed absolute pan amount in viewport
2811 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
2812 * etc.
2813 *
[2351]2814 * \param[in] x Viewport coordinate horizontal panning (positive number pans
2815 * camera left, object right)
2816 * \param[in] y Viewport coordinate vertical panning (positive number pans
2817 * camera up, object down)
[2194]2818 * \param[in] absolute Control if pan amount is relative to current or absolute
[2112]2819 */
[2194]2820void Renderer::panCamera(double x, double y, bool absolute)
[2112]2821{
[2222]2822    TRACE("Enter panCamera: %g %g, current abs: %g %g",
2823          x, y, _cameraPan[0], _cameraPan[1]);
2824
[2200]2825    if (_cameraMode == IMAGE) {
[3419]2826        _userImgWorldOrigin[0] = 0;
2827        _userImgWorldOrigin[1] = 0;
2828        _userImgWorldDims[0] = -1;
2829        _userImgWorldDims[1] = -1;
2830
[2200]2831        // Reverse x rather than y, since we are panning the camera, and client
2832        // expects to be panning/moving the object
2833        x = -x * _screenWorldCoords[2];
2834        y = y * _screenWorldCoords[3];
[2194]2835
[2200]2836        if (absolute) {
2837            double panAbs[2];
2838            panAbs[0] = x;
2839            panAbs[1] = y;
2840            x -= _cameraPan[0];
2841            y -= _cameraPan[1];
2842            _cameraPan[0] = panAbs[0];
2843            _cameraPan[1] = panAbs[1];
2844        } else {
2845            _cameraPan[0] += x;
2846            _cameraPan[1] += y;
2847        }
2848
[2114]2849        _imgWorldOrigin[0] += x;
2850        _imgWorldOrigin[1] += y;
[3419]2851        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2852                             _imgWorldDims[0], _imgWorldDims[1]);
[2194]2853    } else {
[2200]2854        y = -y;
2855        if (absolute) {
2856            double panAbs[2];
2857            panAbs[0] = x;
2858            panAbs[1] = y;
2859            x -= _cameraPan[0];
2860            y -= _cameraPan[1];
2861            _cameraPan[0] = panAbs[0];
2862            _cameraPan[1] = panAbs[1];
2863        } else {
2864            _cameraPan[0] += x;
2865            _cameraPan[1] += y;
2866        }
2867
[2393]2868        if (x != 0.0 || y != 0.0) {
2869            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2870            double viewFocus[4], focalDepth, viewPoint[3];
2871            double newPickPoint[4], oldPickPoint[4], motionVector[3];
[2200]2872
[2393]2873            camera->GetFocalPoint(viewFocus);
2874            computeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2],
2875                                  viewFocus);
2876            focalDepth = viewFocus[2];
[2200]2877
[3639]2878            computeDisplayToWorld((x * 2. + 1.) * (double)_windowWidth / 2.0,
2879                                  (y * 2. + 1.) * (double)_windowHeight / 2.0,
[2393]2880                                  focalDepth,
2881                                  newPickPoint);
[2200]2882
[3639]2883            computeDisplayToWorld((double)_windowWidth / 2.0,
2884                                  (double)_windowHeight / 2.0,
[2393]2885                                  focalDepth,
2886                                  oldPickPoint);
[2260]2887
[2393]2888            // Camera motion is reversed
2889            motionVector[0] = oldPickPoint[0] - newPickPoint[0];
2890            motionVector[1] = oldPickPoint[1] - newPickPoint[1];
2891            motionVector[2] = oldPickPoint[2] - newPickPoint[2];
[2200]2892
[2393]2893            camera->GetFocalPoint(viewFocus);
2894            camera->GetPosition(viewPoint);
2895            camera->SetFocalPoint(motionVector[0] + viewFocus[0],
2896                                  motionVector[1] + viewFocus[1],
2897                                  motionVector[2] + viewFocus[2]);
[2200]2898
[2393]2899            camera->SetPosition(motionVector[0] + viewPoint[0],
2900                                motionVector[1] + viewPoint[1],
2901                                motionVector[2] + viewPoint[2]);
[2200]2902
[3615]2903            resetCameraClippingRange();
[2393]2904            //computeScreenWorldCoords();
2905        }
[2114]2906    }
[2222]2907
2908    TRACE("Leave panCamera: %g %g, current abs: %g %g",
2909          x, y, _cameraPan[0], _cameraPan[1]);
2910
[2112]2911    _needsRedraw = true;
2912}
2913
2914/**
[2320]2915 * \brief Dolly camera or set orthographic scaling based on camera type
[2112]2916 *
2917 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
[2194]2918 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
[2112]2919 */
[2194]2920void Renderer::zoomCamera(double z, bool absolute)
[2112]2921{
[2222]2922    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2923    TRACE("Enter Zoom: current abs: %g, z: %g, view angle %g",
2924          _cameraZoomRatio, z, camera->GetViewAngle());
2925
[2194]2926    if (absolute) {
2927        assert(_cameraZoomRatio > 0.0);
2928        double zAbs = z;
2929        z *= 1.0/_cameraZoomRatio;
2930        _cameraZoomRatio = zAbs;
2931    } else {
2932        _cameraZoomRatio *= z;
2933    }
[2222]2934
[2114]2935    if (_cameraMode == IMAGE) {
[3419]2936        _userImgWorldOrigin[0] = 0;
2937        _userImgWorldOrigin[1] = 0;
2938        _userImgWorldDims[0] = -1;
2939        _userImgWorldDims[1] = -1;
2940
[2114]2941        double dx = _imgWorldDims[0];
2942        double dy = _imgWorldDims[1];
2943        _imgWorldDims[0] /= z;
2944        _imgWorldDims[1] /= z;
2945        dx -= _imgWorldDims[0];
2946        dy -= _imgWorldDims[1];
2947        _imgWorldOrigin[0] += dx/2.0;
2948        _imgWorldOrigin[1] += dy/2.0;
[3419]2949        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2950                             _imgWorldDims[0], _imgWorldDims[1]);
[2194]2951    } else {
[2423]2952        // Keep ortho and perspective modes in sync
2953        // Move camera forward/back for perspective camera
2954        camera->Dolly(z);
2955        // Change ortho parallel scale
2956        camera->SetParallelScale(camera->GetParallelScale()/z);
[3615]2957        resetCameraClippingRange();
[2236]2958        //computeScreenWorldCoords();
[2114]2959    }
[2222]2960
2961    TRACE("Leave Zoom: rel %g, new abs: %g, view angle %g",
2962          z, _cameraZoomRatio, camera->GetViewAngle());
2963
[2112]2964    _needsRedraw = true;
2965}
2966
[3419]2967bool Renderer::setCameraZoomRegionPixels(int x, int y, int width, int height)
2968{
2969    if (_cameraMode != IMAGE)
2970        return false;
2971
2972    double wx, wy, ww, wh;
2973
2974    y = _windowHeight - y;
2975    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
2976    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
2977
2978    wx = _screenWorldCoords[0] + x * pxToWorldX;
2979    wy = _screenWorldCoords[1] + y * pxToWorldY;
2980    ww = abs(width) *  pxToWorldX;
2981    wh = abs(height) * pxToWorldY;
2982    setCameraZoomRegion(wx, wy, ww, wh);
2983
2984    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
2985          x, y, width, height,
2986          wx, wy, ww, wh);
2987
2988    return true;
2989}
2990
2991bool Renderer::setCameraZoomRegion(double x, double y, double width, double height)
2992{
2993    if (_cameraMode != IMAGE)
2994        return false;
2995
2996    _userImgWorldOrigin[0] = x;
2997    _userImgWorldOrigin[1] = y;
2998    _userImgWorldDims[0] = width;
2999    _userImgWorldDims[1] = height;
3000    _setCameraZoomRegion(x, y, width, height);
3001
3002    return true;
3003}
3004
[2112]3005/**
[2423]3006 * \brief Set the pan/zoom using a corner and dimensions in pixel coordinates
3007 *
3008 * \param[in] x left pixel coordinate
3009 * \param[in] y bottom  pixel coordinate (with y=0 at top of window)
3010 * \param[in] width Width of zoom region in pixel coordinates
3011 * \param[in] height Height of zoom region in pixel coordinates
3012 */
[3419]3013void Renderer::_setCameraZoomRegionPixels(int x, int y, int width, int height)
[2423]3014{
[3419]3015    if (_cameraMode != IMAGE) {
3016        ERROR("Called while camera mode is not image");
3017        return;
3018    }
3019
[2423]3020    double wx, wy, ww, wh;
3021
3022    y = _windowHeight - y;
3023    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
3024    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
3025
3026    wx = _screenWorldCoords[0] + x * pxToWorldX;
3027    wy = _screenWorldCoords[1] + y * pxToWorldY;
3028    ww = abs(width) *  pxToWorldX;
3029    wh = abs(height) * pxToWorldY;
[3419]3030    _setCameraZoomRegion(wx, wy, ww, wh);
[2423]3031
3032    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
3033          x, y, width, height,
3034          wx, wy, ww, wh);
3035}
3036
[3419]3037void Renderer::getImageCameraSizes(int *imgWidthPx, int *imgHeightPx,
3038                                   int *_pxOffsetX, int *_pxOffsetY)
3039{
3040    int pxOffsetX, pxOffsetY;
3041    pxOffsetX = (int)(0.17 * (double)_windowWidth);
3042    pxOffsetX = (pxOffsetX > 100 ? 100 : pxOffsetX);
3043    pxOffsetY = (int)(0.15 * (double)_windowHeight);
3044    pxOffsetY = (pxOffsetY > 75 ? 75 : pxOffsetY);
3045
3046    int outerGutter = (int)(0.03 * (double)_windowWidth);
3047    outerGutter = (outerGutter > 15 ? 15 : outerGutter);
3048
3049    *imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
3050    *imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
3051    if (_pxOffsetX != NULL) *_pxOffsetX = pxOffsetX;
3052    if (_pxOffsetY != NULL) *_pxOffsetY = pxOffsetY;
3053}
3054
3055double Renderer::getImageCameraAspect()
3056{
3057    int imgWidthPx, imgHeightPx;
3058    getImageCameraSizes(&imgWidthPx, &imgHeightPx);
3059    return ((double)imgWidthPx / (double)imgHeightPx);
3060}
3061
[2423]3062/**
[2100]3063 * \brief Set the pan/zoom using a corner and dimensions in world coordinates
3064 *
[2112]3065 * \param[in] x left world coordinate
3066 * \param[in] y bottom  world coordinate
3067 * \param[in] width Width of zoom region in world coordinates
3068 * \param[in] height Height of zoom region in world coordinates
[2100]3069 */
[3419]3070void Renderer::_setCameraZoomRegion(double x, double y, double width, double height)
[2100]3071{
[3419]3072    if (_cameraMode != IMAGE) {
3073        ERROR("Called while camera mode is not image");
3074        return;
3075    }
[2100]3076
[3419]3077    int imgWidthPx, imgHeightPx;
3078    int pxOffsetX, pxOffsetY;
[2100]3079
[3419]3080    getImageCameraSizes(&imgWidthPx, &imgHeightPx, &pxOffsetX, &pxOffsetY);
[2200]3081
[3419]3082    double imgWindowAspect = (double)imgWidthPx / (double)imgHeightPx;
[2200]3083
[2194]3084    double pxToWorld;
[3419]3085    double imgWidthWorld;
3086    double imgHeightWorld;
[2200]3087
[3419]3088    double requestedAspect = width / height;
3089
3090    if (requestedAspect >= imgWindowAspect) {
[2865]3091        pxToWorld = width / (double)imgWidthPx;
[3419]3092        imgWidthWorld = width;
3093        imgHeightWorld = (double)imgHeightPx * pxToWorld;
[2200]3094    } else {
[2865]3095        pxToWorld = height / (double)imgHeightPx;
[3419]3096        imgWidthWorld = (double)imgWidthPx * pxToWorld;
3097        imgHeightWorld =  height;
[2200]3098    }
3099
[2100]3100    double offsetX = pxOffsetX * pxToWorld;
3101    double offsetY = pxOffsetY * pxToWorld;
[3198]3102    double winWidthWorld = _windowWidth * pxToWorld;
3103    double winHeightWorld = _windowHeight * pxToWorld;
[2100]3104
3105    TRACE("Window: %d %d", _windowWidth, _windowHeight);
3106    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
3107    TRACE("pxToWorld: %g", pxToWorld);
3108    TRACE("offset: %g %g", offsetX, offsetY);
3109
3110    _imgWorldOrigin[0] = x;
3111    _imgWorldOrigin[1] = y;
[3419]3112    _imgWorldDims[0] = width;
3113    _imgWorldDims[1] = height;
3114    _imgWindowWorldDims[0] = imgWidthWorld;
3115    _imgWindowWorldDims[1] = imgHeightWorld;
[2100]3116
[3419]3117    double camPos[2];
[3198]3118    camPos[0] = _imgWorldOrigin[0] - offsetX + winWidthWorld / 2.0;
3119    camPos[1] = _imgWorldOrigin[1] - offsetY + winHeightWorld / 2.0;
[2100]3120
3121    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
3122    camera->ParallelProjectionOn();
3123    camera->SetClippingRange(1, 2);
3124    // Half of world coordinate height of viewport (Documentation is wrong)
[3198]3125    camera->SetParallelScale(winHeightWorld / 2.0);
[2100]3126
[2423]3127    if (_imgCameraPlane == PLANE_XY) {
3128        // XY plane
3129        camera->SetPosition(camPos[0], camPos[1], _imgCameraOffset + 1.);
3130        camera->SetFocalPoint(camPos[0], camPos[1], _imgCameraOffset);
3131        camera->SetViewUp(0, 1, 0);
3132        // bottom
3133        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
3134        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
3135        // left
3136        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
3137        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
3138        // top
[3419]3139        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWindowWorldDims[1], 0);
[2423]3140        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
3141        // right
[3419]3142        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWindowWorldDims[0], 0, 0);
[2423]3143        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
[3330]3144
[3419]3145        _cubeAxesActor->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0],
3146                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1],
[3198]3147                                  _imgCameraOffset, _imgCameraOffset);
3148        _cubeAxesActor->XAxisVisibilityOn();
3149        _cubeAxesActor->YAxisVisibilityOn();
3150        _cubeAxesActor->ZAxisVisibilityOff();
[2423]3151    } else if (_imgCameraPlane == PLANE_ZY) {
3152        // ZY plane
3153        camera->SetPosition(_imgCameraOffset - 1., camPos[1], camPos[0]);
3154        camera->SetFocalPoint(_imgCameraOffset, camPos[1], camPos[0]);
3155        camera->SetViewUp(0, 1, 0);
3156        // bottom
3157        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
3158        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
3159        // left
3160        _cameraClipPlanes[1]->SetOrigin(0, 0, _imgWorldOrigin[0]);
3161        _cameraClipPlanes[1]->SetNormal(0, 0, 1);
3162        // top
[3419]3163        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWindowWorldDims[1], 0);
[2423]3164        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
3165        // right
[3419]3166        _cameraClipPlanes[3]->SetOrigin(0, 0, _imgWorldOrigin[0] + _imgWindowWorldDims[0]);
[2423]3167        _cameraClipPlanes[3]->SetNormal(0, 0, -1);
[3330]3168
[3198]3169        _cubeAxesActor->SetBounds(_imgCameraOffset, _imgCameraOffset,
[3419]3170                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1],
3171                                  _imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0]);
[3198]3172        _cubeAxesActor->XAxisVisibilityOff();
3173        _cubeAxesActor->YAxisVisibilityOn();
3174        _cubeAxesActor->ZAxisVisibilityOn();
[2423]3175    } else {
3176        // XZ plane
3177        camera->SetPosition(camPos[0], _imgCameraOffset - 1., camPos[1]);
3178        camera->SetFocalPoint(camPos[0], _imgCameraOffset, camPos[1]);
3179        camera->SetViewUp(0, 0, 1);
3180        // bottom
3181        _cameraClipPlanes[0]->SetOrigin(0, 0, _imgWorldOrigin[1]);
3182        _cameraClipPlanes[0]->SetNormal(0, 0, 1);
3183        // left
3184        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
3185        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
3186        // top
[4079]3187        _cameraClipPlanes[2]->SetOrigin(0, 0, _imgWorldOrigin[1] + _imgWindowWorldDims[1]);
[2423]3188        _cameraClipPlanes[2]->SetNormal(0, 0, -1);
3189        // right
[4079]3190        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWindowWorldDims[0], 0, 0);
[2423]3191        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
[3330]3192
[3419]3193        _cubeAxesActor->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0],
[3198]3194                                  _imgCameraOffset, _imgCameraOffset,
[3419]3195                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1]);
[3198]3196        _cubeAxesActor->XAxisVisibilityOn();
3197        _cubeAxesActor->YAxisVisibilityOff();
3198        _cubeAxesActor->ZAxisVisibilityOn();
[2423]3199    }
[2100]3200
[3330]3201    // Fix up axis ranges based on new bounds
3202    setAxesRanges();
3203
[2194]3204    // Compute screen world coordinates
3205    computeScreenWorldCoords();
3206
[2100]3207#ifdef DEBUG
3208    printCameraInfo(camera);
3209#endif
3210
3211    _needsRedraw = true;
3212}
3213
[2236]3214/**
3215 * \brief Convert pixel/display coordinates to world coordinates based on current camera
3216 */
[2200]3217void Renderer::computeDisplayToWorld(double x, double y, double z, double worldPt[4])
3218{
3219    _renderer->SetDisplayPoint(x, y, z);
3220    _renderer->DisplayToWorld();
3221    _renderer->GetWorldPoint(worldPt);
3222    if (worldPt[3]) {
3223        worldPt[0] /= worldPt[3];
3224        worldPt[1] /= worldPt[3];
3225        worldPt[2] /= worldPt[3];
3226        worldPt[3] = 1.0;
3227    }
3228}
3229
[2236]3230/**
3231 * \brief Convert world coordinates to pixel/display coordinates based on current camera
3232 */
[2200]3233void Renderer::computeWorldToDisplay(double x, double y, double z, double displayPt[3])
3234{
3235    _renderer->SetWorldPoint(x, y, z, 1.0);
3236    _renderer->WorldToDisplay();
3237    _renderer->GetDisplayPoint(displayPt);
3238}
3239
[2236]3240/**
3241 * \brief Compute the world coordinate bounds of the display rectangle
3242 */
[2194]3243void Renderer::computeScreenWorldCoords()
3244{
3245    // Start with viewport coords [-1,1]
3246    double x0 = -1;
3247    double y0 = -1;
[2200]3248    double z0 = -1;
[2194]3249    double x1 = 1;
3250    double y1 = 1;
[2200]3251    double z1 = -1;
[2194]3252
3253    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
3254    double result[4];
3255
3256    // get the perspective transformation from the active camera
3257    mat->DeepCopy(_renderer->GetActiveCamera()->
3258                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
3259
3260    // use the inverse matrix
3261    mat->Invert();
3262
3263    // Transform point to world coordinates
3264    result[0] = x0;
3265    result[1] = y0;
3266    result[2] = z0;
3267    result[3] = 1.0;
3268
3269    mat->MultiplyPoint(result, result);
3270
3271    // Get the transformed vector & set WorldPoint
3272    // while we are at it try to keep w at one
3273    if (result[3]) {
3274        x0 = result[0] / result[3];
3275        y0 = result[1] / result[3];
3276        z0 = result[2] / result[3];
3277    }
3278
3279    result[0] = x1;
3280    result[1] = y1;
3281    result[2] = z1;
3282    result[3] = 1.0;
3283
3284    mat->MultiplyPoint(result, result);
3285
3286    if (result[3]) {
3287        x1 = result[0] / result[3];
3288        y1 = result[1] / result[3];
3289        z1 = result[2] / result[3];
3290    }
3291
3292    mat->Delete();
3293
[2423]3294    if (_imgCameraPlane == PLANE_XZ) {
3295        _screenWorldCoords[0] = x0;
3296        _screenWorldCoords[1] = z0;
3297        _screenWorldCoords[2] = x1 - x0;
3298        _screenWorldCoords[3] = z1 - z0;
3299    } else if (_imgCameraPlane == PLANE_ZY) {
3300        _screenWorldCoords[0] = z0;
3301        _screenWorldCoords[1] = y0;
3302        _screenWorldCoords[2] = z1 - z0;
3303        _screenWorldCoords[3] = y1 - y0;
3304    } else {
3305        // XY
3306        _screenWorldCoords[0] = x0;
3307        _screenWorldCoords[1] = y0;
3308        _screenWorldCoords[2] = x1 - x0;
3309        _screenWorldCoords[3] = y1 - y0;
3310    }
[2194]3311}
3312
3313/**
3314 * \brief Get the world coordinates of the image camera plot area
3315 *
3316 * \param[out] xywh Array to hold x,y,width,height world coordinates
3317 */
3318void Renderer::getCameraZoomRegion(double xywh[4]) const
3319{
3320    xywh[0] = _imgWorldOrigin[0];
3321    xywh[1] = _imgWorldOrigin[1];
3322    xywh[2] = _imgWorldDims[0];
3323    xywh[3] = _imgWorldDims[1];
3324}
3325
3326/**
3327 * \brief Get the world origin and dimensions of the screen
3328 *
3329 * \param[out] xywh Array to hold x,y,width,height world coordinates
3330 */
3331void Renderer::getScreenWorldCoords(double xywh[4]) const
3332{
3333    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
3334}
3335
3336/**
3337 * \brief Compute bounding box containing the two input bounding boxes
3338 *
3339 * \param[out] boundsDest Union of the two input bounding boxes
3340 * \param[in] bounds1 Input bounding box
3341 * \param[in] bounds2 Input bounding box
3342 */
[2112]3343void Renderer::mergeBounds(double *boundsDest,
3344                           const double *bounds1, const double *bounds2)
3345{
[2260]3346    assert(boundsDest != NULL);
3347    assert(bounds1 != NULL);
3348    if (bounds2 == NULL) {
3349        WARN("NULL bounds2 array");
3350        return;
3351    }
[3330]3352    bool b1empty = (bounds1[0] > bounds1[1]);
3353    bool b2empty = (bounds2[0] > bounds2[1]);
3354
3355    if (b1empty && b2empty)
3356        return;
3357    if (b1empty) {
3358        memcpy(boundsDest, bounds2, sizeof(double) * 6);
3359        return;
3360    } else if (b2empty) {
3361        memcpy(boundsDest, bounds1, sizeof(double) * 6);
3362        return;
3363    }
3364
[2112]3365    for (int i = 0; i < 6; i++) {
3366        if (i % 2 == 0)
3367            boundsDest[i] = min2(bounds1[i], bounds2[i]);
3368        else
3369            boundsDest[i] = max2(bounds1[i], bounds2[i]);
3370    }
3371}
3372
[2100]3373/**
[2112]3374 * \brief Collect bounds of all graphics objects
3375 *
3376 * \param[out] bounds Bounds of all scene objects
3377 * \param[in] onlyVisible Only collect bounds of visible objects
3378 */
3379void Renderer::collectBounds(double *bounds, bool onlyVisible)
3380{
3381    bounds[0] = DBL_MAX;
3382    bounds[1] = -DBL_MAX;
3383    bounds[2] = DBL_MAX;
3384    bounds[3] = -DBL_MAX;
3385    bounds[4] = DBL_MAX;
3386    bounds[5] = -DBL_MAX;
3387
[3164]3388    mergeGraphicsObjectBounds<Arc>(bounds, onlyVisible);
3389    mergeGraphicsObjectBounds<Arrow>(bounds, onlyVisible);
3390    mergeGraphicsObjectBounds<Box>(bounds, onlyVisible);
3391    mergeGraphicsObjectBounds<Cone>(bounds, onlyVisible);
3392    mergeGraphicsObjectBounds<Contour2D>(bounds, onlyVisible);
3393    mergeGraphicsObjectBounds<Contour3D>(bounds, onlyVisible);
3394    mergeGraphicsObjectBounds<Cutplane>(bounds, onlyVisible);
3395    mergeGraphicsObjectBounds<Cylinder>(bounds, onlyVisible);
3396    mergeGraphicsObjectBounds<Disk>(bounds, onlyVisible);
3397    mergeGraphicsObjectBounds<Glyphs>(bounds, onlyVisible);
[3683]3398    mergeGraphicsObjectBounds<Group>(bounds, onlyVisible);
[3164]3399    mergeGraphicsObjectBounds<HeightMap>(bounds, onlyVisible);
[3818]3400    mergeGraphicsObjectBounds<Image>(bounds, onlyVisible);
[4082]3401    mergeGraphicsObjectBounds<ImageCutplane>(bounds, onlyVisible);
[3164]3402    mergeGraphicsObjectBounds<LIC>(bounds, onlyVisible);
3403    mergeGraphicsObjectBounds<Line>(bounds, onlyVisible);
3404    mergeGraphicsObjectBounds<Molecule>(bounds, onlyVisible);
[3535]3405    mergeGraphicsObjectBounds<Outline>(bounds, onlyVisible);
[3861]3406    mergeGraphicsObjectBounds<Parallelepiped>(bounds, onlyVisible);
[3164]3407    mergeGraphicsObjectBounds<PolyData>(bounds, onlyVisible);
3408    mergeGraphicsObjectBounds<Polygon>(bounds, onlyVisible);
3409    mergeGraphicsObjectBounds<PseudoColor>(bounds, onlyVisible);
3410    mergeGraphicsObjectBounds<Sphere>(bounds, onlyVisible);
3411    mergeGraphicsObjectBounds<Streamlines>(bounds, onlyVisible);
[3818]3412    mergeGraphicsObjectBounds<Text3D>(bounds, onlyVisible);
[3164]3413    mergeGraphicsObjectBounds<Volume>(bounds, onlyVisible);
3414    mergeGraphicsObjectBounds<Warp>(bounds, onlyVisible);
3415
[3138]3416    for (int i = 0; i < 6; i += 2) {
3417        if (bounds[i+1] < bounds[i]) {
3418            bounds[i] = -0.5;
3419            bounds[i+1] = 0.5;
3420        }
3421    }
3422
3423    int numDims = 0;
3424    if (bounds[0] != bounds[1])
3425        numDims++;
3426    if (bounds[2] != bounds[3])
3427        numDims++;
3428    if (bounds[4] != bounds[5])
3429        numDims++;
3430
3431    if (numDims == 0) {
3432        bounds[0] -= .5;
3433        bounds[1] += .5;
3434        bounds[2] -= .5;
3435        bounds[3] += .5;
3436    }
3437
3438    TRACE("Bounds: %g %g %g %g %g %g",
3439          bounds[0],
3440          bounds[1],
3441          bounds[2],
3442          bounds[3],
3443          bounds[4],
3444          bounds[5]);
3445}
3446
3447/**
3448 * \brief Collect bounds of all graphics objects
3449 *
3450 * \param[out] bounds Bounds of all scene objects
3451 * \param[in] onlyVisible Only collect bounds of visible objects
3452 */
3453void Renderer::collectUnscaledBounds(double *bounds, bool onlyVisible)
3454{
3455    bounds[0] = DBL_MAX;
3456    bounds[1] = -DBL_MAX;
3457    bounds[2] = DBL_MAX;
3458    bounds[3] = -DBL_MAX;
3459    bounds[4] = DBL_MAX;
3460    bounds[5] = -DBL_MAX;
3461
[3164]3462    mergeGraphicsObjectUnscaledBounds<Arc>(bounds, onlyVisible);
3463    mergeGraphicsObjectUnscaledBounds<Arrow>(bounds, onlyVisible);
3464    mergeGraphicsObjectUnscaledBounds<Box>(bounds, onlyVisible);
3465    mergeGraphicsObjectUnscaledBounds<Cone>(bounds, onlyVisible);
3466    mergeGraphicsObjectUnscaledBounds<Contour2D>(bounds, onlyVisible);
3467    mergeGraphicsObjectUnscaledBounds<Contour3D>(bounds, onlyVisible);
3468    mergeGraphicsObjectUnscaledBounds<Cutplane>(bounds, onlyVisible);
3469    mergeGraphicsObjectUnscaledBounds<Cylinder>(bounds, onlyVisible);
3470    mergeGraphicsObjectUnscaledBounds<Disk>(bounds, onlyVisible);
3471    mergeGraphicsObjectUnscaledBounds<Glyphs>(bounds, onlyVisible);
3472    mergeGraphicsObjectUnscaledBounds<HeightMap>(bounds, onlyVisible);
[3818]3473    mergeGraphicsObjectUnscaledBounds<Image>(bounds, onlyVisible);
[4082]3474    mergeGraphicsObjectUnscaledBounds<ImageCutplane>(bounds, onlyVisible);
[3164]3475    mergeGraphicsObjectUnscaledBounds<LIC>(bounds, onlyVisible);
3476    mergeGraphicsObjectUnscaledBounds<Line>(bounds, onlyVisible);
3477    mergeGraphicsObjectUnscaledBounds<Molecule>(bounds, onlyVisible);
[3535]3478    mergeGraphicsObjectUnscaledBounds<Outline>(bounds, onlyVisible);
[3164]3479    mergeGraphicsObjectUnscaledBounds<PolyData>(bounds, onlyVisible);
[3861]3480    mergeGraphicsObjectUnscaledBounds<Parallelepiped>(bounds, onlyVisible);
[3164]3481    mergeGraphicsObjectUnscaledBounds<Polygon>(bounds, onlyVisible);
3482    mergeGraphicsObjectUnscaledBounds<PseudoColor>(bounds, onlyVisible);
3483    mergeGraphicsObjectUnscaledBounds<Sphere>(bounds, onlyVisible);
3484    mergeGraphicsObjectUnscaledBounds<Streamlines>(bounds, onlyVisible);
[3818]3485    mergeGraphicsObjectUnscaledBounds<Text3D>(bounds, onlyVisible);
[3164]3486    mergeGraphicsObjectUnscaledBounds<Volume>(bounds, onlyVisible);
3487    mergeGraphicsObjectUnscaledBounds<Warp>(bounds, onlyVisible);
3488
[2423]3489    for (int i = 0; i < 6; i += 2) {
3490        if (bounds[i+1] < bounds[i]) {
3491            bounds[i] = -0.5;
3492            bounds[i+1] = 0.5;
[2112]3493        }
3494    }
[2423]3495
3496    int numDims = 0;
3497    if (bounds[0] != bounds[1])
3498        numDims++;
3499    if (bounds[2] != bounds[3])
3500        numDims++;
3501    if (bounds[4] != bounds[5])
3502        numDims++;
3503
3504    if (numDims == 0) {
3505        bounds[0] -= .5;
3506        bounds[1] += .5;
3507        bounds[2] -= .5;
3508        bounds[3] += .5;
3509    }
3510
[2260]3511    TRACE("Bounds: %g %g %g %g %g %g",
3512          bounds[0],
3513          bounds[1],
3514          bounds[2],
3515          bounds[3],
3516          bounds[4],
3517          bounds[5]);
[2112]3518}
3519
[2121]3520/**
[2194]3521 * \brief Update data ranges for color-mapping and contours
[2146]3522 */
[2612]3523void Renderer::updateFieldRanges()
[2146]3524{
[2402]3525    collectDataRanges();
3526
[3164]3527    updateGraphicsObjectFieldRanges<Contour2D>();
3528    updateGraphicsObjectFieldRanges<Contour3D>();
3529    updateGraphicsObjectFieldRanges<Cutplane>();
3530    updateGraphicsObjectFieldRanges<Glyphs>();
3531    updateGraphicsObjectFieldRanges<HeightMap>();
[3818]3532    updateGraphicsObjectFieldRanges<Image>();
[4082]3533    updateGraphicsObjectFieldRanges<ImageCutplane>();
[3164]3534    updateGraphicsObjectFieldRanges<LIC>();
3535    updateGraphicsObjectFieldRanges<Molecule>();
[3818]3536    updateGraphicsObjectFieldRanges<PolyData>();
[3164]3537    updateGraphicsObjectFieldRanges<PseudoColor>();
3538    updateGraphicsObjectFieldRanges<Streamlines>();
3539    updateGraphicsObjectFieldRanges<Volume>();
3540    updateGraphicsObjectFieldRanges<Warp>();
[2402]3541}
3542
3543/**
3544 * \brief Collect cumulative data range of all DataSets
3545 *
[2612]3546 * \param[out] range Data range of all DataSets
3547 * \param[in] name Field name
3548 * \param[in] type Attribute type: e.g. POINT_DATA, CELL_DATA
3549 * \param[in] component Array component or -1 for magnitude
[2402]3550 * \param[in] onlyVisible Only collect range of visible DataSets
3551 */
[2612]3552void Renderer::collectDataRanges(double *range, const char *name,
3553                                 DataSet::DataAttributeType type,
3554                                 int component, bool onlyVisible)
[2402]3555{
3556    range[0] = DBL_MAX;
3557    range[1] = -DBL_MAX;
3558
3559    for (DataSetHashmap::iterator itr = _dataSets.begin();
3560         itr != _dataSets.end(); ++itr) {
3561        if (!onlyVisible || itr->second->getVisibility()) {
3562            double r[2];
[3136]3563            r[0] = DBL_MAX;
3564            r[1] = -DBL_MAX;
[2612]3565            itr->second->getDataRange(r, name, type, component);
[2402]3566            range[0] = min2(range[0], r[0]);
3567            range[1] = max2(range[1], r[1]);
[2261]3568        }
3569    }
[2402]3570    if (range[0] == DBL_MAX)
3571        range[0] = 0;
3572    if (range[1] == -DBL_MAX)
3573        range[1] = 1;
[2612]3574   
3575    TRACE("n:'%s' t:%d c:%d [%g,%g]", name, type, component,
3576          range[0], range[1]);
[2146]3577}
3578
3579/**
[2612]3580 * \brief Clear field range hashtables and free memory
[2121]3581 */
[2612]3582void Renderer::clearFieldRanges()
[2121]3583{
[2612]3584    TRACE("Deleting Field Ranges");
3585    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
3586         itr != _scalarPointDataRange.end(); ++itr) {
3587        delete [] itr->second;
3588    }
3589    _scalarPointDataRange.clear();
3590    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
3591         itr != _scalarCellDataRange.end(); ++itr) {
3592        delete [] itr->second;
3593    }
3594    _scalarCellDataRange.clear();
[3818]3595    for (FieldRangeHashmap::iterator itr = _scalarFieldDataRange.begin();
3596         itr != _scalarFieldDataRange.end(); ++itr) {
3597        delete [] itr->second;
3598    }
3599    _scalarFieldDataRange.clear();
[2612]3600    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
3601         itr != _vectorPointDataRange.end(); ++itr) {
3602        delete [] itr->second;
3603    }
3604    _vectorPointDataRange.clear();
3605    for (int i = 0; i < 3; i++) {
3606        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
3607             itr != _vectorCompPointDataRange[i].end(); ++itr) {
3608            delete [] itr->second;
3609        }
3610        _vectorCompPointDataRange[i].clear();
3611    }
3612    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
3613         itr != _vectorCellDataRange.end(); ++itr) {
3614        delete [] itr->second;
3615    }
3616    _vectorCellDataRange.clear();
3617    for (int i = 0; i < 3; i++) {
3618        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
3619             itr != _vectorCompCellDataRange[i].end(); ++itr) {
3620            delete [] itr->second;
3621        }
3622        _vectorCompCellDataRange[i].clear();
3623    }
[3818]3624    for (FieldRangeHashmap::iterator itr = _vectorFieldDataRange.begin();
3625         itr != _vectorFieldDataRange.end(); ++itr) {
3626        delete [] itr->second;
3627    }
3628    _vectorFieldDataRange.clear();
3629    for (int i = 0; i < 3; i++) {
3630        for (FieldRangeHashmap::iterator itr = _vectorCompFieldDataRange[i].begin();
3631             itr != _vectorCompFieldDataRange[i].end(); ++itr) {
3632            delete [] itr->second;
3633        }
3634        _vectorCompFieldDataRange[i].clear();
3635    }
[2612]3636}
[2121]3637
[3330]3638/**
3639 * \brief Clear user-defined field range hashtables and free memory
3640 */
3641void Renderer::clearUserFieldRanges()
3642{
3643    TRACE("Deleting User Field Ranges");
3644    for (FieldRangeHashmap::iterator itr = _userScalarPointDataRange.begin();
3645         itr != _userScalarPointDataRange.end(); ++itr) {
3646        delete [] itr->second;
3647    }
3648    _userScalarPointDataRange.clear();
3649    for (FieldRangeHashmap::iterator itr = _userScalarCellDataRange.begin();
3650         itr != _userScalarCellDataRange.end(); ++itr) {
3651        delete [] itr->second;
3652    }
3653    _userScalarCellDataRange.clear();
[3818]3654    for (FieldRangeHashmap::iterator itr = _userScalarFieldDataRange.begin();
3655         itr != _userScalarFieldDataRange.end(); ++itr) {
3656        delete [] itr->second;
3657    }
3658    _userScalarFieldDataRange.clear();
[3330]3659    for (FieldRangeHashmap::iterator itr = _userVectorPointDataRange.begin();
3660         itr != _userVectorPointDataRange.end(); ++itr) {
3661        delete [] itr->second;
3662    }
3663    _userVectorPointDataRange.clear();
3664    for (int i = 0; i < 3; i++) {
3665        for (FieldRangeHashmap::iterator itr = _userVectorCompPointDataRange[i].begin();
3666             itr != _userVectorCompPointDataRange[i].end(); ++itr) {
3667            delete [] itr->second;
3668        }
3669        _userVectorCompPointDataRange[i].clear();
3670    }
3671    for (FieldRangeHashmap::iterator itr = _userVectorCellDataRange.begin();
3672         itr != _userVectorCellDataRange.end(); ++itr) {
3673        delete [] itr->second;
3674    }
3675    _userVectorCellDataRange.clear();
3676    for (int i = 0; i < 3; i++) {
3677        for (FieldRangeHashmap::iterator itr = _userVectorCompCellDataRange[i].begin();
3678             itr != _userVectorCompCellDataRange[i].end(); ++itr) {
3679            delete [] itr->second;
3680        }
3681        _userVectorCompCellDataRange[i].clear();
3682    }
[3818]3683    for (FieldRangeHashmap::iterator itr = _userVectorFieldDataRange.begin();
3684         itr != _userVectorFieldDataRange.end(); ++itr) {
3685        delete [] itr->second;
3686    }
3687    _userVectorFieldDataRange.clear();
3688    for (int i = 0; i < 3; i++) {
3689        for (FieldRangeHashmap::iterator itr = _userVectorCompFieldDataRange[i].begin();
3690             itr != _userVectorCompFieldDataRange[i].end(); ++itr) {
3691            delete [] itr->second;
3692        }
3693        _userVectorCompFieldDataRange[i].clear();
3694    }
[3330]3695}
3696
3697/**
3698 * \brief Set up hashtables for min/max values of all fields from loaded
3699 * datasets
3700 *
3701 * Note that this method does not set the ranges, it just creates the table
3702 * entries
3703 */
[2612]3704void Renderer::initFieldRanges()
3705{
3706    clearFieldRanges();
3707
[2121]3708    for (DataSetHashmap::iterator itr = _dataSets.begin();
3709         itr != _dataSets.end(); ++itr) {
[2612]3710        DataSet *ds = itr->second;
3711        std::vector<std::string> names;
3712        ds->getFieldNames(names, DataSet::POINT_DATA, 1);
3713        for (std::vector<std::string>::iterator itr = names.begin();
3714             itr != names.end(); ++itr) {
3715            FieldRangeHashmap::iterator fritr =
3716                _scalarPointDataRange.find(*itr);
3717            if (fritr == _scalarPointDataRange.end()) {
3718                _scalarPointDataRange[*itr] = new double[2];
3719                _scalarPointDataRange[*itr][0] = 0;
3720                _scalarPointDataRange[*itr][1] = 1;
3721            }
[2194]3722        }
[2612]3723        names.clear();
3724        ds->getFieldNames(names, DataSet::CELL_DATA, 1);
3725        for (std::vector<std::string>::iterator itr = names.begin();
3726             itr != names.end(); ++itr) {
3727            FieldRangeHashmap::iterator fritr =
3728                _scalarCellDataRange.find(*itr);
3729            if (fritr == _scalarCellDataRange.end()) {
3730                _scalarCellDataRange[*itr] = new double[2];
3731                _scalarCellDataRange[*itr][0] = 0;
3732                _scalarCellDataRange[*itr][1] = 1;
3733            }
3734        }
3735        names.clear();
[3818]3736        ds->getFieldNames(names, DataSet::FIELD_DATA, 1);
3737        for (std::vector<std::string>::iterator itr = names.begin();
3738             itr != names.end(); ++itr) {
3739            FieldRangeHashmap::iterator fritr =
3740                _scalarFieldDataRange.find(*itr);
3741            if (fritr == _scalarFieldDataRange.end()) {
3742                _scalarFieldDataRange[*itr] = new double[2];
3743                _scalarFieldDataRange[*itr][0] = 0;
3744                _scalarFieldDataRange[*itr][1] = 1;
3745            }
3746        }
3747        names.clear();
[2612]3748        ds->getFieldNames(names, DataSet::POINT_DATA, 3);
3749        for (std::vector<std::string>::iterator itr = names.begin();
3750             itr != names.end(); ++itr) {
3751            FieldRangeHashmap::iterator fritr =
3752                _vectorPointDataRange.find(*itr);
3753            if (fritr == _vectorPointDataRange.end()) {
3754                _vectorPointDataRange[*itr] = new double[2];
3755                _vectorPointDataRange[*itr][0] = 0;
3756                _vectorPointDataRange[*itr][1] = 1;
3757            }
3758            for (int i = 0; i < 3; i++) {
3759                fritr = _vectorCompPointDataRange[i].find(*itr);
3760                if (fritr == _vectorCompPointDataRange[i].end()) {
3761                    _vectorCompPointDataRange[i][*itr] = new double[2];
3762                    _vectorCompPointDataRange[i][*itr][0] = 0;
3763                    _vectorCompPointDataRange[i][*itr][1] = 1;
3764                }
3765            }
3766        }
3767        names.clear();
3768        ds->getFieldNames(names, DataSet::CELL_DATA, 3);
3769        for (std::vector<std::string>::iterator itr = names.begin();
3770             itr != names.end(); ++itr) {
3771            FieldRangeHashmap::iterator fritr =
3772                _vectorCellDataRange.find(*itr);
3773            if (fritr == _vectorCellDataRange.end()) {
3774                _vectorCellDataRange[*itr] = new double[2];
3775                _vectorCellDataRange[*itr][0] = 0;
3776                _vectorCellDataRange[*itr][1] = 1;
3777            }
3778            for (int i = 0; i < 3; i++) {
3779                fritr = _vectorCompCellDataRange[i].find(*itr);
3780                if (fritr == _vectorCompCellDataRange[i].end()) {
3781                    _vectorCompCellDataRange[i][*itr] = new double[2];
3782                    _vectorCompCellDataRange[i][*itr][0] = 0;
3783                    _vectorCompCellDataRange[i][*itr][1] = 1;
3784                }
3785            }
3786        }
[3818]3787        names.clear();
3788        ds->getFieldNames(names, DataSet::FIELD_DATA, 3);
3789        for (std::vector<std::string>::iterator itr = names.begin();
3790             itr != names.end(); ++itr) {
3791            FieldRangeHashmap::iterator fritr =
3792                _vectorFieldDataRange.find(*itr);
3793            if (fritr == _vectorFieldDataRange.end()) {
3794                _vectorFieldDataRange[*itr] = new double[2];
3795                _vectorFieldDataRange[*itr][0] = 0;
3796                _vectorFieldDataRange[*itr][1] = 1;
3797            }
3798            for (int i = 0; i < 3; i++) {
3799                fritr = _vectorCompFieldDataRange[i].find(*itr);
3800                if (fritr == _vectorCompFieldDataRange[i].end()) {
3801                    _vectorCompFieldDataRange[i][*itr] = new double[2];
3802                    _vectorCompFieldDataRange[i][*itr][0] = 0;
3803                    _vectorCompFieldDataRange[i][*itr][1] = 1;
3804                }
3805            }
3806        }
[2121]3807    }
3808}
3809
[2112]3810/**
[2612]3811 * \brief Returns boolean flag indicating if cumulative data ranges
3812 * should be used
3813 */
3814bool Renderer::getUseCumulativeRange()
3815{
3816    return _useCumulativeRange;
3817}
3818
3819/**
[3330]3820 * \brief Set explicit range to use for mapping fields
3821 */
3822bool Renderer::setCumulativeDataRange(double *range, const char *name,
3823                                      DataSet::DataAttributeType type,
3824                                      int numComponents,
3825                                      int component)
3826{
3827    if (range == NULL || name == NULL)
3828        return false;
3829
3830    _useCumulativeRange = true;
3831    bool found = false;
3832
3833    switch (type) {
3834    case DataSet::POINT_DATA: {
3835        FieldRangeHashmap::iterator itr;
3836        if (numComponents == 1) {
3837            itr = _userScalarPointDataRange.find(name);
3838            if (itr == _userScalarPointDataRange.end()) {
3839                _userScalarPointDataRange[name] = new double[2];
3840                memcpy(_userScalarPointDataRange[name], range, sizeof(double)*2);
3841            } else {
3842                found = true;
3843                memcpy(itr->second, range, sizeof(double)*2);
3844            }
3845        } else if (numComponents == 3) {
3846            if (component == -1) {
3847                itr = _userVectorPointDataRange.find(name);
3848                if (itr == _userVectorPointDataRange.end()) {
3849                    _userVectorPointDataRange[name] = new double[2];
3850                    memcpy(itr->second, _userVectorPointDataRange[name], sizeof(double)*2);
3851                } else {
3852                    found = true;
3853                    memcpy(itr->second, range, sizeof(double)*2);
3854                }
3855                return found;
3856            } else if (component >= 0 && component <= 3) {
3857                itr = _userVectorCompPointDataRange[component].find(name);
3858                if (itr == _userVectorCompPointDataRange[component].end()) {
3859                    _userVectorCompPointDataRange[component][name] = new double[2];
3860                    memcpy(_userVectorCompPointDataRange[component][name], range, sizeof(double)*2);
3861                } else {
3862                    found = true;
3863                    memcpy(itr->second, range, sizeof(double)*2);
3864                }
3865            }
3866        }
3867    }
3868        break;
3869    case DataSet::CELL_DATA: {
3870        FieldRangeHashmap::iterator itr;
3871        if (numComponents == 1) {
3872            itr = _userScalarCellDataRange.find(name);
3873            if (itr == _userScalarCellDataRange.end()) {
3874                _userScalarCellDataRange[name] = new double[2];
3875                memcpy(_userScalarCellDataRange[name], range, sizeof(double)*2);
3876            } else {
3877                found = true;
3878                memcpy(itr->second, range, sizeof(double)*2);
3879            }
3880        } else if (numComponents == 3) {
3881            if (component == -1) {
3882                itr = _userVectorCellDataRange.find(name);
3883                if (itr == _userVectorCellDataRange.end()) {
3884                    _userVectorCellDataRange[name] = new double[2];
3885                    memcpy(_userVectorCellDataRange[name], range, sizeof(double)*2);
3886                } else {
3887                    found = true;
3888                    memcpy(itr->second, range, sizeof(double)*2);
3889                }
3890            } else if (component >= 0 && component <= 3) {
3891                itr = _userVectorCompCellDataRange[component].find(name);
3892                if (itr == _userVectorCompCellDataRange[component].end()) {
3893                    _userVectorCompCellDataRange[component][name] = new double[2];
3894                    memcpy(_userVectorCompCellDataRange[component][name], range, sizeof(double)*2);
3895                } else {
3896                    found = true;
3897                    memcpy(itr->second, range, sizeof(double)*2);
3898                }
3899            }
3900        }
3901    }
3902        break;
[3818]3903    case DataSet::FIELD_DATA: {
3904        FieldRangeHashmap::iterator itr;
3905        if (numComponents == 1) {
3906            itr = _userScalarFieldDataRange.find(name);
3907            if (itr == _userScalarFieldDataRange.end()) {
3908                _userScalarFieldDataRange[name] = new double[2];
3909                memcpy(_userScalarFieldDataRange[name], range, sizeof(double)*2);
3910            } else {
3911                found = true;
3912                memcpy(itr->second, range, sizeof(double)*2);
3913            }
3914        } else if (numComponents == 3) {
3915            if (component == -1) {
3916                itr = _userVectorFieldDataRange.find(name);
3917                if (itr == _userVectorFieldDataRange.end()) {
3918                    _userVectorFieldDataRange[name] = new double[2];
3919                    memcpy(_userVectorFieldDataRange[name], range, sizeof(double)*2);
3920                } else {
3921                    found = true;
3922                    memcpy(itr->second, range, sizeof(double)*2);
3923                }
3924            } else if (component >= 0 && component <= 3) {
3925                itr = _userVectorCompFieldDataRange[component].find(name);
3926                if (itr == _userVectorCompFieldDataRange[component].end()) {
3927                    _userVectorCompFieldDataRange[component][name] = new double[2];
3928                    memcpy(_userVectorCompFieldDataRange[component][name], range, sizeof(double)*2);
3929                } else {
3930                    found = true;
3931                    memcpy(itr->second, range, sizeof(double)*2);
3932                }
3933            }
3934        }
3935    }
3936        break;
[3330]3937    default:
3938        ERROR("Bad Field Type");
3939    }
3940
3941    // Notify graphics objects of new ranges
3942    updateFieldRanges();
3943    // Bounds may have changed
3944    sceneBoundsChanged();
3945    _needsRedraw = true;
3946
3947    TRACE("Field: %s found: %d range: %g %g", name, (found ? 1 : 0), range[0], range[1]);
3948    return found;
3949}
3950
3951/**
[2612]3952 * \brief Get the cumulative range across all DataSets for a point
[3818]3953 * data field if it exists, otherwise a cell data field if it exists,
3954 * otherwise a field data field if it exists
[2402]3955 *
[2612]3956 * \param[out] range Pointer to an array of 2 doubles
3957 * \param[in] name Field name
3958 * \param[in] numComponents Number of components in field
3959 * \param[in] component Index of component or -1 for magnitude/scalar
3960 * \return boolean indicating if field was found
[2402]3961 */
[2612]3962bool Renderer::getCumulativeDataRange(double *range, const char *name,
3963                                      int numComponents,
3964                                      int component)
[2402]3965{
[2612]3966    bool ret;
[3818]3967    if ((ret = getCumulativeDataRange(range, name, DataSet::POINT_DATA,
3968                                      numComponents, component))) {
3969        ; // Found point data
3970    } else if ((ret = getCumulativeDataRange(range, name, DataSet::CELL_DATA,
3971                                             numComponents, component))) {
3972        ; // Found cell data
3973       
3974    } else {
3975        ret = getCumulativeDataRange(range, name, DataSet::FIELD_DATA,
[2612]3976                                     numComponents, component);
[3818]3977    }
[2612]3978    return ret;
3979}
[2402]3980
[2612]3981/**
3982 * \brief Get the cumulative range across all DataSets for a field
3983 *
3984 * \param[out] range Pointer to an array of 2 doubles
3985 * \param[in] name Field name
3986 * \param[in] type DataAttributeType of field
3987 * \param[in] numComponents Number of components in field
3988 * \param[in] component Index of component or -1 for magnitude/scalar
3989 * \return boolean indicating if field was found
3990 */
3991bool Renderer::getCumulativeDataRange(double *range, const char *name,
3992                                      DataSet::DataAttributeType type,
3993                                      int numComponents,
3994                                      int component)
3995{
[3330]3996    if (range == NULL || name == NULL)
[2617]3997        return false;
[3330]3998
[2612]3999    switch (type) {
4000    case DataSet::POINT_DATA: {
4001        FieldRangeHashmap::iterator itr;
4002        if (numComponents == 1) {
[3330]4003            itr = _userScalarPointDataRange.find(name);
4004            if (itr == _userScalarPointDataRange.end()) {
4005                itr = _scalarPointDataRange.find(name);
4006                if (itr == _scalarPointDataRange.end()) {
4007                    return false;
4008                }
[2612]4009            }
[3330]4010            memcpy(range, itr->second, sizeof(double)*2);
4011            return true;
[2612]4012        } else if (numComponents == 3) {
4013            if (component == -1) {
[3330]4014                itr = _userVectorPointDataRange.find(name);
4015                if (itr == _userVectorPointDataRange.end()) {
4016                    itr = _vectorPointDataRange.find(name);
4017                    if (itr == _vectorPointDataRange.end()) {
4018                        return false;
4019                    }
[2612]4020                }
[3330]4021                memcpy(range, itr->second, sizeof(double)*2);
4022                return true;
[2612]4023            } else if (component >= 0 && component <= 3) {
[3330]4024                itr = _userVectorCompPointDataRange[component].find(name);
4025                if (itr == _userVectorCompPointDataRange[component].end()) {
4026                    itr = _vectorCompPointDataRange[component].find(name);
4027                    if (itr == _vectorCompPointDataRange[component].end()) {
4028                        return false;
4029                    }
[2612]4030                }
[3330]4031                memcpy(range, itr->second, sizeof(double)*2);
4032                return true;
[2612]4033            }
[2402]4034        }
4035    }
[2612]4036        break;
4037    case DataSet::CELL_DATA: {
4038        FieldRangeHashmap::iterator itr;
4039        if (numComponents == 1) {
[3330]4040            itr = _userScalarCellDataRange.find(name);
4041            if (itr == _userScalarCellDataRange.end()) {
4042                itr = _scalarCellDataRange.find(name);
4043                if (itr == _scalarCellDataRange.end()) {
4044                    return false;
4045                }
[2612]4046            }
[3330]4047            memcpy(range, itr->second, sizeof(double)*2);
4048            return true;
[2612]4049        } else if (numComponents == 3) {
4050            if (component == -1) {
[3330]4051                itr = _userVectorCellDataRange.find(name);
4052                if (itr == _userVectorCellDataRange.end()) {
4053                    itr = _vectorCellDataRange.find(name);
4054                    if (itr == _vectorCellDataRange.end()) {
4055                        return false;
4056                    }
[2612]4057                }
[3330]4058                memcpy(range, itr->second, sizeof(double)*2);
4059                return true;
[2612]4060            } else if (component >= 0 && component <= 3) {
[3330]4061                itr = _userVectorCompCellDataRange[component].find(name);
4062                if (itr == _userVectorCompCellDataRange[component].end()) {
4063                    itr = _vectorCompCellDataRange[component].find(name);
4064                    if (itr == _vectorCompCellDataRange[component].end()) {
4065                        return false;
4066                    }
[2612]4067                }
[3330]4068                memcpy(range, itr->second, sizeof(double)*2);
4069                return true;
[2612]4070            }
4071        }
4072    }
4073        break;
[3818]4074    case DataSet::FIELD_DATA: {
4075        FieldRangeHashmap::iterator itr;
4076        if (numComponents == 1) {
4077            itr = _userScalarFieldDataRange.find(name);
4078            if (itr == _userScalarFieldDataRange.end()) {
4079                itr = _scalarFieldDataRange.find(name);
4080                if (itr == _scalarFieldDataRange.end()) {
4081                    return false;
4082                }
4083            }
4084            memcpy(range, itr->second, sizeof(double)*2);
4085            return true;
4086        } else if (numComponents == 3) {
4087            if (component == -1) {
4088                itr = _userVectorFieldDataRange.find(name);
4089                if (itr == _userVectorFieldDataRange.end()) {
4090                    itr = _vectorFieldDataRange.find(name);
4091                    if (itr == _vectorFieldDataRange.end()) {
4092                        return false;
4093                    }
4094                }
4095                memcpy(range, itr->second, sizeof(double)*2);
4096                return true;
4097            } else if (component >= 0 && component <= 3) {
4098                itr = _userVectorCompFieldDataRange[component].find(name);
4099                if (itr == _userVectorCompFieldDataRange[component].end()) {
4100                    itr = _vectorCompFieldDataRange[component].find(name);
4101                    if (itr == _vectorCompFieldDataRange[component].end()) {
4102                        return false;
4103                    }
4104                }
4105                memcpy(range, itr->second, sizeof(double)*2);
4106                return true;
4107            }
4108        }
4109    }
4110        break;
[2612]4111    default:
4112        break;
4113    }
4114    return false;
4115}
[2402]4116
[2612]4117void Renderer::collectDataRanges()
4118{
4119    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
4120         itr != _scalarPointDataRange.end(); ++itr) {
4121        collectDataRanges(itr->second, itr->first.c_str(),
4122                          DataSet::POINT_DATA, -1,
4123                          _cumulativeRangeOnlyVisible);
4124    }
4125    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
4126         itr != _scalarCellDataRange.end(); ++itr) {
4127        collectDataRanges(itr->second, itr->first.c_str(),
4128                          DataSet::CELL_DATA, -1,
4129                          _cumulativeRangeOnlyVisible);
4130    }
[3818]4131    for (FieldRangeHashmap::iterator itr = _scalarFieldDataRange.begin();
4132         itr != _scalarFieldDataRange.end(); ++itr) {
4133        collectDataRanges(itr->second, itr->first.c_str(),
4134                          DataSet::FIELD_DATA, -1,
4135                          _cumulativeRangeOnlyVisible);
4136    }
[2612]4137    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
4138         itr != _vectorPointDataRange.end(); ++itr) {
4139        collectDataRanges(itr->second, itr->first.c_str(),
4140                          DataSet::POINT_DATA, -1,
4141                          _cumulativeRangeOnlyVisible);
4142    }
4143    for (int i = 0; i < 3; i++) {
4144        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
4145             itr != _vectorCompPointDataRange[i].end(); ++itr) {
4146            collectDataRanges(itr->second, itr->first.c_str(),
4147                              DataSet::POINT_DATA, i,
4148                              _cumulativeRangeOnlyVisible);
4149        }
4150    }
4151    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
4152         itr != _vectorCellDataRange.end(); ++itr) {
4153        collectDataRanges(itr->second, itr->first.c_str(),
4154                          DataSet::CELL_DATA, -1,
4155                          _cumulativeRangeOnlyVisible);
4156    }
4157    for (int i = 0; i < 3; i++) {
4158        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
4159             itr != _vectorCompCellDataRange[i].end(); ++itr) {
4160            collectDataRanges(itr->second, itr->first.c_str(),
4161                              DataSet::CELL_DATA, i,
4162                              _cumulativeRangeOnlyVisible);
4163        }
4164    }
[3818]4165    for (FieldRangeHashmap::iterator itr = _vectorFieldDataRange.begin();
4166         itr != _vectorFieldDataRange.end(); ++itr) {
4167        collectDataRanges(itr->second, itr->first.c_str(),
4168                          DataSet::FIELD_DATA, -1,
4169                          _cumulativeRangeOnlyVisible);
4170    }
4171    for (int i = 0; i < 3; i++) {
4172        for (FieldRangeHashmap::iterator itr = _vectorCompFieldDataRange[i].begin();
4173             itr != _vectorCompFieldDataRange[i].end(); ++itr) {
4174            collectDataRanges(itr->second, itr->first.c_str(),
4175                              DataSet::FIELD_DATA, i,
4176                              _cumulativeRangeOnlyVisible);
4177        }
4178    }
[2612]4179}
4180
[2402]4181/**
[2423]4182 * \brief Determines if AABB lies in a principal axis plane
4183 * and if so, returns the plane normal
4184 */
4185bool Renderer::is2D(const double bounds[6],
[2612]4186                    PrincipalPlane *plane,
[2423]4187                    double *offset) const
4188{
4189    if (bounds[4] == bounds[5]) {
4190        // Z = 0, XY plane
4191        if (plane)
4192            *plane = PLANE_XY;
4193        if (offset)
4194            *offset = bounds[4];
4195        return true;
4196    } else if (bounds[0] == bounds[1]) {
4197        // X = 0, ZY plane
4198        if (plane)
4199            *plane = PLANE_ZY;
4200        if (offset)
4201            *offset = bounds[0];
4202        return true;
4203    } else if (bounds[2] == bounds[3]) {
4204        // Y = 0, XZ plane
4205        if (plane)
4206            *plane = PLANE_XZ;
4207        if (offset)
4208            *offset = bounds[2];
4209        return true;
4210    }
4211    *plane = PLANE_XY;
[4093]4212    *offset = bounds[4] + (bounds[5] - bounds[4])/2.0;
[2423]4213    return false;
4214}
4215
[3683]4216int Renderer::addLight(float pos[3])
4217{
4218    vtkSmartPointer<vtkLight> light = vtkSmartPointer<vtkLight>::New();
4219    light->SetLightTypeToCameraLight();
4220    light->SetPosition(pos[0], pos[1], pos[2]);
4221    light->SetFocalPoint(0, 0, 0);
4222    light->PositionalOff();
4223    _renderer->AddLight(light);
4224    _needsRedraw = true;
4225    return (_renderer->GetLights()->GetNumberOfItems()-1);
4226}
4227
4228vtkLight *Renderer::getLight(int lightIdx)
4229{
4230    vtkLightCollection *lights = _renderer->GetLights();
4231    if (lights->GetNumberOfItems() < lightIdx+1)
4232        return NULL;
4233    lights->InitTraversal();
4234    vtkLight *light = NULL;
4235    int i = 0;
4236    do {
4237        light = lights->GetNextItem();
4238    } while (i++ < lightIdx);
4239    return light;
4240}
4241
4242void Renderer::setLightSwitch(int lightIdx, bool state)
4243{
4244    vtkLight *light = getLight(lightIdx);
4245    if (light == NULL) {
4246        ERROR("Unknown light %d", lightIdx);
4247        return;
4248    }
4249    light->SetSwitch((state ? 1 : 0));
4250    _needsRedraw = true;
4251}
4252
[2423]4253/**
[2100]4254 * \brief Initialize the camera zoom region to include the bounding volume given
4255 */
[3120]4256void Renderer::initCamera(bool initCameraMode)
[2100]4257{
[2260]4258#ifdef WANT_TRACE
4259    switch (_cameraMode) {
4260    case IMAGE:
4261        TRACE("Image camera");
4262        break;
4263    case ORTHO:
4264        TRACE("Ortho camera");
4265        break;
4266    case PERSPECTIVE:
4267        TRACE("Perspective camera");
4268        break;
4269    default:
4270        TRACE("Unknown camera mode");
4271    }
4272#endif
[3419]4273    // Clear user requested zoom region
4274    _userImgWorldOrigin[0] = 0;
4275    _userImgWorldOrigin[1] = 0;
4276    _userImgWorldDims[0] = -1;
4277    _userImgWorldDims[1] = -1;
4278
[2112]4279    double bounds[6];
[4171]4280    collectBounds(bounds, true);
[2423]4281    bool twod = is2D(bounds, &_imgCameraPlane, &_imgCameraOffset);
4282    if (twod) {
[3120]4283        if (initCameraMode) {
4284            TRACE("Changing camera mode to image");
4285            _cameraMode = IMAGE;
4286        }
[2423]4287        if (_imgCameraPlane == PLANE_ZY) {
4288            _imgWorldOrigin[0] = bounds[4];
4289            _imgWorldOrigin[1] = bounds[2];
4290            _imgWorldDims[0] = bounds[5] - bounds[4];
4291            _imgWorldDims[1] = bounds[3] - bounds[2];
4292        } else if (_imgCameraPlane == PLANE_XZ) {
4293            _imgWorldOrigin[0] = bounds[0];
4294            _imgWorldOrigin[1] = bounds[4];
4295            _imgWorldDims[0] = bounds[1] - bounds[0];
4296            _imgWorldDims[1] = bounds[5] - bounds[4];
4297        } else {
4298            _imgWorldOrigin[0] = bounds[0];
4299            _imgWorldOrigin[1] = bounds[2];
4300            _imgWorldDims[0] = bounds[1] - bounds[0];
4301            _imgWorldDims[1] = bounds[3] - bounds[2];
4302        }
4303    } else {
4304        _imgWorldOrigin[0] = bounds[0];
4305        _imgWorldOrigin[1] = bounds[2];
4306        _imgWorldDims[0] = bounds[1] - bounds[0];
4307        _imgWorldDims[1] = bounds[3] - bounds[2];
4308    }
4309
[2194]4310    _cameraPan[0] = 0;
4311    _cameraPan[1] = 0;
4312    _cameraZoomRatio = 1;
[2112]4313
[2260]4314    switch (_cameraMode) {
4315    case IMAGE:
[3330]4316        //_renderer->ResetCamera(bounds);
[3419]4317        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
4318                             _imgWorldDims[0], _imgWorldDims[1]);
[3198]4319        resetAxes(bounds);
[2260]4320        break;
4321    case ORTHO:
[2112]4322        _renderer->GetActiveCamera()->ParallelProjectionOn();
[2402]4323        resetAxes(bounds);
[3330]4324        //_renderer->ResetCamera(bounds);
[3615]4325        resetVtkCamera();
[2349]4326        //computeScreenWorldCoords();
[2260]4327        break;
4328    case PERSPECTIVE:
[2112]4329        _renderer->GetActiveCamera()->ParallelProjectionOff();
[2402]4330        resetAxes(bounds);
[3330]4331        //_renderer->ResetCamera(bounds);
[3615]4332        resetVtkCamera();
[2349]4333        //computeScreenWorldCoords();
[2260]4334        break;
4335    default:
4336        ERROR("Unknown camera mode");
[2112]4337    }
[2423]4338
[2260]4339#ifdef WANT_TRACE
4340    printCameraInfo(_renderer->GetActiveCamera());
4341#endif
[2100]4342}
4343
[3615]4344#if 0
[2100]4345/**
4346 * \brief Print debugging info about a vtkCamera
4347 */
4348void Renderer::printCameraInfo(vtkCamera *camera)
4349{
[2423]4350    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g",
[2100]4351          camera->GetParallelScale(),
[2222]4352          camera->GetViewAngle(),
[2423]4353          camera->GetDistance(),
[2214]4354          camera->GetPosition()[0],
[2100]4355          camera->GetPosition()[1],
4356          camera->GetPosition()[2],
[2200]4357          camera->GetFocalPoint()[0],
4358          camera->GetFocalPoint()[1],
4359          camera->GetFocalPoint()[2],
4360          camera->GetViewUp()[0],
4361          camera->GetViewUp()[1],
4362          camera->GetViewUp()[2],
[2100]4363          camera->GetClippingRange()[0],
4364          camera->GetClippingRange()[1]);
4365}
[3615]4366#endif
[2100]4367
4368/**
4369 * \brief Set the RGB background color to render into the image
4370 */
4371void Renderer::setBackgroundColor(float color[3])
4372{
4373    _bgColor[0] = color[0];
4374    _bgColor[1] = color[1];
4375    _bgColor[2] = color[2];
4376    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
4377    _needsRedraw = true;
4378}
4379
4380/**
4381 * \brief Set the opacity of the specified DataSet's associated graphics objects
4382 */
[2455]4383void Renderer::setDataSetOpacity(const DataSetId& id, double opacity)
[2100]4384{
[2455]4385    DataSetHashmap::iterator itr;
4386
4387    bool doAll = false;
4388
4389    if (id.compare("all") == 0) {
4390        itr = _dataSets.begin();
[3525]4391        if (itr == _dataSets.end())
4392            return;
[2455]4393        doAll = true;
4394    } else {
4395        itr = _dataSets.find(id);
4396    }
4397    if (itr == _dataSets.end()) {
[3525]4398        ERROR("Unknown dataset %s", id.c_str());
[2455]4399        return;
4400    }
4401
4402    do {
4403        itr->second->setOpacity(opacity);
4404    } while (doAll && ++itr != _dataSets.end());
4405
[2612]4406    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
4407        setGraphicsObjectOpacity<Contour2D>(id, opacity);
4408    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
4409        setGraphicsObjectOpacity<Contour3D>(id, opacity);
4410    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
4411        setGraphicsObjectOpacity<Cutplane>(id, opacity);
4412    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
4413        setGraphicsObjectOpacity<Glyphs>(id, opacity);
4414    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
4415        setGraphicsObjectOpacity<HeightMap>(id, opacity);
[3818]4416    if (id.compare("all") == 0 || getGraphicsObject<Image>(id) != NULL)
4417        setGraphicsObjectOpacity<Image>(id, opacity);
[4082]4418    if (id.compare("all") == 0 || getGraphicsObject<ImageCutplane>(id) != NULL)
4419        setGraphicsObjectOpacity<ImageCutplane>(id, opacity);
[2612]4420    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
4421        setGraphicsObjectOpacity<LIC>(id, opacity);
4422    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
4423        setGraphicsObjectOpacity<Molecule>(id, opacity);
[3535]4424    if (id.compare("all") == 0 || getGraphicsObject<Outline>(id) != NULL)
4425        setGraphicsObjectOpacity<Outline>(id, opacity);
[2612]4426    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
4427        setGraphicsObjectOpacity<PolyData>(id, opacity);
4428    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
4429        setGraphicsObjectOpacity<PseudoColor>(id, opacity);
4430    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
4431        setGraphicsObjectOpacity<Streamlines>(id, opacity);
4432    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
4433        setGraphicsObjectOpacity<Volume>(id, opacity);
[3126]4434    if (id.compare("all") == 0 || getGraphicsObject<Warp>(id) != NULL)
4435        setGraphicsObjectOpacity<Warp>(id, opacity);
[2455]4436
4437    _needsRedraw = true;
[2100]4438}
4439
4440/**
4441 * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
4442 */
[2455]4443void Renderer::setDataSetVisibility(const DataSetId& id, bool state)
[2100]4444{
[2194]4445    DataSetHashmap::iterator itr;
4446
4447    bool doAll = false;
4448
4449    if (id.compare("all") == 0) {
4450        itr = _dataSets.begin();
4451        doAll = true;
[3330]4452        if (itr == _dataSets.end())
4453            return;
[2194]4454    } else {
4455        itr = _dataSets.find(id);
4456    }
4457    if (itr == _dataSets.end()) {
4458        ERROR("Unknown dataset %s", id.c_str());
4459        return;
4460    }
4461
4462    do {
4463        itr->second->setVisibility(state);
4464    } while (doAll && ++itr != _dataSets.end());
4465
[2612]4466    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
4467        setGraphicsObjectVisibility<Contour2D>(id, state);
4468    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
4469        setGraphicsObjectVisibility<Contour3D>(id, state);
4470    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
4471        setGraphicsObjectVisibility<Cutplane>(id, state);
4472    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
4473        setGraphicsObjectVisibility<Glyphs>(id, state);
4474    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
4475        setGraphicsObjectVisibility<HeightMap>(id, state);
[3818]4476    if (id.compare("all") == 0 || getGraphicsObject<Image>(id) != NULL)
4477        setGraphicsObjectVisibility<Image>(id, state);
[4082]4478    if (id.compare("all") == 0 || getGraphicsObject<ImageCutplane>(id) != NULL)
4479        setGraphicsObjectVisibility<ImageCutplane>(id, state);
[2612]4480    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
4481        setGraphicsObjectVisibility<LIC>(id, state);
4482    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
4483        setGraphicsObjectVisibility<Molecule>(id, state);
[3535]4484    if (id.compare("all") == 0 || getGraphicsObject<Outline>(id) != NULL)
4485        setGraphicsObjectVisibility<Outline>(id, state);
[2612]4486    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
4487        setGraphicsObjectVisibility<PolyData>(id, state);
4488    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
4489        setGraphicsObjectVisibility<PseudoColor>(id, state);
4490    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
4491        setGraphicsObjectVisibility<Streamlines>(id, state);
4492    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
4493        setGraphicsObjectVisibility<Volume>(id, state);
[3126]4494    if (id.compare("all") == 0 || getGraphicsObject<Warp>(id) != NULL)
4495        setGraphicsObjectVisibility<Warp>(id, state);
[2455]4496
4497    _needsRedraw = true;
[2100]4498}
4499
4500/**
[2317]4501 * \brief Set a user clipping plane
4502 *
4503 * TODO: Fix clip plane positions after a change in actor bounds
4504 */
4505void Renderer::setClipPlane(Axis axis, double ratio, int direction)
4506{
4507    double bounds[6];
[4171]4508    collectBounds(bounds, true);
[2317]4509
4510    switch (axis) {
4511    case X_AXIS:
4512        if (direction > 0) {
4513            if (ratio > 0.0) {
4514                if (_userClipPlanes[0] == NULL) {
4515                    _userClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
4516                    _userClipPlanes[0]->SetNormal(1, 0, 0);
4517                }
4518                _userClipPlanes[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
4519            } else {
4520                _userClipPlanes[0] = NULL;
4521            }
4522        } else {
[2512]4523            if (ratio < 1.0) {
[2317]4524                if (_userClipPlanes[1] == NULL) {
4525                    _userClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
4526                    _userClipPlanes[1]->SetNormal(-1, 0, 0);
4527                }
4528                _userClipPlanes[1]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
4529            } else {
4530                _userClipPlanes[1] = NULL;
4531            }
4532        }
4533        break;
4534    case Y_AXIS:
4535        if (direction > 0) {
4536            if (ratio > 0.0) {
4537                if (_userClipPlanes[2] == NULL) {
4538                    _userClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
4539                    _userClipPlanes[2]->SetNormal(0, 1, 0);
4540                }
4541                _userClipPlanes[2]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
4542            } else {
4543                _userClipPlanes[2] = NULL;
4544            }
4545        } else {
[2512]4546            if (ratio < 1.0) {
[2317]4547                if (_userClipPlanes[3] == NULL) {
4548                    _userClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
4549                    _userClipPlanes[3]->SetNormal(0, -1, 0);
4550                }
4551                _userClipPlanes[3]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
4552            } else {
4553                _userClipPlanes[3] = NULL;
4554            }
4555        }
4556        break;
4557    case Z_AXIS:
4558        if (direction > 0) {
4559            if (ratio > 0.0) {
4560                if (_userClipPlanes[4] == NULL) {
4561                    _userClipPlanes[4] = vtkSmartPointer<vtkPlane>::New();
4562                    _userClipPlanes[4]->SetNormal(0, 0, 1);
4563                }
4564                _userClipPlanes[4]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
4565            } else {
4566                _userClipPlanes[4] = NULL;
4567            }
4568        } else {
[2512]4569            if (ratio < 1.0) {
[2317]4570                if (_userClipPlanes[5] == NULL) {
4571                    _userClipPlanes[5] = vtkSmartPointer<vtkPlane>::New();
4572                    _userClipPlanes[5]->SetNormal(0, 0, -1);
4573                }
4574                _userClipPlanes[5]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
4575            } else {
4576                _userClipPlanes[5] = NULL;
4577            }
4578        }
4579        break;
4580    default:
4581        ;
4582    }
4583
4584    _needsRedraw = true;
4585}
4586
4587/**
[2270]4588 * \brief Set up clipping planes for image camera mode if needed
4589 */
4590void Renderer::setCameraClippingPlanes()
4591{
4592    /* XXX: Note that there appears to be a bug with setting the
4593     * clipping plane collection to NULL in the VTK Mappers --
4594     * the old clip planes are still applied.  The workaround here
4595     * is to keep the PlaneCollection and add/remove the planes
4596     * to/from the PlaneCollection as needed.
4597     */
4598    if (_cameraMode == IMAGE) {
4599        if (_activeClipPlanes->GetNumberOfItems() == 0) {
4600            for (int i = 0; i < 4; i++)
[2317]4601                _activeClipPlanes->AddItem(_cameraClipPlanes[i]);
[2270]4602        }
4603    } else {
4604        if (_activeClipPlanes->GetNumberOfItems() > 0)
4605            _activeClipPlanes->RemoveAllItems();
[2317]4606        for (int i = 0; i < 6; i++) {
4607            if (_userClipPlanes[i] != NULL) {
4608                _activeClipPlanes->AddItem(_userClipPlanes[i]);
4609            }
4610        }
[2270]4611    }
4612
4613    /* Ensure all Mappers are using the PlaneCollection
4614     * This will not change the state or timestamp of
4615     * Mappers already using the PlaneCollection
4616     */
[3164]4617    setGraphicsObjectClippingPlanes<Arc>(_activeClipPlanes);
4618    setGraphicsObjectClippingPlanes<Arrow>(_activeClipPlanes);
4619    setGraphicsObjectClippingPlanes<Box>(_activeClipPlanes);
4620    setGraphicsObjectClippingPlanes<Cone>(_activeClipPlanes);
4621    setGraphicsObjectClippingPlanes<Contour2D>(_activeClipPlanes);
4622    setGraphicsObjectClippingPlanes<Contour3D>(_activeClipPlanes);
4623    setGraphicsObjectClippingPlanes<Cutplane>(_activeClipPlanes);
4624    setGraphicsObjectClippingPlanes<Cylinder>(_activeClipPlanes);
4625    setGraphicsObjectClippingPlanes<Disk>(_activeClipPlanes);
4626    setGraphicsObjectClippingPlanes<Glyphs>(_activeClipPlanes);
4627    setGraphicsObjectClippingPlanes<Group>(_activeClipPlanes);
4628    setGraphicsObjectClippingPlanes<HeightMap>(_activeClipPlanes);
[3818]4629    setGraphicsObjectClippingPlanes<Image>(_activeClipPlanes);
[4082]4630    setGraphicsObjectClippingPlanes<ImageCutplane>(_activeClipPlanes);
[3164]4631    setGraphicsObjectClippingPlanes<LIC>(_activeClipPlanes);
4632    setGraphicsObjectClippingPlanes<Line>(_activeClipPlanes);
4633    setGraphicsObjectClippingPlanes<Molecule>(_activeClipPlanes);
[3535]4634    setGraphicsObjectClippingPlanes<Outline>(_activeClipPlanes);
[3861]4635    setGraphicsObjectClippingPlanes<Parallelepiped>(_activeClipPlanes);
[3164]4636    setGraphicsObjectClippingPlanes<PolyData>(_activeClipPlanes);
4637    setGraphicsObjectClippingPlanes<Polygon>(_activeClipPlanes);
4638    setGraphicsObjectClippingPlanes<PseudoColor>(_activeClipPlanes);
4639    setGraphicsObjectClippingPlanes<Sphere>(_activeClipPlanes);
4640    setGraphicsObjectClippingPlanes<Streamlines>(_activeClipPlanes);
[3818]4641    setGraphicsObjectClippingPlanes<Text3D>(_activeClipPlanes);
[3164]4642    setGraphicsObjectClippingPlanes<Volume>(_activeClipPlanes);
4643    setGraphicsObjectClippingPlanes<Warp>(_activeClipPlanes);
[2270]4644}
4645
4646/**
[2332]4647 * \brief Control the use of two sided lighting
4648 */
4649void Renderer::setUseTwoSidedLighting(bool state)
4650{
4651    _renderer->SetTwoSidedLighting(state ? 1 : 0);
4652    _needsRedraw = true;
4653}
4654
4655/**
[3993]4656 * \brief Control parameters of depth peeling algorithm
4657 *
4658 * \param occlusionRatio define the threshold under which the algorithm
4659 * stops to iterate over peel layers. This is the ratio of the number of
4660 * pixels that have been touched by the last layer over the total number
4661 * of pixels of the viewport area. Initial value is 0.0, meaning rendering
4662 * have to be exact. Greater values may speed-up the rendering with small
4663 * impact on the quality.
4664 * \param maxPeels define the maximum number of peeling layers. Initial
4665 * value is 100. A special value of 0 means no maximum limit. It has to be
4666 * a positive value.
4667 */
4668void Renderer::setDepthPeelingParams(double occlusionRatio, int maxPeels)
4669{
4670    _renderer->SetOcclusionRatio(occlusionRatio);
4671    _renderer->SetMaximumNumberOfPeels(maxPeels);
4672    _needsRedraw = true;
4673}
4674
4675/**
[2290]4676 * \brief Control the use of the depth peeling algorithm for transparency
4677 */
4678void Renderer::setUseDepthPeeling(bool state)
4679{
4680    _renderer->SetUseDepthPeeling(state ? 1 : 0);
4681    _needsRedraw = true;
4682}
4683
4684/**
[2328]4685 * \brief Sets flag to trigger rendering next time render() is called
4686 */
4687void Renderer::eventuallyRender()
4688{
4689    _needsRedraw = true;
4690}
4691
4692/**
[2100]4693 * \brief Cause the rendering to render a new image if needed
4694 *
4695 * The _needsRedraw flag indicates if a state change has occured since
4696 * the last rendered frame
4697 */
4698bool Renderer::render()
4699{
[3615]4700    TRACE("Enter: redraw: %d axesReset: %d cameraReset: %d clippingRangeReset: %d",
4701          _needsRedraw ? 1 : 0, _needsAxesReset ? 1 : 0, _needsCameraReset ? 1 : 0, _needsCameraClippingRangeReset ? 1 : 0);
[2100]4702    if (_needsRedraw) {
[3330]4703         if (_needsAxesReset) {
4704            resetAxes();
4705            _needsAxesReset = false;
4706        }
4707        if (_needsCameraReset) {
4708            initCamera();
4709            _needsCameraReset = false;
4710            _needsCameraClippingRangeReset = false;
4711        } else if (_needsCameraClippingRangeReset && _cameraMode != IMAGE) {
4712            resetCameraClippingRange();
4713            _needsCameraClippingRangeReset = false;
4714        }
[2270]4715        setCameraClippingPlanes();
[2100]4716        _renderWindow->Render();
[3828]4717        int *sz = _renderWindow->GetSize();
4718        if (sz[0] != _windowWidth || sz[1] != _windowHeight) {
4719            ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], _windowWidth, _windowHeight);
4720        }
[2100]4721        _needsRedraw = false;
4722        return true;
4723    } else
4724        return false;
4725}
4726
4727/// Get the pixel width of the render window/image
4728int Renderer::getWindowWidth() const
4729{
4730    return _windowWidth;
4731}
4732
4733/// Get the pixel height of the render window/image
4734int Renderer::getWindowHeight() const
4735{
4736    return _windowHeight;
4737}
4738
4739/**
4740 * \brief Read back the rendered framebuffer image
4741 */
4742void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
4743{
[2260]4744#ifdef RENDER_TARGA
4745    _renderWindow->MakeCurrent();
4746    // Must clear previous errors first.
4747    while (glGetError() != GL_NO_ERROR){
4748        ;
4749    }
4750    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
4751    int size = bytesPerPixel * _windowWidth * _windowHeight;
4752
4753    if (imgData->GetMaxId() + 1 != size)
4754    {
4755        imgData->SetNumberOfComponents(bytesPerPixel);
4756        imgData->SetNumberOfValues(size);
4757    }
4758    glDisable(GL_TEXTURE_2D);
4759    if (_renderWindow->GetDoubleBuffer()) {
4760        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetBackLeftBuffer()));
4761    } else {
4762        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetFrontLeftBuffer()));
4763    }
4764    glPixelStorei(GL_PACK_ALIGNMENT, 1);
4765#ifdef WANT_TRACE
4766    struct timeval t1, t2;
4767    glFinish();
4768    gettimeofday(&t1, 0);
4769#endif
4770    if (bytesPerPixel == 4) {
4771        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGRA,
4772                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
4773    } else {
4774        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGR,
4775                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
4776    }
4777#ifdef WANT_TRACE
4778    gettimeofday(&t2, 0);
4779    static unsigned int numFrames = 0;
4780    static double accum = 0;
4781    numFrames++;
[3330]4782    accum += MSECS_ELAPSED(t1, t2);
[2260]4783#endif
[3330]4784    TRACE("Readback time: %g ms", MSECS_ELAPSED(t1, t2));
[2260]4785    TRACE("Readback avg: %g ms", accum/numFrames);
4786    if (glGetError() != GL_NO_ERROR) {
4787        ERROR("glReadPixels");
4788    }
4789#else
[2254]4790    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1,
4791                                !_renderWindow->GetDoubleBuffer(), imgData);
[2260]4792#endif
[2100]4793    TRACE("Image data size: %d", imgData->GetSize());
4794}
4795
4796/**
4797 * \brief Get nearest data value given display coordinates x,y
4798 *
[3451]4799 * FIXME: This doesn't work when actors are scaled
4800 *
[2100]4801 * Note: no interpolation is performed on data
4802 */
[2423]4803bool Renderer::getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value)
[2100]4804{
4805    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
4806    coord->SetCoordinateSystemToDisplay();
[2423]4807    coord->SetValue(x, _windowHeight - y, 0);
[2100]4808    double *worldCoords = coord->GetComputedWorldValue(_renderer);
4809
[2423]4810    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
[2100]4811          worldCoords[0],
4812          worldCoords[1],
4813          worldCoords[2]);
4814
[2423]4815    return getScalarValue(id, worldCoords[0], worldCoords[1], worldCoords[2], value);
[2100]4816}
4817
4818/**
4819 * \brief Get nearest data value given world coordinates x,y,z
4820 *
4821 * Note: no interpolation is performed on data
4822 */
[2423]4823bool Renderer::getScalarValue(const DataSetId& id, double x, double y, double z, double *value)
[2100]4824{
4825    DataSet *ds = getDataSet(id);
4826    if (ds == NULL)
[2423]4827        return false;
4828
4829    return ds->getScalarValue(x, y, z, value);
[2100]4830}
[2423]4831
4832/**
4833 * \brief Get nearest data value given display coordinates x,y
4834 *
4835 * Note: no interpolation is performed on data
4836 */
4837bool Renderer::getVectorValueAtPixel(const DataSetId& id, int x, int y, double vector[3])
4838{
4839    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
4840    coord->SetCoordinateSystemToDisplay();
4841    coord->SetValue(x, _windowHeight - y, 0);
4842    double *worldCoords = coord->GetComputedWorldValue(_renderer);
4843
4844    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
4845          worldCoords[0],
4846          worldCoords[1],
4847          worldCoords[2]);
4848
4849    return getVectorValue(id, worldCoords[0], worldCoords[1], worldCoords[2], vector);
4850}
4851
4852/**
4853 * \brief Get nearest data value given world coordinates x,y,z
4854 *
4855 * Note: no interpolation is performed on data
4856 */
4857bool Renderer::getVectorValue(const DataSetId& id, double x, double y, double z, double vector[3])
4858{
4859    DataSet *ds = getDataSet(id);
4860    if (ds == NULL)
4861        return false;
4862
4863    return ds->getVectorValue(x, y, z, vector);
4864}
Note: See TracBrowser for help on using the repository browser.