Initial vecty commit

This commit is contained in:
Ivan Danyliuk 2018-09-05 16:53:09 +03:00
parent 93634ccd42
commit e02c49d61b
No known key found for this signature in database
GPG Key ID: 97ED33CE024E1DBF
14 changed files with 33945 additions and 0 deletions

24
animate.go Normal file
View File

@ -0,0 +1,24 @@
package main
import "github.com/gopherjs/gopherjs/js"
func (p *Page) animate() {
if p.renderer == nil {
return
}
p.controls.Update()
js.Global.Call("requestAnimationFrame", p.animate)
if p.autoRotate {
pos := p.group.Object.Get("rotation")
pos.Set("y", pos.Get("y").Float()+float64(0.01))
}
p.renderer.Render(p.scene, p.camera)
}
func (p *Page) ToggleAutoRotation() {
p.autoRotate = !p.autoRotate
}

21
controls.go Normal file
View File

@ -0,0 +1,21 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
"github.com/lngramos/three"
)
type TrackBallControl struct {
*js.Object
}
func NewTrackBallControl(camera three.PerspectiveCamera, renderer *three.WebGLRenderer) TrackBallControl {
dom := renderer.Get("domElement")
return TrackBallControl{
Object: js.Global.Get("THREE").Get("TrackballControls").New(camera, dom),
}
}
func (t TrackBallControl) Update() {
t.Call("update")
}

12
css/controls.css Normal file
View File

@ -0,0 +1,12 @@
body {
background: #c0c0c0 -webkit-gradient(linear, left top, left bottom, from(#fff), to(#c0c0c0)) no-repeat;
padding-left: 10px;
padding-right: 10px;
height: 1000px;
}
h1 {
font-family: "Helvetica Neue";
font-weight: 300;
}

11
css/pure-min.css vendored Normal file

File diff suppressed because one or more lines are too long

1028
data.go Normal file

File diff suppressed because it is too large Load Diff

43
geom_ethereum.go Normal file
View File

@ -0,0 +1,43 @@
package main
import "github.com/lngramos/three"
func NewEthereumGeometry(zoom float64) three.Geometry {
var geom = three.NewBasicGeometry(three.BasicGeometryParams{})
vertices := []struct {
x, y, z float64
}{
{zoom * 1.0, 0.0, 0.0},
{zoom * -1.0, 0.0, 0.0},
{0.0, zoom * 1.5, 0.0},
{0.0, zoom * -1.5, 0.0},
{0.0, 0.0, zoom * 1.0},
{0.0, 0.0, zoom * -1.0},
}
for _, v := range vertices {
geom.AddVertice(v.x, v.y, v.z)
}
faces := []struct {
a, b, c int
}{
{0, 2, 4},
{0, 4, 3},
{0, 3, 5},
{0, 5, 2},
{1, 2, 5},
{1, 5, 3},
{1, 3, 4},
{1, 4, 2},
}
for _, f := range faces {
geom.AddFace(f.a, f.b, f.c)
}
geom.ComputeBoundingSphere()
geom.ComputeFaceNormals()
return geom
}

14
index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js"></script>
<script src="js/TrackballControls.js"></script>
<script src="whispervis.js"></script>
</body>
</html>

625
js/TrackballControls.js Normal file
View File

