visgl / deck.gl

WebGL2 powered visualization framework
https://deck.gl
MIT License
12.24k stars 2.08k forks source link

Extruded and pickable polygons using binary attributes #4919

Closed dcooley closed 4 years ago

dcooley commented 4 years ago

Example

I've got this polygon (using Solidpolygon layer ) which has already been earcut into triangles and I'm plotting it using the 'binary' specifications.

I've said pickable: true, but it only highlights the individual triangles, not the polygon has a whole.

working jsfiddle

Screen Shot 2020-09-04 at 1 00 17 pm

If I understand correctly the startIndices attribute defines each polygon. So if I set this to 0 it means all the triangles are part of the same polygon. Which makes the highlight work, but it adds extra side-walls between adjoining coordinates.

working jsfiddle

Screen Shot 2020-09-04 at 1 01 30 pm

If I set extruded: false, this removes those walls

working jsfiddle

Screen Shot 2020-09-04 at 1 02 53 pm


Question

In my example, how should I specify the startIndices and indices attributes so the picking selects the whole polygon for both extruded and non-extruded, and also avoids drawing the extra side-walls?

Pessimistress commented 4 years ago

Your positions array should still be organized in loops for extrusion to work.

dcooley commented 4 years ago

Your positions array should still be organized in loops for extrusion to work.

Can I ask how this is handled inside SolidpolygonLayer? Specifically, here is where you call earcut() to triangulate the polygon. But I can't see how you then organise the result into these 'loops'.

Pessimistress commented 4 years ago

Positions must trace the bounds of each polygon, as called for by the getPolygon API (which complies with the GeoJSON specification). The SolidPolygonLayer does check if a polygon is closed (i.e. the first vertex is the same as the last vertex) and automatically closes it if needed - the closing of loops is required for extrusion to work correctly. When you are using binary attributes, it is your responsibility to make sure that all polygons are defined in order and in closed loops.

dcooley commented 4 years ago

I've made a simplified example, but I still can't quite see what I'm doing wrong to solve the extrusion issue.

jsfiddle example

Screen Shot 2020-09-11 at 3 04 25 pm

The process I'm using

Here's the relevant code from the linked jsfiddle - can you see what I'm doing wrong?

    // polygon with a hole
  // each ring is closed
  const dimension = 2;
  const polygon = [
  0,0, 0,1, 0.5,1.5, 1,1, 1,0, 0,0, 
  0.2,0.2, 0.2,0.8, 0.6,0.8, 0.6,0.2, 0.2,0.2
  ];
  const ec = earcut( polygon, [6], dimension ); // hole at idx 6, stride == 2

  // earcut gives the index of triangle coordinates
  // in groups of 3
  //console.log( ec );
  // [9,8,7, 4,3,2, 2,1,0, 4,2,5, 9,7,10]

  const binaryLocation = new Float32Array( ec.length * dimension );
  var counter = 0;
  var idx;
  for( var i = 0; i < ec.length; ++i ) {

    let idx = ec[ i ] * dimension;
    binaryLocation[ counter ] = polygon[ idx ];
    binaryLocation[ counter + 1 ] = polygon[ idx + 1 ];
    counter = counter + dimension;
  }

  const stride = 2;
  const n_points = binaryLocation.length / stride;
  const n_points_per_tri = 3;
  const len = n_points / n_points_per_tri;

  const binaryStartIndices = new Uint16Array([0]);

  const binaryIndices = new Uint16Array( binaryLocation.length );
  for( var i = 0; i < ( binaryLocation.length ); i++ ) {
    binaryIndices[i] = i;
  }

  const polygonLayer = new deck.SolidPolygonLayer({
    id: 'polygon',
    wireframe: true,
    extruded: true,

    data: {
      length: len,
      startIndices: binaryStartIndices,
      attributes: {
        indices: binaryIndices,
        getPolygon: {value: binaryLocation, size: stride}
      }
    },
    _normalize: false, // skip normalization for ear-cut polygons
    getFillColor: [255,0,0,100],
    pickable: true,
    autoHighlight: true
  });

  deckgl.setProps({layers: [polygonLayer] });
ibgreen commented 4 years ago

I am not fully up-to-date so I could be wrong, but I am not sure that the polygonlayer supports pre-tesselated geometries (i.e. the application calling earcut).

Pessimistress commented 4 years ago
  const polygonLayer = new deck.SolidPolygonLayer({
    id: 'polygon',
    wireframe: true,
    extruded: true,

    data: {
      length: 2,  // number of polygons
      startIndices: [0, 6], // vertex count at the start of each polygon
      attributes: {
        indices: new Uint16Array(ec), // result from earcut
        getPolygon: {value: new Float32Array(polygon), size: dimension} // positions tracing the bounds of each polygon
      }
    },
    _normalize: false, // skip normalization for ear-cut polygons
    getFillColor: [255,0,0,100],
    getElevation: 3000,
    pickable: true,
    autoHighlight: true
  });

updated jsfiddle

Picking does not work quite as expected here because there is no mechanism to define "polygon with holes" in binary. You can work around this by providing the pickingColors attribute yourself (it is calculated by stacking the results of layer.encodePickingColor(index)).

dcooley commented 4 years ago

Thanks - this is really helpful.