/framework/Source/CPTUtilities.m

https://github.com/ChronicStim/ChronicStim-CPTClone · Objective C · 750 lines · 365 code · 76 blank · 309 comment · 40 complexity · f0a5978814fcf727c3e673408fc5e6a5 MD5 · raw file

  1. #import "CPTUtilities.h"
  2. #import <tgmath.h>
  3. // cache common values to improve performance
  4. #define kCacheSize 3
  5. static NSDecimal cache[kCacheSize];
  6. static BOOL cacheValueInitialized[kCacheSize] = {NO, NO, NO};
  7. #pragma mark Convert NSDecimal to primitive types
  8. /**
  9. * @brief Converts an NSDecimal value to an 8-bit integer.
  10. * @param decimalNumber The NSDecimal value.
  11. * @return The converted value.
  12. **/
  13. int8_t CPTDecimalCharValue(NSDecimal decimalNumber)
  14. {
  15. return (int8_t)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] charValue];
  16. }
  17. /**
  18. * @brief Converts an NSDecimal value to a 16-bit integer.
  19. * @param decimalNumber The NSDecimal value.
  20. * @return The converted value.
  21. **/
  22. int16_t CPTDecimalShortValue(NSDecimal decimalNumber)
  23. {
  24. return (int16_t)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] shortValue];
  25. }
  26. /**
  27. * @brief Converts an NSDecimal value to a 32-bit integer.
  28. * @param decimalNumber The NSDecimal value.
  29. * @return The converted value.
  30. **/
  31. int32_t CPTDecimalLongValue(NSDecimal decimalNumber)
  32. {
  33. return (int32_t)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] longValue];
  34. }
  35. /**
  36. * @brief Converts an NSDecimal value to a 64-bit integer.
  37. * @param decimalNumber The NSDecimal value.
  38. * @return The converted value.
  39. **/
  40. int64_t CPTDecimalLongLongValue(NSDecimal decimalNumber)
  41. {
  42. return (int64_t)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] longLongValue];
  43. }
  44. /**
  45. * @brief Converts an NSDecimal value to an int.
  46. * @param decimalNumber The NSDecimal value.
  47. * @return The converted value.
  48. **/
  49. int CPTDecimalIntValue(NSDecimal decimalNumber)
  50. {
  51. return (int)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] intValue];
  52. }
  53. /**
  54. * @brief Converts an NSDecimal value to an NSInteger.
  55. * @param decimalNumber The NSDecimal value.
  56. * @return The converted value.
  57. **/
  58. NSInteger CPTDecimalIntegerValue(NSDecimal decimalNumber)
  59. {
  60. return (NSInteger)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] integerValue];
  61. }
  62. /**
  63. * @brief Converts an NSDecimal value to an unsigned 8-bit integer.
  64. * @param decimalNumber The NSDecimal value.
  65. * @return The converted value.
  66. **/
  67. uint8_t CPTDecimalUnsignedCharValue(NSDecimal decimalNumber)
  68. {
  69. return (uint8_t)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] unsignedCharValue];
  70. }
  71. /**
  72. * @brief Converts an NSDecimal value to an unsigned 16-bit integer.
  73. * @param decimalNumber The NSDecimal value.
  74. * @return The converted value.
  75. **/
  76. uint16_t CPTDecimalUnsignedShortValue(NSDecimal decimalNumber)
  77. {
  78. return (uint16_t)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] unsignedShortValue];
  79. }
  80. /**
  81. * @brief Converts an NSDecimal value to an unsigned 32-bit integer.
  82. * @param decimalNumber The NSDecimal value.
  83. * @return The converted value.
  84. **/
  85. uint32_t CPTDecimalUnsignedLongValue(NSDecimal decimalNumber)
  86. {
  87. return (uint32_t)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] unsignedLongValue];
  88. }
  89. /**
  90. * @brief Converts an NSDecimal value to an unsigned 64-bit integer.
  91. * @param decimalNumber The NSDecimal value.
  92. * @return The converted value.
  93. **/
  94. uint64_t CPTDecimalUnsignedLongLongValue(NSDecimal decimalNumber)
  95. {
  96. return (uint64_t)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] unsignedLongLongValue];
  97. }
  98. /**
  99. * @brief Converts an NSDecimal value to an unsigned int.
  100. * @param decimalNumber The NSDecimal value.
  101. * @return The converted value.
  102. **/
  103. unsigned int CPTDecimalUnsignedIntValue(NSDecimal decimalNumber)
  104. {
  105. return (unsigned int)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] unsignedIntValue];
  106. }
  107. /**
  108. * @brief Converts an NSDecimal value to an NSUInteger.
  109. * @param decimalNumber The NSDecimal value.
  110. * @return The converted value.
  111. **/
  112. NSUInteger CPTDecimalUnsignedIntegerValue(NSDecimal decimalNumber)
  113. {
  114. return (NSUInteger)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] unsignedIntegerValue];
  115. }
  116. /**
  117. * @brief Converts an NSDecimal value to a float.
  118. * @param decimalNumber The NSDecimal value.
  119. * @return The converted value.
  120. **/
  121. float CPTDecimalFloatValue(NSDecimal decimalNumber)
  122. {
  123. return (float)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] floatValue];
  124. }
  125. /**
  126. * @brief Converts an NSDecimal value to a double.
  127. * @param decimalNumber The NSDecimal value.
  128. * @return The converted value.
  129. **/
  130. double CPTDecimalDoubleValue(NSDecimal decimalNumber)
  131. {
  132. return (double)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] doubleValue];
  133. }
  134. /**
  135. * @brief Converts an NSDecimal value to a CGFloat.
  136. * @param decimalNumber The NSDecimal value.
  137. * @return The converted value.
  138. **/
  139. CGFloat CPTDecimalCGFloatValue(NSDecimal decimalNumber)
  140. {
  141. #if CGFLOAT_IS_DOUBLE
  142. return (CGFloat)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] doubleValue];
  143. #else
  144. return (CGFloat)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] floatValue];
  145. #endif
  146. }
  147. /**
  148. * @brief Converts an NSDecimal value to an NSString.
  149. * @param decimalNumber The NSDecimal value.
  150. * @return The converted value.
  151. **/
  152. NSString *CPTDecimalStringValue(NSDecimal decimalNumber)
  153. {
  154. return (NSString *)[[NSDecimalNumber decimalNumberWithDecimal:decimalNumber] stringValue];
  155. }
  156. #pragma mark -
  157. #pragma mark Convert primitive types to NSDecimal
  158. /**
  159. * @brief Converts an 8-bit integer value to an NSDecimal.
  160. * @param i The integer value.
  161. * @return The converted value.
  162. **/
  163. NSDecimal CPTDecimalFromChar(int8_t i)
  164. {
  165. if ( (i >= 0) && (i < kCacheSize) ) {
  166. if ( !cacheValueInitialized[i] ) {
  167. cache[i] = [[NSNumber numberWithChar:i] decimalValue];
  168. cacheValueInitialized[i] = YES;
  169. }
  170. return cache[i];
  171. }
  172. return [[NSNumber numberWithChar:i] decimalValue];
  173. }
  174. /**
  175. * @brief Converts a 16-bit integer value to an NSDecimal.
  176. * @param i The integer value.
  177. * @return The converted value.
  178. **/
  179. NSDecimal CPTDecimalFromShort(int16_t i)
  180. {
  181. if ( (i >= 0) && (i < kCacheSize) ) {
  182. if ( !cacheValueInitialized[i] ) {
  183. cache[i] = [[NSNumber numberWithShort:i] decimalValue];
  184. cacheValueInitialized[i] = YES;
  185. }
  186. return cache[i];
  187. }
  188. return [[NSNumber numberWithShort:i] decimalValue];
  189. }
  190. /**
  191. * @brief Converts a 32-bit integer value to an NSDecimal.
  192. * @param i The integer value.
  193. * @return The converted value.
  194. **/
  195. NSDecimal CPTDecimalFromLong(int32_t i)
  196. {
  197. if ( (i >= 0) && (i < kCacheSize) ) {
  198. if ( !cacheValueInitialized[i] ) {
  199. cache[i] = [[NSNumber numberWithLong:i] decimalValue];
  200. cacheValueInitialized[i] = YES;
  201. }
  202. return cache[i];
  203. }
  204. return [[NSNumber numberWithLong:i] decimalValue];
  205. }
  206. /**
  207. * @brief Converts a 64-bit integer value to an NSDecimal.
  208. * @param i The integer value.
  209. * @return The converted value.
  210. **/
  211. NSDecimal CPTDecimalFromLongLong(int64_t i)
  212. {
  213. if ( (i >= 0) && (i < kCacheSize) ) {
  214. if ( !cacheValueInitialized[i] ) {
  215. cache[i] = [[NSNumber numberWithLongLong:i] decimalValue];
  216. cacheValueInitialized[i] = YES;
  217. }
  218. return cache[i];
  219. }
  220. return [[NSNumber numberWithLongLong:i] decimalValue];
  221. }
  222. /**
  223. * @brief Converts an int value to an NSDecimal.
  224. * @param i The int value.
  225. * @return The converted value.
  226. **/
  227. NSDecimal CPTDecimalFromInt(int i)
  228. {
  229. if ( (i >= 0) && (i < kCacheSize) ) {
  230. if ( !cacheValueInitialized[i] ) {
  231. cache[i] = [[NSNumber numberWithInt:i] decimalValue];
  232. cacheValueInitialized[i] = YES;
  233. }
  234. return cache[i];
  235. }
  236. return [[NSNumber numberWithInt:i] decimalValue];
  237. }
  238. /**
  239. * @brief Converts an NSInteger value to an NSDecimal.
  240. * @param i The NSInteger value.
  241. * @return The converted value.
  242. **/
  243. NSDecimal CPTDecimalFromInteger(NSInteger i)
  244. {
  245. if ( (i >= 0) && (i < kCacheSize) ) {
  246. if ( !cacheValueInitialized[i] ) {
  247. cache[i] = [[NSNumber numberWithInteger:i] decimalValue];
  248. cacheValueInitialized[i] = YES;
  249. }
  250. return cache[i];
  251. }
  252. return [[NSNumber numberWithInteger:i] decimalValue];
  253. }
  254. /**
  255. * @brief Converts an unsigned 8-bit integer value to an NSDecimal.
  256. * @param i The unsigned integer value.
  257. * @return The converted value.
  258. **/
  259. NSDecimal CPTDecimalFromUnsignedChar(uint8_t i)
  260. {
  261. if ( i < kCacheSize ) {
  262. if ( !cacheValueInitialized[i] ) {
  263. cache[i] = [[NSNumber numberWithUnsignedChar:i] decimalValue];
  264. cacheValueInitialized[i] = YES;
  265. }
  266. return cache[i];
  267. }
  268. return [[NSNumber numberWithUnsignedChar:i] decimalValue];
  269. }
  270. /**
  271. * @brief Converts an unsigned 16-bit integer value to an NSDecimal.
  272. * @param i The unsigned integer value.
  273. * @return The converted value.
  274. **/
  275. NSDecimal CPTDecimalFromUnsignedShort(uint16_t i)
  276. {
  277. if ( i < kCacheSize ) {
  278. if ( !cacheValueInitialized[i] ) {
  279. cache[i] = [[NSNumber numberWithUnsignedShort:i] decimalValue];
  280. cacheValueInitialized[i] = YES;
  281. }
  282. return cache[i];
  283. }
  284. return [[NSNumber numberWithUnsignedShort:i] decimalValue];
  285. }
  286. /**
  287. * @brief Converts an unsigned 32-bit integer value to an NSDecimal.
  288. * @param i The unsigned integer value.
  289. * @return The converted value.
  290. **/
  291. NSDecimal CPTDecimalFromUnsignedLong(uint32_t i)
  292. {
  293. if ( i < kCacheSize ) {
  294. if ( !cacheValueInitialized[i] ) {
  295. cache[i] = [[NSNumber numberWithUnsignedLong:i] decimalValue];
  296. cacheValueInitialized[i] = YES;
  297. }
  298. return cache[i];
  299. }
  300. return [[NSNumber numberWithUnsignedLong:i] decimalValue];
  301. }
  302. /**
  303. * @brief Converts an unsigned 64-bit integer value to an NSDecimal.
  304. * @param i The unsigned integer value.
  305. * @return The converted value.
  306. **/
  307. NSDecimal CPTDecimalFromUnsignedLongLong(uint64_t i)
  308. {
  309. if ( i < kCacheSize ) {
  310. if ( !cacheValueInitialized[i] ) {
  311. cache[i] = [[NSNumber numberWithUnsignedLongLong:i] decimalValue];
  312. cacheValueInitialized[i] = YES;
  313. }
  314. return cache[i];
  315. }
  316. return [[NSNumber numberWithUnsignedLongLong:i] decimalValue];
  317. }
  318. /**
  319. * @brief Converts an unsigned int value to an NSDecimal.
  320. * @param i The unsigned int value.
  321. * @return The converted value.
  322. **/
  323. NSDecimal CPTDecimalFromUnsignedInt(unsigned int i)
  324. {
  325. if ( i < kCacheSize ) {
  326. if ( !cacheValueInitialized[i] ) {
  327. cache[i] = [[NSNumber numberWithUnsignedInt:i] decimalValue];
  328. cacheValueInitialized[i] = YES;
  329. }
  330. return cache[i];
  331. }
  332. return [[NSNumber numberWithUnsignedInt:i] decimalValue];
  333. }
  334. /**
  335. * @brief Converts an NSUInteger value to an NSDecimal.
  336. * @param i The NSUInteger value.
  337. * @return The converted value.
  338. **/
  339. NSDecimal CPTDecimalFromUnsignedInteger(NSUInteger i)
  340. {
  341. if ( i < kCacheSize ) {
  342. if ( !cacheValueInitialized[i] ) {
  343. cache[i] = [[NSNumber numberWithUnsignedInteger:i] decimalValue];
  344. cacheValueInitialized[i] = YES;
  345. }
  346. return cache[i];
  347. }
  348. return [[NSNumber numberWithUnsignedInteger:i] decimalValue];
  349. }
  350. /**
  351. * @brief Converts a float value to an NSDecimal.
  352. * @param f The float value.
  353. * @return The converted value.
  354. **/
  355. NSDecimal CPTDecimalFromFloat(float f)
  356. {
  357. return [[NSNumber numberWithFloat:f] decimalValue];
  358. }
  359. /**
  360. * @brief Converts a double value to an NSDecimal.
  361. * @param d The double value.
  362. * @return The converted value.
  363. **/
  364. NSDecimal CPTDecimalFromDouble(double d)
  365. {
  366. return [[NSNumber numberWithDouble:d] decimalValue];
  367. }
  368. /**
  369. * @brief Converts a CGFloat value to an NSDecimal.
  370. * @param f The CGFloat value.
  371. * @return The converted value.
  372. **/
  373. NSDecimal CPTDecimalFromCGFloat(CGFloat f)
  374. {
  375. #if CGFLOAT_IS_DOUBLE
  376. return [[NSNumber numberWithDouble:f] decimalValue];
  377. #else
  378. return [[NSNumber numberWithFloat:f] decimalValue];
  379. #endif
  380. }
  381. /**
  382. * @brief Parses a string and extracts the numeric value as an NSDecimal.
  383. * @param stringRepresentation The string value.
  384. * @return The numeric value extracted from the string.
  385. **/
  386. NSDecimal CPTDecimalFromString(NSString *stringRepresentation)
  387. {
  388. // The following NSDecimalNumber-based creation of NSDecimals from strings is slower than
  389. // the NSScanner-based method: (307000 operations per second vs. 582000 operations per second for NSScanner)
  390. /* NSDecimalNumber *newNumber = [[NSDecimalNumber alloc] initWithString:@"1.0" locale:[NSLocale currentLocale]];
  391. newDecimal = [newNumber decimalValue];
  392. [newNumber release];*/
  393. NSDecimal result;
  394. NSScanner *theScanner = [[NSScanner alloc] initWithString:stringRepresentation];
  395. [theScanner scanDecimal:&result];
  396. [theScanner release];
  397. return result;
  398. }
  399. #pragma mark -
  400. #pragma mark NSDecimal arithmetic
  401. /**
  402. * @brief Adds two NSDecimals together.
  403. * @param leftOperand The left-hand side of the addition operation.
  404. * @param rightOperand The right-hand side of the addition operation.
  405. * @return The result of the addition.
  406. **/
  407. NSDecimal CPTDecimalAdd(NSDecimal leftOperand, NSDecimal rightOperand)
  408. {
  409. NSDecimal result;
  410. NSDecimalAdd(&result, &leftOperand, &rightOperand, NSRoundBankers);
  411. return result;
  412. }
  413. /**
  414. * @brief Subtracts one NSDecimal from another.
  415. * @param leftOperand The left-hand side of the subtraction operation.
  416. * @param rightOperand The right-hand side of the subtraction operation.
  417. * @return The result of the subtraction.
  418. **/
  419. NSDecimal CPTDecimalSubtract(NSDecimal leftOperand, NSDecimal rightOperand)
  420. {
  421. NSDecimal result;
  422. NSDecimalSubtract(&result, &leftOperand, &rightOperand, NSRoundBankers);
  423. return result;
  424. }
  425. /**
  426. * @brief Multiplies two NSDecimals together.
  427. * @param leftOperand The left-hand side of the multiplication operation.
  428. * @param rightOperand The right-hand side of the multiplication operation.
  429. * @return The result of the multiplication.
  430. **/
  431. NSDecimal CPTDecimalMultiply(NSDecimal leftOperand, NSDecimal rightOperand)
  432. {
  433. NSDecimal result;
  434. NSDecimalMultiply(&result, &leftOperand, &rightOperand, NSRoundBankers);
  435. return result;
  436. }
  437. /**
  438. * @brief Divides one NSDecimal by another.
  439. * @param numerator The numerator of the multiplication operation.
  440. * @param denominator The denominator of the multiplication operation.
  441. * @return The result of the division.
  442. **/
  443. NSDecimal CPTDecimalDivide(NSDecimal numerator, NSDecimal denominator)
  444. {
  445. NSDecimal result;
  446. NSDecimalDivide(&result, &numerator, &denominator, NSRoundBankers);
  447. return result;
  448. }
  449. #pragma mark -
  450. #pragma mark NSDecimal comparison
  451. /**
  452. * @brief Checks to see if one NSDecimal is greater than another.
  453. * @param leftOperand The left side of the comparison.
  454. * @param rightOperand The right side of the comparison.
  455. * @return YES if the left operand is greater than the right, NO otherwise.
  456. **/
  457. BOOL CPTDecimalGreaterThan(NSDecimal leftOperand, NSDecimal rightOperand)
  458. {
  459. return (NSDecimalCompare(&leftOperand, &rightOperand) == NSOrderedDescending);
  460. }
  461. /**
  462. * @brief Checks to see if one NSDecimal is greater than or equal to another.
  463. * @param leftOperand The left side of the comparison.
  464. * @param rightOperand The right side of the comparison.
  465. * @return YES if the left operand is greater than or equal to the right, NO otherwise.
  466. **/
  467. BOOL CPTDecimalGreaterThanOrEqualTo(NSDecimal leftOperand, NSDecimal rightOperand)
  468. {
  469. return (NSDecimalCompare(&leftOperand, &rightOperand) != NSOrderedAscending);
  470. }
  471. /**
  472. * @brief Checks to see if one NSDecimal is less than another.
  473. * @param leftOperand The left side of the comparison.
  474. * @param rightOperand The right side of the comparison.
  475. * @return YES if the left operand is less than the right, NO otherwise.
  476. **/
  477. BOOL CPTDecimalLessThan(NSDecimal leftOperand, NSDecimal rightOperand)
  478. {
  479. return (NSDecimalCompare(&leftOperand, &rightOperand) == NSOrderedAscending);
  480. }
  481. /**
  482. * @brief Checks to see if one NSDecimal is less than or equal to another.
  483. * @param leftOperand The left side of the comparison.
  484. * @param rightOperand The right side of the comparison.
  485. * @return YES if the left operand is less than or equal to the right, NO otherwise.
  486. **/
  487. BOOL CPTDecimalLessThanOrEqualTo(NSDecimal leftOperand, NSDecimal rightOperand)
  488. {
  489. return (NSDecimalCompare(&leftOperand, &rightOperand) != NSOrderedDescending);
  490. }
  491. /**
  492. * @brief Checks to see if one NSDecimal is equal to another.
  493. * @param leftOperand The left side of the comparison.
  494. * @param rightOperand The right side of the comparison.
  495. * @return YES if the left operand is equal to the right, NO otherwise.
  496. **/
  497. BOOL CPTDecimalEquals(NSDecimal leftOperand, NSDecimal rightOperand)
  498. {
  499. return (NSDecimalCompare(&leftOperand, &rightOperand) == NSOrderedSame);
  500. }
  501. #pragma mark -
  502. #pragma mark NSDecimal utilities
  503. /**
  504. * @brief Creates and returns an NSDecimal struct that represents the value "not a number".
  505. *
  506. * Calling <code>NSDecimalIsNotANumber()</code> on this value will return <code>YES</code>.
  507. *
  508. * @return An NSDecimal struct that represents the value "not a number".
  509. **/
  510. NSDecimal CPTDecimalNaN(void)
  511. {
  512. NSDecimal decimalNaN = [[NSDecimalNumber zero] decimalValue];
  513. decimalNaN._length = 0;
  514. decimalNaN._isNegative = YES;
  515. return decimalNaN;
  516. }
  517. #pragma mark -
  518. #pragma mark Ranges
  519. /**
  520. * @brief Expands an NSRange by the given amount.
  521. *
  522. * The <code>location</code> of the resulting NSRange will be non-negative.
  523. *
  524. * @param range The NSRange to expand.
  525. * @param expandBy The amount the expand the range by.
  526. * @return The expanded range.
  527. **/
  528. NSRange CPTExpandedRange(NSRange range, NSInteger expandBy)
  529. {
  530. NSInteger loc = MAX(0, (int)range.location - expandBy);
  531. NSInteger lowerExpansion = range.location - loc;
  532. NSInteger length = range.length + lowerExpansion + expandBy;
  533. return NSMakeRange(loc, length);
  534. }
  535. #pragma mark -
  536. #pragma mark Colors
  537. /**
  538. * @brief Extracts the color information from a CGColorRef and returns it as a CPTRGBAColor.
  539. *
  540. * Supports RGBA and grayscale colorspaces.
  541. *
  542. * @param color The color.
  543. * @return The RGBA components of the color.
  544. **/
  545. CPTRGBAColor CPTRGBAColorFromCGColor(CGColorRef color)
  546. {
  547. CPTRGBAColor rgbColor;
  548. size_t numComponents = CGColorGetNumberOfComponents(color);
  549. if (numComponents == 2) {
  550. const CGFloat *components = CGColorGetComponents(color);
  551. CGFloat all = components[0];
  552. rgbColor.red = all;
  553. rgbColor.green = all;
  554. rgbColor.blue = all;
  555. rgbColor.alpha = components[1];
  556. } else {
  557. const CGFloat *components = CGColorGetComponents(color);
  558. rgbColor.red = components[0];
  559. rgbColor.green = components[1];
  560. rgbColor.blue = components[2];
  561. rgbColor.alpha = components[3];
  562. }
  563. return rgbColor;
  564. }
  565. #pragma mark -
  566. #pragma mark Coordinates
  567. /**
  568. * @brief Determines the CPTCoordinate that is orthogonal to the one provided.
  569. *
  570. * The current implementation is two-dimensional--X is orthogonal to Y and Y is orthogonal to X.
  571. *
  572. * @param coord The CPTCoordinate.
  573. * @return The orthogonal CPTCoordinate.
  574. **/
  575. CPTCoordinate CPTOrthogonalCoordinate(CPTCoordinate coord)
  576. {
  577. return ( coord == CPTCoordinateX ? CPTCoordinateY : CPTCoordinateX );
  578. }
  579. #pragma mark -
  580. #pragma mark Quartz pixel-alignment functions
  581. /**
  582. * @brief Aligns a point in user space to integral coordinates in device space.
  583. *
  584. * Ensures that the x and y coordinates are at a pixel corner in device space.
  585. * Drawn from <i>Programming with Quartz</i> by D. Gelphman, B. Laden.
  586. *
  587. * @param context The graphics context.
  588. * @param p The point in user space.
  589. * @return The device aligned point in user space.
  590. **/
  591. CGPoint CPTAlignPointToUserSpace(CGContextRef context, CGPoint p)
  592. {
  593. // Compute the coordinates of the point in device space.
  594. p = CGContextConvertPointToDeviceSpace(context, p);
  595. // Ensure that coordinates are at exactly the corner
  596. // of a device pixel.
  597. p.x = round(p.x) + (CGFloat)0.5;
  598. p.y = round(p.y) + (CGFloat)0.5;
  599. // Convert the device aligned coordinate back to user space.
  600. return CGContextConvertPointToUserSpace(context, p);
  601. }
  602. /**
  603. * @brief Adjusts a size in user space to integral dimensions in device space.
  604. *
  605. * Ensures that the width and height are an integer number of device pixels.
  606. * Drawn from <i>Programming with Quartz</i> by D. Gelphman, B. Laden.
  607. *
  608. * @param context The graphics context.
  609. * @param s The size in user space.
  610. * @return The device aligned size in user space.
  611. **/
  612. CGSize CPTAlignSizeToUserSpace(CGContextRef context, CGSize s)
  613. {
  614. // Compute the size in device space.
  615. s = CGContextConvertSizeToDeviceSpace(context, s);
  616. // Ensure that size is an integer multiple of device pixels.
  617. s.width = round(s.width);
  618. s.height = round(s.height);
  619. // Convert back to user space.
  620. return CGContextConvertSizeToUserSpace(context, s);
  621. }
  622. /**
  623. * @brief Aligns a rectangle in user space to integral coordinates in device space.
  624. *
  625. * Ensures that the x and y coordinates are at a pixel corner in device space
  626. * and the width and height are an integer number of device pixels.
  627. * Drawn from <i>Programming with Quartz</i> by D. Gelphman, B. Laden.
  628. *
  629. * @note This function produces a width and height
  630. * that is less than or equal to the original width.
  631. * @param context The graphics context.
  632. * @param r The rectangle in user space.
  633. * @return The device aligned rectangle in user space.
  634. **/
  635. CGRect CPTAlignRectToUserSpace(CGContextRef context, CGRect r)
  636. {
  637. // Compute the coordinates of the rectangle in device space.
  638. r = CGContextConvertRectToDeviceSpace(context, r);
  639. // Ensure that the x and y coordinates are at a pixel corner.
  640. r.origin.x = round(r.origin.x) + (CGFloat)0.5;
  641. r.origin.y = round(r.origin.y) + (CGFloat)0.5;
  642. // Ensure that the width and height are an integer number of
  643. // device pixels.
  644. r.size.width = round(r.size.width);
  645. r.size.height = round(r.size.height);
  646. // Convert back to user space.
  647. return CGContextConvertRectToUserSpace(context, r);
  648. }
  649. #pragma mark -
  650. #pragma mark String formatting for Core Graphics structs
  651. /** @brief Creates a string representation of the given point.
  652. * @param p The point.
  653. * @return A string with the format <code>{x, y}</code>.
  654. **/
  655. NSString *CPTStringFromPoint(CGPoint p)
  656. {
  657. return [NSString stringWithFormat:@"{%g, %g}", p.x, p.y];
  658. }
  659. /** @brief Creates a string representation of the given size.
  660. * @param s The size.
  661. * @return A string with the format <code>{width, height}</code>.
  662. **/
  663. NSString *CPTStringFromSize(CGSize s)
  664. {
  665. return [NSString stringWithFormat:@"{%g, %g}", s.width, s.height];
  666. }
  667. /** @brief Creates a string representation of the given rectangle.
  668. * @param r The rectangle.
  669. * @return A string with the format <code>{{x, y}, {width, height}}</code>.
  670. **/
  671. NSString *CPTStringFromRect(CGRect r)
  672. {
  673. return [NSString stringWithFormat:@"{{%g, %g}, {%g, %g}}", r.origin.x, r.origin.y, r.size.width, r.size.height];
  674. }