I am drawing the curve and line by giving 11 points.Is it possible to detect the point of intersection of two lines (or) two curves (or) one line and one curve.
I am drawing line using
CGMutablePathRef path = CGPathCreateMutable();
for (int i = 0; i < [_points count]; i++)
{
CGPoint pt = [[_points objectAtIndex:i] CGPointValue];
if (i == 0)
{
CGPathMoveToPoint(path, NULL, pt.x+1, pt.y+1);
}
else
{
CGPathAddLineToPoint(path, NULL, pt.x+1, pt.y+1);
}
}
CGContextSetLineWidth(context, 1.0f);
CGContextSetStrokeColorWithColor(context, curveColor.CGColor);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGPathRelease(path);
Curve Draw
CGMutablePathRef path = CGPathCreateMutable();
for (int i = 0; i < [_points count]; i++)
{
CGPoint pt = [[_points objectAtIndex:i] CGPointValue];
NSLog(#"%#",NSStringFromCGPoint(pt));
if (i == 0)
{
CGPathMoveToPoint(path, NULL, pt.x, pt.y);
}
else
{
CGPoint curP = [[_points objectAtIndex:i-1] CGPointValue];
float delta = 1.0f;
for (float pointX = curP.x; fabs(pointX - pt.x) > 1e-5f; pointX += delta)
{
float pointY = func(i-1, pointX);
CGPathAddLineToPoint(path, NULL, pointX, pointY);
}
}
CGContextSetLineWidth(context, 1.0f);
CGContextSetStrokeColorWithColor(context, curveColor.CGColor);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGPathRelease(path);
By using these codes how to find the intersection points.
To find the intersection point of two lines, see this answer.
To intersect a line and a curve - well your curve is just a set of lines, so the line intersects the set of lines if the line intersects any one of the lines in the set.
To intersect a curve and a curve - check each line in one curve against each line in the other.
And there are various ways to optimize like colliding the bounding rectangles of the curves or lines first.
Related
I draw a wave layer by override drawInContext:(CGContextRef)ctx method,since the wave line is sine-wave ,so I cut it into segments by calculate wave value and join them into CGMutablePathRef
Here is what I have done in -(void)drawInContext:(CGContextRef)ctx
//join style
CGContextSetLineJoin(ctx, kCGLineJoinRound);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetAllowsAntialiasing(ctx, true);
CGContextSetShouldAntialias(ctx, true);
CGContextSetFillColorWithColor(ctx, [UIColor themeColor].CGColor);
CGContextSetStrokeColorWithColor(ctx, [UIColor themeColor].CGColor);
CGContextSaveGState(ctx);
CGMutablePathRef mutablePath = CGPathCreateMutable();
CGPathMoveToPoint(mutablePath, NULL, 0, 0);
CGPathAddLineToPoint(mutablePath, NULL, 0, [self getSinPoint:0 WaveHeight:self.waveHeight]);
//Calculate the Sin funciton value to draw lines and join them into path
for (float i = 0; i <= self.bounds.size.width; i+= 1) {
NSInteger pointY = [self getSinPoint:i WaveHeight:self.waveHeight];
CGPathAddLineToPoint(mutablePath, NULL, i, pointY);
}
CGPathAddLineToPoint(mutablePath, NULL, self.bounds.size.width, 0);
CGPathCloseSubpath(mutablePath);
[[UIColor themeColor]set];
CGContextAddPath(ctx, mutablePath);
CGContextFillPath(ctx);
CGContextRestoreGState(ctx);
One solution is using BezierPath to draw the wave,
1.cut the line into segment such as 20
2.if current line index is event, get the control point below the center of the current segment
3.if current line index is odd,then get the control point up the center of the line
...
for (NSInteger i = 0; i <= self.waveCount; i++) {
CGPoint beginPoint = CGPointMake(i * self.waveWidth, pointY);
CGPoint endPoint = CGPointMake((i + 1) * self.waveWidth, pointY);
if (i%2 == 0) {
CGPoint controlPoint = [self getCenterControlPointUp:beginPoint End:endPoint];
CGPathAddQuadCurveToPoint(mutablePath, NULL, controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
}else{
CGPoint controlPoint = [self getCenterControlPointDown:beginPoint End:endPoint];
CGPathAddQuadCurveToPoint(mutablePath, NULL, controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
}
}
...
method to get the control point of a segment line
//Below
-(CGPoint)getCenterControlPointDown:(CGPoint)begin End:(CGPoint)end{
return CGPointMake((begin.x + end.x)/2, (begin.y + end.y)/2 + self.waveHeight * 2);
}
//Above
-(CGPoint)getCenterControlPointUp:(CGPoint)begin End:(CGPoint)end{
return CGPointMake((begin.x + end.x)/2, (begin.y + end.y)/2 - self.waveHeight * 2);
}
I'm using Superpowered SDK, to play a sound.
It has a function that returns unsigned char** called peakWaveForm.
I wrote a custom uiview and try to draw this values, and my view doesn't have good look. My question, is how should be the values to draw my waveform?.
And what kind of variable. An array?. What should be the normal size for a waveform?. The SDK returns an unsigned char** how can i proceed?
- (void)drawRect:(CGRect)updateRect
{
unsigned i, maxIndex;
maxIndex = floor(CGRectGetMaxX(updateRect));
i = floor(CGRectGetMinX(updateRect));
float firstPoint = (float)mPeakWaveForm[0][i];
UIBezierPath *path = [UIBezierPath bezierPath];
path.lineWidth = 2;
[[UIColor blackColor] setFill];
[path moveToPoint:CGPointMake(i,firstPoint)];
for(i; i <= maxIndex; i++)
{
float nextPoint = (float)mPeakWaveForm[0][i];
[path addLineToPoint:CGPointMake(i, nextPoint)];
}
[path fill];
}
I was in the same situation as you, and solved it this way.
First, the waveform you are getting in only a 1-dimension data array, and we want it 2d to have it in both axis.
So what i did was create an array point instead of drawing the path directly and then drawing the path once and another time mirrored arround x-axis as this:
var points = Array<CGPoint>()
for(i; i <= maxIndex; i++)
{
float nextPoint = (float)mPeakWaveForm[0][i];
points.append(CGPoint(x: CGFloat(i), y: CGFloat(nextPoint)))
}
//Move to the center of the view
var xf = CGAffineTransformIdentity;
xf = CGAffineTransformTranslate(xf, 0, halfHeight)
//Scale it as needed (you can avoid it)
xf = CGAffineTransformScale(xf, xscale, yscale)
let path = CGPathCreateMutable()
//Draw the lines
CGPathAddLines(path, &xf, points, points.count)
//Mirror the drawing and draw them again
xf = CGAffineTransformScale(xf, 1.0, -1.0);
CGPathAddLines(path, &xf, points, points.count);
CGPathCloseSubpath(path)
//Draw the path
let ctx = UIGraphicsGetCurrentContext();
CGContextAddPath(ctx, path);
CGContextSetStrokeColorWithColor(ctx,UIColor.whiteColor().CGColor);
CGContextFillPath(ctx);
Sorry about the code being in swift, but the functions are the same that in Objective-C
I have three points, A,B and C, the code to link the three points as following
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
NSArray *points = #[ [NSValue valueWithCGPoint:CGPointMake(10.0f, 15.0f)],
[NSValue valueWithCGPoint:CGPointMake(100.0f, 170.0f)],
[NSValue valueWithCGPoint:CGPointMake(190.0f, 100.0f)],
];
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 2.0f);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
for(int i = 0;i<points.count; ++i){
NSValue *pointValue = [points objectAtIndex:i];
CGPoint point = [pointValue CGPointValue];
if ( i == 0) {
CGContextMoveToPoint(context, point.x, point.y);
} else {
CGContextAddLineToPoint(context, point.x, point.y);
}
}
CGContextStrokePath(context);
}
However, the junction of two lines is an angle, I need to join point B with a round corder. like following image:
how to do it?
What you have is 2 straight lines, what you're asking for is (possibly 2 straight lines, joined by) an arc / bezier curve.
Look at using CGPathAddArcToPoint or CGPathAddCurveToPoint or CGPathAddQuadCurveToPoint.
Background:
I'm making an app that is grid-based using ARC. Basically there is a 4x4-8x8 grid in the center of the screen (that takes up most of the screen). This grid is constructed using a single UIView that is tinted some color and lines drawn with drawRect: (I'll be posting all of the relevant code below for reference).
Each of the cells is contained inside an NSMutableArray for each row that is contained inside another NSMutableArray of the rows:
Array (Rows)
Array (Cols)
Cell Contents
In each of these cells, I either have an actor object or a placeholder object. The placeholder object is essentially just a blank NSObject while the actor object has 8 primitive properties and 1 object property.
For instance, one of the actors is a source, which essentially recursively draws a plain UIView from the source across the grid until it hits another actor or a wall of the grid.
The blue and red lines show different UIViews as they are currently running. With a grid this small, memory doesn't seem to be an issue often; however, when the full game runs with an 8x8 grid, there can feasibly be 50+ drawn UIViews on the screen in addition to the UIImageViews that function as the sources, movables, etc. as well as the other UILabels and buttons that are not included in the grid. There can easily be over 100 UIViews on the screen at once, which, even on the latest devices with the best hardware, causes some pretty bad lag.
I have a feeling that this has to do with the fact that I am rendering 100+ views to the screen at once.
Question:
Can I incorporate all of these dynamically drawn lines into one view, or is there a better solution entirely?
drawRect:
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef color = [[self backgroundColor] CGColor];
int numComponents = CGColorGetNumberOfComponents(color);
CGFloat red = 0, green = 0, blue = 0;
if (numComponents == 4)
{
const CGFloat *components = CGColorGetComponents(color);
red = components[0];
green = components[1];
blue = components[2];
}
CGContextSetRGBFillColor(context, red, green, blue, 0.5);
float top;
float cell = [self cellWidth];
float grid = [self gridWidth];
//Draw
for(int i = 0; i < [self size]+1; i++)
{
top = i*(cell+lineWidth);
CGContextFillRect(context, CGRectMake(0, top, grid, lineWidth));
CGContextFillRect(context, CGRectMake(top, 0, lineWidth, grid));
}
}
addSources:
- (void)addSources:(NSArray*)sources
{
for(int i = 0; i < [sources count]; i++)
{
NSArray* src = [sources objectAtIndex:i];
int row = [[src objectAtIndex:1] intValue];
int column = [[src objectAtIndex:0] intValue];
int direction = [[src objectAtIndex:2] intValue];
int color = [UIColor colorKeyForString:[src objectAtIndex:3]];
float width = [self cellWidth]*scaleActors;
float x = lineWidth + (([self cellWidth]+lineWidth) * (column-1)) + (([self cellWidth]-width)/2.0);
float y = lineWidth + (([self cellWidth]+lineWidth) * (row-1)) + (([self cellWidth]-width)/2.0);
ActorView* actor = [[ActorView alloc] initWithFrame:CGRectMake(x, y, width, width)];
[actor setType:4];
[actor setDirection:direction];
[actor setColorKey:color];
[actor setIsGlowing:YES];
[actor setPicture];
if([self isCreatingLevel])
[actor setCanRotate:YES];
[self addSubview:actor];
[[[self rows] objectAtIndex:(row-1)] replaceObjectAtIndex:(column-1) withObject:actor];
}
}
Edit: Time Profiler Results
By this point, I have roughly 48 drawn views on the screen, (about 70 views total).
I'd suggest WWDC 2012 video iOS App Performance: Responsiveness as a good primer in using Instruments to track down these sorts of issues. Lots of good techniques and tips in that video.
But I agree that this number of views doesn't seem outlandish (though I might be tempted to render this all in CoreGraphics). I'm not using your same model, but here is a pure Core Graphics rendering of that graphic with a single UIView subclass:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// configure the gridlines
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextSetLineWidth(context, 8.0);
CGContextSetLineCap(context, kCGLineCapSquare);
// add the horizontal gridlines
for (NSInteger row = 0; row <= self.rows; row++)
{
CGPoint from = [self coordinateForX:0 Y:row];
CGPoint to = [self coordinateForX:_cols Y:row];
CGContextMoveToPoint(context, from.x, from.y);
CGContextAddLineToPoint(context, to.x, to.y);
}
// add the vertical gridlines
for (NSInteger col = 0; col <= self.cols; col++)
{
CGPoint from = [self coordinateForX:col Y:0 ];
CGPoint to = [self coordinateForX:col Y:_rows];
CGContextMoveToPoint(context, from.x, from.y);
CGContextAddLineToPoint(context, to.x, to.y);
}
// stroke the gridlines
CGContextStrokePath(context);
// now configure the red/blue line segments
CGContextSetLineWidth(context, self.bounds.size.width / _cols / 2.0);
CGContextSetLineCap(context, kCGLineCapRound);
// iterate through our array of points
CGPoint lastPoint = [self.points[0] CGPointValue];
for (NSInteger i = 1; i < [self.points count]; i++)
{
// set the color
if (i % 2)
CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
else
CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
CGPoint nextPoint = [self.points[i] CGPointValue];
// create path
CGPoint from = [self coordinateForCenterX:lastPoint.x Y:lastPoint.y];
CGPoint to = [self coordinateForCenterX:nextPoint.x Y:nextPoint.y];
CGContextMoveToPoint(context, from.x, from.y);
CGContextAddLineToPoint(context, to.x, to.y);
// stroke it
CGContextStrokePath(context);
// save the last point
lastPoint = nextPoint;
}
}
Now, maybe you're doing something else that requires more sophisticated treatment, in which case that WWDC video (or, perhaps iOS App Performance: Graphics and Animations) should point you in the right direction.
I have Drawn the pie-chart using following code:
1)created UIView Custom class Piechart
2)in drawRect
int c=[itemArray count];
[_label removeFromSuperview];
CGFloat angleArray[c];
offset = 0.0;
int sum=0;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetAllowsAntialiasing(context, false);
CGContextSetShouldAntialias(context, false);
for(int i=0;i<[itemArray count];i++)
{
sum+=[[itemArray objectAtIndex:i] intValue];
}
for(int i=0;i<[itemArray count];i++)
{
angleArray[i]=(float)(([[itemArray objectAtIndex:i] intValue])/(float)sum)*(2*3.14); // in radians
CGContextMoveToPoint(context, radius, radius);
if(i==0)
{
CGContextAddArc(context, radius, radius, radius, 0,angleArray[i], 0);
}
else
{
CGContextAddArc(context, radius, radius, radius,offset,offset+angleArray[i], 0);
}
offset+=angleArray[i];
CGContextSetFillColorWithColor(context, ((UIColor *)[myColorArray objectAtIndex:i]).CGColor);
CGContextClosePath(context);
CGContextFillPath(context);
}
3) The problem is How to draw label on each sections.
You can convert polar coordinates to Cartesian coordinates by this formula
x = radius * cos(angle);
y = radius * sin(angle);
Point(x,y) can be the center of your label.
In your case angle is AVG between start and end angle. Radius is the distance between center of pie-chart and label.
- (CGPoint)getApproximateMidPointForArcWithStartAngle:(CGFloat)startAngle andDegrees:(CGFloat)endAngle {
NSLog(#"start angle %f, end angle %f",startAngle,endAngle);
CGFloat midAngle = DEGREES_TO_RADIANS((startAngle + endAngle) / 2);
NSLog(#"midangle %f ",midAngle);
CGFloat x=0;
CGFloat y=0;
x= (250) + (250 * cos(midAngle));
y= (250) + (250 * sin(midAngle));
CGPoint approximateMidPointCenter = CGPointMake(((x+250)/2), ((y+250)/2));
return approximateMidPointCenter; }
here approximateMidPointCenter is Mid-Point of line joining circle center and arc's center.
label.center=approximateMidPointCenter