Skip to content

Commit

Permalink
feat: add side color to ExtrudedPathLayer (#192)
Browse files Browse the repository at this point in the history
* feat: add side color to ExtrudedPathLayer

* test: update story snapshots

* docs: update docs

* refactor: fix type condition
  • Loading branch information
hkfb authored Jan 7, 2025
1 parent 8b9dccb commit 2bb9b47
Show file tree
Hide file tree
Showing 19 changed files with 116 additions and 36 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![codecov](https://codecov.io/github/hkfb/velo.gl/graph/badge.svg?token=51M41YJFGO)](https://codecov.io/github/hkfb/velo.gl)

Library for interactive 3D visualization of GPX activities.
React/Typescript library for interactive 3D visualization of GPX and TCX activities.

![GpxLayer](https://github.com/hkfb/velo.gl/raw/main/sample/velo.gl.webp)

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"name": "velo.gl",
"description": "React/Typescript library for interactive 3D visualization of GPX and TCX activities.",
"repository": "github:hkfb/velo.gl",
"author": "Håvard Bjerke",
"license": "MIT",
"keywords": [
"gpx",
"tcx",
"webgl",
"visualization",
"react",
Expand Down
4 changes: 3 additions & 1 deletion src/layers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const MyActivityMap = () => {
};
```

See also [Story](https://hkfb.github.io/velo.gl/?path=/docs/layers-activity-layer--docs)
See also [Activity Layer Story](https://hkfb.github.io/velo.gl/?path=/docs/layers-activity-layer--docs)

## Profile Layer
Renders paths extruded vertically from sea level, from a set of 3D polylines.
Expand All @@ -38,3 +38,5 @@ Renders the position along a GPX trace at a given time.

## Extruded Path Layer
Extends the [Deck.gl Path Layer](https://deck.gl/docs/api-reference/layers/path-layer) with vertical extrusion of paths.

See also [Extruded Path Layer Story](https://hkfb.github.io/velo.gl/?path=/docs/layers-extruded-path-layer--docs)
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ in vec3 instanceEndPositions64Low;
in vec3 instanceRightPositions64Low;
in float instanceStrokeWidths;
in vec4 instanceColors;
in vec4 instanceSideColors;
in vec3 instancePickingColors;
uniform float widthScale;
Expand Down Expand Up @@ -176,12 +177,12 @@ void main() {
geometry.pickingColor = instancePickingColors;
vColor = vec4(instanceColors.rgb, instanceColors.a * opacity);
vec4 sideColor = vec4(255, 0, 0, 1);
vec4 sideColor = vec4(instanceSideColors.rgb, instanceSideColors.a * opacity);
float isEnd = positions.x;
float isTop = positions.z;
//vColor = mix(sideColor, vColor, isTop);
vColor = mix(sideColor, vColor, isTop);
float seaLevel = 0.0;
Expand Down
47 changes: 37 additions & 10 deletions src/layers/extruded-path-layer/extruded-path-layer.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import {
type ExtrudedPathLayerProps,
} from "./extruded-path-layer";
import { DeckGL } from "@deck.gl/react";
import { Color } from "@deck.gl/core";
import * as React from "react";
import { Point3d } from "../profile-layer/extrudePolylineProfile";
import type { StoryObj } from "@storybook/react";
import { StreetLayer } from "../street-layer";
import { SYNTHETIC_VIEW_STATE } from "../../constant.stories";
import * as d3 from "d3-color";
import { PathGeometry } from "@deck.gl/layers/dist/path-layer/path";
import { Matrix4 } from "@math.gl/core";
import { getRgba } from "../util.stories";

export default {
title: "Layers / Extruded Path Layer",
Expand Down Expand Up @@ -171,14 +170,7 @@ export const ProfileColor: StoryObj<{ color: string }> = {
},
},
render: ({ color }) => {
const { r, g, b, opacity } = d3.color(color)?.rgb() ?? {
r: 0,
g: 0,
b: 0,
opacity: 1,
};

const getColor: Color = [r, g, b, opacity * 255];
const getColor = getRgba(color);

const props = {
...DEFAULT_PROPS,
Expand All @@ -205,6 +197,41 @@ export const ProfileColor: StoryObj<{ color: string }> = {
tags: ["no-test-webkit"],
};

export const SideColor: StoryObj<{ mainColor: string; sideColor: string }> = {
args: {
mainColor: "yellow",
sideColor: "gray",
},

render: ({ mainColor, sideColor }) => {
const getColor = getRgba(mainColor);
const getSideColor = getRgba(sideColor);

const props: ExtrudedPathLayerProps<unknown> = {
...DEFAULT_PROPS,
getColor,
getSideColor,
};

const profile = new ExtrudedPathLayer({ ...props });

return (
<DeckGL
layers={[profile]}
initialViewState={SYNTHETIC_VIEW_STATE}
controller
></DeckGL>
);
},
parameters: {
docs: {
description: {
story: "Control top and side color.",
},
},
},
};

export const ProfileWidth: StoryObj<{ width: number }> = {
args: {
width: 1000,
Expand Down
56 changes: 54 additions & 2 deletions src/layers/extruded-path-layer/extruded-path-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,45 @@ import { PathLayer, type PathLayerProps } from "@deck.gl/layers";
import { fs } from "./extruded-path-layer-fragment.glsl";
import { vs } from "./extruded-path-layer-vertex.glsl";
import { Geometry, Model } from "@luma.gl/engine";
import type { Accessor, Color } from "@deck.gl/core";
import { TransitionSettings } from "@deck.gl/core/dist/lib/attribute/transition-settings";
import { NumberArray, TypedArray } from "@math.gl/types";
import * as _ from "lodash";

export type ExtrudedPathLayerProps<DataT> = PathLayerProps<DataT>;
export type ExtrudedPathLayerProps<DataT> = PathLayerProps<DataT> & {
/**
* Path side wall color accessor.
* @default [20, 20, 20, 255]
*/
getSideColor?: Accessor<DataT, Color | Color[]>;
};

const ATTRIBUTE_TRANSITION: Partial<TransitionSettings> = {
enter: (value: NumberArray, chunk?: NumberArray) => {
if (!_.isTypedArray(chunk)) {
return value;
}

return chunk?.length
? ((chunk as unknown as TypedArray).subarray(
chunk.length - value.length,
) as unknown as NumberArray)
: value;
},
};

const DEFAULT_SIDE_COLOR = [20, 20, 20, 255];

/**
* Deck.gl layer that extrudes a path vertically from sea level.
*/
export class ExtrudedPathLayer extends PathLayer {
export class ExtrudedPathLayer<
DataT = unknown,
PropsT = ExtrudedPathLayerProps<DataT>,
> extends PathLayer<DataT, PropsT & PathLayerProps> {
static defaultProps = {
...PathLayer.defaultProps,
getSideColor: { type: "accessor", value: DEFAULT_SIDE_COLOR },
};
static layerName = "ExtrudedPathLayer";

Expand Down Expand Up @@ -104,4 +134,26 @@ export class ExtrudedPathLayer extends PathLayer {
isInstanced: true,
});
}

initializeState() {
super.initializeState();
const attributeManager = this.getAttributeManager();

const layerProps: ExtrudedPathLayerProps<unknown> = this.props;

// Fall back to main color if side color is not specified.
const sideColorAccessorName = layerProps.getSideColor
? "getSideColor"
: "getColor";

attributeManager!.addInstanced({
instanceSideColors: {
size: this.props.colorFormat.length,
type: "unorm8",
accessor: sideColorAccessorName,
transition: ATTRIBUTE_TRANSITION,
defaultValue: DEFAULT_SIDE_COLOR,
},
});
}
}
23 changes: 3 additions & 20 deletions src/layers/profile-layer/profile-layer.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { ProfileLayer, type ProfileLayerProps } from "./profile-layer";
import { DeckGL } from "@deck.gl/react";
import { Color } from "@deck.gl/core";
import * as React from "react";
import { Point3d } from "./extrudePolylineProfile";
import type { StoryObj } from "@storybook/react";
import { StreetLayer } from "../street-layer";
import { SYNTHETIC_VIEW_STATE } from "../../constant.stories";
import * as d3 from "d3-color";
import { type TextureProps } from "@luma.gl/core";
import { getRgba } from "../util.stories";

export default {
title: "Layers / Profile Layer",
Expand Down Expand Up @@ -212,15 +211,7 @@ export const ProfileColor: StoryObj<{ color: string }> = {
},
render: ({ color }) => {
const data = React.useMemo(() => [PATH_LAT_LONG], []);

const { r, g, b, opacity } = d3.color(color)?.rgb() ?? {
r: 0,
g: 0,
b: 0,
opacity: 1,
};

const getColor: Color = [r, g, b, opacity * 255];
const getColor = getRgba(color);

const props = {
data,
Expand Down Expand Up @@ -312,15 +303,7 @@ export const PhongShading: StoryObj<
},
render: ({ color, phongShading, material }) => {
const data = React.useMemo(() => [PATH_LAT_LONG], []);

const { r, g, b, opacity } = d3.color(color)?.rgb() ?? {
r: 0,
g: 0,
b: 0,
opacity: 1,
};

const getColor: Color = [r, g, b, opacity * 255];
const getColor = getRgba(color);

const props = {
data,
Expand Down
12 changes: 12 additions & 0 deletions src/layers/util.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as d3 from "d3-color";
import { Color } from "@deck.gl/core";

export const getRgba = (color: string): Color => {
const { r, g, b, opacity } = d3.color(color)?.rgb() ?? {
r: 0,
g: 0,
b: 0,
opacity: 1,
};
return [r, g, b, opacity * 255];
};

0 comments on commit 2bb9b47

Please sign in to comment.