@ -0,0 +1,625 @@
/**
* @author Eberhard Graether / http://egraether.com/
* @author Mark Lundin / http://mark-lundin.com
* @author Simone Manini / http://daron1337.github.io
* @author Luca Antiga / http://lantiga.github.io
*/
THREE.TrackballControls = function ( object, domElement ) {
var _this = this;
var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
this.enabled = true;
this.screen = { left: 0, top: 0, width: 0, height: 0 };
this.rotateSpeed = 1.0;
this.zoomSpeed = 1.2;
this.panSpeed = 0.3;
this.noRotate = false;
this.noZoom = false;
this.noPan = false;
this.staticMoving = false;
this.dynamicDampingFactor = 0.2;
this.minDistance = 0;
this.maxDistance = Infinity;
this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
// internals
this.target = new THREE.Vector3();
var EPS = 0.000001;
var lastPosition = new THREE.Vector3();
var _state = STATE.NONE,
_prevState = STATE.NONE,
_eye = new THREE.Vector3(),
_movePrev = new THREE.Vector2(),
_moveCurr = new THREE.Vector2(),
_lastAxis = new THREE.Vector3(),
_lastAngle = 0,
_zoomStart = new THREE.Vector2(),
_zoomEnd = new THREE.Vector2(),
_touchZoomDistanceStart = 0,
_touchZoomDistanceEnd = 0,
_panStart = new THREE.Vector2(),
_panEnd = new THREE.Vector2();
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.up0 = this.object.up.clone();
// events
var changeEvent = { type: 'change' };
var startEvent = { type: 'start' };
var endEvent = { type: 'end' };
// methods
this.handleResize = function () {
if ( this.domElement === document ) {
this.screen.left = 0;
this.screen.top = 0;
this.screen.width = window.innerWidth;
this.screen.height = window.innerHeight;
} else {
var box = this.domElement.getBoundingClientRect();
// adjustments come from similar code in the jquery offset() function
var d = this.domElement.ownerDocument.documentElement;
this.screen.left = box.left + window.pageXOffset - d.clientLeft;
this.screen.top = box.top + window.pageYOffset - d.clientTop;
this.screen.width = box.width;
this.screen.height = box.height;
}
};
this.handleEvent = function ( event ) {
if ( typeof this[ event.type ] == 'function' ) {
this[ event.type ]( event );
}
};
var getMouseOnScreen = ( function () {
var vector = new THREE.Vector2();
return function getMouseOnScreen( pageX, pageY ) {
vector.set(
( pageX - _this.screen.left ) / _this.screen.width,
( pageY - _this.screen.top ) / _this.screen.height
);
return vector;
};
}() );
var getMouseOnCircle = ( function () {
var vector = new THREE.Vector2();
return function getMouseOnCircle( pageX, pageY ) {
vector.set(
( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / ( _this.screen.width * 0.5 ) ),
( ( _this.screen.height + 2 * ( _this.screen.top - pageY ) ) / _this.screen.width ) // screen.width intentional
);
return vector;
};
}() );
this.rotateCamera = ( function() {
var axis = new THREE.Vector3(),
quaternion = new THREE.Quaternion(),
eyeDirection = new THREE.Vector3(),
objectUpDirection = new THREE.Vector3(),
objectSidewaysDirection = new THREE.Vector3(),
moveDirection = new THREE.Vector3(),
angle;
return function rotateCamera() {
moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
angle = moveDirection.length();
if ( angle ) {
_eye.copy( _this.object.position ).sub( _this.target );
eyeDirection.copy( _eye ).normalize();
objectUpDirection.copy( _this.object.up ).normalize();
objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
axis.crossVectors( moveDirection, _eye ).normalize();
angle *= _this.rotateSpeed;
quaternion.setFromAxisAngle( axis, angle );
_eye.applyQuaternion( quaternion );
_this.object.up.applyQuaternion( quaternion );
_lastAxis.copy( axis );
_lastAngle = angle;
} else if ( ! _this.staticMoving && _lastAngle ) {
_lastAngle *= Math.sqrt( 1.0 - _this.dynamicDampingFactor );
_eye.copy( _this.object.position ).sub( _this.target );
quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
_eye.applyQuaternion( quaternion );
_this.object.up.applyQuaternion( quaternion );
}
_movePrev.copy( _moveCurr );
};
}() );
this.zoomCamera = function () {
var factor;
if ( _state === STATE.TOUCH_ZOOM_PAN ) {
factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
_touchZoomDistanceStart = _touchZoomDistanceEnd;
_eye.multiplyScalar( factor );
} else {
factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
if ( factor !== 1.0 && factor > 0.0 ) {
_eye.multiplyScalar( factor );
}
if ( _this.staticMoving ) {
_zoomStart.copy( _zoomEnd );
} else {
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
};
this.panCamera = ( function() {
var mouseChange = new THREE.Vector2(),
objectUp = new THREE.Vector3(),
pan = new THREE.Vector3();
return function panCamera() {
mouseChange.copy( _panEnd ).sub( _panStart );
if ( mouseChange.lengthSq() ) {
mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
_this.object.position.add( pan );
_this.target.add( pan );
if ( _this.staticMoving ) {
_panStart.copy( _panEnd );
} else {
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
}
}
};
}() );
this.checkDistances = function () {
if ( ! _this.noZoom || ! _this.noPan ) {
if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
_this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
_zoomStart.copy( _zoomEnd );
}
if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
_this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
_zoomStart.copy( _zoomEnd );
}
}
};
this.update = function () {
_eye.subVectors( _this.object.position, _this.target );
if ( ! _this.noRotate ) {
_this.rotateCamera();
}
if ( ! _this.noZoom ) {
_this.zoomCamera();
}
if ( ! _this.noPan ) {
_this.panCamera();
}
_this.object.position.addVectors( _this.target, _eye );
_this.checkDistances();
_this.object.lookAt( _this.target );
if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) {
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
}
};
this.reset = function () {
_state = STATE.NONE;
_prevState = STATE.NONE;
_this.target.copy( _this.target0 );
_this.object.position.copy( _this.position0 );
_this.object.up.copy( _this.up0 );
_eye.subVectors( _this.object.position, _this.target );
_this.object.lookAt( _this.target );
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
};
// listeners
function keydown( event ) {
if ( _this.enabled === false ) return;
window.removeEventListener( 'keydown', keydown );
_prevState = _state;
if ( _state !== STATE.NONE ) {
return;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ) {
_state = STATE.ROTATE;
} else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && ! _this.noZoom ) {
_state = STATE.ZOOM;
} else if ( event.keyCode === _this.keys[ STATE.PAN ] && ! _this.noPan ) {
_state = STATE.PAN;
}
}
function keyup( event ) {
if ( _this.enabled === false ) return;
_state = _prevState;
window.addEventListener( 'keydown', keydown, false );
}
function mousedown( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.NONE ) {
_state = event.button;
}
if ( _state === STATE.ROTATE && ! _this.noRotate ) {
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
_movePrev.copy( _moveCurr );
} else if ( _state === STATE.ZOOM && ! _this.noZoom ) {
_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
_zoomEnd.copy( _zoomStart );
} else if ( _state === STATE.PAN && ! _this.noPan ) {
_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
_panEnd.copy( _panStart );
}
document.addEventListener( 'mousemove', mousemove, false );
document.addEventListener( 'mouseup', mouseup, false );
_this.dispatchEvent( startEvent );
}
function mousemove( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.ROTATE && ! _this.noRotate ) {
_movePrev.copy( _moveCurr );
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
} else if ( _state === STATE.ZOOM && ! _this.noZoom ) {
_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
} else if ( _state === STATE.PAN && ! _this.noPan ) {
_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
}
}
function mouseup( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
_state = STATE.NONE;
document.removeEventListener( 'mousemove', mousemove );
document.removeEventListener( 'mouseup', mouseup );
_this.dispatchEvent( endEvent );
}
function mousewheel( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.deltaMode ) {
case 2:
// Zoom in pages
_zoomStart.y -= event.deltaY * 0.025;
break;
case 1:
// Zoom in lines
_zoomStart.y -= event.deltaY * 0.01;
break;
default:
// undefined, 0, assume pixels
_zoomStart.y -= event.deltaY * 0.00025;
break;
}
_this.dispatchEvent( startEvent );
_this.dispatchEvent( endEvent );
}
function touchstart( event ) {
if ( _this.enabled === false ) return;
switch ( event.touches.length ) {
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_movePrev.copy( _moveCurr );
break;
default: // 2 or more
_state = STATE.TOUCH_ZOOM_PAN;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panStart.copy( getMouseOnScreen( x, y ) );
_panEnd.copy( _panStart );
break;
}
_this.dispatchEvent( startEvent );
}
function touchmove( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ) {
case 1:
_movePrev.copy( _moveCurr );
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
break;
default: // 2 or more
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panEnd.copy( getMouseOnScreen( x, y ) );
break;
}
}
function touchend( event ) {
if ( _this.enabled === false ) return;
switch ( event.touches.length ) {
case 0:
_state = STATE.NONE;
break;
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_movePrev.copy( _moveCurr );
break;
}
_this.dispatchEvent( endEvent );
}
function contextmenu( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
}
this.dispose = function() {
this.domElement.removeEventListener( 'contextmenu', contextmenu, false );
this.domElement.removeEventListener( 'mousedown', mousedown, false );
this.domElement.removeEventListener( 'wheel', mousewheel, false );
this.domElement.removeEventListener( 'touchstart', touchstart, false );
this.domElement.removeEventListener( 'touchend', touchend, false );
this.domElement.removeEventListener( 'touchmove', touchmove, false );
document.removeEventListener( 'mousemove', mousemove, false );
document.removeEventListener( 'mouseup', mouseup, false );
window.removeEventListener( 'keydown', keydown, false );
window.removeEventListener( 'keyup', keyup, false );
};
this.domElement.addEventListener( 'contextmenu', contextmenu, false );
this.domElement.addEventListener( 'mousedown', mousedown, false );
this.domElement.addEventListener( 'wheel', mousewheel, false );
this.domElement.addEventListener( 'touchstart', touchstart, false );
this.domElement.addEventListener( 'touchend', touchend, false );
this.domElement.addEventListener( 'touchmove', touchmove, false );
window.addEventListener( 'keydown', keydown, false );
window.addEventListener( 'keyup', keyup, false );
this.handleResize();
// force an update at start
this.update();
};
THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
THREE.TrackballControls.prototype.constructor = THREE.TrackballControls;

