
Variété différentiable orientable
Concept history
A revision trail for this concept page.
Revision 7818
10/26/2026, 11:49:13 AM · Ancient Tree
Updated text
Compare with revision 78172 changed lines
1
##### Intuition2
3
Une [[variete|variété]] est orientable si l'on peut choisir un « sens positif » de manière cohérente sur toute la variété, sans que ce sens s'inverse lorsqu'on se déplace.4
Sur une surface, on peut imaginer choisir un sens de rotation : celui des aiguilles d'une montre ou son inverse.5
```jsxgraph6
{7
<script src="https://cdn.jsdelivr.net/npm/jsxgraph@1.10.1/distrib/jsxgraphcore.js"></script>8
9
<div id="jxgbox" style="10
width:100%;11
aspect-ratio:1180 / 480;12
border:0;13
background:transparent;14
overflow:hidden;15
touch-action:none;16
"></div>17
18
<script>19
(function () {20
const board = JXG.JSXGraph.initBoard('jxgbox', {21
boundingbox: [-5.9, 2.7, 5.9, -2.1],22
keepaspectratio: true,23
axis: false,24
grid: false,25
showNavigation: false,26
showCopyright: false,27
pan: { enabled: false },28
zoom: { enabled: false, wheel: false },29
keyboard: { enabled: false },30
renderer: 'svg'31
});32
33
const TAU = 2 * Math.PI;34
const R = 2, W = 0.62, A = 0.34;35
const BLACK = '#202124';36
const BLUE = '#2f6fd6';37
const GRAY = '#8b9097';38
39
const sy = Math.sin(0.13), cy = Math.cos(0.13);40
const se = Math.sin(0.64), ce = Math.cos(0.64);41
42
const clamp = (x, a, b) => Math.max(a, Math.min(b, x));43
const sub = (a, b) => a.map((x, i) => x - b[i]);44
const cross = (a, b) => [45
a[1]*b[2] - a[2]*b[1],46
a[2]*b[0] - a[0]*b[2],47
a[0]*b[1] - a[1]*b[0]48
];49
const unit = a => a.map(x => x / Math.hypot(...a));50
const light = unit([-0.4, -0.5, 1]);51
52
// k = 0 : cylindre ; k = 1 : Möbius.53
function point(k, u, v) {54
const t = u - Math.PI/2;55
const a = k*u/2;56
const r = R + v*Math.sin(a);57
58
return [59
r*Math.cos(t),60
r*Math.sin(t),61
v*Math.cos(a)62
];63
}64
65
function project(k, p) {66
const [x, y, z] = p;67
68
return [69
(k ? 3 : -3) + x*cy + y*sy,70
-x*sy*se + y*cy*se + z*ce,71
x*sy*ce - y*cy*ce + z*se72
];73
}74
75
const at = (k, u, v) => project(k, point(k, u, v));76
77
const path = ps => 'M' + ps.map(p =>78
((p[0]+5.9)*100).toFixed(2) + ',' +79
((2.1-p[1])*100).toFixed(2)80
).join('L');81
82
// Surfaces vectorielles fixes et contours noirs.83
function surface(k) {84
const pieces = [];85
const du = TAU/180;86
87
for (let i = 0; i < 180; i++) {88
const u = i*du, m = u+du/2, e = 0.0001;89
const n = unit(cross(90
sub(point(k,m+e,0), point(k,m-e,0)),91
sub(point(k,m,e), point(k,m,-e))92
));93
94
const shade = 0.15 + 0.08 * (95
1 - Math.abs(n.reduce((s,x,j) => s+x*light[j], 0))96
);97
const c = Math.round(255*(1-shade) + 32*shade);98
99
pieces.push({100
ps: [101
at(k,u,-W), at(k,u+du,-W),102
at(k,u+du,W), at(k,u,W)103
],104
color: `rgb(${c},${c},${c})`105
});106
107
for (const v of [-W,W]) {108
pieces.push({109
ps: [at(k,u,v), at(k,u+du,v)],110
edge: true111
});112
}113
}114
115
pieces.forEach(p => {116
p.depth = p.ps.reduce((s,q) => s+q[2], 0) /117
p.ps.length + (p.edge ? 0.012 : 0);118
});119
pieces.sort((a,b) => a.depth-b.depth);120
121
const faces = pieces.filter(p => !p.edge);122
123
const filter = `124
<filter id="rim${k}"125
x="-5%" y="-5%" width="110%" height="110%"126
color-interpolation-filters="sRGB">127
<feComponentTransfer in="SourceAlpha" result="solid">128
<feFuncA type="linear" slope="2"/>129
</feComponentTransfer>130
<feMorphology in="solid" operator="dilate"131
radius="0.95" result="outer"/>132
<feMorphology in="solid" operator="erode"133
radius="0.75" result="inner"/>134
<feComposite in="outer" in2="inner"135
operator="out" result="outline"/>136
<feFlood flood-color="${BLACK}" result="ink"/>137
<feComposite in="ink" in2="outline" operator="in"/>138
</filter>`;139
140
const mesh = pieces.map(p => p.edge141
? `<path d="${path(p.ps)}" fill="none"142
stroke="${BLACK}" stroke-width="1.7"143
stroke-linecap="round"/>`144
: `<path d="${path(p.ps)}Z" fill="${p.color}"145
stroke="${p.color}" stroke-width="1"/>`146
).join('');147
148
const outline = faces.map(p =>149
`<path d="${path(p.ps)}Z" fill="black"150
stroke="black" stroke-width="1"/>`151
).join('');152
153
const route = Array.from(154
{length:161}, (_,i) => at(k,i*TAU/160,0)155
);156
157
return `158
<defs>${filter}</defs>159
${mesh}160
<g filter="url(#rim${k})">${outline}</g>161
<path d="${path(route)}" fill="none"162
stroke="${GRAY}" stroke-width="1"163
stroke-dasharray="3 6" opacity="0.38"/>`;164
}165
166
board.suspendUpdate();167
168
const svg = `169
<svg xmlns="http://www.w3.org/2000/svg"170
width="1180" height="420" viewBox="0 0 1180 420">171
${surface(0)}172
${surface(1)}173
</svg>`;174
175
board.create('image', [176
'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg),177
[-5.9,-2.1],178
[11.8,4.2]179
], {180
fixed: true,181
highlight: false,182
layer: 0183
});184
185
const slider = board.create('slider', [186
[-5.35,2.35], [5.35,2.35], [0,0,1]187
], {188
name: '',189
withLabel: false,190
withTicks: false,191
snapWidth: 0.005,192
size: 5,193
strokeColor: BLACK,194
fillColor: BLACK,195
highlight: false,196
baseline: {197
strokeColor: '#d2d5da',198
strokeWidth: 3,199
highlight: false,200
fixed: true201
},202
highline: {203
strokeColor: BLACK,204
strokeWidth: 3,205
highlight: false206
},207
point1: { visible:false, fixed:true },208
point2: { visible:false, fixed:true }209
});210
211
// Transport continu du cercle dans les coordonnées du ruban.212
function arrow(k, u, reference) {213
const ps = Array.from({length:81}, (_,i) => {214
const a = 0.42 + (TAU-0.84)*i/80;215
return at(k, u+A*Math.cos(a)/R, A*Math.sin(a));216
});217
218
// Pointe calculée en pixels pour rester bien visible.219
const screen = ps.map(p => [220
p[0]*board.unitX,221
p[1]*board.unitY222
]);223
224
const tip = screen[80], prev = screen[79];225
const dx = tip[0]-prev[0], dy = tip[1]-prev[1];226
const d = Math.hypot(dx,dy) || 1;227
const tx = dx/d, ty = dy/d;228
229
const len = clamp(230
board.unitX*0.17,231
reference ? 5 : 6,232
reference ? 9 : 11233
);234
235
const head = [236
tip,237
[238
tip[0]-len*tx-len*0.58*ty,239
tip[1]-len*ty+len*0.58*tx240
],241
[242
tip[0]-len*tx+len*0.58*ty,243
tip[1]-len*ty-len*0.58*tx244
],245
tip246
];247
248
const body = screen.slice();249
let remaining = len*0.72;250
251
while (body.length > 2) {252
const b = body[body.length-1];253
const a = body[body.length-2];254
const distance = Math.hypot(b[0]-a[0], b[1]-a[1]);255
256
if (distance > remaining) {257
body[body.length-1] = b.map((x,j) =>258
x + (a[j]-x)*remaining/distance259
);260
break;261
}262
263
remaining -= distance;264
body.pop();265
}266
267
const coords = points => points.map(p => [268
p[0]/board.unitX,269
p[1]/board.unitY270
]);271
272
return { body:coords(body), head:coords(head) };273
}274
275
function marker(k, reference) {276
const color = reference ? GRAY : BLUE;277
278
for (const part of ['body','head']) {279
const curve = board.create('curve', [[0],[0]], {280
fixed: true,281
highlight: false,282
withLabel: false,283
layer: reference ? 5 : 6,284
visible: () => !reference || slider.Value() >= 0.015,285
strokeColor: color,286
fillColor: color,287
strokeWidth: part === 'head'288
? 0 : (reference ? 1.5 : 2.4),289
fillOpacity: part === 'head'290
? (reference ? 0.75 : 1) : 0,291
strokeOpacity: reference ? 0.65 : 1,292
dash: reference && part === 'body' ? 2 : 0293
});294
295
curve.updateDataArray = function () {296
const points = arrow(297
k,298
reference ? 0 : slider.Value()*TAU,299
reference300
)[part];301
302
this.dataX = points.map(p => p[0]);303
this.dataY = points.map(p => p[1]);304
};305
}306
}307
308
for (const k of [0,1]) {309
marker(k,true);310
marker(k,false);311
}312
313
board.unsuspendUpdate();314
})();315
</script>316
}317
```318
*Tirer le curseur pour visualiser : un [[cylindre|cylindre]] est une [[surface|surface]] orientable. Mais un [[ruban de Möbius|ruban de Möbius]], lorsqu'on effectue un tour complet en transportant cette orientation locale, on revient au point de départ avec le sens opposé. Le ruban de Möbius n'est donc pas orientable.*319
320
##### Définition formelle321
322
Soit $M$ une [[variete_differentiable|variété différentiable]] de dimension $n$.323
324
On dit que $M$ est orientable s'il existe un atlas orienté $(U_i,\varphi_i)_{i\in I}$, c'est-à-dire que, pour tous $i,j\in I$, les [[Fonction de transition|fonctions de transition]] sont de déterminant strictement positif :324
On dit que $M$ est orientable s'il existe un [[atlas-differentiable|atlas]] orienté $(U_i,\varphi_i)_{i\in I}$, c'est-à-dire que, pour tous $i,j\in I$, les [[Fonction de transition|fonctions de transition]] sont de déterminant strictement positif :325
$$326
\det D(\varphi_j\circ\varphi_i^{-1})>0327
$$328
en tout point de $\varphi_i(U_i\cap U_j)$.Revision 7817
10/26/2026, 11:48:41 AM · Ancient Tree
Updated text
Compare with revision 78162 changed lines
1
##### Intuition2
3
Une [[variete|variété]] est orientable si l'on peut choisir un « sens positif » de manière cohérente sur toute la variété, sans que ce sens s'inverse lorsqu'on se déplace.4
Sur une surface, on peut imaginer choisir un sens de rotation : celui des aiguilles d'une montre ou son inverse.5
```jsxgraph6
{7
<script src="https://cdn.jsdelivr.net/npm/jsxgraph@1.10.1/distrib/jsxgraphcore.js"></script>8
9
<div id="jxgbox" style="10
width:100%;11
aspect-ratio:1180 / 480;12
border:0;13
background:transparent;14
overflow:hidden;15
touch-action:none;16
"></div>17
18
<script>19
(function () {20
const board = JXG.JSXGraph.initBoard('jxgbox', {21
boundingbox: [-5.9, 2.7, 5.9, -2.1],22
keepaspectratio: true,23
axis: false,24
grid: false,25
showNavigation: false,26
showCopyright: false,27
pan: { enabled: false },28
zoom: { enabled: false, wheel: false },29
keyboard: { enabled: false },30
renderer: 'svg'31
});32
33
const TAU = 2 * Math.PI;34
const R = 2, W = 0.62, A = 0.34;35
const BLACK = '#202124';36
const BLUE = '#2f6fd6';37
const GRAY = '#8b9097';38
39
const sy = Math.sin(0.13), cy = Math.cos(0.13);40
const se = Math.sin(0.64), ce = Math.cos(0.64);41
42
const clamp = (x, a, b) => Math.max(a, Math.min(b, x));43
const sub = (a, b) => a.map((x, i) => x - b[i]);44
const cross = (a, b) => [45
a[1]*b[2] - a[2]*b[1],46
a[2]*b[0] - a[0]*b[2],47
a[0]*b[1] - a[1]*b[0]48
];49
const unit = a => a.map(x => x / Math.hypot(...a));50
const light = unit([-0.4, -0.5, 1]);51
52
// k = 0 : cylindre ; k = 1 : Möbius.53
function point(k, u, v) {54
const t = u - Math.PI/2;55
const a = k*u/2;56
const r = R + v*Math.sin(a);57
58
return [59
r*Math.cos(t),60
r*Math.sin(t),61
v*Math.cos(a)62
];63
}64
65
function project(k, p) {66
const [x, y, z] = p;67
68
return [69
(k ? 3 : -3) + x*cy + y*sy,70
-x*sy*se + y*cy*se + z*ce,71
x*sy*ce - y*cy*ce + z*se72
];73
}74
75
const at = (k, u, v) => project(k, point(k, u, v));76
77
const path = ps => 'M' + ps.map(p =>78
((p[0]+5.9)*100).toFixed(2) + ',' +79
((2.1-p[1])*100).toFixed(2)80
).join('L');81
82
// Surfaces vectorielles fixes et contours noirs.83
function surface(k) {84
const pieces = [];85
const du = TAU/180;86
87
for (let i = 0; i < 180; i++) {88
const u = i*du, m = u+du/2, e = 0.0001;89
const n = unit(cross(90
sub(point(k,m+e,0), point(k,m-e,0)),91
sub(point(k,m,e), point(k,m,-e))92
));93
94
const shade = 0.15 + 0.08 * (95
1 - Math.abs(n.reduce((s,x,j) => s+x*light[j], 0))96
);97
const c = Math.round(255*(1-shade) + 32*shade);98
99
pieces.push({100
ps: [101
at(k,u,-W), at(k,u+du,-W),102
at(k,u+du,W), at(k,u,W)103
],104
color: `rgb(${c},${c},${c})`105
});106
107
for (const v of [-W,W]) {108
pieces.push({109
ps: [at(k,u,v), at(k,u+du,v)],110
edge: true111
});112
}113
}114
115
pieces.forEach(p => {116
p.depth = p.ps.reduce((s,q) => s+q[2], 0) /117
p.ps.length + (p.edge ? 0.012 : 0);118
});119
pieces.sort((a,b) => a.depth-b.depth);120
121
const faces = pieces.filter(p => !p.edge);122
123
const filter = `124
<filter id="rim${k}"125
x="-5%" y="-5%" width="110%" height="110%"126
color-interpolation-filters="sRGB">127
<feComponentTransfer in="SourceAlpha" result="solid">128
<feFuncA type="linear" slope="2"/>129
</feComponentTransfer>130
<feMorphology in="solid" operator="dilate"131
radius="0.95" result="outer"/>132
<feMorphology in="solid" operator="erode"133
radius="0.75" result="inner"/>134
<feComposite in="outer" in2="inner"135
operator="out" result="outline"/>136
<feFlood flood-color="${BLACK}" result="ink"/>137
<feComposite in="ink" in2="outline" operator="in"/>138
</filter>`;139
140
const mesh = pieces.map(p => p.edge141
? `<path d="${path(p.ps)}" fill="none"142
stroke="${BLACK}" stroke-width="1.7"143
stroke-linecap="round"/>`144
: `<path d="${path(p.ps)}Z" fill="${p.color}"145
stroke="${p.color}" stroke-width="1"/>`146
).join('');147
148
const outline = faces.map(p =>149
`<path d="${path(p.ps)}Z" fill="black"150
stroke="black" stroke-width="1"/>`151
).join('');152
153
const route = Array.from(154
{length:161}, (_,i) => at(k,i*TAU/160,0)155
);156
157
return `158
<defs>${filter}</defs>159
${mesh}160
<g filter="url(#rim${k})">${outline}</g>161
<path d="${path(route)}" fill="none"162
stroke="${GRAY}" stroke-width="1"163
stroke-dasharray="3 6" opacity="0.38"/>`;164
}165
166
board.suspendUpdate();167
168
const svg = `169
<svg xmlns="http://www.w3.org/2000/svg"170
width="1180" height="420" viewBox="0 0 1180 420">171
${surface(0)}172
${surface(1)}173
</svg>`;174
175
board.create('image', [176
'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg),177
[-5.9,-2.1],178
[11.8,4.2]179
], {180
fixed: true,181
highlight: false,182
layer: 0183
});184
185
const slider = board.create('slider', [186
[-5.35,2.35], [5.35,2.35], [0,0,1]187
], {188
name: '',189
withLabel: false,190
withTicks: false,191
snapWidth: 0.005,192
size: 5,193
strokeColor: BLACK,194
fillColor: BLACK,195
highlight: false,196
baseline: {197
strokeColor: '#d2d5da',198
strokeWidth: 3,199
highlight: false,200
fixed: true201
},202
highline: {203
strokeColor: BLACK,204
strokeWidth: 3,205
highlight: false206
},207
point1: { visible:false, fixed:true },208
point2: { visible:false, fixed:true }209
});210
211
// Transport continu du cercle dans les coordonnées du ruban.212
function arrow(k, u, reference) {213
const ps = Array.from({length:81}, (_,i) => {214
const a = 0.42 + (TAU-0.84)*i/80;215
return at(k, u+A*Math.cos(a)/R, A*Math.sin(a));216
});217
218
// Pointe calculée en pixels pour rester bien visible.219
const screen = ps.map(p => [220
p[0]*board.unitX,221
p[1]*board.unitY222
]);223
224
const tip = screen[80], prev = screen[79];225
const dx = tip[0]-prev[0], dy = tip[1]-prev[1];226
const d = Math.hypot(dx,dy) || 1;227
const tx = dx/d, ty = dy/d;228
229
const len = clamp(230
board.unitX*0.17,231
reference ? 5 : 6,232
reference ? 9 : 11233
);234
235
const head = [236
tip,237
[238
tip[0]-len*tx-len*0.58*ty,239
tip[1]-len*ty+len*0.58*tx240
],241
[242
tip[0]-len*tx+len*0.58*ty,243
tip[1]-len*ty-len*0.58*tx244
],245
tip246
];247
248
const body = screen.slice();249
let remaining = len*0.72;250
251
while (body.length > 2) {252
const b = body[body.length-1];253
const a = body[body.length-2];254
const distance = Math.hypot(b[0]-a[0], b[1]-a[1]);255
256
if (distance > remaining) {257
body[body.length-1] = b.map((x,j) =>258
x + (a[j]-x)*remaining/distance259
);260
break;261
}262
263
remaining -= distance;264
body.pop();265
}266
267
const coords = points => points.map(p => [268
p[0]/board.unitX,269
p[1]/board.unitY270
]);271
272
return { body:coords(body), head:coords(head) };273
}274
275
function marker(k, reference) {276
const color = reference ? GRAY : BLUE;277
278
for (const part of ['body','head']) {279
const curve = board.create('curve', [[0],[0]], {280
fixed: true,281
highlight: false,282
withLabel: false,283
layer: reference ? 5 : 6,284
visible: () => !reference || slider.Value() >= 0.015,285
strokeColor: color,286
fillColor: color,287
strokeWidth: part === 'head'288
? 0 : (reference ? 1.5 : 2.4),289
fillOpacity: part === 'head'290
? (reference ? 0.75 : 1) : 0,291
strokeOpacity: reference ? 0.65 : 1,292
dash: reference && part === 'body' ? 2 : 0293
});294
295
curve.updateDataArray = function () {296
const points = arrow(297
k,298
reference ? 0 : slider.Value()*TAU,299
reference300
)[part];301
302
this.dataX = points.map(p => p[0]);303
this.dataY = points.map(p => p[1]);304
};305
}306
}307
308
for (const k of [0,1]) {309
marker(k,true);310
marker(k,false);311
}312
313
board.unsuspendUpdate();314
})();315
</script>316
}317
```318
*Un [[cylindre|cylindre]] est une [[surface|surface]] orientable. Mais un [[ruban de Möbius|ruban de Möbius]], lorsqu'on effectue un tour complet en transportant cette orientation locale, on revient au point de départ avec le sens opposé. Le ruban de Möbius n'est donc pas orientable.*318
*Tirer le curseur pour visualiser : un [[cylindre|cylindre]] est une [[surface|surface]] orientable. Mais un [[ruban de Möbius|ruban de Möbius]], lorsqu'on effectue un tour complet en transportant cette orientation locale, on revient au point de départ avec le sens opposé. Le ruban de Möbius n'est donc pas orientable.*319
320
##### Définition formelle321
322
Soit $M$ une [[variete_differentiable|variété différentiable]] de dimension $n$.323
324
On dit que $M$ est orientable s'il existe un atlas orienté $(U_i,\varphi_i)_{i\in I}$, c'est-à-dire que, pour tous $i,j\in I$, les [[Fonction de transition|fonctions de transition]] sont de déterminant strictement positif :325
$$326
\det D(\varphi_j\circ\varphi_i^{-1})>0327
$$328
en tout point de $\varphi_i(U_i\cap U_j)$.Revision 7816
10/26/2026, 11:48:08 AM · Ancient Tree
Updated text
Compare with revision 7815318 changed lines
1
##### Intuition2
3
Une [[variete|variété]] est orientable si l'on peut choisir un « sens positif » de manière cohérente sur toute la variété, sans que ce sens s'inverse lorsqu'on se déplace.4
Sur une surface, on peut imaginer choisir un sens de rotation : celui des aiguilles d'une montre ou son inverse.5
5
```jsxgraph6
*Sur un [[ruban de Möbius|ruban de Möbius]], lorsqu'on effectue un tour complet en transportant cette orientation locale, on revient au point de départ avec le sens opposé. Le ruban de Möbius n'est donc pas orientable.*6
{7
<script src="https://cdn.jsdelivr.net/npm/jsxgraph@1.10.1/distrib/jsxgraphcore.js"></script>7
9
<div id="jxgbox" style="10
width:100%;11
aspect-ratio:1180 / 480;12
border:0;13
background:transparent;14
overflow:hidden;15
touch-action:none;16
"></div>17
18
<script>19
(function () {20
const board = JXG.JSXGraph.initBoard('jxgbox', {21
boundingbox: [-5.9, 2.7, 5.9, -2.1],22
keepaspectratio: true,23
axis: false,24
grid: false,25
showNavigation: false,26
showCopyright: false,27
pan: { enabled: false },28
zoom: { enabled: false, wheel: false },29
keyboard: { enabled: false },30
renderer: 'svg'31
});32
33
const TAU = 2 * Math.PI;34
const R = 2, W = 0.62, A = 0.34;35
const BLACK = '#202124';36
const BLUE = '#2f6fd6';37
const GRAY = '#8b9097';38
39
const sy = Math.sin(0.13), cy = Math.cos(0.13);40
const se = Math.sin(0.64), ce = Math.cos(0.64);41
42
const clamp = (x, a, b) => Math.max(a, Math.min(b, x));43
const sub = (a, b) => a.map((x, i) => x - b[i]);44
const cross = (a, b) => [45
a[1]*b[2] - a[2]*b[1],46
a[2]*b[0] - a[0]*b[2],47
a[0]*b[1] - a[1]*b[0]48
];49
const unit = a => a.map(x => x / Math.hypot(...a));50
const light = unit([-0.4, -0.5, 1]);51
52
// k = 0 : cylindre ; k = 1 : Möbius.53
function point(k, u, v) {54
const t = u - Math.PI/2;55
const a = k*u/2;56
const r = R + v*Math.sin(a);57
58
return [59
r*Math.cos(t),60
r*Math.sin(t),61
v*Math.cos(a)62
];63
}64
65
function project(k, p) {66
const [x, y, z] = p;67
68
return [69
(k ? 3 : -3) + x*cy + y*sy,70
-x*sy*se + y*cy*se + z*ce,71
x*sy*ce - y*cy*ce + z*se72
];73
}74
75
const at = (k, u, v) => project(k, point(k, u, v));76
77
const path = ps => 'M' + ps.map(p =>78
((p[0]+5.9)*100).toFixed(2) + ',' +79
((2.1-p[1])*100).toFixed(2)80
).join('L');81
82
// Surfaces vectorielles fixes et contours noirs.83
function surface(k) {84
const pieces = [];85
const du = TAU/180;86
87
for (let i = 0; i < 180; i++) {88
const u = i*du, m = u+du/2, e = 0.0001;89
const n = unit(cross(90
sub(point(k,m+e,0), point(k,m-e,0)),91
sub(point(k,m,e), point(k,m,-e))92
));93
94
const shade = 0.15 + 0.08 * (95
1 - Math.abs(n.reduce((s,x,j) => s+x*light[j], 0))96
);97
const c = Math.round(255*(1-shade) + 32*shade);98
99
pieces.push({100
ps: [101
at(k,u,-W), at(k,u+du,-W),102
at(k,u+du,W), at(k,u,W)103
],104
color: `rgb(${c},${c},${c})`105
});106
107
for (const v of [-W,W]) {108
pieces.push({109
ps: [at(k,u,v), at(k,u+du,v)],110
edge: true111
});112
}113
}114
115
pieces.forEach(p => {116
p.depth = p.ps.reduce((s,q) => s+q[2], 0) /117
p.ps.length + (p.edge ? 0.012 : 0);118
});119
pieces.sort((a,b) => a.depth-b.depth);120
121
const faces = pieces.filter(p => !p.edge);122
123
const filter = `124
<filter id="rim${k}"125
x="-5%" y="-5%" width="110%" height="110%"126
color-interpolation-filters="sRGB">127
<feComponentTransfer in="SourceAlpha" result="solid">128
<feFuncA type="linear" slope="2"/>129
</feComponentTransfer>130
<feMorphology in="solid" operator="dilate"131
radius="0.95" result="outer"/>132
<feMorphology in="solid" operator="erode"133
radius="0.75" result="inner"/>134
<feComposite in="outer" in2="inner"135
operator="out" result="outline"/>136
<feFlood flood-color="${BLACK}" result="ink"/>137
<feComposite in="ink" in2="outline" operator="in"/>138
</filter>`;139
140
const mesh = pieces.map(p => p.edge141
? `<path d="${path(p.ps)}" fill="none"142
stroke="${BLACK}" stroke-width="1.7"143
stroke-linecap="round"/>`144
: `<path d="${path(p.ps)}Z" fill="${p.color}"145
stroke="${p.color}" stroke-width="1"/>`146
).join('');147
148
const outline = faces.map(p =>149
`<path d="${path(p.ps)}Z" fill="black"150
stroke="black" stroke-width="1"/>`151
).join('');152
153
const route = Array.from(154
{length:161}, (_,i) => at(k,i*TAU/160,0)155
);156
157
return `158
<defs>${filter}</defs>159
${mesh}160
<g filter="url(#rim${k})">${outline}</g>161
<path d="${path(route)}" fill="none"162
stroke="${GRAY}" stroke-width="1"163
stroke-dasharray="3 6" opacity="0.38"/>`;164
}165
166
board.suspendUpdate();167
168
const svg = `169
<svg xmlns="http://www.w3.org/2000/svg"170
width="1180" height="420" viewBox="0 0 1180 420">171
${surface(0)}172
${surface(1)}173
</svg>`;174
175
board.create('image', [176
'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg),177
[-5.9,-2.1],178
[11.8,4.2]179
], {180
fixed: true,181
highlight: false,182
layer: 0183
});184
185
const slider = board.create('slider', [186
[-5.35,2.35], [5.35,2.35], [0,0,1]187
], {188
name: '',189
withLabel: false,190
withTicks: false,191
snapWidth: 0.005,192
size: 5,193
strokeColor: BLACK,194
fillColor: BLACK,195
highlight: false,196
baseline: {197
strokeColor: '#d2d5da',198
strokeWidth: 3,199
highlight: false,200
fixed: true201
},202
highline: {203
strokeColor: BLACK,204
strokeWidth: 3,205
highlight: false206
},207
point1: { visible:false, fixed:true },208
point2: { visible:false, fixed:true }209
});210
211
// Transport continu du cercle dans les coordonnées du ruban.212
function arrow(k, u, reference) {213
const ps = Array.from({length:81}, (_,i) => {214
const a = 0.42 + (TAU-0.84)*i/80;215
return at(k, u+A*Math.cos(a)/R, A*Math.sin(a));216
});217
218
// Pointe calculée en pixels pour rester bien visible.219
const screen = ps.map(p => [220
p[0]*board.unitX,221
p[1]*board.unitY222
]);223
224
const tip = screen[80], prev = screen[79];225
const dx = tip[0]-prev[0], dy = tip[1]-prev[1];226
const d = Math.hypot(dx,dy) || 1;227
const tx = dx/d, ty = dy/d;228
229
const len = clamp(230
board.unitX*0.17,231
reference ? 5 : 6,232
reference ? 9 : 11233
);234
235
const head = [236
tip,237
[238
tip[0]-len*tx-len*0.58*ty,239
tip[1]-len*ty+len*0.58*tx240
],241
[242
tip[0]-len*tx+len*0.58*ty,243
tip[1]-len*ty-len*0.58*tx244
],245
tip246
];247
248
const body = screen.slice();249
let remaining = len*0.72;250
251
while (body.length > 2) {252
const b = body[body.length-1];253
const a = body[body.length-2];254
const distance = Math.hypot(b[0]-a[0], b[1]-a[1]);255
256
if (distance > remaining) {257
body[body.length-1] = b.map((x,j) =>258
x + (a[j]-x)*remaining/distance259
);260
break;261
}262
263
remaining -= distance;264
body.pop();265
}266
267
const coords = points => points.map(p => [268
p[0]/board.unitX,269
p[1]/board.unitY270
]);271
272
return { body:coords(body), head:coords(head) };273
}274
275
function marker(k, reference) {276
const color = reference ? GRAY : BLUE;277
278
for (const part of ['body','head']) {279
const curve = board.create('curve', [[0],[0]], {280
fixed: true,281
highlight: false,282
withLabel: false,283
layer: reference ? 5 : 6,284
visible: () => !reference || slider.Value() >= 0.015,285
strokeColor: color,286
fillColor: color,287
strokeWidth: part === 'head'288
? 0 : (reference ? 1.5 : 2.4),289
fillOpacity: part === 'head'290
? (reference ? 0.75 : 1) : 0,291
strokeOpacity: reference ? 0.65 : 1,292
dash: reference && part === 'body' ? 2 : 0293
});294
295
curve.updateDataArray = function () {296
const points = arrow(297
k,298
reference ? 0 : slider.Value()*TAU,299
reference300
)[part];301
302
this.dataX = points.map(p => p[0]);303
this.dataY = points.map(p => p[1]);304
};305
}306
}307
308
for (const k of [0,1]) {309
marker(k,true);310
marker(k,false);311
}312
313
board.unsuspendUpdate();314
})();315
</script>316
}317
```318
*Un [[cylindre|cylindre]] est une [[surface|surface]] orientable. Mais un [[ruban de Möbius|ruban de Möbius]], lorsqu'on effectue un tour complet en transportant cette orientation locale, on revient au point de départ avec le sens opposé. Le ruban de Möbius n'est donc pas orientable.*319
8
##### Définition formelle9
10
Soit $M$ une [[variete_differentiable|variété différentiable]] de dimension $n$.11
12
On dit que $M$ est orientable s'il existe un atlas orienté $(U_i,\varphi_i)_{i\in I}$, c'est-à-dire que, pour tous $i,j\in I$, les [[carte-locale|changements de cartes]] vérifient :324
On dit que $M$ est orientable s'il existe un atlas orienté $(U_i,\varphi_i)_{i\in I}$, c'est-à-dire que, pour tous $i,j\in I$, les [[Fonction de transition|fonctions de transition]] sont de déterminant strictement positif :13
$$14
\det D(\varphi_j\circ\varphi_i^{-1})>015
$$16
en tout point de $\varphi_i(U_i\cap U_j)$.Revision 7815
10/26/2026, 11:15:27 AM · Ancient Tree
Concept created
##### Intuition
Une [[variete|variété]] est orientable si l'on peut choisir un « sens positif » de manière cohérente sur toute la variété, sans que ce sens s'inverse lorsqu'on se déplace.
Sur une surface, on peut imaginer choisir un sens de rotation : celui des aiguilles d'une montre ou son inverse.

*Sur un [[ruban de Möbius|ruban de Möbius]], lorsqu'on effectue un tour complet en transportant cette orientation locale, on revient au point de départ avec le sens opposé. Le ruban de Möbius n'est donc pas orientable.*
##### Définition formelle
Soit $M$ une [[variete_differentiable|variété différentiable]] de dimension $n$.
On dit que $M$ est orientable s'il existe un atlas orienté $(U_i,\varphi_i)_{i\in I}$, c'est-à-dire que, pour tous $i,j\in I$, les [[carte-locale|changements de cartes]] vérifient :
$$
\det D(\varphi_j\circ\varphi_i^{-1})>0
$$
en tout point de $\varphi_i(U_i\cap U_j)$.