Skip to content

Commit

Permalink
fix: use ref for HouseComponents to solve material reset (#110)
Browse files Browse the repository at this point in the history
* fix: solve flickering of the house materials
  • Loading branch information
ReidyT authored Dec 16, 2024
1 parent 028e27d commit 3099c8d
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 80 deletions.
42 changes: 19 additions & 23 deletions src/context/SimulationContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,22 +128,14 @@ export const SimulationProvider = ({

// Hooks
const houseComponentsHook = useHouseComponents({
houseConfigurator: simulationSettings.houseConfigurator,
onChange: (houseConfigurator) => {
onChange: (newHouseConfigurator) => {
dispatchHistory({
type: 'updateHouseConfigurator',
houseConfigurator,
houseConfigurator: newHouseConfigurator,
});
},
});

const resetSimulation = useCallback(() => {
dispatchHistory({
type: 'reset',
temperatureRows: temperatures.current,
});
}, []);

// Load CSV
useEffect(() => {
if (!csv) {
Expand All @@ -154,15 +146,12 @@ export const SimulationProvider = ({

loadTemperaturesFromCSV(csv.path).then((rows) => {
temperatures.current = rows;
resetSimulation();
dispatchHistory({
type: 'load',
temperatureRows: temperatures.current,
});
});
}, [
csv,
csv.measurementFrequency,
csv.path,
resetSimulation,
simulationDuration.value,
]);
}, [csv, csv.measurementFrequency, csv.path, simulationDuration.value]);

useEffect(() => {
// Only set the status from LOADING to IDLE when the heat loss is computed.
Expand All @@ -180,19 +169,21 @@ export const SimulationProvider = ({
throw new Error('The temperatures are not loaded!');
}

if (simulationStatus === SimulationStatus.FINISHED) {
dispatchHistory({
type: 'restart',
});
}

if (
simulationStatus === SimulationStatus.RUNNING ||
simulationStatus === SimulationStatus.LOADING
) {
return;
}

if (simulationStatus === SimulationStatus.FINISHED) {
resetSimulation();
}

setSimulationStatus(SimulationStatus.RUNNING);
}, [resetSimulation, simulationStatus]);
}, [simulationStatus]);

const pauseSimulation = useCallback((): void => {
if (
Expand All @@ -209,6 +200,11 @@ export const SimulationProvider = ({
// and creating too many new days.
const gotToDay = useDebouncedCallback((idx: number): void => {
pauseSimulation();

if (idx >= numberOfDays - 1) {
setSimulationStatus(SimulationStatus.FINISHED);
}

dispatchHistory({ type: 'goToDay', dayIdx: idx });
}, 10);

Expand Down
21 changes: 10 additions & 11 deletions src/hooks/useHouseComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useMemo } from 'react';
import { useCallback, useMemo, useRef } from 'react';

import {
HOUSE_INSULATIONS,
Expand Down Expand Up @@ -52,16 +52,15 @@ const DEFAULT_COMPONENTS_INSULATION = {
};

type Props = {
houseConfigurator: HouseComponentsConfigurator;
onChange: (newHouseComponents: HouseComponentsConfigurator) => void;
};

export const useHouseComponents = ({
houseConfigurator,
onChange,
}: Props): UseHouseComponentsReturnType => {
// Not working now...
const houseComponentsConfigurator = houseConfigurator;
const houseComponentsConfigurator = useRef(
HouseComponentsConfigurator.create(),
);

const registerComponent = useCallback(
({
Expand All @@ -71,7 +70,7 @@ export const useHouseComponents = ({
componentType,
}: RegisterComponentParams): void => {
const { buildingMaterials, insulationName } =
houseComponentsConfigurator.getFirstOfType(componentType) ??
houseComponentsConfigurator.current.getFirstOfType(componentType) ??
DEFAULT_COMPONENTS_INSULATION[componentType];

if (!buildingMaterials?.length) {
Expand All @@ -81,7 +80,7 @@ export const useHouseComponents = ({
}

onChange(
houseComponentsConfigurator.add({
houseComponentsConfigurator.current.add({
componentId,
parentId,
component: {
Expand All @@ -99,7 +98,7 @@ export const useHouseComponents = ({

const unregisterComponent = useCallback(
({ componentId }: Pick<RegisterComponentParams, 'componentId'>): void => {
onChange(houseComponentsConfigurator.remove({ componentId }));
onChange(houseComponentsConfigurator.current.remove({ componentId }));
},
[houseComponentsConfigurator, onChange],
);
Expand All @@ -126,7 +125,7 @@ export const useHouseComponents = ({
const buildingMaterials = HOUSE_INSULATIONS[componentType][newInsulation];

onChange(
houseComponentsConfigurator.updateInsulation({
houseComponentsConfigurator.current.updateInsulation({
componentType,
insulation: { name: newInsulation, buildingMaterials },
}),
Expand All @@ -144,7 +143,7 @@ export const useHouseComponents = ({
materialProps: { name: string } & FromBuildingMaterial;
}): void => {
const component =
houseComponentsConfigurator.getFirstOfType(componentType);
houseComponentsConfigurator.current.getFirstOfType(componentType);

if (!component) {
throw new Error(`No ${componentType} component was found!`);
Expand All @@ -169,7 +168,7 @@ export const useHouseComponents = ({
});

onChange(
houseComponentsConfigurator.updateInsulation({
houseComponentsConfigurator.current.updateInsulation({
componentType,
insulation: {
name: insulationName,
Expand Down
35 changes: 16 additions & 19 deletions src/hooks/useHouseMaterial.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react';
import { useMemo } from 'react';

import { Color, Material, MeshStandardMaterial } from 'three';

Expand All @@ -9,12 +9,14 @@ type Props = {
houseMaterial: Material;
houseComponent: HouseComponent;
colors: { [name: string]: Color };
defaultColor?: Color;
};

export const useHouseMaterial = ({
houseMaterial,
houseComponent,
colors,
defaultColor,
}: Props): Material => {
const { houseComponentsConfigurator } = useSimulation();

Expand All @@ -24,26 +26,21 @@ export const useHouseMaterial = ({
[houseComponent, houseComponentsConfigurator],
);

const [newMaterial, setNewMaterial] = useState(() => houseMaterial);
const copiedMaterial = new MeshStandardMaterial().copy(houseMaterial);

useEffect(() => {
if (houseComponentMaterials) {
setNewMaterial((curr) => {
const copiedMaterial = new MeshStandardMaterial().copy(curr);
const color = colors[houseComponentMaterials.insulationName];
if (houseComponentMaterials) {
const color = colors[houseComponentMaterials.insulationName];

if (!color) {
throw new Error(
`No color was found for the ${houseComponent} insulation ${houseComponentMaterials.insulationName}!`,
);
}

copiedMaterial.color = color;

return copiedMaterial;
});
if (!color) {
throw new Error(
`No color was found for the ${houseComponent} insulation ${houseComponentMaterials.insulationName}!`,
);
}
}, [colors, houseComponent, houseComponentMaterials]);

return newMaterial;
copiedMaterial.color = color;
} else if (defaultColor) {
copiedMaterial.color = defaultColor;
}

return copiedMaterial;
};
7 changes: 7 additions & 0 deletions src/hooks/useWallMaterial.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Color, Material } from 'three';

import { HouseInsulationPerComponent } from '@/config/houseInsulations';
import { SIMULATION_DEFAULT_WALL_COMPONENT_INSULATION } from '@/config/simulation';
import { HouseComponent } from '@/types/houseComponent';
import { fromHSL } from '@/utils/colors';

Expand Down Expand Up @@ -32,6 +33,8 @@ const COLORS: {
[HouseInsulationPerComponent.Wall.Brick]: fromHSL({ h: 20, s: 0.6, l: 0.4 }),
};

type WallColor = keyof typeof HouseInsulationPerComponent.Wall;

export const useWallMaterial = ({
wallMaterial,
}: {
Expand All @@ -41,4 +44,8 @@ export const useWallMaterial = ({
houseMaterial: wallMaterial,
houseComponent: HouseComponent.Wall,
colors: COLORS,
defaultColor:
COLORS[
SIMULATION_DEFAULT_WALL_COMPONENT_INSULATION.insulationName as WallColor
],
});
7 changes: 7 additions & 0 deletions src/hooks/useWindowMaterial.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Color, Material } from 'three';

import { HouseInsulationPerComponent } from '@/config/houseInsulations';
import { SIMULATION_DEFAULT_WINDOW_COMPONENT_INSULATION } from '@/config/simulation';
import { HouseComponent } from '@/types/houseComponent';
import { fromRGB } from '@/utils/colors';

Expand Down Expand Up @@ -30,6 +31,8 @@ type UseWindowMaterialReturnType = {
frameMaterial: Material;
};

type WindowColor = keyof typeof HouseInsulationPerComponent.Window;

export const useWindowMaterial = ({
windowMaterial,
}: {
Expand All @@ -39,5 +42,9 @@ export const useWindowMaterial = ({
houseMaterial: windowMaterial,
houseComponent: HouseComponent.Window,
colors: COLORS,
defaultColor:
COLORS[
SIMULATION_DEFAULT_WINDOW_COMPONENT_INSULATION.insulationName as WindowColor
],
}),
});
55 changes: 28 additions & 27 deletions src/reducer/simulationHistoryReducer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,12 @@ const computeSimulation = (

type Action =
| {
type: 'reset';
type: 'load';
temperatureRows: TemperatureRow[];
}
| {
type: 'restart';
}
| {
type: 'goToDay';
dayIdx: number;
Expand Down Expand Up @@ -128,32 +131,28 @@ type Action =
windowSize: WindowSizeType;
};

export const createDefault = (): SimulationHistory => {
const houseConfigurator = HouseComponentsConfigurator.create();

return {
currentDayIdx: 0,
simulationDays: CreateNonEmptyArray([
{
heatLoss: {} as SimulationHeatLoss,
totalHeatLoss: 0,
totalElectricityCost: 0,
weatherTemperature: 0,
},
]),
simulationSettings: {
indoorTemperature: SIMULATION_INDOOR_TEMPERATURE_CELCIUS.DEFAULT,
outdoorTemperature: {
userOverride: false,
value: SIMULATION_OUTDOOR_TEMPERATURE_CELCIUS.DEFAULT,
},
pricekWh: SIMULATION_PRICE_KWH,
numberOfFloors: 1,
houseConfigurator,
windowSize: 'Medium',
export const createDefault = (): SimulationHistory => ({
currentDayIdx: 0,
simulationDays: CreateNonEmptyArray([
{
heatLoss: {} as SimulationHeatLoss,
totalHeatLoss: 0,
totalElectricityCost: 0,
weatherTemperature: 0,
},
};
};
]),
simulationSettings: {
indoorTemperature: SIMULATION_INDOOR_TEMPERATURE_CELCIUS.DEFAULT,
outdoorTemperature: {
userOverride: false,
value: SIMULATION_OUTDOOR_TEMPERATURE_CELCIUS.DEFAULT,
},
pricekWh: SIMULATION_PRICE_KWH,
numberOfFloors: 1,
houseConfigurator: HouseComponentsConfigurator.create(), // will be replaced on component register
windowSize: 'Medium',
},
});

export const simulationHistory = (
state: SimulationHistory,
Expand All @@ -166,7 +165,7 @@ export const simulationHistory = (
const { simulationDays } = state;

switch (type) {
case 'reset': {
case 'load': {
const numberOfDays = action.temperatureRows.length;
return computeSimulation(
{
Expand All @@ -187,6 +186,8 @@ export const simulationHistory = (
{},
);
}
case 'restart':
return { ...state, currentDayIdx: 0 };
case 'goToDay': {
const { dayIdx } = action;
const numberOfDays = simulationDays.length;
Expand Down
16 changes: 16 additions & 0 deletions tests/player/house-floors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,19 @@ test('changing the number of floors should change the simulation info', async ({
totElectricityCost,
);
});

test('resetting the number of floors should not change the wall insulation', async ({
page,
}) => {
const housePage = new HousePage(page);
const { settings } = housePage;
await housePage.goto();

await settings.selectWallInsulation('Brick');

await settings.selectNumberOfFloors(2);
await settings.selectWallInsulation('Aerogel');

await settings.selectNumberOfFloors(1);
await expect(page.getByLabel('Aerogel')).toBeVisible();
});

0 comments on commit 3099c8d

Please sign in to comment.