12
keys.go Normal file
View File

@ -0,0 +1,12 @@
package main
import "github.com/gopherjs/vecty"
// KeyListener implements listener for keydown events.
func (p *Page) KeyListener(e *vecty.Event) {
key := e.Get("key").String()
switch key {
case "p":
p.ToggleAutoRotation()
}
}

100
main.go Normal file
View File

@ -0,0 +1,100 @@
package main
import (
"bytes"
"github.com/divan/graphx/formats"
"github.com/divan/graphx/layout"
"github.com/gopherjs/gopherjs/js"
"github.com/gopherjs/vecty"
"github.com/gopherjs/vecty/elem"
"github.com/gopherjs/vecty/event"
"github.com/lngramos/three"
"github.com/vecty/vthree"
)
func main() {
buf := bytes.NewBuffer(inputJSON)
data, err := formats.FromD3JSONReader(buf)
if err != nil {
panic(err)
}
l := layout.NewAuto(data)
l.CalculateN(50)
page := &Page{
layout: l,
}
vecty.SetTitle("Whisper Simulation")
vecty.AddStylesheet("css/pure-min.css")
vecty.AddStylesheet("css/controls.css")
vecty.RenderBody(page)
}
// Page is our main page component.
type Page struct {
vecty.Core
layout *layout.Layout
scene *three.Scene
camera three.PerspectiveCamera
renderer *three.WebGLRenderer
group *three.Group
controls TrackBallControl
autoRotate bool
}
// Render implements the vecty.Component interface.
func (p *Page) Render() vecty.ComponentOrHTML {
return elem.Body(
elem.Div(
vecty.Markup(vecty.Class("pure-g")),
elem.Div(
vecty.Markup(vecty.Class("pure-u-1-5")),
elem.Heading1(vecty.Text("Whisper Message Propagation")),
elem.Paragraph(vecty.Text("This visualization represents message propagation in the p2p network.")),
),
elem.Div(
vecty.Markup(vecty.Class("pure-u-4-5")),
vthree.WebGLRenderer(vthree.WebGLOptions{
Init: p.init,
Shutdown: p.shutdown,
}),
),
),
vecty.Markup(
event.KeyDown(p.KeyListener),
),
)
}
func (p *Page) init(renderer *three.WebGLRenderer) {
windowWidth := js.Global.Get("innerWidth").Float()*80/100 - 20
windowHeight := js.Global.Get("innerHeight").Float() - 20
p.renderer = renderer
p.renderer.SetSize(windowWidth, windowHeight, true)
devicePixelRatio := js.Global.Get("devicePixelRatio").Float()
p.renderer.SetPixelRatio(devicePixelRatio)
p.InitScene(windowWidth, windowHeight)
p.CreateObjects()
// Begin animating.
p.animate()
}
func (p *Page) shutdown(renderer *three.WebGLRenderer) {
// After shutdown, we shouldn't use any of these anymore.
p.scene = nil
p.camera = three.PerspectiveCamera{}
p.renderer = nil
p.group = nil
}

