Published
Edited
Mar 31, 2022
Insert cell
# Perceptual Groups in Bluefish
Insert cell
bfjs = require('@bfjs/core@0.8.6/dist/main.js')
Insert cell
M = bfjs.marks
Insert cell
C = bfjs.constraints
Insert cell
ref = bfjs.ref
Insert cell
render = (dataOrShape, shapeFn = undefined) => myRender(bfjs.render(dataOrShape, shapeFn))
Insert cell
import {myRender} from '@joshpoll/bluefish-tutorial-0-8'
Insert cell
createShape = (shapes, rels={}) => bfjs.createShape({ shapes, rels })
Insert cell
mkList = (xs) => ({
elements: xs,
neighbors: d3.pairs(_.range(xs.length)).map(([curr, next]) => ({
curr: ref(`../../elements/${curr}`),
next: ref(`../../elements/${next}`),
}))
})
Insert cell
render(totalData, totalShape)
Insert cell
totalData = ({
none: data1,
proximity: data2,
commonRegion: data3,
elementConnectedness: data3,
similarColor: data4,
// alignment: data6,
alignment: data8,
})
Insert cell
verticalSpacing = 20
Insert cell
totalShape = createShape({
$none$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)]
})
}),
invisProximityBG: M.rect({fill: 'none'}),
proximityBG: M.rect({fill: '#ccc'}),
$proximity$: createShape({
$elements$: createShape({
$fst$: (_) => M.circle({r: 10}),
$snd$: (_) => M.circle({r: 10}),
},{
'fst->snd': [C.alignMiddle, C.hSpace(5)],
}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(25)],
})
}),
$commonRegion$: createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}),
$pairs$: createShape({
invisbox: M.rect({fill: 'none'}),
box: M.rect({stroke: 'black', fill: 'none'}),
$fst$: 'ref',
$snd$: 'ref',
},{
'invisbox->fst': [...C.containsShrinkWrap],
'invisbox->snd': [...C.containsShrinkWrap],
'box->invisbox': [C.alignBottomSpace(-5), C.alignTopSpace(-5), C.alignLeftSpace(2.5), C.alignRightSpace(2.5)],
})
}),
invisElementConnectednessBG: M.rect({fill: 'none'}),
elementConnectednessBG: M.rect({fill: '#ccc'}),
$elementConnectedness$: createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}),
$pairs$: createShape({
line: M.line({stroke: 'black', strokeWidth: '3px'}),
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->line': [C.makeEqual('right', 'left'), C.makeEqual('centerY', 'top')],
'line->snd': [C.makeEqual('right', 'left'), C.makeEqual('bottom', 'centerY')],
})
}),
$similarColor$: createShape({
$elements$: createShape({
$fst$: (color) => M.circle({r: 10, fill: color}),
$snd$: (color) => M.circle({r: 10, fill: color}),
},{
'fst->snd': [C.alignMiddle, C.hSpace(15)],
}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}),
invisAlignmentBG: M.rect({fill: 'none'}),
alignmentBG: M.rect({fill: '#ccc'}),
$alignment$: createShape({
$elements$: createShape({
$fst$: (_) => M.ellipse({rx: 10, ry: 3}),
$snd$: (_) => M.ellipse({rx: 10, ry: 3}),
invisbox: M.rect({fill: 'none'}),
box: M.rect({fill: 'black'}),
// box: M.rect({fill: 'none', stroke:'black'}),
},{
'invisbox->fst': [...C.containsShrinkWrap],
'invisbox->snd': [...C.containsShrinkWrap],
'box->invisbox': [C.alignBottomSpace(-5), C.alignTopSpace(-5), C.alignLeftSpace(2.5), C.alignRightSpace(2.5)],
'fst->snd': [C.alignMiddle, C.hSpace(15)],
}),
$neighbors$: createShape({
$0$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr/snd->next/fst': [C.alignMiddle, C.hSpace(15)],
}),
$1$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr/snd->next/fst': [C.alignTopSpace(-7.5), C.hSpace(15)],
}),
})
})/* createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.hSpace(15)],
})
}),
$pairs$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignMiddle],
}),
$spacing1$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignBottomSpace(-20)],
}),
$spacing2$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignBottomSpace(10)],
}),
}), */
},{
'proximityBG->invisProximityBG': [C.alignBottomSpace(-10), C.alignTopSpace(-10)/* , C.alignLeftSpace(10), C.alignRightSpace(10) */],
'invisProximityBG->proximity': [...C.containsShrinkWrap],
'elementConnectednessBG->invisElementConnectednessBG': [C.alignBottomSpace(-10), C.alignTopSpace(-10), C.alignLeftSpace(10), C.alignRightSpace(10)],
'elementConnectednessBG->proximityBG': [C.alignLeft, C.alignRight],
'elementConnectednessBG->alignmentBG': [C.alignLeft, C.alignRight],
'alignmentBG->invisAlignmentBG': [C.alignBottomSpace(-10), C.alignTopSpace(-10)/* , C.alignLeftSpace(10), C.alignRightSpace(10) */],
'invisAlignmentBG->alignment': [...C.containsShrinkWrap],
'invisElementConnectednessBG->elementConnectedness': [...C.containsShrinkWrap],
'none->proximity': [C.alignCenter, C.vSpace(verticalSpacing)],
'proximity->commonRegion': [C.alignCenter, C.vSpace(verticalSpacing)],
'commonRegion->elementConnectedness': [C.alignCenter, C.vSpace(verticalSpacing)],
'elementConnectedness->similarColor': [C.alignCenter, C.vSpace(verticalSpacing)],
'similarColor->alignment': [C.alignCenter, C.vSpace(verticalSpacing)],

// 'none->elementConnectedness': [C.alignMiddle, C.hSpace(0)],
})
Insert cell
totalShapeHorizontal = createShape({
$none$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)]
})
}),
invisProximityBG: M.rect({fill: 'none'}),
proximityBG: M.rect({fill: '#ccc'}),
$proximity$: createShape({
$elements$: createShape({
$fst$: (_) => M.circle({r: 10}),
$snd$: (_) => M.circle({r: 10}),
},{
'fst->snd': [C.alignMiddle, C.hSpace(5)],
}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(25)],
})
}),
$commonRegion$: createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}),
$pairs$: createShape({
invisbox: M.rect({fill: 'none'}),
box: M.rect({stroke: 'black', fill: 'none'}),
$fst$: 'ref',
$snd$: 'ref',
},{
'invisbox->fst': [...C.containsShrinkWrap],
'invisbox->snd': [...C.containsShrinkWrap],
'box->invisbox': [C.alignBottomSpace(-5), C.alignTopSpace(-5), C.alignLeftSpace(2.5), C.alignRightSpace(2.5)],
})
}),
invisElementConnectednessBG: M.rect({fill: 'none'}),
elementConnectednessBG: M.rect({fill: '#ccc'}),
$elementConnectedness$: createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}),
$pairs$: createShape({
line: M.line({stroke: 'black', strokeWidth: '3px'}),
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->line': [C.makeEqual('right', 'left'), C.makeEqual('centerY', 'top')],
'line->snd': [C.makeEqual('right', 'left'), C.makeEqual('bottom', 'centerY')],
})
}),
$similarColor$: createShape({
$elements$: createShape({
$fst$: (color) => M.circle({r: 10, fill: color}),
$snd$: (color) => M.circle({r: 10, fill: color}),
},{
'fst->snd': [C.alignMiddle, C.hSpace(15)],
}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}),
invisAlignmentBG: M.rect({fill: 'none'}),
alignmentBG: M.rect({fill: '#ccc'}),
$alignment$: createShape({
$elements$: createShape({
$fst$: (_) => M.ellipse({rx: 10, ry: 3}),
$snd$: (_) => M.ellipse({rx: 10, ry: 3}),
invisbox: M.rect({fill: 'none'}),
box: M.rect({fill: 'black'}),
// box: M.rect({fill: 'none', stroke:'black'}),
},{
'invisbox->fst': [...C.containsShrinkWrap],
'invisbox->snd': [...C.containsShrinkWrap],
'box->invisbox': [C.alignBottomSpace(-5), C.alignTopSpace(-5), C.alignLeftSpace(2.5), C.alignRightSpace(2.5)],
'fst->snd': [C.alignMiddle, C.hSpace(15)],
}),
$neighbors$: createShape({
$0$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr/snd->next/fst': [C.alignMiddle, C.hSpace(15)],
}),
$1$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr/snd->next/fst': [C.alignTopSpace(-7.5), C.hSpace(15)],
}),
})
})/* createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.hSpace(15)],
})
}),
$pairs$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignMiddle],
}),
$spacing1$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignBottomSpace(-20)],
}),
$spacing2$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignBottomSpace(10)],
}),
}), */
},{
'proximityBG->invisProximityBG': [C.alignBottomSpace(-10), C.alignTopSpace(-10)/* , C.alignLeftSpace(10), C.alignRightSpace(10) */],
'invisProximityBG->proximity': [...C.containsShrinkWrap],
'elementConnectednessBG->invisElementConnectednessBG': [C.alignBottomSpace(-10), C.alignTopSpace(-10), C.alignLeftSpace(10), C.alignRightSpace(10)],
'elementConnectednessBG->proximityBG': [C.alignLeft, C.alignRight],
// 'elementConnectednessBG->alignmentBG': [C.alignLeft, C.alignRight],
'alignmentBG->invisAlignmentBG': [C.alignBottomSpace(-10), C.alignTopSpace(-10)/* , C.alignLeftSpace(10), C.alignRightSpace(10) */],
'invisAlignmentBG->alignment': [...C.containsShrinkWrap],
'invisElementConnectednessBG->elementConnectedness': [...C.containsShrinkWrap],
'none->alignment': [C.alignCenter, C.vSpace(verticalSpacing)],
'alignment->commonRegion': [C.alignCenter, C.vSpace(verticalSpacing)],
// 'commonRegion->elementConnectedness': [C.alignCenter, C.vSpace(verticalSpacing)],
'elementConnectedness->similarColor': [C.alignCenter, C.vSpace(verticalSpacing)],
// 'similarColor->alignment': [C.alignCenter, C.vSpace(verticalSpacing)],

'none->elementConnectedness': [C.alignMiddle, C.hSpace(30)],
})
Insert cell
render(totalData, totalShapeHorizontal)
Insert cell
data1 = mkList([0, 0, 0, 0, 0, 0])
Insert cell
render(data1, createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)]
})
}))
Insert cell
data2 = mkList([{fst: 0, snd: 0}, {fst: 0, snd: 0}, {fst: 0, snd: 0}])
Insert cell
render(data2, createShape({
$elements$: createShape({
$fst$: (_) => M.circle({r: 10}),
$snd$: (_) => M.circle({r: 10}),
},{
'fst->snd': [C.alignMiddle, C.hSpace(5)],
}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}))
Insert cell
data3 = ({
list: mkList([0, 0, 0, 0, 0, 0]),
pairs: [
{fst: ref('../../list/elements/0'), snd: ref('../../list/elements/1')},
{fst: ref('../../list/elements/2'), snd: ref('../../list/elements/3')},
{fst: ref('../../list/elements/4'), snd: ref('../../list/elements/5')},
],
})
Insert cell
render(data3, createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}),
$pairs$: createShape({
invisbox: M.rect({fill: 'none'}),
box: M.rect({stroke: 'black', fill: 'none'}),
$fst$: 'ref',
$snd$: 'ref',
},{
'invisbox->fst': [...C.containsShrinkWrap],
'invisbox->snd': [...C.containsShrinkWrap],
'box->invisbox': [C.alignBottomSpace(-5), C.alignTopSpace(-5), C.alignLeftSpace(2.5), C.alignRightSpace(2.5)],
})
}))
Insert cell
render(data3, createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}),
$pairs$: createShape({
line: M.line({stroke: 'black', strokeWidth: '3px'}),
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->line': [C.makeEqual('right', 'left'), C.makeEqual('centerY', 'top')],
'line->snd': [C.makeEqual('right', 'left'), C.makeEqual('bottom', 'centerY')],
})
}))
Insert cell
data4 = mkList([{ fst: 'coral', snd: 'coral' }, { fst: 'seagreen', snd: 'seagreen' }, { fst: 'cornflowerblue', snd: 'cornflowerblue' }])
Insert cell
render(data4, createShape({
$elements$: createShape({
$fst$: (color) => M.circle({r: 10, fill: color}),
$snd$: (color) => M.circle({r: 10, fill: color}),
},{
'fst->snd': [C.alignMiddle, C.hSpace(15)],
}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignMiddle, C.hSpace(15)],
})
}))
Insert cell
data5 = ({
elements: [{fst: 0, snd: 0}, {fst: 0, snd: 0}, {fst: 0, snd: 0}],
neighbors: {
0: {curr: ref('../../elements/0'), next: ref('../../elements/1')},
1: {curr: ref('../../elements/1'), next: ref('../../elements/2')},
},
})
Insert cell
render(data5, createShape({
$elements$: createShape({
$fst$: (_) => M.circle({r: 10}),
$snd$: (_) => M.circle({r: 10}),
},{
'fst->snd': [C.alignMiddle, C.hSpace(15)],
}),
$neighbors$: createShape({
$0$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignTopSpace(5), C.hSpace(15)],
}),
$1$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.alignBottomSpace(15), C.hSpace(15)],
}),
})
}))
Insert cell
data6 = ({
list: mkList([0, 0, 0, 0, 0, 0]),
pairs: [
{fst: ref('../../list/elements/0'), snd: ref('../../list/elements/3')},
{fst: ref('../../list/elements/1'), snd: ref('../../list/elements/2')},
{fst: ref('../../list/elements/4'), snd: ref('../../list/elements/5')},
],
spacing1: {fst: ref('../list/elements/3'), snd: ref('../list/elements/1')},
spacing2: {fst: ref('../list/elements/2'), snd: ref('../list/elements/4')},
})
Insert cell
/* TODO: I think for alignment I need some different shapes... Need shapes of different sizes so that alignment is more prominent. */
render(data6, createShape({
$list$: createShape({
$elements$: (_) => M.circle({r: 10}),
$neighbors$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr->next': [C.hSpace(15)],
})
}),
$pairs$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignMiddle],
}),
$spacing1$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignBottomSpace(-20)],
}),
$spacing2$: createShape({
$fst$: 'ref',
$snd$: 'ref',
},{
'fst->snd': [C.alignBottomSpace(10)],
}),
}))
Insert cell
data8 = ({
elements: [{fst: 0, snd: 0}, {fst: 0, snd: 0}, {fst: 0, snd: 0}],
neighbors: {
0: {curr: ref('../../elements/0'), next: ref('../../elements/1')},
1: {curr: ref('../../elements/1'), next: ref('../../elements/2')},
},
})
Insert cell
render(data8, createShape({
$elements$: createShape({
$fst$: (_) => M.circle({r: 10}),
$snd$: (_) => M.circle({r: 10}),
invisbox: M.rect({fill: 'none'}),
box: M.rect({fill: 'black'}),
},{
'invisbox->fst': [...C.containsShrinkWrap],
'invisbox->snd': [...C.containsShrinkWrap],
'box->invisbox': [C.alignBottomSpace(-5), C.alignTopSpace(-5), C.alignLeftSpace(2.5), C.alignRightSpace(2.5)],
'fst->snd': [C.alignMiddle, C.hSpace(15)],
}),
$neighbors$: createShape({
$0$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr/snd->next/fst': [C.alignMiddle, C.hSpace(15)],
}),
$1$: createShape({
$curr$: 'ref',
$next$: 'ref',
},{
'curr/snd->next/fst': [C.alignTopSpace(-7.5), C.hSpace(15)],
}),
})
}))
Insert cell
data7 = ({ label: 'C' })
Insert cell
render(data7, createShape({
padding: M.rect({ fill: 'none'}),
node: M.rect({width: 30, height: 30, rx: 5, fill: 'blanchedalmond', stroke: '#ddd', strokeWidth: 2}),
$label$: (text) => M.text({ contents: text, fontSize: '18px', }),
},{
'padding->node': [C.alignLeftSpace(5), C.alignRightSpace(5), C.alignTopSpace(-5), C.alignBottomSpace(-5)],
'node->label': [C.alignMiddle, C.alignCenter],
}))
Insert cell
render(data7, createShape({
padding: M.rect({ fill: 'none'}),
node: M.rect({width: 30, height: 30, rx: 5, fill: 'blanchedalmond', stroke: '#ddd', strokeWidth: 2}),
$label$: (text) => M.text({ contents: text, fontSize: '18px', }),
},{
'padding->node': [C.alignLeftSpace(5), C.alignRightSpace(5), C.alignTopSpace(-5), C.alignBottomSpace(-5)],
'node->label': [C.alignCenter, C.makeEqual('bottom', 'centerY')],
}))
Insert cell
render(data7, createShape({
padding: M.rect({ fill: 'none'}),
node: M.rect({width: 30, height: 30, rx: 5, fill: 'blanchedalmond', stroke: '#ddd', strokeWidth: 2}),
$label$: (text) => M.text({ contents: text, fontSize: '18px', }),
},{
'padding->node': [C.alignLeftSpace(5), C.alignRightSpace(5), C.alignTopSpace(-5), C.alignBottomSpace(-5)],
'node->label': [C.alignCenter, C.vSpace(5)],
}))
Insert cell
x = 5
Insert cell
(y) => x + y
Insert cell
Object.keys(C)
Insert cell
Object.keys(M)
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more