Разрываемая ткань

Материал из Department of Theoretical and Applied Mechanics
Версия от 22:02, 10 марта 2015; Wikiadmin (обсуждение | вклад) (Замена текста — «<syntaxhighlight lang="html" line start="1" enclose="div">» на «<syntaxhighlight lang="html5" line start="1" enclose="div">»)

Перейти к: навигация, поиск
Виртуальная лаборатория > Разрываемая ткань

Моделирование ткани с использованием интегрирования Верле.

Лицензия данной программы предполагает, что вы можете копировать, изменять, публиковать, распространять, и даже продавать данную программу при соблюдении условия сохранения копирайта и заголовка (см. файл ниже) во всех копиях данной программы (или программ, существенно основанных на ней).


Скачать Tearable_Cloth.zip Текст программы на языке JavaScript (разработчик Suffick): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default"> Файл "Tearable_Cloth.js"

  1 /*
  2  Copyright (c) 2013 Suffick at Codepen (http://codepen.io/suffick), GitHub (https://github.com/suffick) and lonely-pixel.com
  3 
  4  Permission is hereby granted, free of charge, to any person obtaining a copy
  5  of this software and associated documentation files (the "Software"), to deal
  6  in the Software without restriction, including without limitation the rights
  7  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8  copies of the Software, and to permit persons to whom the Software is
  9  furnished to do so, subject to the following conditions:
 10 
 11  The above copyright notice and this permission notice shall be included in
 12  all copies or substantial portions of the Software.
 13 
 14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 17  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 19  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 20  THE SOFTWARE.
 21  */
 22 
 23 // Настройки
 24 
 25 var physics_accuracy  = 3,          // точность расчетов
 26     mouse_influence   = 20,         // радиус захвата ткани курсором
 27     mouse_cut         = 5,          // радиус обрезания ткани курсором (по правой кнопке мыши)
 28     gravity           = 1200,       // гравитация
 29     cloth_height      = 30,         // ширина ткани
 30     cloth_width       = 50,         // высота ткани
 31     start_y           = 20,         // высота закрепления ткани
 32     spacing           = 7,          // расстояние между узлами ткани
 33     tear_distance     = 60;         // прочность ткани
 34 
 35 
 36 window.requestAnimFrame =
 37     window.requestAnimationFrame ||
 38         window.webkitRequestAnimationFrame ||
 39         window.mozRequestAnimationFrame ||
 40         window.oRequestAnimationFrame ||
 41         window.msRequestAnimationFrame ||
 42         function (callback) {
 43             window.setTimeout(callback, 1000 / 60);
 44         };
 45 
 46 var canvas,
 47     ctx,
 48     cloth,
 49     boundsx,
 50     boundsy,
 51     mouse = {
 52         down: false,
 53         button: 1,
 54         x: 0,
 55         y: 0,
 56         px: 0,
 57         py: 0
 58     };
 59 
 60 var Point = function (x, y) {
 61 
 62     this.x      = x;
 63     this.y      = y;
 64     this.px     = x;
 65     this.py     = y;
 66     this.vx     = 0;
 67     this.vy     = 0;
 68     this.pin_x  = null;
 69     this.pin_y  = null;
 70 
 71     this.constraints = [];
 72 };
 73 
 74 Point.prototype.update = function (delta) {
 75 
 76     if (mouse.down) {
 77 
 78         var diff_x = this.x - mouse.x,
 79             diff_y = this.y - mouse.y,
 80             dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y);
 81 
 82         if (mouse.button == 1) {
 83 
 84             if (dist < mouse_influence) {
 85                 this.px = this.x - (mouse.x - mouse.px) * 1.8;
 86                 this.py = this.y - (mouse.y - mouse.py) * 1.8;
 87             }
 88 
 89         } else if (dist < mouse_cut) this.constraints = [];
 90     }
 91 
 92     this.add_force(0, gravity);
 93 
 94     delta *= delta;
 95     nx = this.x + ((this.x - this.px) * .99) + ((this.vx / 2) * delta);
 96     ny = this.y + ((this.y - this.py) * .99) + ((this.vy / 2) * delta);
 97 
 98     this.px = this.x;
 99     this.py = this.y;
100 
101     this.x = nx;
102     this.y = ny;
103 
104     this.vy = this.vx = 0
105 };
106 
107 Point.prototype.draw = function () {
108 
109     if (!this.constraints.length) return;
110 
111     var i = this.constraints.length;
112     while (i--) this.constraints[i].draw();
113 };
114 
115 Point.prototype.resolve_constraints = function () {
116 
117     if (this.pin_x != null && this.pin_y != null) {
118 
119         this.x = this.pin_x;
120         this.y = this.pin_y;
121         return;
122     }
123 
124     var i = this.constraints.length;
125     while (i--) this.constraints[i].resolve();
126 
127     this.x > boundsx ? this.x = 2 * boundsx - this.x : 1 > this.x && (this.x = 2 - this.x);
128     this.y < 1 ? this.y = 2 - this.y : this.y > boundsy && (this.y = 2 * boundsy - this.y);
129 };
130 
131 Point.prototype.attach = function (point) {
132 
133     this.constraints.push(
134         new Constraint(this, point)
135     );
136 };
137 
138 Point.prototype.remove_constraint = function (constraint) {
139 
140     this.constraints.splice(this.constraints.indexOf(constraint), 1);
141 };
142 
143 Point.prototype.add_force = function (x, y) {
144 
145     this.vx += x;
146     this.vy += y;
147 };
148 
149 Point.prototype.pin = function (pinx, piny) {
150     this.pin_x = pinx;
151     this.pin_y = piny;
152 };
153 
154 var Constraint = function (p1, p2) {
155 
156     this.p1     = p1;
157     this.p2     = p2;
158     this.length = spacing;
159 };
160 
161 Constraint.prototype.resolve = function () {
162 
163     var diff_x  = this.p1.x - this.p2.x,
164         diff_y  = this.p1.y - this.p2.y,
165         dist    = Math.sqrt(diff_x * diff_x + diff_y * diff_y),
166         diff    = (this.length - dist) / dist;
167 
168     if (dist > tear_distance) this.p1.remove_constraint(this);
169 
170     var px = diff_x * diff * 0.5;
171     var py = diff_y * diff * 0.5;
172 
173     this.p1.x += px;
174     this.p1.y += py;
175     this.p2.x -= px;
176     this.p2.y -= py;
177 };
178 
179 Constraint.prototype.draw = function () {
180 
181     ctx.moveTo(this.p1.x, this.p1.y);
182     ctx.lineTo(this.p2.x, this.p2.y);
183 };
184 
185 var Cloth = function () {
186 
187     this.points = [];
188 
189     var start_x = canvas.width / 2 - cloth_width * spacing / 2;
190 
191     for (var y = 0; y <= cloth_height; y++) {
192 
193         for (var x = 0; x <= cloth_width; x++) {
194 
195             var p = new Point(start_x + x * spacing, start_y + y * spacing);
196 
197             x != 0 && p.attach(this.points[this.points.length - 1]);
198             y == 0 && p.pin(p.x, p.y);
199             y != 0 && p.attach(this.points[x + (y - 1) * (cloth_width + 1)])
200 
201             this.points.push(p);
202         }
203     }
204 };
205 
206 Cloth.prototype.update = function () {
207 
208     var i = physics_accuracy;
209 
210     while (i--) {
211         var p = this.points.length;
212         while (p--) this.points[p].resolve_constraints();
213     }
214 
215     i = this.points.length;
216     while (i--) this.points[i].update(.016);
217 };
218 
219 Cloth.prototype.draw = function () {
220 
221     ctx.beginPath();
222 
223     var i = cloth.points.length;
224     while (i--) cloth.points[i].draw();
225 
226     ctx.stroke();
227 };
228 
229 function update() {
230 
231     ctx.clearRect(0, 0, canvas.width, canvas.height);
232 
233     cloth.update();
234     cloth.draw();
235 
236     requestAnimFrame(update);               // эта функция через некоторое время снова вызовет update(), так реализуется анимация
237 }
238 
239 // Инициализация и запуск программы
240 function start() {
241 
242     // функция выполняется при нажатии кнопки мыши
243     canvas.onmousedown = function (e) {
244         mouse.button  = e.which;
245         mouse.px      = mouse.x;
246         mouse.py      = mouse.y;
247         var rect      = canvas.getBoundingClientRect();
248         mouse.x       = e.clientX - rect.left,
249             mouse.y       = e.clientY - rect.top,
250             mouse.down    = true;
251         e.preventDefault();
252     };
253 
254     // функция выполняется при отпускании клавиши мыши
255     window.onmouseup = function (e) {       // на элементе window, чтобы не происходило залипания клавиши мыши
256         mouse.down = false;
257         e.preventDefault();
258     };
259 
260     // функция выполняется при движении мыши
261     canvas.onmousemove = function (e) {
262         mouse.px  = mouse.x;
263         mouse.py  = mouse.y;
264         var rect  = canvas.getBoundingClientRect();
265         mouse.x   = e.clientX - rect.left,
266             mouse.y   = e.clientY - rect.top,
267             e.preventDefault();
268     };
269 
270     // функция предотвращает появление контекстного меню
271     canvas.oncontextmenu = function (e) {
272         e.preventDefault();
273     };
274 
275     boundsx = canvas.width - 1;
276     boundsy = canvas.height - 1;
277 
278     ctx.strokeStyle = '#888';               // цвет ткани
279 
280     cloth = new Cloth();
281 
282     update();                               // начало расчетов/рисования
283 }
284 
285 window.onload = function () {               // эта функция выполняется при загрузке страницы
286 
287     canvas  = document.getElementById('c');
288     ctx     = canvas.getContext('2d');
289 
290     canvas.width  = 560;
291     canvas.height = 350;
292 
293     start();
294 };

Файл "Tearable_Cloth.html"

1 <canvas id="c"></canvas>
2 <script src="Tearable_Cloth.js"></script>

</toggledisplay>