Permalink
Showing
with
166 additions
and 168 deletions.
- +166 −168 Examples/Content/Scripts/helloSpringy.js
@@ -1,213 +1,211 @@ | ||
/// <reference path="typings/ue.d.ts">/> | ||
|
||
(function (global) { | ||
"use strict" | ||
"use strict" | ||
|
||
function GetPC() { | ||
return PlayerController.C(GWorld.GetAllActorsOfClass(PlayerController).OutActors[0]) | ||
} | ||
|
||
let Springy = require('springy') | ||
function GetPC() { | ||
return PlayerController.C(GWorld.GetAllActorsOfClass(PlayerController).OutActors[0]) | ||
} | ||
|
||
// Pure ES6 class (not a UClass) | ||
class SpringyRenderer { | ||
constructor(graph) { | ||
this.graph = graph | ||
let Springy = require('springy') | ||
|
||
// render command to accelerate batched drawing | ||
this.renderCommands = [] | ||
// Pure ES6 class (not a UClass) | ||
class SpringyRenderer { | ||
constructor(graph) { | ||
this.graph = graph | ||
|
||
// clear all render commands | ||
let clear = () => { | ||
this.renderCommands = [] | ||
} | ||
// render command to accelerate batched drawing | ||
this.renderCommands = [] | ||
|
||
// clear all render commands | ||
let clear = () => { | ||
this.renderCommands = [] | ||
} | ||
|
||
let layout = new Springy.Layout.ForceDirected(this.graph, 400, 400, 0.5) | ||
let currentBB = layout.getBoundingBox() | ||
|
||
let toScreen = (p) => { | ||
let size = currentBB.topright.subtract(currentBB.bottomleft) | ||
let sx = p.subtract(currentBB.bottomleft).divide(size.x).x * 200 + 200 | ||
let sy = p.subtract(currentBB.bottomleft).divide(size.y).y * 200 + 200 | ||
return new Springy.Vector(sx,sy) | ||
} | ||
let layout = new Springy.Layout.ForceDirected(this.graph, 400, 400, 0.5) | ||
let currentBB = layout.getBoundingBox() | ||
|
||
let lower = (p) => { | ||
return {X:p.x,Y:p.y} | ||
} | ||
let toScreen = (p) => { | ||
let size = currentBB.topright.subtract(currentBB.bottomleft) | ||
let sx = p.subtract(currentBB.bottomleft).divide(size.x).x * 200 + 200 | ||
let sy = p.subtract(currentBB.bottomleft).divide(size.y).y * 200 + 200 | ||
return new Springy.Vector(sx,sy) | ||
} | ||
|
||
let radius = 5 | ||
let lower = (p) => { | ||
return {X:p.x,Y:p.y} | ||
} | ||
|
||
let drawEdge = (edge,p1,p2) => { | ||
this.renderCommands.push((canvas) => { | ||
let s1 = toScreen(p1) | ||
let s2 = toScreen(p2) | ||
let radius = 5 | ||
|
||
let weight = edge.data.weight || 3 | ||
let bias = edge.data.bias || 0 | ||
let drawEdge = (edge,p1,p2) => { | ||
this.renderCommands.push((canvas) => { | ||
let s1 = toScreen(p1) | ||
let s2 = toScreen(p2) | ||
|
||
let direction = new Springy.Vector(s2.x - s1.x, s2.y - s1.y) | ||
let normal = direction.normalise() | ||
let perp = normal.normal() | ||
let weight = edge.data.weight || 3 | ||
let bias = edge.data.bias || 0 | ||
|
||
if (graph.getEdges(edge.target,edge.source).length > 0) { | ||
let offset = perp.multiply(3) | ||
let direction = new Springy.Vector(s2.x - s1.x, s2.y - s1.y) | ||
let normal = direction.normalise() | ||
let perp = normal.normal() | ||
|
||
s1 = s1.add(offset) | ||
s2 = s2.add(offset) | ||
} | ||
if (graph.getEdges(edge.target,edge.source).length > 0) { | ||
let offset = perp.multiply(3) | ||
|
||
let arrow = 4 | ||
s1 = s1.add(offset) | ||
s2 = s2.add(offset) | ||
} | ||
|
||
s1 = s1.add(normal.multiply(radius)) | ||
s2 = s2.add(normal.multiply(-radius)) | ||
let arrow = 4 | ||
|
||
let u = {x:perp.x * arrow,y:perp.y * arrow} | ||
let v = {x:normal.x * arrow,y:normal.y * arrow} | ||
let s3 = s2.add({x:-u.x-v.x*2,y:-u.y-v.y*2}) | ||
let s4 = s2.add({x:+u.x-v.x*2,y:+u.y-v.y*2}) | ||
s1 = s1.add(normal.multiply(radius)) | ||
s2 = s2.add(normal.multiply(-radius)) | ||
|
||
function gamma(x) { | ||
var y = {} | ||
for (var k in x) { | ||
y[k] = Math.pow(x[k] / 255.0, 2.2) | ||
} | ||
return y | ||
} | ||
let u = {x:perp.x * arrow,y:perp.y * arrow} | ||
let v = {x:normal.x * arrow,y:normal.y * arrow} | ||
let s3 = s2.add({x:-u.x-v.x*2,y:-u.y-v.y*2}) | ||
let s4 = s2.add({x:+u.x-v.x*2,y:+u.y-v.y*2}) | ||
|
||
function color_from_hex(hex) { | ||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) | ||
return result ? gamma({ | ||
R: parseInt(result[1], 16), | ||
G: parseInt(result[2], 16), | ||
B: parseInt(result[3], 16), | ||
A: 255 | ||
}) : {R:1,G:1,B:1,A:1} | ||
function gamma(x) { | ||
var y = {} | ||
for (var k in x) { | ||
y[k] = Math.pow(x[k] / 255.0, 2.2) | ||
} | ||
|
||
let color = color_from_hex(edge.data.color) | ||
|
||
canvas.DrawLine(lower(s1),lower(s2),weight,color) | ||
canvas.DrawLine(lower(s2),lower(s3),weight,color) | ||
canvas.DrawLine(lower(s2),lower(s4),weight,color) | ||
}) | ||
} | ||
|
||
let size = radius / Math.sqrt(2) * 2 | ||
let font = GEngine.SmallFont | ||
|
||
let drawNode = (node,p) => { | ||
this.renderCommands.push((canvas) => { | ||
let pos = lower(toScreen(p)) | ||
let size = canvas.ClippedTextSize(font,node.data.label,{X:1,Y:1}) | ||
size.X += size.Y * 0.7 | ||
size.Y *= 1.3 | ||
pos.X -= size.X/2 | ||
pos.Y -= size.Y/2 | ||
canvas.DrawTexture(null,pos,size,{},{},{A:0.2},'BLEND_Translucent') | ||
canvas.DrawText(font,node.data.label,lower(toScreen(p)),{R:1,G:1,B:1,A:1},0,{R:0,G:0,B:0,A:1},{X:1,Y:1},true,true,true,{R:0,G:0,B:0,A:1}) | ||
}) | ||
} | ||
|
||
this.renderer = new Springy.Renderer(layout,clear,drawEdge,drawNode) | ||
} | ||
|
||
init() { | ||
this.renderer.start() | ||
return y | ||
} | ||
|
||
function color_from_hex(hex) { | ||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) | ||
return result ? gamma({ | ||
R: parseInt(result[1], 16), | ||
G: parseInt(result[2], 16), | ||
B: parseInt(result[3], 16), | ||
A: 255 | ||
}) : {R:1,G:1,B:1,A:1} | ||
} | ||
|
||
let color = color_from_hex(edge.data.color) | ||
|
||
canvas.DrawLine(lower(s1),lower(s2),weight,color) | ||
canvas.DrawLine(lower(s2),lower(s3),weight,color) | ||
canvas.DrawLine(lower(s2),lower(s4),weight,color) | ||
}) | ||
} | ||
|
||
uninit() { | ||
this.renderer.stop() | ||
let size = radius / Math.sqrt(2) * 2 | ||
let font = GEngine.SmallFont | ||
|
||
let drawNode = (node,p) => { | ||
this.renderCommands.push((canvas) => { | ||
let pos = lower(toScreen(p)) | ||
let size = canvas.ClippedTextSize(font,node.data.label,{X:1,Y:1}) | ||
size.X += size.Y * 0.7 | ||
size.Y *= 1.3 | ||
pos.X -= size.X/2 | ||
pos.Y -= size.Y/2 | ||
canvas.DrawTexture(null,pos,size,{},{},{A:0.2},'BLEND_Translucent') | ||
canvas.DrawText(font,node.data.label,lower(toScreen(p)),{R:1,G:1,B:1,A:1},0,{R:0,G:0,B:0,A:1},{X:1,Y:1},true,true,true,{R:0,G:0,B:0,A:1}) | ||
}) | ||
} | ||
|
||
// kick all render commands! | ||
draw(Canvas) { | ||
this.renderCommands.forEach((cmd) => cmd(Canvas)) | ||
} | ||
this.renderer = new Springy.Renderer(layout,clear,drawEdge,drawNode) | ||
} | ||
|
||
function setup_graph(graph) { | ||
graph.addNodes('Dennis', 'Michael', 'Jessica', 'Timothy', 'Barbara') | ||
graph.addNodes('Amphitryon', 'Alcmene', 'Iphicles', 'Heracles'); | ||
graph.addEdges( | ||
['Dennis', 'Michael', {color: '#00A0B0', label: 'Foo bar'}], | ||
['Michael', 'Dennis', {color: '#6A4A3C'}], | ||
['Michael', 'Jessica', {color: '#CC333F'}], | ||
['Jessica', 'Barbara', {color: '#EB6841'}], | ||
['Michael', 'Timothy', {color: '#EDC951'}], | ||
['Amphitryon', 'Alcmene', {color: '#7DBE3C'}], | ||
['Alcmene', 'Amphitryon', {color: '#BE7D3C'}], | ||
['Amphitryon', 'Iphicles'], | ||
['Amphitryon', 'Heracles'], | ||
['Barbara', 'Timothy', {color: '#6A4A3C'}] | ||
); | ||
init() { | ||
this.renderer.start() | ||
} | ||
|
||
function main() { | ||
let PC = GetPC() | ||
|
||
let UMG = require('UMG') | ||
uninit() { | ||
this.renderer.stop() | ||
} | ||
|
||
// Javascript proxy may be garbage collected at any time. | ||
// Because our proxy has special member 'renderers', we should | ||
// keep our proxy not to be gc'ed by holding its reference into | ||
// global variable 'pool'. | ||
let pool = [] | ||
// kick all render commands! | ||
draw(Canvas) { | ||
this.renderCommands.forEach((cmd) => cmd(Canvas)) | ||
} | ||
} | ||
|
||
function setup_graph(graph) { | ||
graph.addNodes('Dennis', 'Michael', 'Jessica', 'Timothy', 'Barbara') | ||
graph.addNodes('Amphitryon', 'Alcmene', 'Iphicles', 'Heracles'); | ||
graph.addEdges( | ||
['Dennis', 'Michael', {color: '#00A0B0', label: 'Foo bar'}], | ||
['Michael', 'Dennis', {color: '#6A4A3C'}], | ||
['Michael', 'Jessica', {color: '#CC333F'}], | ||
['Jessica', 'Barbara', {color: '#EB6841'}], | ||
['Michael', 'Timothy', {color: '#EDC951'}], | ||
['Amphitryon', 'Alcmene', {color: '#7DBE3C'}], | ||
['Alcmene', 'Amphitryon', {color: '#BE7D3C'}], | ||
['Amphitryon', 'Iphicles'], | ||
['Amphitryon', 'Heracles'], | ||
['Barbara', 'Timothy', {color: '#6A4A3C'}] | ||
); | ||
} | ||
|
||
function main() { | ||
let PC = GetPC() | ||
|
||
let UMG = require('UMG') | ||
|
||
// Javascript proxy may be garbage collected at any time. | ||
// Because our proxy has special member 'renderers', we should | ||
// keep our proxy not to be gc'ed by holding its reference into | ||
// global variable 'pool'. | ||
let pool = [] | ||
|
||
let Springy = require('springy') | ||
let graph = new Springy.Graph() | ||
let Springy = require('springy') | ||
let graph = new Springy.Graph() | ||
|
||
setup_graph(graph) | ||
setup_graph(graph) | ||
|
||
// Declare our own HUD class | ||
class MyHUD extends HUD { | ||
// init | ||
ReceiveBeginPlay() { | ||
// guard this object from V8 GC | ||
pool.push(this) | ||
// Declare our own HUD class | ||
class MyHUD extends HUD { | ||
// init | ||
ReceiveBeginPlay() { | ||
// guard this object from V8 GC | ||
pool.push(this) | ||
|
||
this.renderers = []; | ||
this.renderers.push(new SpringyRenderer(graph)) | ||
this.renderers = []; | ||
this.renderers.push(new SpringyRenderer(graph)) | ||
|
||
super.ReceiveBeginPlay() | ||
this.renderers.forEach((r) => r.init && r.init()) | ||
} | ||
super.ReceiveBeginPlay() | ||
this.renderers.forEach((r) => r.init && r.init()) | ||
} | ||
|
||
// clean up | ||
ReceiveEndPlay() { | ||
this.renderers.forEach((r) => r.uninit && r.uninit()) | ||
super.ReceiveEndPlay() | ||
} | ||
// clean up | ||
ReceiveEndPlay() { | ||
this.renderers.forEach((r) => r.uninit && r.uninit()) | ||
super.ReceiveEndPlay() | ||
} | ||
|
||
// Override drawing function | ||
ReceiveDrawHUD() { | ||
super.ReceiveDrawHUD() | ||
this.renderers.forEach((r) => r.draw(this.Canvas)) | ||
} | ||
// Override drawing function | ||
ReceiveDrawHUD() { | ||
super.ReceiveDrawHUD() | ||
this.renderers.forEach((r) => r.draw(this.Canvas)) | ||
} | ||
} | ||
|
||
let MyHUD_C = require('uclass')()(global,MyHUD) | ||
GetPC().ClientSetHUD(MyHUD_C) | ||
let MyHUD_C = require('uclass')()(global,MyHUD) | ||
GetPC().ClientSetHUD(MyHUD_C) | ||
|
||
return function () { | ||
// To destroy our own HUD. | ||
GetPC().ClientSetHUD(HUD) | ||
return function () { | ||
// To destroy our own HUD. | ||
GetPC().ClientSetHUD(HUD) | ||
|
||
// this sentence keeps 'capture' of pool so that 'pool' will | ||
// be alive during lifetime 'main'. | ||
pool = [] | ||
} | ||
// this sentence keeps 'capture' of pool so that 'pool' will | ||
// be alive during lifetime 'main'. | ||
pool = [] | ||
} | ||
} | ||
|
||
try { | ||
module.exports = () => { | ||
let cleanup = null | ||
process.nextTick(() => cleanup = main()); | ||
return () => cleanup() | ||
} | ||
} | ||
catch (e) { | ||
require('bootstrap')('helloSpringy') | ||
try { | ||
module.exports = () => { | ||
let cleanup = null | ||
process.nextTick(() => cleanup = main()); | ||
return () => cleanup() | ||
} | ||
})(this) | ||
} | ||
catch (e) { | ||
require('bootstrap')('helloSpringy') | ||
} |