iOS:Quartz2d to Draw floor plans - ios

I am building an app where I wanted show floor plans to the user which is interactive that means they can tap on each area zoom those and find finer details.
I am getting a JSON response from the back-end which has the metadata info of the floor plans, shape info and points. I am planning to parse the points and draw the shapes on a view using quartz (started learning Quartz2d). As a start I took a simple blue print which looks like the below image.
As per the blue print the center is at (0,0) and there are 4 points.
Below are the points that I am getting from backend for the blue print.
X:-1405.52, Y:686.18
X:550.27, Y:683.97
X:1392.26, Y:-776.79
X:-1405.52, Y:-776.79
I tried to draw this
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
for (Shape *shape in shapes.shapesArray) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
BOOL isFirstPoint = YES;
for (Points *point in shape.arrayOfPoints) {
NSLog(#"X:%f, Y:%f",point.x,point.y);
if (isFirstPoint) {
CGContextMoveToPoint(context, point.x, point.y);
//[bpath moveToPoint:CGPointMake(point.x,point.y)];
isFirstPoint = NO;
continue;
}
CGContextAddLineToPoint(context, point.x, point.x);
}
CGContextStrokePath(context);
}
}
But I am getting the below image as result which looks not the correct one
Questions:
Am I in the correct direction of achieving this?
How to draw points at -ve direction?
According to the co-ordinates, the drawing will be very huge, I want to draw it first and then shrink to fit to the screen so that users can later zoom in/ pan etc
UPDATE:
I have achieved some of the basic things using translation and scaling. Now the problem is how I will fit the content drawn, to the view bounds. Since my co-ordinates are pretty big, it goes out of bounds, I want it to fit.
Please find below the test code that I am having.
Any idea how to fit it?
DrawshapefromJSONPoints

I have found a solution to the problem. Now I am able to draw shapes from a set of points and fit them to the screen and also make them zoom able without quality loss. Please find below the link to the project. This can be tweaked to support colors, different shapes, I am currently handling only polygons.
DrawShapeFromJSON

A few observations:
There is nothing wrong with using quartz for this, but you might find it more flexible using OpenGL since you mention requirements for hit-testing areas and scaling the model in realtime.
Your code is assuming an ordered list of points since you plot the path using CGContextMoveToPoint. If this is guaranteed by the data contract then fine; however, you will need to write a more intelligent renderer if you have multiple closed paths returned in your JSON.
Questions 2 & 3 can be covered with a primer in computer graphics (specifically model-view matrix and transformations). If your world coordinates are centered at (0,0,0) you can scale the vertices by applying a scalar to each vertex. Drawing points on the negative axis will make more sense when you are not using the quartz-2d coordinate system (0,0)-(w, h)

Related

Is it possible to get UIBezierPath current control points after path transformation?

