Skip to content

Commit

Permalink
add: Scene parcels shortcut (#767)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoecheza authored Oct 6, 2023
1 parent 90f5538 commit 9b97e80
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.Container.Scene .content svg {
cursor: pointer;
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
import { useCallback, useState } from 'react'
import { RxBorderAll } from 'react-icons/rx'

import { useComponentInput } from '../../../hooks/sdk/useComponentInput'
import { useHasComponent } from '../../../hooks/sdk/useHasComponent'
import { withSdk } from '../../../hoc/withSdk'
import { Block } from '../../Block'
import { Container } from '../../Container'
import { TextField } from '../TextField'
import { Props } from './types'
import { fromScene, toScene, isValidInput } from './utils'
import { fromScene, toScene, toSceneAuto, getInputValidation } from './utils'

import './SceneInspector.css'

export default withSdk<Props>(({ sdk, entity }) => {
const [auto, setAuto] = useState(false)
const { Scene } = sdk.components

const hasScene = useHasComponent(entity, Scene)
const { getInputProps } = useComponentInput(entity, Scene, fromScene, toScene, isValidInput)
const { getInputProps } = useComponentInput(
entity,
Scene,
fromScene,
auto ? toSceneAuto : toScene,
getInputValidation(auto)
)
const parcelsProps = getInputProps('layout.parcels')

const handleClick = useCallback(() => {
setAuto(!auto)
}, [auto])

if (!hasScene) {
return null
Expand All @@ -20,7 +37,8 @@ export default withSdk<Props>(({ sdk, entity }) => {
return (
<Container label="Settings" className="Scene">
<Block label="Parcels">
<TextField label="" {...getInputProps('layout.parcels')} />
<TextField {...parcelsProps} />
<RxBorderAll onClick={handleClick} style={{ opacity: auto ? 1 : 0.3 }} />
</Block>
</Container>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { EditorComponentsTypes } from '../../../lib/sdk/components'
import { Coords } from '../../../lib/utils/layout'
import { SceneInput } from './types'
import { fromScene, getCoordinatesBetweenPoints, getInputValidation, parseParcels, toScene, toSceneAuto } from './utils'

describe('SceneInspector/utils', () => {
describe('fromScene', () => {
it('should convert a Scene to a SceneInput', () => {
const scene: EditorComponentsTypes['Scene'] = {
layout: {
base: { x: 1, y: 1 },
parcels: [
{ x: 1, y: 1 },
{ x: 2, y: 2 }
]
}
}

const result = fromScene(scene)

expect(result).toEqual({
layout: {
parcels: '1,1 2,2'
}
})
})
})

describe('toScene', () => {
it('should convert a SceneInput to a Scene', () => {
const input: SceneInput = {
layout: {
parcels: '1,1 2,2'
}
}

const result = toScene(input)

expect(result).toEqual({
layout: {
base: { x: 1, y: 1 },
parcels: [
{ x: 1, y: 1 },
{ x: 2, y: 2 }
]
}
})
})
})

describe('toSceneAuto', () => {
it('should convert a SceneInput to a Scene with auto-generated base and parcels', () => {
const input: SceneInput = {
layout: {
parcels: '1,1 2,2'
}
}

const result = toSceneAuto(input)

expect(result).toEqual({
layout: {
base: { x: 1, y: 1 },
parcels: [
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 2, y: 1 },
{ x: 2, y: 2 }
]
}
})
})
})

describe('parseParcels', () => {
it('should parse a string of parcels into Coords array', () => {
const input = '1,1 2,2 3,3'

const result = parseParcels(input)

expect(result).toEqual([
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 }
])
})

it('should return an empty array for invalid input', () => {
const input = '1,1 2,2 invalid 3,3'

const result = parseParcels(input)

expect(result).toEqual([])
})
})

describe('getInputValidation', () => {
it('should return a validation function that checks for connected parcels', () => {
const inputValidation = getInputValidation(false)
const validInput: SceneInput = {
layout: {
parcels: '1,1 1,2'
}
}
const invalidInput: SceneInput = {
layout: {
parcels: '1,1 2,2'
}
}

const isValidValidInput = inputValidation(validInput)
const isValidInvalidInput = inputValidation(invalidInput)

expect(isValidValidInput).toBe(true)
expect(isValidInvalidInput).toBe(false)
})

it('should return a validation function that checks for auto-generated parcels', () => {
const inputValidation = getInputValidation(true)
const validInput: SceneInput = {
layout: {
parcels: '1,1 2,2'
}
}
const invalidInput: SceneInput = {
layout: {
parcels: '1,1 1,2 1,3'
}
}

const isValidValidInput = inputValidation(validInput)
const isValidInvalidInput = inputValidation(invalidInput)

expect(isValidValidInput).toBe(true)
expect(isValidInvalidInput).toBe(false)
})
})

describe('getCoordinatesBetweenPoints', () => {
it('should return an array of coordinates between two points', () => {
const pointA: Coords = { x: 0, y: 0 }
const pointB: Coords = { x: 2, y: 2 }

const result = getCoordinatesBetweenPoints(pointA, pointB)

expect(result).toEqual([
{ x: 0, y: 0 },
{ x: 0, y: 1 },
{ x: 0, y: 2 },
{ x: 1, y: 0 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 2, y: 0 },
{ x: 2, y: 1 },
{ x: 2, y: 2 }
])
})

it('should return an array with a single coordinate when both points are the same', () => {
const pointA: Coords = { x: 3, y: 3 }
const pointB: Coords = { x: 3, y: 3 }

const result = getCoordinatesBetweenPoints(pointA, pointB)

expect(result).toEqual([{ x: 3, y: 3 }])
})

it('should return the bottom-left/top-right parcel as the first & second coords', () => {
const pointA: Coords = { x: 9, y: 7 }
const pointB: Coords = { x: 5, y: 3 }

const result = getCoordinatesBetweenPoints(pointA, pointB)

expect(result[0]).toEqual({ x: 5, y: 3 })
expect(result[result.length - 1]).toEqual({ x: 9, y: 7 })
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,55 @@ export function toScene(inputs: SceneInput): EditorComponentsTypes['Scene'] {
}
}

export function isValidInput(input: SceneInput): boolean {
const parcels = input.layout.parcels.split(' ')
export function toSceneAuto(inputs: SceneInput): EditorComponentsTypes['Scene'] {
const parcels = parseParcels(inputs.layout.parcels)
const points = getCoordinatesBetweenPoints(parcels[0], parcels[1])
return {
layout: {
base: points[0],
parcels: points
}
}
}

export function parseParcels(value: string): Coords[] {
const parcels = value.split(' ')
const coordsList: Coords[] = []

for (const parcel of parcels) {
const coords = parcel.split(',')
const x = parseInt(coords[0])
const y = parseInt(coords[1])
if (coords.length !== 2 || isNaN(x) || isNaN(y)) return false
if (coords.length !== 2 || isNaN(x) || isNaN(y)) return []
coordsList.push({ x, y })
}

return areConnected(coordsList)
return coordsList
}

export function getInputValidation(auto?: boolean) {
return function isValidInput(input: SceneInput): boolean {
const parcels = parseParcels(input.layout.parcels)
return auto ? parcels.length === 2 : areConnected(parcels)
}
}

export function getCoordinatesBetweenPoints(pointA: Coords, pointB: Coords): Coords[] {
const coordinates: Coords[] = []

// ensure pointA is the bottom-left coord
if (pointA.x > pointB.x) {
;[pointA.x, pointB.x] = [pointB.x, pointA.x]
}
if (pointA.y > pointB.y) {
;[pointA.y, pointB.y] = [pointB.y, pointA.y]
}

for (let x = pointA.x; x <= pointB.x; x++) {
for (let y = pointA.y; y <= pointB.y; y++) {
coordinates.push({ x, y })
}
}

return coordinates
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ async function validateAsset(extension: string, data: ArrayBuffer): Promise<Vali
case 'png':
case 'ktx2':
case 'mp3':
case '.wav':
case '.ogg':
case 'wav':
case 'ogg':
return null
default:
return `Invalid asset format ".${extension}"`
Expand Down

0 comments on commit 9b97e80

Please sign in to comment.