2016-12-01 10 views
8

Если кто-то заботится, я использую WPF ....Как я мог polygonise логическое значение [,,]

Начало истории:

Я получил ряд полутоновых изображений (фрагменты модели). Пользователь вводит «диапазон» значений шкалы серого для построения 3D-модели. Таким образом, я создал массив 3D bool, чтобы упростить сборку 3D-модели. Этот массив представляет собой поле с пикселями, указывающее, должен ли каждый пиксель быть создан/сгенерирован/заполнен или нет.


Галстук:

Использование bool[,,], я хочу, чтобы извлечь List<Point3D[]> где каждый Point3D[] имеет длину 3 и представляет собой треугольник в 3D-пространстве.


Дополнительная информация:

Сформированная модель будет 3D распечатаны. В bool[,,]true указывает на наличие вещества, а false указывает на отсутствие вещества. Мне нужно избегать кубических моделей, где каждый пиксель заменяется кубом (это было бы бесполезно в соответствии с моей целью). Модель должна быть максимально гладкой и максимально точной.


То, что я пытался сделать:

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

2- Я продолжал бороться в течение нескольких дней, пытаясь сделать свой собственный алгоритм, но я частично провалился. (Это реальная сложная Если вы хотите узнать больше об этом, пожалуйста, сообщите.)


Некоторые ожидаемые решения, которые я не имею ни малейшего представления о том, как реализовать:

1- изменить алгоритм Маршевые кубов для того, чтобы заставить его работать, используя bool[,,]

2- Изменить алгоритм Маршевые кубов для того, чтобы заставить его работать, используя работу, используя «диапазон» isolevel значений

3- Показывает, как реализовать другой алгоритм, подходящий для моей цели (возможно, алгоритм Ray-Casting) в WPF.

4 Запрос источника сигнала, который я попытался реализовать, показывая мне, как его решить. (Это было сделано для полигонаж bool[,,])

5 Некоторые другие магические решения.

Заранее спасибо.


EDIT:

Говоря

Использование bool[,,], я хочу, чтобы извлечь List<Point3D[]> где каждый Point3D[] имеет длину 3 и представляет собой треугольник в 3D-пространстве.

Я имею в виду, что хочу получить группу треугольников. Каждый треугольник должен быть представлен как 3 Point3D s. Если вы не знаете, что такое Point3D, это struct, содержащий 3 двухместных (X, Y и Z), которые используются для представления позиции в трехмерном пространстве.

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

cubeindex = 0; 
if (grid.val[0] < isolevel) cubeindex |= 1; 
if (grid.val[1] < isolevel) cubeindex |= 2; 
if (grid.val[2] < isolevel) cubeindex |= 4; 
if (grid.val[3] < isolevel) cubeindex |= 8; 
if (grid.val[4] < isolevel) cubeindex |= 16; 
if (grid.val[5] < isolevel) cubeindex |= 32; 
if (grid.val[6] < isolevel) cubeindex |= 64; 
if (grid.val[7] < isolevel) cubeindex |= 128; 

Таким образом, я просто понятия не имею, с чего начать его модифицировать.


Другой EDIT:

После нескольких экспериментов с использованием алгоритма марширующих кубов, я заметил, что polygonising в bool[,,] не будет приводить к гладкой 3D модели, я с нетерпением жду, так что мой первое ожидаемое решение, и мое четвертое ожидаемое решение вышло из игры. После многих исследований я узнал о методе рендеринга Ray-Casting. Кажется, это здорово, но я не уверен, подходит ли он для моих нужд. Я не могу понять, как это реализовано, хотя я знаю как это работает.


Еще один EDIT: TriTable.LookupTable и EdgeTable.LookupTable являются таблицы представляют here

Вот MarchingCubes класс:

public class MarchingCubes 
{ 
    public static Point3D VertexInterp(double isolevel, Point3D p1, Point3D p2, double valp1, double valp2) 
    { 
     double mu; 
     Point3D p = new Point3D(); 

     if (Math.Abs(isolevel - valp1) < 0.00001) 
      return (p1); 

     if (Math.Abs(isolevel - valp2) < 0.00001) 
      return (p2); 

     if (Math.Abs(valp1 - valp2) < 0.00001) 
      return (p1); 

     mu = (isolevel - valp1)/(valp2 - valp1); 

     p.X = p1.X + mu * (p2.X - p1.X); 
     p.Y = p1.Y + mu * (p2.Y - p1.Y); 
     p.Z = p1.Z + mu * (p2.Z - p1.Z); 

     return (p); 
    } 

    public static void Polygonise (ref List<Point3D[]> Triangles, int isolevel, GridCell gridcell) 
    { 
     int cubeindex = 0; 
     if (grid.val[0] < isolevel) cubeindex |= 1; 
     if (grid.val[1] < isolevel) cubeindex |= 2; 
     if (grid.val[2] < isolevel) cubeindex |= 4; 
     if (grid.val[3] < isolevel) cubeindex |= 8; 
     if (grid.val[4] < isolevel) cubeindex |= 16; 
     if (grid.val[5] < isolevel) cubeindex |= 32; 
     if (grid.val[6] < isolevel) cubeindex |= 64; 
     if (grid.val[7] < isolevel) cubeindex |= 128; 

     // Cube is entirely in/out of the surface 
     if (EdgeTable.LookupTable[cubeindex] == 0) 
      return; 

     Point3D[] vertlist = new Point3D[12]; 

     // Find the vertices where the surface intersects the cube 
     if ((EdgeTable.LookupTable[cubeindex] & 1) > 0) 
      vertlist[0] = VertexInterp(isolevel, grid.p[0], grid.p[1], grid.val[0], grid.val[1]); 

     if ((EdgeTable.LookupTable[cubeindex] & 2) > 0) 
      vertlist[1] = VertexInterp(isolevel, grid.p[1], grid.p[2], grid.val[1], grid.val[2]); 

     if ((EdgeTable.LookupTable[cubeindex] & 4) > 0) 
      vertlist[2] = VertexInterp(isolevel, grid.p[2], grid.p[3], grid.val[2], grid.val[3]); 

     if ((EdgeTable.LookupTable[cubeindex] & 8) > 0) 
      vertlist[3] = VertexInterp(isolevel, grid.p[3], grid.p[0], grid.val[3], grid.val[0]); 

     if ((EdgeTable.LookupTable[cubeindex] & 16) > 0) 
      vertlist[4] = VertexInterp(isolevel, grid.p[4], grid.p[5], grid.val[4], grid.val[5]); 

     if ((EdgeTable.LookupTable[cubeindex] & 32) > 0) 
      vertlist[5] = VertexInterp(isolevel, grid.p[5], grid.p[6], grid.val[5], grid.val[6]); 

     if ((EdgeTable.LookupTable[cubeindex] & 64) > 0) 
      vertlist[6] = VertexInterp(isolevel, grid.p[6], grid.p[7], grid.val[6], grid.val[7]); 

     if ((EdgeTable.LookupTable[cubeindex] & 128) > 0) 
      vertlist[7] = VertexInterp(isolevel, grid.p[7], grid.p[4], grid.val[7], grid.val[4]); 

     if ((EdgeTable.LookupTable[cubeindex] & 256) > 0) 
      vertlist[8] = VertexInterp(isolevel, grid.p[0], grid.p[4], grid.val[0], grid.val[4]); 

     if ((EdgeTable.LookupTable[cubeindex] & 512) > 0) 
      vertlist[9] = VertexInterp(isolevel, grid.p[1], grid.p[5], grid.val[1], grid.val[5]); 

     if ((EdgeTable.LookupTable[cubeindex] & 1024) > 0) 
      vertlist[10] = VertexInterp(isolevel, grid.p[2], grid.p[6], grid.val[2], grid.val[6]); 

     if ((EdgeTable.LookupTable[cubeindex] & 2048) > 0) 
      vertlist[11] = VertexInterp(isolevel, grid.p[3], grid.p[7], grid.val[3], grid.val[7]); 

     // Create the triangle 
     for (int i = 0; TriTable.LookupTable[cubeindex, i] != -1; i += 3) 
     { 
      Point3D[] aTriangle = new Point3D[3]; 

      aTriangle[0] = vertlist[TriTable.LookupTable[cubeindex, i]]; 
      aTriangle[1] = vertlist[TriTable.LookupTable[cubeindex, i + 1]]; 
      aTriangle[2] = vertlist[TriTable.LookupTable[cubeindex, i + 2]]; 

      theTriangleList.Add(aTriangle); 
     } 
    } 
} 

