New to Bangle.js and Espruino. Need help with graphics

Posted on
Page
of 2
/ 2
Next
  • Hello,
    I am new to Bangles.js and Espruino and still learning the basic know-hows of it.
    I do have a background of Javascript coding, however never worked with Graphics.

    Sorry, if my questions are very basic. However, after searching a lot over the internet (didn't find a lot of information on coding in this environment), I finally decided to post it here.

    I want to show progress of the steps walked using a progress bar or a circle.
    However, I don't understand how to implement it.
    I need to have a thick bordered circle with some color to show the progress.
    OR a rectangular bar that shows the progress.

    Right now the data is static and am really not counting the real steps.
    So the data in my circle or a bar would not be changing either.
    My question is ,
    Qn1 : How do I create a circle with a thick border?
    Qn2: how do I fill the bar with some color or the thick circle border with some color?

    Thanks a lot!

  • Ar1: Multiple ways to do that: a filled polygon or two filled circles and a filled polygon...
    Ar2: Ar1 for circle, and for rectangle: filled polygons or simply 3 filled rectangles.

    Think of composition.

    PS: You can study some code at https://www.espruino.com/ui - where mainly the composition concept is used: drawing one simple shape over another simple one renders usually faster than otherwise. This UI stuff was developed for 1 bit serial display. BangleJS has 8bit serial display, so it will render faster already from the get go.

  • Thank you very much! I shall definitely go through the link for UI code you shared.
    For my example, I decided that I will use the circle to show the progress.
    To give you an idea, posting an image of my attempt.
    I used 2 circles here, outer circle (filled with red) and inner circle(filled with black), which eventually shows me the circle with a red border.
    Now, I need to fill the red circle with green color depending on the number of steps taken. In my case, a quarter of a circle.
    Is my understanding correct - I need to set color for each pixel that comes inside the border?
    if yes, how do I do this?

    And this following qn. is out of my personal curiosity to know if I am the only one who is finding this a little difficult to code=> I have worked in Javascript and web technologies earlier and I find this coding very different from what I worked on. Is it because that's HTML and web browser that I was working on and here I need to do things differently because its a smart watch?. Even getting a simple icon on the face of the watch is so different. I tried using fonts too, however they didn't work and I didn't find any proper info on how to make them work. It kept saying that it didn't find this font, but I have no clue as to which module needs to be added. I don't have the bangle device and am using the emulator. Setting fonts otherwise in normal Javascript is so easy.

    Thanks a lot once again for answering my questions.

  • Hi,
    I used g.fillpoly() to fill color in the desired red ring and achieved what i wanted. Please see my updated image.
    However, am not sure if there was a better way to do this.
    Finding out the coordinates for each tiny area and then coloring it with green wasn't so easy.
    I am sure there must be some easy way to find the coordinates or not?
    Thanks a lot for all your help

  • Great combination... looking good!

    You can right away use fillCircle() without first using drawCircle();.

    You can simplify by:

    • fill red circle (with outer diameter)
    • draw green polygon (wedge... with outer diameter where you calculate the polygon coordinates)
    • fill black circle (with inner diameter)
    • draw the rest inside the black circle

    For filling a wedge you need only to calculate half the number of 'circle-points'.

    For calculation the points on the (outer) circle, use the trigonometric functions Math.sin() (and Math.cos()). For a smooth curve but not to over do, calculate the steps by dividing pi by the diameter of the circle in pixels. To make it fast, you can have a preset array that you calculate before hand (with some JS either in an html page in the browser or a program on your BangleJS that generates the array as literal / code upfront. And as you know, ou need to calculate actually only pi/4 and the rest you 'mirror'.

    Since there is no anti-aliasing, you can put rounded values into the array, which you may calculate a bit different: you take y and calc x (rounded) for half a quadrant and mirror the other three (3) quadrants.

    When it comes to drawing, you calculate how much of the pre-calculated array you have to use for the polygon.

    Besides @Gordon, I know that @JumJum and @MaBe have worked on circles and ellipses and stroke with.

  • Thank you so much for all the detailed information.
    I shall try doing it the way you have suggested.
    This would be the first time that I would be be applying trigonometric functions in real life application after learning them in high school :)
    Thanks a lot for directing me to that.

    Have a good Sunday evening!

  • This might do the kind of thing you're after. By drawing just the poly (rather than using circles) you can avoid some flicker on the Bangle's unbuffered screen...

    It's a good thing to try yourself though if you're interested - it's not that hard to use sin and cos and once you've got the hang of it it's really useful for a bunch of stuff

    var n=0;
    
    function drawSlice(from,to) {
      var a, res = 24;
      var x = 120, y = 120, r1 = 75, r2 = 95;
      var poly = [];
      for (var i=from*res;i<to*res;i++) {
        a = i*Math.PI*2/res;
        poly.push(x+r2*Math.sin(a), y+r2*Math.cos(a));
        poly.unshift(x+r1*Math.sin(a), y+r1*Math.cos(a));
      }
      a = to*Math.PI*2;
      poly.push(x+r2*Math.sin(a), y+r2*Math.cos(a));
      poly.unshift(x+r1*Math.sin(a), y+r1*Math.cos(a));
    
      g.fillPoly(poly);
    }
    
    function draw() {
      n += 0.01;
      if (n>=1) n=0;
      g.setColor(1,0,0);
      drawSlice(n,1);
      g.setColor(0,1,0);
      drawSlice(0,n);
    }
    
    setInterval(draw, 100);
    
  • Hi,
    Thanks a lot for encouraging me to try this out and for your suggestions.
    I shall definitely try it out.
    Thanks so much for your time to share the code example with me as well.

  • Did play around with a mixed approach: pre calc the points for a polygon composed of arrays of points for outer and inner arc. The resolution is optimized towards the radius... which then obviously breaks the .fillPoly(): limited to 64 points: see pink shape in the screen shot and WARNING in console (did not know that, but explains to me why @bppattan had to 'draw' it in multiple segments). Drawing a poly though works just fine... no limits! The golden / orange arc is small enough to stay in the 64 points limit.

    Noticed furthermore: Passing an array of odd numbers of values to .xyzPoly() locks Espruino (emulator?) up or crashes it.

    This is the code. To draw a segment for a particular value range, picks the vertices sets from the outer and inner arc and concatenates them to a single array of vertices by which the polygon is drawn (or filled). The shot shows two sets of data... The gauge is (will be) defined by starting point in degrees and arc in degrees; latter can range up to 360 degrees.

    The first gage starts at 180 degrees for 180 degrees (clockwise), with the green polygon being g.drawPoly(so.slice(l-150,l).concat(si.s­lice(0,150)),1);. the '150' - segments -is just an example number and is to be calculated proportional of the value to the maximum value relative to the maximum segments.
    The second starts at 135 degrees for 270 degrees (clockwise), with the green polygon being g.drawPoly(so.slice(l-420,l).concat(si.s­lice(0,420)),1);

    Will most likely return to overlays... (not speed optimal, but less impacted by .fillPoly()'s limitation. Segmentation of an arc is though still needed... With precalculation and limited resolution the drawing may become a bit complex... so dropping precalculation is an option (calculate on the fly is obviously not a performance issue... is it?)

    // cbc.js - circular bar dial
    // allObjects - forum.espruiono.com
    // 2020-11-22
    
    // left/top = x/y = 0/0
    
    // visual: 270 degree clock-wise gauge starting mid left bottom quadrant
    // display: ...starting mid 2nd quadrant ending mid 1st quadrant
    var maxExt   = 239
      , center   = {x:80,y:80} // center point 
      , rOuter   =  60
      , rInner   =  50
      , begDeg   = 180 // begin of gauge / dial in degrees (0: +x-axis)
      , degrees  = 180 // arc of gauge / dial in degrees clockwise
      , zeroDeg  = 135 
      , minValue = 0
      , maxValue = 1000
      , s  // segSpec
      , l  // length (of arrays of segs)
      , so // segsOuter
      , si // segsInner
      , ss // segs
      ;
    
    function radian(degree) { return 2*Math.PI * (degree % 360) / 360; }
    function getSegSpec(begDeg,degrees,maxExt,x,y,rOu­t,rIn) { // [x,y,...
      var begRad = radian(begDeg)
        , rad    = ((degrees==360)?Math.PI*2:radian(degrees­))
        , segRad = Math.PI/4/rOut
        , segs   = Math.round(rad/segRad)
        , len    = (segs+1)*2
        , sOut   = ((maxExt<=355) ? new Uint8Array(len) : new Uint16Array(len))
        , sIn    = ((rIn) ? (maxExt<=355) ? new Uint8Array(len) : new Uint16Array(len) : undefined)
        , seg    = -1
        , o      = -1
        , i      = -1
        ;
      console.log("rad:",rad);
      for (var n=0;n<9;n++) console.log();
      while(++seg<=segs) {
        rad = begRad+seg*segRad;
        sOut[++o] = Math.round(x+rOut*Math.cos(rad));
        sOut[++o] = Math.round(y+rOut*Math.sin(rad));
      }
      if (rIn) { while (--seg>=0) {
        rad = begRad+seg*segRad;
        sIn[++i] = Math.round(x+rIn*Math.cos(rad));
        sIn[++i] = Math.round(y+rIn*Math.sin(rad));
      } }
      return {segs:segs,len:len,sOut:sOut,sIn:sIn};
    }
    function t() {
      s=getSegSpec(begDeg,degrees,239,center.x­,center.y,rOuter);
      console.log(s.segs,s.sOut,s.sIn);
      s=getSegSpec(begDeg,degrees,239,center.x­,center.y,rOuter,rInner);
      ss=s.segs;l=s.len;so=s.sOut;si=s.sIn;
      console.log("segs:",ss,", len:",l,so,si);
      console.log(so.slice(0,4),"..",so.slice(­l-4,l),"||",si.slice(0,4),"..",si.slice(­l-4,l));
      d();
    }
    function d() {
      g.clear();
      g.setColor(1,0,0);
      g.drawPoly(so.slice(0,100));
      g.setColor(0,1,0);
      g.drawPoly(si.slice(l-100,l));
      g.drawPoly(si.slice(l-150,150));
      g.drawPoly(so.slice(l-150,l).concat(si.s­lice(0,150)),1);
      g.setColor(0.7,0.5,0);
      g.fillPoly(so.slice(110,140).concat(si.s­lice(l-140,l-110)));
      g.setColor(1,0.5,1);
      var fp = so.slice(160,l-190).concat(si.slice(190,­l-160));
      console.log(fp);
      g.fillPoly(fp,1);
    }
    setTimeout(t,999);
    

    1 Attachment

    • arcs.png
  • @allObjects are you aware of Graphics.quadraticBezier() implementation?

  • @MaBe, heard about... --- Had some fun as you see in the next post. Since the .fillPoly() has some its limitations, I'm thinking to expand my CGauge.js prototype/'class' with a segmented .fillPoly() for re-drawing in-place or a rough, jagged .fillPoly() with background color to clear it and then re-draw it - to avoid to have to clear and re-draw the whole display... but first, it has to useable and work decently with just drawing lines for slim gauges and polygons for 'fat' ones.

  • @bppattan,

    burnt some COVID-19 lockdown oil... and had fun. So far I played in the emulation only and have therefore no feel for performance yet.

    The CGauge() prototype/'class' is a nice thing to draw whatever circular gauges at whatever places with any starting angle, any arch angle, counter or clockwise and...

    The filling of a 'fatt circle' is still to do (and some other stuff as well, as mentioned in previous post, and the already intended 0-tick/zero-tick/-marker at .deg0 with the .clr).

    Load the code into the emulation, run it and click the buttons 1..3 or enter value changes in the console as shown in attached screenshot. The .setVal(newValue,opt) 'method' has the optional parameter opt to suppress a draw update (-1), draw update conditional only when the value changes (absent, 0) and forced (1).

    The code with usage examples is in the next post.


    1 Attachment

    • cgauge.png
  • Updated: Most recent code of 2020-11-27 in post #26. - Code version here is kept for preserving line numbers used in posts up to #26.

    CGauge code with examples:

    // cgauge.js - circular gauge
    // allObjects - forum.espruiono.com
    // 2020-11-24
    
    // left/top = x/y = 0/0
    
    // Circular Gauge cg0:
    // - visual: 270 degree clock-wise gauge starting mid left bottom quadrant
    // - graphics: ...starting mid 2nd quadrant ending mid 1st quadrant
    // - showing values from 0..300 clockwise with initial value of 100
    
    var cg0
      , cg1,cg1c,cg1f,cg1t,cg1h
      , cg2,cg2c,cg2f,cg2t,cg2h
      , cg3,cg3c,cg3f,cg3t,cg3h
      , cg4
      , b1w,b2w,b3w
      ;
    function run() {
      halt();
      cg0=new CGauge(0,0,300,[0,1,0],[1  ,0  ,0  ],135,270,null,120,140,100,96);
      cg1=new CGauge(0,0, 30,[1,1,0],[0  ,1  ,1  ],180,180,null, 80,212, 16,14);
      cg2=new CGauge(0,0,180,[1,1,1],[0.3,0.3,0.3],100­,340,null,120,180, 22, 1);
      cg3=new CGauge(0,0, 30,[1,0,1],[0  ,0  ,1  ],180,180,null,160,212, 16, 0);
      cg4=new CGauge(0,0, 40,[1,0,1],[0.3,0.3,0.3],120,-60,null,12­0,  8, 70,64);
      cg0.setVal(100,-1);
      cg1.setVal( 20,-1);
      cg2.setVal(108,-1);
      cg3.setVal( 15,-1);
      cg4.setVal( 10,-1);
      drawAll();
      cg1c=1; cg1f=function(){ if (!cg1.setVal(cg1.val+cg1c)) cg1c=-cg1c; };
      cg1t=750;
      cg2c=9; cg2f=function(){ if (!cg2.setVal(cg2.val+cg2c)) cg2c=-cg2c; };
      cg2t=1700;
      cg3c=3; cg3f=function(){ if (!cg3.setVal(cg3.val+cg3c)) cg3c=-cg3c; };
      cg3t=1000;
      if (!b1w) b1w=setWatch(cg1f,BTN1,{edge:"rising",re­peat:true});
      if (!b2w) b2w=setWatch(cg2f,BTN2,{edge:"rising",re­peat:true});
      if (!b3w) b3w=setWatch(cg3f,BTN3,{edge:"rising",re­peat:true});
      cont();
    }
    function cont() {
       cg1h=setInterval(cg1f,cg1t);
       cg2h=setInterval(cg2f,cg2t);
       cg3h=setInterval(cg3f,cg3t);
    }
    function halt() {
      if (cg1h) cg1h=clearInterval(cg1h);
      if (cg2h) cg2h=clearInterval(cg2h);
      if (cg3h) cg3h=clearInterval(cg3h);
    }
    
    function drawAll() { 
      g.clear(); setTimeout(function() {
          cg0.draw(); cg1.draw(); cg2.draw(); cg3.draw(); cg4.draw(); }
        , 1000); }
    
    var p; // temp for prototype references
    function CGauge(val,minV,maxV,color,fColor,begDeg­,degs,deg0,x,y,rOuter,rInner) {
      var _=0||this;
      _.mxXY=239;    // x, y max graph coord - defaults for BangleJS Graphics
      _.pps=2;       // 'pixel per segment'/jaggedness/graphical precision/resolution
      _.val=null;    // temporary, set at end of construction
      _.minV=minV;   // minimum value (arc all in fColor)
      _.maxV=maxV;   // maximum value (arc all in color)
      _.clr=color;   // color - as required by Graphics - for the value arc
      _.fClr=fColor; // color - as required by Graphics - for to complete the arc
      _.begD=begDeg; // 0 degrees: +x-Axis
      _.degs=degs;   // gauge full arc in degrees -/+ = counter/clockwise
      _.deg0=(deg0)?deg0:begDeg; // for 0/center value mark; falsy defaults to begDeg
      _.x=x;         // center x
      _.y=y;         // center y
      _.rOut=rOuter; // radius outer
      _.rIn=rInner;  // radius inner (optional)
      _.begR=_.rad(_.begD);                              // begin radian
      _.arcR=(_.degs==360)?Math.PI*2:_.rad(_.d­egs);      // arc radian
      _.segR=(Math.PI/(4/_.pps)/_.rOut)*((degs­>0)?1:-1); // segment radian
      _.sCnt=Math.round(Math.abs(_.arcR/_.segR­));        // segment count in arc
      _.cUp=[];                                          // clean up vertices 
      _.setVal(val,-1); // set value only
    } p=CGauge.prototype;
    p.setVal=function(v,opt) { // --- set min/max adj'd val, draw != && opt=0 || opt>0; 
      var chd = (v=(v<this.minV)?this.minV:(v>this.maxV)­?this.maxV:v)!=this.val; // ret
      if (opt<0) { this.val=v; // update value only, NO drawing
      } else if (v!=this.val||opt>0) { this.val=v; this.draw(); }
      return chd; };
    p.draw=function(_) { // --- draw circular gauge
      var s=this.sCnt, v=Math.round(s/(this.maxV-this.minV)*thi­s.val-1)+1, h=!!this.rIn;
      g.setColor(0,0,0); while (this.cUp.length) g.drawLine.apply(g,this.cUp.pop());
      v=(v<s)?v=v:s+1; // console.log(this.val,v,s);
      if (v<s) g.setColor.apply(g,this.fClr).drawPoly(t­his._pvs(v+1,s+1,0),h);
      g.setColor.apply(g,this.clr).drawPoly(th­is._pvs(0,v,1),h); };
    p._pvs=function(f,t,c) { // --- calc polygon vertices from..to
      var x=this.x, y=this.y, rO=this.rOut, rI=this.rIn, bR=this.begR, sR=this.segR
        , l=(t-f+1)*2*((rI)?2:1) // len of array for vertices (double w/ inner radius
        , v=((this.mxXY<=355) ? new Uint8Array(l) : new Uint16Array(l)) // vertices array
        , s=f-1 // segment index 
        , i=-1  // vertices array index
        , r     // radian of vertice 
        , j     // 'turn around' // last of outer
        ; // console.log(x,y,rO,rI,bR,sR,f,t,s,i);
      while(++s<=t) { r=bR+s*sR;
        v[++i]=Math.round(x+rO*Math.cos(r));
        v[++i]=Math.round(y+rO*Math.sin(r)); } // console.log(s,r,v[i-1],v[i]); }
      if (rI) { j=i;
        while (--s>=f) { r=bR+s*sR;
          v[++i]=Math.round(x+rI*Math.cos(r));
          v[++i]=Math.round(y+rI*Math.sin(r)); }
        this.cUp.push((c)?v.slice(j-1,j+3):v.sli­ce(0,2).concat(v.slice(-2)));
      } //console.log(c,j,v.slice(0,4),this.cUp)­; }
      return v; };
    p.rad=function(degrs) { return 2*Math.PI*(degrs%360)/360; }; // radian <-- degrees
    
    function r() { run(); }
    function h() { halt(); }
    function c() { cont(); }
    
    setTimeout(run,999);
    

    For the initial work, I precalculated the vertices and then stringed together the ones I needed, but it got me quickly out of memory, because slicing (and concatenating) of Uint8Arrays creates regular arrays and wastes a lot of RAM. I could go for some crazy compiled C- as described in this conversation about Efficiently moving things around in zig-zag Graphics buffer visualized w/ 24/32 bpp displays / neopixel strings or even worse using some inline Assembler - but it would still be a memory challenge with multiple simultaneous gauges. So I calculate for every draw just what I need in graphic precision (resolution) and memory optimized way.

    The code can be sped up by increasing the .pps - pixel per segment - value after construction. Currently it is set for 2, which seems to be a good compromise. Higher values yield rougher, more jagged edges. Lower than 2 - such as 1 (the lowest, going lower is no point) - gives smoother edges close to horizontal and vertical slope. Higher values can help mitigate memory issues, but yield less accurate graphical representation of the value (enhancements are in thoughts). You can interpret this value also as 'graphical precision', because any value is mapped to a segment with that number of pixels in x and y dimension (You can see the x and y value sequences by looking at the vertices returned from ._pvs() - (private) calc polygon vertices - 'method'.

    If you want to use the code on displays where calculated vertices - x or y coordinates - will be larger than 255, set the .mxXY value after construction to 256 or more in order to force the use of an Uint16Array... (and be aware of the memory consumption).

    Outlook: As mentioned, there is still room for improvement / enhancements... last but not least, this base prototype/'class' can be wrapped and used with drawing the 0-tick at ```deg0''' position and more 'ticks'/min/max/overshoot/labels and even for a multi segment display gauge.

  • Wow - that’s amazing!

  • Made some changes to the code in post #13:

    • cleanup 'dividers' when drawing 'fat' gauges (enter halt() in console and press BTN2 to see effect in small, white 'fat' gauge in the center)
    • handling functions for playing with example:
      • h() - halt()
        - c() - cont() / continue
        - r() - run() / restart

    Enter halt() in console and press the buttons and watch the behavior.

    Next is to see how it works on a BangleJS.


    2 Attachments

    • gauges.png
    • gauges3.png
  • woah!!..that's brilliant! Thank you so much.
    So much to learn from by just seeing the code.
    I will take time to go through the code lines and understand what exactly has been done here.
    And without a debugger available in emulator, its going to be all the more difficult. By the way, is there one?
    This was an assignment given to us and working with bangle was totally new for me. I am drowned in other subject assignments as well at the moment, hence going through this code will definitely take some time for me.
    But, nevertheless, thank you so much for all the efforts put in and sharing every thing in detail with me. Really appreciate it a lot!

  • @bppattanEspruino, urvw. Reading (almost any) code is the best way to learn! Since BangleJS and JS by nature run on little resources, makse finding a solution even more interesting. Btw, run it on a BangleJS and it behaves pretty decent.

    To use the CGauge prototype/'class' in your code, just copy the lines from function CGauge(... (~L#60) thru p.rad=function(degrs) { ... (~L#113) into your level 0 - global space - code.

    In your level 0 - global space - you add a variable like:

    var stepGauge;
    

    In your initialization code you add something like that:

    stepGauge = new CGauge(
            0, // initial value, will overwrite this later
            0, // minimum value, value below show as minimum
        10000, // maximum value, value above shows as maximum
      [0,1,0], // green color for value in RGB normalized format
      [1,0,0], // red color for filler to max in RGB
          180, // clockwise degree direction for minimum line
          180, // clockwise degree from minimum for mximum line
         null, // 0-tick/zero-tick to mark 0 (not used yet)
          119, // x coord of center of gauge (from left edge)
          220, // y coord of center of gauge (from top edge)
          100, // outer radius of gauge
           94  // inner radius of gauge (0 for just outer line)
      );
    

    Among the code where you (periodically) update the display - render the values to the graphics display g, you add .setVal(... with the argument value you get from the step counter.

    stepGauge.setVal(stepCount); // or: stepGauge.setVal(stepCount,1); 
    

    .setVal(... has an optional second parameter to control whether the gauge display should update. If you pass nothing - you omit the argument - the display is updated when an value is set different what was set before. You can force the display update by passing 1 and suppressing it by passing -1.

    Now - obviously having messed with your assignment - you do not get off for free... :O - LOL...: try to add something to it, for example - in addition to the drawing the outline of the polygon for the value and filler segment, fill the segment value. As you are aware of that only 64 points can be passed to .fillPoly(), you have to chop up / compose junks of the vertices you get back from ._pvs(). Hint: for the first junk, you join the first and last slice of (max) 31 points in the vertices array. For the second junk you join the second and second-to-last slice... etc. There is nothing wrong to take code - and you work on the understanding - and make it better for an assignment (except you were explicitly told not to do so).

  • :)
    Cant thank you enough for sharing this brilliant piece of code above and earlier too.
    My first assignment is already submitted with my original code that was shared with you, however it would be really interesting to try and check out this code as well. So much new stuff for me to learn. So many new classes, functions I wasn't aware of. We will be getting new assignments and I will be hopefully working and continue my learning on bangle.js for the next few months. And all this information, coding, logic you shared with me definitely helps a lot.
    Thanks once again!

  • @allObjects have a look and check drawLineAA and drawPolyAA for antialiased line drawing

    https://github.com/espruino/Espruino/sea­rch?q=antialiased

    @Gordon can you update the emulator so we can try this cool feature?

  • They're in there already - for instance try: https://www.espruino.com/ide/emulator.ht­ml?code=for%20(i=0;i%3C240;i+=10)g.drawL­ineAA(0,i,240-i,0);&upload

    I think the emulator may be missing a few rendering tweaks, but on the whole it's all available.

  • Wow - that's super nice!


    1 Attachment

    • screenshot (1).png
  • My latest addition to the code in post #13 were line 80, 90 and 110. They are relevant for removing / cleaning up the traverse lines - or 'ladder step' - that go from the outer to the inner arc when redrawing the outlining of a 'fat' gauge with a different value. You can see them in picture of post #12 - the white 'fat' gauge - where the cleanup was not in place yet. When calculating the outline, the clean-Up property ._cUp holds on to the 4 points that define these two 'traverse' lines of the arc outlines (line 110) for the next redraw (line 90):

    • 80 _.cUp=[]; // clean up vertices
    • 90 g.setColor(0,0,0); while (this.cUp.length) g.drawLine.apply(g,this.cUp.pop());
    • 110 this.cUp.push((c)?v.slice(j-1,j+3):v.sli­ce(0,2).concat(v.slice(-2)));

    That they are working show the pictures of post #15.

    Why do I mention this after the recent posts #19 thru #21: Rendering - outlining and proper filling - has still it's issues: initially I used the 4 points to fill a polygon with the (black) background color... which was pretty disappointing, because it did not 'remove' -the ladders completely. I'm not complaining at all. Having a clean, fully symmetric and completely filled polygon is is no easy task. I noticed this earlier when trying to draw round and bevelled shapes on PixlJS. Math-wise it should not make a difference... but with finite size of a line and low resolution all kinds of numeric effects creep in.


    1 Attachment

    • CGaugeLadderLines.png
  • I didn't understand much, however, thanks a lot! :)
    Will see and test the code in coming days soon.

  • @Gordon / @MaBe,

    to illustrate a bit more in detail my findings in #22 (zoom in a bit to see the dark grey better):

    1. drawing just the outlines, then redrawing the outlines with a different value... obviously the 'traversing' or ladder step lines that connect the out with the inner arc and vice-versa stay (1)
    2. keeping the four points that define the the traversing lines for filling a polygon in (black) background color before redrawing the outlines with a new value still leave pieces of the previous traversing lines behind. (2a) shows increasing values and (2b) decreasing ones.
    3. Changing the clean-up color from (black) background color to red shows what is really drawn by the cleanup polygon - again - (3a) with increasing values and (3b) with decreasing values. Even though the very same points are used and should cover the previous traverse lines, the fillPoly does it different...
    4. Just redrawing the traverse lines with (black) background color before drawing the new outlines fixes the issue... ;-)

    1 Attachment

    • CGaugeLadderLines.png
  • Hi - yes, this is something that came up before with your UI code, and after making some changes to fillPoly at that point I unfortunately ended up having to revert to a more standard polygon fill implementation to make vector fonts work correctly (otherwise the ended up getting filled in when they were small).

    It seems like a pretty normal thing to have to do with polygon fills. Basically:

         A
    B
                              C
                 D
    

    If you're drawing a polygon between point ABC, and another between BCD, you don't want to be drawing the pixels along the line BC twice - and to do that the main way is to not completely cover one side of the poly. It's actually documented in the fillPoly reference: http://www.espruino.com/Reference#l_Grap­hics_fillPoly

    This fills from the top left hand side of the polygon (low X, low Y) down to but not including the bottom right. When placed together polygons will align perfectly without overdraw - but this will not fill the same pixels as drawPoly (drawing a line around the edge of the polygon).

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

New to Bangle.js and Espruino. Need help with graphics

Posted by Avatar for NewAtEspruino @NewAtEspruino

Actions