Bezier curve algorithm in objective-c needs a tweak - objective-c

I asked a quick question Bezier curve algorithm in objective-c that seemed to solve my issues. I'm asking this new question as I think its different enough rather than repurposing the old one.
I have what seems like a working Bezier Curve algorithm but when compared to built in NSBezierPath version there are some major issues. It looks as though certain types of curves are very much distorted.
You can see from the above image the differences, the red line is my function and the lighter color is the built in version. I am not expecting and exat match, pixel for pixel, but as you can see the red lines go way off course at times.
The first method I'm listing is what calls the 2 Bezier methods, it shows that the inputs are the same to both versions.
- (void)MakeBezier
{
int x1 = [self getMegaNumber:2];
int y1 = self.frame.size.height - [self getMegaNumber:2];
int x2 = [self getMegaNumber:2];
int y2 = self.frame.size.height - [self getMegaNumber:2];
int x3 = [self getMegaNumber:2];
int y3 = self.frame.size.height - [self getMegaNumber:2];
int x4 = [self getMegaNumber:2];
int y4 = self.frame.size.height - [self getMegaNumber:2];
int cnt = [self getMegaNumber:2];
NSBezierPath *bezierPath = [[NSBezierPath alloc] init];
[bezierPath setLineWidth:1.0f];
[bezierPath moveToPoint:NSMakePoint(x1, y1)];
[bezierPath curveToPoint:NSMakePoint(x4, y4) controlPoint1:NSMakePoint(x2, y2) controlPoint2:NSMakePoint(x3, y3)];
// Draw path to image with build in NSBezierPath
[self drawPath:bezierPath fill:NO];
// Draw path with custom algorithm
[self drawBezierFrom:NSMakePoint(x1, y1) to:NSMakePoint(x4, y4) controlA:NSMakePoint(x2, y2) controlB:NSMakePoint(x3, y3) sections:cnt color:4];
}
This next method is the custom algorithm thats used to draw the red lines in the sample image.
- (void)drawBezierFrom:(NSPoint)from to:(NSPoint)to controlA:(NSPoint)a controlB:(NSPoint)b sections:(NSUInteger)cnt color:(NSUInteger)color
{
float qx, qy;
float q1, q2, q3, q4;
int lastx = - 1, lasty;
int plotx, ploty;
float t = 0.0;
while (t <= 1)
{
q1 = t*t*t*-1 + t*t*3 + t*-3 + 1;
q2 = t*t*t*3 + t*t*-6 + t*3;
q3 = t*t*t*-3 + t*t*3;
q4 = t*t*t;
qx = q1*from.x + q2*a.x + q3*to.x + q4*b.x;
qy = q1*from.y + q2*a.y + q3*to.y + q4*b.y;
plotx = round(qx);
ploty = round(qy);
if (lastx != -1)
[self drawLineFrom:NSMakePoint(lastx, lasty) to:NSMakePoint(plotx, ploty) color:color];
else
[self drawLineFrom:NSMakePoint(from.x, from.y) to:NSMakePoint(plotx, ploty) color:color];
lastx = plotx;
lasty = ploty;
t = t + (1.0/(cnt + 0.0f));
}
[self drawLineFrom:NSMakePoint(lastx, lasty) to:NSMakePoint(to.x, to.y) color:color];
}
So my question is; is the custom algorithm way off or is it just missing an edge case for specific kinds of lines, or something else? Either way any help in fixing the algorithm would be very appreciated. To reiterate, I am not looking for a pixel perfect match, but I am expecting the curves to lineup together.

Looking at the Wikipedia page here it seems that your coefficients for the q1-q4 are incorrect. Shouldn't they be symmetric?
It also seems that to.x and b.x should be swapped:
qx = q1*from.x + q2*a.x + q3*to.x + q4*b.x;
qy = ...

Related

iBeacon Trilateration - Vector Math

