Tag Archives: Лайфхаки

Як збудувати геодезичний купол?

Я простий програміст, але іноді свербить побути архітектором. Всякий ентерпрайз проектувати ще не пускають, але тут випала нагода зпроектувати проект іграшкової хатинки для донечки. Тому що після покупки меблів в порожню квартиру в нас все ще залишається вільна кімната і в ній куууупа картону.

Картон в меблевій майстерні

Я погуглив які можна робити хатинки і знайшов таке на instructables. Але там виміри подані все ще в імперській системі і я вирішив перерахувати. І взагалі зробити проект правильною водоспадною методологією – поетапно.

3d модель

Перший етап – змоделювати все на комп’ютері.

  1. Запускаємо Blender, вибираємо куб який там за замовчуванням, натискаємо Delete.
  2. Shift+A (додати об’єкт) -> Icosphere, Subdivision: 2, Size: 1.0 location: (0.0, 0.0, 0.0).
  3. Переключаємось на вид з переду, тиснемо Tab і переходимо в Edit mode.
  4. Тиснемо правою кнопкою на якусь вершину аби вибрати лише її. Тиснемо b, аби вибрати вершини рамкою, і вибираємо ті що нижче 0 по осі Z.
  5. Натискаємо Delete і видалаємо половину вершин знизу.

Має вийти щось таке:

Купол в Blender

Це можна експортувати в розгортку для паперової моделі, якщо зробити наступне:

  1. Зайти в меню File -> User preferences, вкладка Add-ons, категорія Import-Export.
  2. Поставити галочку біля Export Paper Model, і натиснути “Save User Settings”.
  3. File -> Export -> Paper Model експортує модель в файл pdf (хоча пише що svg).

Отримаємо таке:

Розгортка

Для деяких розрахунків більшої моделі ще корисно експортувати в .obj файл.

Паперова модель

В паперовій моделі головне акуратно вирізати розгортку по чорних суцільних лініях, зігнути по пунктирних і склеїти сіренькі краї.

Склеєна паперова модель

Паперова модель дає краще уявлення про те скільки трикутників доведеться вирізати, але як різати картон все ще не ясно. Якщо я хочу купол радіусом 1 метр, то якого розміру мають бути трикутники?

Вони здаються правильними, але насправді лише здаються, тому що з правильних трикутників вийде хіба що площина або дельтаедр. Щоб обчислити розміри трикутників, можна використати obj файл.

Розміри

Наступний скрипт якщо йому згодувати наш obj файл зробить дві речі:

import sys
import math
from collections import Counter

# Distance between two vectors (iterables) rounded to 3 digits after comma
def distance(A, B):
    return round(math.sqrt(sum((a - b)**2 for a, b in zip(A,B))), 3)

def obj_fline_to_edges(line, vertices):
    # get indexes of vertices
    face = [int(e.split('/')[0]) - 1 for e in line[2:].split(' ')]

    # compute edge length's
    edges = sorted([
        distance(vertices[face[0]], vertices[face[1]]),
        distance(vertices[face[2]], vertices[face[1]]),
        distance(vertices[face[2]], vertices[face[0]]),
    ])
    return tuple(edges)

def main(filename):
    vertices = []
    faces = []
    face_lines = []
    with open(filename) as f:
        for line in f:
            if line.startswith('v '): # add line to list of vertices
                coords = [float(e) for e in line[2:].split(' ')]
                vertices.append(coords)

            if line.startswith('f '): # it's a face
                face_lines.append(line.strip())
                edges = obj_fline_to_edges(line, vertices)
                faces.append(edges)

    stats = Counter(faces).most_common()
    for i, (edges, count) in enumerate(stats, 1):
        print(f'{i}) {edges} - {count}')

    materials = [
        'redMtl',
        'blueMtl',
    ]
    for i, (edges_type, count) in enumerate(stats):
        print("usemtl", materials[i])
        for line in face_lines:
            edges = obj_fline_to_edges(line, vertices)
            if edges == edges_type:
                print(line)

if __name__ == "__main__":
    main(sys.argv[1])

Напише статистику по трикутнихах, і скаже що в нас їх є два види, 30 рівнобедрених і 10 рівносторонніх. Розміри в міліметрах, якщо купол метровий:
1) (0.547, 0.547, 0.618) – 30
2) (0.618, 0.618, 0.618) – 10

