Sine CAShapeLayer as a mask of CALayer
Asked Answered
M

1

5

I'm trying to implement the next curious thing: I have a few dots and I'd like to link them with sine waves

Next method returns the array of dots for making plot:

- (NSArray *)plotPointsBetweenPoint:(CGPoint)pointA andPoint:(CGPoint)pointB {
  double H = fabs(pointB.y - pointA.y);
  double L = fabs(pointB.x - pointA.x);

  NSInteger granularity = 40;
  NSMutableArray *array = [NSMutableArray arrayWithCapacity:granularity + 1];
  for (int i = 0; i <= granularity; ++i) {
    CGFloat x = M_PI*(float)i/(granularity);
    CGFloat y = -cosf(x);
    CGFloat multiplier = pointA.y > pointB.y ? 1 : -1;
    CGPoint newPoint = CGPointMake(pointA.x + L*(float)i/granularity, pointA.y - multiplier*H*(1.0 + y)/2.0);
    [array addObject:[NSValue valueWithCGPoint:newPoint]];
  }
  return array;
}

After it I create the special UIBezierPath:

- (UIBezierPath *)sineWaveWithPoints:(NSArray *)points {
  UIBezierPath *path = [[UIBezierPath alloc] init];
  NSMutableArray *array = [NSMutableArray array];

  for (int i = 0; i < points.count - 1; ++i) {
    [array addObjectsFromArray:[self plotPointsBetweenPoint:[points[i] CGPointValue] andPoint:[points[i+1] CGPointValue]]];
  }
  [path moveToPoint:[array[0] CGPointValue]];
  for (NSValue *value in array) {
    CGPoint point = [value CGPointValue];
    [path addLineToPoint:point];
  }
  [path closePath];
  path.lineWidth = 2.0;
  [path stroke];
  return path;
}

Next I'm adding this path using CAShapeLayer:

CAGradientLayer *gradientLayer = [self gradientLayer];
CAShapeLayer *maskLine = [CAShapeLayer layer];
maskLine.path = [self sineWaveWithPoints:pointsArray].CGPath;
maskLine.lineWidth = self.plotWidth;
gradientLayer.mask = maskLine;
[self.view.layer addSublayer:gradientLayer];

this is what I have in the end

So I'd like to have gradient sine wave between this points. What exactly is wrong in my actions?

Ideal: enter image description here

Mickelson answered 10/4, 2014 at 11:30 Comment(3)
I'm sorry but I don't understand what you mean with "I'd like to have gradient sine wave between this points". Can you clarify what you mean or maybe even show a mockup (example made in an image editor) of what you are trying to do?Jasen
Sure. The task is to create a plot, y-value point color depends on the value height (from purple to yellow, for example). I decided that the best way to fill the line with such gradient is to create gradient layer with appropriate colors and perform setMask method using created sine CAShapeLayer. P.S. I pinned the render with the main goal.Mickelson
Mask the gradient with the stroked path?Jasen
S
2

The program is behaving correctly - that is, it is doing exactly what you are telling it to do. The problem is that what you are telling it to do (in your code) is not what you say you want (in your words and picture in your question).

First thing you're doing wrong: you said closePath. So it's closing the path! That means that, having drawn the wave, it connects the last point back to the first point with a straight line, giving the shape that you show.

Second thing you're doing wrong: you are failing to manipulate the shape layer. You are making a vanilla CAShapeLayer and leaving it at its defaults. Well, the default is that the fillColor is black (and no strokeColor). You are not changing that. So you are getting your (incorrect) shape filled with black, and no stroke, and hence the resulting mask is the shape of the inside of your (incorrect) shape, giving the gradient mask shape that you show.

Senescent answered 10/4, 2014 at 17:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.