Skip to content

Latest commit

 

History

History
367 lines (306 loc) · 12.5 KB

File metadata and controls

367 lines (306 loc) · 12.5 KB

Implementation Notes and Ideas.

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
    }
Loading

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 $S$ is the scale used to define how far CartesianPoints are from each other. $c_{xy}$ is the cartesian point coordinates, $s_{xy}$ is the screen coordinates, and $f_{height} \times f_{width}$ is the frame height and width

$$ s_x = S \cdot c_x + \frac{f_{width}}2 $$

$$ s_y = \frac{f_{height}}2 - S \cdot c_y $$

---
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
    }
Loading

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
    
    }
Loading

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.

Add README commit

Commit

Added the basic readme

29 Aug

Add Documentation

Commit

Started documentation on the core classes and deleted the Engine and Frame class as they served no use.

Event Management

Commit

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
Loading

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"]
Loading

As Shown in the diagram, the main expected Event Propagation is:

flowchart LR
    GPoint <--> GLine <--> GTri
Loading

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.

30 Aug

Events

This adds the ObjectType enum shown in the above diagrams.

19 Sept

Events and Stuff

Commit

Implements events into GObjects and adds registeredNodes(), registeredParents() and detachAll() to EventEmitter.

Also marks the registered HashMap as protected.

GUI

Commit

Begin implementing the Debug GUI with a basic draw and clear button.