g9 Gallery | Robotically Interactive Graphics
![](https://blinkingrobots.com/wp-content/uploads/2023/08/g9-Gallery-Automatically-Interactive-Graphics.png)
Welcome to the g9 gallery! Drag all of the graphics!
I’ve tried to prepare stuff from primary to superior, so you may scroll from the highest to get an intuitive really feel for g9. It is value mentioning, after all, that intuitive feeling isn’t any match for reading the docs.
Earlier than you dive in, here is a quick clarification of how g9 works (scroll all the way down to skip):
How It Works
g9.js exposes a single operate, g9(initialData, render[, onChange]). This represents the next circulate:
-
You create some preliminary knowledge as key-value pairs with numeric values. For instance:
var initialData = { foo: 10 }
-
This knowledge and a drawing context are fed right into a operate that makes use of the information to determine what to attract.
operate render(knowledge, ctx){ ctx.level(knowledge.foo, 17) }
- When somebody interacts with the graphics, for instance by attempting to pull a component to a brand new place, g9 optimizes over the area of potential values on your knowledge to discover a set of values that comes closest to creating the change. Should you tried to pull the circle to the left, g9 may give you the brand new knowledge {foo: 8 }.
- g9 rerenders your complete scene with the brand new knowledge, in order that all the pieces is constant. Should you’ve offered one, g9 calls your onChange callback with the brand new knowledge, so you may replace different components of your web page.
For an entire therapy of the g9 API, head to the Docs.
A minimal instance with solely two factors. Our render operate all the time attracts one level at (x, y), and the opposite level at (y, x), so while you drag one of many factors, the opposite sort of mirrors it. Attempt including a minus signal earlier than the primary argument of the primary level. What occurs?
Run
var render = operate(knowledge, ctx){
ctx.level(knowledge.x, knowledge.y)
ctx.level(knowledge.y, knowledge.x)
}
g9(initialData, render)
.align(‘middle’, ‘middle’)
.insertInto(‘#demo-basic’)
Lets add a number of extra factors and use a bit or trigonometry to rearrange them into two circles. Only for enjoyable, we make the interior factors crimson. Attempt dragging the factors!
Run
operate render(knowledge, ctx){
var sides = 10
for(var i=0; i<sides; i++){
var a = knowledge.angle + i/sides * Math.PI * 2
var r1 = knowledge.radius
var r2 = r1/2
ctx.level(r1 * Math.cos( a), r1 * Math.sin( a))
ctx.level(r2 * Math.cos(-a), r2 * Math.sin(-a), {fill: ‘crimson’})
/* uncomment this following line to make this
drawing extra thrilling! */
/*ctx.line(r1 * Math.cos( a), r1 * Math.sin( a),
r2 * Math.cos(-a), r2 * Math.sin(-a)) */
}
}
g9(initialData, render)
.align(‘middle’, ‘middle’)
.insertInto(‘#demo-rings’)
The entire default shapes in g9 settle for an impacts possibility, which tells them what components of the information they will change. Listed below are three traces with completely different values of impacts.
Run
l2_start_x: -100,
l2_start_y: 0,
l2_end_x: 100,
l2_end_y: 0,
l3_start_x: -100,
l3_start_y: 50,
l3_end_x: 100,
l3_end_y: 50,
}
var render = operate(knowledge, ctx){
ctx.line(knowledge.l1_start_x, knowledge.l1_start_y,
knowledge.l1_end_x, knowledge.l1_end_y,
{‘stroke-width’: 10})
// if we offer an `impacts` possibility, this
// line will solely change the parameters we title.
ctx.line(knowledge.l2_start_x, knowledge.l2_start_y,
knowledge.l2_end_x, knowledge.l2_end_y, {
impacts: [‘l2_start_x’, ‘l2_start_y’],
stroke: ‘crimson’,
‘stroke-width’: 10
})
ctx.line(knowledge.l3_start_x, knowledge.l3_start_y,
knowledge.l3_end_x, knowledge.l3_end_y, {
impacts: [‘l3_start_x’, ‘l3_end_y’],
‘stroke’:’blue’,
‘stroke-width’: 10
})
}
g9(initialData, render)
.align(‘middle’, ‘middle’)
.insertInto(‘#demo-lines’)
This is an interactive model of the quadratic bézier curve animation on the bézier curve wikipedia page.
Run
operate render(knowledge, ctx){
var endx = knowledge.endx,
endy = knowledge.endy,
middlex = knowledge.middlex,
middley = knowledge.middley,
startx = knowledge.startx,
starty = knowledge.starty,
width = ctx.width,
peak = ctx.peak,
t = knowledge.t;
var tlabel=”tween=”+t.toString().slice(0,4)
ctx.textual content(tlabel, (t- .5)*300, peak/2 – 30, {alignmentBaseline: “center”})
var steps = 30
var easy = []
for (var i = 0; i
Run
operate invert(m){
var [[a,b], [c,d]] = m
var det = a*d – b*c
return [[d/det, -b/det], [-c/det, a/det]]
}
operate multiply(A, x){
var [[a,b], [c,d]] = A
var [x, y] = x
return [a*x + b*y, c*x+d*y]
}
operate render(knowledge, ctx){
var mx1 = (knowledge.x1 + knowledge.x2)/2
var my1 = (knowledge.y1 + knowledge.y2)/2
var dy1 = knowledge.y1 – knowledge.y2
var dx1 = -(knowledge.x1 – knowledge.x2)
var mx2 = (knowledge.x3 + knowledge.x2)/2
var my2 = (knowledge.y3 + knowledge.y2)/2
var dy2 = knowledge.y2 – knowledge.y3
var dx2 = -(knowledge.x2 – knowledge.x3)
var I = invert([[dy1, -dy2],
[dx1, -dx2]])
var [a,b] = multiply(I, [mx2-mx1, my2-my1])
ctx.line(
knowledge.x1,
knowledge.y1,
knowledge.x2,
knowledge.y2,
{stroke: ‘crimson’})
ctx.line(
knowledge.x3,
knowledge.y3,
knowledge.x2,
knowledge.y2,
{stroke: ‘blue’})
ctx.line(
mx1,
my1,
mx1 + 2 * a * dy1,
my1 + 2 * a * dx1,
{stroke: ‘crimson’})
ctx.line(
mx2,
my2,
mx2 + 2 * b * dy2,
my2 + 2 * b * dx2,
{stroke: ‘blue’})
var cx = mx1 + a*dy1
var cy = my1 + a*dx1
var dx = cx – knowledge.x1
var dy = cy – knowledge.y1
ctx.level(cx, cy)
ctx.circle(cx, cy, Math.sqrt(dx*dx + dy*dy), {
fill: ‘none’,
stroke: ‘inexperienced’,
‘stroke-width’: 3
})
ctx.level(knowledge.x1, knowledge.y1)
ctx.level(knowledge.x2, knowledge.y2)
ctx.level(knowledge.x3, knowledge.y3)
}
var demo = g9(knowledge, render)
.align(‘middle’, ‘middle’)
.insertInto(‘#demo-point’)
Run
var preliminary = {
az: 0,
ax: 0,
ay: 0,
ez: 1,
ex: 100,
ey: 100
};
var sin = Math.sin,
cos = Math.cos;
operate mul(m, v) {
return m.map(operate (row) {
return row.scale back(operate (a, b, i) {
return a + b * v[i];
}, 0);
});
}
operate rx(v, theta) {
return mul([[1, 0, 0], [0, cos(theta), -sin(theta)], [0, sin(theta), cos(theta)]], v);
}
operate ry(v, theta) {
return mul([[cos(theta), 0, sin(theta)], [0, 1, 0], [-sin(theta), 0, cos(theta)]], v);
}
operate rz(v, theta) {
return mul([[cos(theta), -sin(theta), 0], [sin(theta), cos(theta), 0], [0, 0, 1]], v);
}
operate rotate(v, ax, ay, az) {
return rx(ry(rz(v, az), ay), ax);
}
operate translate(p, dx, dy, dz) {
return [p[0] + dx, p[1] + dy, p[2] + dz];
}
operate mission(p) {
return [300 * p[0] / p[2], 300 * p[1] / p[2]];
}
console.log(mission);
// mul([[1,0,0],[1,2,0],[0,0,1]], [1,2,3])
// operate rotate(x,y,z, ){
// return [Math.cos(a)*x – Math.sin(a)*y, Math.sin(a)*x + Math.cos(a)*y]
// }
var factors = [];
for (var x = 0; x
Run
operate render(knowledge, ctx){
var lineoptions = {
‘stroke-width’: 4,
‘stroke-linecap’:’spherical’,
impacts:[‘squareness’]
}
operate dragon(x1,y1, x2,y2, dir, stage){
if(stage == 0){
ctx.line(x1, y1,x2, y2, lineoptions)
} else {
var midx = (x1 + x2 + knowledge.squareness*dir*(y2 – y1))/2
var midy = (y1 + y2 – knowledge.squareness*dir*(x2 – x1))/2
dragon(x1,y1, midx,midy, -1,level-1)
dragon(midx,midy, x2,y2, 1,level-1)
}
}
dragon = ctx.pure(dragon)
dragon(knowledge.fromX, knowledge.fromY, knowledge.toX, knowledge.toY, -1, 9)
ctx.level(knowledge.fromX, knowledge.fromY, {r:5})
ctx.level(knowledge.toX, knowledge.toY, {r:5})
}
var demo = g9(knowledge, render)
.align(‘middle’, ‘middle’)
.insertInto(‘#demo-dragon’)
Run
x: -100,
y: -100
}
operate render (knowledge, ctx){
ctx.picture(‘http://i.imgur.com/LQIsf.jpg’, knowledge.x, knowledge.y, 200, 200)
}
g9(preliminary,render)
.align(‘middle’, ‘middle’)
.insertInto(‘#demo-image’)
Run
operate rotate(x,y, a){
return [Math.cos(a)*x – Math.sin(a)*y, Math.sin(a)*x + Math.cos(a)*y]
}
operate render(knowledge, ctx){
var x = 0, y1 = 0, y2 = -100*Math.sin(knowledge.b)
for (var i = 0; i
Run
var drawTree = ctx.pure(operate (x1, y1, size, angle, n){
var x2 = x1 + size * Math.cos(angle*Math.PI/180);
var y2 = y1 + size * Math.sin(angle*Math.PI/180);
ctx.level(x2, y2, {impacts:[‘deltaAngle’, ‘attenuation’],
fill: ‘inexperienced’})
ctx.line(x1,y1,x2,y2, {‘stroke-width’: 5,
stroke: n>3?’brown’:’inexperienced’,
impacts:[‘deltaAngle’, ‘attenuation’]})
if(n > 0) {
drawTree(x2, y2, size*attenuation, angle+deltaAngle, n-1);
drawTree(x2, y2, size*attenuation, angle-deltaAngle, n-1);
}
})
ctx.level(0, ctx.peak/2 – 30)
drawTree(0, ctx.peak/2 – 30, startLength, -90, 8)
}, operate(newdata, renderees){
console.log(‘modified knowledge to’, newdata)
}).align(‘middle’, ‘middle’)
.insertInto(‘#demo-tree’)
Run
operate render(knowledge, ctx){
var m = Math.max(Math.min(knowledge.x2, knowledge.x3 – 20), knowledge.x1 + 20)
ctx.level(m,0, {impacts: [‘x2’]})
ctx.level(knowledge.x2,20, {impacts: [‘x2’]})
ctx.level(knowledge.x1,0, {fill: ‘crimson’})
ctx.level(knowledge.x3,0, {fill: ‘crimson’})
}
operate log(x, y){
console.log(x,y)
}
var demo = g9(preliminary, render, log)
.align(‘middle’, ‘middle’)
.insertInto(‘#demo-zero_gradients’)
Run
operate render(knowledge, ctx){
var r = 100,
m = 70;
// minute hand
ctx.line(0, 0, Math.cos(knowledge.time / 60) * m, Math.sin(knowledge.time / 60) * m, {
‘stroke-width’: 10,
‘stroke-linecap’: ‘spherical’,
‘stroke’: ‘grey’
})
// second hand
ctx.line(0, 0, Math.cos(knowledge.time) * r, Math.sin(knowledge.time) * r, {
‘stroke-width’: 10,
‘stroke-linecap’: ‘spherical’
})
ctx.circle(0, 0, { r: 120, fill: ‘none’, stroke: ‘black’ })
}
var graphics = g9(preliminary, render)
.insertInto(‘#demo-clock’)
setInterval(operate(){
if(!graphics.isManipulating){
var knowledge = graphics.getData()
knowledge.time += 0.01
graphics.setData(knowledge)
}
}, 50)
Run
operate render(knowledge, ctx){
var t = make_turtle(ctx)
for(var i = 0; i
Run
operate render(knowledge, ctx){
operate warp(dx, dy){
var r = Math.sqrt(dx * dx + dy * dy)
var theta = Math.atan2(dy, dx)
return [
data.scale * r * Math.cos(theta + data.spin * r),
data.scale * r * Math.sin(theta + data.spin * r)
]
}
var drawWarpedPoint = ctx.pure(operate(i, j){
var out = warp(i, j)
ctx.level(30 * out[0], 30 * out[1])
})
for(var i = -10; i
Attempt uncommenting the animation block for further enjoyable!
Run
operate render(knowledge, ctx){
var crank = 40;
var shaft = 120
// housing
var fill=”hsl(“+ (Math.cos(knowledge.time) * 60 + 300) +’, 100%, 90%)’
ctx.rect(-40, 0, 80, -235, { fill: fill, stroke: ‘black’ })
ctx.circle(0, 0, { r: 70, fill: ‘white’, stroke: ‘black’ })
var CrankAngle = Math.asin(crank * Math.cos(knowledge.time) / shaft)
var OppositeAngle = Math.PI – (CrankAngle + knowledge.time);
var Y = shaft * Math.cos(OppositeAngle) / Math.cos(knowledge.time)
// shaft
ctx.line(Math.cos(knowledge.time) * crank, Math.sin(knowledge.time) * crank, 0, Y, {
‘stroke-width’: 10,
‘stroke’: ‘grey’,
‘stroke-linecap’: ‘spherical’
})
// crank
ctx.line(0, 0, Math.cos(knowledge.time) * crank, Math.sin(knowledge.time) * crank, {
‘stroke-width’: 40,
‘stroke’: ‘black’,
‘stroke-linecap’: ‘spherical’
})
// piston
ctx.line(0, Y, 0, Y – 70, {
‘stroke-width’: 70,
‘stroke’: ‘grey’,
})
}
var graphics = g9(preliminary, render)
.insertInto(‘#demo-crank’)
/*setInterval(operate(){
if(!graphics.isManipulating){
var knowledge = graphics.getData()
knowledge.time += 0.05
graphics.setData(knowledge)
}
}, 10)*/