generated at
✅@2022-04-13 水理学の教科書をscrapbox書籍にする

作成方法
scrapbox json dataを作るscriptを書いて実行する
入力
Gyazoのpermalinkが入った配列
これは手元にある
uploadするだけ
節見出しとページ番号との対応
ここまでやらなくていいしやる時間もないので、省略したものを作り直す
以前やったときは、冒頭十数ページしか作成できなかったようだ
OCRデータはGyazo OCRから取得してくる
出力
書式は makePage() で決める

2022-04-13
15:41:43 おしまい。あとは読みながら編集する
15:31:09 作成完了
当然だがprivate

見出しとページ番号との対応
ページ番号は表紙含めて0から始める
scriptの都合上、見出しに , が混じらないようにする
index
カバー0
表紙裏1
表紙2
執筆者一覧3
まえがき6
目次7
1.1 水理学とは?8
1.2 本書のねらいと構成9
1.3 次元・単位・有効数字11
1.4 流体の性質15
1.5 流れの分類18
2.1 なぜ「静水力学」を学ぶのか?20
2.2 静水圧23
2.3 浮力とアルキメデスの原理25
2.4 平面と曲面に働く静水圧34
2.5 浮体の安定・不安定43
3.1 水の持つエネルギー45
3.2 ベルヌーイの定理の導出48
3.3 流管におけるエネルギーの流入・流出と仕事の関係による導出51
3.4 ベルヌーイの定理を使ったトリチェリーの定理53
3.5 ベルヌーイの定理の応用例71
4.1 なぜ「運動量保存則」を学ぶのか?73
4.2 運動量保存則の導出76
4.3 運動量保存則の応用93
5.1 はじめに94
5.2 流体運動の記述方法95
5.3 流体運動の基礎方程式100
5.4 ナビエ・ストークスの式,連続式の導出106
5.5 流体の基本運動要素と渦度109
5.6 速度ポテンシャルと流関数112
6.1 層流・乱流の性質114
6.2 レイノルズ数116
6.3 限界レイノルズ数117
6.4 層流の流速分布―円管路の場合120
6.5 乱流とレイノルズ応力123
6.6 乱流の流速分布―壁面近傍の流れ―133
7.1 管路流れとは?134
7.2 管路流れの基礎式136
7.3 摩擦損失140
7.4 摩擦損失以外の損失146
7.5 管路の損失計算157
8.1 開水路流れとは?161
8.2 フルード数と常流・射流165
8.3 比エネルギー168
8.4 開水路の断面変化に伴う水面形175
8.5 比力182
9.1 等流とは?183
9.2 等流における摩擦抵抗185
9.3 平均流速公式191
9.4 等流水深と限界勾配197
10.1 漸変流とは?198
10.2 基礎式202
10.3 基本的な水面形205
10.4 水面形の出現例209
11.1 水理模型実験と相似則210
11.2 幾何学的相似と力学的相似214
11.3 フルード相似則216
11.4 レイノルズ相似則219
演習問題解答237
索引239
出版情報240
広告241
裏表紙242

sh
deno cache -r=https://scrapbox.io https://scrapbox.io/api/code/takker/✅@2022-04-13_水理学の教科書をscrapbox書籍にする/script.ts
script.ts
import { getImage } from "../deno-gyazo/mod.ts"; import { getGyazoToken } from "../scrapbox-userscript-std/rest.ts"; import { useStatusBar } from "../scrapbox-userscript-std/dom.ts"; import { pool, sort } from "../async-lib/mod.ts"; import { upload } from "../scrapbox-file-uploader/mod.ts"; import { makePage, makePageInit } from "./makePage.ts"; import { makeSectionMap } from "./section.ts"; // 読み込み const pending = fetch("/api/table/takker/『第3版_土質力学』/index.csv"); const file = await upload({ accept: "application/json, *.json"}); if (!file) throw new Error("no file specified"); // データ形成 const table = await (await pending).text(); const sectionMap = makeSectionMap(table); const gyazoList = JSON.parse(await file.text()) as string[]; console.info(sectionMap); console.log(gyazoList); type Gyazo = { url: string; text: string; index: number; }; const result = await getGyazoToken(); if (!result.ok) throw new Error(JSON.stringify(result.value)); const accessToken = result.value; if (!accessToken) throw new Error("Could not get the access token"); const { render, dispose } = useStatusBar(); // OCR取得 const reader = pool( 5, Array(gyazoList.length).keys(), async (index: number): Promise<Gyazo> => { const gyazo = gyazoList[index]; const id = gyazo.match(/\/([^\/]+)$/)?.[1]; if (!id) throw new Error(`Could not find Gyazo ID from "${gyazo}"`); const result = await getImage(id, { accessToken }); if (!result.ok) throw new Error(JSON.stringify(result.ok)); const { permalink_url, ocr } = result.value; return { url: permalink_url, text: ocr?.description || "", index, }; }, ); const pages = [] as { title: string; lines: string[]; }[]; const stack = [] as makePageInit[]; let counter = 0; for (const promise of reader) { const result = await promise; if (!result.success) throw new Error(JSON.stringify(result.reason)); const { url, text, index } = result.value; // データを取得しながらページを作る const data = { section: sectionMap[counter], pageNum: counter, text: text, gyazo: url, }; stack.push(data); if (stack.length === 2) { pages.push(makePage({ ...stack[0], nextSection: stack[1].section, })); counter++; } else if (stack.length === 3) { pages.push(makePage({ ...stack[1], prevSection: stack[0].section, nextSection: stack[2].section, })); stack.shift(); counter++; } else { counter++; } render( { type: "spinner" }, { type: "text", text: `${gyazoList.length} images, ${counter} got`, }, ); } dispose(); // 最後のページ pages.push(makePage({ ...stack[1], prevSection: stack[0].section, })); // download console.log(pages); const blob = new Blob([JSON.stringify({ pages })], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "import.json"; a.click(); URL.revokeObjectURL(url);

以下の書式でページを作る
title
${section} ${page}
body
OCRした本文 <=${prev}|${next}=> [[${gyazo_url}]] [${section}]
末尾に節へのリンクをつけて、同じ節のページを一覧できるようにする
編集しやすくするため
makePage.ts
export interface makePageInit { section: string; gyazo: string; text: string; /** ページ番号 */ pageNum: number; prevSection?: string; nextSection?: string; } export const makePage = (init: makePageInit): { title: string; lines: string[]; } => { const { section, gyazo, text, pageNum, prevSection, nextSection, } = init; const title = `${format(section)} ${pageNum}`; const prevTitle = prevSection ? `${format(prevSection)} ${pageNum - 1}` : undefined; const nextTitle = nextSection ? `${format(nextSection)} ${pageNum + 1}` : undefined; return { title, lines: [ title, ...format(text).split("\n"), "", `<=${ prevTitle ? `[${prevTitle}]` : "" }|${ nextTitle ? `[${nextTitle}]` : "" }=>`, "", `[${gyazo}]`, `[${section}]`, ], }; } const format = (text: string): string => text .replace(/\s+$/, "") // 末尾の余計な空白を消す .replace(/^(\s*)・/, "$1 ") // ・を箇条書きに変える .replace( /[A-Za-z0-9]/g, (s) => String.fromCharCode(s.charCodeAt(0) - 0xFEE0), ) // 全角英数を半角に直す .replace(/\s?\[/g, "[") // リンク記法をescapeする .replace(/\s?\[/g, "[") .replace(/(\d+).\s*/g, "$1. ") // 番号付き箇条書きにする .replaceAll(".", "。") // 句読点を変換する .replaceAll(",", "、");

2022-04-17 13:36:12 scrapbox-file-uploaderに移動した

csvから、ページ番号と見出しとの対応配列を作る
section.ts
export const makeSectionMap = (csv: string): string[] => { const data = csv.split("\n").map((row) => row.split(",")); const sections = [] as string[]; for (const [section, end] of data) { while (sections.length <= parseInt(end)) { sections.push(section); } } return sections };

#2022-04-21 12:43:11
#2022-04-17 13:37:16
#2022-04-13 09:08:05