А крім того, додасть матеріали, які в позначать в моделі трикутники різного типу різними кольорами. Кожен рядок що починається на f, в об’єктному файлі треба замінити на той який виводиться нашим скриптом. (Разом з usemtl, вони позначають кольори). В файл *.mtl який Blender експортує поряд з об’єктним, треба додати наприклад такі матеріали:

newmtl redMtl
Ns 18
Ka 0.000000 0.000000 0.000000
Kd 1.0 0.5 0.5
Ks 0.8 0.8 0.8
d 1
illum 2

newmtl blueMtl
Ns 1
Ka 0.000000 0.000000 0.000000
Kd 0.5 0.5 1.0
Ks 0.8 0.8 0.8
d 1
illum 2

Kd означає колір дифузного відбиття за Фонгом.

Тепер, якщо ми експортуємо те що відредагували в текстовому редакторі назад в Blender – отримаємо таке:

Бачимо що рівнобедрені трикутники треба з’єднати по 5 штук коротшими сторонами в п’ятикутні піраміди, тоді до країв піраміди причепити ще 5 рівносторонніх трикутників, тоді поміж кожні два трикутники вліпити ще по 5 п’ятикутних пірамід і між ними вліпити решту рівносторонніх трикутників. Або іншим способом. Якщо тепер експортувати розгортку, і при експорті вибрати в налаштуваннях документа “Textures: From materials”, то розгортка теж буде кольоровою:

Кольорова розгортка геокупола

Картонна модель

Тепер, головне аби картону вистачило. Виявилось що вирізати трикутник зі стороною майже 62 см я не можу, бо той картон що я маю вужчий. (Насправді головне висота, але я хотів вмістити більше трикутників, тому вирішив що довша сторона буде 38 см, помножив всі розміри на 380/618, і загалом вийшло таке:

1) (0.336, 0.336, 0.380) – 30
2) (0.380, 0.380, 0.380) – 10

І добре, бо діаметр оригінального купола – 2 метри, того що в мене 1.23м, і він набагато краще вміщується в кімнаті. Для того щоб під купол було легше заходити, я зробив лише 9 рівносторонніх трикутників, таким чином що один в основі відсутній, і поставив купол на стіну висотою 50 см з 9 прямокутних секцій шириною по 38 см. Загалом зі стіною висота в центрі складає 1.11м.

Найбільша технічна складність – як побудувати трикутник без велетенського циркуля. Але виявляється циркуль легко робиться за допомогою вимірювальної рулетки, смужки картону і викрутки. Пробиваємо в смужці викруткою дірку, тоді на відстанях які нам потрібно ще дірки. Фіксуємо центр кола викруткою, і вставляючи ручку в дірку на потрібній відстані малюємо коло потрібного радіусу.

Краї для склеювання клеєм я вирішив не креслити аби зекономити картон і поклався на скотч.

Картонний циркуль і креслення зроблені ним

Далі можна не морочитись і використати перші трикутники як шаблони для креслення решти. Далі процес тривіальний.

Натисніть, щоби переглянути слайдшоу.

Враховуючи те що трикутники я вирізав криво, вони прилягають не щільно і в хатинку потрапляє світло через те що скотч прозорий.

Чи сподобалась дитині хатинка? Ніби так, вона туди пару раз лазила грати “ку-ку”, але здається не настільки як мені. 🙂

Тому що нові іграшки – то тимчасове задоволення, а вміння робити 3d штуки без 3d принтера – то назавжди. 😉

Як написати свій букмарклет?

Букмарклет (bookmark) – це слово створене поєднанням слів bookmark (закладка) і applet (application (застосунок) + зменшувальний суфікс -let). Таким чином правильним перекладом bookmarklet було б “закластуночок” (чи “застокладочка”), але ви цього краще не кажіть Юрку Зеленому. Але я відхиляюсь. Отож, букмарклет, це маленька програма що міститься в закладці.

Як її туди помістили? Ну, виявляється браузери крім протоколів http, ftp, ітп, розуміють такий протокол як javascript. І коли отримують посилання з цим протоколом, при кліку по ньому не змінюють сторінку, а виконують його href як код JavaScript. Щоб побачити приклад простого букмарклету, створіть документ bookmarklet.htm, і помістіть в нього такий код:

