All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ai.dragonfly.mesh.shape.Cube.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2023 dragonfly.ai
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ai.dragonfly.mesh.shape

import narr.*
import scala.language.implicitConversions

import slash.vector.*
import Vec.*

import slash.{cubeInPlace, squareInPlace}
import ai.dragonfly.mesh.*

import scala.scalajs.js.annotation.JSExportTopLevel

object Cube {

  def corners(l: Double): NArray[NArray[Vec[3]]] = {
    val out: NArray[NArray[Vec[3]]] = NArray.ofSize[NArray[NArray[Double]]](2).asInstanceOf[NArray[NArray[Vec[3]]]]
    out(0) = NArray[Vec[3]](Vec[3](0, 0, 0), Vec[3](l, 0, 0), Vec[3](l, l, 0), Vec[3](0, l, 0))
    out(1) = NArray[Vec[3]](Vec[3](0, 0, l), Vec[3](l, 0, l), Vec[3](l, l, l), Vec[3](0, l, l))
    out
  }

  def cubePoints(l: Double, n: Int): NArray[Vec[3]] = {

    val basis = corners(l)

    val panelStride: Int = n - 1

    val stride: Int = 4 * panelStride
    val capStride: Int = panelStride - 1
    val capPointCount: Int = squareInPlace(capStride)

    val pointCount: Int = cubeInPlace(n) - cubeInPlace(capStride)
    val sidePointCount = pointCount - (2 * capPointCount) // points.length - (2 * capPointCount)

    val points: NArray[Vec[3]] = NArray.ofSize[Vec[3]](pointCount) // (cubeInPlace(n) - cubeInPlace(n - 2))  // n³ - (n-2)³

    for (p <- 0 until sidePointCount) {
      val i: Int = (p % stride) / panelStride

      val bottomLeft: Vec[3] = basis(0)(i)
      val bottomRight: Vec[3] = basis(0)((i + 1) % 4)

      val z: Double = l * ((p / stride) / panelStride.toDouble)
      val alpha: Double = (p % panelStride) / panelStride.toDouble

      points(p) = Vec[3](0.0, 0.0, z) + (bottomLeft * (1.0 - alpha)) + (bottomRight * alpha)
    }

    val Δl: Double = l / (n - 1)
    var p: Int = sidePointCount

    for (yi <- 1 to capStride) {
      for (xi <- 1 to capStride) {
        val x: Double = xi * Δl
        val y: Double = yi * Δl
        points(p) = Vec[3](x, y, 0)
        points(p + capPointCount) = Vec[3](x, y, l)
        p += 1
      }
    }

    points
  }

  /**
   * Create a cube with minimal number of triangles and points.
   * @param l the length of the cube.
   * @return a triangular cube mesh.
   */
  @JSExportTopLevel("MinimalCube")
  def minimal(l:Double = 1.0): Mesh = apply(l, 2) // apply(l, 1)

