Введение
Эта статья о том, как набросать простенькую схемку из десятка элементов, когда под рукой нет ни Altium'а, ни Orcad'a, ни даже Visio, а Draw.io внезапно сломался.
Это совсем не сложно: современные браузеры поддерживают язык разметки SVG, с помощью которого в обычном текстовом редакторе можно легко и быстро нарисовать небольшую схему типа:

Далее я опишу процесс рисования и несколько тонкостей, полезных при этом. Ещё раз подчеркну, что большую схему намного проще нарисовать в специализированных приложениях, с автоматической трассировкой, мышкой, и прочим WYSIWYG'ом.
При чтении статьи советую представить
Создание наброска
Набросок обычно содержит элементы/блоки и связи между ними, и может быть создан в виде отдельного текстового файла с расширением .svg или напрямую встроен в код веб-страницы.
При рисовании схем удобно использовать определенный порядок действий:
- Создать (или скопировать) используемые элементы, например
горизонтальный диод Шоттки<defs> ... <g width="30" height="10" id="schottky"> <path style="fill:none;stroke:black;stroke-width:1" d="M0.5,5.5 h10 v-5 l10,5 l-10,5 v-5 m7,-4 v-2 h3 v12 h3 v-2 m-3,-4 h10" /> </g> ... </defs>
или
вертикальный резистор<defs> ... <g width="30" height="10" id="resistor"> <path transform="translate(1,0) rotate(90,15,5)" style="fill:none;stroke:black;stroke-width:1" d="M0.5,5.5 h3 l2,-3 l4,6 l4,-6 l4,6 l4,-6 l4,6 l2,-3 h3" /> </g> ... </defs>
Если элементы копируются из разных мест, уже на этом шаге стоит унифицировать их размеры. Вместо непосредственной правки координат для этого проще использовать преобразования (translate,rotate,scaleи т.д.).
Обратите внимание, что определения элементов находятся внутри тэгаdefs, и поэтому пока не отображаются. Каждое определение элемента должно иметь уникальныйid, который потребуется на следующем шаге.
- Расставить элементы на схеме и подписать их. Для этого используется группа из тэга
useдля рисования элемента и одного или нескольких тэговtextдля подписей.
Пример... <g transform="translate(115,45)"><use xlink:href="#resistor"/><text x="20" font-size="10">R1</text><text x="20" y="10" font-size="8">470k</text></g> ...
При использовании одной строки на элемент получается достаточно наглядная таблица. Координаты подписей в группе — относительные, поэтому для перемещения подписанного элемента достаточно изменить координаты группы.
- Нарисовать соединения. Для этого удобно использовать тэг
path, который позволяет легко чертить горизонтальные и вертикальные линии.
Используются в основном следующие команды:
M10,5— начать чертить с точки 10,5h10— горизонтальная линия, 10 пикселей вправоm30,0— перепрыгнуть на 30 пикселей вправоv15— вертикальная линия, 15 пикселей внизm0,30— перепрыгнуть на 30 пикселей внизl-5,-10— косая линия, 5 пикселей влево и 10 пикселей наверх
- Расставить точки
над iсоединения линий. Ничем не отличается от расстановки элементов, но лучше делать это после того, как все соединения уже нарисованы.
- Добавить красивостей по вкусу.
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Определение элементов -->
<defs>
<circle x="0.5" y="0.5" r="1.5" style="fill:blue;stroke:blue;" id="junction"/>
<g width="30" height="10" id="connector">
<path
style="fill:none;stroke:black;stroke-width:1"
d="M10.5,5.5 a3,3,0,0,1,-6,0 a3,3,0,0,1,6,0 h20"
/>
</g>
<g width="30" height="10" id="resistor">
<path
transform="translate(1,0) rotate(90,15,5)"
style="fill:none;stroke:black;stroke-width:1"
d="M0.5,5.5 h3 l2,-3 l4,6 l4,-6 l4,6 l4,-6 l4,6 l2,-3 h3"
/>
</g>
</defs>
<!-- Расстановка элементов -->
<g transform="translate(5, 10)"><use xlink:href="#connector"/><text x="20" font-size="10">+</text> </g>
<g transform="translate(25,40)"><use xlink:href="#resistor"/> <text x="-5" y="10" font-size="10">R1</text></g>
<g transform="translate(5, 70)"><use xlink:href="#connector"/><text x="20" font-size="10">-</text> </g>
<!-- Связи -->
<path d="M35.5,15.5 h5 v15 m0,30 v15 h-5" stroke="red" fill="none"/>
<path d="M40.5,15.5 h25 v60 h-25" stroke="red" fill="none"/>
<!-- Соединения -->
<use xlink:href="#junction" transform="translate(40,15)"/>
<use xlink:href="#junction" transform="translate(40,75)"/>
<!-- Прочее -->
<text x="50" y="88" width="100" text-anchor="middle" font-family="cursive" font-size="10">Сопротивление</text>
<text x="50" y="98" width="100" text-anchor="middle" font-family="monospace" font-size="10">безвредно</text>
</svg><svg width="200" height="150" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Step-up DC-DC converter</desc>
<defs>
<circle x="0.5" y="0.5" r="1.5" style="fill:blue;stroke:blue;" id="junction"/>
<g width="30" height="10" id="connector">
<path
style="fill:none;stroke:black;stroke-width:1"
d="M10.5,5.5 a3,3,0,0,1,-6,0 a3,3,0,0,1,6,0 h20"
/>
</g>
<g width="30" height="10" id="connector180">
<path
transform="translate(1,1) rotate(180,15,5)"
style="fill:none;stroke:black;stroke-width:1"
d="M10.5,5.5 a3,3,0,0,1,-6,0 a3,3,0,0,1,6,0 h20"
/>
</g>
<g width="30" height="10" id="resistor">
<path
transform="translate(1,0) rotate(90,15,5)"
style="fill:none;stroke:black;stroke-width:1"
d="M0.5,5.5 h3 l2,-3 l4,6 l4,-6 l4,6 l4,-6 l4,6 l2,-3 h3"
/>
</g>
<g width="30" height="10" id="capacitor">
<path
transform="translate(1,0) rotate(90,15,5)"
style="fill:none;stroke:black;stroke-width:1"
d="M0.5,5.5 h13 m0,-7 v14 m4,0 v-14 m0,7 h13"
/>
</g>
<g width="30" height="10" id="inductance">
<path
style="fill:none;stroke:black;stroke-width:1"
d="M0.5,5.5 h0.7 a5,10,0,0,1,9.3,5 a5,10,0,0,1,10,0 a5,10,0,0,1,9.3,-5 h0.7"
/>
</g>
<g width="30" height="10" id="schottky">
<path
style="fill:none;stroke:black;stroke-width:1"
d="M0.5,5.5 h10 v-5 l10,5 l-10,5 v-5 m7,-4 v-2 h3 v12 h3 v-2 m-3,-4 h10"
/>
</g>
<g width="40" height="40" id="stepup">
<rect x="0.5" y="0.5" width="40" height="40" style="fill:none;stroke:black;stroke-width:1px;"/>
<text x="2" y="10" font-size="8">VIN</text>
<text x="2" y="37" font-size="8">GND</text>
<text x="25" y="10" font-size="8">SW</text>
<text x="27" y="37" font-size="8">FB</text>
</g>
</defs>
<g transform="translate(0,15)"><use xlink:href="#connector"/><text x="5" y="-5" font-size="10">+Vin</text></g>
<g transform="translate(0,135)"><use xlink:href="#connector"/><text x="5" y="-5" font-size="10">Gnd</text></g>
<g transform="translate(160,15)"><use xlink:href="#connector180"/><text y="-5" font-size="10">+Vout</text></g>
<g transform="translate(160,135)"><use xlink:href="#connector180"/><text y="-5" font-size="10">Gnd</text></g>
<g transform="translate(65,50)"><use xlink:href="#stepup"/><text y="55" font-size="10">SX1308</text></g>
<g transform="translate(115,45)"><use xlink:href="#resistor"/><text x="20" font-size="10">R1</text></g>
<g transform="translate(115,110)"><use xlink:href="#resistor"/><text x="20" font-size="10">R2</text></g>
<g transform="translate(20,75)"><use xlink:href="#capacitor"/><text x="20" font-size="10">C1</text></g>
<g transform="translate(140,75)"><use xlink:href="#capacitor"/><text x="20" font-size="10">C2</text></g>
<g transform="translate(50,15)"><use xlink:href="#inductance"/><text x="10" y="-5" font-size="10">L1</text></g>
<g transform="translate(95,15)"><use xlink:href="#schottky"/><text x="10" y="-5" font-size="10">D1</text></g>
<path d="M30.5,20.5 h20 m30,0 h15 m30,0 h35" stroke="red" fill="none"/>
<path d="M35.5,20.5 v45 m0,30 v45" stroke="red" fill="none"/>
<path d="M130.5,20.5 v15 m0,30 v35 m0,30 v10" stroke="red" fill="none"/>
<path d="M155.5,20.5 v45 m0,30 v45" stroke="red" fill="none"/>
<path d="M45.5,20.5 v35 h20" stroke="red" fill="none"/>
<path d="M50.5,140.5 v-55 h15" stroke="red" fill="none"/>
<path d="M90.5,20.5 v20 h25 v15 h-10" stroke="red" fill="none"/>
<path d="M105.5,85.5 h25" stroke="red" fill="none"/>
<path d="M30.5,140.5 h130" stroke="red" fill="none"/>
<use xlink:href="#junction" transform="translate(35,20)"/>
<use xlink:href="#junction" transform="translate(45,20)"/>
<use xlink:href="#junction" transform="translate(90,20)"/>
<use xlink:href="#junction" transform="translate(130,20)"/>
<use xlink:href="#junction" transform="translate(155,20)"/>
<use xlink:href="#junction" transform="translate(130,85)"/>
<use xlink:href="#junction" transform="translate(35,140)"/>
<use xlink:href="#junction" transform="translate(50,140)"/>
<use xlink:href="#junction" transform="translate(130,140)"/>
<use xlink:href="#junction" transform="translate(155,140)"/>
</svg>
Тонкости рисования
В-общем, рисовать с помощью SVG достаточно просто. Ниже несколько не очень очевидных, но способных сэкономить немного времени деталей.
Почему размываются линии
SVG использует суб-пиксельную точность при рисовании линий. Поэтому для линий толщиной в нечетное число пикселей координаты начала и конца должны располагаться в середине пикселя. Если для рисования использовались только относительные координаты, то можно просто сдвинуть координаты начальной точки на 0.5,0.5. Универсальное решение —
translate(0.5,0.5).Круги или дуги
Круг можно нарисовать тэгом
circle или командой a (от Arc — дуга) тэга path. Если есть выбор, используйте circle.Единственное оправдание рисованию круга с помощью дуг — если хочется нарисовать элемент, содержащий круги, одним единственным тэгом
path. Для этого потребуется две последовательные дуги: ... a3,3,0,0,1,-6,0 a3,3,0,0,1,6,0 ..., где 3 — радиус круга, а 6 — его диаметр.Использование элементов из других файлов
Очень просто — вместо
<use xlink:href="#connector"/> используется <use xlink:href="library.svg#connector"/>.Отдельный файл или встроенный в HTML код
Отдельный файл намного удобнее, но
- если .svg содержит ссылки на другие .svg, то его нужно включать в HTML тэгом
object
<object data="image.svg" type="image/svg+xml"></object>
- если .svg не содержит ссылок на другие .svg, то тэг
imageработает не хуже тэгаobject
<img src="image.svg"/> <object data="image.svg" type="image/svg+xml"></object>
- Элементы встроенного изображения проще и быстрее настраиваются через CSS
Где брать изображения элементов
Нарисовать самостоятельно или поискать
При этом иногда получается быстрее нарисовать самостоятельно, чем выбрать подходящий из библиотеки. Для себя, я начал собирать свою
Как из .svg получить .pdf, .png, .jpg, и т.д.
Самое простое — открыть .svg в Edge и сохранить изображение как .png. Если браузер не позволяет такого, то можно распечатать .svg на PDF принтер или использовать PrintScreen.
Также есть куча онлайн сервисов, например CloudConvert. К сожалению, не все сервисы правильно обрабатывают прозрачность и/или нестандартные шрифты.
Комментарии (17)

sshikov
29.01.2017 15:11+2Пример graphviz нам показывает, что иногда можно вполне просто нарисовать сложные вещи в виде текста.
Главное — придумать подходящий язык, и свалить на программу реально сложные вещи типа автоматического размещения компонентов на листе, трассировку соединений и пр. Но вот это, к сожалению, сделать уже не очень просто.
Shtucer
29.01.2017 20:00Да хотя бы расширить пункт 3, включив туда переработанный пункт 4.
"Нарисовать соединения. Для этого удобно использовать тэг path, который позволяет легко чертить горизонтальные и вертикальные линии."
… тут список команд из алгола…
… вот тут что-то из п.4, например, как-то так:
"Расставить точки соединения линий. Ничем не отличается от расстановки элементов, но лучше знать заранее возможные точки соединений." Но точки соединение, читай In/Out, никак не выделены, пусть это будет технический тэг. И команда построения линии не по координатам, а "от точки A1 до точки B1", где [A;B] — объект, [0..9] — точки соединения. Как-то это стандартизировать… а вдруг уже, просто я не в курсе. А там и до трассеров как-нибудь…
Уйма библиотек в Egale, Fritzin, Proteus и т.д. и все несовместимы, а те вон из гугла — бесполезны чуть менее, чем совсем.
SVG(он же XML, так?) вполне, как мне кажется, подошёл бы для всего этого. Не обязательно выдумывать какой-то новый язык
grossws
29.01.2017 21:32SVG для графики. Пихать в него, например, информацию о сетях (а она необходима для нормальной трассировки, чтобы понимать какие компоненты реально соединены, а где просто линии пересеклись на схеме), — это значит сделать кастомный формат поверх svg, т. к. svg-библиотеки уже не смогут с этим работать: например, разделение одного path на два потребует каких-то дополнительных телодвижений, сообщающих о том, что они всё ещё относятся к одной сети.
Я видел использование подобного подхода в SCADA (TAC Vista): TGML, описывающий мнемосхему, является надмножеством svg, который содержит информацию об обработке событий и биндингах к сигналам реального мира.

