Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ import (
"github.com/socialgorithm/elon-server/domain"
)

var inputs = make(chan domain.CarControlState)
var carControlChannel = make(chan domain.CarControlState)
var simulationControlChannel = make(chan int)

func update(connection *websocket.Conn) {
for {
select {
case carControlState := <-inputs:
case carControlState := <-carControlChannel:
message := fmt.Sprintf("input %f %f", carControlState.Steering, carControlState.Throttle)
connection.WriteMessage(websocket.TextMessage, []byte(message))
case simulationControl := <-simulationControlChannel:
message := fmt.Sprintf("control %d", simulationControl)
fmt.Println(message)
connection.WriteMessage(websocket.TextMessage, []byte(message))
}
}
}
Expand All @@ -39,9 +44,9 @@ func main() {
go update(connection)

if test {
clientrenderer.Manual(inputs)
clientrenderer.Manual(carControlChannel, simulationControlChannel)
} else {
// clientrenderer.ExternalProcess()
// externalClient.Process()
}

log.Println("Closing connection")
Expand Down
12 changes: 10 additions & 2 deletions clientrenderer/manual.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ import (
"time"

"github.com/socialgorithm/elon-server/domain"
"github.com/socialgorithm/elon-server/simulator"

"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
"golang.org/x/image/colornames"
)

var inputs chan domain.CarControlState
var simulationControl chan int

func run() {
cfg := pixelgl.WindowConfig{
Title: "Elon Manual Driver - Socialgorithm",
Bounds: pixel.R(0, 0, 400, 200),
Bounds: pixel.R(0, 0, 300, 200),
VSync: true,
}
win, err := pixelgl.NewWindow(cfg)
Expand Down Expand Up @@ -50,6 +52,11 @@ func run() {
throttle = 1
}

// Control sequences
if win.Pressed(pixelgl.KeyR) {
simulationControl <- simulator.SimulationRestart
}

carControlState := domain.CarControlState{
Steering: float64(steering),
Throttle: float64(throttle),
Expand All @@ -72,7 +79,8 @@ func run() {
}

// Manual initiates the render loop
func Manual(_inputs chan domain.CarControlState) {
func Manual(_inputs chan domain.CarControlState, _simulationControl chan int) {
inputs = _inputs
simulationControl = _simulationControl
pixelgl.Run(run)
}
19 changes: 16 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"flag"
"fmt"
"log"
"net/http"
"strconv"
Expand All @@ -16,14 +17,16 @@ import (

var upgrader = websocket.Upgrader{}
var simulation *simulator.Simulation
var isTest = false

func main() {
var port = flag.String("port", "8080", "the port number to run on")
var test = flag.Bool("test", true, "whether the server should run in test mode")
isTest = *test
simulation = simulator.CreateSimulation(1)

if *test {
go simulation.Start(*test)
if isTest {
go simulation.Start(isTest)
}

log.Printf("Starting Elon Server on localhost:%s", *port)
Expand Down Expand Up @@ -53,7 +56,7 @@ func connectionHandler(w http.ResponseWriter, r *http.Request) {

switch message[0] {
case "start":
go simulation.Start(false)
go simulation.Start(isTest)
break
case "input":
steering, _ := strconv.ParseFloat(message[1], 64)
Expand All @@ -64,6 +67,16 @@ func connectionHandler(w http.ResponseWriter, r *http.Request) {
Throttle: throttle,
}
go simulation.Input(0, carControlState)
break
case "control":
signal, err := strconv.Atoi(message[1])
if err != nil {
fmt.Printf("Received invalid signal %s\n", message[1])
break
}
if signal == simulator.SimulationRestart {
simulation.Restart()
}
}
}
}
12 changes: 9 additions & 3 deletions render/constants.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package render

import "golang.org/x/image/colornames"
import (
"math"

"golang.org/x/image/colornames"
)

const lineThickness = 4
const segmentLength = 15
const carWidth = 15
const carLength = 30
const wheelOffset = 5
const wheelLength = 5
const wheelOffset = carLength / 7
const wheelLength = carLength / 4
const wheelWidth = 3
const maxWheelSteering = math.Pi / 4
const zoom = 3.0

var bgColor = colornames.Green
var roadColor = colornames.Gray
Expand Down
109 changes: 64 additions & 45 deletions render/draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,87 +60,106 @@ func drawStart(draw *imdraw.IMDraw, track domain.Track) {
draw.Line(5)
}

func renderCar(carState domain.CarState) *imdraw.IMDraw {
func renderCar(car domain.Car) *imdraw.IMDraw {
carRender := imdraw.New(nil)

// Prepare some vectors
posVector := pixel.V(carState.Position.X, carState.Position.Y)
dirVector := pixel.V(carState.Direction.X, carState.Direction.Y)
sensorUnitVector := pixel.Unit(-math.Pi / 2)
posVector := pixel.V(car.CarState.Position.X, car.CarState.Position.Y)
dirVector := pixel.V(car.CarState.Direction.X, car.CarState.Direction.Y)

// Rotation matrix for the car rendering
rotation := pixel.IM.Rotated(posVector, dirVector.Angle()+halfPi)
// Set coordinate system to the car center and rotation
rotationAngle := dirVector.Angle() + halfPi
rotation := pixel.IM.Moved(posVector).Rotated(posVector, rotationAngle)
carRender.SetMatrix(rotation)

// Vectors adjusted to new coordinate system
carCenter := pixel.V(-carWidth*.5, -carLength*.5)

// Sensor vectors
sensorUnitVector := pixel.Unit(math.Pi / 2) // unit vector pointing forward from the car
sensorCenter := carCenter.Add(pixel.V(carWidth*.5, 0)) // middle of the front of the car

// render car fill
if carState.Crashed != true {
if car.CarState.Crashed != true {
carRender.Color = carColor
} else {
carRender.Color = carCrashedColor
}

carRender.Push(
pixel.V(carState.Position.X-carWidth*.5, carState.Position.Y-carLength*.5),
pixel.V(carState.Position.X+carWidth*.5, carState.Position.Y+carLength*.5),
carCenter,
pixel.V(carWidth*.5, carLength*.5),
)
carRender.Rectangle(0)

// render car outline
carRender.Color = colornames.Black
carRender.Push(
pixel.V(carState.Position.X-carWidth*.5, carState.Position.Y-carLength*.5),
pixel.V(carState.Position.X+carWidth*.5, carState.Position.Y+carLength*.5),
carCenter,
pixel.V(carWidth*.5, carLength*.5),
)
carRender.Rectangle(1)

// render car middle point
carRender.Color = colornames.Yellow
carRender.Push(
pixel.V(carState.Position.X, carState.Position.Y),
)
carRender.Push(sensorCenter)
carRender.Circle(2, 0)

// render sensors
carRender.Color = colornames.Orange
for i := 0; i < len(carState.Sensors); i++ {
sensor := carState.Sensors[i]
sensorVector := sensorUnitVector.Scaled(sensor.Distance).Rotated(sensor.Angle + math.Pi)
for i := 0; i < len(car.CarState.Sensors); i++ {
sensor := car.CarState.Sensors[i]
sensorVector := sensorUnitVector.Scaled(sensor.Distance).Rotated(sensor.Angle)
carRender.Push(
pixel.V(carState.Position.X, carState.Position.Y),
pixel.V(carState.Position.X+sensorVector.X, carState.Position.Y+sensorVector.Y),
sensorCenter,
sensorCenter.Add(sensorVector),
)
carRender.Line(1)
}

// render wheels
carRender.Color = colornames.Black
// top left
carRender.Push(
pixel.V(carState.Position.X-carWidth*.5, carState.Position.Y-carLength*.5+wheelOffset),
pixel.V(carState.Position.X-carWidth*.5, carState.Position.Y-carLength*.5+wheelOffset+wheelLength),
)
carRender.Line(wheelWidth)
// top right
carRender.Push(
pixel.V(carState.Position.X+carWidth*.5, carState.Position.Y-carLength*.5+wheelOffset),
pixel.V(carState.Position.X+carWidth*.5, carState.Position.Y-carLength*.5+wheelOffset+wheelLength),
)
carRender.Line(wheelWidth)
// bottom left
carRender.Push(
pixel.V(carState.Position.X-carWidth*.5, carState.Position.Y+carLength*.5-wheelOffset),
pixel.V(carState.Position.X-carWidth*.5, carState.Position.Y+carLength*.5-wheelOffset-wheelLength),
)
carRender.Line(wheelWidth)
// bottom right
carRender.Push(
pixel.V(carState.Position.X+carWidth*.5, carState.Position.Y+carLength*.5-wheelOffset),
pixel.V(carState.Position.X+carWidth*.5, carState.Position.Y+carLength*.5-wheelOffset-wheelLength),
)
carRender.Line(wheelWidth)
renderWheels(car, rotation, rotationAngle, carRender)

return carRender
}

func renderWheels(car domain.Car, initialMatrix pixel.Matrix, rotationAngle float64, carRender *imdraw.IMDraw) {
carRender.Color = colornames.Black

wheelPositions := [4][3]float64{
// carWidth, carLength, wheelOffset
[3]float64{-1, -1, +1}, // top left
[3]float64{+1, -1, +1}, // top right
[3]float64{-1, +1, -1}, // bottom left
[3]float64{+1, +1, -1}, // bottom right
}

carPosition := pixel.V(car.CarState.Position.X, car.CarState.Position.Y)

carRender.Color = colornames.Black

for i := 0; i < len(wheelPositions); i++ {
wheelData := wheelPositions[i]
// prepare all vectors
wheelCenter := pixel.V(
wheelData[0]*carWidth*.5,
wheelData[1]*carLength*.5+wheelData[2]*wheelOffset+wheelData[2]*wheelLength/2,
).Add(carPosition)
lengthVec := pixel.V(0, wheelLength/2)
wheelMatrix := pixel.IM.Moved(wheelCenter).Rotated(carPosition, rotationAngle)
adjustedWheelCenter := pixel.ZV
// if i < 2 {
// // front 2 wheels - ugly, whether the wheels turn should be a param somewhere
// wheelMatrix = wheelMatrix.Rotated(wheelCenter, maxWheelSteering)
// }
carRender.SetMatrix(wheelMatrix)
carRender.Push(
adjustedWheelCenter.Add(lengthVec),
adjustedWheelCenter.Sub(lengthVec),
)
carRender.Line(wheelWidth)
}
}

func drawLine(draw *imdraw.IMDraw, points []domain.Position, color color.RGBA, thickness float64) {
for i := 0; i < len(points)-1; i++ {
pointA := points[i]
Expand Down
10 changes: 9 additions & 1 deletion render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,21 @@ func run() {
for !win.Closed() {
win.Clear(bgColor)

// follow top car with camera and zoom
camPos := pixel.V(
cars[0].CarState.Position.X,
cars[0].CarState.Position.Y,
)
cam := pixel.IM.Scaled(camPos, zoom).Moved(win.Bounds().Center().Sub(camPos))
win.SetMatrix(cam)

// redraw the track
trackRender.Draw(win)

// update cars
if len(cars) > 0 {
for i := range cars {
carRender := renderCar(cars[i].CarState)
carRender := renderCar(cars[i])
carRender.Draw(win)
}
}
Expand Down
5 changes: 5 additions & 0 deletions simulator/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ type Simulation struct {
CarsChannel chan []domain.Car
Engine physics.Engine
}

const (
// SimulationRestart Control signal to restart a simulation
SimulationRestart int = 0
)
Loading