开发者

ListPlot3D of data on "regular" lattice contained in an "irregular" region

I have the following problem when trying to plot some 3D data in Mathematica. The data are initially computed on a regular lattice, that is I compute

data = Flatten[Table[{x,y,f[x,y]},{x,x0,x1,dx},{y,y0,y1,dy}],1]

The problem is that f takes real values only in a non-convex subset U on the plane. U is actually quite nasty: a "triangular" region where all corners are cuspy开发者_运维问答 (imagine the region in between three identical circles with any two of them tangent to each other). When I try to plot data with ListPlot3D, the latter plots a surface over the convex hull of U.

I was wondering if there is a way to tell Mathematica to restrict itself only inside U. I was thinking that since my points already lie on a "regular" lattice this should not be too difficult but I have not found yet a solution.

I am aware of the RegionFunction option but it is very expensive to compute in my case so I am trying to figure out a way that uses only the already computed points in data.


To generate a really nice picture, without using zillions of points, you might want to use a non-uniform distribution of points that includes the boundary of the region you want. Here's an example somewhat like what you describe. We start with three mutually tangent circles.

circPic = Graphics[{Circle[{0, Sqrt[3]}, 1],
  Circle[{-1, 0}, 1], Circle[{1, 0}, 1]}]

ListPlot3D of data on "regular" lattice contained in an "irregular" region

We write a Boolean function that determines whether a point in the rectangle {-1/2,1/2} by {0,Sqrt[3]/2} lies outside of all the circles and use this to generate some points in the region of interest.

inRegionQ[p:{x_, y_}] := Norm[p - {1, 0}] > 1 &&
  Norm[p + {1, 0}] > 1 && Norm[p - {0, Sqrt[3]}] > 1;
rectPoints = N[Flatten[Table[{x, y},
  {x, -1/2, 1/2, 0.02}, {y, 0.05, Sqrt[3]/2, 0.02}], 1]];
regionPoints = Select[rectPoints, inRegionQ];

Now we generate the boundary. The parameter n determines how many points we place on the boundary.

n = 120;
boundary = N[Join[
  Table[{1 - Cos[t], Sin[t]}, {t, Pi/n, Pi/3, Pi/n}],
  Table[{Cos[t], Sqrt[3] - Sin[t]}, {t, Pi/3 + Pi/n, 2 Pi/3, Pi/n}],
  Table[{Cos[t] - 1, Sin[t]}, {t, Pi/3 - Pi/n, 0, -Pi/n}]]];
points = Join[boundary, regionPoints];

Let's take a look.

Show[circPic, Graphics[Point[points]],
  PlotRange -> {{-3/4, 3/4}, {-0.3, 1.3}}]

ListPlot3D of data on "regular" lattice contained in an "irregular" region

Now, we define a function and use ListPlot3D to try to plot it.

f[x_, y_] := -(1 - Norm[{x - 1, y}]) (1 - Norm[{x + 1, y}])*
  (1 - Norm[{x, y - Sqrt[3]}]);
points3D = {#[[1]], #[[2]], f[#[[1]], #[[2]]]} & /@ points;
pic = ListPlot3D[points3D, Mesh -> All]

ListPlot3D of data on "regular" lattice contained in an "irregular" region

Somehow, we've got to delete that stuff that lies outside the region. In this particular example, we can use the fact that the function is zero on the boundary.

DeleteCases[Normal[pic], Polygon[{
  {x1_, y1_, z1_?(Abs[#] < 1/10.0^6 &)},
  {x2_, y2_, z2_?(Abs[#] < 1/10.0^6 &)},
  {x3_, y3_, z3_?(Abs[#] < 1/10.0^6 &)}}, ___],
  Infinity]

ListPlot3D of data on "regular" lattice contained in an "irregular" region

Pretty good, but there are a couple of problems near the cusps and it's definitely not very general since it used a specific property of the function. If you examine the structure of pic, you'll find that it contains a GraphicsComplex and the first n points in the first argument of that GraphicsComplex is exactly the boundary. Here's proof:

Most /@ pic[[1, 1, 1 ;; n]] == boundary

Now the boundary comes in three components and we want to delete any triangle that is formed by points chosen from just one of those components. The following code does this. Note that boundaryComponents contains the indices of those points that form the boundary and someSubsetQ[A,Bs] returns true if A is a subset of any one of the Bs. We want to delete those triangle indices in the multi-Polygon that are subsets of one of the boundaryComponents. That's accomplished in the following code by the DeleteCases command.

Oh, and let's add some decoration, too.

subsetQ[A_, B_] := Complement[A, B] == {};
someSubsetQ[A_, Bs_] := Or @@ Map[subsetQ[A, #] &, Bs];
boundaryComponents = Partition[Prepend[Range[n], n], 1 + n/3, n/3];
Show[pic /. Polygon[triangles_] :> {EdgeForm[Opacity[0.3]],
  Polygon[DeleteCases[triangles, _?(someSubsetQ[#, boundaryComponents] &)]]},
Graphics3D[{Thick, Line[Table[Append[pt, 0],
  {pt, Prepend[boundary, Last[boundary]]}]]}]]


This is probably not the optimal solution, so I will keep the question open in case someone has a better idea.

Here is how I solved the problem I described in my question. First, I replaced points {x,y,f[x,y]} in data for which f[x,y] was complex by {x,y,None}. Then the following function creates a 3D surface out of my data points. Note here that data is the result of

data = Table[{x,y,f[x,y]},{x,x0,x1,dx},{y,y0,y1,dy}]

that is, no flattening (which works better for the following function). The function is:

makeSurface[data_] := Module[{len1, len2, polys, a, b, c, d, p},
  len1 = Length[data];
  len2 = Length[data[[1]]];
  polys = Table[
    a = data[[i, j]];
    b = data[[i + 1, j]];
    c = data[[i + 1, j + 1]];
    d = data[[i, j + 1]];
    p = Select[{a, b, c, d}, #[[3]] =!= None &];
    If[Length[p] >= 2, Polygon[p], None],
    {i, 1, len1 - 1}, {j, 1, len2 - 1}];
  Graphics3D[Join[{EdgeForm[None],FaceForm[Directive[GrayLevel[0.5]]]},
    Select[Flatten[polys, 1], # =!= None &]]]]

The above code can be probably optimized but it worked well enough for me.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