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