Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement for Graphics #1702

Closed
5 tasks done
MaBecker opened this issue Nov 12, 2019 · 40 comments
Closed
5 tasks done

Enhancement for Graphics #1702

MaBecker opened this issue Nov 12, 2019 · 40 comments

Comments

@MaBecker
Copy link
Contributor

MaBecker commented Nov 12, 2019

This is just a collection of function that came in my mind when working with the excellent library.

For a better readability, will try to write one comment for each enhancement

Updated 05/29/2020

@MaBecker
Copy link
Contributor Author

  • extend circle to draw arcs

@MaBecker
Copy link
Contributor Author

MaBecker commented Nov 12, 2019

  • extend line with thickness

Edit: Run test_thick_lines.js in Emulator

@MaBecker
Copy link
Contributor Author

MaBecker commented Nov 12, 2019

  • extend draw with rotation possibility

Use a image and some javascript for rotation, like in this sample

http://forum.espruino.com/conversations/344607/#comment15140533

Update: 03/13/2020

@MaBecker
Copy link
Contributor Author

  • where to find details for this nice feature used in Bangle.js app "GPS Time"
setInterval(function() {
  g.drawImage(img,48,48,{scale:1.5,rotate:Math.sin(getTime()*2)/2});
},100);

this would nice to have for poly functions.

@gfwilliams
Copy link
Member

https://banglejs.com/reference#l_Graphics_drawImage

Those definitely seem like good things to add.

I was considering maybe moving to a better fillPoly (that could do irregular polys) then handling drawLine using that. The real bonus of a line width is you could use the HersheyText line fonts, which scale really nicely

@MaBecker
Copy link
Contributor Author

MaBecker commented Nov 14, 2019

At the moment I use this workaround, which allows to scale and translate a array with pairs of points..

function translate(x, y, p) {
    p.forEach((e, i) => {
        p[i] += (i % 2) ? y : x;
    });
    return p;
}

function scale(x, y, p) {
    p.forEach((e, i) => {
        p[i] *= (i % 2) ? y : x;
    });
    return p;
}

var plStop = [0,0,20,0,20,20,0,20];
//g.fillPoly(translate(40,170,scale(2,2,plStop)));
g.fillPoly(translate(40,170,scale(2,2,plStop.slice())));
var plPlay = [0,0,20,10,0,20];
//g.fillPoly(translate(160,170,scale(2,2,plPlay)));
g.fillPoly(translate(160,170,scale(2,2,plPlay.slice())));

Updated: Pass a copy of the array using .slice(), otherwise the original array is updated :-(

@MaBecker
Copy link
Contributor Author

  • process bar, slider .....

@gfwilliams
Copy link
Member

allObjects has a UI library that looks really promising which just needs adding to the EspruinoDocs repo with all his documentation - I just haven't had the time so far

@MaBecker
Copy link
Contributor Author

Yes this UI is very cool 😎

@MaBecker
Copy link
Contributor Author

MaBecker commented Dec 7, 2019

Replace optimized Ellipse with standard Bresenham algorithm to remove the spikes.

Eye draw test on Bangle.js

Source

g.clear();
g.setColor(1,1,1).fillEllipse(40,60,120+80,180);
g.setColor(0,0,1).fillCircle(120,120,50);
g.setColor(0,0,0).fillCircle(120,120,20);
g.setColor(1,1,1).fillCircle(112,112,5);

Result:

IMG_9591

@MaBecker
Copy link
Contributor Author

MaBecker commented Dec 8, 2019

Hershey Fonts

Started with diving into the hershey world. This is extremely cool.
http://coopertype.org/event/the_hershey_fonts
https://emergent.unpythonic.net/software/hershey
http://paulbourke.net/dataformats/hershey/

And now starting to code in javascript to figure out how simple this can be to implement into Graphics library.

implement:

  • JSGRAPHICS_FONTSIZE_HERSHEY = 0
  • JSGRAPHICS_LINETHICKNESS = 1
  • drawLine with line thickness
  • setFontHershey(size,thickness)
  • graphicsDrawHersheyChar()
  • ....

Add a sample

// 1
p1 = [15,22,[ 6,5, 8,4, 11,1, 11,22 ]];
// 2
p2 = [ 15,22, [4,6, 4,5, 5,3, 6,2, 8,1, 12,1, 14,2, 15,3, 16,5, 16,7, 15,9, 13,12, 3,22, 17,22 ]];

g.clear();

var translate = (tx, ty, p) => p.map((v, i)=> v + ((i&1) ? ty : tx));
var scale = (sx, sy, p) => p.map((v, i) => (v * ((i&1) ? sy : sx)+0.5) |0   );

function drawLine(x1,y1,x2,y2,thickness){
  var p = [];
  var angle = Math.atan2(y2-y1,x2-x1);
  cosP = Math.cos(angle+Math.PI/2);
  cosM = Math.cos(angle-Math.PI/2);
  sinP = Math.sin(angle+Math.PI/2);
  sinM = Math.sin(angle-Math.PI/2);
  p[0] = (x1 + thickness*cosP +0.5)|0;
  p[1] = (y1 + thickness*sinP +0.5)|0;
  p[2] = (x1 + thickness*cosM +0.5)|0;
  p[3] = (y1 + thickness*sinM +0.5)|0;
  p[4] = (x2 + thickness*cosM +0.5)|0;
  p[5] = (y2 + thickness*sinM +0.5)|0;
  p[6] = (x2 + thickness*cosP +0.5)|0;
  p[7] = (y2 + thickness*sinP +0.5)|0;
  g.fillPoly(p,true);
}

var p = translate(75,75,scale(3,3,p2[2]));
var t = 3;

g.clear();

for(i=0; i < p.length-2;i += 2){
  console.log(p[i+0],p[i+1],p[i+2],p[i+3]);
  drawLine(p[i+0],p[i+1],p[i+2],p[i+3],t);
  g.fillCircle(p[i+0],p[i+1],t);
  g.fillCircle(p[i+2],p[i+3],t);
}

Bildschirmfoto 2019-12-10 um 18 23 26

adding circles makes it look smoother.

Edit:

compare with existing vector font

Bildschirmfoto 2019-12-11 um 07 05 37

@gfwilliams
Copy link
Member

Do you have a PR for the new ellipse? ;)