Вот GridCell класс:

public class GridCell 
{ 
    public Point3D[] p = new Point3D[8]; 
    public Int32[] val = new Int32[8]; 
} 

Хорошо, это то, что я делаю:

List<int[][]> Slices = new List<int[][]>(); //Each slice is a 2D jagged array. This is a list of slices, each slice is a value between about -1000 to 2300 

public static void Main() 
{ 
    List <Point3D[]> Triangles = new List<Point3D[]>(); 
    int RowCount;//That is my row count 
    int ColumnCount;//That is my column count 
    //Here I fill the List with my values 
    //Now I'll polygonise each GridCell 
    for (int i = 0; i < Slices.Count - 1; i++) 
    { 
     int[][] Slice1 = Slices[i]; 
     int[][] Slice2 = Slices[i + 1]; 
     for (int j = 0; j < RowCount - 1; j++) 
     { 
      for (int k = 0; k < ColumnCount - 1; k++) 
      { 
       GridCell currentCell = GetCurrentCell (Slice1, Slice2, j, k); 
       Polygonise (ref Triangles, int isoLevel, GridCell currentCell); 
      } 
     } 
    } 
    //I got the "Triangles" :D 
} 

//What this simply does is that it returns in 3D space from RI and CI 
public static GridCell GetCurrentCell (int[][] CTSliceFront, int[][] CTSliceBack, int RI, int CI)//RI is RowIndex and CI is ColumnIndex 
{ 
    //Those are preset indicating X,Y or Z coordinates of points/edges 
    double X_Left_Front; 
    double X_Right_Front; 
    double X_Left_Back; 
    double X_Right_Back; 
    double Y_Top_Front; 
    double Y_Botton_Front; 
    double Y_Top_Back; 
    double Y_Botton_Back; 
    double Z_Front; 
    double Z_Back; 

    GridCell currentCell = new GridCell(); 
    currentCell.p[0] = new Point3D(X_Left_Back, Y_Botton_Back, Z_Back); 
    currentCell.p[1] = new Point3D(X_Right_Back, Y_Botton_Back, Z_Back); 
    currentCell.p[2] = new Point3D(X_Right_Front, Y_Botton_Front, Z_Front); 
    currentCell.p[3] = new Point3D(X_Left_Front, Y_Botton_Front, Z_Front); 
    currentCell.p[4] = new Point3D(X_Left_Back, Y_Top_Back, Z_Back); 
    currentCell.p[5] = new Point3D(X_Right_Back, Y_Top_Back, Z_Back); 
    currentCell.p[6] = new Point3D(X_Right_Front, Y_Top_Front, Z_Front); 
    currentCell.p[7] = new Point3D(X_Left_Front, Y_Top_Front, Z_Front); 

    currentCell.val[] = CTSliceBack[theRowIndex + 1][theColumnIndex]; 
    currentCell.val[] = CTSliceBack[theRowIndex + 1][theColumnIndex + 1]; 
    currentCell.val[] = CTSliceFront[theRowIndex + 1][theColumnIndex + 1]; 
    currentCell.val[] = CTSliceFront[theRowIndex][theColumnIndex]; 
    currentCell.val[] = CTSliceBack[theRowIndex][theColumnIndex + 1]; 
    currentCell.val[] = CTSliceFront[theRowIndex][theColumnIndex + 1]; 
    currentCell.val[] = CTSliceFront[theRowIndex][theColumnIndex]; 

    return currentCell; 
} 

