I want to find out if my CGMutablePathRef has more then 3 points! This checking will happen frequently so Im looking for an efficient solution.
This reason I need to do this is because in my project the user draws a shape. As the user drags his/her finger a CGPoint(current location of finger) is added to the path and a physical body is added when the touchEnded: is called.. now if the user just taps the screen the CGMutablePathRef only has one point in it(my reasoning in my head) and I get the following error when I use the my CGMutablePathRef for adding the physical body.
Assertion failed: (count >= 2), function CreateChain, file /SourceCache/PhysicsKit/PhysicsKit-6.5.4/PhysicsKit/Box2D/Collision/Shapes/b2ChainShape.cpp, line 45.
Im looking to make a function to call that takes a cgpathref as a parameter and counts the points until it reaches 3 (or the end if there isn't 3) and returns a bool
Thanks :)
If you want to enumerate the elements of a CGPath, you have to use CGPathApply, and there is no support for early termination. You must enumerate all of the elements.
Grab my Rob_forEachElementOfCGPath function from this answer, and use it like this:
int numberOfSegmentsInCGPath(CGPathRef path) {
__block int count = 0;
Rob_forEachElementOfCGPath(path, ^(const CGPathElement *element) {
if (element->type != kCGPathElementMoveToPoint) {
++count;
}
});
return count;
}
There's no particularly efficient way to do this, but you could keep track of the number of points as you add them.
#interface MyPath : NSObject
#property (assign) int pointCount;
#property (assign) CGPathRef path;
#end
If you wanted to make sure you had complete control over it (and didn't want to worry about someone else incrementing your count or changing your path without incrementing), you could make the properties readonly and have an 'add point' function on the object ... Not great, but it would be more efficient than ApplyPath.
Related
I need to find all simple non overlapping cycles on undirected graph. To find all existing cycles I made an Objective-C version of the algorithm that I found here:
Finding all cycles in undirected graphs
#interface HSValue : NSObject
#property (nonatomic, assign) CGPoint point;
#end
#implementation HSValue
#end
#interface CyclesFinder ()
#property (nonatomic, strong) NSMutableArray <NSArray<HSValue *>*> *cycles;
#property (nonatomic, strong) NSArray <NSArray<HSValue*>*> *edges;
#end
#implementation CyclesFinder
-(void)findCyclesInGraph:(NSArray <NSArray<HSValue*>*> *)edges {
self.edges = edges;
for (NSInteger i=0; i < self.edges.count; i++) {
for (NSInteger j=0; j < self.edges[i].count; j++) {
[self findNewCycles:#[self.edges[i][j]]];
}
}
}
-(void)findNewCycles:(NSArray <HSValue *> *)path {
HSValue *startNode = path[0];
HSValue *nextNode;
NSArray <HSValue *> *sub;
for (NSInteger i=0; i < self.edges.count; i++) {
NSArray <HSValue *> *edge = self.edges[i];
if ([edge containsObject:startNode]) {
if ([edge[0] isEqual:startNode]) {
nextNode = edge[1];
}
else {
nextNode = edge[0];
}
}
else {
nextNode = nil;
}
if (![path containsObject:nextNode] && nextNode) {
sub = #[nextNode];
sub = [sub arrayByAddingObjectsFromArray:path];
[self findNewCycles:sub];
}
else if (path.count > 2 && [nextNode isEqual:path.lastObject]) {
if (![self cycleExist:path]) {
[self.cycles addObject:path];
break;
}
}
}
}
-(BOOL)cycleExist:(NSArray <HSValue*> *)path {
path = [path sortedArrayUsingSelector:#selector(compare:)];
for (NSInteger i=0; i < self.cycles.count; i++) {
NSArray <HSValue *> *cycle = [self.cycles[i] sortedArrayUsingSelector:#selector(compare:)];
if ([cycle isEqualToArray:path]) {
return TRUE;
}
}
return FALSE;
}
Above algorithm works fine (even if it is not very efficient) and it finds all the possible cycles from the graph on the attached picture (please see picture below):
A-B-H-G-F-D-E-A (valid)
B-C-I-H-B (valid)
G-H-I-L-K-G (valid)
F-G-K-J-F (valid)
F-G-H-I-L-K-J-F (invalid)
A-B-C-I-H-G-F-D-E-A (invalid)
A-B-C-I-L-K-J-F-D-E-A (invalid)
A-B-C-I-H-G--K-J-F-D-E-A (invalid)
A-B-H-I-L-K-G-F-D-E-A (invalid)
A-B-H-G-K-J-F-D-E-A (invalid)
A-B-C-I-L-K-G-F-D-E-A (invalid)
B-C-I-L-K-G-H-B (invalid)
B-C-I-L-K-J-F-G-H-B (invalid)
However when I run the above algorithm I want to end up with only those cycles that I highlighted with coloured polygons on the left hand side example. What I don't want are the cycles like the one on the right hand side example.
My first thought was that overlapping cycle will be a cycle that includes all the points from any other cycles, but this is not always true. Can someone point me into the right direction? Is it possible to modify the above algorithm so it finds only non-overlapping cycles or if not what should I do after finding all cycles to filter them?
There isn't enough information just in the undirected graph itself to determine which cycles are which. For example, consider that the following 2 diagrams yield identical undirected graphs:
A-----B E-------------F
| | \ /
C-----D \ A-----B /
| | \| |/
E-----F C-----D
But for the diagram on the left, you want the cycles ABDCA and CDFEC, while for the diagram on the right, you want the cycles ABDCA and EFDBACE. Thus the undirected graph inferred from the diagram isn't enough -- you need to somehow incorporate spatial information from the original diagram.
I'm working on this same problem and a lot of your comments were helpful, especially the comment that all edges will have an area on each side. Thus you could say that each edge has a "left area" and a "right area".
You can add all graph edges to a queue in any order. Peek at the first edge, pick its vertex closer to your origin. Move to the neighbor that is the most counter-clockwise. continue this until you have reached your starting vertex. All of these edges bound your first area. I would give it a unique ID and assign it to a "left area" property of those edges.
Peek at the first edge in the queue and check if it has a "left area". If it does check if it has a "right area" if it does not proceed in a clockwise manner and find the right area. If it has both areas assigned dequeue it and grab the next one.
should be O(e+v) so pretty quick, right?
This is a little bit stream of consciousness but I wanted to get it written down. I'll be writing the algorithm for my actual app and I'll make tweaks as I find problems in it.
Of course I'm open to feedback and suggestions :)
I know this question has been 6 years old yet leaving this answer for someone having the same problem in the future.
Key idea
Every edge has exactly two adjacent faces. Actually, every directed edge has exactly one adjacent face.
Construct polygons by choosing most counter-clockwise adjacent edge. Then the polygons with counter-clockwise orientation will be non-overlapping cycle of the graph.
Algorithm overview
For every edges on the graph, find two polygons containing the edge. One for each direction of the edge.
Filter the polygons whose orientation is counter-clockwise.
The resulting polygons are all of non-overlapping cycles in the given graph.
Finding a polygon containing the given edge in a graph
Basically it's choosing the next edge by most counter-clockwise from the current edge until a cycle is created or it hits dead end.
If the end of the cycle equals to the start of the given edge, then the polygon contains the given edge. Else the polygon doesn't contain the given edge, so ignore it.
I'm just posting the entire code of this function. Read it through and I hope you get the idea.
See caveats below for more informations about orientations and vector calculations.
static public <N extends Location> Optional<Polygon<N>> findPolygon(EndpointPair<N> edge, Graph<N> graph) {
if (!edge.isOrdered()) throw new IllegalArgumentException("The starting edge must be ordered.");
if (!graph.hasEdgeConnecting(edge))
throw new IllegalArgumentException("The starting edge must be contained in the graph");
final N start = edge.source();
final MutableGraph<N> polygonGraph = GraphBuilder.directed()
.incidentEdgeOrder(ElementOrder.stable())
.nodeOrder(ElementOrder.insertion())
.build();
// Set the first edge of the polygon.
N source = start;
N target = edge.adjacentNode(source);
// Start adding edges to polygonGraph.
// Until a cycle is created.
while (true) {
// Check if a cycle is created.
if (polygonGraph.nodes().contains(target)) {
// Connect the last edge.
polygonGraph.putEdge(source, target);
break;
}
// Connect the edge.
polygonGraph.putEdge(source, target);
// Find the most counter-clockwise adjacent vertex from the target.
// Then that vertex is the target of the next edge and the target of the current edge is the source of
// the next edge.
Vector base = source.toVector().clone().subtract(target.toVector());
final N finalTarget = target;
Map<N, Double> angles = graph.adjacentNodes(target).stream().collect(Collectors.toMap(
Function.identity(),
node -> {
Vector u = node.toVector().clone().subtract(finalTarget.toVector());
return Vectors.fullAngle(base, u);
}
));
List<N> adjacentNodes = graph.adjacentNodes(target).stream().filter(not(source::equals)).toList();
// Dead end. Failed to create a polygon. Exit.
if (adjacentNodes.isEmpty()) break;
source = target;
target = Collections.max(adjacentNodes, Comparator.comparingDouble(angles::get));
}
// The created polygon doesn't contain the starting edge.
if (!target.equals(start)) {
return Optional.empty();
}
return Optional.of(new Polygon<>(polygonGraph));
}
Identifying the orientation of a polygon
https://www.baeldung.com/cs/list-polygon-points-clockwise
A polygon is counter-clockwise iff its area > 0.
Optimization
The time complexity of the algorithm is O(E^2). (I think)
But you can apply dynamic programming method and it reduces to O(E) (I think)
The idea is that for every directed edge there exists only one matching polygon.
So when you find a polygon, cache every edges of that polygon and you won't have to find that polygon for that edges again.
// This is a pseudo-code
Map<Edge, Polygon> cache = new HashMap<>();
// If the edge is in cache, skip the polygon search.
if (cache.containsKey(edge)) continue;
// When you have found a polygon, cache the edges.
polygon.edges().forEach(edge -> {
cache.put(edge, polygon);
});
You can also pre-determine if a given edge can construct a polygon by looking at the neighbors of the edge.
If any one of the degree of the vertices of the edge is less than 2, meaning that the edge is not connected to other neighbors at both side, it cannot construct a polygon.
So you can skip the polygon search for this edge.
Caveats
Orientations
About the orientation and the related things, although I wrote this article after choose to use counter-clockwise, it seems that it doesn't matter which side you pick to use as long as be consistent for:
The orientation of the polygon ( counter-clockwise / clockwise)
Which adjacent edge to pick to be the next edge of the polygon ( most counter-clockwise / least counter-clockwise ) (or in other words, least clockwise / most clockwise)
Once you choose one of them to be one thing then the other option is automatically determined in order to the algorithm work.
Angle of two edges
You need to convert edges to vectors in order to calculate the angle between them.
Keep in mind that the tail of the vectors have to be the vertex of the angle.
So, if you're getting the angle between Edge(AB) and Edge(BC) then you have to calculate the angle between u = A - B and w = C - B.
Angle of two vectors
Some APIs define the range of the function for getting angle between two vectors as [-PI/2, PI/2].
But you need it to be [0, 2PI] so, you have to convert it.
You can make it [-PI, PI] by using atan2 function.
https://math.stackexchange.com/questions/878785/how-to-find-an-angle-in-range0-360-between-2-vectors
And then add 2 * PI then take mod 2 * PI.
public class Vectors {
static public double fullAngle(#NotNull Vector v, #NotNull Vector u) {
return (Math.atan2(v.det(u), v.dot(u)) + 2 * Math.PI) % (2 * Math.PI);
}
}
I can easily animate something like the position or size of a UIView. But how can I animate a "custom" variable (such as experiencePoints) to achieve interpolation of values that are not associated with a UIView.
// The variable being animated
CGFloat experiencePoints = 0;
// Pseudo-code
[experiencePoints animateTo:200 duration:2 timingFunction:someTimingFunction];
With this code, if I accessed experiencePoints while it was animating, I would get a value between 0 and 200 based on how long the animation has been going.
Bonus question: Is it possible to use a CAAnimation to do what I want?
You can go with Presentation layer with CADisplayLink and by adding observer on animation status you can increment the variable upto your final one. Observer will observe current status of Animation which will eventually provide you a way to get current value of that VAR.
Ended up using Facebook's POP animation library, which allows the animation of any property on an object. I recommend it!
Here is a quote from the readme, explaining how to do it:
The framework provides many common layer and view animatable properties out of box. You can animate a custom property by creating a new instance of the class. In this example, we declare a custom volume property:
prop = [POPAnimatableProperty propertyWithName:#"com.foo.radio.volume" initializer:^(POPMutableAnimatableProperty *prop) {
// read value
prop.readBlock = ^(id obj, CGFloat values[]) {
values[0] = [obj volume];
};
// write value
prop.writeBlock = ^(id obj, const CGFloat values[]) {
[obj setVolume:values[0]];
};
// dynamics threshold
prop.threshold = 0.01;
}];
anim.property = prop;
"Animating" something is simply moving a value from a starting position to an ending position over time. To "animate" a float from one value to another over a duration you could just run a timer and incrementally change it. Or possibly you could store the begin and end time of the "animation" and whenever the value is accessed, compare those to the actual time to come up with a value.
I want to do this:
typedef struct
{
CGPoint vertices[];
NSUInteger vertexCount;
} Polygon;
But it says Field has incomplete type CGPoint [].
You need to do one of two things:
Declare the array to be a fixed size (probably not what you want)
Make it a pointer. But then you need to properly malloc and free the array as needed.
A better choice is to not use a struct and instead create a full class. Then you can add methods and properties as well as make memory management much easier. You are working in Objective-C. Take advantage of the Object Oriented aspects of the language. Add a method to calculate the circumference and area, etc. Put the logic where it belongs.
Set array size CGPoint vertices[count];
Don't you want a unique name for each element of your struct anyway? If you just want a bunch of CGPoint's in a numerical order, with the ability to count how many of them there are you'd be much better served by shoving them in an NSArray or NSMutableArray (stored as NSValue's of course)
The whole point of a struct would be to have easy access to the values by a descriptive name, ie:
typedef struct {
CGPoint helpfulAndDescriptiveNameOne;
CGPoint helpfulAndDescriptiveNameTwoWhichIsDifferentThanTheOtherName;
etc...
NSUInteger vertexCount;
}
For example, a CGRect is just a struct composed of four different CGFloats, each of which is descriptively and helpfully named:
typedef {
CGFloat x;
CGFloat y;
CGFloat width;
CGFloat height;
} CGRect;
What would be the best way to do this? I see the CCEaseSineInOut action but it doesn't look like that could be used to do this.
I need to move from one side of the screen to the other. The sprite should move in a sine-wave pattern across the screen.
I always like to have complete control over CCNode motion. I only use CCActions to do very basic things. While your case sounds simple enough to possibly do with CCActions, I will show you how to move a CCNode according to any function over time. You can also change scale, color, opacity, rotation, and even anchor point with the same technique.
#interface SomeLayer : CCLayer
{
CCNode *nodeToMove;
float t; // time elapsed
}
#end
#implementation SomeLayer
// Assumes nodeToMove has been created somewhere else
-(void)startAction
{
t = 0;
// updateNodeProperties: gets called at the framerate
// The exact time between calls is passed to the selector
[self schedule:#selector(updateNodeProperties:)];
}
-(void)updateNodeProperties:(ccTime)dt
{
t += dt;
// Option 1: Update properties "differentially"
CGPoint velocity = ccp( Vx(t), Vy(t) ); // You have to provide Vx(t), and Vy(t)
nodeToMove.position = ccpAdd(nodeToMove.position, ccpMult(velocity, dt));
nodeToMove.rotation = ...
nodeToMove.scale = ...
...
// Option 2: Update properties non-differentially
nodeToMove.position = ccp( x(t), y(t) ); // You have to provide x(t) and y(t)
nodeToMove.rotation = ...
nodeToMove.scale = ...
...
// In either case, you may want to stop the action if some condition is met
// i.e.)
if(nodeToMove.position.x > [[CCDirector sharedDirector] winSize].width){
[self unschedule:#selector(updateNodeProperties:)];
// Perhaps you also want to call some other method now
[self callToSomeMethod];
}
}
#end
For your specific problem, you could use Option 2 with x(t) = k * t + c, and y(t) = A * sin(w * t) + d.
Math note #1: x(t) and y(t) are called position parameterizations. Vx(t) and Vy(t) are velocity parameterizations.
Math note #2: If you have studied calculus, it will be readily apparent that Option 2 prevents accumulation of positional errors over time (especially for low framerates). When possible, use Option 2. However, it is often easier to use Option 1 when accuracy is not a concern or when user input is actively changing the parameterizations.
There are many advantages to using CCActions. They handle calling other functions at specific times for you. They are kept track of so that you can easily pause them and restart them, or count them.
But when you really need to manage nodes generally, this is the way to do it. For complex or intricate formulas for position, for example, it is much easier to change the parameterizations than to figure out how to implement that parameterization in CCActions.
heres the problem
i have 5 balls floating around the screen that bounce of the sides, top and bottom. thats working great.
what i want to do now is work out if any of them collide with each other.
i know about
if (CGRectIntersectsRect(image1.frame, image2.frame))
{
}
but that only checks two images, i need to check all and each of them..
ive checked everywhere but cant find the answer, only others searching the same thing, any ideas?
thanks in advance
Spriggsy
edit:
im using this to find the CGRect and store it in an array
ball1 = NSStringFromCGRect(image1.frame);
ball2 = NSStringFromCGRect(image2.frame);
ball3 = NSStringFromCGRect(image3.frame);
ball4 = NSStringFromCGRect(image4.frame);
ball5 = NSStringFromCGRect(image5.frame);
bingoarray = [NSMutableArray arrayWithObjects:ball1,ball2,ball3,ball4,ball5,nil];
this then gets passed to a collision detection method
-(void)collision {
for (int i = 0; i<[bingoarray count]-1 ; i++) {
CGRect ballA = CGRectFromString([bingoarray objectAtIndex:i]);
if (CGRectIntersectsRect(ballA, image1.frame)) {
NSLog(#"test");
}
}
this i guess should check one ball against all the others.
so ball 1 gets checked against the others but doesnt check ball 2 against them. is this nearly there?
}
The ideal solution is to store all the rectangles into a interval tree or a segment tree in order to efficiently compute any overlapping areas. Note that you will have to generalize to 2 dimensions for your use case.
Another efficient approach would be to use a K-d tree to find the nearest other balls and compare against the nearest neighbor until there isn't a collision.
The simple approach is to simply iterate over all the balls and compare them to all other balls with a higher ID (to avoid double checking ball1 -> ball2 and ball2 -> ball1).
Since you only have 5 at once, the iterative approach is likely fast enough to not be dropping frames in the animation, but you should consider a more scalable solution if you plan to support more balls since the simple appreach run in quadratic time.
That is a fun little math problem to avoid being redundant.
You can create an array of the images. And loop through it, checking if each member collides with any successive members.
I can spell it out more with code if need be.
EDIT I couldn't resist
// the images are in imagesArray
//where you want to check for a collision
int ballCount = [imagesArray count];
int v1Index;
int v2Index;
UIImageView * v1;
UIImageView * v2;
for (v1Index = 0; v1Index < ballCount; v1Index++) {
v1 = [imagesArray objectAtIndex:v1Index];
for (v2Index = v1Index+1; v2Index < ballCount; v2Index++) {
v2 = [imagesArray objectAtIndex:v2Index];
if (CGRectIntersectsRect(v1.frame, v2.frame)) {
// objects collided
// react to collision here
}
}
}