Question

Est-ce que Mathematica soutien suppression des lignes cachées pour les images en fil de fer? Si ce n'est pas le cas, a personne ici jamais rencontré une façon de le faire? Commençons par ceci:

Plot3D[Sin[x+y^2], {x, -3, 3}, {y, -2, 2}, Boxed -> False]

sortie

Pour créer un fil de fer que nous pouvons faire:

Plot3D[Sin[x+y^2], {x, -3, 3}, {y, -2, 2}, Boxed -> False, PlotStyle -> None]

sortie

Une chose que nous pouvons faire pour obtenir l'effet de colorer la blanc toutes les surfaces. Cependant, cela est indésirable. La raison est que si nous exportons ce modèle de cadre de fil de ligne cachée au format PDF, nous aurons tous ces polygones blancs qui Mathematica utilise pour rendre l'image. Je veux être en mesure d'obtenir un cadre de fil avec suppression des lignes cachées en pdf et / ou EPS.


Mise à jour:

J'ai posté une solution à ce problème. Le problème est que le code fonctionne très lent. Dans son état actuel, il est incapable de générer le wireframe de l'image dans cette question. Ne hésitez pas à jouer avec mon code. J'ai ajouté un lien vers elle à la fin de mon poste. Vous pouvez également trouver le code dans cette lien

Était-ce utile?

La solution

Je présente ici une solution. D'abord, je vais montrer comment utiliser la fonction qui génère le fil de fer, alors je vais passer à expliquer en détail le reste des fonctions qui composent l'algorithme.


wireFrame

wireFrame[g_] := Module[{figInfo, opt, pts},
   {figInfo, opt} = G3ToG2Info[g];
   pts = getHiddenLines[figInfo];
   Graphics[Map[setPoints[#] &, getFrame[figInfo, pts]], opt]
]

L'entrée de cette fonction est un objet Graphics3D de préférence sans axes.

fig = ListPlot3D[
   {{0, -1, 0}, {0, 1, 0}, {-1, 0, 1}, {1, 0, 1}, {-1, 1, 1}},
   Mesh -> {10, 10},
   Boxed -> False,
   Axes -> False,
   ViewPoint -> {2, -2, 1},
   ViewVertical -> {0, 0, 1},
   MeshStyle -> Directive[RGBColor[0, 0.5, 0, 0.5]],
   BoundaryStyle -> Directive[RGBColor[1, 0.5, 0, 0.5]]
]

surface

Maintenant, nous appliquons la fonction wireFrame.

wireFrame[fig]

wireframe

Comme vous pouvez le voir wireFrame obtenu la plupart des lignes et ses couleurs. Il y a une ligne verte qui n'a pas été inclus dans le wireframe. Ceci est probablement dû à mes paramètres de seuil.

Avant de passer à expliquer les détails de la G3ToG2Info fonctions, getHiddenLines, getFrame et setPoints je vais vous montrer pourquoi les cadres de fil avec suppression des lignes cachées peuvent être utiles.

RasterWire

L'image présentée ci-dessus est une capture d'écran d'un fichier PDF généré en utilisant la technique décrite dans rasters dans les graphiques 3D combinés avec le fil de fer généré ici. Cela peut être avantageux de diverses manières. Il n'y a pas besoin de garder l'information pour les triangles pour montrer une surface colorée. Au lieu de cela, nous montrons une image de trame de la surface. Toutes les lignes sont très lisses, à l'exception des limites de la parcelle de trame ne sont pas couvertes par des lignes. Nous avons aussi une réduction de la taille du fichier. Dans ce cas, la taille du fichier pdf réduit de 1,9 Mo à 78kb en utilisant la combinaison du tracé de trame et le fil de fer. Il prend moins de temps à afficher dans l'afficheur pdf et la qualité d'image est grande.

Mathematica fait un très bon travail à l'exportation 3D images en fichiers PDF. Lorsque nous importons les fichiers pdf, on obtient un objet graphique composé de segments de ligne et de triangles. Dans certains cas, ces objets se chevauchent et donc nous avons des lignes cachées. Pour un modèle de fil de fer sans surfaces nous avons d'abord besoin d'enlever ce chevauchement puis retirez les polygones. Je commencerai par décrire comment obtenir les informations d'une image Graphics3D.


G3ToG2Info

getPoints[obj_] := Switch[Head[obj], 
   Polygon, obj[[1]], 
   JoinedCurve, obj[[2]][[1]], 
   RGBColor, {Table[obj[[i]], {i, 1, 3}]}
  ];
setPoints[obj_] := Switch[Length@obj, 
   3, Polygon[obj], 
   2, Line[obj], 
   1, RGBColor[obj[[1]]]
  ];
G3ToG2Info[g_] := Module[{obj, opt},
   obj = ImportString[ExportString[g, "PDF", Background -> None], "PDF"][[1]];
   opt = Options[obj];
   obj = Flatten[First[obj /. Style[expr_, opts___] :> {opts, expr}], 2];
   obj = Cases[obj, _Polygon | _JoinedCurve | _RGBColor, Infinity];
   obj = Map[getPoints[#] &, obj];
   {obj, opt}
  ]

Ce code est pour Mathematica 8 dans la version 7 vous devez remplacer JoinedCurve dans la fonction getPoints par Line. La getPoints fonction suppose que vous donnez un objet Graphics primitif. Il verra quel type d'objet il recieves puis extraire les informations dont il a besoin de lui. S'il est un polygone obtient une liste de 3 points, pour une ligne, il obtient une liste de 2 points et si elle est une couleur puis il obtient une liste d'une liste unique contenant 3 points. Cela a été fait comme ceci afin de maintenir la cohérence avec les listes.

La fonction setPoints fait l'inverse de getPoints. Vous entrez une liste de points et il déterminera si elle doit retourner un polygone, une ligne ou une couleur.

Pour obtenir une liste des triangles, des lignes et des couleurs que nous utilisons G3ToG2Info. Cette fonction utilisera ExportString et ImportString pour obtenir un objet Graphics de la version Graphics3D. Cette information est en magasin obj. Il y a un certain nettoyage que nous devons réaliser, d'abord, nous obtenons les options du obj. Cette partie est nécessaire, car il peut contenir le PlotRange de l'image. On obtient alors tous les objets Polygon, JoinedCurve et RGBColor comme décrit dans obtenir primitives et directives graphiques . Enfin, nous avons unpply la fonction getPoints sur tous ces objets pour obtenir une liste des triangles, des lignes et des couleurs. Cette partie couvre la ligne {figInfo, opt} = G3ToG2Info[g].


getHiddenLines

Nous voulons être en mesure de savoir quelle partie d'une ligne ne sera pas affichée. Pour ce faire, nous devons connaître le point d'intersection entre deux segments de ligne. L'algorithme que je utilise pour trouver l'intersection se trouve .

lineInt[L_, M_, EPS_: 10^-6] := Module[
  {x21, y21, x43, y43, x13, y13, numL, numM, den},
  {x21, y21} = L[[2]] - L[[1]];
  {x43, y43} = M[[2]] - M[[1]];
  {x13, y13} = L[[1]] - M[[1]];
  den = y43*x21 - x43*y21;
  If[den*den < EPS, Return[-Infinity]];
  numL = (x43*y13 - y43*x13)/den;
  numM = (x21*y13 - y21*x13)/den;
  If[numM < 0 || numM > 1, Return[-Infinity], Return[numL]];
 ]

lineInt suppose que la L ligne et M ne coïncident pas. Il reviendra -Infinity si les lignes sont parallèles ou si la ligne contenant le segment L ne traverse pas le segment de ligne M. Si la ligne contenant L croise le segment de ligne M il renvoie un scalaire. Supposons que ce scalaire est u, le point d'intersection est L[[1]] + u (L[[2]]-L[[1]]). Notez qu'il est bien parfaitement pour u être un nombre réel. Vous pouvez jouer avec cette fonction manipuler pour tester le fonctionnement de lineInt.

Manipulate[
   Grid[{{
      Graphics[{
        Line[{p1, p2}, VertexColors -> {Red, Red}],
        Line[{p3, p4}]
       },
       PlotRange -> 3, Axes -> True],
      lineInt[{p1, p2}, {p3, p4}]
     }}],
   {{p1, {-1, 1}}, Locator, Appearance -> "L1"},
   {{p2, {2, 1}}, Locator, Appearance -> "L2"},
   {{p3, {1, -1}}, Locator, Appearance -> "M1"},
   {{p4, {1, 2}}, Locator, Appearance -> "M2"}
]

Exemple

Maintenant que nous savons comment bien nous devons Voyage de L[[1]] au segment ligne M nous pouvons savoir quelle partie d'un mensonge segment de ligne dans un triangle.

lineInTri[L_, T_] := Module[{res},
  If[Length@DeleteDuplicates[Flatten[{T, L}, 1], SquaredEuclideanDistance[#1, #2] < 10^-6 &] == 3, Return[{}]];
  res = Sort[Map[lineInt[L, #] &, {{T[[1]], T[[2]]}, {T[[2]], T[[3]]},  {T[[3]], T[[1]]} }]];
  If[res[[3]] == Infinity || res == {-Infinity, -Infinity, -Infinity}, Return[{}]];
  res = DeleteDuplicates[Cases[res, _Real | _Integer | _Rational], Chop[#1 - #2] == 0 &];
  If[Length@res == 1, Return[{}]];
  If[(Chop[res[[1]]] == 0 && res[[2]] > 1) || (Chop[res[[2]] - 1] == 0 && res[[1]] < 0), Return[{0, 1}]];
  If[(Chop[res[[2]]] == 0 && res[[1]] < 0) || (Chop[res[[1]] - 1] == 0 && res[[2]] > 1), Return[{}]];
  res = {Max[res[[1]], 0], Min[res[[2]], 1]};
  If[res[[1]] > 1 || res[[1]] < 0 || res[[2]] > 1 || res[[2]] < 0, Return[{}], Return[res]];
 ]

Cette fonction retourne la la partie de l'L de ligne qui doit être supprimé. Par exemple, si elle renvoie {.5, 1} cela signifie que vous allez supprimer 50 pour cent de la ligne, à partir de la moitié du segment au point de fin du segment. Si L = {A, B} et la fonction des rendements {u, v} alors cela signifie que le segment de ligne {A+(B-A)u, A+(B-A)v} est la section de la ligne que son contenu dans le triangle T.

Lors de la mise en œuvre lineInTri vous devez veiller à ce que la L de ligne n'est pas l'un des bords de T, si tel est le cas, alors la ligne ne se trouve pas à l'intérieur du triangle. C'est là arrondi erros peut être mauvais. Lorsque Mathematica exporte l'image parfois un mensonge en ligne sur le bord du triangle, mais ces coordonnées diffèrent d'un certain montant. Il nous appartient de décider à quel point les mensonges de ligne sur le bord, sinon la fonction verra que les mensonges de ligne presque complètement à l'intérieur du triangle. Telle est la raison de la première ligne dans la fonction. Pour voir si un mensonge en ligne sur un bord d'un triangle, nous pouvons énumérer tous les points du triangle et la ligne, et supprimer tous les doublons. Vous devez spécifier ce qu'un double est dans ce cas. En fin de compte, si nous nous retrouvons avec une liste de 3 points cela signifie qu'un mensonge en ligne sur un bord. La partie suivante est un peu compliqué. Ce que nous faisons est de vérifier pour l'intersection de la L ligne avec chaque bord du triangle T et stocker ce les résultats dans une liste. Ensuite, nous trier la liste et savoir quelle section, le cas échéant, des mensonges de ligne dans le triangle. Essayez de donner un sens hors de lui en jouant avec cela, certains des tests comprennent la vérification si un point final de la ligne est un sommet du triangle, si la ligne est tout à fait à l'intérieur du triangle, en partie à l'intérieur ou complètement à l'extérieur.

Manipulate[
  Grid[{{
    Graphics[{
      RGBColor[0, .5, 0, .5], Polygon[{p3, p4, p5}],
      Line[{p1, p2}, VertexColors -> {Red, Red}]
     },
     PlotRange -> 3, Axes -> True],
    lineInTri[{p1, p2}, {p3, p4, p5}]
   }}],
 {{p1, {-1, -2}}, Locator, Appearance -> "L1"},
 {{p2, {0, 0}}, Locator, Appearance -> "L2"},
 {{p3, {-2, -2}}, Locator, Appearance -> "T1"},
 {{p4, {2, -2}}, Locator, Appearance -> "T2"},
 {{p5, {-1, 1}}, Locator, Appearance -> "T3"}
]

test triangulaire

lineInTri sera utilisé pour voir quelle partie de la ligne ne sera pas affichée. Cette ligne sera probablement couvert par de nombreux triangles. Pour cette raison, nous devons garder une liste de toutes les parties de chaque ligne qui ne seront pas tirées. Ces listes ne seront pas avoir un ordre. Tout savoir de nous est que cette liste sont un des segments dimensions. Chacun consistant en un nombre dans l'intervalle de [0,1]. Je ne suis pas au courant d'une fonct syndicaleion pour une dimension segments alors voici ma mise en œuvre.

union[obj_] := Module[{p, tmp, dummy, newp, EPS = 10^-3},
  p = Sort[obj];
  tmp = p[[1]];
  If[tmp[[1]] < EPS, tmp[[1]] = 0];
  {dummy, newp} = Reap[
    Do[
     If[(p[[i, 1]] - tmp[[2]]) > EPS && (tmp[[2]] - tmp[[1]]) > EPS, 
       Sow[tmp]; tmp = p[[i]], 
       tmp[[2]] = Max[p[[i, 2]], tmp[[2]]]
      ];
     , {i, 2, Length@p}
    ];
    If[1 - tmp[[2]] < EPS, tmp[[2]] = 1];
    If[(tmp[[2]] - tmp[[1]]) > EPS, Sow[tmp]];
   ];
  If[Length@newp == 0, {}, newp[[1]]]
 ]

Cette fonction serait plus courte, mais ici, j'ai inclus quelques déclarations si pour vérifier si un nombre est proche de zéro ou un. Si un numéro est EPS en dehors de zéro, alors que nous faisons ce chiffre zéro, même pour un. Un autre aspect que je couvre ici est que s'il y a une partie relativement faible du segment à afficher alors il est fort probable qu'il doit être supprimé. Par exemple, si nous avons {{0,.5}, {.500000000001}} cela signifie que nous devons tirer {{.5, .500000000001}}. Mais ce segment est très petit pour être même remarqué spécialement dans un grand segment de ligne, pour tout ce que nous savons que ces deux chiffres sont les mêmes. Toutes ces choses doivent être prises en compte lors de la mise en œuvre union.

Maintenant, nous sommes prêts à voir ce qui doit être supprimé d'un segment de ligne. La prochaine nécessite la liste des objets générés à partir G3ToG2Info, un objet de cette liste et un index.

getSections[L_, obj_, start_ ] := Module[{dummy, p, seg},
  {dummy, p} = Reap[
    Do[
     If[Length@obj[[i]] == 3,
      seg =  lineInTri[L, obj[[i]]];
      If[Length@seg != 0, Sow[seg]];
     ]
     , {i, start, Length@obj}
    ]
   ];
  If[Length@p == 0, Return[{}], Return[union[First@p]]];
 ]

getSections retourne une liste contenant les portions qui doivent être retirés L. Nous savons que obj la liste des triangles, des lignes et des couleurs, nous savons que les objets dans la liste avec un indice supérieur seront tirés au-dessus de ceux avec un indice inférieur. Pour cette raison, nous avons besoin du start index. Ceci est l'indice que nous allons commencer à chercher des triangles dans obj. Une fois que nous trouvons un triangle que nous obtiendrons la partie du segment qui se trouve dans le triangle en utilisant la fonction lineInTri. A la fin, nous finirons avec une liste des sections que nous pouvons combiner en utilisant union.

Enfin, nous arrivons à getHiddenLines. Tout cela demande est de regarder chaque objet dans la liste renvoyée par G3ToG2Info et appliquer la fonction getSections. getHiddenLines retourne une liste de listes. Chaque élément est une liste des sections qui doivent être supprimés.

getHiddenLines[obj_] := Module[{pts},
  pts = Table[{}, {Length@obj}];
  Do[
   If[Length@obj[[j]] == 2,
      pts[[j]] = getSections[obj[[j]], obj, j + 1]
    ];
    , {j, Length@obj}
   ];
   Return[pts];
  ]

getFrame

Si vous avez réussi à comprendre les concepts jusqu'à ici, je suis sûr que vous savez ce qui sera fait ensuite. Si nous avons la liste des triangles, des lignes et des couleurs et les sections des lignes que nous devons à supprimer besoin de tirer uniquement les couleurs et les sections des lignes qui sont visibles. D'abord, nous faisons une fonction complement, cela va nous dire exactement quoi dessiner.

complement[obj_] := Module[{dummy, p},
  {dummy, p} = Reap[
    If[obj[[1, 1]] != 0, Sow[{0, obj[[1, 1]]}]];
    Do[
     Sow[{obj[[i - 1, 2]], obj[[i, 1]]}]
     , {i, 2, Length@obj}
    ];
    If[obj[[-1, 2]] != 1, Sow[{obj[[-1, 2]], 1}]];
   ];
  If[Length@p == 0, {}, Flatten@ First@p]
 ]

Maintenant, la fonction getFrame

getFrame[obj_, pts_] := Module[{dummy, lines, L, u, d},
  {dummy, lines} = Reap[
    Do[
     L = obj[[i]];
     If[Length@L == 2,
      If[Length@pts[[i]] == 0, Sow[L]; Continue[]];
      u = complement[pts[[i]]];
      If[Length@u > 0, 
       Do[
        d = L[[2]] - L[[1]];
        Sow[{L[[1]] + u[[j - 1]] d, L[[1]] + u[[j]] d}]
        , {j, 2, Length@u, 2 }]
      ];
    ];
    If[Length@L == 1, Sow[L]];
    , {i, Length@obj}]
  ];
 First@lines
]

Derniers mots

Je suis un peu satisfait des résultats de l'algorithme. Ce que je ne aime pas la vitesse d'exécution. Je l'ai écrit comme je le ferais en C / C ++ / java en utilisant des boucles. J'ai essayé de mon mieux pour utiliser Reap et Sow pour créer des listes de plus en plus au lieu d'utiliser la fonction Append. Peu importe tout cela, je devais encore utiliser des boucles. Il convient de noter que l'image de fil de fer affiché ici a pris 63 secondes pour générer. J'ai essayé de faire un fil de fer pour l'image dans la question, mais cet objet 3D contient environ 32000 objets. Il a pris environ 13 secondes pour calculer les parties qui doivent être affichées pour une ligne. Si nous supposons que nous avons 32000 lignes et il faut 13 secondes pour faire tous les calculs qui seront environ 116 heures de temps de calcul.

Je suis sûr que ce temps peut être réduit si l'on utilise la fonction Compile sur toutes les routines et peut-être trouver un moyen de ne pas utiliser les boucles de Do. Puis-je obtenir de l'aide ici Stack Overflow?

Pour votre convenance, j'ai téléchargé le code sur le Web. Vous pouvez trouver . Si vous pouvez appliquer une version modifiée de ce code à l'intrigue dans la question et de montrer le fil de fer, je marquerai votre solution comme la réponse à ce message.

Best, J Manuel Lopez

Autres conseils

Ce n'est pas juste, mais un peu intéressant:

Plot3D[Sin[x + y^2], {x, -3, 3}, {y, -2, 2}, Boxed -> False, PlotStyle -> {EdgeForm[None], FaceForm[Red, None]}, Mesh -> False]

Avec un FaceForm de None, le polygone est pas rendu. Je ne suis pas sûr qu'il ya une façon de le faire avec les lignes de maillage.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top