Page 1 of 1

QCC 2021 Universel épisode 6: Python et zone graphique utile

Unread postPosted: 17 Aug 2021, 20:15
by critor
Quelle Calculatrice Choisir 2021 édition Universelle

Épisode 6 - Python zone graphique utile

5409Pour fêter les 10 ans de TI-Planet en cette rentrée 2021, nous te publions la base de données intégrale de nos classements de rentrée QCC organisés depuis la rentrée 2015.

Nous en profitons de plus pour te réaliser le travail titanesque d'étendre les tests aux modèles plus anciens :
  • toutes les calculatrices graphiques Texas Instruments (depuis la première TI-81 de 1990)
  • toutes les calculatrices graphiques Casio à technologie Flash (depuis 2003)
Ce qui donne pas moins de 132 modèles différents de calculatrices testés sous toutes leurs coutures, 10 ans de tests et découvertes à portée de clic ! :D

Dans un épisode précédent, nous t'avons révélé les dimensions de la zone graphique utile de chaque modèle, c'est-à-dire la zone dont l'utilisateur peut librement allumer les pixels :
  • par tracé de graphes
  • par exécution de programmes en langage constructeur/historique (très souvent un langage Basic)
Nous avions volontairement laissé de côté les scripts Python que nous allons donc traiter séparément aujourd'hui. En effet la zone graphique contrôlable par les scripts Python a bien souvent des dimensions différentes.

Comment donc faire maintenant pour tester la taille de la zone graphique, rien qu'en lisant / écrivant des pixels ? Et bien c'est très simple, tu ne vas pas être dépaysé(e). ;)
Nous allons procéder comme avec une tortue (langage Scratch ou module Python turtle). Nous allons parcourir l'écran, en diagonale, en tentant de lire et modifier chaque pixel rencontré.

Pour savoir si un pixel est accessible en écriture, nous tenterons d'inverser sa couleur :
Code: Select all
def invert_color(c):
  try:
    ci = [0, 0, 0]
    for k in range(3):
      ci[k] = 255 - c[k]
  except:
    ci = ~(c&0xffffff) & 0xffffff
  return ci

def is_pixel_writable(x, y, bad_pixel):
  if is_pixel_readable(x, y, bad_pixel):
    c0 = get_pixel(x, y)
    set_pixel(x, y, invert_color(c0))
    c = get_pixel(x, y)
    return c != c0


Pour savoir si un pixel que l'on arrive à lire correspond bien à un pixel visible de l'écran, nous prendrons comme référence la mauvaise valeur de pixel retournée par une lecture clairement hors écran, coordonnées (-2, -2).
Dans le seul cas où l'on rencontre cette valeur qui peut très bien être justifiée, nous tenterons ici encore de l'inverser.
Code: Select all
def is_pixel_readable(x, y, bad_pixel):
  c = None
  try:
    c = get_pixel(x, y)
  except:
    pass
  if c != None:
    if c == bad_pixel:
      set_pixel(x, y, invert_color(c))
      c = get_pixel(x, y)
  return c != bad_pixel


Voici donc les fonctions principales utilisant tout ça :
Code: Select all
def scr_test(x0, y0, dx0, dy0, test):
  bad_pixel = None
  try:
    bad_pixel = get_pixel(-2, -2)
  except:
    pass
  x, y, dx, dy = x0, y0, dx0, dy0
  while not test(x, y, bad_pixel):
    x += dx
    y += dy
  if test(x, y - dy, bad_pixel): y = y0
  elif test(x - dx, y, bad_pixel): x = x0
  x0, y0 = x, y
  x += dx
  y += dy
  while(dx or dy):
    if not test(x - ((dx == 0) and dx0),y - ((dy == 0) and dy0), bad_pixel):
      if test(x - ((dx == 0) and dx0), y - ((dy == 0) and dy0) - dy0, bad_pixel): dy = 0
      elif test(x - ((dx == 0) and dx0) - dx0, y - ((dy == 0) and dy0), bad_pixel): dx = 0
      else: dx, dy = 0, 0
    x += dx
    y += dy
  return x0, y0, (x - x0) // dx0, (y - y0) // dy0