Я сделал это прямо из стека при переполнении, поэтому, пожалуйста, указать на опечатки. Это работает. В результате получается оболочка модели. Я хочу сделать замену переменной isolevel в функции Polygonise с 2 целыми числами: isolevelMin и isolevelMax. Не только 1 int, но и диапазон.


EDIT: Ребята, пожалуйста, помогите. 50 репутации почти 1/2 моей репутации. Надеюсь, я смогу изменить свои ожидания.Теперь я просто хочу понять, как я могу реализовать то, что я хочу, любой фрагмент, как это сделать, или если кто-то выдаст некоторые ключевые слова, которые я могу использовать для Google, которые я использовал для решения моей проблемы, он получит репутацию. Мне просто нужно немного света - здесь все темно.


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

+3

«Использование bool [,,], я хочу получить список , где каждый Point3D [] имеет длину 3 и представляет треугольник в трехмерном пространстве»: 1. На юридической панели выпишите в longhand * точно, что вы подразумеваете под этим, ваше предположение, безусловно, намного лучше, чем мое. 2. Уточните это, пока оно не будет выглядеть как псевдокод. 3. Простая часть: переведите это в реальный код. 4. Попросите помощи, если его борен. –

+4

«Я реализовал алгоритм маршевых кубов, но он просто не создается, чтобы принять« диапазон »значений». Когда вопрос говорит «не кажется», я немного умираю. Информация в этом предложении раундов равна нулю. Если у вас есть конкретная проблема с внедрением алгоритма, отправьте свой код и скажите, где он болит. Но вы не описываете конкретную проблему реализации; вы описываете эмоциональное состояние. Может быть, кто-то здесь может написать рецепты, я не знаю. Я не могу. –

+2

[здесь] (http://vis.computer.org/vis2004/DVD/vis/papers/nielson2.pdf) - это документ, который может помочь в этом поговорить о том, как сгладить вашу сетку при использовании алгоритма маршевого куба. –

ответ

2

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

Учитывая, что вы можете разделить всю сетку на кубики и решить каждый отдельно. Вы также можете заметить, что хотя конечная сетка зависит от точных значений вашего трехмерного скалярного поля, топология сетки зависит только от факта, если какой-либо данный угол куба равен внутри из сетки или снаружи.

Например: Допустим, ваш isolevel установлен в 0 и ваш 3D поле имеет положительные и отрицательные числа.Если теперь вы измените какое-либо значение от 0.1 до 1.0, тогда вы измените только пропорции некоторых треугольников, но не их число и/или топологию. С другой стороны, если вы измените какое-либо значение от 0.1 до -0.1, тогда ваши треугольники могут переключаться на другую схему, и их число может измениться.

Благодаря этому мы можем использовать некоторые умные оптимизации - для каждого из 8 углов вы проверить, если угол внутри или вне меша, вы используете эти 8 бит построить число - которое становится индексом к вашей предварительно вычисленной таблице возможных решений куба, назовем их Variants.

Каждый Cube Variant представляет собой список треугольников для этой конкретной конфигурации углов. Треугольники в маршевых кубах всегда располагаются между краями куба, поэтому мы используем краевые индексы для его описания. Эти индексы - это точные данные, которые вы видите в этих «волшебных» таблицах в коде Пола Бурка :).

Но это еще не окончательная сетка. Треугольники, которые вы получаете здесь, основаны только на факте, если какой-то угол внутри или снаружи сетки, но насколько далеко от поверхности? Имеет ли это влияние на результат? Вот ваши значения сетки еще раз - когда вы выбрали свой Variant, получили список треугольников и 3 ребра для каждого из этих треугольников, затем вы получите значения на концах края и интерполируйте их, чтобы найти значение 0 (мы, вы установили его как isolevel, помните?). Эти конечные, интерполированные вершины должны использоваться для вашей сетки.

