rikudou/php-scad

OpenSCAD 的 PHP 封装

dev-master 2024-06-12 20:59 UTC

This package is auto-updated.

Last update: 2024-09-12 21:31:17 UTC


README

曾经想过在 PHP 中创建 3D 模型吗?当然,每个人都会。现在你可以做到了。

它是如何工作的?

它生成 OpenSCAD 代码,该代码再生成 STL 3D 模型。OpenScad基本上是这样的

translate([10, 0, 0]) // translate([x, y, z]) - moves the model by given coordinates
cube([10, 10, 10]); // cube([width, depth, height])

difference() { // only the difference between child objects is rendered
    cube([5, 10, 15]);
    cylinder(10, 5, 5); // cylinder(height, bottomRadius, topRadius)
}

这是生成的 3D 模型预览

OpenSCAD example

那么为什么还要使用 PHP 来创建模型,如果你已经可以使用代码创建模型呢?

  1. PHP 是一种功能齐全的通用语言,在互联网上有大量的文档
  2. 有功能齐全的 IDE,可以帮助代码补全
  3. PHP 支持更多范式,如面向对象编程
  4. 更合理的参数名称 - 例如,cylinder 签名是 cylinder(h, r, r1, r2, d, d1, d2, center),与 PhpScad 版本相比 - new Cylinder(height, radius, bottomRadius, topRadius, diameter, bottomDiameter, topDiameter)
    • 请注意,在 OpenSCAD 和 PhpScad 版本中,许多参数都是可选的
  5. 创建了一个有趣的参数化形状?太棒了,通过 composer 分享它,因为 PHP 有一个包管理器!

那么上面的示例在 PHP 中看起来是什么样子呢?

<?php

use Rikudou\PhpScad\ScadModel;
use Rikudou\PhpScad\Shape\Cube;
use Rikudou\PhpScad\Combination\Difference;
use Rikudou\PhpScad\Shape\Cylinder;

$model = new ScadModel();

$model = $model
    ->withRenderable((new Cube(width: 10, depth: 10, height: 10))->movedRight(10)) // using named parameters
    ->withRenderable(new Difference(
        new Cube(5, 10, 15), // using positional parameters
        new Cylinder(height: 10, bottomRadius: 5, topRadius: 5),
    ));

$model->render(__DIR__ . '/output.scad');

你可以注意到预览看起来是一样的

Same example as above but in php

注意方便的方法 ->movedRight(),这是 PHP 中可能但在 OpenSCAD 中不可能的示例之一 - 更自然、更易于思考的流畅 API。

你还可以在 PhpScad 中走 OpenSCAD 的路线

<?php

use Rikudou\PhpScad\Transformation\Translate;
use Rikudou\PhpScad\Coordinate\XYZ;
use Rikudou\PhpScad\Shape\Cube;

new Translate(new XYZ(x: 10, y: 0, z: 0), new Cube(width: 10, depth: 10, height: 10));

Manual translate

安装

composer require rikudou/php-scad:dev-master

用法

形状

形状是所有模型的基础。PhpScad 提供了与 OpenSCAD 相同的基本形状,即

  • 立方体
  • 圆柱体
  • 多面体
  • 球体

PhpScad 提供的附加形状

  • 金字塔

基本上,所有形状都可以通过组合基本形状来创建。

立方体

创建一个立方体。

参数

  • number $width (默认: 0)
  • number $depth (默认: 0)
  • number $height (默认: 0)

渲染条件: 至少有一个参数非零或为引用类型。

示例:

<?php

use Rikudou\PhpScad\Shape\Cube;

$cube = new Cube(width: 10, depth: 10, height: 10);

Cube

圆柱体

创建一个圆柱体。

参数

  • number $height (默认: 0)
  • ?number $radius (默认: null)
  • ?number $bottomRadius (默认: null)
  • ?number $topRadius (默认: null)
  • ?number $diameter (默认: null)
  • ?number $bottomDiameter (默认: null)
  • ?number $topDiameter (默认: null)
  • bool $centerOnZ (默认: false) - 模型是否应位于 Z 轴中心
  • bool $centerOnXY (默认: true) - 模型是否应位于 X 和 Y 轴中心
  • ?FacetsConfiguration $facetsConfiguration (默认: null) - 线条配置,下面会详细介绍

你应该提供半径或直径,但不能同时提供两者。你还应该提供单个参数($radius$diameter)或一对顶/底参数($bottomRadius/$topRadius$bottomDiameter/$topDiameter)。

使用一对半径/直径可以让你创建一个圆锥体。

如果你提供了无效的半径组合,行为是未定义的,并取决于 OpenSCAD 的实现,因为 PhpScad 将使用你提供的所有参数生成形状。

渲染条件: 高度非零且至少提供一个半径参数。

示例:

<?php

use Rikudou\PhpScad\Shape\Cylinder;

// all these shapes are equivalent
$cylinderWithDiameter = new Cylinder(height: 10, diameter: 20);
$cylinderWithTopBottomDiameters = new Cylinder(height: 10, topDiameter: 20, bottomDiameter: 20);
$cylinderWithRadius = new Cylinder(height: 10, radius: 10);
$cylinderWithTopBottomRadii = new Cylinder(height: 10, topRadius: 10, bottomRadius: 10);

// cone
$cone = new Cylinder(height: 10, topRadius: 10, bottomRadius: 20);

// fully centered
$centered = new Cylinder(height: 10, diameter: 10, centerOnXY: true, centerOnZ: true);

Basic cylinder

Cylinder - cone

Cylinder - centered

多面体

最通用的形状之一,允许你使用点和面定义自己的形状。它可以用来创建任何规则或不规则的形状。

参数

  • array|PointVector $points (默认: empty PointVector) - 形状将包含的所有点
  • array|FaceVector $faces (默认: empty FaceVector) - 形状将包含的面的集合
  • int $convexity (默认:1) - 指定射线穿过物体时可能穿透的最大面数,仅在预览模式下使用

虽然数组和点以及面都可以使用,但为了更好的可读性,建议使用提供的 PointVectorFaceVector。当使用 FaceVector 时,您不需要手动填充 PointVector

创建多面体的基本有两种方法,我们可以称之为“OpenSCAD 方法”和“PhpScad 方法”。

多面体 - OpenSCAD 方法

我想不出为什么使用这种方法而不是另一种方法,但为了完整性,它得到了支持,您可以自由地跳过这部分文档。

首先需要定义一个点数组,您可以使用 PointVector 或数组

<?php

use Rikudou\PhpScad\Value\PointVector;
use Rikudou\PhpScad\Value\Point;

// five points to make a pyramid, in no particular order
$points = [
    [10, 15, 0],
    [10, 0, 0],
    [5, 7.5, 20],
    [0, 0, 0],
    [0, 15, 0],
];

// using PointVector
$points = new PointVector(
    new Point(x: 10, y: 15, z: 0),
    new Point(x: 10, y: 0, z: 0),
    new Point(x: 5, y: 7.5, z: 20),
    new Point(x: 0, y: 0, z: 0),
    new Point(x: 0, y: 15, z: 0),
);

然后需要在创建面时引用这些点,使用点数组中的点的索引

所有面在从外部观察每个面向内看时,都必须按 顺时针 方向排列点。

<?php

use Rikudou\PhpScad\Shape\Polyhedron;
use Rikudou\PhpScad\Value\PointVector;
use Rikudou\PhpScad\Value\Point;
use Rikudou\PhpScad\Value\FaceVector;
use Rikudou\PhpScad\Value\Face;

// from previous example, added manual indexes for clarity
$points = [
    0 => [10, 15, 0],
    1 => [10, 0, 0],
    2 => [5, 7.5, 20],
    3 => [0, 0, 0],
    4 => [0, 15, 0],
];

$faces = [
    new Face(0, 1, 2), // points with indexes 0, 1 and 2, meaning '[10, 15, 0]', '[10, 0, 0]' and '[5, 7.5, 20]'
    new Face(1, 3, 2), // points with indexes 1, 3 and 2, meaning '[10, 0, 0]', '[0, 0, 0]' and '[5, 7.5, 20]'
    new Face(3, 4, 2), // '[0, 0, 0]', '[0, 15, 0]' and '[5, 7.5, 20]'
    new Face(4, 0, 2), // '[0, 15, 0]', '[10, 15, 0]', '[5, 7.5, 20]'
    new Face(0, 4, 3, 1), // '[10, 15, 0]', '[0, 15, 0]', '[0, 0, 0]', '[10, 0, 0]'
];

$polyhedron = new Polyhedron(points: $points, faces: $faces);

// or the same example using provided DTOs
$points = new PointVector(
    new Point(x: 10, y: 15, z: 0),
    new Point(x: 10, y: 0, z: 0),
    new Point(x: 5, y: 7.5, z: 20),
    new Point(x: 0, y: 0, z: 0),
    new Point(x: 0, y: 15, z: 0),
);

$faces = new FaceVector(
    new Face(0, 1, 2),
    new Face(1, 3, 2),
    new Face(3, 4, 2),
    new Face(4, 0, 2),
    new Face(0, 4, 3, 1),
);

$polyhedron = new Polyhedron(points: $points, faces: $faces);

// unless I made some mistake, $polyhedron should now hold a proper pyramid

Polyhedron example

如你所见,这很难处理。

多面体 - PhpScad 方法

您不需要通过索引引用点来简化复杂的操作,您可以在创建面时直接创建点,点数组将内部定义

<?php

use Rikudou\PhpScad\Shape\Polyhedron;
use Rikudou\PhpScad\Value\Point;
use Rikudou\PhpScad\Value\Face;
use Rikudou\PhpScad\Value\FaceVector;

// this should be the same pyramid as above (unless I made a mistake)
$faces = new FaceVector(
    new Face(
        new Point(x: 10, y: 15, z: 0),
        new Point(x: 10, y: 0, z: 0),
        new Point(x: 5, y: 7.5, z: 20),
    ),
    new Face(
        new Point(x: 10, y: 0, z: 0),
        new Point(x: 0, y: 0, z: 0),
        new Point(x: 5, y: 7.5, z: 20),
    ),
    new Face(
        new Point(x: 0, y: 0, z: 0),
        new Point(x: 0, y: 15, z: 0),
        new Point(x: 5, y: 7.5, z: 20),
    ),
    new Face(
        new Point(x: 0, y: 15, z: 0),
        new Point(x: 10, y: 15, z: 0),
        new Point(x: 5, y: 7.5, z: 20),
    ),
    new Face(
        new Point(x: 10, y: 15, z: 0),
        new Point(x: 0, y: 15, z: 0),
        new Point(x: 0, y: 0, z: 0),
        new Point(x: 10, y: 0, z: 0),
    )
);

$polyhedron = new Polyhedron(faces: $faces);

// even more readable would be using good old variables like this:

$topPoint = new Point(x: 5, y: 7.5, z: 20);
$bottomLeft = new Point(x: 0, y: 0, z: 0);
$bottomRight = new Point(x: 10, y: 0, z: 0);
$topRight = new Point(x: 10, y: 15, z: 0);
$topLeft = new Point(x: 0, y: 15, z: 0);

// voila, perfectly readable
$polyhedron = new Polyhedron(faces: new FaceVector(
    new Face($topRight, $bottomRight, $topPoint),
    new Face($bottomRight, $bottomLeft, $topPoint),
    new Face($bottomLeft, $topLeft, $topPoint),
    new Face($topLeft, $topRight, $topPoint),
    new Face($topRight, $topLeft, $bottomLeft, $bottomRight),
));

Polyhedron example

球体

创建一个球体。

参数

  • ?number $radius (默认: null)
  • ?number $diameter (默认: null)
  • bool $center (默认:true) - 是否将球体中心对齐于 X、Y 和 Z 轴
  • ?FacetsConfiguration $facetsConfiguration (默认: null) - 线条配置,下面会详细介绍