@MaBecker
Copy link
Contributor Author

MaBecker commented Dec 9, 2019

Yep, just added pr #1720

@MaBecker
Copy link
Contributor Author

Would like to implement thickness for drawLine. Can you please advise if and how it should be implemented.

@gfwilliams
Copy link
Member

Sounds like a good plan. I'd store lineThickness the same way we do fontAlign/etc in graphics, then check in drawPoly and run a separate bit of code. It should be ifdef'd with SAVE_ON_FLASH though.

The easy way to do it would be to draw each line segment as a poly as you've done, but I think that's going to be too slow if we want to replace Vector with Hershey fonts (which I guess is the main goal?) as there'd be loads of overdraw, and really we want to work on the whole polyline

The actual integration with graphics would take a while and would be a waste of your time, so if you could come up with an algorithm that would turn a polyline into a filled poly then I could integrate it. Even doing the algorithm in JS would work.

Something line this - you'd need to generate the red points:

image

In your code above i notice you're taking the angle and the using sin and cos, but that's very slow and you can actually just use the difference between coordinates:

dx = x2-x1;
dy = y2-y1;
d = sqrt(dx*dx + dy*dy);
dx = dx*lineWidth/d;
dy = dy*lineWidth/d;
// then [dy,-dx] is 90 degrees, [-dy,dx] is the other way
// [dx,dy] is in-line, so [(dx+dy)/2, (dy-dx)/2] 

You can even compare dx/dy with the next dx/dy using a cross product in order to work out which way the line bends (and how much) to figure out if points need adding or removing.

@gfwilliams
Copy link
Member

... only saw your update with the Hershey font you printed while writing this. It looks great - it's hard to see how anyone would complain if the font changed :)

@MaBecker
Copy link
Contributor Author

Great, thanks for you guiding informations.

@MaBecker
Copy link
Contributor Author

Just a short update on testing hershey font using a C-Style Coordinate array for the SIMPLEX character set instead of decoding jhf.

Mirrored and move chars to have same height and baseline. Also adjusted some points for a nice shape.

Started with numbers 0 - 9, scaling 1.5

Bildschirmfoto 2019-12-12 um 18 10 15

/*
 var hfl =  [
        width, heigth, numLines,
        [ fist line x,y ],
        ...
        [ last line x,y ]
    ];
*/
  ......
   // /* 1 Ascii 49 */
    [
        6, 22, 1,
        [0, 5, 2, 3, 5, 0, 5, 21]

    ],

function to setLineWith

function setLineWidth(x1, y1, x2, y2, lw) {
    var dx = x2 - x1;
    var dy = y2 - y1;
    var d = Math.sqrt(dx * dx + dy * dy);
    dx = dx * lw / d;
    dy = dy * lw / d;

    return [
        // rounding
        x1 - (dx + dy) / 2, y1 - (dy - dx) / 2,
        x1 - dx, y1 -dy,
        x1 + (dy - dx) / 2, y1 - (dx + dy) / 2,

        x1 + dy, y1 - dx,
        x2 + dy, y2 - dx,

        // rounding
        x2 + (dx + dy) / 2, y2 + (dy - dx) / 2, 
        x2 + dx, y2 + dy,
        x2 - (dy - dx) / 2, y2 + (dx + dy) / 2, 

        x2 - dy, y2 + dx,
        x1 - dy, y1 + dx
    ];
}

g.fillPoly(setLineWidth(20,20,100,200,5));

Bildschirmfoto 2019-12-12 um 18 54 48

