Git Product home page Git Product logo

stretch's Introduction

Stretch

CircleCI Cargo npm cocoapods bintray

Stretch is an implementation of Flexbox written in Rust. The goal of stretch is to provide a solid foundation for layout across all platforms with a specific focus on mobile. Long term we want stretch to not only support flexbox but also many other layout algorithms such as grid layout. Stretch was made for and powers https://visly.app.

Goals

Before using or contributing to stretch it is good to be aware of the core goals of the project. These are goals we are working towards, not necessarily features we currently support.

  • High performance
  • Cross platform
  • Small binary size
  • Support multiple layout systems
  • Multi-threaded layout
  • Language bindings for most common languages

Supported Platforms

  • Rust
  • Android
  • iOS
  • JavaScript / TypeScript

Usage

Stretch is built in Rust but comes with bindings to multiple languages and platforms so you can use it in a way that feels natural to your project.

Rust

# Cargo.toml

[dependencies]
stretch = "0.3.2"
// main.rs

use stretch::geometry::Size;
use stretch::style::*;

fn main() -> Result<(), stretch::Error> {
    let mut stretch = stretch::node::Stretch::new();
    
    let child = stretch.new_node(
        Style { size: Size { width: Dimension::Percent(0.5), height: Dimension::Auto }, ..Default::default() },
        vec![],
    )?;

    let node = stretch.new_node(
        Style {
            size: Size { width: Dimension::Points(100.0), height: Dimension::Points(100.0) },
            justify_content: JustifyContent::Center,
            ..Default::default()
        },
        vec![child],
    )?;

    stretch.compute_layout(node, Size::undefined())?;
    dbg!(stretch.layout(node)?);
}

Android

// Build.gradle

android {
    splits {
        abi {
            enable true
        }
    }
}

dependencies {
    implementation 'app.visly.stretch:stretch:0.3.2'
}
// MainActivity.kt

val node = Node(
  Style(size = Size(Dimension.Points(100f), Dimension.Points(100f)), justifyContent = JustifyContent.Center), 
  listOf(
    Node(Style(size = Size(Dimension.Percent(0.5f), Dimension.Percent(0.5f))), listOf())
  ))

val layout = node.computeLayout(Size(null, null))
Log.d(TAG, "width: ${layout.width}, height: ${layout.height}")

iOS

# Podfile

pod 'StretchKit', '~> 0.3.2'
// ViewController.swift
 
let node = Node(
  style: Style(size: Size(width: .points(100.0), height: .points(100.0)), justifyContent: .center), 
  children: [
    Node(style: Style(size: Size(width: .percent(0.5), height: .percent(0.5))), children: [])
  ])
  
let layout = node.computeLayout(thatFits: Size(width: nil, height: nil))
print("width: \(layout.width), height: \(layout.height)")

JavaScript

> npm install --save stretch-layout
// index.js

import { Allocator, Node, JustifyContent } from 'stretch-layout';

const allocator = new Allocator();
const node = new Node(allocator, {width: 100, height: 100, justifyContent: JustifyContent.Center});
node.addChild(new Node(allocator, {width: '50%', height: '50%'}));
const layout = node.computeLayout();

console.log(layout.width, layout.height);

Contributing

Contributions are very welcome. Though we ask that you open an issue or pull request early in the process (before writing code) so we can discuss solutions and additions before you start spending time on implementing them. There are some specific areas where we would be extra happy to receive contributions in.

  • Binary size reduction
  • Runtime performance
  • Ensure build / test environment works well on non macOS platforms
  • Alternate layout systems (grid layout perhaps?)
  • Web compatibility tests
  • RTL support
  • Platform bindings
  • API improvements
  • Documentation & Examples

Installation

If you don't have Rust installed you have to do that first as well as install some components that we make use of to format and lint the codebase. For more on Rust see their website.

curl https://sh.rustup.rs -sSf | sh
rustup component add rustfmt
rustup component add clippy

Once that is done you can clone the repo and do some sanity checks to make sure everything is working correctly.

git clone https://github.com/vislyhq/stretch.git
cd stretch
cargo test

If you have made any changes to the API you should also update and run tests for all the platform bindings located in /bindings/*.

Testing

Stretch is tested by validating that layouts written in stretch perform the same as in Chrome. This is done by rendering an equivalent layout in HTML and then generating a Rust test case which asserts that the resulting layout is the same when run through stretch.

You can run these tests without setting up a webdriver environment but if you are looking to add any test case you will need to install chromedriver. If you are developing on macOS this is easy to do through brew.

brew tap homebrew/cask
brew cask install chromedriver

Once you have chromedriver installed and available in PATH you can re-generate all tests by running cargo run --package gentest.

To add a new test case add another HTML file to /test_fixtures following the current tests as a template for new tests.

Benchmarking

Benchmarks build on the same infrastructure as testing, and actually benchmarks are automatically generated from test fixtures just like tests. Run cargo bench to run benchmarks locally.

Relationship to Yoga

Yoga is a cross-platform implementation of Flexbox written in C. Yoga is a fantastic project but has some fundamental issues which we hope to resolve. Compared to Yoga we aim to have a stronger adherence to web standards, a flexible architecture eventually supporting multiple layout algorithms, and future performance improvements including multi-threaded layout. In addition to this we aim to use a safer language with a more modern codebase.

LICENCE

Copyright (c) 2018 Visly Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

stretch's People

Contributors

0xflotus avatar adamnemecek avatar anp avatar biezhihua avatar davidpendraykalibrate avatar dflemstr avatar emilsjolander avatar heswell avatar hobofan avatar jugglinmike avatar msiglreith avatar noahsark769 avatar passy avatar seeekr avatar yawor avatar zamotany avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stretch's Issues

[Crash][Native][Kotlin] Generating node trees frequently, crashes due to GC

Hi, I encountered a crash, frequent generation of node trees, and crashes due to GC.

I suspect the problem was caused by the different timing of the GC notification to finzlied.

03-24 10:41:04.630  1974  1974 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstone
03-24 10:41:04.631   940   940 I /system/bin/tombstoned: received crash request for pid 1935
03-24 10:41:04.632  1974  1974 I crash_dump64: performing dump of process 1925 (target tid = 1935)
03-24 10:41:04.648  1974  1974 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
03-24 10:41:04.648  1974  1974 F DEBUG   : Build fingerprint: 'OnePlus/OnePlus5T/OnePlus5T:9/PKQ1.180716.001/2002171214:user/release-keys'
03-24 10:41:04.648  1974  1974 F DEBUG   : Revision: '0'
03-24 10:41:04.648  1974  1974 F DEBUG   : ABI: 'arm64'
03-24 10:41:04.648  1974  1974 F DEBUG   : pid: 1925, tid: 1935, name: FinalizerDaemon  >>> com.youku.gaiax.demo.simple <<<
03-24 10:41:04.648  1974  1974 F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
03-24 10:41:04.648  1974  1974 F DEBUG   :     x0  0000000000000000  x1  000000000000078f  x2  0000000000000006  x3  0000000000000008
03-24 10:41:04.648  1974  1974 F DEBUG   :     x4  0000007b19b831c8  x5  0000007b19b831c8  x6  0000007b19b831c8  x7  0000007b19b83000
03-24 10:41:04.648  1974  1974 F DEBUG   :     x8  0000000000000083  x9  8d1b329898fe9f91  x10 0000000000000000  x11 fffffffc7ffffbdf
03-24 10:41:04.648  1974  1974 F DEBUG   :     x12 0000000000000001  x13 0000000051eb851f  x14 0000000000000064  x15 0000007b1bb11bb8
03-24 10:41:04.648  1974  1974 F DEBUG   :     x16 0000007bba86a2a8  x17 0000007bba789954  x18 0000000000000008  x19 0000000000000785
03-24 10:41:04.649  1974  1974 F DEBUG   :     x20 000000000000078f  x21 0000007b1bb120d0  x22 0000007b19bf2140  x23 0000000000000000
03-24 10:41:04.649  1974  1974 F DEBUG   :     x24 000000000000000f  x25 0000000000000028  x26 0000000000000001  x27 0000007b1bb11f30
03-24 10:41:04.649  1974  1974 F DEBUG   :     x28 0000007b1bb121e0  x29 0000007b1bb11e60
03-24 10:41:04.649  1974  1974 F DEBUG   :     sp  0000007b1bb11e20  lr  0000007bba77d084  pc  0000007bba77d0ac
03-24 10:41:04.682  1974  1974 F DEBUG   :
03-24 10:41:04.682  1974  1974 F DEBUG   : backtrace:
03-24 10:41:04.682  1974  1974 F DEBUG   :     #00 pc 00000000000220ac  /system/lib64/libc.so (abort+116)
03-24 10:41:04.682  1974  1974 F DEBUG   :     #01 pc 000000000002c59c  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so
03-24 10:41:04.682  1974  1974 F DEBUG   :     #02 pc 000000000002abc0  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so
03-24 10:41:04.682  1974  1974 F DEBUG   :     #03 pc 000000000002bad0  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so
03-24 10:41:04.682  1974  1974 F DEBUG   :     #04 pc 000000000002b9a4  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so
03-24 10:41:04.682  1974  1974 F DEBUG   :     #05 pc 000000000002b5bc  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so
03-24 10:41:04.682  1974  1974 F DEBUG   :     #06 pc 000000000003fce0  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so
03-24 10:41:04.682  1974  1974 F DEBUG   :     #07 pc 000000000003fc64  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so
03-24 10:41:04.682  1974  1974 F DEBUG   :     #08 pc 00000000000128f8  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so
03-24 10:41:04.682  1974  1974 F DEBUG   :     #09 pc 000000000000af80  /data/app/com.youku.gaiax.demo.simple-Rjdd1N59Tblc2DXs6p46ng==/lib/arm64/libstretch.so (Java_app_visly_stretch_Node_nFree+32)
03-24 10:41:04.682  1974  1974 F DEBUG   :     #10 pc 000000000055e7e0  /system/lib64/libart.so (art_quick_generic_jni_trampoline+144)
03-24 10:41:04.682  1974  1974 F DEBUG   :     #11 pc 000000000006cb30  /dev/ashmem/dalvik-jit-code-cache (deleted) (app.visly.stretch.Node.finalize+352)
03-24 10:41:04.682  1974  1974 F DEBUG   :     #12 pc 000000000001800c  /dev/ashmem/dalvik-jit-code-cache (deleted) (java.lang.Daemons$FinalizerDaemon.doFinalize+124)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #13 pc 0000000000555788  /system/lib64/libart.so (art_quick_invoke_stub+584)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #14 pc 00000000000cf6c8  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #15 pc 000000000027f2b8  /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #16 pc 00000000002792c0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #17 pc 00000000005260d4  /system/lib64/libart.so (MterpInvokeDirect+296)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #18 pc 0000000000547e14  /system/lib64/libart.so (ExecuteMterpImpl+14484)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #19 pc 00000000000aed9c  /system/framework/boot-core-libart.vdex (java.lang.Daemons$FinalizerDaemon.runInternal+164)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #20 pc 0000000000252fc4  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.545795818+488)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #21 pc 0000000000258ab8  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #22 pc 00000000002792a4  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #23 pc 0000000000524d94  /system/lib64/libart.so (MterpInvokeVirtual+588)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #24 pc 0000000000547d14  /system/lib64/libart.so (ExecuteMterpImpl+14228)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #25 pc 00000000000aeb94  /system/framework/boot-core-libart.vdex (java.lang.Daemons$Daemon.run+20)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #26 pc 0000000000252fc4  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.545795818+488)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #27 pc 0000000000258ab8  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #28 pc 00000000002792a4  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #29 pc 0000000000525d10  /system/lib64/libart.so (MterpInvokeInterface+1392)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #30 pc 0000000000547f14  /system/lib64/libart.so (ExecuteMterpImpl+14740)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #31 pc 00000000000cae9a  /system/framework/boot-core-oj.vdex (java.lang.Thread.run+12)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #32 pc 0000000000252fc4  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.545795818+488)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #33 pc 0000000000515628  /system/lib64/libart.so (artQuickToInterpreterBridge+1020)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #34 pc 000000000055e8fc  /system/lib64/libart.so (art_quick_to_interpreter_bridge+92)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #35 pc 0000000000555788  /system/lib64/libart.so (art_quick_invoke_stub+584)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #36 pc 00000000000cf6c8  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #37 pc 000000000045cd00  /system/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #38 pc 000000000045ddbc  /system/lib64/libart.so (art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, jvalue*)+424)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #39 pc 0000000000488c64  /system/lib64/libart.so (art::Thread::CreateCallback(void*)+1120)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #40 pc 000000000008ffc8  /system/lib64/libc.so (__pthread_start(void*)+36)
03-24 10:41:04.683  1974  1974 F DEBUG   :     #41 pc 0000000000023968  /system/lib64/libc.so (__start_thread+68)

new bug

fn align_test() {
use std::time::Instant;
let n1 = Instant::now();
let layout = stretch::node::Node::new(
stretch::style::Style {
flex_direction: stretch::style::FlexDirection::Column,
align_items: stretch::style::AlignItems::Center,
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(200f32),
height: stretch::style::Dimension::Points(200f32),
..Default::default()
},
..Default::default()
},
vec![&stretch::node::Node::new(
stretch::style::Style { flex_wrap: stretch::style::FlexWrap::Wrap, ..Default::default() },
vec![
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(150f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
&stretch::node::Node::new(
stretch::style::Style {
size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(80f32),
height: stretch::style::Dimension::Points(80f32),
..Default::default()
},
..Default::default()
},
vec![],
),
],
)],
)
.compute_layout(stretch::geometry::Size::undefined())
.unwrap();
let n2 = Instant::now();
println!("-----------------------time:{:?}", n2-n1);
assert_eq!(layout.size.width, 200f32);
assert_eq!(layout.size.height, 200f32);
assert_eq!(layout.location.x, 0f32);
assert_eq!(layout.location.y, 0f32);
assert_eq!(layout.children[0usize].size.width, 200f32);
assert_eq!(layout.children[0usize].size.height, 4080f32);
assert_eq!(layout.children[0usize].location.x, 0f32);
assert_eq!(layout.children[0usize].location.y, 0f32);
assert_eq!(layout.children[0usize].children[0usize].size.width, 150f32);
assert_eq!(layout.children[0usize].children[0usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[0usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[0usize].location.y, 0f32);
assert_eq!(layout.children[0usize].children[1usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[1usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[1usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[1usize].location.y, 80f32);
assert_eq!(layout.children[0usize].children[2usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[2usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[2usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[2usize].location.y, 80f32);
assert_eq!(layout.children[0usize].children[3usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[3usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[3usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[3usize].location.y, 160f32);
assert_eq!(layout.children[0usize].children[4usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[4usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[4usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[4usize].location.y, 160f32);
assert_eq!(layout.children[0usize].children[5usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[5usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[5usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[5usize].location.y, 240f32);
assert_eq!(layout.children[0usize].children[6usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[6usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[6usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[6usize].location.y, 240f32);
assert_eq!(layout.children[0usize].children[7usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[7usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[7usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[7usize].location.y, 320f32);
assert_eq!(layout.children[0usize].children[8usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[8usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[8usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[8usize].location.y, 320f32);
assert_eq!(layout.children[0usize].children[9usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[9usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[9usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[9usize].location.y, 400f32);
assert_eq!(layout.children[0usize].children[10usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[10usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[10usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[10usize].location.y, 400f32);
assert_eq!(layout.children[0usize].children[11usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[11usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[11usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[11usize].location.y, 480f32);
assert_eq!(layout.children[0usize].children[12usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[12usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[12usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[12usize].location.y, 480f32);
assert_eq!(layout.children[0usize].children[13usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[13usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[13usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[13usize].location.y, 560f32);
assert_eq!(layout.children[0usize].children[14usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[14usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[14usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[14usize].location.y, 560f32);
assert_eq!(layout.children[0usize].children[15usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[15usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[15usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[15usize].location.y, 640f32);
assert_eq!(layout.children[0usize].children[16usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[16usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[16usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[16usize].location.y, 640f32);
assert_eq!(layout.children[0usize].children[17usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[17usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[17usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[17usize].location.y, 720f32);
assert_eq!(layout.children[0usize].children[18usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[18usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[18usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[18usize].location.y, 720f32);
assert_eq!(layout.children[0usize].children[19usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[19usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[19usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[19usize].location.y, 800f32);
assert_eq!(layout.children[0usize].children[20usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[20usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[20usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[20usize].location.y, 800f32);
assert_eq!(layout.children[0usize].children[21usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[21usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[21usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[21usize].location.y, 880f32);
assert_eq!(layout.children[0usize].children[22usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[22usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[22usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[22usize].location.y, 880f32);
assert_eq!(layout.children[0usize].children[23usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[23usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[23usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[23usize].location.y, 960f32);
assert_eq!(layout.children[0usize].children[24usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[24usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[24usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[24usize].location.y, 960f32);
assert_eq!(layout.children[0usize].children[25usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[25usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[25usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[25usize].location.y, 1040f32);
assert_eq!(layout.children[0usize].children[26usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[26usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[26usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[26usize].location.y, 1040f32);
assert_eq!(layout.children[0usize].children[27usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[27usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[27usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[27usize].location.y, 1120f32);
assert_eq!(layout.children[0usize].children[28usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[28usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[28usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[28usize].location.y, 1120f32);
assert_eq!(layout.children[0usize].children[29usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[29usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[29usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[29usize].location.y, 1200f32);
assert_eq!(layout.children[0usize].children[30usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[30usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[30usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[30usize].location.y, 1200f32);
assert_eq!(layout.children[0usize].children[31usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[31usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[31usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[31usize].location.y, 1280f32);
assert_eq!(layout.children[0usize].children[32usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[32usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[32usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[32usize].location.y, 1280f32);
assert_eq!(layout.children[0usize].children[33usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[33usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[33usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[33usize].location.y, 1360f32);
assert_eq!(layout.children[0usize].children[34usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[34usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[34usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[34usize].location.y, 1360f32);
assert_eq!(layout.children[0usize].children[35usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[35usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[35usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[35usize].location.y, 1440f32);
assert_eq!(layout.children[0usize].children[36usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[36usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[36usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[36usize].location.y, 1440f32);
assert_eq!(layout.children[0usize].children[37usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[37usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[37usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[37usize].location.y, 1520f32);
assert_eq!(layout.children[0usize].children[38usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[38usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[38usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[38usize].location.y, 1520f32);
assert_eq!(layout.children[0usize].children[39usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[39usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[39usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[39usize].location.y, 1600f32);
assert_eq!(layout.children[0usize].children[40usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[40usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[40usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[40usize].location.y, 1600f32);
assert_eq!(layout.children[0usize].children[41usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[41usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[41usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[41usize].location.y, 1680f32);
assert_eq!(layout.children[0usize].children[42usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[42usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[42usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[42usize].location.y, 1680f32);
assert_eq!(layout.children[0usize].children[43usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[43usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[43usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[43usize].location.y, 1760f32);
assert_eq!(layout.children[0usize].children[44usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[44usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[44usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[44usize].location.y, 1760f32);
assert_eq!(layout.children[0usize].children[45usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[45usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[45usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[45usize].location.y, 1840f32);
assert_eq!(layout.children[0usize].children[46usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[46usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[46usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[46usize].location.y, 1840f32);
assert_eq!(layout.children[0usize].children[47usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[47usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[47usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[47usize].location.y, 1920f32);
assert_eq!(layout.children[0usize].children[48usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[48usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[48usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[48usize].location.y, 1920f32);
assert_eq!(layout.children[0usize].children[49usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[49usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[49usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[49usize].location.y, 2000f32);
assert_eq!(layout.children[0usize].children[50usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[50usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[50usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[50usize].location.y, 2000f32);
assert_eq!(layout.children[0usize].children[51usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[51usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[51usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[51usize].location.y, 2080f32);
assert_eq!(layout.children[0usize].children[52usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[52usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[52usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[52usize].location.y, 2080f32);
assert_eq!(layout.children[0usize].children[53usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[53usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[53usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[53usize].location.y, 2160f32);
assert_eq!(layout.children[0usize].children[54usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[54usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[54usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[54usize].location.y, 2160f32);
assert_eq!(layout.children[0usize].children[55usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[55usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[55usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[55usize].location.y, 2240f32);
assert_eq!(layout.children[0usize].children[56usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[56usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[56usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[56usize].location.y, 2240f32);
assert_eq!(layout.children[0usize].children[57usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[57usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[57usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[57usize].location.y, 2320f32);
assert_eq!(layout.children[0usize].children[58usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[58usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[58usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[58usize].location.y, 2320f32);
assert_eq!(layout.children[0usize].children[59usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[59usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[59usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[59usize].location.y, 2400f32);
assert_eq!(layout.children[0usize].children[60usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[60usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[60usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[60usize].location.y, 2400f32);
assert_eq!(layout.children[0usize].children[61usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[61usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[61usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[61usize].location.y, 2480f32);
assert_eq!(layout.children[0usize].children[62usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[62usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[62usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[62usize].location.y, 2480f32);
assert_eq!(layout.children[0usize].children[63usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[63usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[63usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[63usize].location.y, 2560f32);
assert_eq!(layout.children[0usize].children[64usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[64usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[64usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[64usize].location.y, 2560f32);
assert_eq!(layout.children[0usize].children[65usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[65usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[65usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[65usize].location.y, 2640f32);
assert_eq!(layout.children[0usize].children[66usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[66usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[66usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[66usize].location.y, 2640f32);
assert_eq!(layout.children[0usize].children[67usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[67usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[67usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[67usize].location.y, 2720f32);
assert_eq!(layout.children[0usize].children[68usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[68usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[68usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[68usize].location.y, 2720f32);
assert_eq!(layout.children[0usize].children[69usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[69usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[69usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[69usize].location.y, 2800f32);
assert_eq!(layout.children[0usize].children[70usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[70usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[70usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[70usize].location.y, 2800f32);
assert_eq!(layout.children[0usize].children[71usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[71usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[71usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[71usize].location.y, 2880f32);
assert_eq!(layout.children[0usize].children[72usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[72usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[72usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[72usize].location.y, 2880f32);
assert_eq!(layout.children[0usize].children[73usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[73usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[73usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[73usize].location.y, 2960f32);
assert_eq!(layout.children[0usize].children[74usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[74usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[74usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[74usize].location.y, 2960f32);
assert_eq!(layout.children[0usize].children[75usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[75usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[75usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[75usize].location.y, 3040f32);
assert_eq!(layout.children[0usize].children[76usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[76usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[76usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[76usize].location.y, 3040f32);
assert_eq!(layout.children[0usize].children[77usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[77usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[77usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[77usize].location.y, 3120f32);
assert_eq!(layout.children[0usize].children[78usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[78usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[78usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[78usize].location.y, 3120f32);
assert_eq!(layout.children[0usize].children[79usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[79usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[79usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[79usize].location.y, 3200f32);
assert_eq!(layout.children[0usize].children[80usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[80usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[80usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[80usize].location.y, 3200f32);
assert_eq!(layout.children[0usize].children[81usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[81usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[81usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[81usize].location.y, 3280f32);
assert_eq!(layout.children[0usize].children[82usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[82usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[82usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[82usize].location.y, 3280f32);
assert_eq!(layout.children[0usize].children[83usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[83usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[83usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[83usize].location.y, 3360f32);
assert_eq!(layout.children[0usize].children[84usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[84usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[84usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[84usize].location.y, 3360f32);
assert_eq!(layout.children[0usize].children[85usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[85usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[85usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[85usize].location.y, 3440f32);
assert_eq!(layout.children[0usize].children[86usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[86usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[86usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[86usize].location.y, 3440f32);
assert_eq!(layout.children[0usize].children[87usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[87usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[87usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[87usize].location.y, 3520f32);
assert_eq!(layout.children[0usize].children[88usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[88usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[88usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[88usize].location.y, 3520f32);
assert_eq!(layout.children[0usize].children[89usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[89usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[89usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[89usize].location.y, 3600f32);
assert_eq!(layout.children[0usize].children[90usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[90usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[90usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[90usize].location.y, 3600f32);
assert_eq!(layout.children[0usize].children[91usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[91usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[91usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[91usize].location.y, 3680f32);
assert_eq!(layout.children[0usize].children[92usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[92usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[92usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[92usize].location.y, 3680f32);
assert_eq!(layout.children[0usize].children[93usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[93usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[93usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[93usize].location.y, 3760f32);
assert_eq!(layout.children[0usize].children[94usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[94usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[94usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[94usize].location.y, 3760f32);
assert_eq!(layout.children[0usize].children[95usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[95usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[95usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[95usize].location.y, 3840f32);
assert_eq!(layout.children[0usize].children[96usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[96usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[96usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[96usize].location.y, 3840f32);
assert_eq!(layout.children[0usize].children[97usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[97usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[97usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[97usize].location.y, 3920f32);
assert_eq!(layout.children[0usize].children[98usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[98usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[98usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[98usize].location.y, 3920f32);
assert_eq!(layout.children[0usize].children[99usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[99usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[99usize].location.x, 0f32);
assert_eq!(layout.children[0usize].children[99usize].location.y, 4000f32);
assert_eq!(layout.children[0usize].children[100usize].size.width, 80f32);
assert_eq!(layout.children[0usize].children[100usize].size.height, 80f32);
assert_eq!(layout.children[0usize].children[100usize].location.x, 80f32);
assert_eq!(layout.children[0usize].children[100usize].location.y, 4000f32);
}

----------------------time:328.304µs
thread 'generated::align_test::align_test' panicked at 'assertion failed: (left == right)
left: 200.0,
right: 4080.0', tests\generated\align_test.rs:1142:5
note: Some details are omitted, run with RUST_BACKTRACE=full for a verbose backtrace.
stack backtrace:
0: std::sys_common::backtrace::_print
at src\libstd\sys\windows\backtrace/mod.rs:95
at src\libstd\sys\windows\backtrace/mod.rs:82
at src\libstd\sys_common/backtrace.rs:71
1: std::panicking::default_hook::{{closure}}
at src\libstd\sys_common/backtrace.rs:59
at src\libstd/panicking.rs:197
2: std::panicking::default_hook
at src\libstd/panicking.rs:208
3: std::panicking::rust_panic_with_hook
at src\libstd/panicking.rs:474
4: std::panicking::continue_panic_fmt
at src\libstd/panicking.rs:381
5: std::panicking::begin_panic_fmt
at src\libstd/panicking.rs:336
6: fixtures::generated::align_test::align_test
7: core::ops::function::FnOnce::call_once
8: core::ops::function::FnOnce::call_once{{vtable.shim}}
at src\libtest/lib.rs:1513
at /rustc/3f5152e200c0c02dfe0f79367948c98053d35855\src\libcore\ops/function.rs:231
at /rustc/3f5152e200c0c02dfe0f79367948c98053d35855\src\libcore\ops/function.rs:231
9: <alloc::boxed::Box as core::ops::function::FnOnce>::call_once
at /rustc/3f5152e200c0c02dfe0f79367948c98053d35855\src\liballoc/boxed.rs:704
10: _rust_maybe_catch_panic
at src\libpanic_unwind/lib.rs:85
11: test::run_test::run_test_inner::{{closure}}
at /rustc/3f5152e200c0c02dfe0f79367948c98053d35855\src\libstd/panicking.rs:272
at /rustc/3f5152e200c0c02dfe0f79367948c98053d35855\src\libstd/panic.rs:388
at src\libtest/lib.rs:1468

Interfacing with other languages

Hey there!

First of all: Awesome project.

Just some quick questions:

  • do you plan to expose methods via the C ABI or should that be done in a separate project?
  • do you plan on having bindings to other languages / frameworks in this repository?

(Backstory: I'm working on a React-like framework for Swift and I'm looking for a solid stand-alone implementation of flexbox.)

I'd be happy to help here where I can!

Dimensions with percentages not working in JavaScript port

First of all, this is an awesome project and I really appreciate everyone putting their effort into it.

While working on react-slate (a React renderer for terminal in Node JS), which is based on Stretch, I noticed that, when setting width/height, basically any Dimension property as a percentage, it is converted to plain Points:

setting width to 100% will result in Points(100) being used in layout calculations.

I've done some digging and found one line of code, which might be causing that in get_dimension function in bindings/js:

if string.ends_with('%') {
let len = string.len();
if let Ok(number) = string[..len - 1].parse::<f32>() {
return stretch::style::Dimension::Points(number);
}
}

Shouldn't this if let return return stretch::style::Dimension::Percent(number / 100);?

I'm happy to do a PR to fix that, but wanted a feedback before doing so.

The child node position is incorrect

The child node position is incorrect for node2

use stretch::geometry::Size;
use stretch::style::*;

fn main() {
    let mut stretch = stretch::Stretch::new();

    let node0 = stretch
        .new_node(
            stretch::style::Style {
                flex_grow: 1.0,
                flex_shrink: 1.0,

                ..Default::default()
            },
            vec![],
        )
        .unwrap();

    let node2 = stretch
        .new_node(
            stretch::style::Style {
                size: Size {
                    width: stretch::style::Dimension::Points(10.0),
                    height: stretch::style::Dimension::Points(10.0),
                },

                ..Default::default()
            },
            vec![],
        )
        .unwrap();

    let node1 = stretch
        .new_node(
            stretch::style::Style {
                display: stretch::style::Display::Flex,

                flex_grow: 1.0,
                flex_shrink: 1.0,

                ..Default::default()
            },
            vec![node2],
        )
        .unwrap();

    let node = stretch
        .new_node(
            stretch::style::Style {
                size: stretch::geometry::Size {
                    width: stretch::style::Dimension::Points(100.0),
                    height: stretch::style::Dimension::Points(100.0),
                    ..Default::default()
                },
                ..Default::default()
            },
            vec![node0, node1],
        )
        .unwrap();

    stretch
        .compute_layout(node, stretch::geometry::Size::undefined())
        .unwrap();

    println!("{:?}", stretch.layout(node).unwrap());
    println!("{:?}", stretch.layout(node0).unwrap());

    println!("{:?}", stretch.layout(node1).unwrap());
    println!("{:?}", stretch.layout(node2).unwrap());
}
Layout { order: 0, size: Size { width: 100.0, height: 100.0 }, location: Point { x: 0.0, y: 0.0 } }
Layout { order: 0, size: Size { width: 45.0, height: 100.0 }, location: Point { x: 0.0, y: 0.0 } }
Layout { order: 1, size: Size { width: 55.0, height: 100.0 }, location: Point { x: 45.0, y: 0.0 } }
Layout { order: 0, size: Size { width: 10.0, height: 10.0 }, location: Point { x: 0.0, y: 0.0 } }

MeasureFunc does not allow borrowing

I'm new to Rust so this might be a basic problem. I couldn't find an example of MeasureFuncs in stretch.

I'm trying to create a text leaf node. I wrote a font loading mechanism which returns Font, and I wrote a basic text layout algorithm which takes a borrow to Font (lets call this function layout(in_bounds: Size<Number>, f: &Font) -> Size<f32>, where the returned vector is the bounds of the text. It also takes the bounds where we should layout text into). I'd like to be able to call this layout function inside the MeasureFunc, but no matter what I try it says font does not live long enough. borrowed value does not live long enough.

let mut stretch = stretch::node::Stretch::new();
let font = load_font(...);

let text_node = stretch.new_leaf(
  Style { ..Default::default() },
  Box::new(|size|{
    Ok(layout(size, &f))
  })).unwrap();
stretch.compute_layout(text_node, Size{ width: Number::Defined(500), height: Number::Undefined }).unwrap();

I tried boxing and Rc-ing the font with no luck.

let mut stretch = stretch::node::Stretch::new();
let font = load_font(...);

let rc_font = Rc::new(font);

let text_node = stretch.new_leaf(
  Style { ..Default::default() },
  Box::new(|size|{
    Ok(layout(size, rc_font.as_ref()))
  })).unwrap();
stretch.compute_layout(text_node, Size{ width: Number::Defined(500), height: Number::Undefined }).unwrap();

What's the proper way to do this? An example of this in the future would be immensely useful!

Making stretch-layout importable via ES Module CDN

This is possible with Yoga:

<script type="module">
	import yoga from 'https://jspm.dev/yoga-layout'

	// use yoga.Node, etc.
</script>

Need to investigate how they do it. The import * as wasm from './file.wasm' won't work because it is designed for someone to import using a build tool like Webpack.

Looks like they compile the Wasm to JavaScript for this use case: https://unpkg.com/browse/[email protected]/build/Release/nbind.js

Looks like perhaps a combination of https://github.com/charto/nbind and emscripten with asm.js output.

perf

Hey, first of all, let me thank you for this! I am currently using yoga and I'd like to replace it in the future so the stretch might be a great alternative.

Do you have any benchmarks already? I've stitched really qnd integration of stretch into my project and it seems to be a bit slower than yoga (15-25%) so I'm wondering if this is expected or if I'm doing something very wrong.

when invoke stretch_node_add_child i get a crash

i have wrote a objective-c interface refer to exist swift version, but when i add a child to node, i got a crash

thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidNode(Node { instance: Id { id: 1, generation: 0 }, local: Id { id: 0, generation: 0 } })', src/libcore/result.rs:997:5
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: std::panicking::rust_panic_with_hook
   5: std::panicking::continue_panic_fmt
   6: rust_begin_unwind
   7: core::panicking::panic_fmt
   8: core::result::unwrap_failed
   9: stretch_node_add_child

Proposition: Conditionally implement Serialize/Deserialize traits from serde crate for Style relates structs and enums

Hi,

It would be nice to see stretch implement (as a non-default feature) Serialize and Deserialize traits from Serde crate, so a program could load styles from a JSON or YAML file for example or serialize them.
For now, I've implemented this using Serde's "remote" feature, which allows to overcome Rust's orphan rule for implementing external crate's traits for other external create's types. This works but it's not very robust solution (I've had to make a copy of all Style related structs and enums and modify then according to Serde's documentation).
I can prepare a PR for this if there no objection from the authors.

C-API bindings

Cross-Posting because #6 is closed...

Any planned progress on the C-API? I don't have any Rust experience... so no idea how interfacing Rust and C works.

IMO a C-API should be top-priority because not everyone is doing Web stuff...

Index trees

Hi, I'm currently evaluating the library for usage inside a UI framework as replacement for yoga.
compute returns a newly created layout::Node tree which mirrors the input style::Node tree if I understand it correctly. I would be great to have tighter coupling between those two trees. Ideally, being addressable by an index. This should also give room for reusing memory to avoid many small vec allocations. It might be also possible to decouple the layout_cache from the layout::Node and move into implementation details.
Drawback of this would be a slightly more complex API due to the need of an additional tree/graph struct managing the node tree.

In case there is interest for changes in this direction, I would glady take a stab at it!
Cheers, the crate looks great so far!

Please release a new version to crates.io

The released version in crates.io is lagging behind the crate repo. I would like to use the derived implementation of Style such as PartialEq, Clone, Debug, etc. It is already implemented in the repo, but not released to crates.io

[Documentation] Example of the code for Rust does not compile

From https://vislyhq.github.io/stretch/docs/rust/
Example:

use stretch::{style::*, node::{Node, Stretch}, geometry::Size};
 
fn main() {
  let stretch = Stretch::new();
  
  let mut node = stretch.new_node(Style { ..Default::default() }, vec![
    stretch.new_node(Style {
      size: Size { 
          width: Dimension::Points(100.0), 
          height: Dimension::Points(100.0),
      },
      ..Default::default()
    }).unwrap()
  ]).unwrap();
  
  stretch.compute_layout(node, Size::undefined()).unwrap();
  
  // Mutate node
  stretch.set_style(node, Style {
    size: Size { 
        width: Dimension::Points(100.0), 
        height: Dimension::Points(100.0),
    },
    ..Default::default()
  }).unwrap();
 
  // This call will return partially cached results
  stretch.compute_layout(node, Size::undefined()).unwrap();
}

Message I've got:

warning: unused import: `Node`
 --> src/main.rs:1:32
  |
1 | use stretch::{style::*, node::{Node, Stretch}, geometry::Size};
  |                                ^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0061]: this function takes 2 arguments but 1 argument was supplied
  --> src/main.rs:7:13
   |
7  |       stretch.new_node(Style {
   |  _____________^^^^^^^^_-
   | |             |
   | |             expected 2 arguments
8  | |       size: Size { 
9  | |           width: Dimension::Points(100.0), 
10 | |           height: Dimension::Points(100.0),
11 | |       },
12 | |       ..Default::default()
13 | |     }).unwrap()
   | |_____- supplied 1 argument

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0061`.
error: could not compile `hello_stretch`.

To learn more, run the command again with --verbose.

OS: Linux 4.4.0-19041-Microsoft #1-Microsoft Fri Dec 06 14:06:00 PST 2019 x86_64 x86_64 x86_64 GNU/Linux
Rust version: 1.45.0

EDIT. My proposal to fix it #75

Problems with height? Node get wrong height?

use stretch::geometry::Size;
use stretch::style::*;

fn main() {
    let node = Node {
        size: Size { width: Dimension::Points(800.0), height: Dimension::Points(600.0) },
        justify_content: JustifyContent::Center,

        children: vec![Node {
            size: Size { width: Dimension::Percent(0.5), height: Dimension::Percent(0.5) },
            ..Default::default()
        }],

        ..Default::default()
    };

    let layout = stretch::compute(&node, Size::undefined());

    println!("{:#?}", layout);
}

And the result... But doesn't the sub node have to get a height of 300?
I mean, 50 percent of 600 is 300. 😅

Node {
    order: 0,
    size: Size {
        width: 800.0,
        height: 600.0
    },
    location: Point {
        x: 0.0,
        y: 0.0
    },
    children: [
        Node {
            order: 0,
            size: Size {
                width: 400.0,
                height: 400.0
            },
            location: Point {
                x: 200.0,
                y: 0.0
            },
            children: []
        }
    ]
}

License type in Cargo.toml + Apache 2?

See how it looks on crates.io:

image

How cargo-license sees it:
image

This is mostly because your Cargo.toml does not have a license entry, but only license-file.

Also, would it be possible to add optional licensing using Apache-2.0 as most of the ecosystem?

Thanks

How to work with leaf nodes?

Hi,

I've been wondering how to work with leaf nodes. I have following code:

use stretch::Stretch;
use stretch::style::{Style, Dimension};
use stretch::geometry::Size;
use stretch::number::Number;

fn main() {
    let mut stretch = Stretch::new();

    let leaf1 = stretch.new_leaf(Style::default(), Box::new(|_| {
        Ok(Size {
            width: 75.0,
            height: 30.0,
        })
    })).unwrap();

    let node1 = stretch.new_node(Style {
        flex_grow: 1.0,
        ..Default::default()
    }, vec![leaf1]).unwrap();

    let leaf2 = stretch.new_leaf(Style::default(), Box::new(|_| {
        Ok(Size {
            width: 75.0,
            height: 30.0,
        })
    })).unwrap();

    let node2 = stretch.new_node(Style {
        flex_grow: 1.0,
        ..Default::default()
    }, vec![leaf2]).unwrap();

    let root = stretch.new_node(Style {
        size: Size {
            width: Dimension::Percent(1.0),
            height: Dimension::Percent(1.0),
        },
        ..Default::default()
    }, vec![node1, node2]).unwrap();

    stretch.compute_layout(root, Size {
        width: Number::Defined(100.0),
        height: Number::Defined(50.0),
    }).unwrap();

    println!("root:  {:?}", stretch.layout(root).unwrap());
    println!("node1: {:?}", stretch.layout(node1).unwrap());
    println!("leaf1: {:?}", stretch.layout(leaf1).unwrap());
    println!("node2: {:?}", stretch.layout(node2).unwrap());
    println!("leaf2: {:?}", stretch.layout(leaf2).unwrap());
}

which results in following output:

root:  Layout { order: 0, size: Size { width: 100.0, height: 50.0 }, location: Point { x: 0.0, y: 0.0 } }
node1: Layout { order: 0, size: Size { width: 75.0, height: 50.0 }, location: Point { x: 0.0, y: 0.0 } }
leaf1: Layout { order: 0, size: Size { width: 75.0, height: 50.0 }, location: Point { x: 0.0, y: 0.0 } }
node2: Layout { order: 1, size: Size { width: 75.0, height: 50.0 }, location: Point { x: 75.0, y: 0.0 } }
leaf2: Layout { order: 0, size: Size { width: 75.0, height: 50.0 }, location: Point { x: 0.0, y: 0.0 } }

I'm scratching my head here. Why the sum of widths of node1 and node2 are greater than the number passed to compute_layout?
If I understand this correctly, leaf nodes should represent a content inside a flexbox container (which is represented by a normal node). The leaf requests some size, which is returned by the MeasureFunc passed to the leaf.
Why node1 and node2 grow beyond the size of the root? Shouldn't they shrink to 50 points each? Should the leaf nodes shrink too?
Am I misunderstanding something?
Would the overflow property fix this if it were implemented?

I also don't understand the meaning of the Size argument which is passed into the MeasureFunc. It's not explained in the documentation in any way. In above program, if I print the argument of the MeasureFunc I see it's called multiple times (which is understandable - the layout engine probably does multiple passes), but the width and height properties make no sense to me. The width is always Undefined.

[Feature] Support async functions in rust

I am implementing a UI layer in an async framework, and I am unable to pass Stretch around (including using it as a member on the "self" structure) in async code because MeasureFunc is not Send/Sync.

Boxed(sys::Box<dyn Fn(Size<Number>) -> Size<f32>>),

I believe that the change may be as simple as changing the type of Boxed's value to sys::Box<dyn Fn(Size<Number>) -> Size<f32>> + Send + Sync)

This, unfortunately, could be a breaking change since this is a publicly exposed enum, so before jumping in to create a PR, I wanted to see what the opinion would be of attempting this change. I'm new enough to understanding how async works that I am not sure what the likelihood that these functions in most codebase wouldn't already be Send + Sync.

New API allowing for efficient re-rendering of trees

I'm currently in the early stages of designing a new API with the goal of making it easier to implement efficient re-rendering of partially updated trees. The idea is to go from todays declarative and immutable API to a more imperative and mutable API to make this possible.

Today's API is very easy to work with and makes it especially easy to declare larger hierarchies as a single structure. This apposed to creating a node and calling methods on it to set various properties.

let style = style::Node {
  flex_direction: style::FlexDirection::Column,
  children: vec![
    style::Node { ..Default::default() },
  ],
  ..Default::default()
};

let layout = compute(style, Size::undefined());

However this makes it very difficult to implement efficient re-rendering of partially updated trees. Say for example you make the above style a mutable variable, this way you would be able to update its flex_direction however because this is just a simple struct field you would also manually have to make the node as dirty. And because children is just a regular Vec field the children get no pointer to their parent when inserted into a node which makes it impossible to automatically traverse the tree to its root when marking nodes as dirty.

An API which solves the above problems is one which makes use of methods instead of public struct fields.

let mut style = style::Node::new();
style.set_flex_direction(style::FlexDirection::Column);
style.add_child(style::Node::new());

let layout = compute(style, Size::undefined());

With this API we can automatically mark a node as dirty when a setter is called on it, we can also correctly propagate this dirty flag up the tree by saving a parent pointer when adding a child node. The downside of this API is that there is no way to declaratively create static trees. This is mostly an issue for tests which will become much harder to read. For this I propose we add a macro style::node!, much like how vec! hides the underlying mutable API of Vec.

let style = style::node! {
  flex_direction: style::FlexDirection::Column,
  children: style::node! {},
};

Javascript example doesn't work

I was excited to try the JS API, so I tried following https://vislyhq.github.io/stretch/docs/javascript. Unfortunately it doesn't work on Node v8, v10, or v12 (the current LTS versions of Node).

On 8 and 10:

(function (exports, require, module, __filename, __dirname) { import { Node } from 'stretch-layout';
                                                                     ^

SyntaxError: Unexpected token {
    at new Script (vm.js:80:7)
    at createScript (vm.js:274:10)
    at Object.runInThisContext (vm.js:326:10)
    at Module._compile (internal/modules/cjs/loader.js:664:28)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)
    at startup (internal/bootstrap/node.js:283:19)

On 12:

import { Node } from 'stretch-layout';
       ^

SyntaxError: Unexpected token {
    at Module._compile (internal/modules/cjs/loader.js:703:23)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:770:10)
    at Module.load (internal/modules/cjs/loader.js:628:32)
    at Function.Module._load (internal/modules/cjs/loader.js:555:12)
    at Function.Module.runMain (internal/modules/cjs/loader.js:824:10)
    at internal/main/run_main_module.js:17:11

It also doesn't look like the package is following the modules API in the Node documentation (.mjs and type: "module")

Lack of support to provide baseline

Though there is a means to set baseline as alignment value for alignItems and alignSelf however it is not clear how the baseline of a node is calculated.

Unwind safety issues for FFI functions

#[no_mangle]
pub unsafe extern "C" fn stretch_node_compute_layout(
    stretch: *mut c_void,
    node: *mut c_void,
    width: f32,
    height: f32,
    create_layout: fn(*const f32) -> *mut c_void,
) -> *mut c_void {
    let mut stretch = Box::from_raw(stretch as *mut Stretch);
    let node = Box::from_raw(node as *mut Node);

    stretch
        .compute_layout(
            *node,
            Size {
                width: if f32::is_nan(width) { Number::Undefined } else { Number::Defined(width) },
                height: if f32::is_nan(height) { Number::Undefined } else { Number::Defined(height) },
            },
        )
        .unwrap();

    let mut output = vec![];
    copy_output(&stretch, *node, &mut output);

    Box::leak(stretch);
    Box::leak(node);

    create_layout(output.as_ptr())
}

The unwinding of stretch.compute_layout will introduce double free due to the deallocation of stretch and node.

#[no_mangle]
pub unsafe extern "C" fn stretch_node_compute_layout(
    stretch: *mut c_void,
    node: *mut c_void,
    width: f32,
    height: f32,
    create_layout: fn(*const f32) -> *mut c_void,
) -> *mut c_void {
    let mut stretch = mem::ManuallyDrop::new(Box::from_raw(stretch as *mut Stretch));
    let node = mem::ManuallyDrop::new(Box::from_raw(node as *mut Node));

    stretch
        .compute_layout(
            *node,
            Size {
                width: if f32::is_nan(width) { Number::Undefined } else { Number::Defined(width) },
                height: if f32::is_nan(height) { Number::Undefined } else { Number::Defined(height) },
            },
        )
        .unwrap();

    let mut output = vec![];
    copy_output(&stretch, *node, &mut output);
    create_layout(output.as_ptr())
}

The modification is zero-cost.

(Javascript) stretch dimension of flex children is zero

I can't seem to get basic flex layout working in Javascript, not sure if I'm missing something.
I would expect the child items in the following to be assigned a height matching the full height of the parent node (per align-items: stretch). In fact they are assigned height 0

import('stretch-layout').then((stretch) => {
  
  const { Allocator, Node, Display, AlignItems } = stretch;
  const allocator = new Allocator();
 
  const rootNode = new Node(allocator, {width: 600, height: 300, display: Display.Flex, alignItems: AlignItems.Stretch});

  const childNodes = [
    new Node(allocator, {flexShrink:1, flexGrow:1, flexBasis: 'auto'}),
    new Node(allocator, {flexShrink:1, flexGrow:1, flexBasis: 'auto'})
  ]
  rootNode.addChild(childNodes[0]);
  rootNode.addChild(childNodes[1]);

  const computedLayout = rootNode.computeLayout();
 
  logLayout(computedLayout)

})

function logLayout(computedLayout){
  const {x, y, width, height, childCount} = computedLayout;
  console.log(`x:${x}, y:${y}, width:${width}, height:${height}`);

  for (let i=0;i<childCount; i++) {
      logLayout(computedLayout.child(i));
  };

};

logs the following:

x:0, y:0, width:600, height:300
x:0, y:0, width:300, height:0
x:300, y:0, width:300, height:0

make dependencies optional

I'm not sure what's the idea behind feature flags (!alloc && !std) or if it's actually supposed to work (cargo build --features=alloc fails) but none of the dependencies is actually required for default (std) build so I think it would be good to mark them optional.

I did that in my fork and I can do PR (or you can just copy paste it) but I guess I've missed something because I'm not used to this project. I need to compile this on raspi so that's why I'm worried and did the fork.

Deep nesting results in poor performance

I'm using stretch to lay out a tree-like structure. With 63 nodes and a maximum depth of 16, compute_layout takes about 5 seconds to complete. Profiling illustrates that compute time increases exponentially with depth, and that compute_internal is called over 50k times for my structure of 63 nodes.

Is this sort of performance expected? Is nesting this deeply just an entirely unexpected thing to be doing with stretch? Are there constraints I could be putting in place to elide some of this recursion?

Bindings for C++

Hello there,

I would greatly enjoy using stretch in my C++ project. Sadly there aren't any bindings for that so it would be nice if they existed for stretch.

That's all it, I really hope that they will be made sometimes soon :D

Project status?

Almost a year since any development on stretch it seems? Is the project still going to see active development?

Possible divergence from Chrome?

Thanks for Stretch!

Here's a very basic flexbox layout with five divs, where the yellow top-row div is 24px high in Chrome.

Below is (supposed to be) a purely mechanical translation of that into Stretch, which outputs the size of that top-row node:

size: Size {
    width: 20000.0,
    height: 0.0,
},

So the height from Stretch is 0px, not 24px (and the width a bit squiffy too).

It seems Stretch thinks that the 100% high other_node should win almost all the available 73.0 points of height. Adding a flex_shrink: 0.0 to top_row, or specifying the height of other_node in points rather than 100%, both work around the issue.

Any ideas as to the divergence welcome - apologies if this is just user error somehow.

use std::default::Default;
use stretch::{geometry::*, node::Stretch, style::*, Error};

fn main() -> Result<(), Error> {
    let mut stretch = Stretch::new();

    // top-row (yellow)
    let top_row = stretch.new_node(
        Style {
            size: Size {
                width: Dimension::Percent(100.0),
                height: Dimension::Points(24.0),
            },
            // one way to fix the issue:
            // flex_shrink: 0.0,
            ..Default::default()
        },
        vec![],
    )?;

    // lower-row (cyan)
    let lower_row_1 = stretch.new_node(
        Style {
            size: Size {
                width: Dimension::Percent(100.0),
                height: Dimension::Points(20.0),
            },
            ..Default::default()
        },
        vec![],
    )?;
    let lower_row_2 = lower_row_1.clone();

    // other-rows (red)
    let other_rows = stretch.new_node(
        Style {
            flex_direction: FlexDirection::Column,
            justify_content: JustifyContent::Center,
            size: Size {
                width: Dimension::Percent(100.0),
                height: Dimension::Percent(100.0),
            },
            margin: Rect {
                top: Dimension::Points(5.0),
                ..Default::default()
            },
            ..Default::default()
        },
        vec![lower_row_1, lower_row_2],
    )?;

    // outer (blue)
    let outer = stretch.new_node(
        Style {
            size: Size {
                width: Dimension::Points(200.0),
                height: Dimension::Points(73.0),
            },
            flex_direction: FlexDirection::Column,
            padding: Rect {
                top: Dimension::Points(5.0),
                ..Default::default()
            },
            ..Default::default()
        },
        vec![top_row, other_rows],
    )?;
    stretch.compute_layout(outer, Size::undefined())?;
    dbg!(stretch.layout(top_row)?);
    Ok(())
}

[Question] Why wasn't javascript package updated for such a long time?

Hey guys!

Stumbled upon this library while looking for wasm port's of yoga. I think this implementation has nicer api and it's good to know it is backed by real product needs. I saw few issues here related to javascript that seem to have merged solutions, however latest release [email protected] that was tagged on July 5, 2019 does not include them.

I see there is activity in the repo, so wanted to ask if / why there was no update to js package? Is it still considered a well supported target and would you say it's a good idea to start using it for a personal project (layout engine for canvas)?

EDIT:
Just noticed that all packages are at the same version, so I assume there wasn't an update to all of them in a while?

Possible bug in `Stretch::remove` or `Stretch::replace_child_at_index`

I have a use case where I need to replace specific child nodes on an external request. These child nodes act as placeholders for dynamic content. To replace them, I'm trying to use Stretch::replace_child_at_index. I also need to Stretch::remove the old child node, which is being replaced. This combination results in a panic inside the Stretch::replace_child_at_index on a second replacement.

Here's a short example:

use stretch::Stretch;
use stretch::style::{Style};

fn main() {
    let mut stretch = Stretch::new();

    let child1 = stretch.new_node(Style::default(), vec![]).unwrap();
    let child2 = stretch.new_node(Style::default(), vec![]).unwrap();
    let node = stretch.new_node(Style::default(), vec![child1, child2]).unwrap();

    loop {
        println!("replacing child1");
        let new_child = stretch.new_node(Style::default(), vec![]).unwrap();
        let old_node = stretch.replace_child_at_index(node, 0, new_child).unwrap();
        stretch.remove(old_node);
        println!("done");
    }
}

Here's an output with a backtrace:

replacing child1
done
replacing child1
thread 'main' panicked at 'no entry found for key', src/libcore/option.rs:1034:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.29/src/backtrace/libunwind.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.29/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:47
   3: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:36
   4: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:200
   5: std::panicking::default_hook
             at src/libstd/panicking.rs:214
   6: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:477
   7: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:384
   8: rust_begin_unwind
             at src/libstd/panicking.rs:311
   9: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
  10: core::option::expect_failed
             at src/libcore/option.rs:1034
  11: <std::collections::hash::map::HashMap<K,V,S> as core::ops::index::Index<&Q>>::index
  12: stretch::node::Stretch::replace_child_at_index
  13: serde_tests::main
  14: std::rt::lang_start::{{closure}}
  15: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:49
  16: std::panicking::try::do_call
             at src/libstd/panicking.rs:296
  17: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:82
  18: std::panicking::try
             at src/libstd/panicking.rs:275
  19: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  20: std::rt::lang_start_internal
             at src/libstd/rt.rs:48
  21: main
  22: __libc_start_main
  23: _start

The panic is cause by the return line in Stretch::replace_child_at_index:

        Ok(self.ids_to_nodes[&old_child])

If I remove the stretch.remove(old_node); line, the program doesn't panic, but it doesn't release the memory for the old nodes and the program grows very quickly in memory. This is of course a very extreme example. In my use case, the replacement would occur about every 30 seconds, so it wouldn't grow that fast, but still this is quite a big issue.

[JS][notice me senpai ◕‿◕] child node position not being set

Hello!
I "ported" rust test -- https://github.com/vislyhq/stretch/blob/master/tests/generated/absolute_layout_align_items_center_on_child_only.rs to JS (I believe, I made it correctly)

import { Allocator, Node, AlignSelf, PositionType } from 'stretch-layout';

const allocator = new Allocator();

const node = new Node(allocator, {
    width: 110,
    height: 100,
});

const childNode = new Node(allocator, {
    width: 60,
    height: 40,
    alignSelf: AlignSelf.Center,
    positionType: PositionType.Absolute
});

node.addChild(childNode);

const layout = node.computeLayout(), childLayout = childNode.computeLayout();

console.log(layout.width, layout.height, layout.x, layout.y);
console.log(childLayout.width, childLayout.height, childLayout.x, childLayout.y);

In browser (both FF & Chrome) console I get:

110 100 0 0
60 40 0 0

According to assert_eq!(stretch.layout(node0).unwrap().location.y, 30f32); -- childNode y should be 30 px. I also tried other tests, but no luck either.

Adding depth to the layout structure.

This might be of limited general use, but what do you think about adding a depth field to the Layout struct. It's orthogonal (literally) to the order field, it indicates how deep the particular element is in the tree.

Is there something like an ID?

Is there something like an ID?
Or is there a better way to provide information for other objects? To bind the result to a UI widget.

let node = Node {
        id: 10,
        size: Size { width: Dimension::Points(100.0), height: Dimension::Points(100.0) },
        justify_content: JustifyContent::Center,

        children: vec![Node {
            id: 11,
            size: Size { width: Dimension::Percent(0.5), height: Dimension::Auto },
            ..Default::default()
        }],

        ..Default::default()
    };
Node {
    id: 10,
    order: 0,
    size: Size {
        width: 100.0,
        height: 100.0
    },
    location: Point {
        x: 0.0,
        y: 0.0
    },
    children: [
        Node {
            id: 11,
            order: 0,
            size: Size {
                width: 50.0,
                height: 100.0
            },
            location: Point {
                x: 25.0,
                y: 0.0
            },
            children: []
        }
    ]
}

Question: How it combined with iOS and UIKit?

I decided to try stretch in demo project and I get frustrated.
How should I work with UIView/UILabels and etc.

for example (layout code from docs with small tweaks):

class ViewController: UIViewController {
  let firstView: UIView = UIView()
  let secondView: UIView = UIView()
  
  override func loadView() {
    view = UIView()
    
    view.addSubview(firstView)
    view.addSubview(secondView)
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .systemTeal
    firstView.backgroundColor = .systemPink
    secondView.backgroundColor = .magenta
  }
  
  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let node = Node(
      style: Style(justifyContent: .center,
                   size: Size(width: .points(375.0), height: .points(100.0))),
      children: [
        Node(style: Style(size: Size(width: .percent(0.5), height: .percent(0.5))), children: []),
        Node(style: Style(size: Size(width: .percent(0.5), height: .percent(0.5))), children: [])
      ])
      
    let layout = node.computeLayout(thatFits: Size(width: nil, height: nil))

    print("width: \(layout.width), height: \(layout.height)")
  }
}

questions:

  • is there convenient wrapper/bridge to combine layouts and uikit? or i should write something like UIVIew+Stretch extensions which would convert measured layout to UIVIew frame size
  • which lifecycle method preferable for stretch layout computation?

ps: stretch looks very promising and it need better documentation with examples

[Feature] Support CSS Grid

Opening this as a tracking issue.

I understand there is an intent for stretch to support CSS Grid in future, so for anyone interested in knowing when/if that happens, they can watch this issue for updates and/or show interest/support via 👍 reaction(to this issue, no +1 type comments thanks!).

While slightly biased due to React-Native using yoga for layout, there is strong interest in seeing CSS Grid support with little indication from yoga devs that the feature is on the horizon.

Add no_std support

Would be great if stretch would have #![no_std] support to make it work on as many platforms as possible!

Luckily all std dependencies (except for Vec) are actually from core, so this should be doable.

If you want I can prepare a PR that adds conditional no_std support via a feature flag. Sadly for now the no_std variant will only work on nightly for now, as the alloc crate (required for Vec) is unstable for now.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.