interface syntax - unclear use of this - c#

I couldn't find any explanation for the line in a red rectangle, could anyone help me breaking it down?

The given interface defines an override for the indexer operator ([]). A simple example of use:
class Point
{
public int X { get; set; }
public int Y { get; set; }
}
class PointCollection
{
public List<Point> collection { get; set; }
public Point this[int x, int y]
{
get => collection.FirstOrDefault(item => item.X == x && item.Y == y);
}
}
And then:
PointCollection points = new PointCollection();
var item = points[100, 200];

Hope this answers to your question
public class MyImage : IImage
{
private IColor[,] imageData = null;
int w=0, h = 0;
public MyImage(int width, int height, IColor defaultColor)
{
//throw new NotImplementedException();
imageData = new IColor[width, height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
imageData[i, j] = defaultColor;
}
}
}
public IColor this[int x, int y]
{
get
{
w = imageData.GetLength(0);
h = imageData.GetLength(1);
//throw new NotImplementedException();
if ((w > 0 && w > x) && (h > 0 && h > y))
return imageData[x, y];
else
return null;
}
set
{
//throw new NotImplementedException();
if ((w > 0 && w > x) && (h > 0 && h > y))
imageData[x, y] = value;
}
}
}
public interface IColor
{
}
public interface IImage
{
IColor this[int x, int y] { get; set; }
}

Related

Is there there such a thing as a negative index graph? Index was outside the bounds of the array

I have my unity project set up with a graph like this:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj is Point)
{
Point p = obj as Point;
return this.X == p.X && this.Y == p.Y;
}
return false;
}
public override int GetHashCode()
{
unchecked
{
int hash = 6949;
hash = hash * 7907 + X.GetHashCode();
hash = hash * 7907 + Y.GetHashCode();
return hash;
}
}
public override string ToString()
{
return "P(" + this.X + ", " + this.Y + ")";
}
}
public enum CellType
{
Empty,
Road,
Structure,
SpecialStructure,
None
}
public class Grid
{
public CellType[,] _grid;
private int _width;
public int Width { get { return _width; } }
private int _height;
public int Height { get { return _height; } }
public List<Point> _roadList = new List<Point>();
public List<Point> _specialStructure = new List<Point>();
public List<Point> _houseStructure = new List<Point>();
public Grid(int width, int height)
{
_width = width;
_height = height;
_grid = new CellType[width, height];
}
// Adding index operator to our Grid class so that we can use grid[][] to access specific cell from our grid.
public CellType this[int i, int j]
{
get
{
return _grid[i, j];
}
set
{
if (value == CellType.Road)
{
_roadList.Add(new Point(i, j));
}
if (value == CellType.SpecialStructure)
{
_specialStructure.Add(new Point(i, j));
}
if (value == CellType.Structure)
{
_houseStructure.Add(new Point(i, j));
}
_grid[i, j] = value;
}
}
And I'm instantiating a series of gameobjects using an L-System algorithm and adding their X and Z positions to the graph like this:
public Grid AddToGrid(Vector3Int position, string type)
{
//CellType parsed_enum = (CellType)System.Enum.Parse(typeof(CellType), type);
switch (type)
{
case "Structure":
placementGrid._houseStructure.Add(new Point((int)position.x, (int)position.z));
break;
case "SpecialStructure":
placementGrid._specialStructure.Add(new Point((int)position.x, (int)position.z));
break;
case "Road":
placementGrid._roadList.Add(new Point((int)position.x, (int)position.z));
break;
}
return placementGrid;
}
And then in one of my scripts I'm calling another function:
public List<Point> GetWakableAdjacentCells(int x, int y, bool isAgent)
{
List<Point> adjacentCells = GetAllAdjacentCells(x, y);
Debug.Log("Adjacent Cells"+ adjacentCells.Count);//3
Debug.Log("X"+x); //-1
Debug.Log("Y"+y);//1
for (int i = adjacentCells.Count - 1; i >= 0; i--)
{
Debug.Log(adjacentCells[i].X); //-1
Debug.Log(adjacentCells[i].Y);//2
if (IsCellWakable(_grid[adjacentCells[i].X, adjacentCells[i].Y], isAgent) == false)
{
adjacentCells.RemoveAt(i);
}
}
return adjacentCells;
}
But this says "index was outside the bounds of the array" at if condition. I've commented each relevant value next to the variables for ease.
The function this condition is checking is this:
public static bool IsCellWakable(CellType cellType, bool aiAgent = false)
{
Debug.Log("boo");
if (aiAgent)
{
return cellType == CellType.Road;
}
return cellType == CellType.Empty || cellType == CellType.Road;
}
What am I doing wrong?
is there a way to implement a grid in such a way way that minus values can be accessed?
Well you commented yourself
// -1
-> -1 can not be a valid index in c#.
If you want to have a wrap around you could probably do e.g.
public CellType this[int i, int j]
{
get
{
i = (i % _width) + _width) % _width;
j = (j % _height) + _height) % _height;
return _grid[i, j];
}
set
{
i = (i % _width) + _width) % _width;
j = (j % _height) + _height) % _height;
switch(value)
{
case CellType.Road:
_roadList.Add(new Point(i, j));
break;
case CellType.SpecialStructure:
_specialStructure.Add(new Point(i, j));
break;
case CellType.Structure:
_houseStructure.Add(new Point(i, j));
break;
}
_grid[i, j] = value;
}
}
This means of course that your grid is basically "infinite" and crossing the top boarder you will be at the bottom again etc.

