What would be the reverse of CGRectUnion()? - iphone

I can combine rect1 with rect2 using CGRectUnion() and get a combined rect3 fine.
Is it possible to subtract a rect1 from a rect3 (which contains rect1) and get a remaining part of rect?

As Brad Larson said, you can't do this in Quartz, because the CGRect functions work with nothing but rects and their component parts (points, sizes, and single numbers).
If you were programming the Mac, I would suggest using another API named HIShape. It's the modern successor to QuickDraw Regions, and as such, it is capable of non-rectangular shapes. Unfortunately, though HIShape is still available on 64-bit Mac OS X, it is not available on iOS.
If you really need something like this, you will have to write it yourself, including your own HIShape-like not-necessarily-rectangular shape class.

Try CGRectIntersection if I could understand you correctly.

well, it depends... on how rect3 contains rect1...
i mean, it may happens that the resulting area is no more a rect...
for example, if rect1 is all inside rect3 the remaining area is not a rect, so you couldn't use the CGRect object.
You could obtain a rect just in case rect3 and rect1 share completely a side and have it (all of it) in common. So i need to know what kind of objet you wanna obtain by that subtraction...
may it be a new image with 2 different areas coloured? or slit the resuting area in more CGrect (upper rect, left, bitton, right...)
what are you going to do with the resulting "object"?
luca

Related

Efficiently draw CGPath on CATiledLayer

How would I efficiently draw a CGPath on a CATiledLayer? I'm currently checking if the bounding box of the tile intersects the bounding box of the path like this:
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
CGRect boundingBox = CGPathGetPathBoundingBox(drawPath);
CGRect rect = CGContextGetClipBoundingBox(context);
if( !CGRectIntersectsRect(boundingBox, rect) )
return;
// Draw path...
}
This is not very efficient as the drawLayer:inContext: is called multiple times from multiple threads and results in drawing the path many times.
Is there a better, more efficient way to do this?
The simplest option is to draw your curve into a large image and then tile the image. But if you're tiling, it probably means the image would be too large, or you would have just drawn the path in the first place, right?
So you probably need to split your path up. The simplest approach is to split it up element by element using CGPathApply. For each element, you can check its bounding box and determine if that element falls in your bounds. If not, just keep track of the last end point. If so, then move to the last end point you saw and add the element to a new path for this tile. When you're done, each tile will draw its own path.
Technically you will "draw" things that go outside your bounds here (such as a line that extends beyond the tile), but this is much cheaper than it sounds. Core Graphics is going to clip single elements very easily. The goal is to avoid calculating elements that are not in your bounding box at all.
Be sure to cache the resulting path. You don't need to calculate the path for every tile; just the ones you're drawing. But avoid recalculating it every time the tile draws. Whenever the data changes, dump your cache. If there are a very large number of tiles, you can also use NSCache to optimize this even better.
You don't show where the path gets created. If possible, you might try building the path up in the -drawLayer:inContext: method, only creating the portion of it needed for the tile being drawn.
As with all performance problems, you should use Instruments to profile your code and find out exactly where the bottlenecks are. Have you tried that already, and if so, what did you find?
As a side note, is there a reason you're using CGPath instead of UIBezierPath? From Apple's documentation:
For creating paths in iOS, it is recommended that you use UIBezierPath
instead of CGPath functions unless you need some of the capabilities
that only Core Graphics provides, such as adding ellipses to paths.
For more on creating and rendering paths in UIKit, see “Drawing Shapes
Using Bezier Paths.”

How to determine intersection of CGPaths

My Question is something similar to this.
I have 2 CGPathRef and 1 will be moved by finger touch. I want to find that whether the 2 CGPathRef are intersected? That question was asked almost 2 years ago and I want to know whether something has been found in the mean time.
This is fairly old, but I found it looking for a similar solution, in my problem I wanted to find when a circle overlapped with a path (a special case of your question).
I solved this by using CGPathCreateCopyByStrokingPath to create a stroked version of the original path using the radius of the circle as the stroke width. If the center point of the circle overlaps the stroked path then the original path overlaps the circle.
BOOL CGPathIntersectsCircle(CGPathRef path, CGPoint center, CGFloat radius)
{
CGPathRef fuzzyPath;
fuzzyPath = CGPathCreateCopyByStrokingPath(path, NULL, radius,
kCGLineCapRound,
kCGLineJoinRound, 0.0);
if (CGPathContainsPoint(fuzzyPath, NULL, center, NO))
{
CGPathRelease(fuzzyPath);
return YES;
}
CGPathRelease(fuzzyPath);
return NO;
}
Edit: A minor bug where the fuzzyPath was not released.
I have written a small pixel based path collision detection API for CGPathRefs. It requires that you add a few source directories to your project, and it only works with ARC, but it should at least show you how one might do something like this. It basically draws the two paths on two separate contexts, and then does pixel-by-pixel checks to see if any pixels are on both paths. Obviously this would be slow to run every time the user drags their finger, but it certainly could be done once every half second or so, maybe not even on the main thread.
This is the easiest way I've found of doing something like this, and it may easily be that there's no better way, besides using lots of math.
The source on Github
A quick Youtube demo.
Generally speaking, finding the intersection of two arbitrary CGPaths is going to be very complex.
There are ways to do approximations. Checking the intersections of the bounding boxes is a good first step. You can also subdivide the curve and repeat the process to get better approximations. Another option is to flatten the paths and see if any of the line segments of the flattened paths intersect.
For the general case, however, things get very nasty very fast. Consider, for example, the fact that two cubic bezier segments (never mind an entire path... just one segment) can intersect with another segment at up to 6 points. The more segments in your path, the more potential intersections. There is also the problem of degenerate bezier curves where a segment has a cusp that just touches one point of another segment. Does that count as an intersection? (sometimes yes, sometimes no)
It's not clear from your question, but you might also want to consider the intersections of the strokes that are applied to the curves, and correctly account for line joins and miters. That that gets even harder. Macromedia FreeHand (a drawing program similar to Adobe Illustrator) had a very large, complex, intensely mathematical library for discovering arbitrary bezier curve intersections. The problem is not easily solved.
To find the intersection of two CAShapeLayers, we can use below method, CAShapeLayer won't return frame. But we can get the refPath frame using CGPathGetBoundingBox. But this one will give the frame in rectangle.I thing you may understand.
if (CGRectIntersectsRect(CGPathGetBoundingBox(layer.path), CGPathGetBoundingBox(layer.path)))

Is it ever possible to call pointInside: on an irregular shape?

