chiasm-project / chiasm

A browser based environment for interactive data visualizations.
MIT License
184 stars 27 forks source link

Refactor Layout plugin #33

Closed curran closed 8 years ago

curran commented 9 years ago

The Chiasm Layout plugin should manage a collection of DIV elements, each of which should be assigned as the container property of components. The layout plugin should set the (x, y) offset of these divs, and also assign their (width, height).

The width and height of the container should be assigned as containerWidth and containerHeight on components, replacing the old box property.

Hypercubed commented 9 years ago

Is the layout plugin required for all Chiasm views? Can I have a Chiasm config with only a visualization plugin?

curran commented 9 years ago

The layout plugin is responsible for assigning the box property on components based on the Chiasm container size (see layout.js line 44), which is the (x, y, width, height) specification. So pretty much the answer is, yes the layout plugin is required because it sets the size of components, unless you are willing to do some extra work to set the size.

If you don't include the layout plugin, you'll need to do something to assign the size. I can think of two ways to set the size without using the layout plugin:

I think I understand your perspective though - that in the case of a single visualization the configuration seems to have some redundant stuff (e.g. in this bar chart config ). This is true in a way, but Chiasm is really designed for more complex cases, with multiple views and links between them. So the tradeoff is a situation where the simplest case (a single visualization) still requires the Layout and Links plugins, which are kind of the Chiasm boilerplate setup that will be in pretty much any Chiasm configuration.

Hypercubed commented 9 years ago

On a somewhat related note chiasm not only assumes the layout plugin is setting the size but also assumes other chiasm plugins are retreiving the data. If I want, for example, to push data to the barChart plugin I need the following:

      chiasm.getComponent('barChart').then(function(res) {
        res.data = myAppsData;
      });

Not wrong but cumbersome if the parent app is handling the data loading.

curran commented 9 years ago

Yes, this is one way to pass data into the visualization. I was thinking though with the plugin design that a plugin would be responsible for loading the data. This means in the case of embedding Chiasm in a larger system, there will be a system-specific Chiasm plugin for fetching data, then the application can configure that plugin at runtime via the Chiasm configuration.

I'd be happy to hear any other ideas you may have about a better API though.

Hypercubed commented 9 years ago

Perhaps a method (or separate plugin) to create a layout within a single SVG. This would be useful for charts that contain legends.

curran commented 9 years ago

Oh yes legends are a whole other thing to deal with. I was thinking that legends could also be plugins (e.g. color legend, size legend), and that the layout plugin could have three orientations: x (horizontal split), y (vertical split), and z (layered back to front). With z orientation, the layout items could specify additional (x, y) offsets to be positioned on top of the visualization behind it. This way, the layout could specify legends that appear on top of any visualizations.

For this, I'm not sure they need to be all in a single SVG though. CSS could be used to place a div in front of another div. I'm thinking particularly of the case of using Leaflet for a choropleth map, and using a D3 (SVG) legend on top of it. With the visualizations so far, having everything in a single SVG would work, but going forward I would like the layout plugin to be flexible enough to handle visualization plugins authored using Leaflet or Three.js, which do not use SVG, but could work if given a DIV as a container.

I created a color legend a long time ago in this linked Choropleth example http://curran.github.io/model/examples/d3LinkedChoropleth/ I would like to make this legend into a Chiasm plugin.

On Tue, Jun 30, 2015 at 1:40 AM, Jayson Harshbarger < notifications@github.com> wrote:

Perhaps a method (or separate plugin) to create a layout within a single SVG. This would be useful for charts that contain legends.

— Reply to this email directly or view it on GitHub https://github.com/curran/chiasm/issues/33#issuecomment-117057304.

Arne-Pfeilsticker commented 9 years ago

First of all I want to say that I am thrilled by your work. I want to use your work in an open source project for visualizing and planning municipal budgets.

I want to use angular with material design. Is it possible to bind individual charts to different ID selectors? My suggestion for the layout plugin would be:

"layout": {
    "plugin": "layout",
    "state": {
      "layout": {
        "barChart": {
          "id": "barchartcontainer",
          "size": "100px, 50px",
          "size-min": "100px, 50px",
          "size-max": "300px, 200px"
        },
        "lineChart": {
          "id": "linechartcontainer"
        }
      }
    }
  }

