Converting Author's map in Suneater Series to Map of Milky Way Galaxy

For the Sun Eater Series by Christopher Ruocchio

url: AMAZON

There is a map in BOOK 6 which looks like

I want to convert the map to one in which I can see star system embedded within the Milky Way Galaxy which I can get a copy of in Wikipedia. So I converted it to the one below

Using the following code and the Luxor package.



println("Starting program")

"""
  pixeldist(A,B)
"""
function pixeldist(A,B)
    fA = Float64.(A)
    fB = Float64.(B)
    return sqrt(  (fA[1]-fB[1])^2.0  +  (fA[2]-fB[2])^2.0  )
end

mapsize=[3600,2700]
gc2mc(ms,gx,gy)=[gx,ms[2]-gy]
gc2mc(ms,g)=gc2mc(ms,g[1],g[2])
mc2gc(ms,mx,my)=[mx,ms[2]-my]
mc2gc(ms,m)=mc2gc(ms,m[1],m[2])

earth_gc = [1700,2100]
earth_mc = gc2mc(mapsize,earth_gc)

galaticcore_gc = [1786,1120]
galaticcore_mc = gc2mc(mapsize,galaticcore_gc)

delos_gc = [1933,2201]
delos_mc = gc2mc(mapsize,delos_gc)

# Earth dist to Galatic Core is 27140 light years
earthdist2gc = 27140.0

# Galatic core relative to earth
gc_relative_mc = galaticcore_mc - earth_mc
gc_relative_angle_mc = atan(1.0*gc_relative_mc[1]/gc_relative_mc[2])
gc_pixeldist = pixeldist(earth_mc,galaticcore_mc)

# Rotation function
function Rotation2D_rad(angle,point)
    fpoint = Float64.(point)
    return [fpoint[1]*cos(angle)-fpoint[2]*sin(angle),fpoint[1]*sin(angle)+fpoint[2]*cos(angle)]
end
function Rotation2D_deg(angle,point)
    fpoint = Float64.(point)
    return [fpoint[1]*cosd(angle)-fpoint[2]*sind(angle),fpoint[1]*sind(angle)+fpoint[2]*cosd(angle)]
end

# Asserting that rotation function works properly
@assert Rotation2D_deg(90.0,[1.0,0.0])==[0.0,1.0]  "Rotation by 90deg did not work. Mark 1"
@assert Rotation2D_deg(90.0,[0.0,1.0])==[-1.0,0.0]  "Rotation by 90deg did not work. Mark 2"
@assert isapprox(Rotation2D_deg(45.0,[1.0,0.0]),[1.0/sqrt(2),1.0/sqrt(2)],atol=5.0e-15)  "Rotation by 45deg did not work. Mark 1"
@assert isapprox(Rotation2D_deg(45.0,[0.0,1.0]),[-1.0/sqrt(2),1.0/sqrt(2)],atol=5.0e-15)  "Rotation by 45deg did not work. Mark 2"
@assert isapprox(Rotation2D_rad(pi/2,[1.0,0.0]),[0.0,1.0],atol=5.0e-15)  "Rotation by pi/2 did not work. Mark 1"
@assert isapprox(Rotation2D_rad(pi/2,[0.0,1.0]),[-1.0,0.0],atol=5.0e-15)  "Rotation by pi/2 did not work. Mark 2"
@assert isapprox(Rotation2D_rad(pi/4,[1.0,0.0]),[1.0/sqrt(2),1.0/sqrt(2)],atol=5.0e-15)  "Rotation by pi/4 did not work. Mark 1"
@assert isapprox(Rotation2D_rad(pi/4,[0.0,1.0]),[-1.0/sqrt(2),1.0/sqrt(2)],atol=5.0e-15)  "Rotation by pi/4 did not work. Mark 2"

# function to get XYcoor
function getXYcoor(origin_mc,point_mc)
    global gc_relative_angle_mc,gc_pixeldist
    origin = Float64.(origin_mc)
    point  = Float64.(point_mc)
    xy = point - origin
    xy = Rotation2D_rad(gc_relative_angle_mc,xy)
    xy = xy ./ gc_pixeldist
    return xy
end

delos_xy = getXYcoor(earth_mc,delos_mc)

println("The xy coor for galatic core is ",getXYcoor(earth_mc,galaticcore_mc))
println("The xy coor for delos        is ",delos_xy)

#=
Now we prepare the output map
=#
outputmapsize=[5600,5600]
outputearth_gc = [2800,3873]
outputearth_mc = gc2mc(outputmapsize,outputearth_gc)

outputgalaticcore_gc = [2800,2800]
outputgalaticcore_mc = gc2mc(outputmapsize,outputgalaticcore_gc)

output_gc_pixeldist = pixeldist(outputearth_mc,outputgalaticcore_mc)
println("The output galatic core pixel distance is ",Int64(output_gc_pixeldist))

function getoutputgc(origin_mc,point_xy)
    global outputmapsize,output_gc_pixeldist
    origin = Float64.(origin_mc)
    point  = Float64.(point_xy)
    point  = point .* output_gc_pixeldist
    point  = point + origin
    point  = mc2gc(outputmapsize,point)
    return point
end

function getoutputgraphicalcoor(origin_mc,point_xy)
    return Int64.(round.(getoutputgc(origin_mc,point_xy),digits=0))
end

function getoutput(point_xy)
    global outputearth_mc
    return getoutputgraphicalcoor(outputearth_mc,point_xy)
end

delos_goc = getoutput(delos_xy)
println("The output graphical coor for delos is ",delos_goc)

# Now get emesh_goc
emesh_gc = [2091,1505]
emesh_mc = gc2mc(mapsize,emesh_gc)
emesh_xy = getXYcoor(earth_mc,emesh_mc)
emesh_goc = getoutput(emesh_xy)
println("The output graphical coor for emesh is ",emesh_goc)

# Now setup Luxor
using Luxor,Colors

# Function to plot object
function suneater_drawobj(title,pos;
    halign="left",textoffsetX=40,textoffsetY=0,primarycolor="red")
    local font = "Times"
    local fontsize = 80
    local p = Point(pos[1], pos[2])
    local circlesize = 16  # 25
    local shadowoffset = 5
    sethue("black")
    circle(p + (shadowoffset,shadowoffset), circlesize, :fill)
    sethue(primarycolor)
    circle(p, circlesize, :fill)
    sethue("black")
    setfont(font, fontsize)
    settext(title, p + (textoffsetX+shadowoffset, textoffsetY+shadowoffset), halign=halign, valign="center")
    sethue(primarycolor)
    setfont(font, fontsize)
    settext(title, p + (textoffsetX, textoffsetY), halign=halign, valign="center")
end

board_img = readpng("/Users/ssiew/juliascript/suneater/MilkyWayGalaxy.png")
const canvas_width::Int64  = outputmapsize[1]
const canvas_height::Int64 = outputmapsize[2]
Drawing(canvas_width, canvas_height, "MilkyWayGalaxySuneaterAtlas.png")

placeimage(board_img, Point(0,0), 1.0)

suneater_drawobj("Delos",delos_goc,primarycolor="blue")
suneater_drawobj("Emesh",emesh_goc,primarycolor="blue",textoffsetX=20,textoffsetY=-20)

# Processing Forum
forum_gc  = [1513,2080]
forum_mc  = gc2mc(mapsize,forum_gc)
forum_xy  = getXYcoor(earth_mc,forum_mc)
forum_goc = getoutput(forum_xy)
suneater_drawobj("Forum",forum_goc,textoffsetX=-120,textoffsetY=-60)

# Processing Avalon
avalon_gc  = [1675,2149]
avalon_mc  = gc2mc(mapsize,avalon_gc)
avalon_xy  = getXYcoor(earth_mc,avalon_mc)
avalon_goc = getoutput(avalon_xy)
suneater_drawobj("Avalon",avalon_goc)

# Processing Durannos
durannos_gc  = [1392,2317]
durannos_mc  = gc2mc(mapsize,durannos_gc)
durannos_xy  = getXYcoor(earth_mc,durannos_mc)
durannos_goc = getoutput(durannos_xy)
suneater_drawobj("Durannos",durannos_goc)

# Processing Tiryns
tiryns_gc  = [1689,2401]
tiryns_mc  = gc2mc(mapsize,tiryns_gc)
tiryns_xy  = getXYcoor(earth_mc,tiryns_mc)
tiryns_goc = getoutput(tiryns_xy)
suneater_drawobj("Tiryns",tiryns_goc)

# Processing Jadd
jadd_gc  = [986,1801]
jadd_mc  = gc2mc(mapsize,jadd_gc)
jadd_xy  = getXYcoor(earth_mc,jadd_mc)
jadd_goc = getoutput(jadd_xy)
suneater_drawobj("Jadd",jadd_goc)

# Processing Colchis
colchis_gc  = [1264,1713]
colchis_mc  = gc2mc(mapsize,colchis_gc)
colchis_xy  = getXYcoor(earth_mc,colchis_mc)
colchis_goc = getoutput(colchis_xy)
suneater_drawobj("Colchis",colchis_goc)

# Processing Padmurak
padmurak_gc  = [1053,1329]
padmurak_mc  = gc2mc(mapsize,padmurak_gc)
padmurak_xy  = getXYcoor(earth_mc,padmurak_mc)
padmurak_goc = getoutput(padmurak_xy)
suneater_drawobj("Padmurak",padmurak_goc)

# Processing Eue
eue_gc  = [1673,671]
eue_mc  = gc2mc(mapsize,eue_gc)
eue_xy  = getXYcoor(earth_mc,eue_mc)
eue_goc = getoutput(eue_xy)
suneater_drawobj("Eue",eue_goc,textoffsetX=20,textoffsetY=-40)

# Processing Nessus
nessus_gc  = [1957,1801]
nessus_mc  = gc2mc(mapsize,nessus_gc)
nessus_xy  = getXYcoor(earth_mc,nessus_mc)
nessus_goc = getoutput(nessus_xy)
suneater_drawobj("Nessus",nessus_goc)

# Processing Gododdin
gododdin_gc  = [1924,1884]
gododdin_mc  = gc2mc(mapsize,gododdin_gc)
gododdin_xy  = getXYcoor(earth_mc,gododdin_mc)
gododdin_goc = getoutput(gododdin_xy)
suneater_drawobj("Gododdin",gododdin_goc)

# Processing Tavros
tavros_gc  = [2586,2240]
tavros_mc  = gc2mc(mapsize,tavros_gc)
tavros_xy  = getXYcoor(earth_mc,tavros_mc)
tavros_goc = getoutput(tavros_xy)
suneater_drawobj("Tavros",tavros_goc)

# Processing Sabratha
sabratha_gc  = [2349,2443]
sabratha_mc  = gc2mc(mapsize,sabratha_gc)
sabratha_xy  = getXYcoor(earth_mc,sabratha_mc)
sabratha_goc = getoutput(sabratha_xy)
suneater_drawobj("Sabratha",sabratha_goc)

# Processing Perfugium
perfugium_gc  = [2197,1784]
perfugium_mc  = gc2mc(mapsize,perfugium_gc)
perfugium_xy  = getXYcoor(earth_mc,perfugium_mc)
perfugium_goc = getoutput(perfugium_xy)
suneater_drawobj("Perfugium",perfugium_goc,primarycolor="blue")

# Processing Carteia
carteia_gc  = [2333,1741]
carteia_mc  = gc2mc(mapsize,carteia_gc)
carteia_xy  = getXYcoor(earth_mc,carteia_mc)
carteia_goc = getoutput(carteia_xy)
suneater_drawobj("Carteia",carteia_goc)

# Processing Marinus
marinus_gc  = [1912,1608]
marinus_mc  = gc2mc(mapsize,marinus_gc)
marinus_xy  = getXYcoor(earth_mc,marinus_mc)
marinus_goc = getoutput(marinus_xy)
suneater_drawobj("Marinus",marinus_goc,textoffsetX=20)

# Processing Nemavand
nemavand_gc  = [2219,1624]
nemavand_mc  = gc2mc(mapsize,nemavand_gc)
nemavand_xy  = getXYcoor(earth_mc,nemavand_mc)
nemavand_goc = getoutput(nemavand_xy)
suneater_drawobj("Nemavand",nemavand_goc,textoffsetX=20,primarycolor="blue")

# Processing Thagura
thagura_gc  = [2516,1587]
thagura_mc  = gc2mc(mapsize,thagura_gc)
thagura_xy  = getXYcoor(earth_mc,thagura_mc)
thagura_goc = getoutput(thagura_xy)
suneater_drawobj("Thagura",thagura_goc)

# Processing Vaiartu
vaiartu_gc  = [3017,1481]
vaiartu_mc  = gc2mc(mapsize,vaiartu_gc)
vaiartu_xy  = getXYcoor(earth_mc,vaiartu_mc)
vaiartu_goc = getoutput(vaiartu_xy)
suneater_drawobj("Vaiartu",vaiartu_goc)

# Processing Cressgard
cressgard_gc  = [2236,1400]
cressgard_mc  = gc2mc(mapsize,cressgard_gc)
cressgard_xy  = getXYcoor(earth_mc,cressgard_mc)
cressgard_goc = getoutput(cressgard_xy)
suneater_drawobj("Cressgard",cressgard_goc,textoffsetX=20,textoffsetY=-40,primarycolor="blue")

# Processing Aptucca
aptucca_gc  = [2513,1432]
aptucca_mc  = gc2mc(mapsize,aptucca_gc)
aptucca_xy  = getXYcoor(earth_mc,aptucca_mc)
aptucca_goc = getoutput(aptucca_xy)
suneater_drawobj("Aptucca",aptucca_goc)

# Processing Latarra
latarra_gc  = [1607,1511]
latarra_mc  = gc2mc(mapsize,latarra_gc)
latarra_xy  = getXYcoor(earth_mc,latarra_mc)
latarra_goc = getoutput(latarra_xy)
suneater_drawobj("Latarra",latarra_goc)

# Processing Rustam
rustam_gc  = [1938,1520]
rustam_mc  = gc2mc(mapsize,rustam_gc)
rustam_xy  = getXYcoor(earth_mc,rustam_mc)
rustam_goc = getoutput(rustam_xy)
suneater_drawobj("Rustam",rustam_goc)

# Processing Monmara
monmara_gc  = [1998,1415]
monmara_mc  = gc2mc(mapsize,monmara_gc)
monmara_xy  = getXYcoor(earth_mc,monmara_mc)
monmara_goc = getoutput(monmara_xy)
suneater_drawobj("Monmara",monmara_goc)

# Processing Pharos
pharos_gc  = [2143,1340]
pharos_mc  = gc2mc(mapsize,pharos_gc)
pharos_xy  = getXYcoor(earth_mc,pharos_mc)
pharos_goc = getoutput(pharos_xy)
suneater_drawobj("Pharos",pharos_goc,textoffsetX=-160,textoffsetY=-40)

# Processing Nairi
nairi_gc  = [1928,2542]
nairi_mc  = gc2mc(mapsize,nairi_gc)
nairi_xy  = getXYcoor(earth_mc,nairi_mc)
nairi_goc = getoutput(nairi_xy)
suneater_drawobj("Nairi",nairi_goc)

# Processing Caria
caria_gc  = [2121,2513]
caria_mc  = gc2mc(mapsize,caria_gc)
caria_xy  = getXYcoor(earth_mc,caria_mc)
caria_goc = getoutput(caria_xy)
suneater_drawobj("Caria",caria_goc,primarycolor="blue")

# Processing Lurash
lurash_gc  = [872,1894]
lurash_mc  = gc2mc(mapsize,lurash_gc)
lurash_xy  = getXYcoor(earth_mc,lurash_mc)
lurash_goc = getoutput(lurash_xy)
suneater_drawobj("Lurash",lurash_goc)

# Processing Oannos
oannos_gc  = [800,1693]
oannos_mc  = gc2mc(mapsize,oannos_gc)
oannos_xy  = getXYcoor(earth_mc,oannos_mc)
oannos_goc = getoutput(oannos_xy)
suneater_drawobj("Oannos",oannos_goc)

