Skip to content
This repository was archived by the owner on Jul 3, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
f0d16ce
Added empty Script tuner module
TrentHouliston Oct 3, 2018
ef4c63d
Adding things
TrentHouliston Oct 6, 2018
c178ae1
I don't know how grids work!
TrentHouliston Oct 6, 2018
459b8d9
Initial attempt at line editor
JosephusPaye Oct 22, 2018
e1f8690
Rename ScriptData => Frame
JosephusPaye Oct 22, 2018
67e9ec7
Merge branch 'master' into houliston/script_tuner
JosephusPaye Oct 22, 2018
c298799
Minor style and data changes
JosephusPaye Nov 27, 2018
d290d7d
Add a scrollable timeline ruler at the top
JosephusPaye Nov 28, 2018
5dfd750
WIP: Remove viewBox and draw using a basic scale with a cell size mul…
JosephusPaye Nov 28, 2018
445ae47
Add EditorViewModel with cell timeline width, height, scale, and cell…
JosephusPaye Nov 29, 2018
d6f55df
Make timeline and lines have the same length: the longest script
JosephusPaye Nov 29, 2018
eff78ae
Make points draggable
JosephusPaye Nov 29, 2018
962d2c9
Ensure servo.frames array is sorted for better drag performance
JosephusPaye Nov 29, 2018
85b267e
Use crosshair cursor over the graph
JosephusPaye Nov 29, 2018
1316713
Add play head
JosephusPaye Dec 6, 2018
fd151a8
Fix JSX comments
JosephusPaye Dec 6, 2018
3fa962b
[WIP] Add playback controls
JosephusPaye Dec 6, 2018
7545931
Style fix
JosephusPaye Dec 6, 2018
79a27e9
Refactor playback and pause/play functionality
JosephusPaye Dec 20, 2018
235859e
Merge branch 'master' into houliston/script_tuner
JosephusPaye Jan 8, 2019
86d8d98
[WIP] Add viewer
JosephusPaye Jan 9, 2019
62bbec3
Use binary-search-bounds library for finding frames to interpolate
JosephusPaye Jan 9, 2019
4b782b9
[WIP] Add robot visualiser
JosephusPaye Jan 9, 2019
6d57876
[WIP] Make viewer render edited servo angles
JosephusPaye Jan 16, 2019
d0b97c4
Big refactor! Update darwin_model and model_visualiser to use createT…
JosephusPaye Jan 22, 2019
7f6611f
Rename servos to match C++ code on the robots
JosephusPaye Jan 23, 2019
9865db9
Use ternary to simplify finding servo angle
JosephusPaye Jan 23, 2019
c2cee03
Allow drag only from left mouse button
JosephusPaye Jan 23, 2019
21a3381
Use binary search to find and insert new frame at position, instead o…
JosephusPaye Jan 23, 2019
5257279
Make timeline ruler slightly taller, add zoom
JosephusPaye Jan 23, 2019
adca3af
Add temporary sample salute script from robot
JosephusPaye Jan 23, 2019
73c4d0f
Rename Servo.name to Servo.id, to match scripts
JosephusPaye Jan 23, 2019
3141cee
Fix build
JosephusPaye Jan 23, 2019
73fdb28
Merge branch 'master' into houliston/script_tuner
JosephusPaye Jan 31, 2019
510405d
Add script proto files
JosephusPaye Jan 31, 2019
c2d5d6b
Fix Script.proto
JosephusPaye Jan 31, 2019
f144364
Rename Viewer to Preview
JosephusPaye Jan 31, 2019
56ab605
Add scripts explorer to layout
JosephusPaye Jan 31, 2019
284d715
Fix lint
JosephusPaye Feb 4, 2019
883bb90
Merge branch 'master' into houliston/script_tuner
JosephusPaye Feb 4, 2019
23d818d
Add robot selector
JosephusPaye Feb 4, 2019
888a033
Add loading icon component
JosephusPaye Feb 5, 2019
56e9683
Add editor blank state when no script is selected
JosephusPaye Feb 5, 2019
41546d5
Add loading icon to explorer, move sample script data to network
JosephusPaye Feb 5, 2019
b56bc2d
Merge branch 'master' into houliston/script_tuner
JosephusPaye Feb 7, 2019
1cbe481
Remove functions in render, fix lint
JosephusPaye Feb 12, 2019
4802c0b
Merge branch 'master' into houliston/script_tuner
JosephusPaye Feb 12, 2019
26023d3
Move sample script to simulator, request via network
JosephusPaye Feb 12, 2019
9b0bb80
Add more sample scripts, change all times in script tuner to be milli…
JosephusPaye Feb 12, 2019
c62978d
Remove some sample scripts
JosephusPaye Feb 12, 2019
4a25151
Minor visual changes
JosephusPaye Feb 12, 2019
e44ecf9
Restart playback when pressing play at the end of the timeline
JosephusPaye Feb 12, 2019
48b386b
Lint
JosephusPaye Feb 12, 2019
2e67158
Reset editor state when switching scripts
JosephusPaye Feb 12, 2019
dd3e100
Switch to callback refs for timeline and body scrolling
JosephusPaye Feb 13, 2019
8b8fea5
Allow Spacebar to toggle playback
JosephusPaye Feb 13, 2019
c6ad0cb
Changes
ysims Feb 18, 2019
789c29f
Wrap selected script in a custom DataViewModel to track changes, prom…
JosephusPaye Feb 18, 2019
98f70b1
Add `dropDirection` prop to RobotSelector, Select, and Dropdown
JosephusPaye Feb 18, 2019
b79765e
Add ability to select target robot and save edited script
JosephusPaye Feb 18, 2019
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@types/react-color": "^2.14.0",
"@types/react-dom": "^16.0.11",
"@types/react-hot-loader": "^4.1.0",
"@types/react-outside-click-handler": "^1.2.0",
"@types/react-router": "^4.4.3",
"@types/react-router-dom": "^4.3.1",
"@types/seedrandom": "^2.4.27",
Expand Down
2 changes: 2 additions & 0 deletions src/client/components/app/install.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { installChart } from '../chart/install'
import { installDashboard } from '../dashboard/install'
import { installLocalisation } from '../localisation/install'
import { withRobotSelectorMenuBar } from '../menu_bar/view'
import { installScriptTuner } from '../script_tuner/install'
import { installVision } from '../vision/install'
import { installVisualMesh } from '../visual_mesh/install'

Expand All @@ -23,6 +24,7 @@ export function installNav() {

installDashboard({ nav, appModel, nusightNetwork, menu })
installLocalisation({ nav, appModel, nusightNetwork, menu })
installScriptTuner({ nav, appModel, nusightNetwork, menu })
installChart({ nav, appModel, nusightNetwork, menu })
installVision({ nav, appModel, nusightNetwork, Menu: menu })
installVisualMesh({ nav, appModel, nusightNetwork, Menu: menu })
Expand Down
9 changes: 8 additions & 1 deletion src/client/components/dropdown/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

.dropdownMenu {
position: absolute;
top: 100%;
z-index: 1;
}

Expand All @@ -15,3 +14,11 @@
.dropdownMenuRight {
right: 0;
}

.dropdownMenuUp {
bottom: 100%;
}

.dropdownMenuDown {
top: 100%;
}
4 changes: 3 additions & 1 deletion src/client/components/dropdown/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ export interface DropdownProps {
isOpen: boolean
isFullwidth?: boolean
dropdownPosition?: 'left' | 'right'
dropDirection?: 'up' | 'down'
onRef?(dropdown: HTMLDivElement): void
onToggleClick?(event: MouseEvent<HTMLSpanElement>): void
}

export const Dropdown: StatelessComponent<DropdownProps> = (props: DropdownProps) => {
const fullwidth = props.isFullwidth ? style.dropdownMenuFullwidth : ''
const position = props.dropdownPosition === 'right' ? style.dropdownMenuRight : ''
const dropdownMenuClassName = classNames(style.dropdownMenu, fullwidth, position)
const direction = props.dropDirection === 'up' ? style.dropdownMenuUp : style.dropdownMenuDown
const dropdownMenuClassName = classNames(style.dropdownMenu, fullwidth, position, direction)

return (
<div className={classNames([style.dropdown, props.className])} ref={props.onRef}>
Expand Down
23 changes: 23 additions & 0 deletions src/client/components/script_tuner/balance/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.balance {
display: flex;
flex-direction: column;
max-height: 100%;
overflow: hidden;
}

.balanceHeader {
background-color: #777;
padding: 0 4px;
color: white;
font-size: 14px;
display: flex;
min-height: 24px;
align-items: center;
}

.balanceBody {
padding: 0 4px;
flex-grow: 1;
overflow: hidden;
position: relative;
}
22 changes: 22 additions & 0 deletions src/client/components/script_tuner/balance/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as classNames from 'classnames'
import { observer } from 'mobx-react'
import * as React from 'react'

import * as style from './style.css'

type BalanceProps = {
className?: string
}

@observer
export class Balance extends React.Component<BalanceProps> {
render() {
return <div className={classNames([this.props.className, style.balance])}>
<div className={style.balanceHeader}>
<div className={style.balanceTitle}>Balance</div>
</div>

<div className={style.balanceBody}></div>
</div>
}
}
109 changes: 109 additions & 0 deletions src/client/components/script_tuner/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { action, autorun, IReactionDisposer } from 'mobx'

import { RobotModel } from '../robot/model'

import { Script, ScriptTunerModel } from './model'
import { ScriptTunerNetwork } from './network'
import { createViewModel } from './utils'

interface ScriptTunerOpts {
network: ScriptTunerNetwork
model: ScriptTunerModel
}

export class ScriptTunerController {
network: ScriptTunerNetwork
model: ScriptTunerModel
stopPlaytimeAutorun?: IReactionDisposer

constructor(opts: ScriptTunerOpts) {
this.network = opts.network
this.model = opts.model

this.stopPlaytimeAutorun = autorun(() => {
if (this.model.playTime >= this.model.endTime) {
this.togglePlayback(false)
}
})

// TODO need to add an autorunner that if we are connected to the robot we need to send it update packets
}

static of(opts: ScriptTunerOpts) {
return new ScriptTunerController(opts)
}

@action
selectSourceRobot(robot: RobotModel) {
const isInitialSelect = this.model.sourceRobot === undefined
this.model.sourceRobot = robot

if (isInitialSelect) {
this.network.requestScripts(robot)
}

// Select the robot as target too
this.selectTargetRobot(robot)
}

@action
selectTargetRobot(robot: RobotModel) {
this.model.targetRobot = robot
}

@action
selectScript(script: Script) {
// Do nothing if the script is already selected
if (this.model.selectedScript && this.model.selectedScript.model === script) {
return
}

// Prompt to confirm switch if the current script has unsaved changes
if (this.model.selectedScript && this.model.selectedScript.isDirty) {
const discardChanges = confirm(`${this.model.selectedScript.data.path} has unsaved changes. Discard?`)

if (discardChanges) {
this.model.selectedScript.reset()
} else {
return
}
}

// Reset the editor state
this.model.isPlaying = false
this.model.currentTime = 0
this.model.previousTimelineLength = 0

// Select the script
this.model.selectedScript = createViewModel(script)
}

@action
saveScript() {
if (this.model.selectedScript && this.model.targetRobot) {
this.network.saveScript(this.model.targetRobot)
}
}

@action
setPlayTime(time: number) {
this.model.currentTime = Math.min(Math.max(time, this.model.startTime), this.model.endTime)
this.model.playStartedAt = Date.now()
}

@action
togglePlayback(isPlaying: boolean = !this.model.isPlaying) {
if (isPlaying) {
// Reset to start if we want to play but are at the end of the time
if (this.model.playTime >= this.model.endTime) {
this.model.currentTime = 0
}

this.model.playStartedAt = Date.now()
} else {
this.model.currentTime = this.model.playTime
}

this.model.isPlaying = isPlaying
}
}
23 changes: 23 additions & 0 deletions src/client/components/script_tuner/controls/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.controls {
display: flex;
flex-direction: column;
max-height: 100%;
overflow: hidden;
}

.controlsHeader {
background-color: #777;
padding: 0 4px;
color: white;
font-size: 14px;
display: flex;
min-height: 24px;
align-items: center;
}

.controlsBody {
padding: 0 4px;
flex-grow: 1;
overflow: hidden;
position: relative;
}
22 changes: 22 additions & 0 deletions src/client/components/script_tuner/controls/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as classNames from 'classnames'
import { observer } from 'mobx-react'
import * as React from 'react'

import * as style from './style.css'

type ControlsProps = {
className?: string
}

@observer
export class Controls extends React.Component<ControlsProps> {
render() {
return <div className={classNames([this.props.className, style.controls])}>
<div className={style.controlsHeader}>
<div className={style.controlsTitle}>Controls</div>
</div>

<div className={style.controlsBody}></div>
</div>
}
}
67 changes: 67 additions & 0 deletions src/client/components/script_tuner/editor/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { action, computed, observable } from 'mobx'
import { createTransformer } from 'mobx-utils'

import { ScriptTunerController } from '../controller'

import { EditorViewModel } from './view_model'

interface EditorControllerOpts {
viewModel: EditorViewModel,
controller: ScriptTunerController
}

export class EditorController {
viewModel: EditorViewModel
controller: ScriptTunerController

constructor(opts: EditorControllerOpts) {
this.viewModel = opts.viewModel
this.controller = opts.controller
}

static of = createTransformer((opts: EditorControllerOpts) => {
return new EditorController(opts)
})

setPlayTime = (time: number) => {
this.controller.setPlayTime(time)
}

play = () => {
if (this.viewModel.isPlaying) {
return
}

this.controller.togglePlayback(true)
}

pause = () => {
if (!this.viewModel.isPlaying) {
return
}

this.controller.togglePlayback(false)
}

togglePlayback = () => {
this.controller.togglePlayback()
}

jumpToStart = () => {
this.controller.setPlayTime(this.viewModel.startTime)
}

jumpToEnd = () => {
this.controller.setPlayTime(this.viewModel.endTime)
}

@action
zoomIn = () => {
this.viewModel.scaleX = Math.min(this.viewModel.scaleX + 1, 10)
}

@action
zoomOut = () => {
this.viewModel.scaleX = Math.max(this.viewModel.scaleX - 1, 1)
}
}
51 changes: 51 additions & 0 deletions src/client/components/script_tuner/editor/line/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as bounds from 'binary-search-bounds'
import { action } from 'mobx'

import { Frame, ScriptTunerModel, Servo } from '../../model'

export class LineEditorController {
constructor(private model: Servo) {
this.model = model
}

static of(model: Servo) {
return new LineEditorController(model)
}

@action
addFrame = (data: { time: number, angle: number }) => {
const frame = {
time: data.time,
angle: data.angle,
pGain: 0,
iGain: 0,
dGain: 0,
torque: 0,
}

const index = findNextIndexForTime(data.time, this.model.frames)

if (index > 0) {
this.model.frames.splice(index, 0, frame)
} else {
this.model.frames.unshift(frame)
}
}

@action
updateFrame = (frameIndex: number, data: { time: number, angle: number }) => {
this.model.frames[frameIndex].time = data.time
this.model.frames[frameIndex].angle = data.angle
}

@action
removeFrame = (frameIndex: number) => {
this.model.frames.splice(frameIndex, 1)
}
}

function findNextIndexForTime(time: number, frames: Frame[]) {
return bounds.gt(frames, frames[0], (frame: Frame) => {
return frame.time - time
})
}
Loading