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: add side color to ExtrudedPathLayer #192

Merged
merged 4 commits into from
Jan 7, 2025
Merged
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
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];
};
Loading