create cgpathref(s) from image? - sprite-kit

I have been searching for a way to to take my paths (in blue below) and get a CGPathRef from them, there would be 3/6 paths created per tile. I heard something about using an Alpha but I was unsure because all references were about creating a polygon and not a path.
In the end you will have sprites that followPath per tile point they land on.

Related

How to create a SKPhysicsBody in a shape of a parabola?

I need to create a SKSpriteNode or a SKShapeNode such that it would look like the image below. I can figure out how to use a texture to get the shape of the line but I can't seem to find a way to make the physics body. It needs to be made out of two horizontal lines which can change their y position and the middle parabola-like shape joins the other two lines with a specified maximum point (the maximum would hopefully be a variable).
(Note:- the blue and green lines are just to highlight that the image is compromised of three objects)
Is this possible? Thanks!

How to draw animations of feathered/blurred stroked paths

In this thread I asked how to create animations of blurred-edged filled paths, to create a "circle wipe" animation with feathered edges.
The solution is to install the path in the shadowPath property of a layer an animate that. Core Graphics applies a variable amount of blur to the shadow path, and the shadowPath property is animatable. However, it fills whatever path you install into it. You can't use it to draw feathered/blurred stroked paths, since what gets rendered as the shadow is a closed, filled version of whatever path you install.
There is a method copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:) that will convert a stroked path into a closed path that traces the outline of the stroked path. If you fill the output of that method, you get what looks like a stroked version of the original path. You can install that converted path into the shadowPath of a layer and get a nice, blurred stroked path.
However, under the covers, the copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:) method does some things that make the resulting path unsuitable for path animation. If you look at Rob Mayoff's excellent answer in this thread, you see that for inside corners, Core Graphics creates odd triangles that overlap with the interior of the stroked shape. It also seems to sometimes vary the number of vertices in the output of the path depending on it's shape.
For paths stroked with a fairly thin line width, they animate well. Here is an example:
But when you increase the line width and/or blur radius, things go "all pear-shaped" (meaning they go very wrong.) Here is an animation of the same curve with different settings:
You can see the project that generates these images on Github, and try it yourself.
Question:
Does anybody know of a way to convert a stroked path to a filled path that does not have the artifacts described in Rob Mayoff's post?
If I have to I can write my own version of copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:) that creates a filled path with a fixed number of vertexes and without the artifacts that method creates, but it would be pretty involved. I'm hoping there is an existing solution.

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.”

Lightning effect in opengl es

Is there a way to create a lightning effect on the iPhone using opengl?(like this app)
Right now I have modified the glpaint sample to draw random points around a line (between two points that the user touches) and then connecting them, but the result is a zigzag line that constantly jumps around and lags horribly on the actual device.
you'll probably just want to make a triangle strip from the center of the device to the point that is being touched, then apply a drawn lightning texture to that resultant polygon.
You can animate the texture in order to get the jumping lightning effect.
A simple way to create a lightning effect is to compute the lightning path using a 2D Perlin function, rendering it to a glow buffer, blurring it with a Gaussian blur shader, and merging it with the scene. You can make the lightning move by computing two paths (start and end) with an identical number of path nodes and moving each node of the start path successively towards the corresponding node of the end path. Once the end path has been reached, it becomes the start path and a new end path is computed.

Fill a touch drawn path of CGPoints using OpenGL or CoreGraphics

I have a NSArray of points that make up a path. I can detect when it self-intersects. When this happens, I try to fill the path.
First I used CoreGraphics, now I'm using openGl to draw a triangle array. Doesn't work well as you can see in the image.
How do I fill only the circular area while leaving the "tail" alone? I was thinking of a reverse flood fill but don't think CG has any API functions for this...
Maybe instead of actually drawing the path you can just approximate the diameter of the path and draw a circle with your approximation.
Here is some code to detect a circle gesture on the iPhone:
http://www.mobileorchard.com/iphone-circle-gesture-detection/
Record all of the points in a doubly-linked list. When it comes time to fill, walk the list from the start and find the point that's closest to the end. Then, lineto that point, then lineto each point in reverse order, stopping with the second point in the list. The fill will implicitly close the path, which will jump from where you left off (the second point) back to the start (first) point.
This is just off the top of my head; you can play with a couple of variations on this to see what works best. You might record the closest previous node in each node, but this could get expensive for many nodes.