# Processing Edda
edda_gc  = [2575,2195]
edda_mc  = gc2mc(mapsize,edda_gc)
edda_xy  = getXYcoor(earth_mc,edda_mc)
edda_goc = getoutput(edda_xy)
suneater_drawobj("Edda",edda_goc,textoffsetX=-120,textoffsetY=-50,primarycolor="blue")

# Processing Teukros
teukros_gc  = [2229,2019]
teukros_mc  = gc2mc(mapsize,teukros_gc)
teukros_xy  = getXYcoor(earth_mc,teukros_mc)
teukros_goc = getoutput(teukros_xy)
suneater_drawobj("Teukros",teukros_goc)

# Processing Annica
annica_gc  = [2252,672]
annica_mc  = gc2mc(mapsize,annica_gc)
annica_xy  = getXYcoor(earth_mc,annica_mc)
annica_goc = getoutput(annica_xy)
suneater_drawobj("Annica",annica_goc)

finish()
preview()
4 Likes

Is it worth to read?

I am looking for new SF books since years and most of the time I was disappointed. My favorites are Hyperion by Dan Simmons, Dune by Frank Herbert and The Lensman series (quite old I realize now, didn’t know when I read it). I liked Expanse lately, but really disliked The Three-Body Problem (the first book was not so bad, but then… :-1:). I tried many but the most I didn’t enjoyed.

Do you think I should give The Sun Eaters a try?

Taken from a review of the book on Goodreads

quote

Empire of Silence, the first book in the epic Suneater series and the author’s debut novel, is a fantastic interstellar fantasy tale. Standing tall at 617 pages, a length far too long for most books, it leaves you wanting another 600 pages of this terrific writing. It’s one of those books that you know is top notch right from the getgo and it never wavers or falters.

This is not some quick reading science fiction sword and planet story. Rather, it’s a rich, layered tale that creates complex characters, a universe that is vast and detailed, and a plot line that has no let up. It has no dull moments.

Like Herbert’s Dune series, it begins with humankind spread throughout the galaxy, primarily in an imperium with planets ruled by intermarrying feudal lords. There is also an all powerful religious order that brooks no heresy. And, as in Dune, the lead character, Hadrian, is a noble heir to a planetary fiefdom and trained by the best in logic, language, and the art of fighting. And, here, there is of course swordplay and shields and gladiator battles.

But Hadrian is a very complex character and his complexity is very important to the storyline which takes him from the world of palace intrigue and sibling rivalry to a world where he is penniless, destitute, and powerless. Part of the tale is the beginning of his suneater legend, but he is more a reluctant hero than the normal swashbuckling warrior.

All in all, just terrific and leaves you waiting desperately for the next installment in this new series.

end quote

Review for BOOK 2

quote

“We believe our civilization the product of our struggles when in truth we are its products. We are its children, raised behind walls.”

Howling Dark is a fantastic sequel to Empire of Silence, and this series is on its way to becoming one of my favourites if the subsequent entries are as good as this one was. I had been informed that this book was a large improvement over the previous one but I didn’t realize how much better it was in almost every aspect until I read it.

Ruocchio’s writing is smooth and reads effortlessly. The descriptions of the Dark, the alien Cielcin, the planets, the machines along with the ethereal feeling of the many visions and otherworldly sequences were captivating and lent to the book being very atmospheric. Every location felt distinct and memorable. The action and battle scenes were also very well-written and had a sense of danger and urgency. The pacing is excellent, being much improved from the meandering pace of the first book. Even in its slower and reflective moments, there is a constant tension that drives the book forward.

"The ugliness of the world does not fade, and fear and grief are not made less by time. We are only made stronger. We can only float together on their tides, as otters do, hand in hand.”

Hadrian’s character continues to grow and change, there are times where glimpses are seen of the Hadrian he will eventually become. The side characters such as Pallino, Valka, Lin, and others have distinct voices and personalities; the few new characters that are introduced are also quite interesting and have an imposing presence on-page. They play off each other very well and the different relationships Hadrian forms with each of them play a major role in the story.

The worldbuilding is organically expanded and has some intriguing mysteries that form the backbone of the story, besides the main plot involving the lost planet and the Cielcin. The incorporation of various sci-fi elements like Artificial Intelligences, genetic manipulation, terrible machines into this universe’s lore and the part they play was very interesting to read; I am partial to stories that involve mysterious civilizations and ancient history that the characters slowly have to uncover and piece together and this book had a lot of that.

“Our swords shall play the orators for us.”

The Cielcin was another aspect that I enjoyed — the portrayal of a truly alien culture, showing the impassable gulf between the two species due to their fundamental differences in wiring and how even their language differs in subtext and meaning was exceptionally well-done. It was fascinating to read about the communication barrier causing an irrevocable chasm between them. They are so other to humanity that ascribing human ways of thinking and logic to them is nigh impossible.

The only somewhat major gripe I had was in the first 15-20% of the book. Due to there being a time skip between the end of the first book and the start of this one, it felt like I missed a whole chapter in the story. I don’t usually have an issue with time skips but it felt jarring in this case specifically because some of the initial chapters relied on emotional beats involving a few new side characters and those did not land for me. It took me a while to get invested but once that initial speedbump was crossed, the rest of the book was enthralling. Overall, a great sequel in every aspect and I can’t wait to get to the third one, especially given how this book ended.

end quote

Review for BOOK 3

quote

I was the Red Devil of Meidua, the Emperor’s own black knight and pet sorcerer. I was the Halfmortal, the man they said could not be killed.

Hadrian has now defeated two Cielen princes and discovered a horrific alliance between the Extrasolarians and the Cielens. Monstrous alien and monstrous robot combines to change the game completely.
However, Hadrian’s exploits have turned him into a legend. An inspiration. And there are many at court who don’t like his newfound power and fame.
Thus, he is shipped off to find out the truth of missing legions in the middle of nowhere where with a Prince 107th in line to the throne acting as squire on the Emperor’s orders.

”The Empire does not want a hero, and you’re giving them one anyway.
They’re finding they’ve lost control of the narrative, and it scares them."

Hadrian is someone I feel a sort of kinship too. He spends literally all of his time either reading, or doing other tasks with basically an audiobook playing in his ears. That’s me on a daily basis.

Hadrian’s development from book one and his youthful optimism in peace and collaboration to him absolutely detesting and becoming hellbent on destroying the Cielen is incredible. And terrifying. Bravo.

“You thought we were the villains?”
“I wasn’t sure I believed in villains at all," I said shortly, “or I believed we all were villains. The Cielcin and us.”

The concepts explored in this series are always so well-crafted.
For example, the process of fugue - putting people to sleep until they’re needed - allows the plot to move forward without ruining logical time lines. Hence, centuries can pass, and yet Hadrian can still be alive and considered fit to serve - not only due to his paladin blood, but due to the years he wasn’t actually awake. Yet what does this mean for the people he leaves behind? The differences in society, relationships, infrastructure that he notices, but is expected to move past.
Hadrian draws attention to the thousands of soldiers kept sleeping in Imperial storage, like corpses waiting to rise again. Creepy, yet utterly realistic if you think about what our own capitalist-efficient world would do with such technology in a time of war. Literal toy soldiers.

In this instalment, Marlowe explores ideas more commonly found in Issac Asimov’s works. The idea of the servant machine rising up to become master, yet still intent on protecting mankind and seeing no harm befalls them, even if it means their enslavement.

There’s also a lot of wibbly wobbly timey wimey stuff (yes that’s a Doctor Who reference), which honestly, I still can’t really get my head around. Futures running backwards, time passing in different states…. My brain after a day in university was not ready to be baffled by even more puzzling, ingenious, concepts.
If book one was more fantasy sprinkled with sci fi, this book has definitely transformed this to an epic science fiction opera.

I can’t fully explain my lower rating for this book without diving in to spoilers which is a shame as most people rate this as their favourite book of the series. Mine remains the first.
It just felt like there was a seriously cop-out, and I hate when characters appear invincible and extremely powerful.
I know the entire narrative structure sets this up so we know Hadrian survives and is recounting his story as a scholar, yet what Marlowe does to end an epic battle felt disproportionately like a disappointment.
Perhaps also I must point out that I don’t typically find myself engrossed in long, drawn-out fights and the last 35% of this book was just all battle.

Of course, I will continue and hope that this was just a minor blip!

end quote

1 Like

Thank you, Dave and Pranav Prabhu, for your opinion. But what is Steven saying?

:rofl:

I think, I will give it a try… perhaps I let you know if I liked it, if I remember this thread here.

New source code which loads the image directly from URL, so you can just run the source code to create the final Atlas image after you add the packages into the environment.

# This source code requires the following packages
# Luxor,Colors,HTTP,Images

println("Starting program")

"""
  pixeldist(A,B)
"""
function pixeldist(A,B)
    fA = Float64.(A)
    fB = Float64.(B)
    return sqrt(  (fA[1]-fB[1])^2.0  +  (fA[2]-fB[2])^2.0  )
end

mapsize=[3600,2700]
gc2mc(ms,gx,gy)=[gx,ms[2]-gy]
gc2mc(ms,g)=gc2mc(ms,g[1],g[2])
mc2gc(ms,mx,my)=[mx,ms[2]-my]
mc2gc(ms,m)=mc2gc(ms,m[1],m[2])

earth_gc = [1700,2100]
earth_mc = gc2mc(mapsize,earth_gc)

galaticcore_gc = [1786,1120]
galaticcore_mc = gc2mc(mapsize,galaticcore_gc)

delos_gc = [1933,2201]
delos_mc = gc2mc(mapsize,delos_gc)

# Earth dist to Galatic Core is 27140 light years
earthdist2gc = 27140.0

# Galatic core relative to earth
gc_relative_mc = galaticcore_mc - earth_mc
gc_relative_angle_mc = atan(1.0*gc_relative_mc[1]/gc_relative_mc[2])
gc_pixeldist = pixeldist(earth_mc,galaticcore_mc)

# Rotation function
function Rotation2D_rad(angle,point)
    fpoint = Float64.(point)
    return [fpoint[1]*cos(angle)-fpoint[2]*sin(angle),fpoint[1]*sin(angle)+fpoint[2]*cos(angle)]
end
function Rotation2D_deg(angle,point)
    fpoint = Float64.(point)
    return [fpoint[1]*cosd(angle)-fpoint[2]*sind(angle),fpoint[1]*sind(angle)+fpoint[2]*cosd(angle)]
end

# Asserting that rotation function works properly
@assert Rotation2D_deg(90.0,[1.0,0.0])==[0.0,1.0]  "Rotation by 90deg did not work. Mark 1"
@assert Rotation2D_deg(90.0,[0.0,1.0])==[-1.0,0.0]  "Rotation by 90deg did not work. Mark 2"
@assert isapprox(Rotation2D_deg(45.0,[1.0,0.0]),[1.0/sqrt(2),1.0/sqrt(2)],atol=5.0e-15)  "Rotation by 45deg did not work. Mark 1"
@assert isapprox(Rotation2D_deg(45.0,[0.0,1.0]),[-1.0/sqrt(2),1.0/sqrt(2)],atol=5.0e-15)  "Rotation by 45deg did not work. Mark 2"
@assert isapprox(Rotation2D_rad(pi/2,[1.0,0.0]),[0.0,1.0],atol=5.0e-15)  "Rotation by pi/2 did not work. Mark 1"
@assert isapprox(Rotation2D_rad(pi/2,[0.0,1.0]),[-1.0,0.0],atol=5.0e-15)  "Rotation by pi/2 did not work. Mark 2"
@assert isapprox(Rotation2D_rad(pi/4,[1.0,0.0]),[1.0/sqrt(2),1.0/sqrt(2)],atol=5.0e-15)  "Rotation by pi/4 did not work. Mark 1"
@assert isapprox(Rotation2D_rad(pi/4,[0.0,1.0]),[-1.0/sqrt(2),1.0/sqrt(2)],atol=5.0e-15)  "Rotation by pi/4 did not work. Mark 2"

# function to get XYcoor
function getXYcoor(origin_mc,point_mc)
    global gc_relative_angle_mc,gc_pixeldist
    origin = Float64.(origin_mc)
    point  = Float64.(point_mc)
    xy = point - origin
    xy = Rotation2D_rad(gc_relative_angle_mc,xy)
    xy = xy ./ gc_pixeldist
    return xy
end

delos_xy = getXYcoor(earth_mc,delos_mc)

println("The xy coor for galatic core is ",getXYcoor(earth_mc,galaticcore_mc))
println("The xy coor for delos        is ",delos_xy)

#=
Now we prepare the output map
=#
outputmapsize=[5600,5600]
outputearth_gc = [2800,3873]
outputearth_mc = gc2mc(outputmapsize,outputearth_gc)

outputgalaticcore_gc = [2800,2800]
outputgalaticcore_mc = gc2mc(outputmapsize,outputgalaticcore_gc)

output_gc_pixeldist = pixeldist(outputearth_mc,outputgalaticcore_mc)
println("The output galatic core pixel distance is ",Int64(output_gc_pixeldist))

function getoutputgc(origin_mc,point_xy)
    global outputmapsize,output_gc_pixeldist
    origin = Float64.(origin_mc)
    point  = Float64.(point_xy)
    point  = point .* output_gc_pixeldist
    point  = point + origin
    point  = mc2gc(outputmapsize,point)
    return point
end

function getoutputgraphicalcoor(origin_mc,point_xy)
    return Int64.(round.(getoutputgc(origin_mc,point_xy),digits=0))
end

function getoutput(point_xy)
    global outputearth_mc
    return getoutputgraphicalcoor(outputearth_mc,point_xy)
end

delos_goc = getoutput(delos_xy)
println("The output graphical coor for delos is ",delos_goc)

# Now get emesh_goc
emesh_gc = [2091,1505]
emesh_mc = gc2mc(mapsize,emesh_gc)
emesh_xy = getXYcoor(earth_mc,emesh_mc)
emesh_goc = getoutput(emesh_xy)
println("The output graphical coor for emesh is ",emesh_goc)

# Now setup Luxor and other packages
using Luxor,Colors
import HTTP
import Images

# Function to plot object
function suneater_drawobj(title,pos;
    halign="left",textoffsetX=40,textoffsetY=0,primarycolor="red")
    local font = "Times"
    local fontsize = 80
    local p = Point(pos[1], pos[2])
    local circlesize = 16  # 25
    local shadowoffset = 5
    sethue("black")
    circle(p + (shadowoffset,shadowoffset), circlesize, :fill)
    sethue(primarycolor)
    circle(p, circlesize, :fill)
    sethue("black")
    setfont(font, fontsize)
    settext(title, p + (textoffsetX+shadowoffset, textoffsetY+shadowoffset), halign=halign, valign="center")
    sethue(primarycolor)
    setfont(font, fontsize)
    settext(title, p + (textoffsetX, textoffsetY), halign=halign, valign="center")
end

#=
    Read an JPEG image from URL into Luxor
    requires package Luxor,HTTP,Images
=#
function ReadJPEGImgFromURL(url::String;verbose=false)
    function vprintln(args...)
        if verbose == true
            println(args...)
        end
    end
    local jpeg_filename = tempname() * ".jpg"
    vprintln("jpeg_filename is ", jpeg_filename)
    HTTP.download(url, jpeg_filename)
    # Now convert JPEG file into PNG file   
    local jpeg_img = Images.load(jpeg_filename)
    png_filename = tempname() * ".png"
    vprintln("png_filename is ", png_filename)
    Images.save(png_filename, jpeg_img)
    local board_img = Luxor.readpng(png_filename)
    rm(jpeg_filename)
    rm(png_filename)
    return board_img
end

# Original map URL is
# https://i.imgur.com/DFyPjui.jpeg
# https://upload.wikimedia.org/wikipedia/commons/1/12/Artist%27s_impression_of_the_Milky_Way_%28updated_-_annotated%29.jpg
board_img = ReadJPEGImgFromURL("https://upload.wikimedia.org/wikipedia/commons/1/12/Artist%27s_impression_of_the_Milky_Way_%28updated_-_annotated%29.jpg")
const canvas_width::Int64  = outputmapsize[1]
const canvas_height::Int64 = outputmapsize[2]
Drawing(canvas_width, canvas_height, "MilkyWayGalaxySuneaterAtlas_Mark2.png")

