├── .gitignore ├── 11ty.js ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── _site ├── _data │ └── encore.js ├── _includes │ └── layouts │ │ ├── base.njk │ │ └── page.njk ├── index.njk ├── test.njk ├── tutorials.njk └── tutorials │ ├── 1-balance.md │ ├── 2-robustness.md │ └── 3-fixed-obstacle.md ├── img ├── Hero 1_04-29-20.png ├── Hero 2_04-29-20.png ├── Packaging 1_04-29-20.png ├── background-dark.svg ├── background-light.svg ├── background-video.mp4 ├── balance-plate.png ├── control-arms.png ├── documents-home.png ├── fresh-video-cover.png ├── github-repo.png ├── icons │ ├── close.svg │ ├── doc.svg │ ├── download.svg │ ├── expand.svg │ ├── external.svg │ ├── github.svg │ ├── internal.svg │ ├── play.svg │ ├── tutorial.svg │ └── video.svg ├── servo-motors.png ├── tutorials-home.png ├── tutorials │ ├── 1 │ │ ├── bonsai-home.png │ │ ├── concept-graph-observablestate-highlight.png │ │ ├── concept-graph-simaction-highlight.png │ │ ├── moab-frame-of-reference.png │ │ ├── moab-photo.png │ │ ├── tutorial1-assessment.png │ │ ├── tutorial1-concept-graph-training-mode.png │ │ ├── tutorial1-create-brain.png │ │ ├── tutorial1-inkling-view.png │ │ ├── tutorial1-starting-assessment.png │ │ ├── tutorial1-starting-training.png │ │ ├── tutorial1-training-converged.png │ │ ├── tutorial1-training-started-perf.png │ │ ├── tutorial1-training-started.png │ │ ├── tutorial1-training-stopped.png │ │ ├── tutorial1-ui-sections.png │ │ ├── tutorial1-visualizer-and-sim-chart.png │ │ └── tutorial1-visualizer.png │ ├── 3 │ │ ├── assessment.png │ │ ├── bonsai-home.png │ │ ├── converged.png │ │ └── select-simulator.png │ ├── img01.png │ ├── img01.tif │ ├── img02.png │ ├── img03.png │ ├── img04.png │ ├── img05.png │ ├── img06.png │ ├── img07.png │ ├── img08.png │ ├── perf-chart-converged.png │ ├── tutorial2-converging-goal-sat.png │ └── tutorial2-select-sim.png └── viewer │ ├── camera-module.jpg │ ├── control-arms.jpg │ ├── m2.jpg │ ├── power-control-board.jpg │ ├── raspberry-pi-4.jpg │ └── servos.jpg ├── js ├── app.js ├── jquery-global.js └── sketchfab-viewer-1.7.1.js ├── package-lock.json ├── package.json ├── scss ├── app.css ├── app.scss └── buttons.scss └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/node,macos 2 | # Edit at https://www.gitignore.io/?templates=node,macos 3 | 4 | 5 | ### macOS ### 6 | # General 7 | .DS_Store 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | ### Node ### 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | lerna-debug.log* 24 | 25 | # Dependency directories 26 | node_modules/ 27 | jspm_packages/ 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional eslint cache 33 | .eslintcache 34 | 35 | # Optional REPL history 36 | .node_repl_history 37 | 38 | # Output of 'npm pack' 39 | *.tgz 40 | 41 | # Yarn Integrity file 42 | .yarn-integrity 43 | 44 | # dotenv environment variables file 45 | .env 46 | .env.test 47 | 48 | # Eleventy 49 | build 50 | 51 | # Webpack 52 | dist 53 | 54 | 55 | ### Windows ### 56 | # Windows thumbnail cache files 57 | Thumbs.db 58 | Thumbs.db:encryptable 59 | ehthumbs.db 60 | ehthumbs_vista.db 61 | 62 | # Dump file 63 | *.stackdump 64 | 65 | # Folder config file 66 | [Dd]esktop.ini 67 | 68 | # Recycle Bin used on file shares 69 | $RECYCLE.BIN/ 70 | 71 | # Windows Installer files 72 | *.cab 73 | *.msi 74 | *.msix 75 | *.msm 76 | *.msp 77 | 78 | # Windows shortcuts 79 | *.lnk 80 | 81 | # End of https://www.gitignore.io/api/windows 82 | -------------------------------------------------------------------------------- /11ty.js: -------------------------------------------------------------------------------- 1 | const pluginSyntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight"); 2 | const eleventyNavigationPlugin = require("@11ty/eleventy-navigation"); 3 | 4 | module.exports = function (eleventyConfig) { 5 | 6 | // 11ty uses folder DIST as it's output folder 7 | // encore reads from DIST and outputs to BUILD 8 | 9 | var payload = { 10 | pathPrefix: process.env.ELEVENTY_ENV === "development" ? "/" : "/moab", 11 | dir: { 12 | input: "_site", 13 | output: "dist" 14 | } 15 | }; 16 | 17 | eleventyConfig.addPlugin(pluginSyntaxHighlight); 18 | eleventyConfig.addPlugin(eleventyNavigationPlugin); 19 | eleventyConfig.addPassthroughCopy("build"); 20 | eleventyConfig.addPassthroughCopy("img"); 21 | eleventyConfig.setUseGitIgnore(false); 22 | 23 | eleventyConfig.addCollection("nav", function(collection) { 24 | return collection.getFilteredByTag("menu").sort((a, b) => a.data.order - b.data.order); 25 | }); 26 | 27 | eleventyConfig.addPairedNunjucksShortcode("expanding_content_block", function(content, data) { 28 | return ` 29 |
30 |
31 | 32 |
33 |
34 |
${data.title}
35 |
${data.summary}
36 |
37 |
38 | ${content} 39 |
40 |
41 |
42 |
`; 43 | }); 44 | 45 | eleventyConfig.addPairedNunjucksShortcode("fuji_block", function(content, data) { 46 | return ` 47 |
48 |
49 | 50 |
51 |
52 | 53 |
${content}
54 |
55 |
`; 56 | }); 57 | 58 | eleventyConfig.addNunjucksShortcode("link_content_block", function(data) { 59 | return ` 60 |
61 |
62 | 63 |
64 |
65 |
${data.title}
66 |
${data.summary}
67 |
68 |
`; 69 | }); 70 | 71 | eleventyConfig.addNunjucksShortcode("download_content_block", function(data) { 72 | return ` 73 |
74 | 75 |
76 |
${data.title}
77 | ${data.summary} 78 |
79 |
`; 80 | }); 81 | 82 | eleventyConfig.addPairedNunjucksShortcode("video_content_wrapper", function (content) { 83 | return ` 84 |
85 | ${content} 86 |
87 | ` 88 | }) 89 | 90 | eleventyConfig.addNunjucksShortcode("video_content_block", function(data) { 91 | var anchorBody = ''; 92 | if (data.image) { 93 | anchorBody = `${data.alt}`; 94 | } else { 95 | anchorBody = ''; 96 | } 97 | 98 | return ` 99 | 100 |
101 | ${anchorBody} 102 |
103 |
104 | ${data.title} 105 |
106 |
`; 107 | }); 108 | 109 | return payload; 110 | }; 111 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 BonsaiAI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Moab microsite 2 | 3 | Location for the Project Moab assets, tutorial, documentation and HTML 4 | content. 5 | 6 | ### Installation 7 | ```shell 8 | npm install 9 | ``` 10 | 11 | ### Develop 12 | 13 | Starts a local server. This will start a 11ty server and auto reload on changes. 14 | 15 | ```shell 16 | npm run start 17 | ``` 18 | 19 | 20 | ### Deploy 21 | 22 | Builds and commits to gh-pages branch. 23 | 24 | ```shell 25 | npm run deploy 26 | ``` 27 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /_site/_data/encore.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | module.exports = function () { 4 | try { 5 | const data = fs.readFileSync('./build/entrypoints.json', 'utf8'); 6 | const entrypoints = JSON.parse(data); 7 | 8 | return entrypoints.entrypoints; 9 | }catch (e) { 10 | console.log('looks like the files do not exists, waiting to let them be built'); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /_site/_includes/layouts/base.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {%- for css in encore.app.css -%} 10 | 11 | {%- endfor -%} 12 | 13 | 20 | 21 | {{ title }} 22 | 23 |
24 | {{ content | safe }} 25 | {%- for js in encore.app.js -%} 26 | 27 | {%- endfor -%} 28 |
29 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /_site/_includes/layouts/page.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/base.njk 3 | --- 4 | 18 | 19 |
20 | {{ content | safe }} 21 |
22 | -------------------------------------------------------------------------------- /_site/index.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/page.njk 3 | title: Project Moab 4 | description: A Machine Teaching Hardware Kit for Autonomous Systems from Microsoft 5 | eleventyNavigation: 6 | key: Project Moab 7 | order: 1 8 | --- 9 |
10 |
11 |
Project Moab
12 |
A Machine Teaching Hardware Kit for Autonomous Systems from Microsoft
13 | Moab device 14 |
15 |
16 | 17 |
18 |
19 |

Motion Control, Simulation and Microsoft Bonsai

20 |
Balance using trained Bonsai brains, visualize them in simulation and deploy them to the physical bot.
21 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |

Discover the project

31 |
Learn about the components inside the Moab M2 Bot.
32 | 35 |
36 |
37 |
38 | 41 |
42 |
43 | 44 |
45 |

Getting Started

46 |
You can play with a virtual Moab in the simulator right now. Follow these tutorials to get started.
47 |
48 | 49 |
Tutorial Icon
50 |
Tutorials
51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 |

Components and Features

59 |
The bot uses a variety of sensors and components to balance objects.
60 |
61 |
62 | 70 |
71 |
72 | Camera Module 73 | Raspberry Pi 4 74 | Power & Control Board 75 | UI Board 76 | Control Arms 77 | Servos 78 |
79 |
80 |
81 | 82 |
83 | 84 |
85 |
86 |

A Machine Teaching Hardware Kit

87 |
Sign up to be the first to know when Project Moab is available.
88 |
89 | Sign-up for Notifications 90 | Moab Packaging 91 |
92 |
93 |
94 | -------------------------------------------------------------------------------- /_site/test.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/base.njk 3 | title: Project Moab 4 | description: Test Page 5 | tags: 6 | - page 7 | --- 8 | 9 | 10 | -------------------------------------------------------------------------------- /_site/tutorials.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/page.njk 3 | title: Project Moab - Tutorials 4 | description: Tutorials 5 | eleventyNavigation: 6 | key: Tutorials 7 | order: 2 8 | --- 9 | 10 |

Tutorials

11 |

Machine Teaching with Bonsai and Moab

12 | 13 | {% fuji_block { title: "1. Balance a ball on a virtual Moab", url: "1-balance/index.html" } %} 14 | 15 |
16 | Get started with the Bonsai platform by learning how to teach an AI to balance a ball in the center of the Moab bot's plate. 17 |
18 | 19 | {% endfuji_block %} 20 | 21 | 22 | {% fuji_block { title: "2. Robust Balancing with Domain Randomization", url: "2-robustness/index.html" } %} 23 |
24 | Learn to bridge the simulation-to-real-world gap by using Domain Randomization. By varying the AI's training environment, you will teach a brain that works better on real hardware. 25 |
26 | {% endfuji_block %} 27 | 28 | {% fuji_block { title: "3. Avoid obstacles", url: "3-fixed-obstacle/index.html" } %} 29 |
Go beyond single-objective controllers by teaching an AI to balance the ball in the center while avoiding an obstacle. 30 |
31 | {% endfuji_block %} 32 | 33 | -------------------------------------------------------------------------------- /_site/tutorials/1-balance.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/page.njk 3 | title: "Project Moab - Tutorials 1: Train a Brain to Balance a Ball" 4 | class: md 5 | --- 6 | 7 | # Moab Tutorial 1: Train AI to Balance a Ball 8 | 9 | Teach an AI brain to balance a ball in the center of a plate using a custom simulator and sample code. 10 | 11 | **Total time to complete**: 45 minutes\ 12 | **Active time**: 25 minutes\ 13 | **Machine training time**: 20 minutes 14 | 15 | **Prerequisites**: To complete this tutorial, you must have a Bonsai workspace provisioned on Azure. If you do not have one, follow the [the account setup guide](https://docs.microsoft.com/en-us/bonsai/guides/account-setup). 16 | 17 | ## Outline 18 | 19 | 20 | 21 | 22 | - [Outline](#outline) 23 | - [Step 1: Define the problem](#step-1-define-the-problem) 24 | - [Step 2: Create a new brain](#step-2-create-a-new-brain) 25 | - [Step 3: Inspect the brain](#step-3-inspect-the-brain) 26 | - [Step 4: Understand goals](#step-4-understand-goals) 27 | - [Step 5. Train the brain](#step-5-train-the-brain) 28 | - [Step 6: Evaluate training progress](#step-6-evaluate-training-progress) 29 | - [Step 7: Assess the trained brain](#step-7-assess-the-trained-brain) 30 | - [Step 8: Export and Deploy](#step-8-export-and-deploy) 31 | - [Next steps](#next-steps) 32 | - [Feedback and discussion](#feedback-and-discussion) 33 | 34 | 35 | 36 | 37 | 38 | ## Step 1: Define the problem 39 | 40 | Imagine you are holding a plate and trying to keep a ball balanced in the center. 41 | 42 | How would you do it? 43 | 44 | First, you observe the ball: 45 | 46 | - You could track the movement visually. 47 | - You could track the vibrations in the plate by feel. 48 | - You could track the rolling sound. 49 | 50 | In observing the ball, you intuitively determine its **current location and speed**. 51 | 52 | Next, you act on the gathered information: 53 | 54 | - If the ball is already in the center, hold the plate flat to keep it there. 55 | - If the ball moves away from the center, adjust the plate angle to move the ball back. 56 | 57 | In adjusting the plate angle, you alter its **pitch and roll**. 58 | 59 | ### Ball Balance using the Moab Device 60 | 61 | ![A photo of the Moab device](../../img/tutorials/1/moab-photo.png) 62 | 63 | Microsoft Project Moab is a fully integrated system for users of all levels to learn and explore building autonomous intelligent controls using reinforcement learning through Project Bonsai's Machine Teaching platform. The device (shown in the previous image) has three arms powered by servo motors. These arms work in tandem to control the angle of the transparent plate to keep the ball balanced. 64 | 65 | Diagram of the Z-Up right hand coordinate system used by the Moab device. 66 | 67 | 68 | The Moab device tracks and maps the the ball movement onto a standard 2D coordinate system. Looking at the front the of the device, the **x-axis** runs left-to-right, and the **y-axis** runs front-to-back, with the plate center at location (0, 0), and a radius of r. 69 | 70 | The same coordinate system is also used to define the two different tilt angles. Pitch is the plate angle about the x-axis, roll is the plate angle about the y-axis. A perfectly level plate would have pitch and roll of (0, 0). 71 | 72 | The trained AI must learn how to adjust the plate pitch and roll to balance a ball using the following objectives: 73 | 74 | 1. The ball position (x, y) will reach the plate center at (0, 0) and stay there. 75 | 2. The ball position will not get near the plate edge at ( | (x, y) - (0, 0) | << r). 76 | 77 | Now that you have identified the problem and defined the objectives, use machine teaching to train an AI to balance a ball. 78 | 79 | 80 | 81 | ## Step 2: Create a new brain 82 | 83 | A screenshot of the Bonsai home screen 84 | 85 | To start a new Moab brain: 86 | 87 | 1. Create an account or sign into Bonsai. 88 | 2. Click the **Moab** icon in the **Getting started** panel, or **Create Brain** and **Moab demo**, as in the previous illustration. 89 | 3. Name your new brain (e.g., "Moab Tutorial 1"). 90 | 4. Click **Create** 91 | 92 | 93 | 94 | ## Step 3: Inspect the brain 95 | 96 | After creating the Moab sample brain and simulator sample, Bonsai automatically opens the teaching interface, which is prepopulated with everything you need to get started. 97 | 98 | Screenshot of the Bonsai teaching interface 99 | 100 | The teaching interface has three areas, as in the previous illustration: 101 | 102 | - The **Navigation sidebar** lists all your brains and simulators. 103 | - The **Coding panel** is your Inkling code editor. Inkling is a machine teaching proprietary language, designed to focus on what you want to teach while handling the AI details for you. 104 | - The **Graphing panel** displays an interactive, graphical representation of the observable state, concept, and `SimAction` currently defined in the coding panel. 105 | 106 | ### Inspect the state node: ObservableState 107 | 108 | screenshot of ObservableState in the Graphing panel 109 | 110 | Click the `ObservableState`node to jump to the relevant part of your Inkling code, as in the previous illustration. 111 | 112 | `ObservableState` defines what information the brain is sent during every simulation iteration. For your ball balancing problem, the Moab device tracks the ball position and velocity. So, your simulation `ObservableState` is: 113 | 114 | - `ball_x`, `ball_y`: the (x, y) ball position. 115 | - `ball_vel_x`, `ball_vel_y`: the x and y ball velocity components. 116 | 117 | ``` 118 | # State received from the simulator after each iteration 119 | type ObservableState { 120 | # Ball X,Y position 121 | ball_x: number<-RadiusOfPlate .. RadiusOfPlate>, 122 | ball_y: number<-RadiusOfPlate .. RadiusOfPlate>, 123 | 124 | # Ball X,Y velocity 125 | ball_vel_x: number<-MaxVelocity .. MaxVelocity>, 126 | ball_vel_y: number<-MaxVelocity .. MaxVelocity>, 127 | } 128 | ``` 129 | 130 | Each state has an associated expected range. In the previous code snippet, the `ball_x` and `ball_y` locations are bounded by the plate radius. If provided, ranges can reduce AI training time. 131 | 132 | ### Inspect the actions node: SimAction 133 | 134 | screenshot of SimAction in the Graphing panel 135 | 136 | Click on the `SimAction` node to jump to the next part of your Inkling code. 137 | 138 | `SimAction` defines the ways that the brain interacts with the simulated environment. In your simulation, this is reflected by the following variables: 139 | 140 | - `input_pitch`: a value that sets the plate angle along the X axis 141 | - -1 means tilt all the way forwards (away from the joystick), +1 means tilt all the way backwards (toward the joystick) 142 | - `input_roll`: a value that sets the target plate angle along the Y axis 143 | - -1 means tilt all the way to the left, +1 means tilt all the way to the right 144 | 145 | As highlighted before, the Moab device tilts the plate with two orthogonal angles (`SimAction`). An onboard algorithm translates this action to the three servo-powered arms, achieving the desired tilt. 146 | 147 | ``` 148 | # Action provided as output by policy and sent as 149 | # input to the simulator 150 | type SimAction { 151 | # Range -1 to 1 is a scaled value that represents 152 | # the full plate rotation range supported by the hardware. 153 | input_pitch: number<-1 .. 1>, # rotate about x-axis 154 | input_roll: number<-1 .. 1>, # rotate about y-axis 155 | } 156 | ``` 157 | 158 | ### Inspect the concept node: MoveToCenter 159 | 160 | Click on the `MoveToCenter`concept node to define what the AI should learn, as in the following example: 161 | 162 | A `goal` describes what you want the brain to learn using one or more objectives (previously defined), as in the following example: 163 | 164 | ``` 165 | # Define a concept graph with a single concept 166 | graph (input: ObservableState) { 167 | concept MoveToCenter(input): SimAction { 168 | curriculum { 169 | # The source of training for this concept is a simulator that 170 | # - can be configured for each episode using fields defined in SimConfig, 171 | # - accepts per-iteration actions defined in SimAction, and 172 | # - outputs states with the fields defined in SimState. 173 | source simulator (Action: SimAction, Config: SimConfig): ObservableState { 174 | } 175 | ... 176 | ``` 177 | 178 | A `concept` defines what the AI needs to learn and the `curriculum` is how it learns. Your `MoveToCenter`concept will receive simulator states and respond with actions. 179 | 180 | 181 | 182 | ## Step 4: Understand goals 183 | 184 | We previously defined two objectives for our brain: 185 | 186 | - The ball position will not get near the plate edge at ( | (x, y) - (0, 0) | << r). 187 | - The ball position (x, y) will reach the plate center at (0, 0) and stay there. 188 | 189 | A `goal` describes what you want the brain to learn using one or more objectives, as in the following example: 190 | 191 | ``` 192 | # The training goal has two objectives: 193 | # - don't let the ball fall off the plate 194 | # - drive the ball to the center of the plate 195 | goal (State: ObservableState) { 196 | 197 | avoid `Fall Off Plate`: 198 | Math.Hypot(State.ball_x, State.ball_y) in Goal.RangeAbove(RadiusOfPlate * 0.8) 199 | 200 | drive `Center Of Plate`: 201 | [State.ball_x, State.ball_y] in Goal.Sphere([0, 0], CloseEnough) 202 | } 203 | ``` 204 | 205 | As the brain trains, it attempts to simultaneously meet all the defined objectives during each episode. 206 | 207 | Available goal objectives include: 208 | 209 | - `avoid`: Avoid a defined region. 210 | - `drive`: Get to a target as quickly as possible and stay in that place. 211 | - `reach`: Get to a target as quickly as possible. 212 | 213 | For your objectives, use `avoid` and `drive`, as follows: 214 | 215 | ### Objective: Avoid falling off the plate 216 | 217 | To teach the brain to keep the ball on the plate, use `avoid` to define an objective called `Fall Off Plate`, as in the following code snippet: 218 | 219 | ``` 220 | avoid `Fall Off Plate`: 221 | Math.Hypot(State.ball_x, State.ball_y) in Goal.RangeAbove(RadiusOfPlate * 0.8) 222 | ``` 223 | 224 | An `avoid` objective asks the brain to learn to avoid a certain region of states. Your objective states that the ball's distance from the center must not reach values above 80% of the plate radius. This will teach the brain to keep the ball on the plate. 225 | 226 | ### Objective: Move the ball to the center of the plate 227 | 228 | To teach the brain to move the ball to a specific spot and keep it there, use `drive` to define a objective called `Center Of Plate`, as in the following code snippet: 229 | 230 | ``` 231 | drive `Center Of Plate`: 232 | [State.ball_x, State.ball_y] in Goal.Sphere([0, 0], CloseEnough) 233 | ``` 234 | 235 | A `drive` objective asks the brain to learn to reach to a target as soon as possible and stay there. In this case, the target is for the ball's X and Y coordinates (`state.ball_x` and `state.ball_y`) to stay within `CloseEnough` radial distance from the plate center. 236 | 237 | 238 | 239 | ## Step 5. Train the brain 240 | 241 | After defining the brain objectives, Click the **Train** button to start training and open the Train UI, as seen in the following screenshot: 242 | 243 | Screenshot of Bonsai UI starting training 244 | 245 | When training starts, Bonsai launches multiple Moab simulator instances in the cloud. The training progress is reflected in several UI sections, as seen in the following screenshot: 246 | 247 | Screenshot of Bonsai UI training 248 | 249 | ### Section 1: Training performance plot 250 | 251 | Start with the chart at the top of the data panel that displays the **performance plot**, as seen in the following screenshot: 252 | 253 | Screenshot of the goal satisfaction performance chart 254 | 255 | This shows the average performance of the brain from test episodes that are regularly run during training. (A test episode evaluates the brain's performance without the exploratory actions used during training to help the brain learn.) 256 | 257 | Next, inspect the goal satisfaction plots. Goal satisfaction plots display the brains' achievement progress for each objective. For example, 100% Goal Satisfaction for `Fall Off Plate` indicates that the brain has learned to consistently keep the ball on the plate. The overall **Goal Satisfaction** line is the average goal satisfactions across all the objectives. Select several other performance metrics using the Y-axis selector. 258 | 259 | As it trains, the brain improves at accomplishing the goals you defined. The goal satisfaction values should eventually reach close to 100%. 260 | 261 | ### Section 2: Simulator node 262 | 263 | The teaching graph has a new **Simulator** node in the graphing panel, as seen in the following screenshot: 264 | 265 | Screenshot of teaching graph with a simulator node 266 | 267 | The simulator node displays the following: 268 | 269 | - the number of simulation iterations running in parallel. 270 | - the overall iterations per second. 271 | 272 | During training, the concept node displays the latest goal satisfaction. 273 | 274 | ### Section 3: Moab Device Visualization 275 | 276 | There is a live visualization of the Moab simulator below the performance plot, as seen in the following screenshot: 277 | 278 | 3D visualization of Moab simulator 279 | 280 | In addition to the 3D ball and hardware, the visualization displays the following: 281 | 282 | - The ball velocity (the blue arrow projected onto the plate) 283 | - The estimated ball position (blue circle projected on to the plate under the ball). 284 | 285 | Click and drag to rotate the visualization view angle. 286 | 287 | Below the visualization is an interactive graph that plots training values, as seen in the following screenshot: 288 | 289 | Screenshot of live-streaming state and action chart 290 | 291 | Click `ball_x` and `ball_y` to display the ball X and Y coordinates. The vertical dashed lines show the stop and start of new episodes. 292 | 293 | As the brain learns to keep the ball from falling off the plate, each episode will get longer. As the brain learns to center the ball, `ball_x` and `ball_y` will reach zero every episode. 294 | 295 | While you wait for the brain to train, try charting other values. 296 | 297 | 298 | 299 | ## Step 6: Evaluate training progress 300 | 301 | ### When should we stop training? 302 | 303 | The **Goal Satisfaction %** trends upwards during the first 100k – 200k iterations. Afterwards, the various performance lines will converge and flatten. After training for an hour or so, you should see a plot similar to the following screenshot: 304 | 305 | Screenshot of converged goal satisfaction chart 306 | 307 | Here, performance has reached a peak level and additional training time does not seem to yield any improvement. 308 | 309 | **Training can be stopped when you notice that the Goal Satisfaction has not made any meaningful progress, which should occur before 500k iterations.** Hitting the Train button afterwards will resume training. 310 | 311 | Congratulations! You have successfully trained a brain to balance the ball! 312 | 313 | 314 | 315 | ## Step 7: Assess the trained brain 316 | 317 | Click the **Start Assessment** button on the Train page, and the following sub-window should appear. 318 | 319 | Screenshot of assessment mode loading state 320 | 321 | After the simulator starts, the visualizer and streaming charts from training are displayed. In assessment mode, the brain is tested using the same random scenarios defined in the Inkling lesson. 322 | 323 | Screenshot of assessment mode loading state 324 | 325 | 326 | 327 | ## Step 8: Export and Deploy 328 | 329 | Here is a video of a trained brain from this tutorial, deployed on the Moab hardware: 330 | 331 | 332 | 333 | Once Moab kits ship, look here for instructions on deploying the trained brain onto your bot. 334 | 335 | 336 | ## Next steps 337 | 338 | Congratulations! You trained a brain that can balance a ball on a plate, and can be deployed on real hardware. In [Tutorial 2](../2-robustness/index.html), you will learn how to use domain randomization to make the deployed brain more robust to differences between simulation and real life. 339 | 340 | 341 | ## Feedback and discussion 342 | 343 | Discuss this tutorial and ask questions in the [Bonsai community forums](https://aka.ms/as/forums). 344 | 345 | We would appreciate your feedback! [Submit feedback and product feature requests](https://aka.ms/as/productfeedback). -------------------------------------------------------------------------------- /_site/tutorials/2-robustness.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/page.njk 3 | title: "Project Moab - Tutorial 2: Robust Balancing via Domain Randomization" 4 | class: md 5 | --- 6 | 7 | # Moab Tutorial 2: Robust Balancing via Domain Randomization 8 | 9 | In Tutorial 1, you taught an AI to balance a ball on a plate. If you deployed it on real hardware, you observed that it works, but does not react very quickly, and it's easy to push the ball off the plate. In this tutorial, you will teach an AI to balance a ball more robustly. 10 | 11 | **Total time to complete**: 60 minutes\ 12 | **Active time**: 20 minutes\ 13 | **Machine training time**: 40 minutes 14 | 15 | **Prerequisites**: To complete this tutorial, you must have a Bonsai workspace provisioned on Azure. If you do not have one, follow the [the account setup guide](https://docs.microsoft.com/en-us/bonsai/guides/account-setup). 16 | 17 | ## Outline 18 | 19 | - [Step 1: Intro to Domain Randomization](#intro) 20 | - [Step 2: Choose Physical Parameters to Vary](#vary) 21 | - [Step 3: Start a New Brain](#new) 22 | - [Step 4: Using Lessons to Implement DR](#dr) 23 | - [Step 5. Implement New Lesson](#newlesson) 24 | - [Step 6. Experimentation Tips](#experimentation) 25 | - [Step 7: Assess Performance of the Trained Brain](#prediction) 26 | - [Step 8 (optional): Export and Deploy](#deploy) 27 | - [Next steps](#nextsteps) 28 | 29 | 30 | 31 | ## Step 1: Intro to Domain Randomization 32 | 33 | Training an AI in simulation is much easier and faster than training on real hardware: there are no safety concerns, and no need to build, maintain, and continually reset physical training environments. Many simulators can be run in parallel at low cost, making training a much faster process. However, there is a risk of training in simulation: a simulation almost never captures every aspect of the real world, and there is a tradeoff between simulator fidelity and its cost and simulation speed. If the AI learns to take advantage of imperfections in the simulation, it can work very well in simulation and poorly when deployed. This is often called the sim-to-real gap. 34 | 35 | This tutorial will use a technique called Domain Randomization (DR) to address the sim-to-real gap. DR works by introducing extra variability into the simulated environment during AI training. By doing so, the AI must learn a strategy that works in scenarios with different properties. This increases the chance that the trained AI will perform well in the real-world environment. 36 | 37 | The following steps will illustrate DR on Bonsai: 38 | 39 | 1. Select physical parameters that are useful for DR 40 | 2. Learn how to specify DR in Inkling by reviewing sample code. 41 | 3. Use DR to train Moab to be more robust when balancing a ball 42 | 43 | 44 | 45 | 46 | ## Step 2: Choose Physical Parameters to Vary 47 | 48 | ### Varying initial conditions 49 | # 50 | When you place a ball on Moab, the following conditions can vary: 51 | - plate position 52 | - initial velocity 53 | - initial plate tilt. 54 | 55 | The Moab simulator can model these initial conditions using the parameters in the following table: 56 | 57 | | **Parameter Name** | **Units** | **Description** | 58 | | --- | --- | --- | 59 | | initial_pitch | Scalar between -1, +1 | Normalized initial pitch angle of plate | 60 | | initial_roll | Scalar between -1, +1 | Normalized initial roll angle of plate | 61 | | initial_x | Meters | Initial X position of ball | 62 | | initial_y | Meters | Initial Y position of ball | 63 | | initial_vel_x | Meters/Sec | Initial X velocity of ball | 64 | | initial_vel_y | Meters/Sec | Initial Y velocity of ball | 65 | 66 | ### Varying environmental properties 67 | 68 | By default, the Moab simulator simulates a fixed size and weight ping-pong ball. The ball radius(`ball_radius`) and shell thickness (`ball_shell`) can be changed to alter the ball dynamics. The ratio between shell thickness and ball radius defines the ball thickness. The thicker the ball, the faster the acceleration. The following table details these simulator parameters: 69 | 70 | | **Parameter Name** | **Units** | **Description** | 71 | | --- | --- | --- | 72 | | ball_radius | Meters | Radius of the ball (0.02 for Ping-Pong ball) | 73 | | ball_shell | Meters | Shell thickness of the ball (0.0002 for Ping-Pong ball) | 74 | 75 | 76 | 77 | ## Step 3: Start a New Brain 78 | 79 | bonsai home page 80 | 81 | Start a new brain for this tutorial. 82 | 83 | To start a new Moab brain: 84 | 85 | 1. Create an account or sign into Bonsai. 86 | 2. Click the **Moab** icon in the **Getting started** panel, or **Create Brain** and **Moab demo** 87 | 3. Name your new brain (e.g., "Moab Tutorial 2"). 88 | 4. Click **Create** 89 | 90 | 91 | 92 | ## Step 4: Using Lessons to Implement DR 93 | 94 | To implement DR, configure the simulated environment differently for each training episode. You do this in Inkling using a lesson. A lesson describes the set of scenarios the simulator can be configured with at the beginning of each episode. You can vary initial conditions (such as positions and velocities), and environmental properties (such as object sizes, sensor accuracies, hardware properties). 95 | 96 | In the starting Moab Inkling, review the lesson in the `MoveToCenter` concept: 97 | 98 | Click on the concept node in the right panel to navigate to the Inkling code section defining the `MoveToCenter` concept. 99 | 100 | concept node in graphing panel 101 | 102 | The cursor should automatically scroll and highlight the following Inkling declaration: 103 | 104 | ``` 105 | graph (input: ObservableState) { 106 | concept MoveToCenter(input): SimAction { 107 | curriculum { 108 | source MoabSim 109 | ... 110 | ... 111 | } 112 | ... 113 | ... 114 | ``` 115 | 116 | Teaching the AI the `MoveToCenter` concept requires defining a goal and one or more lessons to teach that goal, as in the following code example: 117 | 118 | ``` 119 | lesson `Randomize Start` { 120 | # Specify the configuration parameters that should be varied 121 | # from one episode to the next during this lesson. 122 | scenario { 123 | initial_x: number<-RadiusOfPlate * 0.5 .. RadiusOfPlate * 0.5>, 124 | initial_y: number<-RadiusOfPlate * 0.5 .. RadiusOfPlate * 0.5>, 125 | initial_vel_x: number<-0.02 .. 0.02>, 126 | initial_vel_y: number<-0.02 .. 0.02>, 127 | initial_pitch: number<-0.2 .. 0.2>, 128 | initial_roll: number<-0.2 .. 0.2>, 129 | } 130 | } 131 | ``` 132 | 133 | Each scenario parameter is selected randomly at the start of each training episode. The previous example configuration initializes the ball position in the square with x and y between –0.5*r and 0.5*r, where r is the plate radius. Initial velocity is up to 0.02 m/s in each direction. This is quite slow. The initial roll and pitch go up to 20% of the maximum tilt (22 degrees). 134 | 135 | 136 | 137 | ## Step 5. Implement New Lesson 138 | 139 | Increase the difficulty of the lesson by increasing the ranges of existing scenario parameters, and domain randomizing the ball parameters. 140 | 141 | To add DR for the ball, you will perform the following steps: 142 | - Add constants describing the ping pong ball 143 | - Hook up additional configuration parameters for the simulator 144 | - add the new parameters to the lesson to and increase the ranges on the others. 145 | 146 | ### Increase parameter ranges 147 | 148 | Increase the initial position up to 0.6 times the plate radius, and initial velocity up to 0.4 * MaxVelocity, as in the following code: 149 | 150 | ``` 151 | scenario { 152 | initial_x: number<-RadiusOfPlate * 0.6 .. RadiusOfPlate * 0.6>, 153 | initial_y: number<-RadiusOfPlate * 0.6 .. RadiusOfPlate * 0.6>, 154 | initial_vel_x: number<-MaxVelocity * 0.4 .. MaxVelocity * 0.4>, 155 | initial_vel_y: number<-MaxVelocity * 0.4 .. MaxVelocity * 0.4>, 156 | initial_pitch: number<-0.2 .. 0.2>, 157 | initial_roll: number<-0.2 .. 0.2>, 158 | } 159 | ``` 160 | 161 | We could increase the pitch and roll limits as well. The initial angle turns out not to matter much because the Moab can change pitch and roll very quickly. 162 | 163 | ### Add Constants 164 | 165 | At the top of your Inkling, add the `PingPongRadius` and `PingPongShell` constants between the `RadiusOfPlate` and `MaxVelocity` constants using the following code: 166 | 167 | ``` 168 | # Ping-Pong ball constants 169 | const PingPongRadius = 0.020 # m 170 | const PingPongShell = 0.0002 # m 171 | ... 172 | ``` 173 | 174 | ### Adding More Parameters in Simulator Configuration 175 | 176 | Add the `ball_radius` and `ball_shell` parameters to the bottom of the `SimConfig` Inkling section, using the following code: 177 | 178 | ``` 179 | ball_radius: number, # Radius of the ball in (m) 180 | ball_shell: number, # Shell thickness of ball in (m), shell>0, shell<=radius 181 | ``` 182 | 183 | Any simulator parameters that aren't included in `SimConfig` are assigned a default value by the simulator. 184 | 185 | > **Note:** You can see the full list of available state, action, and configuration variables for a simulator by opening the `Moab` simulator in the **Navigation Side Panel**. 186 | 187 | ### Domain Randomize Ball Radius and Ball Shell 188 | 189 | Use the new `SimConfig` parameters in a lesson. Add `ball_radius` and `ball_shell` to the bottom of the `Randomize Start` lesson with the following lines of code: 190 | 191 | ``` 192 | ball_radius: number, 193 | ball_shell: number, 194 | ``` 195 | By adding this change, the AI will strive to achieve the same goal but uses a ball with a randomly selected radius and shell thickness (between 80% and 120%) for every new episode. 196 | 197 | *Note:* if you had any trouble completing the above steps, please tell us what went wrong in the [Bonsai community forums](https://aka.ms/as/forums), and copy the full Inkling from the [github repo](https://github.com/microsoft/moabsim-py/blob/main/moab_tutorial_2.ink) to continue. 198 | 199 | To start the experiment, click **Train**. The Moab simulator will start automatically based on the `package` statement in Inkling. 200 | 201 | 202 | 203 | ## Step 6. Experimentation Tips 204 | 205 | Successfully developing a trained AI often requires multiple rounds of experimentation. Some experiments may be successful, reach a dead end, or inspire completely different courses of investigation. The Bonsai interface has features to assist your experimentation. 206 | 207 | ### Training Multiple Different Brains in Parallel 208 | 209 | The Navigation Panel on the left shows all brains in your workspace, as in the following illustration: 210 | 211 | Brains list in navigation panel 212 | 213 | 214 | > **Note:** If you have an Azure free account, the quota restrictions for Azure Container Instances (used to run the simulators) will prevent training more than one brain a time. Upgrade to a pay-as-you-go account or use an organizational account with higher quotas to take full advantage of Bonsai's experimentation features. 215 | 216 | To train multiple brains, select an existing brain and then click **Train** to start training. Each brain will contain the latest corresponding Inkling code. As brains train, the Brains Panel will also display the latest performance. 217 | 218 | Rename your brain or add an experiment description by right clicking on a brain and selecting **Info**, as in the following illustrations: 219 | 220 | right-click context menu for a brain 221 | 222 | Brains info view 223 | 224 | 225 | ### Versioning 226 | 227 | The Bonsai interface enables you to keep track of different brain versions. If the Inkling code is changed in a way that requires a reset, a training start will automatically generate a new brain version. 228 | 229 | Brains versions list 230 | 231 | 232 | Write per-version notes or copy a brain version by right clicking on it. 233 | 234 | Brain version context menu 235 | 236 | 237 | 238 | 239 | ## Step 7: Assess Performance of the Trained Brain 240 | 241 | Once training is stopped, run tests on the trained AI. 242 | 243 | Click the **Start Assessment** button on the **Train** tab, and the following sub-window will appear after a minute or so. 244 | 245 | brain assessment window 246 | 247 | In this mode, the trained brain is tested continuously, initializing each episode using the domain randomization defined in Inkling. Monitor performance using the Moab visualizer and line charts. 248 | 249 | 250 | 251 | ## Step 8 (optional): Export and Deploy 252 | 253 | The following video shows a trained brain from this tutorial: 254 | 255 | 256 | 257 | Once Moab kits ship, look here for instructions on deploying the trained brain onto your bot. 258 | 259 | 260 | 261 | ## Next steps 262 | 263 | Congratulations! You trained a brain that can robustly balance a ball on real hardware. In [Tutorial 3](../3-fixed-obstacle/index.html), you will learn how to use additional goals to make the ball balance while avoiding an obstacle. 264 | 265 | ## Feedback and discussion 266 | 267 | Discuss this tutorial and ask questions in the [Bonsai community forums](https://aka.ms/as/forums). 268 | 269 | We would appreciate your feedback! [Submit feedback and product feature requests](https://aka.ms/as/productfeedback). -------------------------------------------------------------------------------- /_site/tutorials/3-fixed-obstacle.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/page.njk 3 | title: "Project Moab - Tutorial 3: Balance with a Fixed Obstacle" 4 | class: md 5 | --- 6 | 7 | # Moab Tutorial 3: Balance with a Fixed Obstacle 8 | 9 | Teach an AI to balance a ball in the center of a plate with Bonsai, a predefined simulator, and sample code. 10 | 11 | **Total time to complete**: 3 hours\ 12 | **Active time**: 25 minutes\ 13 | **Machine training time**: 2.5 hours 14 | 15 | **Prerequisites**: To complete this tutorial, you must have a Bonsai workspace provisioned on Azure. If you do not have one, follow the [the account setup guide](https://docs.microsoft.com/en-us/bonsai/guides/account-setup). 16 | 17 | ## Outline 18 | 19 | - [Step 1: Define the problem](#problem) 20 | - [Step 2: Start a new brain](#start) 21 | - [Step 3: Define hole location](#hole) 22 | - [Step 4. Adding additional simulator states](#states) 23 | - [Step 5. Add a new objective](#objective) 24 | - [Step 6: Train](#train) 25 | - [Step 7: Running predictions on trained brain](#predictions) 26 | - [Step 8 (optional): Export and deploy](#export) 27 | - [Next steps](#nextsteps) 28 | - [Give us Feedback](#feedback) 29 | 30 | 31 | ## Step 1: Define the problem 32 | 33 | Imagine that once you completed training the Moab device to balance a ball, someone decides to swap the plate you are holding with a new plate and poses a new challenge. The new plate is the same size but contains a hole. The new challenge is to balance the ball to the center but keep the ball away from landing in the hole, and a solution needs to be developed as soon as possible. 34 | 35 | How does this added complexity change your approach? How would you translate a new strategy and teach a machine to do this? 36 | 37 | The machine teaching tools on the Bonsai platform are designed to tackle situations like this. In this tutorial, you will learn how to naturally and efficiently translate this new requirement and solve this more complex problem. 38 | 39 | 40 | ## Step 2: Start a new brain 41 | 42 | Select Moab from list 43 | 44 | To build your brain: 45 | 46 | 1. [Sign into the Bonsai UI](https://preview.bons.ai). 47 | 1. Select **Moab** from the list of demo brains from the **Getting Started** 48 | dialog. 49 | 1. Name your new brain (for example, "Moab Tutorial 3"). 50 | 1. Click **Create Brain** to load the sample brain and simulator. 51 | 1. Start from the [Inkling for tutorial 2](https://github.com/microsoft/moabsim-py/blob/main/moab_tutorial_2.ink). Copy and paste it into the Inkling editor, replacing the default Inkling. 52 | 53 | 54 | ## Step 3: Define hole location 55 | 56 | ### Add obstacle configuration 57 | 58 | To start, simulate the scenario. The Moab simulator can model one obstacle, represented as a circular region on the plate. These are the Moab simulator parameters used to set up obstacles: 59 | 60 | |**Parameter Name** |**Units** |**Description** | 61 | |----------------|------|-------------| 62 | |obstacle_radius |Meters|Radius of the circular obstacle region| 63 | |obstacle_x |Meters|Center X location of circle region| 64 | |obstacle_y |Meters|Center Y location of circle region| 65 | 66 | Example 1: Values to create an avoid region at the center of the plate with radius of 2cm: 67 | 68 | - obstacle_radius = 0.02 69 | - obstacle_x = 0.0 70 | - obstacle_y = 0.0 71 | 72 | Example 2: Values to create an avoid region centered 5 cm directly above the center of the plate with radius of 1 cm: 73 | 74 | - obstacle_radius = 0.01 75 | - obstacle_x = 0.0 76 | - obstacle_y = 0.05 77 | 78 | Example 3: Values to create an avoid region centered 3 cm directly above the center of the plate with radius of 3 cm: 79 | 80 | - obstacle_radius = 0.03 81 | - obstacle_x = -0.03 82 | - obstacle_y = 0.0 83 | 84 | Scroll to `SimConfig`, then add `obstacle_radius`, `obstacle_x`, and `obstacle_y` to the end of `SimConfig` as in the following code: 85 | 86 | ``` 87 | # Obstacle information 88 | obstacle_radius: number, 89 | obstacle_x: number, 90 | obstacle_y: number, 91 | ``` 92 | 93 | ### Pick a location for the obstacle 94 | 95 | Use these new `SimConfig` parameters to configure the obstacle position and size. Put an obstacle with radius 1cm at (4cm, 4cm) and define a 1cm “cushion” -- how far from the obstacle you want the ball to stay. At the top of the Inkling file, define constants for these (remember that distances are in meters.) 96 | 97 | ``` 98 | # Obstacle definitions 99 | const ObstacleRadius = 0.01 100 | const ObstacleLocationX = 0.04 101 | const ObstacleLocationY = 0.04 102 | 103 | # How far to stay from the obstacle 104 | const Cushion = 0.01 105 | ``` 106 | 107 | ### Add obstacle config to the scenario 108 | 109 | At the end of the **Randomize Start** lesson, add the following lines to set the obstacle position and size. Note that these are being set to constant values, so will not be randomized during training. 110 | 111 | ``` 112 | # Configure obstacle parameters 113 | obstacle_radius: ObstacleRadius, 114 | obstacle_x: ObstacleLocationX, 115 | obstacle_y: ObstacleLocationY, 116 | ``` 117 | 118 | Note that for some specified scenarios, our specified goal will be impossible to achieve. Since each variable in the scenario is randomized independently, it is possible that the ball will start on top of the obstacle or headed quickly toward it. These should not impede training—the AI will fail in impossible-to-satisfy scenarios and keep trying others. This will affect the achievable success rate – even a perfect brain will not be able to succeed 100% of the time. 119 | 120 | 121 | ## Step 4. Adding additional simulator states 122 | 123 | Because the obstacle is fixed in this tutorial, the brain does not need to be passed information about the obstacle as part of the `ObservableState`. However, the brain does need the obstacle location during training. To support this, we will define a new type: `SimState`, which will include the distance and direction to the obstacle. We will use this in our goal definition to train the brain. 124 | 125 | The Moab simulator outputs the direction and distance from ball to obstacle. The obstacle distance is measured between the edge of the ball and the edge of the obstacle. Values less than 0 mean that the ball has hit the obstacle. 126 | To reveal these new states to the AI brain during training, In Inkling, copy `ObservableState` and rename it `SimState`, as in the following code: 127 | 128 | ``` 129 | type SimState { 130 | # Ball X,Y position, noise applied 131 | ball_x: number<-RadiusOfPlate .. RadiusOfPlate>, 132 | ball_y: number<-RadiusOfPlate .. RadiusOfPlate>, 133 | 134 | # Ball X,Y velocity, noise applied 135 | ball_vel_x: number<-MaxVelocity .. MaxVelocity>, 136 | ball_vel_y: number<-MaxVelocity .. MaxVelocity>, 137 | } 138 | ``` 139 | 140 | Next, add parameters `obstacle_direction` and `obstacle_distance` to the Inkling, as in the following code: 141 | 142 | ``` 143 | type SimState { 144 | ... 145 | 146 | # Obstacle data 147 | obstacle_direction: number<-Math.Pi .. Math.Pi>, 148 | obstacle_distance: number<-2.0*RadiusOfPlate .. 2.0*RadiusOfPlate>, 149 | } 150 | ``` 151 | 152 | Next, tell the system that the simulator will be sending us values of type `SimState`. Change `ObservableState` to `SimState` in the `source simulator` line in the `MoveToCenter` concept: 153 | 154 | ``` 155 | source simulator (Action: SimAction, Config: SimConfig): SimState { 156 | } 157 | ``` 158 | 159 | Because `ObservableState` is a subset of `SimState`, the brain will automatically pass only the observable values to the policy, and the final trained brain will only require `ObservableState` values to make its decisions. The goal definition can use the full `SimState`, so change `ObservableState` to `SimState` in the `goal` statement, as in the following code: 160 | 161 | ``` 162 | goal (State: SimState) { 163 | ... 164 | ``` 165 | 166 | 167 | ## Step 5. Add a new objective 168 | 169 | Next, tell the AI that it should avoid the obstacle as it attempts to balance the ball. Add the following code after the `drive CenterOfPlate:` objective at the end of the `goal` statement inside the `curriculum`: 170 | 171 | ``` 172 | avoid HitObstacle: State.obstacle_distance in Goal.RangeBelow(Cushion) 173 | ``` 174 | 175 | That’s it! Now the AI will learn to avoid hitting the obstacle if it can. 176 | 177 | *Note:* if you had any trouble completing the above steps, please tell us what went wrong in the [Bonsai community forums](https://aka.ms/as/forums), and copy the full Inkling from the [github repo](https://github.com/microsoft/moabsim-py/blob/main/moab_tutorial_3.ink) to continue. 178 | 179 | 180 | 181 | ## Step 6: Train 182 | 183 | 184 | > **NOTE: Running simulations consumes Azure resources. Following the tutorial as 185 | > written will charge your Azure subscription approximately 1.00 USD. Repeated 186 | > training or running the training longer than recommended will result in 187 | > additional cost.** 188 | 189 | 190 | 191 | To start an experiment, click **Train**. The system will prompt you to select a simulator. Pick Moab. 192 | 193 | select simulator 194 | 195 | Note: To have the Moab simulator start without prompting, you can add a package statement to the simulator statement in Inkling, as in the following code: 196 | 197 | ``` 198 | source simulator (Action: SimAction, Config: SimConfig): SimState { 199 | # Automatically launch the simulator with this 200 | # registered package name. 201 | package "Moab" 202 | } 203 | ``` 204 | 205 | The system will automatically start simulators and start training. This problem is more complex than simply balancing, and may take 30 minutes or more to train. You should see a goal satisfaction plot similar to this: 206 | 207 | goal satisfaction plot 208 | 209 | 210 | ## Step 7: Running predictions on trained brain 211 | 212 | Once training is stopped, you can run tests on the trained AI. 213 | 214 | Click the **Start Assessment** button on the **Train** page, and the following sub-window should appear: 215 | 216 | run assessment 217 | 218 | In this mode, the trained brain is tested continuously, with each episode initialized using the domain randomization defined in Inkling. You can watch the Moab visualizer and line charts. 219 | 220 | 221 | ## Step 8 (optional): Export and deploy 222 | 223 | Here is a video of a trained brain from this tutorial: 224 | 225 | 226 | 227 | Once Moab kits ship, look here for instructions on deploying the trained brain onto your bot. 228 | 229 | 230 | 231 | ## Next steps 232 | 233 | Congratulations! You trained a brain that can robustly balance a ball on real hardware, while avoiding an obstacle. Look for more tutorials coming soon. 234 | 235 | In the meantime, try exploring on your own: can you train a brain to avoid obstacles at different locations? 236 | 237 | 238 | 239 | # Give us feedback 240 | 241 | We're just opening the platform to the public, and would really appreciate your feedback to help us improve! 242 | 243 | * Fill out the feedback survey! 244 | * [Submit feedback and product feature requests](https://aka.ms/as/productfeedback). 245 | * Discuss this tutorial and ask questions in the [Bonsai community forums](https://aka.ms/as/forums). 246 | 247 | 248 | Thank you! -------------------------------------------------------------------------------- /img/Hero 1_04-29-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/Hero 1_04-29-20.png -------------------------------------------------------------------------------- /img/Hero 2_04-29-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/Hero 2_04-29-20.png -------------------------------------------------------------------------------- /img/Packaging 1_04-29-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/Packaging 1_04-29-20.png -------------------------------------------------------------------------------- /img/background-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/background-video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/background-video.mp4 -------------------------------------------------------------------------------- /img/balance-plate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/balance-plate.png -------------------------------------------------------------------------------- /img/control-arms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/control-arms.png -------------------------------------------------------------------------------- /img/documents-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/documents-home.png -------------------------------------------------------------------------------- /img/fresh-video-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/fresh-video-cover.png -------------------------------------------------------------------------------- /img/github-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/github-repo.png -------------------------------------------------------------------------------- /img/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /img/icons/doc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /img/icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /img/icons/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /img/icons/external.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /img/icons/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /img/icons/internal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /img/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /img/icons/tutorial.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /img/icons/video.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /img/servo-motors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/servo-motors.png -------------------------------------------------------------------------------- /img/tutorials-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials-home.png -------------------------------------------------------------------------------- /img/tutorials/1/bonsai-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/bonsai-home.png -------------------------------------------------------------------------------- /img/tutorials/1/concept-graph-observablestate-highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/concept-graph-observablestate-highlight.png -------------------------------------------------------------------------------- /img/tutorials/1/concept-graph-simaction-highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/concept-graph-simaction-highlight.png -------------------------------------------------------------------------------- /img/tutorials/1/moab-frame-of-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/moab-frame-of-reference.png -------------------------------------------------------------------------------- /img/tutorials/1/moab-photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/moab-photo.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-assessment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-assessment.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-concept-graph-training-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-concept-graph-training-mode.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-create-brain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-create-brain.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-inkling-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-inkling-view.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-starting-assessment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-starting-assessment.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-starting-training.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-starting-training.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-training-converged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-training-converged.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-training-started-perf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-training-started-perf.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-training-started.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-training-started.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-training-stopped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-training-stopped.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-ui-sections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-ui-sections.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-visualizer-and-sim-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-visualizer-and-sim-chart.png -------------------------------------------------------------------------------- /img/tutorials/1/tutorial1-visualizer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/1/tutorial1-visualizer.png -------------------------------------------------------------------------------- /img/tutorials/3/assessment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/3/assessment.png -------------------------------------------------------------------------------- /img/tutorials/3/bonsai-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/3/bonsai-home.png -------------------------------------------------------------------------------- /img/tutorials/3/converged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/3/converged.png -------------------------------------------------------------------------------- /img/tutorials/3/select-simulator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/3/select-simulator.png -------------------------------------------------------------------------------- /img/tutorials/img01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img01.png -------------------------------------------------------------------------------- /img/tutorials/img01.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img01.tif -------------------------------------------------------------------------------- /img/tutorials/img02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img02.png -------------------------------------------------------------------------------- /img/tutorials/img03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img03.png -------------------------------------------------------------------------------- /img/tutorials/img04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img04.png -------------------------------------------------------------------------------- /img/tutorials/img05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img05.png -------------------------------------------------------------------------------- /img/tutorials/img06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img06.png -------------------------------------------------------------------------------- /img/tutorials/img07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img07.png -------------------------------------------------------------------------------- /img/tutorials/img08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/img08.png -------------------------------------------------------------------------------- /img/tutorials/perf-chart-converged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/perf-chart-converged.png -------------------------------------------------------------------------------- /img/tutorials/tutorial2-converging-goal-sat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/tutorial2-converging-goal-sat.png -------------------------------------------------------------------------------- /img/tutorials/tutorial2-select-sim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/tutorials/tutorial2-select-sim.png -------------------------------------------------------------------------------- /img/viewer/camera-module.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/viewer/camera-module.jpg -------------------------------------------------------------------------------- /img/viewer/control-arms.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/viewer/control-arms.jpg -------------------------------------------------------------------------------- /img/viewer/m2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/viewer/m2.jpg -------------------------------------------------------------------------------- /img/viewer/power-control-board.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/viewer/power-control-board.jpg -------------------------------------------------------------------------------- /img/viewer/raspberry-pi-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/viewer/raspberry-pi-4.jpg -------------------------------------------------------------------------------- /img/viewer/servos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/moab/bc3242bddd985e971cf73eab518c58d79685e353/img/viewer/servos.jpg -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | import '../scss/app.scss'; 2 | import Sketchfab from './sketchfab-viewer-1.7.1'; 3 | import './jquery-global'; 4 | import fancyBox from './../node_modules/@fancyapps/fancybox/dist/jquery.fancybox'; 5 | 6 | document.querySelector(".hamburger").addEventListener('click', function () { 7 | document.querySelector("nav > ul").classList.toggle("active"); 8 | }); 9 | 10 | (function () { 11 | // Sketchfab 12 | var iframe = document.getElementById( 'api-frame' ); 13 | if (iframe) { 14 | var uid = 'aa056783e78445f19182dad31d99935f'; 15 | var client = new Sketchfab( iframe ); 16 | client.init( uid, { 17 | transparent: 1, 18 | ui_controls: 0, 19 | ui_infos: 0, 20 | success: function onSuccess( api ){ 21 | api.start(function () { 22 | // Handles the buttons outside of sketchfab 23 | var buttons = document.getElementsByClassName('sketchfab-button'); 24 | for (let button of buttons) { 25 | button.addEventListener('click', function (e) { 26 | e.preventDefault(); 27 | api.gotoAnnotation(button.getAttribute('annotation'), {}); 28 | for(let b of buttons) { 29 | b.classList.remove('active'); 30 | } 31 | button.classList.toggle('active'); 32 | }); 33 | } 34 | }); 35 | } 36 | }); 37 | } 38 | 39 | //moab viewer 40 | var buttons = document.getElementsByClassName('viewer-button'); 41 | var imgs = document.getElementsByClassName('moab-viewer-image'); 42 | for (let i = 0; i < buttons.length; i++) { 43 | buttons[i].addEventListener('mouseover', function (e) { 44 | document.querySelector('.viewer-button.active').classList.remove('active'); 45 | document.querySelector('.moab-viewer-image.active').classList.remove('active'); 46 | e.target.classList.add('active'); 47 | imgs[i].classList.add('active'); 48 | }); 49 | } 50 | 51 | //expand content 52 | var expandButtons = document.querySelectorAll('.expand-button'); 53 | for (let button of expandButtons) { 54 | button.addEventListener('click', function (e) { 55 | if (e && e.target) { 56 | var parent = e.target.closest('.section'); 57 | var button = e.target; 58 | var right = parent.querySelector('.right'); 59 | right.classList.toggle('expand'); 60 | button.classList.toggle('expand-button') 61 | button.classList.toggle('close-button') 62 | } 63 | }); 64 | } 65 | 66 | //expand content on url pound 67 | var hash = window.location.hash; 68 | if (hash) { 69 | var expand = document.getElementById(window.location.hash.split('#')[1]); 70 | if (expand) { 71 | expand.querySelector('.expand-button').click(); 72 | } 73 | } 74 | 75 | })(); 76 | -------------------------------------------------------------------------------- /js/jquery-global.js: -------------------------------------------------------------------------------- 1 | import jquery from 'jquery'; 2 | window.jQuery = jquery; 3 | window.$ = jquery; -------------------------------------------------------------------------------- /js/sketchfab-viewer-1.7.1.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Sketchfab=e():t.Sketchfab=e()}(window,(function(){return function(t){var e={};function i(n){if(e[n])return e[n].exports;var s=e[n]={i:n,l:!1,exports:{}};return t[n].call(s.exports,s,s.exports,i),s.l=!0,s.exports}return i.m=t,i.c=e,i.d=function(t,e,n){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var s in t)i.d(n,s,function(e){return t[e]}.bind(null,s));return n},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="/static/builds/web/dist/",i(i.s=1)}([function(t,e){function i(t){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function n(e){return"function"==typeof Symbol&&"symbol"===i(Symbol.iterator)?t.exports=n=function(t){return i(t)}:t.exports=n=function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":i(t)},n(e)}t.exports=n},function(t,e,i){"use strict";i.r(e);var n=i(0),s=i.n(n),r=function(t,e){t.forEach((function(t){this[t]=function(){var i,n=e._requestIdCounter++,s=Array.prototype.slice.call(arguments);if(s.length>0){var r=s[s.length-1];"function"==typeof r&&(i=s.pop())}i&&(e._pendingRequests[n]=i.bind(this)),e._target.postMessage({type:"api.request",instanceId:e.getIdentifier(),requestId:n,member:t,arguments:s},e.getDomain())}}),this),this.addEventListener=function(t,i,n){"viewerready"===t&&e.isViewerReady&&i(),e._eventListeners[t]||(e._eventListeners[t]=[]),e._eventListeners[t].push(i),n&&this.setListenerOptions&&(n.name=t,this.setListenerOptions(n))},this.removeEventListener=function(t,i){if(e._eventListeners[t]){var n=e._eventListeners[t].indexOf(i);-1!==n&&e._eventListeners[t].splice(n,1)}}},o=function(t,e,i){this._target=t,this._requestIdCounter=0,this._pendingRequests={},this._eventListeners={},this._ready=!1,this._domain=i,this._instanceId=e,this.listenServer()};o.prototype={getIdentifier:function(){return this._instanceId},getDomain:function(){return this._domain},setIdentifier:function(t){this._instanceId=t},use:function(t,e){this._version=t,this._ready=!0;var i=this._requestIdCounter++;this._pendingRequests[i]=function(t,i,n){t?e.call(this,t):e.call(this,null,new r(n,this))}.bind(this),this._target.postMessage({type:"api.initialize",requestId:i,name:t,instanceId:this._instanceId},this._domain)},listenServer:function(){var t=["api.initialize.result","api.request.result","api.event"];window.addEventListener("message",function(e){if(e.origin===this._domain&&e.data&&e.data.type&&e.data.instanceId&&e.data.instanceId===this.getIdentifier()){var i=e.data.type;if(-1!==t.indexOf(i))if("api.event"===i){var n=e.data.results,s=n[0];if(this._eventListeners["*"]||this._eventListeners.all)return void["*","all"].forEach((function(t){var e=this._eventListeners[t];e&&e.forEach((function(t){t.apply(t,n)}))}),this);var r=n.slice(1),o=this._eventListeners[s];o?o.forEach((function(t){t.apply(t,r)})):"viewerready"===s&&(this.isViewerReady=!0)}else{var a=e.data.requestId,u=this._pendingRequests[a];if(!u)return;u.apply(null,e.data.results),this._pendingRequests[a]=void 0}}}.bind(this))}};var a=o,u=/[&|;]+/g;function d(t){var e={};return Object.keys(t).forEach((function(i){e[i]=Array.isArray(t[i])?t[i]:[t[i]]})),e}function c(t){return"object"===s()(t)?d(t):("?"===t[0]&&(t=t.substr(1)),t.split(u).reduce((function(t,e){if(0===e.length)return t;var i=e.indexOf("=");-1===i&&(i=e.length);var n=decodeURIComponent(e.substr(0,i).replace(/\+/g,"%20")),s=decodeURIComponent(e.substr(i+1).replace(/\+/g,"%20"));return void 0===t[n]&&(t[n]=[]),t[n].push(s),t}),{}))}window.SketchfabAPIClient=a;var h=function(t,e){var i=t,n=e;"object"===s()(t)&&(n=t,i=null),this._version=i,this._target=n,window.sketchfabAPIinstances||(window.sketchfabAPIinstances=[]),window.sketchfabAPIinstances.push(this),this._apiId=window.sketchfabAPIinstances.length.toString(),this._target.id&&(this._apiId+="_"+this._target.id),this._target.allow||(this._target.allow="vr; autoplay; fullscreen"),this._client=void 0,this._options=void 0,this._domain="sketchfab.com",this._domain="same-as-current"===this._domain?window.location.hostname:this._domain,this._urlTemplate="https://YYYY/models/XXXX/embed",this._url=this._urlTemplate.replace("YYYY",this._domain),this._transmitOptions={},this._getURLOptions()};h.prototype={_urlOptionsDict:{skfb_api_version:{default:"1.7.1",type:"string"}},_optionsLoaded:function(t){this._urlOptions=t,this._version=this._getURLOption("skfb_api_version",this._version)},_getURLOption:function(t,e){var i=this._urlOptionsDict[t];if(!i)return e;null==e&&(e=i.default);var n=this._urlOptions[t];return n&&n.length?n[0]:e},_getURLOptions:function(){if(!window||!window.location.search)return this._optionsLoaded({});var t=c(window.location.search);for(var e in t)e.startsWith("skfb_")&&(this._transmitOptions[e.substr(5)]=t[e]);return this._optionsLoaded(t)},getEmbedURL:function(t,e){var i=this._url+"?api_version="+this._version+"&api_id="+this._apiId;e&&Object.keys(e).forEach((function(t){null!=e[t]&&"function"!=typeof e[t]&&(i+="&"+t.toString()+"="+e[t].toString())}));var n=this._transmitOptions;return Object.keys(this._transmitOptions).forEach((function(t){i+="&"+t.toString()+"="+n[t].toString()})),i.replace("XXXX",t)},init:function(t,e){this._options=e,this._uid=t,this._realInit()},reload:function(t){var e=document.createElement("script");e.setAttribute("src","https://static."+t+"/api/sketchfab-viewer-"+this._version+".js"),e.addEventListener("load",function(){this._url=this._urlTemplate.replace("YYYY",t),-1!==this._domain.indexOf("sketchfab.com")&&(this._transmitOptions.hook_prod=1,this._transmitOptions.model=this._uid),this._realInit()}.bind(this)),document.body.appendChild(e)},_initializeAPIEmbed:function(t){if(t.data&&t.data.instanceId&&this._apiId===t.data.instanceId&&"api.ready"===t.data.type&&this._target.src){var e=t.data.options;if(e&&e.domain)this.reload(e.domain);else{if(void 0!==t.data.error)return this.error(t.data.error),void window.removeEventListener("message",this._initializeAPIEmbedBinded);var i=this._target.src.split("/");i="https://"+i[2],this._client=new window.SketchfabAPIClient(this._target.contentWindow,this._apiId,i),this._client.use(this._version,function(t,e){if(t)throw t;this.success.call(this,e)}.bind(this)),window.removeEventListener("message",this._initializeAPIEmbedBinded)}}},_realInit:function(){this._initializeAPIEmbedBinded=this._initializeAPIEmbed.bind(this),window.addEventListener("message",this._initializeAPIEmbedBinded),this._target.src=this.getEmbedURL(this._uid,this._options)},success:function(t){this._options.success&&"function"==typeof this._options.success&&this._options.success(t)},error:function(t){this._options.error&&"function"==typeof this._options.error&&this._options.error(t)},show:function(){var t=this._target.style.top;this._target.style.top="-1000vh",Promise.resolve().then(function(){this._target.style.top=t}.bind(this))}};e.default=h}]).default})); 2 | //# sourceMappingURL=sketchfab-viewer-1.7.1.js.map -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-moab", 3 | "version": "1.1.0", 4 | "description": "Project Moab", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/scotstan/moab" 9 | }, 10 | "scripts": { 11 | "dev": "ELEVENTY_ENV=development npm-run-all --parallel dev:*", 12 | "prod": "ELEVENTY_ENV=production npm-run-all prod:encore prod:eleventy", 13 | "deploy": "npm-run-all prod gh-pages", 14 | "clean": "rm -rf {build,dist,node_modules}", 15 | "dev:eleventy": "eleventy --config=11ty.js --serve --quiet", 16 | "dev:encore": "encore dev --watch", 17 | "prod:eleventy": "eleventy --config=11ty.js", 18 | "prod:encore": "encore production", 19 | "gh-pages": "gh-pages -d dist" 20 | }, 21 | "author": "Scott Stanfield ", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "@11ty/eleventy": ">0.10", 25 | "@11ty/eleventy-navigation": "^0.1.5", 26 | "http-server": "^0.12.3", 27 | "sass-loader": "^10.0.0" 28 | }, 29 | "dependencies": { 30 | "@11ty/eleventy-plugin-syntaxhighlight": "^2.0.3", 31 | "@fancyapps/fancybox": "^3.5.7", 32 | "@symfony/webpack-encore": "^0.33.0", 33 | "all-contributors-cli": "^6.14.0", 34 | "gh-pages": "^2.2.0", 35 | "jquery": "^3.5.1", 36 | "npm-run-all": "^4.1.5", 37 | "prism-themes": "^1.3.0", 38 | "prod": "^1.0.1", 39 | "sass": "^1.30.0", 40 | "share-api-polyfill": "^1.0.11" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /scss/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | font-family: "Roboto Mono", monospace; 5 | } 6 | .splash { 7 | background-color: #fff; 8 | } 9 | .splash .bg { 10 | height: 100vh; 11 | opacity: 0.2; 12 | background: no-repeat center url("../img/background-dark.svg"); 13 | background-size: 3500px; 14 | } 15 | .splash .headline-container { 16 | display: flex; 17 | flex-direction: column; 18 | justify-content: center; 19 | align-items: center; 20 | height: 100vh; 21 | width: 100%; 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | } 26 | .splash .headline-container .headline { 27 | margin-top: -10px; 28 | } 29 | .splash .headline-container .headline h1 { 30 | font-weight: bold; 31 | font-size: 40px; 32 | margin: 0; 33 | color: #3b3b3b; 34 | } 35 | .splash .headline-container .headline .typewriter { 36 | margin-top: 10px; 37 | color: #ee7100; 38 | letter-spacing: 16px; 39 | text-transform: uppercase; 40 | overflow: hidden; 41 | white-space: nowrap; 42 | border-right: 0.45em solid #ee7100; 43 | animation: typing 3s steps(45, end), blink-caret 0.8s step-end infinite; 44 | } 45 | @keyframes typing { 46 | from { 47 | width: 0; 48 | } 49 | to { 50 | width: 100%; 51 | } 52 | } 53 | @keyframes blink-caret { 54 | from, 55 | to { 56 | border-color: transparent; 57 | } 58 | 50% { 59 | border-color: #ee7100; 60 | } 61 | } 62 | @media (prefers-color-scheme: dark) { 63 | .splash { 64 | background-color: #3b3b3b; 65 | } 66 | .splash .bg { 67 | background: no-repeat center url("../img/background-light.svg"); 68 | background-size: 3500px; 69 | } 70 | .splash .headline-container .headline h1 { 71 | color: #fff; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /scss/app.scss: -------------------------------------------------------------------------------- 1 | 2 | $dark: #3B3B3B; 3 | $light: #fff; 4 | $orange: #EE7100; 5 | $grey: #C4C4C4; 6 | $light-grey: #F4F4F4; 7 | 8 | @import url("https://use.typekit.net/zfi0znh.css"); 9 | @import url("../node_modules/@fancyapps/fancybox/dist/jquery.fancybox.min.css"); 10 | @import "./buttons.scss"; 11 | 12 | /* common styles */ 13 | 14 | body { 15 | font-family: 'Segoe UI', sans-serif; 16 | font-size: 16px; 17 | color: $dark; 18 | margin: 0; 19 | } 20 | 21 | img { 22 | max-width: 100%; 23 | } 24 | 25 | .page-wrap { 26 | max-width: 1440px; 27 | margin: 0 auto; 28 | } 29 | 30 | footer { 31 | background-color: $dark; 32 | height: 300px; 33 | margin-top: 200px; 34 | padding-top: 30px; 35 | color: $light; 36 | } 37 | 38 | hr { 39 | border: 0; 40 | border-bottom: 1px solid $grey; 41 | margin-top: 280px; 42 | margin-bottom: 280px; 43 | } 44 | 45 | a { 46 | text-decoration: none; 47 | color: $dark; 48 | white-space: nowrap; 49 | } 50 | 51 | .lnk { 52 | background: none; 53 | border: none; 54 | padding: 0; 55 | cursor: pointer; 56 | outline: none; 57 | } 58 | 59 | .mt-160 { 60 | margin-top: 160px; 61 | } 62 | 63 | .section, .subsection { 64 | margin-left: 120px; 65 | margin-right: 120px; 66 | } 67 | 68 | .subsection { 69 | margin-top: 150px; 70 | margin-bottom: 0; 71 | padding-bottom: 50px; 72 | 73 | &:first-of-type { 74 | margin-top: 100px; 75 | } 76 | } 77 | 78 | h1{ 79 | font-family: 'din-2014', 'Segoe UI', sans-serif; 80 | text-transform: uppercase; 81 | font-size: 64px; 82 | margin-bottom: 0; 83 | } 84 | 85 | h2 { 86 | font-size: 40px; 87 | } 88 | 89 | h3 { 90 | font-size: 40px; 91 | font-weight: normal; 92 | margin-top: 105px; 93 | margin-bottom: 0; 94 | border-bottom: 1px solid $grey; 95 | padding-bottom: 54px; 96 | } 97 | 98 | .subheading { 99 | font-size: 24px; 100 | font-weight: bold; 101 | } 102 | 103 | .subheading2 { 104 | font-size: 24px; 105 | margin-top: 15px; 106 | margin-bottom: 60px; 107 | } 108 | 109 | .subheading3 { 110 | font-size: 16px; 111 | font-weight: bold; 112 | } 113 | 114 | 115 | td, th { 116 | padding-right: 30px; 117 | text-align: left; 118 | } 119 | 120 | 121 | /* top nav */ 122 | nav { 123 | font-family: 'din-2014', 'Roboto Mono', monospace; 124 | padding: 0 120px; 125 | font-style: normal; 126 | font-weight: 600; 127 | 128 | ul { 129 | width: 100%; 130 | margin-top: 70px; 131 | padding-inline-start: 0; 132 | display: flex; 133 | list-style-type: none; 134 | flex-direction: row; 135 | height: 30px; 136 | 137 | li { 138 | margin-right: 63px; 139 | font-size: 16px; 140 | text-transform: uppercase; 141 | 142 | a { 143 | padding-bottom: 9px; 144 | } 145 | 146 | &.active { 147 | a { 148 | display: inline-block; 149 | border-bottom: 2px solid $orange; 150 | padding-bottom: 9px; 151 | } 152 | } 153 | 154 | &:hover { 155 | a { 156 | display: inline-block; 157 | border-bottom: 2px solid $orange; 158 | padding-bottom: 9px; 159 | } 160 | } 161 | 162 | &:last-child { 163 | flex: 2; 164 | text-align: end; 165 | margin: -10px 0 0 0; 166 | a { 167 | font-weight: bold; 168 | color: $orange; 169 | border-bottom: none; 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | 177 | /* front page section */ 178 | .hero1, .tech-spec, .teach, .get-started { 179 | position: relative; 180 | .subheading2 { 181 | max-width: 600px; 182 | } 183 | h2 { 184 | margin-bottom: 24px; 185 | } 186 | } 187 | 188 | .hero2 { 189 | text-align: center; 190 | margin-top: 160px; 191 | 192 | .machine-header { 193 | font-family: 'din-2014', 'Segoe UI', sans-serif; 194 | text-transform: uppercase; 195 | font-size: 55px; 196 | font-weight: bold; 197 | } 198 | 199 | .subheading2 { 200 | margin-bottom: 50px; 201 | } 202 | 203 | img { 204 | width: 100%; 205 | } 206 | } 207 | 208 | .hero1 { 209 | background: url('../img/Hero\ 2_04-29-20.png') no-repeat; 210 | background-position: top; 211 | height: 890px; 212 | width: 100vw; 213 | position: relative; 214 | left: calc(-50vw + 50%); 215 | 216 | .section { 217 | max-width: 1440px; 218 | margin-left: auto; 219 | margin-right: auto; 220 | } 221 | h2, .subheading2, .content { 222 | border: 0; 223 | margin-left: 120px; 224 | margin-right: 120px; 225 | max-width: 600px; 226 | } 227 | } 228 | 229 | .background-video { 230 | margin-top: 200px; 231 | margin-bottom: 200px; 232 | width: 100vw; 233 | position: relative; 234 | left: calc(-50vw + 50%); 235 | 236 | .background-text-wrapper { 237 | margin-left: auto; 238 | margin-right: auto; 239 | width: 1440px; 240 | } 241 | 242 | .section { 243 | display: flex; 244 | align-items: center; 245 | width: 100%; 246 | height: 100%; 247 | margin-left: auto; 248 | margin-right: auto; 249 | position: absolute; 250 | color: white; 251 | z-index: 1; 252 | 253 | h2, .subheading2, .content { 254 | border: 0; 255 | margin-left: 120px; 256 | margin-right: 120px; 257 | max-width: 600px; 258 | } 259 | } 260 | .video-wrapper { 261 | background-color: black; 262 | video { 263 | width: 100%; 264 | opacity: 0.3; 265 | } 266 | } 267 | } 268 | 269 | .get-started { 270 | h2 { 271 | text-align: center; 272 | } 273 | .subheading2 { 274 | max-width: 725px; 275 | margin-left: auto; 276 | margin-right: auto; 277 | text-align: center; 278 | } 279 | .tiles { 280 | justify-content: space-around; 281 | } 282 | } 283 | 284 | .teach { 285 | img { 286 | margin-top: 125px; 287 | margin-left: -30px; 288 | width: 120%; 289 | } 290 | } 291 | 292 | .content { 293 | &.moab-viewer { 294 | display: flex; 295 | 296 | .left { 297 | min-width: 300px; 298 | display: flex; 299 | flex-direction: column; 300 | 301 | .moab-viewer-links { 302 | font-family: 'din-2014', 'Roboto Mono', monospace; 303 | margin: 120px 0 0 70px; 304 | 305 | div { 306 | margin-top: 38px; 307 | } 308 | 309 | span { 310 | cursor:pointer; 311 | font-size: 16px; 312 | text-transform: uppercase; 313 | display: inline-block; 314 | padding-bottom: 10px; 315 | 316 | &.active { 317 | border-bottom: 1px solid $orange; 318 | } 319 | } 320 | } 321 | } 322 | 323 | .right { 324 | width: 52vw; 325 | height: 52vw; 326 | 327 | img { 328 | position: absolute; 329 | width: 52vw; 330 | height: 52vw; 331 | object-fit: contain; 332 | opacity: 0; 333 | transition: opacity 1.0s ease-in-out; 334 | 335 | &.active { 336 | opacity: 1; 337 | } 338 | } 339 | } 340 | } 341 | } 342 | 343 | /* tiles (download page and index page) */ 344 | .tiles { 345 | display: flex; 346 | flex-wrap: wrap; 347 | 348 | a { 349 | margin-bottom: 16px; 350 | margin-top: 16px; 351 | } 352 | .tile { 353 | display: flex; 354 | position: relative; 355 | flex-direction: column; 356 | align-items: center; 357 | box-shadow: 0px 2px 15px rgba(59, 59, 59, 0.1); 358 | width: 300px; 359 | height: 300px; 360 | margin-right: 35px; 361 | margin-left: 35px; 362 | border: 1px solid $grey; 363 | transition: all 0.2s ease-in-out; 364 | font-weight: bold; 365 | 366 | .tile-top { 367 | display: flex; 368 | width: 100%; 369 | height: 200px; 370 | background-color: $light-grey; 371 | justify-items: center; 372 | align-items: center; 373 | 374 | img { 375 | margin-left: auto; 376 | margin-right: auto; 377 | } 378 | 379 | span { 380 | width: 300px; 381 | height: 200px; 382 | position: absolute; 383 | display: block; 384 | top: 0; 385 | left: 0; 386 | background: url('../img/icons/play.svg') center center no-repeat rgba(0, 0, 0, 0.3); 387 | 388 | &.no-image { 389 | background: url('../img/icons/play.svg') center center no-repeat rgba(0, 0, 0, 0.0); 390 | } 391 | } 392 | } 393 | 394 | .tile-bottom { 395 | display: flex; 396 | height: 100px; 397 | align-items: center; 398 | } 399 | 400 | &:hover { 401 | transition: all 0.2s ease-in-out; 402 | transform: translateY(-2px); 403 | box-shadow: 0px 2px 15px rgba(59, 59, 59, 0.4); 404 | } 405 | } 406 | } 407 | 408 | /* expanding / link block sections */ 409 | .tutorial-item { 410 | display: flex; 411 | border-bottom: 1px solid $grey; 412 | padding: 65px 0 65px 0; 413 | 414 | .right { 415 | font-size: 22px; 416 | margin-left: 10px; 417 | 418 | a { 419 | display: inline-block; 420 | padding-bottom: 2px; 421 | font-weight: 500; 422 | color: $orange 423 | } 424 | 425 | .summary { 426 | margin-top: 5px; 427 | max-width: 75%; 428 | } 429 | 430 | .subheading { 431 | transition: color 1s ease; 432 | } 433 | 434 | .details { 435 | transition: margin 1s, max-height 1s, opacity 1s; 436 | opacity: 0; 437 | max-height: 0; 438 | overflow: hidden; 439 | } 440 | 441 | &.expand { 442 | .subheading { 443 | transition: color 1s ease; 444 | color: $orange; 445 | } 446 | .details { 447 | height: auto; 448 | max-height: 600px; 449 | margin-top: 40px; 450 | opacity: 1; 451 | transition: margin 1s , max-height 1s, opacity 1s; 452 | } 453 | } 454 | } 455 | } 456 | 457 | .download { 458 | display: flex; 459 | max-width: 600px; 460 | height: 100px; 461 | border: 1px solid $grey; 462 | box-shadow: 0px 2px 15px rgba(59, 59, 59, 0.1); 463 | align-items: center; 464 | margin-top: 25px; 465 | transition: all 0.2s ease-in-out; 466 | 467 | .content { 468 | margin-left: 20px; 469 | } 470 | 471 | &:hover { 472 | transition: all 0.2s ease-in-out; 473 | transform: translateY(-2px); 474 | box-shadow: 0px 2px 15px rgba(59, 59, 59, 0.4); 475 | } 476 | } 477 | 478 | h3 + .download { 479 | margin-top: 55px; 480 | } 481 | 482 | // assets page 483 | .gallery { 484 | margin-top: 50px; 485 | display: grid; 486 | grid-template-columns: repeat(4, 1fr); 487 | grid-gap: 15px; 488 | 489 | img { 490 | width: 100%; 491 | height: 100%; 492 | object-fit: cover; 493 | } 494 | } 495 | 496 | .sketchfab { 497 | display: flex; 498 | 499 | .left { 500 | min-width: 300px; 501 | display: flex; 502 | flex-direction: column; 503 | div { 504 | margin: 22px 0; 505 | a { 506 | font-size: 16px; 507 | text-transform: uppercase; 508 | padding: 0; 509 | 510 | &.active { 511 | border-bottom: 1px solid $orange; 512 | } 513 | } 514 | } 515 | } 516 | 517 | .right { 518 | iframe { 519 | width: 60vw; 520 | height: 40vw; 521 | } 522 | } 523 | } 524 | 525 | /* tutorial specific markdown */ 526 | 527 | .md { 528 | 529 | margin-left: auto; 530 | margin-right: auto; 531 | max-width: 800px; 532 | 533 | h1, h2, h3 { 534 | margin-top: 20px; 535 | margin-bottom: 0px; 536 | border: 0; 537 | padding-bottom: 10px; 538 | } 539 | 540 | h3 { 541 | margin-top:20px; 542 | font-size: 32px; 543 | } 544 | 545 | img { 546 | max-width: 800px; 547 | margin-left: auto; 548 | margin-right: auto; 549 | display: block; 550 | } 551 | 552 | a { 553 | display: inline-block; 554 | padding-bottom: 2px; 555 | color: $orange; 556 | } 557 | } 558 | 559 | @media only screen and (max-width: 870px) { 560 | nav { 561 | padding: 30px 30px 0 0; 562 | text-align: right; 563 | 564 | ul { 565 | text-align: right; 566 | display: none; 567 | margin-top: 0; 568 | 569 | &.active { 570 | display: block; 571 | } 572 | 573 | li { 574 | margin: 0; 575 | 576 | &:last-child { 577 | margin: 0; 578 | } 579 | } 580 | } 581 | 582 | .hamburger { 583 | display: inline-block; 584 | } 585 | } 586 | 587 | .hero1 { 588 | .section { 589 | margin: 0; 590 | .subheading2, h2, .content { 591 | margin-left: 10px; 592 | margin-right: 10px; 593 | } 594 | } 595 | } 596 | 597 | .background-video { 598 | width: 220vw; 599 | .background-text-wrapper { 600 | max-width: 100vw; 601 | margin: 0 10px; 602 | .subheading2, h2, .content { 603 | margin-left: 10px; 604 | margin-right: 10px; 605 | } 606 | } 607 | } 608 | 609 | .teach { 610 | img { 611 | margin: 100px 10px; 612 | width: 100%; 613 | } 614 | } 615 | .content { 616 | &.sketchfab, &.moab-viewer { 617 | display: flex; 618 | flex-direction: column; 619 | } 620 | 621 | &.tile { 622 | display: flex; 623 | flex-wrap: wrap; 624 | } 625 | } 626 | 627 | .section, .subsection, .md { 628 | margin-left: 10px; 629 | margin-right: 10px; 630 | } 631 | } 632 | -------------------------------------------------------------------------------- /scss/buttons.scss: -------------------------------------------------------------------------------- 1 | /* buttons */ 2 | 3 | @mixin animated-button($url) { 4 | font-family: 'Roboto Mono', monospace; 5 | font-weight: bold; 6 | display: inline-block; 7 | align-items: center; 8 | transition: color 0.75s ease; 9 | background: none; 10 | color: $orange; 11 | position: relative; 12 | text-transform: uppercase; 13 | padding-right: 25px; 14 | z-index: 1; 15 | 16 | &::before { 17 | content:""; 18 | background: url($url) no-repeat; 19 | display: inline-block; 20 | height: 50px; 21 | width: 50px; 22 | margin-right: 13px; 23 | vertical-align: middle; 24 | } 25 | 26 | &:hover { 27 | color: #fff; 28 | } 29 | 30 | &::after { 31 | position: absolute; 32 | content: ''; 33 | top:0; 34 | left: 0; 35 | width: 0; 36 | height: 100%; 37 | background-color: $orange; 38 | transform-origin:right; 39 | transition:width 0.75s ease; 40 | z-index:-1; 41 | } 42 | 43 | &:hover::after { 44 | width: 100%; 45 | } 46 | } 47 | 48 | @mixin animated-button-right($url) { 49 | font-family: 'Roboto Mono', monospace; 50 | font-weight: bold; 51 | display: inline-block; 52 | align-items: center; 53 | transition: color 0.75s ease; 54 | background: none; 55 | color: $orange; 56 | position: relative; 57 | text-transform: uppercase; 58 | padding-left: 25px; 59 | z-index: 1; 60 | 61 | &::after { 62 | content:""; 63 | background: url($url) no-repeat; 64 | display: inline-block; 65 | height: 50px; 66 | width: 50px; 67 | margin-left: 13px; 68 | vertical-align: middle; 69 | } 70 | 71 | &:hover { 72 | color: #fff; 73 | } 74 | 75 | &::before { 76 | position: absolute; 77 | content: ''; 78 | top:0; 79 | right: 0; 80 | width: 0; 81 | height: 100%; 82 | background-color: $orange; 83 | transform-origin:right; 84 | transition:width 0.75s ease; 85 | z-index:-1; 86 | } 87 | 88 | &:hover::before { 89 | width: 100%; 90 | } 91 | } 92 | 93 | @mixin simple-button($url) { 94 | cursor:pointer; 95 | display: inline-block; 96 | color: $orange; 97 | position: relative; 98 | z-index: 1; 99 | 100 | &::before { 101 | content:""; 102 | background: url($url) no-repeat; 103 | transition: background-image 0.5s ease-in-out; 104 | display: inline-block; 105 | height: 50px; 106 | width: 50px; 107 | margin-right: 13px; 108 | vertical-align: middle; 109 | } 110 | } 111 | 112 | .internal-button-animate { 113 | @include animated-button('../img/icons/internal.svg'); 114 | } 115 | 116 | .external-button-animate { 117 | @include animated-button('../img/icons/external.svg'); 118 | } 119 | 120 | .play-button-animate { 121 | @include animated-button('../img/icons/video.svg'); 122 | } 123 | 124 | .internal-button-right-animate { 125 | @include animated-button-right('../img/icons/internal.svg'); 126 | } 127 | 128 | .external-button-right-animate { 129 | @include animated-button-right('../img/icons/external.svg'); 130 | } 131 | 132 | .play-button-right-animate { 133 | @include animated-button-right('../img/icons/video.svg'); 134 | } 135 | 136 | 137 | /* simple buttons */ 138 | .expand-button { 139 | @include simple-button('../img/icons/expand.svg'); 140 | } 141 | 142 | .close-button { 143 | @include simple-button('../img/icons/close.svg'); 144 | } 145 | 146 | .external-button { 147 | @include simple-button('../img/icons/external.svg'); 148 | } 149 | 150 | 151 | .download-button { 152 | cursor:pointer; 153 | display: inline-block; 154 | color: $orange; 155 | position: relative; 156 | z-index: 1; 157 | 158 | &::before { 159 | content:""; 160 | background: url('../img/icons/download.svg') no-repeat; 161 | display: inline-block; 162 | height: 100px; 163 | width: 50px; 164 | vertical-align: middle; 165 | } 166 | } 167 | 168 | /* hamburger */ 169 | .hamburger { 170 | position: relative; 171 | display: none; 172 | width: 1.25em; 173 | height: 0.8em; 174 | margin-right: 0.3em; 175 | border-top: 0.2em solid $grey; 176 | border-bottom: 0.2em solid $grey; 177 | font-size: 20px; 178 | } 179 | 180 | .hamburger:before { 181 | content: ""; 182 | position: absolute; 183 | top: 0.3em; 184 | left: 0px; 185 | width: 100%; 186 | border-top: 0.2em solid $grey; 187 | } 188 | 189 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const Encore = require('@symfony/webpack-encore'); 2 | 3 | Encore 4 | .setOutputPath('build') 5 | //.setPublicPath('/fresh-moab-sandbox/build') //for fresh site directory 6 | .setPublicPath('/build') 7 | .setManifestKeyPrefix('build') 8 | .addEntry('app', './js/app.js') 9 | .enableSingleRuntimeChunk() 10 | .enableSourceMaps(!Encore.isProduction()) 11 | .enableSassLoader() 12 | ; 13 | 14 | if (Encore.isProduction()) { 15 | Encore 16 | .setPublicPath('/moab/build') 17 | .cleanupOutputBeforeBuild() 18 | .enableVersioning() 19 | ; 20 | } 21 | 22 | module.exports = Encore.getWebpackConfig(); 23 | --------------------------------------------------------------------------------