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; | |
} | |
} | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
Question, in the print function at the part: 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. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Nice work!