File: planar_net.rules

package info (click to toggle)
polymake 4.14-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 35,888 kB
  • sloc: cpp: 168,933; perl: 43,407; javascript: 31,575; ansic: 3,007; java: 2,654; python: 632; sh: 268; xml: 117; makefile: 61
file content (253 lines) | stat: -rw-r--r-- 10,140 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#  Copyright (c) 1997-2024
#  Ewgenij Gawrilow, Michael Joswig, and the polymake team
#  Technische Universität Berlin, Germany
#  https://polymake.org
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#-------------------------------------------------------------------------------

object PlanarNet {

# @category Geometry
# The corresponding 3-polytope.

property POLYTOPE : polytope::Polytope<Scalar>;

# @category Combinatorics
# Counter clockwise ordering of the [[VERTICES]] in each [[MAXIMAL_POLYTOPES]].
# This is a copy of the property [[Polytope::VIF_CYCLIC_NORMAL]] of [[POLYTOPE]].

property VIF_CYCLIC_NORMAL : Array<Array<Int>>;

# @category Combinatorics
# Map which tells which vertex/facet pair of the 3-polytope is mapped to which vertex of the planar net.
# The dependence on the facets comes from the fact that most [[Polytope::VERTICES]] occur more than once in the planar net.
# The inverse is [[VF_MAP_INV]].

property VF_MAP : Map<Pair<Int,Int>,Int>;

# @category Combinatorics
# Map which tells how the [[VERTICES]] of the planar net are mapped to the [[Polytope::VERTICES]] of the 3-polytope.
# Inverse of [[VF_MAP]].

property VF_MAP_INV : Array<Int>;
    
# @category Combinatorics
# The spanning tree in the [[Polytope::DUAL_GRAPH]] which defines the layout.
# Encoded as a list of edges, directed away from the root facet.
# Indices correspond to [[MAXIMAL_POLYTOPES]] as well as [[Polytope::FACETS]].
# @example The planar net of the 3-cube consists of 6 squares. Looking at the dual tree we can see that the client [[planar_net]]
# gives us the classical cross-shaped planar net, as we have a vertex which is connected to four different vertices and a last one
# which has an edge to one of these four vertices:
# > print planar_net(cube(3))->DUAL_TREE;
# | (0 2) (0 3) (0 4) (0 5) (2 1)

property DUAL_TREE : Array<Pair<Int,Int>>;

# @category Combinatorics
# List of directed edges in the primal graph [[Polytope::GRAPH]] which need flaps for gluing.
# Indices correspond to [[VERTICES]].  The orientation is chosen such that the facet in the layout is always on the left.
# In fact, the flaps define a spanning tree of [[Polytope::GRAPH]].

property FLAPS : Array<Pair<Int,Int>>;

rule VIF_CYCLIC_NORMAL : POLYTOPE.VIF_CYCLIC_NORMAL {
   $this->VIF_CYCLIC_NORMAL(temporary) = $this->POLYTOPE->VIF_CYCLIC_NORMAL;
}
weight 0.10;

}

# @topic objects/Visual::PlanarNet::Polygons
# @category Visualization
# Special variant of Visual::Polygons with extra data for
# folding planar nets via threejs:
#   Axes: vertex indices of all edges
#   DihedralAngles: angle at each edge in the graph of the polytope (in radians)
#   DependendVertices: all vertices that are affected by a rotation around an edge
#   PolytopeRoot: [ coordinates of the first vertex -> mapped to (0,0) in the net,
#                   outer normal of the first facet -> mapped to (0, 0, 1),
#                   up direction for the first edge -> mapped to (0, 1, 0) ]
# @super Visual::Polygons
# @relates PlanarNet
package Visual::PlanarNet::Polygons;

use Polymake::Struct (
   [ '@ISA' => 'Visual::Polygons' ],
   [ '$Axes' => '#%', default => 'undef' ],
   [ '$DihedralAngles' => '#%', default => 'undef' ],
   [ '$DependendVertices' => '#%', default => 'undef' ],
   [ '$PolytopeRoot' => '#%', default => 'undef' ],
);


# @topic objects/Visual::PlanarNet
# @category Visualization
# Visualization of a 3-polytope as a planar net.
# @super Visual::Container
# @relates PlanarNet

package Visual::PlanarNet;

use Polymake::Struct (
   [ '@ISA' => 'Container' ],
   [ '$PlanarNet' => '#%', default => 'undef' ],
);

# where to keep any additional data
method representative { $_[0]->PlanarNet }

# @category Visualization
# Parameters to control the shapes of the flaps.
# The precise values are more or less randomly chosen.  Should work fine for most polytopes
# whose edge lengths do not vary too much.

options %flap_options=(
    # Float  absolute minimum width
    WidthMinimum=>0.25,
    # Float  minimum relative width (w.r.t. length of first edge)
    WidthRelativeMinimum=>0.1,
    # Float  absolute width, overrides the previous
    Width=>undef,
    # Float  proportion by which outer edge of flap is shorter than the actual edge (on both sides)
    FlapCutOff=>0.2
);

# Add flaps for gluing.
# @options %Visual::Polygons::decorations
# @options %flap_options
# @return Visual::PlanarNet