I'm having a bit of trouble with Trilateration using iBeacon's. I found a formula using vector math in this thread, and I implemented it in my project. However it seems to be giving me odd numbers.
It seems like it works most of the time, however I've noticed that sometimes it will give me extremely similar answers when using distances that are vastly different, which seems odd to me.
CGPoint pointA = [[curBlufiPoints objectAtIndex:0] CGPointValue];
CGPoint pointB = [[curBlufiPoints objectAtIndex:1] CGPointValue];
CGPoint pointC = [[curBlufiPoints objectAtIndex:2] CGPointValue];
/*Point A = (40, 612)
Point B = (379, 788)
Point C = (0, 352)*/
float distanceA = [[distances objectAtIndex:0] floatValue];
float distanceB = [[distances objectAtIndex:1] floatValue];
float distanceC = [[distances objectAtIndex:2] floatValue];
/*distanceA = 13.535637
distanceB = 46.931522
distanceC = 51.585461
----OR----
distanceA = 349.9057;
distanceB = 352.84134;
distanceC = 353.37515;*/
CGPoint P2MinusP1 = CGPointMake(pointB.x - pointA.x, pointB.y - pointA.y);
CGPoint P3MinusP1 = CGPointMake(pointC.x - pointA.x, pointC.y - pointA.y);
CGFloat magP2MinusP1 = sqrt(pow((P2MinusP1.x), 2) + pow((P2MinusP1.y), 2));
CGPoint eX = CGPointMake(P2MinusP1.x / magP2MinusP1, P2MinusP1.y / magP2MinusP1);
CGFloat i = eX.x * P3MinusP1.x + eX.y * P3MinusP1.y;
CGPoint eXTimesI = CGPointMake(eX.x * i, eX.y * i);
CGPoint P3MinusP1MinusITimesEX = CGPointMake(P3MinusP1.x - eXTimesI.x, P3MinusP1.y - eXTimesI.y);
CGFloat magP3MinusP1MinusITimesEX = sqrt(pow(P3MinusP1MinusITimesEX.x, 2) + pow(P3MinusP1MinusITimesEX.y, 2));
CGPoint eY = CGPointMake(P3MinusP1MinusITimesEX.x / magP3MinusP1MinusITimesEX, P3MinusP1MinusITimesEX.y / magP3MinusP1MinusITimesEX);
CGFloat j = eY.x * P3MinusP1.x + eY.y * P3MinusP1.y;
CGFloat x = (pow(distanceA, 2) - pow(distanceB, 2) + pow(magP2MinusP1, 2)) / (2 * magP2MinusP1);
CGFloat y = (pow(distanceA, 2) - pow(distanceC, 2) + pow(i, 2) + pow(j, 2)) / (2 * j) - (i * x) / j;
CGPoint finalPoint = CGPointMake(pointA.x + x * eX.x + y * eY.x, pointA.y + x * eX.y + y * eY.y);
NSLog(#"%f %f %f %f", finalPoint.x, finalPoint.y);
//finalPoint.x = ~343
//finalPoint.y = ~437
As you can see from the values I've commented in the code above, when I am using the same points, but different distances I end up with the same result. It doesn't make sense to me how they can both have around the same answer. If the point (343, 437), (the answer I get for both sets of input) is 13.5 units away from point (40, 612), how can the same point also be 349.9 units away?
I'm not sure where my math has gone wrong but I think that something about calculating x and y is where the problems comes in. I've discovered that the lines "pow(distanceA, 2) - pow(distanceB, 2)" and "pow(distanceA, 2) - pow(distanceC, 2)" give me approximately the same answer for both sets of numbers, which is why x and y end up being the same regardless of which set of numbers I use.
I don't think that this should be the case, any help would be greatly appreciated, thank you!
I tried your code with a sample (and bad looking code test).
The testing code is here.
To debug, I used the "3D" new feature of XCode 6, to see the points and circle that were out of the screen bounds, because I didn't want to do translation (recentering the plane, etc.)
The main issue (with your first sample test (#[#(13.535637), #(46.931522), #(51.585461)];) is that the circles aren't overlapping. There is NO intersection. So you can't guess the position.
If you draw a circle with center pointA, and radius distanceA, and do it for point B and C, you'll need to find the intersection to geolocalize, which is clearly illustrated on the wiki-related article on the linked question.
I added a "check" method to see if they overlap correctly, with a "allowed mistake", because we're doing some maths with float and there could be a few rounding issue.
The check method consist on recalculating the distance between the finalPoint (coordinate found) and each point. The question is now if they differs (a lot?) from the original ones.
Screenshot with first set of distances
Screenshot with second set of distances

finding a point on a path

I have an app that draws a bezier curve in a UIView and I need to find the X intersect when I set a value for Y. First, as I understand, there isn’t a way to find a point directly of a UIBezierPath but you can locate a point of a CGPath.
First, if I “stroke” my UIBezierPath (as I have done in my code) is this actually creating a CGPath or do I need to take further steps to actually convert this to a CGPath?
Second, I want to find the curves intersect at X by providing the value for Y.
My intention is to automatically calculate X for the given value of Y as the user moves the slider (which moves the curve left or right respectively).
My starting display.
What happens when I currently adjust slider.
What I want my display too look like.
GraphView.h
#import <UIKit/UIKit.h>
#interface GraphView : UIView
{
float adjust;
int x;
int y;
}
- (IBAction)sliderChanged:(id)sender;
- (IBAction)yChanged:(id)sender;
#property (weak, nonatomic) IBOutlet UISlider *sliderValue;
#property (weak, nonatomic) IBOutlet UITextField *xValue;
#property (weak, nonatomic) IBOutlet UITextField *yValue;
#end
GraphView.m
#import "GraphView.h"
#interface GraphView ()
#end
#implementation GraphView
#synthesize sliderValue, xValue, yValue;
- (id)initWithCoder:(NSCoder *)graphView
{
self = [super initWithCoder:graphView];
if (self) {
adjust = 194;
y = 100;
}
return self;
}
- (IBAction)sliderChanged:(id)sender
{
adjust = sliderValue.value;
// Calcualtion of the X Value and setting of xValue.text textField goes here
[self setNeedsDisplay];
}
- (IBAction)yChanged:(id)sender
{
y = yValue.text.intValue;
[self setNeedsDisplay];
[self resignFirstResponder];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
if(touch.phase == UITouchPhaseBegan) {
y = yValue.text.intValue;
[self setNeedsDisplay];
[yValue resignFirstResponder];
}
}
- (void)drawRect:(CGRect)rect
{
UIBezierPath *lines = [[UIBezierPath alloc] init];
[lines moveToPoint:CGPointMake(0, y)];
[lines addLineToPoint:CGPointMake(200, y)];
[lines addLineToPoint:CGPointMake(200, 280)];
[lines setLineWidth:1];
[[UIColor redColor] setStroke];
float dashPattern[] = {2, 2};
[lines setLineDash:dashPattern count:2 phase:0.0];
[lines stroke];
UIBezierPath *curve = [[UIBezierPath alloc] init];
[curve moveToPoint:CGPointMake(0, 280)];
[curve addCurveToPoint:CGPointMake(280, 0) controlPoint1:CGPointMake(adjust, 280) controlPoint2:CGPointMake(adjust, 0)];
[curve setLineWidth:2];
[[UIColor blueColor] setStroke];
[curve stroke];
}
#end
A cubic Bézier curve is defined by 4 points
P0 = (x0, y0) = start point,
P1 = (x1, y1) = first control point,
P2 = (x2, y2) = second control point,
P3 = (x3, y3) = end point,
and consists of all points
x(t) = (1-t)^3 * x0 + 3*t*(1-t)^2 * x1 + 3*t^2*(1-t) * x2 + t^3 * x3
y(t) = (1-t)^3 * y0 + 3*t*(1-t)^2 * y1 + 3*t^2*(1-t) * y2 + t^3 * y3
where t runs from 0 to 1.
Therefore, to calculate X for a given value of Y, you first have to calculate a
parameter value T such that 0 <= T <= 1 and
Y = (1-T)^3 * y0 + 3*T*(1-T)^2 * y1 + 3*T^2*(1-T) * y2 + T^3 * y3 (1)
and then compute the X coordinate with
X = (1-T)^3 * x0 + 3*T*(1-T)^2 * x1 + 3*T^2*(1-T) * x2 + T^3 * x3 (2)
So you have to solve the cubic equation (1) for T and substitute the value into (2).
Cubic equations can be solved explicitly (see e.g. http://en.wikipedia.org/wiki/Cubic_function) or iteratively (for example using the http://en.wikipedia.org/wiki/Bisection_method).
In general, a cubic equation can have up to three different solutions. In your concrete case we have
P0 = (0, 280), P1 = (adjust, 280), P3 = (adjust, 0), P4 = (280, 0)
so that the equation (1) becomes
Y = (1-T)^3 * 280 + 3*T*(1-T)^2 * 280
which simplifies to
Y/280 = 1 - 3*T^2 + 2*T^3 (3)
The right hand side of (3) is a strictly decreasing function of T in the interval [0, 1], so it is not difficult to see that (3) has exactly one solution if 0 <= Y <= 280.
Substituting this solution into (2) gives the desired X value.
It took me a while but the code below is how I solved finding a point on a bezier curve. The math only finds one of the potential 3 values so I suspect if there is more than one it will fail, but in my circumstance my bezier should only ever have one solution since my curve should never cross the same X or Y plane more than once. I wanted to share what I have and I welcome any questions, comments, or suggestions.
#import "Calculation.h"
#implementation Calculation
#synthesize a, b, c, d, xy;
- (float) calc
{
float squareRootCalc =
sqrt(
6*pow(xy,2)*b*d
+4*a*pow(c,3)
-3*pow(b,2)*pow(c,2)
+9*pow(xy,2)*pow(c,2)
-6*a*c*b*d
+6*a*xy*c*b
-18*pow(xy,2)*b*c
+6*a*pow(xy,2)*c
-12*a*xy*pow(c,2)
-2*pow(a,2)*xy*d
+pow(a,2)*pow(d,2)
+4*pow(b,3)*d
+pow(xy,2)*pow(d,2)
-4*pow(b,3)*xy
-4*pow(c,3)*xy
+pow(a,2)*pow(xy,2)
+6*c*b*d*xy
+6*a*c*d*xy
+6*a*b*d*xy
-12*pow(b,2)*d*xy
+6*xy*c*pow(b,2)
+6*xy*b*pow(c,2)
-2*a*pow(xy,2)*d
-2*a*xy*pow(d,2)
-6*c*d*pow(xy,2)
+9*pow(xy,2)*pow(b,2)
-6*a*pow(xy,2)*b)
;
float aCalc = 24*c*d*xy + 24*a*pow(c,2) - 36*xy*pow(c,2) + 4 * squareRootCalc * a;
float bCalc = -12 * squareRootCalc * b;
float cCalc = 12 * squareRootCalc * c;
float dCalc = -4 * squareRootCalc * d;
float xyCalc =
24*xy*a*b
-24*xy*b*d
-12*b*a*d
-12*c*a*d
-12*c*b*d
+8*xy*a*d
+8*pow(b,3)
+8*pow(c,3)
+4*pow(a,2)*d
+24*pow(b,2)*d
-4*xy*pow(a,2)
-4*xy*pow(d,2)
+4*a*pow(d,2)
-12*c*pow(b,2)
-12*b*pow(c,2)
-12*a*b*c
-24*xy*a*c
+72*xy*c*b
-36*xy*pow(b,2)
;
float cubeRootCalc = cbrt(aCalc + bCalc + cCalc + dCalc + xyCalc);
float denomCalc = (a-3*b+3*c-d);
float secOneCalc = 0.5 * cubeRootCalc / denomCalc;
float secTwoCalc = -2 * ((a*c - a*d - pow(b,2) + c*b + b*d - pow(c,2)) / (denomCalc * cubeRootCalc));
float secThreeCalc = (a - 2*b + c) / denomCalc;
return secOneCalc + secTwoCalc + secThreeCalc;
}
- (Calculation *) initWithA:(float)p0 andB:(float)p1 andC:(float)p2 andD:(float)p3 andXy:(float)xyValue
{
self = [super init];
if (self) {
[self setA:p0];
[self setB:p1];
[self setC:p2];
[self setD:p3];
[self setXy:xyValue];
}
return self;
}
- (void) setA:(float)p0 andB:(float)p1 andC:(float)p2 andD:(float)p3 andXy:(float)xyValue
{
[self setA:p0];
[self setB:p1];
[self setC:p2];
[self setD:p3];
[self setXy:xyValue];
}
#end

Parametric Expression of a Bezier Curve

I have used the Parametric Expression of a Bezier Curve to locate a point along my curve and it's working as it should. The problem is I'm setting my t value as the percentage of the y axis and unfortunately (and obviously) it doesn't correlate because my curve is longer than my Y axis. So in this program if I set my Y Value to 75 I want to return the point on my line that sits at the Y value of 25 (the inverse because in iOS the (0, 0) sits at the top left instead of the bottom left as my graph reads). Currently setting my Y value retunes the point on my curve at 75% which has a Y of 15.62.
Anyone have a recommendation of how to get the point on my curve at Y instead of at 75%?
This is a follow-up question to a previous question, finding a point on a path, but I felt it was different enough to warrant its own thread.
#import "GraphView.h"
#interface GraphView ()
{
float yVal;
}
#end
#implementation GraphView
#synthesize myLabel, yValue;
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
yVal = 50;
}
return self;
}
- (IBAction)yValueTextField:(id)sender
{
yVal = yValue.text.intValue;
[self resignFirstResponder];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
float t = yVal / 100;
// Starting point
float p1x = 0;
float p1y = 100;
// Control point 1
float c1x = 50;
float c1y = 100;
// Control point 2
float c2x = 50;
float c2y = 0;
// End Point
float p2x = 100;
float p2y = 0;
CGPoint p1 = CGPointMake(p1x, p1y);
CGPoint c1 = CGPointMake(c1x, c1y);
CGPoint c2 = CGPointMake(c2x, c2y);
CGPoint p2 = CGPointMake(p2x, p2y);
// Cubic Bezier Curver Parmetic Expression
float X = pow((1 - t), 3) * p1x + 3 * pow((1 - t), 2) * t * c1x + 3 * (1 - t) * pow(t, 2) * c2x + pow(t, 3) * p2x;
float Y = pow((1 - t), 3) * p1y + 3 * pow((1 - t), 2) * t * c1y + 3 * (1 - t) * pow(t, 2) * c2y + pow(t, 3) * p2y;
myLabel.text = [NSString stringWithFormat:#"Coord = %.2f, %.2f", X, Y];
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake((X - 2), (Y - 2), 4, 4)];
[[UIColor blackColor] setFill];
[circle fill];
UIBezierPath *curve = [[UIBezierPath alloc] init];
[curve moveToPoint:p1];
[curve addCurveToPoint:p2 controlPoint1:c1 controlPoint2:c2];
[curve setLineWidth:1];
[[UIColor blueColor] setStroke];
[curve stroke];
}
#end
Here is my solution to finding my point on my bezier curve. For more background regarding this see another related post of mine --> finding a point on a path
#import "Calculation.h"
#implementation Calculation
#synthesize a, b, c, d, xy;
- (float) calc
{
float squareRootCalc =
sqrt(
6*pow(xy,2)*b*d
+4*a*pow(c,3)
-3*pow(b,2)*pow(c,2)
+9*pow(xy,2)*pow(c,2)
-6*a*c*b*d
+6*a*xy*c*b
-18*pow(xy,2)*b*c
+6*a*pow(xy,2)*c
-12*a*xy*pow(c,2)
-2*pow(a,2)*xy*d
+pow(a,2)*pow(d,2)
+4*pow(b,3)*d
+pow(xy,2)*pow(d,2)
-4*pow(b,3)*xy
-4*pow(c,3)*xy
+pow(a,2)*pow(xy,2)
+6*c*b*d*xy
+6*a*c*d*xy
+6*a*b*d*xy
-12*pow(b,2)*d*xy
+6*xy*c*pow(b,2)
+6*xy*b*pow(c,2)
-2*a*pow(xy,2)*d
-2*a*xy*pow(d,2)
-6*c*d*pow(xy,2)
+9*pow(xy,2)*pow(b,2)
-6*a*pow(xy,2)*b)
;
float aCalc = 24*c*d*xy + 24*a*pow(c,2) - 36*xy*pow(c,2) + 4 * squareRootCalc * a;
float bCalc = -12 * squareRootCalc * b;
float cCalc = 12 * squareRootCalc * c;
float dCalc = -4 * squareRootCalc * d;
float xyCalc =
24*xy*a*b
-24*xy*b*d
-12*b*a*d
-12*c*a*d
-12*c*b*d
+8*xy*a*d
+8*pow(b,3)
+8*pow(c,3)
+4*pow(a,2)*d
+24*pow(b,2)*d
-4*xy*pow(a,2)
-4*xy*pow(d,2)
+4*a*pow(d,2)
-12*c*pow(b,2)
-12*b*pow(c,2)
-12*a*b*c
-24*xy*a*c
+72*xy*c*b
-36*xy*pow(b,2)
;
float cubeRootCalc = cbrt(aCalc + bCalc + cCalc + dCalc + xyCalc);
float denomCalc = (a-3*b+3*c-d);
float secOneCalc = 0.5 * cubeRootCalc / denomCalc;
float secTwoCalc = -2 * ((a*c - a*d - pow(b,2) + c*b + b*d - pow(c,2)) / (denomCalc * cubeRootCalc));
float secThreeCalc = (a - 2*b + c) / denomCalc;
return secOneCalc + secTwoCalc + secThreeCalc;
}
- (Calculation *) initWithA:(float)p0 andB:(float)p1 andC:(float)p2 andD:(float)p3 andXy:(float)xyValue
{
self = [super init];
if (self) {
[self setA:p0];
[self setB:p1];
[self setC:p2];
[self setD:p3];
[self setXy:xyValue];
}
return self;
}
- (void) setA:(float)p0 andB:(float)p1 andC:(float)p2 andD:(float)p3 andXy:(float)xyValue
{
[self setA:p0];
[self setB:p1];
[self setC:p2];
[self setD:p3];
[self setXy:xyValue];
}
#end

Drawing an Arrow with NSBezierPath between two Points

So I'm trying to generate a NSBezierPath that looks like an arrow between two points, which can lie anywhere on the view, so the startPoint can be larger or smaller than the endpoint.
The arrow is updated while a user drags the mouse like in a drawing app.
I already figured out, that I probably have to use transformations and some math to do trigonometric and have come up with this implementation:
+(NSBezierPath *)arrowWithStart:(NSPoint)startPoint andEnd:(NSPoint)endPoint{
NSBezierPath* path = [NSBezierPath bezierPath];
CGFloat width = endPoint.x - startPoint.x;
CGFloat height = endPoint.y - startPoint.y;
CGFloat angle = atan2(width, height);
NSAffineTransform *tr = [NSAffineTransform transform];
[tr translateXBy:startPoint.x yBy:startPoint.y];
[tr scaleXBy:width yBy:height];
[tr rotateByDegrees:angle];
[path moveToPoint:CGPointZero];
[path lineToPoint:CGPointMake(0.75, 0.7)];
[path lineToPoint:CGPointMake(0.8, 0.65)];
[path lineToPoint:CGPointMake(1, 1)];
[path lineToPoint:CGPointMake(0.65, 0.8)];
[path lineToPoint:CGPointMake(0.7, 0.75)];
[path closePath];
[path transformUsingAffineTransform:tr];
return path;
}
This Code generates pretty nice arrows, when the points are some kind of diagonal,
like
(0,0)
(-2,-2)
to each other, but when the points are getting nearer to a horizontal or vertical line, like
(2,3)
(5,3)
the result becomes a straight line without an arrowhead.
So I think I'm doing something wrong in the transformation Matrix.
If somebody knows where I'm making a mistake it would be great.
What you need is an affine transformation that transforms the point (0, 0) to startPoint and (1, 1) to endPoint. This transformation can be computed directly:
CGFloat tx = startPoint.x;
CGFloat ty = startPoint.y;
CGFloat a = ((endPoint.x - startPoint.x) + (endPoint.y - startPoint.y))/2.;
CGFloat b = (-(endPoint.x - startPoint.x) + (endPoint.y - startPoint.y))/2.;
NSAffineTransformStruct transformStruct = { a, b, -b, a, tx, ty };
NSAffineTransform *tr = [NSAffineTransform transform];
[tr setTransformStruct:transformStruct];
Explanation:
NSAffineTransformStruct transformStruct = { a, b, -b, a, tx, ty };
describes a general combination of translation, scaling and rotation, i.e. an affine transformation without shearing. The requirements (0, 0) -> startPoint, (1, 1) -> endPoint give the equations
startPoint.x = 0 * a + 0 * (-b) + tx
startPoint.y = 0 * b + 0 * a + ty
endPoint.x = 1 * a + 1 * (-b) + tx
endPoint.y = 1 * b + 1 * a + tx
and solving these equations for a, b, tx, ty gives above solution. (See Transform Mathematics in the "Cocoa Drawing Guide" for more information.)
The problem with your original code is that
atan2 takes y as first argument, so atan2(height, width) would compute the angle.
For horizontal or vertical lines, width or height and therefore one scaling factor is zero, this causes the straight lines without arrowhead.

Recursive flood fill on an iPad paint app

Im using recursive flood fill algorithm on my ipad painting app and it crashes with stack overflow i think. Can someone help me solve this problem with example code or good advice because im a noob?
-(void)paintingBucket:(int)point point2:(int)point2 width:(int)width colorAtPoint:(UIColor *)color {
int offset = 0;
int x = point;
int y = point2;
offset = 4*((width*round(y))+round(x));
if (((x<1025 && y<705)||(x<1025) ||(y<705)) && (x>0 && y>0) && (offset<2887648)) {
int alpha = data[offset];
int red = data[offset + 1];
int green = data[offset + 2];
int blue = data[offset + 3];
color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];
if (![color1 isEqual: color] ) {
return;
} else {
color3 = self.currentColor ;
CGFloat r,g,b,a;
[color3 getRed:&r green:&g blue: &b alpha: &a];
int reda = (int)(255.0 * r);
int greena = (int)(255.0 * g);
int bluea = (int)(255.0 * b);
int alphaa = (int)(255.0 * a);
// NSLog(#" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);
data[offset + 3] = alphaa;
data[offset + 2] = reda;
data[offset + 1] = greena;
data[offset] = bluea;
}
}
[self paintingBucket:x+1 point2:y width:width colorAtPoint:color];
[self paintingBucket:x-1 point2:y width:width colorAtPoint:color];
[self paintingBucket:x point2:y+1 width:width colorAtPoint:color];
[self paintingBucket:x point2:y-1 width:width colorAtPoint:color];
}
Here's a naive example fo making this a dynamic algorithm, not recursive.
NSMutableArray *pointsToRender = [NSMutableArray new];
[pointsToRender addObject:startingPoint];
while (pointsToRender.length>0) {
// Get a point from the array and fill it
MyPoint *point = [pointsToRender lastObject];
[pointsToRender removeLastObject];
[self drawColor:color atPoint:point];
// Are any surrounding points needing to be filled?
if (point 1px above) needs to be filled
[pointsToRender addObject : point1Above];
.. repeat for the other three points
}
Yea, this is half objective C and half pseudocode, sorry. But you get the idea. In English it's this :
Make an array of points that you need to fill, containing the start point.
For each point
Fill it
Remove it from the array
Look at it's neighbours. Add each neighbour to the array if it also needs to be filled
repeat until your points to be filled array is empty
This will consume heap, not stack.
Breaking the function into the fillRight and fillLeft methods isn't a long-term solution because if the image gets bigger, overflow may occur again.
I'd recommend using a faster flood-fill algorithm. The 4-Way flood fill described on Wikipedia is easy to implement, but goes through every point four times.
I'd recommend using a scanline fill: see http://lodev.org/cgtutor/floodfill.html - Scanline Floodfill Algorithm With Stack. I replaced my 4-way flood fill in my drawing app and it now is much faster and fills a 1024x768px area in less than a second. Of course, the speed may vary depending on your implementation.
And at last, some notes:
1.You can use CGPoint to store points in an array.
CGPoint point=CGPointMake(100, 50); //Declare a point and put a value in it
Then you can get and set the x and y values using point.x and point.y
2.Use an array to store the points to be checked as deanWombourne suggested.
NSMutableArray * pointsToCheck=[[NSMutableArray alloc]init];//Initialise the array
[pointsToCheck addObject:[NSValue valueWithCGPoint:start]];//Assuming you have a CGPoint start, you need to convert it to a NSValue before you put it into the array like [NSValue valueWithCGPoint:point]
while ([pointsToCheck count]>0){//While there are points to be checked:
NSValue * pointValue=[pointsToCheck objectAtIndex:0];//Get the point: It doesn't matter if you get the first or last item
[pointsToCheck removeObjectAtIndex:0];//Remove the point from the queue so we won't go through it again
CGPoint point=[pointValue CGPointValue];//Get the CGPoint from the NSValue object
//Implement your algorithm here: check for neighbour points, paint the current point or whatever. I'd recommend using scanline fill - see above
//When you need to add a point to the queue (If it was a recursive function, then you'd call the function from itself with different parameters) use:
[pointsToCheck addObject:[NSValue valueWithCGPoint:newPoint];
}
i solved my problem using this two methods. it is a little slow but im working on optimizing this solution.
-(void)fillRight:(int)x point2:(int)y witdh:(int)width {
int x1 = x;
int y1 = y;
int offset = 4*((width*round(y1))+round(x1));
int alpha = data[offset];
int red = data[offset + 1];
int green = data[offset + 2];
int blue = data[offset + 3];
color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];
// NSLog(#"%d %d %# %#", x,y,color,color1);
if([color1 isEqual: color])
{
color3 = self.currentColor ;
CGFloat r,g,b,a;
[color3 getRed:&r green:&g blue: &b alpha: &a];
int reda = (int)(255.0 * r);
int greena = (int)(255.0 * g);
int bluea = (int)(255.0 * b);
int alphaa = (int)(255.0 * a);
// NSLog(#" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);
data[offset + 3] = alphaa;
data[offset + 2] = reda;
data[offset + 1] = greena;
data[offset] = bluea;
[self fillRight:++x1 point2:y1 witdh:width];
x1 = x1 - 1 ;
[self fillRight:x1 point2:y1-1 witdh:width];
[self fillRight:x1 point2:y1+1 witdh:width];
}
}
-(void)fillLeft:(int)x point2:(int)y width:(int)width {
int x1 = x;
int y1 = y;
int offset = 4*((width*round(y1))+round(x1));
int alpha = data[offset];
int red = data[offset + 1];
int green = data[offset + 2];
int blue = data[offset + 3];
color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];
// NSLog(#"%d %d %# %#", x,y,color,color1);
if([color1 isEqual: color])
{
color3 = self.currentColor ;
CGFloat r,g,b,a;
[color3 getRed:&r green:&g blue: &b alpha: &a];
int reda = (int)(255.0 * r);
int greena = (int)(255.0 * g);
int bluea = (int)(255.0 * b);
int alphaa = (int)(255.0 * a);
// NSLog(#" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);
data[offset + 3] = alphaa;
data[offset + 2] = reda;
data[offset + 1] = greena;
data[offset] = bluea;
[self fillLeft:--x1 point2:y1 width:width];
x1 = x1 + 1 ;
[self fillLeft:x1 point2:y1-1 width:width];
[self fillLeft:x1 point2:y1+1 width:width];
}
}
Personally I prefer using the A* algorithm for flood fills. Basically, you color the visited nodes. while searching for path to a point that is known to be outside of your fill area. (-1, -1 does the trick)