-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathworldsynth.py
executable file
·726 lines (614 loc) · 28.8 KB
/
worldsynth.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
#!/usr/bin/env python
"""
World Generator: Generating worlds
This program is a world generator using various simulations based
on real world phenomenon.
author: Bret Curtis
license: LGPL v2
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
"""
# system libraries
import os, math, numpy, tables
from PySide import QtGui, QtCore, QtUiTools, QtXml
# mapGen libraries
from library.constants import *
from library.menu import Menu
from library.render import Render
from library.heightmap import HeightMap
from library.temperature import Temperature
from library.weather import Weather
from library.rivers import Rivers
from library.biomes import Biomes
class MapGen(QtGui.QMainWindow):
def __init__(self, mapSize=256, debug=False):
'''Attempt to allocate the necessary resources'''
super(MapGen, self).__init__()
# application variables
self.mapSize = (mapSize, mapSize)
# setup our working directories
self.fileLocation = None
self.homeDir = os.path.expanduser('~') + os.sep + '.mapGen'
if not os.path.exists(self.homeDir):
os.makedirs(self.homeDir)
# set our state
# self.settings = QSettings("Mindwerks", "mapGen")
self.viewState = VIEWER_HEIGHTMAP
# set initial world data
self.resetDatasets()
self.elevation = numpy.zeros(self.mapSize)
self.world = {'elevation': self.elevation}
# display the GUI!
self.initUI()
# make our world persistent
self.updateWorld()
# Our debug / autopilot / crash dummy
if debug:
print("Going on full autopilot...")
self.dNewWorld.rPRL.click()
#self.mapSize = [1024,1024]
self.genHeightMap()
def initUI(self):
'''Initialize the GUI for usage'''
width, height = self.mapSize
self.setMinimumSize(512, 512)
self.setWindowTitle('WorldGen: Generating Worlds')
self.sb = self.statusBar()
self.setWindowIcon(QtGui.QIcon('./data/images/icon.png'))
self.menuBar = self.menuBar()
Menu(self)
self.mainImage = QtGui.QLabel(self)
self.mainImage.setPixmap(self.getBlankPixmap(width,height))
self.scrollArea = QtGui.QScrollArea(self)
self.scrollArea.setBackgroundRole(QtGui.QPalette.Dark)
self.scrollArea.setWidget(self.mainImage)
self.setCentralWidget(self.scrollArea)
self.show()
# load our gui files
loader = QtUiTools.QUiLoader()
fileLocation = QtCore.QFile("./data/qt4/dNewWorld.ui")
fileLocation.open(QtCore.QFile.ReadOnly)
self.dNewWorld = loader.load(fileLocation, self)
fileLocation.close()
# New World defaults and attach signals
self.dNewWorld.cSymmetricSize.setCurrentIndex(math.log(width, 2) - 5)
self.dNewWorld.buttonBox.accepted.connect(self.acceptNewWorld)
self.dNewWorld.buttonBox.rejected.connect(self.rejectNewWorld)
self.dNewWorld.gbRoughness.hide()
# Load our default values from UI
self.algorithm = self.getAlgorithm()
self.roughness = self.dNewWorld.sbRoughness.value()
self.avgLandmass = self.dNewWorld.cbAvgLandmass.isChecked()
self.avgElevation = self.dNewWorld.cbAvgElevation.isChecked()
self.hasMountains = self.dNewWorld.cbMountains.isChecked()
self.isIsland = self.dNewWorld.cbIslands.isChecked()
self.hemisphere = self.getHemisphere()
self.seaLevel = self.dNewWorld.sbSeaLevel.value()
def getBlankPixmap(self, width, height):
blank = QtGui.QImage(width, height, QtGui.QImage.Format_Indexed8)
blank.fill(QtGui.QColor(0,0,0))
return QtGui.QPixmap.fromImage(blank)
def resizeEvent(self, e):
# Capture resize event, and align to new layout
offset = 2
sa = self.scrollArea.geometry()
self.mainImage.setGeometry(sa.x(), sa.y(), sa.width() - offset, sa.height() - offset) # set to be just as big as our scrollarea
self.mainImage.setAlignment(QtCore.Qt.AlignCenter) # center our image
def mouseMoveEvent(self, e):
# Give information about graphics being shown
width, height = self.mapSize
mx, my = e.pos().toTuple()
if not self.menuBar.isNativeMenuBar(): # Does menu exists in parent window?
offset = 25 # offset from menu
else:
offset = 0
# calculate our imagearea offsets
sa = self.scrollArea.geometry()
ox = sa.width() / 2 - width / 2
oy = sa.height() / 2 + offset - height / 2
if mx < ox or my < oy or mx > width + ox - 1 or my > height + oy - 1:
return # do not bother going out of range of size
x, y = mx - ox, my - oy # transpose
sX, sY = str(x).zfill(4), str(y).zfill(4) # string formatting
message = ''
if self.viewState == VIEWER_HEIGHTMAP:
message = 'Elevation is: ' + "{:4.2f}".format(self.elevation[x, y])
elif self.viewState == VIEWER_HEATMAP:
message = 'Temperature is: ' + "{:4.2f}".format(self.temperature[x, y])
elif self.viewState == VIEWER_RAINFALL:
message = 'Precipitation is: ' + "{:4.2f}".format(self.rainfall[x, y])
elif self.viewState == VIEWER_WIND:
message = 'Wind strength is: ' + "{:4.2f}".format(self.wind[x, y])
elif self.viewState == VIEWER_DRAINAGE:
message = 'Drainage is: ' + "{:4.2f}".format(self.drainage[x, y])
elif self.viewState == VIEWER_RIVERS:
message = 'River flow is: ' + "{:4.2f}".format(self.rivers[x, y])
elif self.viewState == VIEWER_BIOMES:
message = 'Biome type is: ' + Biomes().biomeType(self.biome[x, y])
elif self.viewState == VIEWER_EROSION:
message = 'Erosion amount is: ' + "{:4.2f}".format(self.erosion[x, y])
elif self.viewState == VIEWER_EROSIONAPP:
message = 'Elevation in eroded heightmap is: ' + "{:4.2f}".format(self.elevation[x, y] - self.erosion[x, y])
self.statusBar().showMessage(' At position: ' + sX + ',' + sY + ' - ' + message)
def genWorld(self):
self.genHeightMap()
self.genHeatMap()
self.genWeatherMap()
self.genDrainageMap()
self.genRiverMap()
self.genBiomeMap()
def getAlgorithm(self):
if self.dNewWorld.rMDA.isChecked():
method = HM_MDA
elif self.dNewWorld.rDSA.isChecked():
method = HM_DSA
elif self.dNewWorld.rSPH.isChecked():
method = HM_SPH
elif self.dNewWorld.rPRL.isChecked():
method = HM_PERLIN
else:
method = None
print("Error: no heightmap algo selected.")
return method
def setAlgorithm(self, method):
if method == HM_MDA:
self.dNewWorld.rMDA.click()
elif method == HM_DSA:
self.dNewWorld.rDSA.click()
elif method == HM_SPH:
self.dNewWorld.rSPH.click()
elif method == HM_PERLIN:
self.dNewWorld.rPRL.click()
else:
print("Error: no heightmap algo selected.")
return
def genHeightMap(self):
'''Generate our heightmap'''
self.sb.showMessage('Generating heightmap...')
# create our heightmap
heightObject = HeightMap(self.mapSize, self.roughness, self.isIsland)
found = False
# method = 3 #testing
while not found: # loop until we have something workable
heightObject.run( self.getAlgorithm() )
#break #testing
if self.avgLandmass and heightObject.landMassPercent() < 0.15:
self.statusBar().showMessage('Too little land mass')
elif self.avgLandmass and heightObject.landMassPercent() > 0.85:
self.statusBar().showMessage('Too much land mass')
elif self.avgElevation and heightObject.averageElevation() < 0.2:
self.statusBar().showMessage('Average elevation is too low')
elif self.avgElevation and heightObject.averageElevation() > 0.8:
self.statusBar().showMessage('Average elevation is too high')
elif self.hasMountains and not heightObject.hasMountains():
self.statusBar().showMessage('Not enough mountains')
else:
found = True
self.elevation = heightObject.heightmap
del heightObject
self.viewHeightMap()
self.statusBar().showMessage('Successfully generated a heightmap!')
def viewHeightMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('heightmap')))
self.viewState = VIEWER_HEIGHTMAP
self.statusBar().showMessage('Viewing heightmap.')
def viewElevation(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('elevation', self.seaLevel)))
self.viewState = VIEWER_HEIGHTMAP
self.statusBar().showMessage('Viewing elevation.')
def viewSeaLevel(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('sealevel', self.seaLevel)))
self.viewState = VIEWER_HEIGHTMAP
self.statusBar().showMessage('Viewing sealevel.')
def getHemisphere(self):
from random import randint
if self.dNewWorld.rbHemisphereRandom.isChecked():
hemisphere = randint(1, 3)
elif self.dNewWorld.rbHemisphereBoth.isChecked():
hemisphere = WGEN_HEMISPHERE_EQUATOR
elif self.dNewWorld.rbHemisphereNorth.isChecked():
hemisphere = WGEN_HEMISPHERE_NORTH
elif self.dNewWorld.rbHemisphereSouth.isChecked():
hemisphere = WGEN_HEMISPHERE_SOUTH
else:
hemisphere = None
self.statusBar().showMessage('Error: No Hemisphere chosen for heatmap.')
return hemisphere
def setHemisphere(self, hemisphere):
if hemisphere == WGEN_HEMISPHERE_EQUATOR:
self.dNewWorld.rbHemisphereBoth.click()
elif hemisphere == WGEN_HEMISPHERE_NORTH:
self.dNewWorld.rbHemisphereNorth.click()
elif hemisphere == WGEN_HEMISPHERE_SOUTH:
self.dNewWorld.rbHemisphereSouth.click()
else:
self.statusBar().showMessage('Error: No Hemisphere chosen for heatmap.')
return hemisphere
def genHeatMap(self):
'''Generate a heatmap based on heightmap'''
if self.elevation is None:
self.statusBar().showMessage('Error: You have not yet generated a heightmap.')
return
self.statusBar().showMessage('Generating heatmap...')
tempObject = Temperature(self.elevation, self.seaLevel, self.getHemisphere())
tempObject.run(sb=self.sb)
self.temperature = tempObject.temperature
del tempObject
self.viewHeatMap()
self.statusBar().showMessage('Successfully generated a heatmap!')
def viewHeatMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('heatmap')))
self.viewState = VIEWER_HEATMAP
self.statusBar().showMessage('Viewing heatmap.')
def viewRawHeatMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('rawheatmap')))
self.viewState = VIEWER_HEATMAP
self.statusBar().showMessage('Viewing raw heatmap.')
def genWeatherMap(self):
'''Generate a weather based on heightmap and heatmap'''
self.sb.showMessage('Generating weather...')
if self.elevation is None:
self.statusBar().showMessage('Error: No heightmap!')
return
if self.temperature is None:
self.statusBar().showMessage('Error: No heatmap!')
return
weatherObject = Weather(self.elevation, self.temperature)
weatherObject.run(self.sb)
self.wind = weatherObject.windMap
self.rainfall = weatherObject.rainMap
self.erosion = weatherObject.erosionMap
del weatherObject
self.viewWeatherMap()
self.statusBar().showMessage('Successfully generated weather!')
def viewWeatherMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('windandrainmap')))
self.viewState = VIEWER_RAINFALL
self.statusBar().showMessage('Viewing weathermap.')
def viewWindMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('windmap')))
self.viewState = VIEWER_WIND
self.statusBar().showMessage('Viewing windmap.')
def viewPrecipitation(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('rainmap')))
self.viewState = VIEWER_RAINFALL
self.statusBar().showMessage('Viewing rainmap.')
def genDrainageMap(self):
'''Generate a fractal drainage map'''
self.sb.showMessage('Generating drainage...')
drainObject = HeightMap(self.mapSize)
drainObject.run(HM_DSA)
self.drainage = drainObject.heightmap
del drainObject
self.viewDrainageMap()
self.statusBar().showMessage('Successfully generated drainage!')
def viewDrainageMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('drainagemap')))
self.viewState = VIEWER_DRAINAGE
self.statusBar().showMessage('Viewing drainmap.')
def genBiomeMap(self):
'''Generate a biome map'''
self.sb.showMessage('Generating biomes...')
if self.elevation is None:
self.statusBar().showMessage('Error: No heightmap!')
return
if self.temperature is None:
self.statusBar().showMessage('Error: No heatmap!')
return
if self.drainage is None:
self.statusBar().showMessage('Error: No drainage!')
return
if self.wind.sum is None or self.rainfall is None:
self.statusBar().showMessage('Error: No weather!')
return
biomeObject = Biomes(self.elevation, self.rainfall, self.drainage, self.temperature, self.seaLevel)
biomeObject.run()
self.biome = biomeObject.biome
self.biomeColour = biomeObject.biomeColourCode
del biomeObject
self.viewBiomeMap()
self.statusBar().showMessage('Successfully generated biomes!')
def viewBiomeMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('biomemap')))
self.viewState = VIEWER_BIOMES
self.statusBar().showMessage('Viewing biomes.')
def genRiverMap(self):
'''Generate a river map'''
self.sb.showMessage('Generating rivers and lakes...')
if self.elevation is None:
self.statusBar().showMessage('Error: No heightmap!')
return
if self.wind is None or self.rainfall is None:
self.statusBar().showMessage('Error: No weather!')
return
if self.drainage is None:
self.statusBar().showMessage('Error: No drainage!')
return
riversObject = Rivers()
riversObject.generate(self.elevation, self.seaLevel, self.rainfall, self.sb)
self.rivers = riversObject.riverMap
self.lakes = riversObject.lakeMap
self.erosion += riversObject.erosionMap
del riversObject
self.viewRiverMap()
self.statusBar().showMessage('Successfully generated rivers and lakes!')
def viewRiverMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('rivermap', self.seaLevel)))
self.viewState = VIEWER_RIVERS
self.statusBar().showMessage('Viewing rivers and lakes.')
def viewErosionMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('erosionmap')))
self.viewState = VIEWER_EROSION
self.statusBar().showMessage('Viewing raw erosion.')
def viewErosionAppliedMap(self):
self.updateWorld()
self.mainImage.setPixmap(QtGui.QPixmap.fromImage(Render(self.world).convert('erosionappliedmap')))
self.viewState = VIEWER_EROSIONAPP
self.statusBar().showMessage('Viewing applied erosion map.')
def updateWorld(self):
# update and package up our world data
self.world = {
'elevation': self.elevation,
'wind': self.wind,
'rainfall': self.rainfall,
'temperature': self.temperature,
'drainage': self.drainage,
'rivers': self.rivers,
'lakes': self.lakes,
'erosion': self.erosion,
'biome': self.biome,
'biomeColour': self.biomeColour,
}
self.mapSize = self.elevation.shape
def resetDatasets(self):
self.elevation = None
self.wind = None
self.rainfall = None
self.temperature = None
self.drainage = None
self.rivers = None
self.lakes = None
self.erosion = None
self.biome = None
self.biomeColour = None
def newWorld(self):
self.dNewWorld.show()
def acceptNewWorld(self):
#size = 2 ** (self.dNewWorld.cSymmetricSize.currentIndex() + 5)
#if self.mapSize[0] != size:
# self.newWorld(size)
# local
width = int(self.dNewWorld.leWidth.text())
height = int(self.dNewWorld.leHeight.text())
# global
self.algorithm = self.getAlgorithm()
self.roughness = self.dNewWorld.sbRoughness.value()
self.avgLandmass = self.dNewWorld.cbAvgLandmass.isChecked()
self.avgElevation = self.dNewWorld.cbAvgElevation.isChecked()
self.hasMountains = self.dNewWorld.cbMountains.isChecked()
self.isIsland = self.dNewWorld.cbIslands.isChecked()
self.hemisphere = self.getHemisphere()
self.seaLevel = self.dNewWorld.sbSeaLevel.value()
self.resetDatasets()
self.mapSize = (width, height)
self.elevation = numpy.zeros(self.mapSize)
self.mainImage.setPixmap(self.getBlankPixmap(width, height))
self.updateWorld()
self.statusBar().showMessage(' Created New World!')
def rejectNewWorld(self):
#size = 2 ** (self.dNewWorld.cSize.currentIndex() + 5)
#if self.mapSize[0] != size:
# self.dNewWorld.cSize.setCurrentIndex(math.log(self.mapSize[0], 2) - 5)
self.statusBar().showMessage(' Canceled New World! ')
def editWorldSettings(self):
# TODO: update this
self.dNewWorld.cSize.setCurrentIndex(math.log(self.mapSize[0], 2) - 5)
self.dNewWorld.sbRoughness.setValue(int(settings['roughness']))
self.dNewWorld.cbAvgLandmass.setCheckState((QtCore.Qt.Unchecked, QtCore.Qt.Checked)[settings['avgLandmass']])
self.dNewWorld.cbAvgElevation.setCheckState((QtCore.Qt.Unchecked, QtCore.Qt.Checked)[settings['avgElevation']])
self.dNewWorld.cbMountains.setCheckState((QtCore.Qt.Unchecked, QtCore.Qt.Checked)[settings['hasMountains']])
self.dNewWorld.cbMountains.setCheckState((QtCore.Qt.Unchecked, QtCore.Qt.Checked)[settings['isIsland']])
self.setAlgorithm(settings['algorithm'])
self.setHemisphere(settings['hemisphere'])
def saveWorld(self):
'''TODO: check if we are currently working on a world, save it.
if not, we ignore the command. '''
self.updateWorld()
alreadyTried = False
if not self.fileLocation and not alreadyTried:
alreadyTried = True
self.saveWorldAs()
else:
h5Filter = tables.Filters(complevel=9, complib='zlib', shuffle=True, fletcher32=True)
h5file = tables.openFile(self.fileLocation, mode='w', title="worldData", filters=h5Filter)
# store our numpy datasets
for k in self.world:
if self.world[k] is not None:
atom = tables.Atom.from_dtype(self.world[k].dtype)
shape = self.world[k].shape
cArray = h5file.createCArray(h5file.root, k, atom, shape)
cArray[:] = self.world[k]
# store our world settings
pyDict = {
'key' : tables.StringCol(itemsize=40),
'value' : tables.UInt16Col(),
}
settingsTable = h5file.createTable('/', 'settings', pyDict)
settings = dict(
width=self.mapSize[0],
height=self.mapSize[1],
algorithm=self.algorithm,
roughness=self.roughness,
avgLandmass=self.avgLandmass,
avgElevation=self.avgElevation,
hasMountains=self.hasMountains,
hemisphere=self.hemisphere,
isIsland=self.isIsland,
seaLevel=self.seaLevel
)
settingsTable.append(list(settings.items()))
settingsTable.cols.key.createIndex() # create an index
h5file.close()
del h5file, h5Filter
def saveWorldAs(self):
'''Present a save world dialog'''
self.fileLocation, _ = QtGui.QFileDialog.getSaveFileName(self, 'Save world as...')
if self.fileLocation:
self.saveWorld()
else:
self.statusBar().showMessage('Canceled save world as.')
def openWorld(self):
'''Open existing world project'''
fileLocation, _ = QtGui.QFileDialog.getOpenFileName(self, 'Open file')
if not fileLocation:
self.statusBar().showMessage('Canceled open world.')
return
if tables.isHDF5File(fileLocation) < 0 :
self.statusBar().showMessage(fileLocation + ' does not exist')
return
elif tables.isHDF5File(fileLocation) == 0 :
self.statusBar().showMessage(fileLocation + ' is not valid')
return
h5file = tables.openFile(fileLocation, mode='r')
# restore our world settings
settings = dict(h5file.root.settings.read())
self.mapSize = (int(settings[b'width']), int(settings[b'height']))
self.algorithm=settings[b'algorithm']
self.roughness=settings[b'roughness']
self.hemisphere=settings[b'hemisphere']
self.avgLandmass=settings[b'avgLandmass']
self.avgElevation=settings[b'avgElevation']
self.hasMountains=settings[b'hasMountains']
self.isIsland=settings[b'isIsland']
self.seaLevel=settings[b'seaLevel']
#TODO: apply to edit screen
# restore our numpy datasets
self.resetDatasets()
for array in h5file.walkNodes("/", "Array"):
exec('self.' + array.name + '= array.read()')
# print h5file
# print dict(h5file.root.settings.read())
h5file.close()
self.fileLocation = fileLocation
del h5file
#print self.elevation[0]
self.updateWorld()
self.statusBar().showMessage('Imported world.')
self.viewHeightMap()
def importWorld(self):
'''Eventually allow importing from all formats, but initially only heightmap
from greyscale png'''
# Create filter of all possible images that Qt supports, then ask user to pick one
files = "Images ("
for file in QtGui.QImageReader.supportedImageFormats():
files += "*."+str(file)+" "
files += ")"
fileLocation, _ = QtGui.QFileDialog.getOpenFileName(self, caption='Import world from...', filter=files)
if not fileLocation:
self.statusBar().showMessage('Aborted.')
return
# Read image from file
image = QtGui.QImageReader(fileLocation).read()
# Gather information about the image
width, height, isGreyscale = image.size().width(), image.size().height(), image.isGrayscale()
self.elevation = numpy.zeros((width, height))
# Debug
#print "Debug: ", fileLocation, image.format(), image.depth(), image.isGrayscale()
#test = QtGui.QColor(image.pixel(0,0))
#print "Image Pixel info: ",test, test.value(), test.valueF(), test.redF(), QtGui.qGray(test.Rgb), QtGui.qGray(test.red(),test.green(),test.blue())
# handle 16-bit greyscale PNGs with PyPNG, Qt doesn't handle this important case
_, fileExtension = os.path.splitext(fileLocation)
foundGrey16 = False
if isGreyscale and fileExtension.lower() == ".png":
import png, itertools
width, height, pixels, meta = png.Reader(str(fileLocation)).asDirect()
if meta['bitdepth'] == 16:
foundGrey16 = True
greyImage16 = numpy.vstack(map(numpy.uint16, pixels))
self.elevation = numpy.flipud(numpy.rot90(greyImage16.reshape((width,height)) / float(2**meta['bitdepth'])))
if not foundGrey16:
# take current image and convert to greyscale
for x in range(width):
for y in range(height):
pixel = QtGui.QColor(image.pixel(x,y))
if isGreyscale:
# just grab the value which is usually the first value (red)
self.elevation[x,y] = pixel.valueF()
else:
# use luminance and perception trick to convert image to greyscale
pixelGrey = 0.299 * pixel.redF() + 0.587 * pixel.greenF() + 0.114 * pixel.blueF()
self.elevation[x,y] = pixelGrey
self.mapSize = self.elevation.shape
self.viewHeightMap()
self.statusBar().showMessage('Successfully imported a heightmap!')
def exportWorld(self):
'''Eventually allow exporting to all formats, but initially only heightmap
as 16-bit greyscale png'''
import png
fileLocation, _ = QtGui.QFileDialog.getSaveFileName(self, 'Export heightmap as...')
width, height = self.mapSize
heightmap = self.elevation.copy() * 65535
heightmap = numpy.flipud(numpy.rot90(heightmap)).astype(numpy.uint16) # massage data
#print heightmap[0]
# png heightmap
pngObject = png.Writer(width, height, greyscale=True, bitdepth=16)
fileObject = open(fileLocation + '.png', 'wb')
pngObject.write(fileObject, heightmap)
fileObject.close()
# raw heightmap
heightmap.flatten('C').tofile(fileLocation + '.raw', format='C')
# heightmap.flatten( 'F' ).tofile( fileLocation+'.raw', format = 'F' )
# csv heightmap
heightmap.flatten('C').tofile(fileLocation + '.csv', sep=",")
# heightmap.flatten( 'F' ).tofile( fileLocation+'.csv', sep = "," )
def aboutApp(self):
'''All about the application'''
pass
def main():
from sys import argv
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--profile", help="profile/benchmark process",
action="store_true")
parser.add_argument("-d", "--debug", help="debug mode",
action="store_true")
parser.add_argument("-v", "--verbosity", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbosity:
print("verbosity turned on")
if args.debug:
print("debug turned on")
debug=True
else:
debug=False
app = QtGui.QApplication(argv)
if args.profile:
import cProfile
cProfile.run('ex = MapGen()')
else:
ex = MapGen(debug=debug)
exit(app.exec_())
if __name__ == '__main__':
main()