placeimage(board_img, Point(0,0), 1.0)

suneater_drawobj("Delos",delos_goc,primarycolor="blue")
suneater_drawobj("Emesh",emesh_goc,primarycolor="blue",textoffsetX=20,textoffsetY=-20)

# Processing Forum
forum_gc  = [1513,2080]
forum_mc  = gc2mc(mapsize,forum_gc)
forum_xy  = getXYcoor(earth_mc,forum_mc)
forum_goc = getoutput(forum_xy)
suneater_drawobj("Forum",forum_goc,textoffsetX=-120,textoffsetY=-60)

# Processing Avalon
avalon_gc  = [1675,2149]
avalon_mc  = gc2mc(mapsize,avalon_gc)
avalon_xy  = getXYcoor(earth_mc,avalon_mc)
avalon_goc = getoutput(avalon_xy)
suneater_drawobj("Avalon",avalon_goc)

# Processing Durannos
durannos_gc  = [1392,2317]
durannos_mc  = gc2mc(mapsize,durannos_gc)
durannos_xy  = getXYcoor(earth_mc,durannos_mc)
durannos_goc = getoutput(durannos_xy)
suneater_drawobj("Durannos",durannos_goc)

# Processing Tiryns
tiryns_gc  = [1689,2401]
tiryns_mc  = gc2mc(mapsize,tiryns_gc)
tiryns_xy  = getXYcoor(earth_mc,tiryns_mc)
tiryns_goc = getoutput(tiryns_xy)
suneater_drawobj("Tiryns",tiryns_goc)

# Processing Jadd
jadd_gc  = [986,1801]
jadd_mc  = gc2mc(mapsize,jadd_gc)
jadd_xy  = getXYcoor(earth_mc,jadd_mc)
jadd_goc = getoutput(jadd_xy)
suneater_drawobj("Jadd",jadd_goc)

# Processing Colchis
colchis_gc  = [1264,1713]
colchis_mc  = gc2mc(mapsize,colchis_gc)
colchis_xy  = getXYcoor(earth_mc,colchis_mc)
colchis_goc = getoutput(colchis_xy)
suneater_drawobj("Colchis",colchis_goc)

# Processing Padmurak
padmurak_gc  = [1053,1329]
padmurak_mc  = gc2mc(mapsize,padmurak_gc)
padmurak_xy  = getXYcoor(earth_mc,padmurak_mc)
padmurak_goc = getoutput(padmurak_xy)
suneater_drawobj("Padmurak",padmurak_goc)

# Processing Eue
eue_gc  = [1673,671]
eue_mc  = gc2mc(mapsize,eue_gc)
eue_xy  = getXYcoor(earth_mc,eue_mc)
eue_goc = getoutput(eue_xy)
suneater_drawobj("Eue",eue_goc,textoffsetX=20,textoffsetY=-40)

# Processing Nessus
nessus_gc  = [1957,1801]
nessus_mc  = gc2mc(mapsize,nessus_gc)
nessus_xy  = getXYcoor(earth_mc,nessus_mc)
nessus_goc = getoutput(nessus_xy)
suneater_drawobj("Nessus",nessus_goc)

# Processing Gododdin
gododdin_gc  = [1924,1884]
gododdin_mc  = gc2mc(mapsize,gododdin_gc)
gododdin_xy  = getXYcoor(earth_mc,gododdin_mc)
gododdin_goc = getoutput(gododdin_xy)
suneater_drawobj("Gododdin",gododdin_goc)

# Processing Tavros
tavros_gc  = [2586,2240]
tavros_mc  = gc2mc(mapsize,tavros_gc)
tavros_xy  = getXYcoor(earth_mc,tavros_mc)
tavros_goc = getoutput(tavros_xy)
suneater_drawobj("Tavros",tavros_goc)

# Processing Sabratha
sabratha_gc  = [2349,2443]
sabratha_mc  = gc2mc(mapsize,sabratha_gc)
sabratha_xy  = getXYcoor(earth_mc,sabratha_mc)
sabratha_goc = getoutput(sabratha_xy)
suneater_drawobj("Sabratha",sabratha_goc)

# Processing Perfugium
perfugium_gc  = [2197,1784]
perfugium_mc  = gc2mc(mapsize,perfugium_gc)
perfugium_xy  = getXYcoor(earth_mc,perfugium_mc)
perfugium_goc = getoutput(perfugium_xy)
suneater_drawobj("Perfugium",perfugium_goc,primarycolor="blue")

# Processing Carteia
carteia_gc  = [2333,1741]
carteia_mc  = gc2mc(mapsize,carteia_gc)
carteia_xy  = getXYcoor(earth_mc,carteia_mc)
carteia_goc = getoutput(carteia_xy)
suneater_drawobj("Carteia",carteia_goc)

# Processing Marinus
marinus_gc  = [1912,1608]
marinus_mc  = gc2mc(mapsize,marinus_gc)
marinus_xy  = getXYcoor(earth_mc,marinus_mc)
marinus_goc = getoutput(marinus_xy)
suneater_drawobj("Marinus",marinus_goc,textoffsetX=20)

# Processing Nemavand
nemavand_gc  = [2219,1624]
nemavand_mc  = gc2mc(mapsize,nemavand_gc)
nemavand_xy  = getXYcoor(earth_mc,nemavand_mc)
nemavand_goc = getoutput(nemavand_xy)
suneater_drawobj("Nemavand",nemavand_goc,textoffsetX=20,primarycolor="blue")

# Processing Thagura
thagura_gc  = [2516,1587]
thagura_mc  = gc2mc(mapsize,thagura_gc)
thagura_xy  = getXYcoor(earth_mc,thagura_mc)
thagura_goc = getoutput(thagura_xy)
suneater_drawobj("Thagura",thagura_goc)

# Processing Vaiartu
vaiartu_gc  = [3017,1481]
vaiartu_mc  = gc2mc(mapsize,vaiartu_gc)
vaiartu_xy  = getXYcoor(earth_mc,vaiartu_mc)
vaiartu_goc = getoutput(vaiartu_xy)
suneater_drawobj("Vaiartu",vaiartu_goc)

# Processing Cressgard
cressgard_gc  = [2236,1400]
cressgard_mc  = gc2mc(mapsize,cressgard_gc)
cressgard_xy  = getXYcoor(earth_mc,cressgard_mc)
cressgard_goc = getoutput(cressgard_xy)
suneater_drawobj("Cressgard",cressgard_goc,textoffsetX=20,textoffsetY=-40,primarycolor="blue")

# Processing Aptucca
aptucca_gc  = [2513,1432]
aptucca_mc  = gc2mc(mapsize,aptucca_gc)
aptucca_xy  = getXYcoor(earth_mc,aptucca_mc)
aptucca_goc = getoutput(aptucca_xy)
suneater_drawobj("Aptucca",aptucca_goc)

# Processing Latarra
latarra_gc  = [1607,1511]
latarra_mc  = gc2mc(mapsize,latarra_gc)
latarra_xy  = getXYcoor(earth_mc,latarra_mc)
latarra_goc = getoutput(latarra_xy)
suneater_drawobj("Latarra",latarra_goc)

# Processing Rustam
rustam_gc  = [1938,1520]
rustam_mc  = gc2mc(mapsize,rustam_gc)
rustam_xy  = getXYcoor(earth_mc,rustam_mc)
rustam_goc = getoutput(rustam_xy)
suneater_drawobj("Rustam",rustam_goc)

# Processing Monmara
monmara_gc  = [1998,1415]
monmara_mc  = gc2mc(mapsize,monmara_gc)
monmara_xy  = getXYcoor(earth_mc,monmara_mc)
monmara_goc = getoutput(monmara_xy)
suneater_drawobj("Monmara",monmara_goc)

# Processing Pharos
pharos_gc  = [2143,1340]
pharos_mc  = gc2mc(mapsize,pharos_gc)
pharos_xy  = getXYcoor(earth_mc,pharos_mc)
pharos_goc = getoutput(pharos_xy)
suneater_drawobj("Pharos",pharos_goc,textoffsetX=-160,textoffsetY=-40)

# Processing Nairi
nairi_gc  = [1928,2542]
nairi_mc  = gc2mc(mapsize,nairi_gc)
nairi_xy  = getXYcoor(earth_mc,nairi_mc)
nairi_goc = getoutput(nairi_xy)
suneater_drawobj("Nairi",nairi_goc)

# Processing Caria
caria_gc  = [2121,2513]
caria_mc  = gc2mc(mapsize,caria_gc)
caria_xy  = getXYcoor(earth_mc,caria_mc)
caria_goc = getoutput(caria_xy)
suneater_drawobj("Caria",caria_goc,primarycolor="blue")

# Processing Lurash
lurash_gc  = [872,1894]
lurash_mc  = gc2mc(mapsize,lurash_gc)
lurash_xy  = getXYcoor(earth_mc,lurash_mc)
lurash_goc = getoutput(lurash_xy)
suneater_drawobj("Lurash",lurash_goc)

# Processing Oannos
oannos_gc  = [800,1693]
oannos_mc  = gc2mc(mapsize,oannos_gc)
oannos_xy  = getXYcoor(earth_mc,oannos_mc)
oannos_goc = getoutput(oannos_xy)
suneater_drawobj("Oannos",oannos_goc)

# Processing Edda
edda_gc  = [2575,2195]
edda_mc  = gc2mc(mapsize,edda_gc)
edda_xy  = getXYcoor(earth_mc,edda_mc)
edda_goc = getoutput(edda_xy)
suneater_drawobj("Edda",edda_goc,textoffsetX=-120,textoffsetY=-50,primarycolor="blue")

# Processing Teukros
teukros_gc  = [2229,2019]
teukros_mc  = gc2mc(mapsize,teukros_gc)
teukros_xy  = getXYcoor(earth_mc,teukros_mc)
teukros_goc = getoutput(teukros_xy)
suneater_drawobj("Teukros",teukros_goc)

# Processing Annica
annica_gc  = [2252,672]
annica_mc  = gc2mc(mapsize,annica_gc)
annica_xy  = getXYcoor(earth_mc,annica_mc)
annica_goc = getoutput(annica_xy)
suneater_drawobj("Annica",annica_goc)

finish()
preview()

1 Like

And not?

  • Riverworld and other stories by Philip Farmer

  • A Wizard of Earthsea by Ursula Le Guin

1 Like

Thanks for those, read them 25 years ago :older_man: and both are definitely great. All time classics of science fiction and fantasy I would say.

1 Like