2015-12-14 3 views
1

За последние несколько дней я заинтересовался идеей использования программного обеспечения на основе языка программирования для создания 3D-моделей. Одним из языков, с которыми я играл, является OpenSCAD, который оказался чрезвычайно полезным при создании интересных фигур.Как передать функцию в качестве параметра в модуль OpenSCAD?

В настоящее время я пытаюсь создать цветок с OpenSCAD, и я столкнулся с проблемой, которую я не смог обойти, используя документацию или другие ресурсы, которые я нашел в Интернете.

Вот краткая форма вопроса:

Могу ли я передать функцию в качестве параметра модуля OpenSCAD?

Если да, то как? Если нет, то почему нет и что я могу сделать вместо этого?

Это подводит меня к длинной форме вопроса со спецификой в ​​моей ситуации:

Я пытаюсь создать лепестки, используя линейную экструзию 2D полярных функций и пересекающиеся, что с 3D-функцией.

Для этого я начинаю с двух очень хороших модулей, которые я нашел на http://spolearninglab.com/curriculum/lessonPlans/hacking/resources/software/3d/openscad/openscad_math.html. Я не утверждаю, что написал их в первую очередь.

Первый - 3D плоттер Дэн Ньюман /* */3Dplot.scad

// 3dplot -- the 3d surface generator  
// x_range -- 2-tuple [x_min, x_max], the minimum and maximum x values 
// y_range -- 2-tuple [y_min, y_max], the minimum and maximum y values 

// grid -- 2-tuple [grid_x, grid_y] indicating the number of grid cells along the x and y axes 

// z_min -- Minimum expected z-value; used to bound the underside of the surface 

// dims -- 2-tuple [x_length, y_length], the physical dimensions in millimeters 

//Want to pass in z(x,y) as parameter 

module 3dplot(x_range=[-10, +10], y_range=[-10,10], grid=[50,50], z_min=-5, dims=[80,80]){ 
    dx = (x_range[1] - x_range[0])/grid[0]; 
    dy = (y_range[1] - y_range[0])/grid[1]; 

// The translation moves the object so that its center is at (x,y)=(0,0) 
// and the underside rests on the plane z=0 

scale([dims[0]/(max(x_range[1],x_range[0])-min(x_range[0],x_range[1])), 
     dims[1]/(max(y_range[1],y_range[0])-min(y_range[0],y_range[1])),1]) 
translate([-(x_range[0]+x_range[1])/2, -(y_range[0]+y_range[1])/2, -z_min]) 
union() 
{ 
    for (x = [x_range[0] : dx : x_range[1]]) 
    { 
     for (y = [y_range[0] : dy : y_range[1]]) 
     { 
      polyhedron(points=[[x,y,z_min], [x+dx,y,z_min], [x,y,z(x,y)], [x+dx,y,z(x+dx,y)], 
           [x+dx,y+dy,z_min], [x+dx,y+dy,z(x+dx,y+dy)]], 
         faces=prism_faces_1); 
      polyhedron(points=[[x,y,z_min], [x,y,z(x,y)], [x,y+dy,z_min], [x+dx,y+dy,z_min], 
           [x,y+dy,z(x,y+dy)], [x+dx,y+dy,z(x+dx,y+dy)]], 
         faces=prism_faces_2); 
      } 
     } 
    } 
} 

Второй - 2D Grapher /* */2dgraphing.scad

// function to convert degrees to radians 
function d2r(theta) = theta*360/(2*pi); 

// These functions are here to help get the slope of each segment, and use that to find points for a correctly oriented polygon 
function diffx(x1, y1, x2, y2, th) = cos(atan((y2-y1)/(x2-x1)) + 90)*(th/2); 
function diffy(x1, y1, x2, y2, th) = sin(atan((y2-y1)/(x2-x1)) + 90)*(th/2); 
function point1(x1, y1, x2, y2, th) = [x1-diffx(x1, y1, x2, y2, th), y1-diffy(x1, y1, x2, y2, th)]; 
function point2(x1, y1, x2, y2, th) = [x2-diffx(x1, y1, x2, y2, th), y2-diffy(x1, y1, x2, y2, th)]; 
function point3(x1, y1, x2, y2, th) = [x2+diffx(x1, y1, x2, y2, th), y2+diffy(x1, y1, x2, y2, th)]; 
function point4(x1, y1, x2, y2, th) = [x1+diffx(x1, y1, x2, y2, th), y1+diffy(x1, y1, x2, y2, th)]; 
function polarX(theta) = cos(theta)*r(theta); 
function polarY(theta) = sin(theta)*r(theta); 