Not sure about those six rounding lines.

@MaBecker
Copy link
Contributor Author

First result with adding line width to poly segments

IMG_9606

Fontsize: 66px

@MaBecker
Copy link
Contributor Author

working with scaling, thickness and kerning every one can create his individual look

hf = { sx: 2.5, sy :5, t: 5, k: 5 };
sx : font width
sy : font height, default 22px
t: thickness in px
k : kerning in px

IMG_9608

@MaBecker
Copy link
Contributor Author

Or as small font with hf = { sx: 1, sy :1, t: 1, k: 2 };

IMG_9617

@MaBecker
Copy link
Contributor Author

Maybe add a function like this to handle the style.

 g.setFontHershey({width: in_px, heigth : in_px, bold : in_px, kerning : in_px});

@MaBecker
Copy link
Contributor Author

Before starting to convert hershey font files to header file, some advise for output format would be helpful to create eg hershey_romans_font.h

static const unsigned char hersheyFontPolys[] IN_FLASH_MEMORY = {
 // Character code 32
...
 // Character code 255
 }

@gfwilliams
Copy link
Member

Nice! About the rounding, try changing x1 - (dx + dy) / 2, y1 - (dy - dx) / 2, to x1 - (dx + dy) * 0.71, y1 - (dy - dx) * 0.71, - it should make it more rounded :)

@gfwilliams
Copy link
Member

In other fonts I've had one array for character data, one array for # of points. It means you have to iterate through to find each character, but it immediately shaves 100 bytes off the structure because you can use 8 bits rather than 16 for the length.

@MaBecker
Copy link
Contributor Author

In other fonts I've had one array for character data, one array for # of points

Yes, seem to be the easiest way to implement it, like you did for the vector font.

@MaBecker
Copy link
Contributor Author

Got a version javascript version running

https://gist.github.com/MaBecker/89a8ec3314f456ccfe397593783ec8b4

What about spacing between chars?

@MaBecker
Copy link
Contributor Author

Decided to use different factor for drawing the rounded edges.

Bildschirmfoto 2019-12-16 um 17 14 28

Is there a solution to handle yellow marked section, because of the slope the width is to small

@gfwilliams
Copy link
Member

Are you actually doing what's suggested in #1702 (comment) ? Looks like you might just be producing one polygon per line segment?

Doing one big poly would go a long way towards fixing those glitches, but I had considered maybe making the internal poly fill algorithm take coordinates that were 16x bigger (so 16 = 1 pixel, 32 = 2, etc) would help with issues like this

@MaBecker
Copy link
Contributor Author

Looks like you might just be producing one polygon per line segment?

Yes, just started working on this :)

making the internal poly fill algorithm take coordinates that were 16x bigger (so 16 = 1 pixel, 32 = 2, etc) would help with issues like this

Good point, will include it.

@MaBecker
Copy link
Contributor Author

Just added this pr #1757 for g.quadraticBezier()

@MaBecker
Copy link
Contributor Author

  • draw rectangle with rounded corners

http://forum.espruino.com/conversations/344773/

Would be easy to add to firmware.

@gfwilliams
Copy link
Member

Here's my attempt at Hershey rendering - creating one big poly rather than one per line:

https://www.espruino.com/ide/emulator.html?gist=663f7d6e280e33511881a3dfff2ee30f&upload

Still a few edge cases, but reasonably quick and significantly less overdraw.

hershey8

@MaBecker
Copy link
Contributor Author

MaBecker commented May 13, 2020

Wow, just tested, very nice!

Have you thought about about chars > 127 and < 256 ?

Edit: Even still quick enough on a Bangle.js

@gfwilliams
Copy link
Member

It'll be much quicker when it's converted to C too :)

Potentially we could add them, yes. Euros and degrees at least.

One of the reasons for doing this was to reduce flash usage though so I don't want to bump it up too much!

I'm not 100% on the characters but it strikes me that for many of them we could maybe hardcode it? So Ü = U + umlaut ?

@gfwilliams
Copy link
Member

Obviously there's still the fillPoly issue. You wouldn't think it'd be so hard to get a reliable polygon fill algorithm!

@MaBecker
Copy link
Contributor Author

I'm not 100% on the characters but it strikes me that for many of them we could maybe hardcode it? So Ü = U + umlaut ?

Yes they are called Umlaute äöüÄÖÜ plus €ß°

ü = u + umlaut
Ü = U + same as umlaut, just on a different position
€: parts from O plus two lines
ß: parts of 3 plus a line

I was spending many hours scanning other graphic libs for a smart, short, quick and working algorithm, without success. fillPoly is not as simple as it looks.

@MaBecker
Copy link
Contributor Author

I like the look and feel of the chars with debug flag!

@gfwilliams
Copy link
Member

Ok, change of plan again. A custom-made vector font seems preferable: #1824

@MaBecker
Copy link
Contributor Author

time to close this ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants