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 p
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.
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:
to this one (after removing front
):
to this one (after sorting):
I think the third picture is what you want.
I used a python script to sort the polygons, which basically does:
set object x polygon from
line.set view theta, phi
, it rotates the polygons around x and z axis (temporarily, just for sorting).x
in set object x polygon
.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
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:
Formally It's works for any angle: