Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explanation #1

Closed
bdlukaa opened this issue Nov 15, 2021 · 6 comments
Closed

Explanation #1

bdlukaa opened this issue Nov 15, 2021 · 6 comments

Comments

@bdlukaa
Copy link

bdlukaa commented Nov 15, 2021

this is a question

I am trying to port this algorithm to Flutter - Dart. I don't swift, so I would like to have some better documentation on what the code itself does, if possible.

I was able to convert the whole code to Dart, except for the following line:

//! I haven't found any `lazy` variables in GCRect. Also, `self` is not a list. Am I missing something?
let nonEmpties = self.lazy.filter { !$0.isEmpty }

https://github.com/mayoff/RectangleContour/blob/main/Sources/RectangleContour/Collection.contour.swift#L129

@mayoff
Copy link
Owner

mayoff commented Nov 16, 2021

That line is in the contourEdges method, which is defined in an extension to the Collection protocol. So the contourEdges method is generic; self there is some type that conforms to Collection. For example, Array and Set both conform to Collection.

Hmm. There's a bug in the definition of ys immediately after. I should have written this:

        let ys = nonEmpties.map(\.origin.y).makeSet()
            .union(nonEmpties.map { $0.origin.y + $0.size.height })
            .sorted()

I should probably fix that.

Anyway, back to your question… You will get the same result, at the cost of using more memory, if you remove the use of lazy:

let nonEmpties = self.filter { !$0.isEmpty }

The filter method is defined on Sequence, and returns an Array. So that line would set nonEmpties to an Array of all the non-empty CGRects in self. And then the subsequent statement (initializing ys) would create another Array by calling map.

The use of lazy eliminates the creation of those temporary arrays.

The lazy property returns a LazySequence that wraps self. LazySequence has its own definitions of filter and map that also return lazy sequences. These lazy sequences perform the filtering and mapping on demand when they are iterated (which happens in my code when I call makeSet and union) without creating temporary Arrays.

I don't know if Dart or Flutter has anything equivalent to lazy. If not, just use arrays and don't worry about it unless your collection contains thousands or millions of rectangles.

@bdlukaa
Copy link
Author

bdlukaa commented Nov 16, 2021

Thanks for your quick response! I think I got it now, but then I ran into another question. What does

let iForY: [CGFloat: Int] = ys.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset }

do? I really can't get it using the documentation

https://github.com/mayoff/RectangleContour/blob/main/Sources/RectangleContour/Collection.contour.swift#L139

@mayoff
Copy link
Owner

mayoff commented Nov 16, 2021

enumerated turns a sequence of elements into a sequence of pairs, where each element is paired with its offset into the sequence. Offsets start at 0.

let iForY: [CGFloat: Int] = ys.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset }
                                                         └┬┘    ├┘ └───┬────┘    └───┬───┘
            An empty dictionary of type [CGFloat: Int] ───┘     │      │             │
                                                                │      │             │
        A partially filled dictionary of type [CGFloat: Int] ───┘      │             │
                                                                       │             │
                                            A CGFloat element of ys ───┘             │
                                                                                     │
                                               The Int offset of $1.element in ys ───┘

@mayoff
Copy link
Owner

mayoff commented Nov 16, 2021

Because all elements of ys are unique, you could write it like this instead:

var iForY: [CGFloat: Int] = []
for y in ys {
    iForY[y] = iForY.count
}

@bdlukaa
Copy link
Author

bdlukaa commented Nov 16, 2021

Omg! It's a lot easier way to do it! I'll try and LYK

@bdlukaa
Copy link
Author

bdlukaa commented Feb 20, 2022

I ended up doing another implementation using line heights provided by TextPainter.

https://pub.dev/packages/rounded_background_text

@bdlukaa bdlukaa closed this as completed Feb 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants