From 6c03c13c85036d94944a0038e5d7129da81eba41 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Sat, 1 Sep 2018 12:19:48 -0400 Subject: [PATCH 1/7] 3 --- package-lock.json | 5 +++++ package.json | 1 + 2 files changed, 6 insertions(+) diff --git a/package-lock.json b/package-lock.json index 0f5e626432..589aa682a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7168,6 +7168,11 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "three": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.96.0.tgz", + "integrity": "sha512-tS+A5kelQgBblElc/E1G5zR3m6wNjbqmrf6OAjijuNJM7yoYQjOktPoa+Lglx73OTiTOJ3+Ff+pgWdOFt7cOhQ==" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index a4966cfd98..fcc0019797 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "react-select": "1.2.1", "relay-runtime": "1.6.0", "temp": "0.8.3", + "three": "0.96.0", "tinycolor2": "1.4.1", "tree-kill": "1.2.0", "what-the-diff": "0.4.0", From 77734fa161fb2f4386978000d7de2100f6d0afa4 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Sat, 1 Sep 2018 21:01:40 -0400 Subject: [PATCH 2/7] Allow PaneItem subtrees to defer rendering until they are added --- lib/atom/pane-item.js | 70 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/lib/atom/pane-item.js b/lib/atom/pane-item.js index f564d021d0..3021453145 100644 --- a/lib/atom/pane-item.js +++ b/lib/atom/pane-item.js @@ -39,7 +39,7 @@ export default class PaneItem extends React.Component { constructor(props) { super(props); - autobind(this, 'opener'); + autobind(this, 'opener', 'notifyItemAdded'); const uriPattern = new URIPattern(this.props.uriPattern); const currentlyOpen = this.props.workspace.getPaneItems() @@ -56,7 +56,9 @@ export default class PaneItem extends React.Component { return arr; }, []); + this.pendingNotification = new Map(); this.subs = new CompositeDisposable(); + this.state = {uriPattern, currentlyOpen}; } @@ -80,9 +82,13 @@ export default class PaneItem extends React.Component { if (this.props.className) { openItem.addClassName(this.props.className); } + openItem.didAddItem(); } - this.subs.add(this.props.workspace.addOpener(this.opener)); + this.subs.add( + this.props.workspace.onDidAddPaneItem(this.notifyItemAdded), + this.props.workspace.addOpener(this.opener), + ); } render() { @@ -99,6 +105,14 @@ export default class PaneItem extends React.Component { this.subs.dispose(); } + notifyItemAdded({item}) { + const openItem = this.pendingNotification.get(item); + if (openItem !== undefined) { + openItem.didAddItem(); + this.pendingNotification.delete(item); + } + } + opener(uri) { const m = this.state.uriPattern.matches(uri); if (!m.ok()) { @@ -118,6 +132,7 @@ export default class PaneItem extends React.Component { copy: () => this.copyOpenItem(openItem), }); this.registerCloseListener(paneItem, openItem); + this.pendingNotification.set(paneItem, openItem); resolve(paneItem); }); }); @@ -149,6 +164,7 @@ export default class PaneItem extends React.Component { if (item === paneItem) { sub.dispose(); this.subs.remove(sub); + this.pendingNotification.delete(item); this.setState(prevState => ({ currentlyOpen: prevState.currentlyOpen.filter(each => each !== openItem), })); @@ -159,6 +175,37 @@ export default class PaneItem extends React.Component { } } +const ItemAddedContext = React.createContext(Promise.resolve()); + +export class ItemAdded extends React.Component { + static propTypes = { + children: PropTypes.func.isRequired, + } + + constructor(props) { + super(props); + + this.state = {attached: false}; + } + + render() { + return ( + + + {addedPromise => { + addedPromise.then(() => { + if (!this.state.attached) { + this.setState({attached: true}); + } + }); + }} + + {this.state.attached ? this.props.children() : null} + + ); + } +} + /** * A subtree rendered through a portal onto a detached DOM node for use as the root as a PaneItem. */ @@ -174,6 +221,9 @@ class OpenItem { this.domNode.onfocus = this.onFocus.bind(this); this.stubItem = stub; this.match = match; + this.itemAddedPromise = new Promise(resolve => { + this.resolveItemAdded = resolve; + }); this.itemHolder = new RefHolder(); } @@ -217,13 +267,19 @@ class OpenItem { return this.itemHolder.map(item => item.focus && item.focus()); } + didAddItem() { + this.resolveItemAdded(); + } + renderPortal(renderProp) { return ReactDOM.createPortal( - renderProp({ - itemHolder: this.itemHolder, - params: this.match.getParams(), - uri: this.match.getURI(), - }), + + {renderProp({ + itemHolder: this.itemHolder, + params: this.match.getParams(), + uri: this.match.getURI(), + })} + , this.domNode, ); } From 1ab13b21f5fed8a123ae958e70cc5e27e8fe9f35 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Sat, 1 Sep 2018 21:02:47 -0400 Subject: [PATCH 3/7] Component to render a three.js scene --- lib/views/three-scene.js | 101 +++++++++++++++++++++++++++++++++++++++ styles/three-scene.less | 4 ++ 2 files changed, 105 insertions(+) create mode 100644 lib/views/three-scene.js create mode 100644 styles/three-scene.less diff --git a/lib/views/three-scene.js b/lib/views/three-scene.js new file mode 100644 index 0000000000..fd53ad548f --- /dev/null +++ b/lib/views/three-scene.js @@ -0,0 +1,101 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import * as Three from 'three'; +import {CompositeDisposable, Disposable} from 'event-kit'; + +import RefHolder from '../models/ref-holder'; +import {ItemAdded} from '../atom/pane-item'; +import {autobind} from '../helpers'; + +export default class ThreeScene extends React.Component { + static propTypes = { + fieldOfView: PropTypes.number, + nearClippingPlane: PropTypes.number, + farClippingPlane: PropTypes.number, + + setUp: PropTypes.func, + animate: PropTypes.func, + } + + static defaultProps = { + fieldOfView: 75, + nearClippingPlane: 0.1, + farClippingPlane: 1000, + + setUp: () => {}, + animate: () => {}, + } + + constructor(props) { + super(props); + autobind(this, 'setUpScene', 'animateScene'); + + this.subs = new CompositeDisposable(); + + this.refRoot = new RefHolder(); + + this.scene = null; + this.camera = null; + this.renderer = null; + this.active = false; + + this.subs.add( + this.refRoot.observe(this.setUpScene), + new Disposable(() => { this.active = false; }), + ); + } + + render() { + return ( + + {() =>
} + + ); + } + + componentWillUnmount() { + this.subs.dispose(); + } + + setUpScene(rootElement) { + if (!rootElement) { + this.active = false; + return; + } + this.active = true; + + const {width, height} = rootElement.getBoundingClientRect(); + console.log('ThreeScene::setUpScene', {width, height}); + + this.scene = new Three.Scene(); + + this.camera = new Three.PerspectiveCamera( + this.props.fieldOfView, + width / height, + this.props.nearClippingPlane, + this.props.farClippingPlane, + ); + + this.renderer = new Three.WebGLRenderer(); + this.renderer.setSize(width, height); + rootElement.appendChild(this.renderer.domElement); + + this.props.setUp({scene: this.scene, camera: this.camera}); + + this.animateScene(); + } + + animateScene() { + this.refRoot.map(rootElement => { + if (!this.active) { + return null; + } + + requestAnimationFrame(this.animateScene); + this.props.animate({scene: this.scene, camera: this.camera}); + this.renderer.render(this.scene, this.camera); + + return null; + }); + } +} diff --git a/styles/three-scene.less b/styles/three-scene.less new file mode 100644 index 0000000000..ee2f29305b --- /dev/null +++ b/styles/three-scene.less @@ -0,0 +1,4 @@ +.github-ThreeScene { + width: 100%; + height: 100%; +} From 7ec7756cbb5cfa11c2e221b4fa562b519020dd36 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Sat, 1 Sep 2018 21:03:03 -0400 Subject: [PATCH 4/7] "LogItem" that renders... a box --- lib/controllers/root-controller.js | 12 +++++++++++- lib/items/log-item.js | 27 +++++++++++++++++++++++++++ lib/views/log-view.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 lib/items/log-item.js create mode 100644 lib/views/log-view.js diff --git a/lib/controllers/root-controller.js b/lib/controllers/root-controller.js index 3144cbb2fe..0e23ab6b23 100644 --- a/lib/controllers/root-controller.js +++ b/lib/controllers/root-controller.js @@ -18,6 +18,7 @@ import FilePatchController from './file-patch-controller'; import IssueishDetailItem from '../items/issueish-detail-item'; import GitTabItem from '../items/git-tab-item'; import GitHubTabItem from '../items/github-tab-item'; +import LogItem from '../items/log-item'; import StatusBarTileController from './status-bar-tile-controller'; import RepositoryConflictController from './repository-conflict-controller'; import GitCacheView from '../views/git-cache-view'; @@ -64,7 +65,8 @@ export default class RootController extends React.Component { autobind( this, 'installReactDevTools', 'getRepositoryForWorkdir', 'clearGithubToken', 'initializeRepo', 'showOpenIssueishDialog', - 'showWaterfallDiagnostics', 'showCacheDiagnostics', 'acceptClone', 'cancelClone', 'acceptInit', 'cancelInit', + 'showLogItem', 'showWaterfallDiagnostics', 'showCacheDiagnostics', + 'acceptClone', 'cancelClone', 'acceptInit', 'cancelInit', 'acceptOpenIssueish', 'cancelOpenIssueish', 'surfaceFromFileAtPath', 'destroyFilePatchPaneItems', 'destroyEmptyFilePatchPaneItems', 'openCloneDialog', 'quietlySelectItem', 'viewUnstagedChangesForCurrentFile', 'viewStagedChangesForCurrentFile', 'openFiles', 'getUnsavedFiles', 'ensureNoUnsavedFiles', @@ -126,6 +128,7 @@ export default class RootController extends React.Component { + )} + + {({itemHolder}) => } + {({itemHolder}) => } @@ -423,6 +429,10 @@ export default class RootController extends React.Component { this.setState({openIssueishDialogActive: true}); } + showLogItem() { + return this.props.workspace.open(LogItem.buildURI()); + } + showWaterfallDiagnostics() { this.props.workspace.open(GitTimingsView.buildURI()); } diff --git a/lib/items/log-item.js b/lib/items/log-item.js new file mode 100644 index 0000000000..8cbd64fdd1 --- /dev/null +++ b/lib/items/log-item.js @@ -0,0 +1,27 @@ +import React from 'react'; + +import LogView from '../views/log-view'; + +export default class LogItem extends React.Component { + static uriPattern = 'atom-github://log' + + static buildURI() { + return this.uriPattern; + } + + render() { + return ; + } + + getTitle() { + return 'Git Log'; + } + + getIconName() { + return 'git-commit'; + } + + getURI() { + return this.constructor.uriPattern; + } +} diff --git a/lib/views/log-view.js b/lib/views/log-view.js new file mode 100644 index 0000000000..8c69f5dfb3 --- /dev/null +++ b/lib/views/log-view.js @@ -0,0 +1,29 @@ +import React from 'react'; +import * as Three from 'three'; + +import ThreeScene from './three-scene'; +import {autobind} from '../helpers'; + +export default class LogView extends React.Component { + constructor(props) { + super(props); + autobind(this, 'setUp', 'animate'); + } + + render() { + return ; + } + + setUp({scene, camera}) { + const geometry = new Three.BoxGeometry(1, 1, 1); + const material = new Three.MeshBasicMaterial({color: 0x00ff00}); + const cube = new Three.Mesh(geometry, material); + + scene.add(cube); + camera.position.z = 5; + } + + animate({scene}) { + // + } +} From fb6eed9ce0f93ec99e1c60f0e08015cb68ceae6e Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Sat, 1 Sep 2018 21:58:49 -0400 Subject: [PATCH 5/7] :fire: console.log --- lib/views/three-scene.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/views/three-scene.js b/lib/views/three-scene.js index fd53ad548f..3a2be9cdfc 100644 --- a/lib/views/three-scene.js +++ b/lib/views/three-scene.js @@ -65,7 +65,6 @@ export default class ThreeScene extends React.Component { this.active = true; const {width, height} = rootElement.getBoundingClientRect(); - console.log('ThreeScene::setUpScene', {width, height}); this.scene = new Three.Scene(); From c6d85a8f85b2088154215e316e4d11df509d6637 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Sat, 1 Sep 2018 21:59:31 -0400 Subject: [PATCH 6/7] Some rotation and a wireframe --- lib/views/log-view.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/views/log-view.js b/lib/views/log-view.js index 8c69f5dfb3..d0f146a7b8 100644 --- a/lib/views/log-view.js +++ b/lib/views/log-view.js @@ -8,6 +8,9 @@ export default class LogView extends React.Component { constructor(props) { super(props); autobind(this, 'setUp', 'animate'); + + this.cube = null; + this.wireframe = null; } render() { @@ -16,14 +19,23 @@ export default class LogView extends React.Component { setUp({scene, camera}) { const geometry = new Three.BoxGeometry(1, 1, 1); - const material = new Three.MeshBasicMaterial({color: 0x00ff00}); - const cube = new Three.Mesh(geometry, material); + const faceMaterial = new Three.MeshBasicMaterial({color: 0x339999}); + this.cube = new Three.Mesh(geometry, faceMaterial); + + const lineMaterial = new Three.MeshBasicMaterial({color: 0xffffff, wireframe: true}); + this.wireframe = new Three.Mesh(geometry, lineMaterial); + + scene.add(this.cube); + scene.add(this.wireframe); - scene.add(cube); camera.position.z = 5; } animate({scene}) { - // + this.cube.rotation.x += 0.01; + this.cube.rotation.y += 0.01; + + this.wireframe.rotation.x += 0.01; + this.wireframe.rotation.y += 0.01; } } From 133a42ebd9e7d80e63aa39567717fda817341731 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Sat, 1 Sep 2018 22:01:28 -0400 Subject: [PATCH 7/7] Enable antialiasing --- lib/views/three-scene.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/views/three-scene.js b/lib/views/three-scene.js index 3a2be9cdfc..4865424e9f 100644 --- a/lib/views/three-scene.js +++ b/lib/views/three-scene.js @@ -75,7 +75,7 @@ export default class ThreeScene extends React.Component { this.props.farClippingPlane, ); - this.renderer = new Three.WebGLRenderer(); + this.renderer = new Three.WebGLRenderer({antialias: true}); this.renderer.setSize(width, height); rootElement.appendChild(this.renderer.domElement);