0

我正在尝试将 Voronoi 单元格输出为可用于 3D 打印的格式。

MATLAB 从 X 和 Y 坐标列表生成 voronoi 单元。我的脚本会生成这样一个点列表,但要获得我可以导出的格式似乎有问题。

我的主要希望在于 stlwrite,http://www.mathworks.com/matlabcentral/fileexchange/20922-stlwrite-write-binary-or-ascii-stl-file

此函数/脚本需要一个要导出的表面。

function [lines, LineData, pOut] = makeSurfaceFromVorVerts(Lx, Ly, varargin)
    p = inputParser;
    addRequired(p,'Lx',...
        @(x) (isequal(class(x),'double') && isequal(size(x,1),2)));
    addRequired(p,'Ly',...
        @(x) (isequal(class(x),'double') && isequal(size(x,1),2)));
    defaultResolution = 100;
    addOptional(p,'Resolution',defaultResolution,...
        @(x) (isequal(class(x),'double') && isequal(size(x),[1 1])));
    defaultBoundary = [0 110; 0 110];
    addOptional(p,'Boundaries',defaultBoundary,...
        @(x) (isequal(class(x),'double') && isequal(size(x),[2 2])));
    parse(p,Lx,Ly,varargin{:});
    pOut = p;

    LX = p.Results.Lx;
    LY = p.Results.Ly;
    Bounds = p.Results.Boundaries;

    % Strip high values
    reducedXdat = [];
    reducedYdat = [];
    for i=1:size(LX,2)
        if LX(1,i) > Bounds(1,1) && LX(1,i) < Bounds(1,2) && ... % X value of start of line
           LX(2,i) > Bounds(2,1) && LX(2,i) < Bounds(2,2) && ... % Y value of start of line
           LY(1,i) > Bounds(1,1) && LY(1,i) < Bounds(1,2) && ... % X value of end of line
           LY(2,i) > Bounds(2,1) && LY(2,i) < Bounds(2,2),       % Y value of end of line
            reducedXdat = [reducedXdat, LX(:,i)];
            reducedYdat = [reducedYdat, LY(:,i)];
        end
    end

    % Initialise a grid of points
    %sXnew = (Bounds(1,2) - Bounds(1,1)) * p.Results.Resolution;
    %sYnew = (Bounds(2,2) - Bounds(2,1)) * p.Results.Resolution;
    %Z = zeros(sXnew, sYnew,'uint8');


    %for x=1:size(X,1)
    %    for y=1:size(Y,1)
    %        nX = floor(X(x)*p.Results.Resolution);
    %        nY = floor(Y(y)*p.Results.Resolution);
    %        Z(nX,nY) = 1;
    %    end
    %end
    %surface = Z;
    %coords = [X,Y];
    lines = line(reducedXdat,reducedYdat);
    LineData = [reducedXdat; reducedYdat];
end

上面我的处理脚本采用命令生成的点

[Lx, Ly] = voronoi(xValuesOfCellCentres, yValuesOfCellCentres);

连同一个可选的“边界”矩阵(对于注释部分,还有一个分辨率检查),然后输出行。

我希望这些线条形成表面。我考虑使用二进制 Z 值创建网格(1 表示点,0 表示其他任何地方),但我不知道如何还包括点之间的位置,即线所覆盖的位置。