Shtucer
29.01.2017 22:34Но есть же в этом svgml вот это вот
https://www.w3.org/TR/SVG2/struct.html#MetadataElement

mickey99
30.01.2017 23:51К счастью, всё это уже сделано, и многократно.
Правда иногда использовать такого монстра проектирования для рисования простой картинки — всё равно, что стрелять из пушки по воробьям.
Я же описываю противоположную крайность. Метафорически, когда дырка нужна, а сверла нет, то и гвоздиком проковырять можно.

pOmelchenko
29.01.2017 19:47По работе приходилось сталкиваться с svg, но всегда через редакторы. Было желание ковырнуть код, но всегда останавливала лень и "ну не серьезно это".
А так остается вставить картинку про "как рисовать сову на svg в блокноте"

user343
29.01.2017 21:20+1"Как натянуть SVG сову на SVG глобус" (ASCII-видео туториал) :)
P.S. а мне нравятся редакторы схем, где можно таскать элементы вместе с присоединёнными к ним "резиновыми" связями.
Простейшие схемы можно не рисовать, а из типовых даташитных и википедийных выдернуть, если допустим назначение резистора нужно кому-то объяснить по его номеру.

kx13
30.01.2017 08:53XML вообще плохой формат для ручной обработки и написания.
И поэтому вручную с помощью него рисовать — самое последнее дело.
Если хочется рисовать графику в текстовом редакторе, то есть более подходящие форматы:
PostSctipt, MetaPost
Которые можно конвертируются в форматы, которые понимают браузеры.
А если надо серьезно рисовать в SVG, то лучше всего Inkscape.

mickey99
30.01.2017 23:57Про самое последнее дело — совершенно согласен. Но если вдруг припрёт, то текстовый редактор найти проще, чем конвертер Постскрипта. Кроме того, документация по SVG кажется мне более полной и понятной.

kx13
31.01.2017 07:23А если кроме текстового редактора, завалялся еще и язык python, то можно использовать библиотеку обертку для рисования, например: svgwrite :)
Думаю и для других языков есть нечто подобное.
ivan386
Вы можете использовать оригинальные SVG изображения в статье опубликовав их на Github Gist.
Aingis
Там используется rawgit, который является сторонним сервисом к Гитхабу. Это не так надёжно, как непосредственно на Github Pages (github.io).
ivan386
Можно и Github Pages и ipfs.io использовать. Но это уже квест по сложнее.
mickey99
Спасибо за подсказку, квест с Github Pages оказался несложным.