如果同时指定了半径和直径,则半径优先。

渲染条件:当半径或直径非零或为引用类型时。

示例:

<?php

use Rikudou\PhpScad\Shape\Sphere;

$sphere = new Sphere(radius: 5);
$sphere = new Sphere(diameter: 10);
$sphere = new Sphere(radius: 5, center: false);

Sphere

Non-centered sphere

金字塔

创建一个金字塔。

参数

  • number $width
  • number $depth
  • number $height

渲染条件:始终

示例:

<?php

use Rikudou\PhpScad\Shape\Pyramid;

$pyramid = new Pyramid(width: 10, depth: 10, height: 10);

Pyramid

面配置

STL 模型由三角形组成,这些三角形与圆柱体和球体等形状非常不兼容,因此您必须稍作欺骗 - 创建一个由许多许多三角形组成的球体,直到您对球体的外观满意为止。

这就是面配置发挥作用的地方 - 它配置了球形物体的外观。

签名

  • new FacetsNumber(float $numberOfFragments)
  • new FacetsAngleAndSize(float $angle, float $size)

参数

  • $numberOfFragments - 使用此数量片段渲染圆形
  • $angle - 片段的最小角度,没有圆比这个数量360度多
  • $size - 片段的最小大小

示例

默认

<?php

use Rikudou\PhpScad\Shape\Sphere;

$sphere = new Sphere(radius: 10);

Default facets;

面数 60

<?php

use Rikudou\PhpScad\Shape\Sphere;
use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber;

$sphere = new Sphere(radius: 10, facetsConfiguration: new FacetsNumber(60));

60 facets;

面数 120

<?php

use Rikudou\PhpScad\Shape\Sphere;
use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber;

$sphere = new Sphere(radius: 10, facetsConfiguration: new FacetsNumber(120));

120 facets;

面数 240

<?php

use Rikudou\PhpScad\Shape\Sphere;
use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber;

$sphere = new Sphere(radius: 10, facetsConfiguration: new FacetsNumber(240));

120 facets;

面数 360

<?php

use Rikudou\PhpScad\Shape\Sphere;
use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber;

$sphere = new Sphere(radius: 10, facetsConfiguration: new FacetsNumber(360));

120 facets;

如你所见,使用的面越多,球形物体看起来越平滑,但渲染时间也越长,物体也更复杂。

注意:面配置也可以全局设置,而不是按形状设置

<?php

use Rikudou\PhpScad\ScadModel;
use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber;

$model = new ScadModel(facetsConfiguration: new FacetsNumber(30));
// all renderables will now use the above facets number as default

创建自定义形状

这个主题在单独的文档中介绍。

这份文档还在不断完善中

渲染输出

默认情况下,ScadModel 使用 ScadFileRenderer 类,正如其名称所暗示的,将内容渲染到 SCAD 文件中,然后您可以在 OpenSCAD 中打开该文件。

还有其他内置的渲染器可用

  • PngPreviewRenderer
  • StlRenderer

ScadFileRenderer 不同,这两个渲染器需要找到 OpenSCAD 二进制文件。如果您已经将 OpenSCAD 安装在您的路径中或您使用的是 flatpak 版本,则可以立即使用

<?php

use Rikudou\PhpScad\ScadModel;
use Rikudou\PhpScad\Renderer\PngPreviewRenderer;
use Rikudou\PhpScad\Renderer\StlRenderer;
use Rikudou\PhpScad\Shape\Cube;

$model = (new ScadModel())
    ->withRenderable(new Cube(5, 5, 5))
;

$model->withRenderer(new PngPreviewRenderer())->render(__DIR__ . '/preview.png');
$model->withRenderer(new StlRenderer())->render(__DIR__ . '/output.stl');

// you can also provide some settings to the renderers:

$previewRenderer = new PngPreviewRenderer(width: 512, height: 512, openScadPath: '/custom/path/to/openscad');
$stlRenderer = new StlRenderer(openScadPath: '/custom/path/to/openscad');

提示:您还可以创建自己的可重用渲染器,请参考此处的教程