I have a problem. I have a few hexagons drawn in SkiaSharp. All the corners are stored in a List<HexagonRegistryList>.
Now I want to know how many shared edges the a hexagon has with the rest of the hexagons, so I created this code:
public class HexagonRegistryList
{
public int HexagonNum { get; set; }
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public float x4 { get; set; }
public float y4 { get; set; }
public float x5 { get; set; }
public float y5 { get; set; }
public float x6 { get; set; }
public float y6 { get; set; }
public int ShapeNum { get; set; }
public HexagonRegistryList()
{
this.AdjacentShapeNumbers = new List<int>();
}
public List<int> AdjacentShapeNumbers { get; set; }
public IEnumerable<(float x, float y)> GetPoints()
{
yield return (x1, y1);
yield return (x2, y2);
yield return (x3, y3);
yield return (x4, y4);
yield return (x5, y5);
yield return (x6, y6);
}
public bool IsAdjacentTo(HexagonRegistryList other)
{
var isAdjacentTo = GetPoints().Intersect(other.GetPoints()).Count() >= 2;
if (isAdjacentTo)
{
if (other.ShapeNum != 0)
{
AdjacentShapeNumbers.Add(other.ShapeNum);
}
}
return isAdjacentTo;
}
}
Now this code works, except for points that are not exactly the same but have a difference of 0.001 pixel. So I want to change this to a comparison function which checks if the point of the other corner is closer then 1 pixel away, because that should also be allowed. Can someone help me with this.
I already tried this code, but that just ignores it and still returns false:
public struct PointComparer : IEqualityComparer<(float x, float y)>
{
public bool Equals((float x, float y) p1, (float x, float y) p2)
{
return Math.Abs(p1.x - p2.x) < 1f && Math.Abs(p1.y - p2.y) < 1f;
}
public int GetHashCode((float x, float y) obj)
{
return obj.GetHashCode();
}
}
then I changed this line:
var isAdjacentTo = GetPoints().Intersect(other.GetPoints()).Count() >= 2;
to:
var isAdjacentTo = GetPoints().Intersect(other.GetPoints(), new PointComparer()).Count() >= 2;
But like I already told u, that doesn't work...
Can someone tell me how to make this work?
Since the hexagons all have the same rotation degree and same size you can simply the problem quite a lot.
The main idea is to calculate the distance between two hexagons' center. If the distance between their center point is 2x radius ± 1pixel (or whatever epsilon you wish) then they "might" share one edge.
Now take the vector between the two center point and calculate the angle of the vector. If (angle - 30) % 60 == 0 then they share one edge.
Special case: if two hexagons completely overlap then they will have the same center point
This way you only have to do two comparison with every other hexagons instead of 6x6 comparisons if you do the edge compare way.
Implementation should be quite trivial. The only tricky part would be to apply math formula to find the hexagon's center point. So I will leave it as exercise.
Edit:
Since its a hexagon grid, you got multiple ways of solving this. The center point way still applies. As long as the difference between the center points is less than 2x radius + 1pixel they share one edge. Or you can calculate the distance between two line segments. If the distance between any edge a in hexagon A and any edge b in hexagon B is less than the radius then those two hexagon shares an edge.
Related
This is a code for MonoGame 3.8.1 and .NET 6.
public interface IEntity
{
int Size { get; }
Vector2 Position { get; }
}
public class Player : IEntity
{
public int Size { get; } = 32;
public Vector2 Position { get; private set; }
public Player(Scene scene)
{
var spawnOffset = new Vector2(0, -3);
Position = new Vector2(
spawnOffset.X * scene.TileSize + scene.Room.Center.x - Size / 2,
spawnOffset.Y * scene.TileSize + scene.Room.Center.y - Size / 2);
}
}
public class Enemy : IEntity
{
public int Size { get; } = 32;
public Vector2 Position { get; private set; }
public Enemy(Scene scene)
{
var spawnOffset = new Vector2(0, 0);
Position = new Vector2(
spawnOffset.X * scene.TileSize + scene.Room.Center.x - Size / 2,
spawnOffset.Y * scene.TileSize + scene.Room.Center.y - Size / 2);
}
}
Without the use of inheritance, I want every class that implements IEntity to be able to reuse calculations for Position assignment. The only difference between both constructors is the spawnOffset values.
What's the most simple way to do this?
Normally - I would call the base class constructor, but here I'm trying to do this without a base class.
Looks like you follow "prefer composition over inheritance" - indeed that is a valid approach, just finish that by extracting "position" functionality into separate class:
class EntityPosition
{
public Vector2 Position { get; private set; }
public EntityPosition (Scene scene, Vector2 spawnOffset, int size)
{
Position = new Vector2(
spawnOffset.X * scene.TileSize + scene.Room.Center.x - size / 2,
spawnOffset.Y * scene.TileSize + scene.Room.Center.y - size / 2);
}
// add other shared methods for position updates here as necessary
}
public class Player : IEntity
{
EntityPosition position;
public int Size { get; } = 32;
public Vector2 Position => position.Position;
public Player(Scene scene)
{
var spawnOffset = new Vector2(0, -3);
position = EntityPosition(scene, spawnOffset, Size);
}
}
Problem Statement
There is a custom vector class:
namespace StackoverflowQuestion1
{
public class MyVector
{
public float x;
public float y;
public float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
}
There is an interface for anything that's movable, which means positions may change:
namespace StackoverflowQuestion1
{
public interface IMovable
{
public string Name { get; }
public MyVector Position { get; }
}
}
Furniture is movable, therefore it implements the corresponding interface:
namespace StackoverflowQuestion1
{
public class Furniture : IMovable
{
public string Name { get; private set; }
public MyVector Position { get; private set; }
public Furniture(string name, float x, float y, float z)
{
this.Name = name;
this.Position = new MyVector(x, y, z);
}
}
}
Accessing the private getter of the Name is not possible, as expected. Accessing the private setter of Position is also not working, as expected. However, accessing the fields of Position is possible, as they are public.
using StackoverflowQuestion1;
class Program
{
static void Main(string[] args)
{
Furniture F = new Furniture("Chair", 1f, 2f, 3f);
F.Name = "Office chair"; // doesn't work, as expected
F.Position = new MyVector(5f, 6f, 7f); // doesn't work, as expected
F.Position.x = 5f; // works, unfortunately
F.Position.y = 6f; // works, unfortunately
F.Position.z = 7f; // works, unfortunately
}
}
Question
How to make it impossible to change the furniture's position, without making the coordinates of MyVector private and, thus, inaccesible? I want to have encapsulation, by only letting Furniture members access the position, but MyVector will become useless in other places if its values can't be changed.
A couple of points to make here:
By design you chose to make the fields public which means they are readily accessible from other classes. They are not private which is what the title implies. To force them to be read only use the readonly keyword
public class MyVector
{
public readonly float x;
public readonly float y;
public readonly float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
Typically you won't expose the fields but instead use properties with getters defined only.
public class MyVector
{
private readonly float x;
private readonly float y;
private readonly float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public float X { get => x; }
public float Y { get => y; }
public float Z { get => z; }
}
Furthermore, you can simplify things using auto-properties
public class MyVector
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
}
Finially, it recommended for value semantics where (x,y,z) will always go together to use struct declarations.
public readonly struct MyVector
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
}
As a side note, if you try to modify the contents of a struct exposed by a property, the C# is going to complain.
Consider this code
public struct MyVector
{
public float x;
public float y;
public float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
public class Movable
{
public Movable(MyVector position)
{
Position = position;
}
public MyVector Position { get; }
}
So even with by design allowing the contents of MyVector to be mutable (change), the compiler is going to stop you. This is because with struct types you have local copies of the data everywhere and by writing Position.x = 10f you would have modified a local copy of Position that exists in the scope where this is called, and not modified the original data.
In the question MyVector is a class and so Position.x = 10f modifies the original data and as stated this is undesirable behavior, so follow the steps above to disallow this behavior.
To make MyVector work well with other classes I often add the following functionality to such deflations. I add support for .ToString() with formatting and I add support for .Equals() (and == for structures) in order to be to write code like this:
static void Main(string[] args)
{
var pos = new MyVector(1f, 1/2f, 1/3f);
var m = new Movable(pos);
if (m.Position == pos)
{
Console.WriteLine($"{m.Position:f2}");
// (1.00,0.50,0.33)
}
}
Notice the formatting with 2 decimals and the equality check.
here is the complete code that allows this for your reference
MyVector.cs
public readonly struct MyVector : IEquatable<MyVector>, IFormattable
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
#region IEquatable Members
/// <summary>
/// Equality overrides from <see cref="System.Object"/>
/// </summary>
/// <param name="obj">The object to compare this with</param>
/// <returns>False if object is a different type, otherwise it calls <code>Equals(MyVector)</code></returns>
public override bool Equals(object obj)
{
if (obj is MyVector other)
{
return Equals(other);
}
return false;
}
public static bool operator ==(MyVector target, MyVector other) { return target.Equals(other); }
public static bool operator !=(MyVector target, MyVector other) { return !(target == other); }
/// <summary>
/// Checks for equality among <see cref="MyVector"/> classes
/// </summary>
/// <param name="other">The other <see cref="MyVector"/> to compare it to</param>
/// <returns>True if equal</returns>
public bool Equals(MyVector other)
{
return X.Equals(other.X)
&& Y.Equals(other.Y)
&& Z.Equals(other.Z);
}
/// <summary>
/// Calculates the hash code for the <see cref="MyVector"/>
/// </summary>
/// <returns>The int hash value</returns>
public override int GetHashCode()
{
unchecked
{
int hc = -1817952719;
hc = (-1521134295) * hc + X.GetHashCode();
hc = (-1521134295) * hc + Y.GetHashCode();
hc = (-1521134295) * hc + Z.GetHashCode();
return hc;
}
}
#endregion
#region Formatting
public override string ToString() => ToString("g");
public string ToString(string formatting) => ToString(formatting, null);
public string ToString(string format, IFormatProvider provider)
{
return $"({X.ToString(format, provider)},{Y.ToString(format, provider)},{Z.ToString(format, provider)})";
}
#endregion
}
The problem with using a private setter for an object is that it only prevents you from replacing the object entirely. As it's not an immutable object, you can still access its properties change them, as you have found.
You could define an IMyVector interface with get only properties, have MyVector implement it, and then use the interface for your public Position property.
public interface IMyVector
{
float x {get;}
...
}
public class MyVector : IMyVector
{
...
}
public class Furniture : IMovable
{
public string Name { get; private set; }
public IMyVector Position { get; private set; }
...
Another design possibility is to declare IMyReadOnlyVector interface and expose it whenever we don't want to allow change vectors:
public interface IMyReadOnlyVector {
float x { get; }
float y { get; }
float z { get; }
}
public interface IMyVector : IMyReadOnlyVector {
float x { get; set; }
float y { get; set; }
float z { get; set; }
}
Then you implement MyVector:
public class MyVector : IMyVector {
public MyVector(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float x { get; set; }
public float y { get; set; }
public float z { get; set; }
}
Now, time for the trick: IMovable uses IMyReadOnlyVector interface: we let user see Position but not allow to change it.
public interface IMovable {
string Name { get; }
// User can see position, but not allowed to change it
IMyReadOnlyVector Position { get; }
}
public class Furniture : IMovable {
// Private usage only: we don't want user explicitly change position
private MyVector m_Position;
public string Name { get; private set; }
// Public usage: user can't change vector's coordinates here
public IMyReadOnlyVector Position => m_Position;
public Furniture(string name, float x, float y, float z) {
this.Name = name;
this.m_Position = new MyVector(x, y, z);
}
// But we can change Position within the class
public void ShiftMe(int dx, int dy, int dz) {
m_Position.x += dx;
m_Position.y += dy;
m_Position.z += dz;
}
}
I have a problem. I created a TriangleGrid using SkiaSharp. While I was drawing the grid I saved each triangle info in a Dictionary. The Dictionary looks like this:
public class TriangleRegistryObject
{
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public bool Selected { get; set; }
public bool Visible { get; set; }
}
Now when I select a Triangle I set the boolean Selected to true. At the end I want to check if the Triangles I have selected are connected with eachother. I thought I could count the connected lines. Here is an example image:
Now I want to count the purple lines where Selected=true.
I have every coordinate (x1, y1) (x2, y2) and (x3, y3).
UPDATE:
Here is the code I use that return 0 for me!
public static bool ValidLayout()
{
bool IsValid;
int sharedEdges;
int SelectedTriangles = TriangleRegistry.Count(tr => tr.Value.Selected.Equals(true));
var triangles = new List<TriangleRegistryList>();
foreach (KeyValuePair<string, TriangleRegistryObject> row in TriangleRegistry.Where(n => n.Value.Selected == true).ToList())
{
triangles.Add(new TriangleRegistryList { x1 = row.Value.x1,
y1 = row.Value.y1,
x2 = row.Value.x2,
y2 = row.Value.y2,
x3 = row.Value.x3,
y3 = row.Value.y3
});
}
sharedEdges = triangles.GetKCombs(2).Where(t => t.First().IsAdjacentTo(t.Skip(1).Take(1).Single())).Count();
if (sharedEdges >= (SelectedTriangles - 1))
{
IsValid = true;
}
else
{
IsValid = false;
}
return IsValid;
}
But I have no idea how I can compare the coordinates with each other, to count the connected lines!
Can someone help me?
Here is a very simple solution. It definitely isn't the most efficient, but it gets the job done.
I've added a method to your triangle class that returns true if it shares at least 2 vertices with another triangle.
I've also used a method of finding the distinct permutations that is slightly modified from the one discussed here.
public class Program
{
public static void Main()
{
var triangles = new List<TriangleRegistryObject>{
new TriangleRegistryObject{x1=10,y1=10, x2=12,y2=10, x3=1,y3=11},
new TriangleRegistryObject{x1=9,y1=11, x2=11,y2=11, x3=10,y3=10},
new TriangleRegistryObject{x1=9,y1=11, x2=11,y2=11, x3=10,y3=12},
new TriangleRegistryObject{x1=34,y1=14, x2=15,y2=11, x3=10,y3=12},
};
var sharedEdges = triangles.GetPairs().Where(t => t.first.IsAdjacentTo(t.second)).Count();
Console.WriteLine($"Number shared edges: {sharedEdges}");
}
}
public class TriangleRegistryObject
{
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public bool Selected { get; set; }
public bool Visible { get; set; }
public IEnumerable<(float x, float y)> GetPoints()
{
yield return (x1, y1);
yield return (x2, y2);
yield return (x3, y3);
}
public bool IsAdjacentTo(TriangleRegistryObject other)
{
return this.GetPoints().Intersect(other.GetPoints()).Count() >= 2;
}
}
public static class EnumerableExtensions
{
public static IEnumerable<(T first, T second)> GetPairs<T>(this IEnumerable<T> list)
{
return list.SelectMany((value, index) => list.Skip(index + 1),
(first, second) => (first, second));
}
}
I created this class called Genes and I want to get all it's properties inside of another script. I tried using
PropertyInfo[] props = typeof(Genes).GetProperties();
but props is an empty array. I think typeof isn't working. This is my class Genes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Genes
{
private float size;
private float speed;
private float probability;
private int color;
public Genes(float size, float speed, float probability, int color)
{
this.size = size;
this.speed = speed;
this.probability = probability;
this.color = color;
}
}
and I basically want to run over size, speed, probability and color using a foreach loop. What is the problem?
Either make your fields properties which have getters and setters:
public class Genes
{
private float Size { get; set; }
private float Speed { get; set; }
private float Probability { get; set; }
private int Color { get; set; }
public Genes(float size, float speed, float probability, int color)
{
this.Size = size;
this.Speed = speed;
this.Probability = probability;
this.Color = color;
}
}
or use GetFields if you really want fields:
typeof(Genes).GetFields();
No matter what you chose, these members should furthermore either be public, or you have to use the BindingFlags.NonPublic to look for non-public members:
typeof(Genes).GetProperties(BindingFlags.NonPublic);
or
typeof(Genes).GetFields(BindingFlags.NonPublic);
Im trying to create a seesaw with ball on its shape, that based on the shapes angle, the ball rolls.
Here is the screenshot of it.
So, the shape of the seesaw moves based on the angle generatated by a trackbar value.
Here are the variables declared:
private const float ONE_DEGREE = 0.0174532924f;
private ID3DMesh tab;
private ID3DMesh ball;
The 'tab' variable is the shape.
This method sets the angle of the shape:
public void setShapeAngle(float degree)
{
tabTargetAngle = Util.DegreeToRadian(degree);
}
And here is the method that updates it:
public void Update(int elapsedTime)
{
if (tab.Pitch != tabTargetAngle)
{
if (tabTargetAngle > tab.Pitch)
{
if (tab.Pitch >= (tabTargetAngle - ONE_DEGREE))
{
tab.Pitch = tabTargetAngle;
}
else
{
tab.Pitch += tabuaSpeed * elapsedTime;
}
}
else if (tabTargetAngle < tab.Pitch)
{
if (tab.Pitch <= (tabTargetAngle + ONE_DEGREE))
{
tab.Pitch = tabTargetAngle;
}
else
{
tab.Pitch -= tabuaSpeed * elapsedTime;
}
}
}
}
All of the objects, are ID3DMesh objects. Here is the code of the ID3DMesh class.
public interface ID3DMesh : IDisposable
{
Color Ambient { get; set; }
CollisionTestMethod CollisionDetectionMethod { get; set; }
Mesh D3DXMesh { get; }
Color Diffuse { get; set; }
Color Emissive { get; set; }
Material[] Materials { get; set; }
ID3DMesh Parent { get; set; }
float Pitch { get; set; }
Vector3 PivotOffset { get; set; }
float PivotOffsetX { get; set; }
float PivotOffsetY { get; set; }
float PivotOffsetZ { get; set; }
Vector3 Position { get; set; }
RenderOptions RenderSettings { get; set; }
float Roll { get; set; }
Vector3 Scale { get; set; }
float ScaleX { get; set; }
float ScaleY { get; set; }
float ScaleZ { get; set; }
Color Specular { get; set; }
float SpecularSharpness { get; set; }
Texture[] Textures { get; set; }
Color WireColor { get; set; }
float X { get; set; }
float Y { get; set; }
float Yaw { get; set; }
float Z { get; set; }
MeshBoundingBox GetBoundingBox();
MeshBoundingSphere GetBoundingSphere();
float GetDepth();
float GetHeight();
float GetWidth();
Matrix GetWorldMatrix();
bool Intersects(ID3DMesh mesh);
void Link(ID3DMesh parentMesh, Vector3 linkPosition);
void Move(float xAmount, float yAmount, float zAmount);
void Render();
void RenderPlanarShadow(Plane groundPlane, Light light, bool allowDoubleBlending);
void SetDepth(float depth);
void SetDepth(float depth, bool uniformScale);
void SetHeight(float height);
void SetHeight(float height, bool uniformScale);
void SetPlanarShadowOpacity(float shadowOpacity);
void SetScale(float amount);
void SetScale(float xAmount, float yAmount, float zAmount);
void SetSize(float width, float height, float depth);
void SetWidth(float width);
void SetWidth(float width, bool uniformScale);
}
I tried to use the Move(float, float, float) method. But it didnt moved as it should. If you could help me with that.
Thank you.
(Note: Below I'll be ignoring the third dimension, because the ball will always move along the same plane)
If we take the seesaw as a reference frame, I think the movement of the ball will be similar to that of an harmonic oscillator. That is to say, the position of the ball along the seesaw at a given instant of time, s(t), will be given by the following formula:
s(t) = L cos(2π t / T + ϕ)
where L is the length of the seesaw (the amplitude of the harmonic) and T is the time it takes the ball to move from one end of the seesaw to the other and back to the start (the period of the harmonic). ϕ, the initial phase of the harmonic, is there to adjust the formula so s(0) gives you the starting position. If you want it to start at the center, you need to make s(0) = 0, which means you need the cosine to be 0. So you have to make ϕ be π/2 (90 degrees), because cos(π/2) = 0.
With this you can put the ball in place by changing the world transform. If you rotate it to the current angle of the seesaw (let's call it θ(t)), you can just translate the ball by the value of s(t) along the xx axis.
This is equivalent to treating (s(t),θ(t)) as the position of the ball in polar coordinates. You can then get the cartesian coordinates at a given time (x(t),y(t)) with these formulae:
x(t) = s(t) cos(θ)
y(t) = s(t) sin(θ)
(Let's assume the up-vector is (0, 1, 0) and the tab is aligned with the X-axis)
You can imagine the ball would have to "roll" down the tab along the X-axis, and you would have to calculate the Y-position to let it stick to the tab.
You could use the Move() method for the X position, as the ball's speed has an impact on it's X-position in a relative way.
The Y-position though (as long as the ball remains on the tab) could more easily be calculated for each X-position, by setting the Y property.
If I were you I'd start by creating a method that calculates the Y position to make the ball "stick to the tab" for any X position.
If this doesn't point you in the right direction, please elaborate a bit more on "it didnt moved as it should".