block by curran b4aa88691528c0f0b1fa

Chiasm Foundation

Full Screen

An example that demonstrates basic features of Chiasm.

The Chiasm plugins demonstrated here are

web counter

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Chiasm Foundation</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
    
    <!-- A functional reactive model library. github.com/curran/model -->
    <script src="//curran.github.io/model/cdn/model-v0.2.4.js"></script>

    <!-- Chiasm core and plugins. github.com/chiasm-project -->
    <script src="//chiasm-project.github.io/chiasm/chiasm-v0.2.0.js"></script>
    <script src="//chiasm-project.github.io/chiasm-component/chiasm-component-v0.2.0.js"></script>
    <script src="//chiasm-project.github.io/chiasm-layout/chiasm-layout-v0.2.1.js"></script>
    <script src="//chiasm-project.github.io/chiasm-links/chiasm-links-v0.2.0.js"></script>

    <!-- Custom Chiasm plugins for this example. -->
    <script src="dummyVis.js"></script>
    <script src="wave.js"></script>

    <style>

      body {
        background-color: black;
      }

      /* Make the chart container fill the page using CSS. */
      #chiasm-container {
        position: fixed;
        left: 20px;
        right: 20px;
        top: 20px;
        bottom: 20px;
      }
    </style>

  </head>
  <body>
    <div id="chiasm-container"></div>

    <script>

      var chiasm = Chiasm();

      chiasm.plugins.layout = ChiasmLayout;
      chiasm.plugins.links = ChiasmLinks;
      chiasm.plugins.dummyVis = DummyVis;
      chiasm.plugins.wave = Wave;

      chiasm.setConfig({
        "layout": {
          "plugin": "layout",
          "state": {
            "containerSelector": "#chiasm-container",
            "layout": {
              "orientation": "vertical",
              "children": [
                "A",
                {
                  "orientation": "horizontal",
                  "children": [
                    "B",
                    "wave",
                    "C"
                  ]
                }
              ]
            },
            "sizes": {
              "wave": {
                "size": 3
              }
            }
          }
        },
        "A": {
          "plugin": "dummyVis",
          "state": {
            "text": "A",
            "color": "#a8ffd0"
          }
        },
        "B": {
          "plugin": "dummyVis",
          "state": {
            "text": "B",
            "color": "#a8f0ff"
          }
        },
        "C": {
          "plugin": "dummyVis",
          "state": {
            "text": "C",
            "color": "#ffe2a8"
          }
        },
        "wave": {
          "plugin": "wave",
          "state": {
            "waterColor": "#005387",
            "skyColor": "#7DCDFF"
          }
        },
        "links": {
          "plugin": "links",
          "state": {
            "bindings": [
              "B.lineWidth -> C.lineWidth"
            ]
          }
        }
      });
    
    </script>
  </body>
</html>

dummyVis.js

// This is an example Chaism plugin that uses D3.  A colored rectangle is
// created with an X in the background and text in the foreground.  The X in the
// background is interactive. Clicking and dragging it updates `lineWidth`.
function DummyVis() {

  // Construct a Chiasm component instance,
  // specifying default values for public properties.
  var my = ChiasmComponent({

    // The background color, a CSS color string.
    color: "white",

    // The string that gets displayed in the center of the box.
    text: "",

    // The width in pixels of lines for the X.
    lineWidth: 8

  });

  // Expose a div element that will be added to the Chiasm container.
  // This is a special property that Chiasm looks for after components are constructed.
  my.el = document.createElement("div");

  // Construct the SVG DOM.
  var svg = d3.select(my.el).append("svg");

  // Add a background rectangle to the SVG.
  // The location of the rect will be fixed at (0, 0)
  // with respect to the containing SVG.
  svg.append("rect")
    .attr("x", 0)
    .attr("y", 0);

  // Add a text element to the SVG,
  // which will render the `text` my property.
  svg.append("text")
    .attr("font-size", "7em")
    .attr("text-anchor", "middle")
    .attr("alignment-baseline", "middle");

  // Update the color and text based on the my.
  my.when("color", function (color){
    svg.select("rect").attr("fill", color);
  });

  // Update the text.
  my.when("text", function (text){
    svg.select("text").text(text);
  });

  // When the size of the visualization is set
  // by the chiasm layout engine,
  my.when("box", function (box) {

    // Set the size of the SVG.
    svg
      .attr("width", box.width)
      .attr("height", box.height);

    // Set the size of the background rectangle.
    svg.select("rect")
      .attr("width", box.width)
      .attr("height", box.height);

    // Update the text label to be centered.
    svg.select("text")
      .attr("x", box.width / 2)
      .attr("y", box.height / 2);

  });

  // Update the X lines whenever either
  // the `box` or `lineWidth` my properties change.
  my.when(["box", "lineWidth"], function (box, lineWidth) {
    var w = box.width,
        h = box.height,
        lines = svg.selectAll("line").data([
          {x1: 0, y1: 0, x2: w, y2: h},
          {x1: 0, y1: h, x2: w, y2: 0}
        ]);
    lines.enter().append("line");
    lines
      .attr("x1", function (d) { return d.x1; })
      .attr("y1", function (d) { return d.y1; })
      .attr("x2", function (d) { return d.x2; })
      .attr("y2", function (d) { return d.y2; })
      .style("stroke-width", lineWidth)
      .style("stroke-opacity", 0.2)
      .style("stroke", "black")
      .call(lineDrag);
  });

  // Make the X lines draggable. This shows how to add interaction to
  // visualization modules.  Dragging updates the `lineWidth` property.
  var lineDrag = (function () {
    var x1, x2;
    return d3.behavior.drag()
      .on("dragstart", function (d) {
        x1 = d3.event.sourceEvent.pageX;
      })
      .on("drag", function (d) {
        var x2 = d3.event.sourceEvent.pageX,
            newLineWidth = my.lineWidth + x2 - x1;

        // Enforce minimum line width of 1 pixel.
        newLineWidth = newLineWidth < 1 ? 1 : newLineWidth;

        // Set the lineWidth property on the component.
        // This change will be propagated into the Chiasm configuration.
        my.lineWidth = newLineWidth;

        x1 = x2;
      });
  }());

  return my;
}

