java - 如何将小立方体装入给定体积并在网页上以图形方式表示?

标签 java graphics 3d linear-algebra

我的查询是以编程方式显示,在一个较大体积的立方体(例如存储单元)内,许多给定的、大小各不相同的非规则(但矩形)立方体(即盒子)的拟合。

理解了数学部分。就像在线性规划/线性代数中一样,我们可以添加所有较小立方体的拟合体积,以找出最适合较大立方体体积的体积。 实际要求是在网页上以图形方式显示或允许此拟合,最好是 3d。如果可能,允许用户与配件进行交互,即洗牌立方体的放置等。

此外,由于我的专业是 Java 开发人员,因此我会选择 Java 或相关语言/框架。但是,如果满足最终结果,我可以使用任何其他技术/框架/语言。

注意:重量也是一个问题(参数)。在任何给定的存储单元中都可以堆叠最大重量。 此外,由于可以在未经许可的情况下(窃贼)访问存储单元,因此堆叠在一个单元中的立方体的成本也受到限制。用户可能希望在具有更高安全性的一个单元中安装成本更高的立方体,反之亦然。

示例:允许在给定房间内安装许多包含家用电子产品的矩形盒子。盒子可能是电视、冰箱、洗衣机、洗碗机、游戏机、xbox 360s 等。这些盒子的不同尺寸,是为了让您了解在适应有限体积的同时可以期待什么。

如果有任何 FOSS 库/项目(甚至非 FOSS 库或项目),欢迎提供指向它的指针。

最佳答案

免责声明: 好的,我知道它不能 100% 回答您的问题,而且它的代码也很旧(可以从老式的 CVS 注释中得出结论) 今天我不会再那样写了。不过,它仍然可以在 Java 8 上运行,我对其进行了测试。但是除了解决水从上到下流过长方体的 3D 矩阵的小信息学挑战问题之外,这取决于矩阵的“泄漏”程度(象征某种瑞士奶酪),它还通过 Java 使用一些非常简单的 3D 可视化3D。因此,您需要安装 Java 3D 并将相应的库放入您的类路径中。

3D 输出看起来像这样:

Leaky cheese

package vhs.bwinfo.cheese;

// $Id: Cuboid.java,v 1.1.2.1 2006/01/10 19:48:41 Robin Exp $

import javax.media.j3d.Appearance;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.vecmath.Point3f;
import javax.vecmath.TexCoord2f;
import javax.vecmath.Vector3f;

public class Cuboid extends Shape3D {
  private static final float POS = +0.5f;
  private static final float NEG = -0.5f;
  private static final Point3f[] POINTS = new Point3f[] {
    new Point3f(NEG, NEG, NEG),
    new Point3f(POS, NEG, NEG),
    new Point3f(POS, NEG, POS),
    new Point3f(NEG, NEG, POS),
    new Point3f(NEG, POS, NEG),
    new Point3f(POS, POS, NEG),
    new Point3f(POS, POS, POS),
    new Point3f(NEG, POS, POS)
  };
  private static final TexCoord2f[] TEX_COORDS = new TexCoord2f[] {
    new TexCoord2f(0, 1),
    new TexCoord2f(1, 1),
    new TexCoord2f(1, 0),
    new TexCoord2f(0, 0)
  };
  private static final int VERTEX_FORMAT =
    QuadArray.COORDINATES |
      QuadArray.NORMALS |
      QuadArray.TEXTURE_COORDINATE_2;

  public Cuboid(float scaleX, float scaleY, float scaleZ) {
    Point3f[] points = new Point3f[8];
    for (int i = 0; i < 8; i++)
      points[i] = new Point3f(
        POINTS[i].x * scaleX,
        POINTS[i].y * scaleY,
        POINTS[i].z * scaleZ
      );

    Point3f[] vertices = {
      points[3], points[2], points[1], points[0],  // bottom
      points[4], points[5], points[6], points[7],  // top
      points[7], points[3], points[0], points[4],  // left
      points[6], points[5], points[1], points[2],  // right
      points[7], points[6], points[2], points[3],  // front
      points[5], points[4], points[0], points[1]    // back
    };

    QuadArray geometry = new QuadArray(24, VERTEX_FORMAT);
    geometry.setCoordinates(0, vertices);
    for (int i = 0; i < 24; i++)
      geometry.setTextureCoordinate(0, i, TEX_COORDS[i % 4]);

    Vector3f normal = new Vector3f();
    Vector3f v1 = new Vector3f();
    Vector3f v2 = new Vector3f();
    Point3f[] pts = new Point3f[4];
    for (int i = 0; i < 4; i++)
      pts[i] = new Point3f();

    for (int face = 0; face < 6; face++) {
      geometry.getCoordinates(face * 4, pts);
      v1.sub(pts[0], pts[2]);
      v2.sub(pts[1], pts[3]);
      normal.cross(v1, v2);
      normal.normalize();
      for (int i = 0; i < 4; i++)
        geometry.setNormal((face * 4 + i), normal);
    }
    setGeometry(geometry);
    setAppearance(new Appearance());
  }