I am working on creating a way for a user to move polygons on screen and change their shape by dragging their corner vertices. I then need to be able to re-draw those modified polygons on other devices - so I need to be able to get final positions of all vertices for all polygons on screen.
This is what I am currently doing:
I draw polygons using UIBezierPath for each shape in the override of the drawRect function. I then allow user to drag them around the screen by applying CGAffineTransformMakeTranslation function and the x and y coordinate deltas in touchedMoved function override. I have the initial control points from which initial polygons are drawn (as described here). But once a path instance is moved on screen, those values don't change - so I am only able to get initial values.
Is there something built - in in the Core Graphics framework that will allow me to grab a set of current control points in a UIBezierPath instance? I am trying to avoid keeping track of those points manually. I will consider using other ways to draw if:
there is a built - in way to detect if a point lies within that polygon (such as UIBezierPath#contains method
A way to easily introduce constraints so user can't move a polygon out of bounds of the superview (I need the whole polygon to be visible)
A way to grab all points easily when user is done
Everything can run under 60fps on iPhone 5.
Thanks for your time!
As you're only applying the transform to the view/layer, to get the transformed control points from the path, simply apply that same transform to the path, and then fetch the control points. Something like:
UIBezierPath* copiedPath = [myPath copy];
[copiedPath applyTransform:[view transform]];
[self yourExistingMethodToFetchPointsFromPath:copiedPath];
The way that you're currently pulling out points from a path is unfortunately at the only API available for re-fetching points from an input UIBezierPath. However - you might be interested in a library I wrote to make working with Bezier path's much simpler: PerformanceBezier. This library makes it significantly easier to get the points from a path:
for(NSInteger i=0;i<[path elementCount];i++){
CGPathElement element = [path elementAtIndex:n];
// now you know the element.type and the element.points
}
In addition to adding functionality to make paths easier to work with, it also adds a caching layer on top of the existing API to make the performance hit of working with paths much much smaller. Depending on how much CPU time you're spending on UIBezierPath methods, this library will make a significant improvement. I saw between 2x and 10x improvement, depending on the operations I was using.

How to draw smooth ink annotation on PDF in iOS using Objective-C

I am drawing ink annotation in pdf using objective-c. Pdf specifications say that we need to provide an array of points for the ink drawing. I am using PoDoFo library.
This is how I am drawing ink annotation currently:
PoDoFo::PdfArray arr; //This is the array of points to be drawn
arr.push_back(X1);
arr.push_back(Y1);
arr.push_back(X2);
arr.push_back(Y2);
arr.push_back(X3);
arr.push_back(Y3);
.
.
.
arr.push_back(Xn-1);
arr.push_back(Yn-1);
arr.push_back(Xn);
arr.push_back(Yn);
PoDoFo::PdfMemDocument* doc = (PoDoFo::PdfMemDocument *)aDoc;
PoDoFo::PdfPage* pPage = doc->GetPage(pageIndex);
//PageIndex is page number
if (! pPage) {
// couldn't get that page
return;
}
PoDoFo::PdfAnnotation* anno;
PoDoFo::EPdfAnnotation type= PoDoFo::ePdfAnnotation_Ink;
PoDoFo::PdfRect rect;
rect.SetBottom(aRect.origin.y);
rect.SetLeft(aRect.origin.x);
rect.SetHeight(aRect.size.height);
rect.SetWidth(aRect.size.width);
//aRect is CGRect where annotation is to be drawn on page
anno = pPage->CreateAnnotation(type , rect);
anno->GetObject()->GetDictionary().AddKey("InkList", arr);
The problem is that how do I make an array which covers every point. I am getting points from touches delegate methods (e.g. TouchesMoved), but when user draw with high velocity then some points/pixels are skipped and pdf just can’t interpolate those skipped points for itself. Bezier curves can interpolate and draw smooth curves but Bezier-curve don’t provide me the array of all the points (including skipped one) and I need such an array so that when the pdf is opened in adobe reader, it shows smooth curves. Right now I get smooth curve in any iOS device but the curve isn’t smooth on adobe reader. Here is the comparison of curves, one drawn using Bezier-curves in simulator and other in adobe reader.
Above picture is taken from iPad simulator which is drawn using Bezier curve and is smooth.
Above picture is taken from adobe reader. You can see the red curve isn’t smooth like the blue one. How do I make it smooth?
According to PDF 1.3 specification for InkList array:
An array of n arrays, each representing a stroked path. Each array is
a series of alternating x and y coordinates in default user space,
specifying points along the path. When drawn, the points are connected
by straight lines or curves in an implementation-dependent way. (See
implementation note 60 in Appendix H.)
And from note 60:
Acrobat viewers always connect the points along each path with
straight lines.
So you can't have smooth line here like bezier curve. This is true up to version 1.7.
The only way to have it smooth is to render as image with Core Graphics and add it to PDF.

Core graphics using too much CPU

I am implementing the following graphics drawRect function but it uses more than 50% of the CPU - any idea on how I could solve that? I just draw a few random lines, but I want that they all have a different width.
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
#autoreleasepool {
CGContextRef context = UIGraphicsGetCurrentContext();
CGMutablePathRef path = CGPathCreateMutable();
float width = rect.size.width;
int nbLine=10; // i want to draw 10 paths
for (int iLine=1;iLine<nbLine;iLine++){
float Pathwidth=0.8*(nbLine-(float)iLine)/nbLine;
CGContextBeginPath(context);
CGContextSetLineWidth(context, Pathwidth); //each path should have its own width
CGPathMoveToPoint(path, NULL, 0,0);
for (int i=0;i<10;i++){
float x=width/(i+1);
float y=1;//for this example, I just put a fixed number here - it's normally an external variable
CGPathAddQuadCurveToPoint(path, NULL, x+width/10, y, x,0);
}
CGContextAddPath(context, path);
CGContextStrokePath(context);
}
CGPathRelease(path);
}
}
thank you !
There are a few things you can try.
Use instruments to find out exactly which line(s) are using the CPU.
Build the path in a UIBezierPath once and then draw them each time in drawRect.
Look at where setNeedsDisplay is being called from. Most likely each time it draws it isn't using up too much CPU. It is very possible that the problem is that it is rapidly drawing over and over.
If you are pressed for performance you can use a GLKView. Core Drawing is based in OpenGL with a whole bunch of optimizations set for graphical clarity and quality. But if those options are slowing you down to the point of non-usability then that may be your best bet.
My second suggestion would be to not call the draw so often. You said it gets called every 4 ms which is 250 times per second. The user can't see that fine of detail, so that is extravagant.
My third suggestion is to use a UIView, draw once, then modify it's transform based on your y variable. It appears as though you could do a simple y scaling to achieve what you are trying to do (no x changes, no width of line changes (after drawing it once)). I could be over simplifying based on your code, but it would be a good thing to try. You could also do a mix of this suggestion and your code and redraw if the y scale transform becomes too large.

