SignatureViewM.m 22 KB

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