Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gnuplot: Respect depthorder for Gnuplot 3d polygons

Tags:

render

gnuplot

3d

I have a 3D model of a complete city, and would like to show an isometric view of those buildings. I use gnuplot polygons for this, since I don't think I can use pm3d for polygons with coordinates that aren't on a well defined grid. The polygons are displayed with a color corresponding to the average amount of solar irradiance they get. I also display the ground polygons as wireframe, mostly to set xrange, yrange and zrange automatically.

It looks okay from far away, but when you zoom in, you see that the depthorder of some polygons are wrong.

This is the CityGML model imported into SketchUp : enter image description here

This is what comes out of Gnuplot : enter image description here

Do you have any idea how I could display those polygons in the right order?

Something like this would be great, but it only seems to work with splot.

Here is a Gnuplot file to see the problem (2 Buildings and 4 roofs) :

set style line 1 linecolor rgbcolor "#777777" linewidth 1
set style increment user
set palette file "-"
0.00000 0.23137 0.22353 0.64314
0.17990 0.47843 0.34510 0.71765
0.32010 0.34902 0.17647 0.55686
0.43990 0.40784 0.10980 0.36471
0.59010 0.46275 0.09020 0.16863
0.78000 0.86667 0.41569 0.24314
0.92990 0.92549 0.66667 0.33725
1.00000 0.97647 0.91765 0.82745
e
set cbrange [50:150]
set cbtics 25
set format cb "%.0f W/m²"
unset xtics
unset ytics
unset ztics
unset border
set colorbox

kzoom=1.0
phi=30.0
theta=60.0
set xyplane 0       # removes the offset of the xy plane
set view equal xyz  # force equal units to all three axes
set view theta,phi,kzoom

set object 1 polygon from \
2.0,0.0,3.0 to \
3.0,0.0,3.5 to \
3.0,2.0,3.5 to \
2.0,2.0,3.0 to \
2.0,0.0,3.0
set object 1 fill solid 0.9 border lw 2 fc palette cb 128.1315 front

set object 2 polygon from \
4.0,0.0,3.0 to \
4.0,2.0,3.0 to \
3.0,2.0,3.5 to \
3.0,0.0,3.5 to \
4.0,0.0,3.0
set object 2 fill solid 0.9 border lw 2 fc palette cb 128.62192 front

set object 3 polygon from \
2.0,0.0,0.0 to \
4.0,0.0,0.0 to \
4.0,0.0,3.0 to \
3.0,0.0,3.5 to \
2.0,0.0,3.0 to \
2.0,0.0,0.0
set object 3 fill solid 0.9 border lw 2 fc palette cb 100.545204

set object 4 polygon from \
4.0,0.0,0.0 to \
4.0,2.0,0.0 to \
4.0,2.0,3.0 to \
4.0,0.0,3.0 to \
4.0,0.0,0.0
set object 4 fill solid 0.9 border lw 2 fc palette cb 85.58082

set object 5 polygon from \
4.0,2.0,0.0 to \
2.0,2.0,0.0 to \
2.0,2.0,3.0 to \
3.0,2.0,3.5 to \
4.0,2.0,3.0 to \
4.0,2.0,0.0
set object 5 fill solid 0.9 border lw 2 fc palette cb 55.88219

set object 6 polygon from \
2.0,2.0,0.0 to \
2.0,0.0,0.0 to \
2.0,0.0,3.0 to \
2.0,2.0,0.0
set object 6 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 7 polygon from \
2.0,2.0,0.0 to \
2.0,0.0,3.0 to \
2.0,2.0,3.0 to \
2.0,2.0,0.0
set object 7 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 8 polygon from \
0.0,0.0,3.0 to \
1.0,0.0,3.5 to \
1.0,2.0,3.5 to \
0.0,2.0,3.0 to \
0.0,0.0,3.0
set object 8 fill solid 0.9 border lw 2 fc palette cb 128.1315 front

set object 9 polygon from \
2.0,0.0,3.0 to \
2.0,2.0,3.0 to \
1.0,2.0,3.5 to \
1.0,0.0,3.5 to \
2.0,0.0,3.0
set object 9 fill solid 0.9 border lw 2 fc palette cb 128.62192 front

set object 10 polygon from \
0.0,0.0,0.0 to \
2.0,0.0,0.0 to \
2.0,0.0,3.0 to \
1.0,0.0,3.5 to \
0.0,0.0,3.0 to \
0.0,0.0,0.0
set object 10 fill solid 0.9 border lw 2 fc palette cb 100.545204

set object 11 polygon from \
2.0,0.0,0.0 to \
2.0,2.0,0.0 to \
2.0,2.0,3.0 to \
2.0,0.0,3.0 to \
2.0,0.0,0.0
set object 11 fill solid 0.9 border lw 2 fc palette cb 85.58082

set object 12 polygon from \
2.0,2.0,0.0 to \
0.0,2.0,0.0 to \
0.0,2.0,3.0 to \
1.0,2.0,3.5 to \
2.0,2.0,3.0 to \
2.0,2.0,0.0
set object 12 fill solid 0.9 border lw 2 fc palette cb 55.88219

set object 13 polygon from \
0.0,2.0,0.0 to \
0.0,0.0,0.0 to \
0.0,0.0,3.0 to \
0.0,2.0,0.0
set object 13 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 14 polygon from \
0.0,2.0,0.0 to \
0.0,0.0,3.0 to \
0.0,2.0,3.0 to \
0.0,2.0,0.0
set object 14 fill solid 0.9 border lw 2 fc palette cb 85.25754

splot "-" with lines notitle ls 1, NaN notitle palette
2.0 0.0 0.0
2.0 2.0 0.0
4.0 2.0 0.0
4.0 0.0 0.0
2.0 0.0 0.0

0.0 0.0 0.0
0.0 2.0 0.0
2.0 2.0 0.0
2.0 0.0 0.0
0.0 0.0 0.0
e

pause mouse

Thanks for the interest!

like image 214
Eric Duminil Avatar asked Nov 09 '16 13:11

Eric Duminil


3 Answers

I could not find an option to convince gnuplot that it should set the depthorder of the polygon objects automatically. The only way I found was to already define the objects in the correct order. Then gnuplot draws each of them on top of the previous one which leads to the expected result.

Taking your example polygons after removing the front specifiers, this approach changes the original picture from this one:

enter image description here

to this one (after removing front):

enter image description here

to this one (after sorting):

enter image description here

I think the third picture is what you want.

I used a python script to sort the polygons, which basically does:

  • Read all polygons from a file "poly_raw.dat". The file must start with a set object x polygon from line.
  • Like gnuplot with set view theta, phi, it rotates the polygons around x and z axis (temporarily, just for sorting).
  • It sorts the polygons with respect to the new y-axis. This should be the axis which is orthogonal to the screen.
  • It updates the gnuplot object IDs, i.e. the x in set object x polygon.
  • It prints the sorted polygon data including the set object lines.

Notice that this approach only works for static pictures. It does not work if you want to interactively rotate the picture.

Also notice, if the approach does not work for your full data file, I have probably messed up the rotation matrix or the axis assignment :)

I attach the python script, the plot file, and the data files "plot_raw.dat" and "plot_sorted.dat" for reference.

# poly.py
from math import cos, sin, pi
from re import sub, match, findall

class Polygon:

   def __init__(self):

      self.firstLine = ""
      self.points = []
      self.lastLine = ""


   def __str__(self):

      pointStrings = [",".join(map(str,point)) for point in self.points]

      s = ""
      s += self.firstLine
      s += " to \\\n".join(pointStrings)
      s += "\n"
      s += self.lastLine

      return s


   def readObjectFromFile(self, f, firstObjectLine):
      self.firstLine = firstObjectLine
      line = f.readline()
      while not match("set",line):
         coordinates = findall("[\d.]+", line)
         coordinates = tuple(float(x) for x in coordinates)
         self.points.append(coordinates)
         line = f.readline()
      self.lastLine = line

      return self


   def meanAfterRotation(self, theta, phi):
      cx = 0.0
      cy = 0.0
      cz = 0.0

      theta = theta*pi/180.0
      phi = phi*pi/180.0

      for point in self.points:
         x,y,z = point
         x,y,z = (x, y*cos(theta) - z*sin(theta), y*sin(theta) + z*cos(theta))
         x,y,z = (x*cos(phi) - y*sin(phi), x*sin(phi) + y*cos(phi), z)
         cx, cy, cz = (cx + x, cy + y, cz + z)

      n = len(self.points)

      return (cx/n, cy/n, cz/n)


   def setPosition(self, posIndex):

      self.firstLine = sub("(\d+)", str(posIndex), self.firstLine, 1)
      self.lastLine = sub("(\d+)", str(posIndex), self.lastLine, 1)




polygons = []
with open("poly_raw.dat") as f:

    line = f.readline()
    while line:

       if line.strip() != "":
          polygons.append(Polygon().readObjectFromFile(f,line))

       line = f.readline()

polygons.sort(key = lambda x: x.meanAfterRotation(60,30)[1], reverse = True)
pos = 1
for p in polygons:
   p.setPosition(pos)
   pos += 1

for p in polygons:
   print(p)

The gnuplot script:

# poly.plt, gnuplot 4.6
# Does not work when interactively rotating the picture.

set style line 1 linecolor rgbcolor "#777777" linewidth 1
set style increment user
set palette file "-"
0.00000 0.23137 0.22353 0.64314
0.17990 0.47843 0.34510 0.71765
0.32010 0.34902 0.17647 0.55686
0.43990 0.40784 0.10980 0.36471
0.59010 0.46275 0.09020 0.16863
0.78000 0.86667 0.41569 0.24314
0.92990 0.92549 0.66667 0.33725
1.00000 0.97647 0.91765 0.82745
e
set cbrange [50:150]
set cbtics 25
set format cb "%.0f W/m²"
unset xtics
unset ytics
unset ztics
unset border
set colorbox

kzoom=1.0
phi=30.0
theta=60.0
set xyplane 0       # removes the offset of the xy plane
set view equal xyz  # force equal units to all three axes
set view theta,phi,kzoom

#load "poly_raw.dat"
load "poly_sorted.dat"

set terminal png
#set output "raw.png"
set output "sorted.png"
splot "-" with lines notitle ls 1, NaN notitle palette
2.0 0.0 0.0
2.0 2.0 0.0
4.0 2.0 0.0
4.0 0.0 0.0
2.0 0.0 0.0

0.0 0.0 0.0
0.0 2.0 0.0
2.0 2.0 0.0
2.0 0.0 0.0
0.0 0.0 0.0
e

The unsorted polygon data after removing the front specifiers:

set object 1 polygon from \
2.0,0.0,3.0 to \
3.0,0.0,3.5 to \
3.0,2.0,3.5 to \
2.0,2.0,3.0 to \
2.0,0.0,3.0
set object 1 fill solid 0.9 border lw 2 fc palette cb 128.1315

set object 2 polygon from \
4.0,0.0,3.0 to \
4.0,2.0,3.0 to \
3.0,2.0,3.5 to \
3.0,0.0,3.5 to \
4.0,0.0,3.0
set object 2 fill solid 0.9 border lw 2 fc palette cb 128.62192

set object 3 polygon from \
2.0,0.0,0.0 to \
4.0,0.0,0.0 to \
4.0,0.0,3.0 to \
3.0,0.0,3.5 to \
2.0,0.0,3.0 to \
2.0,0.0,0.0
set object 3 fill solid 0.9 border lw 2 fc palette cb 100.545204

set object 4 polygon from \
4.0,0.0,0.0 to \
4.0,2.0,0.0 to \
4.0,2.0,3.0 to \
4.0,0.0,3.0 to \
4.0,0.0,0.0
set object 4 fill solid 0.9 border lw 2 fc palette cb 85.58082

set object 5 polygon from \
4.0,2.0,0.0 to \
2.0,2.0,0.0 to \
2.0,2.0,3.0 to \
3.0,2.0,3.5 to \
4.0,2.0,3.0 to \
4.0,2.0,0.0
set object 5 fill solid 0.9 border lw 2 fc palette cb 55.88219

set object 6 polygon from \
2.0,2.0,0.0 to \
2.0,0.0,0.0 to \
2.0,0.0,3.0 to \
2.0,2.0,0.0
set object 6 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 7 polygon from \
2.0,2.0,0.0 to \
2.0,0.0,3.0 to \
2.0,2.0,3.0 to \
2.0,2.0,0.0
set object 7 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 8 polygon from \
0.0,0.0,3.0 to \
1.0,0.0,3.5 to \
1.0,2.0,3.5 to \
0.0,2.0,3.0 to \
0.0,0.0,3.0
set object 8 fill solid 0.9 border lw 2 fc palette cb 128.1315

set object 9 polygon from \
2.0,0.0,3.0 to \
2.0,2.0,3.0 to \
1.0,2.0,3.5 to \
1.0,0.0,3.5 to \
2.0,0.0,3.0
set object 9 fill solid 0.9 border lw 2 fc palette cb 128.62192

set object 10 polygon from \
0.0,0.0,0.0 to \
2.0,0.0,0.0 to \
2.0,0.0,3.0 to \
1.0,0.0,3.5 to \
0.0,0.0,3.0 to \
0.0,0.0,0.0
set object 10 fill solid 0.9 border lw 2 fc palette cb 100.545204

set object 11 polygon from \
2.0,0.0,0.0 to \
2.0,2.0,0.0 to \
2.0,2.0,3.0 to \
2.0,0.0,3.0 to \
2.0,0.0,0.0
set object 11 fill solid 0.9 border lw 2 fc palette cb 85.58082

set object 12 polygon from \
2.0,2.0,0.0 to \
0.0,2.0,0.0 to \
0.0,2.0,3.0 to \
1.0,2.0,3.5 to \
2.0,2.0,3.0 to \
2.0,2.0,0.0
set object 12 fill solid 0.9 border lw 2 fc palette cb 55.88219

set object 13 polygon from \
0.0,2.0,0.0 to \
0.0,0.0,0.0 to \
0.0,0.0,3.0 to \
0.0,2.0,0.0
set object 13 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 14 polygon from \
0.0,2.0,0.0 to \
0.0,0.0,3.0 to \
0.0,2.0,3.0 to \
0.0,2.0,0.0
set object 14 fill solid 0.9 border lw 2 fc palette cb 85.25754

The sorted data after running python poly.py > poly_sorted.dat

set object 1 polygon from \
4.0,0.0,0.0 to \
4.0,2.0,0.0 to \
4.0,2.0,3.0 to \
4.0,0.0,3.0 to \
4.0,0.0,0.0
set object 1 fill solid 0.9 border lw 2 fc palette cb 85.58082

set object 2 polygon from \
4.0,2.0,0.0 to \
2.0,2.0,0.0 to \
2.0,2.0,3.0 to \
3.0,2.0,3.5 to \
4.0,2.0,3.0 to \
4.0,2.0,0.0
set object 2 fill solid 0.9 border lw 2 fc palette cb 55.88219

set object 3 polygon from \
2.0,2.0,0.0 to \
2.0,0.0,0.0 to \
2.0,0.0,3.0 to \
2.0,2.0,0.0
set object 3 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 4 polygon from \
2.0,2.0,0.0 to \
2.0,0.0,3.0 to \
2.0,2.0,3.0 to \
2.0,2.0,0.0
set object 4 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 5 polygon from \
2.0,0.0,0.0 to \
2.0,2.0,0.0 to \
2.0,2.0,3.0 to \
2.0,0.0,3.0 to \
2.0,0.0,0.0
set object 5 fill solid 0.9 border lw 2 fc palette cb 85.58082

set object 6 polygon from \
2.0,2.0,0.0 to \
0.0,2.0,0.0 to \
0.0,2.0,3.0 to \
1.0,2.0,3.5 to \
2.0,2.0,3.0 to \
2.0,2.0,0.0
set object 6 fill solid 0.9 border lw 2 fc palette cb 55.88219

set object 7 polygon from \
2.0,0.0,0.0 to \
4.0,0.0,0.0 to \
4.0,0.0,3.0 to \
3.0,0.0,3.5 to \
2.0,0.0,3.0 to \
2.0,0.0,0.0
set object 7 fill solid 0.9 border lw 2 fc palette cb 100.545204

set object 8 polygon from \
0.0,2.0,0.0 to \
0.0,0.0,0.0 to \
0.0,0.0,3.0 to \
0.0,2.0,0.0
set object 8 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 9 polygon from \
4.0,0.0,3.0 to \
4.0,2.0,3.0 to \
3.0,2.0,3.5 to \
3.0,0.0,3.5 to \
4.0,0.0,3.0
set object 9 fill solid 0.9 border lw 2 fc palette cb 128.62192

set object 10 polygon from \
0.0,2.0,0.0 to \
0.0,0.0,3.0 to \
0.0,2.0,3.0 to \
0.0,2.0,0.0
set object 10 fill solid 0.9 border lw 2 fc palette cb 85.25754

set object 11 polygon from \
0.0,0.0,0.0 to \
2.0,0.0,0.0 to \
2.0,0.0,3.0 to \
1.0,0.0,3.5 to \
0.0,0.0,3.0 to \
0.0,0.0,0.0
set object 11 fill solid 0.9 border lw 2 fc palette cb 100.545204

set object 12 polygon from \
2.0,0.0,3.0 to \
3.0,0.0,3.5 to \
3.0,2.0,3.5 to \
2.0,2.0,3.0 to \
2.0,0.0,3.0
set object 12 fill solid 0.9 border lw 2 fc palette cb 128.1315

set object 13 polygon from \
2.0,0.0,3.0 to \
2.0,2.0,3.0 to \
1.0,2.0,3.5 to \
1.0,0.0,3.5 to \
2.0,0.0,3.0
set object 13 fill solid 0.9 border lw 2 fc palette cb 128.62192

set object 14 polygon from \
0.0,0.0,3.0 to \
1.0,0.0,3.5 to \
1.0,2.0,3.5 to \
0.0,2.0,3.0 to \
0.0,0.0,3.0
set object 14 fill solid 0.9 border lw 2 fc palette cb 128.1315

Changes for gnuplot 5.4

It seems that the script produces different output with gnuplot 5.4.1 compared to version 4.6:

  • black buildings
  • black borders of the polygons
  • the drawing is not centered

Here are some workarounds:

  • The black buildings seem to be caused by the border lw 2 part of the set object ... commands. If I remove all the border lw 2, color comes back.

  • Unfortunately, now the borders of the polygons are still black. This can be changed by not removing the border lw 2 part not completely, but replacing it by border lc palette cb 128.62192 with 128.62192 being the same number as in the fc palette cb 128.62192 part.

    Now, the complete set object ... lines look like:

    set object 2 fill solid 0.9 border lc palette cb 128.62192 fc palette cb 128.62192

  • Finally, at least in my tests, the plot is not centered within the image. I worked around this by adding

    set origin -0.2, -0.3

    set size 1.2, 1.2

    just before the splot-command.

like image 106
maij Avatar answered Nov 16 '22 04:11

maij


I know this is an old question, but there is now a gnuplot setting that I think will do what is asked:

set pm3d depthorder base

This sorts pm3d polygons by projecting them onto the base plane (usually z=0) and then sorting by distance from the view point. This algorithm was intended to handle simpler cases like 3D boxes, but your model is close enough being boxes-on-a-base that I think it would benefit from the same treatment. Here is an example from the "boxes3d" demo. enter image description here

like image 2
Ethan Avatar answered Nov 16 '22 05:11

Ethan


Even though this is old question, I believe that made some progress in this sense. I used x-y-z data instead polygons.The correct z-order is achieved creating the faces in a certain sequence. I used a datafile named house.dat

0.0000   0.0000   0.0000
2.0000   0.0000   0.0000
2.0000   0.0000   3.0000
1.0001   0.0000   4.0000
0.9999   0.0000   4.0000
0.0000   0.0000   3.0000
0.0000   0.0000   0.0000

2.0000   0.0000   0.0000
2.0000   2.0000   0.0000
2.0000   2.0000   3.0000
1.0001   2.0000   4.0000
1.0001   0.0000   4.0000
2.0000   0.0000   3.0000
2.0000   0.0000   0.0000

2.0000   2.0000   0.0000
0.0000   2.0000   0.0000
0.0000   2.0000   3.0000
0.9999   2.0000   4.0000
1.0001   2.0000   4.0000
2.0000   2.0000   3.0000
2.0000   2.0000   0.0000

2.0000   0.0000   0.0000
0.0000   0.0000   0.0000
0.0000   0.0000   3.0000
0.9999   0.0000   4.0000
0.9999   2.0000   4.0000
0.0000   2.0000   3.0000
0.0000   2.0000   0.0000
2.0000   0.0000   0.0000

and this gnuplot script

set terminal pngcairo font ',10'
set output "house.png"
set view equal xyz
set view ,,1.5
unset tics
set xyplane at 0
set pm3d depthorder lighting border lw 0.5
set grid ls -1 lc "gray"
set xrange [-1:5]
set yrange [-1:3]
set zrange [0:4]
set cbrange [0:4]
unset colorbox
unset key
unset border

set object polygon from \
    graph 0, 0, 0 to \
    graph 1, 0, 0 to \
    graph 1, 1, 0 to \
    graph 0, 1, 0 to \
    graph 0, 0, 0 fc rgb "dark-plum" fs transparent solid 0.50 noborder

splot \
    "house.dat" u   ($1):2:3:3 w pm3d ,\
    "house.dat" u ($1+2):2:3:3 w pm3d 

to achieve this result:

3D house

Formally It's works for any angle:

3D house rotation

like image 1
GRSousaJr Avatar answered Nov 16 '22 05:11

GRSousaJr