  @JSExportTopLevel("Cube")
  def apply(l: Double = 1.0, n: Int = 64, name:String = "Cube"): Mesh = {

    if (n < 2) throw IllegalArgumentException("A cube must have at least two points on each line segment.")

    val points: NArray[Vec[3]] = cubePoints(l, n)

    val panelStride: Int = n - 1

    val stride: Int = 4 * panelStride

    val triangles: NArray[Triangle] = new NArray[Triangle](12 * squareInPlace(n - 1)) // 12(n-1)²

    var t: Int = 0

    // [front)[right)[back)[left)
    val sidePointCount = cubeInPlace(n) - cubeInPlace(n - 2) - (2 * squareInPlace(n - 2))

    for (p <- 1 until sidePointCount - stride + 1) {

      t = if (p % stride == 0) addQuad(p, p - 1, p - stride, p - 1 + stride, triangles, t)
      else addQuad(p + stride, p - 1, p, p + stride - 1, triangles, t)

    }

    // caps

    val capStride: Int = panelStride - 1
    val capPointCount: Int = squareInPlace(capStride)

    for (p <- capStride + 1 until capPointCount) {
      val pi: Int = sidePointCount + p

      if (p % capStride != 0) {
        // (bottom)
        t = addQuad(pi - capStride, pi - 1, pi, pi - capStride - 1, triangles, t)

        // (top)
        val pj: Int = sidePointCount + p + capPointCount
        t = addQuad(pj - 1, pj - capStride, pj, pj - capStride - 1, triangles, t)
      }
    }

    // Connect Caps to Sides:

    if (n > 2) {
      // Bottom Corners:
      // Left Front Bottom
      t = addQuad(stride - 1, 1, 0, sidePointCount, triangles, t)

      // Front Bottom Right
      t = addQuad(panelStride, sidePointCount + capStride - 1, panelStride + 1, panelStride - 1, triangles, t)

      // Bottom Right Back
      var temp: Int = 2 * panelStride
      t = addQuad(temp + 1, temp - 1, sidePointCount + capPointCount - 1, temp, triangles, t)

      // Bottom Back Left
      temp = 3 * panelStride
      t = addQuad(sidePointCount + capPointCount - capStride, temp, temp - 1, temp + 1, triangles, t)

      // Top Corners:
      val topOffset: Int = sidePointCount + capPointCount

      // Left Front Top
      temp = sidePointCount - stride
      t = addQuad(temp + 1, sidePointCount - 1, temp, topOffset, triangles, t)

      // Front Top Right
      temp = sidePointCount - stride + panelStride
      t = addQuad(temp, topOffset + capStride - 1, temp - 1, temp + 1, triangles, t)

      // Top Right Back
      temp = sidePointCount - 2 * panelStride
      t = addQuad(temp - 1, temp + 1, points.length - 1, temp, triangles, t)

      // Top Back Left
      temp = sidePointCount - panelStride
      t = addQuad(temp, points.length - capStride, temp - 1, temp + 1, triangles, t)

      // connect sides to caps
      for (i <- 0 until capStride - 1) {
        // Bottom Front
        temp = sidePointCount + i
        t = addQuad(temp, i + 2, i + 1, temp + 1, triangles, t)

        // Bottom Right
        val ibr: Int = sidePointCount + capStride - 1 + (capStride * i)
        temp = panelStride + i + 1
        t = addQuad(ibr + capStride, temp, ibr, temp + 1, triangles, t)

        // Bottom Back
        val ibb: Int = 3 * panelStride - i - 1
        temp = sidePointCount + capPointCount - capStride + i
        t = addQuad(ibb, temp + 1, temp, ibb - 1, triangles, t)

        // Bottom Left
        val ibl: Int = stride - i - 1
        temp = sidePointCount + (i * capStride)
        t = addQuad(ibl - 1, temp, ibl, temp + capStride, triangles, t)

        // Top Front
        val itf: Int = sidePointCount - stride + 1 + i
        temp = topOffset + i
        t = addQuad(temp, itf + 1, temp + 1, itf, triangles, t)

        // Top Right
        val itr: Int = sidePointCount - stride + panelStride + i + 1
        temp = topOffset + (i + 1) * capStride
        t = addQuad(itr, temp + capStride - 1, temp - 1, itr + 1, triangles, t)

        // Top Back
        val itb: Int = sidePointCount - panelStride - 1 - i
        temp = points.length - capStride + i
        t = addQuad(itb, temp + 1, itb - 1, temp, triangles, t)

        // Top Left
        val itl: Int = sidePointCount - i - 1
        temp = topOffset + i * capStride
        t = addQuad(temp, itl - 1, itl, temp + capStride, triangles, t)
      }
    } else {
      t = addQuad(0, 2, 1, 3, triangles, t)
      t = addQuad(7, 5, 6, 4, triangles, t)
    }

    new Mesh(points, triangles, name)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy