Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Drawing library for SE v2.4
public class SEFix {
public static T[] arr<T>(params T[] arg) {
return arg; //becuse SE is stupid
}
}
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, 0),
SEFix.arr( 255, 0, 0),
SEFix.arr( 0, 0, 255),
SEFix.arr( 0, 255, 0),
SEFix.arr( 255, 255, 255),
SEFix.arr( 97, 97, 97),
SEFix.arr( 0, 0, 0)
};
private static string[] colorStrings = new string[] {
"\uE004", //oh but it works fine with *strings* -_-
"\uE003",
"\uE002",
"\uE001",
"\uE007\u0458",
"\uE00D",
"\u2014\u0060"
};
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) {
int ratio = 32;
if (c1[0] != c2[0] || c1[1] != c2[1] || c1[2] != c2[2]) {
ratio =
((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));
if (ratio < 0)
ratio = 0;
else if (ratio > 63)
ratio = 63;
}
return ratio;
}
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(
r,g,b,
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,
palette[i], palette[j],
(double)ratio/64.0);
if (penalty < minPenalty) {
minPenalty = penalty;
result[0] = i;
result[1] = j;
result[2] = ratio;
}
}
}
return result;
}
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 ]];
}
}
return dithered;
}
}
public class Ascii {
private static int offset = 0x21;
// binary numbers represent bitmap glyphs, three bits to a line
// eg 9346 == 010 010 010 000 010 == !
// 5265 == 001 010 010 010 001 == (
private static short[] glyphs = SEFix.arr<short>(
9346, 23040, 24445, 15602,
17057, 10923, 9216, 5265,
17556, 21824, 1488, 20,
448, 2, 672, 31599,
11415, 25255, 29326, 23497,
31118, 10666, 29370, 10922,
10954, 1040, 1044, 5393,
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,
25750, 10752, 7, 17408,
239, 18862, 227, 4843,
1395, 14756, 1886, 18861,
8595, 4302, 18805, 25745,
509, 429, 170, 1396,
1369, 228, 1934, 18851,
363, 362, 383, 341,
2766, 3671, 5521, 9234,
17620, 1920
);
public static short getGlyph(char code) {
return glyphs[code-offset];
}
}
public class Graphics {
public readonly int width;
public readonly int height;
private IMyTextPanel console;
private string[] screen;
private string[] screenLines;
private string[][] fgDither;
private string[][] bgDither;
private int[] clip = new int[4];
private Dictionary<string, string[][]> oldPatterns = new Dictionary<string, string[][]>();
public Graphics(int w, int h, IMyTextPanel c) {
width = w;
height = h;
screen = new string[height*width];
screenLines = new string[width*height+height-1];
console = c;
mask();
setFG(255,255,255);
setBG(0,0,0);
}
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) {
clip[0] = x1;
clip[1] = y1;
clip[2] = x2;
clip[3] = y2;
}
public void mask() {
clip[0] = 0;
clip[1] = 0;
clip[2] = width-1;
clip[3] = height-1;
}
public void paint() {
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();
}
public void clear() {
for (int i=0; i < 8; i++) {
for (int j=0; j < width; j+=8) {
Array.Copy(bgDither[i], 0, screen, i*width+j, 8);
}
}
int size = width*height;
int half = width*height >> 1;
for (int i = width*8; i < size; i *= 2) {
int copyLength = i;
if (i > half) {
copyLength = size - i;
}
Array.Copy(screen, 0, screen, i, copyLength);
}
}
public void pixel(int x, int y) {
if (x >= clip[0] && x <= clip[2] && y >= clip[1] && y <= clip[3]) {
screen[width*y + x] = fgDither[y&7][x&7];
}
}
public void line(int x0, int y0, int x1, int y1) {
if (x0 == x1) {
int high = Math.Max(y1,y0);
for (int y = Math.Min(y1,y0); y <= high; y++) {
pixel(x0,y);
}
} else if (y0 == y1) {
int high = Math.Max(x1,x0);
for (int x = Math.Min(x1,x0); x <= high; x++) {
pixel(x,y0);
}
} else {
bool yLonger=false;
int incrementVal, endVal;
int shortLen=y1-y0;
int longLen=x1-x0;
if (Math.Abs(shortLen)>Math.Abs(longLen)) {
int swap=shortLen;
shortLen=longLen;
longLen=swap;
yLonger=true;
}
endVal=longLen;
if (longLen<0) {
incrementVal=-1;
longLen=-longLen;
} else incrementVal=1;
int decInc;
if (longLen==0) decInc=0;
else decInc = (shortLen << 16) / longLen;
int j=0;
if (yLonger) {
for (int i=0;i-incrementVal!=endVal;i+=incrementVal) {
pixel(x0+(j >> 16),y0+i);
j+=decInc;
}
} else {
for (int i=0;i-incrementVal!=endVal;i+=incrementVal) {
pixel(x0+i,y0+(j >> 16));
j+=decInc;
}
}
}
}
private void flatBottom(int x1, int y1, int x2, int y2, int x3, int y3) {
float invslope1 = (float)(x2 - x1) / (y2 - y1);
float invslope2 = (float)(x3 - x1) / (y3 - y1);
float curx1 = x1;
float curx2 = x1;
for (int scanlineY = y1; scanlineY <= y2; scanlineY++) {
line((int)curx1, scanlineY, (int)curx2, scanlineY);
curx1 += invslope1;
curx2 += invslope2;
}
}
private void flatTop(int x1, int y1, int x2, int y2, int x3, int y3) {
float invslope1 = (float)(x3 - x1) / (y3 - y1);
float invslope2 = (float)(x3 - x2) / (y3 - y2);
float curx1 = x3;
float curx2 = x3;
for (int scanlineY = y3; scanlineY > y1; scanlineY--) {
curx1 -= invslope1;
curx2 -= invslope2;
line((int)curx1, scanlineY, (int)curx2, scanlineY);
}
}
private void swap(ref int a, ref int b) {
int c = a;
a = b;
b = c;
}
public void tri(string m, int x1, int y1, int x2, int y2, int x3, int y3) {
if (m == "line") {
line(x1,y1,x2,y2);
line(x2,y2,x3,y3);
line(x3,y3,x1,y1);
} else if (m == "fill") {
if (y1 > y3) {
swap(ref y1, ref y3);
swap(ref x1, ref x3);
}
if (y1 > y2) {
swap(ref y1, ref y2);
swap(ref x1, ref x2);
}
if (y2 > y3) {
swap(ref y2, ref y3);
swap(ref x2, ref x3);
}
if (y2 == y3) {
flatBottom(x1,y1, x2,y2, x3,y3);
} else if (y1 == y2) {
flatTop(x1,y1, x2,y2, x3,y3);
} else {
int x4 = (int)(x1 + ((float)(y2 - y1) / (float)(y3 - y1)) * (x3 - x1));
flatBottom(x1,y1, x2,y2, x4,y2);
flatTop(x2,y2, x4,y2, x3,y3);
}
}
}
public void rect(string m, int xb, int yb, int w, int h) {
if (m == "line") {
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++) {
pixel(x,y);
}
}
}
}
public void ellipse(string m, int cx, int cy, int rx, int ry) {
int rx2 = rx*rx;
int ry2 = ry*ry;
if (m == "fill") {
int rxsys = rx2*ry2;
pixel(cx, cy);
for (int i=1; i < rx*ry; i++) {
int x = i % rx;
int y = i / rx;
if (ry2*x*x+rx2*y*y <= rxsys) {
pixel(cx+x, cy+y);
pixel(cx-x, cy-y);
//if (x && y) { //unnecessary (prevents overdrawing pixels)
pixel(cx+x, cy-y);
pixel(cx-x, cy+y);
//}
}
}
} else if (m == "line") {
int frx2 = 4 * rx2;
int fry2 = 4 * ry2;
int s = 2*ry2+rx2*(1-2*ry);
int y = ry;
for (int x = 0; ry2*x <= rx2*y; x++) {
pixel(cx + x, cy + y);
pixel(cx - x, cy + y);
pixel(cx + x, cy - y);
pixel(cx - x, cy - y);
if (s >= 0) {
s += frx2 * (1 - y);
y--;
}
s += ry2 * ((4 * x) + 6);
}
y = 0;
s = 2*rx2+ry2*(1-2*rx);
for (int x = rx; rx2*y <= ry2*x; y++) {
pixel(cx + x, cy + y);
pixel(cx - x, cy + y);
pixel(cx + x, cy - y);
pixel(cx - x, cy - y);
if (s >= 0) {
s += fry2 * (1 - x);
x--;
}
s += rx2 * ((4 * y) + 6);
}
}
}
public void circle(string m, int cx, int cy, int r) {
if (m == "fill") {
int rr = r*r;
pixel(cx, cy);
for (int i=1; i < r*r; i++) {
int x = i % r;
int y = i / r;
if (x*x+y*y < rr) {
pixel(cx+x, cy+y);
pixel(cx-x, cy-y);
if (x>0 && y>0) {
pixel(cx+x, cy-y);
pixel(cx-x, cy+y);
}
}
}
} else if (m == "line") {
int x = r;
int y = 0;
int do2 = 1 - x;
while (y <= x) {
pixel(cx+x, cy+y);
pixel(cx+y, cy+x);
pixel(cx-x, cy+y);
pixel(cx-y, cy+x);
pixel(cx-x, cy-y);
pixel(cx-y, cy-x);
pixel(cx+x, cy-y);
pixel(cx+y, cy-x);
y++;
if (do2 <= 0) {
do2 += 2 * y + 1;
} else {
do2 += 2 * (y - --x) + 1;
}
}
}
}
public void print(int x, int y, string text) {
int x1 = x;
int y1 = y;
for (int i = 0; i < text.Length; i++) {
switch(text[i]) {
case '\n':
y1 += 6;
x1 = x;
break;
case ' ':
x1 += 4;
break;
default:
short glyph = Ascii.getGlyph(text[i]);
int j = 14;
do {
if ((glyph & 1) != 0) {
pixel(x1+j%3, y1-4+j/3);
}
glyph >>= 1;
j--;
} while (glyph > 0);
x1 += 4;
break;
}
}
}
}
@laftho

This comment has been minimized.

Copy link

@laftho laftho commented Feb 10, 2016

Nice work!

@Thodor12

This comment has been minimized.

Copy link

@Thodor12 Thodor12 commented Mar 7, 2019

Question, in the print function at the part:
if ((glyph & 1) != 0) { pixel(x1+j%3, y1-4+j/3); }

Is the -4 necessary. Whenever I want to try to print something at 0,0 it only shows the bottom pixel, the other 4 are off the screen.
When I removed this part it worked just fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment