@@ -120,4 +120,242 @@ final class XMLFormatterTests: XCTestCase {
120120 """
121121 )
122122 }
123+
124+ func testPairedSequence( ) {
125+ XCTAssertEqual (
126+ [ " a " , " b " , " c " , " d " ] . paired ( with: . nextSkippingLast) . map { " \( $0) \( $1) " } ,
127+ [ " ab " , " bc " , " cd " ]
128+ )
129+
130+ XCTAssertEqual (
131+ [ " a " , " b " , " c " , " d " ] . paired ( with: . nextWrappingToFirst) . map { " \( $0) \( $1) " } ,
132+ [ " ab " , " bc " , " cd " , " da " ]
133+ )
134+
135+ XCTAssertEqual (
136+ [ " a " , " b " ] . paired ( with: . nextSkippingLast) . map { " \( $0) \( $1) " } ,
137+ [ " ab " ]
138+ )
139+
140+ XCTAssertEqual (
141+ [ " a " , " b " ] . paired ( with: . nextWrappingToFirst) . map { " \( $0) \( $1) " } ,
142+ [ " ab " , " ba " ]
143+ )
144+
145+ XCTAssertEqual (
146+ [ " a " ] . paired ( with: . nextSkippingLast) . map { " \( $0) \( $1) " } ,
147+ [ ]
148+ )
149+
150+ XCTAssertEqual (
151+ [ " a " ] . paired ( with: . nextWrappingToFirst) . map { " \( $0) \( $1) " } ,
152+ [ ]
153+ )
154+
155+ XCTAssertEqual (
156+ [ ] . paired ( with: . nextSkippingLast) . map { " \( $0) \( $1) " } ,
157+ [ ]
158+ )
159+
160+ XCTAssertEqual (
161+ [ ] . paired ( with: . nextWrappingToFirst) . map { " \( $0) \( $1) " } ,
162+ [ ]
163+ )
164+ }
165+
166+ func testEvenOddPathDirection( ) throws {
167+ let pathData = """
168+ M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22Z
169+ M12 13C11.4477 13 11 12.5523 11 12V8C11 7.44772 11.4477 7 12 7C12.5523 7 13 7.44772 13 8V12C13 12.5523 12.5523 13 12 13
170+ ZM13 17H11V15H13V17Z
171+ """
172+ let domPath = try XMLParser ( ) . parsePath ( from: pathData)
173+ let layerPath = try LayerTree . Builder. createPath ( from: domPath)
174+
175+ XCTAssertEqual (
176+ layerPath. subPaths ( ) . map ( \. direction) ,
177+ [ . clockwise, . clockwise, . clockwise]
178+ )
179+ }
180+
181+ func testNonZeroDirection( ) throws {
182+ let pathData = """
183+ M12,22C17.523,22 22,17.523 22,12C22,6.477 17.523,2 12,2C6.477,2 2,6.477 2,12C2,17.523 6.477,22 12,22ZM13,17L11,17L11,15L13,15L13,17ZM12,13C11.448,13 11,12.552 11,12L11,8C11,7.448 11.448,7 12,7C12.552,7 13,7.448 13,8L13,12C13,12.552 12.552,13 12,13Z
184+ """
185+ let domPath = try XMLParser ( ) . parsePath ( from: pathData)
186+ let layerPath = try LayerTree . Builder. createPath ( from: domPath)
187+
188+ XCTAssertEqual (
189+ layerPath. subPaths ( ) . map ( \. direction) ,
190+ [ . anticlockwise, . clockwise, . clockwise]
191+ )
192+ }
193+
194+ func testEvenOdd( ) throws {
195+ let pathData = """
196+ M 75 100
197+ l 50 -50 l 50 50 l -50 50 Z
198+ m 25 0
199+ l 25 -25 l 25 25 l -25 25 Z
200+ m 10 0
201+ l 15 -15 l 15 15 l -15 15 Z
202+ m 10 0
203+ l 5 -5 l 5 5 l -5 5 Z
204+ M 225 100
205+ l 50 -50 l 50 50 l -50 50 Z
206+ m 25 0
207+ l 25 -25 l 25 25 l -25 25 Z
208+ m 10 0
209+ l 15 -15 l 15 15 l -15 15 Z
210+ m 10 0
211+ l 5 -5 l 5 5 l -5 5 Z
212+ """
213+ let domPath = try XMLParser ( ) . parsePath ( from: pathData)
214+ let layerPath = try LayerTree . Builder. createPath ( from: domPath)
215+
216+ let paths = MyPath . make ( from: layerPath)
217+ print ( paths. count)
218+ print ( paths [ 0 ] . inside. count)
219+ print ( paths [ 1 ] . inside. count)
220+ }
221+
222+ func testNonZero( ) throws {
223+ let pathData = """
224+ M 75 100
225+ l 50 -50 l 50 50 l -50 50 Z
226+ m 25 0
227+ l 25 25 l 25 -25 l -25 -25 Z
228+ m 10 0
229+ l 15 15 l 15 -15 l -15 -15 Z
230+ m 10 0
231+ l 5 -5 l 5 5 l -5 5 Z
232+ M 225 100
233+ l 50 -50 l 50 50 l -50 50 Z
234+ m 25 0
235+ l 25 25 l 25 -25 l -25 -25 Z
236+ m 10 0
237+ l 15 15 l 15 -15 l -15 -15 Z
238+ m 10 0
239+ l 5 -5 l 5 5 l -5 5 Z
240+ """
241+ let domPath = try XMLParser ( ) . parsePath ( from: pathData)
242+ let layerPath = try LayerTree . Builder. createPath ( from: domPath)
243+
244+ let paths = MyPath . make ( from: layerPath)
245+ print ( paths. count)
246+ print ( paths [ 0 ] . inside. count)
247+ print ( paths [ 1 ] . inside. count)
248+
249+ print ( paths [ 0 ] . inside [ 0 ] . inside. count)
250+ print ( paths [ 0 ] . inside [ 0 ] . inside [ 0 ] . inside. count)
251+ }
252+ }
253+
254+ extension LayerTree . Path {
255+
256+ func subPaths( ) -> [ ArraySlice < Segment > ] {
257+ segments. split ( separator: . close)
258+ }
259+ }
260+
261+ extension Array where Element == LayerTree . Point {
262+ func isPointInside( _ test: LayerTree . Point ) -> Bool {
263+ var j : Int = count - 1
264+ var contains = false
265+
266+ for i in indices {
267+ if ( ( ( self [ i] . y < test. y && self [ j] . y >= test. y) || ( self [ j] . y < test. y && self [ i] . y >= test. y) )
268+ && ( self [ i] . x <= test. x || self [ j] . x <= test. x) ) {
269+ contains = contains != ( self [ i] . x + ( test. y - self [ i] . y) / ( self [ j] . y - self [ i] . y) * ( self [ j] . x - self [ i] . x) < test. x)
270+ }
271+
272+ j = i
273+ }
274+
275+ if !contains {
276+ print ( " **** " )
277+ print ( " polygon " , self . map { $0. stringValue } )
278+ print ( " point " , test. stringValue, contains)
279+ print ( " **** " )
280+ }
281+
282+ return contains
283+ }
284+ }
285+
286+ struct MyPath {
287+ var segments : ArraySlice < LayerTree . Path . Segment >
288+ var inside : [ MyPath ] = [ ]
289+
290+ func bounds( segments: ArraySlice < LayerTree . Path . Segment > ) -> Bool {
291+ let outer = self . segments. compactMap ( \. location)
292+ let inner = segments. compactMap ( \. location)
293+ guard !inner. isEmpty else { return false }
294+
295+ for p in inner {
296+ if !outer. isPointInside ( p) {
297+ return false
298+ }
299+ }
300+
301+ return true
302+ }
303+
304+ mutating func appendPath( _ p: MyPath ) {
305+ for (idx, path) in inside. enumerated ( ) {
306+ if path. bounds ( segments: p. segments) {
307+ inside [ idx] . appendPath ( p)
308+ return
309+ }
310+ }
311+ inside. append ( p)
312+ }
313+
314+ static func make( from path: LayerTree . Path ) -> [ MyPath ] {
315+ var paths = [ MyPath] ( )
316+ for segments in path. segments. split ( separator: . close) {
317+ paths. append ( segments: segments)
318+ }
319+ return paths
320+ }
321+ }
322+
323+ private extension Array where Element == MyPath {
324+
325+ mutating func append( segments: ArraySlice < LayerTree . Path . Segment > ) {
326+ for (idx, path) in enumerated ( ) {
327+ if path. bounds ( segments: segments) {
328+ self [ idx] . appendPath ( MyPath ( segments: segments) )
329+ return
330+ }
331+ }
332+
333+ append ( MyPath ( segments: segments) )
334+ }
335+
336+ }
337+
338+ private extension LayerTree . Path . Segment {
339+
340+ var isMove : Bool {
341+ switch self {
342+ case . move: return true
343+ default : return false
344+ }
345+ }
346+
347+ var location : LayerTree . Point ? {
348+ switch self {
349+ case . move( to: let p) : return p
350+ case . line( let p) : return p
351+ case . cubic( let p, _, _) : return p
352+ case . close: return nil
353+ }
354+ }
355+ }
356+
357+ extension LayerTree . Point {
358+ var stringValue : String {
359+ " \( x) , \( y) "
360+ }
123361}
0 commit comments