多重連結バネをやってみた

wonderflにあった面白いものをiPhone(iPad)用に書き直してみるシリーズ。

元ネタはここ。

MeshViewを作る。ViewController.viewにでもはりつけて表示すれば使えまする。

@interface MeshView : UIView {
@private
  NSMutableArray *mi_ar;
}
@end
#import "MeshView.h"
#import <QuartzCore/QuartzCore.h>

@interface Pin : UIView
{
  Pin *right_, *left_, *up_, *down_;
}
@property (nonatomic, assign) Pin *right, *left, *up, *down;
+ (NSArray *)directions;
- (void)setup;
- (void)resetWithLeft:(Pin *)left Right:(Pin *)right Up:(Pin*)up Down:(Pin *)down;
@end
@interface Mi : Pin
{
@private
  CGFloat k, t;
  CGFloat ax, ay, vx, vy;
  BOOL ignoreUpdate;
}
- (void)update;
@end


@implementation MeshView

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        // Initialization code
    CGRect field = CGRectMake(0, 0, 320, 320);
    CGFloat dxy = 30;
    int w = field.size.width / dxy;
    int h = field.size.height / dxy;
    mi_ar = [[NSMutableArray alloc] init];
    CGFloat sx = (field.size.width - dxy * (w - 1)) * .5;
    CGFloat sy = (field.size.height - dxy * (h - 1)) * .5;
    
    Pin *p;
    for (int j = 0 ; j < h; j++) {
      for (int i = 0; i < w; i++) {
        if (j == 0 || j == h - 1 || i == 0 || i == w - 1) {
          p = [[Pin alloc] initWithFrame:CGRectMake(0, 0, dxy, dxy)];
        } else {
          p = [[Mi alloc] initWithFrame:CGRectMake(0, 0, dxy, dxy)];
        }
        [p setup];
        [self addSubview:p];
        p.center = CGPointMake(sx + i * dxy, sy + j * dxy + 80);
        [mi_ar addObject:p];
      }
    }
    
    for (int i = 0; i < [mi_ar count]; i++) {
      if (w - 1 < i && i < [mi_ar count] - w && i % w != 0 &&i % w != w - 1) {
        p = [mi_ar objectAtIndex:i];
        [p resetWithLeft:[mi_ar objectAtIndex:i - 1]
               Right:[mi_ar objectAtIndex:i + 1]
                Up:[mi_ar objectAtIndex:i - w]
              Down:[mi_ar objectAtIndex:i + w]];        
      }
    }

    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)];
    displayLink.frameInterval = 2;
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    return self;
}


// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
  // Drawing code
  CGContextRef ctx = UIGraphicsGetCurrentContext();
  CGContextClearRect(ctx, rect);
  for (id m in mi_ar) {
    if ([m respondsToSelector:@selector(update)]) {
      [m update];
    }
  }

  [[UIColor redColor] setStroke];
  Pin *pn;
  for (Pin *p in mi_ar) {
    if ((pn = p.up) != nil && pn.up == nil) {
      CGContextMoveToPoint(ctx, p.center.x, p.center.y);
      CGContextAddLineToPoint(ctx, pn.center.x, pn.center.y);      
    }
    if ((pn = p.left) != nil && pn.left == nil) {
      CGContextMoveToPoint(ctx, p.center.x, p.center.y);
      CGContextAddLineToPoint(ctx, pn.center.x, pn.center.y);      
    }
    if ((pn = p.right) != nil) {
      CGContextMoveToPoint(ctx, p.center.x, p.center.y);
      CGContextAddLineToPoint(ctx, pn.center.x, pn.center.y);      
    }
    if ((pn = p.down) != nil) {
      CGContextMoveToPoint(ctx, p.center.x, p.center.y);
      CGContextAddLineToPoint(ctx, pn.center.x, pn.center.y);      
    }
  }
  CGContextStrokePath(ctx);

}


- (void)dealloc {
  [mi_ar release];
    [super dealloc];
}

@end


@implementation Pin
@synthesize right = right_, left = left_, up = up_, down = down_;