wave.js

// This is an example Chiasm plugin based on HTML5 Canvas.
// This displays a wave simulation.
// Clicking on the canvas interacts with the simulation.
// Note that D3.js is not used at all here.
function Wave (){

  var my = ChiasmComponent({
    waterColor: "red",
    skyColor: Model.None,
    gridSize: 50,
    pullStrength: 0.01,
    dampeningFactor: 0.99,
    initialHeight: 0.5
  });

  // These functions will be populated later when the canvas is available.
  var drawCells = function (){};
  var clearCanvas = function (){};
  var executeMouseInteraction = function (){};
  var iterateSimulation = function (){};

  var canvas = document.createElement("canvas");
  var c = canvas.getContext("2d");

  var cells = [];

  // Poor man's RAF polyfill
  var nextFrame = requestAnimationFrame || setTimeout;

  // Expose the canvas as the element that Chiasm will append to its container.
  my.el = canvas;

  // This function executes once per animation frame
  function executeFrame(){
    nextFrame(executeFrame);
    clearCanvas();
    drawCells();
    iterateSimulation();
    executeMouseInteraction();
  }

  // Initialize the cells.
  my.when(["gridSize", "initialHeight"], function (gridSize, initialHeight) {

    cells = [];

    // Initialize the water height
    for(var i = 0; i < gridSize; i++){
      cells.push({

        // for a still initial surface
        //height: 0.5,
         
        // for an initial wave:
        height: i === Math.floor(gridSize*1/4) ? 2 : initialHeight,
        
        velocity: 0
      });
    }
  });

  my.when(["box", "gridSize", "waterColor", "skyColor"],
      function (box, gridSize, waterColor, skyColor){

    clearCanvas = function (){
      canvas.width = box.width;
      canvas.height = box.height;

      if(skyColor !== Model.None){
        c.fillStyle = skyColor;
        c.fillRect(0, 0, box.width, box.height);
      }
    }

    drawCells = function (){
      c.beginPath();
      c.moveTo(box.width, box.height);
      c.lineTo(0, box.height);
      for(var i = 0; i < gridSize; i++){
        var cell = cells[i];
        var x = i / (gridSize-1) * box.width;
        var y = box.height - cell.height * box.height;
        c.lineTo(x,y);
      }
      c.closePath();
      c.fillStyle = waterColor;
      c.fill();
    }
  });

  my.when(["gridSize", "pullStrength", "dampeningFactor", "initialHeight"],
      function (gridSize, pullStrength, dampeningFactor, initialHeight){

    var conservationOfMassCorrection = 0;

    // Increment the wave simulation:
    // Neighboring cells pull on one another.
    iterateSimulation = function(){
      var avgHeight = 0;
      for(var i = 0; i < gridSize; i++){
        // center cell
        var c = cells[i];
        
        // left neighbor
        var l = cells[((i - 1) + gridSize) % gridSize];
        
        // right neighbor
        var r = cells[(i + 1) % gridSize];
        
        // pull toward neighbors
        c.velocity += pullStrength * (l.height - c.height);
        c.velocity += pullStrength * (r.height - c.height);
        
        // increment velocity
        c.height += c.velocity;
        
        // ensure conservation of mass
        c.height += conservationOfMassCorrection;
        
        // apply dampening
        c.velocity *= dampeningFactor;
        
        avgHeight += c.height;
      }
      avgHeight /= (gridSize - 1);
      
      conservationOfMassCorrection = initialHeight - avgHeight;
    }
  });

  my.when(["mouseDown", "mouseX", "mouseY", "box"], function (mouseDown, mouseX, mouseY, box){

    var cellWidth = 1 / (cells.length - 1) * box.width;

    // Pull the wave cell closest to the mouse
    executeMouseInteraction = function(){
      if(mouseDown){
        for(var i = 0; i < cells.length; i++){
          var x = i / (cells.length - 1) * box.width;
          if(Math.abs(x - mouseX) < cellWidth){
            var cell = cells[i];
            cell.height = 1 - mouseY / box.height;
            cell.velocity = 0;
          }
        }
      }
    }
  });

  // Record when the mouse is pressed
  canvas.addEventListener("mousedown", function (e){
    my.mouseDown = true;

    // This line prevents unintended drag behavior, like selecting page elements.
    e.preventDefault();
  });

  // Record when the mouse is moved
  canvas.addEventListener("mousemove", function (e){
    var rect = canvas.getBoundingClientRect();
    my.mouseX = e.clientX - rect.left;
    my.mouseY = e.clientY - rect.top;
  });

  // Record when the mouse is released
  canvas.addEventListener("mouseup", function (e){
    my.mouseDown = false;
  });

  // Draw the first frame
  executeFrame();

  return my;
}