1/** NSGeometry.m - geometry functions 2 * Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc. 3 * 4 * Written by: Adam Fedor <fedor@boulder.colorado.edu> 5 * Date: Mar 1995 6 * 7 * This file is part of the GNUstep Base Library. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free 21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110 USA. 23 24 <title>NSGeometry class reference</title> 25 $Date$ $Revision$ 26 */ 27 28/* 29 * Define IN_NSGEOMETRY_M so that the Foundation/NSGeometry.h header can 30 * provide non-inline versions of the function implementations for us. 31 */ 32#define IN_NSGEOMETRY_M 33 34 35/**** Included Headers *******************************************************/ 36 37#import "common.h" 38#include <math.h> 39#import "Foundation/NSGeometry.h" 40#import "Foundation/NSScanner.h" 41#import "Foundation/NSNotification.h" 42#import "GSPrivate.h" 43 44static Class NSStringClass = 0; 45static Class NSScannerClass = 0; 46static SEL scanFloatSel; 47static SEL scanStringSel; 48static SEL scannerSel; 49static BOOL (*scanFloatImp)(NSScanner*, SEL, CGFloat*); 50static BOOL (*scanStringImp)(NSScanner*, SEL, NSString*, NSString**); 51static id (*scannerImp)(Class, SEL, NSString*); 52 53static inline void 54setupCache(void) 55{ 56 if (NSStringClass == 0) 57 { 58 NSStringClass = [NSString class]; 59 NSScannerClass = [NSScanner class]; 60 if (sizeof(CGFloat) == sizeof(double)) 61 { 62 scanFloatSel = @selector(scanDouble:); 63 } 64 else 65 { 66 scanFloatSel = @selector(scanFloat:); 67 } 68 scanStringSel = @selector(scanString:intoString:); 69 scannerSel = @selector(scannerWithString:); 70 scanFloatImp = (BOOL (*)(NSScanner*, SEL, CGFloat*)) 71 [NSScannerClass instanceMethodForSelector: scanFloatSel]; 72 scanStringImp = (BOOL (*)(NSScanner*, SEL, NSString*, NSString**)) 73 [NSScannerClass instanceMethodForSelector: scanStringSel]; 74 scannerImp = (id (*)(Class, SEL, NSString*)) 75 [NSScannerClass methodForSelector: scannerSel]; 76 } 77} 78 79static BOOL GSMacOSXCompatibleGeometry(void) 80{ 81 if (GSPrivateDefaultsFlag(GSOldStyleGeometry) == YES) 82 return NO; 83 return GSPrivateDefaultsFlag(GSMacOSXCompatible); 84} 85 86/**** Function Implementations ***********************************************/ 87/* Most of these are implemented in the header file as inline functkions */ 88 89NSRect 90NSIntegralRect(NSRect aRect) 91{ 92 NSRect rect; 93 94 if (NSIsEmptyRect(aRect)) 95 return NSMakeRect(0, 0, 0, 0); 96 97 rect.origin.x = floor(NSMinX(aRect)); 98 rect.origin.y = floor(NSMinY(aRect)); 99 rect.size.width = ceil(NSMaxX(aRect)) - rect.origin.x; 100 rect.size.height = ceil(NSMaxY(aRect)) - rect.origin.y; 101 return rect; 102} 103 104void 105NSDivideRect(NSRect aRect, 106 NSRect *slice, 107 NSRect *remainder, 108 CGFloat amount, 109 NSRectEdge edge) 110{ 111 static NSRect sRect; 112 static NSRect rRect; 113 114 if (!slice) 115 slice = &sRect; 116 if (!remainder) 117 remainder = &rRect; 118 119 if (NSIsEmptyRect(aRect)) 120 { 121 *slice = NSMakeRect(0,0,0,0); 122 *remainder = NSMakeRect(0,0,0,0); 123 return; 124 } 125 126 switch (edge) 127 { 128 case NSMinXEdge: 129 if (amount > aRect.size.width) 130 { 131 *slice = aRect; 132 *remainder = NSMakeRect(NSMaxX(aRect), 133 aRect.origin.y, 134 0, 135 aRect.size.height); 136 } 137 else 138 { 139 *slice = NSMakeRect(aRect.origin.x, 140 aRect.origin.y, 141 amount, 142 aRect.size.height); 143 *remainder = NSMakeRect(NSMaxX(*slice), 144 aRect.origin.y, 145 NSMaxX(aRect) - NSMaxX(*slice), 146 aRect.size.height); 147 } 148 break; 149 case NSMinYEdge: 150 if (amount > aRect.size.height) 151 { 152 *slice = aRect; 153 *remainder = NSMakeRect(aRect.origin.x, 154 NSMaxY(aRect), 155 aRect.size.width, 0); 156 } 157 else 158 { 159 *slice = NSMakeRect(aRect.origin.x, 160 aRect.origin.y, 161 aRect.size.width, 162 amount); 163 *remainder = NSMakeRect(aRect.origin.x, 164 NSMaxY(*slice), 165 aRect.size.width, 166 NSMaxY(aRect) - NSMaxY(*slice)); 167 } 168 break; 169 case (NSMaxXEdge): 170 if (amount > aRect.size.width) 171 { 172 *slice = aRect; 173 *remainder = NSMakeRect(aRect.origin.x, 174 aRect.origin.y, 175 0, 176 aRect.size.height); 177 } 178 else 179 { 180 *slice = NSMakeRect(NSMaxX(aRect) - amount, 181 aRect.origin.y, 182 amount, 183 aRect.size.height); 184 *remainder = NSMakeRect(aRect.origin.x, 185 aRect.origin.y, 186 NSMinX(*slice) - aRect.origin.x, 187 aRect.size.height); 188 } 189 break; 190 case NSMaxYEdge: 191 if (amount > aRect.size.height) 192 { 193 *slice = aRect; 194 *remainder = NSMakeRect(aRect.origin.x, 195 aRect.origin.y, 196 aRect.size.width, 197 0); 198 } 199 else 200 { 201 *slice = NSMakeRect(aRect.origin.x, 202 NSMaxY(aRect) - amount, 203 aRect.size.width, 204 amount); 205 *remainder = NSMakeRect(aRect.origin.x, 206 aRect.origin.y, 207 aRect.size.width, 208 NSMinY(*slice) - aRect.origin.y); 209 } 210 break; 211 default: 212 break; 213 } 214 215 return; 216} 217 218/** Get a String Representation... **/ 219/* NOTE: Spaces around '=' so that old OpenStep implementations can 220 read our strings (Both GNUstep and Mac OS X can read these as well). */ 221 222NSString* 223NSStringFromPoint(NSPoint aPoint) 224{ 225 setupCache(); 226 if (GSMacOSXCompatibleGeometry() == YES) 227 return [NSStringClass stringWithFormat: 228 @"{%g, %g}", aPoint.x, aPoint.y]; 229 else 230 return [NSStringClass stringWithFormat: 231 @"{x = %g; y = %g}", aPoint.x, aPoint.y]; 232} 233 234NSString* 235NSStringFromRect(NSRect aRect) 236{ 237 setupCache(); 238 if (GSMacOSXCompatibleGeometry() == YES) 239 return [NSStringClass stringWithFormat: 240 @"{{%g, %g}, {%g, %g}}", 241 aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height]; 242 else 243 return [NSStringClass stringWithFormat: 244 @"{x = %g; y = %g; width = %g; height = %g}", 245 aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height]; 246} 247 248NSString* 249NSStringFromSize(NSSize aSize) 250{ 251 setupCache(); 252 if (GSMacOSXCompatibleGeometry() == YES) 253 return [NSStringClass stringWithFormat: 254 @"{%g, %g}", aSize.width, aSize.height]; 255 else 256 return [NSStringClass stringWithFormat: 257 @"{width = %g; height = %g}", aSize.width, aSize.height]; 258} 259 260NSPoint 261NSPointFromString(NSString* string) 262{ 263 NSScanner *scanner; 264 NSPoint point; 265 266 setupCache(); 267 scanner = (*scannerImp)(NSScannerClass, scannerSel, string); 268 if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL) 269 && (*scanStringImp)(scanner, scanStringSel, @"x", NULL) 270 && (*scanStringImp)(scanner, scanStringSel, @"=", NULL) 271 && (*scanFloatImp)(scanner, scanFloatSel, &point.x) 272 && (*scanStringImp)(scanner, scanStringSel, @";", NULL) 273 && (*scanStringImp)(scanner, scanStringSel, @"y", NULL) 274 && (*scanStringImp)(scanner, scanStringSel, @"=", NULL) 275 && (*scanFloatImp)(scanner, scanFloatSel, &point.y) 276 && (*scanStringImp)(scanner, scanStringSel, @"}", NULL)) 277 { 278 return point; 279 } 280 else 281 { 282 [scanner setScanLocation: 0]; 283 if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL) 284 && (*scanFloatImp)(scanner, scanFloatSel, &point.x) 285 && (*scanStringImp)(scanner, scanStringSel, @",", NULL) 286 && (*scanFloatImp)(scanner, scanFloatSel, &point.y) 287 && (*scanStringImp)(scanner, scanStringSel, @"}", NULL)) 288 { 289 return point; 290 } 291 else 292 { 293 return NSMakePoint(0, 0); 294 } 295 } 296} 297 298NSSize 299NSSizeFromString(NSString* string) 300{ 301 NSScanner *scanner; 302 NSSize size; 303 304 setupCache(); 305 scanner = (*scannerImp)(NSScannerClass, scannerSel, string); 306 if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL) 307 && (*scanStringImp)(scanner, scanStringSel, @"width", NULL) 308 && (*scanStringImp)(scanner, scanStringSel, @"=", NULL) 309 && (*scanFloatImp)(scanner, scanFloatSel, &size.width) 310 && (*scanStringImp)(scanner, scanStringSel, @";", NULL) 311 && (*scanStringImp)(scanner, scanStringSel, @"height", NULL) 312 && (*scanStringImp)(scanner, scanStringSel, @"=", NULL) 313 && (*scanFloatImp)(scanner, scanFloatSel, &size.height) 314 && (*scanStringImp)(scanner, scanStringSel, @"}", NULL)) 315 { 316 return size; 317 } 318 else 319 { 320 [scanner setScanLocation: 0]; 321 if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL) 322 && (*scanFloatImp)(scanner, scanFloatSel, &size.width) 323 && (*scanStringImp)(scanner, scanStringSel, @",", NULL) 324 && (*scanFloatImp)(scanner, scanFloatSel, &size.height) 325 && (*scanStringImp)(scanner, scanStringSel, @"}", NULL)) 326 { 327 return size; 328 } 329 else 330 { 331 return NSMakeSize(0, 0); 332 } 333 } 334} 335 336NSRect 337NSRectFromString(NSString* string) 338{ 339 NSScanner *scanner; 340 NSRect rect; 341 342 setupCache(); 343 scanner = (*scannerImp)(NSScannerClass, scannerSel, string); 344 if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL) 345 && (*scanStringImp)(scanner, scanStringSel, @"x", NULL) 346 && (*scanStringImp)(scanner, scanStringSel, @"=", NULL) 347 && (*scanFloatImp)(scanner, scanFloatSel, &rect.origin.x) 348 && (*scanStringImp)(scanner, scanStringSel, @";", NULL) 349 350 && (*scanStringImp)(scanner, scanStringSel, @"y", NULL) 351 && (*scanStringImp)(scanner, scanStringSel, @"=", NULL) 352 && (*scanFloatImp)(scanner, scanFloatSel, &rect.origin.y) 353 && (*scanStringImp)(scanner, scanStringSel, @";", NULL) 354 355 && (*scanStringImp)(scanner, scanStringSel, @"width", NULL) 356 && (*scanStringImp)(scanner, scanStringSel, @"=", NULL) 357 && (*scanFloatImp)(scanner, scanFloatSel, &rect.size.width) 358 && (*scanStringImp)(scanner, scanStringSel, @";", NULL) 359 360 && (*scanStringImp)(scanner, scanStringSel, @"height", NULL) 361 && (*scanStringImp)(scanner, scanStringSel, @"=", NULL) 362 && (*scanFloatImp)(scanner, scanFloatSel, &rect.size.height) 363 && (*scanStringImp)(scanner, scanStringSel, @"}", NULL)) 364 { 365 return rect; 366 } 367 else 368 { 369 [scanner setScanLocation: 0]; 370 if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL) 371 && (*scanStringImp)(scanner, scanStringSel, @"{", NULL) 372 && (*scanFloatImp)(scanner, scanFloatSel, &rect.origin.x) 373 && (*scanStringImp)(scanner, scanStringSel, @",", NULL) 374 375 && (*scanFloatImp)(scanner, scanFloatSel, &rect.origin.y) 376 && (*scanStringImp)(scanner, scanStringSel, @"}", NULL) 377 && (*scanStringImp)(scanner, scanStringSel, @",", NULL) 378 379 && (*scanStringImp)(scanner, scanStringSel, @"{", NULL) 380 && (*scanFloatImp)(scanner, scanFloatSel, &rect.size.width) 381 && (*scanStringImp)(scanner, scanStringSel, @",", NULL) 382 383 && (*scanFloatImp)(scanner, scanFloatSel, &rect.size.height) 384 && (*scanStringImp)(scanner, scanStringSel, @"}", NULL) 385 && (*scanStringImp)(scanner, scanStringSel, @"}", NULL)) 386 { 387 return rect; 388 } 389 else 390 { 391 return NSMakeRect(0, 0, 0, 0); 392 } 393 } 394} 395 396/* Tests for equality of floats/doubles. 397 * WARNING assumes the values are in the standard IEEE format ... 398 * this may not be true on all systems, though afaik it is the case 399 * on all systems we target. 400 * 401 * We use integer arithmetic for speed, assigning the float/double 402 * to an integer of the same size and then converting any negative 403 * values to twos-complement integer values so that a simple integer 404 * comparison can be done. 405 * 406 * MAX_ULP specified the number of Units in the Last Place by which 407 * the two values may differ and still be considered equal. A value 408 * of zero means that the two numbers must be identical. 409 * 410 * The way that infinity is represented means that it will be considered 411 * equal to MAX_FLT (or MAX_DBL) unless we are doing an exact comparison 412 * with MAX_ULP set to zero. 413 * 414 * The implementation will also treat two NaN values as being equal, which 415 * is technically wrong ... but is it worth adding a check for that? 416 */ 417#define MAX_ULP 0 418static inline BOOL 419almostEqual(CGFloat A, CGFloat B) 420{ 421#if MAX_ULP == 0 422 return (A == B) ? YES : NO; 423#else /* MAX_UPL == 0 */ 424#if defined(CGFLOAT_IS_DBL) 425 union {int64_t i; double d;} valA, valB; 426 427 valA.d = A; 428 valB.d = B; 429#if GS_SIZEOF_LONG == 8 430 if (valA.i < 0) 431 { 432 valA.i = 0x8000000000000000L - valA.i; 433 } 434 if (valB.i < 0) 435 { 436 valB.i = 0x8000000000000000L - valB.i; 437 } 438 if (labs(valA.i - valB.i) <= MAX_ULP) 439 { 440 return YES; 441 } 442#else /* GS_SIZEOF_LONG == 8 */ 443 if (valA.i < 0) 444 { 445 valA.i = 0x8000000000000000LL - valA.i; 446 } 447 if (valB.i < 0) 448 { 449 valB.i = 0x8000000000000000LL - valB.i; 450 } 451 if (llabs(valA.i - valB.i) <= MAX_ULP) 452 { 453 return YES; 454 } 455#endif /* GS_SIZEOF_LONG == 8 */ 456 return NO; 457#else /* DEFINED(CGFLOAT_IS_DBL) */ 458 union {int32_t i; float f;} valA, valB; 459 460 valA.f = A; 461 if (valA.i < 0) 462 { 463 valA.i = 0x80000000 - valA.i; 464 } 465 valB.f = B; 466 if (valB.i < 0) 467 { 468 valB.i = 0x80000000 - valB.i; 469 } 470 if (abs(valA.i - valB.i) <= MAX_ULP) 471 { 472 return YES; 473 } 474 return NO; 475#endif /* DEFINED(CGFLOAT_IS_DBL) */ 476#endif /* MAX_UPL == 0 */ 477} 478 479BOOL 480NSEqualRects(NSRect aRect, NSRect bRect) 481{ 482 return (almostEqual(NSMinX(aRect), NSMinX(bRect)) 483 && almostEqual(NSMinY(aRect), NSMinY(bRect)) 484 && almostEqual(NSWidth(aRect), NSWidth(bRect)) 485 && almostEqual(NSHeight(aRect), NSHeight(bRect))) ? YES : NO; 486} 487 488BOOL 489NSEqualSizes(NSSize aSize, NSSize bSize) 490{ 491 return (almostEqual(aSize.width, bSize.width) 492 && almostEqual(aSize.height, bSize.height)) ? YES : NO; 493} 494 495BOOL 496NSEqualPoints(NSPoint aPoint, NSPoint bPoint) 497{ 498 return (almostEqual(aPoint.x, bPoint.x) 499 && almostEqual(aPoint.y, bPoint.y)) ? YES : NO; 500} 501 502BOOL 503NSEdgeInsetsEqual(NSEdgeInsets e1, NSEdgeInsets e2) 504{ 505 return ( 506 almostEqual(e1.top, e2.top) 507 && almostEqual(e1.left, e2.left) 508 && almostEqual(e1.bottom, e2.bottom) 509 && almostEqual(e1.right, e2.right) 510 ); 511} 512 513