If a size specification is not specified, then it should be calculated automatically. Do you have some tips to change the layout plugin?

Or is it possible to have multiple visConfig.json files that have different container IDs and are linked by a common crossfilter?

curran commented 9 years ago

Hi Arne,

Thank you for your input.

The original idea for the layout plugin was that it would append elements to the top-level Chiasm container, and these would serve as containers for visualizations. However, I like your idea to have the layout plugin append elements to arbitrary elements on the page identified by an id. This would allow visualizations to be scattered around the page, which would be very cool.

To have visualizations linked by a common crossfilter within Chiasm, they should all be inside the same visConfig.json.

Based on your suggestion, it would be nice to have two alternative behaviors:

I hope I can get to this refactoring soon. Thanks again for your thoughts on this.

Arne-Pfeilsticker commented 9 years ago

Let me add a few thoughts with respect to Chiasm, Angular and Material Design.

Does it make sense and is it posible to have crossfilter on the $rootScope so that a common data basis between multiple controllers or views is available?

Do I understand you correctly that a common visConfig.json file could be used to maintain status between multiple controllers and views? If different views showing the same chart, then a change in one view would be adopted by the other view.

The idea is to manipulate and to look at a virtual complex model partially or completely from different perspectives.

With respect to the id for charts one more note: One of the impressive features of the current layout is the automatic scaling of charts with a change in size of the window.

Would it be possible to align the width of a chart at the surrounding flexbox/container? The height should comply with a specified ratio of height to width. Fixed specifications are rarely a good idea.

"layout": {
    "plugin": "layout",
    "state": {
      "layout": {
        "barChart": {
          "id": "barchartcontainer",
          "width": "container",
          "hight": "60%",  // 60% of the width
        }
      }
    }
  }

With respect to the implementation, I would like to support you. But I would certainly have questions about your programs.

curran commented 9 years ago

I Arne,

Thank you for exposing me to Material Design, those components look amazing! I would also be interested in exploring how to do an integration with Chiasm and Material Design UI elements. I have never attempted this before, so I'm really not sure what the best approach would be.

The concepts of controllers and views are more in the Angular world, and Chiasm does not really work in terms of those concepts. In Chiasm, there is the single central representation of state, the "config", which drives the components (which are kind of like views). In Chiasm, it would make sense to make a Crossfilter component, then link that with visualization components.

@Hypercubed made an attempt to integrate Chiasm with Angular. You can see his results at http://hypercubed.github.io/Project-Chi/#/ . The Angular integration seems to happen here https://github.com/Hypercubed/Project-Chi/blob/3b7702f0fecfdf3833849412a7cb1af48f4c8460/app/components/examples/chiasm/chiasm.js

One idea I had was that one could perhaps make an Angular component that wraps around Chiasm. Then Angular string templating could be used on the config, so values from UI widgets could be passed through to visualizations/components that way.

With the layout idea, it would definitely be possible to have components detect the size of their containers and use that. This would allow you to use other technologies to actually set the size (e.g. via CSS / Bootstrap grid layout). Here's the code that accesses the CSS-computed size of the Chiasm container. Perhaps this same technique could be used to extract the size for individual components.

I'm not sure about the idea of having a Crossfilter on the root scope interacting with Chiasm components. That might make sense if each visualization were an Angular component, which is maybe another approach worth investigating. If you are going to use Chiasm though, perhaps it would be best to wrap Crossfilter as a Chiasm component, then have the visualization instances all share a reference to that, via the Links plugin. You can see one example of this here http://bl.ocks.org/curran/19d42e98ce25291eb45d

curran commented 9 years ago

Based on the feedback in this thread, here are some considerations/questions that I would like to address with a refactoring effort.

Perhaps one approach would be to have an optional containerId property on the components themselves, as an alternative to using the Chiasm layout plugin. In this case, the notion of a "Chiasm container" would no longer be needed in all cases, so this concept should be moved out of the Chiasm runtime constructor, and should exist only within the layout plugin.

Just some thoughts. Thank you @Hypercubed and @Arne-Pfeilsticker for your feedback.

Arne-Pfeilsticker commented 8 years ago

Hi Curran, I myself use Material Design (MD) only recently. I like the whole concept of material design very well.

I think Chiasm could win if the layout is performed externally. Let the specialists do their job.

The width of the charts should be determined by the surrounding container. The height should be determined by specifying an aspect ratio in the config file.

For example, in the case of MD:

<div layout="row" layout-align="space-between center" layout-fill layout-sm="column">
    <div flex="60">
        <div layout="column" layout-align="space-between center" layout-fill>
            <h2 class="md-title">Einzahlungen in Mio €</h2>
            <div id="Chart1"></div>
        </div>
    </div>
    <div flex>
        <div id="Chart2"></div>
    </div>
</div>

In this example Chart1 should take 60% and Chart2 40% of the available width. Specifying layout-sm = "column" means that on small devices, the row layout is changed into column layout. In this case, the width of the container is 100% and both charts should adapted accordingly.

A flexible external orientation would probably work without modification with bootstrap or CSS, too.

$rootScope in the context of Angular does not mean DOM root scope, but application root scope. And the idea is to share the same crossfilter between different controllers and views.

curran commented 8 years ago

Hello @Hypercubed and @Arne-Pfeilsticker ,

I am currently working on a refactoring of the Chiasm layout and plugin API that you can see here: http://bl.ocks.org/curran/b4aa88691528c0f0b1fa

This new structure will support the two alternative DOM injection strategies we have discussed here:

  1. The "layout" plugin (existing), which will manage nested boxes within a single container, and
  2. The "injector" plugin (described above, not written yet), which will inject the DOM elements for Chiasm components into arbitrary existing DOM elements based on their IDs.

I'd appreciate any feedback you may have on the refactoring. I plan to push it through the whole codebase soon.

curran commented 8 years ago

The new layout plugin now lives in its own repository: https://github.com/chiasm-project/chiasm-layout

Thanks to @jmlewis for insights on how to structure the DOM management. Now the Chiasm core is free from DOM logic, and it is all managed by the layout plugin.

Hypercubed commented 8 years ago

Hi @curran I'll give it a try soon. One thing I notice in the example, however, is you are still using divs for your layout and each plugin creates its own svg... is that true? How could one create a single svg that is composed of multiple components?

curran commented 8 years ago

Indeed, the layout plugin injects the DOM elements for each component into a parent div. This is so multiple different technologies can be used by plugins, such as Canvas, WebGL, and Leaflet maps. Putting everything into a single SVG would limit plugins to be SVG only.

Perhaps you mentioned it before, but I forgot - what is your use case for having multiple visualizations in a single SVG?

Hypercubed commented 8 years ago

I agree in general the plugin should be able to use non-SVG elements (canvas, etc). But perhaps this plugin or a separate one could handle layouts within a single SVG.

Visualizations are composed of elements, for example axis and legends or a chart composed of multiple chart types. Currently axes and legends are within a single SVG and do not take advantage of the layout plugin. For a chart composed of multiple chart types if the user wants to save a publication quality image it should be a single SVG.

curran commented 8 years ago

Ah yes that's right, for printing. That makes sense, but I'm not sure how to make it happen at the top level of the nested box layout while also still supporting different kinds of DOM elements. I will keep it in mind though as a feature that would be nice to have.

Maybe the layout plugin can manage its own SVG element, and components that expose a special property other than el, maybe it could be called svg_el or something, and that would be appended to the single SVG element managed by the layout plugin. Hmmm..

curran commented 8 years ago

Hello @Hypercubed , I have done an experiment with adding multiple visualizations to a single SVG element. Please take a look at this example Fundamental Visualizations and let me know if this looks like a good solution to you. The layout plugin here manages a DIV and an SVG, and adds component containers to one or the other depending on whether or not their el property is an SVG Graphics Element or not. The Scatter Plot is the only visualization that currently takes advantage of the SVG API.

Hypercubed commented 8 years ago