Это стало довольно длинным введением, надеюсь, вы понимаете, что я написал.

Мое предложение: Самое простое изменение с углами, вместо того, чтобы сделать это:

if (grid.val[0] < isolevel) cubeindex |= 1; 

Попробуйте изменить его таким образом:

if (grid.val[0] > isolevelMin && grid.val[0] < isolevelMax) cubeindex |= 1; 
// And the same for other lines... 

Final интерполяция является немного сложнее и У меня нет возможности проверить его, но давайте попробуем:

  • Изменить ваш VertexInterp пройти два isolevels - минимальное и максимальное
  • Внутри VertexInterp чек, который isolevel между valp1 и valp2 дорожит
  • Interpolate как в текущем коде, но с использованием выбранного isolevel (мин или макс)

И дайте мне знать, если это сработало :) или если у вас есть вопросы.

2

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

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

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

луч является вектором, в этом случае, если вы идете от плоскости х, у, с одной стороны на другую, это будет что-то вроде этого [псевдокоде]

bool[,,] points = getpoints(); 
bool[,,] outsidepoints = new bool[width,height,depth]; 
//this is traceray from x,y,0 to x,y,depth 
for(int x=0;x<width;x++){ 
    for(int y=0;y<height;y++){ 
    for(int z=0;z<depth;z++){ 
     if (points[x,y,z]==true){ 
     outsidepoints[x,y,z]=true 
     z=depth;//we break the last for 
     } 
    } 
    } 
} 

сделать то же самое для всех 6 комбинаций в последнем цикле (x = 0..width, x = width..0, y = 0..height и т. Д.)

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

когда у вас есть ваши внешние точки просто нужен алгоритм, чтобы сделать их на треугольники (что-то вроде делают ломтики соединений в каждой плоскости вдоль оси, то шила в течение последних 2-х соединений.)

пс: если то, что вы хотите, просто отображается, вы можете использовать raytracing, беря значение первой точки, которая соединяется с лучом для каждой точки вашего дисплея (ожидайте изображение каждые пару секунд в зависимости от вашего оборудования и размера изображения). Для этого вы можете используйте алгоритм рисования линии (проверьте википедию, есть образец, в 2d). Лучше всего это то, что вы можете напрямую получить цвет с вашего изображения (просто установите порог, при котором пиксель недействителен (луч проходит)).

другого редактирования: если вам нужна точность, П.М. меня, или комментарий здесь

используя проход через лучи:

bool lastwasempty=true; 
for(int x=0;x<width;x++){ 
    for(int y=0;y<height;y++){ 
    for(int z=0;z<depth;z++){ 
     if (points[x,y,z]==true){ 
     if(lastwasempty){ 
      outsidepoints[x,y,z]=true 
     } 
     lastwasempty=false; 
     }else{ 
     lastwasempty=true; 
     } 
    } 
    } 
} 
+1

Проблема в том, что мне нужно, чтобы изображение было полым в соответствии с минимальным заданным значением. Я бы дал +1, если вы покажете мне, как получить точки на краях внутри сферы, а не только внешнюю границу. Спасибо за «алгоритм рисования линии». – None

+1

@ Только вы можете попытаться пропустить лучи (не ломаться при первом попадании), а затем проверить, была ли точка раньше прозрачной (false), я добавлю код для этого в ответ. – satibel

+1

@ В самом деле, код будет определять, есть ли пустое место после того, как вы разобрали все стороны (и сверху и снизу), прежде чем один из способов пройдет по другому пути. для создания печатаемой модели вы, вероятно, могли бы просто сканировать по строке или сделать куб, центрированный вокруг каждой точки, что, вероятно, сделало бы что-то уродливое. Поэтому я подумал, что если вам нужна более плавная модель, вы можете использовать алгоритм поверхности подразделения Catmull-Clark. для создания 3D-файла для печати вы можете экспортировать его в obj http://www.hodge.net.au/sam/blog/wp-content/uploads/obj_format.txt и использовать слайсер или просто генерировать g-код , – satibel