module nextPolygon(x1, y1, x2, y2, x3, y3, th) { 
    if((x2 > x1 && x2-diffx(x2, y2, x3, y3, th) < x2-diffx(x1, y1, x2, y2, th) || (x2 <= x1 && x2-diffx(x2, y2, x3, y3, th) > x2-diffx(x1, y1, x2, y2, th)))) { 
     polygon(
      points = [ 
       point1(x1, y1, x2, y2, th), 
       point2(x1, y1, x2, y2, th), 
       // This point connects this segment to the next 
       point4(x2, y2, x3, y3, th), 
       point3(x1, y1, x2, y2, th), 
       point4(x1, y1, x2, y2, th) 
      ], 
      paths = [[0,1,2,3,4]] 
     ); 
    } 
    else if((x2 > x1 && x2-diffx(x2, y2, x3, y3, th) > x2-diffx(x1, y1, x2, y2, th) || (x2 <= x1 && x2-diffx(x2, y2, x3, y3, th) < x2-diffx(x1, y1, x2, y2, th)))) { 
     polygon(
      points = [ 
       point1(x1, y1, x2, y2, th), 
       point2(x1, y1, x2, y2, th), 
       // This point connects this segment to the next 
       point1(x2, y2, x3, y3, th), 
       point3(x1, y1, x2, y2, th), 
       point4(x1, y1, x2, y2, th) 
      ], 
      paths = [[0,1,2,3,4]] 
     ); 
    } 
    else { 
     polygon(
      points = [ 
       point1(x1, y1, x2, y2, th), 
       point2(x1, y1, x2, y2, th), 
       point3(x1, y1, x2, y2, th), 
       point4(x1, y1, x2, y2, th) 
      ], 
      paths = [[0,1,2,3]] 
     ); 
    } 
} 

module 2dgraph(bounds=[-10,10], th=2, steps=10, polar=false, parametric=false) { 

    step = (bounds[1]-bounds[0])/steps; 
    union() { 
     for(i = [bounds[0]:step:bounds[1]-step]) { 
      if(polar) { 
       nextPolygon(polarX(i), polarY(i), polarX(i+step), polarY(i+step), polarX(i+2*step), polarY(i+2*step), th); 
      } 
      else if(parametric) { 
       nextPolygon(x(i), y(i), x(i+step), y(i+step), x(i+2*step), y(i+2*step), th); 
      } 
      else { 
       nextPolygon(i, f(i), i+step, f(i+step), i+2*step, f(i+2*step), th); 
      } 
     } 
    } 
} 

Мой обертка код:

include <2dgraphing.scad>; 
include <3dplot.scad>; 

function z(x,y) = pow(x,2)+pow(y,2); //function used in 3dplot 
function r(theta) = cos(4*theta); //function used in 2dgraph 

module Petals() { 
    difference() { 
     union() { //everything to add 
      intersection() { 
       3dplot([-4,4],[-4,4],[50,50],-2.5); 
       scale([20, 20, 20]) linear_extrude(height=0.35) 
        2dgraph([0, 720], 0.1, steps=160, polar=true); 
      } 
     } 
     union() { //everything to subtract 

     } 
    } 

} 

Petals(); 

И все хорошо и денди с миром, когда я делаю мир ds наиболее вычислительно дорогие лепестки.

[Здесь я хотел бы опубликовать изображение, но так как это мой первый пост, я не имею предварительно необходимые пункты 10 репутации]

Однако, теперь я хочу, чтобы вычесть избыток в нижней части лепестков. Поэтому я мог бы использовать 3D-график с более крутой функцией и более низкой начальной точкой и вычесть из исходного 3D-графика.

Так что в той же программе я хочу использовать две разные функции для двух разных применений модуля 3Dplot.

Я попытался модифицировать 3dplot и мой код, чтобы сделать это:

Modified 3dplot: 

module 3dplot(x_range=[-10, +10], y_range=[-10,10], grid=[50,50], z_min=-5, dims=[80,80], input_function) 
{ 
    dx = (x_range[1] - x_range[0])/grid[0]; 
    dy = (y_range[1] - y_range[0])/grid[1]; 

    // The translation moves the object so that its center is at (x,y)=(0,0) 
    // and the underside rests on the plane z=0 

    scale([dims[0]/(max(x_range[1],x_range[0])-min(x_range[0],x_range[1])), 
      dims[1]/(max(y_range[1],y_range[0])-min(y_range[0],y_range[1])),1]) 
    translate([-(x_range[0]+x_range[1])/2, -(y_range[0]+y_range[1])/2, -z_min]) 
    union() 
    { 
     for (x = [x_range[0] : dx : x_range[1]]) 
     { 
      for (y = [y_range[0] : dy : y_range[1]]) 
      { 
       polyhedron(points=[[x,y,z_min], [x+dx,y,z_min], [x,y,input_function(x,y)], [x+dx,y,input_function(x+dx,y)], 
            [x+dx,y+dy,z_min], [x+dx,y+dy,input_function(x+dx,y+dy)]], 
          faces=prism_faces_1); 
       polyhedron(points=[[x,y,z_min], [x,y,input_function(x,y)], [x,y+dy,z_min], [x+dx,y+dy,z_min], 
            [x,y+dy,input_function(x,y+dy)], [x+dx,y+dy,input_function(x+dx,y+dy)]], 
          faces=prism_faces_2); 
      } 
     } 
    } 
} 

Модифицированный мой код:

include <2dgraphing.scad>; 
include <3dplot.scad>; 

function z1(x,y) = pow(x,2)+pow(y,2); //function used in 3dplot 
function z2(x,y) = pow(pow(x,2)+pow(y,2),1.5)-1; //function to be subtracted out 
function r(theta) = cos(4*theta); //function used in 2dgraph 

module Petals() { 
    difference() { 
     union() { //everything to add 
      intersection() { 
       3dplot([-4,4],[-4,4],[50,50],-2.5); 
       scale([20, 20, 20]) linear_extrude(height=0.35) 
        2dgraph([0, 720], 0.1, steps=160, polar=true, input_function=z1); 
      } 
     } 
     union() { //everything to subtract 
      3dplot([-4,4],[-4,4],[50,50],-2.5,input_function=z2); 
     } 
    } 

} 

Petals(); 

я получил следующее сообщение об ошибке: ВНИМАНИЕ: Не обращая внимания на неизвестную функцию 'input_function'.

Так как же я могу сделать передачу функции в качестве параметра?

Я до сих пор не писал ни одного функционального языка, но из моего руководства OpenSCAD я понимаю, что «Начиная с версии 2015.03 переменные теперь могут быть назначены в любой области». Поэтому я должен иметь возможность изменять значение input_function для каждого запуска 3dplot, как и переменные в 3dplot. Я интерпретирую это неправильно?

И как необязательный вопрос: существует ли открытый путь с OpenSCAD для достижения моих текущих целей без создания массивной вычислительной нагрузки во время процесса рендеринга?

Я потратил достаточно приличное количество времени, пытаясь решить эту проблему, и я излагаю этот длинный вопрос, и прошу прощения, если я застекляю простую существующую версию. Я очень ценю любого, кто хочет помочь.

ответ

1

Передача функции в качестве параметра в настоящее время невозможна. Кроме того, создание большого количества небольших объектов (например, многогранников в модуле 3dplot) сделает рендеринг модели очень медленным. Для этого конкретных вариантов использования существуют другие варианты генерации модели.

Новые функции генерации списка, доступные в последних версиях OpenSCAD, позволяют создавать один многогранник на основе функции.

См. 3d-functions.scad в демонстрационном репозитории. Это plots функция f (x, y).