rikudou / php-scad
OpenSCAD 的 PHP 封装
Requires
- php: ^8.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.13
- phpstan/phpstan: ^1.9
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 模型预览
那么为什么还要使用 PHP 来创建模型,如果你已经可以使用代码创建模型呢?
- PHP 是一种功能齐全的通用语言,在互联网上有大量的文档
- 有功能齐全的 IDE,可以帮助代码补全
- PHP 支持更多范式,如面向对象编程
- 更合理的参数名称 - 例如,
cylinder
签名是cylinder(h, r, r1, r2, d, d1, d2, center)
,与 PhpScad 版本相比 -new Cylinder(height, radius, bottomRadius, topRadius, diameter, bottomDiameter, topDiameter)
- 请注意,在 OpenSCAD 和 PhpScad 版本中,许多参数都是可选的
- 创建了一个有趣的参数化形状?太棒了,通过 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');
你可以注意到预览看起来是一样的
注意方便的方法 ->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));
安装
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);
圆柱体
创建一个圆柱体。
参数
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);
多面体
最通用的形状之一,允许你使用点和面定义自己的形状。它可以用来创建任何规则或不规则的形状。
参数
array|PointVector $points
(默认: empty PointVector) - 形状将包含的所有点array|FaceVector $faces
(默认: empty FaceVector) - 形状将包含的面的集合int $convexity
(默认:1) - 指定射线穿过物体时可能穿透的最大面数,仅在预览模式下使用
虽然数组和点以及面都可以使用,但为了更好的可读性,建议使用提供的 PointVector
和 FaceVector
。当使用 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
如你所见,这很难处理。
多面体 - 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), ));
球体
创建一个球体。
参数
?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);
金字塔
创建一个金字塔。
参数
number $width
number $depth
number $height
渲染条件:始终
示例:
<?php use Rikudou\PhpScad\Shape\Pyramid; $pyramid = new Pyramid(width: 10, depth: 10, height: 10);
面配置
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);
面数 60
<?php use Rikudou\PhpScad\Shape\Sphere; use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber; $sphere = new Sphere(radius: 10, facetsConfiguration: new FacetsNumber(60));
面数 120
<?php use Rikudou\PhpScad\Shape\Sphere; use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber; $sphere = new Sphere(radius: 10, facetsConfiguration: new FacetsNumber(120));
面数 240
<?php use Rikudou\PhpScad\Shape\Sphere; use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber; $sphere = new Sphere(radius: 10, facetsConfiguration: new FacetsNumber(240));
面数 360
<?php use Rikudou\PhpScad\Shape\Sphere; use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber; $sphere = new Sphere(radius: 10, facetsConfiguration: new FacetsNumber(360));
如你所见,使用的面越多,球形物体看起来越平滑,但渲染时间也越长,物体也更复杂。
注意:面配置也可以全局设置,而不是按形状设置
<?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');
提示:您还可以创建自己的可重用渲染器,请参考此处的教程