我希望我可以采取一些相关的中间步骤,根据绘制的线条的挤压创建一个框架(通过这个脚本,它已经将多余的线条切割成无穷大,或者通过voronoi(X,Y),但我无法工作出去。

4

1 回答 1

0

找到了一种方法来做到这一点。

更改了脚本,新脚本粘贴在底部。

工作流程:

  1. Matlab(创建线图,然后for i=1:size(lineHandles,1), set(lineHandles(i),'lineWidth',4),end)。根据需要更改线宽。)
  2. 另存为 .png 文件。
  3. Gimp(将文件裁剪为漂亮的矩形)
  4. InkScape(打开 png 文件,单击图像,菜单路径 -> 跟踪位图 -> 颜色量化(2 种颜色)-> 将路径拖到一边(当您选择此选项时,它会在底部的状态栏中显示路径)。单击图像并删除。将路径(带节点)放回页面上(在顶部的工具栏中有一个 x/y 坐标设置,将它们都设置为 0。
  5. 另存为 .dxf 文件。要使这些输入更好,请使用http://www.thingiverse.com/thing:14221提供的扩展。这应该通过将 .py 和 .inx 文件复制到 /usr/share/inkscape/extensions (或类似文件)来安装
  6. 使用命令在 OpenSCAD 中打开import("/path/to/filename.dxf");
  7. linear_extrude(height = x)可以使用x 以 mm 为单位的高度来挤压此对象(其他长度可能可配置)
  8. 使用 CGAL 渲染(F6 是 OpenSCAD 中的快捷方式。)
  9. 导出到 .stl 文件(菜单设计 > 导出为 STL...)

MATLAB 脚本(根据需要编辑并根据需要获取输出,如果您想要线句柄,则需要放入几乎所有输出参数(或重新排序):

%voronoiScriptHex generates voronoi cells in a hexagonal tesselation grid
%   [X, Y, Fig, Axis, Lx, Ly, lH, lD] = voronoiScript(Bnd, Spc, D, ...)
%   
%   Output variables
%       X is the x coordinate of the voronoi cell centres
%       Y is the y coordinate of the voronoi cell centres
%       Fig is the handle of the figure generated
%       Axis is the handle of the axes used
%       Lx gives the start and end x coordinates of the voronoi lines
%       Ly gives the start and end y coordinates of the voronoi lines
%       lH is the set of handles for the voronoi lines
%       lD is constructed from [Lx; Ly], it is a [4 by Length] array
%
%   Bnd specifies the boundaries for the region to be covered. It should be
%   either one number, or a pair of numbers in a [1x2] vector, specifying
%   [maxX, maxY]. 0 is taken as the minimum value.
%
%   Spc specifies the average spacing. For a hex grid, it only accepts one
%   value.
%
%   D specifies the variation from a uniform grid. It is multiplied by a 
%   random number between -0.5 and 0.5 and added to the [x,y] coordinates
%   of a point. If size(D) = [1x2], then the values are for [Dx, Dy].
%
%   Optional arguments can be used to place some points exactly on the grid
%   they would lie on with D[x/y] = 0. The first should be 'PartFixed' -
%   this is a boolean and if true, some points are fixed to the grid.
%
%   The second argument is 'FractionFixed'. This is an integer value
%   (double class variables are accepted, but floor(arg) must be equal to
%   (arg)). It specifies inversely how often points should be fixed, eg a
%   value of 1 fixes every point, whilst a value of 5 fixes 1/5 of the
%   points.
%
%   PlotScatter is another boolean value, which sets if a scatter plot of
%   the X,Y values corresponding to the cell centres should be included in
%   the figure.

function [X, Y, Figure, Axis, Lx, Ly, lineHandles, lineData] = ...
    voronoiScriptHex(Boundary, Spacing, Delta, varargin)

    p = inputParser;
    p.FunctionName = 'voronoiScript';
    addRequired(p, 'Boundary', @checkTypes);
    addRequired(p, 'Spacing', @isnumeric);
    addRequired(p, 'Delta', @checkTypes);
    defaultPart = false;
    addOptional(p, 'PartFixed', defaultPart, @islogical);
    defaultFraction = 2;
    addOptional(p, 'FractionFixed', defaultFraction, @isAnInt);
    defaultScatter = false;
    addOptional(p, 'PlotScatter', defaultScatter, @islogical);
    parse(p, Boundary, Spacing, Delta, varargin{:});


    % Get values for boundaries and delta
    % (Can be vectors or scalars)
    if isequal(size(p.Results.Boundary),[1,2])
        % Boundary is a vector [maxX, maxY]
        BoundaryY = p.Results.Boundary(1,2);
    else
        BoundaryY = p.Results.Boundary(1,1);
    end
    if isequal(size(p.Results.Delta),[1,2])
        % Delta is a vector [dX, dY]
        DeltaY = p.Results.Delta(1,2);
    else
        DeltaY = p.Results.Delta(1,1);
    end

    Spacing = p.Results.Spacing;
    BoundaryX = p.Results.Boundary(1,1);
    DeltaX = p.Results.Delta(1,1);
    D1 = [2*Spacing*cosd(30), Spacing];
    numP = [ceil(BoundaryX/D1(1,1)) ceil(BoundaryY/D1(1,2))];

    D2 = D1 ./ 2;

    % Create the values
    counter = 1;
    xList(numP(1,1)*numP(1,2)) = 0;
    yList(numP(1,1)*numP(1,2)) = 0;
    for x=1:numP(1,1)
        for y = 1:numP(1,2)
            xList(counter) = (getPointValue(x, D1(1,1), DeltaX)-D2(1,1));
            xList(counter+1) = getPointValue(x, D1(1,1), DeltaX);
            yList(counter) = (getPointValue(y, D1(1,2), DeltaY)-D2(1,2));
            yList(counter+1) = getPointValue(y, D1(1,2), DeltaY);
            counter = counter + 2;
        end
    end

    % Set some of the points to be without random change
    if (p.Results.PartFixed),
        for counter=1:p.Results.FractionFixed:size(xList,2),
            [x, y] = getXYfromC(counter, numP(1,2));
            xList(counter) = x*Spacing;
            yList(counter) = y*Spacing;
        end
    end

    X = xList;
    Y = yList;

    % Set manual ticks for the figure axes
    ticksX = zeros(1,numP(1,1)+1);
    for i=1:numP(1,1)+1,
        ticksX(i) = i*D1(1,1);
    end
    ticksY = zeros(1,numP(1,2)+1);
    for i=1:numP(1,2)+1,
        ticksY(i) = i*D1(1,2);
    end

    BoundCoeff = 1.08;
    Bounds = [0 BoundCoeff*BoundaryX; 0 BoundCoeff*BoundaryY];

    % Give the figure a handle that is returned, and set axes values
    Figure = figure;
    Axis = axes;
    axis equal;
    minor = 'off';
    gridtoggle = 'off';
    set(Axis,'XTickMode','manual','YTickMode','manual', ...
        'XGrid',gridtoggle,'YGrid',gridtoggle, ...
        'XMinorGrid',minor,'YMinorGrid',minor, ...
        'XTick',ticksX,'YTick',ticksY, ...
        'XMinorTick',minor,'YMinorTick',minor, ...
        'XLim',[0 Bounds(1,2)],'YLim',[0 Bounds(2,2)]);

    %set(Axis,'XLim',[0 Bounds(1,2)],'YLim',[0 Bounds(2,2)]);

    % Create the voronoi cells, returning the line points
    [Lx, Ly] = voronoi(X,Y);

    % Strip high values
    counter = 1;
    reducedXdat = zeros(2,size(Lx,2));
    reducedYdat = zeros(2,size(Lx,2));
    for i=1:size(Lx,2)
        if Lx(1,i) > Bounds(1,1) && Lx(1,i) < Bounds(1,2) && ... % X value of start of line
           Lx(2,i) > Bounds(2,1) && Lx(2,i) < Bounds(2,2) && ... % Y value of start of line
           Ly(1,i) > Bounds(1,1) && Ly(1,i) < Bounds(1,2) && ... % X value of end of line
           Ly(2,i) > Bounds(2,1) && Ly(2,i) < Bounds(2,2),       % Y value of end of line
            reducedXdat(:,counter) = Lx(:,i);
            reducedYdat(:,counter) = Ly(:,i);
            counter = counter + 1;
        end
    end
    Lx = reducedXdat(:,1:counter-1);
    Ly = reducedYdat(:,1:counter-1);

    % Plot the voronoi lines
    lineHandles = line(Lx, Ly);

    % Set colours to black
    if (1)
        for i=1:size(lineHandles,1)
            set(lineHandles(i),...
                'LineWidth',3, ...
                'LineSmoothing','on', ...
                'Color',[0 0 0]);
        end
    end

    lineData = [Lx; Ly];
    if (p.Results.PlotScatter)
        hold on;
        scatter(X,Y);
    end
end

function bool = checkTypes(arg)
    bool = (isequal(class(arg),'double') && ...
        (isequal(size(arg),[1,1]) || isequal(size(arg),[1,2])));
end

function bool = isAnInt(arg)
    bool = (isequal(floor(arg),arg) && ...
        isequal(size(arg),[1,1]) && ...
        isnumeric(arg));
end

function val = getPointValue(intV, spacing, delta)
    val = (((rand(1)-0.5)*delta)+(intV*spacing));
end

function [x,y] = getXYfromC(counter, sizeY)
    x = floor(counter/sizeY)+1;
    y = counter - ((x-1)*sizeY);
end

SCAD 脚本文件,用于将 voronoi 单元放置在一对圆柱体中,用于 3D 打印网格。根据需要使用路径进行编辑,或更改形状等:

$fn = 360;
inkFile = "/path/to/my/file";
scl = 1.05; // Scale variable
transVec = [-100, -98, 0]; // Move the imported image pattern so that it's centred.
union(){
    makeRing([0,0,3],inR=96, outR=99, height=6);
    makeRing([0,0,1.5], inR=99, outR=102, height=3);

    intersection() {
        #linear_extrude(height = 6.05, convexity=40, center=false) // Removing the # will get rid of the overlay, which helps see where the grid is. 
            scale([scl, scl, 1])
            translate(transVec)
            import(inkFile);
        makeCylinder([0,0,3], 96, 6, $fn=360);
    }
}
module makeCylinder(centre, radius, height) {
    translate(centre){
        cylinder(h = height, r = radius, center=true);
    }
}
module makeRing(centre,inR, outR, height) {
    difference() {
        makeCylinder(centre, outR, height);
        makeCylinder(centre, inR, height+0.1);
    }
}
于 2014-07-24T06:51:30.373 回答