This file is meant to document my progress within this PAT.
After unwrapping and revealing the entire engine, it is wrapped around Vector3, ScreenPoint and CartesianPoint
Where BasePoint (should be) an abstract class that holds an X and Y value, which ScreenPoint and CartesianPoint extend off.
---
title: Base Coordinate classes
---
classDiagram
note for ScreenPoint "ScreenPoint defines a point using the screen's X and Y coordinates (0, 0 is the top left)"
note for BasePoint "The Base class for any other class which has only an X and a Y value."
note for CartesianPoint "Defines a point using the Cartesian Coordinates (0, 0 is the centre of the screen)"
ScreenPoint --|> BasePoint~T extends Number~: Extends from (Type = Integer)
CartesianPoint --|> BasePoint~T extends Number~: Extends from (Type = Double)
class BasePoint~T extends Number~ {
+y: T
+x: T
Constructor(xcord: T, ycord: T)
+areCollinear(p1: CartesianPoint, p2: CartesianPoint, p3: CartesianPoint)) boolean$
}
CartesianPoint ..> ScreenPoint: converts to
ScreenPoint ..> CartesianPoint: can convert to (unused)
Vector3 ..> CartesianPoint: converts to
class ScreenPoint {
Constructor(X: integer, Y: integer)
+toPoint(sceneManager: Renderer) CartesianPoint
}
class CartesianPoint {
-EPSILON: number = 0.01$
Constructor()
Constructor(X: number, Y: number)
+isNotEmpty() boolean
+distanceTo(other: CartesianPoint) number
+distanceSquaredTo(other: CartesianPoint) number
+toScreen(sceneManager: Renderer) ScreenPoint
+hashCode() integer
+equals(obj: Object) boolean
+toString() string
}
class Dimension {
+width: integer
+height: integer
Constructor(width: integer, height: integer)
+toString() string
}
class Vector3 {
-X: number
-Y: number
-Z: number
Constructor()
Constructor(x: number, y: number, z: number)
+isNotEmpty() boolean
+getX() number
+getY() number
+getZ() number
+normalize(v : Vector3) Vector3$
+toPoint(cam : Camera) CartesianPoint
+equals(o : Object) boolean
+toString() string
+hashCode() int
+distanceSquaredTo(mousePos : CartesianPoint) number
+add(v: Vector3) Vector3
+magnitude() number
+mult(scalar: number) Vector3
+mult(B: Vector3) Vector3
+div(scalar: number) Vector3
+cross(B: Vector3) Vector3
+sub(B: Vector3) Vector3
+distance(B: Vector3) Vector3
+random(low: Vector3, high: Vector3) Vector3$
+reduce(vectors: ArrayLisr~Vector3~, reducer: BiFunction~Vector3, Vector3, Vector3~) Vector3
}
The core of 3D Graphics is projecting a 3D object onto a 2D screen. How Vector3 becomes a CartesianPoint is not simple.
(See Wikipedia's Article on 3D to 2D Projection)
The method this engine uses is Perspective Projection, where objects further away from the camera appear to be smaller When in reality they could be the exact same size.
This exact formula from Wikipedia is used for this conversion.
After converting it to a CartesianPoint, we need to convert it to Swing's coordinates. as a Cartesian Point's coordinates originate at the centre of the screen and
Swing's is the top left corner of the frame. This is done using the following formula (ScreenPoint represents the coordinates Swing uses.)
where
---
title: Orchestrator Classes
---
classDiagram
Main ..> Executor : Instantiates
Executor ..> Renderer
Main --> Renderer : Instantiates and passes into Executor
Main --> JPanel : Extends from
Renderer --o GObject : Holds a list of
note for Executor "A class used to draw objects once."
note for Renderer "The class that manages how things get drawn and what gets drawn"
note for Main "The Main class."
class Executor {
-sceneManager: Renderer
Constructor(r: Renderer)
+run()
}
class Main {
+dimension: Dimension$
+jBundler: JBundler$
#paintComponent(g: Graphics)
+main(args: String[])$
}
class Renderer {
-graphics: Graphics2D
-screenSize: Dimension
-objectQueue: ArrayDeque~GObject~
+SCALE: number
+cameraOffset: Point
Constructor(g: Graphics, dim: Dimension)
+line(A: CartesianPoint, B: CartesianPoint) GLine
+point(point: CartesianPoint) GPoint
+axis()
+clear()
+findOrCreatePoint(CartesianPoint) GPoint
+getGraphics() Graphics2D
}
Which manage everything from rendering, redrawing. Then the shapes themselves
---
title: Core 2D Classes
---
classDiagram
note for GObject "Base class for 2D shapes."
note for GPoint "A single point. When drawn is a circle with radius 2"
note for GLine "A line. When drawn, it's a line segment between 2 points."
note for GTri "A Triangle, when drawn it fills a polygon using 3 specified points"
GPoint --|> GObject
GLine --|> GObject
GTri --|> GObject
class GObject {
-pivot : CartesianPoint
-Id: string
Constructor()
+getPivot() CartesianPoint
+setPivot(pivot : CartesianPoint)
+getId() string
}
class GPoint {
-drawPoint(sceneManager : Renderer, cartesianPoint : CartesianPoint)
Constructor(sceneManager : Renderer, cartesianPoint : CartesianPoint)
+update(sceneManager : Renderer, cartesianPoint : CartesianPoint)
+equals(obj : Object) boolean
}
class GLine {
-startPoint: CartesianPoint
-endPoint : CartesianPoint
Constructor(sceneManager : Renderer, startPoint : GPoint, endPoint : GPoint)
+setEndPoint(end : CartesianPoint)
+setStartPoint(start : CartesianPoint)
+getEndPoint() CartesianPoint
+getStartPoint() CartesianPoint
+length() number
}
class GTri {
-LegA : GLine
-LegB : GLine
-LegC : GLine
Constructor(sceneManager : Renderer, A : GPoint, B : GPoint, C : GPoint)
Constructor(sceneManager : Renderer, A : GLine, B : GLine, C : GLine)
+getLegA() GLine
+getLegB() GLine
+getLegC() GLine
+setLegA(GLine legA)
+setLegB(GLine legB)
+setLegC(GLine legC)
+area() number
}
And other classes which aren't of importance to the implementation such as Point3D which
holds only a public x, y and z value. And Dimension which holds a width and height.
Classes such as Arrays and Testing came as a result of trying to embed
jaiva (my custom programming language) into
this engine. (Which is where the JBundler instance comes from) They aren't used in
the main run of the engine and probably won't be until the engine is stable enough.
Therefore until then, No class diagrams will be shown for them.
Also the Frame class, came as I thought i will maybe later save stuff as frames from
the engine, but then i realized i'm biting more than I can chew, so I deleted it in later commits.
As it's never used, it won't be documented here either.
Another class implemented for the first time is the Events enum, which serves
no purpose in the current commit and therefore will not be part of the class diagrams
(as yet).
The core idea before making any GUI was to have a set of base shapes to be able to draw anytime.
Where Renderer holds the master-list of objects to draw anytime the frame needs
to be redrawn and Main handles the input and registers the classes. Executor is
nothing but debug class used to draw specific shapes at specific coordinates. It may
be removed when the engine is stable with proper GUI.
Added the basic readme
Started documentation on the core classes and deleted the
Engine and Frame class as they served no use.
Started work on Events.
How this was thought out was: When drawing, a point may update and a line or triangle needs to react to said update and update themselves. Or if a triangle gets deleted, the lines (nodes) need to delete themselves.
The main idea is that if an object, such as a GLine has 2 GPoints, those points
are nodes to the GLine, while the GLine is the parent to each GPoint
GPoint, GLine and GTri each have their own Event class which
extend EventBroadcast which other GObjects should listen for.
---
title: Event Classes
---
classDiagram
EventEmitter --* EventListener : Holds a list of EventListeners
EventEmitter ..> EventBroadcast : Creates a new EventBroadcast when broadcasting.
EventBroadcast --> EventEmitter : Holds the Event Originator
EventBroadcast --> Renderer : Holds the Renderer Instance
EventListener ..> EventType : To determine the type of Event that happened.
GObject --|> EventEmitter : Extends
GObject --|> EventListener : Implements
note for ObjectType "An enum defining whether the object that we are listening to is a NODE or PARENT."
class EventEmitter {
<<abstract>>
-registered: HashMap~ObjectType, ArrayList~EventListener~~
+attach(event: EventListener, type: ObjectType)
+detach(event: EventListener, type: ObjectType)
+broadcast(type: EventType, type : ObjectType, properties: EventBroadcast)
}
class EventListener {
<<interface>>
+onEvent(event: EventType, properties: EventBroadcast)
}
class EventBroadcast {
<<abstract>>
+emitter: EventEmitter
+sceneManager: Renderer
Constructor(e: EventEmitter, r: Renderer)
}
class EventType {
<<enumeration>>
NODE_UPDATED
NODE_DELETED
PARENT_DELETED
PARENT_UPDATED
}
class ObjectType {
<<enumeration>>
NODE
PARENT
}
`GPoint.Event` --|> EventBroadcast
`GLine.Event` --|> EventBroadcast
`GTri.Event` --|> EventBroadcast
How The Event happens and propagates is the following:
Assuming you have the GTri tri with 3 lines (GLines) sharing
3 GPoints respectively, such that:
A connects to B to form Line1, B connects to C to form Line2 and C connects to A to form Line3.
flowchart
User["User moves Point A"]
PA["Point A calls broadcast(EventType.NODE, properties) to parents"]
User --> PA
L1["Line 1 receives the event"]
L3["Line 3 receives the event"]
PA --> L1
PA --> L3
U1["Line 1 updates it's properties"]
U3["Line 3 updates it's properties"]
L1 --> U1
L3 --> U3
Tri["Sends Event to the main triangle"]
U1 --> Tri
U3 --> Tri
Tri --> C["Triangle updates and recalculates area, perimeter, etc"]
As Shown in the diagram, the main expected Event Propagation is:
flowchart LR
GPoint <--> GLine <--> GTri
Where the only initiators of events, are the ends GPoint and GTri. A GLine being in the middle,
shouldn't be able to initiate events. They propagate events up and down, but never
start one.
This adds the ObjectType enum shown in the above diagrams.
Implements events into GObjects and adds registeredNodes(),
registeredParents() and detachAll() to EventEmitter.
Also marks the registered HashMap as protected.
Begin implementing the Debug GUI with a basic draw and clear button.