@@ -712,159 +712,203 @@ public func NSIntegralRect(_ aRect: NSRect) -> NSRect {
712712 return . zero
713713 }
714714
715- return NSIntegralRectWithOptions ( aRect, [ . alignMinXOutward, . alignMaxXOutward, . alignMinYOutward, . alignMaxYOutward] )
715+ var result : NSRect = . zero
716+ result. origin. x = CGFloat ( floor ( aRect. origin. x) )
717+ result. origin. y = CGFloat ( floor ( aRect. origin. y) )
718+ result. size. width = CGFloat ( ceil ( Double ( aRect. origin. x) + Double( aRect. size. width) ) - Double( result. origin. x) )
719+ result. size. height = CGFloat ( ceil ( Double ( aRect. origin. y) + Double( aRect. size. height) ) - Double( result. origin. y) )
720+ return result
716721}
717- public func NSIntegralRectWithOptions( _ aRect: NSRect , _ opts: AlignmentOptions ) -> NSRect {
718- let listOfOptionsIsInconsistentErrorMessage = " List of options is inconsistent "
719-
720- if opts. contains ( . alignRectFlipped) {
721- NSUnimplemented ( )
722- }
723722
724- var width = CGFloat . NativeType. nan
725- var height = CGFloat . NativeType. nan
726- var minX = CGFloat . NativeType. nan
727- var minY = CGFloat . NativeType. nan
728- var maxX = CGFloat . NativeType. nan
729- var maxY = CGFloat . NativeType. nan
723+ fileprivate func roundedTowardPlusInfinity( _ value: Double ) -> Double {
724+ return floor ( value + 0.5 )
725+ }
730726
731- if aRect. size. height. native < 0 {
732- height = 0
733- }
734- if aRect. size. width. native < 0 {
735- width = 0
736- }
737-
727+ fileprivate func roundedTowardMinusInfinity( _ value: Double ) -> Double {
728+ return ceil ( value - 0.5 )
729+ }
738730
739- if opts. contains ( . alignWidthInward) && width != 0 {
740- guard width. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
741- width = floor ( aRect. size. width. native)
742- }
743- if opts. contains ( . alignHeightInward) && height != 0 {
744- guard height. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
745- height = floor ( aRect. size. height. native)
746- }
747- if opts. contains ( . alignWidthOutward) && width != 0 {
748- guard width. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
749- width = ceil ( aRect. size. width. native)
750- }
751- if opts. contains ( . alignHeightOutward) && height != 0 {
752- guard height. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
753- height = ceil ( aRect. size. height. native)
731+ fileprivate extension AlignmentOptions {
732+ var isAlignInward : Bool {
733+ return ( rawValue & 0xFF ) != 0
754734 }
755- if opts. contains ( . alignWidthNearest) && width != 0 {
756- guard width. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
757- width = round ( aRect. size. width. native)
758- }
759- if opts. contains ( . alignHeightNearest) && height != 0 {
760- guard height. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
761- height = round ( aRect. size. height. native)
762- }
763-
764735
765- if opts. contains ( . alignMinXInward) {
766- guard minX. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
767- minX = ceil ( aRect. origin. x. native)
768- }
769- if opts. contains ( . alignMinYInward) {
770- guard minY. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
771- minY = ceil ( aRect. origin. y. native)
772- }
773- if opts. contains ( . alignMaxXInward) {
774- guard maxX. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
775- maxX = floor ( aRect. origin. x. native + aRect. size. width. native)
776- }
777- if opts. contains ( . alignMaxYInward) {
778- guard maxY. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
779- maxY = floor ( aRect. origin. y. native + aRect. size. height. native)
736+ var isAlignNearest : Bool {
737+ return ( rawValue & 0xFF0000 ) != 0
780738 }
781-
782739
783- if opts. contains ( . alignMinXOutward) {
784- guard minX. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
785- minX = floor ( aRect. origin. x. native)
786- }
787- if opts. contains ( . alignMinYOutward) {
788- guard minY. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
789- minY = floor ( aRect. origin. y. native)
790- }
791- if opts. contains ( . alignMaxXOutward) {
792- guard maxX. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
793- maxX = ceil ( aRect. origin. x. native + aRect. size. width. native)
794- }
795- if opts. contains ( . alignMaxYOutward) {
796- guard maxY. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
797- maxY = ceil ( aRect. origin. y. native + aRect. size. height. native)
740+ var minXOptions : AlignmentOptions {
741+ return intersection ( [ . alignMinXInward, . alignMinXNearest, . alignMinXOutward] )
798742 }
799743
800-
801- if opts. contains ( . alignMinXNearest) {
802- guard minX. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
803- minX = round ( aRect. origin. x. native)
804- }
805- if opts. contains ( . alignMinYNearest) {
806- guard minY. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
807- minY = round ( aRect. origin. y. native)
744+ var maxXOptions : AlignmentOptions {
745+ return intersection ( [ . alignMaxXInward, . alignMaxXNearest, . alignMaxXOutward] )
808746 }
809- if opts. contains ( . alignMaxXNearest) {
810- guard maxX. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
811- maxX = round ( aRect. origin. x. native + aRect. size. width. native)
812- }
813- if opts. contains ( . alignMaxYNearest) {
814- guard maxY. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
815- maxY = round ( aRect. origin. y. native + aRect. size. height. native)
747+
748+ var widthOptions : AlignmentOptions {
749+ return intersection ( [ . alignWidthInward, . alignWidthNearest, . alignWidthOutward] )
816750 }
817751
818- var resultOriginX = CGFloat . NativeType. nan
819- var resultOriginY = CGFloat . NativeType. nan
820- var resultWidth = CGFloat . NativeType. nan
821- var resultHeight = CGFloat . NativeType. nan
752+ var minYOptions : AlignmentOptions {
753+ return intersection ( [ . alignMinYInward, . alignMinYNearest, . alignMinYOutward] )
754+ }
822755
823- if !minX . isNaN {
824- resultOriginX = minX
756+ var maxYOptions : AlignmentOptions {
757+ return intersection ( [ . alignMaxYInward , . alignMaxYNearest , . alignMaxYOutward ] )
825758 }
826- if !width. isNaN {
827- resultWidth = width
759+
760+ var heightOptions : AlignmentOptions {
761+ return intersection ( [ . alignHeightInward, . alignHeightNearest, . alignHeightOutward] )
828762 }
829- if !maxX. isNaN {
830- if width. isNaN {
831- guard resultWidth. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
832- resultWidth = maxX - minX
763+ }
764+
765+ fileprivate func integralizeRectAttribute( _ num: Double , options: AlignmentOptions , inward: ( Double ) -> Double , outward: ( Double ) -> Double , nearest: ( Double ) -> Double ) -> Double {
766+ let tolerance : Double = ( 1.0 / Double( 1 << 8 ) )
767+ if options. isAlignNearest {
768+ let numTimesTwo = num * 2
769+ let roundedNumTimesTwo = roundedTowardPlusInfinity ( numTimesTwo)
770+ if fabs ( numTimesTwo - roundedNumTimesTwo) < 2 * tolerance {
771+ return nearest ( roundedNumTimesTwo / 2 )
833772 } else {
834- guard resultOriginX. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
835- resultOriginX = maxX - width
773+ return nearest ( num)
836774 }
837- }
838-
839-
840- if !minY. isNaN {
841- resultOriginY = minY
842- }
843- if !height. isNaN {
844- resultHeight = height
845- }
846- if !maxY. isNaN {
847- if height. isNaN {
848- guard resultHeight. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
849- resultHeight = maxY - minY
775+ } else {
776+ let roundedNum = roundedTowardPlusInfinity ( num)
777+ if fabs ( num - roundedNum) < tolerance {
778+ return roundedNum
850779 } else {
851- guard resultOriginY. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
852- resultOriginY = maxY - height
780+ if options. isAlignInward {
781+ return inward ( num)
782+ } else {
783+ return outward ( num)
784+ }
853785 }
854786 }
855-
856- if resultOriginX. isNaN || resultOriginY. isNaN
857- || resultHeight. isNaN || resultWidth. isNaN {
858- fatalError ( listOfOptionsIsInconsistentErrorMessage)
787+ }
788+
789+ extension AlignmentOptions {
790+ func assertValid( ) {
791+ let inAttributes = rawValue & 0xFF
792+ let outAttributes = ( rawValue & 0xFF00 ) >> 8
793+ let nearestAttributes = ( rawValue & 0xFF0000 ) >> 16
794+
795+ let horizontal : AlignmentOptions = [ . alignMinXInward, . alignMinXOutward, . alignMinXNearest, . alignMaxXInward, . alignMaxXOutward, . alignMaxXNearest, . alignWidthInward, . alignWidthOutward, . alignWidthNearest]
796+ let vertical : AlignmentOptions = [ . alignMinYInward, . alignMinYOutward, . alignMinYNearest, . alignMaxYInward, . alignMaxYOutward, . alignMaxYNearest, . alignHeightInward, . alignHeightOutward, . alignHeightNearest]
797+
798+ if ( ( inAttributes & outAttributes) | ( inAttributes & nearestAttributes) | ( outAttributes & nearestAttributes) ) != 0 {
799+ preconditionFailure ( " The options parameter is invalid. Only one of {in, out, nearest} may be set for a given rect attribute. " )
800+ }
801+
802+ if intersection ( horizontal) . rawValue. nonzeroBitCount != 2 {
803+ preconditionFailure ( " The options parameter is invalid. There should be specifiers for exactly two out of {minX, maxX, width}. " )
804+ }
805+
806+ if intersection ( vertical) . rawValue. nonzeroBitCount != 2 {
807+ preconditionFailure ( " The options parameter is invalid. There should be specifiers for exactly two out of {minY, maxY, height}. " )
808+ }
859809 }
860-
861- var result = NSRect . zero
862- result. origin. x. native = resultOriginX
863- result. origin. y. native = resultOriginY
864- result. size. width. native = resultWidth
865- result. size. height. native = resultHeight
866-
867- return result
810+ }
811+
812+ public func NSIntegralRectWithOptions( _ aRect: NSRect , _ opts: AlignmentOptions ) -> NSRect {
813+ opts. assertValid ( )
814+
815+ var integralRect : NSRect = . zero
816+ let horizontalEdgeNearest = roundedTowardPlusInfinity
817+ let verticalEdgeNearest = opts. contains ( . alignRectFlipped) ? roundedTowardMinusInfinity : roundedTowardPlusInfinity
818+
819+ // two out of these three sets of options will have a single bit set:
820+ let minXOptions = opts. minXOptions
821+ let maxXOptions = opts. maxXOptions
822+ let widthOptions = opts. widthOptions
823+
824+ if minXOptions. isEmpty {
825+ // we have a maxX and a width
826+ integralRect. size. width = CGFloat ( integralizeRectAttribute ( Double ( NSWidth ( aRect) ) ,
827+ options: widthOptions,
828+ inward: floor,
829+ outward: ceil,
830+ nearest: roundedTowardPlusInfinity) )
831+ integralRect. origin. x = CGFloat ( integralizeRectAttribute ( Double ( NSMaxX ( aRect) ) ,
832+ options: maxXOptions,
833+ inward: floor,
834+ outward: ceil,
835+ nearest: horizontalEdgeNearest) ) - NSWidth( integralRect)
836+ } else if maxXOptions. isEmpty {
837+ // we have a minX and a width
838+ integralRect. origin. x = CGFloat ( integralizeRectAttribute ( Double ( NSMinX ( aRect) ) ,
839+ options: minXOptions,
840+ inward: ceil,
841+ outward: floor,
842+ nearest: horizontalEdgeNearest) )
843+ integralRect. size. width = CGFloat ( integralizeRectAttribute ( Double ( NSWidth ( aRect) ) ,
844+ options: widthOptions,
845+ inward: floor,
846+ outward: ceil,
847+ nearest: roundedTowardPlusInfinity) )
848+ } else {
849+ // we have a minX and a width
850+ integralRect. origin. x = CGFloat ( integralizeRectAttribute ( Double ( NSMinX ( aRect) ) ,
851+ options: minXOptions,
852+ inward: ceil,
853+ outward: floor,
854+ nearest: horizontalEdgeNearest) )
855+ integralRect. size. width = CGFloat ( integralizeRectAttribute ( Double ( NSMaxX ( aRect) ) ,
856+ options: maxXOptions,
857+ inward: floor,
858+ outward: ceil,
859+ nearest: horizontalEdgeNearest) ) - NSMinX( integralRect)
860+ }
861+
862+ // no negarects
863+ integralRect. size. width = max ( integralRect. size. width, 0 )
864+
865+ // two out of these three sets of options will have a single bit set:
866+ let minYOptions = opts. minYOptions
867+ let maxYOptions = opts. maxYOptions
868+ let heightOptions = opts. heightOptions
869+
870+ if minYOptions. isEmpty {
871+ // we have a maxY and a height
872+ integralRect. size. height = CGFloat ( integralizeRectAttribute ( Double ( NSHeight ( aRect) ) ,
873+ options: heightOptions,
874+ inward: floor,
875+ outward: ceil,
876+ nearest: roundedTowardPlusInfinity) )
877+ integralRect. origin. y = CGFloat ( integralizeRectAttribute ( Double ( NSMaxY ( aRect) ) ,
878+ options: maxYOptions,
879+ inward: floor,
880+ outward: ceil,
881+ nearest: verticalEdgeNearest) ) - NSHeight( integralRect)
882+ } else if maxYOptions. isEmpty {
883+ // we have a minY and a height
884+ integralRect. origin. y = CGFloat ( integralizeRectAttribute ( Double ( NSMinY ( aRect) ) ,
885+ options: minYOptions,
886+ inward: ceil,
887+ outward: floor,
888+ nearest: verticalEdgeNearest) )
889+ integralRect. size. height = CGFloat ( integralizeRectAttribute ( Double ( NSHeight ( aRect) ) ,
890+ options: heightOptions,
891+ inward: floor,
892+ outward: ceil,
893+ nearest: roundedTowardPlusInfinity) )
894+ } else {
895+ // we have a minY and a maxY
896+ integralRect. origin. y = CGFloat ( integralizeRectAttribute ( Double ( NSMinY ( aRect) ) ,
897+ options: minYOptions,
898+ inward: ceil,
899+ outward: floor,
900+ nearest: verticalEdgeNearest) )
901+ integralRect. size. height = CGFloat ( integralizeRectAttribute ( Double ( NSMaxY ( aRect) ) ,
902+ options: maxYOptions,
903+ inward: floor,
904+ outward: ceil,
905+ nearest: verticalEdgeNearest) ) - NSMinY( integralRect)
906+ }
907+
908+ // no negarects
909+ integralRect. size. height = max ( integralRect. size. height, 0 )
910+
911+ return integralRect
868912}
869913
870914public func NSUnionRect( _ aRect: NSRect , _ bRect: NSRect ) -> NSRect {
0 commit comments