Code Sketch
bhimraj
Category: Art
// ============================================================================
// GLOBE-X ? Interactive Global Network & Economic Intelligence
// Kojo 2.9.x compatible CanvasDraw version
//
// IMPORTANT:
// This version uses Kojo's canvasSketch API:
// class Sketch { setup(surface); drawLoop(surface) }
// and imports the CanvasDraw methods with:
// import surface._
//
// It intentionally does NOT use:
// canvas.textSize / canvas.strokeWidth / canvas.mouseX / canvas.mouseY
// canvas.mouseClicked / canvas.keyPressed / canvas.key
//
// Mouse and keyboard interaction use Kojo's global event helpers.
// ============================================================================
import java.awt.Color
cleari()
originBottomLeft()
// ----------------------------------------------------------------------------
// DATA MODELS
// ----------------------------------------------------------------------------
case class Country(
name: String,
code: String,
continent: String,
lat: Double,
lon: Double,
pop: Double,
gdp: Double,
gdpGrowth: Double,
gdpRank: Int,
humidity: Double,
status: String,
connections: List[String]
)
case class NetworkRoute(
fromCode: String,
toCode: String,
routeType: String,
volume: Double
)
case class DataParticle(
fromCode: String,
toCode: String,
var progress: Double,
speed: Double,
routeType: String
)
case class ContinentPoly(
name: String,
coords: List[(Double, Double)]
)
// ----------------------------------------------------------------------------
// CONTINENT OUTLINES
// ----------------------------------------------------------------------------
val continents = List(
ContinentPoly("North America", List(
(-168.0,65.0),(-160.0,71.0),(-140.0,70.0),(-120.0,75.0),
(-90.0,73.0),(-80.0,62.0),(-65.0,58.0),(-55.0,52.0),
(-60.0,44.0),(-70.0,42.0),(-75.0,35.0),(-80.0,25.0),
(-88.0,16.0),(-84.0,10.0),(-95.0,16.0),(-105.0,20.0),
(-115.0,30.0),(-124.0,38.0),(-130.0,54.0),(-160.0,58.0),
(-168.0,65.0)
)),
ContinentPoly("South America", List(
(-77.0,8.0),(-72.0,12.0),(-62.0,10.0),(-50.0,4.0),
(-35.0,-5.0),(-35.0,-12.0),(-40.0,-22.0),(-48.0,-28.0),
(-53.0,-34.0),(-65.0,-45.0),(-67.0,-55.0),(-75.0,-45.0),
(-72.0,-35.0),(-70.0,-20.0),(-81.0,-5.0),(-77.0,8.0)
)),
ContinentPoly("Europe", List(
(-10.0,36.0),(-9.0,43.0),(-4.0,48.0),(2.0,51.0),
(8.0,54.0),(10.0,58.0),(5.0,62.0),(14.0,68.0),
(28.0,71.0),(40.0,68.0),(50.0,65.0),(60.0,60.0),
(40.0,45.0),(30.0,46.0),(20.0,37.0),(12.0,44.0),
(4.0,43.0),(-2.0,37.0),(-10.0,36.0)
)),
ContinentPoly("Africa", List(
(-17.0,15.0),(-17.0,21.0),(-6.0,36.0),(10.0,37.0),
(25.0,32.0),(36.0,28.0),(43.0,12.0),(51.0,12.0),
(45.0,2.0),(40.0,-5.0),(35.0,-15.0),(28.0,-34.0),
(18.0,-34.0),(12.0,-20.0),(9.0,-5.0),(5.0,4.0),
(-4.0,5.0),(-17.0,15.0)
)),
ContinentPoly("Asia", List(
(60.0,60.0),(70.0,72.0),(100.0,76.0),(135.0,74.0),
(170.0,66.0),(160.0,52.0),(140.0,50.0),(130.0,40.0),
(122.0,30.0),(108.0,12.0),(104.0,1.0),(100.0,8.0),
(90.0,22.0),(78.0,8.0),(68.0,24.0),(54.0,16.0),
(36.0,28.0),(36.0,36.0),(50.0,40.0),(60.0,60.0)
)),
ContinentPoly("Australia", List(
(114.0,-22.0),(115.0,-34.0),(122.0,-35.0),(135.0,-35.0),
(140.0,-38.0),(150.0,-37.0),(153.0,-28.0),(145.0,-15.0),
(136.0,-12.0),(124.0,-16.0),(114.0,-22.0)
)),
ContinentPoly("Antarctica", List(
(-180.0,-75.0),(-120.0,-72.0),(-60.0,-65.0),(0.0,-70.0),
(60.0,-67.0),(120.0,-66.0),(180.0,-75.0),(180.0,-88.0),
(-180.0,-88.0),(-180.0,-75.0)
))
)
// ----------------------------------------------------------------------------
// COUNTRY DATA
// ----------------------------------------------------------------------------
val countries = List(
Country("United States","USA","North America",37.09,-95.71,339.9,27360.0,2.5,1,55.0,
"Global Tech Superhub",List("CHN","JPN","DEU","GBR","IND","CAN","MEX")),
Country("China","CHN","Asia",35.86,104.19,1409.7,17790.0,5.2,2,62.0,
"Manufacturing Powerhouse",List("USA","JPN","IND","DEU","RUS","SAU","AUS")),
Country("Germany","DEU","Europe",51.16,10.45,84.4,4456.0,0.2,3,74.0,
"Industrial Core & Engineering",List("FRA","GBR","USA","CHN","ITA","NLD")),
Country("Japan","JPN","Asia",36.20,138.25,124.5,4212.0,1.3,4,68.0,
"Advanced Robotics & Tech",List("USA","CHN","KOR","AUS","IND")),
Country("India","IND","Asia",20.59,78.96,1428.6,3730.0,7.6,5,65.0,
"Fastest Major Growth Engine",List("USA","CHN","JPN","SAU","ARE","SGP","GBR")),
Country("United Kingdom","GBR","Europe",55.37,-3.43,67.7,3332.0,0.5,6,80.0,
"Fintech & Banking Hub",List("USA","DEU","FRA","IND","SGP")),
Country("France","FRA","Europe",46.22,2.21,68.0,3052.0,0.9,7,72.0,
"Aerospace & Nuclear Energy",List("DEU","GBR","ITA","ESP","USA")),
Country("Italy","ITA","Europe",41.87,12.56,58.8,2255.0,0.7,8,67.0,
"Design & Precision Machinery",List("FRA","DEU","ESP","GBR")),
Country("Brazil","BRA","South America",-14.23,-51.92,215.3,2174.0,2.9,9,78.0,
"Agribusiness & Mineral Giant",List("USA","CHN","ARG","DEU")),
Country("Canada","CAN","North America",56.13,-106.34,40.5,2140.0,1.2,10,60.0,
"Critical Minerals & AI",List("USA","GBR","JPN","MEX")),
Country("Russia","RUS","Europe",61.52,105.31,144.2,2000.0,3.2,11,64.0,
"Energy & Raw Materials",List("CHN","IND","TUR","SAU")),
Country("Mexico","MEX","North America",23.63,-102.55,128.5,1790.0,3.1,12,58.0,
"Nearshoring Assembly Nexus",List("USA","CAN","BRA","ESP")),
Country("South Korea","KOR","Asia",35.90,127.76,51.7,1713.0,1.4,13,66.0,
"Semiconductor Fabrication",List("USA","CHN","JPN","VNM")),
Country("Australia","AUS","Oceania",-25.27,133.77,26.6,1688.0,1.8,14,52.0,
"Indo-Pacific Mineral Power",List("CHN","JPN","USA","IND","NZL")),
Country("Spain","ESP","Europe",40.46,-3.74,48.4,1581.0,2.5,15,59.0,
"Renewable Energy & Gateway",List("FRA","ITA","DEU","MEX")),
Country("Indonesia","IDN","Asia",-0.78,113.92,277.5,1371.0,5.0,16,82.0,
"Nickel Refining & ASEAN Core",List("CHN","SGP","MYS","AUS")),
Country("Turkey","TUR","Asia",38.96,35.24,85.3,1154.0,4.5,17,61.0,
"Eurasian Crossroads",List("DEU","RUS","SAU","EGY")),
Country("Netherlands","NLD","Europe",52.13,5.29,17.9,1093.0,0.6,18,81.0,
"Lithography & Rotterdam Port",List("DEU","GBR","FRA","USA")),
Country("Saudi Arabia","SAU","Asia",23.88,45.07,36.4,1069.0,1.7,19,32.0,
"Global Hydrocarbon Leader",List("CHN","IND","USA","ARE","EGY")),
Country("Switzerland","CHE","Europe",46.81,8.22,8.9,905.0,1.1,20,71.0,
"Biotech & Wealth Standard",List("DEU","FRA","ITA","USA")),
Country("Argentina","ARG","South America",-38.41,-63.61,46.0,622.0,-1.6,24,62.0,
"Lithium Basin & Agriculture",List("BRA","USA","ESP")),
Country("Sweden","SWE","Europe",60.12,18.64,10.5,593.0,0.4,26,75.0,
"Clean-Tech Innovation",List("DEU","NOR","GBR")),
Country("Thailand","THA","Asia",15.87,100.99,71.8,512.0,2.7,29,75.0,
"Automotive & Logistics",List("JPN","CHN","USA","MYS")),
Country("Israel","ISR","Asia",31.04,34.85,9.8,509.0,2.0,28,57.0,
"Cybersecurity & Deep Tech",List("USA","GBR","DEU","ARE")),
Country("UAE","ARE","Asia",23.42,53.84,9.5,504.0,3.9,30,50.0,
"Aviation, Logistics & AI",List("IND","SAU","GBR","USA","SGP")),
Country("Singapore","SGP","Asia",1.35,103.81,5.9,501.0,2.8,31,84.0,
"Global Maritime Chokepoint",List("CHN","USA","IND","JPN","MYS")),
Country("Norway","NOR","Europe",60.47,8.46,5.5,485.0,1.0,32,78.0,
"Sovereign Wealth & Hydro",List("SWE","GBR","DEU")),
Country("Bangladesh","BGD","Asia",23.68,90.35,173.0,460.0,5.8,33,77.0,
"Apparel Export & Delta Hub",List("IND","CHN","USA")),
Country("Vietnam","VNM","Asia",14.05,108.27,98.9,430.0,5.0,34,80.0,
"Electronics Supply Nexus",List("CHN","USA","KOR","JPN")),
Country("Malaysia","MYS","Asia",4.21,101.97,34.3,415.0,4.2,35,83.0,
"Semiconductor Testing Hub",List("SGP","CHN","USA","JPN")),
Country("Egypt","EGY","Africa",26.82,30.80,112.7,398.0,3.8,36,48.0,
"Suez Maritime Lifeline",List("SAU","ARE","ITA","TUR")),
Country("Nigeria","NGA","Africa",9.08,8.67,223.8,395.0,3.0,37,79.0,
"African Demography & Energy",List("ZAF","CHN","IND","USA")),
Country("South Africa","ZAF","Africa",-30.55,22.93,60.4,380.0,0.6,38,58.0,
"Platinum Metals & Finance",List("BRA","GBR","CHN","IND")),
Country("Iran","IRN","Asia",32.42,53.68,88.5,366.0,3.5,40,40.0,
"Petrochemical Resources",List("CHN","RUS","TUR","IND")),
Country("Pakistan","PAK","Asia",30.37,69.34,240.5,340.0,2.1,42,56.0,
"CPEC Corridor & Transit",List("CHN","SAU","ARE","TUR")),
Country("New Zealand","NZL","Oceania",-40.90,174.88,5.2,253.0,1.1,50,76.0,
"Agri-Tech & Pacific Clean",List("AUS","CHN","USA","JPN"))
)
val countryMap = countries.map(c => c.code -> c).toMap
// ----------------------------------------------------------------------------
// NETWORK ROUTES
// ----------------------------------------------------------------------------
val routes = List(
NetworkRoute("IND","USA","data",9.0),
NetworkRoute("IND","ARE","finance",8.0),
NetworkRoute("IND","SAU","energy",8.0),
NetworkRoute("IND","CHN","trade",7.0),
NetworkRoute("IND","JPN","trade",7.0),
NetworkRoute("IND","SGP","finance",7.0),
NetworkRoute("CHN","USA","trade",9.0),
NetworkRoute("CHN","JPN","trade",8.0),
NetworkRoute("CHN","SAU","energy",8.0),
NetworkRoute("CHN","RUS","energy",7.0),
NetworkRoute("JPN","USA","data",8.0),
NetworkRoute("KOR","USA","trade",7.0),
NetworkRoute("DEU","FRA","trade",8.0),
NetworkRoute("DEU","GBR","finance",7.0),
NetworkRoute("USA","CAN","trade",9.0),
NetworkRoute("USA","MEX","trade",9.0),
NetworkRoute("USA","GBR","finance",9.0),
NetworkRoute("BRA","ARG","trade",7.0),
NetworkRoute("BRA","USA","trade",7.0),
NetworkRoute("AUS","CHN","energy",8.0),
NetworkRoute("SGP","IND","finance",7.0),
NetworkRoute("SAU","IND","energy",8.0),
NetworkRoute("SAU","CHN","energy",8.0),
NetworkRoute("ZAF","GBR","finance",6.0),
NetworkRoute("NGA","USA","energy",6.0),
NetworkRoute("EGY","ITA","trade",6.0)
)
var particles = routes.zipWithIndex.map {
case (r,i) =>
DataParticle(
r.fromCode,
r.toCode,
(i * 0.13) % 1.0,
0.004 + (i % 4) * 0.0015,
r.routeType
)
}
// ----------------------------------------------------------------------------
// STATE
// ----------------------------------------------------------------------------
var animationTime = 0.0
var selectedCode: Option[String] = Some("IND")
var hoveredCode: Option[String] = None
var showGrid = true
var showNetwork = true
var showDashboard = true
var showContinents = true
var isPaused = false
val mapX = 15.0
val mapY = 55.0
val mapW = 680.0
val mapH = 480.0
val dashX = 705.0
val dashY = 15.0
val dashW = 280.0
val dashH = 570.0
// ----------------------------------------------------------------------------
// HELPERS
// ----------------------------------------------------------------------------
def geoToScreen(lat: Double, lon: Double): (Double,Double) = {
val sx = mapX + ((lon + 180.0) / 360.0) * mapW
val sy = mapY + ((90.0 - lat) / 180.0) * mapH
(sx,sy)
}
def distance(ax: Double, ay: Double, bx: Double, by: Double): Double = {
Math.hypot(ax - bx, ay - by)
}
def updateParticles(): Unit = {
if (!isPaused) {
for (p <- particles) {
p.progress += p.speed
if (p.progress >= 1.0) p.progress = 0.0
}
}
}
def updateState(): Unit = {
if (!isPaused) animationTime += 0.016
updateParticles()
}
// ----------------------------------------------------------------------------
// DRAW BACKGROUND
// ----------------------------------------------------------------------------
def drawBackground(surface: CanvasDraw): Unit = {
import surface._
background(6,12,28)
fill(10,22,44)
stroke(24,48,80)
strokeWeight(2)
rect(mapX,mapY,mapW,mapH)
fill(8,18,38)
noStroke()
rect(dashX,dashY,dashW,dashH)
}
// ----------------------------------------------------------------------------
// DRAW GRID
// ----------------------------------------------------------------------------
def drawGrid(surface: CanvasDraw): Unit = {
import surface._
if (!showGrid) return
stroke(30,60,90,80)
strokeWeight(1)
var lon = -150.0
while (lon <= 180.0) {
val (x1,y1) = geoToScreen(85.0,lon)
val (x2,y2) = geoToScreen(-85.0,lon)
line(x1,y1,x2,y2)
lon += 30.0
}
val lats = List(66.5,23.5,0.0,-23.5,-66.5)
for (lat <- lats) {
val (x1,y1) = geoToScreen(lat,-180.0)
val (x2,y2) = geoToScreen(lat,180.0)
if (lat == 0.0) {
stroke(0,200,255,120)
strokeWeight(2)
} else {
stroke(30,60,90,80)
strokeWeight(1)
}
line(x1,y1,x2,y2)
}
// Labels use the CanvasDraw text command's explicit size parameter.
fill(80,130,180)
text("0 DEG EQUATOR",mapX + 8,mapY + mapH/2 - 3)
text("PRIME MERIDIAN",mapX + mapW/2 + 4,mapY + 14)
}
// ----------------------------------------------------------------------------
// DRAW CONTINENTS
// ----------------------------------------------------------------------------
def drawContinents(surface: CanvasDraw): Unit = {
import surface._
if (!showContinents) return
for (continent <- continents) {
val pts = continent.coords.map {
case (lon,lat) => geoToScreen(lat,lon)
}
stroke(20,110,140,160)
strokeWeight(1)
fill(15,38,62,170)
var i = 0
while (i < pts.length - 1) {
val (x1,y1) = pts(i)
val (x2,y2) = pts(i+1)
line(x1,y1,x2,y2)
i += 1
}
val center = pts(pts.length/2)
fill(60,130,160)
text(continent.name.toUpperCase,center._1 - 22,center._2)
}
}
// ----------------------------------------------------------------------------
// DRAW NETWORK
// ----------------------------------------------------------------------------
def drawConnections(surface: CanvasDraw): Unit = {
import surface._
if (!showNetwork) return
for (route <- routes) {
(countryMap.get(route.fromCode),countryMap.get(route.toCode)) match {
case (Some(from),Some(to)) =>
val (x1,y1) = geoToScreen(from.lat,from.lon)
val (x2,y2) = geoToScreen(to.lat,to.lon)
val selected =
selectedCode.contains(from.code) ||
selectedCode.contains(to.code)
if (selected) {
stroke(0,255,220,220)
strokeWeight(2)
} else {
stroke(20,80,110,80)
strokeWeight(1)
}
val midX = (x1+x2)/2.0
val midY = (y1+y2)/2.0 - 25.0
line(x1,y1,midX,midY)
line(midX,midY,x2,y2)
case _ =>
}
}
}
// ----------------------------------------------------------------------------
// DRAW PARTICLES
// ----------------------------------------------------------------------------
def drawParticles(surface: CanvasDraw): Unit = {
import surface._
if (!showNetwork || isPaused) return
for (p <- particles) {
(countryMap.get(p.fromCode),countryMap.get(p.toCode)) match {
case (Some(from),Some(to)) =>
val (x1,y1) = geoToScreen(from.lat,from.lon)
val (x2,y2) = geoToScreen(to.lat,to.lon)
val midX = (x1+x2)/2.0
val midY = (y1+y2)/2.0 - 25.0
val t = p.progress
val px =
(1-t)*(1-t)*x1 +
2*(1-t)*t*midX +
t*t*x2
val py =
(1-t)*(1-t)*y1 +
2*(1-t)*t*midY +
t*t*y2
val active =
selectedCode.contains(from.code) ||
selectedCode.contains(to.code)
if (active) {
fill(255,255,255)
stroke(0,255,220)
strokeWeight(1)
ellipse(px-3,py-3,6,6)
} else {
fill(0,220,255,180)
noStroke()
ellipse(px-2,py-2,4,4)
}
case _ =>
}
}
}
// ----------------------------------------------------------------------------
// COUNTRY MARKERS
// ----------------------------------------------------------------------------
def drawCountryMarkers(surface: CanvasDraw): Unit = {
import surface._
val pulse = (Math.sin(animationTime*4.0)+1.0)/2.0
for (c <- countries) {
val (x,y) = geoToScreen(c.lat,c.lon)
val selected = selectedCode.contains(c.code)
val hovered = hoveredCode.contains(c.code)
val baseRadius =
3.5 + Math.min(14.0,Math.sqrt(c.pop)*0.45)
val ringColor =
if (c.gdpGrowth >= 3.0) (0,240,120,210)
else if (c.gdpGrowth >= 0.0) (240,200,40,210)
else (255,60,60,210)
if (selected) {
stroke(0,255,240,220)
strokeWeight(2)
noFill()
val expand = pulse*8.0
ellipse(
x-baseRadius-expand-4,
y-baseRadius-expand-4,
(baseRadius+expand+4)*2,
(baseRadius+expand+4)*2
)
stroke(0,255,240,150)
strokeWeight(1)
line(x-18,y,x+18,y)
line(x,y-18,x,y+18)
}
stroke(ringColor._1,ringColor._2,ringColor._3,ringColor._4)
strokeWeight(2)
noFill()
ellipse(
x-baseRadius-2,
y-baseRadius-2,
(baseRadius+2)*2,
(baseRadius+2)*2
)
val intensity =
Math.min(255,(80+(c.gdp/27360.0)*175).toInt)
if (selected || hovered)
fill(255,255,255)
else
fill(0,intensity,255)
noStroke()
ellipse(
x-baseRadius,
y-baseRadius,
baseRadius*2,
baseRadius*2
)
val angle = animationTime + c.gdpRank
val hx = x + Math.cos(angle)*(baseRadius+5)
val hy = y + Math.sin(angle)*(baseRadius+5)
fill(50,180,255,200)
ellipse(hx-1.5,hy-1.5,3,3)
if (selected || hovered || c.gdpRank <= 10) {
fill(0,255,240)
text(c.code,x+baseRadius+4,y+3)
}
}
}
// ----------------------------------------------------------------------------
// HEADER
// ----------------------------------------------------------------------------
def drawHeader(surface: CanvasDraw): Unit = {
import surface._
fill(12,26,52)
stroke(0,160,220,130)
strokeWeight(1)
rect(mapX,10,mapW,38)
fill(0,255,220)
text(
"GLOBE-X // GLOBAL NETWORK & ECONOMIC INTELLIGENCE",
mapX+12,34
)
if (isPaused) fill(255,180,0)
else fill(0,255,120)
ellipse(mapX+mapW-130,24,8,8)
fill(180,210,240)
if (isPaused)
text("SIM PAUSED",mapX+mapW-116,32)
else
text("LIVE NETWORK",mapX+mapW-116,32)
}
// ----------------------------------------------------------------------------
// DASHBOARD FRAME
// ----------------------------------------------------------------------------
def drawDashboard(surface: CanvasDraw): Unit = {
import surface._
if (!showDashboard) return
fill(8,18,38)
stroke(0,160,220,160)
strokeWeight(2)
rect(dashX,dashY,dashW,dashH)
fill(14,32,64)
noStroke()
rect(dashX+1,dashY+1,dashW-2,38)
fill(0,240,255)
text("INTELLIGENCE HUD",dashX+14,dashY+25)
selectedCode match {
case Some(code) =>
countryMap.get(code) match {
case Some(c) => drawCountryProfile(surface,c)
case None => drawGlobalStatistics(surface)
}
case None =>
drawGlobalStatistics(surface)
}
}
// ----------------------------------------------------------------------------
// COUNTRY PROFILE
// ----------------------------------------------------------------------------
def drawCountryProfile(surface: CanvasDraw,c: Country): Unit = {
import surface._
var cy = dashY+58.0
fill(255,255,255)
text(c.name.toUpperCase,dashX+14,cy)
cy += 16
fill(0,200,240)
text(
c.continent+" // RANK #"+c.gdpRank+" IN GDP",
dashX+14,cy
)
cy += 10
stroke(0,120,180,100)
strokeWeight(1)
line(
dashX+14,cy,
dashX+dashW-14,cy
)
// GDP
cy += 24
fill(150,180,210)
text("NOMINAL GDP",dashX+14,cy)
fill(255,255,255)
val gdpString = "$"+c.gdp.toInt+" BILLION"
text(gdpString,dashX+14,cy+18)
val gdpBarW =
Math.min(
dashW-28,
(c.gdp/27360.0)*(dashW-28)
)
fill(20,50,80)
rect(dashX+14,cy+24,dashW-28,6)
fill(0,220,255)
rect(dashX+14,cy+24,gdpBarW,6)
// Growth and population
cy += 48
fill(150,180,210)
text("GDP GROWTH",dashX+14,cy)
text("POPULATION",dashX+150,cy)
if (c.gdpGrowth >= 0)
fill(0,240,120)
else
fill(255,80,80)
val sign =
if (c.gdpGrowth > 0) "+" else ""
text(
sign+c.gdpGrowth+"%",
dashX+14,cy+18
)
fill(255,255,255)
text(
c.pop+" M",
dashX+150,cy+18
)
// Climate
cy += 42
fill(150,180,210)
text("HUMIDITY INDEX",dashX+14,cy)
text("COORDINATES",dashX+150,cy)
fill(60,200,255)
text(
c.humidity.toInt+"% RELATIVE",
dashX+14,cy+18
)
fill(200,220,240)
text(
c.lat+" deg, "+c.lon+" deg",
dashX+150,cy+18
)
// Strategic profile
cy += 38
fill(12,36,68)
stroke(0,180,220,120)
strokeWeight(1)
rect(dashX+14,cy,dashW-28,44)
fill(0,240,220)
text("STRATEGIC PROFILE:",dashX+22,cy+16)
fill(230,240,250)
text(c.status,dashX+22,cy+32)
// Connections
cy += 60
fill(150,180,210)
text(
"ACTIVE CORRIDORS ("+c.connections.length+")",
dashX+14,cy
)
cy += 14
var tagX = dashX+14.0
var tagY = cy
for (connCode <- c.connections) {
if (tagX+44 > dashX+dashW-14) {
tagX = dashX+14
tagY += 24
}
fill(18,48,80)
stroke(0,160,220,150)
strokeWeight(1)
rect(tagX,tagY,40,18)
fill(0,255,240)
text(connCode,tagX+8,tagY+13)
tagX += 46
}
// Footer
cy = dashY+dashH-42
stroke(0,100,160,80)
strokeWeight(1)
line(
dashX+14,cy,
dashX+dashW-14,cy
)
fill(120,160,200)
text(
"[ESC] GLOBAL | [1-9] COUNTRY",
dashX+14,cy+18
)
text(
"[R] RESET | [G] GRID | [N] NET",
dashX+14,cy+32
)
}
// ----------------------------------------------------------------------------
// GLOBAL STATISTICS
// ----------------------------------------------------------------------------
def drawGlobalStatistics(surface: CanvasDraw): Unit = {
import surface._
var cy = dashY+58
val totalGdp = countries.map(_.gdp).sum
val totalPop = countries.map(_.pop).sum
val topGdp = countries.minBy(_.gdpRank)
val topPop = countries.maxBy(_.pop)
val topGrowth = countries.maxBy(_.gdpGrowth)
fill(255,255,255)
text("GLOBAL OVERVIEW",dashX+14,cy)
cy += 16
fill(0,200,240)
text(
"AGGREGATE INTELLIGENCE & METRICS",
dashX+14,cy
)
cy += 10
stroke(0,120,180,100)
strokeWeight(1)
line(
dashX+14,cy,
dashX+dashW-14,cy
)
cy += 24
fill(150,180,210)
text(
"TOTAL TRACKED GDP",
dashX+14,cy
)
fill(255,255,255)
text(
"$"+totalGdp.toInt+" BILLION",
dashX+14,cy+18
)
cy += 38
fill(150,180,210)
text(
"TOTAL POPULATION",
dashX+14,cy
)
fill(255,255,255)
text(
(totalPop/1000.0)+" BILLION",
dashX+14,cy+18
)
cy += 38
fill(150,180,210)
text(
"LEADERS & BENCHMARKS",
dashX+14,cy
)
cy += 18
fill(0,220,255)
text(
"#1 Economy: "+topGdp.name+" ($"+topGdp.gdp.toInt+"B)",
dashX+14,cy
)
cy += 16
text(
"Largest Pop: "+topPop.name+" ("+topPop.pop.toInt+"M)",
dashX+14,cy
)
cy += 16
text(
"Fastest Growth: "+topGrowth.name+
" (+"+topGrowth.gdpGrowth+"%)",
dashX+14,cy
)
cy += 28
fill(150,180,210)
text(
"NETWORK: "+routes.length+
" ROUTES / "+countries.length+" NODES",
dashX+14,cy
)
cy += 18
val top5 = countries.sortBy(_.gdpRank).take(5)
for (item <- top5) {
val barW =
(item.gdp/top5.head.gdp)*(dashW-80)
fill(180,210,240)
text(
item.code,
dashX+14,cy+10
)
fill(20,50,80)
rect(
dashX+46,cy,
dashW-60,12
)
fill(0,180,240)
rect(
dashX+46,cy,
barW,12
)
cy += 18
}
cy = dashY+dashH-42
stroke(0,100,160,80)
strokeWeight(1)
line(
dashX+14,cy,
dashX+dashW-14,cy
)
fill(120,160,200)
text(
"CLICK MAP MARKER TO INSPECT",
dashX+14,cy+18
)
text(
"[1] USA [2] CHN [3] DEU [4] JPN [5] IND",
dashX+14,cy+32
)
}
// ----------------------------------------------------------------------------
// FOOTER
// ----------------------------------------------------------------------------
def drawFooter(surface: CanvasDraw): Unit = {
import surface._
fill(8,16,32)
stroke(0,120,180,100)
strokeWeight(1)
rect(mapX,545,mapW,35)
fill(140,180,220)
text(
"KEYBOARD: [1-9] COUNTRY | [ESC] GLOBAL | [R] RESET",
mapX+12,567
)
text(
"[G] GRID | [N] NETWORK | [D] DASHBOARD | [A] PAUSE",
mapX+12,554
)
}
// ----------------------------------------------------------------------------
// KEYBOARD HANDLING
//
// Kojo provides isKeyPressed through its normal keyboard API.
// This avoids treating CanvasDraw as if it were a keyboard object.
// ----------------------------------------------------------------------------
def handleKeyboard(): Unit = {
if (isKeyPressed('1')) selectedCode = Some("USA")
else if (isKeyPressed('2')) selectedCode = Some("CHN")
else if (isKeyPressed('3')) selectedCode = Some("DEU")
else if (isKeyPressed('4')) selectedCode = Some("JPN")
else if (isKeyPressed('5')) selectedCode = Some("IND")
else if (isKeyPressed('6')) selectedCode = Some("GBR")
else if (isKeyPressed('7')) selectedCode = Some("FRA")
else if (isKeyPressed('8')) selectedCode = Some("BRA")
else if (isKeyPressed('9')) selectedCode = Some("RUS")
}
// ----------------------------------------------------------------------------
// MOUSE HANDLING
//
// CanvasDraw itself is not used as the source of mouseX/mouseY.
// The normal Kojo mouse API is used instead.
// ----------------------------------------------------------------------------
def updateHover(mx: Double,my: Double): Unit = {
var found: Option[String] = None
for (c <- countries) {
val (sx,sy) =
geoToScreen(c.lat,c.lon)
if (distance(mx,my,sx,sy) < 14)
found = Some(c.code)
}
hoveredCode = found
}
// ----------------------------------------------------------------------------
// MAIN SKETCH
// ----------------------------------------------------------------------------
class GlobeXSketch {
def setup(surface: CanvasDraw): Unit = {
import surface._
background(6,12,28)
noStroke()
// Activate the canvas so keyboard events can reach Kojo.
activateCanvas()
}
def drawLoop(surface: CanvasDraw): Unit = {
handleKeyboard()
updateState()
drawBackground(surface)
drawGrid(surface)
drawContinents(surface)
drawConnections(surface)
drawParticles(surface)
drawCountryMarkers(surface)
drawHeader(surface)
drawDashboard(surface)
drawFooter(surface)
}
}
val globeX = new GlobeXSketch
canvasSketch(globeX)