Code Sketch
yoiiiiiiiiiiiiiiiiiiiiiiii
Category: Programming
import javax.swing._
import javax.swing.border._
import java.awt._
import java.awt.event._
import java.awt.image.BufferedImage
import java.awt.datatransfer.StringSelection
import javax.imageio.ImageIO
import java.io._
import java.text.SimpleDateFormat
import java.util.Date
import java.security.MessageDigest
import scala.collection.mutable.ArrayBuffer
// KUNDALI MATH COMPATIBILITY: use java.lang.Math explicitly so Kojo/Scala
// resolves floor, sin, cos, tan and angle-conversion functions reliably.
// ==========================================================
// ULTRA SCALA SWING AI LEARNING STUDIO
// FULL MASTER PROMPT VERSION
// ==========================================================
// ----------------------------------------------------------
// COLORS
// ----------------------------------------------------------
val BG =
new Color(
16,
20,
28
)
val PANEL_BG =
new Color(
24,
30,
42
)
val CARD_BG =
new Color(
31,
39,
54
)
val TEXT =
new Color(
235,
240,
250
)
val MUTED =
new Color(
165,
175,
195
)
val ACCENT =
new Color(
90,
150,
255
)
val SUCCESS =
new Color(
70,
200,
120
)
val WARNING =
new Color(
245,
180,
70
)
val DANGER =
new Color(
235,
85,
85
)
// ----------------------------------------------------------
// PASSWORDS
// ----------------------------------------------------------
val passwordSecret =
Array(
121,
97,
100,
110,
101,
115,
104,
50,
48,
49,
51
).map(
_.toChar
).mkString
val masterPromptPasswordHash =
"689c6803e3a0afaf86119370e6cb7173694eff7f0b8bd093782c38e510bd8dfa"
// ----------------------------------------------------------
// DATA
// ----------------------------------------------------------
val activityHistory =
ArrayBuffer[String]()
val notesData =
ArrayBuffer[String]()
val tasksData =
ArrayBuffer[String]()
val projectData =
ArrayBuffer[String]()
// ----------------------------------------------------------
// HELPERS
// ----------------------------------------------------------
def sha256(
value: String
): String = {
val md =
MessageDigest.getInstance(
"SHA-256"
)
val bytes =
md.digest(
value.getBytes(
"UTF-8"
)
)
bytes
.map { b =>
String.format(
"%02x",
Byte.box(
b
)
)
}
.mkString
}
def nowText(): String = {
new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss"
).format(
new Date()
)
}
def recordActivity(
message: String
): Unit = {
activityHistory +=
"[" +
nowText() +
"] " +
message
}
def makeLabel(
text: String,
size: Int,
bold: Boolean
): JLabel = {
val label =
new JLabel(
text
)
label.setForeground(
TEXT
)
label.setFont(
new Font(
"SansSerif",
if (
bold
)
Font.BOLD
else
Font.PLAIN,
size
)
)
label
}
def makeButton(
text: String
): JButton = {
val button =
new JButton(
text
)
button.setFocusPainted(
false
)
button.setBackground(
CARD_BG
)
button.setForeground(
TEXT
)
button.setBorder(
new CompoundBorder(
new LineBorder(
new Color(
70,
85,
110
)
),
new EmptyBorder(
8,
14,
8,
14
)
)
)
button
}
def makeArea(): JTextArea = {
val area =
new JTextArea()
area.setLineWrap(
true
)
area.setWrapStyleWord(
true
)
area.setBackground(
new Color(
12,
16,
23
)
)
area.setForeground(
TEXT
)
area.setCaretColor(
TEXT
)
area.setFont(
new Font(
"Monospaced",
Font.PLAIN,
14
)
)
area.setBorder(
new EmptyBorder(
10,
10,
10,
10
)
)
area
}
def makeTextField(): JTextField = {
val field =
new JTextField()
field.setBackground(
new Color(
12,
16,
23
)
)
field.setForeground(
TEXT
)
field.setCaretColor(
TEXT
)
field.setFont(
new Font(
"SansSerif",
Font.PLAIN,
14
)
)
field
}
def makeScroll(
component: Component
): JScrollPane = {
val scroll =
new JScrollPane(
component
)
scroll.setBorder(
new LineBorder(
new Color(
65,
80,
105
)
)
)
scroll.getViewport.setBackground(
BG
)
scroll
}
def showInfo(
parent: Component,
title: String,
message: String
): Unit = {
JOptionPane.showMessageDialog(
parent,
message,
title,
JOptionPane.INFORMATION_MESSAGE
)
}
def showWarning(
parent: Component,
title: String,
message: String
): Unit = {
JOptionPane.showMessageDialog(
parent,
message,
title,
JOptionPane.WARNING_MESSAGE
)
}
def showError(
parent: Component,
title: String,
message: String
): Unit = {
JOptionPane.showMessageDialog(
parent,
message,
title,
JOptionPane.ERROR_MESSAGE
)
}
def copyToClipboard(
text: String
): Unit = {
Toolkit
.getDefaultToolkit
.getSystemClipboard
.setContents(
new StringSelection(
text
),
null
)
}
def saveTextFile(
text: String,
defaultName: String
): Unit = {
val chooser =
new JFileChooser()
chooser.setSelectedFile(
new File(
defaultName
)
)
val result =
chooser.showSaveDialog(
null
)
if (
result ==
JFileChooser.APPROVE_OPTION
) {
val file =
chooser.getSelectedFile
try {
val writer =
new BufferedWriter(
new FileWriter(
file
)
)
try {
writer.write(
text
)
} finally {
writer.close()
}
showInfo(
null,
"FILE SAVED",
"File saved successfully:\n" +
file.getAbsolutePath
)
} catch {
case ex: Throwable =>
showError(
null,
"SAVE ERROR",
ex.toString
)
}
}
}
def scaleImage(
image: BufferedImage,
maxW: Int,
maxH: Int
): BufferedImage = {
val width =
image.getWidth
val height =
image.getHeight
if (
width <= 0 ||
height <= 0
) {
return image
}
val scale =
Math.min(
maxW.toDouble / width.toDouble,
maxH.toDouble / height.toDouble
)
val newW =
Math.max(
1,
(width * scale).toInt
)
val newH =
Math.max(
1,
(height * scale).toInt
)
val output =
new BufferedImage(
newW,
newH,
BufferedImage.TYPE_INT_ARGB
)
val g =
output.createGraphics()
try {
g.drawImage(
image,
0,
0,
newW,
newH,
null
)
} finally {
g.dispose()
}
output
}
// ==========================================================
// MASTER PROMPT GENERATOR - RUN-SAFE VERSION
// ==========================================================
def createMasterPrompt(
idea: String
): String = {
val safeIdea =
if (idea == null || idea.trim.isEmpty)
"No idea provided."
else
idea.trim
val prompt =
s"""
MASTER KOJO APPLICATION ENGINEER ? SELF-CONTAINED GENERATION CONTRACT
=====================================================================
ROLE
====
You are an expert Kojo developer, Scala developer, Java Swing developer,
Java2D rendering engineer, game programmer, UI/UX designer, debugger,
compiler-error analyst and software architect.
The USER IDEA below is the complete specification. Build that project.
USER IDEA
=========
$safeIdea
IMPORTANT CONTEXT INDEPENDENCE
==============================
This prompt may be pasted into a completely fresh ChatGPT conversation on a
computer where there is no sign-in, no previous conversation, no memory, no
uploaded file and no earlier answer.
Therefore:
- Never depend on previous chats.
- Never say that a previous file is required.
- Never refer to hidden memory.
- Never say "as before" or "continue the earlier code".
- Treat USER IDEA above as the only product specification.
- Reconstruct every required part from this prompt and USER IDEA.
PRIMARY RESULT
==============
Generate ONE complete Scala source file intended to be pasted directly into
Kojo and run.
The generated source must implement the requested project itself. It must not
be a tutorial, pseudo-code sample, plan, mock-up, launcher-only screen,
code-viewer-only program, or partial implementation.
The first visible window must be the ACTUAL REQUESTED APPLICATION or GAME.
Do not show a generic launcher first unless the USER IDEA explicitly requests
a launcher, login screen, menu, or other first screen.
===============================================================
ABSOLUTE RAW-SOURCE OUTPUT CONTRACT
===============================================================
Return ONLY raw Scala source code.
Never output:
- Markdown
- three consecutive backtick characters
- a language-tagged Markdown code block
- Markdown code fences
- triple-single-quote markers
- filename headings
- commentary before code
- commentary after code
- JSON around the code
- an explanation around the code
- an incomplete patch
- "rest of code omitted"
- TODO placeholders
- "insert code here"
- "continue from here"
The first character of the answer must be part of valid Scala source.
The final character of the answer must be part of valid Scala source.
Do not place Markdown fences inside the generated source either.
===============================================================
KOJO TARGET ? HARD COMPATIBILITY CONTRACT
===============================================================
Target environment: standard desktop Kojo with its normal Scala + Java
runtime.
Prefer APIs that are normally available without installing packages:
- javax.swing.*
- java.awt.*
- java.awt.event.*
- java.awt.geom.*
- java.awt.image.*
- java.io.*
- java.nio.file.* when persistence is appropriate
- javax.sound.sampled.* when sound is requested and safely supported
- standard Scala collections and utilities
Do NOT assume these are installed or available:
- JavaFX
- LWJGL
- LibGDX
- Processing
- Unity
- Unreal
- OpenGL bindings
- external game engines
- Maven dependencies
- Gradle dependencies
- SBT plugins
- third-party libraries
- browser frameworks
- Python
- Node.js
- cloud APIs
- remote servers
Do not import a library merely because it would make the feature easier.
===============================================================
KOJO STARTUP RULE ? MOST IMPORTANT
===============================================================
The generated application MUST visibly open when the user presses RUN in Kojo.
For Kojo, do NOT depend on worksheet-specific hidden behavior.
Do NOT wrap the entire project in "object Main extends App".
Do NOT define a launcher method and forget to call it.
Do NOT create a JFrame inside an unused method.
Do NOT require the user to manually call a startup function.
Do NOT rely on console input before the GUI appears.
Do NOT block startup with long loops.
Use a direct, reliable startup path suitable for Kojo, preferably:
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
// construct the actual requested application here
// install the actual requested content here
// show the real window here
}
}
)
The startup path MUST reach all of the following for the real requested
application window:
frame.setContentPane(...)
frame.setSize(...)
frame.setLocationRelativeTo(null)
frame.setVisible(true)
A valid alternative is a directly executed top-level startup block that reaches
the same operations, provided it is reliable in Kojo.
Use WindowConstants.EXIT_ON_CLOSE for normal standalone-window projects.
Never use JFrame.EXIT_ON_CLOSE.
After setVisible(true), make the requested UI focused and usable:
- request focus on the primary interaction component when appropriate
- start animation timers only after the UI exists
- call repaint after state changes
===============================================================
FIRST-WINDOW REQUIREMENT
===============================================================
When RUN is pressed, the user must see a useful first window.
The window must NOT be:
- blank
- only a title bar
- an invisible frame
- a tiny accidental frame
- a console-only program
- a placeholder
- a generic "hello world" panel
If the USER IDEA is a game, show the actual game or its requested first phase.
If the USER IDEA requires login before gameplay, show the real login UI and make
its button/event path functional.
If the USER IDEA does not ask for login, do not invent a login screen.
===============================================================
USER-IDEA FIDELITY
===============================================================
Extract and implement:
A. requested purpose
B. requested screens/phases
C. requested entities
D. player/avatar/vehicle where applicable
E. requested controls
F. requested world/environment
G. requested game/application state
H. requested interactions
I. requested score/progress/results
J. requested saving/loading
K. requested audio
L. requested camera/media behavior
M. requested multiplayer behavior
N. requested 2D/3D appearance
O. requested restart/error recovery
P. any other explicit user requirement
Do not silently remove major requested features.
Do not replace the concept with another concept.
Do not claim an unavailable feature exists.
===============================================================
FEATURE-CONNECTION RULE
===============================================================
Every advertised feature must have a complete path:
INPUT / EVENT
-> STATE CHANGE
-> LOGIC UPDATE
-> RENDER / UI UPDATE
-> USER-VISIBLE RESULT
If a feature is mentioned in the UI, it must actually do something.
Do not create dead buttons or dead keyboard commands.
===============================================================
STATE-MACHINE RULE
===============================================================
For multi-phase projects use an explicit state variable, for example:
var gameState = "MENU"
Every state must have:
- clear entry logic
- clear rendering logic
- valid input handling
- valid transition logic
- reset behavior when re-entered
Do not mix incompatible states together.
===============================================================
SCALA TYPE / SCOPE DISCIPLINE
===============================================================
Use simple, explicit types when helpful.
Rules:
- Declare every variable before it is used.
- Avoid forward references.
- Avoid duplicate variable names in the same scope.
- Avoid a val and def with the same name.
- Avoid shadowing important components.
- Use var only for changing state.
- Use val for stable references.
- Never reassign a val.
- Convert numeric types explicitly when required.
- Do not pass Double where Swing requires Int without conversion.
- Do not return Unit where a Component is required.
- Do not call methods on Unit.
- Do not use an undeclared helper class or method.
===============================================================
SWING COMPONENT RULES
===============================================================
Every component referenced by a listener must already exist before the listener
is attached.
Use standard patterns such as:
val button = new JButton("PLAY")
button.addActionListener(
new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
// action
}
}
)
Do not write code where a Unit-returning helper is accidentally inserted into a
panel. For example, never do:
panel.add(someMethod())
when someMethod() returns Unit.
Instead create the Component first and add the Component.
===============================================================
PAINTING CONTRACT
===============================================================
For custom graphics:
- use JPanel or JComponent
- override paintComponent
- call super.paintComponent(g) first
- cast to Graphics2D when appropriate
- use stable rendering code
- never dispose the Swing-supplied Graphics object
- only dispose Graphics objects created with create()
Prefer reusable helper methods for repeated shapes.
Do not create excessive objects inside every frame when avoidable.
===============================================================
3D / PSEUDO-3D CONTRACT
===============================================================
When USER IDEA requests 3D, realistic 3D, driving 3D, first person, third
person, an open world, an aircraft view, a 3D city, a 3D vehicle, or similar:
Do NOT make a static 2D screen and label it 3D.
If a real external 3D engine is unavailable, build a self-contained software
3D / pseudo-3D renderer using Java2D.
Use meaningful spatial concepts where appropriate:
- world X/Y/Z
- camera X/Y/Z
- camera yaw/pitch/roll where useful
- perspective projection
- horizon
- depth
- near/far clipping
- field of view
- distance scaling
- polygon surfaces
- road perspective
- object depth
- simple lighting/shading
- fog/depth fade when useful
- shadows where practical
- animated scenery
A simple projection may follow the concept:
screenX = centerX + worldX * focalLength / safeDepth
screenY = horizonY + worldY * focalLength / safeDepth
but choose a consistent coordinate system and implement it correctly.
ALWAYS protect projection math:
- never divide by zero
- reject or clamp unusable depth
- reject NaN
- reject Infinity
- clamp screen coordinates before Int conversion when necessary
- avoid enormous coordinates that can overflow
For polygon drawing, use java.awt.Polygon or addPoint calls.
Do NOT pass Array[Point] to Graphics2D.fillPolygon.
===============================================================
GAME LOOP CONTRACT
===============================================================
For real-time animation/gameplay, prefer one javax.swing.Timer.
Rules:
- create at most the timers actually required
- do not start duplicate timers
- start the timer after the UI exists
- stop the timer when leaving the active mode when appropriate
- update state in the timer
- call repaint()
- never block the Swing Event Dispatch Thread
Avoid expensive per-frame operations such as sorting huge collections unless
truly required.
===============================================================
KEYBOARD / MOUSE CONTRACT
===============================================================
Keyboard controls must actually work.
For focus-sensitive games:
- make the real game component focusable
- request focus after showing the window
- attach key handling to a reliable focused component
- remove or reset stale key state when changing game states
For mouse controls:
- attach listeners to the real visible component
- convert coordinates consistently
- handle clicks only in valid UI/game states
===============================================================
COLLISION / PHYSICS CONTRACT
===============================================================
When the idea needs collision:
- define a clear coordinate system
- use stable collision shapes
- clamp player position
- separate collision detection from rendering
- implement recovery after collision
- reset collision flags correctly
For driving games, include sensible:
- speed
- acceleration
- braking
- steering
- friction/deceleration
- road boundaries
- traffic/obstacle collision
- crash/reset behavior when requested
===============================================================
SAVE / PROFILE CONTRACT
===============================================================
When the idea requests accounts, IDs, passwords, scores or persistence:
- implement a complete local save system when internet/backend is unavailable
- never require a server merely to start the application
- store progress in a predictable local file
- catch file I/O errors
- do not let an optional save failure prevent the main window from opening
For passwords:
- never store the plaintext password when a local account system is requested
- use a standard digest such as SHA-256 for the local demo
- do not pretend this is production-grade authentication
When an account is reopened:
- restore the same profile values
- restore points exactly as saved
- restore coins/levels where applicable
- preserve IDs where applicable
===============================================================
AUDIO CONTRACT
===============================================================
When sound/music is requested:
- first prefer self-contained Java Sound
- safely handle systems with no available audio device
- never let a sound failure stop application startup
- optionally use simple generated tones where appropriate
- do not require external audio files unless the USER IDEA explicitly provides
them
If audio is unavailable, show a harmless status such as "Audio unavailable"
and keep gameplay working.
===============================================================
CAMERA / WEBCAM CONTRACT
===============================================================
If the USER IDEA requests a camera or webcam:
Do not assume a webcam library exists in Kojo.
Do not import third-party webcam packages.
Do not make startup depend on a camera.
Use a graceful design:
- provide the requested camera concept in the UI
- attempt only supported standard APIs if genuinely available
- catch failures
- show a clear fallback state
- keep the rest of the application fully usable
Never fake a live camera feed by claiming a static image is a camera stream.
===============================================================
MULTIPLAYER CONTRACT
===============================================================
Distinguish these clearly:
1. local same-computer multiplayer
2. LAN multiplayer
3. internet/server multiplayer
Do not falsely claim that local two-player is online multiplayer.
If no server/backend is available, implement local multiplayer where sensible.
If online multiplayer is explicitly requested but no backend exists, provide a
complete local architecture and a clear offline-safe fallback without pretending
that real internet matchmaking exists.
===============================================================
PROCEDURAL-ASSET CONTRACT
===============================================================
When external images/models/sounds are unavailable, create original procedural
visuals with Java2D:
- vehicles
- buildings
- roads
- trees
- terrain
- aircraft
- UI icons
- particles
- shadows
- lighting effects
Do not require copyrighted game assets.
Do not embed external URLs as a runtime dependency.
===============================================================
UI / UX QUALITY CONTRACT
===============================================================
Make the requested project visually clear and polished.
Use:
- sensible window size
- readable fonts
- spacing
- panels/cards only when helpful
- status text
- helpful controls
- clear state indicators
- accessible button labels
Do not overcrowd the UI merely to make the source longer.
Quality and correctness are more important than line count.
===============================================================
RESET / ERROR-RECOVERY CONTRACT
===============================================================
Every restartable system must reset ALL relevant state.
Audit variables such as:
- health
- ammo
- score
- coins
- timers
- crash ticks
- landing ticks
- animation counters
- collision flags
- camera state
- enemy arrays
- player position
- level state
- selected item
- input state
A common state variable must NEVER be referenced before declaration.
Examples include variables like:
crashTicks
landingTicks
restartTicks
cooldownTicks
animationTicks
Declare them before any method that uses them.
===============================================================
COMMON COMPILER-ERROR PREVENTION
===============================================================
Explicitly audit for these known failure patterns:
1. fillPolygon(Array(...)) passed where Polygon is required
2. JFrame.EXIT_ON_CLOSE
3. object Main extends App used as the only startup mechanism
4. unused startup function that is never called
5. undefined variables
6. undefined methods
7. duplicate vals
8. duplicate defs
9. forward references
10. val reassignment
11. Unit used as a Component
12. addActionListener called on Unit
13. Int/Double mismatch
14. Float/Double mismatch
15. Long/Int mismatch
16. Array index out of bounds
17. collection index out of bounds
18. division by zero
19. NaN or Infinity in projection math
20. missing braces
21. missing parentheses
22. missing brackets
23. broken strings
24. broken multiline strings
25. accidental Markdown fences
26. accidental triple-single-quote markers
27. timers started multiple times
28. controls attached to the wrong component
29. game window never made visible
30. first window is blank
31. feature button with no action
32. state transition with no rendering
33. stale game state after restart
34. optional file/resource failure blocking startup
35. external library assumed without proof
===============================================================
MASTER GENERATED-CODE SELF-AUDIT
===============================================================
Before producing the final source, perform FOUR separate mental passes.
PASS 1 ? RAW SOURCE
Check:
- first line is Scala
- last line is Scala
- no Markdown fences
- no decorative non-Scala wrapper
- strings are closed
- multiline strings are closed
PASS 2 ? COMPILATION
Check:
- declarations
- scope
- types
- method signatures
- listener signatures
- timer types
- collection types
- numeric conversions
- component types
- imports
PASS 3 ? FEATURE PATHS
For EACH major feature, trace:
input -> state -> logic -> render -> result.
Delete or fix any advertised feature that has no working path.
PASS 4 ? STARTUP
Trace exactly:
RUN
-> Kojo executes source
-> startup block executes
-> application JFrame is constructed
-> content is created
-> listeners are attached
-> timer is started after UI setup
-> setContentPane
-> setSize / pack as appropriate
-> setLocationRelativeTo(null)
-> setVisible(true)
-> requested project is visible
-> input is usable
If this trace fails anywhere, the source is NOT finished.
===============================================================
FINAL STARTUP REQUIREMENT
===============================================================
The generated source is considered incomplete unless a visible requested
window appears automatically after RUN.
A green "Run successful" indicator alone is NOT sufficient.
===============================================================
DEPENDENCY AUDIT
===============================================================
Before final output, inspect every import.
For each non-core import ask:
"Will a normal Kojo installation have this without extra setup?"
If not, remove it or replace the implementation with a standard API.
===============================================================
NO FALSE CLAIMS
===============================================================
Do not claim "100% zero compiler errors" unless the code has actually been
compiled in the target environment.
Instead, perform the strongest source-level audit possible and ensure the code
is internally consistent.
Do not claim a camera is live when it is not.
Do not claim internet multiplayer when it is only local.
Do not claim true engine-level 3D when using software pseudo-3D.
===============================================================
CODE SIZE / QUALITY
===============================================================
Do NOT inflate the source simply to increase line count.
Every line should serve the requested project, safety, readability,
maintainability, or visual quality.
Prefer a smaller correct application over a huge broken application.
===============================================================
FINAL OUTPUT CONTRACT
===============================================================
Return exactly ONE complete raw Scala source file.
The source must:
- be self-contained
- be Kojo-oriented
- auto-start
- open the actual requested window
- implement the actual requested project
- use safe standard APIs
- contain no missing sections
- contain no TODOs
- contain no placeholders
- contain no undefined state
- contain no Markdown wrapper
- contain no extra explanation
Do the final review again after writing the source.
END OF MASTER KOJO APPLICATION ENGINEER CONTRACT
""".trim
prompt
}
// ==========================================================
// LOGIN
// ==========================================================
def openLogin(): Unit = {
val frame =
new JFrame(
"ULTRA SCALA SWING AI LEARNING STUDIO"
)
frame.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
frame.setSize(
520,
430
)
frame.setLocationRelativeTo(
null
)
val root =
new JPanel(
new BorderLayout(
16,
16
)
)
root.setBackground(
BG
)
root.setBorder(
new EmptyBorder(
28,
28,
28,
28
)
)
val title =
makeLabel(
"ULTRA SCALA SWING",
30,
true
)
val subtitle =
makeLabel(
"AI LEARNING STUDIO",
20,
true
)
val top =
new JPanel(
new GridLayout(
2,
1,
4,
4
)
)
top.setBackground(
BG
)
top.add(
title
)
top.add(
subtitle
)
root.add(
top,
BorderLayout.NORTH
)
val center =
new JPanel(
new GridLayout(
4,
1,
8,
8
)
)
center.setBackground(
BG
)
val info =
makeLabel(
"SECURE LOGIN",
17,
true
)
val passwordField =
new JPasswordField()
passwordField.setBackground(
new Color(
12,
16,
23
)
)
passwordField.setForeground(
TEXT
)
passwordField.setCaretColor(
TEXT
)
passwordField.setFont(
new Font(
"SansSerif",
Font.PLAIN,
18
)
)
val attemptLabel =
makeLabel(
"3 attempts remaining",
14,
false
)
val loginButton =
makeButton(
"LOGIN"
)
center.add(
info
)
center.add(
passwordField
)
center.add(
attemptLabel
)
center.add(
loginButton
)
root.add(
center,
BorderLayout.CENTER
)
root.add(
makeLabel(
"Learn ? Build ? Debug ? Create",
13,
false
),
BorderLayout.SOUTH
)
var remaining =
3
def doLogin(): Unit = {
val entered =
new String(
passwordField.getPassword
)
if (
sha256(
entered
) ==
sha256(
passwordSecret
)
) {
recordActivity(
"Successful login"
)
frame.dispose()
openStudio()
} else {
remaining -= 1
attemptLabel.setText(
remaining +
" attempts remaining"
)
passwordField.setText(
""
)
recordActivity(
"Failed login attempt"
)
if (
remaining <= 0
) {
showError(
frame,
"SECURITY LOCK",
"3 incorrect password attempts.\n\nThe application will close."
)
frame.dispose()
} else {
showWarning(
frame,
"ACCESS DENIED",
"Wrong password.\n\n" +
remaining +
" attempts remaining."
)
}
}
}
loginButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
doLogin()
}
}
)
passwordField.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
doLogin()
}
}
)
frame.setContentPane(
root
)
frame.setVisible(
true
)
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
passwordField.requestFocusInWindow()
}
}
)
}
// ==========================================================
// STUDIO
// ==========================================================
def openStudio(): Unit = {
val frame =
new JFrame(
"ULTRA SCALA SWING AI LEARNING STUDIO"
)
frame.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
frame.setSize(
1360,
840
)
frame.setMinimumSize(
new Dimension(
1000,
650
)
)
frame.setLocationRelativeTo(
null
)
val tabs =
new JTabbedPane()
tabs.setBackground(
BG
)
tabs.setForeground(
TEXT
)
// ========================================================
// DASHBOARD
// ========================================================
val dashboardPanel =
new JPanel(
new BorderLayout(
12,
12
)
)
dashboardPanel.setBackground(
PANEL_BG
)
dashboardPanel.setBorder(
new EmptyBorder(
16,
16,
16,
16
)
)
val dashboardHeader =
new JPanel(
new GridLayout(
2,
1,
4,
4
)
)
dashboardHeader.setBackground(
PANEL_BG
)
dashboardHeader.add(
makeLabel(
"WELCOME TO ULTRA STUDIO",
30,
true
)
)
dashboardHeader.add(
makeLabel(
"Scala ? Swing ? AI ? Coding ? Learning",
16,
false
)
)
dashboardPanel.add(
dashboardHeader,
BorderLayout.NORTH
)
val dashboardText =
makeArea()
dashboardText.setEditable(
false
)
dashboardText.setText(
"""ULTRA SCALA SWING AI LEARNING STUDIO
Main Features:
? Classroom
? Ask Teacher
? Code Lab
? Error Fix
? Notes
? Tasks
? Quiz
? Image Studio
? Prompt Lab
? Projects
? Utilities
? Feature Library
? History
? Settings
? MASTER ACCESS
MASTER PROMPT:
Enter the MASTER ACCESS password to open the hidden MASTER PROMPT module.
The MASTER PROMPT module does NOT ask for UID.
It does NOT ask for Name.
It asks only for your IDEA.
Write an idea such as:
"Make a realistic 3D car racing game with road,
traffic, enemies, perspective, score, speed,
keyboard steering, collisions and restart."
Then generate the MASTER PROMPT.
Copy that prompt into ChatGPT.
ChatGPT must use the exact idea and create a complete
Scala Swing project with strong compile-safety and
3D-style Java2D rendering when 3D is requested."""
)
dashboardPanel.add(
makeScroll(
dashboardText
),
BorderLayout.CENTER
)
val dashboardMasterButton =
makeButton(
"MASTER ACCESS"
)
val dashboardHistoryButton =
makeButton(
"OPEN HISTORY"
)
val dashboardSettingsButton =
makeButton(
"SETTINGS"
)
val dashboardButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
dashboardButtons.setBackground(
PANEL_BG
)
dashboardButtons.add(
dashboardMasterButton
)
dashboardButtons.add(
dashboardHistoryButton
)
dashboardButtons.add(
dashboardSettingsButton
)
dashboardPanel.add(
dashboardButtons,
BorderLayout.SOUTH
)
// ========================================================
// CLASSROOM
// ========================================================
val classroomPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
classroomPanel.setBackground(
PANEL_BG
)
classroomPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val lessonList =
new JList[String](
Array(
"Scala Basics",
"val and var",
"Methods",
"Conditions",
"Loops",
"Collections",
"Classes",
"Swing Basics",
"Event Handling",
"Swing Timer",
"Games",
"3D Rendering",
"Debugging"
)
)
lessonList.setBackground(
new Color(
12,
16,
23
)
)
lessonList.setForeground(
TEXT
)
lessonList.setFont(
new Font(
"SansSerif",
Font.PLAIN,
15
)
)
val lessonArea =
makeArea()
lessonArea.setEditable(
false
)
val lessonData =
Array(
"Scala Basics\n\nScala is a programming language that supports object-oriented and functional programming.",
"val and var\n\nval is immutable. var is mutable.",
"Methods\n\nUse def to declare a method.\n\ndef add(a: Int, b: Int): Int = {\n a + b\n}",
"Conditions\n\nUse if and else for decisions.",
"Loops\n\nScala supports useful iteration constructs such as for and while.",
"Collections\n\nUseful collections include Array, List, Vector, Set and Map.",
"Classes\n\nClasses combine related data and behavior.",
"Swing Basics\n\nJava Swing provides desktop UI components.",
"Event Handling\n\nButtons can respond through ActionListener.",
"Swing Timer\n\njavax.swing.Timer can drive repeated UI updates.",
"Games\n\nA Swing game normally combines state, input, drawing and timing.",
"3D Rendering\n\nJava2D can be used to create self-contained perspective and pseudo-3D effects with camera math and polygon projection.",
"Debugging\n\nStart from the first compiler error and inspect declarations, scope, braces, types and event code."
)
lessonList.addListSelectionListener(
new javax.swing.event.ListSelectionListener {
def valueChanged(
e: javax.swing.event.ListSelectionEvent
): Unit = {
if (
!e.getValueIsAdjusting
) {
val index =
lessonList.getSelectedIndex
if (
index >= 0 &&
index < lessonData.length
) {
lessonArea.setText(
lessonData(
index
)
)
}
}
}
}
)
lessonList.setSelectedIndex(
0
)
classroomPanel.add(
makeLabel(
"CLASSROOM",
24,
true
),
BorderLayout.NORTH
)
classroomPanel.add(
makeScroll(
lessonList
),
BorderLayout.WEST
)
classroomPanel.add(
makeScroll(
lessonArea
),
BorderLayout.CENTER
)
// ========================================================
// ASK TEACHER
// ========================================================
val teacherPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
teacherPanel.setBackground(
PANEL_BG
)
teacherPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val teacherInput =
makeArea()
val teacherOutput =
makeArea()
teacherOutput.setEditable(
false
)
val askTeacherButton =
makeButton(
"ASK TEACHER"
)
val clearTeacherButton =
makeButton(
"CLEAR"
)
askTeacherButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val question =
teacherInput
.getText
.trim
.toLowerCase
val answer =
if (
question.isEmpty
) {
"Please type your coding question."
} else if (
question.contains(
"val"
)
) {
"val creates an immutable value."
} else if (
question.contains(
"var"
)
) {
"var creates a mutable variable."
} else if (
question.contains(
"swing"
)
) {
"Scala can use Java Swing components such as JFrame, JPanel, JButton, JLabel and JTextArea."
} else if (
question.contains(
"listener"
)
) {
"Listeners allow Swing components to react to user actions."
} else if (
question.contains(
"timer"
)
) {
"Use javax.swing.Timer for repeated Swing updates and animation."
} else if (
question.contains(
"3d"
)
) {
"A Swing application can create strong 3D-style visuals using Java2D, perspective projection, camera math and procedural polygon rendering."
} else {
"Break the problem into input, state, UI, events and output. Then implement one part at a time."
}
teacherOutput.setText(
answer
)
recordActivity(
"Used Ask Teacher"
)
}
}
)
clearTeacherButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
teacherInput.setText(
""
)
teacherOutput.setText(
""
)
}
}
)
val teacherButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
teacherButtons.setBackground(
PANEL_BG
)
teacherButtons.add(
askTeacherButton
)
teacherButtons.add(
clearTeacherButton
)
val teacherSplit =
new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
makeScroll(
teacherInput
),
makeScroll(
teacherOutput
)
)
teacherSplit.setResizeWeight(
0.45
)
teacherPanel.add(
makeLabel(
"ASK TEACHER",
24,
true
),
BorderLayout.NORTH
)
teacherPanel.add(
teacherSplit,
BorderLayout.CENTER
)
teacherPanel.add(
teacherButtons,
BorderLayout.SOUTH
)
// ========================================================
// CODE LAB
// ========================================================
val codeLabPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
codeLabPanel.setBackground(
PANEL_BG
)
codeLabPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val codeArea =
makeArea()
codeArea.setText(
"""import javax.swing._
import java.awt._
val frame =
new JFrame("Scala Swing")
frame.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
frame.setSize(
500,
300
)
val label =
new JLabel("Hello Scala Swing!")
frame.add(
label
)
frame.setLocationRelativeTo(
null
)
frame.setVisible(
true
)
"""
)
val codeOutput =
makeArea()
codeOutput.setEditable(
false
)
val analyzeCodeButton =
makeButton(
"ANALYZE CODE"
)
val copyCodeButton =
makeButton(
"COPY"
)
val saveCodeButton =
makeButton(
"SAVE"
)
val clearCodeButton =
makeButton(
"CLEAR"
)
analyzeCodeButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val code =
codeArea.getText
val problems =
ArrayBuffer[String]()
if (
code.trim.isEmpty
) {
problems +=
"Code area is empty."
}
if (
code.contains(
"JFrame.EXIT_ON_CLOSE"
)
) {
problems +=
"Use WindowConstants.EXIT_ON_CLOSE."
}
if (
code.count(
_ == '{'
) !=
code.count(
_ == '}'
)
) {
problems +=
"Possible { } mismatch."
}
if (
code.count(
_ == '('
) !=
code.count(
_ == ')'
)
) {
problems +=
"Possible ( ) mismatch."
}
if (
code.count(
_ == '['
) !=
code.count(
_ == ']'
)
) {
problems +=
"Possible [ ] mismatch."
}
if (
code.contains(
"def refreshHistory"
) &&
code.contains(
"val refreshHistory"
)
) {
problems +=
"Possible duplicate refreshHistory name. Use refreshHistoryButton and refreshHistoryView."
}
if (
problems.isEmpty
) {
codeOutput.setText(
"No obvious common issue detected.\n\n" +
"This quick analyzer is not a replacement for the Scala compiler."
)
} else {
codeOutput.setText(
problems.mkString(
"Possible issues:\n\n",
"\n\n",
""
)
)
}
recordActivity(
"Analyzed Code Lab code"
)
}
}
)
copyCodeButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
copyToClipboard(
codeArea.getText
)
showInfo(
frame,
"COPIED",
"Code copied."
)
}
}
)
saveCodeButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
saveTextFile(
codeArea.getText,
"ScalaCode.scala"
)
}
}
)
clearCodeButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
codeArea.setText(
""
)
codeOutput.setText(
""
)
}
}
)
val codeButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
codeButtons.setBackground(
PANEL_BG
)
codeButtons.add(
analyzeCodeButton
)
codeButtons.add(
copyCodeButton
)
codeButtons.add(
saveCodeButton
)
codeButtons.add(
clearCodeButton
)
val codeSplit =
new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
makeScroll(
codeArea
),
makeScroll(
codeOutput
)
)
codeSplit.setResizeWeight(
0.65
)
codeLabPanel.add(
makeLabel(
"CODE LAB",
24,
true
),
BorderLayout.NORTH
)
codeLabPanel.add(
codeSplit,
BorderLayout.CENTER
)
codeLabPanel.add(
codeButtons,
BorderLayout.SOUTH
)
// ========================================================
// ERROR FIX
// ========================================================
val errorPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
errorPanel.setBackground(
PANEL_BG
)
errorPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val errorInput =
makeArea()
val errorOutput =
makeArea()
errorOutput.setEditable(
false
)
val fixErrorButton =
makeButton(
"FIX ERROR"
)
val clearErrorButton =
makeButton(
"CLEAR"
)
fixErrorButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val error =
errorInput
.getText
.trim
val answer =
if (
error.contains(
"EXIT_ON_CLOSE"
)
) {
"""Use:
frame.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
Do not use JFrame.EXIT_ON_CLOSE."""
} else if (
error.contains(
"already defined as value"
)
) {
"""The same name is being used twice.
For example:
val refreshHistory = ...
def refreshHistory(): Unit = ...
Use unique names:
val refreshHistoryButton = ...
def refreshHistoryView(): Unit = ..."""
} else if (
error.contains(
"cannot be applied to (Unit)"
)
) {
"""A method returning Unit is being passed where a Component is required.
Wrong:
historyButtons.add(
refreshHistory()
)
Correct:
historyButtons.add(
refreshHistoryButton
)"""
} else if (
error.contains(
"addActionListener is not a member of Unit"
)
) {
"""The variable before addActionListener is resolving to Unit.
Make sure it is actually a JButton or another valid Swing component."""
} else if (
error.contains(
"reassignment to val"
)
) {
"""A val cannot be reassigned.
Use var only for state that truly changes, or calculate a new value instead."""
} else if (
error.contains(
"required: Int"
)
) {
"""A Swing method usually expects an Int, but a Double or another numeric type was provided.
Convert safely with .toInt when appropriate."""
} else {
"Check the first compiler error first.\n\n" +
"? missing braces\n" +
"? duplicate names\n" +
"? undefined variables\n" +
"? undefined methods\n" +
"? wrong Swing component types\n" +
"? forward references\n" +
"? incorrect listeners\n" +
"? numeric type mismatch\n" +
"? invalid timer or rendering code"
}
errorOutput.setText(
if (
error.isEmpty
)
"Paste a compiler error first."
else
answer
)
recordActivity(
"Used Error Fix"
)
}
}
)
clearErrorButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
errorInput.setText(
""
)
errorOutput.setText(
""
)
}
}
)
val errorButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
errorButtons.setBackground(
PANEL_BG
)
errorButtons.add(
fixErrorButton
)
errorButtons.add(
clearErrorButton
)
val errorSplit =
new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
makeScroll(
errorInput
),
makeScroll(
errorOutput
)
)
errorSplit.setResizeWeight(
0.45
)
errorPanel.add(
makeLabel(
"ERROR FIX",
24,
true
),
BorderLayout.NORTH
)
errorPanel.add(
errorSplit,
BorderLayout.CENTER
)
errorPanel.add(
errorButtons,
BorderLayout.SOUTH
)
// ========================================================
// NOTES
// ========================================================
val notesPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
notesPanel.setBackground(
PANEL_BG
)
notesPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val notesTitleField =
makeTextField()
val notesArea =
makeArea()
val notesModel =
new DefaultListModel[String]()
val notesList =
new JList[String](
notesModel
)
notesList.setBackground(
new Color(
12,
16,
23
)
)
notesList.setForeground(
TEXT
)
val saveNoteButton =
makeButton(
"SAVE NOTE"
)
val loadNoteButton =
makeButton(
"LOAD"
)
val deleteNoteButton =
makeButton(
"DELETE"
)
saveNoteButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val title =
notesTitleField
.getText
.trim
val body =
notesArea.getText
if (
title.isEmpty
) {
showWarning(
frame,
"NOTES",
"Enter a note title."
)
} else {
notesData +=
title +
"||" +
body
notesModel.addElement(
title
)
notesTitleField.setText(
""
)
notesArea.setText(
""
)
recordActivity(
"Saved note: " +
title
)
}
}
}
)
loadNoteButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val index =
notesList.getSelectedIndex
if (
index >= 0 &&
index < notesData.length
) {
val parts =
notesData(
index
).split(
"\\|\\|",
2
)
if (
parts.length == 2
) {
notesTitleField.setText(
parts(
0
)
)
notesArea.setText(
parts(
1
)
)
}
}
}
}
)
deleteNoteButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val index =
notesList.getSelectedIndex
if (
index >= 0 &&
index < notesData.length
) {
notesData.remove(
index
)
notesModel.remove(
index
)
notesTitleField.setText(
""
)
notesArea.setText(
""
)
}
}
}
)
val notesButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
notesButtons.setBackground(
PANEL_BG
)
notesButtons.add(
saveNoteButton
)
notesButtons.add(
loadNoteButton
)
notesButtons.add(
deleteNoteButton
)
val notesEditor =
new JPanel(
new BorderLayout(
8,
8
)
)
notesEditor.setBackground(
PANEL_BG
)
notesEditor.add(
notesTitleField,
BorderLayout.NORTH
)
notesEditor.add(
makeScroll(
notesArea
),
BorderLayout.CENTER
)
notesEditor.add(
notesButtons,
BorderLayout.SOUTH
)
notesPanel.add(
makeLabel(
"NOTES",
24,
true
),
BorderLayout.NORTH
)
notesPanel.add(
makeScroll(
notesList
),
BorderLayout.WEST
)
notesPanel.add(
notesEditor,
BorderLayout.CENTER
)
// ========================================================
// TASKS
// ========================================================
val tasksPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
tasksPanel.setBackground(
PANEL_BG
)
tasksPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val taskField =
makeTextField()
val taskModel =
new DefaultListModel[String]()
val taskList =
new JList[String](
taskModel
)
taskList.setBackground(
new Color(
12,
16,
23
)
)
taskList.setForeground(
TEXT
)
val addTaskButton =
makeButton(
"ADD TASK"
)
val completeTaskButton =
makeButton(
"COMPLETE"
)
val deleteTaskButton =
makeButton(
"DELETE"
)
addTaskButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val task =
taskField
.getText
.trim
if (
task.nonEmpty
) {
tasksData +=
task
taskModel.addElement(
task
)
taskField.setText(
""
)
recordActivity(
"Added task: " +
task
)
}
}
}
)
completeTaskButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val index =
taskList.getSelectedIndex
if (
index >= 0 &&
index < taskModel.getSize
) {
val text =
taskModel.getElementAt(
index
)
if (
!text.startsWith(
"[DONE]"
)
) {
taskModel.setElementAt(
"[DONE] " +
text,
index
)
}
}
}
}
)
deleteTaskButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val index =
taskList.getSelectedIndex
if (
index >= 0 &&
index < taskModel.getSize
) {
taskModel.remove(
index
)
if (
index < tasksData.length
) {
tasksData.remove(
index
)
}
}
}
}
)
val taskTop =
new JPanel(
new BorderLayout(
6,
6
)
)
taskTop.setBackground(
PANEL_BG
)
taskTop.add(
taskField,
BorderLayout.CENTER
)
taskTop.add(
addTaskButton,
BorderLayout.EAST
)
val taskBottom =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
taskBottom.setBackground(
PANEL_BG
)
taskBottom.add(
completeTaskButton
)
taskBottom.add(
deleteTaskButton
)
val taskCenter =
new JPanel(
new BorderLayout(
8,
8
)
)
taskCenter.setBackground(
PANEL_BG
)
taskCenter.add(
taskTop,
BorderLayout.NORTH
)
taskCenter.add(
makeScroll(
taskList
),
BorderLayout.CENTER
)
taskCenter.add(
taskBottom,
BorderLayout.SOUTH
)
tasksPanel.add(
makeLabel(
"TASKS",
24,
true
),
BorderLayout.NORTH
)
tasksPanel.add(
taskCenter,
BorderLayout.CENTER
)
// ========================================================
// QUIZ
// ========================================================
val quizPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
quizPanel.setBackground(
PANEL_BG
)
quizPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val quizQuestion =
makeLabel(
"QUIZ READY",
20,
true
)
val quizButtonsPanel =
new JPanel(
new GridLayout(
4,
1,
8,
8
)
)
quizButtonsPanel.setBackground(
PANEL_BG
)
val quizOptions =
Array(
makeButton(""),
makeButton(""),
makeButton(""),
makeButton("")
)
quizOptions.foreach(
quizButtonsPanel.add
)
val quizQuestions =
Array(
(
"Which keyword declares an immutable value?",
Array(
"var",
"val",
"let",
"const"
),
1
),
(
"Which component accepts multi-line text?",
Array(
"JLabel",
"JTextArea",
"JButton",
"JFrame"
),
2
),
(
"Which constant is correct for JFrame closing?",
Array(
"JFrame.EXIT_ON_CLOSE",
"WindowConstants.EXIT_ON_CLOSE",
"Frame.EXIT",
"JWindow.EXIT"
),
2
)
)
var quizIndex =
0
var quizScore =
0
var quizAnswered =
false
val quizScoreLabel =
makeLabel(
"Score: 0",
15,
true
)
val nextQuizButton =
makeButton(
"NEXT"
)
def loadQuiz(): Unit = {
if (
quizIndex <
quizQuestions.length
) {
val q =
quizQuestions(
quizIndex
)
quizQuestion.setText(
(
quizIndex + 1
) +
". " +
q._1
)
var i =
0
while (
i <
4
) {
quizOptions(
i
).setText(
q._2(
i
)
)
quizOptions(
i
).setEnabled(
true
)
i += 1
}
quizAnswered =
false
nextQuizButton.setEnabled(
quizIndex <
quizQuestions.length
)
} else {
quizQuestion.setText(
"QUIZ COMPLETE!"
)
var i =
0
while (
i <
4
) {
quizOptions(
i
).setText(
""
)
quizOptions(
i
).setEnabled(
false
)
i += 1
}
nextQuizButton.setEnabled(
false
)
}
quizScoreLabel.setText(
"Score: " +
quizScore +
" / " +
quizQuestions.length
)
}
var quizButtonIndex =
0
while (
quizButtonIndex <
quizOptions.length
) {
val selectedIndex =
quizButtonIndex
quizOptions(
quizButtonIndex
).addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (
!quizAnswered &&
quizIndex <
quizQuestions.length
) {
quizAnswered =
true
val correctIndex =
quizQuestions(
quizIndex
)._3 - 1
if (
selectedIndex ==
correctIndex
) {
quizScore +=
1
quizQuestion.setText(
quizQuestion.getText +
" ? Correct"
)
} else {
quizQuestion.setText(
quizQuestion.getText +
" ? Wrong"
)
}
var i =
0
while (
i <
quizOptions.length
) {
quizOptions(
i
).setEnabled(
false
)
i += 1
}
quizScoreLabel.setText(
"Score: " +
quizScore +
" / " +
quizQuestions.length
)
}
}
}
)
quizButtonIndex += 1
}
nextQuizButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (
quizIndex <
quizQuestions.length
) {
quizIndex +=
1
loadQuiz()
}
}
}
)
val quizBottom =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
quizBottom.setBackground(
PANEL_BG
)
quizBottom.add(
quizScoreLabel
)
quizBottom.add(
nextQuizButton
)
quizPanel.add(
makeLabel(
"QUIZ",
24,
true
),
BorderLayout.NORTH
)
val quizCenter =
new JPanel(
new BorderLayout(
10,
10
)
)
quizCenter.setBackground(
PANEL_BG
)
quizCenter.add(
quizQuestion,
BorderLayout.NORTH
)
quizCenter.add(
quizButtonsPanel,
BorderLayout.CENTER
)
quizCenter.add(
quizBottom,
BorderLayout.SOUTH
)
quizPanel.add(
quizCenter,
BorderLayout.CENTER
)
loadQuiz()
// ========================================================
// IMAGE STUDIO
// ========================================================
val imagePanel =
new JPanel(
new BorderLayout(
10,
10
)
)
imagePanel.setBackground(
PANEL_BG
)
imagePanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val imagePreview =
new JLabel(
"No image selected",
SwingConstants.CENTER
)
imagePreview.setForeground(
MUTED
)
val openImageButton =
makeButton(
"OPEN IMAGE"
)
val clearImageButton =
makeButton(
"CLEAR"
)
openImageButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val chooser =
new JFileChooser()
if (
chooser.showOpenDialog(
frame
) ==
JFileChooser.APPROVE_OPTION
) {
val file =
chooser.getSelectedFile
try {
val image =
ImageIO.read(
file
)
if (
image == null
) {
showError(
frame,
"IMAGE",
"Unsupported image."
)
} else {
imagePreview.setIcon(
new ImageIcon(
scaleImage(
image,
800,
550
)
)
)
imagePreview.setText(
""
)
recordActivity(
"Opened image: " +
file.getName
)
}
} catch {
case ex: Throwable =>
showError(
frame,
"IMAGE",
ex.toString
)
}
}
}
}
)
clearImageButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
imagePreview.setIcon(
null
)
imagePreview.setText(
"No image selected"
)
}
}
)
val imageButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
imageButtons.setBackground(
PANEL_BG
)
imageButtons.add(
openImageButton
)
imageButtons.add(
clearImageButton
)
imagePanel.add(
makeLabel(
"IMAGE STUDIO",
24,
true
),
BorderLayout.NORTH
)
imagePanel.add(
imagePreview,
BorderLayout.CENTER
)
imagePanel.add(
imageButtons,
BorderLayout.SOUTH
)
// ========================================================
// PROMPT LAB
// ========================================================
val promptLabPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
promptLabPanel.setBackground(
PANEL_BG
)
promptLabPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val promptIdeaArea =
makeArea()
val promptOutputArea =
makeArea()
promptOutputArea.setEditable(
false
)
val generatePromptButton =
makeButton(
"GENERATE PROMPT"
)
val copyPromptButton =
makeButton(
"COPY"
)
generatePromptButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val idea =
promptIdeaArea
.getText
.trim
promptOutputArea.setText(
if (
idea.isEmpty
) {
"Write your idea first."
} else {
createMasterPrompt(
idea
)
}
)
recordActivity(
"Generated prompt from Prompt Lab"
)
}
}
)
copyPromptButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val generated =
promptOutputArea
.getText
.trim
if (
generated.isEmpty
) {
showWarning(
frame,
"PROMPT LAB",
"Generate the prompt first."
)
} else {
copyToClipboard(
generated
)
showInfo(
frame,
"COPIED",
"Prompt copied."
)
}
}
}
)
val promptButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
promptButtons.setBackground(
PANEL_BG
)
promptButtons.add(
generatePromptButton
)
promptButtons.add(
copyPromptButton
)
val promptSplit =
new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
makeScroll(
promptIdeaArea
),
makeScroll(
promptOutputArea
)
)
promptSplit.setResizeWeight(
0.4
)
promptLabPanel.add(
makeLabel(
"PROMPT LAB",
24,
true
),
BorderLayout.NORTH
)
promptLabPanel.add(
promptSplit,
BorderLayout.CENTER
)
promptLabPanel.add(
promptButtons,
BorderLayout.SOUTH
)
// ========================================================
// PROJECTS
// ========================================================
val projectsPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
projectsPanel.setBackground(
PANEL_BG
)
projectsPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val projectNameField =
makeTextField()
val projectDescriptionArea =
makeArea()
val projectModel =
new DefaultListModel[String]()
val projectList =
new JList[String](
projectModel
)
projectList.setBackground(
new Color(
12,
16,
23
)
)
projectList.setForeground(
TEXT
)
val saveProjectButton =
makeButton(
"SAVE"
)
val loadProjectButton =
makeButton(
"LOAD"
)
val deleteProjectButton =
makeButton(
"DELETE"
)
saveProjectButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val name =
projectNameField
.getText
.trim
if (
name.isEmpty
) {
showWarning(
frame,
"PROJECTS",
"Enter a project name."
)
} else {
projectData +=
name +
"||" +
projectDescriptionArea.getText
projectModel.addElement(
name
)
projectNameField.setText(
""
)
projectDescriptionArea.setText(
""
)
recordActivity(
"Saved project: " +
name
)
}
}
}
)
loadProjectButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val index =
projectList.getSelectedIndex
if (
index >= 0 &&
index < projectData.length
) {
val parts =
projectData(
index
).split(
"\\|\\|",
2
)
if (
parts.length == 2
) {
projectNameField.setText(
parts(
0
)
)
projectDescriptionArea.setText(
parts(
1
)
)
}
}
}
}
)
deleteProjectButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val index =
projectList.getSelectedIndex
if (
index >= 0 &&
index < projectModel.getSize
) {
projectModel.remove(
index
)
if (
index < projectData.length
) {
projectData.remove(
index
)
}
}
}
}
)
val projectEditor =
new JPanel(
new BorderLayout(
8,
8
)
)
projectEditor.setBackground(
PANEL_BG
)
projectEditor.add(
projectNameField,
BorderLayout.NORTH
)
projectEditor.add(
makeScroll(
projectDescriptionArea
),
BorderLayout.CENTER
)
val projectButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
projectButtons.setBackground(
PANEL_BG
)
projectButtons.add(
saveProjectButton
)
projectButtons.add(
loadProjectButton
)
projectButtons.add(
deleteProjectButton
)
projectEditor.add(
projectButtons,
BorderLayout.SOUTH
)
projectsPanel.add(
makeLabel(
"PROJECTS",
24,
true
),
BorderLayout.NORTH
)
projectsPanel.add(
makeScroll(
projectList
),
BorderLayout.WEST
)
projectsPanel.add(
projectEditor,
BorderLayout.CENTER
)
// ========================================================
// UTILITIES
// ========================================================
val utilitiesPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
utilitiesPanel.setBackground(
PANEL_BG
)
utilitiesPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val stopwatchLabel =
makeLabel(
"00:00:00",
42,
true
)
stopwatchLabel.setHorizontalAlignment(
SwingConstants.CENTER
)
var stopwatchSeconds =
0L
var stopwatchRunning =
false
val stopwatchTimer =
new javax.swing.Timer(
1000,
null
)
stopwatchTimer.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (
stopwatchRunning
) {
stopwatchSeconds +=
1
val h =
stopwatchSeconds / 3600
val m =
(
stopwatchSeconds % 3600
) / 60
val s =
stopwatchSeconds % 60
stopwatchLabel.setText(
"%02d:%02d:%02d".format(
h,
m,
s
)
)
}
}
}
)
val startStopwatchButton =
makeButton(
"START"
)
val stopStopwatchButton =
makeButton(
"STOP"
)
val resetStopwatchButton =
makeButton(
"RESET"
)
startStopwatchButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
stopwatchRunning =
true
stopwatchTimer.start()
}
}
)
stopStopwatchButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
stopwatchRunning =
false
}
}
)
resetStopwatchButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
stopwatchRunning =
false
stopwatchSeconds =
0L
stopwatchLabel.setText(
"00:00:00"
)
}
}
)
val utilityButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
utilityButtons.setBackground(
PANEL_BG
)
utilityButtons.add(
startStopwatchButton
)
utilityButtons.add(
stopStopwatchButton
)
utilityButtons.add(
resetStopwatchButton
)
utilitiesPanel.add(
makeLabel(
"UTILITIES",
24,
true
),
BorderLayout.NORTH
)
utilitiesPanel.add(
stopwatchLabel,
BorderLayout.CENTER
)
utilitiesPanel.add(
utilityButtons,
BorderLayout.SOUTH
)
// ========================================================
// FEATURE LIBRARY
// ========================================================
val libraryPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
libraryPanel.setBackground(
PANEL_BG
)
libraryPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val libraryArea =
makeArea()
libraryArea.setEditable(
false
)
libraryArea.setText(
"""FEATURE LIBRARY
SECURE LOGIN
3-attempt password protection.
CLASSROOM
Scala and Swing learning lessons.
ASK TEACHER
Simple coding guidance.
CODE LAB
Write and analyze code.
ERROR FIX
Common Scala Swing error guidance.
NOTES
Save notes during the session.
TASKS
Manage coding tasks.
QUIZ
Practice Scala and Swing knowledge.
IMAGE STUDIO
Open and preview images.
PROMPT LAB
Generate reusable coding prompts.
PROJECTS
Store project ideas.
UTILITIES
Stopwatch.
HISTORY
Track activity.
SETTINGS
Application settings.
MASTER ACCESS
Protected MASTER PROMPT area.
MASTER PROMPT
No UID.
No NAME.
Only IDEA.
3D RULES
When a 3D game is requested, the generated prompt
requires a self-contained software 3D / pseudo-3D
renderer using Scala Swing and Java2D when an external
3D engine is not guaranteed.
The generated prompt also requires complete compile
review, type safety, correct event handling, timer
safety, rendering safety, game state handling,
restart, win/lose and playable controls."""
)
libraryPanel.add(
makeLabel(
"FEATURE LIBRARY",
24,
true
),
BorderLayout.NORTH
)
libraryPanel.add(
makeScroll(
libraryArea
),
BorderLayout.CENTER
)
// ========================================================
// HISTORY
// ========================================================
val historyPanel =
new JPanel(
new BorderLayout(
8,
8
)
)
historyPanel.setBackground(
PANEL_BG
)
historyPanel.setBorder(
new EmptyBorder(
12,
12,
12,
12
)
)
val historyArea =
makeArea()
historyArea.setEditable(
false
)
val refreshHistoryButton =
makeButton(
"REFRESH"
)
val exportHistoryButton =
makeButton(
"EXPORT"
)
val clearHistoryButton =
makeButton(
"CLEAR"
)
val historyButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
historyButtons.setBackground(
PANEL_BG
)
historyButtons.add(
refreshHistoryButton
)
historyButtons.add(
exportHistoryButton
)
historyButtons.add(
clearHistoryButton
)
def refreshHistoryView(): Unit = {
if (
activityHistory.isEmpty
) {
historyArea.setText(
"No activity yet."
)
} else {
historyArea.setText(
activityHistory
.reverse
.mkString(
"\n"
)
)
}
historyArea.setCaretPosition(
0
)
}
refreshHistoryButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
refreshHistoryView()
}
}
)
exportHistoryButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (
activityHistory.isEmpty
) {
showInfo(
frame,
"HISTORY",
"Nothing to export."
)
} else {
saveTextFile(
activityHistory
.reverse
.mkString(
"\n"
),
"ActivityHistory.txt"
)
}
}
}
)
clearHistoryButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
activityHistory.clear()
refreshHistoryView()
}
}
)
historyPanel.add(
makeLabel(
"ACTIVITY HISTORY",
24,
true
),
BorderLayout.NORTH
)
historyPanel.add(
makeScroll(
historyArea
),
BorderLayout.CENTER
)
historyPanel.add(
historyButtons,
BorderLayout.SOUTH
)
// ========================================================
// SETTINGS
// ========================================================
val settingsPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
settingsPanel.setBackground(
PANEL_BG
)
settingsPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val settingsText =
makeArea()
settingsText.setEditable(
false
)
settingsText.setText(
"""ULTRA SCALA SWING AI LEARNING STUDIO
STATUS: READY
MASTER PROMPT:
Password protected.
No UID field.
No NAME field.
Only the IDEA is required.
3D support:
The MASTER PROMPT requests a self-contained
Java2D/Swing software 3D or pseudo-3D renderer when
the user's idea requires 3D."""
)
settingsPanel.add(
makeLabel(
"SETTINGS",
24,
true
),
BorderLayout.NORTH
)
settingsPanel.add(
makeScroll(
settingsText
),
BorderLayout.CENTER
)
// ========================================================
// MASTER PROMPT
// ========================================================
var masterUnlocked =
false
var masterTabAdded =
false
val masterPromptPanel =
new JPanel(
new BorderLayout(
10,
10
)
)
masterPromptPanel.setBackground(
PANEL_BG
)
masterPromptPanel.setBorder(
new EmptyBorder(
14,
14,
14,
14
)
)
val masterTitle =
makeLabel(
"MASTER PROMPT",
26,
true
)
val masterStatus =
makeLabel(
"ACCESS GRANTED",
16,
true
)
masterStatus.setForeground(
SUCCESS
)
val masterIntro =
makeLabel(
"Enter your IDEA only. No UID. No Name.",
15,
false
)
val masterIdeaArea =
makeArea()
masterIdeaArea.setToolTipText(
"Write only your game/app/software idea here."
)
val masterOutputArea =
makeArea()
masterOutputArea.setEditable(
false
)
val generateMasterPromptButton =
makeButton(
"GENERATE MASTER PROMPT"
)
val copyMasterPromptButton =
makeButton(
"COPY MASTER PROMPT"
)
val clearMasterPromptButton =
makeButton(
"CLEAR"
)
val lockMasterPromptButton =
makeButton(
"LOCK MASTER PROMPT"
)
val masterButtons =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
masterButtons.setBackground(
PANEL_BG
)
masterButtons.add(
generateMasterPromptButton
)
masterButtons.add(
copyMasterPromptButton
)
masterButtons.add(
clearMasterPromptButton
)
masterButtons.add(
lockMasterPromptButton
)
val masterIdeaPanel =
new JPanel(
new BorderLayout(
6,
6
)
)
masterIdeaPanel.setBackground(
PANEL_BG
)
masterIdeaPanel.add(
masterIntro,
BorderLayout.NORTH
)
masterIdeaPanel.add(
makeScroll(
masterIdeaArea
),
BorderLayout.CENTER
)
val masterOutputPanel =
new JPanel(
new BorderLayout(
6,
6
)
)
masterOutputPanel.setBackground(
PANEL_BG
)
masterOutputPanel.add(
makeLabel(
"GENERATED MASTER PROMPT",
17,
true
),
BorderLayout.NORTH
)
masterOutputPanel.add(
makeScroll(
masterOutputArea
),
BorderLayout.CENTER
)
val masterSplit =
new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
masterIdeaPanel,
masterOutputPanel
)
masterSplit.setResizeWeight(
0.38
)
val masterHeader =
new JPanel(
new BorderLayout(
10,
4
)
)
masterHeader.setBackground(
PANEL_BG
)
val masterHeading =
new JPanel(
new GridLayout(
2,
1,
2,
2
)
)
masterHeading.setBackground(
PANEL_BG
)
masterHeading.add(
masterTitle
)
masterHeading.add(
masterStatus
)
masterHeader.add(
masterHeading,
BorderLayout.WEST
)
masterPromptPanel.add(
masterHeader,
BorderLayout.NORTH
)
masterPromptPanel.add(
masterSplit,
BorderLayout.CENTER
)
masterPromptPanel.add(
masterButtons,
BorderLayout.SOUTH
)
// --------------------------------------------------------
// GENERATE MASTER PROMPT
// --------------------------------------------------------
generateMasterPromptButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (
!masterUnlocked
) {
showWarning(
frame,
"MASTER PROMPT",
"MASTER PROMPT is locked."
)
} else {
val idea =
masterIdeaArea
.getText
.trim
if (
idea.isEmpty
) {
showWarning(
frame,
"MASTER PROMPT",
"Write your IDEA first."
)
} else {
masterOutputArea.setText(
createMasterPrompt(
idea
)
)
masterOutputArea.setCaretPosition(
0
)
recordActivity(
"Generated MASTER PROMPT"
)
}
}
}
}
)
// --------------------------------------------------------
// COPY MASTER PROMPT
// --------------------------------------------------------
copyMasterPromptButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val generated =
masterOutputArea
.getText
.trim
if (
generated.isEmpty
) {
showWarning(
frame,
"MASTER PROMPT",
"Generate the prompt first."
)
} else {
copyToClipboard(
generated
)
showInfo(
frame,
"MASTER PROMPT",
"MASTER PROMPT copied to clipboard."
)
recordActivity(
"Copied MASTER PROMPT"
)
}
}
}
)
// --------------------------------------------------------
// CLEAR MASTER PROMPT
// --------------------------------------------------------
clearMasterPromptButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
masterIdeaArea.setText(
""
)
masterOutputArea.setText(
""
)
}
}
)
// --------------------------------------------------------
// LOCK MASTER PROMPT
// --------------------------------------------------------
def lockMasterPrompt(): Unit = {
if (
masterTabAdded
) {
tabs.remove(
masterPromptPanel
)
masterTabAdded =
false
}
masterUnlocked =
false
masterIdeaArea.setText(
""
)
masterOutputArea.setText(
""
)
masterStatus.setText(
"LOCKED"
)
masterStatus.setForeground(
WARNING
)
recordActivity(
"Locked MASTER PROMPT"
)
if (
tabs.getTabCount > 0
) {
tabs.setSelectedIndex(
0
)
}
}
lockMasterPromptButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
lockMasterPrompt()
}
}
)
// --------------------------------------------------------
// UNLOCK MASTER PROMPT
// --------------------------------------------------------
def unlockMasterPrompt(): Unit = {
val passwordField =
new JPasswordField()
passwordField.setPreferredSize(
new Dimension(
320,
35
)
)
val wrapper =
new JPanel(
new BorderLayout(
8,
8
)
)
wrapper.add(
new JLabel(
"Enter MASTER ACCESS password:"
),
BorderLayout.NORTH
)
wrapper.add(
passwordField,
BorderLayout.CENTER
)
val result =
JOptionPane.showConfirmDialog(
frame,
wrapper,
"MASTER PROMPT ACCESS",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE
)
if (
result ==
JOptionPane.OK_OPTION
) {
val entered =
new String(
passwordField.getPassword
)
val enteredHash =
sha256(
entered
)
if (
enteredHash ==
masterPromptPasswordHash
) {
masterUnlocked =
true
masterStatus.setText(
"ACCESS GRANTED"
)
masterStatus.setForeground(
SUCCESS
)
if (
!masterTabAdded
) {
tabs.addTab(
"MASTER PROMPT",
masterPromptPanel
)
masterTabAdded =
true
}
tabs.setSelectedComponent(
masterPromptPanel
)
recordActivity(
"MASTER PROMPT unlocked"
)
} else {
masterUnlocked =
false
showWarning(
frame,
"ACCESS DENIED",
"Wrong MASTER ACCESS password.\n\nMASTER PROMPT remains hidden."
)
recordActivity(
"Failed MASTER PROMPT access"
)
}
}
}
dashboardMasterButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
unlockMasterPrompt()
}
}
)
dashboardHistoryButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
tabs.setSelectedComponent(
historyPanel
)
refreshHistoryView()
}
}
)
dashboardSettingsButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
tabs.setSelectedComponent(
settingsPanel
)
}
}
)
// ========================================================
// AI DOCTOR - SAFE EDUCATIONAL HEALTH ASSISTANT
// ========================================================
val doctorPanel =
new JPanel(
new BorderLayout(12, 12)
)
doctorPanel.setBackground(
PANEL_BG
)
doctorPanel.setBorder(
new EmptyBorder(12, 12, 12, 12)
)
var doctorUnlocked = false
var doctorMood = "NEUTRAL"
var doctorPulse = 0
var doctorStatusText = "LOCKED"
var doctorTimer: javax.swing.Timer = null
val doctorTitle =
makeLabel(
"AI DOCTOR - HEALTH ASSISTANT",
26,
true
)
val doctorStatus =
makeLabel(
"LOCKED",
15,
true
)
doctorStatus.setForeground(
WARNING
)
val doctorHeader =
new JPanel(
new BorderLayout(10, 10)
)
doctorHeader.setBackground(
PANEL_BG
)
doctorHeader.add(
doctorTitle,
BorderLayout.WEST
)
doctorHeader.add(
doctorStatus,
BorderLayout.EAST
)
doctorPanel.add(
doctorHeader,
BorderLayout.NORTH
)
// --------------------------------------------------------
// ANIMATED AI DOCTOR AVATAR
// --------------------------------------------------------
val doctorAvatar =
new JPanel {
setOpaque(false)
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(graphics)
val g =
graphics.asInstanceOf[Graphics2D]
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
val w = getWidth
val h = getHeight
val cx = w / 2
val cy = h / 2
val glow =
36 + Math.abs(doctorPulse % 20)
g.setColor(
new Color(60, 150, 255, 22)
)
g.fillOval(
cx - 100 - glow,
cy - 100 - glow,
200 + glow * 2,
200 + glow * 2
)
var ring = 0
while (ring < 3) {
g.setColor(
new Color(
80,
170,
255,
35 + ring * 18
)
)
g.drawOval(
cx - 90 - ring * 18,
cy - 90 - ring * 18,
180 + ring * 36,
180 + ring * 36
)
ring += 1
}
// Head
g.setColor(
new Color(205, 220, 235)
)
g.fillOval(
cx - 64,
cy - 72,
128,
128
)
g.setColor(
new Color(70, 80, 95)
)
g.fillArc(
cx - 64,
cy - 78,
128,
65,
0,
180
)
// Ears
g.fillRoundRect(
cx - 76,
cy - 20,
18,
38,
10,
10
)
g.fillRoundRect(
cx + 58,
cy - 20,
18,
38,
10,
10
)
val happy =
doctorMood == "HAPPY"
val sad =
doctorMood == "SAD"
// Eyes
g.setColor(
new Color(30, 40, 50)
)
if (happy) {
g.drawArc(
cx - 40,
cy - 15,
22,
14,
0,
180
)
g.drawArc(
cx + 18,
cy - 15,
22,
14,
0,
180
)
} else {
g.fillOval(
cx - 33,
cy - 12,
14,
14
)
g.fillOval(
cx + 20,
cy - 12,
14,
14
)
}
// Nose
g.drawLine(
cx,
cy - 4,
cx - 5,
cy + 17
)
g.drawLine(
cx - 5,
cy + 17,
cx + 6,
cy + 17
)
// Mouth
if (sad) {
g.drawArc(
cx - 25,
cy + 15,
50,
25,
0,
180
)
} else {
g.drawArc(
cx - 25,
cy + 5,
50,
30,
180,
180
)
}
// Medical badge / coat
g.setColor(
new Color(242, 246, 250)
)
g.fillRoundRect(
cx - 83,
cy + 54,
166,
94,
24,
24
)
g.setColor(
DANGER
)
g.fillRect(
cx - 10,
cy + 70,
20,
55
)
g.fillRect(
cx - 27,
cy + 87,
54,
20
)
g.setColor(
new Color(30, 45, 60)
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
15
)
)
g.drawString(
doctorMood,
cx - 40,
cy + 176
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
11
)
)
g.drawString(
doctorStatusText,
cx - 65,
cy + 195
)
}
}
doctorAvatar.setPreferredSize(
new Dimension(300, 390)
)
// --------------------------------------------------------
// DOCTOR LOGIN
// --------------------------------------------------------
val doctorPasswordArea =
new JPanel(
new BorderLayout(8, 8)
)
doctorPasswordArea.setBackground(
PANEL_BG
)
val doctorPasswordTitle =
makeLabel(
"DOCTOR ACCESS",
18,
true
)
val doctorPasswordField =
new JPasswordField()
doctorPasswordField.setFont(
new Font(
"SansSerif",
Font.PLAIN,
18
)
)
val doctorLoginButton =
makeButton(
"OPEN AI DOCTOR"
)
doctorPasswordArea.add(
doctorPasswordTitle,
BorderLayout.NORTH
)
doctorPasswordArea.add(
doctorPasswordField,
BorderLayout.CENTER
)
doctorPasswordArea.add(
doctorLoginButton,
BorderLayout.SOUTH
)
// --------------------------------------------------------
// ADVANCED MULTILINGUAL CHECKUP INPUTS
// --------------------------------------------------------
val doctorSymptomArea =
makeArea()
doctorSymptomArea.setLineWrap(
true
)
doctorSymptomArea.setWrapStyleWord(
true
)
doctorSymptomArea.setText(
"????? / ?????? / English / mixed language ????? symptoms ????.\n" +
"Example: ??? ????? ???, nose blocked ??? ??? ???? cough ???."
)
val doctorTempField =
makeTextField()
val doctorDaysField =
makeTextField()
val doctorLocationField =
makeTextField()
val doctorAgeField =
makeTextField()
val doctorSexField =
makeTextField()
doctorSexField.setToolTipText(
"Optional: Male / Female / Other"
)
val doctorLanguageBox =
new JComboBox[String](
Array(
"AUTO DETECT",
"MARATHI",
"HINDI",
"ENGLISH",
"MIXED"
)
)
doctorLanguageBox.setFont(
new Font(
"SansSerif",
Font.PLAIN,
13
)
)
val doctorResultArea =
makeArea()
doctorResultArea.setEditable(
false
)
doctorResultArea.setLineWrap(
true
)
doctorResultArea.setWrapStyleWord(
true
)
val doctorCheckupButton =
makeButton(
"FULL CHECKUP"
)
val doctorUnderstandButton =
makeButton(
"UNDERSTAND TEXT"
)
val doctorMedicineButton =
makeButton(
"MEDICINE INFORMATION"
)
val doctorClearButton =
makeButton(
"CLEAR"
)
val doctorSafetyButton =
makeButton(
"SAFETY GUIDE"
)
val doctorModeLabel =
makeLabel(
"MODE: MULTILINGUAL OFFLINE UNDERSTANDING",
12,
true
)
doctorModeLabel.setForeground(
new Color(150, 210, 255)
)
val doctorForm =
new JPanel(
new BorderLayout(8, 8)
)
doctorForm.setBackground(
PANEL_BG
)
val doctorFields =
new JPanel(
new GridLayout(
6,
2,
8,
8
)
)
doctorFields.setBackground(
PANEL_BG
)
doctorFields.add(
makeLabel(
"Temperature (optional)",
12,
false
)
)
doctorFields.add(
doctorTempField
)
doctorFields.add(
makeLabel(
"How many days?",
12,
false
)
)
doctorFields.add(
doctorDaysField
)
doctorFields.add(
makeLabel(
"Pain / location",
12,
false
)
)
doctorFields.add(
doctorLocationField
)
doctorFields.add(
makeLabel(
"Age (optional)",
12,
false
)
)
doctorFields.add(
doctorAgeField
)
doctorFields.add(
makeLabel(
"Sex (optional)",
12,
false
)
)
doctorFields.add(
doctorSexField
)
doctorFields.add(
makeLabel(
"Input language",
12,
false
)
)
doctorFields.add(
doctorLanguageBox
)
doctorForm.add(
doctorFields,
BorderLayout.NORTH
)
doctorForm.add(
makeScroll(
doctorSymptomArea
),
BorderLayout.CENTER
)
val doctorButtons =
new JPanel(
new GridLayout(
2,
3,
6,
6
)
)
doctorButtons.setBackground(
PANEL_BG
)
doctorButtons.add(
doctorCheckupButton
)
doctorButtons.add(
doctorUnderstandButton
)
doctorButtons.add(
doctorMedicineButton
)
doctorButtons.add(
doctorClearButton
)
doctorButtons.add(
doctorSafetyButton
)
doctorButtons.add(
doctorModeLabel
)
doctorForm.add(
doctorButtons,
BorderLayout.SOUTH
)
val doctorMain =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
doctorAvatar,
doctorForm
)
doctorMain.setResizeWeight(
0.29
)
doctorMain.setDividerLocation(
330
)
val doctorOutputPanel =
new JPanel(
new BorderLayout(8, 8)
)
doctorOutputPanel.setBackground(
PANEL_BG
)
doctorOutputPanel.add(
makeLabel(
"CHECKUP / UNDERSTANDING REPORT",
17,
true
),
BorderLayout.NORTH
)
doctorOutputPanel.add(
makeScroll(
doctorResultArea
),
BorderLayout.CENTER
)
val doctorStack =
new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
doctorMain,
doctorOutputPanel
)
doctorStack.setResizeWeight(
0.57
)
doctorPanel.add(
doctorStack,
BorderLayout.CENTER
)
doctorResultArea.setText(
"AI DOCTOR LOCKED\n\n" +
"Open with the Doctor Access password first.\n\n" +
"Then type symptoms in Marathi, Hindi, English, transliterated text, or a mixture.\n" +
"The app performs offline keyword-based understanding and safety triage.\n\n" +
"MEDICINE INFORMATION mode gives general educational guidance only. It does not issue a personal prescription or dosage."
)
// --------------------------------------------------------
// ADVANCED OFFLINE LANGUAGE / SYMPTOM ENGINE
// --------------------------------------------------------
def doctorNormalize(
text: String
): String = {
if (text == null)
""
else
text
.trim
.toLowerCase
.replace("?", ".")
.replace(",", " ")
.replace(";", " ")
.replace("/", " ")
}
def doctorContainsAny(
text: String,
words: Array[String]
): Boolean = {
var i = 0
while (i < words.length) {
if (text.contains(words(i)))
return true
i += 1
}
false
}
def doctorDetectLanguage(
raw: String
): String = {
if (raw == null || raw.trim.isEmpty)
"UNKNOWN"
else {
var marathiHindi = 0
var latin = 0
var other = 0
var i = 0
while (i < raw.length) {
val ch = raw.charAt(i).toInt
if (ch >= 0x0900 && ch <= 0x097F)
marathiHindi += 1
else if (
(ch >= 65 && ch <= 90) ||
(ch >= 97 && ch <= 122)
)
latin += 1
else if (!Character.isWhitespace(raw.charAt(i)))
other += 1
i += 1
}
if (marathiHindi > 0 && latin > 0)
"MIXED MARATHI/HINDI + ENGLISH"
else if (marathiHindi > latin && marathiHindi >= other)
"MARATHI/HINDI SCRIPT"
else if (latin > 0)
"ENGLISH / LATIN"
else
"OTHER / UNRECOGNIZED SCRIPT"
}
}
def doctorCanonicalize(
raw: String
): String = {
var t = doctorNormalize(raw)
val replacements = Array(
("jukham", " cold "),
("zukam", " cold "),
("sardi", " cold "),
("sardi hui", " cold "),
("khokla", " cough "),
("khaansi", " cough "),
("khansi", " cough "),
("bukhar", " fever "),
("taap", " fever "),
("tap", " fever "),
("dok duk", " headache "),
("doka duk", " headache "),
("ang duk", " body pain "),
("ghasa duk", " sore throat "),
("gala dard", " sore throat "),
("pet dard", " stomach pain "),
("ulti", " vomiting "),
("julaab", " diarrhea "),
("dast", " diarrhea "),
("naak band", " blocked nose "),
("naak vahate", " runny nose "),
("nausea", " nausea "),
("chakkar", " dizziness "),
("thakva", " fatigue "),
("thakwat", " fatigue "),
("male", " male "),
("female", " female ")
)
var i = 0
while (i < replacements.length) {
t = t.replace(
replacements(i)._1,
replacements(i)._2
)
i += 1
}
t
}
def doctorBuildUnderstanding(
raw: String
): String = {
val text = doctorCanonicalize(raw)
val language = doctorDetectLanguage(raw)
val cold = doctorContainsAny(
text,
Array("cold", "?????", "????", "runny nose", "blocked nose", "sneeze")
)
val cough = doctorContainsAny(
text,
Array("cough", "?????", "?????", "?????")
)
val fever = doctorContainsAny(
text,
Array("fever", "???", "?????", "????")
)
val headache = doctorContainsAny(
text,
Array("headache", "????????", "?? ????", "???? ???")
)
val throat = doctorContainsAny(
text,
Array("sore throat", "throat pain", "??? ???", "??? ????")
)
val stomach = doctorContainsAny(
text,
Array("stomach pain", "abdominal pain", "???????", "??? ????")
)
val vomiting = doctorContainsAny(
text,
Array("vomiting", "vomit", "????", "?????")
)
val diarrhea = doctorContainsAny(
text,
Array("diarrhea", "loose motion", "?????", "????")
)
val bodyPain = doctorContainsAny(
text,
Array("body pain", "muscle pain", "??? ???", "???? ???")
)
val dizziness = doctorContainsAny(
text,
Array("dizziness", "?????", "????")
)
val fatigue = doctorContainsAny(
text,
Array("fatigue", "tired", "????", "?????")
)
val rash = doctorContainsAny(
text,
Array("rash", "????", "????", "skin rash")
)
val earPain = doctorContainsAny(
text,
Array("ear pain", "??? ???", "??? ????")
)
val sb = new StringBuilder
sb.append("AI DOCTOR - TEXT UNDERSTANDING\n")
sb.append("================================\n\n")
sb.append("Detected writing style: ").append(language).append("\n")
sb.append("Normalized symptom text:\n")
sb.append(text.trim).append("\n\n")
sb.append("Recognized topics:\n")
sb.append(" Cold / nose : ").append(cold).append("\n")
sb.append(" Cough : ").append(cough).append("\n")
sb.append(" Fever : ").append(fever).append("\n")
sb.append(" Headache : ").append(headache).append("\n")
sb.append(" Sore throat : ").append(throat).append("\n")
sb.append(" Stomach pain : ").append(stomach).append("\n")
sb.append(" Vomiting : ").append(vomiting).append("\n")
sb.append(" Diarrhea : ").append(diarrhea).append("\n")
sb.append(" Body pain : ").append(bodyPain).append("\n")
sb.append(" Dizziness : ").append(dizziness).append("\n")
sb.append(" Fatigue : ").append(fatigue).append("\n")
sb.append(" Rash : ").append(rash).append("\n")
sb.append(" Ear pain : ").append(earPain).append("\n\n")
sb.append("Note: this is offline text matching, not unrestricted translation or a clinical diagnosis.\n")
sb.toString
}
def doctorMedicineInformation(
raw: String
): String = {
val text = doctorCanonicalize(raw)
val cold = doctorContainsAny(
text,
Array("cold", "?????", "runny nose", "blocked nose")
)
val cough = doctorContainsAny(
text,
Array("cough", "?????", "?????", "?????")
)
val fever = doctorContainsAny(
text,
Array("fever", "???", "?????")
)
val headache = doctorContainsAny(
text,
Array("headache", "????????", "?? ????")
)
val diarrhea = doctorContainsAny(
text,
Array("diarrhea", "loose motion", "?????", "????")
)
val vomiting = doctorContainsAny(
text,
Array("vomiting", "vomit", "????", "?????")
)
val sb = new StringBuilder
sb.append("MEDICINE INFORMATION MODE\n")
sb.append("===========================\n\n")
sb.append("This is GENERAL EDUCATIONAL information, not a personal prescription.\n")
sb.append("No individual drug choice, dose, duration, or combination is issued by this app.\n\n")
if (cold) {
sb.append("Cold / nasal symptoms:\n")
sb.append("- Non-drug supportive care may include fluids, rest, and saline nasal care.\n")
sb.append("- Ask a pharmacist/clinician whether any age-appropriate OTC option is suitable.\n\n")
}
if (cough) {
sb.append("Cough:\n")
sb.append("- The right treatment depends on whether the cough is dry, mucus-producing, allergic, infectious, or another cause.\n")
sb.append("- Some cough/cold products are not appropriate for every age or situation; check with a pharmacist/clinician.\n\n")
}
if (fever || headache) {
sb.append("Fever / headache:\n")
sb.append("- Rest, fluids, and monitoring are useful supportive steps.\n")
sb.append("- A clinician/pharmacist can advise whether an age-appropriate fever/pain medicine is appropriate.\n\n")
}
if (diarrhea || vomiting) {
sb.append("Vomiting / diarrhea:\n")
sb.append("- Hydration is important; an oral rehydration solution may be discussed with a pharmacist/clinician.\n")
sb.append("- Persistent vomiting, blood, severe weakness, or signs of dehydration need medical assessment.\n\n")
}
if (!cold && !cough && !fever && !headache && !diarrhea && !vomiting) {
sb.append("No specific medicine-information category was confidently matched.\n\n")
}
sb.append("Before taking any medicine, tell the pharmacist/clinician about:\n")
sb.append("age, allergies, other medicines, long-term conditions, pregnancy possibility when relevant, and the full symptom history.\n\n")
sb.append("This module intentionally avoids personalized prescriptions and dosages.\n")
sb.toString
}
def runDoctorCheckup(): Unit = {
val raw = doctorSymptomArea.getText.trim
if (!doctorUnlocked) {
doctorResultArea.setText(
"DOCTOR IS LOCKED.\n\nOpen AI Doctor using the access password first."
)
doctorMood = "SAD"
doctorStatusText = "ACCESS REQUIRED"
doctorAvatar.repaint()
return
}
if (raw.isEmpty) {
doctorResultArea.setText(
"Please write your symptoms first.\n\n" +
"Example: ??? ????? ???, nose blocked ??? ??? ???? cough ???."
)
doctorMood = "SAD"
doctorStatusText = "NEED MORE INFORMATION"
doctorAvatar.repaint()
return
}
val text = doctorCanonicalize(raw)
val temperature = doctorTempField.getText.trim
val duration = doctorDaysField.getText.trim
val location = doctorLocationField.getText.trim
val age = doctorAgeField.getText.trim
val sex = doctorSexField.getText.trim
val breathingRedFlag = doctorContainsAny(
text,
Array(
"????? ???? ??? ????",
"????? ??????? ?????",
"?? ?????",
"???? ???? ??? ??????",
"???? ???? ? ???",
"breathing difficulty",
"difficulty breathing",
"shortness of breath"
)
)
val chestRedFlag = doctorContainsAny(
text,
Array(
"????? ????? ?????",
"???? ?????",
"???? ??? ????",
"chest pain",
"severe chest pain"
)
)
val faintRedFlag = doctorContainsAny(
text,
Array(
"???????",
"????? ?????",
"?????",
"faint",
"fainted",
"unconscious"
)
)
val seriousBleeding = doctorContainsAny(
text,
Array(
"??? ????",
"??????????? ????? ????",
"???? ???",
"heavy bleeding",
"bleeding won't stop"
)
)
val severeAllergy = doctorContainsAny(
text,
Array(
"????? ?????",
"?? ?????",
"??? ?????",
"????? ???",
"???? ???",
"severe allergy",
"swollen tongue",
"face swelling"
)
)
val seizureRedFlag = doctorContainsAny(
text,
Array(
"seizure",
"????",
"fits",
"??????"
)
)
val blueLipsRedFlag = doctorContainsAny(
text,
Array(
"blue lips",
"?? ????",
"???? ????"
)
)
val emergencyFlag =
breathingRedFlag ||
chestRedFlag ||
faintRedFlag ||
seriousBleeding ||
severeAllergy ||
seizureRedFlag ||
blueLipsRedFlag
val cold = doctorContainsAny(
text,
Array("cold", "?????", "??? ?????", "runny nose", "blocked nose", "sneeze")
)
val cough = doctorContainsAny(
text,
Array("?????", "?????", "?????", "cough")
)
val fever = doctorContainsAny(
text,
Array("???", "?????", "????", "fever")
)
val headache = doctorContainsAny(
text,
Array("????????", "???? ???", "?? ????", "headache")
)
val throat = doctorContainsAny(
text,
Array("??? ???", "??? ????", "??? ????", "sore throat", "throat pain")
)
val stomach = doctorContainsAny(
text,
Array("???????", "??? ???", "??? ????", "stomach pain", "abdominal pain")
)
val vomiting = doctorContainsAny(
text,
Array("????", "?????", "vomit", "vomiting")
)
val diarrhea = doctorContainsAny(
text,
Array("?????", "????? ???????", "????", "diarrhea", "loose motion")
)
val bodyPain = doctorContainsAny(
text,
Array("??? ???", "???? ???", "body pain", "muscle pain")
)
val dizziness = doctorContainsAny(
text,
Array("?????", "????", "dizziness")
)
val rash = doctorContainsAny(
text,
Array("????", "????", "rash", "skin rash")
)
val earPain = doctorContainsAny(
text,
Array("??? ???", "??? ????", "ear pain")
)
val fatigue = doctorContainsAny(
text,
Array("????", "?????", "tired", "fatigue")
)
val sb = new StringBuilder
doctorMood = if (emergencyFlag) "SAD" else "HAPPY"
doctorStatusText = if (emergencyFlag) "URGENT HUMAN HELP" else "CHECKUP COMPLETE"
sb.append("AI DOCTOR - MULTILINGUAL CHECKUP\n")
sb.append("================================\n\n")
sb.append("Detected writing style: ")
sb.append(doctorDetectLanguage(raw)).append("\n")
sb.append("Selected input mode: ")
sb.append(doctorLanguageBox.getSelectedItem.toString).append("\n")
sb.append("Reported symptoms:\n")
sb.append(raw).append("\n\n")
if (age.nonEmpty)
sb.append("Age entered: ").append(age).append("\n")
if (sex.nonEmpty)
sb.append("Sex entered: ").append(sex).append("\n")
if (temperature.nonEmpty)
sb.append("Temperature entered: ").append(temperature).append("\n")
if (duration.nonEmpty)
sb.append("Duration entered: ").append(duration).append("\n")
if (location.nonEmpty)
sb.append("Pain/location entered: ").append(location).append("\n")
sb.append("\n")
sb.append("UNDERSTANDING\n")
sb.append("------------\n")
sb.append("Cold/nose: ").append(cold).append(" | Cough: ").append(cough).append(" | Fever: ").append(fever).append("\n")
sb.append("Headache: ").append(headache).append(" | Throat: ").append(throat).append(" | Body pain: ").append(bodyPain).append("\n")
sb.append("Stomach: ").append(stomach).append(" | Vomiting: ").append(vomiting).append(" | Diarrhea: ").append(diarrhea).append("\n")
sb.append("Dizziness: ").append(dizziness).append(" | Rash: ").append(rash).append(" | Ear pain: ").append(earPain).append(" | Fatigue: ").append(fatigue).append("\n\n")
if (emergencyFlag) {
sb.append("URGENT SAFETY ALERT\n")
sb.append("-------------------\n")
sb.append("The text contains one or more possible emergency warning signs.\n")
sb.append("Tell a parent/guardian or another trusted adult immediately and seek urgent medical care.\n")
sb.append("Do not wait for this program to diagnose the cause.\n\n")
} else {
sb.append("GENERAL SUPPORT\n")
sb.append("----------------\n")
sb.append("Rest, fluids, comfortable surroundings, and monitoring can be useful while arranging appropriate medical advice.\n")
if (cold || cough || throat)
sb.append("Respiratory/cold-like wording was recognized.\n")
if (fever)
sb.append("Fever-like wording was recognized; ask a trusted adult to help monitor temperature and overall condition.\n")
if (stomach || vomiting || diarrhea)
sb.append("Digestive symptoms were recognized; hydration is important and worsening/persistent symptoms need clinical advice.\n")
if (rash)
sb.append("A rash was mentioned; worsening, widespread, or rapidly developing rash needs medical assessment.\n")
sb.append("\n")
}
sb.append("MEDICINE INFORMATION\n")
sb.append("---------------------\n")
sb.append("Use the MEDICINE INFORMATION button for general educational guidance.\n")
sb.append("This app does not issue a personal prescription, drug selection, or dosage.\n\n")
sb.append("WHEN TO GET HELP\n")
sb.append("----------------\n")
sb.append("Get medical advice for severe, worsening, unusual, or persistent symptoms.\n")
sb.append("Urgent warning signs include severe breathing difficulty, severe chest pain, fainting, uncontrolled bleeding, seizures, blue lips, or major swelling affecting breathing.\n\n")
sb.append("IMPORTANT\n")
sb.append("---------\n")
sb.append("This is an educational software assistant using local text patterns. It is not a doctor or emergency service.\n")
doctorResultArea.setText(
sb.toString
)
doctorResultArea.setCaretPosition(
0
)
doctorAvatar.repaint()
recordActivity(
"Used AI Doctor multilingual checkup"
)
}
def doctorAccessGranted(): Unit = {
doctorUnlocked = true
doctorMood = "HAPPY"
doctorStatusText = "READY"
doctorStatus.setText(
"ACCESS GRANTED"
)
doctorStatus.setForeground(
SUCCESS
)
doctorResultArea.setText(
"AI DOCTOR READY\n\n" +
"Type Marathi, Hindi, English, transliteration, or mixed text.\n\n" +
"UNDERSTAND TEXT = show what the offline parser recognized.\n" +
"FULL CHECKUP = safety triage + symptom summary.\n" +
"MEDICINE INFORMATION = general educational guidance only; no personal prescription or dosage."
)
doctorAvatar.repaint()
doctorPasswordField.setText(
""
)
recordActivity(
"AI Doctor access granted"
)
}
doctorLoginButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val entered =
new String(
doctorPasswordField.getPassword
).trim
val passA =
"yadnesh@member2026"
if (entered == passA) {
doctorAccessGranted()
} else {
doctorUnlocked = false
doctorMood = "SAD"
doctorStatusText = "ACCESS DENIED"
doctorStatus.setText(
"ACCESS DENIED"
)
doctorStatus.setForeground(
DANGER
)
doctorResultArea.setText(
"ACCESS DENIED\n\nThe Doctor Access password is incorrect.\n\nThe AI Doctor remains locked."
)
doctorAvatar.repaint()
recordActivity(
"Failed AI Doctor access"
)
}
}
}
)
doctorPasswordField.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
doctorLoginButton.doClick()
}
}
)
doctorCheckupButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
runDoctorCheckup()
}
}
)
doctorUnderstandButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (!doctorUnlocked) {
doctorResultArea.setText(
"DOCTOR IS LOCKED.\n\nOpen AI Doctor first."
)
doctorMood = "SAD"
doctorStatusText = "ACCESS REQUIRED"
} else {
val raw = doctorSymptomArea.getText.trim
if (raw.isEmpty) {
doctorResultArea.setText(
"Please write symptoms first."
)
doctorMood = "SAD"
doctorStatusText = "NEED TEXT"
} else {
doctorResultArea.setText(
doctorBuildUnderstanding(raw)
)
doctorResultArea.setCaretPosition(0)
doctorMood = "HAPPY"
doctorStatusText = "TEXT UNDERSTOOD"
recordActivity("Used AI Doctor text understanding")
}
}
doctorAvatar.repaint()
}
}
)
doctorMedicineButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (!doctorUnlocked) {
doctorResultArea.setText(
"DOCTOR IS LOCKED.\n\nOpen AI Doctor first."
)
doctorMood = "SAD"
doctorStatusText = "ACCESS REQUIRED"
} else {
val raw = doctorSymptomArea.getText.trim
if (raw.isEmpty) {
doctorResultArea.setText(
"Write the symptom description first, then press MEDICINE INFORMATION."
)
doctorMood = "SAD"
doctorStatusText = "NEED TEXT"
} else {
doctorResultArea.setText(
doctorMedicineInformation(raw)
)
doctorResultArea.setCaretPosition(0)
doctorMood = "HAPPY"
doctorStatusText = "MEDICINE INFO"
recordActivity("Opened AI Doctor medicine information")
}
}
doctorAvatar.repaint()
}
}
)
doctorClearButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
doctorSymptomArea.setText("")
doctorTempField.setText("")
doctorDaysField.setText("")
doctorLocationField.setText("")
doctorAgeField.setText("")
doctorSexField.setText("")
doctorLanguageBox.setSelectedIndex(0)
doctorResultArea.setText(
"Fields cleared. Write symptoms and choose the action you need."
)
doctorMood = if (doctorUnlocked) "HAPPY" else "NEUTRAL"
doctorStatusText = if (doctorUnlocked) "READY" else "LOCKED"
doctorAvatar.repaint()
}
}
)
doctorSafetyButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
doctorMood = "HAPPY"
doctorStatusText = "SAFETY GUIDE"
doctorResultArea.setText(
"SAFETY GUIDE\n\n" +
"1. Severe breathing difficulty, severe chest pain, fainting, uncontrolled bleeding, seizures, blue lips, or major swelling affecting breathing requires urgent human medical help.\n\n" +
"2. Severe, worsening, unusual, or persistent symptoms need medical advice.\n\n" +
"3. Do not use this program to choose, combine, stop, or dose medicines. Ask a qualified clinician/pharmacist and a trusted adult.\n\n" +
"4. Keep the original medicine packaging/label available when asking a pharmacist or clinician about a product."
)
doctorAvatar.repaint()
}
}
)
// --------------------------------------------------------
// LIGHTWEIGHT DOCTOR ANIMATION
// --------------------------------------------------------
doctorTimer =
new javax.swing.Timer(
70,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
doctorPulse =
(doctorPulse + 1) % 120
doctorAvatar.repaint()
}
}
)
doctorTimer.setCoalesce(
true
)
doctorTimer.start()
// ========================================================
// AIR TO WATER - REAL PHYSICS ENGINE + 3D PLANT MODEL
// ========================================================
val airWaterPanel =
new JPanel(
new BorderLayout(10, 10)
)
airWaterPanel.setBackground(PANEL_BG)
airWaterPanel.setBorder(
new EmptyBorder(10, 10, 10, 10)
)
// --------------------------------------------------------
// REAL-WORLD INPUTS
// --------------------------------------------------------
var airWaterRunning = false
var airWaterHumidity = 60
var airWaterTemperature = 27
var airWaterCoilTemperature = 8
var airWaterAirflow = 900
var airWaterPressureKPa = 101
var airWaterHXEfficiency = 78
var airWaterCollectionEfficiency = 88
var airWaterCOP = 3.0
var airWaterFanPressurePa = 180
var airWaterFanEfficiency = 62
var airWaterCO2ppm = 420
var airWaterCO2Capture = false
var airWaterCO2Efficiency = 55
var airWaterRuntimeHours = 1.0
var airWaterTank = 0.0
var airWaterProduced = 0.0
var airWaterCO2Captured = 0.0
var airWaterFrame = 0
var airWaterTotalEnergy = 0.0
val airWaterTitle =
makeLabel(
"AIR TO WATER - ENGINEERING ESTIMATE + 3D PLANT",
23,
true
)
val airWaterStatus =
makeLabel(
"READY - ENGINEERING MODEL",
13,
true
)
airWaterStatus.setForeground(SUCCESS)
val airWaterHeader =
new JPanel(
new BorderLayout(8, 8)
)
airWaterHeader.setBackground(PANEL_BG)
airWaterHeader.add(
airWaterTitle,
BorderLayout.WEST
)
airWaterHeader.add(
airWaterStatus,
BorderLayout.EAST
)
airWaterPanel.add(
airWaterHeader,
BorderLayout.NORTH
)
// --------------------------------------------------------
// PHYSICS HELPERS
// --------------------------------------------------------
def airWaterClamp(
value: Double,
low: Double,
high: Double
): Double = {
Math.max(
low,
Math.min(high, value)
)
}
def airWaterSaturationVaporPressurePa(
tempC: Double
): Double = {
val t =
airWaterClamp(tempC, -45.0, 70.0)
val esKPa =
0.61094 *
Math.exp(
(17.625 * t) / (t + 243.04)
)
esKPa * 1000.0
}
def airWaterHumidityRatio(
tempC: Double,
rhPercent: Double,
pressurePa: Double
): Double = {
val rh =
airWaterClamp(
rhPercent,
0.1,
100.0
)
val pws =
airWaterSaturationVaporPressurePa(
tempC
)
val pv =
airWaterClamp(
pws * rh / 100.0,
0.0,
pressurePa * 0.98
)
0.62198 * pv /
Math.max(
500.0,
pressurePa - pv
)
}
def airWaterDewPointC(
tempC: Double,
rhPercent: Double
): Double = {
val rh =
airWaterClamp(
rhPercent,
1.0,
100.0
)
val a = 17.625
val b = 243.04
val gamma =
Math.log(rh / 100.0) +
(a * tempC) /
(b + tempC)
(b * gamma) /
Math.max(
0.0001,
a - gamma
)
}
def airWaterMoistAirSpecificVolume(
tempC: Double,
humidityRatio: Double,
pressurePa: Double
): Double = {
val rd = 287.055
val tk =
Math.max(
200.0,
tempC + 273.15
)
rd * tk *
(1.0 + 1.6078 * humidityRatio) /
Math.max(
500.0,
pressurePa
)
}
def airWaterEnthalpyKJPerKgDryAir(
tempC: Double,
humidityRatio: Double
): Double = {
1.006 * tempC +
humidityRatio *
(2501.0 + 1.86 * tempC)
}
def airWaterCoilState(): (Double, Double, Double, Double, Double) = {
val pressure = airWaterPressureKPa.toDouble * 1000.0
val tin = airWaterTemperature.toDouble
val rh = airWaterHumidity.toDouble
val coil = airWaterCoilTemperature.toDouble
val eff = airWaterHXEfficiency.toDouble / 100.0
val win =
airWaterHumidityRatio(
tin,
rh,
pressure
)
val dew =
airWaterDewPointC(
tin,
rh
)
val shouldCondense =
coil < dew
val wsCoil =
airWaterHumidityRatio(
coil,
100.0,
pressure
)
val wout =
if (shouldCondense)
wsCoil +
(1.0 - eff) *
Math.max(
0.0,
win - wsCoil
)
else
win
val tout =
if (shouldCondense)
coil +
(1.0 - eff) *
Math.max(
0.0,
tin - coil
)
else
tin
val specificVolume =
airWaterMoistAirSpecificVolume(
tin,
win,
pressure
)
val dryAirKgPerM3 =
1.0 /
Math.max(
0.05,
specificVolume
)
val dryAirKgPerH =
airWaterAirflow.toDouble *
dryAirKgPerM3
val waterKgPerH =
dryAirKgPerH *
Math.max(
0.0,
win - wout
)
val hin =
airWaterEnthalpyKJPerKgDryAir(
tin,
win
)
val hout =
airWaterEnthalpyKJPerKgDryAir(
tout,
wout
)
val coolingKW =
Math.max(
0.0,
dryAirKgPerH *
Math.max(0.0, hin - hout) /
3600.0
)
val airflowM3PerS =
airWaterAirflow.toDouble /
3600.0
val fanKW =
airflowM3PerS *
airWaterFanPressurePa.toDouble /
Math.max(
0.20,
airWaterFanEfficiency.toDouble /
100.0
) /
1000.0
val compressorKW =
coolingKW /
Math.max(
1.0,
airWaterCOP
)
val totalKW =
compressorKW + fanKW
val litresPerH =
waterKgPerH *
airWaterCollectionEfficiency.toDouble /
100.0
(
dew,
win,
wout,
litresPerH,
totalKW
)
}
def airWaterWaterLitresPerH(): Double = {
airWaterCoilState()._4
}
def airWaterTheoreticalMoistureAvailableKgPerH(): Double = {
val pressure =
airWaterPressureKPa.toDouble * 1000.0
val w =
airWaterHumidityRatio(
airWaterTemperature.toDouble,
airWaterHumidity.toDouble,
pressure
)
val v =
airWaterMoistAirSpecificVolume(
airWaterTemperature.toDouble,
w,
pressure
)
val dryAirKgPerM3 =
1.0 /
Math.max(0.05, v)
airWaterAirflow.toDouble *
dryAirKgPerM3 *
w
}
def airWaterPowerKW(): Double = {
airWaterCoilState()._5
}
def airWaterCO2KgPerH(): Double = {
val pressure = 101325.0
val tk =
airWaterTemperature.toDouble +
273.15
val r = 8.314462618
val molarMassKg =
44.01 / 1000.0
val moleFraction =
airWaterCO2ppm.toDouble /
1000000.0
airWaterAirflow.toDouble *
moleFraction *
pressure /
(r * Math.max(200.0, tk)) *
molarMassKg
}
def airWaterCO2CaptureRateKgPerH(): Double = {
if (airWaterCO2Capture)
airWaterCO2KgPerH() *
airWaterCO2Efficiency.toDouble /
100.0
else
0.0
}
// --------------------------------------------------------
// 3D PROCEDURAL PLANT VIEW
// --------------------------------------------------------
val airWaterCanvas =
new JPanel {
setBackground(
new Color(8, 15, 23)
)
def boxFront(
x: Int,
y: Int,
w: Int,
h: Int
): Polygon = {
val p = new Polygon()
p.addPoint(x, y)
p.addPoint(x + w, y)
p.addPoint(x + w, y + h)
p.addPoint(x, y + h)
p
}
def boxTop(
x: Int,
y: Int,
w: Int,
depth: Int
): Polygon = {
val p = new Polygon()
p.addPoint(x, y)
p.addPoint(x + depth, y - depth)
p.addPoint(x + w + depth, y - depth)
p.addPoint(x + w, y)
p
}
def boxSide(
x: Int,
y: Int,
w: Int,
h: Int,
depth: Int
): Polygon = {
val p = new Polygon()
p.addPoint(x + w, y)
p.addPoint(x + w + depth, y - depth)
p.addPoint(x + w + depth, y + h - depth)
p.addPoint(x + w, y + h)
p
}
def drawPipe(
g: Graphics2D,
x1: Int,
y1: Int,
x2: Int,
y2: Int,
c: Color,
width: Float
): Unit = {
val oldStroke = g.getStroke
g.setColor(c)
g.setStroke(
new BasicStroke(
width,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND
)
)
g.drawLine(
x1,
y1,
x2,
y2
)
g.setStroke(oldStroke)
}
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(graphics)
val g =
graphics.asInstanceOf[Graphics2D]
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
val cw = getWidth
val ch = getHeight
// Sky gradient
var sy = 0
while (sy < ch) {
val t =
sy.toDouble /
Math.max(1, ch).toDouble
val rr =
(7.0 + 26.0 * t).toInt
val gg =
(18.0 + 50.0 * t).toInt
val bb =
(38.0 + 66.0 * t).toInt
g.setColor(
new Color(
rr,
gg,
bb
)
)
g.fillRect(0, sy, cw, 1)
sy += 1
}
// Sun glow
val pulse =
8 +
Math.abs(
(airWaterFrame % 60) - 30
)
g.setColor(
new Color(
255,
220,
110,
25
)
)
g.fillOval(
34 - pulse,
24 - pulse,
86 + pulse * 2,
86 + pulse * 2
)
g.setColor(
new Color(
255,
219,
112,
190
)
)
g.fillOval(
47,
37,
60,
60
)
// Atmospheric particles
var ap = 0
while (ap < 34) {
val phase =
(airWaterFrame * 4 + ap * 37) %
Math.max(100, cw - 50)
val x =
12 +
(phase + ap * 53) %
Math.max(80, cw - 22)
val y =
88 +
((ap * 49 + airWaterFrame / 3) %
Math.max(100, ch - 190))
val s =
3 + ap % 5
g.setColor(
new Color(
150,
225,
255,
45 + ap * 4
)
)
g.fillOval(
x,
y,
s,
s
)
ap += 1
}
// 3D floor
val floorTop =
Math.max(
300,
ch - 225
)
val floor = new Polygon()
floor.addPoint(0, floorTop)
floor.addPoint(cw, floorTop)
floor.addPoint(cw, ch)
floor.addPoint(0, ch)
g.setColor(
new Color(16, 34, 40)
)
g.fillPolygon(floor)
g.setColor(
new Color(84, 126, 135, 70)
)
var fx = 0
while (fx <= cw) {
val q = new Polygon()
q.addPoint(
cw / 2,
floorTop
)
q.addPoint(
fx,
ch
)
q.addPoint(
fx + 4,
ch
)
q.addPoint(
cw / 2 + 4,
floorTop
)
g.fillPolygon(q)
fx += 80
}
var fy = floorTop + 25
while (fy < ch) {
g.drawLine(
0,
fy,
cw,
fy
)
fy += 32
}
// ----------------------------------------------------
// 1. AIR INTAKE
// ----------------------------------------------------
val ax = 22
val ay = 205
val aw = 145
val ah = 155
val ad = 24
g.setColor(
new Color(55, 75, 95)
)
g.fillPolygon(
boxFront(
ax,
ay,
aw,
ah
)
)
g.setColor(
new Color(78, 101, 126)
)
g.fillPolygon(
boxTop(
ax,
ay,
aw,
ad
)
)
g.setColor(
new Color(37, 53, 67)
)
g.fillPolygon(
boxSide(
ax,
ay,
aw,
ah,
ad
)
)
g.setColor(
new Color(12, 18, 23)
)
var grillY = ay + 18
while (grillY < ay + ah - 10) {
g.fillRoundRect(
ax + 20,
grillY,
aw - 40,
5,
4,
4
)
grillY += 16
}
g.setColor(TEXT)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
11
)
)
g.drawString(
"AIR INTAKE",
ax + 32,
ay + ah + 20
)
// Air arrows
g.setColor(
new Color(150, 225, 255, 180)
)
var ai = 0
while (ai < 5) {
val xx =
ax - 48 +
((airWaterFrame * 3 + ai * 22) % 55)
val yy =
ay + 30 + ai * 20
g.drawLine(
xx,
yy,
xx + 20,
yy
)
g.fillPolygon(
new Polygon(
Array(
xx + 20,
xx + 14,
xx + 14
),
Array(
yy,
yy - 4,
yy + 4
),
3
)
)
ai += 1
}
// ----------------------------------------------------
// 2. PRE-FILTER
// ----------------------------------------------------
val px = 198
val py = 213
val pw = 140
val ph = 118
val pd = 19
g.setColor(
new Color(68, 100, 76)
)
g.fillPolygon(
boxFront(
px,
py,
pw,
ph
)
)
g.setColor(
new Color(90, 130, 96)
)
g.fillPolygon(
boxTop(
px,
py,
pw,
pd
)
)
g.setColor(
new Color(46, 67, 51)
)
g.fillPolygon(
boxSide(
px,
py,
pw,
ph,
pd
)
)
g.setColor(TEXT)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
11
)
)
g.drawString(
"PRE-FILTER",
px + 33,
py + 45
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
9
)
)
g.drawString(
"DUST / PARTICLES",
px + 14,
py + 69
)
g.drawString(
"NOT GAS SEPARATION",
px + 10,
py + 90
)
// ----------------------------------------------------
// 3. HEAT EXCHANGER / CONDENSER
// ----------------------------------------------------
val cx = 372
val cy = 150
val cw0 = 225
val ch0 = 265
val cd = 28
g.setColor(
new Color(50, 69, 94)
)
g.fillPolygon(
boxFront(
cx,
cy,
cw0,
ch0
)
)
g.setColor(
new Color(74, 96, 124)
)
g.fillPolygon(
boxTop(
cx,
cy,
cw0,
cd
)
)
g.setColor(
new Color(35, 47, 66)
)
g.fillPolygon(
boxSide(
cx,
cy,
cw0,
ch0,
cd
)
)
g.setColor(
new Color(105, 208, 255, 170)
)
var coil = cy + 42
while (coil < cy + ch0 - 25) {
g.drawRoundRect(
cx + 24,
coil,
cw0 - 56,
18,
8,
8
)
coil += 28
}
val compPulse =
10 +
Math.abs(
(airWaterFrame % 30) - 15
)
g.setColor(
new Color(80, 185, 255, 24)
)
g.fillOval(
cx + 62 - compPulse,
cy + 92 - compPulse,
92 + compPulse * 2,
92 + compPulse * 2
)
g.setColor(TEXT)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
14
)
)
g.drawString(
"CONDENSER",
cx + 78,
cy + 27
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
10
)
)
g.drawString(
"COIL: " +
String.format(
"%.1f",
airWaterCoilTemperature.toDouble
) +
" C",
cx + 72,
cy + ch0 - 13
)
// Condensation droplets
val dropletOn =
airWaterWaterLitresPerH() > 0.001
g.setColor(
new Color(105, 220, 255, 205)
)
var dp = 0
while (dp < 24) {
val phase =
(airWaterFrame * 5 + dp * 23) % 190
val dx =
cx + 34 +
(dp * 7) % 155
val dy =
cy + 108 + phase
if (dropletOn && dy < cy + ch0 - 8) {
g.fillOval(
dx,
dy,
6,
10
)
}
dp += 1
}
// ----------------------------------------------------
// 4. COLLECTION TANK
// ----------------------------------------------------
val tx = 670
val ty = 348
val tw = 205
val th = 188
val td = 24
g.setColor(
new Color(57, 84, 94)
)
g.fillPolygon(
boxFront(
tx,
ty,
tw,
th
)
)
g.setColor(
new Color(80, 113, 125)
)
g.fillPolygon(
boxTop(
tx,
ty,
tw,
td
)
)
g.setColor(
new Color(39, 57, 64)
)
g.fillPolygon(
boxSide(
tx,
ty,
tw,
th,
td
)
)
val fillRatio =
airWaterClamp(
airWaterTank / 100.0,
0.0,
1.0
)
val fillH =
(th.toDouble * fillRatio).toInt
g.setColor(
new Color(80, 190, 235, 170)
)
g.fillRect(
tx + 11,
ty + th - 11 - fillH,
tw - 22,
fillH
)
g.setColor(
new Color(190, 235, 255, 170)
)
var bubble = 0
while (bubble < 12) {
val bx =
tx + 20 +
((bubble * 31 + airWaterFrame * 2) %
Math.max(25, tw - 40))
val by =
ty + th - 20 -
((bubble * 17 + airWaterFrame * 3) %
Math.max(30, fillH + 10))
if (by > ty + th - fillH - 5) {
g.fillOval(
bx,
by,
5,
5
)
}
bubble += 1
}
g.setColor(TEXT)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
13
)
)
g.drawString(
"RAW CONDENSATE TANK",
tx + 33,
ty + 28
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
10
)
)
g.drawString(
"Estimated level: " +
String.format(
"%.1f L",
airWaterTank
),
tx + 38,
ty + th + 22
)
// ----------------------------------------------------
// 5. TREATMENT TRAIN
// ----------------------------------------------------
val ux = 915
val uy = 215
val uw = 235
val uh = 220
g.setColor(
new Color(57, 81, 66)
)
g.fillRoundRect(
ux,
uy,
uw,
uh,
20,
20
)
g.setColor(
new Color(92, 139, 101)
)
g.fillRoundRect(
ux + 12,
uy + 12,
uw - 24,
42,
12,
12
)
g.setColor(TEXT)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
13
)
)
g.drawString(
"POST-TREATMENT TRAIN",
ux + 35,
uy + 38
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
11
)
)
g.drawString(
"1 Particle / carbon filtration",
ux + 23,
uy + 82
)
g.drawString(
"2 Disinfection stage",
ux + 23,
uy + 110
)
g.drawString(
"3 Mineral / chemistry check",
ux + 23,
uy + 138
)
g.drawString(
"4 Water-quality verification",
ux + 23,
uy + 166
)
g.drawString(
"5 Sealed storage / output",
ux + 23,
uy + 194
)
// ----------------------------------------------------
// 6. CO2 SIDE PATH
// ----------------------------------------------------
val ccx = 1090
val ccy = 475
g.setColor(
if (airWaterCO2Capture)
new Color(107, 79, 60)
else
new Color(55, 63, 70)
)
g.fillRoundRect(
ccx,
ccy,
135,
88,
12,
12
)
g.setColor(
if (airWaterCO2Capture)
new Color(239, 165, 78)
else
new Color(136, 143, 152)
)
g.fillOval(
ccx + 49,
ccy + 14,
36,
36
)
g.setColor(TEXT)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
11
)
)
g.drawString(
"CO2 SIDE PATH",
ccx + 20,
ccy + 65
)
if (airWaterCO2Capture) {
var cp = 0
while (cp < 10) {
val qq =
(airWaterFrame * 3 + cp * 21) % 90
g.setColor(
new Color(240, 185, 110, 160)
)
g.fillOval(
ccx - qq,
ccy + 18 + cp * 5,
5,
5
)
cp += 1
}
}
// Pipes and animated flow dots
drawPipe(
g,
ax + aw + 8,
ay + 78,
px,
py + 58,
new Color(80, 170, 230),
7f
)
drawPipe(
g,
px + pw + 18,
py + 61,
cx,
cy + 121,
new Color(90, 180, 235),
7f
)
drawPipe(
g,
cx + cw0 / 2,
cy + ch0 + 3,
tx + 28,
ty,
new Color(95, 205, 240),
8f
)
drawPipe(
g,
tx + tw,
ty + 88,
ux,
uy + 89,
new Color(95, 210, 225),
8f
)
var fd = 0
while (fd < 16) {
val progress =
(airWaterFrame * 4 + fd * 31) % 100
val xx =
ax + aw + 8 +
(px - (ax + aw + 8)) * progress / 100
val yy =
ay + 78 +
(py + 58 - (ay + 78)) * progress / 100
g.setColor(
new Color(170, 230, 255, 170)
)
g.fillOval(
xx,
yy,
4,
4
)
fd += 1
}
// ----------------------------------------------------
// LIVE DATA HUD
// ----------------------------------------------------
val values =
airWaterCoilState()
val dew = values._1
val litresH = values._4
val totalKW = values._5
val waterPerKWh =
if (totalKW > 0.0001)
litresH / totalKW
else
0.0
g.setColor(
new Color(8, 14, 20, 230)
)
g.fillRoundRect(
18,
18,
560,
110,
16,
16
)
g.setColor(new Color(255, 215, 0))
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
14
)
)
g.drawString(
"REAL-TIME ATMOSPHERIC WATER CALCULATION",
34,
42
)
g.setColor(TEXT)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
12
)
)
g.drawString(
"Ambient: " +
airWaterTemperature +
" C / " +
airWaterHumidity +
"% RH",
34,
68
)
g.drawString(
"Dew point: " +
String.format(
"%.2f",
dew
) +
" C",
220,
68
)
g.drawString(
"Coil: " +
String.format(
"%.1f",
airWaterCoilTemperature.toDouble
) +
" C",
380,
68
)
g.drawString(
"Estimated water: " +
String.format(
"%.2f",
litresH
) +
" L/h",
34,
94
)
g.drawString(
"Electrical load: " +
String.format(
"%.2f",
totalKW
) +
" kW",
220,
94
)
g.drawString(
"Water / kWh: " +
String.format(
"%.2f",
waterPerKWh
),
390,
94
)
// 3D labels
g.setColor(MUTED)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
10
)
)
g.drawString(
"AIR + H2O VAPOR",
ax + 20,
ay - 12
)
g.drawString(
"CLEANER AIR",
px + 24,
py - 12
)
g.drawString(
"DEW-POINT CONTROL",
cx + 45,
cy - 14
)
g.drawString(
"CONDENSED LIQUID",
tx + 36,
ty - 13
)
g.drawString(
"TREATED OUTPUT",
ux + 59,
uy - 13
)
// ====================================================
// DETAILED PROCESS-CIRCUIT LABELS / CALLOUTS
// ====================================================
g.setColor(
new Color(235, 242, 248, 215)
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
12
)
)
g.drawString(
"LABELED AIR-TO-WATER PROCESS CIRCUIT",
1285,
92
)
def callout(
g2: Graphics2D,
x1: Int,
y1: Int,
x2: Int,
y2: Int,
titleText: String,
bodyText: String
): Unit = {
g2.setColor(
new Color(160, 215, 240, 155)
)
g2.setStroke(
new BasicStroke(
1.2f,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND
)
)
g2.drawLine(
x1,
y1,
x2,
y2
)
g2.fillOval(
x1 - 3,
y1 - 3,
6,
6
)
g2.setColor(
new Color(8, 14, 20, 232)
)
g2.fillRoundRect(
x2,
y2 - 24,
236,
58,
12,
12
)
g2.setColor(
new Color(125, 185, 220)
)
g2.drawRoundRect(
x2,
y2 - 24,
236,
58,
12,
12
)
g2.setFont(
new Font(
"SansSerif",
Font.BOLD,
11
)
)
g2.setColor(TEXT)
g2.drawString(
titleText,
x2 + 12,
y2 - 4
)
g2.setFont(
new Font(
"SansSerif",
Font.PLAIN,
9
)
)
g2.setColor(MUTED)
g2.drawString(
bodyText,
x2 + 12,
y2 + 16
)
}
callout(
g,
ax + 70,
ay + 42,
1285,
142,
"S1 / AIR INTAKE",
"T1 + RH1 + P1 : incoming air condition"
)
callout(
g,
px + 70,
py + 44,
1285,
214,
"F1 / PRE-FILTER",
"Particle protection before cooling stage"
)
callout(
g,
cx + 112,
cy + 114,
1285,
286,
"HX-01 / CONDENSER",
"Cooling surface + dew-point crossing"
)
callout(
g,
cx + 112,
cy + ch0 - 12,
1285,
358,
"DRAIN-01 / WATER SEPARATOR",
"Condensate collection from cooled air"
)
callout(
g,
tx + tw / 2,
ty + th / 2,
1285,
430,
"TK-01 / RAW WATER TANK",
"Level = estimated accumulated condensate"
)
callout(
g,
ux + uw / 2,
uy + uh / 2,
1285,
510,
"WQ-01 / TREATMENT TRAIN",
"Filtration + disinfection + quality gate"
)
// Engineering mini-panels on the far right.
g.setColor(
new Color(7, 12, 18, 238)
)
g.fillRoundRect(1530, 130, 270, 470, 18, 18)
g.setColor(
new Color(120, 170, 205)
)
g.drawRoundRect(1530, 130, 270, 470, 18, 18)
g.setColor(TEXT)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
13
)
)
g.drawString(
"LIVE ENGINEERING CHECKS",
1552,
158
)
val dryFlow = airWaterAirflow
val inletW = values._2
val outletW = values._3
val grossWater = Math.max(0.0, (inletW - outletW))
val dewGap =
airWaterTemperature.toDouble - dew
val coilMargin =
dew - airWaterCoilTemperature.toDouble
val energyPerL =
if (litresH > 0.0001)
totalKW / litresH
else
0.0
val checks = Array(
"AIRFLOW " + dryFlow + " m3/h",
"DEW-POINT GAP " + String.format("%.1f C", dewGap),
"COIL MARGIN " + String.format("%.1f C", coilMargin),
"H2O RATIO DROP " + String.format("%.6f", grossWater),
"CONDENSATE " + String.format("%.3f L/h", litresH),
"POWER " + String.format("%.3f kW", totalKW),
"ENERGY / L " + String.format("%.3f kWh/L", energyPerL),
"CO2 MODE " + (if (airWaterCO2Capture) "CAPTURE" else "SEPARATE"),
"MODEL STATE " + (if (airWaterRunning) "RUNNING" else "HOLD")
)
var ci = 0
while (ci < checks.length) {
val yy = 190 + ci * 34
val ok =
if (ci == 2) coilMargin > 0.0
else if (ci == 4) litresH > 0.01
else true
g.setColor(
if (ok) SUCCESS else WARNING
)
g.fillOval(1553, yy - 9, 9, 9)
g.setColor(TEXT)
g.setFont(
new Font(
"Monospaced",
Font.PLAIN,
10
)
)
g.drawString(
checks(ci),
1570,
yy
)
ci += 1
}
g.setColor(
new Color(120, 155, 175, 150)
)
g.drawLine(1550, 500, 1780, 500)
g.setColor(MUTED)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
9
)
)
g.drawString(
"The labels describe the software model only.",
1552,
526
)
g.drawString(
"They are not a construction drawing.",
1552,
544
)
// Numbered process chain.
val stepXs = Array(92, 268, 485, 770, 1035, 1200)
val stepYs = Array(392, 392, 392, 547, 325, 442)
var si = 0
while (si < stepXs.length) {
g.setColor(
new Color(255, 215, 90, 220)
)
g.fillOval(
stepXs(si) - 10,
stepYs(si) - 10,
20,
20
)
g.setColor(
new Color(25, 28, 34)
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
10
)
)
g.drawString(
(si + 1).toString,
stepXs(si) - 3,
stepYs(si) + 4
)
si += 1
}
}
}
airWaterCanvas.setPreferredSize(
new Dimension(
1850,
700
)
)
// Horizontal mouse-wheel navigation for the wide engineering circuit.
// The canvas can be explored left/right without shrinking the 3D view.
val airWaterCanvasScroll =
new JScrollPane(
airWaterCanvas
)
airWaterCanvasScroll.setHorizontalScrollBarPolicy(
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
)
airWaterCanvasScroll.setVerticalScrollBarPolicy(
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED
)
airWaterCanvasScroll.getHorizontalScrollBar.setUnitIncrement(45)
airWaterCanvasScroll.getHorizontalScrollBar.setBlockIncrement(180)
airWaterCanvasScroll.getVerticalScrollBar.setUnitIncrement(35)
airWaterCanvasScroll.getVerticalScrollBar.setBlockIncrement(160)
airWaterCanvas.addMouseWheelListener(
new MouseWheelListener {
override def mouseWheelMoved(
e: MouseWheelEvent
): Unit = {
val bar =
airWaterCanvasScroll.getHorizontalScrollBar
val delta =
e.getWheelRotation *
bar.getUnitIncrement
val maxValue =
Math.max(
0,
bar.getMaximum - bar.getVisibleAmount
)
val next =
Math.max(
0,
Math.min(
maxValue,
bar.getValue + delta
)
)
bar.setValue(next)
e.consume()
}
}
)
// --------------------------------------------------------
// CONTROL PANEL
// --------------------------------------------------------
val airWaterControlPanel =
new JPanel(
new GridLayout(
15,
2,
5,
5
)
)
airWaterControlPanel.setBackground(PANEL_BG)
val airWaterHumiditySlider =
new JSlider(
10,
95,
airWaterHumidity
)
val airWaterTempSlider =
new JSlider(
5,
45,
airWaterTemperature
)
val airWaterCoilSlider =
new JSlider(
0,
25,
airWaterCoilTemperature
)
val airWaterAirflowSlider =
new JSlider(
100,
3000,
airWaterAirflow
)
val airWaterPressureSlider =
new JSlider(
80,
105,
airWaterPressureKPa
)
val airWaterHXSlider =
new JSlider(
45,
95,
airWaterHXEfficiency
)
val airWaterCollectionSlider =
new JSlider(
60,
98,
airWaterCollectionEfficiency
)
val airWaterCOPSlider =
new JSlider(
15,
50,
30
)
val airWaterFanPressureSlider =
new JSlider(
50,
500,
airWaterFanPressurePa
)
val airWaterFanEfficiencySlider =
new JSlider(
40,
90,
airWaterFanEfficiency
)
val airWaterCO2Slider =
new JSlider(
300,
2000,
airWaterCO2ppm
)
val airWaterCO2EffSlider =
new JSlider(
10,
95,
airWaterCO2Efficiency
)
val airWaterStartButton =
makeButton(
"START / PAUSE REAL MODEL"
)
val airWaterResetButton =
makeButton(
"RESET"
)
val airWaterCO2Button =
makeButton(
"CO2 CAPTURE: OFF"
)
val airWaterExplainButton =
makeButton(
"REAL PHYSICS REPORT"
)
val airWaterDiagnosticsButton =
makeButton(
"SYSTEM DIAGNOSTICS"
)
val airWaterKundaliButton =
makeButton(
"OPEN KUNDALI PURAN"
)
airWaterControlPanel.add(
makeLabel(
"Humidity (%)",
11,
false
)
)
airWaterControlPanel.add(
airWaterHumiditySlider
)
airWaterControlPanel.add(
makeLabel(
"Ambient temp (C)",
11,
false
)
)
airWaterControlPanel.add(
airWaterTempSlider
)
airWaterControlPanel.add(
makeLabel(
"Coil temp (C)",
11,
false
)
)
airWaterControlPanel.add(
airWaterCoilSlider
)
airWaterControlPanel.add(
makeLabel(
"Airflow (m3/h)",
11,
false
)
)
airWaterControlPanel.add(
airWaterAirflowSlider
)
airWaterControlPanel.add(
makeLabel(
"Pressure (kPa)",
11,
false
)
)
airWaterControlPanel.add(
airWaterPressureSlider
)
airWaterControlPanel.add(
makeLabel(
"HX effectiveness (%)",
11,
false
)
)
airWaterControlPanel.add(
airWaterHXSlider
)
airWaterControlPanel.add(
makeLabel(
"Water collection (%)",
11,
false
)
)
airWaterControlPanel.add(
airWaterCollectionSlider
)
airWaterControlPanel.add(
makeLabel(
"COP x10",
11,
false
)
)
airWaterControlPanel.add(
airWaterCOPSlider
)
airWaterControlPanel.add(
makeLabel(
"Fan pressure drop (Pa)",
11,
false
)
)
airWaterControlPanel.add(
airWaterFanPressureSlider
)
airWaterControlPanel.add(
makeLabel(
"Fan efficiency (%)",
11,
false
)
)
airWaterControlPanel.add(
airWaterFanEfficiencySlider
)
airWaterControlPanel.add(
makeLabel(
"CO2 ppm",
11,
false
)
)
airWaterControlPanel.add(
airWaterCO2Slider
)
airWaterControlPanel.add(
makeLabel(
"CO2 capture (%)",
11,
false
)
)
airWaterControlPanel.add(
airWaterCO2EffSlider
)
airWaterControlPanel.add(
airWaterStartButton
)
airWaterControlPanel.add(
airWaterResetButton
)
airWaterControlPanel.add(
airWaterCO2Button
)
airWaterControlPanel.add(
airWaterExplainButton
)
airWaterControlPanel.add(
airWaterDiagnosticsButton
)
airWaterControlPanel.add(
airWaterKundaliButton
)
val airWaterReadout =
makeArea()
airWaterReadout.setEditable(false)
val airWaterTopSplit =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
airWaterCanvasScroll,
airWaterControlPanel
)
airWaterTopSplit.setDividerLocation(875)
airWaterTopSplit.setResizeWeight(0.76)
val airWaterBottomSplit =
new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
airWaterTopSplit,
makeScroll(
airWaterReadout
)
)
airWaterBottomSplit.setDividerLocation(575)
airWaterBottomSplit.setResizeWeight(0.80)
airWaterPanel.add(
airWaterBottomSplit,
BorderLayout.CENTER
)
// --------------------------------------------------------
// REAL CALCULATION REPORT
// --------------------------------------------------------
def refreshAirWaterReadout(): Unit = {
val values =
airWaterCoilState()
val dew = values._1
val win = values._2
val wout = values._3
val litresH = values._4
val powerKW = values._5
val lpkwh =
if (powerKW > 0.0001)
litresH / powerKW
else
0.0
val co2In =
airWaterCO2KgPerH()
val theoretical =
airWaterTheoreticalMoistureAvailableKgPerH()
val co2Captured =
airWaterCO2CaptureRateKgPerH()
val dryAirFlow =
airWaterAirflow
val daily =
litresH *
airWaterRuntimeHours
airWaterReadout.setText(
"AIR TO WATER - REAL PHYSICS REPORT\n" +
"=====================================\n\n" +
"WHAT THE MODEL CALCULATES\n" +
"-------------------------\n" +
"This version no longer uses an arbitrary water-production index.\n" +
"It estimates condensate from atmospheric water vapour using\n" +
"air temperature, relative humidity, pressure, airflow, coil\n" +
"temperature and heat-exchanger effectiveness.\n\n" +
"CURRENT AMBIENT CONDITIONS\n" +
"---------------------------\n" +
"Air temperature: " +
airWaterTemperature +
" C\n" +
"Relative humidity: " +
airWaterHumidity +
" %\n" +
"Dew point: " +
String.format(
"%.2f",
dew
) +
" C\n" +
"Airflow: " +
dryAirFlow +
" m3/h\n" +
"Air pressure: " +
airWaterPressureKPa +
" kPa\n" +
"Coil temperature: " +
String.format(
"%.1f",
airWaterCoilTemperature.toDouble
) +
" C\n" +
"HX effectiveness: " +
airWaterHXEfficiency +
" %\n" +
"Collection efficiency: " +
airWaterCollectionEfficiency +
" %\n" +
"Estimated COP: " +
String.format(
"%.1f",
airWaterCOP
) +
"\n\n" +
"PSYCHROMETRIC RESULT\n" +
"--------------------\n" +
"Inlet humidity ratio: " +
String.format(
"%.6f",
win
) +
" kg water/kg dry air\n" +
"Outlet humidity ratio: " +
String.format(
"%.6f",
wout
) +
" kg water/kg dry air\n" +
"Theoretical moisture available: " +
String.format(
"%.3f",
theoretical
) +
" kg/h\n" +
"Estimated liquid production: " +
String.format(
"%.3f",
litresH
) +
" L/h\n" +
"Estimated 8-hour production: " +
String.format(
"%.2f",
litresH * 8.0
) +
" L\n" +
"At selected runtime (" +
String.format(
"%.1f",
airWaterRuntimeHours
) +
" h): " +
String.format(
"%.2f",
daily
) +
" L\n\n" +
"ENERGY ESTIMATE\n" +
"----------------\n" +
"Approx. electrical load: " +
String.format(
"%.3f",
powerKW
) +
" kW\n" +
"Fan pressure drop: " +
airWaterFanPressurePa +
" Pa\n" +
"Fan efficiency: " +
airWaterFanEfficiency +
" %\n" +
"Water yield per electrical kWh: " +
String.format(
"%.3f",
lpkwh
) +
" L/kWh\n\n" +
"CO2 SIDE PATH - SEPARATE FROM WATER\n" +
"-----------------------------------\n" +
"Ambient CO2: " +
airWaterCO2ppm +
" ppm\n" +
"CO2 entering with selected airflow: " +
String.format(
"%.4f",
co2In
) +
" kg/h\n" +
"Capture path: " +
(if (airWaterCO2Capture) "ON" else "OFF") +
"\n" +
"Estimated captured CO2: " +
String.format(
"%.4f",
co2Captured
) +
" kg/h\n\n" +
"IMPORTANT CHEMISTRY\n" +
"-------------------\n" +
"The water comes from H2O vapour that is already in the air.\n" +
"CO2 does NOT become water by simply being mixed with air.\n" +
"The CO2 path is therefore calculated separately.\n\n" +
"ENGINEERING LIMITS\n" +
"------------------\n" +
"Real equipment performance depends on altitude, pressure,\n" +
"air leakage, coil fouling, fan pressure drop, condenser\n" +
"approach temperature, compressor performance, control strategy,\n" +
"ambient weather and water-treatment performance.\n" +
"The displayed L/h and kW values are calculated estimates from the selected\n" +
"conditions and simplified component-performance assumptions; field output must\n" +
"be verified with calibrated sensors and measured equipment performance.\n\n" +
"POTABLE WATER SAFETY\n" +
"--------------------\n" +
"Condensed atmospheric water must not be assumed potable without\n" +
"appropriate treatment, maintenance and water-quality testing.\n" +
"The 3D treatment blocks are a system model, not a construction recipe."
)
airWaterReadout.setCaretPosition(0)
}
// --------------------------------------------------------
// BUTTON EVENTS
// --------------------------------------------------------
airWaterStartButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
airWaterRunning =
!airWaterRunning
if (airWaterRunning) {
airWaterStatus.setText(
"RUNNING - REAL MASS + ENERGY BALANCE"
)
airWaterStatus.setForeground(
SUCCESS
)
} else {
airWaterStatus.setText(
"PAUSED - ENGINEERING MODEL HOLD"
)
airWaterStatus.setForeground(
WARNING
)
}
}
}
)
airWaterResetButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
airWaterRunning = false
airWaterHumidity = 60
airWaterTemperature = 27
airWaterCoilTemperature = 8
airWaterAirflow = 900
airWaterPressureKPa = 101
airWaterHXEfficiency = 78
airWaterCollectionEfficiency = 88
airWaterCOP = 3.0
airWaterFanPressurePa = 180
airWaterFanEfficiency = 62
airWaterCO2ppm = 420
airWaterCO2Capture = false
airWaterCO2Efficiency = 55
airWaterTank = 0.0
airWaterProduced = 0.0
airWaterCO2Captured = 0.0
airWaterTotalEnergy = 0.0
airWaterHumiditySlider.setValue(
airWaterHumidity
)
airWaterTempSlider.setValue(
airWaterTemperature
)
airWaterCoilSlider.setValue(
airWaterCoilTemperature
)
airWaterAirflowSlider.setValue(
airWaterAirflow
)
airWaterPressureSlider.setValue(
airWaterPressureKPa
)
airWaterHXSlider.setValue(
airWaterHXEfficiency
)
airWaterCollectionSlider.setValue(
airWaterCollectionEfficiency
)
airWaterCOPSlider.setValue(30)
airWaterFanPressureSlider.setValue(
airWaterFanPressurePa
)
airWaterFanEfficiencySlider.setValue(
airWaterFanEfficiency
)
airWaterCO2Slider.setValue(
airWaterCO2ppm
)
airWaterCO2EffSlider.setValue(
airWaterCO2Efficiency
)
airWaterCO2Button.setText(
"CO2 CAPTURE: OFF"
)
airWaterStatus.setText(
"READY - PHYSICS MODEL"
)
airWaterStatus.setForeground(
SUCCESS
)
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterCO2Button.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
airWaterCO2Capture =
!airWaterCO2Capture
airWaterCO2Button.setText(
if (airWaterCO2Capture)
"CO2 CAPTURE: ON"
else
"CO2 CAPTURE: OFF"
)
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterExplainButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
showInfo(
frame,
"AIR TO WATER - REAL MODEL",
"REAL calculation chain:\n\n" +
"1. Atmospheric air enters the intake.\n" +
"2. Temperature + RH determine water-vapour content.\n" +
"3. Dew point is calculated.\n" +
"4. Air is cooled toward the coil temperature.\n" +
"5. A humidity-ratio mass balance estimates condensation.\n" +
"6. Collection efficiency converts gross condensate to recovered liquid.\n" +
"7. Enthalpy change + COP + fan pressure/efficiency estimate electricity use.\n" +
"8. CO2 entering the system is calculated separately.\n\n" +
"Water source: H2O vapour already in the atmosphere.\n" +
"CO2 is not converted into water by simple mixing.\n\n" +
"This is a software engineering model. Actual hardware needs\n" +
"site measurement, validated components, controls, sanitation\n" +
"and water-quality testing before potable use."
)
}
}
)
airWaterDiagnosticsButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val s = airWaterCoilState()
val dew = s._1
val litres = s._4
val power = s._5
val coilMargin =
dew - airWaterCoilTemperature.toDouble
val status =
if (litres <= 0.01)
"LOW / NO CONDENSATION"
else if (coilMargin <= 0.0)
"CHECK COIL VS DEW POINT"
else
"CONDENSATION CONDITIONS PRESENT"
showInfo(
frame,
"AIR TO WATER - SYSTEM DIAGNOSTICS",
"LIVE DIAGNOSTIC SNAPSHOT\n\n" +
"STATE: " + status + "\n\n" +
"Ambient temperature : " + airWaterTemperature + " C\n" +
"Relative humidity : " + airWaterHumidity + " %\n" +
"Pressure : " + airWaterPressureKPa + " kPa\n" +
"Dew point : " + String.format("%.2f", dew) + " C\n" +
"Coil temperature : " + String.format("%.1f", airWaterCoilTemperature.toDouble) + " C\n" +
"Dew/coil margin : " + String.format("%.2f", coilMargin) + " C\n" +
"Airflow : " + airWaterAirflow + " m3/h\n" +
"HX effectiveness : " + airWaterHXEfficiency + " %\n" +
"Collection : " + airWaterCollectionEfficiency + " %\n" +
"Estimated water : " + String.format("%.3f", litres) + " L/h\n" +
"Estimated power : " + String.format("%.3f", power) + " kW\n" +
"Water per kWh : " + String.format("%.3f", if (power > 0.0001) litres / power else 0.0) + " L/kWh\n\n" +
"CO2 path : " + (if (airWaterCO2Capture) "CAPTURE ENABLED" else "SEPARATE / OFF") + "\n\n" +
"This diagnostic is calculated from the model inputs; it is not a field calibration report."
)
}
}
)
airWaterHumiditySlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterHumidity =
airWaterHumiditySlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterTempSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterTemperature =
airWaterTempSlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterCoilSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterCoilTemperature =
airWaterCoilSlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterAirflowSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterAirflow =
airWaterAirflowSlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterHXSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterHXEfficiency =
airWaterHXSlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterCollectionSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterCollectionEfficiency =
airWaterCollectionSlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterPressureSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterPressureKPa =
airWaterPressureSlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterCOPSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterCOP =
airWaterCOPSlider.getValue.toDouble /
10.0
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterFanPressureSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterFanPressurePa =
airWaterFanPressureSlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterFanEfficiencySlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterFanEfficiency =
airWaterFanEfficiencySlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterCO2Slider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterCO2ppm =
airWaterCO2Slider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
airWaterCO2EffSlider.addChangeListener(
new javax.swing.event.ChangeListener {
def stateChanged(
e: javax.swing.event.ChangeEvent
): Unit = {
airWaterCO2Efficiency =
airWaterCO2EffSlider.getValue
refreshAirWaterReadout()
airWaterCanvas.repaint()
}
}
)
// --------------------------------------------------------
// REAL-TIME SIMULATION TIMER
// --------------------------------------------------------
val airWaterTimer =
new javax.swing.Timer(
60,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
airWaterFrame =
(airWaterFrame + 1) % 100000
if (airWaterRunning) {
val rate =
airWaterWaterLitresPerH()
val power =
airWaterPowerKW()
airWaterProduced +=
rate * 0.06 /
60.0
airWaterTank +=
rate * 0.06 /
60.0
airWaterTotalEnergy +=
power * 0.06 /
3600.0
if (airWaterTank > 100.0) {
airWaterTank = 100.0
}
if (airWaterCO2Capture) {
airWaterCO2Captured +=
airWaterCO2CaptureRateKgPerH() *
0.06 /
3600.0
}
}
if (airWaterFrame % 4 == 0) {
refreshAirWaterReadout()
}
airWaterCanvas.repaint()
}
}
)
airWaterTimer.setCoalesce(true)
airWaterTimer.start()
refreshAirWaterReadout()
// ========================================================
// KUNDALI PURAN - INTEGRATED VEDIC / SIDEREAL MODULE
// Based on the supplied standalone Kundali Puran source,
// redesigned as a rich studio tab with chart visualization,
// report tools, animation and navigation back to Air-to-Water.
// ========================================================
val kundaliPanel =
new JPanel(
new BorderLayout(10, 10)
)
kundaliPanel.setBackground(
new Color(8, 8, 28)
)
kundaliPanel.setBorder(
new EmptyBorder(10, 10, 10, 10)
)
var kundaliJD = 0.0
var kundaliCalculated = false
var kundaliAnimFrame = 0
var kundaliLastReport = ""
var kundaliShowDegrees = true
var kundaliGlow = true
var kundaliPrecisionMode = "STANDALONE APPROXIMATION"
val kundaliHeader =
new JPanel(
new BorderLayout(8, 8)
)
kundaliHeader.setBackground(
new Color(14, 12, 35)
)
kundaliHeader.setBorder(
BorderFactory.createLineBorder(
new Color(220, 170, 70),
1
)
)
val kundaliTitle =
makeLabel(
"KUNDALI PURAN - VEDIC / SIDEREAL BIRTH CHART",
23,
true
)
kundaliTitle.setForeground(
new Color(255, 215, 110)
)
val kundaliStatus =
makeLabel(
"READY - ENTER BIRTH DETAILS",
12,
true
)
kundaliStatus.setForeground(
new Color(190, 205, 220)
)
kundaliHeader.add(
kundaliTitle,
BorderLayout.WEST
)
kundaliHeader.add(
kundaliStatus,
BorderLayout.EAST
)
kundaliPanel.add(
kundaliHeader,
BorderLayout.NORTH
)
// --------------------------------------------------------
// KUNDALI INPUT CARD
// --------------------------------------------------------
val kundaliInputCard =
new JPanel()
kundaliInputCard.setLayout(null)
kundaliInputCard.setBackground(
new Color(24, 20, 50)
)
kundaliInputCard.setBorder(
BorderFactory.createLineBorder(
new Color(220, 170, 70),
2
)
)
kundaliInputCard.setPreferredSize(
new Dimension(330, 590)
)
val kundaliInputTitle =
new JLabel(
"BIRTH DETAILS",
SwingConstants.CENTER
)
kundaliInputTitle.setForeground(
new Color(255, 220, 120)
)
kundaliInputTitle.setFont(
new Font(
"Serif",
Font.BOLD,
22
)
)
kundaliInputTitle.setBounds(15, 14, 300, 34)
kundaliInputCard.add(kundaliInputTitle)
def kundaliFieldLabel(
t: String,
y: Int
): JLabel = {
val l = new JLabel(t)
l.setForeground(
new Color(240, 230, 205)
)
l.setFont(
new Font(
"SansSerif",
Font.BOLD,
12
)
)
l.setBounds(16, y, 115, 27)
kundaliInputCard.add(l)
l
}
def kundaliField(
y: Int
): JTextField = {
val f = new JTextField()
f.setFont(
new Font(
"SansSerif",
Font.PLAIN,
14
)
)
f.setBackground(
new Color(250, 245, 230)
)
f.setForeground(
new Color(30, 25, 40)
)
f.setBounds(125, y, 185, 30)
kundaliInputCard.add(f)
f
}
kundaliFieldLabel("First Name", 64)
val kundaliFirstName = kundaliField(61)
kundaliFieldLabel("Surname", 101)
val kundaliSurname = kundaliField(98)
kundaliFieldLabel("Birth Date", 138)
val kundaliBirthDate = kundaliField(135)
kundaliBirthDate.setToolTipText("DD/MM/YYYY")
kundaliFieldLabel("Birth Time", 175)
val kundaliBirthTime = kundaliField(172)
kundaliBirthTime.setToolTipText("HH:MM or HH:MM:SS")
kundaliFieldLabel("Birth Place", 212)
val kundaliBirthPlace = kundaliField(209)
kundaliFieldLabel("Latitude", 249)
val kundaliLatitude = kundaliField(246)
kundaliFieldLabel("Longitude", 286)
val kundaliLongitude = kundaliField(283)
kundaliFieldLabel("Timezone", 323)
val kundaliTimezone = kundaliField(320)
kundaliTimezone.setText("5.5")
val kundaliInfo =
new JLabel(
"<html><center>Use the real birth-place coordinates.<br>India commonly uses UTC +5.5.<br>This module is a traditional sidereal-style calculator.</center></html>",
SwingConstants.CENTER
)
kundaliInfo.setForeground(
new Color(205, 192, 160)
)
kundaliInfo.setFont(
new Font(
"SansSerif",
Font.PLAIN,
10
)
)
kundaliInfo.setBounds(15, 357, 300, 55)
kundaliInputCard.add(kundaliInfo)
val kundaliCalculateButton =
makeButton(
"CALCULATE KUNDALI"
)
kundaliCalculateButton.setBounds(16, 420, 294, 40)
kundaliInputCard.add(kundaliCalculateButton)
val kundaliSampleButton =
makeButton(
"LOAD SAMPLE"
)
kundaliSampleButton.setBounds(16, 467, 142, 38)
kundaliInputCard.add(kundaliSampleButton)
val kundaliClearButton =
makeButton(
"CLEAR"
)
kundaliClearButton.setBounds(168, 467, 142, 38)
kundaliInputCard.add(kundaliClearButton)
val kundaliCopyButton =
makeButton(
"COPY REPORT"
)
kundaliCopyButton.setBounds(16, 512, 142, 38)
kundaliInputCard.add(kundaliCopyButton)
val kundaliExportButton =
makeButton(
"EXPORT TXT"
)
kundaliExportButton.setBounds(168, 512, 142, 38)
kundaliInputCard.add(kundaliExportButton)
val kundaliFullPackButton =
makeButton(
"FULL KUNDALI PACK"
)
kundaliFullPackButton.setBounds(16, 547, 294, 38)
kundaliInputCard.add(kundaliFullPackButton)
// --------------------------------------------------------
// KUNDALI MATH - SAME BASIC TERMS / STRUCTURE AS SOURCE
// --------------------------------------------------------
def kundaliNormalize(
value: Double
): Double = {
var x = value % 360.0
if (x < 0.0) x += 360.0
x
}
def kundaliJulianDay(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int,
timezone: Double
): Double = {
var y = year
var m = month
if (m <= 2) {
y -= 1
m += 12
}
val a = Math.floor(y / 100.0)
val b = 2.0 - a + Math.floor(a / 4.0)
val decimalHour =
hour +
minute / 60.0 +
second / 3600.0 -
timezone
Math.floor(365.25 * (y + 4716)) +
Math.floor(30.6001 * (m + 1)) +
day + b -
1524.5 +
decimalHour / 24.0
}
def kundaliLahiriAyanamsha(
jd: Double
): Double = {
val t =
(jd - 2451545.0) / 36525.0
23.85675 + 0.013968 * t
}
def kundaliSidereal(
tropical: Double,
jd: Double
): Double = {
kundaliNormalize(
tropical -
kundaliLahiriAyanamsha(jd)
)
}
def kundaliSignName(
longitude: Double
): String = {
val names = Array(
"Mesha (Aries)",
"Vrishabha (Taurus)",
"Mithuna (Gemini)",
"Karka (Cancer)",
"Simha (Leo)",
"Kanya (Virgo)",
"Tula (Libra)",
"Vrishchika (Scorpio)",
"Dhanu (Sagittarius)",
"Makara (Capricorn)",
"Kumbha (Aquarius)",
"Meena (Pisces)"
)
names(
Math.floor(
kundaliNormalize(longitude) / 30.0
).toInt
)
}
def kundaliDegreeText(
longitude: Double
): String = {
val x = kundaliNormalize(longitude)
val signDegree = x % 30.0
val d = Math.floor(signDegree).toInt
val min =
Math.floor((signDegree - d) * 60.0).toInt
val sec =
Math.floor(
(((signDegree - d) * 60.0) - min) * 60.0
).toInt
d + " deg " + min + "' " + sec + "\""
}
def kundaliSunLongitude(
jd: Double
): Double = {
val n = jd - 2451545.0
val l =
kundaliNormalize(
280.460 + 0.9856474 * n
)
val g =
Math.toRadians(
kundaliNormalize(
357.528 + 0.9856003 * n
)
)
kundaliNormalize(
l +
1.915 * Math.sin(g) +
0.020 * Math.sin(2.0 * g)
)
}
def kundaliMoonLongitude(
jd: Double
): Double = {
val n = jd - 2451545.0
val l =
kundaliNormalize(
218.316 + 13.176396 * n
)
val m =
Math.toRadians(
kundaliNormalize(
134.963 + 13.064993 * n
)
)
val f =
Math.toRadians(
kundaliNormalize(
93.272 + 13.229350 * n
)
)
kundaliNormalize(
l +
6.289 * Math.sin(m) +
1.274 *
Math.sin(
Math.toRadians(
2.0 * kundaliNormalize(297.850 + 12.190749 * n)
) - m
) +
0.658 *
Math.sin(
Math.toRadians(
2.0 * kundaliNormalize(357.529 + 0.9856003 * n)
)
) +
0.214 * Math.sin(2.0 * m) -
0.186 *
Math.sin(
Math.toRadians(
kundaliNormalize(357.529 + 0.9856003 * n)
)
) +
0.040 * Math.sin(f)
)
}
def kundaliLinearLongitude(
jd: Double,
base: Double,
rate: Double
): Double = {
kundaliNormalize(
base +
rate * (jd - 2451545.0)
)
}
def kundaliMercuryLongitude(jd: Double): Double =
kundaliLinearLongitude(jd, 252.25084, 4.09233445)
def kundaliVenusLongitude(jd: Double): Double =
kundaliLinearLongitude(jd, 181.97973, 1.60213034)
def kundaliMarsLongitude(jd: Double): Double =
kundaliLinearLongitude(jd, 355.433, 0.5240208)
def kundaliJupiterLongitude(jd: Double): Double =
kundaliLinearLongitude(jd, 34.351, 0.0830853)
def kundaliSaturnLongitude(jd: Double): Double =
kundaliLinearLongitude(jd, 50.077, 0.0334442)
def kundaliRahuLongitude(jd: Double): Double =
kundaliNormalize(
125.04452 -
0.0529538083 * (jd - 2451545.0)
)
def kundaliKetuLongitude(jd: Double): Double =
kundaliNormalize(
kundaliRahuLongitude(jd) + 180.0
)
def kundaliNakshatraName(
longitude: Double
): String = {
val names = Array(
"Ashwini", "Bharani", "Krittika", "Rohini", "Mrigashira",
"Ardra", "Punarvasu", "Pushya", "Ashlesha", "Magha",
"Purva Phalguni", "Uttara Phalguni", "Hasta", "Chitra",
"Swati", "Vishakha", "Anuradha", "Jyeshtha", "Mula",
"Purva Ashadha", "Uttara Ashadha", "Shravana", "Dhanishtha",
"Shatabhisha", "Purva Bhadrapada", "Uttara Bhadrapada", "Revati"
)
val i =
Math.floor(
kundaliNormalize(longitude) / (360.0 / 27.0)
).toInt
names(
Math.min(
26,
Math.max(0, i)
)
)
}
def kundaliNakshatraPada(
longitude: Double
): Int = {
val span = 360.0 / 108.0
(Math.floor(kundaliNormalize(longitude) / span).toInt % 4) + 1
}
def kundaliAscendant(
jd: Double,
latitude: Double,
longitude: Double
): Double = {
val t =
(jd - 2451545.0) / 36525.0
val gmst =
280.46061837 +
360.98564736629 * (jd - 2451545.0) +
0.000387933 * t * t -
t * t * t / 38710000.0
val lst =
kundaliNormalize(
gmst + longitude
)
val phi = Math.toRadians(latitude)
val eps = Math.toRadians(23.439291)
val theta = Math.toRadians(lst)
val asc =
Math.atan2(
-Math.cos(theta),
Math.sin(theta) * Math.cos(eps) +
Math.tan(phi) * Math.sin(eps)
)
kundaliSidereal(
kundaliNormalize(Math.toDegrees(asc)),
jd
)
}
def kundaliHouseFromLongitude(
planet: Double,
lagna: Double
): Int = {
Math.floor(
kundaliNormalize(planet - lagna) / 30.0
).toInt + 1
}
def kundaliNavamsaNumber(
longitude: Double
): Int = {
val withinSign =
kundaliNormalize(longitude) % 30.0
Math.floor(
withinSign / (30.0 / 9.0)
).toInt + 1
}
def kundaliPlanetArray(
jd: Double
): Array[(String, Double)] = {
Array(
("Sun", kundaliSidereal(kundaliSunLongitude(jd), jd)),
("Moon", kundaliSidereal(kundaliMoonLongitude(jd), jd)),
("Mercury", kundaliSidereal(kundaliMercuryLongitude(jd), jd)),
("Venus", kundaliSidereal(kundaliVenusLongitude(jd), jd)),
("Mars", kundaliSidereal(kundaliMarsLongitude(jd), jd)),
("Jupiter", kundaliSidereal(kundaliJupiterLongitude(jd), jd)),
("Saturn", kundaliSidereal(kundaliSaturnLongitude(jd), jd)),
("Rahu", kundaliSidereal(kundaliRahuLongitude(jd), jd)),
("Ketu", kundaliSidereal(kundaliKetuLongitude(jd), jd))
)
}
def kundaliPlanetShort(name: String): String = {
name match {
case "Sun" => "Su"
case "Moon" => "Mo"
case "Mercury" => "Me"
case "Venus" => "Ve"
case "Mars" => "Ma"
case "Jupiter" => "Ju"
case "Saturn" => "Sa"
case "Rahu" => "Ra"
case "Ketu" => "Ke"
case _ => name.take(2)
}
}
def kundaliAddApproxYears(
base: java.util.GregorianCalendar,
years: Double
): java.util.GregorianCalendar = {
val copy =
new java.util.GregorianCalendar()
copy.setTimeInMillis(
base.getTimeInMillis
)
val millisPerYear =
365.2425 * 24.0 * 60.0 * 60.0 * 1000.0
copy.setTimeInMillis(
copy.getTimeInMillis +
(years * millisPerYear).toLong
)
copy
}
def kundaliDashaText(
moonLongitude: Double,
year: Int,
month: Int,
day: Int
): String = {
val dashaLords =
Array(
"Ketu",
"Venus",
"Sun",
"Moon",
"Mars",
"Rahu",
"Jupiter",
"Saturn",
"Mercury"
)
val dashaYears =
Array(
7.0,
20.0,
6.0,
10.0,
7.0,
18.0,
16.0,
19.0,
17.0
)
val nakSize =
360.0 / 27.0
val nakIndex =
Math.floor(
kundaliNormalize(moonLongitude) / nakSize
).toInt
val lordIndex =
nakIndex % 9
val withinNak =
kundaliNormalize(moonLongitude) % nakSize
val fractionElapsed =
withinNak / nakSize
val firstRemaining =
dashaYears(lordIndex) *
(1.0 - fractionElapsed)
val birth =
new java.util.GregorianCalendar(
year,
Math.max(0, month - 1),
day
)
val fmt =
new SimpleDateFormat("dd/MM/yyyy")
val sb =
new StringBuilder()
sb.append("VIMSHOTTARI MAHADASHA - TRADITIONAL TIMING VIEW\n")
sb.append("=================================================\n\n")
sb.append("Moon Nakshatra: ")
.append(kundaliNakshatraName(moonLongitude))
.append("\n")
sb.append("Starting Mahadasha: ")
.append(dashaLords(lordIndex))
.append("\n")
sb.append("Approximate balance at birth: ")
.append(String.format("%.2f", firstRemaining))
.append(" years\n\n")
var currentIndex = lordIndex
var startDate = birth
var duration = firstRemaining
var totalYears = 0.0
var row = 0
while (row < 9) {
val endDate =
kundaliAddApproxYears(
startDate,
duration
)
sb.append(
String.format(
"%d. %-8s %.2f years %s -> %s (age %.2f to %.2f)\n",
row + 1,
dashaLords(currentIndex),
duration,
fmt.format(startDate.getTime),
fmt.format(endDate.getTime),
totalYears,
totalYears + duration
)
)
startDate = endDate
totalYears += duration
currentIndex =
(currentIndex + 1) % 9
duration =
dashaYears(currentIndex)
row += 1
}
sb.append("\nInterpretation note\n")
sb.append("-------------------\n")
sb.append("The dates above are an approximate traditional Vimshottari timing calculation based on the standalone Moon position in this app.\n")
sb.append("They are not scientifically validated predictions and should not be treated as guaranteed future events.\n")
sb.toString
}
def kundaliKarmicText(
lagna: Double,
moon: Double,
rahu: Double,
ketu: Double
): String = {
val sb = new StringBuilder
sb.append("KARMIC / LIFE-THEME REFLECTION\n")
sb.append("==============================\n\n")
sb.append("This section presents traditional astrological symbolism, not verified facts about past or future lives.\n\n")
sb.append("PAST-LIFE SYMBOLISM (TRADITIONAL)\n")
sb.append("----------------------------------\n")
sb.append("Ketu sign: ")
.append(kundaliSignName(ketu))
.append("\n")
sb.append("Ketu nakshatra: ")
.append(kundaliNakshatraName(ketu))
.append("\n")
sb.append("Traditional reading uses Ketu as a symbolic marker for familiar patterns, detachment, or previously developed themes.\n\n")
sb.append("FUTURE / GROWTH SYMBOLISM (TRADITIONAL)\n")
sb.append("----------------------------------------\n")
sb.append("Rahu sign: ")
.append(kundaliSignName(rahu))
.append("\n")
sb.append("Rahu nakshatra: ")
.append(kundaliNakshatraName(rahu))
.append("\n")
sb.append("Traditional reading uses Rahu as a symbolic marker for unfamiliar goals, growth, experimentation, or areas of strong desire.\n\n")
sb.append("CORE THEMES\n")
sb.append("-----------\n")
sb.append("Lagna Rashi : ")
.append(kundaliSignName(lagna))
.append("\n")
sb.append("Moon Rashi : ")
.append(kundaliSignName(moon))
.append("\n")
sb.append("Moon Nakshatra : ")
.append(kundaliNakshatraName(moon))
.append("\n\n")
sb.append("ABOUT 'WHAT WILL HAPPEN'\n")
sb.append("-------------------------\n")
sb.append("No chart can establish an exact, guaranteed event, date, cause, or future-life outcome. This app therefore presents timing as traditional reflection windows rather than certainty.\n")
sb.toString
}
def kundaliPrecisionText(): String = {
"PRECISION TRANSPARENCY\n" +
"=======================\n\n" +
"This standalone build uses self-contained astronomical approximations.\n" +
"It is useful for an educational/traditional chart display but should not be called professional-grade ephemeris precision.\n\n" +
"For high-precision chart work, compare the birth data and planetary positions against a dedicated ephemeris engine.\n"
}
def kundaliCreateReport(
name: String,
date: String,
time: String,
place: String,
lat: Double,
lon: Double,
tz: Double,
jd: Double
): String = {
val lagna = kundaliAscendant(jd, lat, lon)
val planets = kundaliPlanetArray(jd)
val moon = planets(1)._2
val sb = new StringBuilder
sb.append("==============================================================\n")
sb.append(" KUNDALI PURAN\n")
sb.append(" VEDIC / SIDEREAL REPORT\n")
sb.append("==============================================================\n\n")
sb.append("PERSONAL DETAILS\n")
sb.append("--------------------------------------------------------------\n")
sb.append("Name : ").append(name).append("\n")
sb.append("Birth Date : ").append(date).append("\n")
sb.append("Birth Time : ").append(time).append("\n")
sb.append("Birth Place : ").append(place).append("\n")
sb.append("Latitude : ").append(String.format("%.6f", lat)).append("\n")
sb.append("Longitude : ").append(String.format("%.6f", lon)).append("\n")
sb.append("Timezone : UTC ").append(String.format("%.2f", tz)).append("\n\n")
sb.append("CALCULATION\n")
sb.append("--------------------------------------------------------------\n")
sb.append("Julian Day : ").append(String.format("%.6f", jd)).append("\n")
sb.append("Lahiri Ayanamsha : ").append(String.format("%.6f", kundaliLahiriAyanamsha(jd))).append(" deg\n\n")
sb.append("LAGNA / ASCENDANT\n")
sb.append("--------------------------------------------------------------\n")
sb.append("Lagna Degree : ").append(kundaliDegreeText(lagna)).append("\n")
sb.append("Lagna Rashi : ").append(kundaliSignName(lagna)).append("\n")
sb.append("Lagna House : 1\n\n")
sb.append("MOON / JANMA DETAILS\n")
sb.append("--------------------------------------------------------------\n")
sb.append("Moon Degree : ").append(kundaliDegreeText(moon)).append("\n")
sb.append("Moon Rashi : ").append(kundaliSignName(moon)).append("\n")
sb.append("Nakshatra : ").append(kundaliNakshatraName(moon)).append("\n")
sb.append("Nakshatra Pada : ").append(kundaliNakshatraPada(moon)).append("\n")
sb.append("Navamsa Number : ").append(kundaliNavamsaNumber(moon)).append("\n\n")
sb.append("PLANETARY POSITIONS\n")
sb.append("--------------------------------------------------------------\n")
var i = 0
while (i < planets.length) {
val p = planets(i)
sb.append(
String.format(
"%-9s | %-18s | %-12s | House %2d | Navamsa %d\n",
p._1,
kundaliDegreeText(p._2),
kundaliSignName(p._2),
kundaliHouseFromLongitude(p._2, lagna),
kundaliNavamsaNumber(p._2)
)
)
i += 1
}
sb.append("\n12 HOUSE STRUCTURE\n")
sb.append("--------------------------------------------------------------\n")
var h = 1
while (h <= 12) {
val houseLongitude =
kundaliNormalize(lagna + (h - 1) * 30.0)
sb.append(
String.format(
"House %2d : %-18s : start %s\n",
h,
kundaliSignName(houseLongitude),
kundaliDegreeText(houseLongitude)
)
)
h += 1
}
sb.append("\nTRADITIONAL CHART MARKERS\n")
sb.append("--------------------------------------------------------------\n")
sb.append("Janma Rashi : ").append(kundaliSignName(moon)).append("\n")
sb.append("Janma Nakshatra : ").append(kundaliNakshatraName(moon)).append("\n")
sb.append("Pada : ").append(kundaliNakshatraPada(moon)).append("\n")
sb.append("Lagna Rashi : ").append(kundaliSignName(lagna)).append("\n\n")
sb.append("\nTRADITIONAL KARMIC / GROWTH VIEW\n")
sb.append("--------------------------------------------------------------\n")
sb.append("\nVIMSHOTTARI TIMING VIEW\n")
sb.append("--------------------------------------------------------------\n")
val datePartsForDasha = date.split("/")
if (datePartsForDasha.length == 3) {
try {
sb.append(
kundaliDashaText(
moon,
datePartsForDasha(2).toInt,
datePartsForDasha(1).toInt,
datePartsForDasha(0).toInt
)
)
} catch {
case _: Throwable =>
sb.append("Dasha timing could not be generated from the entered date.\n")
}
}
sb.append("\n")
sb.append(kundaliPrecisionText())
sb.append("\nIMPORTANT NOTE\n")
sb.append("--------------------------------------------------------------\n")
sb.append("This standalone studio module follows a simplified sidereal-style\n")
sb.append("calculation approach based on the supplied Kundali Puran source.\n")
sb.append("It is not a professional-grade ephemeris engine. For high-precision\n")
sb.append("astronomy, a dedicated ephemeris such as Swiss Ephemeris is required.\n")
sb.append("Astrological interpretations shown here are traditional/cultural\n")
sb.append("content and should not be treated as scientific predictions.\n")
sb.append("==============================================================\n")
sb.toString
}
// --------------------------------------------------------
// KUNDALI REPORT VIEW
// --------------------------------------------------------
val kundaliReportArea = makeArea()
kundaliReportArea.setEditable(false)
kundaliReportArea.setLineWrap(false)
kundaliReportArea.setBackground(
new Color(10, 10, 28)
)
kundaliReportArea.setForeground(
new Color(250, 235, 190)
)
kundaliReportArea.setFont(
new Font(
"Monospaced",
Font.PLAIN,
12
)
)
val kundaliReportScroll =
makeScroll(kundaliReportArea)
// --------------------------------------------------------
// KUNDALI PLANET TABLE
// --------------------------------------------------------
val kundaliTableModel =
new javax.swing.table.DefaultTableModel(
Array[Object](
"Planet", "Degree", "Rashi", "House", "Nakshatra", "Navamsa"
),
0
) {
override def isCellEditable(
row: Int,
column: Int
): Boolean = false
}
val kundaliTable =
new JTable(kundaliTableModel)
kundaliTable.setRowHeight(24)
kundaliTable.setBackground(
new Color(20, 18, 40)
)
kundaliTable.setForeground(
new Color(245, 232, 188)
)
kundaliTable.setGridColor(
new Color(85, 70, 110)
)
kundaliTable.getTableHeader.setBackground(
new Color(60, 46, 85)
)
kundaliTable.getTableHeader.setForeground(Color.WHITE)
kundaliTable.getTableHeader.setFont(
new Font("SansSerif", Font.BOLD, 12)
)
val kundaliTableScroll =
new JScrollPane(kundaliTable)
// --------------------------------------------------------
// KUNDALI CHART CANVAS
// --------------------------------------------------------
val kundaliChartCanvas =
new JPanel {
setBackground(
new Color(8, 8, 28)
)
setPreferredSize(
new Dimension(515, 515)
)
def drawStarField(
g: Graphics2D,
w: Int,
h: Int
): Unit = {
var s = 0
while (s < 42) {
val sx =
(s * 97 + kundaliAnimFrame * (1 + s % 3)) % Math.max(1, w)
val sy =
(s * 53 + kundaliAnimFrame / 2) % Math.max(1, h)
val alpha =
70 + ((s * 13 + kundaliAnimFrame) % 120)
g.setColor(
new Color(255, 235, 160, alpha)
)
val size =
2 + s % 3
g.fillOval(
sx,
sy,
size,
size
)
s += 1
}
}
def houseCenters(
cx: Int,
cy: Int,
r: Int
): Array[(Int, Int)] = {
Array(
(cx, cy - r / 2),
(cx - r / 2, cy - r / 2),
(cx - 3 * r / 4, cy),
(cx - r / 2, cy + r / 2),
(cx, cy + r / 2),
(cx + r / 2, cy + r / 2),
(cx + 3 * r / 4, cy),
(cx + r / 2, cy - r / 2),
(cx, cy - 3 * r / 4),
(cx + r / 2, cy),
(cx, cy),
(cx - r / 2, cy)
)
}
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(graphics)
val g = graphics.asInstanceOf[Graphics2D]
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
val w = getWidth
val h = getHeight
drawStarField(g, w, h)
val cx = w / 2
val cy = h / 2 + 8
val r = Math.min(w, h) / 2 - 42
if (kundaliGlow) {
val pulse =
8 + Math.abs((kundaliAnimFrame % 40) - 20)
g.setColor(
new Color(255, 215, 90, 18)
)
g.fillOval(
cx - r - pulse,
cy - r - pulse,
(r + pulse) * 2,
(r + pulse) * 2
)
}
g.setColor(
new Color(24, 20, 42, 245)
)
g.fillRect(
cx - r,
cy - r,
r * 2,
r * 2
)
g.setColor(
new Color(220, 170, 70)
)
g.setStroke(
new BasicStroke(2.0f)
)
g.drawRect(
cx - r,
cy - r,
r * 2,
r * 2
)
val diamond = new Polygon()
diamond.addPoint(cx, cy - r)
diamond.addPoint(cx + r, cy)
diamond.addPoint(cx, cy + r)
diamond.addPoint(cx - r, cy)
g.drawPolygon(diamond)
g.drawLine(cx - r, cy - r, cx, cy)
g.drawLine(cx + r, cy - r, cx, cy)
g.drawLine(cx - r, cy + r, cx, cy)
g.drawLine(cx + r, cy + r, cx, cy)
if (!kundaliCalculated) {
g.setColor(
new Color(235, 220, 170)
)
g.setFont(
new Font(
"Serif",
Font.BOLD,
20
)
)
g.drawString(
"KUNDALI CHAKRA",
cx - 82,
cy + 10
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
11
)
)
g.setColor(MUTED)
g.drawString(
"Calculate a birth chart to populate houses and planets.",
cx - 151,
cy + 34
)
} else {
val lagna = kundaliAscendant(
kundaliJD,
kundaliLatitude.getText.trim.toDouble,
kundaliLongitude.getText.trim.toDouble
)
val planets = kundaliPlanetArray(kundaliJD)
val houses = houseCenters(cx, cy, r)
val houseText = Array.fill(12)("")
var pi = 0
while (pi < planets.length) {
val p = planets(pi)
val house =
kundaliHouseFromLongitude(p._2, lagna) - 1
val old = houseText(house)
houseText(house) =
old +
kundaliPlanetShort(p._1) +
" " +
String.format("%.0f", p._2 % 30.0) +
"\n"
pi += 1
}
var hi = 0
while (hi < 12) {
val center = houses(hi)
g.setColor(
new Color(150, 122, 74)
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
11
)
)
g.drawString(
(hi + 1).toString,
center._1 - 4,
center._2 - 14
)
g.setFont(
new Font(
"Monospaced",
Font.BOLD,
10
)
)
g.setColor(
new Color(245, 226, 175)
)
val lines =
houseText(hi).split("\n").filter(_.nonEmpty)
var li = 0
while (li < lines.length && li < 4) {
g.drawString(
lines(li),
center._1 - 15,
center._2 + li * 13
)
li += 1
}
hi += 1
}
g.setColor(
new Color(255, 213, 102)
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
12
)
)
g.drawString(
"LAGNA",
cx - 22,
cy + 5
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
10
)
)
g.setColor(MUTED)
g.drawString(
kundaliSignName(lagna),
cx - 47,
cy + 20
)
}
g.setColor(
new Color(255, 220, 120)
)
g.setFont(
new Font(
"Serif",
Font.BOLD,
18
)
)
g.drawString(
"KUNDALI PURAN",
18,
28
)
g.setColor(MUTED)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
10
)
)
g.drawString(
"North-Indian-style visual chart | traditional layout",
18,
46
)
}
}
val kundaliChartScroll =
new JScrollPane(kundaliChartCanvas)
kundaliChartScroll.setBorder(
BorderFactory.createLineBorder(
new Color(100, 80, 120),
1
)
)
// --------------------------------------------------------
// KUNDALI DETAIL TABS
// --------------------------------------------------------
val kundaliDetailsTabs =
new JTabbedPane()
kundaliDetailsTabs.setBackground(
new Color(18, 17, 42)
)
kundaliDetailsTabs.setForeground(Color.WHITE)
val kundaliOverviewPanel =
new JPanel(
new BorderLayout(8, 8)
)
kundaliOverviewPanel.setBackground(
new Color(18, 17, 42)
)
val kundaliOverviewText = makeArea()
kundaliOverviewText.setEditable(false)
kundaliOverviewText.setLineWrap(true)
kundaliOverviewText.setWrapStyleWord(true)
kundaliOverviewText.setFont(
new Font(
"SansSerif",
Font.PLAIN,
13
)
)
kundaliOverviewText.setBackground(
new Color(10, 10, 28)
)
kundaliOverviewText.setForeground(
new Color(245, 232, 188)
)
kundaliOverviewPanel.add(
makeScroll(kundaliOverviewText),
BorderLayout.CENTER
)
val kundaliMarkerPanel =
new JPanel(
new GridLayout(0, 1, 4, 4)
)
kundaliMarkerPanel.setBackground(
new Color(24, 20, 50)
)
kundaliMarkerPanel.setBorder(
new EmptyBorder(8, 8, 8, 8)
)
val kundaliMarkers = Array(
"Lagna : -",
"Moon Rashi : -",
"Nakshatra : -",
"Pada : -",
"Ayanamsha : -"
)
var km = 0
while (km < kundaliMarkers.length) {
val lab = makeLabel(
kundaliMarkers(km),
12,
true
)
lab.setForeground(
new Color(255, 218, 130)
)
kundaliMarkerPanel.add(lab)
km += 1
}
kundaliOverviewPanel.add(
kundaliMarkerPanel,
BorderLayout.NORTH
)
val kundaliNavamsaArea = makeArea()
kundaliNavamsaArea.setEditable(false)
kundaliNavamsaArea.setFont(
new Font(
"Monospaced",
Font.PLAIN,
12
)
)
kundaliNavamsaArea.setBackground(
new Color(10, 10, 28)
)
kundaliNavamsaArea.setForeground(
new Color(240, 225, 180)
)
val kundaliTraditionalArea = makeArea()
kundaliTraditionalArea.setEditable(false)
kundaliTraditionalArea.setLineWrap(true)
kundaliTraditionalArea.setWrapStyleWord(true)
kundaliTraditionalArea.setBackground(
new Color(10, 10, 28)
)
kundaliTraditionalArea.setForeground(
new Color(240, 225, 180)
)
kundaliTraditionalArea.setFont(
new Font(
"SansSerif",
Font.PLAIN,
13
)
)
kundaliDetailsTabs.addTab(
"OVERVIEW",
kundaliOverviewPanel
)
kundaliDetailsTabs.addTab(
"REPORT",
kundaliReportScroll
)
kundaliDetailsTabs.addTab(
"PLANETS",
kundaliTableScroll
)
kundaliDetailsTabs.addTab(
"NAVAMSA",
makeScroll(kundaliNavamsaArea)
)
kundaliDetailsTabs.addTab(
"TRADITIONAL NOTES",
makeScroll(kundaliTraditionalArea)
)
val kundaliKarmicArea = makeArea()
kundaliKarmicArea.setEditable(false)
kundaliKarmicArea.setLineWrap(true)
kundaliKarmicArea.setWrapStyleWord(true)
kundaliKarmicArea.setBackground(
new Color(10, 10, 28)
)
kundaliKarmicArea.setForeground(
new Color(240, 225, 180)
)
kundaliDetailsTabs.addTab(
"KARMIC / GROWTH",
makeScroll(kundaliKarmicArea)
)
val kundaliTimingArea = makeArea()
kundaliTimingArea.setEditable(false)
kundaliTimingArea.setLineWrap(true)
kundaliTimingArea.setWrapStyleWord(true)
kundaliTimingArea.setBackground(
new Color(10, 10, 28)
)
kundaliTimingArea.setForeground(
new Color(240, 225, 180)
)
kundaliDetailsTabs.addTab(
"DASHA / TIMING",
makeScroll(kundaliTimingArea)
)
val kundaliRightSplit =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
kundaliChartScroll,
kundaliDetailsTabs
)
kundaliRightSplit.setResizeWeight(0.47)
kundaliRightSplit.setDividerLocation(520)
val kundaliCenterSplit =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
kundaliInputCard,
kundaliRightSplit
)
kundaliCenterSplit.setResizeWeight(0.24)
kundaliCenterSplit.setDividerLocation(340)
kundaliPanel.add(
kundaliCenterSplit,
BorderLayout.CENTER
)
val kundaliBottomBar =
new JPanel(
new BorderLayout(8, 8)
)
kundaliBottomBar.setBackground(
new Color(14, 12, 35)
)
val kundaliAnimationButton =
makeButton(
"CHART GLOW: ON"
)
val kundaliDegreesButton =
makeButton(
"DETAILS: ON"
)
val kundaliBackButton =
makeButton(
"BACK TO AIR TO WATER"
)
val kundaliFooter =
makeLabel(
"Traditional / cultural chart module - calculation display only",
10,
false
)
kundaliFooter.setForeground(
new Color(175, 165, 145)
)
kundaliBottomBar.add(
kundaliFooter,
BorderLayout.CENTER
)
val kundaliBottomButtons =
new JPanel(
new GridLayout(1, 3, 5, 5)
)
kundaliBottomButtons.setBackground(
new Color(14, 12, 35)
)
kundaliBottomButtons.add(kundaliAnimationButton)
kundaliBottomButtons.add(kundaliDegreesButton)
kundaliBottomButtons.add(kundaliBackButton)
kundaliBottomBar.add(
kundaliBottomButtons,
BorderLayout.EAST
)
kundaliPanel.add(
kundaliBottomBar,
BorderLayout.SOUTH
)
// --------------------------------------------------------
// KUNDALI REFRESH / TABLE / DASHBOARD HELPERS
// --------------------------------------------------------
def refreshKundaliView(): Unit = {
if (!kundaliCalculated) {
kundaliStatus.setText(
"READY - ENTER BIRTH DETAILS"
)
kundaliReportArea.setText(
"Enter birth date, time, place, latitude, longitude and timezone, then press CALCULATE KUNDALI."
)
kundaliOverviewText.setText(
"KUNDALI PURAN READY\n\nThe visual chart, planet table, navamsa view and report will populate after calculation."
)
kundaliNavamsaArea.setText("")
kundaliTraditionalArea.setText("")
kundaliKarmicArea.setText("")
kundaliTimingArea.setText("")
kundaliChartCanvas.repaint()
return
}
val lat = kundaliLatitude.getText.trim.toDouble
val lon = kundaliLongitude.getText.trim.toDouble
val tz = kundaliTimezone.getText.trim.toDouble
val lagna = kundaliAscendant(kundaliJD, lat, lon)
val planets = kundaliPlanetArray(kundaliJD)
val moon = planets(1)._2
val report =
kundaliCreateReport(
kundaliFirstName.getText.trim + " " + kundaliSurname.getText.trim,
kundaliBirthDate.getText.trim,
kundaliBirthTime.getText.trim,
kundaliBirthPlace.getText.trim,
lat,
lon,
tz,
kundaliJD
)
kundaliLastReport = report
kundaliReportArea.setText(report)
kundaliReportArea.setCaretPosition(0)
val markerTexts = Array(
"Lagna : " + kundaliSignName(lagna),
"Moon Rashi : " + kundaliSignName(moon),
"Nakshatra : " + kundaliNakshatraName(moon),
"Pada : " + kundaliNakshatraPada(moon),
"Ayanamsha : " + String.format("%.4f", kundaliLahiriAyanamsha(kundaliJD)) + " deg"
)
var mi = 0
while (mi < kundaliMarkerPanel.getComponentCount && mi < markerTexts.length) {
kundaliMarkerPanel.getComponent(mi).asInstanceOf[JLabel].setText(markerTexts(mi))
mi += 1
}
while (kundaliTableModel.getRowCount > 0) {
kundaliTableModel.removeRow(0)
}
var pi = 0
while (pi < planets.length) {
val p = planets(pi)
kundaliTableModel.addRow(
Array[Object](
p._1,
kundaliDegreeText(p._2),
kundaliSignName(p._2),
Integer.valueOf(kundaliHouseFromLongitude(p._2, lagna)),
kundaliNakshatraName(p._2),
Integer.valueOf(kundaliNavamsaNumber(p._2))
)
)
pi += 1
}
val ns = new StringBuilder
ns.append("NAVAMSA DETAIL TABLE\n")
ns.append("====================\n\n")
ns.append("Navamsa number is derived from each planet's position within its 30-degree sign.\n")
ns.append("This is a traditional Vedic chart marker, not a scientific measurement.\n\n")
pi = 0
while (pi < planets.length) {
val p = planets(pi)
ns.append(
String.format(
"%-9s Sign=%-18s Navamsa=%d\n",
p._1,
kundaliSignName(p._2),
kundaliNavamsaNumber(p._2)
)
)
pi += 1
}
kundaliNavamsaArea.setText(ns.toString)
kundaliNavamsaArea.setCaretPosition(0)
val tr = new StringBuilder
tr.append("TRADITIONAL CHART SUMMARY\n")
tr.append("=========================\n\n")
tr.append("Lagna Rashi: ").append(kundaliSignName(lagna)).append("\n")
tr.append("Moon Rashi: ").append(kundaliSignName(moon)).append("\n")
tr.append("Moon Nakshatra: ").append(kundaliNakshatraName(moon)).append("\n")
tr.append("Nakshatra Pada: ").append(kundaliNakshatraPada(moon)).append("\n\n")
tr.append("Planet-to-house placements shown in the PLANETS tab are derived\n")
tr.append("from the simplified ascendant and sidereal longitudes used by\n")
tr.append("this module. Interpretations remain traditional/cultural.\n")
kundaliTraditionalArea.setText(tr.toString)
kundaliTraditionalArea.setCaretPosition(0)
kundaliKarmicArea.setText(
kundaliKarmicText(
lagna,
moon,
planets(7)._2,
planets(8)._2
)
)
kundaliKarmicArea.setCaretPosition(0)
try {
val dps = kundaliBirthDate.getText.trim.split("/")
if (dps.length == 3) {
kundaliTimingArea.setText(
kundaliDashaText(
moon,
dps(2).toInt,
dps(1).toInt,
dps(0).toInt
)
)
} else {
kundaliTimingArea.setText(
"Enter a valid date to build the traditional dasha timing view."
)
}
} catch {
case _: Throwable =>
kundaliTimingArea.setText(
"The timing view could not be generated from the current date input."
)
}
kundaliTimingArea.setCaretPosition(0)
kundaliStatus.setText(
"CALCULATED - " + kundaliSignName(moon)
)
kundaliStatus.setForeground(
new Color(150, 225, 170)
)
kundaliChartCanvas.repaint()
kundaliDetailsTabs.repaint()
}
// --------------------------------------------------------
// KUNDALI CALCULATE EVENT
// --------------------------------------------------------
kundaliCalculateButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
try {
val first = kundaliFirstName.getText.trim
val last = kundaliSurname.getText.trim
val date = kundaliBirthDate.getText.trim
val time = kundaliBirthTime.getText.trim
val place = kundaliBirthPlace.getText.trim
val lat = kundaliLatitude.getText.trim.toDouble
val lon = kundaliLongitude.getText.trim.toDouble
val tz = kundaliTimezone.getText.trim.toDouble
if (
first.isEmpty ||
last.isEmpty ||
date.isEmpty ||
time.isEmpty ||
place.isEmpty
) {
showWarning(
frame,
"KUNDALI PURAN",
"Please fill all birth details."
)
} else {
val dp = date.split("/")
val tp = time.split(":")
if (
dp.length != 3 ||
(tp.length != 2 && tp.length != 3)
) {
showWarning(
frame,
"INVALID FORMAT",
"Use Date: DD/MM/YYYY\nUse Time: HH:MM or HH:MM:SS"
)
} else {
val day = dp(0).toInt
val month = dp(1).toInt
val year = dp(2).toInt
val hour = tp(0).toInt
val minute = tp(1).toInt
val second =
if (tp.length == 3) tp(2).toInt else 0
if (
day < 1 || day > 31 ||
month < 1 || month > 12 ||
hour < 0 || hour > 23 ||
minute < 0 || minute > 59 ||
second < 0 || second > 59 ||
lat < -90.0 || lat > 90.0 ||
lon < -180.0 || lon > 180.0 ||
tz < -14.0 || tz > 14.0
) {
showWarning(
frame,
"INVALID DETAILS",
"Please check date, time, latitude, longitude and timezone."
)
} else {
kundaliJD =
kundaliJulianDay(
year,
month,
day,
hour,
minute,
second,
tz
)
kundaliCalculated = true
refreshKundaliView()
recordActivity(
"Calculated Kundali Puran chart"
)
}
}
}
} catch {
case _: NumberFormatException =>
showWarning(
frame,
"KUNDALI INPUT ERROR",
"Please enter valid numeric values for latitude, longitude and timezone."
)
case _: Throwable =>
showWarning(
frame,
"KUNDALI CALCULATION ERROR",
"Please check all birth details and formats."
)
}
}
}
)
kundaliSampleButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
kundaliFirstName.setText("Sample")
kundaliSurname.setText("User")
kundaliBirthDate.setText("15/08/2000")
kundaliBirthTime.setText("12:00:00")
kundaliBirthPlace.setText("Pune")
kundaliLatitude.setText("18.5204")
kundaliLongitude.setText("73.8567")
kundaliTimezone.setText("5.5")
kundaliStatus.setText("SAMPLE LOADED")
kundaliStatus.setForeground(
new Color(255, 215, 110)
)
}
}
)
kundaliClearButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
kundaliFirstName.setText("")
kundaliSurname.setText("")
kundaliBirthDate.setText("")
kundaliBirthTime.setText("")
kundaliBirthPlace.setText("")
kundaliLatitude.setText("")
kundaliLongitude.setText("")
kundaliTimezone.setText("5.5")
kundaliJD = 0.0
kundaliCalculated = false
kundaliLastReport = ""
while (kundaliTableModel.getRowCount > 0) {
kundaliTableModel.removeRow(0)
}
refreshKundaliView()
}
}
)
kundaliCopyButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (kundaliLastReport.trim.isEmpty) {
showWarning(
frame,
"KUNDALI PURAN",
"Calculate the chart before copying the report."
)
} else {
val clip =
Toolkit.getDefaultToolkit.getSystemClipboard
clip.setContents(
new StringSelection(kundaliLastReport),
null
)
recordActivity("Copied Kundali Puran report")
showInfo(
frame,
"KUNDALI PURAN",
"Report copied to clipboard."
)
}
}
}
)
kundaliExportButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (kundaliLastReport.trim.isEmpty) {
showWarning(
frame,
"KUNDALI PURAN",
"Calculate the chart before exporting."
)
} else {
val chooser =
new JFileChooser()
chooser.setDialogTitle(
"Export Kundali Report"
)
if (
chooser.showSaveDialog(frame) ==
JFileChooser.APPROVE_OPTION
) {
val file = chooser.getSelectedFile
val out =
new PrintWriter(
new OutputStreamWriter(
new FileOutputStream(file),
"UTF-8"
)
)
try {
out.print(kundaliLastReport)
} finally {
out.close()
}
showInfo(
frame,
"KUNDALI PURAN",
"Report exported successfully."
)
recordActivity("Exported Kundali Puran report")
}
}
}
}
)
kundaliFullPackButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (kundaliLastReport.trim.isEmpty) {
showWarning(
frame,
"KUNDALI PURAN",
"Calculate the chart before exporting the full pack."
)
} else {
val chooser = new JFileChooser()
chooser.setDialogTitle(
"Export Full Kundali Pack"
)
if (chooser.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
val base = chooser.getSelectedFile
val out = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream(base),
"UTF-8"
)
)
try {
out.print(kundaliLastReport)
out.print("\n\n")
out.print("================ KARMIC / GROWTH ================\n\n")
out.print(kundaliKarmicArea.getText)
out.print("\n\n================ DASHA / TIMING ================\n\n")
out.print(kundaliTimingArea.getText)
} finally {
out.close()
}
showInfo(
frame,
"KUNDALI PURAN",
"Full Kundali Pack exported successfully."
)
recordActivity(
"Exported full Kundali Puran pack"
)
}
}
}
}
)
kundaliAnimationButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
kundaliGlow = !kundaliGlow
kundaliAnimationButton.setText(
if (kundaliGlow) "CHART GLOW: ON" else "CHART GLOW: OFF"
)
kundaliChartCanvas.repaint()
}
}
)
kundaliDegreesButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
kundaliShowDegrees = !kundaliShowDegrees
kundaliDegreesButton.setText(
if (kundaliShowDegrees) "DETAILS: ON" else "DETAILS: BASIC"
)
kundaliChartCanvas.repaint()
}
}
)
val kundaliAnimationTimer =
new javax.swing.Timer(
55,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
kundaliAnimFrame =
(kundaliAnimFrame + 1) % 100000
kundaliChartCanvas.repaint()
if (kundaliAnimFrame % 10 == 0) {
kundaliHeader.repaint()
}
}
}
)
kundaliAnimationTimer.setCoalesce(true)
kundaliAnimationTimer.start()
// --------------------------------------------------------
// NAVIGATION BUTTONS BETWEEN MODULES
// --------------------------------------------------------
airWaterKundaliButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
tabs.setSelectedComponent(
kundaliPanel
)
recordActivity(
"Opened Kundali Puran from Air-to-Water"
)
}
}
)
kundaliBackButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
tabs.setSelectedComponent(
airWaterPanel
)
recordActivity(
"Returned to Air-to-Water from Kundali Puran"
)
}
}
)
// --------------------------------------------------------
// FINAL KUNDALI INITIAL STATE
// --------------------------------------------------------
refreshKundaliView()
// ========================================================
// NORMAL TABS
// ========================================================
tabs.addTab(
"DASHBOARD",
dashboardPanel
)
tabs.addTab(
"AIR TO WATER REAL 3D",
airWaterPanel
)
tabs.addTab(
"KUNDALI PURAN",
kundaliPanel
)
tabs.addTab(
"AI DOCTOR",
doctorPanel
)
tabs.addTab(
"CLASSROOM",
classroomPanel
)
tabs.addTab(
"ASK TEACHER",
teacherPanel
)
tabs.addTab(
"CODE LAB",
codeLabPanel
)
tabs.addTab(
"ERROR FIX",
errorPanel
)
tabs.addTab(
"NOTES",
notesPanel
)
tabs.addTab(
"TASKS",
tasksPanel
)
tabs.addTab(
"QUIZ",
quizPanel
)
tabs.addTab(
"IMAGE STUDIO",
imagePanel
)
tabs.addTab(
"PROMPT LAB",
promptLabPanel
)
tabs.addTab(
"PROJECTS",
projectsPanel
)
tabs.addTab(
"UTILITIES",
utilitiesPanel
)
tabs.addTab(
"FEATURE LIBRARY",
libraryPanel
)
tabs.addTab(
"HISTORY",
historyPanel
)
tabs.addTab(
"SETTINGS",
settingsPanel
)
// ========================================================
// MENU
// ========================================================
val menuBar =
new JMenuBar()
val fileMenu =
new JMenu(
"File"
)
val historyMenuItem =
new JMenuItem(
"Open History"
)
val masterMenuItem =
new JMenuItem(
"MASTER ACCESS"
)
val exitMenuItem =
new JMenuItem(
"Exit"
)
historyMenuItem.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
tabs.setSelectedComponent(
historyPanel
)
refreshHistoryView()
}
}
)
masterMenuItem.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
unlockMasterPrompt()
}
}
)
exitMenuItem.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
frame.dispose()
}
}
)
fileMenu.add(
historyMenuItem
)
fileMenu.addSeparator()
fileMenu.add(
masterMenuItem
)
fileMenu.addSeparator()
fileMenu.add(
exitMenuItem
)
val helpMenu =
new JMenu(
"Help"
)
val aboutItem =
new JMenuItem(
"About"
)
aboutItem.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
showInfo(
frame,
"ABOUT",
"ULTRA SCALA SWING AI LEARNING STUDIO\n\n" +
"MASTER PROMPT enabled.\n" +
"No UID field.\n" +
"No NAME field.\n" +
"Only IDEA is required.\n\n" +
"Enhanced 3D-oriented prompt generation enabled."
)
}
}
)
helpMenu.add(
aboutItem
)
menuBar.add(
fileMenu
)
menuBar.add(
helpMenu
)
frame.setJMenuBar(
menuBar
)
// ========================================================
// KEYBOARD SHORTCUT
// ========================================================
val rootPane =
frame.getRootPane
val inputMap =
rootPane.getInputMap(
JComponent.WHEN_IN_FOCUSED_WINDOW
)
val actionMap =
rootPane.getActionMap
inputMap.put(
KeyStroke.getKeyStroke(
KeyEvent.VK_H,
InputEvent.CTRL_DOWN_MASK
),
"openHistory"
)
actionMap.put(
"openHistory",
new AbstractAction {
override def actionPerformed(
e: ActionEvent
): Unit = {
tabs.setSelectedComponent(
historyPanel
)
refreshHistoryView()
}
}
)
inputMap.put(
KeyStroke.getKeyStroke(
KeyEvent.VK_F1,
0
),
"showAbout"
)
actionMap.put(
"showAbout",
new AbstractAction {
override def actionPerformed(
e: ActionEvent
): Unit = {
showInfo(
frame,
"HELP",
"Ctrl+H = History\nF1 = Help\n\nMASTER ACCESS = Protected MASTER PROMPT"
)
}
}
)
// ========================================================
// START
// ========================================================
recordActivity(
"Studio opened"
)
refreshHistoryView()
frame.setContentPane(
tabs
)
frame.setVisible(
true
)
frame.addWindowListener(
new WindowAdapter {
override def windowClosed(
e: WindowEvent
): Unit = {
if (doctorTimer != null)
doctorTimer.stop()
}
}
)
}
// ==========================================================
// SAFE STARTUP
// ==========================================================
try {
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
try {
openLogin()
} catch {
case ex: Throwable =>
JOptionPane.showMessageDialog(
null,
ex.toString,
"Startup Error",
JOptionPane.ERROR_MESSAGE
)
}
}
}
)
} catch {
case ex: Throwable =>
JOptionPane.showMessageDialog(
null,
ex.toString,
"Startup Error",
JOptionPane.ERROR_MESSAGE
)
}