SignatureViewM.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. //
  2. // SignatureViewM.m
  3. // HelloTriangle
  4. //
  5. // Created by Rui Zhang on 4/20/23.
  6. // Copyright © 2023 Apple. All rights reserved.
  7. //
  8. #import "SignatureViewM.h"
  9. #import "const.h"
  10. #import "RAUtils.h"
  11. //#import "AAPLRenderer.h"
  12. #define STROKE_WIDTH_SMOOTHING 0.5 // Low pass filter alpha
  13. #define VELOCITY_CLAMP_MIN 20
  14. #define VELOCITY_CLAMP_MAX 5000
  15. #define QUADRATIC_DISTANCE_TOLERANCE 3.0 // Minimum distance to make a curve
  16. #define MAXIMUM_VERTECES 100000
  17. static vector_float4 StrokeColor = { 0, 0, 0, 1 };
  18. static vector_float4 clearColor = { 1, 1, 1, 0 };
  19. //typedef struct
  20. //{
  21. //
  22. //} AAPLVertex;
  23. static const uint maxLength = MAXIMUM_VERTECES;
  24. static inline void addVertex(uint *Totallength,NSMutableArray *partlength, PPSSignaturePoint v,PPSSignaturePoint* dest) {
  25. if ((*Totallength) >= maxLength) {
  26. return;
  27. }
  28. dest[*Totallength]=v;
  29. //
  30. // GLvoid *data = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
  31. // memcpy(data + sizeof(PPSSignaturePoint) * (*length), &v, sizeof(PPSSignaturePoint));
  32. // glUnmapBufferOES(GL_ARRAY_BUFFER);
  33. //
  34. partlength[partlength.count-1]=@([partlength[partlength.count-1] intValue] +1);
  35. (*Totallength)++;
  36. #ifdef DEBUG
  37. NSLog(@"dest %p total %d part %ld ,partlength %@ vertex (%f,%f)(%f,%f,%f,%f)",dest,*Totallength,partlength.count-1, partlength[partlength.count-1],v.position[0],v.position[1],v.color[0],v.color[1],v.color[2],v.color[3]);
  38. #endif
  39. }
  40. static inline CGPoint QuadraticPointInCurve(CGPoint start, CGPoint end, CGPoint controlPoint, float percent) {
  41. double a = pow((1.0 - percent), 2.0);
  42. double b = 2.0 * percent * (1.0 - percent);
  43. double c = pow(percent, 2.0);
  44. return (CGPoint) {
  45. a * start.x + b * controlPoint.x + c * end.x,
  46. a * start.y + b * controlPoint.y + c * end.y
  47. };
  48. }
  49. static float generateRandom(float from, float to) { return random() % 10000 / 10000.0 * (to - from) + from; }
  50. static float clamp(float min, float max, float value) { return fmaxf(min, fminf(max, value)); }
  51. // Find perpendicular vector from two other vectors to compute triangle strip around line
  52. static vector_float2 perpendicular(PPSSignaturePoint p1, PPSSignaturePoint p2) {
  53. vector_float2 ret={0,0};
  54. // GLKVector3 ret;
  55. ret[0] = p2.position[1] - p1.position[1];
  56. ret[1] = -1 * (p2.position[0] - p1.position[0]);
  57. return ret;
  58. }
  59. static PPSSignaturePoint ViewPointToMTK(CGPoint viewPoint, CGSize view_size,CGSize drawable_size ,vector_float4 color) {
  60. vector_float2 inverseViewSize={1.0f / view_size.width, 1.0f / view_size.height}; // passed in a buffer
  61. float clipX = (2.0f * viewPoint.x * inverseViewSize.x) - 1.0f;
  62. float clipY = (2.0f * -viewPoint.y * inverseViewSize.y) + 1.0f;
  63. return (PPSSignaturePoint) {
  64. {
  65. clipX*drawable_size.width/2,
  66. clipY*drawable_size.height/2
  67. },
  68. {color[0],color[1],color[2],color[3]}
  69. };
  70. //
  71. // float4 clipPosition(clipX, clipY, 0.0f, 1.0f);
  72. }
  73. @interface SignatureViewM ()
  74. @property(nonatomic,assign)CGPoint mixPoint;
  75. @property(nonatomic,assign)CGPoint maxPoint;
  76. //@property (assign, nonatomic) NSMutableArray *drawable;
  77. @end
  78. @interface SignatureViewM () {
  79. // OpenGL state
  80. // EAGLContext *context;
  81. // GLKBaseEffect *effect;
  82. // GLuint vertexArray;
  83. // GLuint vertexBuffer;
  84. // GLuint dotsArray;
  85. // GLuint dotsBuffer;
  86. //
  87. // Array of verteces, with current length
  88. PPSSignaturePoint SignatureVertexData[maxLength];
  89. uint vertexTotal;
  90. NSMutableArray* vertexLength;
  91. PPSSignaturePoint SignatureDotsData[maxLength];
  92. uint dotsTotal;
  93. // NSMutableArray* dotsLength;
  94. // Width of line at current and previous vertex
  95. float penThickness;
  96. float previousThickness;
  97. // Previous points for quadratic bezier computations
  98. CGPoint previousPoint;
  99. CGPoint previousMidPoint;
  100. PPSSignaturePoint previousVertex;
  101. PPSSignaturePoint currentVelocity;
  102. }
  103. @end
  104. @implementation SignatureViewM
  105. SignatureRenderer *_renderer;
  106. - (void)commonInit {
  107. [self setLineWidth:5];
  108. self.framebufferOnly=false;
  109. vertexLength = [NSMutableArray new];
  110. [vertexLength addObject:@0];
  111. // dotsLength = [NSMutableArray new];
  112. //#define STROKE_WIDTH_MIN 0.004 // Stroke width determined by touch velocity
  113. //#define STROKE_WIDTH_MAX 0.010
  114. self.MaxLineWidth = 0.010*1000;
  115. self.MinLineWidth = 0.004*1000;
  116. self.device = MTLCreateSystemDefaultDevice();
  117. NSAssert(self.device, @"Metal is not supported on this device");
  118. _renderer = [[SignatureRenderer alloc] initWithMetalKitView:self];
  119. _renderer.parts =vertexLength;
  120. _renderer.SignatureVertexData = SignatureVertexData;
  121. NSAssert(_renderer, @"Renderer failed initialization");
  122. // Initialize our renderer with the view size
  123. [_renderer mtkView:self drawableSizeWillChange:self.drawableSize];
  124. self.delegate = _renderer;
  125. // context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
  126. //
  127. // if (context) {
  128. // time(NULL);
  129. //
  130. // self.backgroundColor = [UIColor clearColor];
  131. // self.opaque = YES;
  132. //
  133. // self.context = context;
  134. // self.drawableDepthFormat = GLKViewDrawableDepthFormat24;
  135. // self.enableSetNeedsDisplay = YES;
  136. //
  137. // // Turn on antialiasing
  138. // self.drawableMultisample = GLKViewDrawableMultisample4X;
  139. //
  140. // [self setupGL];
  141. //
  142. // Capture touches
  143. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
  144. pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;
  145. pan.cancelsTouchesInView = YES;
  146. [self addGestureRecognizer:pan];
  147. // For dotting your i's
  148. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
  149. tap.cancelsTouchesInView = YES;
  150. tap.numberOfTapsRequired = 1;
  151. [self addGestureRecognizer:tap];
  152. //
  153. // Erase with long press
  154. // UILongPressGestureRecognizer *longer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
  155. // longer.cancelsTouchesInView = YES;
  156. // [self addGestureRecognizer:longer];
  157. //
  158. // } else [NSException raise:@"NSOpenGLES2ContextException" format:@"Failed to create OpenGL ES2 context"];
  159. }
  160. - (id)initWithCoder:(NSCoder *)aDecoder
  161. {
  162. if (self = [super initWithCoder:aDecoder])
  163. {
  164. [self commonInit];
  165. _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
  166. _maxPoint =CGPointMake(0, 0);
  167. }
  168. return self;
  169. }
  170. - (instancetype)initWithFrame:(CGRect)frameRect device:(id<MTLDevice>)device
  171. {
  172. if(self=[super initWithFrame:frameRect device:device])
  173. {
  174. [self commonInit];
  175. _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
  176. _maxPoint =CGPointMake(0, 0);
  177. }
  178. return self;
  179. }
  180. - (void)setLineWidth:(CGFloat)width {
  181. self.MaxLineWidth = width;//400.0;
  182. self.MinLineWidth = width*0.4;//400.0*0.4;
  183. penThickness = (self.MaxLineWidth - self.MinLineWidth)/2.0;
  184. }
  185. //清除画布
  186. - (void)clear {
  187. [vertexLength removeAllObjects];
  188. [vertexLength addObject:@0];
  189. vertexTotal =0;
  190. // _renderer.SignatureVertexData = SignatureVertexData;
  191. // dotsLength = nil;
  192. self.isSigned = NO;
  193. _mixPoint =CGPointMake(self.bounds.size.width, self.bounds.size.height);
  194. _maxPoint =CGPointMake(0, 0);
  195. [self setNeedsDisplay];
  196. }
  197. - (UIImage *)signatureImage {
  198. return [self Signature2Image];
  199. }
  200. - (NSData *)signatureData {
  201. UIImage* signatureImage = [self Signature2Image];
  202. return UIImagePNGRepresentation(signatureImage);
  203. }
  204. - (UIImage*)Signature2Image
  205. {
  206. if (!self.isSigned)
  207. return nil;
  208. UIImage *screenshot = [self snapshot];
  209. if(screenshot==nil)
  210. {
  211. NSAssert(false, @"screenshot is null");
  212. }
  213. float x = _mixPoint.x - MAX_LINEWIDTH /2.0;
  214. float y = _mixPoint.y - MAX_LINEWIDTH / 2.0;
  215. if(x<0)
  216. x=0;
  217. if(y<0)
  218. y=0;
  219. float screen_scale = [[UIScreen mainScreen] scale];
  220. CGRect rect = CGRectMake(x*screenshot.scale*screen_scale, y*screenshot.scale*screen_scale, (_maxPoint.x-_mixPoint.x+MAX_LINEWIDTH)*screenshot.scale*screen_scale, (_maxPoint.y-_mixPoint.y+MAX_LINEWIDTH)*screenshot.scale*screen_scale);
  221. CGImageRef imageRef=screenshot.CGImage;
  222. CGImageRef imagePartRef=CGImageCreateWithImageInRect(imageRef,rect);
  223. UIImage*cropImage=[UIImage imageWithCGImage:imagePartRef];
  224. CGImageRelease(imagePartRef);;
  225. if(cropImage==nil)
  226. {
  227. NSAssert(false, @"Signature2Image: cropImage is nil");
  228. // int debug = 1;
  229. }
  230. return cropImage;
  231. }
  232. /** 按给定的方向旋转图片 */
  233. - (UIImage*)rotate:(UIImage*)source orient:(UIImageOrientation)orient
  234. {
  235. CGRect bnds = CGRectZero;
  236. UIImage* copy = nil;
  237. CGContextRef ctxt = nil;
  238. CGImageRef imag = source.CGImage;
  239. CGRect rect = CGRectZero;
  240. CGAffineTransform tran = CGAffineTransformIdentity;
  241. rect.size.width = CGImageGetWidth(imag);
  242. rect.size.height = CGImageGetHeight(imag);
  243. bnds = rect;
  244. switch (orient)
  245. {
  246. case UIImageOrientationUp:
  247. return source;
  248. case UIImageOrientationUpMirrored:
  249. tran = CGAffineTransformMakeTranslation(rect.size.width, 0.0);
  250. tran = CGAffineTransformScale(tran, -1.0, 1.0);
  251. break;
  252. case UIImageOrientationDown:
  253. tran = CGAffineTransformMakeTranslation(rect.size.width,
  254. rect.size.height);
  255. tran = CGAffineTransformRotate(tran, M_PI);
  256. break;
  257. case UIImageOrientationDownMirrored:
  258. tran = CGAffineTransformMakeTranslation(0.0, rect.size.height);
  259. tran = CGAffineTransformScale(tran, 1.0, -1.0);
  260. break;
  261. // case UIImageOrientationLeft:
  262. // bnds = swapWidthAndHeight(bnds);
  263. // tran = CGAffineTransformMakeTranslation(0.0, rect.size.width);
  264. // tran = CGAffineTransformRotate(tran, 3.0 * M_PI / 2.0);
  265. // break;
  266. //
  267. // case UIImageOrientationLeftMirrored:
  268. // bnds = swapWidthAndHeight(bnds);
  269. // tran = CGAffineTransformMakeTranslation(rect.size.height,
  270. // rect.size.width);
  271. // tran = CGAffineTransformScale(tran, -1.0, 1.0);
  272. // tran = CGAffineTransformRotate(tran, 3.0 * M_PI / 2.0);
  273. // break;
  274. //
  275. // case UIImageOrientationRight:
  276. // bnds = swapWidthAndHeight(bnds);
  277. // tran = CGAffineTransformMakeTranslation(rect.size.height, 0.0);
  278. // tran = CGAffineTransformRotate(tran, M_PI / 2.0);
  279. // break;
  280. //
  281. // case UIImageOrientationRightMirrored:
  282. // bnds = swapWidthAndHeight(bnds);
  283. // tran = CGAffineTransformMakeScale(-1.0, 1.0);
  284. // tran = CGAffineTransformRotate(tran, M_PI / 2.0);
  285. // break;
  286. default:
  287. return source;
  288. }
  289. UIGraphicsBeginImageContext(bnds.size);
  290. ctxt = UIGraphicsGetCurrentContext();
  291. switch (orient)
  292. {
  293. case UIImageOrientationLeft:
  294. case UIImageOrientationLeftMirrored:
  295. case UIImageOrientationRight:
  296. case UIImageOrientationRightMirrored:
  297. CGContextScaleCTM(ctxt, -1.0, 1.0);
  298. CGContextTranslateCTM(ctxt, -rect.size.height, 0.0);
  299. break;
  300. default:
  301. CGContextScaleCTM(ctxt, 1.0, -1.0);
  302. CGContextTranslateCTM(ctxt, 0.0, -rect.size.height);
  303. break;
  304. }
  305. CGContextConcatCTM(ctxt, tran);
  306. CGContextDrawImage(UIGraphicsGetCurrentContext(), rect, imag);
  307. copy = UIGraphicsGetImageFromCurrentImageContext();
  308. UIGraphicsEndImageContext();
  309. return copy;
  310. }
  311. -(UIImage*) snapshot
  312. {
  313. CIContext * context = [CIContext contextWithMTLDevice:self.device];
  314. CIImage *outputImage = [[CIImage alloc] initWithMTLTexture:self.currentDrawable.texture options:@{kCIImageColorSpace:(__bridge_transfer id)CGColorSpaceCreateDeviceRGB()}];
  315. CGImageRef cgImg = [context createCGImage:outputImage fromRect:CGRectMake(0, 0, [UIScreen mainScreen].nativeBounds.size.width, [UIScreen mainScreen].nativeBounds.size.height)];
  316. UIImage *resultImg = [UIImage imageWithCGImage:cgImg scale:1.0 orientation:UIImageOrientationUp];
  317. resultImg = [self rotate:resultImg orient: UIImageOrientationDownMirrored];
  318. //
  319. // UIImageOrientationUp, // default orientation
  320. // UIImageOrientationDown, // 180 deg rotation
  321. // UIImageOrientationLeft, // 90 deg CCW
  322. // UIImageOrientationRight, // 90 deg CW
  323. // UIImageOrientationUpMirrored, // as above but image mirrored along other axis. horizontal flip
  324. // UIImageOrientationDownMirrored, // horizontal flip
  325. // UIImageOrientationLeftMirrored, // vertical flip
  326. // UIImageOrientationRightMirrored, // vertical flip
  327. CGImageRelease(cgImg);
  328. return resultImg;
  329. // id<MTLTexture> lastDrawableDisplayed = [self.currentDrawable texture];
  330. //
  331. // int width = (int)[lastDrawableDisplayed width];
  332. // int height = (int)[lastDrawableDisplayed height];
  333. // int rowBytes = width * 4;
  334. // int selfturesize = width * height * 4;
  335. //
  336. // void *p = malloc(selfturesize);
  337. //
  338. //
  339. //
  340. // [lastDrawableDisplayed getBytes:p bytesPerRow:rowBytes fromRegion:MTLRegionMake2D(0, 0, width, height) mipmapLevel:0];
  341. //
  342. // CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  343. // CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaFirst;
  344. //
  345. // CGDataProviderRef provider = CGDataProviderCreateWithData(nil, p, selfturesize, nil);
  346. // CGImageRef cgImageRef = CGImageCreate(width, height, 8, 32, rowBytes, colorSpace, bitmapInfo, provider, nil, true, (CGColorRenderingIntent)kCGRenderingIntentDefault);
  347. //
  348. // UIImage *getImage = [UIImage imageWithCGImage:cgImageRef];
  349. // CGImageRelease(cgImageRef);
  350. //
  351. // CGDataProviderRelease(provider);
  352. // CGColorSpaceRelease(colorSpace);
  353. // free(p);
  354. // return getImage;
  355. //
  356. // NSData *pngData = UIImagePNGRepresentation(getImage);
  357. // UIImage *pngImage = [UIImage imageWithData:pngData];
  358. }
  359. - (void)updateStrokeColor {
  360. CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0, white = 0.0;
  361. if ( self.strokeColor && [self.strokeColor getRed:&red green:&green blue:&blue alpha:&alpha]) {
  362. StrokeColor[0]=red;
  363. StrokeColor[1]=green;
  364. StrokeColor[2]=blue;
  365. StrokeColor[3]=alpha;
  366. } else if (self.strokeColor && [self.strokeColor getWhite:&white alpha:&alpha]) {
  367. StrokeColor[0]=white;
  368. StrokeColor[1]=white;
  369. StrokeColor[2]=white;
  370. StrokeColor[3]=alpha;
  371. } else
  372. {
  373. StrokeColor[0]=0;
  374. StrokeColor[1]=0;
  375. StrokeColor[2]=0;
  376. StrokeColor[3]=1;
  377. }
  378. // StrokeColor
  379. }
  380. - (void)setBackgroundColor:(UIColor *)backgroundColor {
  381. [super setBackgroundColor:backgroundColor];
  382. CGFloat red, green, blue, alpha, white;
  383. if ([backgroundColor getRed:&red green:&green blue:&blue alpha:&alpha]) {
  384. clearColor[0] = red;
  385. clearColor[1] = green;
  386. clearColor[2] = blue;
  387. } else if ([backgroundColor getWhite:&white alpha:&alpha]) {
  388. clearColor[0] = white;
  389. clearColor[1] = white;
  390. clearColor[2] = white;
  391. }
  392. }
  393. - (void)setStrokeColor:(UIColor *)strokeColor {
  394. _strokeColor = strokeColor;
  395. [self updateStrokeColor];
  396. }
  397. - (void)updateRectWithPoint:(CGPoint)p
  398. {
  399. if (p.x<_mixPoint.x) {
  400. _mixPoint.x =p.x;
  401. }
  402. if (p.y<_mixPoint.y) {
  403. _mixPoint.y =p.y;
  404. }
  405. if (p.x>_maxPoint.x) {
  406. _maxPoint.x =p.x;
  407. }
  408. if (p.y>_maxPoint.y) {
  409. _maxPoint.y =p.y;
  410. }
  411. }
  412. /*
  413. // Only override drawRect: if you perform custom drawing.
  414. // An empty implementation adversely affects performance during animation.
  415. - (void)drawRect:(CGRect)rect {
  416. // Drawing code
  417. }
  418. */
  419. #pragma mark - Gesture Recognizers
  420. - (void)tap:(UITapGestureRecognizer *)t {
  421. DebugLog(@"on tap");
  422. CGPoint l = [t locationInView:self];
  423. // l=CGPointMake(self.bounds.size.width/2, 0);
  424. [self updateRectWithPoint:l];
  425. if (t.state == UIGestureRecognizerStateRecognized) {
  426. self.isSigned = YES;
  427. DebugLog(@"UIGestureRecognizerStateRecognized");
  428. // glBindBuffer(GL_ARRAY_BUFFER, dotsBuffer);
  429. //
  430. // [dotsLength addObject:@0];
  431. PPSSignaturePoint touchPoint = ViewPointToMTK(l, self.bounds.size, self.drawableSize, StrokeColor);
  432. addVertex(&vertexTotal,vertexLength, touchPoint,SignatureVertexData);
  433. //
  434. PPSSignaturePoint centerPoint = touchPoint;
  435. centerPoint.color =StrokeColor;
  436. addVertex(&vertexTotal,vertexLength, centerPoint,SignatureVertexData);
  437. //
  438. static int segments = 20;
  439. vector_float2 radius = (vector_float2){
  440. clamp(1, 8, penThickness * generateRandom(2, 12)),
  441. clamp(1, 8, penThickness * generateRandom(2, 12))
  442. };
  443. vector_float2 velocityRadius = radius;
  444. float angle = 0;
  445. for (int i = 0; i <= segments; i++) {
  446. PPSSignaturePoint p = centerPoint;
  447. p.position.x += velocityRadius.x * cosf(angle);
  448. p.position.y += velocityRadius.y * sinf(angle);
  449. addVertex(&vertexTotal,vertexLength, p,SignatureVertexData);
  450. addVertex(&vertexTotal,vertexLength, centerPoint,SignatureVertexData);
  451. angle += M_PI * 2.0 / segments;
  452. }
  453. addVertex(&vertexTotal,vertexLength, touchPoint,SignatureVertexData);
  454. //
  455. // glBindBuffer(GL_ARRAY_BUFFER, 0);
  456. }
  457. [self setNeedsDisplay];
  458. }
  459. //- (void)longPress:(UILongPressGestureRecognizer *)lp {
  460. // //注释regturn,可以打开长按清除
  461. // return;
  462. //
  463. //// [self signatureImage];
  464. // [self clear];
  465. //}
  466. - (void)pan:(UIPanGestureRecognizer *)p {
  467. // glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
  468. //
  469. CGPoint v = [p velocityInView:self];
  470. CGPoint l = [p locationInView:self];
  471. [self updateRectWithPoint:l];
  472. currentVelocity = ViewPointToMTK(v, self.bounds.size, self.drawableSize,StrokeColor);
  473. float distance = 0.;
  474. if (previousPoint.x > 0) {
  475. distance = sqrtf((l.x - previousPoint.x) * (l.x - previousPoint.x) + (l.y - previousPoint.y) * (l.y - previousPoint.y));
  476. }
  477. float velocityMagnitude = sqrtf(v.x*v.x + v.y*v.y);
  478. float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);
  479. float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);
  480. float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;
  481. float newThickness = (self.MaxLineWidth - self.MinLineWidth) * (1 - normalizedVelocity) + self.MinLineWidth;
  482. penThickness = penThickness * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);
  483. if ([p state] == UIGestureRecognizerStateBegan) {
  484. previousPoint = l;
  485. previousMidPoint = l;
  486. PPSSignaturePoint startPoint = ViewPointToMTK(l, self.bounds.size, self.drawableSize,StrokeColor);
  487. previousVertex = startPoint;
  488. previousThickness = penThickness;
  489. addVertex(&vertexTotal,vertexLength, startPoint,SignatureVertexData);
  490. addVertex(&vertexTotal,vertexLength, previousVertex,SignatureVertexData);
  491. self.isSigned = YES;
  492. } else if ([p state] == UIGestureRecognizerStateChanged) {
  493. CGPoint mid = CGPointMake((l.x + previousPoint.x) / 2.0, (l.y + previousPoint.y) / 2.0);
  494. if (distance > QUADRATIC_DISTANCE_TOLERANCE) {
  495. // Plot quadratic bezier instead of line
  496. unsigned int i;
  497. int segments = (int) distance / 1.5;
  498. float startPenThickness = previousThickness;
  499. float endPenThickness = penThickness;
  500. previousThickness = penThickness;
  501. for (i = 0; i < segments; i++)
  502. {
  503. penThickness = startPenThickness + ((endPenThickness - startPenThickness) / segments) * i;
  504. CGPoint quadPoint = QuadraticPointInCurve(previousMidPoint, mid, previousPoint, (float)i / (float)(segments));
  505. PPSSignaturePoint v = ViewPointToMTK(quadPoint, self.bounds.size,self.drawableSize, StrokeColor);
  506. [self addTriangleStripPointsForPrevious:previousVertex next:v];
  507. previousVertex = v;
  508. }
  509. } else if (distance > 1.0) {
  510. PPSSignaturePoint v = ViewPointToMTK(l, self.bounds.size,self.drawableSize, StrokeColor);
  511. [self addTriangleStripPointsForPrevious:previousVertex next:v];
  512. previousVertex = v;
  513. previousThickness = penThickness;
  514. }
  515. previousPoint = l;
  516. previousMidPoint = mid;
  517. } else if (p.state == UIGestureRecognizerStateEnded | p.state == UIGestureRecognizerStateCancelled) {
  518. PPSSignaturePoint v = ViewPointToMTK(l, self.bounds.size, self.drawableSize,StrokeColor);
  519. addVertex(&vertexTotal,vertexLength, v,SignatureVertexData);
  520. previousVertex = v;
  521. addVertex(&vertexTotal,vertexLength, previousVertex,SignatureVertexData);
  522. }
  523. [self setNeedsDisplay];
  524. }
  525. - (void)addTriangleStripPointsForPrevious:(PPSSignaturePoint)previous next:(PPSSignaturePoint)next {
  526. float toTravel = penThickness / 2.0;
  527. for (int i = 0; i < 2; i++) {
  528. vector_float2 p = perpendicular(previous, next);
  529. vector_float2 p1 = next.position;
  530. vector_float2 ref = p1+p;
  531. float distance = simd_distance(p1,ref);
  532. float difX = p1.x - ref.x;
  533. float difY = p1.y - ref.y;
  534. float ratio = -1.0 * (toTravel / distance);
  535. difX = difX * ratio;
  536. difY = difY * ratio;
  537. PPSSignaturePoint stripPoint = {
  538. { p1.x + difX, p1.y + difY},
  539. StrokeColor
  540. };
  541. addVertex(&vertexTotal,vertexLength, stripPoint,SignatureVertexData);
  542. toTravel *= -1;
  543. }
  544. }
  545. @end