Использование SVG для рисования набросков схем

в 23:26, , рубрики: schematic, sketch, svg, прототипирование, Работа с векторной графикой

Введение

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

Это совсем не сложно: современные браузеры поддерживают язык разметки SVG, с помощью которого в обычном текстовом редакторе можно легко и быстро нарисовать небольшую схему типа:

Sketch of a low-power step-up converter


Далее я опишу процесс рисования и несколько тонкостей, полезных при этом. Ещё раз подчеркну, что большую схему намного проще нарисовать в специализированных приложениях.

Создание наброска

Набросок обычно содержит элементы/блоки и связи между ними, и может быть создан в виде отдельного текстового файла с расширением .svg или напрямую встроен в код веб-страницы.

При рисовании схем удобно использовать определенный порядок действий:

  1. Создать (или скопировать) используемые элементы, например
    горизонтальный диод Шоттки

    <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, который потребуется на следующем шаге.

  2. Расставить элементы на схеме и подписать их. Для этого используется группа из тэга 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>
    ...
    

    При использовании одной строки на элемент получается достаточно наглядная таблица. Координаты подписей в группе — относительные, поэтому для перемещения подписанного элемента достаточно изменить координаты группы.

  3. Нарисовать соединения. Для этого удобно использовать тэг path, который позволяет легко чертить горизонтальные и вертикальные линии.

    Используются в основном следующие команды:

    • M10,5 — начать чертить с точки 10,5
    • h10 — горизонтальная линия, 10 пикселей вправо
    • m30,0 — перепрыгнуть на 30 пикселей вправо
    • v15 — вертикальная линия, 15 пикселей вниз
    • m0,30 — перепрыгнуть на 30 пикселей вниз
    • l-5,-10 — косая линия, 5 пикселей влево и 10 пикселей наверх

  4. Расставить точки над i соединения линий. Ничем не отличается от расстановки элементов, но лучше делать это после того, как все соединения уже нарисованы.
  5. Добавить красивостей по вкусу.

Код и картинка простой схемы
Использование SVG для рисования набросков схем - 2

<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 для рисования набросков схем - 3
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
    <image src="image.svg"/>
    <object data="image.svg" type="image/svg+xml"></object>
    

  • Элементы встроенного изображения проще и быстрее настраиваются через CSS

Где брать изображения элементов

Нарисовать самостоятельно или поискать

При этом иногда получается быстрее нарисовать самостоятельно, чем выбрать подходящий из библиотеки.

Как из .svg получить .pdf, .png, .jpg, и т.д.

Самое простое — открыть .svg в Edge и сохранить изображение как .png. Если браузер не позволяет такого, то можно распечатать .svg на PDF принтер или использовать PrintScreen.

Также есть куча онлайн сервисов, например CloudConvert. К сожалению, не все сервисы правильно обрабатывают прозрачность и/или нестандартные шрифты.

Автор: mickey99

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js