static public int CircularCompare(TermPosition a, TermPosition b, TermPosition center, bool clock) { double a1 = (toDegrees(Math.Atan2(a.X - center.X, a.Y - center.Y)) + 360) % 360; double a2 = (toDegrees(Math.Atan2(b.X - center.X, b.Y - center.Y)) + 360) % 360; return((clock ? 1 : -1) * (int)(a1 - a2)); }
/// <summary> /// Creates a surface without initializing it /// </summary> /// <param name="position">the top-left corner of the surface, in global terms</param> /// <param name="resolution">the resolution of the surface</param> /// <returns>The surface</returns> public FucksSurfaceManager CreateSurface(TermPosition position, TermResolution resolution) { var sz = new TermSize(resolution.Xres, resolution.Yres); var manager = new FucksSurfaceManager(position, sz, resolution, BasicColor.Default); surfaces.Add(manager); return(manager); }
/// <summary> /// Translates a surface along the XY plane /// </summary> /// <param name="manager">the surface</param> /// <param name="x">amount to move in X</param> /// <param name="y">amount to move in Y</param> public void Translate(FucksSurfaceManager manager, int x, int y) { // invalidated = true; var postl = manager.surfacePosition; var bound = manager.bounds; manager.Translate(x, y); postl = postl.CompoundMinimumBound(x, y); var posbr = bound.CompoundMaximumBound(x, y) + postl; for (TermPosition pos = postl; pos.X <= posbr.X; pos.X++) { for (pos.Y = postl.Y; pos.Y <= posbr.Y; pos.Y++) { ref var v = ref renderState[pos.X, pos.Y]; v.rendered = false; v.dirty = true; surfaces.ForEach(mgr => mgr.MarkCellDirtyIfInBounds(pos)); } }
public void PutChar(FucksSurfaceManager surface, char c, TermPosition termPosition) { surface.PutChar(c, ref termPosition); dirty = surface.Dirty; }
/// <summary> /// Generates an arc mapped to a grid /// </summary> /// <returns>The arc's coordinates.</returns> /// <param name="x">The x coordinate of the arc's center.</param> /// <param name="y">The y coordinate of the arc's center.</param> /// <param name="rad">Radius.</param> /// <param name="start">Start of the arc (in degrees).</param> /// <param name="end">End of the arc (in degrees).</param> /// <param name="clockwise_order">If set to <c>false</c> the output will be ordered counter-clockwise.</param> static public TermPosition[] GenerateArc(int x, int y, int rad, int start, int end, bool clockwise_order = true) { double dstart, dend, temp; int stopval_start = 0, stopval_end = 0; int cx = 0, cy = rad, df = 1 - rad, d_e = 3, d_se = -2 * rad + 5, xpcx, xmcx, xpcy, xmcy, ypcx, ymcx, ypcy, ymcy; var Cells = new List <Tuple <TermPosition, double> >(); // Radius sanity check if (rad < 0) { return new TermPosition[] { } } ; // Special case: point if (rad == 0) { return new TermPosition[] { new TermPosition(x, y) } } ; // Octant Labels /* Let there be peace of mind */ // \ 5 | 6 / // \ | / // 4 \ | / 7 // \|/ //------.------- x // /|\ // 3 / | \ 0 // / | \ // / 2 | 1 \ // y // Let there be this bitmast to set whether we're drawing // a given octant; e.g. 0x00111100 = octants 2-5 byte drawoct = 0; // Fix angles start %= 360; end %= 360; // 0 <= start, end < 360. (sometimes, start > end :: arc goes back through 0) while (start < 0) { start += 360; } while (end < 0) { end += 360; } start %= 360; end %= 360; // where am I drawing at? int startoct = start / 45; int endoct = end / 45; // do-while increments int oct = startoct - 1; do { oct = (oct + 1) % 8; if (oct == startoct) { // first compute stopval_start for this octant dstart = (double)start; temp = 0; switch (oct) { case 0: case 3: temp = Math.Sin(dstart * Math.PI / 180d); break; case 1: case 6: temp = Math.Cos(dstart * Math.PI / 180d); break; case 2: case 5: temp = -Math.Cos(dstart * Math.PI / 180d); break; case 4: case 7: temp = -Math.Sin(dstart * Math.PI / 180d); break; } temp *= rad; // Round down. always. // Every draw changes drawoct, and we stop right before // the last sensibe "pixel" at floor(temp) // I wish computers had graph paper and it was cheap stopval_start = (int)temp; // should we draw at this initial oct? if (oct % 2 == 1) { drawoct |= (byte)(1 << oct); // bit magick (TM) } else { drawoct &= (byte)(255 - (1 << oct)); } } if (oct == endoct) { // and lastly, compute stopval_end for this oct dend = (double)end; temp = 0; switch (oct) { case 0: case 3: temp = Math.Sin(dend * Math.PI / 180d); break; case 1: case 6: temp = Math.Cos(dend * Math.PI / 180d); break; case 2: case 5: temp = -Math.Cos(dend * Math.PI / 180d); break; case 4: case 7: temp = -Math.Sin(dend * Math.PI / 180d); break; } temp *= rad; stopval_end = (int)temp; if (startoct == endoct) { // if we do decide to draw here; // we've started here, ended here, and then start here again // otherwise, we only draw here, so just set it not to be drawn // it will be set back to be drawn at the end if (start > end) { // if we're in the same octant and need to draw the entire circle, // we'll have to set this to render twice drawoct = 255; } else { drawoct &= (byte)(255 - (1 << oct)); } } else if (oct % 2 == 1) { drawoct &= (byte)(255 - (1 << oct)); } else { drawoct |= (byte)(1 << oct); } } else if (oct != startoct) { // we know it's not != endoct // draw this entire octant drawoct |= (byte)(1 << oct); } } while (oct != endoct); // now we know what octants to draw and when to draw them // unto the raster code // Draw Arc do { ypcy = y + cy; ymcy = y - cy; if (cx > 0) { xpcx = x + cx; xmcx = x - cx; // which octant? if ((drawoct & 4) != 0) { Cells.Add(WrapTermPosition(xmcx, ypcy, 2d)); } if ((drawoct & 2) != 0) { Cells.Add(WrapTermPosition(xpcx, ypcy, 1d)); } if ((drawoct & 32) != 0) { Cells.Add(WrapTermPosition(xmcx, ymcy, 5d)); } if ((drawoct & 64) != 0) { Cells.Add(WrapTermPosition(xpcx, ymcy, 6d)); } } else { if ((drawoct & 6) != 0) { Cells.Add(WrapTermPosition(x, ypcy, 2.5d)); } if ((drawoct & 96) != 0) { Cells.Add(WrapTermPosition(x, ymcy, 6.5d)); } } xpcy = x + cy; xmcy = x - cy; if (cx > 0 && cx != cy) { ypcx = y + cx; ymcx = y - cx; if ((drawoct & 8) != 0) { Cells.Add(WrapTermPosition(xmcy, ypcx, 3d)); } if ((drawoct & 1) != 0) { Cells.Add(WrapTermPosition(xpcy, ypcx, 0d)); } if ((drawoct & 16) != 0) { Cells.Add(WrapTermPosition(xmcy, ymcx, 4d)); } if ((drawoct & 128) != 0) { Cells.Add(WrapTermPosition(xpcy, ymcx, 7d)); } } else if (cx == 0) { if ((drawoct & 24) != 0) { Cells.Add(WrapTermPosition(xmcy, y, 4.5d)); } if ((drawoct & 2) != 0) { Cells.Add(WrapTermPosition(xpcy, y, 1d)); } } // now update the octant mask if (stopval_start == cx) { // beep-boop, no, you're a switch [start and end can be in the same octant] if ((drawoct & (byte)(1 << startoct)) != 0) { drawoct &= (byte)(255 - (1 << startoct)); } else { drawoct |= (byte)(1 << startoct); } } if (stopval_end == cx) { if ((drawoct & (byte)(1 << endoct)) != 0) { drawoct &= (byte)(255 - (1 << endoct)); } else { drawoct |= (byte)(1 << endoct); } } // update "pixels" if (df < 0) { df += d_e; d_e += 2; d_se += 2; } else { df += d_se; d_e += 2; d_se += 4; cy--; } cx++; } while (cx <= cy); var center = new TermPosition(x, y); Cells.Sort((a, b) => CircularCompare(a.Item1, b.Item1, center, clockwise_order)); return(Cells.Select(a => a.Item1).ToArray()); }