Differences
This shows you the differences between two versions of the page.
— | user_guide:tutorials:latest:lattice_polytopes_tutorial [2023/11/06 10:57] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Tutorial for Lattice Polytopes ====== | ||
+ | |||
+ | This page gives a small introduction to lattice polytopes in '' | ||
+ | |||
+ | '' | ||
+ | |||
+ | ===== Dependence on other Software ===== | ||
+ | |||
+ | For some computations '' | ||
+ | |||
+ | * [[http:// | ||
+ | * [[http:// | ||
+ | * [[http:// | ||
+ | * ([[http:// | ||
+ | |||
+ | Unless you want to deal with Hilbert bases of cones you don't need them. If you do, either the bundled extension '' | ||
+ | |||
+ | For some of the commands in this tutorial you will need at least one of '' | ||
+ | |||
+ | ===== Lattice Points in Rational Polytopes ===== | ||
+ | |||
+ | We start by creating a rational polytope using one of '' | ||
+ | |||
+ | <code perl> | ||
+ | > $p=cube(3); | ||
+ | </ | ||
+ | Suppose we want to know how many lattice points this cube contains. The answer is of course already known, as the cube has one relative interior integral point per non-empty face. So we expect to get the answer 27. | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | 27 | ||
+ | </ | ||
+ | To satisfy this request, '' | ||
+ | |||
+ | We can of course also ask '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | 1 0 0 0 | ||
+ | </ | ||
+ | Internally, '' | ||
+ | |||
+ | You can also ask for the HILBERT_BASIS, | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | 1 -1 -1 -1 | ||
+ | 1 -1 -1 0 | ||
+ | 1 -1 -1 1 | ||
+ | 1 -1 0 -1 | ||
+ | 1 -1 0 0 | ||
+ | 1 -1 0 1 | ||
+ | 1 -1 1 -1 | ||
+ | 1 -1 1 0 | ||
+ | 1 -1 1 1 | ||
+ | 1 0 -1 -1 | ||
+ | 1 0 -1 0 | ||
+ | 1 0 -1 1 | ||
+ | 1 0 0 -1 | ||
+ | 1 0 0 0 | ||
+ | 1 0 0 1 | ||
+ | 1 0 1 -1 | ||
+ | 1 0 1 0 | ||
+ | 1 0 1 1 | ||
+ | 1 1 -1 -1 | ||
+ | 1 1 -1 0 | ||
+ | 1 1 -1 1 | ||
+ | 1 1 0 -1 | ||
+ | 1 1 0 0 | ||
+ | 1 1 0 1 | ||
+ | 1 1 1 -1 | ||
+ | 1 1 1 0 | ||
+ | 1 1 1 1 | ||
+ | </ | ||
+ | '' | ||
+ | |||
+ | Note that so far these commands also work for rational polytopes. | ||
+ | |||
+ | ===== Lattice Polytopes ===== | ||
+ | |||
+ | Now we want to do some computations that don't make sense for polytopes that have non-integral vertex coordinates. We can let '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | true | ||
+ | </ | ||
+ | A particularly interesting class of lattice polytopes is that of reflexive polytopes. A polytope is // | ||
+ | |||
+ | Reflexivity is a property that is not defined for polytopes with non-integral vertices. So if we ask for it in '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | true | ||
+ | </ | ||
+ | Lattice polytopes can be used to define toric varieties with an ample line bundle, and many properties of the variety are reflected by the polytope. here is an example: The toric variety defined by our cube is //smooth//, i.e. it is one of the //smooth toric Fano varieties// | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | true | ||
+ | </ | ||
+ | The number of integral points in the k-th dilate of a polytope is given by a polynomial of degree d in k. This is the famous //Ehrhart Theorem//. In '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | 8*x^3 + 12*x^2 + 6*x + 1 | ||
+ | </ | ||
+ | '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | 8 | ||
+ | </ | ||
+ | By a theorem of Stanley, the generating function for the number of lattice points can be written as the quotient of a polynomial h< | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | 1 23 23 1 | ||
+ | > print $p-> | ||
+ | 3 | ||
+ | > print $p-> | ||
+ | 1 | ||
+ | </ | ||
+ | In our case the coefficient vector is symmetric, as the polytope is reflexive. The // | ||
+ | |||
+ | We can obtain the volume of our polytope also from the '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $p-> | ||
+ | 48 | ||
+ | </ | ||
+ | Let us look at a different example: | ||
+ | |||
+ | <code perl> | ||
+ | > $q=new Polytope(INEQUALITIES=> | ||
+ | </ | ||
+ | This actually defines a lattice polytope, which we can see from the list of vertices: | ||
+ | |||
+ | <code perl> | ||
+ | > print $q-> | ||
+ | 1 3 1 7 | ||
+ | 1 2 0 3 | ||
+ | 1 3 0 7 | ||
+ | 1 2 1 7 | ||
+ | 1 2 0 4 | ||
+ | 1 3 1 8 | ||
+ | 1 3 0 8 | ||
+ | 1 2 1 8 | ||
+ | </ | ||
+ | '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $q-> | ||
+ | false | ||
+ | </ | ||
+ | So our polytope is not normal. We can also find a point that violates the condition. Being normal is equivalent to the fact, that the Hilbert basis of the cone C(Q) obtained from Q by embedding the polytope at height one and the coning over it has all its generators in height one. The property HILBERT_BASIS computes these generators: | ||
+ | |||
+ | <code perl> | ||
+ | > print $q-> | ||
+ | 1 2 0 3 | ||
+ | 1 2 0 4 | ||
+ | 1 2 1 7 | ||
+ | 1 2 1 8 | ||
+ | 1 3 0 7 | ||
+ | 1 3 0 8 | ||
+ | 1 3 1 7 | ||
+ | 1 3 1 8 | ||
+ | 2 5 1 13 | ||
+ | </ | ||
+ | The last row is the desired vector: [2,5,1,13] is a vector in 2*Q, but it is not a sum of lattice points in Q. The cone C(Q) corresponds to an affine toric variety, and the above tells us that this variety is not normal. Yet, it is very ample, as we can check with | ||
+ | |||
+ | <code perl> | ||
+ | > print $q-> | ||
+ | true | ||
+ | </ | ||
+ | Now assume we are particularly interested in the third facet of Q. We can pick this via | ||
+ | |||
+ | <code perl> | ||
+ | > $f=facet($q, | ||
+ | </ | ||
+ | Recall that indexes in '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $f-> | ||
+ | true | ||
+ | </ | ||
+ | The result is no surprise, being very ample is inherited by faces. We could also be interested in the facet width of the polytope '' | ||
+ | |||
+ | <code perl> | ||
+ | > #print $f-> | ||
+ | </ | ||
+ | Almost. It tells you that it can only do this for a full dimensional polytope, i.e. for a polytope whose dimension coincides with the ambient dimension. This is not true for our facet: It lives in the same ambient space as '' | ||
+ | |||
+ | <code perl> | ||
+ | > $g=ambient_lattice_normalization($f); | ||
+ | > print $g-> | ||
+ | 1 | ||
+ | </ | ||
+ | The function '' | ||
+ | |||
+ | <code perl> | ||
+ | > $cr=vertex_lattice_normalization($p); | ||
+ | > print $cr-> | ||
+ | (4) (0 1) | ||
+ | 1 1 0 0 | ||
+ | 1 0 1 0 | ||
+ | 1 1 1 0 | ||
+ | 1 0 0 1 | ||
+ | 1 1 0 1 | ||
+ | 1 0 1 1 | ||
+ | 1 1 1 1 | ||
+ | </ | ||
+ | '' | ||
+ | |||
+ | ===== Toric Varieties ===== | ||
+ | |||
+ | '' | ||
+ | |||
+ | * [[https:// | ||
+ | * [[http:// | ||
+ | |||
+ | Here we will do some computations that do not require one of the extensions. We start by defining a fan. We'll make our live easy and take the normal fan of our cube: | ||
+ | |||
+ | <code perl> | ||
+ | > application " | ||
+ | > $f = normal_fan($p); | ||
+ | > print $f-> | ||
+ | true | ||
+ | </ | ||
+ | With the last line we have verified that our fan defines a smooth toric variety. Note that switching the application is not strictly necessary, you can also prepend calls to functions and constructors with '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $f-> | ||
+ | true | ||
+ | </ | ||
+ | Similarly, we could check for Q-Gorensteinness with '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $f-> | ||
+ | true | ||
+ | </ | ||
+ | but currently there is little support to detect completeness in '' | ||
+ | |||
+ | <code perl> | ||
+ | > $g=normal_fan($q); | ||
+ | > print $g-> | ||
+ | -1 0 1/4 | ||
+ | 0 -1 1/4 | ||
+ | 1 0 0 | ||
+ | 1 1 -1/4 | ||
+ | 0 1 0 | ||
+ | 0 0 -1 | ||
+ | 0 -1 0 | ||
+ | -1 0 0 | ||
+ | </ | ||
+ | This is not what we wanted. We would like to see the minimal lattice generators of the rays. We can fix this using | ||
+ | |||
+ | <code perl> | ||
+ | > print primitive($g-> | ||
+ | -4 0 1 | ||
+ | 0 -4 1 | ||
+ | 1 0 0 | ||
+ | 4 4 -1 | ||
+ | 0 1 0 | ||
+ | 0 0 -1 | ||
+ | 0 -1 0 | ||
+ | -1 0 0 | ||
+ | </ | ||
+ | Note that the function '' | ||
+ | |||
+ | <code perl> | ||
+ | > print $g-> | ||
+ | false | ||
+ | > print $g-> | ||
+ | true | ||
+ | </ | ||
+ | You can also access the maximal cones of the fan via | ||
+ | |||
+ | <code perl> | ||
+ | > print $g-> | ||
+ | {0 1 6 7} | ||
+ | {0 1 2 4} | ||
+ | {0 4 7} | ||
+ | {1 2 6} | ||
+ | {2 3 4} | ||
+ | {5 6 7} | ||
+ | {3 4 5 7} | ||
+ | {2 3 5 6} | ||
+ | </ | ||
+ | The indices in these list refer to the list of rays. Sometimes you might be interested in the walls, i.e. the codimension 2 faces of the fan. Here is one way to get them | ||
+ | |||
+ | <code perl> | ||
+ | > print rows_numbered($g-> | ||
+ | 0:-1 | ||
+ | 1:0 1 6 7 | ||
+ | 2:0 1 2 4 | ||
+ | 3:0 4 7 | ||
+ | 4:1 2 6 | ||
+ | 5:2 3 4 | ||
+ | 6:5 6 7 | ||
+ | 7:3 4 5 7 | ||
+ | 8:2 3 5 6 | ||
+ | 9:0 1 | ||
+ | 10:0 7 | ||
+ | 11:1 6 | ||
+ | 12:6 7 | ||
+ | 13:0 4 | ||
+ | 14:1 2 | ||
+ | 15:2 4 | ||
+ | 16:4 7 | ||
+ | 17:2 6 | ||
+ | 18:3 4 | ||
+ | 19:2 3 | ||
+ | 20:5 7 | ||
+ | 21:5 6 | ||
+ | 22:3 5 | ||
+ | 23:0 | ||
+ | 24:1 | ||
+ | 25:7 | ||
+ | 26:6 | ||
+ | 27:4 | ||
+ | 28:2 | ||
+ | 29:3 | ||
+ | 30:5 | ||
+ | 31: | ||
+ | > print $g-> | ||
+ | {23 24 25 26 27 28 29 30} | ||
+ | </ | ||
+ | where the list of numbers given by the latter are the indices of the codimension 2 faces in the list of all faces given before. There is a more concise way to list those, using some simple perl programming: | ||
+ | |||
+ | <code perl> | ||
+ | > print map($g-> | ||
+ | {0}{1}{7}{6}{4}{2}{3}{5} | ||
+ | </ | ||
+ | ===== Visualization ===== | ||
+ | |||
+ | If the lattice polytope lives in R^2 or R^3, then we can visualize the polytope together with its lattice points. | ||
+ | |||
+ | <code perl> | ||
+ | > $p-> | ||
+ | </ | ||
+ | < | ||
+ | <!-- | ||
+ | polymake for knusper | ||
+ | Thu Mar 3 00:37:13 2022 | ||
+ | p | ||
+ | --> | ||
+ | |||
+ | |||
+ | < | ||
+ | < | ||
+ | <meta charset=utf-8> | ||
+ | < | ||
+ | < | ||
+ | /* | ||
+ | // COMMON_CODE_BLOCK_BEGIN | ||
+ | */ | ||
+ | html {overflow: scroll;} | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | # | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | } | ||
+ | /* | ||
+ | // COMMON_CODE_BLOCK_END | ||
+ | */ | ||
+ | </ | ||
+ | </ | ||
+ | < | ||
+ | < | ||
+ | <div id=' | ||
+ | <div class=group id=' | ||
+ | < | ||
+ | < | ||
+ | <div class=indented>< | ||
+ | <div class=suboption> | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | <div class=group id=' | ||
+ | < | ||
+ | < | ||
+ | <div class=indented>< | ||
+ | </ | ||
+ | |||
+ | <div class=group id=' | ||
+ | < | ||
+ | <div class=indented> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | <div class=suboption> | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | |||
+ | <div class=group id=' | ||
+ | < | ||
+ | <div class=indented> | ||
+ | < | ||
+ | </ | ||
+ | <div class=suboption> | ||
+ | <div class=indented> | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | < | ||
+ | <div class=indented> | ||
+ | < | ||
+ | <select id=" | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <div class=group id=' | ||
+ | < | ||
+ | <div class=indented> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | </ | ||
+ | <img id=' | ||
+ | <img id=' | ||
+ | <div id=" | ||
+ | </ | ||
+ | < | ||
+ | requirejs.config({ | ||
+ | paths: { | ||
+ | three: '/ | ||
+ | TrackballControls: | ||
+ | OrbitControls: | ||
+ | Projector: '/ | ||
+ | SVGRenderer: | ||
+ | WEBGL: '/ | ||
+ | }, | ||
+ | shim: { | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | } | ||
+ | }); | ||
+ | | ||
+ | require([' | ||
+ | window.THREE = THREE; | ||
+ | require([' | ||
+ | | ||
+ | THREE.TrackballControls = TrackballControls; | ||
+ | THREE.OrbitControls = OrbitControls; | ||
+ | THREE.Projector = Projector; | ||
+ | THREE.SVGRenderer = SVGRenderer; | ||
+ | THREE.WEBGL = WEBGL; | ||
+ | |||
+ | // COMMON_CODE_BLOCK_BEGIN | ||
+ | |||
+ | const intervalLength = 25; // for automatic animations | ||
+ | const explodableModel = true; | ||
+ | const modelContains = { points: false, pointlabels: | ||
+ | const foldables = []; | ||
+ | |||
+ | var three = document.getElementById(" | ||
+ | var scene = new THREE.Scene(); | ||
+ | var renderer = new THREE.WebGLRenderer( { antialias: true } ); | ||
+ | var svgRenderer = new THREE.SVGRenderer( { antialias: true } ); | ||
+ | renderer.setPixelRatio( window.devicePixelRatio ); | ||
+ | renderer.setClearColor(0xFFFFFF, | ||
+ | svgRenderer.setClearColor(0xFFFFFF, | ||
+ | three.appendChild(renderer.domElement); | ||
+ | |||
+ | var frustumSize = 4; | ||
+ | var cameras = [new THREE.PerspectiveCamera(75, | ||
+ | cameras.forEach(function(cam) { | ||
+ | cam.position.set(0, | ||
+ | cam.lookAt(0, | ||
+ | cam.up.set(0, | ||
+ | }); | ||
+ | var controls = [new THREE.TrackballControls(cameras[0], | ||
+ | var camera, control; | ||
+ | |||
+ | controls[0].zoomSpeed = 0.2; | ||
+ | controls[0].rotateSpeed = 4; | ||
+ | |||
+ | |||
+ | // class to allow move points together with labels and spheres | ||
+ | var PMPoint = function (x,y,z) { | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | PMPoint.prototype.addLabel = function(labelsprite) { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | PMPoint.prototype.addSphere = function(spheremesh) { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | PMPoint.prototype.set = function(x, | ||
+ | | ||
+ | if (this.sprite) { | ||
+ | this.sprite.position.copy(this.vector); | ||
+ | } | ||
+ | if (this.sphere) { | ||
+ | this.sphere.position.copy(this.vector); | ||
+ | } | ||
+ | } | ||
+ | PMPoint.prototype.radius = function() { | ||
+ | if (this.sphere) { | ||
+ | return this.sphere.geometry.parameters.radius; | ||
+ | } else { | ||
+ | return 0; | ||
+ | } | ||
+ | }; | ||
+ | // select the target node | ||
+ | var target = document.querySelector('# | ||
+ | |||
+ | // create an observer instance | ||
+ | var observer = new MutationObserver(function(mutations) { | ||
+ | | ||
+ | if (mutation.removedNodes && mutation.removedNodes.length > 0) { | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | }); | ||
+ | }); | ||
+ | |||
+ | // configuration of the observer: | ||
+ | var config = { childList: true, characterData: | ||
+ | |||
+ | // pass in the target node, as well as the observer options | ||
+ | while (target) { | ||
+ | if (target.className==" | ||
+ | observer.observe(target, | ||
+ | break; | ||
+ | } | ||
+ | | ||
+ | } | ||
+ | |||
+ | // COMMON_CODE_BLOCK_END | ||
+ | |||
+ | var obj0 = new THREE.Object3D(); | ||
+ | obj0.name = " | ||
+ | obj0.userData.explodable = 1; | ||
+ | obj0.userData.points = []; | ||
+ | obj0.userData.points.push(new PMPoint(-1, -1, -1)); | ||
+ | obj0.userData.points.push(new PMPoint(1, -1, -1)); | ||
+ | obj0.userData.points.push(new PMPoint(-1, 1, -1)); | ||
+ | obj0.userData.points.push(new PMPoint(1, 1, -1)); | ||
+ | obj0.userData.points.push(new PMPoint(-1, -1, 1)); | ||
+ | obj0.userData.points.push(new PMPoint(1, -1, 1)); | ||
+ | obj0.userData.points.push(new PMPoint(-1, 1, 1)); | ||
+ | obj0.userData.points.push(new PMPoint(1, 1, 1)); | ||
+ | |||
+ | obj0.userData.edgeindices = [0, 1, 0, 2, 1, 3, 2, 3, 0, 4, 1, 5, 4, 5, 2, 6, 4, 6, 3, 7, 5, 7, 6, 7]; | ||
+ | < | ||
+ | obj0.userData.edgematerial = new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 1.5, transparent: | ||
+ | obj0.userData.facets = [[0, 4, 6, 2], [7, 5, 1, 3], [5, 4, 0, 1], [2, 6, 7, 3], [0, 2, 3, 1], [6, 4, 5, 7]]; | ||
+ | init_object(obj0); | ||
+ | scene.add(obj0); | ||
+ | |||
+ | var obj1 = new THREE.Object3D(); | ||
+ | obj1.name = " | ||
+ | obj1.userData.explodable = 1; | ||
+ | obj1.userData.points = []; | ||
+ | obj1.userData.points.push(new PMPoint(0, 0, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, -1, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, -1, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, -1, 1)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, 0, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, 0, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, 0, 1)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, 1, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, 1, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(-1, 1, 1)); | ||
+ | obj1.userData.points.push(new PMPoint(0, -1, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(0, -1, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(0, -1, 1)); | ||
+ | obj1.userData.points.push(new PMPoint(0, 0, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(0, 0, 1)); | ||
+ | obj1.userData.points.push(new PMPoint(0, 1, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(0, 1, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(0, 1, 1)); | ||
+ | obj1.userData.points.push(new PMPoint(1, -1, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(1, -1, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(1, -1, 1)); | ||
+ | obj1.userData.points.push(new PMPoint(1, 0, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(1, 0, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(1, 0, 1)); | ||
+ | obj1.userData.points.push(new PMPoint(1, 1, -1)); | ||
+ | obj1.userData.points.push(new PMPoint(1, 1, 0)); | ||
+ | obj1.userData.points.push(new PMPoint(1, 1, 1)); | ||
+ | |||
+ | obj1.userData.pointradii = 0.02; | ||
+ | < | ||
+ | obj1.userData.pointmaterial = [new THREE.MeshBasicMaterial( { color: 0x1EFA1E, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | new THREE.MeshBasicMaterial( { color: 0x469646, side: THREE.DoubleSide, | ||
+ | init_object(obj1); | ||
+ | scene.add(obj1); | ||
+ | |||
+ | // COMMON_CODE_BLOCK_BEGIN | ||
+ | function textSpriteMaterial(message, | ||
+ | if ( parameters === undefined ) parameters = {}; | ||
+ | var fontface = " | ||
+ | var fontsize = parameters.hasOwnProperty(" | ||
+ | fontsize = fontsize*10; | ||
+ | var lines = message.split(' | ||
+ | var size = 512; | ||
+ | for(var i = 0; i< | ||
+ | var tmp = lines[i].length; | ||
+ | while(tmp*fontsize > size){ | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | | ||
+ | var canvas = document.createElement(' | ||
+ | canvas.width = size; | ||
+ | canvas.height = size; | ||
+ | var context = canvas.getContext(' | ||
+ | context.fillStyle = " | ||
+ | context.fill(); | ||
+ | context.font = fontsize + "px " + fontface; | ||
+ | | ||
+ | // text color | ||
+ | context.fillStyle = " | ||
+ | | ||
+ | context.fillText(lines[i], | ||
+ | } | ||
+ | | ||
+ | // canvas contents will be used for a texture | ||
+ | var texture = new THREE.Texture(canvas); | ||
+ | texture.needsUpdate = true; | ||
+ | | ||
+ | var spriteMaterial = new THREE.SpriteMaterial({map: | ||
+ | return spriteMaterial; | ||
+ | } | ||
+ | |||
+ | |||
+ | // ---------------------- INITIALIZING OBJECTS-------------------------------------- | ||
+ | // --------------------------------------------------------------------------------- | ||
+ | |||
+ | function init_object(obj) { | ||
+ | if (obj.userData.hasOwnProperty(" | ||
+ | init_points(obj); | ||
+ | modelContains.points = true; | ||
+ | } | ||
+ | if (obj.userData.hasOwnProperty(" | ||
+ | init_pointlabels(obj); | ||
+ | modelContains.pointlabels = true; | ||
+ | } | ||
+ | if (obj.userData.hasOwnProperty(" | ||
+ | init_lines(obj); | ||
+ | modelContains.lines = true; | ||
+ | } | ||
+ | if (obj.userData.hasOwnProperty(" | ||
+ | init_edgelabels(obj); | ||
+ | modelContains.edgelabels = true; | ||
+ | } | ||
+ | if (obj.userData.hasOwnProperty(" | ||
+ | init_arrowheads(obj); | ||
+ | modelContains.arrowheads = true; | ||
+ | } | ||
+ | if (obj.userData.hasOwnProperty(" | ||
+ | init_faces(obj); | ||
+ | modelContains.faces = true; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function init_points(obj) { | ||
+ | var pointgroup = new THREE.Group(); | ||
+ | pointgroup.name = " | ||
+ | var points = obj.userData.points; | ||
+ | var radii = obj.userData.pointradii; | ||
+ | var materials = obj.userData.pointmaterial; | ||
+ | var geometry, | ||
+ | if (!Array.isArray(radii)) { | ||
+ | geometry = new THREE.SphereBufferGeometry(radii); | ||
+ | } | ||
+ | if (!Array.isArray(materials)) { | ||
+ | material = materials; | ||
+ | } | ||
+ | for (var i=0; i< | ||
+ | var point = points[i]; | ||
+ | if (Array.isArray(radii)) { | ||
+ | if (radii[i] == 0) { | ||
+ | continue; | ||
+ | } | ||
+ | geometry = new THREE.SphereBufferGeometry(radii[i]); | ||
+ | } | ||
+ | if (Array.isArray(materials)) { | ||
+ | material = materials[i]; | ||
+ | } | ||
+ | var sphere = new THREE.Mesh(geometry, | ||
+ | point.addSphere(sphere); | ||
+ | pointgroup.add(sphere); | ||
+ | } | ||
+ | obj.add(pointgroup); | ||
+ | } | ||
+ | |||
+ | function init_pointlabels(obj) { | ||
+ | var points = obj.userData.points; | ||
+ | var labels = obj.userData.pointlabels; | ||
+ | var pointlabels = new THREE.Group(); | ||
+ | pointlabels.name = " | ||
+ | if (Array.isArray(labels)) { | ||
+ | for (var i=0; i< | ||
+ | var point = points[i]; | ||
+ | var spriteMaterial = textSpriteMaterial( labels[i] ); | ||
+ | var sprite = new THREE.Sprite(spriteMaterial); | ||
+ | point.addLabel(sprite); | ||
+ | pointlabels.add(sprite); | ||
+ | } | ||
+ | } else { | ||
+ | var spriteMaterial = textSpriteMaterial( labels ); | ||
+ | for (var i=0; i< | ||
+ | var point = points[i]; | ||
+ | var sprite = new THREE.Sprite(spriteMaterial); | ||
+ | point.addLabel(sprite); | ||
+ | pointlabels.add(sprite); | ||
+ | } | ||
+ | } | ||
+ | obj.add(pointlabels); | ||
+ | } | ||
+ | |||
+ | function init_lines(obj) { | ||
+ | var edgeindices = obj.userData.edgeindices; | ||
+ | var points = obj.userData.points; | ||
+ | var materials = obj.userData.edgematerial; | ||
+ | var geometry = new THREE.BufferGeometry(); | ||
+ | var bufarr = new Float32Array( obj.userData.edgeindices.length * 3 ); | ||
+ | var bufattr = new THREE.Float32BufferAttribute( bufarr, 3 ); | ||
+ | var geometry = new THREE.BufferGeometry(); | ||
+ | geometry.setAttribute(' | ||
+ | if (Array.isArray(materials)) { | ||
+ | for (var i=0; i< | ||
+ | geometry.addGroup(2*i, | ||
+ | } | ||
+ | } | ||
+ | var lines = new THREE.LineSegments(geometry, | ||
+ | lines.name = " | ||
+ | obj.add(lines); | ||
+ | updateEdgesPosition(obj); | ||
+ | } | ||
+ | |||
+ | function init_edgelabels(obj) { | ||
+ | var points = obj.userData.points; | ||
+ | var edgeindices = obj.userData.edgeindices; | ||
+ | var labels = obj.userData.edgelabels; | ||
+ | var edgelabels = new THREE.Group(); | ||
+ | edgelabels.name = " | ||
+ | if (Array.isArray(labels)) { | ||
+ | for (var i=0; i< | ||
+ | var spriteMaterial = textSpriteMaterial( labels[i] ); | ||
+ | var sprite = new THREE.Sprite(spriteMaterial); | ||
+ | sprite.position.copy(new THREE.Vector3().addVectors(points[edgeindices[2*i]].vector, | ||
+ | edgelabels.add(sprite); | ||
+ | } | ||
+ | } else { | ||
+ | var spriteMaterial = textSpriteMaterial( labels ); | ||
+ | for (var i=0; i< | ||
+ | var sprite = new THREE.Sprite(spriteMaterial); | ||
+ | sprite.position.copy(new THREE.Vector3().addVectors(points[edgeindices[2*i]].vector, | ||
+ | edgelabels.add(sprite); | ||
+ | } | ||
+ | } | ||
+ | obj.add(edgelabels); | ||
+ | } | ||
+ | |||
+ | function init_arrowheads(obj) { | ||
+ | var arrowheads = new THREE.Group(); | ||
+ | arrowheads.name = " | ||
+ | var arrowstyle = obj.userData.arrowstyle; | ||
+ | var edgeindices = obj.userData.edgeindices; | ||
+ | var edgematerials = obj.userData.edgematerial; | ||
+ | var points = obj.userData.points; | ||
+ | var material; | ||
+ | if (!Array.isArray(edgematerials)) { | ||
+ | material = new THREE.MeshBasicMaterial( {color: edgematerials.color} ); | ||
+ | } | ||
+ | |||
+ | for (var i=0; i< | ||
+ | var start = points[edgeindices[i]]; | ||
+ | var end = points[edgeindices[i+1]]; | ||
+ | var dist = start.vector.distanceTo( end.vector ) - start.radius() - end.radius(); | ||
+ | if (dist <= 0) { | ||
+ | continue; | ||
+ | } | ||
+ | var dir = new THREE.Vector3().subVectors(end.vector, | ||
+ | dir.normalize(); | ||
+ | var axis = new THREE.Vector3().set(dir.z, | ||
+ | axis.normalize(); | ||
+ | var radians = Math.acos( dir.y ); | ||
+ | var radius = dist/25; | ||
+ | var height = dist/5; | ||
+ | var geometry = new THREE.ConeBufferGeometry(radius, | ||
+ | var position = new THREE.Vector3().addVectors(start.vector, | ||
+ | if (Array.isArray(edgematerials)) { | ||
+ | material = new THREE.MeshBasicMaterial( {color: edgematerials[i].color} ); | ||
+ | } | ||
+ | var cone = new THREE.Mesh( geometry, material ); | ||
+ | cone.quaternion.setFromAxisAngle(axis, | ||
+ | cone.position.copy(position);; | ||
+ | arrowheads.add(cone); | ||
+ | } | ||
+ | obj.add(arrowheads); | ||
+ | } | ||
+ | |||
+ | function init_faces(obj) { | ||
+ | var points = obj.userData.points; | ||
+ | var facets = obj.userData.facets; | ||
+ | obj.userData.triangleindices = []; | ||
+ | for (var i=0; i< | ||
+ | facet = facets[i]; | ||
+ | for (var t=0; t< | ||
+ | obj.userData.triangleindices.push(facet[0], | ||
+ | } | ||
+ | } | ||
+ | var bufarr = new Float32Array( obj.userData.triangleindices.length * 3 ); | ||
+ | var bufattr = new THREE.Float32BufferAttribute(bufarr, | ||
+ | | ||
+ | var materials = obj.userData.facetmaterial; | ||
+ | var geometry = new THREE.BufferGeometry(); | ||
+ | var frontmaterials = []; | ||
+ | var backmaterials = []; | ||
+ | geometry.setAttribute(' | ||
+ | if (Array.isArray(materials)) { | ||
+ | var tricount = 0; | ||
+ | var facet; | ||
+ | for (var i=0; i< | ||
+ | facet = facets[i]; | ||
+ | geometry.addGroup(tricount, | ||
+ | tricount += (facet.length-2)*3; | ||
+ | } | ||
+ | for (var j=0; j< | ||
+ | var fmat = materials[j].clone() | ||
+ | fmat.side = THREE.FrontSide; | ||
+ | frontmaterials.push(fmat); | ||
+ | var bmat = materials[j].clone() | ||
+ | bmat.side = THREE.BackSide; | ||
+ | backmaterials.push(bmat); | ||
+ | obj.userData.facetmaterial = frontmaterials.concat(backmaterials); | ||
+ | } | ||
+ | } else if (materials instanceof THREE.Material) { | ||
+ | frontmaterials = materials.clone() | ||
+ | frontmaterials.side = THREE.FrontSide; | ||
+ | backmaterials = materials.clone() | ||
+ | backmaterials.side = THREE.BackSide; | ||
+ | obj.userData.facetmaterial = [frontmaterials, | ||
+ | } | ||
+ | // duplicating the object with front and back should avoid transparency issues | ||
+ | var backmesh = new THREE.Mesh(geometry, | ||
+ | // meshname is used to show/hide objects | ||
+ | backmesh.name = " | ||
+ | obj.add(backmesh); | ||
+ | var frontmesh = new THREE.Mesh(geometry, | ||
+ | frontmesh.name = " | ||
+ | obj.add(frontmesh); | ||
+ | updateFacesPosition(obj); | ||
+ | } | ||
+ | // // | ||
+ | |||
+ | |||
+ | function updateFacesPosition(obj) { | ||
+ | var points = obj.userData.points; | ||
+ | var indices = obj.userData.triangleindices; | ||
+ | var faces = obj.getObjectByName(" | ||
+ | var ba = faces.geometry.getAttribute(" | ||
+ | for (var i=0; i< | ||
+ | ba.setXYZ(i, | ||
+ | } | ||
+ | faces.geometry.attributes.position.needsUpdate = true; | ||
+ | | ||
+ | } | ||
+ | |||
+ | function updateEdgesPosition(obj) { | ||
+ | var points = obj.userData.points; | ||
+ | var indices = obj.userData.edgeindices; | ||
+ | var lines = obj.getObjectByName(" | ||
+ | var ba = lines.geometry.getAttribute(" | ||
+ | for (var i=0; i< | ||
+ | ba.setXYZ(i, | ||
+ | } | ||
+ | lines.geometry.attributes.position.needsUpdate = true; | ||
+ | } | ||
+ | |||
+ | function onWindowResize() { | ||
+ | renderer.setSize( three.clientWidth, | ||
+ | svgRenderer.setSize( three.clientWidth, | ||
+ | updateCamera(); | ||
+ | } | ||
+ | |||
+ | function updateCamera() { | ||
+ | var width = three.clientWidth; | ||
+ | var height = three.clientHeight; | ||
+ | var aspect = width / height; | ||
+ | if (camera.type == " | ||
+ | camera.left = frustumSize * aspect / - 2; | ||
+ | camera.right = frustumSize * aspect / 2; | ||
+ | camera.top = frustumSize / 2; | ||
+ | camera.bottom = - frustumSize / 2; | ||
+ | } else if (camera.type == " | ||
+ | camera.aspect = aspect; | ||
+ | } | ||
+ | camera.updateProjectionMatrix(); | ||
+ | } | ||
+ | |||
+ | function changeCamera(event) { | ||
+ | var selindex = event.currentTarget.selectedIndex; | ||
+ | camera = cameras[selindex]; | ||
+ | control = controls[selindex]; | ||
+ | control.enabled = true; | ||
+ | for (var i=0; i< | ||
+ | if (i!=selindex) { | ||
+ | controls[i].enabled = false; | ||
+ | } | ||
+ | } | ||
+ | updateCamera(); | ||
+ | } | ||
+ | |||
+ | var camtypenode = document.getElementById(' | ||
+ | camtypenode.onchange = changeCamera; | ||
+ | camtypenode.dispatchEvent(new Event(' | ||
+ | |||
+ | onWindowResize(); | ||
+ | window.addEventListener(' | ||
+ | |||
+ | |||
+ | var xRotationEnabled = false; | ||
+ | var yRotationEnabled = false; | ||
+ | var zRotationEnabled = false; | ||
+ | var rotationSpeedFactor = 1; | ||
+ | var settingsShown = false; | ||
+ | var labelsShown = true; | ||
+ | var intervals = []; | ||
+ | var timeouts = []; | ||
+ | var explodingSpeed = 0.05; | ||
+ | var explodeScale = 0.000001; | ||
+ | var XMLS = new XMLSerializer(); | ||
+ | var svgElement; | ||
+ | var renderId; | ||
+ | |||
+ | var render = function () { | ||
+ | |||
+ | renderId = requestAnimationFrame(render); | ||
+ | |||
+ | // comment in for automatic explosion | ||
+ | // | ||
+ | |||
+ | var phi = 0.02 * rotationSpeedFactor; | ||
+ | |||
+ | if (xRotationEnabled) { | ||
+ | scene.rotation.x += phi; | ||
+ | } | ||
+ | if (yRotationEnabled) { | ||
+ | scene.rotation.y += phi; | ||
+ | } | ||
+ | if (zRotationEnabled) { | ||
+ | scene.rotation.z += phi; | ||
+ | } | ||
+ | |||
+ | control.update(); | ||
+ | renderer.render(scene, | ||
+ | }; | ||
+ | |||
+ | if ( THREE.WEBGL.isWebGLAvailable() ) { | ||
+ | render(); | ||
+ | } else { | ||
+ | var warning = WEBGL.getWebGLErrorMessage(); | ||
+ | three.appendChild( warning ); | ||
+ | } | ||
+ | | ||
+ | function changeTransparency() { | ||
+ | var opacity = 1-Number(event.currentTarget.value); | ||
+ | for (var i=0; i< | ||
+ | child = scene.children[i]; | ||
+ | if ( child.userData.hasOwnProperty(" | ||
+ | if (Array.isArray(child.userData.facetmaterial)) { | ||
+ | for (var j=0; j< | ||
+ | child.userData.facetmaterial[j].opacity = opacity; | ||
+ | } | ||
+ | } else { | ||
+ | child.userData.facetmaterial.opacity = opacity; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function toggleDepthWrite(event) { | ||
+ | depthwrite = event.currentTarget.checked; | ||
+ | for (var i=0; i< | ||
+ | child = scene.children[i]; | ||
+ | if ( child.userData.hasOwnProperty(" | ||
+ | if (Array.isArray(child.userData.facetmaterial)) { | ||
+ | for (var j=0; j< | ||
+ | child.userData.facetmaterial[j].depthWrite = depthwrite; | ||
+ | } | ||
+ | } else { | ||
+ | child.userData.facetmaterial.depthWrite = depthWrite; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function changeRotationX(event){ | ||
+ | xRotationEnabled = event.currentTarget.checked; | ||
+ | } | ||
+ | |||
+ | function changeRotationY(event){ | ||
+ | yRotationEnabled = event.currentTarget.checked; | ||
+ | } | ||
+ | |||
+ | function changeRotationZ(event){ | ||
+ | zRotationEnabled = event.currentTarget.checked; | ||
+ | } | ||
+ | |||
+ | |||
+ | function changeRotationSpeedFactor(event){ | ||
+ | rotationSpeedFactor = Number(event.currentTarget.value); | ||
+ | } | ||
+ | |||
+ | function resetScene(){ | ||
+ | scene.rotation.set(0, | ||
+ | camera.position.set(0, | ||
+ | camera.up.set(0, | ||
+ | } | ||
+ | |||
+ | function showSettings(event){ | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | settingsShown = true; | ||
+ | } | ||
+ | |||
+ | function hideSettings(event){ | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | settingsShown = false; | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | var pos = 150* Math.PI; | ||
+ | |||
+ | function updateFactor() { | ||
+ | pos++; | ||
+ | return Math.sin(.01*pos)+1; | ||
+ | } | ||
+ | |||
+ | // ------------------------ FOLDING ------------------------------------------------ | ||
+ | // --------------------------------------------------------------------------------- | ||
+ | // rotate point p around axis defined by points p1 and p2 by given angle | ||
+ | function rotate(p, p1, p2, angle ){ | ||
+ | angle = -angle; | ||
+ | var x = p.x, y = p.y, z = p.z, | ||
+ | a = p1.x, b = p1.y, c = p1.z, | ||
+ | u = p2.x-p1.x, v = p2.y-p1.y, w = p2.z-p1.z; | ||
+ | var result = []; | ||
+ | var L = u*u + v*v + w*w; | ||
+ | var sqrt = Math.sqrt; | ||
+ | var cos = Math.cos; | ||
+ | var sin = Math.sin; | ||
+ | |||
+ | result[0] = ((a*(v*v+w*w)-u*(b*v+c*w-u*x-v*y-w*z))*(1-cos(angle))+L*x*cos(angle)+sqrt(L)*(-c*v+b*w-w*y+v*z)*sin(angle))/ | ||
+ | result[1] = ((b*(u*u+w*w)-v*(a*u+c*w-u*x-v*y-w*z))*(1-cos(angle))+L*y*cos(angle)+sqrt(L)*(c*u-a*w+w*x-u*z)*sin(angle))/ | ||
+ | result[2] = ((c*(u*u+v*v)-w*(a*u+b*v-u*x-v*y-w*z))*(1-cos(angle))+L*z*cos(angle)+sqrt(L)*(-b*u+a*v-v*x+u*y)*sin(angle))/ | ||
+ | |||
+ | return result; | ||
+ | } | ||
+ | |||
+ | var fold = function(event){ | ||
+ | var obj = foldables[Number(event.currentTarget.name)]; | ||
+ | var foldvalue = Number(event.currentTarget.value); | ||
+ | var scale = foldvalue - obj.userData.oldscale; | ||
+ | |||
+ | for (var j=0; j< | ||
+ | rotateVertices(obj, | ||
+ | } | ||
+ | update(obj); | ||
+ | obj.userData.oldscale += scale; | ||
+ | lookAtBarycenter(obj); | ||
+ | } | ||
+ | |||
+ | function lookAtBarycenter(obj){ | ||
+ | control.target = barycenter(obj); | ||
+ | } | ||
+ | |||
+ | function barycenter(obj) { | ||
+ | var center = new THREE.Vector3(0, | ||
+ | var points = obj.userData.points; | ||
+ | for (var i=0; i< | ||
+ | center.add(points[i].vector); | ||
+ | } | ||
+ | center.divideScalar(points.length); | ||
+ | return center; | ||
+ | } | ||
+ | |||
+ | function rotateVertices(obj, | ||
+ | var axes = obj.userData.axes; | ||
+ | var subtrees = obj.userData.subtrees; | ||
+ | var points = obj.userData.points; | ||
+ | var angles = obj.userData.angles; | ||
+ | if (edge < axes.length){ | ||
+ | for (var j=0; j< | ||
+ | var rotP = rotate(points[subtrees[edge][j]].vector, | ||
+ | points[subtrees[edge][j]].set(rotP[0], | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function update(obj) { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | if (foldables.length) { | ||
+ | var settings = document.getElementById(' | ||
+ | var foldDiv = document.createElement(' | ||
+ | foldDiv.id = ' | ||
+ | var title = document.createElement(' | ||
+ | title.innerHTML = ' | ||
+ | foldDiv.appendChild(title); | ||
+ | foldDiv.className = ' | ||
+ | for (var i=0; i< | ||
+ | var range = document.createElement(' | ||
+ | range.type = ' | ||
+ | range.min = 0; | ||
+ | range.max = 1; | ||
+ | range.value = 0; | ||
+ | range.step = 0.001; | ||
+ | range.name = String(i); | ||
+ | range.oninput = fold; | ||
+ | foldDiv.appendChild(range); | ||
+ | } | ||
+ | lookAtBarycenter(foldables[0]); | ||
+ | settings.insertBefore(foldDiv, | ||
+ | } | ||
+ | |||
+ | | ||
+ | // ---------------------- EXPLOSION ------------------------------------------------ | ||
+ | // --------------------------------------------------------------------------------- | ||
+ | |||
+ | if (explodableModel) { | ||
+ | for (var i=0; i< | ||
+ | obj = scene.children[i]; | ||
+ | if ( obj.userData.explodable ) { | ||
+ | computeCentroid(obj); | ||
+ | } | ||
+ | } | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | explode(0.000001); | ||
+ | } | ||
+ | |||
+ | function computeCentroid(obj) { | ||
+ | centroid = new THREE.Vector3(); | ||
+ | obj.userData.points.forEach(function(pmpoint) { | ||
+ | centroid.add(pmpoint.vector); | ||
+ | }); | ||
+ | centroid.divideScalar(obj.userData.points.length); | ||
+ | obj.userData.centroid = centroid; | ||
+ | } | ||
+ | |||
+ | function explode(factor) { | ||
+ | for (var i=0; i< | ||
+ | var obj = scene.children[i]; | ||
+ | if (obj.userData.hasOwnProperty(" | ||
+ | var c = obj.userData.centroid; | ||
+ | obj.position.set(c.x*factor, | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function triggerExplode(event){ | ||
+ | explodeScale = Number(event.currentTarget.value); | ||
+ | explode(explodeScale); | ||
+ | } | ||
+ | |||
+ | function setExplodingSpeed(event){ | ||
+ | explodingSpeed = Number(event.currentTarget.value); | ||
+ | } | ||
+ | |||
+ | function triggerAutomaticExplode(event){ | ||
+ | if (event.currentTarget.checked){ | ||
+ | startExploding(); | ||
+ | } else { | ||
+ | clearIntervals(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function startExploding(){ | ||
+ | intervals.push(setInterval(explodingInterval, | ||
+ | } | ||
+ | |||
+ | |||
+ | function explodingInterval(){ | ||
+ | explodeScale += explodingSpeed; | ||
+ | if (explodeScale <= 6){ | ||
+ | explode(explodeScale); | ||
+ | } | ||
+ | else{ | ||
+ | explode(6); | ||
+ | explodeScale = 6; | ||
+ | clearIntervals(); | ||
+ | timeouts.push(setTimeout(startUnexploding, | ||
+ | } | ||
+ | document.getElementById(' | ||
+ | } | ||
+ | |||
+ | |||
+ | function startUnexploding(){ | ||
+ | intervals.push(setInterval(unexplodingInterval, | ||
+ | } | ||
+ | |||
+ | function unexplodingInterval(){ | ||
+ | explodeScale -= explodingSpeed; | ||
+ | if (explodeScale >= 0){ | ||
+ | explode(explodeScale); | ||
+ | } | ||
+ | else { | ||
+ | explode(0); | ||
+ | explodeScale = 0; | ||
+ | clearIntervals(); | ||
+ | timeouts.push(setTimeout(startExploding, | ||
+ | } | ||
+ | document.getElementById(' | ||
+ | } | ||
+ | |||
+ | function clearIntervals(){ | ||
+ | intervals.forEach(function(interval){ | ||
+ | clearInterval(interval); | ||
+ | }); | ||
+ | intervals = []; | ||
+ | timeouts.forEach(function(timeout){ | ||
+ | clearTimeout(timeout); | ||
+ | }); | ||
+ | timeouts = []; | ||
+ | } | ||
+ | |||
+ | // ---------------------- DISPLAY -------------------------------------------------- | ||
+ | // --------------------------------------------------------------------------------- | ||
+ | |||
+ | const objectTypeInnerHTMLs = { points: " | ||
+ | const objectTypeVisible = {}; | ||
+ | Object.assign(objectTypeVisible, | ||
+ | const sortedObjectTypeKeys = Object.keys(objectTypeInnerHTMLs).sort(); | ||
+ | const shownObjectTypesList = document.getElementById(' | ||
+ | |||
+ | function setVisibility(bool, | ||
+ | for (var i=0; i< | ||
+ | var obj = scene.children[i].getObjectByName(objname); | ||
+ | if (obj) { | ||
+ | obj.visible = bool; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function toggleObjectTypeVisibility(event){ | ||
+ | var name = event.currentTarget.name; | ||
+ | var checked = event.currentTarget.checked; | ||
+ | objectTypeVisible[name] = checked; | ||
+ | if (name == " | ||
+ | setVisibility(checked," | ||
+ | setVisibility(checked," | ||
+ | } else { | ||
+ | setVisibility(checked, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | for (var i=0; i< | ||
+ | var key = sortedObjectTypeKeys[i]; | ||
+ | if (modelContains[key]) { | ||
+ | var objTypeNode = document.createElement(' | ||
+ | objTypeNode.innerHTML = objectTypeInnerHTMLs[key] + '< | ||
+ | var checkbox = document.createElement(' | ||
+ | checkbox.type = ' | ||
+ | checkbox.checked = true; | ||
+ | checkbox.name = key; | ||
+ | checkbox.onchange = toggleObjectTypeVisibility; | ||
+ | shownObjectTypesList.appendChild(checkbox); | ||
+ | shownObjectTypesList.appendChild(objTypeNode); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // ------------------------------------------------------ | ||
+ | |||
+ | function toggleObjectVisibility(event){ | ||
+ | var nr = Number(event.currentTarget.name); | ||
+ | scene.children[nr].visible = event.currentTarget.checked; | ||
+ | } | ||
+ | |||
+ | // append checkboxes for displaying or hiding objects | ||
+ | var shownObjectsList = document.getElementById(' | ||
+ | for (var i=0; i< | ||
+ | obj = scene.children[i]; | ||
+ | var objNode = document.createElement(' | ||
+ | objNode.innerHTML = obj.name + '< | ||
+ | var checkbox = document.createElement(' | ||
+ | checkbox.type = ' | ||
+ | checkbox.checked = true; | ||
+ | checkbox.name = String(i); | ||
+ | checkbox.onchange = toggleObjectVisibility; | ||
+ | shownObjectsList.appendChild(checkbox); | ||
+ | shownObjectsList.appendChild(objNode); | ||
+ | } | ||
+ | |||
+ | // ---------------------- SVG ------------------------------------------------------ | ||
+ | // --------------------------------------------------------------------------------- | ||
+ | |||
+ | function takeSvgScreenshot() { | ||
+ | if (objectTypeVisible[" | ||
+ | setVisibility(false," | ||
+ | } | ||
+ | if (objectTypeVisible[" | ||
+ | setVisibility(false," | ||
+ | } | ||
+ | svgRenderer.render(scene, | ||
+ | svgElement = XMLS.serializeToString(svgRenderer.domElement); | ||
+ | | ||
+ | if (objectTypeVisible[" | ||
+ | setVisibility(true," | ||
+ | } | ||
+ | if (objectTypeVisible[" | ||
+ | setVisibility(true," | ||
+ | } | ||
+ | |||
+ | if (document.getElementById(' | ||
+ | //show in new tab | ||
+ | var myWindow = window.open("","" | ||
+ | myWindow.document.body.innerHTML = svgElement; | ||
+ | } else{ | ||
+ | // download svg file | ||
+ | download(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function download(filename, | ||
+ | var element = document.createElement(' | ||
+ | element.setAttribute(' | ||
+ | element.setAttribute(' | ||
+ | |||
+ | element.style.display = ' | ||
+ | document.body.appendChild(element); | ||
+ | |||
+ | element.click(); | ||
+ | |||
+ | document.body.removeChild(element); | ||
+ | } | ||
+ | |||
+ | |||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | document.getElementById(' | ||
+ | |||
+ | |||
+ | // ------------------ SHORTCUTS -------------------------------------------- | ||
+ | // ------------------------------------------------------------------------- | ||
+ | |||
+ | /** | ||
+ | * http:// | ||
+ | * Version : 2.01.B | ||
+ | * By Binny V A | ||
+ | * License : BSD | ||
+ | */ | ||
+ | shortcut = { | ||
+ | ' | ||
+ | ' | ||
+ | //Provide a set of default options | ||
+ | var default_options = { | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | } | ||
+ | if(!opt) opt = default_options; | ||
+ | else { | ||
+ | for(var dfo in default_options) { | ||
+ | if(typeof opt[dfo] == ' | ||
+ | } | ||
+ | } | ||
+ | |||
+ | var ele = opt.target; | ||
+ | if(typeof opt.target == ' | ||
+ | var ths = this; | ||
+ | shortcut_combination = shortcut_combination.toLowerCase(); | ||
+ | |||
+ | //The function to be called at keypress | ||
+ | var func = function(e) { | ||
+ | e = e || window.event; | ||
+ | |||
+ | if(opt[' | ||
+ | var element; | ||
+ | if(e.target) element=e.target; | ||
+ | else if(e.srcElement) element=e.srcElement; | ||
+ | if(element.nodeType==3) element=element.parentNode; | ||
+ | |||
+ | if(element.tagName == ' | ||
+ | } | ||
+ | |||
+ | //Find Which key is pressed | ||
+ | if (e.keyCode) code = e.keyCode; | ||
+ | else if (e.which) code = e.which; | ||
+ | var character = String.fromCharCode(code).toLowerCase(); | ||
+ | |||
+ | if(code == 188) character=","; | ||
+ | if(code == 190) character=" | ||
+ | |||
+ | var keys = shortcut_combination.split(" | ||
+ | //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked | ||
+ | var kp = 0; | ||
+ | |||
+ | //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken | ||
+ | var shift_nums = { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ";":":", | ||
+ | "'":" | ||
+ | ",":"<", | ||
+ | " | ||
+ | "/":"?", | ||
+ | " | ||
+ | } | ||
+ | // | ||
+ | var special_keys = { | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | } | ||
+ | |||
+ | var modifiers = { | ||
+ | shift: { wanted: | ||
+ | ctrl : { wanted: | ||
+ | alt | ||
+ | meta : { wanted: | ||
+ | }; | ||
+ | | ||
+ | if(e.ctrlKey) modifiers.ctrl.pressed = true; | ||
+ | if(e.shiftKey) modifiers.shift.pressed = true; | ||
+ | if(e.altKey) modifiers.alt.pressed = true; | ||
+ | if(e.metaKey) | ||
+ | | ||
+ | for(var i=0; k=keys[i], | ||
+ | // | ||
+ | if(k == ' | ||
+ | kp++; | ||
+ | modifiers.ctrl.wanted = true; | ||
+ | |||
+ | } else if(k == ' | ||
+ | kp++; | ||
+ | modifiers.shift.wanted = true; | ||
+ | |||
+ | } else if(k == ' | ||
+ | kp++; | ||
+ | modifiers.alt.wanted = true; | ||
+ | } else if(k == ' | ||
+ | kp++; | ||
+ | modifiers.meta.wanted = true; | ||
+ | } else if(k.length > 1) { //If it is a special key | ||
+ | if(special_keys[k] == code) kp++; | ||
+ | |||
+ | } else if(opt[' | ||
+ | if(opt[' | ||
+ | |||
+ | } else { //The special keys did not match | ||
+ | if(character == k) kp++; | ||
+ | else { | ||
+ | if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase | ||
+ | character = shift_nums[character]; | ||
+ | if(character == k) kp++; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if(kp == keys.length && | ||
+ | modifiers.ctrl.pressed == modifiers.ctrl.wanted && | ||
+ | modifiers.shift.pressed == modifiers.shift.wanted && | ||
+ | modifiers.alt.pressed == modifiers.alt.wanted && | ||
+ | modifiers.meta.pressed == modifiers.meta.wanted) { | ||
+ | callback(e); | ||
+ | |||
+ | if(!opt[' | ||
+ | // | ||
+ | e.cancelBubble = true; | ||
+ | e.returnValue = false; | ||
+ | |||
+ | // | ||
+ | if (e.stopPropagation) { | ||
+ | e.stopPropagation(); | ||
+ | e.preventDefault(); | ||
+ | } | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | this.all_shortcuts[shortcut_combination] = { | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | }; | ||
+ | //Attach the function with the event | ||
+ | if(ele.addEventListener) ele.addEventListener(opt[' | ||
+ | else if(ele.attachEvent) ele.attachEvent(' | ||
+ | else ele[' | ||
+ | }, | ||
+ | |||
+ | //Remove the shortcut - just specify the shortcut and I will remove the binding | ||
+ | ' | ||
+ | shortcut_combination = shortcut_combination.toLowerCase(); | ||
+ | var binding = this.all_shortcuts[shortcut_combination]; | ||
+ | delete(this.all_shortcuts[shortcut_combination]) | ||
+ | if(!binding) return; | ||
+ | var type = binding[' | ||
+ | var ele = binding[' | ||
+ | var callback = binding[' | ||
+ | |||
+ | if(ele.detachEvent) ele.detachEvent(' | ||
+ | else if(ele.removeEventListener) ele.removeEventListener(type, | ||
+ | else ele[' | ||
+ | } | ||
+ | } | ||
+ | |||
+ | shortcut.add(" | ||
+ | var event = new Event(' | ||
+ | if (settingsShown){ | ||
+ | document.getElementById(' | ||
+ | } else { | ||
+ | document.getElementById(' | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | |||
+ | // COMMON_CODE_BLOCK_END | ||
+ | |||
+ | });}); | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | The command '' | ||
+ | |||
+ | ===== External Packages ===== | ||
+ | |||
+ | '' | ||
+ | |||
+ | < | ||
+ | Application polytope uses following third-party software (for details: help ' | ||
+ | 4ti2, cddlib, latte, libnormaliz, | ||
+ | </ | ||
+ | The output at this position depends on the software available on your computer. To see each call to an external program you can set the variable '' | ||
+ | |||
+ | <code perl> | ||
+ | > $Verbose:: | ||
+ | > print $p-> | ||
+ | 8*x^3 + 12*x^2 + 6*x + 1 | ||
+ | </ | ||
+ | You can ask '' | ||
+ | |||
+ | To prefer one program only for some computations you may append one of .integer_points, | ||
+ | |||
+ | <code perl> | ||
+ | > print cube(2)-> | ||
+ | 9 | ||
+ | > prefer_now " | ||
+ | > print cube(2)-> | ||
+ | 9 | ||
+ | > print cube(2)-> | ||
+ | 4*x^2 + 4*x + 1 | ||
+ | </ | ||