@@ -712,159 +712,203 @@ public func NSIntegralRect(_ aRect: NSRect) -> NSRect {
712
712
return . zero
713
713
}
714
714
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
716
721
}
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
- }
723
722
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
+ }
730
726
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
+ }
738
730
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
754
734
}
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
-
764
735
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
780
738
}
781
-
782
739
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] )
798
742
}
799
743
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] )
808
746
}
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] )
816
750
}
817
751
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
+ }
822
755
823
- if !minX . isNaN {
824
- resultOriginX = minX
756
+ var maxYOptions : AlignmentOptions {
757
+ return intersection ( [ . alignMaxYInward , . alignMaxYNearest , . alignMaxYOutward ] )
825
758
}
826
- if !width. isNaN {
827
- resultWidth = width
759
+
760
+ var heightOptions : AlignmentOptions {
761
+ return intersection ( [ . alignHeightInward, . alignHeightNearest, . alignHeightOutward] )
828
762
}
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 )
833
772
} else {
834
- guard resultOriginX. isNaN else { fatalError ( listOfOptionsIsInconsistentErrorMessage) }
835
- resultOriginX = maxX - width
773
+ return nearest ( num)
836
774
}
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
850
779
} 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
+ }
853
785
}
854
786
}
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
+ }
859
809
}
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
868
912
}
869
913
870
914
public func NSUnionRect( _ aRect: NSRect , _ bRect: NSRect ) -> NSRect {
0 commit comments