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

feat(texture): support selecting textures #356

Merged
merged 13 commits into from
Mar 7, 2024
10 changes: 6 additions & 4 deletions src/components/bookmarks-panel/bookmarks-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
useAppLayout,
} from "../../utils/hooks/layout";

import { FileType } from "../../types";
import { FileType, FileUploaded } from "../../types";
import { parseBookmarks } from "../../utils/bookmarks-utils";

enum PopoverType {
Expand Down Expand Up @@ -241,10 +241,12 @@ export const BookmarksPanel = ({
setPopoverType(PopoverType.none);
};

const onBookmarksUploadedHandler = async (bookmarks) => {
const onBookmarksUploadedHandler = async ({ fileContent }: FileUploaded) => {
setPopoverType(PopoverType.none);
const bookmarksParsed = await parseBookmarks(bookmarks);
bookmarksParsed && onBookmarksUploaded(bookmarksParsed);
if (typeof fileContent === "string") {
const bookmarksParsed = await parseBookmarks(fileContent);
bookmarksParsed && onBookmarksUploaded(bookmarksParsed);
}
};

const renderPopoverContent = () => {
Expand Down
58 changes: 25 additions & 33 deletions src/components/debug-panel/debug-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { ReactEventHandler } from "react";
import styled from "styled-components";
import { useState, useMemo } from "react";
import { selectIconList } from "../../redux/slices/icon-list-slice";
import { ITexture, IconListSetName } from "../../types";
import { useState } from "react";
import { addIconItem } from "../../redux/slices/icon-list-slice";
import { IIconItem, IconListSetName } from "../../types";
import md5 from "md5";
import {
addUVDebugTexture,
initTextures,
} from "../../redux/slices/uv-debug-texture-slice";
import { IconListPanel } from "../icon-list-panel/icon-list-panel";
import { ActionIconButton } from "../action-icon-button/action-icon-button";
import PlusIcon from "../../../public/icons/plus.svg";
import { ButtonSize } from "../../types";
import { UploadPanel } from "../upload-panel/upload-panel";
import { FileType } from "../../types";
import { FileType, FileUploaded } from "../../types";

import {
BoundingVolumeColoredBy,
Expand Down Expand Up @@ -93,20 +89,31 @@ export const DebugPanel = ({ onClose }: DebugPanelProps) => {
const dispatch = useAppDispatch();
const [showFileUploadPanel, setShowFileUploadPanel] = useState(false);
const debugOptions = useAppSelector(selectDebugOptions);
const uvDebugTextureArray = useAppSelector(
selectIconList(IconListSetName.uvDebugTexture)
);

useMemo(() => {
if (!uvDebugTextureArray.length) {
dispatch(initTextures());
}
}, []);

const onTextureInsertClick = () => {
setShowFileUploadPanel(true);
};

const onFileUploadedHandler = async ({ info }: FileUploaded) => {
setShowFileUploadPanel(false);
const url = info.url as string;
const hash = md5(url);

const texture: IIconItem = {
id: `${hash}`,
icon: url,
extData: { imageUrl: url },
custom: false,
};
dispatch(
addIconItem({
iconListSetName: IconListSetName.uvDebugTexture,
iconItem: texture,
setCurrent: true,
})
);
};

return (
<PanelContainer layout={layout}>
<PanelHeader panel={Panels.Debug}>
Expand Down Expand Up @@ -226,22 +233,7 @@ export const DebugPanel = ({ onClose }: DebugPanelProps) => {
onCancel={() => {
setShowFileUploadPanel(false);
}}
onFileUploaded={async ({ info }) => {
setShowFileUploadPanel(false);
const url = info.url as string;
const hash = md5(url);

const texture: ITexture = {
id: `${hash}`,
image: null,
imageUrl: url,
icon: url,
custom: true,
};
await dispatch(
addUVDebugTexture({ texture: texture, setCurrent: true })
);
}}
onFileUploaded={onFileUploadedHandler}
/>
)}

Expand Down
10 changes: 9 additions & 1 deletion src/components/deck-gl-wrapper/deck-gl-wrapper.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,15 @@ describe("Deck.gl I3S map component", () => {
const { onTileLoad: onTileLoadSecond } = (CustomTile3DLayer as any).mock
.lastCall[0];
await act(() => onTileLoadSecond(tile3d));
expect(selectDebugTextureForTile).toHaveBeenCalledWith(tile3d, null);
const imageStubObject = {
width: 1024,
height: 1024,
data: new ArrayBuffer(0),
};
expect(selectDebugTextureForTile).toHaveBeenCalledWith(
tile3d,
imageStubObject
);
expect(selectOriginalTextureForTile).toHaveBeenCalledTimes(1);
});

Expand Down
23 changes: 8 additions & 15 deletions src/components/deck-gl-wrapper/deck-gl-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@
import { useAppSelector, useAppDispatch } from "../../redux/hooks";
import { selectColorsByAttribute } from "../../redux/slices/symbolization-slice";
import { selectDragMode } from "../../redux/slices/drag-mode-slice";
import { selectIconItemPickedId } from "../../redux/slices/icon-list-slice";
import { selectIconItemPicked } from "../../redux/slices/icon-list-slice";
import {
initTextures,
fetchUVDebugTexture,
selectUVDebugTexture,
} from "../../redux/slices/uv-debug-texture-slice";
Expand Down Expand Up @@ -206,7 +205,7 @@
pickable = false,
layers3d,
lastLayerSelectedId,
loadDebugTextureImage = false,

Check warning on line 208 in src/components/deck-gl-wrapper/deck-gl-wrapper.tsx

View workflow job for this annotation

GitHub Actions / Linter

'loadDebugTextureImage' is assigned a value but never used
featurePicking = true,
normalsDebugData,
normalsTrianglesPercentage = 100,
Expand Down Expand Up @@ -293,10 +292,11 @@
},
});
const [terrainTiles, setTerrainTiles] = useState({});
const iconItemPickedId = useAppSelector(
selectIconItemPickedId(IconListSetName.uvDebugTexture)
const iconItemPicked = useAppSelector(
selectIconItemPicked(IconListSetName.uvDebugTexture)
);
const uvDebugTexture = useAppSelector(selectUVDebugTexture(iconItemPickedId));
const imageUrl = (iconItemPicked?.extData.imageUrl as string) || "";
const uvDebugTexture = useAppSelector(selectUVDebugTexture(imageUrl));
const uvDebugTextureRef = useRef<ImageBitmap | null>(null);
uvDebugTextureRef.current = uvDebugTexture;
const [needTransitionToTileset, setNeedTransitionToTileset] = useState(false);
Expand All @@ -310,18 +310,11 @@

const dispatch = useAppDispatch();

/** Load debug texture if necessary */
useMemo(() => {
belom88 marked this conversation as resolved.
Show resolved Hide resolved
if (loadDebugTextureImage && !uvDebugTexture) {
belom88 marked this conversation as resolved.
Show resolved Hide resolved
dispatch(initTextures());
if (imageUrl) {
dispatch(fetchUVDebugTexture(imageUrl));
}
}, [loadDebugTextureImage]);

useMemo(() => {
if (!uvDebugTexture && iconItemPickedId) {
dispatch(fetchUVDebugTexture(iconItemPickedId));
}
}, [iconItemPickedId]);
}, [imageUrl]);

/**
* Hook to call multiple changing function based on selected tileset.
Expand Down
2 changes: 0 additions & 2 deletions src/components/icon-list-panel/icon-list-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ const TextureIcon = styled.div<{
active &&
css`
box-shadow: 0 0 0 2px #000000, 0 0 0 4px #ffffff;
-moz-box-shadow: 0 0 0 2px #000000, 0 0 0 4px #ffffff;
-webkit-shadow: 0 0 0 2px #000000, 0 0 0 4px #ffffff;
`}
`;

Expand Down
7 changes: 2 additions & 5 deletions src/components/upload-panel/upload-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled from "styled-components";

import { FileType } from "../../types";
import { FileType, FileUploaded } from "../../types";
import { UploadPanelItem } from "./upload-panel-item";

import UploadIcon from "../../../public/icons/upload.svg";
Expand Down Expand Up @@ -61,10 +61,7 @@ type UploadProps = {
fileType: FileType;
multipleFiles?: boolean;
onCancel: () => void;
onFileUploaded: (fileUploaded: {
fileContent: string | ArrayBuffer;
info: Record<string, unknown>;
}) => void;
onFileUploaded: (fileUploaded: FileUploaded) => void;
};

export const UploadPanel = ({
Expand Down
90 changes: 68 additions & 22 deletions src/redux/slices/icon-list-slice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
belom88 marked this conversation as resolved.
Show resolved Hide resolved
import { RootState } from "../store";
import { IIconItem } from "../../types";
import { IIconItem, IconListSetName } from "../../types";

import uv1 from "../../../public/images/uvTexture1.png";
import uv1Icon from "../../../public/images/uvTexture1.thumb.png";
import uv2 from "../../../public/images/uvTexture2.png";
import uv2Icon from "../../../public/images/uvTexture2.thumb.png";
import uv3 from "../../../public/images/uvTexture3.png";
import uv3Icon from "../../../public/images/uvTexture3.thumb.png";
import uv4 from "../../../public/images/uvTexture4.png";
import uv4Icon from "../../../public/images/uvTexture4.thumb.png";
import uv5 from "../../../public/images/uvTexture5.png";
import uv5Icon from "../../../public/images/uvTexture5.thumb.png";

/**
* IconListSet "BaseMap"
Expand Down Expand Up @@ -35,8 +46,34 @@ interface IIconListState {
iconListSets: Record<string, IIconListSet>;
}

const initialState: IIconListState = {
iconListSets: {},
const initialState = (): IIconListState => {
belom88 marked this conversation as resolved.
Show resolved Hide resolved
const initState = { iconListSets: {} };
const UV_DEBUG_TEXTURE_URL_ARRAY: {
id: string;
uv: string;
icon: string;
}[] = [
{ id: "uv1", uv: uv1, icon: uv1Icon },
{ id: "uv2", uv: uv2, icon: uv2Icon },
{ id: "uv3", uv: uv3, icon: uv3Icon },
{ id: "uv4", uv: uv4, icon: uv4Icon },
{ id: "uv5", uv: uv5, icon: uv5Icon },
];

for (const tex of UV_DEBUG_TEXTURE_URL_ARRAY) {
const texture: IIconItem = {
id: tex.id,
icon: tex.icon,
extData: { imageUrl: tex.uv },
custom: false,
};
addItem(initState, {
iconListSetName: IconListSetName.uvDebugTexture,
iconItem: texture,
setCurrent: tex.id === "uv1",
});
}
return initState;
};

const iconListSlice = createSlice({
Expand All @@ -51,25 +88,7 @@ const iconListSlice = createSlice({
setCurrent: boolean;
}>
) => {
const arg = action.payload;
if (!state.iconListSets[arg.iconListSetName]) {
state.iconListSets[arg.iconListSetName] = {
iconList: [],
iconItemIdPicked: "",
};
}
const element =
arg.iconItem.id &&
state.iconListSets[arg.iconListSetName].iconList.find(
(item) => item.id === arg.iconItem.id
);
if (!element) {
state.iconListSets[arg.iconListSetName].iconList.push(arg.iconItem);
}
if (arg.setCurrent) {
state.iconListSets[arg.iconListSetName].iconItemIdPicked =
arg.iconItem.id;
}
addItem(state, action.payload);
},
setIconItemPicked: (
state: IIconListState,
Expand Down Expand Up @@ -102,6 +121,33 @@ const iconListSlice = createSlice({
},
});

const addItem = (
state: IIconListState,
arg: {
iconListSetName: string;
iconItem: IIconItem;
setCurrent: boolean;
}
) => {
if (!state.iconListSets[arg.iconListSetName]) {
state.iconListSets[arg.iconListSetName] = {
iconList: [],
iconItemIdPicked: "",
};
}
const element =
arg.iconItem.id &&
state.iconListSets[arg.iconListSetName].iconList.find(
(item) => item.id === arg.iconItem.id
);
if (!element) {
state.iconListSets[arg.iconListSetName].iconList.push(arg.iconItem);
}
if (arg.setCurrent) {
state.iconListSets[arg.iconListSetName].iconItemIdPicked = arg.iconItem.id;
}
};

export const selectIconList =
(iconListSetName: string, group?: string) =>
(state: RootState): IIconItem[] => {
Expand Down
11 changes: 6 additions & 5 deletions src/redux/slices/uv-debug-texture-slice.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { setupStore } from "../store";
import {
fetchUVDebugTexture,
selectUVDebugTexture,
initTextures,
} from "./uv-debug-texture-slice";
import { ImageLoader } from "@loaders.gl/images";

jest.mock("@loaders.gl/core");

Expand All @@ -24,14 +24,15 @@ describe("slice: uv-debug-texture", () => {
it("fetchUVDebugTexture should call loading mocked texture and put it into the slice state", async () => {
const store = setupStore();
const state = store.getState();
expect(selectUVDebugTexture("uv1")(state)).toEqual(null);
const imageUrl = "https://localhost:3000/images/uvTexture1.png";
expect(selectUVDebugTexture(imageUrl)(state)).toEqual(null);

await store.dispatch(initTextures());
await store.dispatch(fetchUVDebugTexture("uv1"));
await store.dispatch(fetchUVDebugTexture(imageUrl));

expect(load).toHaveBeenCalledTimes(1);
expect(load).toHaveBeenCalledWith(imageUrl, ImageLoader);

const newState = store.getState();
expect(selectUVDebugTexture("uv1")(newState)).toEqual(imageStubObject);
expect(selectUVDebugTexture(imageUrl)(newState)).toEqual(imageStubObject);
});
});
Loading
Loading