def scr_size():
  xrd0, yrd0, xrd, yrd = scr_test(0, 0, -1, -1, is_pixel_readable)
  xra0, yra0, xra, yra = scr_test(1, 1, 1, 1, is_pixel_readable)
  xr0, yr0 = xrd0 - xrd + 1, yrd0 - yrd + 1
  xr, yr = xra + xrd, yra + yrd
  xw0, yw0, xw, yw = scr_test(xr0, yr0, 1, 1, is_pixel_writable)
  print("at (" + str(xr0) + "," + str(yr0) + "): " + str(xr) + "x" + str(yr) + " readable pixels")
  print("at (" + str(xw0) + "," + str(yw0) + "): " + str(xw) + "x" + str(yw) + " writable pixels")
  return xr0, yr0, xr, yr, xw0, yw0, xw, yw

Il suffit donc d'appeler scr_size(), et tu vas vite comprendre avec notre premier exemple. :)

12630Les TI-83 Premium CE Edition Python et TI-84 Plus CE-T Python Edition t'offrent donc un écran 320×240 pixels, et une zone utile pour graphes et programmes de 265×165 pixels (56,93%).

En Python, les fonctions relatives aux pixels sont ti_graphic.getPixel(x, y) et ti_graphic.setPixel(x, y, couleur).

Après donc parcours de l'écran en diagonale par notre tortue virtuelle, cette dernière nous reporte :
  • 321×241= 77361 pixels pouvant être lus à partir des coordonnées (-1, -1)
  • 321×210= 67410 pixels pouvant être écrits à partir des coordonnées (-1, 30)

Si le fonctionnement est simple, plusieurs bizarreries dans le résultat sont toutefois à traiter ici.

Déjà sur les deux zones différentes qui nous sont retournées :
  • la première signifie que l'on peut lire l'intégralité des pixels de l'écran
  • la deuxième ne retient donc plus que les pixels pouvant être modifiés, ici situés en-dessous de la barre d'état de 30 pixels de hauteur, et c'est celle-ci qui correspond à la zone graphique, la seule zone où la tortue a réussi à tracer son chemin comme tu vois ci-contre

Ensuite, selon notre tortue nous aurions donc un écran de 321×241 pixels, soit plus que les 320×240 pixels précédemment annoncés, avec :
  • une ligne de pixels d'ordonnée -1 pouvant être lus
  • une colonne de pixels d'abscisse -1 pouvant être lus, et également écrits à partir de l'ordonnée 30 marque le début de la zone graphique précédente

Or une tentative d'écriture donc sur cette dernière colonne ne donne visiblement rien à l'écran.

Soit il y a un bug dans notre code expliquant ce résultat différent de la réalité, et tu verras bien ci-dessous si la même anomalie est présente sur d'autres modèles ou pas avec exactement le même code.

Soit la chose vient de Texas Instruments. Nous aurions donc un écran de 320×240 pixels, mais avec en mémoire un buffer associé de 321×241 pixels. Ou bien c'est un bug, ou bien il y a une raison technique que nous ignorons à ce jour pour disposer en mémoire d'1 ligne et d'1 colonne supplémentaires de pixels sans aucune existence physique.

Pour notre classement, nous retiendrons donc ici une zone graphique correspondant à la réalité, c'est-à-dire de 320×210, soit 87,50% de la définition de l'écran, c'est déjà bien mieux qu'avec le langage historique ! :bj:

12631La Casio Graph 90+E t'offre un écran de 396×224 pixels, avec une zone utile pour graphes et programmes de 379×187 pixels (79,90%).

En Python, les fonctions relatives aux pixels sont casioplot.get_pixel(x, y) et casioplot.set_pixel(x, y, couleur).

Mais voyons maintenant ce que cela donne dans l'application Python.

Et c'est fantastique, Casio ici aussi a fait un effort, ce sont pas moins de 384×192 pixels qui sont contrôlables en Python, soit 83,12% de l'écran ! :bj:

Sur les modèles précédents Casio fx-CG10 et fx-CG20, pas de mise à jour avec Python.

Toutefois tu as également la possibilité d'installer l'application KhiCAS, une adaptation pour ta calculatrice du logiciel de mathématiques intégré Xcas par Bernard Parisse, enseignant-chercheur à l'Université de Grenoble.

L'environnement est également programmable avec une syntaxe proche du Python. Pas de fonctions pour contrôler individuellement les pixels ici, mais par contre nous y disposons d'une tortue dont on peut spécifier les déplacements en pixels. Tentons de tracer un rectangle le plus grand possible à l'aide du code suivant :
Code: Select all
def scrtest(w, h):
  efface
  leve_crayon
  tourne_gauche 180
  avance w // 3
  tourne_gauche 90
  avance h // 2
  baisse_crayon
  for k in range(2):
    tourne_gauche 90
    avance w
    tourne_gauche 90
    avance h

C'est l'appel scrtest(383, 191) qui nous permet de rentrer le plus grand rectangle possible dans l'écran. Comme les paramètres concernent ici des déplacements de la tortue cela correspond à une zone graphique de 384×192 (83,12%), soit exactement comme avec l'application Python officielle.

La Casio Graph 35+E II dispose d'un écran de 128×64 pixels, avec une zone utile pour graphes et programmes de 127×63 pixels (97,67%).

En Python, les fonctions relatives aux pixels sont ici encore casioplot.get_pixel(x, y) et casioplot.set_pixel(x, y, couleur).

Et en Python c'est donc superbe, nous contrôlons apparemment 128×64 pixels soit 100% de l'écran. :)

12696Les TI-Nspire CX II utilisent un écran 320×240 pixels, avec une zone utile pour graphes et programmes de 318×212 pixels (87,78%).

Le module Python de tracé par pixels est ti_draw. Il ne dispose pas de fonction permettant d'allumer un pixel isolé comme un set_pixel(). On pourrait certainement remplacer cela par un appel draw_rect(x, y, 0, 0) pour tracer un rectangle d'1 pixel de surface. Mais surtout, ti_draw ne dispose pas de fonction get_pixel().

En fait, get_pixel() et set_pixel() sont offertes dans le cadre d'une autre module ti_image, permettant de travailler sur un calque avant de l'afficher. Mais comme il faut définir les dimensions du calque en question, un test basé là-dessus ne serait pas pertinent.

Toutefois, ti_draw fournit une fonction get_screen_dim() nous permettant de récupérer les dimensions de la zone graphique utilisable, sans surprise ici les mêmes 318×212 pixels.

Sur les anciennes TI-Nspire CX, pas de mise à jour avec Python.

Toutefois, si ta calculatrice n'a pas été mise à jour avec la dernière version 4.5.5, tu peux installer Ndless et ensuite l'application KhiCAS.

KhiCAS intègre un interpréteur Python et surtout un mode examen compatible avec celui de Texas Instruments. Si tu actives le mode examen depuis les menus de KhiCAS, ce dernier restera disponible en mode examen ! :bj:

Les fonctions get_pixel() et set_pixel() sont ici offertes via le module graphic. Ce module est également accessible via les alias casioplot et kandinsky, ce qui permet une compatibilité directe avec les scripts conçus pour Casio et NumWorks ! :bj:

Notre script de test détecte ici 320×222 pixels pouvant à la fois être lus et écrits, soit 92,5%. :D

Sur les TI-Nspire CM et TI-Nspire monochromes, nous n'avons pas que 32 Mio de SDRAM au lieu de 64 Mio. Ce n'est pas suffisant pour lancer KhiCAS.

Tu peux ici installer Ndless puis ensuite l'application MicroPython.

On y contrôle alors 320×240 pixels soit 100% de l'écran ! :bj:

Attention toutefois, l'activation du mode examen t'interdira l'usage de cette application. :'(

12695La NumWorks t'apporte un écran de 320×240 pixels, dont comme nous avons vu 320×204 pixels pour les graphes (85%).

En Python, les fonctions qui nous intéressent ici sont kandinsky.get_pixel(x, y) et kandinsky.set_pixel(x, y, couleur).

Nous avons donc accès ici à une zone graphique de 320×222 pixels, soit 92,5% ! :D

6774La HP Prime t'offre un écran de 320×240 pixels, intégralement utilisables par les graphes et programmes en langage constructeur (HPPPL).

Le module Python hpprime nous offre de quoi écrire un pixel : pixon(numero_calque, x, y, couleur).

Il ne fournit pas directement de quoi lire un pixel, mais par contre une fonction eval() permettant de faire appel au langage constructeur HPPPL où cette fonction existe. On peut alors se redéfinir une fonction get_pixel() en Python :
Code: Select all
def get_pixel(x, y):
      return int(eval("get_pixel(" + str(x) + "," + str(y) + ")"))


Sans surprise ici aussi, nous contrôlons les mêmes 320×240 pixels soit 100% de l'écran ! :D

Les mesures sont toutes disponibles et facilement comparables sur le lien ci-dessous :