41
objects.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"github.com/lngramos/three"
)
func (p *Page) CreateObjects() {
p.group = three.NewGroup()
p.scene.Add(p.group)
geometry := NewEthereumGeometry(2)
materialParams := three.NewMaterialParameters()
materialParams.Color = three.NewColor(0, 255, 0)
material := three.NewMeshPhongMaterial(materialParams)
for _, node := range p.layout.Positions() {
mesh := three.NewMesh(geometry, material)
mesh.Position.Set(node.X, node.Y, node.Z)
p.group.Add(mesh)
}
// Lines
lineMaterialParams := three.NewMaterialParameters()
lineMaterialParams.Color = three.NewColor(200, 200, 255)
lineMaterial := three.NewLineBasicMaterial(lineMaterialParams)
for _, link := range p.layout.Links() {
from := link.From()
to := link.To()
start := p.layout.Positions()[from]
end := p.layout.Positions()[to]
var geom = three.NewBasicGeometry(three.BasicGeometryParams{})
geom.AddVertice(start.X, start.Y, start.Z)
geom.AddVertice(end.X, end.Y, end.Z)
line := three.NewLine(geom, lineMaterial)
p.group.Add(line)
}
}

29
scene.go Normal file
View File

@ -0,0 +1,29 @@
package main
import "github.com/lngramos/three"
// InitScene inits a new scene, sets up camera, lights and all that.
func (p *Page) InitScene(width, height float64) {
p.camera = three.NewPerspectiveCamera(70, width/height, 1, 1000)
p.camera.Position.Set(0, 0, 400)
p.scene = three.NewScene()
p.InitLights()
p.InitControls()
}
// InitLights init lights for the scene.
func (p *Page) InitLights() {
ambLight := three.NewAmbientLight(three.NewColor(187, 187, 187), 0.5)
p.scene.Add(ambLight)
light := three.NewDirectionalLight(three.NewColor(255, 255, 255), 0.3)
//light.Position.Set(256, 256, 256).Normalize()
p.scene.Add(light)
}
// InitControls init controls for the scene.
func (p *Page) InitControls() {
p.controls = NewTrackBallControl(p.camera, p.renderer)
}

31984
whispervis.js Normal file

File diff suppressed because one or more lines are too long

1
whispervis.js.map Normal file

File diff suppressed because one or more lines are too long