A simple, 'hands-on' example of solving a linear mathematical program using d3.js + Angular.js.
Last active
March 16, 2016 00:57
-
-
Save mtaptich/397426e88385a2a5d82a to your computer and use it in GitHub Desktop.
Solve a Linear Program
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /****** dropdown-select *******/ | |
| .wrap-dd-select { | |
| /* Size and position */ | |
| position: relative; | |
| width: 80px; | |
| margin: 0 auto; | |
| padding: 5px 10px; | |
| padding-right: 35px; | |
| margin-top: -6px; | |
| -webkit-user-select:none; | |
| -moz-user-select:none; | |
| user-select:none; | |
| /* Styles */ | |
| background: #fff; | |
| border-radius: 7px; | |
| border: 1px solid rgba(0,0,0,0.15); | |
| box-shadow: 0 1px 1px rgba(50,50,50,0.1); | |
| cursor: pointer; | |
| outline: none; | |
| /* Font settings */ | |
| font-weight: bold; | |
| color: #000; | |
| font-size: 20px; | |
| display: inline-block; | |
| } | |
| .wrap-dd-select:after { | |
| content: ""; | |
| width: 0; | |
| height: 0; | |
| position: absolute; | |
| right: 15px; | |
| top: 50%; | |
| margin-top: -3px; | |
| border-width: 6px 6px 0 6px; | |
| border-style: solid; | |
| border-color: #8aa8bd transparent; | |
| } | |
| .wrap-dd-select .dropdown { | |
| /* Size & position */ | |
| position: absolute; | |
| top: 100%; | |
| left: 0; | |
| right: 0; | |
| z-index: 1000; | |
| /* Styles */ | |
| background: white; | |
| padding: 0; | |
| border-radius: inherit; | |
| border: 1px solid rgba(0,0,0,0.17); | |
| box-shadow: 0 0 5px rgba(0,0,0,0.1); | |
| font-weight: normal; | |
| transition: all 0.2s ease-in; | |
| list-style: none; | |
| /* Hiding */ | |
| opacity: 0; | |
| pointer-events: none; | |
| } | |
| .wrap-dd-select .dropdown li.divider { | |
| padding: 2px 0; | |
| background: #e6e8ea; | |
| } | |
| .wrap-dd-select .dropdown li a { | |
| display: block; | |
| padding: 5px 10px; | |
| text-decoration: none; | |
| color: #8aa8bd; | |
| border-bottom: 1px solid #e6e8ea; | |
| box-shadow: inset 0 1px 0 rgba(255,255,255,1); | |
| transition: all 0.3s ease-out; | |
| } | |
| .wrap-dd-select .dropdown li i { | |
| float: right; | |
| color: inherit; | |
| } | |
| .wrap-dd-select .dropdown li:first-of-type a { | |
| border-radius: 7px 7px 0 0; | |
| } | |
| .wrap-dd-select .dropdown li:last-of-type a { | |
| border-radius: 0 0 7px 7px; | |
| border: none; | |
| } | |
| /* Hover state */ | |
| .wrap-dd-select .dropdown li:hover a { | |
| background: #f3f8f8; | |
| } | |
| .wrap-dd-select .dropdown:after { | |
| content: ""; | |
| width: 0; | |
| height: 0; | |
| position: absolute; | |
| bottom: 100%; | |
| right: 15px; | |
| border-width: 0 6px 6px 6px; | |
| border-style: solid; | |
| border-color: #fff transparent; | |
| } | |
| .wrap-dd-select .dropdown:before { | |
| content: ""; | |
| width: 0; | |
| height: 0; | |
| position: absolute; | |
| bottom: 100%; | |
| right: 13px; | |
| border-width: 0 8px 8px 8px; | |
| border-style: solid; | |
| border-color: rgba(0,0,0,0.1) transparent; | |
| } | |
| .wrap-dd-select.active .dropdown { | |
| opacity: 1; | |
| pointer-events: auto; | |
| } | |
| /****** dropdown-menu *******/ | |
| .wrap-dd-menu { | |
| /* Size and position */ | |
| position: relative; | |
| z-index: 1000; | |
| width: 200px; | |
| margin: 0 auto; | |
| padding: 10px; | |
| } | |
| .wrap-dd-menu .dropdown { | |
| /* Size & position */ | |
| position: absolute; | |
| z-index: 1000; | |
| top: 70%; | |
| left: 0; | |
| right: 0; | |
| /* Styles */ | |
| background: white; | |
| padding: 0; | |
| border-radius: 7px; | |
| border: 1px solid rgba(0,0,0,0.17); | |
| box-shadow: 0 0 5px rgba(0,0,0,0.1); | |
| font-weight: normal; | |
| transition: all 0.2s ease-in; | |
| list-style: none; | |
| /* Hiding */ | |
| opacity: 0; | |
| pointer-events: none; | |
| } | |
| .wrap-dd-menu .dropdown li.divider { | |
| padding: 2px 0; | |
| background: #e6e8ea; | |
| } | |
| .wrap-dd-menu .dropdown li a { | |
| display: block; | |
| padding: 10px; | |
| text-decoration: none; | |
| color: #8aa8bd; | |
| border-bottom: 1px solid #e6e8ea; | |
| box-shadow: inset 0 1px 0 rgba(255,255,255,1); | |
| transition: all 0.3s ease-out; | |
| } | |
| .wrap-dd-menu .dropdown li i { | |
| float: right; | |
| color: inherit; | |
| } | |
| .wrap-dd-menu .dropdown li:first-of-type a { | |
| border-radius: 7px 7px 0 0; | |
| } | |
| .wrap-dd-menu .dropdown li:last-of-type a { | |
| border-radius: 0 0 7px 7px; | |
| border: none; | |
| } | |
| /* Hover state */ | |
| .wrap-dd-menu .dropdown li:hover a { | |
| background: #f3f8f8; | |
| } | |
| .wrap-dd-menu .dropdown:after { | |
| content: ""; | |
| width: 0; | |
| height: 0; | |
| position: absolute; | |
| bottom: 100%; | |
| border-width: 0 6px 6px 6px; | |
| border-style: solid; | |
| border-color: #fff transparent; | |
| } | |
| .wrap-dd-menu .dropdown:before { | |
| content: ""; | |
| width: 0; | |
| height: 0; | |
| position: absolute; | |
| bottom: 100%; | |
| right: 95px; | |
| border-width: 0 8px 8px 8px; | |
| border-style: solid; | |
| border-color: rgba(0,0,0,0.1) transparent; | |
| } | |
| .wrap-dd-menu .dropdown.active { | |
| opacity: 1; | |
| pointer-events: auto; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @license MIT http://jseppi.mit-license.org/license.html | |
| */ | |
| (function(window, angular, undefined) { | |
| 'use strict'; | |
| var dd = angular.module('ngDropdowns', []); | |
| dd.run(['$templateCache', function ($templateCache) { | |
| $templateCache.put('ngDropdowns/templates/dropdownSelect.html', [ | |
| '<div class="wrap-dd-select">', | |
| '<span class="selected">{{dropdownModel[labelField]}}</span>', | |
| '<ul class="dropdown">', | |
| '<li ng-repeat="item in dropdownSelect"', | |
| ' class="dropdown-item"', | |
| ' dropdown-select-item="item"', | |
| ' dropdown-item-label="labelField">', | |
| '</li>', | |
| '</ul>', | |
| '</div>' | |
| ].join('')); | |
| $templateCache.put('ngDropdowns/templates/dropdownSelectItem.html', [ | |
| '<li ng-class="{divider: dropdownSelectItem.divider}">', | |
| '<a href="" class="dropdown-item"', | |
| ' ng-if="!dropdownSelectItem.divider"', | |
| ' ng-href="{{dropdownSelectItem.href}}"', | |
| ' ng-click="selectItem()">', | |
| '{{dropdownSelectItem[dropdownItemLabel]}}', | |
| '</a>', | |
| '</li>' | |
| ].join('')); | |
| $templateCache.put('ngDropdowns/templates/dropdownMenu.html', [ | |
| '<ul class="dropdown">', | |
| '<li ng-repeat="item in dropdownMenu"', | |
| ' class="dropdown-item"', | |
| ' dropdown-item-label="labelField"', | |
| ' dropdown-menu-item="item">', | |
| '</li>', | |
| '</ul>' | |
| ].join('')); | |
| $templateCache.put('ngDropdowns/templates/dropdownMenuItem.html', [ | |
| '<li ng-class="{divider: dropdownMenuItem.divider}">', | |
| '<a href="" class="dropdown-item"', | |
| ' ng-if="!dropdownMenuItem.divider"', | |
| ' ng-href="{{dropdownMenuItem.href}}"', | |
| ' ng-click="selectItem()">', | |
| '{{dropdownMenuItem[dropdownItemLabel]}}', | |
| '</a>', | |
| '</li>' | |
| ].join('')); | |
| }]); | |
| dd.directive('dropdownSelect', ['DropdownService', | |
| function (DropdownService) { | |
| return { | |
| restrict: 'A', | |
| replace: true, | |
| scope: { | |
| dropdownSelect: '=', | |
| dropdownModel: '=', | |
| dropdownOnchange: '&' | |
| }, | |
| controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) { | |
| $scope.labelField = $attrs.dropdownItemLabel || 'text'; | |
| DropdownService.register($element); | |
| this.select = function (selected) { | |
| if (selected !== $scope.dropdownModel) { | |
| angular.copy(selected, $scope.dropdownModel); | |
| } | |
| $scope.dropdownOnchange({ | |
| selected: selected | |
| }); | |
| }; | |
| $element.bind('click', function (event) { | |
| event.stopPropagation(); | |
| DropdownService.toggleActive($element); | |
| }); | |
| $scope.$on('$destroy', function () { | |
| DropdownService.unregister($element); | |
| }); | |
| }], | |
| templateUrl: 'ngDropdowns/templates/dropdownSelect.html' | |
| }; | |
| } | |
| ]); | |
| dd.directive('dropdownSelectItem', [ | |
| function () { | |
| return { | |
| require: '^dropdownSelect', | |
| replace: true, | |
| scope: { | |
| dropdownItemLabel: '=', | |
| dropdownSelectItem: '=' | |
| }, | |
| link: function (scope, element, attrs, dropdownSelectCtrl) { | |
| scope.selectItem = function () { | |
| if (scope.dropdownSelectItem.href) { | |
| return; | |
| } | |
| dropdownSelectCtrl.select(scope.dropdownSelectItem); | |
| }; | |
| }, | |
| templateUrl: 'ngDropdowns/templates/dropdownSelectItem.html' | |
| }; | |
| } | |
| ]); | |
| dd.directive('dropdownMenu', ['$parse', '$compile', 'DropdownService', '$templateCache', | |
| function ($parse, $compile, DropdownService, $templateCache) { | |
| return { | |
| restrict: 'A', | |
| replace: false, | |
| scope: { | |
| dropdownMenu: '=', | |
| dropdownModel: '=', | |
| dropdownOnchange: '&' | |
| }, | |
| controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) { | |
| $scope.labelField = $attrs.dropdownItemLabel || 'text'; | |
| var $template = angular.element($templateCache.get('ngDropdowns/templates/dropdownMenu.html')); | |
| // Attach this controller to the element's data | |
| $template.data('$dropdownMenuController', this); | |
| var tpl = $compile($template)($scope); | |
| var $wrap = angular.element('<div class="wrap-dd-menu"></div>'); | |
| $element.replaceWith($wrap); | |
| $wrap.append($element); | |
| $wrap.append(tpl); | |
| DropdownService.register(tpl); | |
| this.select = function (selected) { | |
| if (selected !== $scope.dropdownModel) { | |
| angular.copy(selected, $scope.dropdownModel); | |
| } | |
| $scope.dropdownOnchange({ | |
| selected: selected | |
| }); | |
| }; | |
| $element.bind('click', function (event) { | |
| event.stopPropagation(); | |
| DropdownService.toggleActive(tpl); | |
| }); | |
| $scope.$on('$destroy', function () { | |
| DropdownService.unregister(tpl); | |
| }); | |
| }] | |
| }; | |
| } | |
| ]); | |
| dd.directive('dropdownMenuItem', [ | |
| function () { | |
| return { | |
| require: '^dropdownMenu', | |
| replace: true, | |
| scope: { | |
| dropdownMenuItem: '=', | |
| dropdownItemLabel: '=' | |
| }, | |
| link: function (scope, element, attrs, dropdownMenuCtrl) { | |
| scope.selectItem = function () { | |
| if (scope.dropdownMenuItem.href) { | |
| return; | |
| } | |
| dropdownMenuCtrl.select(scope.dropdownMenuItem); | |
| }; | |
| }, | |
| templateUrl: 'ngDropdowns/templates/dropdownMenuItem.html' | |
| }; | |
| } | |
| ]); | |
| dd.factory('DropdownService', ['$document', | |
| function ($document) { | |
| var body = $document.find('body'), | |
| service = {}, | |
| _dropdowns = []; | |
| body.bind('click', function () { | |
| angular.forEach(_dropdowns, function (el) { | |
| el.removeClass('active'); | |
| }); | |
| }); | |
| service.register = function (ddEl) { | |
| _dropdowns.push(ddEl); | |
| }; | |
| service.unregister = function (ddEl) { | |
| var index; | |
| index = _dropdowns.indexOf(ddEl); | |
| if (index > -1) { | |
| _dropdowns.splice(index, 1); | |
| } | |
| }; | |
| service.toggleActive = function (ddEl) { | |
| angular.forEach(_dropdowns, function (el) { | |
| if (el !== ddEl) { | |
| el.removeClass('active'); | |
| } | |
| }); | |
| ddEl.toggleClass('active'); | |
| }; | |
| return service; | |
| } | |
| ]); | |
| })(window, window.angular); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <html> | |
| <head> | |
| <!-- CSS --> | |
| <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"> | |
| <link href="angular-dropdowns.css" rel="stylesheet"> | |
| <link href="style.css" rel="stylesheet"> | |
| <!-- JS --> | |
| <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.18/angular.min.js"></script> | |
| <script src="angular-dropdowns.js"></script> | |
| <script src="http://d3js.org/d3.v3.min.js"></script> | |
| </head> | |
| <body ng-app="LPapp" ng-controller="LPcontroller"> | |
| <h2>Objective Value: Z = {{xobj+"x +"+yobj+"y = "}} <span ng-style="UporDown">{{(xobj*Z.x + yobj*Z.y | number : 5)}}</span></h2> | |
| <div class="row"> | |
| <div style="float: left;"> | |
| <div linear-chart chart-data="lines"></div> | |
| </div> | |
| <div style="float: right; margin-top:25px"> | |
| <div> | |
| <form > | |
| <span style="font-weight: bold;">OBJ: </span> | |
| <div dropdown-select="objOptions" dropdown-model="objSelected" class="btn" ></div> | |
| <input type="text" ng-model="xobj" size="3" | |
| placeholder="Cx"> x + | |
| <input type="text" ng-model="yobj" size="3" | |
| placeholder="Cy"> y   | |
| </form> | |
| </div> | |
| <span style="font-weight: bold;">Subject to:</span> | |
| <br> | |
| <ul class="unstyled"> | |
| <li ng-repeat="constraint in constraints"> | |
| <span>{{constraint.text}}</span> | |
| </li> | |
| </ul> | |
| <div> | |
| <form ng-submit="addconstraint()"> | |
| <input type="text" ng-model="xconstraint" size="3" | |
| placeholder="Ax"> x + | |
| <input type="text" ng-model="yconstraint" size="3" | |
| placeholder="Ay"> y   | |
| <div dropdown-select="ddSelectOptions" dropdown-model="ddSelectSelected" class="btn"></div> | |
| <input type="text" ng-model="bconstraint" size="3" | |
| placeholder="b"> | |
| <input class="btn" type="submit" value="add"> | |
| </form> | |
| <div class="clear"> | |
| [ <a href="" ng-click="poplast()">undo</a> ]   [ <a href="" ng-click="clear()">clear</a> ] | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <p style="font-size: 0.9em;"> (Hint: If you over-constrain your circle, click the feasile region and it will reappear.)</p> | |
| <p>Drag the blue ball to the coordinate in the x-y space that optimizes each linear program. Feel free to build your own problems, too. Examples: <a data-id='ex22' ng-click='updateexample($event)'>Problem 1</a>, <a data-id='ex23' ng-click='updateexample($event)'>Problem 2</a>. </p> | |
| </body> | |
| <!-- Angular.js APP --> | |
| <script type="text/javascript"> | |
| var app = angular.module('LPapp', ['ngDropdowns']) | |
| app.controller('LPcontroller', ['$scope', function($scope) { | |
| $scope.example = "ex21" | |
| $scope.options = {"ex21": [{text: "x >= 0"}, {text: "x <= 10"},{text: "y >= 0"}, {text: "y <= 10"}], "ex22": [{text: "x >= 0"}, {text: "x <= 10"},{text: "y >= 0"}, {text: "y <= 10"}, {text: "1x + 1y <= 12"}, {text: "3x + 1y <= 25"}] } | |
| $scope.constraints = [{text: "x >= 0"}, {text: "x <= 10"},{text: "y >= 0"}, {text: "y <= 10"}]; | |
| $scope.lines = []; | |
| $scope.obj = [] | |
| $scope.yconstraint = ''; | |
| $scope.xconstraint = ''; | |
| $scope.bconstraint = ''; | |
| $scope.yobj = 1; | |
| $scope.xobj = 1; | |
| $scope.UporDown = {color:'#000'} | |
| $scope.xybound = [{x: -1, y: 0, b:0, sign:"<="}, {x: 1, y: 0, b:10, sign:"<="}, {x: 0, y: -1, b:0, sign:"<="},{x: 0, y: 1, b:10, sign:"<="}]; | |
| $scope.Z = {x: 0, y:0}; | |
| $scope.updateexample = function(obj){ | |
| $scope.example = obj.target.attributes["data-id"].value; | |
| } | |
| $scope.addconstraint = function() { | |
| if($scope.xconstraint !="" && $scope.yconstraint !="" && $scope.bconstraint !=""){ | |
| array = [$scope.xconstraint, $scope.yconstraint] | |
| $scope.constraints.push( | |
| { | |
| text:array[0]+"x + "+array[1]+"y "+$scope.ddSelectSelected.text+" "+$scope.bconstraint | |
| } | |
| ); | |
| if ($scope.yconstraint == 0) $scope.yconstraint = 0.00005; | |
| $scope.lines.push([ | |
| { | |
| xdata: 0, ydata: +$scope.bconstraint / +$scope.yconstraint, bdata: +$scope.bconstraint, sign: $scope.ddSelectSelected.text | |
| }, | |
| { | |
| xdata: 10, ydata: (+$scope.bconstraint -10*+$scope.xconstraint) / +$scope.yconstraint, bdata: +$scope.bconstraint, sign:$scope.ddSelectSelected.text | |
| }] | |
| ) | |
| $scope.xybound.push({x:$scope.xconstraint, y:$scope.yconstraint, b:$scope.bconstraint, sign:$scope.ddSelectSelected.text}) | |
| $scope.yconstraint = ''; | |
| $scope.xconstraint = ''; | |
| $scope.bconstraint = ''; | |
| } | |
| }; | |
| $scope.clear = function() { | |
| $scope.constraints = [{text: "x >= 0"}, {text: "x <= 10"},{text: "y >= 0"}, {text: "y <= 10"}]; | |
| $scope.lines = []; | |
| $scope.myStyle = {color:'#C33851', background:'#fff', borderColor: 'red'} | |
| $scope.yobj = 1; | |
| $scope.xobj = 1; | |
| $scope.xybound = [{x: -1, y: 0, b:0, sign:"<="}, {x: 1, y: 0, b:10, sign:"<="}, {x: 0, y: -1, b:0, sign:"<="},{x: 0, y: 1, b:10, sign:"<="}]; | |
| d3.selectAll(".area").remove() | |
| d3.selectAll(".line").remove() | |
| if(d3.select("#linearChart circle")[0][0] != null){ | |
| d3.select("#linearChart circle")[0][0].__data__.y = 330; | |
| d3.select("#linearChart circle")[0][0].__data__.x = 0; | |
| d3.select("#linearChart circle").transition().duration(300).attr("cx", 0).attr("cy", 330) | |
| } | |
| }; | |
| $scope.poplast = function(){ | |
| if($scope.constraints.length > 4){ | |
| var a = $scope.constraints.slice(0,-1) | |
| var b = $scope.lines.slice(0,-1) | |
| var c = $scope.xybound.slice(0,-1) | |
| $scope.constraints = a; | |
| $scope.lines = b; | |
| $scope.xybound = c; | |
| } | |
| } | |
| $scope.ddSelectOptions = [ | |
| { | |
| text: '<=', | |
| }, | |
| { | |
| text: '>=', | |
| } | |
| ]; | |
| $scope.ddSelectSelected = { text: '<='}; | |
| $scope.objOptions = [ | |
| { | |
| text: 'min', | |
| }, | |
| { | |
| text: 'max', | |
| } | |
| ]; | |
| $scope.objSelected = { text: 'max'}; // Must be an object | |
| }]) | |
| app.directive("linearChart", function($parse, $window) { | |
| return{ | |
| restrict: "EA", | |
| template: "<svg id='linearChart' width='600' height='400'></svg>", | |
| link: function(scope, elem, attrs){ | |
| var margin = {top: 20, right: 20, bottom: 50, left: 50}, | |
| width = 600 - margin.left - margin.right, | |
| height = 400 - margin.top - margin.bottom, | |
| xAxis, yAxis, line, x, ylc, line, areaup, clip, drag | |
| var d3 = $window.d3; | |
| var svg = d3.select("#linearChart").append("g") | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
| var clip = svg.append("defs").append("clipPath") | |
| .attr("id", "clip") | |
| .append("rect") | |
| .attr("id", "clip-rect") | |
| .attr("x", "0") | |
| .attr("y", "0") | |
| .attr("width", width) | |
| .attr("height", height); | |
| svg.append('svg:rect') | |
| .attr('width', width) | |
| .attr('height', height) | |
| .attr('fill', '#eee') | |
| .on("click", function(){ | |
| if (d3.select("circle")[0][0] == null){ | |
| var a = d3.mouse(this); | |
| svg.append("g") | |
| .attr("class", "dot") | |
| .selectAll("circle") | |
| .data([{x: a[0], y: a[1]}]) | |
| .enter().append("circle") | |
| .attr("r", 5) | |
| .attr("cx", function(d) { return d.x; }) | |
| .attr("cy", function(d) { return d.y; }) | |
| .call(drag); | |
| } | |
| }) | |
| var exp = $parse(attrs.chartData); | |
| var DataToPlot=exp(scope); | |
| // Update the constraints | |
| scope.$watchCollection(exp, function(newVal, oldVal) { | |
| var e = scope.xybound[scope.xybound.length - 1]; | |
| var cx = d3.select("#linearChart circle")[0][0].__data__.x | |
| var cy = d3.select("#linearChart circle")[0][0].__data__.y | |
| if (e.sign == "<="){ | |
| if (e.x * x.invert(cx) + e.y * ylc.invert(cy) > e.b){ | |
| d3.select("#linearChart circle").transition().duration(500).attr("r", 10).transition().duration(300).attr("r", 1).remove() | |
| } | |
| DataToPlot = newVal; | |
| drawline(); | |
| } else{ | |
| if (e.x * x.invert(cx) + e.y * ylc.invert(cy) < e.b){ | |
| d3.select("#linearChart circle").transition().duration(300).attr("r", 8).transition().duration(300).attr("r", 1).remove() | |
| } | |
| DataToPlot = newVal; | |
| drawline(); | |
| } | |
| }); | |
| //Load Examples | |
| scope.$watch("example", function(oldVal, newVal){ | |
| scope.clear() | |
| if(scope.example =="ex22"){ | |
| d3.select("#linearChart circle")[0][0].__data__.y = ylc(4); | |
| d3.select("#linearChart circle")[0][0].__data__.x = x(4); | |
| d3.select("#linearChart circle").transition().duration(300).attr("cx", x(4)).attr("cy", ylc(4)); | |
| scope.yobj = -2; | |
| scope.objSelected = { text: 'min'}; | |
| scope.constraints.push({text: "1x + 1y <= 12"}, {text: "3x + 1y <= 25"}, {text: "-2x + 1y <= 5"}, {text: "3x + 1y >= 6"}); | |
| scope.lines = [[{xdata: 12, ydata: 0, bdata: 12, sign:"<="}, {xdata: 0, ydata: 12, bdata: 12, sign:"<="}], [{xdata: 0, ydata: 25, bdata: 25, sign:"<="}, {xdata: 10, ydata: -5, bdata: 25, sign:"<="}],[{xdata: 0, ydata: 5, bdata: 5, sign:"<="}, {xdata: 10, ydata: 25, bdata: 5, sign:"<="}], [{xdata: 0, ydata: 6, bdata: 6, sign:">="}, {xdata: 10, ydata: -24, bdata: 6, sign:">="}]] | |
| scope.xybound.push({x: 1, y: 1, b:12, sign:"<="},{x: 3, y: 1, b:25, sign:"<="},{x: -2, y: 1, b:5, sign:"<="}, {x: 3, y: 1, b:6, sign:">="}) | |
| } else if (scope.example =="ex23"){ | |
| d3.select("#linearChart circle")[0][0].__data__.y = ylc(5); | |
| d3.select("#linearChart circle")[0][0].__data__.x = x(5); | |
| d3.select("#linearChart circle").transition().duration(300).attr("cx", x(5)).attr("cy", ylc(5)); | |
| scope.xobj = -1; | |
| scope.yobj = 1; | |
| scope.objSelected = { text: 'min'}; | |
| scope.constraints.push({text: "0x + 1y <= 8"}, {text: "10x + 1y >= 10"}, {text: "-30x + 9y <= 30"}, {text: "0x + 1y >= 2"}); | |
| scope.lines = [[{xdata: 0, ydata: 8, bdata: 8, sign:"<="}, {xdata: 10, ydata: 8, bdata: 8, sign:"<="}], [{xdata: 0, ydata: 10, bdata: 10, sign:">="}, {xdata: 10, ydata: -90, bdata: 10, sign:">="}], [{xdata: 10, ydata: 36.666666666666666666, bdata: 30, sign:"<="}, {xdata: 0, ydata: 3.333333333333333333, bdata: 30, sign:"<="}], [{xdata: 10, ydata: 2, bdata: 2, sign:">="}, {xdata: 0, ydata: 2, bdata: 2, sign:">="}]] | |
| scope.xybound.push({x: 0, y: 1, b:8, sign:"<="}, {x: 10, y: 1, b:10, sign:">="}, {x: -30, y: 9, b:30, sign:"<="}, {x: 0, y: 1, b:2, sign:">="}) | |
| } | |
| drawline() | |
| }) | |
| // Update the style of the objective result | |
| scope.$watchGroup(["Z.x","Z.y"], function(newVal, oldVal) { | |
| if(scope.objSelected.text =="max"){ | |
| if((scope.xobj*newVal[0] + scope.yobj*newVal[1]) > (scope.xobj*oldVal[0] + scope.yobj*oldVal[1])){ | |
| scope.UporDown = {color:'green'} | |
| }else{ | |
| scope.UporDown = {color:'red'} | |
| } | |
| } else{ | |
| if((scope.xobj*newVal[0] + scope.yobj*newVal[1]) < (scope.xobj*oldVal[0] + scope.yobj*oldVal[1])){ | |
| scope.UporDown = {color:'green'} | |
| }else{ | |
| scope.UporDown = {color:'red'} | |
| } | |
| } | |
| }); | |
| function drawLineChart() { | |
| x = d3.scale.linear() | |
| .range([0, width]) | |
| .domain([0,10]) | |
| ylc = d3.scale.linear() | |
| .range([height, 0]) | |
| .domain([0,10]) | |
| line = d3.svg.line() | |
| .x(function(d) { return x(+d.xdata); }) | |
| .y(function(d) { return ylc(+d.ydata); }); | |
| areaup = d3.svg.area() | |
| .x(function(d) { return x(+d.xdata); }) | |
| .y0(function(d) { return ylc(+d.ydata)}) | |
| .y1(function(d){ | |
| if (d.sign =="<="){ | |
| return ylc(10); | |
| } else if(d.sign ==">="){ | |
| return ylc(0); | |
| } | |
| }); | |
| xAxis = d3.svg.axis() | |
| .scale(x) | |
| .orient("bottom") | |
| .ticks(5); | |
| yAxis = d3.svg.axis() | |
| .scale(ylc) | |
| .orient("left") | |
| .ticks(5); | |
| drag = d3.behavior.drag() | |
| .origin(function(d) { return d; }) | |
| .on("dragstart", dragstarted) | |
| .on("drag", dragged) | |
| .on("dragend", dragended); | |
| svg.append("svg:g") | |
| .attr("class", "x axis") | |
| .attr("transform", "translate(0," + height + ")") | |
| .call(xAxis) | |
| .append("text") | |
| .attr("class", "label") | |
| .attr("x", width) | |
| .attr("y", +40) | |
| .style("text-anchor", "end") | |
| .text("x"); | |
| svg.append("svg:g") | |
| .attr("class", "y axis") | |
| .call(yAxis) | |
| .append("text") | |
| .attr("transform", "rotate(-90)") | |
| .attr("y", -50) | |
| .attr("dy", ".71em") | |
| .style("text-anchor", "end") | |
| .text("y"); | |
| svg.append("g") | |
| .attr("class", "dot") | |
| .selectAll("circle") | |
| .data([{x: 0, y: height}]) | |
| .enter().append("circle") | |
| .attr("r", 5) | |
| .attr("cx", function(d) { return d.x; }) | |
| .attr("cy", function(d) { return d.y; }) | |
| .call(drag); | |
| function checkbounds(e, index, array){ | |
| if (e.sign =="<="){ | |
| return e.x * x.invert(d3.event.x) + e.y * ylc.invert(d3.event.y) <= e.b | |
| } else{ | |
| return e.x * x.invert(d3.event.x) + e.y * ylc.invert(d3.event.y) >= e.b | |
| } | |
| } | |
| function dragstarted(d) { | |
| d3.event.sourceEvent.stopPropagation(); | |
| d3.select(this).classed("dragging", true); | |
| } | |
| function dragged(d) { | |
| if(scope.xybound.every(checkbounds)){ | |
| d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y); | |
| scope.Z.x = x.invert(d3.event.x); | |
| scope.Z.y = ylc.invert(d3.event.y); | |
| scope.$apply() | |
| } | |
| } | |
| function dragended(d) { | |
| d3.select(this).classed("dragging", false); | |
| } | |
| } | |
| function drawline(){ | |
| d3.selectAll(".area").remove() | |
| d3.selectAll(".line").remove() | |
| svg.selectAll("areas") | |
| .data(DataToPlot).enter() | |
| .append("path") | |
| .attr("class", "area") | |
| .attr("d", areaup) | |
| .attr("clip-path", "url(#clip)"); | |
| svg.selectAll(".lines") | |
| .data(DataToPlot).enter() | |
| .append("path") | |
| .attr("class", "line") | |
| .attr("d", line) | |
| .attr("clip-path", "url(#clip)"); | |
| } | |
| drawLineChart() | |
| } | |
| }; | |
| }) | |
| d3.select(self.frameElement).style("height", " 700px"); | |
| </script> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| body { | |
| width: 960px; | |
| margin-top: 0; | |
| margin: auto; | |
| font-family: "Lato", "PT Serif", serif; | |
| color: #222222; | |
| padding: 0; | |
| font-weight: 300; | |
| line-height: 33px; | |
| -webkit-font-smoothing: antialiased; | |
| } | |
| p { | |
| font-size: 20px; | |
| display: block; | |
| -webkit-margin-before: 1em; | |
| -webkit-margin-after: 1em; | |
| -webkit-margin-start: 0px; | |
| -webkit-margin-end: 0px; | |
| } | |
| .axis path, | |
| .axis line { | |
| fill: none; | |
| stroke: #000; | |
| } | |
| .x.axis text, .y.axis text{ | |
| font-size: 20px; | |
| font-weight: normal; | |
| } | |
| .line, .xpline{ | |
| fill: none; | |
| stroke: #000; | |
| stroke-dasharray: 10px 1px; | |
| stroke-width: 2px; | |
| } | |
| .pairs{ | |
| fill: none; | |
| stroke: #000; | |
| stroke-dasharray: 3px 3px; | |
| stroke-width: 3px; | |
| } | |
| .clear{ | |
| text-align: right | |
| } | |
| .area, .xp{ | |
| fill-opacity: 0.9; | |
| fill:#fff; | |
| } | |
| .dot{ | |
| fill: #2966C0; | |
| } | |
| .boundsunder path, .boundsunder line { | |
| fill: none; | |
| stroke: #eee; | |
| stroke-width: 3px; | |
| } | |
| .bounds { | |
| stroke: #000; | |
| stroke-width: 5px; | |
| } | |
| .dial{ | |
| fill: #B22222; | |
| } | |
| .paired{ | |
| width:100%; | |
| } | |
| .paired .simplechart{ | |
| float: left; | |
| width:50%; | |
| } | |
| .clear { | |
| clear: both; | |
| } | |
| .space{ | |
| margin-top: 30px; | |
| } | |
| .boundedarea{ | |
| fill: #B22222; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment