Выдавливание 2D-фигуры по эллипсу, а не по окружности

Я могу создать тор, вращая двумерную фигуру, например, круг, используя rotate_extrude:

module ring(diameter, length) {
    rotate_extrude(angle=360)
    translate([length-diameter/2,0,0])circle(r=diameter);
}

ring(10, 50);

ring

В результате получается кольцо.

На самом деле мне нужно выдавить этот объект по эллиптической траектории, которая, например, в два раза длиннее его ширины.

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

scale([2, 1, 1])
ring(10, 50);

чешуйчатое кольцо

Какой самый простой способ этого добиться?

, 👍0


1 ответ


Лучший ответ:

3

Я не думаю, что это возможно без явного повторения. Но эта итерация не так уж и сложна.

Одностороннее движение (из многих)

module ringlet(R1, R2, a, D){
    translate([R1*cos(a), R2*sin(a), 0]) rotate([0,0,atan2(R1*sin(a),R2*cos(a))]) rotate([90,0,0]) cylinder(d=D, h=0.1);
}

module ring(diameter, length, ratio, step=10) {
    R1=length-diameter/2;
    R2=R1*ratio;
    for(i=[0:step:360]){
        hull(){
            ringlet(R1, R2, i, diameter);
            ringlet(R1, R2, i+step, diameter);
        }
    }
}

ring(20, 50, 2, 5);

Обратите внимание, что я использую 20 в качестве диаметра, а не ваш 10, просто потому, что в вашем коде вы называете его диаметром, но используете радиусом (передаёте его как параметр r функции circle). Таким образом, в моём коде это действительно диаметр. Поэтому то, что вы называете 10, я называю 20.

Сложность (ну, на уровне средней школы. Тем не менее, нужно немного поразмыслить с бумагой и ручкой) заключается в том, что для эллипса с параметрическим уравнением x=R₁cos(α), y=R₂sin(α) существует 3 различных угла.

α, параметр этого параметрического уравнения

β, угол этой точки (x, y) с осью x, не равен α. Он равен β = arctan(R₂.sin(α) / R1.cos(α)) = arctan((R₂/R₁).tan(α)). Очевидно, что если R₁ = R₂, то это arctan(tan(α)) = α. Таким образом, в окружности α (по определению, можно сказать) является углом этой точки с осью x. Но пока R₁ ≠ R₂, это уже не так.

γ — нормаль к касательной. Это угол, под которым будет образовано круглое сечение, если разрезать тор в точке (x, y) (угол ножа при разрезании, если требуется получить круглое сечение).

Это нормаль к касательной, поскольку касательная является осью «местного цилиндра». Тангенс является производной, поэтому коллинеарен с (-R₁sin(α), R₂cos(α)). Итак, норма — это (R₂.cos(α), R₁.sin(α)). И, следовательно, угол γ равен arctan(R₁.sin(α) / R₂.cos(α)) = arc tan((R₁/R₂).tan(α)).

Итак, снова, очевидно, в частном случае окружности R₁=R₂ ⇒ γ=α. Но в общем случае γ≠α и γ≠β.

Итак, то, что я ringlet здесь делаю, - это рисую окружность (точнее, цилиндр очень малой длины. Возможно, есть решение получше, но мне нужна трехмерная фигура, а не двухмерная), то есть круговое сечение тора в параметрической позиции α (то есть x=R₁cosα, y=R₂sinα).

Я поворачиваю этот круг так, чтобы он принял правильную ориентацию (γ в предыдущем объяснении). И перемещаю его так, чтобы он оказался в позиции x, y.

Теперь, когда с помощью этого колечка я могу нарисовать круговое сечение любого α тора, я просто обращаю их в оболочку 2 на 2.

Возможно, есть решения получше. Например, в OpenSCAD у нас всегда возникает ощущение, что при использовании translate с некоторыми sin и cos в аргументах мы делаем что-то неправильно, и что комбинация более простого translate с некоторыми rotate будет лучше. Но здесь, из-за того, что я объяснил с помощью эллипса, это, вероятно, означало бы вычисление какого-то другого arctan(..*tan), что на самом деле не лучше.

Другой способ, вместо использования hull для создания этих небольших секций (которые не являются цилиндрами, поскольку две грани не параллельны, а представляют собой почти цилиндры), заключается в интерполяции с небольшими секциями rotate_extrude: для каждого угла α существует центр вращения (не (0,0,0), если только он не является окружностью: его положение находится на расстоянии локального радиуса от (x,y), и этот локальный радиус тем больше, чем (x,y) ближе к (0,0,0)). Если рассмотреть два последовательных шага, как это сделал я, то локальный центр вращения будет пересечением плоскости z=0 и сечения двух окружностей. Таким образом, мы могли бы rotate_extrude создать окружность с начальным углом γ вокруг центра вращения и с диаметром выдавливания, соответствующим этому локальному радиусу.

Это было бы немного сложнее. Но позволило бы избежать оболочки и, возможно, потребовало бы меньше шагов (но и более сложных), по той же причине, по которой попытка аппроксимировать эллипс путём объединения дуг окружностей точнее, чем попытка аппроксимировать его путём объединения отрезков прямых (для одинакового визуального эффекта нужно меньше дуг, чем отрезков).

Но, что ж, этот метод достаточно быстрый и при этом достаточно читабельный. И я не думаю, что «последовательность rotate_extrude» что-то изменит.


,