iOS Calculation control points for bezier path curve animation given two points

I'm trying to animate the movement of a view using CGPathAddCurveToPoint and generating a bezier path but I'm having trouble figuring out the control points between the origin and the destionation. Any of you knows how can I can calculate dynamically the control points given the origin and destionation of the UIView?
For example (iPad), I'm trying to animate a bird (UIView) flying in from point (455,482) to (31,100). I would like to be a curve movement of bird flying instead of lineal movement. I'll really appreciate your help.
thank you for answers but still wonder how can I calculate the control points.
Can you clarify a little about the effect you're looking for? The choice of control points is not only a function of the starting and end coordinates, but also is function of the effect you're trying to achieve. In the past, animating the motion of an imageview where I wanted the illusion of "picking up and dropping" the view, I've done slight perpendicular offset on the control points both in the same direction, which yields a slightly more dynamic motion than a purely linear motion, but it depends entirely upon what you're trying to do. I've also done this with animating the enlarging and then returning to normal of the transform scale at the same time, to further enrich the illusion of picking up and dropping the animated view.
In your revised answer, you say that you want to show the flight of a bird. Now, that's a fascinating problem. Is the view of the bird from the side, e.g. taking off from one branch to another, in which case, your control points probably would both point straight up a little to show the bird taking off and landing. Or if you're trying to simulate the flapping motion, you might also have interim upward control points, one for each flap. Or is it looking at the bird from below or above, in which case a slight "s" shaped flight pattern might be enough, so you would only need have one control pointing 45 degrees from the take off location pointing along the flight path, and the other control point at the end, pointing 45 degrees back towards the start point, but pointing in the other direction. It all depends upon the effect you're looking for given the app's theoretical the relationship between the bird and the viewer in 3d space.
You might need to show a screen snapshot of the scene and the image for the bird (is it from the side, or from above or below). You might also want to draw an example of the flight pattern you're envisaging, and perhaps we can make other, more concrete, suggestions.
Other resources:
By the way, there are some interesting demonstrations of animation that might be helpful. For example this race car animation by Mike Nachbaur was helpful when I did my first animation, showing me how to draw bezier paths, how to rotate the image along the path, too, etc. I know it's not the flight of a bird, but it's still illustrative. I'm sure there are other great examples online, too. Anyway, these sorts of tutorials might help you bridge the gap between your vision of your app and what the bezier paths might look like.
//Draw points
UIBezierPath *aPath = [UIBezierPath bezierPath];
[aPath setLineWidth: LINE_SIZE];
for (int i=0; i<[results count]; i++) {
CGPoint endPt = [[results objectAtIndex: i] CGPointValue];
if (i == 0) {
[aPath moveToPoint:endPt];
} else {
//Previous (start) point
CGPoint startPt =[[results objectAtIndex: i-1] CGPointValue];
//Default control points
CGPoint cPt1, cPt2;
if(ABS(startPt.x - endPt.x) > ABS(startPt.y - endPt.y)) {
cPt1 = (CGPoint){(startPt.x + endPt.x) / 2, startPt.y};
cPt2 = (CGPoint){cPt1.x, endPt.y};
} else {
cPt1 = (CGPoint){startPt.x, (startPt.y + endPt.y) / 2};
cPt2 = (CGPoint){endPt.x, cPt1.y};
}
//Add curve to end point
[aPath addCurveToPoint: endPt controlPoint1: cPt1 controlPoint2: cPt2];
}
}
[aPath stroke];

PDF Annots on iOS with Quartz

Hmm, spending a couple of days trying to get the PDF annotations on my iPad application.
I'm using the following code to get the annotations, and yes! it works :)
But the rect value is completely different then the IOS rect values.
I can't figure it out how to place UIButtons on the spot where the annotation supposed to be.
For example, i have an annotation in the top left corner of the pdf file.
My /Annots/Rect values are, 1208.93, 2266.28, 1232.93, 2290.28 (WHAT?!)
How can i translate the PDF /Annots /Rect values to iOS x an y coordinates?
CGPDFPageRef page = CGPDFDocumentGetPage(doc, i+1);
CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(page);
CGPDFArrayRef outputArray;
if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray)) {
return;
.... .... ....}
I think those coordinates are in the "default user coordinate space" of the PDF. You need to apply a transformation that sends them into screen coordinates. You can use CGPDFPageGetDrawingTransform to get such a transformation. Make sure you're using the same transformation for drawing the page and the annotations.
I am not sure if this is the problem but the Quartz 2D Coordinate Systems begins at the bottom left.
See Quartz 2D Programming Guide's Coordinate Systems for more information.
Ps. If you got it working, I would like to see the result code for annotating.
EDIT:
Just found this code (not tested):
// flip context so page is right way up
CGContextTranslateCTM(currentContext, 0, paperSize.size.height);
CGContextScaleCTM(currentContext, 1.0, -1.0);
Source

Resources