static NSArray *directions;
static UIColor *pinColor;
+ (NSArray *)directions {
  if (directions != nil) {
    return directions;
  }
  directions = [[NSArray alloc] initWithObjects:@"up", @"left", @"down", @"right", nil]; 
  return directions;
}
+ (UIColor *)color {
  if (pinColor != nil) {
    return pinColor;
  }
  pinColor = [[UIColor blueColor] retain];
  return pinColor;
}
+ (CGFloat)radius {
  return 10;
}
/*
 - (NSString *)description {
 return [NSString stringWithFormat:@"%@ %@ %@ %@", left_, right_, up_, down_];
 }
 */
- (void)setup {
  CGRect rect = self.frame;
  CGFloat radius = [[self class] radius];
  UIGraphicsBeginImageContext(rect.size);
  CGContextRef ctx = UIGraphicsGetCurrentContext();  
  CGContextAddEllipseInRect(ctx, CGRectMake(rect.size.width / 2 - radius / 2, rect.size.height / 2 - radius / 2, radius, radius));
  [[[self class] color] setFill];
  CGContextFillPath(ctx);
  UIImage *contents = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  self.layer.contents = (id)contents.CGImage;  
}
- (void)resetWithLeft:(Pin *)left Right:(Pin *)right Up:(Pin *)up Down:(Pin *)down{
  self.left = left;
  self.right = right;
  self.up = up;
  self.down = down;
  [self setup];
}
@end

@implementation Mi
static UIColor *miColor;
+ (UIColor *)color {
  if (miColor != nil) {
    return miColor;
  }
  miColor = [[UIColor redColor] retain];
  return miColor;
}

- (void)setup {
  [super setup];
  ax = ay = vx = vx = 0;
  t = 0.04;
  k = 0.02;
  UIPanGestureRecognizer *gr = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(move:)] autorelease];
  [self addGestureRecognizer:gr];
}

- (void)dealloc {
  for (UIGestureRecognizer *gr in self.gestureRecognizers) {
    [self removeGestureRecognizer:gr];
  }
  [super dealloc];
}

#pragma mark -
#pragma mark pan action
- (void)move:(UIPanGestureRecognizer *)gr {
  UIGestureRecognizerState state = [gr state];
  if (state == UIGestureRecognizerStateChanged || state == UIGestureRecognizerStateBegan) {
    self.center = [gr locationInView:self.superview];
    CGPoint translation = [gr translationInView:self.superview];
    self.center = CGPointMake(self.center.x + translation.x, self.center.y + translation.y);
    [gr setTranslation:CGPointZero inView:self.superview];
  }
  if (state == UIGestureRecognizerStateBegan) {
    ignoreUpdate = YES;
  } else if (state == UIGestureRecognizerStateEnded) {
    ignoreUpdate = NO;
  }
}

#pragma mark 0
#pragma mark update properties

- (void)update {
  if (ignoreUpdate) {
    return;
  }
  CGFloat dx = 0, dy = 0;

  CGPoint center = self.center;
/*
  Pin *pn;
  pn = left_;
  if (pn != nil) {
    dx += pn.center.x - center.x;
    dy += pn.center.y - center.y;
  }
  pn = right_;
  if (pn != nil) {
    dx += pn.center.x - center.x;
    dy += pn.center.y - center.y;
  }
  pn = up_;
  if (pn != nil) {
    dx += pn.center.x - center.x;
    dy += pn.center.y - center.y;
  }
  pn = down_;
  if (pn != nil) {
    dx += pn.center.x - center.x;
    dy += pn.center.y - center.y;
  }
*/
  

  Pin *pn;
   NSArray *directions = [Pin directions];
  for (NSString *direction in directions) {
    pn = [self valueForKey:direction];
    if (pn == nil) {
      continue;
    }
    dx += pn.center.x - center.x;
    dy += pn.center.y - center.y;
  }

  ax = k * dx;
  ay = k * dy;
  vx += ax - vx * t;
  vy += ay - vy * t;
  self.center = CGPointMake(self.center.x + vx, self.center.y + vy);
}

@end