I'm drawing an oblong 'egg' shape (on iOS), and want to use it as a boundary for particles. My thought is to use the curve paths to make a UIView and then use hitTest:withEvent: or pointInside:withEvent: to enforce boundary collision.
The problem is, of course, that UIView is always rectangular. How would you go about checking to see if a point is inside an irregular shape like this?
- (void)drawRect:(CGRect)rect {
int w = rect.size.width;
int h = rect.size.height;
CGContextBeginPath(context);
CGContextMoveToPoint(context, w/2, h/5);
CGContextAddCurveToPoint(context, w*0.1, h/4.3, w*0.1, h*0.82, w/2, h*0.8);
CGContextAddCurveToPoint(context, w*0.9, h*0.82, w*0.9, h/4.3, w/2, h*0.2);
I'm using openFrameworks, for what that's worth. This code is just Obj-C but I'm open to any C++/obj-C++ solutions out there.
If you make a CGPathRef you can use CGPathContainsPoint. You can use that same CGPathRef to render into the context. You could also call CGContextPathContainsPoint on the context containing the path, but depending on when you need to test you might not have a context. And another alternative is the containsPoint selector on UIBezierPath.
If you want to code this from scratch, http://www.softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm goes through a couple of different algorithms that will work for an arbitrary polygon.

Drawing Routes with iPhone

I am trying to make an iPhone application which can draw a path between two points (similar to Google Maps) but instead of the map i want to use any other image as a background, this path between the two points might not be straight and there might be multiple paths to get from one point to another then I want to draw the shortest path between the two points.
I tried using the CGContext & CGPath but I got stacked.
Can you help me plz.
Thanx,
Ghaith
I think you're looking for UIBezierPath. You can add simple lines/polygons with something like:
UIBezierPath* path = [UIBezierPath bezierPath];
[aPath moveToPoint:CGPointMake(50.0, 50.0)];
[aPath addLineToPoint:CGPointMake(10.0, 10.0)];
[aPath addLineToPoint:CGPointMake(10.0, 50.0)];
[aPath closePath];
You can also, of course, add curves (bezier ones!) and other shapes. Then to draw it use the [aPath stroke] call in your view's drawRect method.
For more information see the iPad Programming Guide
This seems like a problem that's not really related to drawing the route.
You want to find the shortest path from one point to another, given certain criteria - where you can and cannot move, for example. I don't see this problem as something you can solve with drawing, but with actually calculating the different possible ways and then compare them. When you have decided which is the best route. Drawing is pretty simple.
How you would go by deciding I'm actually not sure - sorry 'bout that. But you should probably have a look at some shortest path algorithms. But that probably means you have to represent the underlying image as a pattern, or a series of nodes but graphical problems are not my cup of tea, so I'm not really sure how.
Just a side note - If the number of possible ways of getting from point A to point B are great, this can become a computational problem, and you have to make sure that the iPhone can manage.
(this should probably be a comment somewhere, but since I can't yet and I still wanted to share my two cents, it became an answer.)
Edit:
I just thought of really naive aproach! - for fun mostly, but I couldn't keep myself from posting.
Suppose you have a representation of the image. What parts can't be traveled on and what parts can be. Each pixel that can be travelled on is represented by a 1, and every other pixel is represented by a 0. Thus the pixels represented by 1s can be seen as nodes on which we can travel.
Each node can reach, at most, 8 other nodes - the adjacent pixels. And the weight of travelling between any two nodes could be set as 1. But we have to account that travelling in a diagonal is a greater distance so that weight should be sqrt(2).
Now we have a great bunch of nodes - each with weights in between them. From here we can apply a djikstra-algorithm to find the best route. (maybe some other algorithm is more beneficial at this point - but djikstras is the only one I'm familiar with).
hum, wonder how bad of a solution this would be. ... again, you probably don't want this solution...
EDIT 2:
I will say this again that this is probably not the best way to do this! You should seriously ask someone with more experience in algorithms and in graphical problems. - This was something I thought of at 3am and was mostly for laughs.
If your question is about calculating routes instead of drawing routes, that's a whole different problem. The standard algorithm for finding efficient routes through a given space are the "A*" (pronounced A-star) algorithms, which are typically what real-time strategy games use when you click a unit and tell it to "go there". It's also got many uses in AI when searching for a transition through a space.
It's not easy to get right, though. It might be easier to find a good game engine that already includes an A* implementation and integrate that into your software.

How to determine if iPad user taps within an irregular shaped image?

I've hooked up a UITapGestureRecognizer to a UIImageView containing the image I'd like to display on an iPad screen and am able to consume the user taps just fine. However, my image is that of a hand on a table and I'd like to know if the user has tapped on the hand or on the table part of the image. I can get the x,y coordinates of the user tap with CGPoint tapLocation = [recognizer locationInView:self.view]; but I'm at a loss for how to map that CGPoint to, say, the region of the image that contains the hand vs. the region that contains the table. Everything I've read so far deals with determining if a CGPoint is in a particular rectangular area, but what if you need to determine if that CGPoint is located in the boundaries of a more irregular shape? Is that even possible? Any suggestions or just pointing me in the right direction would be a big help. Thanks!
You could use pointInside:withEvent: to define the hit area programmatically.
To elaborate, you just take the point and evaluate to see if it falls in the area you're after with a series of if statements. If it does, return TRUE. If it doesn't, return FALSE. If this is related to this post, then you could use a circular conditional to compare the distance of the point to the center of your circle using Pythagorean Theorem.
late to the party,
but the core tool you want here is a "point in polygon" routine.
this is a generic approach, independent of iOS.
google has lots of info,
but the general approach is:
1) define your closed polygon.
- it sounds like this might be a bit of work in your case.
2) choose any point not equal to your original point.
(yes, any point)
3) for each edge in the polygon,
determine if the ray from your original point through the seconds point intersects with that polygon edge.
- this requires a line-segment-intersect-ray routine, also available on the 'tubes.
4) if the number of intersections is odd, it's inside the polygon.
if the count is even, it's outside.
for general geometry-type issues,
i highly recommend Paul Bourke: http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
You can use a bounding rectangle that covers most or all of the hand.
If the user is using his finger to tap either the hand or the table, I doubt that you want him or her to be extremely precise with the tap.
An extension of the bounding rectangle answer,
you could define several smaller bounding rectangles that would approximate a hand without covering the rest of the screen.
OR
you could use a list of rectangles, for each of your objects and put the hand at the end of the list. In this case, if you had a tap on button X on the top right hand of the screen which is technically inside the hand rectangle, it would choose the button X because that rectangle is found first.
define the shape by a black and white bitmap (1 bit per pixel). Check if the particular bit is set. This would eat a lot of memory if you had a lot of large shapes, but for one bitmap with a hand, it should not be a big deal.
define the shape as a polygon. Then you need to do point-in-polygon test. Wikipedia has a wonderful article on this, with links to code here: http://en.wikipedia.org/wiki/Point_in_polygon
iPad libraries might have this already implemented. Sorry, I cannot help you there, not an iPad developer.