using System.Collections.Generic;
public class IMyTextPanel {
public void WritePublicText(string s) {
s = s.Replace("\u2014\u0060", "█");
public void ShowTextureOnScreen() {
public void ShowPublicTextOnScreen() {
public static T[] arr<T>(params T[] arg) {
public class ColorUtils {
private static double oo64 = 1.0/64.0;
private static double[][] map = new double[][] {
new double[] { 0*oo64, 48*oo64, 12*oo64, 60*oo64, 3*oo64, 51*oo64, 15*oo64, 63*oo64},
new double[] {32*oo64, 16*oo64, 44*oo64, 28*oo64, 35*oo64, 19*oo64, 47*oo64, 31*oo64},
new double[] { 8*oo64, 56*oo64, 4*oo64, 52*oo64, 11*oo64, 59*oo64, 7*oo64, 55*oo64},
new double[] {40*oo64, 24*oo64, 36*oo64, 20*oo64, 43*oo64, 27*oo64, 39*oo64, 23*oo64},
new double[] { 2*oo64, 50*oo64, 14*oo64, 62*oo64, 1*oo64, 49*oo64, 13*oo64, 61*oo64},
new double[] {34*oo64, 18*oo64, 46*oo64, 30*oo64, 33*oo64, 17*oo64, 45*oo64, 29*oo64},
new double[] {10*oo64, 58*oo64, 6*oo64, 54*oo64, 9*oo64, 57*oo64, 5*oo64, 53*oo64},
new double[] {42*oo64, 26*oo64, 38*oo64, 22*oo64, 41*oo64, 25*oo64, 37*oo64, 21*oo64}
private static int[][] palette = new int[][] {
SEFix.arr( 255, 255, 255),
private static string[] colorStrings = new string[] {
private static int redC = 300;
private static int greenC = 540;
private static int blueC = 150;
private static double compareColors(int r1, int g1, int b1, int r2, int g2, int b2) {
double dl = ((r1*redC + g1*greenC + b1*blueC)-(r2*redC + g2*greenC + b2*blueC))/255000.0;
double dr = (r1-r2)/255.0, dg = (g1-g2)/255.0, db = (b1-b2)/255.0;
return ((dr*dr*redC + dg*dg*greenC + db*db*blueC)*0.0075 + dl*dl);
private static double calcError(int r, int g, int b, int r0, int g0, int b0, int[] color1, int[] color2, double ratio) {
return compareColors(r, g, b, r0,g0,b0) +
compareColors(color1[0], color1[1], color1[2], color2[0], color2[1], color2[2]) *0.03*(Math.Abs(ratio-0.5)+0.5)*
(1+ (color1[0]==color1[1] && color1[0]==color1[2] && color1[0]==color2[0] &&
color1[0] == color2[1] && color1[0]==color2[2] ? 0.03 : 0));
private static int makeRatio(int r, int g, int b, int[] c1, int[] c2) {
if (c1[0] != c2[0] || c1[1] != c2[1] || c1[2] != c2[2]) {
((c2[0] != c1[0] ? redC *64 * (r - c1[0]) / (c2[0]-c1[0]) : 0) +
(c2[1] != c1[1] ? greenC*64 * (g - c1[1]) / (c2[1]-c1[1]) : 0) +
(c1[2] != c2[2] ? blueC *64 * (b - c1[2]) / (c2[2]-c1[2]) : 0))/
((c2[0] != c1[0] ? redC : 0)+
(c2[1] != c1[1] ? greenC : 0)+
(c2[2] != c1[2] ? blueC : 0));
private static int[] createMix(int r, int g, int b) {
int[] result = SEFix.arr(0,0,32);
double minPenalty = Single.MaxValue;
for (int i = 0; i < palette.Length; i++) {
for (int j = i; j < palette.Length; j++) {
int ratio = makeRatio(r,g,b, palette[i], palette[j]);
double penalty = calcError(
palette[i][0] + ratio * (palette[j][0]-palette[i][0]) / 64,
palette[i][1] + ratio * (palette[j][1]-palette[i][1]) / 64,
palette[i][2] + ratio * (palette[j][2]-palette[i][2]) / 64,
if (penalty < minPenalty) {
public static string[][] genDitherPattern(int r, int g, int b) {
int[] mix = createMix(r,g,b);
string[][] dithered = new string[8][];
for (int x = 0; x < 8; x++) {
dithered[x] = new string[8];
for (int y = 0; y < 8; y++) {
double mapValue = map[y&7][x&7];
double ratio = mix[2]/64.0;
dithered[x][y] = colorStrings[mix[ mapValue < ratio ? 1 : 0 ]];
private static int offset = 0x21;
private static short[] glyphs = SEFix.arr<short>(
9346, 23040, 24445, 15602,
17057, 10923, 9216, 5265,
11415, 25255, 29326, 23497,
31118, 10666, 29370, 10922,
3640, 17492, 25218, 15203,
11245, 27566, 14627, 27502,
31143, 31140, 14827, 23533,
29847, 12906, 23469, 18727,
24557, 27501, 11114, 27556,
11131, 27565, 14478, 29842,
23403, 23378, 23549, 23213,
23186, 29351, 13459, 2184,
1395, 14756, 1886, 18861,
8595, 4302, 18805, 25745,
public static short getGlyph(char code) {
return glyphs[code-offset];
public readonly int width, height;
public Canvas(int w, int h) {
data = new string[width*height];
Array.Clear(data, 0, width*height);
public readonly int width, height;
private int cwidth, cheight;
private IMyTextPanel console;
private string[] currentSurface;
private string[] screenLines;
private string[][] fgDither, bgDither;
private int[] clip = new int[4];
private Dictionary<string, string[][]> oldPatterns = new Dictionary<string, string[][]>();
public Graphics(int w, int h, IMyTextPanel c) {
screen = new string[height*width];
screenLines = new string[width*height+height-1];
public void setCanvas(Canvas c) {
public void setCanvas() {
public void setFG(int r, int g, int b) {
string k = r+":"+g+":"+b;
if (!oldPatterns.TryGetValue(k, out fgDither)) {
fgDither = ColorUtils.genDitherPattern(r,g,b);
oldPatterns[k] = fgDither;
public void setBG(int r, int g, int b) {
string k = r+":"+g+":"+b;
if (!oldPatterns.TryGetValue(k, out bgDither)) {
bgDither = ColorUtils.genDitherPattern(r,g,b);
oldPatterns[k] = bgDither;
public void mask(int x1, int y1, int x2, int y2) {
public void draw(int x1, int y1, Canvas canvas) {
if (canvas.data == currentSurface) return;
x1 = Math.Max(x1, clip[0]);
y1 = Math.Max(y1, clip[1]);
int w = Math.Min(canvas.width, clip[2]-x1+1);
int h = Math.Min(canvas.height, clip[3]-y1+1);
for (int x=0; x < w; x++) {
for (int y=0; y < h; y++) {
string px = canvas.data[canvas.width*y + x];
currentSurface[cwidth*(y1+y)+(x1+x)] = px;
if (currentSurface != screen) return;
for (int i=0; i < height; i++) {
screenLines[i] = string.Join(null, screen, i*width, width)+"\n";
console.WritePublicText(string.Concat(screenLines));
console.ShowTextureOnScreen();
console.ShowPublicTextOnScreen();
for (int i=0; i < 8; i++) {
for (int j=0; j < cwidth; j+=8) {
Array.Copy(bgDither[i], 0, currentSurface, i*cwidth+j, 8);
int size = cwidth*cheight;
int half = cwidth*cheight >> 1;
for (int i = cwidth*8; i < size; i *= 2) {
Array.Copy(currentSurface, 0, currentSurface, i, copyLength);
public void pixel(int x, int y) {
if (x >= clip[0] && x <= clip[2] && y >= clip[1] && y <= clip[3]) {
currentSurface[cwidth*y + x] = fgDither[y&7][x&7];
private void horizontal(int x1, int x2, int y) {
x1 = Math.Max(Math.Min(x1, x2), clip[0]);
x2 = Math.Min(Math.Max(x1, x2), clip[2]);
string[] ditherRow = fgDither[y&7];
int x1index = cwidth*y + x1;
Array.Copy(ditherRow,start, currentSurface, x1index, 8-start);
Array.Copy(ditherRow,0, currentSurface, x1index+8-start, start);
int after = x1index+length;
int half = x1index+(length>>1);
for (int i = begin; i < after; i *= 2) {
Array.Copy(currentSurface, begin, currentSurface, i, copyLength);
for (int x = x1; x <= x2; x++) {
currentSurface[cwidth*y + x] = ditherRow[x&7];
public void line(int x0, int y0, int x1, int y1) {
rect("fill", x0, y0, 1, y1-y0+1);
rect("fill", x0, y0, x1-x0+1, 1);
int incrementVal, endVal;
if (Math.Abs(shortLen)>Math.Abs(longLen)) {
if (longLen==0) decInc=0;
else decInc = (shortLen << 16) / longLen;
for (int i=0;i-incrementVal!=endVal;i+=incrementVal) {
pixel(x0+(j >> 16),y0+i);
for (int i=0;i-incrementVal!=endVal;i+=incrementVal) {
pixel(x0+i,y0+(j >> 16));
private void flatBottom(int x1, int y1, int x2, int y2, int x3, int y3, bool rotated) {
float invslope1 = (float)(x2 - x1) / (y2 - y1);
float invslope2 = (float)(x3 - x1) / (y3 - y1);
for (int scanlineY = y1; scanlineY <= y2; scanlineY++) {
if (rotated) line(scanlineY, (int)curx1, scanlineY, (int)curx2);
else line((int)curx1, scanlineY, (int)curx2, scanlineY);
private void flatTop(int x1, int y1, int x2, int y2, int x3, int y3, bool rotated) {
float invslope1 = (float)(x3 - x1) / (y3 - y1);
float invslope2 = (float)(x3 - x2) / (y3 - y2);
for (int scanlineY = y3; scanlineY > y1; scanlineY--) {
if (rotated) line(scanlineY, (int)curx1, scanlineY, (int)curx2);
else line((int)curx1, scanlineY, (int)curx2, scanlineY);
private void swap(ref int a, ref int b) {
public void tri(string m, int x1, int y1, int x2, int y2, int x3, int y3) {
} else if (m == "fill") {
if (x1 == x2 || x2 == x3 || x3 == x1) {
if (x2 == x3) flatBottom(y1,x1, y2,x2, y3,x3, true);
else if (x1 == x2) flatTop(y1,x1, y2,x2, y3,x3, true);
flatBottom(x1,y1, x2,y2, x3,y3, false);
flatTop(x1,y1, x2,y2, x3,y3, false);
int x4 = (int)(x1 + ((float)(y2 - y1) / (float)(y3 - y1)) * (x3 - x1));
flatBottom(x1,y1, x2,y2, x4,y2, false);
flatTop(x2,y2, x4,y2, x3,y3, false);
public void rect(string m, int xb, int yb, int w, int h) {
line(xb, yb, xb, yb+h-1);
line(xb, yb, xb+w-1, yb);
line(xb+w-1, yb, xb+w-1, yb+h-1);
line(xb, yb+h-1, xb+w-1, yb+h-1);
} else if (m == "fill") {
for (int x = xb; x < xb+w; x++) {
for (int y = yb; y < yb+h; y++) {
public void ellipse(string m, int cx, int cy, int rx, int ry) {
for (int i=1; i < rx*ry; i++) {
if (ry2*x*x+rx2*y*y <= rxsys) {
} else if (m == "line") {
int s = 2*ry2+rx2*(1-2*ry);
for (int x = 0; ry2*x <= rx2*y; x++) {
s += ry2 * ((4 * x) + 6);
for (int x = rx; rx2*y <= ry2*x; y++) {
s += rx2 * ((4 * y) + 6);
public void circle(string m, int cx, int cy, int r) {
for (int i=1; i < r*r; i++) {
} else if (m == "line") {
do2 += 2 * (y - --x) + 1;
public void print(int x, int y, string text) {
for (int i = 0; i < text.Length; i++) {
short glyph = Ascii.getGlyph(text[i]);
static Graphics g = new Graphics(20, 13, new IMyTextPanel());
public static void Main()