How do I find the location where my grid is instantiated?

I began setting up my system by taking the Grid system supplied by Pack publishing (). This is how it's initialized:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj is Point)
{
Point p = obj as Point;
return this.X == p.X && this.Y == p.Y;
}
return false;
}
public override int GetHashCode()
{
unchecked
{
int hash = 6949;
hash = hash * 7907 + X.GetHashCode();
hash = hash * 7907 + Y.GetHashCode();
return hash;
}
}
public override string ToString()
{
return "P(" + this.X + ", " + this.Y + ")";
}
}
public enum CellType
{
Empty,
Road,
Structure,
SpecialStructure,
None
}
public class Grid
{
private CellType[,] _grid;
private int _width;
public int Width { get { return _width; } }
private int _height;
public int Height { get { return _height; } }
private List<Point> _roadList = new List<Point>();
private List<Point> _specialStructure = new List<Point>();
private List<Point> _houseStructure = new List<Point>();
public Grid(int width, int height)
{
_width = width;
_height = height;
_grid = new CellType[width, height];
}
public enum CellType
{
Empty,
Road,
Structure,
SpecialStructure,
None
}
public class Grid
{
private CellType[,] _grid;
private int _width;
public int Width { get { return _width; } }
private int _height;
public int Height { get { return _height; } }
private List<Point> _roadList = new List<Point>();
private List<Point> _specialStructure = new List<Point>();
private List<Point> _houseStructure = new List<Point>();
public Grid(int width, int height)
{
_width = width;
_height = height;
_grid = new CellType[width, height];
}
public void Update()
{
Console.Write("GRID IS RUNNING");
}
// Adding index operator to our Grid class so that we can use grid[][] to access specific cell from our grid.
public CellType this[int i, int j]
{
get
{
return _grid[i, j];
}
set
{
if (value == CellType.Road)
{
_roadList.Add(new Point(i, j));
}
if (value == CellType.SpecialStructure)
{
_specialStructure.Add(new Point(i, j));
}
if (value == CellType.Structure)
{
_houseStructure.Add(new Point(i, j));
}
_grid[i, j] = value;
}
}
After I initialized it, I called it from another function like this.
public int width, height;
Grid placementGrid;
private void Start()
{
placementGrid = new Grid(width, height);
Debug.Log(placementGrid.GetRandomRoadPoint());
}
It seems to initialize perfectly. But when I call the functions inside it, I'm returned a null value. I tried calling this function for example:
public Point GetRandomRoadPoint()
{
if (_roadList.Count == 0)
{
return null;
}
return _roadList[UnityEngine.Random.Range(0, _roadList.Count)];
}
I have no idea what I'm doing wrong. I'm basically trying to create a pedestrian AI system for my procedurally generated town.
I tried debugging several times, but I can't seem to find my way around it.

Console game to Winforms C#

I have done a console game called "Labyrinth", now I want to bring it to Windows Forms, but I have some problems and exceptions and I can't understand how to make it work.
So, here is functional for printing field in console:
Base class Cell(it has children, like Wall, Door,Key,Empty, Player)
abstract class Cell
{
protected ConsoleColor foregroundColor;
protected ConsoleColor backgroundColor;
protected char symbol;
public Cell(ConsoleColor foregroundColor = ConsoleColor.White,
ConsoleColor backgroundColor = ConsoleColor.Black,
char symbol = ' ')
{
this.foregroundColor = foregroundColor;
this.backgroundColor = backgroundColor;
this.symbol = symbol;
}
public virtual void Draw()
{
Console.ForegroundColor = foregroundColor;
Console.BackgroundColor = backgroundColor;
Console.Write(symbol);
}
}
Here is class FileStorer that works with file(reads level from .txt file) and fills the array string[] field
class FileStorer
{
private string path;
public string[] Result;
public FileStorer(string path)
{
this.path = path;
Result = new string[0];
ReadFile();
}
private void Add(string item)
{
string[] newStr = new string[Result.Length + 1];
for (int i = 0; i < Result.Length; i++)
{
newStr[i] = Result[i];
}
newStr[Result.Length] = item;
Result = newStr;
}
public void ReadFile()
{
using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default))
{
string line;
while ((line = sr.ReadLine()) != null)
{
Add(line);
}
}
}
}
Class Field
class Field
{
private bool toggle_key = false;
private Cell[,] field;
public int Width
{
get { return field.GetLength(1); }
}
public int Height
{
get { return field.GetLength(0); }
}
public Field(int width, int height)
{
field = new Cell[height, width];
}
public Cell this[int x, int y]
{
get { return field[y, x]; }
set { field[y, x] = value; }
}
public void Print()
{
Console.SetCursorPosition(0, 0);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
this[x, y].Draw();
}
Console.WriteLine();
}
}
public bool NotOutOfField(int x, int y)
{
return x >= 0 && x < Width && y >= 0 && y < Height;
}
public void MakeEmpty(int x, int y)
{
this[x, y] = new Empty();
}
public bool CanMakeStep(int x, int y)
{
if (NotOutOfField(x, y) && this[x, y] is Empty)
return true;
if (NotOutOfField(x, y) && this[x, y] is Key && !toggle_key)
{
toggle_key = true;
Key.count--;
return true;
}
if (NotOutOfField(x, y) && this[x, y] is Door && toggle_key)
{
toggle_key = false;
Door.count--;
return true;
}
return false;
}
}
Here is class LevelLoader that fills array Cell[,] with proper classes (Door,Key etc.)
class LevelLoader
{
private string[] field;
public LevelLoader(int level)
{
field = new FileStorer($"../../Levels/{level}.txt").Result;
}
public void LoadField(out Field cellField, out Player player)
{
cellField = new Field(field[0].Length, field.Length);
player = new Player(0, 0);
for (int y = 0; y < field.Length; y++)
{
for (int x = 0; x < field[y].Length; x++)
{
switch (field[y][x])
{
case '#':
cellField[x, y] = new Wall();
break;
case ' ':
cellField[x, y] = new Empty();
break;
case 'K':
cellField[x, y] = new Key();
break;
case 'D':
cellField[x, y] = new Door();
break;
case 'P':
player = new Player(x, y);
goto case ' ';
default:
break;
}
}
}
}
}
And finally in class Level I have methods that puts characters on field and prints them
private void PutCharactersOnField()
{
for (int y = 0; y < gameField.Height; y++)
{
for (int x = 0; x < gameField.Width; x++)
{
if (!(gameField[x, y] is Wall) && !((gameField[x, y] is Key)) && !((gameField[x, y] is Door)))
{
gameField[x, y] = new Empty();
}
}
}
gameField[player.X, player.Y] = player;
}
public void PrintLevel()
{
gameField.Print();
player.Draw();
LivesMessage();
if (Door.count == 0)
{
Win();
};
}
I have done some changes for winforms, and I got IndexOutOfRanfeExceprion, I can't understand what went wrong, so here are the same classes and functions but changed for Winforms.
Class Cell
public abstract class Cell
{
public string Path;
public Image Image;
public Cell(string path)
{
Path = path;
if (!File.Exists(Path))
{
throw new Exception("File does not exist: " + Path);
}
Image = new Bitmap(Path);
}
public virtual void Draw(Graphics gr, int j, int i, int side, int x, int y)
{
gr.DrawImage(this.Image, j * side + x, i * side + y, new Rectangle(new Point(0, 0), new Size(side, side)), GraphicsUnit.Pixel);
}
}
FileStorer wasn't modified.
Class Player
public class Player:Cell
{
public string path;
protected Direction direction;
public Image SpriteAnimation;
public int X, Y;
public Size Scale;
public int CurrentFrame = 2;
public int CurrentAnimation = 5;
public Image Part;
public int speed;
public Direction CurrentDirection
{
get { return direction; }
}
public Player() : base("D:\\GIT\\maze-game\\MazeGame\\MazeGame\\Resources\\sprite.png") { }
public Player(Size scale, int x, int y, Image spriteAnimation):base("D:\\GIT\\maze-game\\MazeGame\\MazeGame\\Resources\\sprite.png")
{
SpriteAnimation = spriteAnimation;
Scale = scale;
X = x;
Y = y;
speed = 2;
if (!File.Exists(Path))
{
throw new Exception("File does not exist: " + Path);
}
base.Image = new Bitmap(Path);
}
public Player(int x, int y) : base("D:\\GIT\\maze-game\\MazeGame\\MazeGame\\Resources\\sprite.png")
{
X = x;
Y = y;
}
public void Action()
{
switch (direction)
{
case Direction.Left:
CurrentAnimation = 1;
break;
case Direction.Up:
CurrentAnimation = 3;
break;
case Direction.Right:
CurrentAnimation = 0;
break;
case Direction.Down:
CurrentAnimation = 2;
break;
}
}
public void Static()
{
switch (direction)
{
case Direction.Left:
CurrentAnimation = 6;
break;
case Direction.Up:
CurrentAnimation = 8;
break;
case Direction.Right:
CurrentAnimation = 5;
break;
case Direction.Down:
CurrentAnimation = 7;
break;
}
}
public void Rotate(int dx, int dy)
{
if (dx == -speed)
direction = Direction.Left;
else if (dx == speed)
direction = Direction.Right;
else if (dy == -speed)
direction = Direction.Up;
else if (dy == speed)
direction = Direction.Down;
}
public void MakeStep(int dx, int dy)
{
X += dx;
Y += dy;
}
public void DrawAction(Graphics gr, int x, int y)
{
gr.DrawImage(SpriteAnimation, X + x, Y + y, new Rectangle(new Point(75 * CurrentFrame, 170 * CurrentAnimation), new Size(75, 170)), GraphicsUnit.Pixel);
}
public void DrawStatic(Graphics gr, int x, int y)
{
gr.DrawImage(SpriteAnimation, X + x, Y + y, new Rectangle(new Point(75 * CurrentFrame, 170 * (CurrentAnimation - 5)), new Size(75, 170)), GraphicsUnit.Pixel);
}
}
Class LevelLoader was modified juct for player cell
case 'P':
player = new Player(new Size(35, 75), 100, 100,player.Image);
goto case ' ';
Class Field
class Field
{
private bool toggle_key = false;
private readonly Cell[,] field;
public int Width
{
get { return field.GetLength(1); }
}
public int Height
{
get { return field.GetLength(0); }
}
public Field(int width, int height)
{
field = new Cell[height, width];
}
public Cell this[int x, int y]
{
get { return field[y, x]; }
set { field[y, x] = value; }
}
public void Print(Graphics gr, int side, int j, int i)
{
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
this[x, y].Draw(gr, x, y,side, j, i);
}
}
}
public bool NotOutOfField(int x, int y)
{
return x >= 0 && x < Width && y >= 0 && y < Height;
}
public void MakeEmpty(int x, int y)
{
this[x, y] = new Empty();
}
public bool CanMakeStep(int x, int y)
{
if (NotOutOfField(x, y) && this[x, y] is Empty)
return true;
if (NotOutOfField(x, y) && this[x, y] is Key && !toggle_key)
{
toggle_key = true;
Key.count--;
return true;
}
if (NotOutOfField(x, y) && this[x, y] is Door && toggle_key)
{
toggle_key = false;
Door.count--;
return true;
}
return false;
}
}
Here are Level class method PrintLevel,(PutCharactersOnField wasn't modified)
public void PrintLevel(Graphics gr)
{
gameField.Print(gr, mapSide,point.X, point.Y);
player.DrawStatic(gr, point.X,point.Y);
if (Door.count == 0)
{
Win();
};
}
I'm very new in this topic, but I want to deal with this issue.So, I need help, what is wrong, where I made a mistake?
**Exception was called in the stack of calls

Limit array to specific type - custom Texture Class C#?

I am trying to create a custom texture class, and I want to be able to choose if I the texture is 8bit or float a.k.a I want to be able to set _buffer to be either RGBFloat[] or RGBByte[] but I cant figure out how to do it with out making the whole class generic but if I do it generic it will be possible to set other object types other then just the ones that I want to limit it to. Maybe its possible to use inheritance or interface in some way, I don't know.
Example C# Code: (the places with T are not correct, just placeholder)
public struct RGBFloat
{
public float R { get; }
public float G { get; }
public float B { get; }
public RGBFloat(float r, float g, float b)
{
R = r;
G = g;
B = b;
}
}
public struct RGBByte
{
public byte R { get; }
public byte G { get; }
public byte B { get; }
public RGBByte(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
}
public enum TextureFormat
{
RGBFloat,
RGBByte
}
public class Texture2D
{
public int Width { get; }
public int Height { get; }
private T[] _buffer; // want it to be able to be either RGBFloat[] or RGBByte[] but nothing else.
public Texture2D(int width, int height, TextureFormat textureFormat)
{
Width = width;
Height = height;
switch (textureFormat)
{
case TextureFormat.RGBFloat:
_buffer = new RGBFloat[width * height];
break;
default:
_buffer = new RGBByte[width * height];
break;
}
}
public T this[int i]
{
get { return (_buffer[i]); }
set { _buffer[i] = value; }
}
public T this[int x, int y]
{
get { return (_buffer[Width * y + x]); }
set { _buffer[Width * y + x] = value; }
}
}
Example of a working generic class I have created that is similar to my TextureClass but this one is meant to be able to support any object type.
public class Computebuffer<T>
{
public int Count { get; }
private T[] _buffer;
public Computebuffer(int count)
{
Count = count;
_buffer = new T[count];
}
public T this[int i]
{
get { return (_buffer[i]); }
set { _buffer[i] = value; }
}
public void Initialize(T value)
{
for (int i = 0; i < Count; i++)
{
_buffer[i] = value;
}
}
public void SetData(T[] data)
{
for (int i = 0; i < data.Length; i++)
{
_buffer[i] = data[i];
}
}
public void GetData(T[] data)
{
for (int i = 0; i < data.Length; i++)
{
data[i] = _buffer[i];
}
}
public void Clear()
{
_buffer = new T[Count];
}
}
EDIT 1: (Updated version, almost working):
Issue: With this code I am able to add a RGBFloat to a Texture2D and that doesn't make sense. (see main program)
public interface IRGBType { }
public struct RGBFloat : IRGBType
{
public float R { get; }
public float G { get; }
public float B { get; }
public RGBFloat(float r, float g, float b)
{
R = r;
G = g;
B = b;
}
}
public struct RGBByte : IRGBType
{
public byte R { get; }
public byte G { get; }
public byte B { get; }
public RGBByte(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
}
public class Texture2D<T> where T : struct, IRGBType
{
public int Width { get; }
public int Height { get; }
private IRGBType[] _buffer;
public Texture2D(int width, int height)
{
Width = width;
Height = height;
_buffer = new IRGBType[width * height];
Initialize();
}
private void Initialize()
{
Type type = this.GetType();
if (type == typeof(RGBByte))
{
for (int i = 0; i < Width * Height; i++)
{
_buffer[i] = new RGBByte(0, 0, 0);
}
}
else
{
for (int i = 0; i < Width * Height; i++)
{
_buffer[i] = new RGBFloat(0, 0, 0);
}
}
}
public IRGBType this[int i]
{
get { return (_buffer[i]); }
set { _buffer[i] = value; }
}
public IRGBType this[int x, int y]
{
get { return (_buffer[Width * y + x]); }
set { _buffer[Width * y + x] = value; }
}
}
MainProgram:
Texture2D<RGBFloat> texFloat = new Texture2D<RGBFloat>(64, 64);
Texture2D<RGBByte> texByte = new Texture2D<RGBByte>(64, 64);
texFloat[32, 32] = new RGBFloat(10, 50, 60);
texByte[32, 32] = new RGBFloat(0.9f, 0.24f, 0.5f); // this should not work I should not be able to add a RGBFloat to a RGBByte Texture2D
EDIT 2: (Updated version v3) still one issue, how would I Initialize _buffer array with an optional value, so new Texture2D<RGBFloat>(64, 64) should Initialize with new RGBFloat(0,0,0) and Texture2D<RGBFloat>(64, 64, new RGBFloat(0.5f,0.6f,0.7f)) should Initialize with value provided:
public class Texture2D<T> where T : struct, IRGBType
{
public int Width { get; }
public int Height { get; }
private T[] _buffer;
public Texture2D(int width, int height, T? initializeValue = null)
{
Width = width;
Height = height;
_buffer = new T[width * height];
Initialize(initializeValue);
}
private void Initialize(T? value)
{
Type type = this.GetType();
T rgb;
if (type == typeof(RGBFloat))
{
rgb = value ?? new RGBFloat(0, 0, 0); // Error: Operator '??' cannot be applied to operands of type 'T' and 'RGBFloat'
}
else
{
rgb = value ?? new RGBByte(0, 0, 0); // Error: Operator '??' cannot be applied to operands of type 'T' and 'RGBByte'
}
for(int i = 0; i < Width*Height; i++)
{
_buffer[i] = rgb;
}
}
public T this[int i]
{
get { return (_buffer[i]); }
set { _buffer[i] = value; }
}
public T this[int x, int y]
{
get { return (_buffer[Width * y + x]); }
set { _buffer[Width * y + x] = value; }
}
}
EDIT 3: The full final version that now works correctly:
public interface IRGBType { }
public struct RGBFloat : IRGBType
{
public float R { get; }
public float G { get; }
public float B { get; }
public RGBFloat(float r, float g, float b)
{
R = r;
G = g;
B = b;
}
}
public struct RGBByte : IRGBType
{
public byte R { get; }
public byte G { get; }
public byte B { get; }
public RGBByte(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
}
public class Texture2D<T> where T : struct, IRGBType
{
public int Width { get; }
public int Height { get; }
private T[] _buffer;
public Texture2D(int width, int height, T? initializeValue = null)
{
Width = width;
Height = height;
_buffer = new T[width * height];
if (initializeValue.HasValue)
{
for (int i = 0; i < Width * Height; i++)
{
_buffer[i] = initializeValue.Value;
}
};
}
public T this[int i]
{
get { return (_buffer[i]); }
set { _buffer[i] = value; }
}
public T this[int x, int y]
{
get { return (_buffer[Width * y + x]); }
set { _buffer[Width * y + x] = value; }
}
}
}
You can create an interface that your RGBFloat and RGBByte structs implement, and limit the generic type to that interface. The interface can just be a "marker", with nothing in it:
public interface IRGBType { }
public struct RGBFloat : IRGBType
{
public float R { get; }
public float G { get; }
public float B { get; }
public RGBFloat(float r, float g, float b)
{
R = r;
G = g;
B = b;
}
}
public struct RGBByte : IRGBType
{
public byte R { get; }
public byte G { get; }
public byte B { get; }
public RGBByte(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
}
You can then have your generic class only accept types that implement this interface:
public class Texture2D<T> where T: IRGBType
{
...
}
If you know that you'll always have this interface implemented by a struct, you can further restrict the generic:
public class Texture2D<T> where T: struct, IRGBType
{
...
}
EDIT: Make sure to use the generic T parameter everywhere in your class, not the interface. This is necessary to ensure type safety, otherwise your indexers can accept any IRGBType, which can be very problematic. Correct usage:
public class Texture2D<T> where T: struct, IRGBType
{
private T[] _buffer;
public Texture2D(int width, int height)
{
...
_buffer = new T[width * height];
...
}
public T this[int i]
{
get => _buffer[i];
set => _buffer[i] = value;
}
}
You can try using only one generic struct like this:
public struct RGB<T>
where T : struct,
IComparable, IFormattable, IConvertible,
IComparable<T>, IEquatable<T>
{
public T R { get; }
public T G { get; }
public T B { get; }
public RGB(T r, T g, T b)
{
R = r;
G = g;
B = b;
}
}
public class RGBComputeBuffer<T>
where T : struct,
IComparable, IFormattable, IConvertible,
IComparable<T>, IEquatable<T>
{
public int Count { get; private set; }
private RGB<T>[] _buffer;
public RGBComputeBuffer(int count)
{
_buffer = new RGB<T>[count];
Count = count;
}
public RGB<T> this[int i]
{
get { return _buffer[i]; }
set { if ( !_buffer[i].Equals(value) ) _buffer[i] = value; }
}
public void Initialize(RGB<T> value)
{
for ( int i = 0; i < Count; i++ )
_buffer[i] = value;
}
public void SetData(RGB<T>[] data)
{
Array.Resize(ref _buffer, data.Length);
Count = data.Length;
for ( int i = 0; i < data.Length; i++ )
_buffer[i] = data[i];
}
public void GetData(RGB<T>[] data)
{
Array.Resize(ref data, _buffer.Length);
for ( int i = 0; i < data.Length; i++ )
data[i] = _buffer[i];
}
public void Clear()
{
_buffer = new RGB<T>[Count];
}
}
Example of usage:
var bufferOfRGBByte = new RGBCompositeBuffer<byte>(100);
var bufferOfRGBFloat = new RGBCompositeBuffer<float>(100);
An interface would be your best bet. By having RGBFloat and RGBByte implement an interface then you can use that interface type in your array. It will also allow adding other RGB types later.
public interface IRGB
{
}
public struct RGBFloat : IRGB
{
public float R { get; }
public float G { get; }
public float B { get; }
public RGBFloat(float r, float g, float b)
{
R = r;
G = g;
B = b;
}
}
public struct RGBByte : IRGB
{
public byte R { get; }
public byte G { get; }
public byte B { get; }
public RGBByte(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
}
public enum TextureFormat
{
RGBFloat,
RGBByte
}
public class Texture2D
{
public int Width { get; }
public int Height { get; }
private IRGB[] _buffer; // want it to be able to be either RGBFloat[] or RGBByte[] but nothing else.
public Texture2D(int width, int height, TextureFormat textureFormat)
{
Width = width;
Height = height;
switch (textureFormat)
{
case TextureFormat.RGBFloat:
_buffer = new IRGB[width * height];
break;
default:
_buffer = new IRGB[width * height];
break;
}
}
public IRGB this[int i]
{
get { return (_buffer[i]); }
set { _buffer[i] = value; }
}
public IRGB this[int x, int y]
{
get { return (_buffer[Width * y + x]); }
set { _buffer[Width * y + x] = value; }
}
}
If you want to access the R,G,B variables however you may need to add some kind of accessor methods/properties to the interface.

K-means with initial centers

I'm doing K-means clustering of n points and k centers.
To start off, here's my code so far:
A class for a point:
public class Point
{
public int Id { get; set; }
public double X { get; set; }
public double Y { get; set; }
public Point()
{
Id = -1;
X = -1;
Y = -1;
}
public Point(int id, double x, double y)
{
this.Id = id;
this.X = x;
this.Y = y;
}
public static double FindDistance(Point pt1, Point pt2)
{
double x1 = pt1.X, y1 = pt1.Y;
double x2 = pt2.X, y2 = pt2.Y;
double distance = Math.Sqrt(Math.Pow(x2 - x1, 2.0) + Math.Pow(y2 - y1, 2.0));
return (distance);
}
}
A class for data:
public class PointCollection : List<Point>
{
public Point Centroid { get; set; }
public PointCollection()
: base()
{
Centroid = new Point();
}
public void AddPoint(Point p)
{
this.Add(p);
UpdateCentroid();
}
public Point RemovePoint(Point p)
{
Point removedPoint = new Point(p.Id, p.X, p.Y);
this.Remove(p);
UpdateCentroid();
return (removedPoint);
}
public void UpdateCentroid()
{
double xSum = (from p in this select p.X).Sum();
double ySum = (from p in this select p.Y).Sum();
Centroid.X = (xSum / (double)this.Count);
Centroid.Y = (ySum / (double)this.Count);
}
}
Main class:
public class KMeans
{
public static List<PointCollection> DoKMeans(PointCollection points, int clusterCount)
{
List<PointCollection> allClusters = new List<PointCollection>();
List<List<Point>> allGroups = ListUtility.SplitList<Point>(points, clusterCount);
foreach (List<Point> group in allGroups)
{
PointCollection cluster = new PointCollection();
cluster.AddRange(group);
allClusters.Add(cluster);
}
int movements = 1;
while (movements > 0)
{
movements = 0;
foreach (PointCollection cluster in allClusters)
{
for (int pointIndex = 0; pointIndex < cluster.Count; pointIndex++)
{
Point point = cluster[pointIndex];
int nearestCluster = FindNearestCluster(allClusters, point);
if (nearestCluster != allClusters.IndexOf(cluster))
{
if (cluster.Count > 1)
{
Point removedPoint = cluster.RemovePoint(point);
allClusters[nearestCluster].AddPoint(removedPoint);
movements += 1;
}
}
}
}
}
return (allClusters);
}
public static int FindNearestCluster(List<PointCollection> allClusters, Point point)
{
double minimumDistance = 0.0;
int nearestClusterIndex = -1;
for (int k = 0; k < allClusters.Count; k++)
{
double distance = Point.FindDistance(point, allClusters[k].Centroid);
if (k == 0)
{
minimumDistance = distance;
nearestClusterIndex = 0;
}
else if (minimumDistance > distance)
{
minimumDistance = distance;
nearestClusterIndex = k;
}
}
return (nearestClusterIndex);
}
}
And finally helping function for list split:
public static List<List<T>> SplitList<T>(List<T> items, int groupCount)
{
List<List<T>> allGroups = new List<List<T>>();
int startIndex = 0;
int groupLength = (int)Math.Round((double)items.Count / (double)groupCount, 0);
while (startIndex < items.Count)
{
List<T> group = new List<T>();
group.AddRange(items.GetRange(startIndex, groupLength));
startIndex += groupLength;
if (startIndex + groupLength > items.Count)
{
groupLength = items.Count - startIndex;
}
allGroups.Add(group);
}
if (allGroups.Count > groupCount && allGroups.Count > 2)
{
allGroups[allGroups.Count - 2].AddRange(allGroups.Last());
allGroups.RemoveAt(allGroups.Count - 1);
}
return (allGroups);
}
So, now I'm trying to write a second method for the main class, which would accept a predefined starting centres. I have trouble grasping that though, as I can't find anything on the internet where the k-means algorithm would use initial centers. Can someone point me in a direction of such guide or give me any ideas how to modify the code? Thanks.
Edit: Maybe something more why am I trying to do this: I try to write LBG algorithm using k-means, like so https://onlinecourses.science.psu.edu/stat557/node/67
I can access the computed centers for splitting in each of the steps with my code, however I need to find a way to feed them back to the k-means class. Like, if I calculated the starting centre, i need to put this centre and another offset by epsilon into k-means algorithm.
Edit2: Code now in English(I hope)
Found a solution, maybe someone will use:
public static List<PointCollection> DoKMeans(PointCollection points, int clusterCount, Point[] startingCentres)
{
// code...
int ctr = 0;
foreach (List<Point> group in allGroups)
{
PointCollection cluster = new PointCollection();
cluster.c.X = startingCentres[ctr].X;
cluster.c.Y = startingCentres[ctr].Y;
cluster.AddRange(group);
allClusters.Add(cluster);
}
// rest of code the same
}

Categories

Resources