Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

WIP Wasm Yoga port #37

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"index.js": {
"bundled": 14895,
"minified": 7820,
"gzipped": 2675,
"bundled": 16595,
"minified": 8974,
"gzipped": 2897,
"treeshaked": {
"rollup": {
"code": 126,
"import_statements": 126
"code": 151,
"import_statements": 151
},
"webpack": {
"code": 1242
"code": 1300
}
}
},
"index.cjs.js": {
"bundled": 21462,
"minified": 10644,
"gzipped": 3409
"bundled": 24156,
"minified": 12326,
"gzipped": 3877
}
}
31 changes: 22 additions & 9 deletions examples/config-overrides.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
const { override, addWebpackAlias, addWebpackPlugin } = require('customize-cra')
const { override, addWebpackAlias, addWebpackPlugin, removeModuleScopePlugin, babelInclude } = require('customize-cra')
const { addReactRefresh } = require('customize-cra-react-refresh')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const path = require('path')

module.exports = override(
addReactRefresh(),
addWebpackAlias({
three$: path.resolve('./src/utils/three.js'),
'../../../build/three.module.js': path.resolve('./src/utils/three.js'),
}),
//addWebpackPlugin(new BundleAnalyzerPlugin())
)
module.exports = (config, env) => {
config.resolve.extensions = [...config.resolve.extensions, '.ts', '.tsx']
return override(
addReactRefresh(),
removeModuleScopePlugin(),
babelInclude([path.resolve('src'), path.resolve('../src')]),
addWebpackAlias({
'react-three-flex': path.resolve('../src/index'),
postprocessing: path.resolve('node_modules/postprocessing'),
react: path.resolve('node_modules/react'),
'react-dom': path.resolve('node_modules/react-dom'),
scheduler: path.resolve('node_modules/scheduler'),
'react-scheduler': path.resolve('node_modules/react-scheduler'),
'react-three-fiber': path.resolve('node_modules/react-three-fiber'),
drei: path.resolve('node_modules/drei'),
three$: path.resolve('./src/utils/three.js'),
'../../../build/three.module.js': path.resolve('./src/utils/three.js'),
}),
//addWebpackPlugin(new BundleAnalyzerPlugin())
)(config, env)
}
1 change: 0 additions & 1 deletion examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"react-postprocessing": "1.2.0",
"react-scripts": "3.4.3",
"react-three-fiber": "5.0.0-beta.12",
"react-three-flex": "0.5.2",
"three": "0.120.1",
"troika-three-text": "https://github.com/jmatsushita/troika-three-text",
"zustand": "3.1.0"
Expand Down
Binary file added examples/public/yoga.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion examples/src/App.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as THREE from 'three'
import React, { Suspense, useEffect, useRef, useState, useCallback, useLayoutEffect } from 'react'
import { Canvas, useThree, useFrame, useLoader } from 'react-three-fiber'
import { Flex, Box, useFlexSize } from 'react-three-flex'
import { Flex, Box, useFlexSize } from '../../src/index'
import { useAspect } from 'drei/misc/useAspect'
import { Line } from 'drei/abstractions/Line'
import { Loader } from 'drei/prototyping/Loader'
Expand Down
2 changes: 1 addition & 1 deletion examples/src/components/Text.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { useReflow } from 'react-three-flex'
import { useReflow } from '../../../src/index'
import { Text as TextImpl } from 'drei/abstractions/Text'

export default function Text({ bold = false, anchorX = 'left', anchorY = 'top', textAlign = 'left', ...props }) {
Expand Down
19 changes: 0 additions & 19 deletions examples/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1637,11 +1637,6 @@
dependencies:
"@types/yargs-parser" "*"

"@types/[email protected]":
version "1.9.2"
resolved "https://registry.yarnpkg.com/@types/yoga-layout/-/yoga-layout-1.9.2.tgz#efaf9e991a7390dc081a0b679185979a83a9639a"
integrity sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==

"@typescript-eslint/eslint-plugin@^2.10.0":
version "2.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9"
Expand Down Expand Up @@ -8830,13 +8825,6 @@ [email protected]:
tiny-emitter "^2.1.0"
utility-types "^3.10.0"

[email protected]:
version "0.5.2"
resolved "https://registry.yarnpkg.com/react-three-flex/-/react-three-flex-0.5.2.tgz#d6670f4e3f1b6f332d654010bd37f49298912e11"
integrity sha512-XciDjhZBnd1s/1LQtawg3vLBEh/Wp2yRzTKjq9tnpw9x2oxqRa2yVpn7LCxR1DPu2s/pKFUuT/TGtGqB3wCJoA==
dependencies:
yoga-layout-prebuilt "^1.9.6"

react-use-measure@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.0.1.tgz#4f23f94c832cd4512da55acb300d1915dcbf3ae8"
Expand Down Expand Up @@ -11087,13 +11075,6 @@ yargs@^13.3.0, yargs@^13.3.2:
y18n "^4.0.0"
yargs-parser "^13.1.2"

yoga-layout-prebuilt@^1.9.6:
version "1.9.6"
resolved "https://registry.yarnpkg.com/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.9.6.tgz#98dde95bbf8e6e12835876e9305f1e995c4bb801"
integrity sha512-Wursw6uqLXLMjBAO4SEShuzj8+EJXhCF71/rJ7YndHTkRAYSU0GY3OghRqfAk9HPUAAFMuqp3U1Wl+01vmGRQQ==
dependencies:
"@types/yoga-layout" "1.9.2"

[email protected], zustand@^3.0.3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.1.0.tgz#8b8ca50c53c9ee44a197a71930274f97d64365b6"
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
"test": "echo no tests yet"
},
"dependencies": {
"yoga-layout-prebuilt": "^1.9.6"
"react-promise-suspense": "^0.3.3",
"yoga-wasm-slim": "^0.0.6"
},
"devDependencies": {
"@babel/core": "7.11.0",
Expand Down
17 changes: 9 additions & 8 deletions src/Box.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { useLayoutEffect, useRef, useMemo, useState } from 'react'
import * as THREE from 'three'
import Yoga from 'yoga-layout-prebuilt'
import { ReactThreeFiber, useFrame } from 'react-three-fiber'

import { setYogaProperties, rmUndefFromObj } from './util'
import { setYogaProperties, rmUndefFromObj, useYogaAsync } from './util'
import { boxContext, flexContext } from './context'
import { R3FlexProps } from './props'
import { useReflow, useContext } from './hooks'
Expand Down Expand Up @@ -182,26 +181,28 @@ export function Box({
wrap,
])

const yoga = useYogaAsync('./')

const { registerBox, unregisterBox, scaleFactor } = useContext(flexContext)
const { node: parent } = useContext(boxContext)
const group = useRef<THREE.Group>()
const node = useMemo(() => Yoga.Node.create(), [])
const node = useMemo(() => yoga._YGNodeNew(), [])
const reflow = useReflow()

useLayoutEffect(() => {
setYogaProperties(node, flexProps, scaleFactor)
setYogaProperties(yoga, node, flexProps, scaleFactor)
}, [flexProps, node, scaleFactor])

// Make child known to the parents yoga instance *before* it calculates layout
useLayoutEffect(() => {
if (!group.current || !parent) return

parent.insertChild(node, parent.getChildCount())
yoga._YGNodeInsertChild(parent, node, yoga._YGNodeGetChildCount(parent))
registerBox(node, group.current, flexProps, centerAnchor)

// Remove child on unmount
return () => {
parent.removeChild(node)
yoga._YGNodeRemoveChild(parent, node)
unregisterBox(node)
}
}, [node, parent, flexProps, centerAnchor, registerBox, unregisterBox])
Expand All @@ -215,10 +216,10 @@ export function Box({
const epsilon = 1 / scaleFactor
useFrame(() => {
const width =
(typeof flexProps.width === 'number' ? flexProps.width : null) || node.getComputedWidth().valueOf() / scaleFactor
(typeof flexProps.width === 'number' ? flexProps.width : null) || yoga._YGNodeLayoutGetWidth(node) / scaleFactor
const height =
(typeof flexProps.height === 'number' ? flexProps.height : null) ||
node.getComputedHeight().valueOf() / scaleFactor
yoga._YGNodeLayoutGetHeight(node) / scaleFactor

if (Math.abs(width - size[0]) > epsilon || Math.abs(height - size[1]) > epsilon) {
setSize([width, height])
Expand Down
51 changes: 31 additions & 20 deletions src/Flex.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import React, { useLayoutEffect, useMemo, useCallback, PropsWithChildren, useRef } from 'react'
import Yoga, { YogaNode } from 'yoga-layout-prebuilt'
import { YGNodeRef, CONSTANTS } from 'yoga-wasm-slim'
import { Vector3, Group, Box3 } from 'three'
import { useFrame, useThree, ReactThreeFiber } from 'react-three-fiber'

import { setYogaProperties, rmUndefFromObj, vectorFromObject, Axis, getDepthAxis, getFlex2DSize } from './util'
import {
setYogaProperties,
rmUndefFromObj,
vectorFromObject,
Axis,
getDepthAxis,
getFlex2DSize,
useYogaAsync,
setPropertyString,
} from './util'
import { boxContext, flexContext, SharedFlexContext, SharedBoxContext } from './context'
import type { R3FlexProps, FlexYogaDirection, FlexPlane } from './props'

Expand Down Expand Up @@ -196,9 +205,9 @@ export function Flex({
])

// Keeps track of the yoga nodes of the children and the related wrapper groups
const boxesRef = useRef<{ node: YogaNode; group: Group; flexProps: R3FlexProps; centerAnchor: boolean }[]>([])
const boxesRef = useRef<{ node: YGNodeRef; group: Group; flexProps: R3FlexProps; centerAnchor: boolean }[]>([])
const registerBox = useCallback(
(node: YogaNode, group: Group, flexProps: R3FlexProps, centerAnchor: boolean = false) => {
(node: YGNodeRef, group: Group, flexProps: R3FlexProps, centerAnchor: boolean = false) => {
const i = boxesRef.current.findIndex((b) => b.node === node)
if (i !== -1) {
boxesRef.current.splice(i, 1)
Expand All @@ -207,17 +216,19 @@ export function Flex({
},
[]
)
const unregisterBox = useCallback((node: YogaNode) => {
const unregisterBox = useCallback((node: YGNodeRef) => {
const i = boxesRef.current.findIndex((b) => b.node === node)
if (i !== -1) {
boxesRef.current.splice(i, 1)
}
}, [])

const yoga = useYogaAsync('./')

// Reference to the yoga native node
const node = useMemo(() => Yoga.Node.create(), [])
const node = useMemo(() => yoga._YGNodeNew(), [])
useLayoutEffect(() => {
setYogaProperties(node, flexProps, scaleFactor)
setYogaProperties(yoga, node, flexProps, scaleFactor)
}, [node, flexProps, scaleFactor])

// Mechanism for invalidating and recalculating layout
Expand All @@ -241,7 +252,11 @@ export function Flex({
const depthAxis = getDepthAxis(plane)
const [flexWidth, flexHeight] = getFlex2DSize(size, plane)
const yogaDirection_ =
yogaDirection === 'ltr' ? Yoga.DIRECTION_LTR : yogaDirection === 'rtl' ? Yoga.DIRECTION_RTL : yogaDirection
yogaDirection === 'ltr'
? CONSTANTS.DIRECTION_LTR
: yogaDirection === 'rtl'
? CONSTANTS.DIRECTION_RTL
: yogaDirection

// Shared context for flex and box
const sharedFlexContext = useMemo<SharedFlexContext>(
Expand All @@ -263,23 +278,16 @@ export function Flex({
function reflow() {
// Recalc all the sizes
boxesRef.current.forEach(({ group, node, flexProps }) => {
const scaledWidth = typeof flexProps.width === 'number' ? flexProps.width * scaleFactor : flexProps.width
const scaledHeight = typeof flexProps.height === 'number' ? flexProps.height * scaleFactor : flexProps.height

if (scaledWidth !== undefined && scaledHeight !== undefined) {
// Forced size, no need to calculate bounding box
node.setWidth(scaledWidth)
node.setHeight(scaledHeight)
} else {
if (flexProps.width === undefined || flexProps.height === undefined) {
// No size specified, calculate bounding box
boundingBox.setFromObject(group).getSize(vec)
node.setWidth(scaledWidth || vec[mainAxis] * scaleFactor)
node.setHeight(scaledHeight || vec[crossAxis] * scaleFactor)
setPropertyString(yoga, node, 'Width', flexProps.width || vec[mainAxis], scaleFactor)
setPropertyString(yoga, node, 'Height', flexProps.height || vec[crossAxis], scaleFactor)
}
})

// Perform yoga layout calculation
node.calculateLayout(flexWidth * scaleFactor, flexHeight * scaleFactor, yogaDirection_)
yoga._YGNodeCalculateLayout(node, flexWidth * scaleFactor, flexHeight * scaleFactor, yogaDirection_)

let minX = 0
let maxX = 0
Expand All @@ -288,7 +296,10 @@ export function Flex({

// Reposition after recalculation
boxesRef.current.forEach(({ group, node, centerAnchor }) => {
const { left, top, width, height } = node.getComputedLayout()
const left = yoga._YGNodeLayoutGetLeft(node)
const top = yoga._YGNodeLayoutGetTop(node)
const width = yoga._YGNodeLayoutGetWidth(node)
const height = yoga._YGNodeLayoutGetHeight(node)
const position = vectorFromObject({
[mainAxis]: (left + (centerAnchor ? width / 2 : 0)) / scaleFactor,
[crossAxis]: -(top + (centerAnchor ? height / 2 : 0)) / scaleFactor,
Expand Down
8 changes: 4 additions & 4 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { createContext } from 'react'
import { YogaNode } from 'yoga-layout-prebuilt'
import { YGNodeRef } from 'yoga-wasm-slim'
import { Group } from 'three'
import { R3FlexProps } from './props'

export interface SharedFlexContext {
scaleFactor: number
requestReflow(): void
registerBox(node: YogaNode, group: Group, flexProps: R3FlexProps, centerAnchor?: boolean): void
unregisterBox(node: YogaNode): void
registerBox(node: YGNodeRef, group: Group, flexProps: R3FlexProps, centerAnchor?: boolean): void
unregisterBox(node: YGNodeRef): void
}

const initialSharedFlexContext: SharedFlexContext = {
Expand All @@ -26,7 +26,7 @@ const initialSharedFlexContext: SharedFlexContext = {
export const flexContext = createContext<SharedFlexContext>(initialSharedFlexContext)

export interface SharedBoxContext {
node: YogaNode | null
node: YGNodeRef | null
size: [number, number]
}

Expand Down
18 changes: 4 additions & 14 deletions src/props.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import { YogaFlexDirection, YogaAlign, YogaJustifyContent, YogaFlexWrap, YogaDirection } from 'yoga-layout-prebuilt'

export type FlexYogaDirection = YogaDirection | 'ltr' | 'rtl'
export type FlexYogaDirection = 'ltr' | 'rtl'
export type FlexPlane = 'xy' | 'yz' | 'xz'

export type Value = string | number

export type FlexDirection = YogaFlexDirection | 'row' | 'column' | 'row-reverse' | 'column-reverse'
export type FlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse'

export type JustifyContent =
| YogaJustifyContent
| 'center'
| 'flex-end'
| 'flex-start'
| 'space-between'
| 'space-evenly'
| 'space-around'
export type JustifyContent = 'center' | 'flex-end' | 'flex-start' | 'space-between' | 'space-evenly' | 'space-around'

export type Align =
| YogaAlign
| 'auto'
| 'baseline'
| 'center'
Expand All @@ -27,7 +17,7 @@ export type Align =
| 'space-between'
| 'stretch'

export type FlexWrap = YogaFlexWrap | 'no-wrap' | 'wrap' | 'wrap-reverse'
export type FlexWrap = 'no-wrap' | 'wrap' | 'wrap-reverse'

export type R3FlexProps = Partial<{
// Align
Expand Down
Loading