Hello @curran, I haven't had time to look in detail... quickly it appears that Chiasm (or the layout plugin, I'm not sure) creates two layers within the chiasm container, one div element and one svg element. Each chiasm plugin generates either a SVGGraphicsElement or an HTMLElement then Chiasm figures out which layer it goes on and the appropriate way to position and size the element within the container. So now Chiasm plugins can just generate an g element and chaism will know that it needs to be in an SVG layer? That is amazing! Can it nest? Can I create a plugin that is a layout of plugins that is, etc. Chiasm plugins all the way down!

Arne-Pfeilsticker commented 8 years ago

Hello @curran, I am following with great interest the development of your layout plugins. What I have not yet understood is how to distribute individual charts or groups of charts on different DIV IDs. What I mean, I pointed out in my post on 3 August. Could you please give an example of this?

All your examples relate hitherto to a single external id. I think there are many use cases in which the parent layout for example is made with bootstrap or material design.

curran commented 8 years ago

@Hypercubed Yes, that's right! I want to start experimenting with nesting. I think everything is indeed nestable, but I haven't got an example of that working. Related to nesting, one thing I'd like to explore is small multiples. Conceivably, a small multiples plugin could create a small multiples visualization using any arbitrary Chaism visualization plugins. Perhaps this could be done by having the plugin create nested Chiasm instance that dynamically generates the layout and visualization configurations based on data.

@Arne-Pfeilsticker Your use case helped shape the refactoring, and what you describe (injecting the Chaism components into existing Divs) is now possible. Each component now creates its own el DOM element (similarly to el in Backbone views) that exists independently from the chiasm-layout plugin. I would like to create an example that injects components into a Bootstrap grid, but haven't created it yet. I think the best approach would be to create a variation of chiasm-layout, maybe called chiasm-injector, that can be configured with a mapping between DIV IDs and Chiasm component aliases. I will post here when such an example exists.

Arne-Pfeilsticker commented 8 years ago

Hello @curran, may I send you a very small angular material design app that you could then supplement accordingly with Chiasm?

The idea would be to have a Angular / Material Design variant of your fundamental Visualizations example.

curran commented 8 years ago

@Arne-Pfeilsticker Oh yes please do send me your example.

I'm starting on an example now actually and I just want to make sure that it matches what you are looking for - Here's a Bootstrap layout example in JSBin that has divs with different IDs. The idea is that Chiasm components will be injected into each one, and will be automatically resized to be the same size as the container.

I'm thinking as a first phase, we can assume that the size of the container is set already via CSS, and the Chiasm component should be resized to fit inside it. As a second phase, maybe we can try adding width and height specifications into the Chaism configuration.

Arne-Pfeilsticker commented 8 years ago

Hello @curran, could you please have a look at the project https://github.com/Arne-Pfeilsticker/magic-bar-chart and give me a few hints. I'm trying to combine Chiasm, AngularJS and Material Design. In your Bootstrap layout example it is not clear to me how to do the layout configuration. But from the idea, your example is exactly what I want.

curran commented 8 years ago

I realized that it should be possible to use multiple instances of the existing Chiasm layout plugin (without modification) to inject visualizations into elements managed by CSS. Here's one example of this in action: Bootstrap + Chiasm. It's a bit verbose, but I think it pretty much does what you are looking for. Maybe we can use this as a starting point for a more clean solution, maybe an alternative to chiasm-layout like "chiasm-injector" that will inject multiple charts into multiple existing divs.

Arne-Pfeilsticker commented 8 years ago

Hello @curran, I am thrilled by your Bootstrap + Chiasm example. Just as I had imagined.

I've changed the example from Bootstrap to Angular Material. In a Crome browser it works correctly. Unfortunately, it works in Firefox not quite right. The

<div class = "chiasm-container-for-non-SVG-element"> </ div>

is much too high.

You can reproduce the error in my Chaism Playground project. Here I try to put Chiasm in a larger application context. About comments and suggestions I would be very grateful.

curran commented 8 years ago

Hi @Arne-Pfeilsticker , I'm happy to see your work with the Chiasm playground. Thank you for isolating this positioning bug. I had seen the positioning be off before but then it went away and I never tracked it down. I think it has to do with the CSS 'position' attribute of the parent element of the Chiasm container, or something like that. I will try to investigate.

curran commented 8 years ago

This is a great discussion, but I'm closing this as an issue in favor of smaller granularity issues: