How to draw line between two points with animation? - iphone

I have used the code below for drawing a line between two points.
#define PI 3.14159
CGContextBeginPath(ctx);
int x;
for(int y=rect.origin.y; y < rect.size.height; y++)
{
x = ((rect.size.width/4) * sin(((y*4) % 360) * PI/180)) + rect.size.width/2;
if(y == 0)
{
CGContextMoveToPoint(ctx, x, y);
}
else
{
CGContextAddLineToPoint(ctx, x, y);
}
}
CGContextStrokePath(ctx);
But I want to implement this with animation. How would it be possible? Can you help me?

Related

Rotating a Line by an Angle

I have a line drawn between 2 points A(100, 100) and B(100, 200) which I would like to rotate about point A by x degrees. Apparently the code only works for 45 degrees. I tried 90 degrees and it looks way wrong. I hope someone will be able to point out what's wrong.
float newX = 100.0f;
float newY = 100.0f;
float newX1 = 100.0f;
float newY1 = 200.0f;
float degree = 90.0;
float x, y;
x = newX1;
y = newY1;
x -= newX;
y -= newY;
newX1 = (x * cos(degree)) - (y * sin(degree));
newY1 = (x * sin(degree)) + (y * cos(degree));
newX1 += newX;
newY1 += newY;
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, newX, newY);
CGContextAddLineToPoint(ctx, newX1, newY1);
CGContextStrokePath(ctx);

Detect paper as series of points with OpenCV

I'm attempting to detect a piece of paper in a photo on the iPhone using OpenCV. I'm using the code from this question: OpenCV C++/Obj-C: Detecting a sheet of paper / Square Detection
Here's the code:
- (void)findEdges {
image = [[UIImage imageNamed:#"photo.JPG"] retain];
Mat matImage = [image CVMat];
find_squares(matImage, points);
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
[imageView setFrame:CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height)];
[self addSubview:imageView];
[imageView setAlpha:0.3f];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 0.8);
CGFloat scaleX = self.frame.size.width / image.size.width;
CGFloat scaleY = self.frame.size.height / image.size.height;
// Draw the detected squares.
for( vector<vector<cv::Point> >::const_iterator it = points.begin(); it != points.end(); it++ ) {
vector<cv::Point> square = *it;
cv::Point p1 = square[0];
cv::Point p2 = square[1];
cv::Point p3 = square[2];
cv::Point p4 = square[3];
CGContextBeginPath(context);
CGContextMoveToPoint(context, p1.x * scaleX, p1.y * scaleY); //start point
CGContextAddLineToPoint(context, p2.x * scaleX, p2.y * scaleY);
CGContextAddLineToPoint(context, p3.x * scaleX, p3.y * scaleY);
CGContextAddLineToPoint(context, p4.x * scaleX, p4.y * scaleY); // end path
CGContextClosePath(context);
CGContextSetLineWidth(context, 4.0);
CGContextStrokePath(context);
}
}
double angle( cv::Point pt1, cv::Point pt2, cv::Point pt0 ) {
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
void find_squares(Mat& image, vector<vector<cv::Point> >& squares) {
// blur will enhance edge detection
Mat blurred(image);
medianBlur(image, blurred, 9);
Mat gray0(blurred.size(), CV_8U), gray;
vector<vector<cv::Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 3; c++)
{
int ch[] = {c, 0};
mixChannels(&blurred, 1, &gray0, 1, ch, 1);
// try several threshold levels
const int threshold_level = 2;
for (int l = 0; l < threshold_level; l++)
{
// Use Canny instead of zero threshold level!
// Canny helps to catch squares with gradient shading
if (l == 0)
{
Canny(gray0, gray, 10, 20, 3); //
// Dilate helps to remove potential holes between edge segments
dilate(gray, gray, Mat(), cv::Point(-1,-1));
}
else
{
gray = gray0 >= (l+1) * 255 / threshold_level;
}
// Find contours and store them in a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Test contours
vector<cv::Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
}
Here's the input image:
Here's the result:
What am I doing wrong?

How to display Text on a PieChart?

I have Created a PieChart Using the following Code. Here, I Have taken the sum of x and y as 100% to display the x% as red color and y% as green color on pieChart.
On the red color i want to display the exact x% (Example 45% (or) 55%) and on the green color i want to display the exact y% (Example 55% (or) 45%).
In my application the X and Y values are Variables. Those Values are changing each time when i run my application. I want to display the pieChart with green and red colors with the percentage on those regions. How Can i make frames to display percentages on that regions?
.m file
- (void)drawRect:(CGRect)rect
{
// Drawing code
int x = 25;
int y = 42;
float sum = x + y;
float mult = (360/sum);
float startDeg = 0;
float endDeg = 0;
int x = 74;
int y = 60;
int r = 60;
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx, 1.0, 1.0, 1.0, 1.0);
CGContextSetLineWidth(ctx, 2.0);
startDeg = 0;
endDeg = (x * mult);
if(startDeg != endDeg)
{
CGContextSetRGBFillColor(ctx, 0.0, 0.8, 0.0, 1.0);
CGContextMoveToPoint(ctx, x, y);
CGContextAddArc(ctx, x, y, r, (startDeg)*M_PI/180.0, (endDeg)*M_PI/180.0, 0);
CGContextClosePath(ctx);
CGContextFillPath(ctx);
}
startDeg = endDeg;
endDeg = endDeg + (y * mult);
if(startDeg != endDeg)
{
CGContextSetRGBFillColor(ctx, 0.8, 0.0, 0.0, 1.0);
CGContextMoveToPoint(ctx, x, y);
CGContextAddArc(ctx, x, y, r, (startDeg)*M_PI/180.0, (endDeg)*M_PI/180.0, 0);
CGContextClosePath(ctx);
CGContextFillPath(ctx);
}
}
You can draw a string with NSString drawInRect during the drawRect method. For example:
[percentageString drawInRect:CGRectMake(100, 100, 30, 20)
withFont:font
lineBreakMode:UILineBreakModeClip
alignment:UITextAlignmentLeft];
Update
Here is how to find the x, y for placing text:
CGFloat startDegree = 180; // start at 180
CGFloat endDegree = degree; // end at some value
CGFloat mid = (endDegree + startDegree) / 2; // find midpoint
CGFloat rad = DEGREES_TO_RADIANS(mid); // convert to radians
// center is center of pie chart
// radius is how big the pie chart is
// 30 is extra to draw text outside circle
CGFloat x = center.x + ((radius + 30) * cos(rad));
CGFloat y = center.y + ((radius + 30) * sin(rad));
NSLog(#"X Y: %f %f %f", x, y, degree);
NSString *text = #"Test";
[text drawInRect:CGRectMake(x, y, 50, 44) withFont:[UIFont systemFontOfSize:14.0f]];
I had a sample pie chart so I used that. It has a slider with degrees. In the example, I can draw a segment of the pie chart from 180 degrees to 360 degrees.

Draw Brush Is Not Completly Drawing When Moving Diagonally?

When drawing from currentPoint.x and currentPoint.y to lastPoint.x and lastPoint.y
in diagonally right upside or right downwards it's giving me gaps (spaces) in the drawn line as shown in below image.
-(void)brushType{
UIGraphicsBeginImageContext(drawImage.frame.size);
CGContextRef Mycontext = UIGraphicsGetCurrentContext();
int x, cx, deltax, xstep,y, cy, deltay, ystep,error, st, dupe;
int x0, y0, x1, y1;
x0 = currentPoint.x;
y0 = currentPoint.y;
x1 = lastPoint.x;
y1 = lastPoint.y;
// find largest delta for pixel steps
st =(abs(y1 - y0) > abs(x1 - x0));
// if deltay > deltax then swap x,y
if (st) {
(x0 ^= y0);
(y0 ^= x0);
(x0 ^= y0); //swap(x0, y0);
(x1 ^= y1);
(y1 ^= x1);
(x1 ^= y1); // swap(x1, y1);
}
deltax = abs(x1 - x0);
deltay = abs(y1 - y0);
error = (deltax / 4);
y = y0;
if (x0 > x1) {
xstep = -1;
}
else {
xstep = 1;
}
if (y0 > y1) {
ystep = -1;
}
else {
ystep = 1;
}
for ((x = x0); (x != (x1 + xstep)); (x += xstep))
{
(cx = x);
(cy = y); // copy of x, copy of y
// if x,y swapped above, swap them back now
if (st) {
(cx ^= cy);
(cy ^= cx);
(cx ^= cy);
}
(dupe = 0); // initialize no dupe
if(!dupe) {
CGContextSetRGBStrokeColor(Mycontext, 0.0, 0.0, 0.0, 1.00f);
CGContextMoveToPoint(Mycontext, cx+brushSize*4,cy-brushSize/2);
CGContextAddLineToPoint(Mycontext, cx-brushSize/2, cy+brushSize*4);
}
(error -= deltay); // converge toward end of line
if (error < 0) { // not done yet
(y += ystep);
(error += deltax);}
}
CGContextStrokePath(Mycontext);
[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
brushSize = 4;
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:drawImage];
currentPoint.y -= 20;
[self brushType];
}
If anyone has an idea please helpe me to fix this issue!
I see a bunch of problems with your function, most of which are poor style. You are using XOR-swap, which makes the code hard to read. You declare all of your variables at the top of the method, making it harder to understand the lifetime of each variable. You put unnecessary parentheses all over the place, making the code harder to read. You call CGContextSetRGBStrokeColor repeatedly in your loop, but you only need to set the stroke color once. So first let's rewrite your method to fix those problems:
static inline void swap(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
-(void)brushType {
int x0 = currentPoint.x;
int y0 = currentPoint.y;
int x1 = lastPoint.x;
int y1 = lastPoint.y;
int deltax = abs(x1 - x0);
int deltay = abs(y1 - y0);
int needSwap = deltay > deltax;
if (needSwap) {
swap(&x0, &y0);
swap(&x1, &y1);
swap(&deltax, &deltay);
}
int error = deltax / 4;
int y = y0;
int xstep = x0 > x1 ? -1 : 1;
int ystep = y0 > y1 ? -1 : 1;
CGSize size = self.drawImage.bounds.size;
UIGraphicsBeginImageContext(size); {
CGContextRef gc = UIGraphicsGetCurrentContext();
for (int x = x0; x != x1 + xstep; x += xstep)
{
int cx = x;
int cy = y;
if (needSwap)
swap(&cx, &cy);
CGContextMoveToPoint(gc, cx + brushSize*4, cy - brushSize/2);
CGContextAddLineToPoint(gc, cx - brushSize/2, cy + brushSize*4);
error -= deltay; // converge toward end of line
if (error < 0) { // not done yet
y += ystep;
error += deltax;
}
}
[UIColor.blackColor setStroke];
CGContextStrokePath(gc);
[self.drawImage.image drawInRect:CGRectMake(0, 0, size.width, size.height)];
self.drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
} UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
So now it's easier to understand that you're stepping along the straight line from lastPoint to currentPoint. If that line is primarily horizontal, you increment x by 1 (or -1) at each step, and increment y as necessary to stay close to the true straight line. If the line is primarily vertical, you swap x and y.
Here's the problem. Suppose the straight line is at a 45 degree angle. You're going to increment x by 1 and y by 1 at every step. Pythagoras tells us that you're moving a distance of sqrt(2) ≈ 1.4142 points per step. Since your “brush” is stroked with the default stroke width of 1 point, there's a gap between adjacent stamps of the brush.
The correct way to fix this is to stop using ints and error correction terms to compute the points along the line. Core Graphics and UIKit use CGFloats anyway, so by using ints you're not only getting less accurate results, you're triggering extra conversions from CGFloat to int and back.
What you need to do is store x and y as CGFloat, and increment them both at each step so that the distance between brush stamps is 1.
-(void)brushType {
CGFloat dx = currentPoint.x - lastPoint.x;
CGFloat dy = currentPoint.y - lastPoint.y;
CGFloat length = hypotf(dx, dy);
dx /= length;
dy /= length;
CGSize size = self.drawImage.bounds.size;
// Bonus! This works correctly on Retina devices!
UIGraphicsBeginImageContextWithOptions(size, NO, self.drawImage.window.screen.scale); {
CGContextRef gc = UIGraphicsGetCurrentContext();
for (CGFloat i = 0; i < length; ++i) {
CGFloat x = lastPoint.x + i * dx;
CGFloat y = lastPoint.y + i * dy;
CGContextMoveToPoint(gc, x + brushSize * 4, y - brushSize / 2);
CGContextAddLineToPoint(gc, x - brushSize / 2, y + brushSize * 4);
}
[UIColor.blackColor setStroke];
CGContextSetLineWidth(gc, 1.01);
CGContextStrokePath(gc);
[self.drawImage.image drawAtPoint:CGPointZero];
self.drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
} UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
If you use this version of the method, you'll find that it looks much better, but still not quite perfect:
The problem, I believe, is that diagonal strokes are drawn using anti-aliasing, and anti-aliasing is an approximation. If you drawn two width-1 lines 1 point apart, the partial colorings that each line contributes to the common pixels don't add up perfectly.
An easy, cheesy way to fix it is to make your stroke width a little wider. Just add this before CGContextStrokePath:
CGContextSetLineWidth(gc, 1.1);
Result:

problem in drawing line

brushSize=1.0f;
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)]; //originally self.frame.size.width, self.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapButt); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushSize);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 1.0, 0.0, 1.0);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x+brushSize*4,lastPoint.y-brushSize);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x-brushSize, lastPoint.y+brushSize*4);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I am using this Code for drawing......In touchesmoved.
i do getting gaps when iam drawing fastly... what to do?
You should use UITouch's previousLocationInView: method to get the previous known point you finger was and draw a line between that point and the current location.
Another solution is to keep the currentPoint in a static variable between two calls.
CGContextBeginPath(Mycontext);
CGContextMoveToPoint(Mycontext, lastPoint.x, lastPoint.y);
int x, cx, deltax, xstep,y, cy, deltay, ystep,error, st, dupe;
int x0, y0, x1, y1;
x0 = currentPoint.x;
y0 = currentPoint.y;
x1 = lastPoint.x;
y1 = lastPoint.y;
// find largest delta for pixel steps
st = (abs(y1 - y0) > abs(x1 - x0));
// if deltay > deltax then swap x,y
if (st) {
(x0 ^= y0); (y0 ^= x0); (x0 ^= y0); // swap(x0, y0);
(x1 ^= y1); (y1 ^= x1); (x1 ^= y1); // swap(x1, y1);
}
deltax = abs(x1 - x0);
deltay = abs(y1 - y0);
error = (deltax / 2);
y = y0;
if (x0 > x1) { xstep = -1; }
else { xstep = 1; }
if (y0 > y1) { ystep = -1; }
else { ystep = 1; }
for ((x = x0); (x != (x1 + xstep)); (x += xstep))
{
(cx = x); (cy = y); // copy of x, copy of y
// if x,y swapped above, swap them back now
if (st) { (cx ^= cy); (cy ^= cx); (cx ^= cy); }
(dupe = 0); // initialize no dupe
if(!dupe) {
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), cx+brushSize*4,cy-brushSize);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), cx-brushSize, cy+brushSize*4);
}
(error -= deltay); // converge toward end of line
if (error < 0) { // not done yet
(y += ystep);
(error += deltax);
}
}
CGContextStrokePath(Mycontext);
Image_Cookie.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;