Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Mask Feature #187

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,23 @@ function() { return document.createElement("canvas"); }

When using Node.js, you will almost definitely override this default, e.g.
using the [canvas module](https://www.npmjs.com/package/canvas).

<a name="mask" href="#mask">#</a> <b>mask</b>([<i>mask</i>])

If specified, sets the **mask** image used internally
to draw text fitting within the transaparent areas of the provided image. If not specified, returns the current mask image, defaults to null.

```js
// Mask Example
var img = document.querySelector("img#mask")
img.onload = ()=>{
d3.layout.cloud()
.size([500, 500])
.mask(img)
}
```

Ensure the image is loaded before using it, typically using the onload event on the image.

Note: The image must be hosted with the appropriate CORS headers if being hosted on a different origin.

49 changes: 47 additions & 2 deletions build/d3.layout.cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const SPIRALS = {

const cw = 1 << 11 >> 5;
const ch = 1 << 11;
const random = Math.random;

module.exports = function() {
var size = [256, 256],
Expand All @@ -30,15 +31,17 @@ module.exports = function() {
timer = null,
random = Math.random,
cloud = {},
canvas = cloudCanvas;
canvas = cloudCanvas,
mask = false
maskBoard = null;

cloud.canvas = function(_) {
return arguments.length ? (canvas = functor(_), cloud) : canvas;
};

cloud.start = function() {
var contextAndRatio = getContext(canvas()),
board = zeroArray((size[0] >> 5) * size[1]),
board = mask ? maskBoard : zeroArray((size[0] >> 5) * size[1]),
bounds = null,
n = words.length,
i = -1,
Expand Down Expand Up @@ -161,6 +164,16 @@ module.exports = function() {
return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval;
};

cloud.mask = function(_){
if(arguments.length){
mask = _
maskBoard = createMaskBoard(_,size[0],size[1])
return cloud
}else{
return mask
}
};

cloud.words = function(_) {
return arguments.length ? (words = _, cloud) : words;
};
Expand Down Expand Up @@ -404,6 +417,38 @@ function functor(d) {
return typeof d === "function" ? d : function() { return d; };
}

function get_mask_pixels(mask,w,h){
var canvas = document.createElement('canvas')
canvas.width = w
canvas.height = h;
var ctx = canvas.getContext("2d");
ctx.drawImage(mask, 0, 0, w, h)
return ctx.getImageData(0, 0, w, h).data
}

function create_mask_board_from_pixels(mask_pixels, w,h){
var w32 = w >> 5 //divedes by 32
var sprite = []
// Zero the buffer
for (var i = 0; i < h * w32; i++){
sprite[i] = 0
}
for (var j = 0; j < h; j++) {
for (var i = 0; i < w; i++) {
var k = w32 * j + (i >> 5);
var m = mask_pixels[(j * w + (i)) << 2] ? 1 << (31 - (i % 32)) : 0;
sprite[k] |= m;
}
}
return sprite.slice(0, w * w32)
}

function createMaskBoard(mask,w,h){
var pixels = get_mask_pixels(mask,w,h)
var board = create_mask_board_from_pixels(pixels,w,h)
return board
}

},{"d3-dispatch":2}],2:[function(require,module,exports){
// https://d3js.org/d3-dispatch/ v1.0.6 Copyright 2019 Mike Bostock
(function (global, factory) {
Expand Down
Binary file added examples/mask.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions examples/mask_example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<html>

<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<script src="../build/d3.layout.cloud.js"></script>
</head>

<body>
<img src="./mask.png" id="mask" />
<script>
document.querySelector("#mask").onload = () => {
var count = 500
var scale = d3.scaleSqrt().range([20, 8]).domain([0, count])
var words = d3.range(0, count).map((d) => {
return {
text: "Hello",
size: scale(d),
};
});

var layout = d3.layout
.cloud()
.size([500, 500])
.mask(document.querySelector("#mask"))
.words(words)
.padding(1)
.font("Impact")
.fontSize(function (d) {
return d.size;
})
.on("end", function draw(words) {
d3.select("body")
.append('svg').
attr("viewBox", "0 0 500 500")
.attr("width", 500)
.append("g")
.attr(
"transform",
"translate(" +
layout.size()[0] / 2 +
"," +
layout.size()[1] / 2 +
")"
)
.selectAll("text")
.data(words)
.enter()
.append("text")
.style("font-size", function (d) {
return d.size + "px";
})
.style("font-family", "Impact")
.attr("text-anchor", "middle")
.attr("transform", function (d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function (d) {
return d.text;
});
});
layout.start()
}

</script>
</body>

</html>
49 changes: 47 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const SPIRALS = {

const cw = 1 << 11 >> 5;
const ch = 1 << 11;
const random = Math.random;

module.exports = function() {
var size = [256, 256],
Expand All @@ -29,15 +30,17 @@ module.exports = function() {
timer = null,
random = Math.random,
cloud = {},
canvas = cloudCanvas;
canvas = cloudCanvas,
mask = false
maskBoard = null;

cloud.canvas = function(_) {
return arguments.length ? (canvas = functor(_), cloud) : canvas;
};

cloud.start = function() {
var contextAndRatio = getContext(canvas()),
board = zeroArray((size[0] >> 5) * size[1]),
board = mask ? maskBoard : zeroArray((size[0] >> 5) * size[1]),
bounds = null,
n = words.length,
i = -1,
Expand Down Expand Up @@ -160,6 +163,16 @@ module.exports = function() {
return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval;
};

cloud.mask = function(_){
if(arguments.length){
mask = _
maskBoard = createMaskBoard(_,size[0],size[1])
return cloud
}else{
return mask
}
};

cloud.words = function(_) {
return arguments.length ? (words = _, cloud) : words;
};
Expand Down Expand Up @@ -402,3 +415,35 @@ function cloudCanvas() {
function functor(d) {
return typeof d === "function" ? d : function() { return d; };
}

function get_mask_pixels(mask,w,h){
var canvas = document.createElement('canvas')
canvas.width = w
canvas.height = h;
var ctx = canvas.getContext("2d");
ctx.drawImage(mask, 0, 0, w, h)
return ctx.getImageData(0, 0, w, h).data
}

function create_mask_board_from_pixels(mask_pixels, w,h){
var w32 = w >> 5 //divedes by 32
var sprite = []
// Zero the buffer
for (var i = 0; i < h * w32; i++){
sprite[i] = 0
}
for (var j = 0; j < h; j++) {
for (var i = 0; i < w; i++) {
var k = w32 * j + (i >> 5);
var m = mask_pixels[(j * w + (i)) << 2] ? 1 << (31 - (i % 32)) : 0;
sprite[k] |= m;
}
}
return sprite.slice(0, w * w32)
}

function createMaskBoard(mask,w,h){
var pixels = get_mask_pixels(mask,w,h)
var board = create_mask_board_from_pixels(pixels,w,h)
return board
}