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

textures.scale? #7

Open
bollwyvl opened this issue Mar 18, 2015 · 12 comments
Open

textures.scale? #7

bollwyvl opened this issue Mar 18, 2015 · 12 comments

Comments

@bollwyvl
Copy link
Contributor

Here's the shortest way I saw at present to represent a scale of textures:
http://codepen.io/anon/pen/wBRwvj

Perhaps a textures.scale, necessarily ordinal, could make this easier:

scale = textures.scale.lines()
  .domain(data)
  .strokeWidth(function(d){ return d.value; });

svg.call(scale.init()) // compute all the textures needed, insert the defs

svg.selectAll("circle")
  .data(data)
  .enter()
  .style({fill: scale})
@oskarols
Copy link

You can still use the normal scales to map between the domain and a range that would be appropriate for the strokeWidth. In other words, I don't think it's essential to mix in domains, since you can do the domain<->configuration mapping elsewhere using the existing scales, no?

I.e.

strokeWidthScale = d3.scale.linear()
   .domain([dataMin, dataMax])
   .range([1, maxStrokeWidth])

textures.lines()
   .strokeWidth(function(d) { return strokeWidthScale(d.value); };

I will however chime in that using the strokeWidth in this way (by sending in a function) — as is possible in most places in D3 — would be very handy indeed, and I actually tried doing something similar to this just thinking it would work.

@bollwyvl
Copy link
Contributor Author

Yeah, d3.functor for the win.

However, due to how the svg pattern thing works, one must actually create DOM for each background in defs, so a pure scale won't work... There needs to be a selectall someplace. This could be hidden, such that it was just in time, i.e. a call to url() would figure out if the desired texture actually exists, but explicit may be better... This would allow for reconfiguring.

I'll work up a better example that mixes color and strokewidth, maybe the example grid in the doc.

@bollwyvl
Copy link
Contributor Author

Here's a thing that creates a stepped scale in color and stroke:

http://codepen.io/anon/pen/gbZMLy

It uses several scales to generate the resulting scale, but I can see the benefit to having a factory that encapsulated this pattern in a few lines.

I don't know how one would go about encapsulating multiple path types and then handling their various options.

@riccardoscalco
Copy link
Owner

@bollwyvl the pattern you are proposing is, I think, the right way to do that kind of thing, and the benefit to having a factory that simplifies the code is clear. However, I have some doubts it has benefits from the point of view of the designer. I explain myself: textures should not be used to represent quantitative variables, and the introduction of a "scale" method induce to use textures in that way. I imagine the designer choosing carefully a pattern for every qualitative variable (usually few categories), and such kind of process involve pattern definitions that in most of the cases does not follow a scale. Actually I expect a sort of "trial and error" process that ends once the graphical representation satisfies the designer taste.

For that reason I suggest to wait and see what people needs.
Let see if textures are used first of all.

@bollwyvl
Copy link
Contributor Author

I think your star count indicates some people are interested! It will be interesting to see who uses it!

Looking at this:
https://github.com/riccardoscalco/textures/blob/gh-pages/map.js#L46
I see a scale.

It could be that it is a completely non-data-driven, and purely aesthetic, but even then it seems it could be stored in a more compact manner than a switch statement!

But understood if you wish to close this!

@riccardoscalco
Copy link
Owner

Well, you are right with the switch! As it is, that piece of code is simply a mistake, indeed it can be replaced by a random choice.

(It is there because my first attempt was to visualize some real data within the intro map and, not having a datasheet (csv/json) ready, I decided to hard code data into the script. At the end of the process I changed idea and I went for a random map, without removing the code.)

It could be that it is a completely non-data-driven, and purely aesthetic, but even then it seems it could be stored in a more compact manner than a switch statement!

There is the possibility I am missing the point of you are suggesting, so I did an example:
http://codepen.io/riccardoscalco/pen/MYLaVb?editors=101
In that example the designer chose three well different patterns to identify three categories. Saying that pattern definitions do not follow a scale I mean that they are not functions. In which way the definition of a scale method can help where the patterns are not data-driven?

In the case of 1000 circles to fill with 3 patterns it is possible to use the code you suggested

scale = d3.scale.ordinal()
    .domain [category1, category2, category3]
    .range [t1.url(), t2.url(), t3.url()]

svg.selectAll "circle"
     .data data
  .enter()
    .append "circle"
    .style "fill", (d) -> scale(d.category)

The US map on the website has only line patterns, the idea is to represent an ordered relation and therefore there is a scale somehow, but even in this case I think the designer will design by hand the few patterns she will need (they must be few otherwise the result is a mess).

@riccardoscalco
Copy link
Owner

Btw, I want to add this example to the thread: http://bl.ocks.org/curran/0ad2eef56811e04f3aa6

@curran
Copy link

curran commented Mar 24, 2015

Ideally a texture scale could be defined that uses three independent scales for

  1. Pattern (the base texture)
  2. Size
  3. Color

This would match Bertin's conception of retinal variables:
bertin1982

Here is one way of making texture scales that combine pattern, size, and color. http://bl.ocks.org/curran/04cd8c28e06facc55bd7

This includes the just-in-time approach suggested by @bollwyvl.

Because textures.js has an API that mutates the created texture (rather than creating a new texture) when you call modifiers like size(), fill(), and stroke(), the pattern scale needs to actually contain generators for textures, so the other scales (Size, Color) can modify them.

If the textures.js API were purely functional, I think this issue of combined scales would have a cleaner solution. Then, the pattern scale could use straight textures, rather than having to use texture generators. As an example of what I mean, if the API were purely functional, then the following code would work:

var svg = d3.select("#example").append("svg");

var t1 = textures.lines().thicker();
var t2 = t1.fill("red") // This could create a new texture instance

// t1 and t2 would have separate defs in the SVG
svg.call(t1);
svg.call(t2);

svg.append("circle").style("fill", t1.url());
svg.append("circle").style("fill", t2.url());

@bollwyvl
Copy link
Contributor Author

Love the bertin image! Well have to study that in more depth.

The svg api drives the textures api, hard to avoid that entirely.

I think the "always be copying" api would be confusing, though an explicit
texture.copy(), like d3.scale.linear.copy(), would be great.

It would be possible to smooth over the api some, at a cost of performance
and deception.

an element in an svg can always know its svg. Since these textures must be
singletons anyway a la #id, and since it can only ever drive "fill"... one
could remove the svg.call(t1) and t1.url(). Then it would just be
shape.call(t1), which would be much more d3-like. Under the hood, it would
selectAll("defs #some-texture-id", [t]) and then the whole enter/update
dance... But finally selection.style("fill", t.url()).

Unless i am mistaken.

This would have the unintended consequence of updating all uses of the
texture, though.

Then yes, a texture.scale could be a generator... But with (necessary) side
effects... Maybe worth investigating more!
On Mar 24, 2015 2:59 PM, "Curran Kelleher" notifications@github.com wrote:

Ideally a texture scale could be defined that uses three independent
scales for

  1. Pattern (the base texture)
  2. Size
  3. Color

This would match Bertin's conception of retinal variables:
[image: bertin1982] http://th-mayer.de/bochum2013/#/9

Here is one way of making texture scales that combine pattern, size, and
color. http://bl.ocks.org/curran/04cd8c28e06facc55bd7

Because textures.js has an API that mutates the created texture (rather
than creating a new texture) when you call modifiers like size(), fill(),
and stroke(), the pattern scale needs to actually contain generators
for textures, so the other scales (Size, Color) can modify them.

If the textures.js API were purely functional, I think this issue of
combined scales would have a cleaner solution. Then, the pattern scale
could use straight textures, rather than having to use texture generators.
As an example of what I mean, if the API were purely functional, then the
following code would work:

var svg = d3.select("#example").append("svg");

var t1 = textures.lines().thicker();
var t2 = t1.fill("red") // This could create a new texture instance

// t1 and t2 would have separate defs in the SVG
svg.call(t1);
svg.call(t2);

svg.append("circle").style("fill", t1.url());
svg.append("circle").style("fill", t2.url());


Reply to this email directly or view it on GitHub
#7 (comment)
.

@curran
Copy link

curran commented Mar 25, 2015

Oh yes definitely, adding a copy() or clone() method would do the trick. Great idea!

@riccardoscalco
Copy link
Owner

A lot of good ideas here, I added two new issues and suggest to leave open this one.
Thanks for the code @curran !

@curran
Copy link

curran commented Mar 25, 2015

Great! My pleasure, thanks for the nice library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants