mirror of
https://github.com/simon987/music-graph-ui.git
synced 2025-04-19 18:16:41 +00:00
Label placement & style tweaks
This commit is contained in:
parent
52c9b9f6ff
commit
0a1868c4e3
@ -1,6 +1,7 @@
|
|||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3'
|
||||||
import icons from './icons'
|
import icons from './icons'
|
||||||
import {MusicGraphApi} from './MusicGraphApi'
|
import {MusicGraphApi} from './MusicGraphApi'
|
||||||
|
import {fitCaptionIntoCircle} from './graphGeometry'
|
||||||
|
|
||||||
// TODO: export somewhere else
|
// TODO: export somewhere else
|
||||||
const arc = function (radius, itemNumber, itemCount, width) {
|
const arc = function (radius, itemNumber, itemCount, width) {
|
||||||
@ -32,7 +33,7 @@ export function MusicGraph(data) {
|
|||||||
this.simulation = d3.forceSimulation()
|
this.simulation = d3.forceSimulation()
|
||||||
.force('charge', d3.forceManyBody())
|
.force('charge', d3.forceManyBody())
|
||||||
.force('collide', d3.forceCollide()
|
.force('collide', d3.forceCollide()
|
||||||
.radius(50)
|
.radius(40)
|
||||||
.strength(1))
|
.strength(1))
|
||||||
.force('center', d3.forceCenter(width / 2, height / 2))
|
.force('center', d3.forceCenter(width / 2, height / 2))
|
||||||
|
|
||||||
@ -138,9 +139,9 @@ export function MusicGraph(data) {
|
|||||||
n.targetLinks.has(d.id))
|
n.targetLinks.has(d.id))
|
||||||
|
|
||||||
this.label.classed('selected', n =>
|
this.label.classed('selected', n =>
|
||||||
n.sourceLinks.has(d.id) ||
|
n.node.sourceLinks.has(d.id) ||
|
||||||
n.targetLinks.has(d.id) ||
|
n.node.targetLinks.has(d.id) ||
|
||||||
n.id === d.id)
|
n.node.id === d.id)
|
||||||
|
|
||||||
this.node.classed('hover', n => n.id === d.id)
|
this.node.classed('hover', n => n.id === d.id)
|
||||||
|
|
||||||
@ -182,8 +183,8 @@ export function MusicGraph(data) {
|
|||||||
fn: (d) => {
|
fn: (d) => {
|
||||||
this.api.getRelatedByMbid(d.mbid)
|
this.api.getRelatedByMbid(d.mbid)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
this.addNodes(data.newNodes, data.relations, d.id)
|
|
||||||
this.expandedNodes.add(d.id)
|
this.expandedNodes.add(d.id)
|
||||||
|
this.addNodes(data.newNodes, data.relations, d.id)
|
||||||
d.relatedExpanded = true
|
d.relatedExpanded = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -408,12 +409,10 @@ export function MusicGraph(data) {
|
|||||||
.force('link', d3.forceLink(this.links)
|
.force('link', d3.forceLink(this.links)
|
||||||
.id(d => d.id)
|
.id(d => d.id)
|
||||||
.strength(l => l.weight)
|
.strength(l => l.weight)
|
||||||
.distance(d => Math.min(
|
.distance(d => (1.15 / d.weight) * (82 * (this.expandedNodes.size + 1)))
|
||||||
(1.2 / d.weight) * (94 * (this.expandedNodes.size + 1)))
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
this.simulation.alphaTarget(0.01).restart()
|
this.simulation.alphaTarget(0.03).restart()
|
||||||
|
|
||||||
// Add new links
|
// Add new links
|
||||||
this.link = this.container.select('#links')
|
this.link = this.container.select('#links')
|
||||||
@ -451,14 +450,16 @@ export function MusicGraph(data) {
|
|||||||
// Add new labels
|
// Add new labels
|
||||||
this.label = this.container.select('#labels')
|
this.label = this.container.select('#labels')
|
||||||
.selectAll('.label')
|
.selectAll('.label')
|
||||||
.data(this.nodes)
|
.data([].concat(...this.nodes.map(d => fitCaptionIntoCircle(d.name, d))))
|
||||||
this.label.exit().remove()
|
this.label.exit().remove()
|
||||||
this.label = this.label
|
this.label = this.label
|
||||||
.enter()
|
.enter()
|
||||||
.append('text')
|
.append('text')
|
||||||
.merge(this.label)
|
.merge(this.label)
|
||||||
.text(d => d.name)
|
|
||||||
.classed('label', true)
|
.classed('label', true)
|
||||||
|
.classed('release', d => d.node.type === 'Album' || d.node.type === 'EP' || d.node.type === 'Single')
|
||||||
|
.classed('tag', d => d.node.type === 'Tag')
|
||||||
|
.text(d => d.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setupKeyBindings = function () {
|
this.setupKeyBindings = function () {
|
||||||
@ -514,8 +515,8 @@ export function MusicGraph(data) {
|
|||||||
.attr('cx', d => d.x)
|
.attr('cx', d => d.x)
|
||||||
.attr('cy', d => d.y)
|
.attr('cy', d => d.y)
|
||||||
this.label
|
this.label
|
||||||
.attr('x', d => d.x)
|
.attr('x', d => Math.round(d.node.x))
|
||||||
.attr('y', d => d.y + 5)
|
.attr('y', d => d.node.y + d.baseline)
|
||||||
})
|
})
|
||||||
|
|
||||||
this._update()
|
this._update()
|
||||||
|
@ -48,17 +48,7 @@ const nodeUtils = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
radius: function (type) {
|
radius: function (type) {
|
||||||
if (type === 'Group') {
|
|
||||||
return 35
|
return 35
|
||||||
} else if (type === 'Artist') {
|
|
||||||
return 25
|
|
||||||
} else if (type === 'Tag') {
|
|
||||||
return 20
|
|
||||||
} else if (type === 'Album') {
|
|
||||||
return 20
|
|
||||||
} else if (type === 'EP' || type === 'Single') {
|
|
||||||
return 15
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
<div id="mm"></div>
|
<div id="mm"></div>
|
||||||
<InputBar v-on:query="onQuery($event)"></InputBar>
|
<InputBar v-on:query="onQuery($event)"></InputBar>
|
||||||
<ArtistInfo v-bind:artist="hoverArtist"/>
|
<ArtistInfo v-bind:artist="hoverArtist"/>
|
||||||
|
<canvas id="textMeasurementCanvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -74,13 +75,12 @@ export default {
|
|||||||
/* Link */
|
/* Link */
|
||||||
svg .link.selected {
|
svg .link.selected {
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
stroke-opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svg .link {
|
svg .link {
|
||||||
stroke: orange;
|
stroke: #FFE082;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
stroke-opacity: 1;
|
stroke-opacity: 0.3;
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,11 +91,11 @@ export default {
|
|||||||
|
|
||||||
/* Node */
|
/* Node */
|
||||||
svg .node.selected {
|
svg .node.selected {
|
||||||
stroke: red;
|
stroke: #7C4DFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg .node.hover {
|
svg .node.hover {
|
||||||
stroke: red;
|
stroke: #7C4DFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.hover .node:not(.selected):not(.hover) {
|
svg.hover .node:not(.selected):not(.hover) {
|
||||||
@ -104,11 +104,22 @@ export default {
|
|||||||
|
|
||||||
svg .node {
|
svg .node {
|
||||||
fill: transparent;
|
fill: transparent;
|
||||||
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Label */
|
/* Label */
|
||||||
svg .label {
|
svg .label {
|
||||||
text-anchor: middle;
|
text-anchor: middle;
|
||||||
|
font-family: Tahoma, sans-serif;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg .label.release {
|
||||||
|
fill: darkgrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg .label.tag {
|
||||||
|
fill: darkgrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.hover .label:not(.selected) {
|
svg.hover .label:not(.selected) {
|
||||||
@ -116,27 +127,18 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: #E7EDEB;
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* test */
|
/* test */
|
||||||
|
|
||||||
#menu .menu-item {
|
#menu .menu-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
fill: orange;
|
fill: #FFB300;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .menu-item.hover {
|
#menu .menu-item.hover {
|
||||||
fill: darkorange;
|
fill: #FF8F00;
|
||||||
}
|
|
||||||
|
|
||||||
#menu .menu-item.hover text {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu .menu-item text {
|
|
||||||
text-anchor: middle;
|
|
||||||
fill: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .menu-icon {
|
#menu .menu-icon {
|
||||||
|
110
music_graph/src/graphGeometry.js
Normal file
110
music_graph/src/graphGeometry.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2002-2019 "Neo4j,"
|
||||||
|
* Neo4j Sweden AB [http://neo4j.com]
|
||||||
|
*
|
||||||
|
* This file is part of Neo4j.
|
||||||
|
*
|
||||||
|
* Neo4j is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import measureText from './textMeasurement'
|
||||||
|
|
||||||
|
let addShortenedNextWord = (line, word, measure) => {
|
||||||
|
const result = []
|
||||||
|
while (!(word.length <= 2)) {
|
||||||
|
word = word.substr(0, word.length - 2) + '\u2026'
|
||||||
|
if (measure(word) < line.remainingWidth) {
|
||||||
|
line.text += ` ${word}`
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
result.push(undefined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
let noEmptyLines = function (lines) {
|
||||||
|
for (let line of Array.from(lines)) {
|
||||||
|
if (line.text.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export let fitCaptionIntoCircle = function (captionText, node) {
|
||||||
|
const fontFamily = 'Tahoma'
|
||||||
|
const fontSize = 11
|
||||||
|
const lineHeight = fontSize
|
||||||
|
const measure = text => measureText(text, fontFamily, fontSize)
|
||||||
|
|
||||||
|
const words = captionText.split(' ')
|
||||||
|
|
||||||
|
const emptyLine = function (lineCount, iLine) {
|
||||||
|
let baseline = (1 + iLine - lineCount / 2) * lineHeight
|
||||||
|
const containingHeight = iLine < lineCount / 2 ? baseline - lineHeight : baseline
|
||||||
|
const lineWidth =
|
||||||
|
Math.sqrt(node.radius * node.radius - containingHeight * containingHeight) * 2
|
||||||
|
return {
|
||||||
|
node,
|
||||||
|
text: '',
|
||||||
|
baseline,
|
||||||
|
remainingWidth: lineWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fitOnFixedNumberOfLines = function (lineCount) {
|
||||||
|
const lines = []
|
||||||
|
let iWord = 0
|
||||||
|
for (
|
||||||
|
let iLine = 0, end = lineCount - 1, asc = end >= 0;
|
||||||
|
asc ? iLine <= end : iLine >= end;
|
||||||
|
asc ? iLine++ : iLine--
|
||||||
|
) {
|
||||||
|
const line = emptyLine(lineCount, iLine)
|
||||||
|
while (
|
||||||
|
iWord < words.length &&
|
||||||
|
measure(` ${words[iWord]}`) < line.remainingWidth) {
|
||||||
|
line.text += ` ${words[iWord]}`
|
||||||
|
line.remainingWidth -= measure(` ${words[iWord]}`)
|
||||||
|
iWord++
|
||||||
|
}
|
||||||
|
lines.push(line)
|
||||||
|
}
|
||||||
|
if (iWord < words.length) {
|
||||||
|
addShortenedNextWord(lines[lineCount - 1], words[iWord], measure)
|
||||||
|
}
|
||||||
|
return [lines, iWord]
|
||||||
|
}
|
||||||
|
|
||||||
|
let consumedWords = 0
|
||||||
|
const maxLines = (node.radius * 2) / fontSize
|
||||||
|
|
||||||
|
let lines = [emptyLine(1, 0)]
|
||||||
|
for (
|
||||||
|
let lineCount = 1, end = maxLines, asc = end >= 1;
|
||||||
|
asc ? lineCount <= end : lineCount >= end;
|
||||||
|
asc ? lineCount++ : lineCount--
|
||||||
|
) {
|
||||||
|
const [candidateLines, candidateWords] = Array.from(
|
||||||
|
fitOnFixedNumberOfLines(lineCount)
|
||||||
|
)
|
||||||
|
if (noEmptyLines(candidateLines)) {
|
||||||
|
[lines, consumedWords] = Array.from([candidateLines, candidateWords])
|
||||||
|
}
|
||||||
|
if (consumedWords >= words.length) {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
@ -1,50 +1,41 @@
|
|||||||
const icons = {
|
const icons = {
|
||||||
expand: '<svg viewBox="0 0 55 55" width="30px" height="30px" transform="translate(-7,0)">\n' +
|
expand: '<svg viewBox="0 0 55 55" width="30px" height="30px" transform="translate(-7,-5)"><path d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17\n' +
|
||||||
'<path d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17\n' +
|
's-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4\n' +
|
||||||
'\ts-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4\n' +
|
'c0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562\n' +
|
||||||
'\tc0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562\n' +
|
'C8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829\n' +
|
||||||
'\tC8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829\n' +
|
'c1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91\n' +
|
||||||
'\tc1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91\n' +
|
'v-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4\n' +
|
||||||
'\tv-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4\n' +
|
's-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665\n' +
|
||||||
'\ts-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665\n' +
|
'C46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z M11,9c0-1.103,0.897-2,2-2s2,0.897,2,2s-0.897,2-2,2\n' +
|
||||||
'\tC46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z M11,9c0-1.103,0.897-2,2-2s2,0.897,2,2s-0.897,2-2,2\n' +
|
'S11,10.103,11,9z M6,51c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S8.206,51,6,51z M33,49c0,2.206-1.794,4-4,4s-4-1.794-4-4\n' +
|
||||||
'\tS11,10.103,11,9z M6,51c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S8.206,51,6,51z M33,49c0,2.206-1.794,4-4,4s-4-1.794-4-4\n' +
|
's1.794-4,4-4S33,46.794,33,49z M29,31c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S32.309,31,29,31z M47,41c0,1.103-0.897,2-2,2\n' +
|
||||||
'\ts1.794-4,4-4S33,46.794,33,49z M29,31c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S32.309,31,29,31z M47,41c0,1.103-0.897,2-2,2\n' +
|
's-2-0.897-2-2s0.897-2,2-2S47,39.897,47,41z M49,10c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S51.206,10,49,10z"/></svg>',
|
||||||
'\ts-2-0.897-2-2s0.897-2,2-2S47,39.897,47,41z M49,10c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S51.206,10,49,10z"/>\n' +
|
release: '<svg viewBox="0 0 217.465 217.465" width="30px" height="30px" transform="translate(-1,-5)"><path d="M108.732,152.869c-24.337,0-44.137-19.8-44.137-44.137c0-24.337,19.8-44.137,44.137-44.137\n' +
|
||||||
'</svg>',
|
'c24.337,0,44.137,19.8,44.137,44.137C152.869,133.07,133.07,152.869,108.732,152.869z M108.732,75.505\n' +
|
||||||
release: '<svg viewBox="0 0 217.465 217.465" width="30px" height="30px" transform="translate(-1,-5)">\n' +
|
'c-18.322,0-33.227,14.906-33.227,33.228c0,18.322,14.906,33.227,33.227,33.227c18.322,0,33.227-14.906,33.227-33.227\n' +
|
||||||
'<path d="M108.732,152.869c-24.337,0-44.137-19.8-44.137-44.137c0-24.337,19.8-44.137,44.137-44.137\n' +
|
'C141.96,90.411,127.054,75.505,108.732,75.505z M108.732,129.388c-11.408,0-20.655-9.248-20.655-20.656\n' +
|
||||||
'\tc24.337,0,44.137,19.8,44.137,44.137C152.869,133.07,133.07,152.869,108.732,152.869z M108.732,75.505\n' +
|
'c0-11.408,9.248-20.655,20.655-20.655c11.408,0,20.655,9.248,20.655,20.655C129.388,120.14,120.14,129.388,108.732,129.388z\n' +
|
||||||
'\tc-18.322,0-33.227,14.906-33.227,33.228c0,18.322,14.906,33.227,33.227,33.227c18.322,0,33.227-14.906,33.227-33.227\n' +
|
'M108.732,0C48.777,0,0,48.778,0,108.732s48.777,108.732,108.732,108.732s108.732-48.777,108.732-108.732S168.687,0,108.732,0z\n' +
|
||||||
'\tC141.96,90.411,127.054,75.505,108.732,75.505z M108.732,129.388c-11.408,0-20.655-9.248-20.655-20.656\n' +
|
'M108.732,46.79c-34.155,0-61.942,27.787-61.942,61.942c0,2.762-2.239,5-5,5s-5-2.238-5-5c0-39.669,32.273-71.942,71.942-71.942\n' +
|
||||||
'\tc0-11.408,9.248-20.655,20.655-20.655c11.408,0,20.655,9.248,20.655,20.655C129.388,120.14,120.14,129.388,108.732,129.388z\n' +
|
'c2.761,0,5,2.238,5,5S111.494,46.79,108.732,46.79z M108.732,26.306c-45.45,0-82.427,36.977-82.427,82.427c0,2.762-2.239,5-5,5\n' +
|
||||||
'\t M108.732,0C48.777,0,0,48.778,0,108.732s48.777,108.732,108.732,108.732s108.732-48.777,108.732-108.732S168.687,0,108.732,0z\n' +
|
's-5-2.238-5-5c0-50.964,41.462-92.427,92.427-92.427c2.761,0,5,2.238,5,5S111.494,26.306,108.732,26.306z"/></svg>',
|
||||||
'\t M108.732,46.79c-34.155,0-61.942,27.787-61.942,61.942c0,2.762-2.239,5-5,5s-5-2.238-5-5c0-39.669,32.273-71.942,71.942-71.942\n' +
|
guitar: '<svg width="30px" height="30px" viewBox="0 0 481.684 481.683" transform="translate(-3,-3)"><path d="M469.783,14.968c0.312-0.693,0.412-1.196,0.292-1.491c-2.701-6.424-4.66,2.016-24.221-8.874\n' +
|
||||||
'\tc2.761,0,5,2.238,5,5S111.494,46.79,108.732,46.79z M108.732,26.306c-45.45,0-82.427,36.977-82.427,82.427c0,2.762-2.239,5-5,5\n' +
|
'c-19.548-10.893-24.63,0.966-24.63,0.966s-19.411,36.914-39.833,51.327c-20.435,14.413-21.428,12.381-22.586,24.95\n' +
|
||||||
'\ts-5-2.238-5-5c0-50.964,41.462-92.427,92.427-92.427c2.761,0,5,2.238,5,5S111.494,26.306,108.732,26.306z"/>\n' +
|
'c-1.167,12.565-5.438,16.646-5.438,16.646l0.309,0.331l-105.384,97.529c-0.1-6.39-6.159-6.073-6.159-6.073\n' +
|
||||||
'</svg>',
|
'c-24.964-11.68-19.266-18.578-6.664-46.466c12.595-27.89-4.482-33.897-4.482-33.897s-33.654,44.781-56.961,44.24\n' +
|
||||||
guitar: '<svg width="30px" height="30px" viewBox="0 0 481.684 481.683" transform="translate(-3,-3)">' +
|
'c-23.309-0.545-23.898,7.456-23.898,7.456s17.294,56.719-40.287,66.014c-21.516,3.468-37.612,8.636-49.333,13.74\n' +
|
||||||
'<path d="M469.783,14.968c0.312-0.693,0.412-1.196,0.292-1.491c-2.701-6.424-4.66,2.016-24.221-8.874\n' +
|
'c-2.761,1.146-5.464,2.433-8.109,3.851c-13.604,7.062-18.905,13.101-18.905,13.101l0.167,0.156c-1.515,1.379-3.005,2.797-4.45,4.3\n' +
|
||||||
'\t\t\tc-19.548-10.893-24.63,0.966-24.63,0.966s-19.411,36.914-39.833,51.327c-20.435,14.413-21.428,12.381-22.586,24.95\n' +
|
'c-43.687,45.3-35.432,124.143,18.456,176.11c53.884,51.965,132.977,57.362,176.664,12.066c3.65-3.787,6.869-7.786,9.744-11.941\n' +
|
||||||
'\t\t\tc-1.167,12.565-5.438,16.646-5.438,16.646l0.309,0.331l-105.384,97.529c-0.1-6.39-6.159-6.073-6.159-6.073\n' +
|
'l0.024,0.031c15.018-16.967,19.787-55.803,19.787-55.803s4.685-49.344,35.374-43.112c30.688,6.243,30.824-13.168,30.824-13.168\n' +
|
||||||
'\t\t\tc-24.964-11.68-19.266-18.578-6.664-46.466c12.595-27.89-4.482-33.897-4.482-33.897s-33.654,44.781-56.961,44.24\n' +
|
's-1.639-36.853,12.748-46.338c14.391-9.481,16.562-23.104-2.22-15.485c-18.791,7.622-30.068,14.375-47.557,0.377\n' +
|
||||||
'\t\t\tc-23.309-0.545-23.898,7.456-23.898,7.456s17.294,56.719-40.287,66.014c-21.516,3.468-37.612,8.636-49.333,13.74\n' +
|
'c-10.251-8.211-12.295-20.478-12.251-29.224l113.555-109.09c2.513-1.28,6.816-2.831,13.785-3.422\n' +
|
||||||
'\t\t\tc-2.761,1.146-5.464,2.433-8.109,3.851c-13.604,7.062-18.905,13.101-18.905,13.101l0.167,0.156c-1.515,1.379-3.005,2.797-4.45,4.3\n' +
|
'c12.58-1.07,10.548-2.082,25.115-22.406c14.559-20.324,51.623-39.455,51.623-39.455s11.891-4.993,1.143-24.627\n' +
|
||||||
'\t\t\tc-43.687,45.3-35.432,124.143,18.456,176.11c53.884,51.965,132.977,57.362,176.664,12.066c3.65-3.787,6.869-7.786,9.744-11.941\n' +
|
'C467.719,22.125,470.849,17.533,469.783,14.968z"/></svg>',
|
||||||
'\t\t\tl0.024,0.031c15.018-16.967,19.787-55.803,19.787-55.803s4.685-49.344,35.374-43.112c30.688,6.243,30.824-13.168,30.824-13.168\n' +
|
hash: '<svg viewBox="0 0 281.465 281.465" width="28px" height="28px" transform="translate(-4,0)"><path d="M273.661,114.318V67.035h-45.558L236.886,0h-47.69l-8.783,67.035h-60.084L129.113,0H81.425L72.64,67.035H7.804v47.283\n' +
|
||||||
'\t\t\ts-1.639-36.853,12.748-46.338c14.391-9.481,16.562-23.104-2.22-15.485c-18.791,7.622-30.068,14.375-47.557,0.377\n' +
|
'h58.649l-6.904,52.791H7.804v47.289h45.559l-8.784,67.066h47.687l8.787-67.066h60.083l-8.786,67.066h47.691l8.783-67.066h64.836\n' +
|
||||||
'\t\t\tc-10.251-8.211-12.295-20.478-12.251-29.224l113.555-109.09c2.513-1.28,6.816-2.831,13.785-3.422\n' +
|
'v-47.289h-58.647l6.901-52.791H273.661z M167.326,167.109h-60.084l6.9-52.791h60.082L167.326,167.109z"/></svg>',
|
||||||
'\t\t\tc12.58-1.07,10.548-2.082,25.115-22.406c14.559-20.324,51.623-39.455,51.623-39.455s11.891-4.993,1.143-24.627\n' +
|
delete: '<svg width="28px" height="28px" viewBox="0 0 510 510" ><path d="M255,0C114.75,0,0,114.75,0,255s114.75,255,255,255s255-114.75,255-255S395.25,0,255,0z M382.5,346.8l-35.7,35.7\n' +
|
||||||
'\t\t\tC467.719,22.125,470.849,17.533,469.783,14.968z"/></svg>',
|
'L255,290.7l-91.8,91.8l-35.7-35.7l91.8-91.8l-91.8-91.8l35.7-35.7l91.8,91.8l91.8-91.8l35.7,35.7L290.7,255L382.5,346.8z"/></svg>'
|
||||||
hash: '<svg viewBox="0 0 281.465 281.465" width="28px" height="28px" transform="translate(-4,0)">\n' +
|
|
||||||
'<path d="M273.661,114.318V67.035h-45.558L236.886,0h-47.69l-8.783,67.035h-60.084L129.113,0H81.425L72.64,67.035H7.804v47.283\n' +
|
|
||||||
'\th58.649l-6.904,52.791H7.804v47.289h45.559l-8.784,67.066h47.687l8.787-67.066h60.083l-8.786,67.066h47.691l8.783-67.066h64.836\n' +
|
|
||||||
'\tv-47.289h-58.647l6.901-52.791H273.661z M167.326,167.109h-60.084l6.9-52.791h60.082L167.326,167.109z"/>\n' +
|
|
||||||
'</svg>',
|
|
||||||
delete: '<svg width="28px" height="28px" viewBox="0 0 510 510" >\n' +
|
|
||||||
'\t\t<path d="M255,0C114.75,0,0,114.75,0,255s114.75,255,255,255s255-114.75,255-255S395.25,0,255,0z M382.5,346.8l-35.7,35.7\n' +
|
|
||||||
'\t\t\tL255,290.7l-91.8,91.8l-35.7-35.7l91.8-91.8l-91.8-91.8l35.7-35.7l91.8,91.8l91.8-91.8l35.7,35.7L290.7,255L382.5,346.8z"/>\n' +
|
|
||||||
'</svg>'
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
music_graph/src/textMeasurement.js
Normal file
39
music_graph/src/textMeasurement.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2002-2019 "Neo4j,"
|
||||||
|
* Neo4j Sweden AB [http://neo4j.com]
|
||||||
|
*
|
||||||
|
* This file is part of Neo4j.
|
||||||
|
*
|
||||||
|
* Neo4j is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import * as d3 from 'd3'
|
||||||
|
|
||||||
|
const measureUsingCanvas = function (text, font) {
|
||||||
|
const canvasSelection = d3.select('canvas#textMeasurementCanvas').data([this])
|
||||||
|
canvasSelection
|
||||||
|
.enter()
|
||||||
|
.append('canvas')
|
||||||
|
.attr('id', 'textMeasurementCanvas')
|
||||||
|
.style('display', 'none')
|
||||||
|
|
||||||
|
const canvas = canvasSelection.node()
|
||||||
|
const context = canvas.getContext('2d')
|
||||||
|
context.font = font
|
||||||
|
return context.measureText(text).width
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (text, fontFamily, fontSize) {
|
||||||
|
const font = `normal normal normal ${fontSize}px/normal ${fontFamily}`
|
||||||
|
return measureUsingCanvas(text, font)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user