bare-minimum-3d icon indicating copy to clipboard operation
bare-minimum-3d copied to clipboard

Implement custom geometry API

Open fuddl opened this issue 4 years ago • 6 comments

@mithi next I'll try to implement the API into bare-minimum-3d. I guess it should look like this:

renderScene(viewSettings, sceneSettings, sceneOptions, data3d, plugins)

fuddl avatar Mar 21 '21 21:03 fuddl

@mithi I suppose we need a way to tell the DataRenderer.render() which projection method should be used. Currently this is determined by it's (hardcoded) type.

How about we instead ask if the data is shaped like a point or a line. Eg: does it have a property x or x0? The line case seems rather exotic to me. Did you plan other primitive types?

In order to make my triangles example work, all I needed to do was:

    render(data: Array<Data3dSpecs>): Array<Data2dSpecs> {
        console.debug(data)
        return data.map((element: Data3dSpecs) => {
            switch (element.type) {
-               case DataSpecType.polygon:
-               case DataSpecType.points:
-                   return this._projectPolygonOrPoints(element)
                case DataSpecType.lines:
                    return this._projectLines(element)
+               default: 
+                   return this._projectPolygonOrPoints(element)
            }
        })
    }

What do you think? should _projectPolygonOrPoints be used as the default?

fuddl avatar Mar 22 '21 19:03 fuddl

I think it is better to be more explicit with the types and to be more explicit on the algorithm that will be used based on the type.

Also I think it would be better, if DataRenderer is not modified at all inspired by the open-closed principle. The code might be easier to maintain if it's closed for modification but open for extension..

It might be better if the pseudo code could be something like this:


// plugin = {
//  Renderer: (The actual renderer)
//  types: (an array of types it can render)
// }

const renderScene = (
  viewSettings: ViewSettings,
  sceneSettings: SceneSettings,
  sceneOptions: SceneOptions,
  data3d: Array<Unknown>,
  plugins: Array<Unknown>
)  {

  // .... initial computations 

  const bareMinimumDataRenderer = new DataRenderer(/*...arguments...*/ )

  const models = data.map((element: Unknown) => {
    if ([DataSpecType.polygon, DataSpecType.points, DataSpecType.lines].includes(element.type)) {
      // this could be optimized by adding the function:
      // bareMinimumDataRenderer.renderSingle(element)
      return bareMinimumDataRenderer.render([element]) 
    } 
    
    // the bareMinimumDataRenderer does not know how to render this type of data
    // use one of your customRenderers here
    for (let plugin of plugins) {
      if(!plugin.types.includes(element.type)) {
        continue
      }
      return new plugin.Renderer(/*...arguments...*/).render(element)  
    }
    // write an error here `unhandled datatype: ${element.type}`
  })

  // more code here....

  return { container, data: [...sceneData, ...models] }
}


mithi avatar Mar 23 '21 08:03 mithi

Did you plan other primitive types?

I only planned one other type which is a 3d circle.. see also: https://github.com/mithi/bare-minimum-3d/issues/6

mithi avatar Mar 23 '21 08:03 mithi

@fuddl

If you also think that you need to reuse the the built DataRenderer, feel free to modify bare-minimum-3d to expose it so that your custom plugin / data renderer could use it. ie modify https://github.com/mithi/bare-minimum-3d/blob/master/src/index.ts

import { renderScene } from "./utils.js"
import DataRenderer from "./data-renderer"
export { DataRenderer }
export default renderScene

mithi avatar Mar 23 '21 09:03 mithi

you are suggesting plug-ins should look like this?

const trianglesPlugin = {
  triangles: {
    types: ['points'],
    Renderer: (element, transforms) => {
      const { size, color, opacity, id } = element
      return element.x.map((x, i) => (
        <Triangle
          {...{
            x,
            y: element.y[i],
            size,
            color,
            opacity,
            id,
            i,
            transforms
          }}
          key={`${id}-${i}`}
        />
      ))
    }
  }
}

and their invocation should look like this?

const { container, data } = renderScene(
  viewSettings,
  sceneSettings,
  sceneOptions,
  [{
    id: 'my-trangle',
    type: 'triangles',
    opacity: 1.0,
    color: 'white',
    size: 2,
    x: [42],
    y: [23],
    z: [47],
  }],
  [trianglesPlugin]
)

fuddl avatar Mar 23 '21 15:03 fuddl

you are suggesting plug-ins should look like this?

No.

renderScene converts 3d data to 2d data.

For example, this is the input

const dataInput3d = [{
        color: "#FC427B",
        opacity: 1,
        size: 14,
        id: "head",
        type: DataSpecType.points,
        x: [0.0],
        y: [100.0],
        z: [100.0],
    }]

this would be the output


const dataOutput2d = [{
            color: "#FC427B",
            opacity: 1,
            size: 14,
            id: "head",
            type: "points",
            x: [-20.91252502759967],
            y: [-93.39172830586264],
            z: [100] // having a z is a stupid bug, please ignore, I will fix this soon
        }
]

And this 2d data is the one you feed to BareMinimum2d ie

const { container, dataOutput2d } = renderScene(
        viewSettings,
        sceneSettings,
        sceneOptions,
        dataInput3d
    )

<BareMinimum2d {...{container, data: dataOutput2d} />

But that's how it works...

It's alright if you think it's too much work to make it work... don't feel obligated to continue implementing this feature!

mithi avatar Mar 23 '21 17:03 mithi