Skip to content
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
8 changes: 5 additions & 3 deletions src/common/geometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ export const snapToGrid = (value, tolerance) => {
return Math.round(value / tolerance) * tolerance
}

export const distance = (v1, v2) => {
return Math.sqrt(Math.pow(v1.x - v2.x, 2.0) + Math.pow(v1.y - v2.y, 2.0))
}
// Using x*x instead of Math.pow(x, 2) avoids function call overhead (~10-20x
// faster for squaring).
export const magnitude = (x, y) => Math.sqrt(x * x + y * y)

export const distance = (v1, v2) => magnitude(v1.x - v2.x, v1.y - v2.y)

// Calculate the centroid (geometric center) of a set of vertices.
// Excludes duplicate closing vertex if present (would skew the average).
Expand Down
5 changes: 3 additions & 2 deletions src/features/app/rootSlice.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* global structuredClone */
import { combineReducers } from "redux"
import machinesReducer from "@/features/machines/machinesSlice"
import exporterReducer from "@/features/export/exporterSlice"
Expand All @@ -21,7 +22,7 @@ const combinedReducer = combineReducers({
})

const resetPattern = (state, action) => {
const newState = JSON.parse(JSON.stringify(state)) // deep copy
const newState = structuredClone(state)

newState.layers = undefined
newState.effects = undefined
Expand All @@ -39,7 +40,7 @@ const resetAll = (state, action) => {

const loadPattern = (state, action) => {
const { layers, effects, images } = action.payload
const newState = JSON.parse(JSON.stringify(state)) // deep copy
const newState = structuredClone(state)

newState.layers = layers
newState.effects = effects
Expand Down
60 changes: 36 additions & 24 deletions src/features/effects/EffectList.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react"
import React, { useCallback } from "react"
import { useTranslation } from "react-i18next"
import { useDispatch, useSelector } from "react-redux"
import Button from "react-bootstrap/Button"
Expand All @@ -20,14 +20,14 @@ import {
} from "./effectsSlice"
import { getEffect } from "@/features/effects/effectFactory"

const EffectRow = ({
const EffectRow = React.memo(function EffectRow({
current,
selected,
effect,
handleEffectSelected,
handleToggleEffectVisible,
t,
}) => {
}) {
const { name, id, visible } = effect
const instance = getEffect(effect.type)
const { attributes, listeners, setNodeRef, transform, isDragging } =
Expand Down Expand Up @@ -80,7 +80,7 @@ const EffectRow = ({
</div>
</ListGroup.Item>
)
}
})

const EffectList = ({ effects, selectedLayer }) => {
const { t } = useTranslation()
Expand All @@ -98,28 +98,40 @@ const EffectList = ({ effects, selectedLayer }) => {
}),
)

const handleDragStart = ({ active }) => dispatch(setCurrentEffect(active.id))
const handleDragStart = useCallback(
({ active }) => dispatch(setCurrentEffect(active.id)),
[dispatch],
)

const handleDragEnd = ({ active, over }) => {
if (!over) {
return
}
if (active.id !== over.id) {
const oldIndex = effects.findIndex((effect) => effect.id === active.id)
const newIndex = effects.findIndex((effect) => effect.id === over.id)
dispatch(moveEffect({ id: selectedLayer.id, oldIndex, newIndex }))
}
}
const handleDragEnd = useCallback(
({ active, over }) => {
if (!over) {
return
}
if (active.id !== over.id) {
const oldIndex = effects.findIndex((effect) => effect.id === active.id)
const newIndex = effects.findIndex((effect) => effect.id === over.id)
dispatch(moveEffect({ id: selectedLayer.id, oldIndex, newIndex }))
}
},
[dispatch, effects, selectedLayer?.id],
)

const handleToggleEffectVisible = (id, visible) => {
dispatch(setCurrentEffect(id))
dispatch(updateEffect({ id, visible: !visible }))
}
const handleToggleEffectVisible = useCallback(
(id, visible) => {
dispatch(setCurrentEffect(id))
dispatch(updateEffect({ id, visible: !visible }))
},
[dispatch],
)

const handleEffectSelected = (event) => {
const id = event.target.closest(".list-group-item").id
dispatch(setCurrentEffect(id))
}
const handleEffectSelected = useCallback(
(event) => {
const id = event.target.closest(".list-group-item").id
dispatch(setCurrentEffect(id))
},
[dispatch],
)

return (
<DndContext
Expand Down Expand Up @@ -158,4 +170,4 @@ const EffectList = ({ effects, selectedLayer }) => {
)
}

export default EffectList
export default React.memo(EffectList)
58 changes: 35 additions & 23 deletions src/features/layers/LayerList.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react"
import React, { useCallback } from "react"
import { useTranslation } from "react-i18next"
import Button from "react-bootstrap/Button"
import ListGroup from "react-bootstrap/ListGroup"
Expand All @@ -23,15 +23,15 @@ import {
updateLayer,
} from "@/features/layers/layersSlice"

const LayerRow = ({
const LayerRow = React.memo(function LayerRow({
current,
selected,
numLayers,
layer,
handleLayerSelected,
handleToggleLayerVisible,
t,
}) => {
}) {
const { name, id, visible } = layer
const activeClass = current ? "active" : selected ? "selected" : ""
const dragClass = numLayers > 1 ? "cursor-move" : ""
Expand Down Expand Up @@ -90,7 +90,7 @@ const LayerRow = ({
</div>
</ListGroup.Item>
)
}
})

const LayerList = () => {
const { t } = useTranslation()
Expand All @@ -109,28 +109,40 @@ const LayerList = () => {
const numLayers = useSelector(selectNumLayers)
const layers = useSelector(selectAllLayers)

const handleLayerSelected = (event) => {
const id = event.target.closest(".list-group-item").id
dispatch(setCurrentLayer(id))
}
const handleLayerSelected = useCallback(
(event) => {
const id = event.target.closest(".list-group-item").id
dispatch(setCurrentLayer(id))
},
[dispatch],
)

const handleDragStart = ({ active }) => dispatch(setCurrentLayer(active.id))
const handleDragStart = useCallback(
({ active }) => dispatch(setCurrentLayer(active.id)),
[dispatch],
)

const handleToggleLayerVisible = (id, visible) => {
dispatch(setCurrentLayer(id))
dispatch(updateLayer({ id, visible: !visible }))
}
const handleToggleLayerVisible = useCallback(
(id, visible) => {
dispatch(setCurrentLayer(id))
dispatch(updateLayer({ id, visible: !visible }))
},
[dispatch],
)

const handleDragEnd = ({ active, over }) => {
if (!over) {
return
}
if (active.id !== over.id) {
const oldIndex = layers.findIndex((layer) => layer.id === active.id)
const newIndex = layers.findIndex((layer) => layer.id === over.id)
dispatch(moveLayer({ oldIndex, newIndex }))
}
}
const handleDragEnd = useCallback(
({ active, over }) => {
if (!over) {
return
}
if (active.id !== over.id) {
const oldIndex = layers.findIndex((layer) => layer.id === active.id)
const newIndex = layers.findIndex((layer) => layer.id === over.id)
dispatch(moveLayer({ oldIndex, newIndex }))
}
},
[dispatch, layers],
)

return (
<DndContext
Expand Down
7 changes: 4 additions & 3 deletions src/features/machines/PolarMachine.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
cloneVertex,
subsample,
circle,
magnitude,
} from "@/common/geometry"
import Victor from "victor"
import Machine, { machineOptions } from "./Machine"
Expand Down Expand Up @@ -173,9 +174,9 @@ export default class PolarMachine extends Machine {

// Returns whether a given path lies on the perimeter of the circle.
onPerimeter(v1, v2, delta = 1) {
const rm = Math.sqrt(Math.pow(this.state.maxRadius, 2))
const r1 = Math.sqrt(Math.pow(v1.x, 2) + Math.pow(v1.y, 2))
const r2 = Math.sqrt(Math.pow(v2.x, 2) + Math.pow(v2.y, 2))
const rm = this.state.maxRadius
const r1 = magnitude(v1.x, v1.y)
const r2 = magnitude(v2.x, v2.y)

return Math.abs(r1 - rm) < delta && Math.abs(r2 - rm) < delta
}
Expand Down
Loading