user_method FLAPS (%Visual::Polygons::decorations, %flap_options) {
   my ($self, $decor, $options)=@_;

   my $vertices=dehomogenize($self->PlanarNet->VERTICES);
   my $flap_edges=$self->PlanarNet->FLAPS;

   my $flap_width_min=$options->{WidthMinimum};
   my $flap_width_rel=$options->{WidthRelativeMinimum};
   my $flap_cutoff=$options->{FlapCutOff};
   my $flap_width=$options->{Width};

   my $n_flaps=$flap_edges->size();
   my @flaps=();
   my @flap_vertices=@{rows($vertices)};
   my $next_flap_vertex=$vertices->rows();
   for (my $i=0; $i<$n_flaps; ++$i) {
       my ($ia,$ib) = @{$flap_edges->[$i]};
       my ($a,$b) = ($vertices->[$ia],$vertices->[$ib]);
       my $vec = $b-$a;
       my $norm_vec = sqrt($vec*$vec);
       $flap_width = max($norm_vec*$flap_width_rel, $flap_width_min) unless defined($flap_width); # only first flap counts
       my $scp = ($b->[0] - $a->[0])/$norm_vec;
       # beware of numerical atrocities
       if ($scp>1.0) { $scp=1.0 } elsif ($scp<-1.0) { $scp=-1.0 }
       my $alpha = $vec->[1]>=0.0 ? Math::Trig::acos($scp) : -Math::Trig::acos($scp);
       $alpha -= Math::Trig::pi/2;
       my $translation = new Vector<Float>(cos($alpha) * $flap_width, sin($alpha) * $flap_width);
       push @flaps, [$ia, $next_flap_vertex, $next_flap_vertex+1, $ib];
       push @flap_vertices, $a+$translation+$flap_cutoff*$vec;
       push @flap_vertices, $b+$translation-$flap_cutoff*$vec;
       $next_flap_vertex += 2;
   }

   my $flap_vertices_matrix=new Matrix<Float>(@flap_vertices);

   unshift @{$self->elements},
       new Visual::Polygons( Name => "flaps",
                             Points => $flap_vertices_matrix,
                             PointLabels => "hidden",
                             PointStyle => "hidden",
                             Facets => \@flaps,
                             FacetColor => $Visual::Color::PlanarNetFlapColor,
                             EdgeThickness => 0.05,
                             NEdges => 4*($self->PlanarNet->POLYTOPE->N_VERTICES-1),
                             $decor
           );
   visualize($self);
}


object PlanarNet {

# @category Visualization
# This function is the main purpose of the entire class [[PlanarNet]].
# @options %Visual::Polygons::decorations
# @return Visual::PlanarNet

user_method VISUAL(%Visual::Polygons::decorations) : VERTICES, MAXIMAL_POLYTOPES, VIF_CYCLIC_NORMAL, VF_MAP, VF_MAP_INV, FLAPS, DUAL_TREE, POLYTOPE.N_VERTICES, POLYTOPE.DUAL_GRAPH.DIHEDRAL_ANGLES {
   my ($this,$decor)=@_;
   
   my $vif=$this->VIF_CYCLIC_NORMAL;
   my $vf_map=$this->VF_MAP;
   my $vf_map_inv=$this->VF_MAP_INV;

   my $mp = $this->MAXIMAL_POLYTOPES;
   my $n_facets=$mp->rows();
   die "VIF_CYCLIC_NORMAL and MAXIMAL_POLYTOPES sizes do not match" unless $vif->size() == $n_facets;

   # faces of the planar net
   my $vertices=dehomogenize($this->VERTICES);
   my @polygons = ();
   for (my $i=0; $i<$n_facets; ++$i) {
      my @this_face = map { $vf_map->{new Pair<Int,Int>($_,$i)} } @{$vif->[$i]};
      push @polygons, \@this_face;
   }
   
   my %dependmap = map { $_ => new Set } (0..$n_facets-1);
   my @angles;
   my @axes;
   my @edgedepend;
   foreach my $pair (reverse(@{$this->DUAL_TREE})) {
      my ($f,$t) = @$pair;
      my @a = grep { $mp->[$f]->contains($_) } @{$polygons[$t]};
      push @axes, $a[0] == $polygons[$t]->[0] && $a[1] == $polygons[$t]->[-1] ? [reverse(@a)] : \@a;
      push @angles, $this->POLYTOPE->DUAL_GRAPH->DIHEDRAL_ANGLES->edge($f,$t);
      my $depend = $dependmap{$t} + $mp->[$t] - $mp->[$f];
      push @edgedepend, [@$depend];
      $dependmap{$f} += $depend;
   }
   my ($first,$second) = @$vf_map_inv[0..1];
   my $rootfirst = convert_to<Float>($this->POLYTOPE->VERTICES->[$first]->slice(range_from(1)));
   my $edge = convert_to<Float>($this->POLYTOPE->VERTICES->[$second]->slice(range_from(1))) - $rootfirst;
   my $rootnormal = convert_to<Float>(-1 * $this->POLYTOPE->FACETS->[$this->DUAL_TREE->[0]->first]->slice(range_from(1)));
   my $rootup = [ $rootnormal->[1] * $edge->[2] - $rootnormal->[2] * $edge->[1],
                  $rootnormal->[2] * $edge->[0] - $rootnormal->[0] * $edge->[2],
                  $rootnormal->[0] * $edge->[1] - $rootnormal->[1] * $edge->[0] ];

   my $Layout = new Visual::PlanarNet::Polygons( Name => "planar_net_".$this->name,
                                      Points => $vertices,
                                      PointLabels => $vf_map_inv,
                                      Facets => \@polygons,
                                      NEdges => $n_facets-1+2*($this->POLYTOPE->N_VERTICES-1),
                                      Axes => \@axes,
                                      DihedralAngles => \@angles,
                                      DependendVertices => \@edgedepend,
                                      PolytopeRoot => [[@$rootfirst],[@$rootnormal],$rootup],
                                      $decor
       );
   visualize( new Visual::PlanarNet(Name => $this->name, PlanarNet => $this, $Layout) );
}

}


package Visual::Color;

# Color for flaps in the visualization of planar nets
custom $PlanarNetFlapColor="255 255 255"; # white


# Local Variables:
# mode: perl
# cperl-indent-level:3
# indent-tabs-mode:nil
# End: