arcRgba function

bool arcRgba(
  1. Pointer<SdlRenderer> renderer,
  2. double x,
  3. double y,
  4. double rad,
  5. double start,
  6. double end,
  7. int r,
  8. int g,
  9. int b,
  10. int a,
)

Implementation

bool arcRgba(Pointer<SdlRenderer> renderer, double x, double y, double rad,
    double start, double end, int r, int g, int b, int a) {
  bool result = true;
  double cx = 0;
  double cy = rad;
  double df = 1 - rad;
  double dE = 3;
  double dSe = -2 * rad + 5;
  double xpcx, xmcx, xpcy, xmcy;
  double ypcy, ymcy, ypcx, ymcx;
  int drawoct;
  int startoct, endoct, oct, stopvalStart = 0, stopvalEnd = 0;
  double dstart, dend, temp = 0.0;

  /*
	* Sanity check radius
	*/
  if (rad < 0) {
    return false;
  }

  /*
	* Special case for rad=0 - draw a point
	*/
  if (rad == 0) {
    return pixelRgba(renderer, x, y, r, g, b, a);
  }

  /*
	 Octant labeling

	  \ 5 | 6 /
	   \  |  /
	  4 \ | / 7
	     \|/
	------+------ +x
	     /|\
	  3 / | \ 0
	   /  |  \
	  / 2 | 1 \
	      +y

	 Initially reset bitmask to 0x00000000
	 the set whether or not to keep drawing a given octant.
	 For example: 0x00111100 means we're drawing in octants 2-5
	*/
  drawoct = 0;

  /*
	* Fixup angles
	*/
  start %= 360;
  end %= 360;
  /* 0 <= start & end < 360; note that sometimes start > end - if so, arc goes back through 0. */
  while (start < 0) {
    start += 360;
  }
  while (end < 0) {
    end += 360;
  }
  start %= 360;
  end %= 360;

  /* now, we find which octants we're drawing in. */
  startoct = start ~/ 45;
  endoct = end ~/ 45;
  oct = startoct - 1;

  /* stopvalStart, stopvalEnd; what values of cx to stop at. */
  do {
    oct = (oct + 1) % 8;

    if (oct == startoct) {
      /* need to compute stopvalStart for this octant.  Look at picture above if this is unclear */
      dstart = start.toDouble();
      switch (oct) {
        case 0:
        case 3:
          temp = math.sin(dstart * math.pi / 180.0);
          break;
        case 1:
        case 6:
          temp = math.cos(dstart * math.pi / 180.0);
          break;
        case 2:
        case 5:
          temp = -math.cos(dstart * math.pi / 180.0);
          break;
        case 4:
        case 7:
          temp = -math.sin(dstart * math.pi / 180.0);
          break;
      }
      temp *= rad;
      stopvalStart = temp.toInt();
      /*
			This isn't arbitrary, but requires graph paper to explain well.
			The basic idea is that we're always changing drawoct after we draw, so we
			stop immediately after we render the last sensible pixel at x = ((int)temp).
			and whether to draw in this octant initially
			*/
      if ((oct % 2) != 0) {
        drawoct |= (1 <<
            oct); /* this is basically like saying drawoct[oct] = true, if drawoct were a bool array */
      } else {
        drawoct &= 255 -
            (1 << oct); /* this is basically like saying drawoct[oct] = false */
      }
    }
    if (oct == endoct) {
      /* need to compute stopvalEnd for this octant */
      dend = end.toDouble();
      switch (oct) {
        case 0:
        case 3:
          temp = math.sin(dend * math.pi / 180);
          break;
        case 1:
        case 6:
          temp = math.cos(dend * math.pi / 180);
          break;
        case 2:
        case 5:
          temp = -math.cos(dend * math.pi / 180);
          break;
        case 4:
        case 7:
          temp = -math.sin(dend * math.pi / 180);
          break;
      }
      temp *= rad;
      stopvalEnd = temp.toInt();

      /* and whether to draw in this octant initially */
      if (startoct == endoct) {
        /* note:      we start drawing, stop, then start again in this case */
        /* otherwise: we only draw in this octant, so initialize it to false, it will get set back to true */
        if (start > end) {
          /* unfortunately, if we're in the same octant and need to draw over the whole circle, */
          /* we need to set the rest to true, because the while loop will end at the bottom. */
          drawoct = 255;
        } else {
          drawoct &= 255 - (1 << oct);
        }
      } else if ((oct % 2) != 0) {
        drawoct &= 255 - (1 << oct);
      } else {
        drawoct |= (1 << oct);
      }
    } else if (oct != startoct) {
      /* already verified that it's != endoct */
      drawoct |= (1 << oct); /* draw this entire segment */
    }
  } while (oct != endoct);

  /* so now we have what octants to draw and when to draw them. all that's left is the actual raster code. */

  /*
	* Set color
	*/
  result = true;
  if (result) {
    result = sdlSetRenderDrawBlendMode(
        renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
  }
  if (result) {
    result = sdlSetRenderDrawColor(renderer, r, g, b, a);
  }

  /*
	* Draw arc
	*/
  do {
    ypcy = y + cy;
    ymcy = y - cy;
    if (cx > 0) {
      xpcx = x + cx;
      xmcx = x - cx;

      /* always check if we're drawing a certain octant before adding a pixel to that octant. */
      if ((drawoct & 4) != 0) {
        if (result) {
          result = pixel(renderer, xmcx, ypcy);
        }
      }
      if ((drawoct & 2) != 0) {
        if (result) {
          result = pixel(renderer, xpcx, ypcy);
        }
      }
      if ((drawoct & 32) != 0) {
        if (result) {
          result = pixel(renderer, xmcx, ymcy);
        }
      }
      if ((drawoct & 64) != 0) {
        if (result) {
          result = pixel(renderer, xpcx, ymcy);
        }
      }
    } else {
      if ((drawoct & 96) != 0) {
        if (result) {
          result = pixel(renderer, x, ymcy);
        }
      }
      if ((drawoct & 6) != 0) {
        if (result) {
          result = pixel(renderer, x, ypcy);
        }
      }
    }

    xpcy = x + cy;
    xmcy = x - cy;
    if (cx > 0 && cx != cy) {
      ypcx = y + cx;
      ymcx = y - cx;
      if ((drawoct & 8) != 0) {
        if (result) {
          result = pixel(renderer, xmcy, ypcx);
        }
      }
      if ((drawoct & 1) != 0) {
        if (result) {
          result = pixel(renderer, xpcy, ypcx);
        }
      }
      if ((drawoct & 16) != 0) {
        if (result) {
          result = pixel(renderer, xmcy, ymcx);
        }
      }
      if ((drawoct & 128) != 0) {
        if (result) {
          result = pixel(renderer, xpcy, ymcx);
        }
      }
    } else if (cx == 0) {
      if ((drawoct & 24) != 0) {
        if (result) {
          result = pixel(renderer, xmcy, y);
        }
      }
      if ((drawoct & 129) != 0) {
        if (result) {
          result = pixel(renderer, xpcy, y);
        }
      }
    }
    /*
		* Update whether we're drawing an octant
		*/
    if (stopvalStart == cx) {
      /* works like an on-off switch. */
      /* This is just in case start & end are in the same octant. */
      if ((drawoct & (1 << startoct)) != 0) {
        drawoct &= 255 - (1 << startoct);
      } else {
        drawoct |= (1 << startoct);
      }
    }
    if (stopvalEnd == cx) {
      if ((drawoct & (1 << endoct)) != 0) {
        drawoct &= 255 - (1 << endoct);
      } else {
        drawoct |= (1 << endoct);
      }
    }

    /*
		* Update pixels
		*/
    if (df < 0) {
      df += dE;
      dE += 2;
      dSe += 2;
    } else {
      df += dSe;
      dE += 2;
      dSe += 4;
      cy--;
    }
    cx++;
  } while (cx <= cy);
  return result;
}