|                                                                                                                                                                                                                                                                                                       | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 | /**
 * @Author: Caven
 * @Date: 2022-05-28 10:25:24
 */
import { Cesium } from '../../../namespace'
import Overlay from '../Overlay'
import State from '../../state/State'
import Parse from '../../parse/Parse'
import { Transform } from '../../transform'
import { Util } from '../../utils'
import { IMG_PARTICLES } from '../../images/base64'
const DEF_STYLE = {
  color: Cesium.Color.ORANGE,
}
class LightCylinderPrimitive extends Overlay {
  constructor(center, length, topRadius, bottomRadius) {
    super()
    this._center = Parse.parsePosition(center)
    this._length = length || 100
    this._topRadius = topRadius || 1
    this._bottomRadius = bottomRadius || 50
    this._delegate = new Cesium.PrimitiveCollection()
    this._style = { ...DEF_STYLE }
    this._state = State.INITIALIZED
  }
  get type() {
    return Overlay.getOverlayType('light-cylinder-primitive')
  }
  set center(position) {
    this._center = Parse.parsePosition(position)
    this._updatePrimitives()
  }
  get center() {
    return this._center
  }
  set length(length) {
    this._length = length
    this._updatePrimitives()
  }
  get length() {
    return this._length
  }
  set topRadius(topRadius) {
    this._topRadius = topRadius
    this._updatePrimitives()
  }
  get topRadius() {
    return this._topRadius
  }
  set bottomRadius(bottomRadius) {
    this._bottomRadius = bottomRadius
    this._updatePrimitives()
  }
  get bottomRadius() {
    return this._bottomRadius
  }
  /**
   *
   * @param center
   * @param radius
   * @return {[]}
   * @private
   */
  _computeEllipsePositions(center, radius) {
    let cep = Cesium.EllipseGeometryLibrary.computeEllipsePositions(
      {
        center: Transform.transformWGS84ToCartesian(center),
        semiMajorAxis: radius,
        semiMinorAxis: radius,
        rotation: 0,
        granularity: 0.005,
      },
      false,
      true
    )
    let pnts = Cesium.Cartesian3.unpackArray(cep.outerPositions)
    pnts.push(pnts[0])
    return pnts
  }
  /**
   *
   * @param topPts
   * @param bottomPts
   * @param height
   * @return {Cesium.GeometryInstance}
   */
  _createCylinderInstance(topPts, bottomPts, height) {
    let newpts = bottomPts.slice()
    let length = bottomPts.length
    let len_2 = 2 * length
    let sts = []
    let st_interval = 1.0 / (length - 1)
    let define_indices = []
    let ep = []
    const addHeight = (p, alt = 0) => {
      let c = Cesium.Cartographic.fromCartesian(p)
      c.height += alt
      return Cesium.Cartographic.toCartesian(c)
    }
    for (let i = 0; i < length; i++) {
      ep.push(addHeight(topPts[i], height))
      sts.push(i * st_interval, 0)
      let i_1 = i + 1
      let i_11 = (i + 1) % length
      let len_2_i_1 = len_2 - i_1
      define_indices.push(...[len_2_i_1 - 1, len_2_i_1, i])
      define_indices.push(...[i, i_11, len_2_i_1 - 1])
    }
    for (let i in ep) {
      newpts.push(ep[length - i - 1])
      sts.push(1 - i * st_interval, 1)
    }
    let polygon = Cesium.PolygonGeometry.createGeometry(
      new Cesium.PolygonGeometry({
        polygonHierarchy: new Cesium.PolygonHierarchy(newpts),
        perPositionHeight: true,
      })
    )
    polygon.indices = define_indices
    polygon.attributes.st.values = sts
    return new Cesium.GeometryInstance({
      geometry: polygon,
    })
  }
  /**
   *
   * @return {HTMLCanvasElement}
   * @private
   */
  _getCircleImage() {
    let canvas = document.createElement('canvas')
    canvas.width = 512
    canvas.height = 512
    let ctx = canvas.getContext('2d')
    ctx.fillStyle = 'rgba(255,255,255,0)'
    ctx.strokeStyle = 'rgba(255, 255, 255,255)'
    ctx.setLineDash([50, 50])
    ctx.lineWidth = 30
    ctx.beginPath()
    ctx.arc(256, 256, 150, 0, Math.PI * 2, true)
    ctx.stroke()
    ctx.restore()
    return canvas
  }
  /**
   *
   * @param image
   * @return {HTMLCanvasElement}
   * @private
   */
  _getParticlesImage(image) {
    let canvas = document.createElement('canvas')
    canvas.width = 64
    canvas.height = 256
    let ctx = canvas.getContext('2d')
    ctx.clearRect(0, 0, 64, 256)
    ctx.drawImage(image, 0, 0)
    ctx.drawImage(image, 33, 0)
    return canvas
  }
  /**
   *
   * @private
   */
  _updatePrimitives() {
    this._delegate.removeAll()
    const isGroud = this._center.alt === 0
    let topPositions = this._computeEllipsePositions(
      this._center,
      this._topRadius
    )
    let innerBottomPostions = this._computeEllipsePositions(
      this._center,
      this._bottomRadius * 0.7
    )
    let bottomPositions = this._computeEllipsePositions(
      this._center,
      this._bottomRadius
    )
    // update buttom circle
    const circleOpt = {
      geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.PolygonGeometry({
          polygonHierarchy: new Cesium.PolygonHierarchy(
            this._computeEllipsePositions(this._center, this._bottomRadius * 2)
          ),
          perPositionHeight: !isGroud,
        }),
        asynchronous: false,
      }),
    }
    // ring
    let ring = isGroud
      ? new Cesium.GroundPrimitive(circleOpt)
      : new Cesium.Primitive(circleOpt)
    ring.appearance = new Cesium.EllipsoidSurfaceAppearance({
      material: Cesium.Material.fromType(Cesium.Material.CircleRingType, {
        color: this._style.color,
      }),
    })
    // circle
    let circle = isGroud
      ? new Cesium.GroundPrimitive(circleOpt)
      : new Cesium.Primitive(circleOpt)
    circle.appearance = new Cesium.EllipsoidSurfaceAppearance({
      material: Cesium.Material.fromType(Cesium.Material.CircleRotateType, {
        color: this._style.color,
        image: this._getCircleImage(),
      }),
    })
    // cylinder
    let cylinder = new Cesium.Primitive({
      geometryInstances: this._createCylinderInstance(
        topPositions,
        innerBottomPostions,
        this._length
      ),
      appearance: new Cesium.EllipsoidSurfaceAppearance({
        material: Cesium.Material.fromType(Cesium.Material.CylinderFadeType, {
          color: this._style.color,
        }),
      }),
      asynchronous: false,
    })
    if (isGroud) {
      Cesium.GroundPrimitive.initializeTerrainHeights().then(() => {
        this._delegate.add(ring)
        this._delegate.add(circle)
        this._delegate.add(cylinder)
      })
    } else {
      this._delegate.add(ring)
      this._delegate.add(circle)
      this._delegate.add(cylinder)
    }
    // particles
    Cesium.Resource.fetchImage({ url: IMG_PARTICLES }).then((image) => {
      let particles = new Cesium.Primitive({
        geometryInstances: this._createCylinderInstance(
          topPositions,
          bottomPositions,
          this._length
        ),
        appearance: new Cesium.EllipsoidSurfaceAppearance({
          material: Cesium.Material.fromType(
            Cesium.Material.CylinderParticlesType,
            {
              color: this._style.color,
              image: this._getParticlesImage(image),
            }
          ),
        }),
        asynchronous: false,
      })
      this._delegate.add(particles)
    })
  }
  /**
   *
   * @private
   */
  _mountedHook() {
    /**
     *  set the positions
     */
    this.center = this._center
  }
  /**
   *
   * @param frameState
   */
  update(frameState) {
    this._delegate.update(frameState)
  }
  /**
   *
   * @param style
   * @returns {LightCylinderPrimitive}
   */
  setStyle(style) {
    if (!style || Object.keys(style).length === 0) {
      return this
    }
    Util.merge(this._style, style)
    return this
  }
  destroy() {
    return Cesium.destroyObject(this)
  }
}
Overlay.registerType('light-cylinder-primitive')
export default LightCylinderPrimitive
 |