  public Cuboid(float scaleFactor) {
    this(scaleFactor, scaleFactor, scaleFactor);
  }
}
package vhs.bwinfo.cheese;

// $Id: LeakyCheese.java,v 1.2.2.2 2006/01/10 15:37:14 Robin Exp $

import com.sun.j3d.utils.applet.JMainFrame;

import javax.swing.*;
import java.util.Random;

import static java.lang.System.out;

public class LeakyCheese {
  private int width = 20, height = 20, depth = 20;
  private int numClasses = 100, samplesPerClass = 100;
  private double pMin = 0, pMax = 1;
  private double pDiff = pMax - pMin;
  private double classSize = pDiff / numClasses;
  private int[] stats;

  enum CubeState {CHEESE, AIR, WATER}

  final private CubeState[][][] cheese;

  private static final Random RND = new Random();

  public LeakyCheese(
    int width, int height, int depth,
    int numClasses, int samplesPerClass,
    double pMin, double pMax
  ) {
    this.width = width;
    this.height = height;
    this.depth = depth;
    this.numClasses = numClasses;
    this.samplesPerClass = samplesPerClass;
    this.pMin = pMin;
    this.pMax = pMax;
    pDiff = pMax - pMin;
    classSize = pDiff / numClasses;
    cheese = new CubeState[width][height][depth];
  }

  public LeakyCheese(
    int width, int height, int depth,
    int numClasses, int samplesPerClass
  ) {
    this(width, height, depth, numClasses, samplesPerClass, 0, 1);
  }

  public LeakyCheese() {
    cheese = new CubeState[width][height][depth];
  }

  private boolean pourWater(int x, int y, int z) {
    if (x < 0 || x >= width || y < 0 || y >= height || z < 0 || z >= depth)
      return false;
    if (cheese[x][y][z] != CubeState.AIR)
      return false;
    cheese[x][y][z] = CubeState.WATER;
    boolean retVal = (y == 0);
    retVal = pourWater(x + 1, y, z) || retVal;
    retVal = pourWater(x - 1, y, z) || retVal;
    retVal = pourWater(x, y + 1, z) || retVal;
    retVal = pourWater(x, y - 1, z) || retVal;
    retVal = pourWater(x, y, z + 1) || retVal;
    retVal = pourWater(x, y, z - 1) || retVal;
    return retVal;
  }

  private boolean isLeaky(double p) {
    for (int x = 0; x < width; x++)
      for (int y = 0; y < height; y++)
        for (int z = 0; z < depth; z++)
          cheese[x][y][z] = (RND.nextDouble() < p)
            ? CubeState.CHEESE
            : CubeState.AIR;
    boolean retVal = false;
    for (int x = 0; x < width; x++)
      for (int z = 0; z < depth; z++)
        retVal = pourWater(x, height - 1, z) || retVal;
    return retVal;
  }

  private void generateStats() {
    if (stats != null)
      return;
    stats = new int[numClasses];
    for (int i = 0; i < numClasses; i++) {
      for (int j = 0; j < samplesPerClass; j++) {
        double p = pMin + classSize * (RND.nextDouble() + i);
        if (isLeaky(p))
          stats[i]++;
      }
    }
  }

  public void printStats() {
    generateStats();
    out.println(
      "p (cheese)        |  p (leaky)\n" +
        "------------------+-----------"
    );
    for (int i = 0; i < numClasses; i++) {
      out.println(
        String.format(
          "%1.5f..%1.5f  |  %1.5f",
          pMin + classSize * i,
          pMin + classSize * (i + 1),
          (double) stats[i] / samplesPerClass
        )
      );
    }
  }

  public static void main(String[] args) {
    //new LeakyCheese().printStats();
    //new LeakyCheese(40, 40, 40, 50, 100, 0.66, .71).printStats();

    LeakyCheese cheeseBlock = new LeakyCheese(5, 20, 5, 20, 100);
    //LeakyCheese cheeseBlock = new LeakyCheese(20, 20, 20, 20, 100);
    while (!cheeseBlock.isLeaky(0.65))
      ;
    out.println("*** required solution found - now rendering... ***");
    JMainFrame f = new JMainFrame(new LeakyCheeseGUI(cheeseBlock.cheese), 512, 512);
    f.setLocationRelativeTo(null);
    f.setExtendedState(JFrame.MAXIMIZED_BOTH);
  }
}
package vhs.bwinfo.cheese;

// $Id: LeakyCheeseGUI.java,v 1.1.2.1 2006/01/10 15:25:18 Robin Exp $

import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.SimpleUniverse;
import vhs.bwinfo.cheese.LeakyCheese.CubeState;

import javax.media.j3d.*;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;
import java.applet.Applet;
import java.awt.*;
import java.util.Random;

public class LeakyCheeseGUI extends Applet {
  static final long serialVersionUID = -8194627556699837928L;

  public BranchGroup createSceneGraph(CubeState[][][] cheese) {
    // Create the root of the branch graph
    BranchGroup bgRoot = new BranchGroup();

    // Composite of two rotations around different axes. The resulting
    // TransformGroup is the parent of all our cheese cubes, because their
    // orientation is identical. They only differ in their translation
    // values and colours.
    Transform3D tRotate = new Transform3D();
    Transform3D tRotateTemp = new Transform3D();
    tRotate.rotX(Math.PI / 8.0d);
    tRotateTemp.rotY(Math.PI / -4.0d);
    tRotate.mul(tRotateTemp);
    TransformGroup tgRotate = new TransformGroup(tRotate);
    bgRoot.addChild(tgRotate);

    // Bounding sphere for rendering
    BoundingSphere bounds =
      new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

    // Set background colour
    // Note: Using Canvas3D.setBackground does not work, because it is an
    // AWT method. Java 3D, though, gets its background colour from its
    // background node (black, if not present).
    Background background = new Background(0.5f, 0.5f, 0.5f);
    background.setApplicationBounds(bounds);
    bgRoot.addChild(background);

    TransparencyAttributes transpAttr;

    // Little cheese cubes
    Appearance cheeseAppearance = new Appearance();
    transpAttr =
      new TransparencyAttributes(TransparencyAttributes.NICEST, 0.98f);
    cheeseAppearance.setTransparencyAttributes(transpAttr);
    cheeseAppearance.setColoringAttributes(
      new ColoringAttributes(1, 1, 0, ColoringAttributes.NICEST));
    PolygonAttributes pa = new PolygonAttributes();
    //pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
    pa.setCullFace(PolygonAttributes.CULL_NONE);
    cheeseAppearance.setPolygonAttributes(pa);

    // Little water cubes
    Appearance waterAppearance = new Appearance();
    transpAttr =
      new TransparencyAttributes(TransparencyAttributes.NICEST, 0.85f);
    waterAppearance.setTransparencyAttributes(transpAttr);
    waterAppearance.setColoringAttributes(
      new ColoringAttributes(0, 0, 1, ColoringAttributes.NICEST));
    pa = new PolygonAttributes();
    pa.setCullFace(PolygonAttributes.CULL_NONE);
    waterAppearance.setPolygonAttributes(pa);

    // Little air cubes
    Appearance airAppearance = new Appearance();
    transpAttr =
      new TransparencyAttributes(TransparencyAttributes.NICEST, 0.95f);
    airAppearance.setTransparencyAttributes(transpAttr);
    airAppearance.setColoringAttributes(
      new ColoringAttributes(1, 1, 1, ColoringAttributes.NICEST));
    pa = new PolygonAttributes();
    //pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
    pa.setCullFace(PolygonAttributes.CULL_NONE);
    airAppearance.setPolygonAttributes(pa);

    // Water-coloured (i.e. blue) wire frame around cheese block, if leaky
    Appearance waterWireFrameAppearance = new Appearance();
    waterWireFrameAppearance.setColoringAttributes(
      new ColoringAttributes(0, 0, 1, ColoringAttributes.NICEST));
    pa = new PolygonAttributes();
    pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
    pa.setCullFace(PolygonAttributes.CULL_NONE);
    waterWireFrameAppearance.setPolygonAttributes(pa);

    // Cheese-coloured (i.e. yellow) wire frame around cheese block, if not leaky
    Appearance cheeseWireFrameAppearance = new Appearance();
    cheeseWireFrameAppearance.setColoringAttributes(
      new ColoringAttributes(1, 1, 0, ColoringAttributes.NICEST));
    pa = new PolygonAttributes();
    pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
    pa.setCullFace(PolygonAttributes.CULL_NONE);
    cheeseWireFrameAppearance.setPolygonAttributes(pa);

    // Absolute offsets for the cheese block to fit into the viewing canvas
    final float xOffs = -0.8f;
    final float yOffs = -0.55f;
    final float zOffs = 0;

    // Create all those little cubes ;-)
    final int xSize = cheese.length;
    final int ySize = cheese[0].length;
    final int zSize = cheese[0][0].length;
    final int maxSize = Math.max(xSize, Math.max(ySize, zSize));
    final float xCenterOffs = 0.5f * (maxSize - xSize) / maxSize;
    final float yCenterOffs = 0.5f * (maxSize - ySize) / maxSize;
    final float zCenterOffs = -0.5f * (maxSize - zSize) / maxSize;
    boolean isLeaky = false;
    for (int x = 0; x < xSize; x++)
      for (int y = 0; y < ySize; y++)
        for (int z = 0; z < zSize; z++) {
          Transform3D tTranslate = new Transform3D();
          tTranslate.setTranslation(
            new Vector3f(
              xOffs + xCenterOffs + 1.0f * x / maxSize,
              yOffs + yCenterOffs + 1.0f * y / maxSize,
              zOffs + zCenterOffs - 1.0f * z / maxSize
            )
          );
          TransformGroup tgTranslate = new TransformGroup(tTranslate);
          tgRotate.addChild(tgTranslate);
          Cuboid cube = new Cuboid(1.0f / maxSize);
          switch (cheese[x][y][z]) {
            case CHEESE:
              cube.setAppearance(cheeseAppearance);
              break;
            case WATER:
              cube.setAppearance(waterAppearance);
              if (y == 0)
                isLeaky = true;
              break;
            case AIR:
              cube.setAppearance(airAppearance);
          }
          tgTranslate.addChild(cube);
        }

    // If cheese block is leaky, visualise it by drawing a water-coloured
    // (i.e. blue) wire frame around it. Otherwise use a cheese-coloured
    // (i.e. yellow) one.
    Transform3D tTranslate = new Transform3D();
    tTranslate.setTranslation(
      new Vector3f(
        xOffs + xCenterOffs + 0.5f * (xSize - 1) / maxSize,
        yOffs + yCenterOffs + 0.5f * (ySize - 1) / maxSize,
        zOffs + zCenterOffs - 0.5f * (zSize - 1) / maxSize
      )
    );
    TransformGroup tgTranslate = new TransformGroup(tTranslate);
    tgRotate.addChild(tgTranslate);
    Cuboid cuboid = new Cuboid(
      1.0f * xSize / maxSize,
      1.0f * ySize / maxSize,
      1.0f * zSize / maxSize
    );
    cuboid.setAppearance(isLeaky ? waterWireFrameAppearance : cheeseWireFrameAppearance);
    tgTranslate.addChild(cuboid);

    // Let Java 3D perform optimizations on this scene graph.
    bgRoot.compile();

    return bgRoot;
  }

  public LeakyCheeseGUI(CubeState[][][] cheese) {
    // Create a simple scene and attach it to the virtual universe
    GraphicsConfiguration graphCfg = SimpleUniverse.getPreferredConfiguration();
    Canvas3D canvas = new Canvas3D(graphCfg);
    setLayout(new BorderLayout());
    add(canvas, "Center");
    SimpleUniverse universe = new SimpleUniverse(canvas);

    // This will move the ViewPlatform back a bit so the objects
    // in the scene can be viewed.
    universe.getViewingPlatform().setNominalViewingTransform();
    universe.addBranchGraph(createSceneGraph(cheese));
  }

  public static void main(String[] args) {
    final Random RND = new Random(System.currentTimeMillis());
    CubeState[][][] testCheese = new CubeState[5][8][11];
    for (int x = 0; x < 5; x++)
      for (int y = 0; y < 8; y++)
        for (int z = 0; z < 11; z++)
          testCheese[x][y][z] = (RND.nextFloat() < 0.7f)
            ? CubeState.CHEESE
            : (RND.nextBoolean() ? CubeState.WATER : CubeState.AIR);
    // Applet can also run as a stand-alone application
    new MainFrame(new LeakyCheeseGUI(testCheese), 512, 512);
  }
}

关于java - 如何将小立方体装入给定体积并在网页上以图形方式表示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43118970/

相关文章:

java - Java 上的 plot/drow 分形图形库

c++ - 图形 API 的包装器

c++ - 如何将 opengl 3.3 渲染成 SDL2 纹理?

java - JSP 表单标签操作属性 URI Spring MVC

java - 静态类的表达式开始非法?

java - 如何从 2 个不同的父项创建 Maven 模块?

.net - 使用 .NET 的跨平台图形 3D

java - IntelliJ iDEA 中有快速搜索工具吗?

java - 更新 JPanel 图形时出现无限循环

c++ - OpenGL 茶壶绘图错误