Private variable not updating

morning,

I have a working program that displays a map on an LCD screen at a zoom level set in a globalconfig.h file, ie (#define DEFAULT_MAP_ZOOM 2.0) . The WorkingWP.ino file, has a toggle button loop, to cycle between two values 0.5 and 1.0. This passes the value to a setter function in a class to change the private variable _zoomScale, which should be used in a render function to change the zoomlevel on an LCD when I press the button.

So far the button code works ok, toggling between the two values which are passed to the setter function which performs some maths and produces a new _zoomScale.

I can serial print out the new _zoomScale values from within the setter function so it appears a private variable named _zoomScale is changed by this function. However the actual function in the class that render's the map and uses the _zoomScale, always defaults to the original value defined (DEFAULT_MAP_ZOOM 2.0) in the initialisation function, and not changed. so I am questioning whether the private variable _zoomScale is changed outside of the setter function.
Whatever I have done the render function does not see any change and uses the default _zoomScale set in the initialisation function. After many hours I am not getting any closer to solving it.

any suggestiions would be appreciated
regards

TileBlockRenderer.h

class TileBlockRenderer {

private:

    bool _hasPositionProvider, _hasHeader, _hasTrackIn;
    
    int _heading;
    float* _rotMtxBuf;
    uint64_t* _renderTileIds;
    int16_t* _renderTileData;  // changed from int16_t
    uint64_t* _renderTileSizes;
    uint64_t _perTileBufferSize;    
    uint64_t _centerTileId, _prevCenterTileId;
    long long _prevCenterChangeTime, _prevTileUpdateTime;
    int* _offsetDirectionMap;
    float _zoomLevel, _zoomScale;


    SimpleTile::Header* _header;
    SharedSPISDCard* _sd;
    SharedSPIDisplay* _display;
    GPXTrack* _track;
    GeoPositionProvider* _positionProvider;
    
      bool isOnDisplay(int disp_LL_x, int disp_LL_y, int disp_UR_x, int disp_UR_y, int16_t x0, int16_t y0);
    void updateTileBuffer(LocalGeoPosition& center);
    void render(LocalGeoPosition& center);
    void renderGPX(LocalGeoPosition& center);
public:
     TileBlockRenderer(); // constructor
    void initialize(SimpleTile::Header* mapHeader, SharedSPISDCard* sd, SharedSPIDisplay* display);
    void setPositionProvider(GeoPositionProvider* newPositionProvider);
    void setGPXTrackIn(GPXTrack* track);
    void setZoom(float newZoomLevel);
    float getZoom();
    bool step(bool holdOn=false);

};

TileBlockRenderer.cpp

tyTileBlockRenderer::TileBlockRenderer()
  : _hasPositionProvider(false), _hasHeader(false), _hasTrackIn(false) {
   _renderTileIds = new uint64_t[N_RENDER_TILES]{ 0 };   // Store tile IDs currently in view
  _renderTileSizes = new uint64_t[N_RENDER_TILES]{ 0 };  // Array to store tile size for each tile
   _prevCenterTileId = 0;                                // Initialize previous center tile ID
   _prevTileUpdateTime = -TILE_UPDATE_DEBOUNCE_MS;       // Initialize previous tile update time.
  _heading = 0;                                          // Initialise heading to zero
  _rotMtxBuf = new float[4];                             // map rotation matrix
  _zoomLevel = DETAULT_ZOOM_LEVEL ;                     // set defaul _zoomLevel from (globalconfig.h) 
 }

void TileBlockRenderer::initialize(SimpleTile::Header* mapHeader, SharedSPISDCard* sd, SharedSPIDisplay* display) {
  _header = mapHeader;
  _sd = sd;
  _display = display;
  _zoomScale = ((float)_zoomLevel) * ((float)120 / (float)(_header->tile_size));
  _perTileBufferSize = _header->max_nodes * 2;
  _renderTileData = new int16_t[_perTileBufferSize * N_RENDER_TILES]{ 0 };  //changed int32_t
  _hasHeader = true;
}


//  THIS (setZoom) WORKS,,, pressing the button toggles between two set values 0.5 and 1.0 from main loop()
//  HOWEVER the retun value  
void TileBlockRenderer::setZoom(float newZoomLevel){                // Setter function to get value from togglebutton code in loop()
 _zoomScale = ((float)newZoomLevel) * ((float)120 / (float)(10000));// create new zoomScale from newzoomLevel passed to this function/method from main loop()
 Serial.print("newzoomlevel ");                                     // confirm the toggle button code in loop() works... IT DOES          
 Serial.println(newZoomLevel,2);                                    // confirm  pressing button toggles zoomLevels ....IT WORKS 
 Serial.print("changed_zoomScale ");                                // confirm the code changes the _zoomScale ....IT DOES         
 Serial.println(_zoomScale, 3);                                     // prints out the variable _zoomScale...IT WORKS and DOES
 }

 float TileBlockRenderer::getZoom(){                                // Getter function to return value -zoomScale from Setter funtion above
  return _zoomScale;                                               // returns the new Scale into private variable _zoomScale
 }


void TileBlockRenderer::render(LocalGeoPosition& center) {
  // Now we have the tile data in the buffer and the current position
 //    Serial.print("renderZoom ");
 //   Serial.println(_zoomScale, 3);
  int x0, y0, x1, y1;
  int curr_tile_offset_x, curr_tile_offset_y;
  int64_t curr_tile_LL_x, curr_tile_LL_y;
  int disp_LL_x, disp_LL_y, disp_UR_x, disp_UR_y;

  for (int tidx = 0; tidx < N_RENDER_TILES; tidx++) {

    // Get lower left corner of tile in global (x, y) coordinates
    LocalGeoPosition::getTileLL(_renderTileIds[tidx], _header, &curr_tile_LL_x, &curr_tile_LL_y);

    // Current position relative to current tile.
    curr_tile_offset_x = center.x() - curr_tile_LL_x;
    curr_tile_offset_y = center.y() - curr_tile_LL_y;

    // Get lower left and upper right corner of display relative to tile origin.
    // TODO: make this a bit nicer
    if (std::abs(_heading % 180) > 15) {

      disp_LL_x = curr_tile_offset_x - DISPLAY_MAX_DIM / (_zoomScale);     // DISPLAY_MAX_DIM sqrt(DISPLAY_WIDTH*DISPLAY_WIDTH/2)
      disp_UR_x = curr_tile_offset_x + DISPLAY_MAX_DIM / (_zoomScale);
      disp_LL_y = curr_tile_offset_y - DISPLAY_MAX_DIM / (_zoomScale);
      disp_UR_y = curr_tile_offset_y + DISPLAY_MAX_DIM / (_zoomScale);
    } else {
      disp_LL_x = curr_tile_offset_x - DISPLAY_WIDTH_HALF / (_zoomScale);  // DISPLAY_WIDTH_HALF/(**_zoomScale**)
      disp_UR_x = curr_tile_offset_x + DISPLAY_WIDTH_HALF / (_zoomScale);
      disp_LL_y = curr_tile_offset_y - DISPLAY_WIDTH_HALF / (_zoomScale);
      disp_UR_y = curr_tile_offset_y + DISPLAY_WIDTH_HALF / (_zoomScale);
    }


Suggestion - Post the main .ino file that uses this class and displays the objectionable behavior.

Suggestion - Don't try to make text bold inside of Code Tags. It doesn't work and makes the code hard to read because it looks like pointer-to-pointer.

In your main code (that we cannot see)... do you call the constructor each loop? In other words do you have something like

TileBlockRenderer myRenderer;

in loop?
If so: take that outside loop...

Thankyou,
The main code calls a UIREDERER class from a loop.

void loop() {
   button.loop();                           // MUST call the loop() function first
    if(button.isPressed()) {
    Serial.println("The button is pressed");

if(zoomvalue == 1.0){
   zoomvalue = 0.5;
   togglezoom.setZoom(0.5);                // changes zoom number ,, but does not work changing zoom on screen  
}
else{
  zoomvalue = 1.0;
  togglezoom.setZoom(1.0); 
}

}
    
UIRENDERER.step();

}

uirenderer.cpp

#include "uirenderer.h"
#include "serialutils.h"
#include "globalconfig.h"
#include <Arduino.h>

UIRenderer::UIRenderer()
  : _currentScreen(&BOOTSCREEN), _hasGNSS(false), _hasHeader(false), _hasPositionProvider(false) {
  _textBuffer = new char[(N_CHAR_PER_STAT + 1) * 2];
  _tLastRender = millis();
}

bool UIRenderer::initializeMap(SharedSPISDCard* sd) {
  if (_hasHeader) {
  //  _mapRenderer.getZoom();
    _mapRenderer.initialize(_header, sd, _disp);
    
    if (_hasPositionProvider) {
      _mapRenderer.setPositionProvider(_posProvider);

    }
    return true;
  } else {
    return false;
  }
};

void UIRenderer::setDisplay(SharedSPIDisplay* display) {
  _disp = display;
  _hasDisplay = true;
}

void UIRenderer::setGNSS(GNSSModule* gnss) {
  _gnss = gnss;
  _hasGNSS = true;
}

void UIRenderer::setPositionProvider(GeoPositionProvider* positionProvider) {
  _posProvider = positionProvider;
  _hasPositionProvider = true;
  _mapRenderer.setPositionProvider(_posProvider);
}

void UIRenderer::setHeader(SimpleTile::Header* header) {
  _header = header;
  _hasHeader = true;
}

void UIRenderer::setGPXTrackIn(GPXTrack* track) {
  _track = track;
  _hasTrackIn = true;
  _mapRenderer.setGPXTrackIn(_track);
};

void UIRenderer::setScreen(Screen* newScreen) {
  _currentScreen = newScreen;
}

// Note: This function always returns true such that it can be used to keep a while loop alive.
bool UIRenderer::step() {

  if (!_hasDisplay) return true;
  _disp->clearDisplayBuffer();
  _currentScreen->render(_disp);
  if (_hasGNSS) {
    _gnss->step();
  }

  if (_currentScreen->hasMap && _hasPositionProvider) {
    _posProvider->step();

    // Try to render map. If it is not ready, render the waiting screen instead
    if (!_mapRenderer.step(1)) {
      FIXWAITINGSCREEN.setCenterText(_textBuffer);
      FIXWAITINGSCREEN.render(_disp);
    }
  }

  // Note: Statusbar must be rendered after screen!
  if (_currentScreen->hasStatusBar) {
    renderStatusBar();
  }
  _disp->refresh();  // -> It is used to access the public members of a class, structure.

  // Check if we need to wait before next render loop to achieve desired FPS.
  _tLoopRender = millis() - _tLastRender;
  if (_tLoopRender < TARGET_FRAME_TIME_MS) {
    delayMicroseconds((TARGET_FRAME_TIME_MS - _tLoopRender) * 1000);
  }
  _tLastRender = millis();

  return true;
}

void UIRenderer::renderStatusBar() {
  renderStat(_textBuffer);
}

void UIRenderer::renderStat( char* textBuff) {
  _disp->drawDataWindow();          // added this to show text in the status bar
  snprintf(textBuff, N_CHAR_PER_STAT, "Ft:%.0f", _gnss->getAltitude());
  _disp->drawGPSdata(textBuff, 260, 20);  // added this to show text in the status bar
  snprintf(textBuff, N_CHAR_PER_STAT, "HE%03i", _posProvider->getHeading());
  _disp->drawGPSdata(textBuff, 260, 70);  // added this to show text
  snprintf(textBuff, N_CHAR_PER_STAT, "%s", _gnss->getDirection());
   _disp->drawGPSdata(textBuff, 360, 70);  // added this to show text
  snprintf(textBuff, N_CHAR_PER_STAT, "Km:%.0f", _gnss->getSpeed());
  _disp->drawGPSdata(textBuff, 260, 120);  // added this to show text
  //snprintf(textBuff, N_CHAR_PER_STAT, "%2.2f", _posProvider->getLatitude());
  // _disp->drawGPSdata(textBuff,256,170);    // added this to show text
  //snprintf(textBuff, N_CHAR_PER_STAT, "%2.2f", _posProvider->getLongitude());
  // _disp->drawGPSdata(textBuff,300,170);    // added this to show text
  snprintf(textBuff, N_CHAR_PER_STAT, "%02i:%02i", _gnss->getHour(), _gnss->getMinute());
  _disp->drawGPSdata(textBuff, 260, 215);  // added this to show text

  _disp->drawWindIndicator();
}


// Perform a delay/sleep operation while keeping UI alive.
void UIRenderer::delay(uint64_t milliseconds) {
  long t_start = millis();
  while ((millis() - t_start) < milliseconds) {
    step();
  }
}

UIRenderer UIRENDERER;

I have just written the below test program to better understand things ,and it works, ie it changes the private variable called " _zoomScale" inside the ZoomClass which is then accessable in the render function in the ZoomClass. This is done so from a button press in the myzoomtest.ino loop() . I am clearly missing a lot of knowledge here as to why when I apply this logic to the map program I can not change the _zoomScale variable in the render function.

myzoomtest.ino
type o#include <Arduino.h>
#include <ezButton.h>
#include "ZoomClass.h"

const int BUTTON_PIN = 2;  // Connect the Button to pin 2 for zoom change on the fly
int lastButtonState;       // the previous state of button
int currentButtonState;    // the current state of button
float zoomvalue = 1.0;     // default zoom level
ezButton button(BUTTON_PIN);  // create ezButton object that attach to pin 2;
ZoomClass test;               // create an object of ZoomClass called test

void setup() {
  Serial.begin(9600);          // set Serial baurd rate to
  pinMode(BUTTON_PIN, INPUT);  // set arduino pin 2 to input mode
  button.setDebounceTime(50);  // set debounce time to 50 milliseconds
}

// loop toggles between two set zoomlevels 0.5 and 1.0
void loop() {
  button.loop();                              // MUST call the loop() function first
  if (button.isPressed()) {                   // if the button is pressed on pin 2
    Serial.println("The button is pressed");  // confirm button has been pressed
    if (zoomvalue == 1.0) {               // if zoomvalue is equal to 1.0
      zoomvalue = 0.5;                    // then change zoomvalue to 0.5
      test.setValue(0.5);                 // call setValue in ZoomClass.cpp which preforms a calc to gives a zoomScale in this case 0.006
    } else {  // do it all again ie toggles between zoomvalue 0.5 and 1.0
      zoomvalue = 1.0;
      test.setValue(1.0);
    }
  }
  test.render();
}r paste code here

globalconfig.h

#ifndef GLOBAL_CONFIG_h
#define GLOBAL_CONFIG_h

#define ZOOM_LEVEL 2.0

#endif

ZoomClass.h

#ifndef ZoomClass_h
#define ZoomClass_h

#include <Arduino.h>

//using namespace std;

class ZoomClass {

private:

  float _value;
  
public:

  ZoomClass();  //constructor decleration
  float getValue();
  void setValue(float _value);
  void render();
};

#endif

ZoomClass.cpp

#include <Arduino.h>
#include "ZoomClass.h"
#include "globalconfig.h"

ZoomClass::ZoomClass() {
  _value = ZOOM_LEVEL;  // set the default zoom level to the value (in globalconfig.h) #define ZOOM_LEVEL 2.0
}

//the getter function
float ZoomClass::getValue() {
  return this->_value;
}
//the setter function
void ZoomClass::setValue(float newvalue) {
  this->_value = ((float)newvalue) * ((float)120 / (float)(10000));
}

void ZoomClass::render() {
  float newzoom;
  Serial.print("Render function Scalevalue is: ");
  Serial.println(this->_value, 3);
  newzoom = _value * 2;
  Serial.print("Render function newzoom is: ");
  Serial.println(newzoom, 3);
}

Do not compare a float with ==.
There is a fair chance that a rounding error will cause it to be never true
Like 0.33333333333333333333333 is not equal to 1/3...

1 Like

If you want newzoom to be avvailable outside this function, you need to remove float.

1 Like

ok thanks
I just need to find out why i cannot change private variable _zoomScale in the TileBlockRenderer class ? the first post when I can in my test program.

Why do you use this->.
That should not be needed...

It was part of an example i came across to access a private variable in a class ( setter and getter functions) . I thought that must be how you get the address of a private variable so you can change it. Obviously wrong as it does not work changing the variable in TileBlockRenderer

Start by removing this-> then...
I had a quick look at the use of this->. It should work as you have it, but it is not needed inbyour case...

Also:

Can be written as:

newvalue * 120.0 / 10000.0;

A cast of newvalue to float does nothing as newvalue already is a float..

To help debugging, you can show which object instance is being modified by printing its address

struct Zee {
  float f = 12 / 10.0;
  void inc() {
    ++f;
    Serial.print(reinterpret_cast<uintptr_t>(this), HEX);
    Serial.print(": ");
    Serial.println(f, 15);
  }
};

void IncZCopy(Zee z) {
  z.inc();
}

void IncZRef(Zee &z) {
  z.inc();
}

void setup() {
  Serial.begin(115200);
  Zee z1, z2;
  z1.inc();
  z1.inc();
  z2.inc();
  IncZCopy(z2);
  IncZCopy(z2);
  z2.inc();
  IncZRef(z2);
  IncZRef(z2);
  z2.inc();
}

void loop() {
}

thankyou
very helpful I will use that code to see if I have managed to change _zoomScale
regards

thanks build_1971

Solved,
Here's how I did it,
After lots of reading . I created a zoomclass.h and .cpp, and within the class, a static float variable named NewZoom. I could then access this throughtout my program classes. I then added _zoomLevel = ZoomClass::NewZoom; to the function that sets the map zoom level and ultimately renders the map ie, void TileBlockRenderer::render(LocalGeoPosition& center) and used a loop in the main sketch to change the static variable in a toggle routine ie ZoomClass::NewZoom= 0.5; and ZoomClass::NewZoom= 1.0; respectively. and bingo! I can now toggle between two zoomscales on the LCD .

zoomclass.h

#ifndef _ZOOMCLASS_H
#define _ZOOMCLASS_H
#include <Arduino.h>

class ZoomClass {
  public:
ZoomClass();
static float NewZoom;
  private:
};

#endif

zoomclass.cpp

#include "zoomclass.h"
#include "Arduino.h"


ZoomClass::ZoomClass(){
}

float ZoomClass::NewZoom = 0.5;

Sketch.ino

void loop() {

  button.loop();  // MUST call the loop() function first
  if (button.isPressed()) {
    Serial.println("The button is pressed");
    if (zoomvalue == 1.0) {
      zoomvalue = 0.5;
      ZoomClass::NewZoom= 0.5;
    } else {
      zoomvalue = 1.0;
      ZoomClass::NewZoom = 1.0;
    }
  }
    UIRENDERER.step();
  
  }

TileBlockRederer.cpp

void TileBlockRenderer::render(LocalGeoPosition& center) {

_zoomLevel = ZoomClass::NewZoom;
_zoomScale = ((float)_zoomLevel) * ((float)120 / (float)(_header->tile_size));
// Serial.println(_zoomLevel,3);  //this prints the static var from zoomclass

  int x0, y0, x1, y1;
  int curr_tile_offset_x, curr_tile_offset_y;
  int64_t curr_tile_LL_x, curr_tile_LL_y;
  int disp_LL_x, disp_LL_y, disp_UR_x, disp_UR_y;

  for (int tidx = 0; tidx < N_RENDER_TILES; tidx++) {

    // Get lower left corner of tile in global (x, y) coordinates
    LocalGeoPosition::getTileLL(_renderTileIds[tidx], _header, &curr_tile_LL_x, &curr_tile_LL_y);

    // Current position relative to current tile.
    curr_tile_offset_x = center.x() - curr_tile_LL_x;
    curr_tile_offset_y = center.y() - curr_tile_LL_y;

    // Get lower left and upper right corner of display relative to tile origin.
    // TODO: make this a bit nicer
    if (std::abs(_heading % 180) > 15) {

      disp_LL_x = curr_tile_offset_x - DISPLAY_MAX_DIM / (_zoomScale);  // DISPLAY_MAX_DIM sqrt(DISPLAY_WIDTH*DISPLAY_WIDTH/2)
      disp_UR_x = curr_tile_offset_x + DISPLAY_MAX_DIM / (_zoomScale);
      disp_LL_y = curr_tile_offset_y - DISPLAY_MAX_DIM / (_zoomScale);
      disp_UR_y = curr_tile_offset_y + DISPLAY_MAX_DIM / (_zoomScale);
    } else {
      disp_LL_x = curr_tile_offset_x - DISPLAY_WIDTH_HALF / (_zoomScale);  // DISPLAY_WIDTH_HALF/(_zoomScale)
      disp_UR_x = curr_tile_offset_x + DISPLAY_WIDTH_HALF / (_zoomScale);
      disp_LL_y = curr_tile_offset_y - DISPLAY_WIDTH_HALF / (_zoomScale);
      disp_UR_y = curr_tile_offset_y + DISPLAY_WIDTH_HALF / (_zoomScale);
    }

    uint64_t p = _perTileBufferSize * tidx;
    uint64_t pEnd = p + _renderTileSizes[tidx];

    while (p < pEnd) {

      // Check if current or next coordinate is a separator, skip otherwise.
      if ((!_renderTileData[p] && !_renderTileData[p + 1]) || (!_renderTileData[p + 2] && !_renderTileData[p + 3])) {
        p += 2;
        continue;
      }

      // Check if current or next coordinate is on display, skip otherwise.
      if (!isOnDisplay(disp_LL_x, disp_LL_y, disp_UR_x, disp_UR_y, _renderTileData[p], _renderTileData[p + 1])
          && !isOnDisplay(disp_LL_x, disp_LL_y, disp_UR_x, disp_UR_y, _renderTileData[p + 2], _renderTileData[p + 3])) {
        p += 2;
        continue;
      }

      // Calculate non-rotated position on screen.
      // I will use the screen in portrait mode so I see more km in front of me. I need the centre of the screen x=120 y= 240 !
      // if i change screen size in the globalconfig.h it does not work but does typing in manually, but rotation need doing (maths.h)

      x0 = DISPLAY_WIDTH_HALF + (_renderTileData[p] - curr_tile_offset_x) * _zoomScale;
      y0 = DISPLAY_WIDTH_HALF - (_renderTileData[p + 1] - curr_tile_offset_y) * _zoomScale;
      x1 = DISPLAY_WIDTH_HALF + (_renderTileData[p + 2] - curr_tile_offset_x) * _zoomScale;
      y1 = DISPLAY_WIDTH_HALF - (_renderTileData[p + 3] - curr_tile_offset_y) * _zoomScale;

      // Calculate rotated position on screen.
      if (_heading != 0) {
        rotatePointInplaceAroundScreenCenter(x0, y0, _rotMtxBuf);
        rotatePointInplaceAroundScreenCenter(x1, y1, _rotMtxBuf);
      }

      _display->draw_line(
        x0,
        y0,
        x1,
        y1,
        2, BLACK);

      p += 2;
    }
  }
}

That basically means that you have two separate instantiations of your zoomclass...
And that might not be what you wanted.
Through the static declaration you have made your private variable available in all instantiations...
My guess is that you need one instsntiation of your zoomclass. And no ststic declaration.
Static declarations are often used to count the number of instantiations of a class.

Glad it's working. A few general notes.

You include Arduino.h in the header zoomclass.h, and you include that header in the implementation file zoomclass.cpp. The preprocessor #include is just doing "insert content here" like copy&paste, with some rules like #ifndef. Because it's been included by the header, you don't need to then include it again in the .cpp

Better yet: you don't need to include it in either file. Doing so is "safe", but makes the compiles slower and is clutter. If for example, you were using String, then you'd need it. If it appears in the class declaration, in the header (e.g. in a function parameter or return type), you would include it there, because other files that #include "zoomclass.h" would need to compile that code. Another possibility is that only the implementation code uses String, so you'd include it in the .cpp, but not the header.

But all you have in either file are C++ keywords like class and float, and the names ZoomClass and NewZoom.

The other thing is that the casts here

are excessive (with superfluous parentheses around the whole thing). It should be just

  _zoomScale = _zoomLevel * 120 / _header->tile_size;

After overflow, the next "unexpected" thing in C/C++ arithmetic is integer division -- the difference between these two

  Serial.println(30/12);    // 2
  Serial.println(30/12.0);  // 2.5

The result of basic arithmetic between two integer types is an integer. If the types are not the same size, e.g. byte and long, the smaller gets promoted to the larger type to perform the operation. Division is the one that you have to think twice about. Sometimes you want to truncate.

Floating point (float and double) and integer results in the floating point type. Overflow is much less likely, but can still happen. So

  • _zoomLevel, which you don't show, is already a float I'm guessing, because you assigned ZoomClass::NewZoom to it
  • you can multiply it by the integer 120. If you "just want to be sure", 120.0 is a double that exactly represents an integer, and a lot easier to read and write than a cast. (12.0f is a float that exactly represents the same integer. There can be a subtle difference.)
  • because the result of the multiplication is a float, you don't have to cast the _header->tile_size (unless it is actually a double and you don't want the operation to be performed as a double and then converted back to a float -- there may be cases where that might matter)

Earlier, you were dividing two integers for a constant

Because it's multiplication before the division, and they're equal precedence, this is done left to right, and could then be

  newvalue * 120 / 10000  // could be 12/1000, whatever has more meaning

But if it was addition, the division would be done first, and it would be integer division unless at least one of those is a floating pointer number. Because the division is the reason it matters, it makes sense to put it on the divisor.

  newvalue + 120 / 10000.0

Compare

  float f = 42;
  Serial.println(f/12);           // 3.50
  Serial.println(f * 30 / 12);    // 105.0  left-to-right
  Serial.println((f * 30) / 12);  // 105.0  same
  Serial.println(f * (30 / 12));  // 84.00  (integer) division first
  Serial.println(f + 30 / 12);    // 44.00  division higher precedence
  Serial.println(f + 30 / 12.0);  // 44.50

hi,
kenb4 Many thanks for the most excellent critique.
The original code is kindly and freely donated, I am adapting\changing it for my flight instrument project.
I am used to using a compiled basic called proton+ on microchip PIC24 (super easy string handling and 64bit maths) so OOP and Arduino/C is new to me. Steep learning curve!

build_1971 thanks ,my test program, with get and set functions worked ok on its own changing a _zoomLevel variable inside a class, until I tried to apply it to the map program. I could not for the life of me use getter and setter functions to change the _zoomLevel variable, the default _zoomLevel was always unchanged, hence going down the static variable route which worked. As I get more adept at Arduino code I may get to understand why.

kenb4 I will tidy up the code as you described , very informative about the maths .
no doublt I will struggle with other OOP proccesses as I develop this project.
many thanks

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.