-
-
Notifications
You must be signed in to change notification settings - Fork 361
Add Jarvis March in C#. #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Jarvis March in C#. #85
Conversation
|
||
namespace JarvisMarch | ||
{ | ||
public class Vector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this a struct and drop the properties. It has better performance and the properties are completely superfluous for a Vector
type:
public struct Vector
{
public int X;
public int Y;
public Vector(int x, int y)
{
this.X = x;
this.Y = y;
}
}
var secondVector = new Vector(points[i].X - currentPoint.X, points[i].Y - currentPoint.Y); | ||
|
||
// Calculate the current scalar product. | ||
var tempScalarProduct = (firstVector.X * secondVector.X) + (firstVector.Y * secondVector.Y); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd make a dot product method on the vector. Similarly for the used operators (minus) in this case. That way this could look like:
var secondVector = points[i] - currentPoint;
var tempScalarProduct = firstVector.Dot(secondVector);
{ | ||
// Search for next Point. | ||
// Set the first vector, which is currentPoint -> previousPoint. | ||
var firstVector = new Vector(previousPoint.X - currentPoint.X, previousPoint.Y - currentPoint.Y); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not the biggest fan of this naming. First, the Point
and Vector
suffix is pretty redundant. Second, firstVector
means nothing. I'll have to read the Jarvis march algorithm again to come up with a better name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name is not the best, I agree. But I don't know, if getting rid of Point
and Vector
is the best. I think they provide quite some clarity. If better names were used tho, the suffix might be redundant. I have to think about the names.
|
||
public static class JarvisMarch | ||
{ | ||
public static List<Vector> RunJarvisMarch(List<Vector> points) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant naming:
JarvisMarch.Run(...);
is pretty self-explanatory. If you want to follow best practices then don't make these static classes. That way you could potentially implement an interface IGiftWrapping
and have multiple algorithms that you can swap out at runtime (static classes/methods make this impossible)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So just use normal classes in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah
public static List<Vector> RunJarvisMarch(List<Vector> points) | ||
{ | ||
// Set the intial point to the first point of the list. | ||
var initialPoint = points[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should have a shared library somewhere in the AAA for example with List
/IEnumeable
extensions methods (similarly for other languages that need it). These lines just take up a bunch of space in the implementation when by all rights it should just be:
var iniitalPoint = points.MinBy(p => p.X);
We should talk about @leios about it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is somewhat related to #69, but I am not sure about the best way to do this in practice.
This is actually why the language choice chapter exists. The hope was to introduce any extra libraries and such people would need to use the language for the purpose of this book, while also benchmarking the languages against each other so people could see their strengths and weaknesses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's something to discuss.
Thanks for the review @Gustorn ! I will target your reviews in the following days as well :) |
I added a ToDoList for this PR. |
…dd ScalarProduct to Vector.
…Vector. Add mention for gustorn's help.
{ | ||
public struct Vector | ||
{ | ||
public readonly int x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public fields should be Pascal case
// Set the first vector, which is currentPoint -> previousPoint. | ||
var firstVector = previousPoint - currentPoint; | ||
|
||
Vector? nextPoint = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation feels a bit unelegant
} | ||
|
||
// Returns true, if p is more left than b, if viewed from a. | ||
private bool IsLeftOf(Vector a, Vector b, Vector p) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does someone know better names for this parameters? And should I provide a more in-depth explanation of what the method does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment is incorrect. It returns true if p
is left of the line defined by a
and b
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean it's somewhat correct, but far from perfect. I'll change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see it. It's incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, you were right, it's technically correct. I still think it's better to use more rigorous terms when defining operations like this.
public static bool operator==(Vector a, Vector b) => a.Equals(b); | ||
public static bool operator!=(Vector a, Vector b) => !(a == b); | ||
public static Vector operator+(Vector a, Vector b) => new Vector(a.x + b.x, a.y + b.y); | ||
public static Vector operator-(Vector a, Vector b) => new Vector(a.x - b.x, a.y - b.y); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we leave those operators in here (-
and +
). They are nice to have, but not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they're not used I say remove them
// Set the intial point to the point of the list, where the x-position is the lowest. | ||
var initialPoint = points.Aggregate((leftmost, current) => leftmost.x < current.x ? leftmost : current); | ||
|
||
convexHull.Add(initialPoint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just move this inside the do-while loop (at the start), it doesn't need to be special cased
}); | ||
|
||
convexHull.Add(nextPoint); | ||
points.Remove(nextPoint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to remove the point if we don't care about collinear points (and the consensus was that we don't). There are some other reasons why you shouldn't have it here:
- It kind of messes with the complexity of the algorithm. It won't affect big O notation but you're adding another linear search.
- It's very bad manners to modify an incoming parameter
If you do want to remove the point add something like:
var remainingPoints = new HashSet<Vector>(points);
And change stuff to use that one. But again, removing the point isn't necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I removed the point from the points we search, since that point isn't needed anymore and won't be used anyway. But you're right with the linear search, it creates a bigger disadvantage than advantage... The question is tho, if I should go for the HashSet
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just not remove the points
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I just exclude that now.
return potentialNextPoint; | ||
}); | ||
|
||
convexHull.Add(nextPoint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As said in the other comment, you can just move this to the beginning and then you don't need the Add
outside of the do...while
loop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This Add
and the other one are in there, since the output should include the start/end-point at the beginning and at the end. If we don't want that, we can just move one Add
at the beginning and remove the other one like you said.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's very useful to include the starting point in the output. It doesn't really give you anything and it makes the algorithm a bit harder to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, I think it's ready to be merged
Thanks for all the work on this guys! |
ToDo
IsLeftOf
.