<html>
<head><meta charset="utf-8" /></head>
<body>
<a href="javascript:
    alert('Привіт!')
">Мій букмарклет</a>
</body>
</html>

Якщо відкрити його в браузері, ви побачите чисту сторінку з єдиним посиланням Мій букмарклет, яке при кліку буде показувати повідомлення “Привіт!”. Якщо перетягнути це посилання на панель закладок в браузері, то тепер ви зможете в будь-який момент отримати привітання на будь-якій сторінці яку дивитесь, досить лише натиснути букмарклет.

Але це не дуже корисний букмарклет. Щоб він робив щось корисніше, досить замінити код в рядку 5, на якийсь корисніший.

Наприклад є такий сайт – lib.rus.ec. Вони хоч мають домен еквадору – але москалі, і коли відкрити їх сайт, то вискакує повідомлення, яке блокує доступ до сторінки, і просить грошей за чужі книжки. Я переконаний що в цьому випадку дозволяється використати принцип “грабуй награбоване”, і додати в букмарклет такий код:

 document.getElementsByClassName('lsp-overlay')[0].remove()

І тоді при кліку по букмарклету це вікно пропадатиме. Клас, правда? (Не забудьте забрати виклик alert() який ми написали там раніше)

Але давайте зробимо ще щось складніше і корисніше. Давайте зробимо букмарклет, який оформлює посилання на веб-сторінку в форматі вікіпедії cite web, і дає її нам в якомусь вікні. Це такий шаблон, який гарно оформлює посилання, але заповнювати його руками – ще та морока.

Для букмарклетів є кілька правил. Їх код все таки міститься всередині атрибуту елемента HTML, і повинен відповідати стандартам цієї мови. Наприклад всі подвійні лапки """ (ну ви зрозуміли), повинні бути заміненими HTML-кодом ". Крім того, перехід на новий рядок вважається еквівалентним пробілу, тому однорядкові коментарі можливі лише в кінці скрипта:

alert('Це вискочить');
// alert('Це не вискочить бо закоментовано');
alert('Це теж не вискочить бо коментар продовжується');

Ну і тому що ввесь скрипт здається браузеру написаним на одному рядку – бажано шанувати крапки з комою.

Якщо ми хочемо написати великий букмарклет, який можливо використовуватиме jQuery, та інші бібліотеки, нам захочеться їх якось підвантажувати. Але, віднедавна в адміністраторів серверів з’явилась нова можливість збільшувати безпеку вводячи обмеження на джерела з яких на сайт завантажуються ресурси – Content Security Policy, тому такі букмарклети можуть працювати не всюди. Можливо доведеться все переписати, мініфікувати і запакувати, але на всяк випадок, ось вам код підвантаження скрипта:

(function () {
    var script = document.createElement('script');
    script.src='https://gist.githubusercontent.com/bunyk/6ae97e5c3de490cfb4a1/raw/b7fcfde5c40314262893761418a6a25dd6ed0ce8/cite_web.js';
    document.body.appendChild(script);
}());

Подібним чином вже з того скрипта ми можемо підвантажити jQuery та наприклад CSS.

Я тут наковбасив трохи JavaScript, який можливо не на всіх сайтах працює, і не всі дані статті витягує, але на моєму блозі він дає щось подібне до цього:

cite_web

Щоб поставити собі такий, перенесіть оце посилання в закладки: “{{cite web|…“, а тоді в контекстному меню закладки натисніть “Властивості”, чи “Редагувати”, і замініть текст на

(function () {
    var script = document.createElement('script');
    script.src='https://gist.githubusercontent.com/bunyk/6ae97e5c3de490cfb4a1/raw/3102c3e285c6e24a9aed98d1bc7f0b7abeda20fc/cite_web.js';
    document.body.appendChild(script);
}());

Ось код:


Filed under: Кодерство, Павутина Tagged: Лайфхаки, вікіпедія, JavaScript

Кулінкулятор

2014-06-02_23-59-43_642Написав калькулятор для обчислення енергетичної цінності, ціни і терміну зберігання страв що створюються з набору наперед відомих інгредієнтів за всілякими рецептами.

Наприклад рецепт на одну дію:

> print(кукурудза + тунець)
Кукурудза ніжна вакуумована стерилізована і
Консерви рибні. Тунець подрібнений стерилізований.
Ціна: 32.48 грн. Вихід: 470 грам
Енергетична цінність в 100 г продукту: 487.6 кДж
білки - 8.34 г, жири - 1.54 г, вуглеводи 15.79 г. 
Після відкриття зберігати не більше ніж: 48 год

Кожен продукт окрім додавання ще можна множини на скаляр. Але інгредієнти звісно треба задати наперед:

кукурудза = Product(
    bar_code=4824024004211,
    name='Кукурудза ніжна вакуумована стерилізована',
    producer='Бондюель',
    country='HU', 

    price=12.49,
    amount=340, # g
    
    # g per 100g:
    proteins=3.1,
    carbohydrates=21.8,
    fats=1.6,
    energy=505000, # joules

    opening_shelf_life=48, # hours
)

тунець = Product(
    bar_code=4824024002439,
    name='Консерви рибні. Тунець подрібнений стерилізований.',
    producer='Chotiwat manufacturing',
    country='TH', 

    price=19.99,
    amount=130, # g

    # per 100g:
    proteins=22.06,
    carbohydrates=0.07,
    fats=1.4,
    energy=442000, # joules

    opening_shelf_life=48, # hours
)

Але ідеальним для мене буде сайт рецептів, на якому писатиме: макарони в твоєму холодильнику пора буде викинути через 5 годин, тому сьогодні на вечерю ти їстимеш їх. Для повного денного раціону тобі бракує ще 500 грам яблук, найшвидше їх купити буде на Стрийському ринку де вони вчора коштували 8 грн/кг, тому очікуємо що це коштуватиме тобі сьогодні 4 грн.

Або хоча б на вікіпедії й на вікіпідручнику завести такі таблички, щоб кулінарити було легше.


Filed under: Кодерство Tagged: Лайфхаки, Python

To be the best…

To be the best, we gotta pass the test
We gotta make it all the way
To the top of the mountain!

Tenacious D

Пройшло вже більше 10-ти років відколи я вперше побачив IDE Turbo Pascal 7.1. І впевнений що Microsoft житиме і працюватиме щонайменше поки Андерс Гелйсберг в них працюватиме.

Але програмувати мені чомусь досі важко як і тоді. І то, тоді було легше, тому що я не займався цим по 50 годин на тиждень і енергії було більше. Хоча, 10 років – немалий термін, і Пітер Норвіг пише що за 10 років вже можна було б навчитись. Розмірковувати чому в університеті мене нічого не навчили я тут не буду. Досить того що зараз я рухаюсь. Наприклад здається вже осягнув поняття співпрограми (coroutine). А рік тому мав шок – як так, мене в університеті вчили лише про підпрограми та функції, а тут на тобі – підпрограма є лише частковим випадком співпрограми. :)

Зараз інша проблема. На роботі є кінцевий термін виконання проекту. Якось я задав питання “А зробити дешево чи правильно?” і задача на 2 години стала задачею на 3 дні. Вчора знову задав таке дурне запитання. Сьогодні накодив дешево – на нехай мене на рев’ю виховають. Все одно ще всі баги не пофікшені.

Почитав StackOverflow. Щоб стати кращим Python-програмістом треба прочитати Real World Haskell. Цікава ідея, так і зроблю якщо звісно інші ідеї не прийдуть в голову.

І випадково натрапив на взагалі іскрометну історію:

Він був російським програмістом який працював переважно з ассемблером. Він фанатично ставився до економії байтів тому що раніше працював з системами з дуже обмеженою пам’яттю. “Вона була для супутників. Лишк кілька байтів, тому ми перевикористовували кожен байт з кількома цілями.” (побітові маніпуляції, використання байтів інструкцій для їх числових значень). Коли я спробував з’ясувати які саме супутники, я отримав у відповідь лише “Орбітальні супутники. Для запуску на орбіту.”

Він мав дві інші примхи: випукле дзеркало вмонтоване над монітором “Щоб знати хто підглядає”, та нерегулярне випадкове вставання з крісла для того щоб швидко зробити десять віджимань. Він пояснював останнє як “Компілятор знайшов помилку в коді. Це покарання.”

Опукле дзеркало – цікавий приклад совка, але от покарання – можливо має певний з точки зору фізіології сенс.


Filed under: Кодерство, Психософія Tagged: Лайфхаки, люди, психологія, цілі

Журнал роботи в Google Sheets та розширення його функцій з JavaScript

Коли я два тижні тому на роботі признався начальнику що вже все зробив, мій начальник вирішив поділитись мною з іншим начальником. Тепер я не можу все зробити, тому що один начальник проводить по дві наради на день на якій мене питають що я зробив, а інший начальник проводить одну нараду на день, на якій теж питають що я зробив. Крім того треба щодня писати звіти, по кожному проекту окремо, бо це впливає на бюджет проектів і на мою зарплатню. І щоб мені було легше пояснити одному начальнику що я сидів на нараді в іншого, та навпаки, я вирішив записувати що роблю протягом дня. Ну звісно виявилось що не багато роблю, але ви коли-небудь пробували записувати що робите? Якщо більше ніж 5 годин корисної роботи на добу – ви мій герой, навчіть мене.

Власне спочатку я хотів завести табличку по методології Pomodoro. Але, виявилось що Pomodoro – це занадто складно коли тебе три наради на день, якщо не рахувати переписок зі співробітниками в Skype. Тому я вирішив просто записувати час початку виконання і час закінчення.

В Google Sheets є дві корисні комбінації клавіш:

  1. Ctrl + ; – вставляє в клітинку дату
  2. Ctrl + Shift + ; – вставляє в клітинку час. В мене чомусь в форматі AM/PM.

Цього було б досить, але рахувати тривалість задач доводиться вручну. Незручно. Якщо є табличка:

A B C D
1 Time started Task Time finished Duration
2 1:32 PM Project A daily stand-up 1:51 PM 0:19

Хочеться щоб колонка D заповнювалась формулою. Але якщо написати в D2 =C2 - A2, воно видасть: “#VALUE!” – Помилка: не число: 1:51 PM. Тому треба використати функції.

Десь я чув фразочку що електронні таблиці – це функціональна мова програмування. Ах. :) Функцій там купа. Клітинок аби зберігати результати їх роботи – теж досить. Але вчити всі ті функції – ну їх.

На щастя Google Docs можна скриптувати за допомогою JavaScript! Ну й правда, не Visual Basic-ом ж користуватись у браузерному застосунку.

Як ми це робимо? Натискаємо “Інструменти” -> “Редактор сценаріїв”. Відкриється онлайновий редактор коду. Вставляємо туди щось таке:

// (c) Salman A     http://stackoverflow.com/a/1788084/816449
function date_diff(date1, date2) {
  if (date2 < date1) {
    date2.setDate(date2.getDate() + 1);
  }
  return date2 - date1;
}

function fmtmsec(msec) {
  var hh = Math.floor(msec / 1000 / 60 / 60);
  msec -= hh * 1000 * 60 * 60;
  var mm = Math.floor(msec / 1000 / 60);
  msec -= mm * 1000 * 60;
  return hh + ':' + mm;
}

function parsetime(cell) {
    var part = cell.match(/(\d+):(\d+)(?: )?(am|pm)?/i);
    var hh = parseInt(part[1], 10);
    var mm = parseInt(part[2], 10);
    var ap = part[3] ? part[3].toUpperCase() : null;
    if (ap === "AM") {
        if (hh == 12) {
            hh = 0;
        }
    }
    if (ap === "PM") {
        if (hh != 12) {
            hh += 12;
        }
    }
    return new Date(2000, 0, 1, hh, mm, 0);
}

function time_diff(arg1, arg2) {
  try {
    arg1 = parsetime(arg1);
    arg2 = parsetime(arg2);
  } catch (e) {
    return '';
  };
  return fmtmsec(date_diff(arg1, arg2));
};

function test() {
  Logger.log(time_diff('9:12 PM', '10:30 PM'));
}

Даємо модулю назву, на зразок Time diff, зберігаємо. В нашій табличці відкриваємо “Інструменти” -> “Менеджер сценаріїв” та переконуємось що наш сценарій там видно.

Тепер, в клітинці D2 можна писати =time_diff(A2;C2). І не поставте кому між аргументами. Буде синтаксична помилка яку я довго шукав в JavaScript, в той час як виявилось що проблема була в записі формули. І це ніби все.

Так просто що мені аж сподобалось.


Filed under: Інструменти, Кодерство, Павутина Tagged: Лайфхаки, JavaScript, робота