Subversion Repositories HomeAutomation

Rev

Rev 1038 | Rev 1087 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1. #include "ks0108.h"
  2. //#include <avr/io.h>
  3. #include "font.h"
  4. //#include <avr\pgmspace.h>
  5. #include <avr/io.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8.  
  9. #include <avr/pgmspace.h>
  10.  
  11. //Some parts of the code is from proycon avrlib written by Pascal Stang:
  12. //*****************************************************************************
  13. //
  14. // File Name    : 'glcd.c'
  15. // Title        : Graphic LCD API functions
  16. // Author       : Pascal Stang - Copyright (C) 2002
  17. // Date         : 5/30/2002
  18. // Revised      : 5/30/2002
  19. // Version      : 0.5
  20. // Target MCU   : Atmel AVR
  21. // Editor Tabs  : 4
  22. //
  23. // NOTE: This code is currently below version 1.0, and therefore is considered
  24. // to be lacking in some functionality or documentation, or may not be fully
  25. // tested.  Nonetheless, you can expect most functions to work.
  26. //
  27. // This code is distributed under the GNU Public License
  28. //      which can be found at http://www.gnu.org/licenses/gpl.txt
  29. //
  30. //*****************************************************************************
  31.  
  32. #define OUTPUT  0
  33. #define INPUT   1
  34.  
  35. volatile GrLcdStateType GrLcdState;
  36.  
  37. void SetControls(uint8_t RS, uint8_t RW){
  38.     if(RS) {LCD_CONTROL_PORT |= (1<<LCD_CONTROL_PIN_RS);} else { LCD_CONTROL_PORT &= ~(1<<LCD_CONTROL_PIN_RS);};
  39.     if(RW) {LCD_CONTROL_PORT |= (1<<LCD_CONTROL_PIN_RW);} else { LCD_CONTROL_PORT &= ~(1<<LCD_CONTROL_PIN_RW);};
  40. }
  41.  
  42. void SetData(uint8_t Data){
  43.     if(Data&0x01) {LCD_DATA_PORT_DB0 |= (1<<LCD_DATA_PIN_DB0);} else { LCD_DATA_PORT_DB0 &= ~(1<<LCD_DATA_PIN_DB0);};
  44.     if(Data&0x02) {LCD_DATA_PORT_DB1 |= (1<<LCD_DATA_PIN_DB1);} else { LCD_DATA_PORT_DB1 &= ~(1<<LCD_DATA_PIN_DB1);};
  45.     if(Data&0x04) {LCD_DATA_PORT_DB2 |= (1<<LCD_DATA_PIN_DB2);} else { LCD_DATA_PORT_DB2 &= ~(1<<LCD_DATA_PIN_DB2);};
  46.     if(Data&0x08) {LCD_DATA_PORT_DB3 |= (1<<LCD_DATA_PIN_DB3);} else { LCD_DATA_PORT_DB3 &= ~(1<<LCD_DATA_PIN_DB3);};
  47.     if(Data&0x10) {LCD_DATA_PORT_DB4 |= (1<<LCD_DATA_PIN_DB4);} else { LCD_DATA_PORT_DB4 &= ~(1<<LCD_DATA_PIN_DB4);};
  48.     if(Data&0x20) {LCD_DATA_PORT_DB5 |= (1<<LCD_DATA_PIN_DB5);} else { LCD_DATA_PORT_DB5 &= ~(1<<LCD_DATA_PIN_DB5);};
  49.     if(Data&0x40) {LCD_DATA_PORT_DB6 |= (1<<LCD_DATA_PIN_DB6);} else { LCD_DATA_PORT_DB6 &= ~(1<<LCD_DATA_PIN_DB6);};
  50.     if(Data&0x80) {LCD_DATA_PORT_DB7 |= (1<<LCD_DATA_PIN_DB7);} else { LCD_DATA_PORT_DB7 &= ~(1<<LCD_DATA_PIN_DB7);};
  51. }
  52. uint8_t GetData(void){
  53.     uint8_t input = 0;
  54.     if (LCD_DATA_IN_DB0 & (1<<LCD_DATA_PIN_DB0)) {input += 1;}
  55.     if (LCD_DATA_IN_DB1 & (1<<LCD_DATA_PIN_DB1)) {input += 2;}
  56.     if (LCD_DATA_IN_DB2 & (1<<LCD_DATA_PIN_DB2)) {input += 4;}
  57.     if (LCD_DATA_IN_DB3 & (1<<LCD_DATA_PIN_DB3)) {input += 8;}
  58.     if (LCD_DATA_IN_DB4 & (1<<LCD_DATA_PIN_DB4)) {input += 16;}
  59.     if (LCD_DATA_IN_DB5 & (1<<LCD_DATA_PIN_DB5)) {input += 32;}
  60.     if (LCD_DATA_IN_DB6 & (1<<LCD_DATA_PIN_DB6)) {input += 64;}
  61.     if (LCD_DATA_IN_DB7 & (1<<LCD_DATA_PIN_DB7)) {input += 128;}
  62.     return input;
  63. }
  64.  
  65. void Disable(){
  66.     LCD_CONTROL_PORT &= ~(1<<LCD_CONTROL_PIN_E);
  67. }
  68.  
  69. void Delay(){
  70.     delay_us(1);
  71. }
  72.  
  73. void Enable(){
  74.     Delay();
  75.     LCD_CONTROL_PORT |= (1<<LCD_CONTROL_PIN_E);
  76.     Delay();
  77.     Disable();
  78.     Delay();
  79. }
  80.  
  81. void SetDirection(uint8_t dir){
  82.   if (dir == OUTPUT){
  83.     LCD_DATA_DDR_DB0 |= (1<<LCD_DATA_PIN_DB0);
  84.     LCD_DATA_DDR_DB1 |= (1<<LCD_DATA_PIN_DB1);
  85.     LCD_DATA_DDR_DB2 |= (1<<LCD_DATA_PIN_DB2);
  86.     LCD_DATA_DDR_DB3 |= (1<<LCD_DATA_PIN_DB3);
  87.     LCD_DATA_DDR_DB4 |= (1<<LCD_DATA_PIN_DB4);
  88.     LCD_DATA_DDR_DB5 |= (1<<LCD_DATA_PIN_DB5);
  89.     LCD_DATA_DDR_DB6 |= (1<<LCD_DATA_PIN_DB6);
  90.     LCD_DATA_DDR_DB7 |= (1<<LCD_DATA_PIN_DB7);
  91.   }else if (dir == INPUT){
  92.     LCD_DATA_DDR_DB0 &= ~(1<<LCD_DATA_PIN_DB0);
  93.     LCD_DATA_DDR_DB1 &= ~(1<<LCD_DATA_PIN_DB1);
  94.     LCD_DATA_DDR_DB2 &= ~(1<<LCD_DATA_PIN_DB2);
  95.     LCD_DATA_DDR_DB3 &= ~(1<<LCD_DATA_PIN_DB3);
  96.     LCD_DATA_DDR_DB4 &= ~(1<<LCD_DATA_PIN_DB4);
  97.     LCD_DATA_DDR_DB5 &= ~(1<<LCD_DATA_PIN_DB5);
  98.     LCD_DATA_DDR_DB6 &= ~(1<<LCD_DATA_PIN_DB6);
  99.     LCD_DATA_DDR_DB7 &= ~(1<<LCD_DATA_PIN_DB7);
  100.   }
  101. }
  102. void glcdSetColor(uint8_t color){
  103.   GrLcdState.color=color;
  104. }
  105. uint8_t glcdGetColor(void){
  106.   return GrLcdState.color;
  107. }
  108. void glcdWriteData(uint8_t data, uint8_t color){
  109.     SetControls(1,0);
  110.     if (color == GLCD_COLOR_CLEAR)
  111.       data = ~data;
  112.     if (GrLcdState.color == GLCD_COLOR_BLACK)
  113.       SetData(data);
  114.     else
  115.       SetData(~data);
  116.     Enable();
  117.     GrLcdState.lcdXAddr++;
  118.  
  119.     if (GrLcdState.lcdXAddr > 63){
  120.         glcdSetXY(GrLcdState.lcdXAddr,GrLcdState.lcdYAddr);
  121.     }
  122.     if (GrLcdState.lcdXAddr > 128){
  123.         glcdSetXY(0,GrLcdState.lcdYAddr+8);
  124.     }
  125. }
  126. void glcdWriteDataTransparent(uint8_t inputdata, uint8_t color){
  127.     uint8_t data = 0;
  128.     SetDirection(INPUT);
  129.     SetControls(1,1);
  130.     Enable();   //dummy read
  131.     Delay();
  132.     LCD_CONTROL_PORT |= (1<<LCD_CONTROL_PIN_E);
  133.     Delay();
  134.     if (GrLcdState.color == GLCD_COLOR_BLACK)
  135.       data = GetData();
  136.     else
  137.       data = ~GetData();
  138.     Disable();
  139.     Delay();
  140.     SetControls(1,0);
  141.     SetDirection(OUTPUT);
  142.     glcdSetXY(GrLcdState.lcdXAddr, GrLcdState.lcdYAddr);
  143.     SetControls(1,0);
  144.     if (color == GLCD_COLOR_CLEAR)
  145.       data = data&(~inputdata);
  146.     else
  147.       data = data|inputdata;
  148.     if (GrLcdState.color == GLCD_COLOR_BLACK)
  149.       SetData(data);
  150.     else
  151.       SetData(~data);
  152.     Enable();
  153.     GrLcdState.lcdXAddr++;
  154.  
  155.     if (GrLcdState.lcdXAddr > 63){
  156.         glcdSetXY(GrLcdState.lcdXAddr,GrLcdState.lcdYAddr);
  157.     }
  158.     if (GrLcdState.lcdXAddr > 128){
  159.         glcdSetXY(0,GrLcdState.lcdYAddr+8);
  160.     }
  161. }
  162.  
  163. uint8_t glcdReadData(void){
  164.     uint8_t data = 0;
  165.     SetDirection(INPUT);
  166.     SetControls(1,1);
  167.     Enable();   //dummy read
  168.     Delay();
  169.     LCD_CONTROL_PORT |= (1<<LCD_CONTROL_PIN_E);
  170.     Delay();
  171.     if (GrLcdState.color == GLCD_COLOR_BLACK)
  172.       data = GetData();
  173.     else
  174.       data = ~GetData();
  175.     Disable();
  176.     Delay();
  177.     SetControls(1,0);
  178.     SetDirection(OUTPUT);
  179.     glcdSetXY(GrLcdState.lcdXAddr, GrLcdState.lcdYAddr);
  180.     return data;
  181. }
  182.  
  183. void glcdPowerOn(){
  184.     LCD_CONTROL_DDR |= (1<<LCD_CONTROL_PIN_RS);
  185.     LCD_CONTROL_DDR |= (1<<LCD_CONTROL_PIN_RW);
  186.     LCD_CONTROL_DDR |= (1<<LCD_CONTROL_PIN_E);
  187.     LCD_CONTROL_DDR_CS |= (1<<LCD_CONTROL_PIN_CS1);
  188.     LCD_CONTROL_DDR_CS |= (1<<LCD_CONTROL_PIN_CS2);
  189.  
  190.     SetDirection(OUTPUT);
  191.     GrLcdState.color = GLCD_COLOR_WHITE;
  192.     LCD_CONTROL_PORT_CS |= (1<<LCD_CONTROL_PIN_CS2)|(1<<LCD_CONTROL_PIN_CS1);
  193.     Disable();
  194.     Delay();
  195.     //set display on
  196.     SetControls(0,0);
  197.     SetData(0x3F);
  198.     Enable();
  199.  
  200.     //set startaddress of the first row (set to row 0)
  201.     SetControls(0,0);
  202.     SetData(0xc0);
  203.     Enable();
  204.  
  205.     glcdClear();
  206. }
  207.  
  208. void glcdClear(){
  209.     LCD_CONTROL_PORT_CS |= (1<<LCD_CONTROL_PIN_CS2)|(1<<LCD_CONTROL_PIN_CS1);
  210.     Enable();
  211.     Delay();
  212.     Disable();
  213.  
  214.     uint8_t i;
  215.     uint8_t j;
  216.  
  217.     for(j = 0; j < 8; j++){
  218.         SetControls(0,0);
  219.         SetData(0xB8 + j);
  220.         Enable();
  221.  
  222.         SetControls(0,0);
  223.         SetData(0x40);
  224.         Enable();
  225.         for (i = 0; i < 64; i++){
  226.             SetControls(1,0);
  227.             if (GrLcdState.color == GLCD_COLOR_BLACK)
  228.               SetData(0x00);
  229.             else
  230.               SetData(0xff);
  231.             Enable();
  232.         }
  233.     }
  234.  
  235. }
  236.  
  237. void glcdSetXY(uint8_t x, uint8_t y){
  238.     GrLcdState.lcdXAddr = x;
  239.     GrLcdState.lcdYAddr = y;
  240.     GrLcdState.lcdYpage = y/8;
  241.  
  242.     //Vi b�rjar med X. Steg 1: V�lj r�tt chip:
  243.     if (x > 63){
  244.         LCD_CONTROL_PORT_CS |= (1<<LCD_CONTROL_PIN_CS2);
  245.         LCD_CONTROL_PORT_CS &= ~(1<<LCD_CONTROL_PIN_CS1);
  246.     } else {
  247.         LCD_CONTROL_PORT_CS |= (1<<LCD_CONTROL_PIN_CS1);
  248.         LCD_CONTROL_PORT_CS &= ~(1<<LCD_CONTROL_PIN_CS2);
  249.     }
  250.  
  251.     //Steg 2: S�tt r�tt x-adress p� det aktiva chippet
  252.     SetControls(0,0);
  253.     SetData(0x40 + x%64);
  254.     Enable();
  255.  
  256.     //Steg 3: S�tt r�tt y-adress p� det aktiva chippet
  257.     SetData(0xB8 + GrLcdState.lcdYpage);
  258.     Enable();
  259. }
  260. uint8_t glcdGetX(void){
  261.     return GrLcdState.lcdXAddr;
  262. }
  263. uint8_t glcdGetY(void){
  264.     return GrLcdState.lcdYAddr;
  265. }
  266.  
  267.  
  268. void glcdWriteChar(char c, uint8_t color)
  269. {
  270.     uint8_t i = 0;
  271.     if (GrLcdState.lcdXAddr > (128-5))
  272.     {
  273.         glcdSetXY(0,GrLcdState.lcdYAddr + 8);
  274.     }
  275.  
  276.     for(i=0; i<5; i++)
  277.     {
  278.         glcdWriteData(pgm_read_byte(&Font5x7[((c - 0x20) * 5) + i]), color); //Borde snyggas till med pekare
  279.     }
  280.     glcdWriteData(0x00, color); //space between this and the next char
  281. }
  282.  
  283. void glcdPutStr(char *data, uint8_t color){
  284.     while (*data){
  285.         glcdWriteChar(*data, color);
  286.         data++;
  287.     }
  288. }
  289. void glcdWriteCharTransparent(char c, uint8_t color)
  290. {
  291.     uint8_t i = 0;
  292.     if (GrLcdState.lcdXAddr > (128-5))
  293.     {
  294.         glcdSetXY(0,GrLcdState.lcdYAddr + 8);
  295.     }
  296.  
  297.     for(i=0; i<5; i++)
  298.     {
  299.         glcdWriteDataTransparent(pgm_read_byte(&Font5x7[((c - 0x20) * 5) + i]), color); //Borde snyggas till med pekare
  300.     }
  301.     glcdWriteDataTransparent(0x00, color); //space between this and the next char
  302. }
  303.  
  304. void glcdPutStrTransparent(char *data, uint8_t color){
  305.     while (*data){
  306.         glcdWriteCharTransparent(*data, color);
  307.         data++;
  308.     }
  309. }
  310.  
  311. void glcdDrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) {
  312.     uint8_t length, i, y, yAlt, xTmp, yTmp;
  313.     int16_t m;
  314.    
  315.     //
  316.     // vertical line
  317.     //
  318.     if(x1 == x2) {
  319.         // x1|y1 must be the upper point
  320.         if(y1 > y2) {
  321.             yTmp = y1;
  322.             y1 = y2;
  323.             y2 = yTmp;
  324.         }
  325.         glcdDrawVertLine(x1, y1, y2-y1, color);
  326.    
  327.     //
  328.     // horizontal line
  329.     //
  330.     } else if(y1 == y2) {  
  331.         // x1|y1 must be the left point
  332.         if(x1 > x2) {
  333.             xTmp = x1;
  334.             x1 = x2;
  335.             x2 = xTmp;
  336.         }
  337.         glcdDrawHoriLine(x1, y1, x2-x1, color);
  338.    
  339.     //
  340.     // schiefe line :)
  341.     //
  342.     } else {
  343.         // angle >= 45�
  344.         if((y2-y1) >= (x2-x1) || (y1-y2) >= (x2-x1)) {
  345.             // x1 must be smaller than x2
  346.             if(x1 > x2) {
  347.                 xTmp = x1;
  348.                 yTmp = y1;
  349.                 x1 = x2;
  350.                 y1 = y2;
  351.                 x2 = xTmp;
  352.                 y2 = yTmp;
  353.             }
  354.        
  355.             length = x2-x1;     // not really the length :)
  356.             m = ((y2-y1)*200)/length;
  357.             yAlt = y1;
  358.            
  359.             for(i=0; i<=length; i++) {
  360.                 y = ((m*i)/200)+y1;
  361.                
  362.                 if((m*i)%200 >= 100)
  363.                     y++;
  364.                 else if((m*i)%200 <= -100)
  365.                     y--;
  366.                
  367.                 glcdDrawLine(x1+i, yAlt, x1+i, y, color);
  368.                
  369.                 if(length <= (y2-y1) && y1 < y2)
  370.                     yAlt = y+1;
  371.                 else if(length <= (y1-y2) && y1 > y2)
  372.                     yAlt = y-1;
  373.                 else
  374.                     yAlt = y;
  375.             }
  376.        
  377.         // angle < 45�
  378.         } else {
  379.             // y1 must be smaller than y2
  380.             if(y1 > y2) {
  381.                 xTmp = x1;
  382.                 yTmp = y1;
  383.                 x1 = x2;
  384.                 y1 = y2;
  385.                 x2 = xTmp;
  386.                 y2 = yTmp;
  387.             }
  388.            
  389.             length = y2-y1;
  390.             m = ((x2-x1)*200)/length;
  391.             yAlt = x1;
  392.            
  393.             for(i=0; i<=length; i++) {
  394.                 y = ((m*i)/200)+x1;
  395.                
  396.                 if((m*i)%200 >= 100)
  397.                     y++;
  398.                 else if((m*i)%200 <= -100)
  399.                     y--;
  400.                
  401.                 glcdDrawLine(yAlt, y1+i, y, y1+i, color);
  402.                 if(length <= (x2-x1) && x1 < x2)
  403.                     yAlt = y+1;
  404.                 else if(length <= (x1-x2) && x1 > x2)
  405.                     yAlt = y-1;
  406.                 else
  407.                     yAlt = y;
  408.             }
  409.         }
  410.     }
  411. }
  412.  
  413. void glcdDrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
  414.     glcdDrawHoriLine(x, y, width, color);               // top
  415.     glcdDrawHoriLine(x, y+height, width, color);        // bottom
  416.     glcdDrawVertLine(x, y, height, color);          // left
  417.     glcdDrawVertLine(x+width, y, height, color);        // right
  418. }
  419.  
  420.  
  421. void glcdFillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
  422.     uint8_t mask, pageOffset, h, i; //, data;
  423.     height++;
  424.    
  425.     pageOffset = y%8;
  426.     y -= pageOffset;
  427.     mask = 0xFF;
  428.     if(height < 8-pageOffset) {
  429.         mask >>= (8-height);
  430.         h = height;
  431.     } else {
  432.         h = 8-pageOffset;
  433.     }
  434.     mask <<= pageOffset;
  435.    
  436.     glcdSetXY(x, y);
  437.     for(i=0; i<=width; i++) {
  438.         glcdWriteDataTransparent(mask, color);
  439.     }
  440.    
  441.     while(h+8 <= height) {
  442.         h += 8;
  443.         y += 8;
  444.         glcdSetXY(x, y);
  445.        
  446.         for(i=0; i<=width; i++) {
  447.             glcdWriteData(0xff, color);
  448.         }
  449.     }
  450.    
  451.     if(h < height) {
  452.         mask = ~(0xFF << (height-h));
  453.         glcdSetXY(x, y+8);
  454.        
  455.         for(i=0; i<=width; i++) {
  456.             glcdWriteDataTransparent(mask, color);
  457.         }
  458.     }
  459. }
  460.  
  461. void glcdInvertRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
  462.     uint8_t mask, pageOffset, h, i, data, tmpData;
  463.     height++;
  464.    
  465.     pageOffset = y%8;
  466.     y -= pageOffset;
  467.     mask = 0xFF;
  468.     if(height < 8-pageOffset) {
  469.         mask >>= (8-height);
  470.         h = height;
  471.     } else {
  472.         h = 8-pageOffset;
  473.     }
  474.     mask <<= pageOffset;
  475.    
  476.     glcdSetXY(x, y);
  477.     for(i=0; i<=width; i++) {
  478.         data = glcdReadData(); //glcdReadData();
  479.         tmpData = ~data;
  480.         data = (tmpData & mask) | (data & ~mask);
  481.         glcdWriteData(data, GLCD_COLOR_SET);
  482.     }
  483.    
  484.     while(h+8 <= height) {
  485.         h += 8;
  486.         y += 8;
  487.         glcdSetXY(x, y);
  488.        
  489.         for(i=0; i<=width; i++) {
  490.             data = glcdReadData();
  491.             glcdWriteData(~data, GLCD_COLOR_SET);
  492.         }
  493.     }
  494.    
  495.     if(h < height) {
  496.         mask = ~(0xFF << (height-h));
  497.         glcdSetXY(x, y+8);
  498.        
  499.         for(i=0; i<=width; i++) {
  500.             data = glcdReadData(); //glcdReadData();
  501.             tmpData = ~data;
  502.             data = (tmpData & mask) | (data & ~mask);
  503.             glcdWriteData(data, GLCD_COLOR_SET);
  504.         }
  505.     }
  506. }
  507.  
  508. void glcdInvert(void) {
  509.     if(GrLcdState.color == GLCD_COLOR_BLACK)
  510.         GrLcdState.color = GLCD_COLOR_WHITE;
  511.     else
  512.         GrLcdState.color = GLCD_COLOR_BLACK;
  513.     glcdInvertRect(0,0,127,63);
  514. }
  515. void glcdSetDot(uint8_t x, uint8_t y, uint8_t color) {
  516.     glcdSetXY(x, y-y%8);                    // read data from display memory
  517.     glcdWriteDataTransparent(0x01 << (y%8), GLCD_COLOR_SET);
  518. }
  519.  
  520. void glcdDrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius, uint8_t color) {
  521.     int16_t tSwitch, x1 = 0, y1 = radius;
  522.     tSwitch = 3 - 2 * radius;
  523.    
  524.     while (x1 <= y1) {
  525.         glcdSetDot(x+radius - x1, y+radius - y1, color);
  526.         glcdSetDot(x+radius - y1, y+radius - x1, color);
  527.  
  528.         glcdSetDot(x+width-radius + x1, y+radius - y1, color);
  529.         glcdSetDot(x+width-radius + y1, y+radius - x1, color);
  530.        
  531.         glcdSetDot(x+width-radius + x1, y+height-radius + y1, color);
  532.         glcdSetDot(x+width-radius + y1, y+height-radius + x1, color);
  533.  
  534.         glcdSetDot(x+radius - x1, y+height-radius + y1, color);
  535.         glcdSetDot(x+radius - y1, y+height-radius + x1, color);
  536.  
  537.         if (tSwitch < 0) {
  538.             tSwitch += (4 * x1 + 6);
  539.         } else {
  540.             tSwitch += (4 * (x1 - y1) + 10);
  541.             y1--;
  542.         }
  543.         x1++;
  544.     }
  545.        
  546.     glcdDrawHoriLine(x+radius, y, width-(2*radius), color);         // top
  547.     glcdDrawHoriLine(x+radius, y+height, width-(2*radius), color);  // bottom
  548.     glcdDrawVertLine(x, y+radius, height-(2*radius), color);            // left
  549.     glcdDrawVertLine(x+width, y+radius, height-(2*radius), color);  // right
  550. }
  551.