<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>АйТи притчи</title><subtitle>Сказ про IT, языки и компиляторы</subtitle><author><name>АйТи притчи</name></author><id>https://teletype.in/atom/it_tale</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/it_tale?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/it_tale?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-08-12T10:57:16.520Z</updated><entry><id>it_tale:thoughts-on-paths</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/thoughts-on-paths?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Thoughts on Paths</title><published>2026-08-09T13:25:47.627Z</published><updated>2026-08-09T13:25:47.627Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/f1/bc/f1bca035-c516-4803-8c28-f5fc1d83fef5.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/8a/2b/8a2b8ff0-1c06-48a4-a26d-b8e16c2e7231.png&quot;&gt;or what you need to know about using paths in a compiler</summary><content type="html">
  &lt;p id=&quot;Ev6O&quot;&gt;or what you need to know about using paths in a compiler&lt;/p&gt;
  &lt;figure id=&quot;jyfY&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/ea/95/ea954597-81ec-4bc1-a5bd-110a1a296ce2.png&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://blog.ufs-online.ru/recommendations/altai-puteshestvie-v-samoe-serdce-sibiri?amp&amp;amp&quot; target=&quot;_blank&quot;&gt;Алтай. Путешествие в самое сердце Сибири&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;tm3j&quot;&gt;Where It All Begins&lt;/h2&gt;
  &lt;p id=&quot;LqiY&quot;&gt;What does the compiler know about its environment when it starts working?&lt;/p&gt;
  &lt;p id=&quot;fc5m&quot;&gt;Of what it &lt;em&gt;guaranteed&lt;/em&gt; knows, one can only name the path to the binary file that served as the «launcher». How do we know this?&lt;/p&gt;
  &lt;p id=&quot;xtAh&quot;&gt;We know this from a convention that all operating systems I am aware of try to adhere to — at application startup, the path to the executable is passed as an &lt;code&gt;argv&lt;/code&gt; parameter:&lt;/p&gt;
  &lt;p id=&quot;X7PV&quot;&gt;сКопировать&lt;/p&gt;
  &lt;pre id=&quot;h15G&quot;&gt;int main(int argc, char** argv) {
   ...
}&lt;/pre&gt;
  &lt;p id=&quot;2qQ8&quot;&gt;By convention, the zeroth element of the array passes a pointer to a string containing the path by which the program’s executable was launched — in our case, the compiler. Situations where this is not the case are possible, but have not been encountered in practice. Therefore, we proceed from the assumption that the compiler knows this.&lt;/p&gt;
  &lt;p id=&quot;vYiJ&quot;&gt;Furthermore, other elements of this array may pass command-line parameters into the compiler. This information is also guaranteed to be available to the compiler.&lt;/p&gt;
  &lt;p id=&quot;GTQg&quot;&gt;All other information the compiler has to obtain itself.&lt;/p&gt;
  &lt;h2 id=&quot;t89a&quot;&gt;Environment Information&lt;/h2&gt;
  &lt;p id=&quot;IVNT&quot;&gt;What the compiler learns next, it finds out by querying the operating system.&lt;/p&gt;
  &lt;p id=&quot;HcOW&quot;&gt;The information to be discovered can be slightly grouped.&lt;/p&gt;
  &lt;blockquote id=&quot;YbR6&quot;&gt;Here and further below, we speak of the compiler as a program; the issue of code generation and target platforms deserves separate consideration.&lt;/blockquote&gt;
  &lt;ol id=&quot;OsSS&quot;&gt;
    &lt;li id=&quot;GwFe&quot;&gt;Operating System&lt;br /&gt;For the compiler as a tool, knowledge of which OS we started on is not very interesting per se. It needs to be able to work with files and texts, and this, in turn, may depend on the operating system. Obvious among these dependencies is the structure of paths. In Windows, a path begins with a drive letter and a colon, like «C:». Different OSes use different path separators, «\» or «/», and in quite exotic cases, other types of separators might be used, and the way a filename is constructed differs significantly — for instance, the version might be part of the «filename.name; version».&lt;/li&gt;
    &lt;li id=&quot;h5Fe&quot;&gt;Current Directory&lt;br /&gt;It is not always needed, but we can only find out by asking the operating system. This information is already strictly platform-dependent; different platforms use different methods (different functions) to get the current directory.&lt;/li&gt;
    &lt;li id=&quot;GAHv&quot;&gt;Environment Variables&lt;br /&gt;A lot of information in an OS is passed via a list of environment variables. This is a set of «name=value» pairs. Retrieving this list (or its individual elements) is also platform-dependent.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;h2 id=&quot;TT0G&quot;&gt;What the Compiler Needs to Work&lt;/h2&gt;
  &lt;p id=&quot;4hIb&quot;&gt;Let us now enumerate what information the compiler needs to work. I will try to list as much as might be required; depending on the language and runtime properties, this list may change.&lt;/p&gt;
  &lt;p id=&quot;DOBQ&quot;&gt;Moreover, I will mention degenerate cases I have encountered, as they provide a complete picture.&lt;/p&gt;
  &lt;ol id=&quot;eOjd&quot;&gt;
    &lt;li id=&quot;0ufW&quot;&gt;Location of the File Being Compiled&lt;br /&gt;The file we intend to process is usually specified explicitly. That is, the path to this file is typically passed as a command-line parameter, but this is not necessarily so. For example, a compiler might be designed such that search paths matter to it, and only the filename is fed as input, while its location is determined by some predefined rules. Or, alternatively, the compiler might be configured to process a file whose location is hard-coded. In any case, the file (or another entity) with which the compiler’s work begins is the first and necessary piece of information for operation.&lt;/li&gt;
    &lt;li id=&quot;H7E9&quot;&gt;Location of Library Code&lt;br /&gt;The compiler may require a set of entities used for handling the semantics of the language. This is «library» code and cannot be circumvented. It can be either part of the compiler or located externally.For example, Delphi has a module &lt;code&gt;System.pas&lt;/code&gt;, which, if I remember correctly, wasn’t compiled but rather supplied so that «pseudo-definitions» would be available — i.e., entities (e.g., types) were implemented inside the compiler, yet they looked as if declared in a file.Haskell has a module &lt;code&gt;Prelude&lt;/code&gt;, used during the compilation of any module.And so on; depending on the language, the workflow scheme may differ, but it is important to understand that we are talking about mandatory elements of language semantics. The presence of such modules is obligatory, and the compiler must be aware of them.&lt;/li&gt;
    &lt;li id=&quot;GvGR&quot;&gt;Imported Entities&lt;br /&gt;Programs are rarely written without using external modules. Again, this is implemented differently across languages, but the general scheme is roughly the same. When an &lt;code&gt;import&lt;/code&gt; or &lt;code&gt;use&lt;/code&gt; operation appears in the source code (the specific term is not crucial right now), the compiler must go to some code repository and retrieve information about the imported module there.&lt;/li&gt;
    &lt;li id=&quot;zeok&quot;&gt;Cached Imported Entities&lt;br /&gt;Unlike the previous point, here we refer to entities that might have already been compiled and transformed into a form prepared for the final stages of compilation. We are talking about pre-compiled modules. Such entities can take up quite a bit of space and, in some cases, might even be located on a network and downloaded on demand. I did projects like this twice, and both times had to abandon them due to synchronization difficulties, timestamp management, and other technological complexities. However, the refusal was compensated by a general acceleration of compilation. Nevertheless, this group of artifacts must have its own access mechanism (and/or search).&lt;/li&gt;
    &lt;li id=&quot;tsjA&quot;&gt;Tooling&lt;br /&gt;During compilation, not only the compiler’s own tools but also external auxiliary instruments may be used. Many compilers make calls to C/C++ compilers or their components. Calling assemblers, linters, checkers, and other tools is also possible here. And although in interactive mode it is almost always sufficient that these tools can be found via the system &lt;code&gt;PATH&lt;/code&gt;, within CI/CD server builds this becomes highly non-obvious and often requires additional configuration or direct path specification.&lt;/li&gt;
    &lt;li id=&quot;gauv&quot;&gt;Annotation Processors&lt;br /&gt;Java and Lisp have mechanisms allowing intervention in the compilation process using code written in those languages (other languages feature this too, but less explicitly). And although formally one could say this falls under one of the previous definitions, such a tool might have its own setup and path-finding mechanism.&lt;/li&gt;
    &lt;li id=&quot;zKmG&quot;&gt;Target Storage&lt;br /&gt;When compilation ends, the created artifact must be saved somewhere. This place where the file or files are saved can also be configured. For some compilers, saving happens next to the source text; for others, it can be set explicitly, and for still others, the storage of the compiled result is determined by calling a separate tool (&lt;code&gt;jar&lt;/code&gt; in the same Java).&lt;/li&gt;
    &lt;li id=&quot;Mz08&quot;&gt;Secondary Artifacts&lt;br /&gt;Compilation proper usually ends with creating an executable file. But a product rarely consists of just one file. There might also be media files, documentation, data files, UI files, etc. Furthermore, the compiler might include build management mechanisms and be capable of creating what are called «distributions». This, again, can be considered a tool invocation falling under item 5, or it can be part of the compiler, and where the distribution will be saved can and should also be configurable.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;h2 id=&quot;h7uG&quot;&gt;Where To Look?&lt;/h2&gt;
  &lt;p id=&quot;zIoE&quot;&gt;Now let us talk about what the compiler can rely on while doing its job.&lt;/p&gt;
  &lt;p id=&quot;16vX&quot;&gt;I can distinguish three mechanics that a compiler can use for its work: «portable build», «hard path», and «combined path».&lt;/p&gt;
  &lt;h3 id=&quot;B2tc&quot;&gt;Hard Path&lt;/h3&gt;
  &lt;p id=&quot;M5o6&quot;&gt;Let’s start with the simplest, albeit very limited option — the hard path.&lt;/p&gt;
  &lt;p id=&quot;9t5w&quot;&gt;Such a compiler workflow scheme assumes that the entire compiler and all files for operation are located at a known, predefined path.&lt;/p&gt;
  &lt;p id=&quot;wpRR&quot;&gt;This scheme is good because all instrumentation can be reduced to a certain set of constants used everywhere. It is very convenient, allows one not to think about anything, and not to worry about placement.&lt;/p&gt;
  &lt;p id=&quot;ynqT&quot;&gt;Everything works fine until the question of cross-platform development or a significant change in environment arises.&lt;/p&gt;
  &lt;p id=&quot;xJ8b&quot;&gt;Thus, back in the day, Windows systems noticeably complicated writing to disk root folders, which led to breakages in a large number of systems written based on the assumption that they would run from «C:\my_system\».&lt;/p&gt;
  &lt;p id=&quot;tfwb&quot;&gt;Moving to &lt;code&gt;%USERPROFILE%&lt;/code&gt; didn’t change the situation much, as issues with long names and names with spaces began.&lt;/p&gt;
  &lt;p id=&quot;FFFT&quot;&gt;Furthermore, moving such a program to Linux or FreeBSD required non-trivial effort. Moreover, considering the differences between Unix-family operating systems, it often happens that a path exists and is accessible for execution on one OS, while on another it might be absent or present other difficulties, for example, requiring administrative access for normal operation.&lt;/p&gt;
  &lt;p id=&quot;cnMy&quot;&gt;Such a compiler workflow scheme generally has a right to exist, but requires a very clear understanding of the necessity to unify and control the environment in which this compiler operates.&lt;/p&gt;
  &lt;h3 id=&quot;8mO9&quot;&gt;Portable Build&lt;/h3&gt;
  &lt;p id=&quot;jk3o&quot;&gt;This term emerged with the advent of USB drives, where the entire program could fit on that drive or in a separate folder on a hard disk. Such a program requires nothing to work; everything is located in one place.&lt;/p&gt;
  &lt;p id=&quot;GYot&quot;&gt;With compilers, this scheme basically works well, with one small «but». During operation, the program might require a large amount of information, loadable modules, and other active development attributes, which will lead to a significant growth in the assembly contents, making it technologically very inconvenient to maintain.&lt;/p&gt;
  &lt;p id=&quot;duAr&quot;&gt;Here perhaps a joke about the depth of a cave full of npm modules is appropriate, but it’s not really funny at all.&lt;/p&gt;
  &lt;p id=&quot;WAbL&quot;&gt;Therefore, although the scheme is generally viable, once the assembly size exceeds a certain threshold, it becomes burdensome.&lt;/p&gt;
  &lt;h3 id=&quot;8L4b&quot;&gt;Combined Path&lt;/h3&gt;
  &lt;p id=&quot;Q0Qg&quot;&gt;The name of this mechanic is quite conditional.&lt;/p&gt;
  &lt;p id=&quot;4MNh&quot;&gt;One can somehow combine the previous two approaches — place part of the compiler, including possibly some library functions and other artifacts, so that the compiler gains access to them without ever referring to the OS, and place another part so that their usage, on one hand, does not depend on the compiler, and on the other hand, can be easily changed while maintaining operability.&lt;/p&gt;
  &lt;p id=&quot;Ndsk&quot;&gt;For example, in the &lt;code&gt;&lt;a href=&quot;https://github.com/area9innovation/flow9&quot; target=&quot;_blank&quot;&gt;flow9&lt;/a&gt;&lt;/code&gt; language, an interesting mechanic was applied.&lt;/p&gt;
  &lt;p id=&quot;ECYa&quot;&gt;The compiler serves as the foundation for product development, and all products are built around it. So, on the disk in the working folder, we see something like:&lt;/p&gt;
  &lt;pre id=&quot;ywHc&quot;&gt;- flow
- product1
- product2 
...
- productN&lt;/pre&gt;
  &lt;p id=&quot;5zLM&quot;&gt;and from any product folder, one can always call &lt;code&gt;../flow/bin/flowc main.flow&lt;/code&gt;, which will lead to product compilation and obtaining a distribution (I omitted a few details, but that’s essentially how it is).&lt;/p&gt;
  &lt;p id=&quot;6eKl&quot;&gt;Furthermore, if the code says &lt;code&gt;import ds/list;&lt;/code&gt;, the compiler knows it needs to go to the &lt;code&gt;../lib/ds&lt;/code&gt; folder relative to its executable and grab the &lt;code&gt;list.flow&lt;/code&gt; file from there.&lt;/p&gt;
  &lt;p id=&quot;5KTF&quot;&gt;Or, for example, in Go, there are environment variables &lt;code&gt;GOROOT&lt;/code&gt; and &lt;code&gt;GOPATH&lt;/code&gt;, defining, respectively, where the compiler’s binary files are located and where to look for files for compilation.&lt;/p&gt;
  &lt;p id=&quot;jHZ0&quot;&gt;A similar function is performed by the combination of &lt;code&gt;JAVA_HOME&lt;/code&gt;/&lt;code&gt;CLASSPATH&lt;/code&gt; in Java. Although these are not the only variables that can be configured, these two substantially affect the compiler’s functionality.&lt;/p&gt;
  &lt;p id=&quot;GqNr&quot;&gt;At the same time, some files might be located in places with hard-coded paths. Thus, cached imported entities for ECL are stored in &lt;code&gt;/home/user/.cache/common-lisp/ecl-23.9.9-9f27c69d-linux-x64/&lt;/code&gt; and can be deleted at any time.&lt;/p&gt;
  &lt;p id=&quot;gj7x&quot;&gt;Rust, for example, stores imported modules in the &lt;code&gt;~/.cargo&lt;/code&gt; folder.&lt;/p&gt;
  &lt;p id=&quot;LgKP&quot;&gt;And so on.&lt;/p&gt;
  &lt;h2 id=&quot;mfih&quot;&gt;How to Use This&lt;/h2&gt;
  &lt;p id=&quot;obGp&quot;&gt;What can the compiler use to gain access to the required entities?&lt;/p&gt;
  &lt;ul id=&quot;8mqH&quot;&gt;
    &lt;li id=&quot;Jdc7&quot;&gt;path to its own executable file&lt;/li&gt;
    &lt;li id=&quot;vPBJ&quot;&gt;contents of environment variables, &lt;code&gt;GOPATH&lt;/code&gt; or &lt;code&gt;CLASSPATH&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;4fhO&quot;&gt;a config file located at a known address, e.g. &lt;code&gt;~/.sbclrc&lt;/code&gt; or &lt;code&gt;%USERPROFILE%/my.cnf&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;NhhA&quot;&gt;a config file defined within the compiler&lt;/li&gt;
    &lt;li id=&quot;jYOr&quot;&gt;a config file defined in the project&lt;/li&gt;
    &lt;li id=&quot;bbE9&quot;&gt;parameters passed via the command line&lt;/li&gt;
    &lt;li id=&quot;hcuv&quot;&gt;parameters embedded in files during installation (Go does this, for example)&lt;/li&gt;
    &lt;li id=&quot;YJ0w&quot;&gt;dynamically changing parameters, if the system allows it&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;0Ymn&quot;&gt;If several of the listed configuration methods are used, a priority system must definitely be established for them, meaning some should have higher «importance» than others, which will allow resolving conflicts if multiple ways of specifying settings are indicated simultaneously.&lt;/p&gt;
  &lt;h2 id=&quot;ix3X&quot;&gt;Conclusion&lt;/h2&gt;
  &lt;p id=&quot;mCD5&quot;&gt;All of the above is intended so that when working on your next compiler, you have arguments for choosing the layout scheme for parts and libraries.&lt;/p&gt;
  &lt;p id=&quot;jsEL&quot;&gt;The list of used entities and configuration methods depends significantly on incoming requirements, and maximum flexibility is not always required, but limiting oneself to just one thing is usually not worth it either.&lt;/p&gt;

</content></entry><entry><id>it_tale:lisp-format-comma</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/lisp-format-comma?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Вывод элементов списка через запятую</title><published>2025-11-22T11:03:18.380Z</published><updated>2025-11-22T11:03:18.380Z</updated><category term="lisp" label="lisp"></category><summary type="html">Вот регулярно надо, и все время забываю, как это делается.</summary><content type="html">
  &lt;p id=&quot;CHXa&quot;&gt;Вот регулярно надо, и все время забываю, как это делается.&lt;/p&gt;
  &lt;p id=&quot;8MT0&quot;&gt;А просто надо использовать шапку:&lt;/p&gt;
  &lt;pre id=&quot;8PAe&quot;&gt;(format nil &amp;quot;~{~A~^, ~}&amp;quot; (list 1 2 3 ))
&amp;quot;1, 2, 3&amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;Po6b&quot;&gt;Вуаля!&lt;/p&gt;

</content></entry><entry><id>it_tale:ai-smells</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/ai-smells?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>От «творчества» ИИ воняет за километр</title><published>2025-08-24T02:38:47.041Z</published><updated>2025-08-24T02:39:35.316Z</updated><summary type="html">Копия статьи Игоря Мальцева, vc.ru</summary><content type="html">
  &lt;p id=&quot;PCN5&quot;&gt;Копия статьи Игоря Мальцева, &lt;a href=&quot;https://vz.ru/opinions/2025/8/20/1351683.html&quot; target=&quot;_blank&quot;&gt;vz.ru&lt;/a&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;Z2O9&quot;&gt;Можно, конечно, сломать ход истории, подойти и плюнуть на картину Малевича, и это тоже будет актом художественного творчества — акционизмом. Но слабая сторона ИИ в том, что он даже этого сделать никогда не сможет.&lt;/blockquote&gt;
  &lt;p id=&quot;SFaJ&quot;&gt;Нам рассказывают про то, как ИИ стремительно идет в территорию вдохновения, в творчество, в частности в изобразительное искусство и музыку.&lt;/p&gt;
  &lt;p id=&quot;HisZ&quot;&gt;У меня плохие новости для верящих в данный маркетологический прогон.&lt;/p&gt;
  &lt;p id=&quot;9bza&quot;&gt;Публика, которая верит в то, что ИИ может создать оригинальное произведение, просто пребывает в иллюзиях, что искусство — бесконечно. Но это просто от незнания, ничего страшного. Хотя нет. Страшновато.&lt;/p&gt;
  &lt;p id=&quot;yhWX&quot;&gt;Как ИИ на выдаче в поисковике паразитирует на том, что собирали и писали живые профессиональные люди на разных ресурсах, таким образом обкрадывая целые издательские дома, точно так же в изо и музыке ИИ тупо паразитирует на всем наследии творческого человечества. А оно — конечно.&lt;/p&gt;
  &lt;p id=&quot;pw8I&quot;&gt;Ведь вы не задумываетесь о том, что музыка имеет очень жесткие законы, в том числе физические и математические, и все они исследованы до последнего герца (440 Hz — нота «ля»).&lt;/p&gt;
  &lt;p id=&quot;Rofx&quot;&gt;Законы квинто-квартового круга, из которого не вырваться. Тон-тон, полутон, три тона-полутон. Кто учил, тот в курсе. Ведь наш гений не зря писал: «поверил алгеброй гармонию». Вариантов гармонических последовательностей настолько мало, что на самые популярные из них, скажем, в интервалах IV–I-II–V, написаны сотни произведений, начиная с «Канона ре мажор» Иоганна Пахельбеля до «Гимна Советского Союза» и Let it Be группы Beatles. Aerosmith с Green Day не упоминать. Четвертая-первая-вторая минор-пятая. Уныло до невозможности. Бесконечные G — D — Em — C. Но есть нюансы.&lt;/p&gt;
  &lt;p id=&quot;o4GE&quot;&gt;Мощный ИИ напишет вам еще тысячу песенок на IV–I-II–V. И он будет собирать мозаику из аранжировок, звучаний, обрывков мелодий, существующих записанных человеческих голосов, безусловно ограниченных тюремными законами гармонии, и создаст еще одну тонну мусора. Две тонны. То есть он делает то, что малоталантливые живые авторы делали все эти годы — строго в соответствии с законами музтеории и музпрактики, плюс мода, плюс конъюнктура, плюс жрать хочется. Просто ИИ может сделать это быстро и много.&lt;/p&gt;
  &lt;p id=&quot;3HQs&quot;&gt;Собственно — последнее «Евровидение» всё звучало, как продукт ИИ. Симпатичная, правильная полная бессмыслица.&lt;/p&gt;
  &lt;p id=&quot;sy4w&quot;&gt;Любой дурак сыграет в подворотне IV–I-II–V в той или иной тональности. Но только у Пола Маккартни получилась Let It Be. А у Александрова — «Союз нерушимый».&lt;/p&gt;
  &lt;p id=&quot;HozO&quot;&gt;По одной простой причине — в рамках строжайших законов ремесла у них есть душа, которая и есть вселенская загадка.&lt;/p&gt;
  &lt;p id=&quot;Iq1B&quot;&gt;А у цифирок с душой напряженка.&lt;/p&gt;
  &lt;p id=&quot;hYc9&quot;&gt;А ведь живые люди уже переступили строгие законы квинто-квартового круга и создали новые — Венская школа с Шенбергом расправилась с законами, по которым писал Моцарт, и открыла врата Густаву Хольсту, а тот — Black Sabbath и так до полной деконструкции и мелодии, и гармонии, но тоже в новых, очень строгих рамках.&lt;/p&gt;
  &lt;p id=&quot;wf3H&quot;&gt;Всё уже написано и записано человечеством. А кого и это не устраивало — тот Карлхайнц Штокхаузен с Джоном Кейджем. Зато на них выросли все «Крафтверки» Германии и вся электронщина мира.&lt;/p&gt;
  &lt;p id=&quot;2Ja3&quot;&gt;И на всем этом тоже вынужден паразитировать бездушный ИИ. Прекрасный пример — недавний хайп стопроцентной ИИ-группы Velvet Sundown на Spotify. Причем только человеку без ушей и без музыкальной памяти непонятно, что это ИИ.&lt;/p&gt;
  &lt;p id=&quot;SF7z&quot;&gt;Как отличить фальшивые надувные шарики? А они точно такие же, только не радуют.&lt;/p&gt;
  &lt;p id=&quot;XCLn&quot;&gt;ИИ заменит, к их сожалению, сотни тысяч неталантливых живых ремесленников, которые лепят песенки по шаблонам, то есть по тем же самым алгоритмам, потому что он делает то же самое только шустрее. Ну и бог с ними.&lt;/p&gt;
  &lt;p id=&quot;uwhU&quot;&gt;Но потом начнется самое интересное: он, ИИ, переварит все то, что сделано людьми, а потом ему придется переваривать уже то, что создано ИИ.&lt;/p&gt;
  &lt;p id=&quot;Gf3x&quot;&gt;И по всем природным законам — продукт пищеварения, еще раз переваренный, остается все той же субстанцией.&lt;/p&gt;
  &lt;p id=&quot;uIuu&quot;&gt;И поэтому так воняет за километр от «художественного творчества» ИИ. Он вынужден переваривать то, что уже придумано людьми за всю историю художественного творчества. А придумано человеком уже буквально всё, что можно изобразить при помощи материалов — осталась одна Марина Абрамович. Да и та скисла, старушка. Провокаторов и без нее хватает.&lt;/p&gt;
  &lt;p id=&quot;RymU&quot;&gt;С каким-то дикарским восторгом в рамках строгого карго-культа неофиты от нейронок ходят буквально под себя, когда им удается получить смешную картинку Йоды в стиле ван Гога. Или «оживить» картину Васнецова. Да, печальный факт — люди знают обычно всего два-три имени и три-четыре стиля и в рамках своих знаний «творят» при помощи нейронок. Насытившись «приколами», начинают генерить портреты шестипалого себя «в стиле».&lt;/p&gt;
  &lt;p id=&quot;7dbd&quot;&gt;И это невозможно видеть глазами. Они кровоточат.&lt;/p&gt;
  &lt;p id=&quot;Ae3w&quot;&gt;Но если размышлять о будущем «недвижущихся картинок», то там произойдет абсолютно то же, что и в музыке «от ИИ».&lt;/p&gt;
  &lt;p id=&quot;w22q&quot;&gt;Дело в том, что уже довольно давно художественное творчество, как совокупность стилей, подходов, материалов, химических составов, пигментов, цвета и формы, давно себя исчерпало.&lt;/p&gt;
  &lt;p id=&quot;9dUe&quot;&gt;Не случайно же есть идиотское выражение, что мы живем в эпоху постомодерна (выпендрялы еще придумывают всякие новые приставки к корню «модерн») — то есть пережевываем по сотому разу всё, что создали предки — начиная с того, который выскребал на стене пещеры бегущих оленей оленьей же костью.&lt;/p&gt;
  &lt;p id=&quot;rJuj&quot;&gt;Придумано всё. Буквально.&lt;/p&gt;
  &lt;p id=&quot;enOG&quot;&gt;Потому что у изобразительного искусства все те же строгие законы. Все цветовые закономерности исследованы до последнего пантона, все формы и антиформы уже создали Ван Дейк с Джексоном Поллоком (и сто тыщ авторов между ними). А искусство еще живо — хотя бы потому, что есть руки, голова и магия материала.&lt;/p&gt;
  &lt;p id=&quot;sKNA&quot;&gt;ИИ лишен рук, материала — красок, кистей или просто ведра для разливания пигмента по холсту, как у Поллока. А фактура, отражение света и даже запах краски — это все составляющие, без которых изображение становится репродукцией в журнале «Огонек».&lt;/p&gt;
  &lt;p id=&quot;3eAf&quot;&gt;А у ИИ — цифра, танец электронов на мониторе. И даже когда ИИ даст мастеру краску и кисть в руки, чтобы поместить картину на поверхность чего-нибудь, то это будет только робот или даже андроид. То есть ходячий принтер. А на принтере мы и сейчас можем безо всякого ИИ слепить что угодно.&lt;/p&gt;
  &lt;p id=&quot;PDld&quot;&gt;И вот точно так же, как люди, которые пережевывают достижения и открытия прошлого, ИИ обречен тупо пережевывать за людьми, а потом будет уже до мышей добираться — то есть извращенцем-Уроборосом жевать свой собственный хвост.&lt;/p&gt;
  &lt;p id=&quot;zqxC&quot;&gt;Потому что все происходит быстро. Очень быстро. В гигантских объемах. И формальное развитие идет очень быстро. Только развиваться некуда.&lt;/p&gt;
  &lt;p id=&quot;SepM&quot;&gt;Ну хорошо, посчитаете себя остроумным, напишете ИИ промпт: «Нарисуй заседание центробанка в стиле Мэн Рэя и Караваджо одновременно». Вау, как смело.&lt;/p&gt;
  &lt;p id=&quot;DMQf&quot;&gt;А потом ведь люди, профессионально занимающиеся, например, историей живописи, обязательно найдут имя и фамилию какого-нибудь деятеля Веймарской республики, который уже придумал это в 1925 году. За что нацисты выгнали его в Америку.&lt;/p&gt;
  &lt;p id=&quot;jpVw&quot;&gt;Потому что было уже всё. Стоило Малевичу намалевать черный квадрат, как обязательно найдется какой-нибудь Марк Ротко, который нарисует черных, красных и синих прямоугольников штук тридцать и будет продаваться за миллионы и висеть в музеях. Не потому, что публика — дура. А потому, что старик Ротко тоже вложил что-то в идиотский черный квадрат. Наверное, это называется «человеческая душа».&lt;/p&gt;
  &lt;p id=&quot;j3Kr&quot;&gt;Можно, конечно, сломать ход истории, подойти и плюнуть на картину Малевича, и это тоже будет актом художественного творчества — акционизмом. Но слабая сторона ИИ в том, что он даже этого сделать не сможет просто никогда. Даже при помощи андроида.&lt;/p&gt;
  &lt;p id=&quot;kYdd&quot;&gt;Зато что касается «картинок движущихся», то по всему интернету бегают новые гуру, которые рассказывают, как при помощи ИИ делать хайповые видео.&lt;/p&gt;
  &lt;p id=&quot;wJ7x&quot;&gt;— А-а-а! — кричат они — Вышла новая версия Reve 1.0 — вы только посмотрите-посмотрите, вы еще быстрее сделаете видео со старухой и бегемотом и порвете видеоплатформы на миллионы просмотров!&lt;/p&gt;
  &lt;p id=&quot;EmoC&quot;&gt;— Или вот! — Новая эра создания видео и игры Genie — всего за 200 долларов подписки! О-о-о!&lt;/p&gt;
  &lt;p id=&quot;59lf&quot;&gt;Причем с таким видом, как будто все приблуды внутри нейронки разработали они лично, а не анонимные индусы из Кремниевой долины. Как шаманы. Помните, как быстро появились «евангелисты» какого-нибудь «Клабхауса» или НФТ — и куда они все делись?&lt;/p&gt;
  &lt;p id=&quot;cyZP&quot;&gt;Так и здесь — восторг от того, что можно вспомнить самую тупую шутку и сгенерить на нее видео с участием модного реального актера из «Марвела». Эта мысль приводит публику в состояние исступления. И публика платит. Надеется порвать интернет и наконец стать хозяином востребованного контента.&lt;/p&gt;
  &lt;p id=&quot;Fc5y&quot;&gt;Срочно бежим на VEO-3! И будет нам счастье и деньги. Завтра выйдет еще пять инструментов, которые для профессиональной работы пригодны так же, как айфон для свадебной фотографии. А со всех сторон несется — «промпт тебе напишет Чат ДжиПи-Ти — даже не надо думать самому, потом заваливаешь в Мидджорни, а потом в ВЕО ТРИ!».&lt;/p&gt;
  &lt;p id=&quot;3nqz&quot;&gt;Дерзай, вперед! Две старухи на самокате! Уморительно смешно! Естественно, стоит увеличить результат «творчества» хотя бы до размеров ноутбучного монитора, то качество картинки посыплется сразу — видно, что это все отвратный ИИ.&lt;/p&gt;
  &lt;p id=&quot;nHRI&quot;&gt;Именно поэтому говорить о какой-то помощи кинематографу невозможно. Оно, конечно, скоро улучшится, но ровно настолько, чтобы никогда не стать профессиональным. Потому что чайник должен сидеть в своей песочнице и время от времени оплачивать подписку за месяц.&lt;/p&gt;
  &lt;p id=&quot;ox5C&quot;&gt;Это как со смартфонами — у всех есть камеры, кругом миллиарды камер, но когда что-нибудь случается — отовсюду нам показывают только мутные видео и бегущие ноги, и даже простые фотки — абсолютно нечитаемые. Даже неподвижный вулкан со столбом пламени внятно снять никто не может. Так и ИИ-видео — только для заработков единиц на очередном «тубе» или тиктоке, пока миллионы чайников платят за подписки на эти инструменты.&lt;/p&gt;
  &lt;p id=&quot;fBym&quot;&gt;Революция отменяется. Без души ничего не работает.&lt;/p&gt;

</content></entry><entry><id>it_tale:kensington-expert-mouse-trackball-cursor-freeze</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/kensington-expert-mouse-trackball-cursor-freeze?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Иногда надо чистить даже лазерные мыши и трекболы</title><published>2025-05-15T08:59:46.544Z</published><updated>2025-05-15T09:00:03.660Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/55/a9/55a90d96-d087-403e-a7b4-118eac12cc9c.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/7f/03/7f039ef7-3a9e-4a52-972b-9cbb65d12921.png&quot;&gt;Век живи, век учись. Сказ о том, что чистить надо не только мозги и рабочее место</summary><content type="html">
  &lt;p id=&quot;GX1N&quot;&gt;Довольно давно я переложил мышку под левую руку, а потом и вовсе заменил ее на трекбол. Вот такой:&lt;/p&gt;
  &lt;figure id=&quot;K3RG&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/7f/03/7f039ef7-3a9e-4a52-972b-9cbb65d12921.png&quot; width=&quot;1500&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;HRXm&quot;&gt;Это Kensington Expert Mouse Trackball. Все в нем замечательно, но со временем появилась проблема — стал останавливаться курсор мыши. Крутишь шар, а мышка на экране не движется. Лечится перетыканием USB разъема.&lt;/p&gt;
  &lt;p id=&quot;taxg&quot;&gt;Честно говоря, думал деградация электроники. При этом я трекбол регулярно чистил, разумеется, но только снаружи.&lt;/p&gt;
  &lt;p id=&quot;KgEM&quot;&gt;Естественно, что я такой был не один. На Reddit&amp;#x27;е народ жалуется, производителю пишут, на Амазон возвращают.&lt;/p&gt;
  &lt;p id=&quot;iU3L&quot;&gt;Производитель даже патч какой-то выпустил, но для моего устройства он не подошел.&lt;/p&gt;
  &lt;p id=&quot;PMYG&quot;&gt;В итоге, случайно, на одном из форумов (они еще живы!) прочитал о том, что у пользователя трекболы этой марки уже больше 15 лет, и что их просто надо эпизодически &lt;em&gt;разбирать&lt;/em&gt; и чистить.&lt;/p&gt;
  &lt;p id=&quot;677y&quot;&gt;Я тут же, вооружившись отверткой и любопытством (а что мне терять то, хотел уже другой покупать?) разобрал, продул, протер линзу, прочистил все, до чего смог дотянуться и собрал обратно.&lt;/p&gt;
  &lt;p id=&quot;eyvz&quot;&gt;И, о чудо!, устройство перестало терять курсор. Даже, кажется, быстрее работать стало!&lt;/p&gt;
  &lt;p id=&quot;qgm5&quot;&gt;Так что, как раньше мы чистили механические мышки, а то и спиртом их протирали, так и современные лазерные устройства тоже, оказывается, надо чистить механическим способом.&lt;/p&gt;
  &lt;p id=&quot;zRFM&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;cCpG&quot;&gt;Век живи, век учись...&lt;/p&gt;

</content></entry><entry><id>it_tale:tail-recursion</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/tail-recursion?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Оптимизация хвостовой рекурсии в трансляторе</title><published>2024-11-28T14:16:54.726Z</published><updated>2024-11-28T14:16:54.726Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/33/df/33dfe90d-5dec-4848-841e-c9d94e70efb1.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/0a/27/0a272035-a2ea-47d9-8b48-03a620352570.png&quot;&gt;Копия моей статьи 2018 года о некоторых проблемах реализацииTCO в трансляторах.</summary><content type="html">
  &lt;figure id=&quot;ayZI&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/0a/27/0a272035-a2ea-47d9-8b48-03a620352570.png&quot; width=&quot;1250&quot; /&gt;
  &lt;/figure&gt;
  &lt;blockquote id=&quot;Y5TH&quot;&gt;Копия моей статьи 2018 года. В принципе, актуальности не потеряла.&lt;/blockquote&gt;
  &lt;h2 id=&quot;l8Tn&quot;&gt;О чем это вообще?&lt;/h2&gt;
  &lt;p id=&quot;xQ0r&quot;&gt;В ежедневной работе программисты, пишущие на императивных языках, нечасто сталкиваются с рекурсивными алгоритмами. Для функциональных же языков рекурсия — естественный способ записи алгоритмов. И пока эти два мира не пересекаются, мало кто задумывается о том, что рекурсия может быть довольно дорогим удовольствием в вычислительном плане. Нет рекурсии — нет проблем.&lt;/p&gt;
  &lt;p id=&quot;BH3T&quot;&gt;Сложности начинаются тогда, когда приходится отображать функциональный подход на императивный. Так, если есть необходимость организовать компиляцию из функционального языка в императивный, например ML в Java или JavaScript.&lt;/p&gt;
  &lt;p id=&quot;ibfQ&quot;&gt;Проблема рекурсивных вызовов в том, что на вызов тратится место в стеке вызовов, т. к. каждый новый рекурсивный вызов, помимо затрат на исполнение тела функции, дополнительно сохраняет точку возврата из текущей функции.&lt;br /&gt;А в рекурсивных алгоритмах количество вызовов может быть большим или очень большим. Стек вызовов исчерпывается очень быстро. Частичным решением служит увеличение размера стека вызовов, однако практически это слабо помогает.&lt;/p&gt;
  &lt;h2 id=&quot;9hL5&quot;&gt;Что сложного то?&lt;/h2&gt;
  &lt;p id=&quot;gd1c&quot;&gt;Практический размер стека вызовов редко превосходит несколько тысяч элементов. Это не позволяет делать, например, рекурсивный обход больших бинарный деревьев. И если на императивных языках это можно исправить путем переписывания рекурсивного алгоритма в явно циклический алгоритм (обычно за счет существенного его усложнения), то в функциональных языках избавиться от рекурсии бывает сложно или вообще невозможно.&lt;/p&gt;
  &lt;p id=&quot;Skcq&quot;&gt;Перейдем к &lt;s&gt;кошкам&lt;/s&gt; примеру.&lt;/p&gt;
  &lt;p id=&quot;tVrS&quot;&gt;Есть функция &lt;code&gt;for()&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;vVFl&quot;&gt;// Flow9
for (init, predicate, fn) {
    if (predicate(init))
        for (fn(init), predicate, fn)
    else init
}&lt;/pre&gt;
  &lt;p id=&quot;DvJL&quot;&gt;Транслятор переведет ее в Java или JavaScript один в один, никакой магии (приведен код на Javacript):&lt;/p&gt;
  &lt;pre id=&quot;suxP&quot; data-lang=&quot;javascript&quot;&gt;// JavaScript
function for__(init, predicate, fn) {
        if (predicate(init)) {
            return for__(fn(init), predicate, fn);
        } else {
            return init;
        }
    }
}&lt;/pre&gt;
  &lt;p id=&quot;xVlP&quot;&gt;Она даже синтаксически будет выглядеть практически также.&lt;/p&gt;
  &lt;p id=&quot;REAL&quot;&gt;Разумеется, на той же Java эту функцию вполне можно заменить обычным циклом. Но мы же в трансляторе это делаем, нам нужен автоматический перевод, верно?&lt;/p&gt;
  &lt;p id=&quot;pbzK&quot;&gt;Поэтому мы получим рекурсивный вызов. Ну и следом во время выполнения получим исчерпание стека вызовов.&lt;/p&gt;
  &lt;h2 id=&quot;1DiO&quot;&gt;Что делать? Спасите, помогите!&lt;/h2&gt;
  &lt;p id=&quot;bg59&quot;&gt;Чтобы исправить ситуацию и как-то заставить это работать, разработана техника, позволяющая экономить место в стеке вызовов или избавиться от рекурсивных вызовов вообще.&lt;/p&gt;
  &lt;p id=&quot;PQrH&quot;&gt;Мы модифицируем функции таким образом, чтобы изменить рекурсивный вызов на цикл. Это возможно, но, к сожалению, заменить можно не все рекурсивные вызовы. Так, чтобы вызов функции можно было заменить на итерацию цикла, нужно соблюсти несколько условий:&lt;/p&gt;
  &lt;ol id=&quot;ccsG&quot;&gt;
    &lt;li id=&quot;kgk3&quot;&gt;вызов функции должен быть последним в последовательности операций;&lt;/li&gt;
    &lt;li id=&quot;c0v0&quot;&gt;это должен быть именно рекурсивное обращение к функции, без каких-либо дополнительный операций (например, в вычислении факториала, куда уж без него! , выражение n*fac (n-1) нельзя автоматически заменить на циклический вызов);&lt;/li&gt;
    &lt;li id=&quot;60HK&quot;&gt;вызов функции не может быть вложен в замыкание, т. е. в случае с &lt;code&gt;for()&lt;/code&gt; конструкция &lt;code&gt;for(..., \x-&amp;gt;for()..., ...)&lt;/code&gt; не будет заменена на циклическую.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;s4QY&quot;&gt;Выполнения этих условий достаточно, чтобы рекурсивный вызов можно было заменить на итерацию цикла. Из-за первого условия (вызов находится в конце последовательности) это явление получило название &lt;a href=&quot;https://ru.wikipedia.org/wiki/%D0%A5%D0%B2%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%B0%D1%8F_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F&quot; target=&quot;_blank&quot;&gt;«хвостовая рекурсия»&lt;/a&gt;.&lt;/p&gt;
  &lt;p id=&quot;JsuE&quot;&gt;Решение состоит в том, что мы функцию с хвостовой рекурсией заменяем на цикл, в котором производим манипуляции с аргументами функции так, как если бы мы готовили рекурсивный вызов, а затем передаем управление в начало цикла.&lt;/p&gt;
  &lt;p id=&quot;Koiu&quot;&gt;Есть масса способов это сделать, на просторах Интернета можно найти разные решения.&lt;/p&gt;
  &lt;p id=&quot;qqED&quot;&gt;Я покажу два решения, которые успешно применяются в трансляторах.&lt;/p&gt;
  &lt;h3 id=&quot;8iQp&quot;&gt;Решение первое&lt;/h3&gt;
  &lt;p id=&quot;SkDc&quot;&gt;Функция предварительно готовится с помощью вспомогательной операции:&lt;/p&gt;
  &lt;pre id=&quot;t3Sa&quot; data-lang=&quot;javascript&quot;&gt;// JavaScript
function TCO(fn, fn_name) {
    var top_args;
    window[fn_name] = function() {
        var result, old_top_args = top_args;
        top_args = arguments;
        while (top_args !== null) {
            var cur_args = top_args;
            top_args = null;
            result = fn.apply(null, cur_args);
        }
        top_args = old_top_args;
        return result;
    };
    window[&amp;#x27;tc_&amp;#x27; + fn_name] = function() {
        top_args = arguments;
    };
}

TCO((init, predicate, fn) =&amp;gt; {
    if (predicate(init)) {
        return tc_for__(fn(init), predicate, fn);
    } else {
        return init;
    }
}, &amp;#x27;for__&amp;#x27;);&lt;/pre&gt;
  &lt;p id=&quot;25Q2&quot;&gt;Мы запускаем цикл по аргументам функции, вычисляем тело функции, а затем, при необходимости произвести рекурсивный вызов, модифицируем аргументы, с которыми работает тело функции. Модификацией аргументов занимается дополнительная функция, создаваемая в процессе подготовки функции. В приведенном фрагменте это функция, наименование которой начинается с префикса &lt;code&gt;tc_&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;cepX&quot;&gt;Метод работает безупречно, правда медленно. Виной тому использование метода не самого быстрого метода &lt;code&gt;fn.apply()&lt;/code&gt;. В Java используется рефлексия, которая тоже дает падение производительности.&lt;/p&gt;
  &lt;p id=&quot;j4AZ&quot;&gt;Этих недостатков лишен второй метод.&lt;/p&gt;
  &lt;h3 id=&quot;U8fi&quot;&gt;Решение второе&lt;/h3&gt;
  &lt;p id=&quot;CEQB&quot;&gt;Поскольку речь идет о внутренностях транслятора, мы многое знаем о модифицируемой функции. Создадим явный цикл и будем самостоятельно заниматься модификацией аргументов.&lt;/p&gt;
  &lt;pre id=&quot;rFWF&quot; data-lang=&quot;javascript&quot;&gt;// JavaScript
function for__(init, predicate, fn) {
    T: while (true) {
        if (predicate(init)) {
            let $a_ = fn(init);
            let $b_ = predicate;
            let $c_ = fn;
            init = $a_;
            predicate = $b_;
            fn = $c_;
            continue T
        } else {
            return init;
        }
    }
}&lt;/pre&gt;
  &lt;p id=&quot;KRzu&quot;&gt;Для каждого аргумента функции, вызываемой рекурсивно, мы создаем дополнительную переменную, а следующим шагом возвращаем значения в аргументы функции. Дополнительный шаг нужен на случай использования аргументов в сложных выражениях.&lt;/p&gt;
  &lt;p id=&quot;iBuz&quot;&gt;Очевидную оптимизацию с неизменяемыми аргументами (см. &lt;code&gt;$b_ = predicate;&lt;/code&gt;) оставлю пытливому читателю для самостоятельного изучения.&lt;/p&gt;
  &lt;p id=&quot;OV5V&quot;&gt;Поскольку технически рекурсивный вызов функции отсутствует, этот метод существенно производительнее предыдущего. В большинстве случаев это работает хорошо. Однако мы столкнулись с интересной проблемой, заставившей изрядно поломать голову.&lt;/p&gt;
  &lt;h3 id=&quot;DAnB&quot;&gt;Неожиданная проблема&lt;/h3&gt;
  &lt;p id=&quot;nbgu&quot;&gt;Есть функция, в которой хвостовой рекурсивный вызов параметром получает функцию.&lt;/p&gt;
  &lt;p id=&quot;aD4u&quot;&gt;Модификации аргументов в приведенном методе недостаточно, если в рекурсивный вызов уходит замыкание, использующее аргументы исходной функции.&lt;/p&gt;
  &lt;pre id=&quot;yyUa&quot;&gt;// Flow9
main() {
    recursiveFn(1,  \res -&amp;gt; println(&amp;quot;Внешний вызов. Результат = &amp;quot; + int2str(res)));
}

recursiveFn(counter : int, onOK : (int) -&amp;gt; void) -&amp;gt; void {
        println(&amp;quot;Шапка recursiveFn. Counter = &amp;quot; + int2str(counter));

        if (counter &amp;gt; 1) {
                println(&amp;quot;Первая ветка recursiveFn. Counter = &amp;quot; + int2str(counter));
                onOK(counter)
        } else {
                println(&amp;quot;Вторая ветка recursiveFn. Counter = &amp;quot; + int2str(counter));
                recursiveFn(counter + 1, \newCount -&amp;gt; {
                        println(&amp;quot;Внутренний вызов. newCounter = &amp;quot; + int2str(newCount));
                        onOK(newCount);
                });
        }
}&lt;/pre&gt;
  &lt;p id=&quot;PKkZ&quot;&gt;Ключевой момент — вызов функции &lt;code&gt;onOK&lt;/code&gt; после &lt;code&gt;println(&amp;quot;Внутренний вызов. newCounter = &amp;quot; + int2str(newCount))&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;V9fg&quot;&gt;Если мы, что называется, в лоб, преобразуем функцию в JavaScript, результатом будет бесконечная рекурсия.&lt;/p&gt;
  &lt;pre id=&quot;Iy6d&quot; data-lang=&quot;javascript&quot;&gt;// JavaScript
function main() {
    return recursiveFn(1, ((res) =&amp;gt; {
        return println((&amp;quot;Внешний вызов. Результат = &amp;quot; + int2str(res)));
    }));
}

function recursiveFn(counter, onOK) {
    T: while (true) {
        println((&amp;quot;Шапка recursiveFn. Counter = &amp;quot; + int2str(counter)));
        if ((counter &amp;gt; 1)) {
            println((&amp;quot;Первая ветка recursiveFn. Counter = &amp;quot; + int2str(counter)));
            return onOK(counter);
        } else {
            println((&amp;quot;Вторая ветка recursiveFn. Counter = &amp;quot; + int2str(counter)));

            let $a_ = ((counter + 1) | 0);
            let $b_ = ((newCount) =&amp;gt; {
                println((&amp;quot;Внутренний вызов. newCounter = &amp;quot; + int2str(newCount)));
                return onOK(newCount);
            });
            counter = $a_;
            onOK = $b_;
            continue T
        }
    }
}&lt;/pre&gt;
  &lt;p id=&quot;r0Xg&quot;&gt;Неважно что будет стоять после строки &lt;code&gt;println((&amp;quot;Внутренний вызов. newCounter = &amp;quot; + int2str(newCount)))&lt;/code&gt;, &lt;code&gt;onOK&lt;/code&gt; или &lt;code&gt;$b_&lt;/code&gt;. Мы получим бесконечную рекурсию из-за того, что замыкание использует внешнее значение функционального параметра &lt;code&gt;onOK&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;6723&quot;&gt;Проблема заключается в том, что замыкание должно оставаться замыканием, т. е. должно захватывать лексическое окружение. Мы же это самое окружение модифицируем для подмены рекурсии циклом. Решение заключается в честной модификации лексического окружения с передачей модифицированных параметров:&lt;/p&gt;
  &lt;pre id=&quot;grmi&quot; data-lang=&quot;javascript&quot;&gt;// JavaScript
function main() {
    return recursiveFn(1, ((res) =&amp;gt; {
        return println((&amp;quot;Внешний вызов. Результат = &amp;quot; + int2str(res)));
    }));
}

function recursiveFn(counter, onOK) {
    T: while (true) {
        println((&amp;quot;Шапка recursiveFn. Counter = &amp;quot; + int2str(counter)));
        if ((counter &amp;gt; 1)) {
            println((&amp;quot;Первая ветка recursiveFn. Counter = &amp;quot; + int2str(counter)));
            return onOK(counter);
        } else {
            println((&amp;quot;Вторая ветка recursiveFn. Counter = &amp;quot; + int2str(counter)));

            let $_ta = onOK;
            let $a_ = ((counter + 1) | 0);
            let $b_ = ((newCount) =&amp;gt; {
                println((&amp;quot;Внутренний вызов. newCounter = &amp;quot; + int2str(newCount)));
                return $_ta(newCount);
            });
            counter = $a_;
            onOK = $b_;
            continue T
        }
    }
}&lt;/pre&gt;
  &lt;p id=&quot;6Z1j&quot;&gt;Внимание следует обратить на переменную &lt;code&gt;$_ta&lt;/code&gt;, которая уже внутри замыкания используется вместо &lt;code&gt;onOK&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;RIBB&quot;&gt;При трансляции мы модифицируем лексическое окружение для генерации кода замыкания. После генерации тела замыкания лексическое окружение восстанавливается, чтобы не поломать нижележащий код. Это делается тем же способом, что и генерация кода для рабочих серверов (в неотладочном режиме).&lt;/p&gt;
  &lt;p id=&quot;tLGI&quot;&gt;Этот метод трансляции работает корректно и уже используется в боевых системах.&lt;/p&gt;
  &lt;h2 id=&quot;GBvP&quot;&gt;И что, вот так вот и работает?&lt;/h2&gt;
  &lt;p id=&quot;pxEN&quot;&gt;Трансляция хвостовой рекурсии еще споткнулась о ключевые слова. Если посмотреть на определение функции &lt;code&gt;for()&lt;/code&gt; выше, то можно увидеть, что в результирующем коде стоит название &lt;code&gt;for__()&lt;/code&gt;. Транслятор не видел измененное имя и, соответственно, не помечал его как рекурсивный вызов.&lt;/p&gt;
  &lt;p id=&quot;wNN9&quot;&gt;Не рассмотрены остались некоторые сложные случаи вроде взаимной хвостовой рекурсии, однако для &lt;em&gt;практического использования&lt;/em&gt; приведенного решения вполне достаточно.&lt;/p&gt;

</content></entry><entry><id>it_tale:Forth</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/Forth?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Важные языки. Часть 1. Forth</title><published>2024-10-20T17:11:29.226Z</published><updated>2024-10-20T19:19:06.379Z</updated><category term="compilers" label="Компиляторы"></category><summary type="html">4 октября 1957 году Советский Союз запустил в космос первый искусственный спутник земли, а затем 3 ноября 1957 года — второй спутник. Это были события поистине планетарного масштаба! Победа советской научной и технической мысли, результат титанических усилий тысяч ученых и инженеров. Мы по праву гордимся этим достижением и по сей день.</summary><content type="html">
  &lt;p id=&quot;Itlp&quot;&gt;Этой статьей начинаю краткий цикл о трех языках программирования, знакомство с которыми считаю очень важным для любого профессионала в программировании: Форт, Лисп и Оберон.&lt;/p&gt;
  &lt;p id=&quot;nHKO&quot;&gt;Сразу оговорюсь, что я не призываю на этих языках разрабатывать, нужны очень веские причины, чтобы принять такое решение. Но знакомство с ними — важная часть профессионального роста.&lt;/p&gt;
  &lt;h2 id=&quot;Pw71&quot;&gt;Историческая справка&lt;/h2&gt;
  &lt;p id=&quot;ERdr&quot;&gt;4 октября 1957 году Советский Союз запустил в космос первый искусственный спутник земли, а затем 3 ноября 1957 года — второй спутник. Это были события поистине планетарного масштаба! Победа советской научной и технической мысли, результат титанических усилий тысяч ученых и инженеров. Мы по праву гордимся этим достижением и по сей день.&lt;/p&gt;
  &lt;p id=&quot;S1PG&quot;&gt;У конкурирующей же стороны, американцев, еще не было своих спутников (появятся позже), они очень внимательно следили за советскими. Научное сообщество всего мира собирало данные о полете спутников, проводило расчеты и пыталось предсказывать траекторию движения, параллельно подстраивая расчетные методики. Помимо расчета положения спутника (расчета &lt;a href=&quot;https://ru.wikipedia.org/wiki/%D0%AD%D1%84%D0%B5%D0%BC%D0%B5%D1%80%D0%B8%D0%B4%D0%B0&quot; target=&quot;_blank&quot;&gt;эфемерид&lt;/a&gt; и других данных полета), ученые проводили изучение ионосферы Земли, поскольку данные передатчиков, излучающих знаменитое «Бип! Бип!» были известны. На основе наблюдений и расчетов было сделано много научных открытий.&lt;/p&gt;
  &lt;p id=&quot;1qLk&quot;&gt;Занималась этим и Смитсоновская астрофизическая обсерватория, где работал Чарльз Мур, позднее ставший создателем языка Форт.&lt;/p&gt;
  &lt;p id=&quot;PDpw&quot;&gt;Очень любопытным представляется &lt;a href=&quot;https://github.com/it-tale/language-history/blob/main/Forth/IGY_Satellite_Report.pdf&quot; target=&quot;_blank&quot;&gt;отчет&lt;/a&gt; с данными наблюдений за спутниками, а также расчетами, пытающихся предсказать место пролета спутников для проведения их фотосъемки. Это несколько томов многостраничных таблиц, перемежающихся небольшими статьями с расчетами тех или иных характеристик полета или окружающей среды.&lt;/p&gt;
  &lt;p id=&quot;BLge&quot;&gt;На странице 30 отчета (страница 175 в PDF) приведена статья «Predictions for Photographic Satellite Tracking Stations — APO Ephemeris 4» за авторством Чарльза Мура (физика-ассистента) и Дона Лаутмана (математика). В этой статье описывается программа Ephemeris 4 для компьютера IBM EDPM 704, предсказывающая положение спутника для использования следящими станциями.&lt;/p&gt;
  &lt;p id=&quot;LsFK&quot;&gt;На трех листах печатного текста дается сухое описание того, что сейчас жаргонно называется «числодробилка», то есть программа из входного массива данных получает другие данные, непосредственно используемые в экспериментах и практической работе.&lt;/p&gt;
  &lt;p id=&quot;WRfJ&quot;&gt;Как позже сам Мур напишет в статье &lt;a href=&quot;https://github.com/it-tale/language-history/blob/main/Forth/The%20Evolution%20of%20Forth.pdf&quot; target=&quot;_blank&quot;&gt;The Evolution of Forth&lt;/a&gt;, чтобы избежать постоянной перекомпиляции исходных текстов программы, он реализовал простой интерпретатор, читающий входные данные с карт. Это позволило компоновать уравнения для разных спутников без повторной компиляции.&lt;/p&gt;
  &lt;p id=&quot;ip97&quot;&gt;Интерпретатор предоставлял несколько команд и концепций, перекочевавших в современные реализации Форта. Использовалась команда чтения «слов», разделенных пробелами, команда преобразования чисел из внешнего во внутреннее представление (&lt;em&gt;по всей видимости имеется ввиду лексический разбор — прим. авт.&lt;/em&gt;), а также конструкт &lt;code&gt;IF ... ELSE ...&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;b5Tu&quot;&gt;Мур обнаружил, что свободное форматирование кода может быть и более эффективным (код меньше и производительнее), и более надежным в сравнении с распространенным в то время колоночным расположением кода на Фортране.&lt;/p&gt;
  &lt;p id=&quot;tI4C&quot;&gt;После завершения учебы Мур в работе использовал разные языки программирования: ALGOL, JOVIAL, PL/I, FORTRAN и всевозможные ассемблеры, не оставляя, тем не менее, идеи использования интерпретатора, который он «таскал с собой, буквально нося пачку перфокарт» (&lt;em&gt;прямая цитата — прим. авт.&lt;/em&gt;)&lt;/p&gt;
  &lt;p id=&quot;1HGp&quot;&gt;Из языков программирования Мур заимствовал идеи в свой интерпретатор, реализуя с его помощью все более сложные программы. Так появились кросс-ассемблер, примитивный редактор и система управления исходниками. Кроме того, он создал программу для генерации анимированных 3D изображений и шахматную программу, а также игру &lt;em&gt;Spacewar&lt;/em&gt;. В интерпретатор добавился стек значений, переменные, арифметические операции и операции сравнения, возможность задания процедур.&lt;/p&gt;
  &lt;p id=&quot;yMA8&quot;&gt;В это время появляется название FORTH. Оно отсылает к четвертому поколению компьютеров, как тогда называли маленькие компьютеры. Буквой U из слова FOURTH (четвертый) пришлось пожертвовать из-за того, что файловые системы того времени не позволяли использовать имена файлов длиной более 5 символов. Написание Forth (без капитализации) появилось значительно позже, опять же благодаря файловым системам, способным различать строчные и заглавные буквы.&lt;/p&gt;
  &lt;p id=&quot;dky1&quot;&gt;Важной вехой стало появление «словаря». Это отдельная сущность, в которую помещались имена процедур вместе с указателем на код, который их реализует. Фактически это была реализация «&lt;a href=&quot;https://ru.wikipedia.org/wiki/%D0%A8%D0%B8%D1%82%D1%8B%D0%B9_%D0%BA%D0%BE%D0%B4#%D0%9A%D0%BE%D1%81%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%88%D0%B8%D1%82%D1%8B%D0%B9_%D0%BA%D0%BE%D0%B4&quot; target=&quot;_blank&quot;&gt;косвенного шитого кода&lt;/a&gt;», осуществленная за 5 лет до появления этого &lt;a href=&quot;https://github.com/it-tale/language-history/blob/main/Forth/Indirect%20threaded%20code.pdf&quot; target=&quot;_blank&quot;&gt;термина&lt;/a&gt;.&lt;/p&gt;
  &lt;p id=&quot;hbYd&quot;&gt;Наконец, последней концепцией, дополнившей картину Форта стала идея отдельного «стека возвратов», упростившего использование данных и процедур, без необходимости балансировать стек до/после вызова процедуры.&lt;/p&gt;
  &lt;h2 id=&quot;tTb9&quot;&gt;Философия Мура&lt;/h2&gt;
  &lt;p id=&quot;Rypk&quot;&gt;Для Мура Форт был личным ответом на разочарование от современного ему программного обеспечения, которое он называл «Вавилонской башней»:&lt;/p&gt;
  &lt;blockquote id=&quot;0h27&quot;&gt;The software provided with large computers supplies a hierarchy of languages: the assembler defines the language for describing the compiler and supervisor; the supervisor the language for job control; the compiler the language for application programs; the application program the language for its input. The user may not know, or know of, all these languages: but they are there. They stand between him and his computer, imposing their restrictions on what he can do and what it will cost. &lt;br /&gt;&lt;br /&gt;And cost it does, for this vast hierarchy of languages requires a huge investment of man and machine time to produce, and an equally large effort to maintain. The cost of documenting these programs and of reading the documentation is enormous. And after all this effort the programs are still full of bugs, awkward to use and satisfying to no one.&lt;/blockquote&gt;
  &lt;p id=&quot;SFLM&quot;&gt;Вольный перевод:&lt;/p&gt;
  &lt;blockquote id=&quot;Rh4V&quot;&gt;ПО, предоставляемое с большими компьютерами привносит иерархию языков: ассемблер определяет язык для описания компилятора и супервизора; супервизор — языки для управления заданиями; компилятор — язык для приложений; приложения — входной язык для данных. Пользователь может не знать все языки или знать не все языки, но они присутствуют. Языки находятся между пользователем и его компьютером, выставляя собственные ограничения того, что можно делать и ожидаему стоимость решения.&lt;br /&gt;&lt;br /&gt;И эта стоимость обширной иерархии языков требует огромных вложений времени человека и машины для создания, равно как и огромных усилий по поддержанию работоспособности. Огромна и стоимость документирования и изучения документации этих программ. В конечном итоге программы все-равно полны ошибок, неудобны в использовании и никому не нравятся.&lt;/blockquote&gt;
  &lt;p id=&quot;hLqZ&quot;&gt;С религиозной настойчивостью Мур отстаивал принцип «Keep it simple!», считая его «Основополагающим принципом». Не без оснований он считал, что для многих задач Форта достаточно. &lt;/p&gt;
  &lt;p id=&quot;UGiB&quot;&gt;С этим можно спорить, но Мур был последователен в отстаивании своих принципов. Он реализовал Форт для 18 разных ЦПУ, писал весь требуемый инструментарий, включая кросс-ассемблеры, драйверы и т.д.&lt;/p&gt;
  &lt;p id=&quot;dTZx&quot;&gt;Мур был очень требователен к ПО, которое он разрабатывал. Многократно перерабатывая и улучшая Форт, он добивался того, что система выполняла требуемые функции, оставаясь при этом маленькой, эффективной и гибкой.&lt;br /&gt;Гибкость Форта вместе с его интерпретируемой природой показали огромную эффективность и впечатляющие результаты во многих приложениях.&lt;/p&gt;
  &lt;h2 id=&quot;u14G&quot;&gt;Важность Форта&lt;/h2&gt;
  &lt;p id=&quot;humm&quot;&gt;Форт очень простой язык, и ценность его для программиста состоит в том, что он позволяет практически дотронуться до нескольких концепций, лежащих в его основе.&lt;/p&gt;
  &lt;h3 id=&quot;lGuT&quot;&gt;Интерпретация&lt;/h3&gt;
  &lt;p id=&quot;VssJ&quot;&gt;Код на Форте интерпретируется. Каждое «слово» на языке исполняется либо кодом внутри интерпретатора, либо комбинацией других «слов». Понимание этого механизма позволяет лучше разбираться в системах, которые используют подобные техники. Так, то, что сейчас называется intrinsic в виртуальных машинах или в других системах, где есть сколь-нибудь сложный рантайм, корнями уходят в том числе в Форт.&lt;/p&gt;
  &lt;h3 id=&quot;Wr7X&quot;&gt;Стек значений&lt;/h3&gt;
  &lt;p id=&quot;66mz&quot;&gt;Использование стека для значений выражений с непривычки может показаться неудобным. Однако, подобная структура данных позволяет очень компактно и элегантно реализовывать весьма непростые вещи. Кроме того, алгоритмы для работы со стеком очень легко генерировать. Поэтому нередко виртуальные машины для высокоуровневых языков реализуются именно как &lt;a href=&quot;https://habr.com/ru/companies/intel/articles/254793/&quot; target=&quot;_blank&quot;&gt;стековые машины&lt;/a&gt;.&lt;/p&gt;
  &lt;h3 id=&quot;nmUt&quot;&gt;Словари&lt;/h3&gt;
  &lt;p id=&quot;hIim&quot;&gt;Структура, отображающая имя в какую-то другую сущность, сегодня, кажется, проникла во все языки программирования в том или ином виде, однако, в Форте она имеет особое значение. Словарь является более высокоуровневой сущностью, по сравнению со «словом». Он может быть наделен своей семантикой и в программе может быть разное количество словарей.&lt;/p&gt;
  &lt;h3 id=&quot;bt2O&quot;&gt;Простота реализации&lt;/h3&gt;
  &lt;p id=&quot;9rCk&quot;&gt;Интерпретатор Форт можно реализовать за один-два вечера. Причем это будет полноценная реализация.&lt;/p&gt;
  &lt;h2 id=&quot;58cD&quot;&gt;Современные реализации&lt;/h2&gt;
  &lt;p id=&quot;xpoZ&quot;&gt;Сегодня сам по себе Форт используется нечасто. Однако идеи этого языка и того, как он реализован используются очень много.&lt;/p&gt;
  &lt;p id=&quot;sScl&quot;&gt;Так, PDF и PostScript — это прямые потомки языка Форт. Во FreeBSD с версии 3.1 до версии 12 в загрузчике использовался интерпретатор Forth (позже заменен на Lua). Уже упомянутые стековые виртуальные машины используют идеи Форта.&lt;/p&gt;
  &lt;p id=&quot;fKAY&quot;&gt;TON Blockchain внутри использует Форт-подобный язык и стековую машину.&lt;/p&gt;
  &lt;p id=&quot;srcW&quot;&gt;Не могу не упомянуть российскую разработку — &lt;a href=&quot;https://github.com/rufig/spf&quot; target=&quot;_blank&quot;&gt;SP-Forth&lt;/a&gt; и разработку российского разработчика по рождению Славы Пестова &lt;a href=&quot;https://factorcode.org/&quot; target=&quot;_blank&quot;&gt;Factor&lt;/a&gt;.&lt;/p&gt;
  &lt;p id=&quot;uxVQ&quot;&gt;Надеюсь, эта информация поможет расширить ваш программный инструментарий!&lt;/p&gt;

</content></entry><entry><id>it_tale:svg-drawing-3</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/svg-drawing-3?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Чертежи в SVG формате. Часть 3 — Черновик стандарта</title><published>2024-06-05T11:21:11.521Z</published><updated>2024-06-05T11:21:11.521Z</updated><summary type="html">&lt;img src=&quot;https://img2.teletype.in/files/12/27/1227f37d-61ba-4a8e-96e5-aa0b38719897.png&quot;&gt;В &quot;Чертежи в SVG формате. Часть 2 — Черновик стандарта&quot; приведён пример рисование простых графических объектов из CAD систем. В продолжении рассмотрим рисование штриховки для разных материалов.</summary><content type="html">
  &lt;p id=&quot;IbBI&quot;&gt;В &amp;quot;Чертежи в SVG формате. Часть 2 — Черновик стандарта&amp;quot; приведён пример рисование простых графических объектов из CAD систем. В продолжении рассмотрим рисование штриховки для разных материалов.&lt;/p&gt;
  &lt;h3 id=&quot;Kk4Z&quot;&gt;Штриховка&lt;/h3&gt;
  &lt;p id=&quot;PzkZ&quot;&gt;Шаблон штриховки рисуем в &lt;code&gt;defs&lt;/code&gt; части чертежа используя тег &lt;code&gt;pattern&lt;/code&gt;.&lt;br /&gt;Шаблон для штриховки металла (тип &lt;code&gt;0&lt;/code&gt;) под углом &lt;code&gt;45°&lt;/code&gt;.&lt;/p&gt;
  &lt;pre id=&quot;QyIX&quot;&gt;&amp;lt;pattern id=&amp;quot;hatch0_45&amp;quot; width=&amp;quot;20&amp;quot; height=&amp;quot;20&amp;quot; patternUnits=&amp;quot;userSpaceOnUse&amp;quot;&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;1&amp;quot; y1=&amp;quot;20&amp;quot; x2=&amp;quot;20&amp;quot; y2=&amp;quot;1&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;0&amp;quot; y1=&amp;quot;1&amp;quot; x2=&amp;quot;1&amp;quot; y2=&amp;quot;0&amp;quot; /&amp;gt;
&amp;lt;/pattern&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;TJab&quot;&gt;Многие заметят и зададут вопрос, зачем нарисовано две линии вместо одной с координатами &lt;code&gt;x1=&amp;quot;0&amp;quot; y1=&amp;quot;20&amp;quot; x2=&amp;quot;20&amp;quot; y2=&amp;quot;0&amp;quot;&lt;/code&gt;?&lt;br /&gt;Пример штриховки с одной линией в шаблоне&lt;/p&gt;
  &lt;figure id=&quot;tIQU&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/12/27/1227f37d-61ba-4a8e-96e5-aa0b38719897.png&quot; width=&quot;337&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;GeV9&quot;&gt;если внимательно присмотреться то увидим разрывы в линии штриховки.&lt;br /&gt;Пример штриховки с двумя линиями в шаблоне&lt;/p&gt;
  &lt;figure id=&quot;gMtX&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fb/b5/fbb5b282-345d-4931-9110-f414c260edb2.png&quot; width=&quot;342&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;khtc&quot;&gt;с двумя линиями таких разрывов нет.&lt;/p&gt;
  &lt;p id=&quot;kjsV&quot;&gt;Фрагмент кода области заштрихованной типом металл под углом &lt;code&gt;45°&lt;/code&gt;.&lt;/p&gt;
  &lt;pre id=&quot;N5IK&quot;&gt;&amp;lt;path d=&amp;quot;M475,225 L475,325 L500,325 L500,225 Z&amp;quot; fill=&amp;quot;url(#hatch0_45)&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;jsEx&quot;&gt;Шаблон для штриховки металла (тип &lt;code&gt;0&lt;/code&gt;) под углом &lt;code&gt;30°&lt;/code&gt;:&lt;/p&gt;
  &lt;figure id=&quot;AOSX&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/8c/52/8c52fd36-9533-4dec-965e-31664c0399a3.png&quot; width=&quot;391&quot; /&gt;
  &lt;/figure&gt;
  &lt;pre id=&quot;96GH&quot;&gt;&amp;lt;pattern id=&amp;quot;hatch0_30&amp;quot; width=&amp;quot;34&amp;quot; height=&amp;quot;20&amp;quot; patternUnits=&amp;quot;userSpaceOnUse&amp;quot;&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;2&amp;quot; y1=&amp;quot;20&amp;quot; x2=&amp;quot;34&amp;quot; y2=&amp;quot;1&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;-1&amp;quot; y1=&amp;quot;2&amp;quot; x2=&amp;quot;3&amp;quot; y2=&amp;quot;-1&amp;quot; /&amp;gt;
&amp;lt;/pattern&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;ZIMZ&quot;&gt;Примеры штриховки других материалов:&lt;/p&gt;
  &lt;figure id=&quot;4EIN&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/98/13/98133a30-bda8-43cf-96f3-96f1e8ba4d14.png&quot; width=&quot;359&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;V7eb&quot;&gt;Шаблон для штриховки неметалла (тип &lt;code&gt;1&lt;/code&gt;) под углом &lt;code&gt;45°&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;3z1E&quot;&gt;&amp;lt;pattern id=&amp;quot;hatch1_45&amp;quot; width=&amp;quot;34&amp;quot; height=&amp;quot;20&amp;quot; patternUnits=&amp;quot;userSpaceOnUse&amp;quot;&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025 &amp;quot; x1=&amp;quot;0&amp;quot; y1=&amp;quot;20&amp;quot; x2=&amp;quot;20&amp;quot; y2=&amp;quot;0&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025 &amp;quot; x1=&amp;quot;0&amp;quot; y1=&amp;quot;0&amp;quot; x2=&amp;quot;20&amp;quot; y2=&amp;quot;20&amp;quot; /&amp;gt;
&amp;lt;/pattern&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;dtfU&quot;&gt;Шаблон для штриховки дерева (тип &lt;code&gt;2&lt;/code&gt;), в таком варианте для каждой области штриховки необходимо создавать свой шаблон. Пока другого варианта не придумал, как нарисовать окружности и габарит шаблона больше от области заштриховывания. Может кто-то удачнее решение придумает.&lt;/p&gt;
  &lt;pre id=&quot;ef8M&quot;&gt;&amp;lt;pattern id=&amp;quot;hatch2_45&amp;quot; width=&amp;quot;350&amp;quot; height=&amp;quot;350&amp;quot; patternUnits=&amp;quot;userSpaceOnUse&amp;quot;&amp;gt;
    &amp;lt;circle class=&amp;quot;line-type-2_025&amp;quot; cx=&amp;quot;0&amp;quot; cy=&amp;quot;0&amp;quot; r=&amp;quot;180&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;
    &amp;lt;circle class=&amp;quot;line-type-2_025&amp;quot; cx=&amp;quot;0&amp;quot; cy=&amp;quot;0&amp;quot; r=&amp;quot;200&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;
    &amp;lt;circle class=&amp;quot;line-type-2_025&amp;quot; cx=&amp;quot;0&amp;quot; cy=&amp;quot;0&amp;quot; r=&amp;quot;220&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;
    &amp;lt;circle class=&amp;quot;line-type-2_025&amp;quot; cx=&amp;quot;0&amp;quot; cy=&amp;quot;0&amp;quot; r=&amp;quot;240&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;
    &amp;lt;circle class=&amp;quot;line-type-2_025&amp;quot; cx=&amp;quot;0&amp;quot; cy=&amp;quot;0&amp;quot; r=&amp;quot;260&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;
    &amp;lt;circle class=&amp;quot;line-type-2_025&amp;quot; cx=&amp;quot;0&amp;quot; cy=&amp;quot;0&amp;quot; r=&amp;quot;280&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;
    &amp;lt;circle class=&amp;quot;line-type-2_025&amp;quot; cx=&amp;quot;0&amp;quot; cy=&amp;quot;0&amp;quot; r=&amp;quot;300&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;
    &amp;lt;circle class=&amp;quot;line-type-2_025&amp;quot; cx=&amp;quot;0&amp;quot; cy=&amp;quot;0&amp;quot; r=&amp;quot;320&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;    
&amp;lt;/pattern&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;CdLS&quot;&gt;Шаблон для штриховки камня естественного (тип 3) под углом 45°&lt;/p&gt;
  &lt;pre id=&quot;TxqC&quot;&gt;&amp;lt;pattern id=&amp;quot;hatch3_45&amp;quot; width=&amp;quot;20&amp;quot; height=&amp;quot;20&amp;quot; patternUnits=&amp;quot;userSpaceOnUse&amp;quot;&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025 &amp;quot; x1=&amp;quot;0&amp;quot; y1=&amp;quot;20&amp;quot; x2=&amp;quot;12&amp;quot; y2=&amp;quot;8&amp;quot; /&amp;gt;
&amp;lt;/pattern&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;b0BV&quot;&gt;Шаблон для штриховки керамики (тип 4) под углом 45°&lt;/p&gt;
  &lt;pre id=&quot;iv1u&quot;&gt;&amp;lt;pattern id=&amp;quot;hatch4_45&amp;quot; width=&amp;quot;20&amp;quot; height=&amp;quot;20&amp;quot; patternUnits=&amp;quot;userSpaceOnUse&amp;quot;&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;0&amp;quot; y1=&amp;quot;16&amp;quot; x2=&amp;quot;16&amp;quot; y2=&amp;quot;0&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;4&amp;quot; y1=&amp;quot;20&amp;quot; x2=&amp;quot;20&amp;quot; y2=&amp;quot;4&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;0&amp;quot; y1=&amp;quot;4&amp;quot; x2=&amp;quot;4&amp;quot; y2=&amp;quot;0&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;16&amp;quot; y1=&amp;quot;20&amp;quot; x2=&amp;quot;20&amp;quot; y2=&amp;quot;16&amp;quot; /&amp;gt;
&amp;lt;/pattern&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;7xLS&quot;&gt;Шаблон для штриховки бетона (тип 5) под углом 45°&lt;/p&gt;
  &lt;pre id=&quot;fkN5&quot;&gt;&amp;lt;pattern id=&amp;quot;hatch5_45&amp;quot; width=&amp;quot;20&amp;quot; height=&amp;quot;20&amp;quot; patternUnits=&amp;quot;userSpaceOnUse&amp;quot;&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;0&amp;quot; y1=&amp;quot;30&amp;quot; x2=&amp;quot;6&amp;quot; y2=&amp;quot;24&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;10&amp;quot; y1=&amp;quot;20&amp;quot; x2=&amp;quot;26&amp;quot; y2=&amp;quot;4&amp;quot; /&amp;gt;
&amp;lt;/pattern&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;QBpt&quot;&gt;Шаблон для штриховки стекла (тип 6) под углом 45°&lt;/p&gt;
  &lt;pre id=&quot;nlMr&quot;&gt;&amp;lt;pattern id=&amp;quot;hatch6_45&amp;quot; width=&amp;quot;30&amp;quot; height=&amp;quot;30&amp;quot; patternUnits=&amp;quot;userSpaceOnUse&amp;quot;&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;5&amp;quot; y1=&amp;quot;15&amp;quot; x2=&amp;quot;13&amp;quot; y2=&amp;quot;7&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;5&amp;quot; y1=&amp;quot;25&amp;quot; x2=&amp;quot;25&amp;quot; y2=&amp;quot;5&amp;quot; /&amp;gt;
    &amp;lt;line class=&amp;quot;line-type-2_025&amp;quot; x1=&amp;quot;17&amp;quot; y1=&amp;quot;23&amp;quot; x2=&amp;quot;25&amp;quot; y2=&amp;quot;15&amp;quot; /&amp;gt;
&amp;lt;/pattern&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;22rW&quot;&gt;В случаи когда необходимо в заштрихованной области иметь не заштрихованную можно применить такое решение.&lt;/p&gt;
  &lt;pre id=&quot;rOwn&quot;&gt;&amp;lt;path class=&amp;quot;line-type-2&amp;quot; d=&amp;quot;M0,575L0,600 15,675 100,650 250,675 300,575z&amp;quot;/&amp;gt;
&amp;lt;path d=&amp;quot;M0,575L0,600 15,675 100,650 250,675 300,575z&amp;quot; fill=&amp;quot;url(#hatch0_45)&amp;quot;/&amp;gt;
&amp;lt;circle class=&amp;quot;line-type-2&amp;quot; cx=&amp;quot;150&amp;quot; cy=&amp;quot;625&amp;quot; r=&amp;quot;40&amp;quot;/&amp;gt;
&amp;lt;circle cx=&amp;quot;150&amp;quot; cy=&amp;quot;625&amp;quot; r=&amp;quot;40&amp;quot; fill=&amp;quot;white&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;fZFe&quot;&gt;Область окружности накладывается на заштрихованную область.&lt;/p&gt;
  &lt;figure id=&quot;kj6d&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/61/b3/61b3b767-2a48-46d5-b3a1-071d8c0431fa.png&quot; width=&quot;208&quot; /&gt;
  &lt;/figure&gt;

</content></entry><entry><id>it_tale:svg-drawing-2</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/svg-drawing-2?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Чертежи в SVG формате. Часть 2 — Черновик стандарта</title><published>2024-05-30T12:07:41.488Z</published><updated>2024-05-30T12:07:41.488Z</updated><summary type="html">&lt;img src=&quot;https://img2.teletype.in/files/5f/48/5f48160a-7c85-45f3-93a8-e6c5a7cf7efd.png&quot;&gt;Рассмотрим рисование простых графических объектов из CAD систем.</summary><content type="html">
  &lt;p id=&quot;IiWo&quot;&gt;В &amp;quot;&lt;a href=&quot;https://teletype.in/@it_tale/svg-drawing-1&quot; target=&quot;_blank&quot;&gt;Чертежи в SVG формате. Часть 1 — Черновик стандарта&lt;/a&gt;&amp;quot; приведён пример объектной модели чертежа, задания масштаба и стилей линий. В продолжении рассмотрим рисование простых графических объектов из CAD систем.&lt;/p&gt;
  &lt;h2 id=&quot;00pU&quot;&gt;&lt;strong&gt;Простые графические объекты&lt;/strong&gt;&lt;/h2&gt;
  &lt;p id=&quot;FAen&quot;&gt;В CAD системах используются следующие простые графические объекты:&lt;/p&gt;
  &lt;h3 id=&quot;1syM&quot;&gt;&lt;strong&gt;Точка&lt;/strong&gt; (маркер)&lt;/h3&gt;
  &lt;figure id=&quot;Vc6x&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/5f/48/5f48160a-7c85-45f3-93a8-e6c5a7cf7efd.png&quot; width=&quot;413&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;NxkN&quot;&gt;Шаблон и стили отображения точки мы рисуем в &lt;strong&gt;defs&lt;/strong&gt; части чертежа.&lt;/p&gt;
  &lt;pre id=&quot;ez08&quot;&gt;&amp;lt;marker id=&amp;quot;Point0&amp;quot; viewBox=&amp;quot;-10 -10 20 20&amp;quot; markerWidth=&amp;quot;5&amp;quot; markerHeight=&amp;quot;5&amp;quot;&amp;gt;
  &amp;lt;path d=&amp;quot;M-3,0 L0,-3 3,0 0,3 z&amp;quot; stroke=&amp;quot;red&amp;quot; fill=&amp;quot;none&amp;quot;/&amp;gt;
&amp;lt;/marker&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;xnsk&quot;&gt;Выводим точку в чертеже с помощью тега &lt;strong&gt;path&lt;/strong&gt;. В свойстве &lt;strong&gt;stroke-width&lt;/strong&gt; задаём обратный масштаб вида, если масштаб вида &lt;code&gt;1:4&lt;/code&gt;, то для точки указываем масштаб &lt;code&gt;4:1&lt;/code&gt;.&lt;/p&gt;
  &lt;pre id=&quot;07qW&quot;&gt;&amp;lt;path stroke-width=&amp;quot;4&amp;quot; d=&amp;quot;M50,35&amp;quot; marker-end=&amp;quot;url(#Point0)&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;h3 id=&quot;kZSs&quot;&gt;Линия&lt;/h3&gt;
  &lt;figure id=&quot;yzDm&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/e2/4e/e24e83e6-057a-49fb-ac2f-aaa14c2ca04d.png&quot; width=&quot;263&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;NOES&quot;&gt;В CSS файле описываем типы линий.&lt;/p&gt;
  &lt;pre id=&quot;GGj3&quot;&gt;line, rect, circle, ellipse, path, text {
  vector-effect: non-scaling-stroke;
}
/* основная */
.line-type-1 {
  fill: none;
  stroke: blue;
  stroke-width: 2;
}
/* тонкая */
.line-type-2 {
  fill: none;
  stroke: black;
  stroke-width: .7;
}
/* осевая */
.line-type-3 {
  fill: none;
  stroke: red;
  stroke-width: .7;
  stroke-dasharray: 25, 4, 3, 4;
}
/* штриховая */
.line-type-4 {
  fill: none;
  stroke: black;
  stroke-width: .7;
  stroke-dasharray: 7, 4;
}
...&lt;/pre&gt;
  &lt;p id=&quot;vhof&quot;&gt;А в чертеже используем тег &lt;strong&gt;line&lt;/strong&gt; с присваиванием ему класса типа линии.&lt;/p&gt;
  &lt;pre id=&quot;7YAI&quot;&gt;&amp;lt;line class=&amp;quot;line-type-1&amp;quot; x1=&amp;quot;0&amp;quot; y1=&amp;quot;0&amp;quot; x2=&amp;quot;500&amp;quot; y2=&amp;quot;0&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;S1Lu&quot;&gt;Для тега &lt;strong&gt;line&lt;/strong&gt; задано свойство &lt;em&gt;&lt;code&gt;vector-effect&lt;/code&gt;&lt;/em&gt; с значением &lt;strong&gt;&lt;code&gt;non-scaling-stroke&lt;/code&gt;&lt;/strong&gt;. В любом заданном масштабе для вида линии будут отрисовываться с одинаковой толщиной.&lt;/p&gt;
  &lt;h3 id=&quot;lTXX&quot;&gt;Окружность&lt;/h3&gt;
  &lt;figure id=&quot;ncc0&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/22/de/22de36b0-7904-4108-9cfd-98c4060ea75d.png&quot; width=&quot;243&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;XOBK&quot;&gt;Типы линий используем как и для тега &lt;strong&gt;line&lt;/strong&gt; из CSS файла.&lt;br /&gt;А в чертеже используем тег &lt;strong&gt;circle&lt;/strong&gt; с присваиванием ему класса типа линии.&lt;/p&gt;
  &lt;pre id=&quot;c8pF&quot;&gt;&amp;lt;circle class=&amp;quot;line-type-1&amp;quot; cx=&amp;quot;165&amp;quot; cy=&amp;quot;25&amp;quot; r=&amp;quot;125&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;h3 id=&quot;AOaa&quot;&gt;Эллипс&lt;/h3&gt;
  &lt;figure id=&quot;kbfV&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/77/3b/773b8105-6a67-4ff2-a39d-0748484654fc.png&quot; width=&quot;110&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Ukuz&quot;&gt;Типы линий используем как и для тега &lt;strong&gt;line&lt;/strong&gt; из CSS файла.&lt;br /&gt;А в чертеже используем тег &lt;strong&gt;ellipse&lt;/strong&gt; с присваиванием ему класса типа линии.&lt;/p&gt;
  &lt;pre id=&quot;d2u6&quot;&gt;&amp;lt;ellipse class=&amp;quot;line-type-3&amp;quot; cx=&amp;quot;120&amp;quot; cy=&amp;quot;280&amp;quot; rx=&amp;quot;100&amp;quot; ry=&amp;quot;50&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;h3 id=&quot;rv5p&quot;&gt;&lt;strong&gt;Дуга и дуга эллипса&lt;/strong&gt;&lt;/h3&gt;
  &lt;figure id=&quot;CX12&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://web.archive.org/web/20230607233050im_/http://habrastorage.org/storage2/5c5/0ea/c69/5c50eac6989662ea50bec077ff357867.png&quot; width=&quot;142&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;xuu1&quot;&gt;Типы линий используем как и для тега &lt;strong&gt;line&lt;/strong&gt; из CSS файла.&lt;br /&gt;А в чертеже используем тег &lt;strong&gt;path&lt;/strong&gt; с присвоением ему класса типа линии. В свойстве &lt;strong&gt;d&lt;/strong&gt; задаём параметры дуги.&lt;/p&gt;
  &lt;pre id=&quot;ErTb&quot;&gt;&amp;lt;path class=&amp;quot;line-type-2&amp;quot; d=&amp;quot;M50,50 A140,140 0 0 1 200,350&amp;quot;/&amp;gt;	
&amp;lt;path class=&amp;quot;line-type-3&amp;quot; d=&amp;quot;M20,150 A20,40 0 0 1 130,50&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;h3 id=&quot;eE9u&quot;&gt;&lt;strong&gt;Прямоугольник&lt;/strong&gt;&lt;/h3&gt;
  &lt;figure id=&quot;48Wn&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://web.archive.org/web/20230607233050im_/http://habrastorage.org/storage2/104/1e6/9d1/1041e69d17be4cccadd325c6473eb1b8.png&quot; width=&quot;115&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;rN4q&quot;&gt;Типы линий используем как и для тега &lt;strong&gt;line&lt;/strong&gt; из CSS файла.&lt;br /&gt;А в чертеже используем тег &lt;strong&gt;rect&lt;/strong&gt; с присвоением ему класса типа линии.&lt;/p&gt;
  &lt;pre id=&quot;XXsW&quot;&gt;&amp;lt;rect class=&amp;quot;line-type-1&amp;quot; x=&amp;quot;20&amp;quot; y=&amp;quot;5&amp;quot; width=&amp;quot;395&amp;quot; height=&amp;quot;287&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;h3 id=&quot;HQ3H&quot;&gt;&lt;strong&gt;Ломаная линия&lt;/strong&gt;&lt;/h3&gt;
  &lt;figure id=&quot;DTYD&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://web.archive.org/web/20230607233050im_/http://habrastorage.org/storage2/6ab/458/0b6/6ab4580b6747edd24943e898c85a5c1b.png&quot; width=&quot;104&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;4ZwE&quot;&gt;Типы линий используем как и для тега &lt;strong&gt;line&lt;/strong&gt; из CSS файла.&lt;br /&gt;А в чертеже используем тег &lt;strong&gt;polyline&lt;/strong&gt; с присвоением ему класса типа линии.&lt;/p&gt;
  &lt;pre id=&quot;f8LJ&quot;&gt;&amp;lt;polyline class=&amp;quot;line-type-7&amp;quot; points=&amp;quot;20,250 50,250 60,300 80,120 120,140 200,180&amp;quot;/&amp;gt;&lt;/pre&gt;
  &lt;h3 id=&quot;KqGz&quot;&gt;&lt;strong&gt;Кривая NURBS (сплайн)&lt;/strong&gt;&lt;/h3&gt;
  &lt;figure id=&quot;CmKN&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://web.archive.org/web/20230607233050im_/http://habrastorage.org/storage2/297/dc7/d76/297dc7d761cb43c3a7e01e7d932bcb8f.png&quot; width=&quot;128&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;gt6J&quot;&gt;Типы линий используем как и для тега &lt;strong&gt;line&lt;/strong&gt; из CSS файла.&lt;br /&gt;А в чертеже используем тег &lt;strong&gt;path&lt;/strong&gt; с присвоением ему класса типа линии.&lt;/p&gt;
  &lt;pre id=&quot;Yre0&quot;&gt;&amp;lt;path class=&amp;quot;line-type-2&amp;quot; d=&amp;quot;M140,201.34 C144.696,186.7859 152.303,175.3664 162.822,167.0820
    C167.996,163.0066 174.363,158.9944 180.459,157.132
    C194.3626,152.8843 213.0561,157.132 225.5589,167.082
    C232.5067,172.6114 241.8569,177.1221 250,177.9937
    C263.3438,179.4220 275.9399,163.2633 290.0,160
    C299.5142,157.7918 310.4253,154.6301 319.5409,157.132
    C325.7604,158.8390 332.0035,163.0066 337.1780,167.082
    C347.6966,175.3664 353.3294,189.206 360,201.3405&amp;quot;/&amp;gt;&lt;/pre&gt;

</content></entry><entry><id>it_tale:svg-drawing-1</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/svg-drawing-1?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Чертежи в SVG формате. Часть 1 — Черновик стандарта</title><published>2024-05-30T10:22:03.769Z</published><updated>2024-11-28T15:35:48.752Z</updated><summary type="html">Мопед не мой, я только объявление разместил (с)</summary><content type="html">
  &lt;blockquote id=&quot;w0j0&quot; data-align=&quot;right&quot;&gt;&lt;em&gt;Мопед не мой, я только объявление разместил (с)&lt;/em&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;KRnd&quot; data-align=&quot;right&quot;&gt;Это цикл статей, посвященных работе с SVG, размещенный на сайте &lt;a href=&quot;http://v.sytnik.lviv.ua&quot; target=&quot;_blank&quot;&gt;http://v.sytnik.lviv.ua&lt;/a&gt; (ныне недоступный). Статьи мне показались очень информативными, поэтому захотелось их сохранить.&lt;/p&gt;
  &lt;p id=&quot;woOT&quot;&gt;В интернете можно найти много разной информации о создании чертежей в формате SVG. Чаще предлагается какой-то редактор и экспорт из формата DXF в SVG. Просматривая код SVG сразу видно что там много лишнего. Созданный в одном редакторе файл SVG не всегда может корректно открыться в другом. Одно радует, что браузеры начали поддерживать SVG формат. Всюду пишут про недостатки использования SVG. А может надо придерживаться единых правил структуры файла для отображения чертежей?&lt;/p&gt;
  &lt;p id=&quot;uDvB&quot;&gt;Из экспериментов и тестов пришёл к таким правилам при создании чертёжа:&lt;/p&gt;
  &lt;ul id=&quot;rynw&quot;&gt;
    &lt;li id=&quot;9Rr3&quot;&gt;использование объектной модели чертежа;&lt;/li&gt;
    &lt;li id=&quot;NvCO&quot;&gt;используем только одну единицу измерения (та что по умолчанию в пикселях);&lt;/li&gt;
    &lt;li id=&quot;KzsQ&quot;&gt;условно принимаем — один пиксель равен одному миллиметру (для браузера будет в &lt;code&gt;пикселях&lt;/code&gt;, а для нас в &lt;code&gt;мм&lt;/code&gt;);&lt;/li&gt;
    &lt;li id=&quot;gjcW&quot;&gt;масштаб описания элементов всегда &lt;code&gt;1:1&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;urWQ&quot;&gt; для отображения объектов в другом масштабе используем вложенный &lt;em&gt;svg&lt;/em&gt; рисунок;&lt;/li&gt;
    &lt;li id=&quot;VXM4&quot;&gt;для уникальных объектов задаём &lt;code&gt;id&lt;/code&gt; а для характерных &lt;code&gt;class&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;IfhT&quot;&gt;…&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;43ix&quot;&gt;&lt;strong&gt;Объектная модель чертежа&lt;/strong&gt;&lt;/h2&gt;
  &lt;p id=&quot;XQRO&quot;&gt;Упрощенно чертёж можно описать в виде XML структуры.&lt;/p&gt;
  &lt;pre id=&quot;c0AF&quot; data-lang=&quot;svg&quot;&gt;&amp;lt;svg id=&amp;quot;Detail1&amp;quot; ...&amp;gt;
    &amp;lt;defs id=&amp;quot;defsCAD&amp;quot;&amp;gt; ... &amp;lt;/defs&amp;gt;
    &amp;lt;svg id=&amp;quot;Shtamp&amp;quot; name=&amp;quot;Frame drawing&amp;quot; ... &amp;gt;
        ...
    &amp;lt;/svg&amp;gt;
    &amp;lt;svg id=&amp;quot;View1&amp;quot; ... &amp;gt;
        &amp;lt;g id=&amp;quot;layer-0&amp;quot; name=&amp;quot;System layer&amp;quot; ...&amp;gt;
            &amp;lt;line class=&amp;quot;line-type-1&amp;quot; ... /&amp;gt;
            &amp;lt;line class=&amp;quot;line-type-1&amp;quot; ... /&amp;gt;
            &amp;lt;circle class=&amp;quot;line-type-1&amp;quot; ... /&amp;gt;
            &amp;lt;path class=&amp;quot;line-type-1&amp;quot; ... /&amp;gt;
            &amp;lt;rect class=&amp;quot;line-type-1&amp;quot; ... /&amp;gt;
            &amp;lt;line class=&amp;quot;line-type-2&amp;quot; ... /&amp;gt;
            &amp;lt;line class=&amp;quot;line-type-2&amp;quot; ... /&amp;gt;
            &amp;lt;g class=&amp;quot;dimL&amp;quot;&amp;gt;
                &amp;lt;line class=&amp;quot;line-type-2&amp;quot; ... /&amp;gt;
                &amp;lt;line class=&amp;quot;line-type-2&amp;quot; ... /&amp;gt;
                &amp;lt;line class=&amp;quot;line-type-2&amp;quot; ... /&amp;gt;
                &amp;lt;text ... &amp;gt;...&amp;lt;/text&amp;gt; 
            &amp;lt;/g&amp;gt;
            ...
        &amp;lt;/g&amp;gt;
        &amp;lt;g id=&amp;quot;layer-1&amp;quot; name=&amp;quot;Layer 1&amp;quot; ...&amp;gt;
            ...
        &amp;lt;/g&amp;gt;
    &amp;lt;/svg&amp;gt;
    &amp;lt;svg id=&amp;quot;View2&amp;quot; ... &amp;gt;
        &amp;lt;line class=&amp;quot;line-type-1&amp;quot; ... /&amp;gt;
        ...
    &amp;lt;/svg&amp;gt;
    &amp;lt;svg id=&amp;quot;View3&amp;quot; ... &amp;gt; ... &amp;lt;/svg&amp;gt;
&amp;lt;/svg&amp;gt;&lt;/pre&gt;
  &lt;h3 id=&quot;PeQD&quot;&gt;Тег svg&lt;/h3&gt;
  &lt;p id=&quot;2ZdY&quot;&gt;&amp;lt;&lt;strong&gt;svg&lt;/strong&gt;&amp;gt; — тег используется для описания самого чертежа и встроенных видов, штампа и техтребований. Если необходимо использовать масштаб отличный от &lt;code&gt;1:1&lt;/code&gt;, то реализуется с помощью свойств тега.&lt;br /&gt;Например масштаб &lt;code&gt;1:4&lt;/code&gt;&lt;/p&gt;
  &lt;pre id=&quot;PnJk&quot; data-lang=&quot;svg&quot;&gt;&amp;lt;svg id=&amp;quot;View1&amp;quot;
     x=&amp;quot;50&amp;quot;
     y=&amp;quot;7&amp;quot;
     width=&amp;quot;150&amp;quot;
     height=&amp;quot;162&amp;quot;
     viewBox=&amp;quot;-25 -200 600 648&amp;quot;&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;IoK4&quot;&gt;width=&amp;quot;&lt;strong&gt;150&amp;quot;&lt;/strong&gt; height=&amp;quot;&lt;strong&gt;162&amp;quot;&lt;/strong&gt; viewBox=&amp;quot;…… &lt;strong&gt;600&lt;/strong&gt; &lt;strong&gt;648&lt;/strong&gt;&amp;quot; — соотношение величин задаёт масштаб отображения вида на листе.&lt;/p&gt;
  &lt;p id=&quot;Ao8c&quot;&gt;Например масштаб 10:1&lt;/p&gt;
  &lt;pre id=&quot;uwus&quot; data-lang=&quot;svg&quot;&gt;&amp;lt;svg id=&amp;quot;View1&amp;quot; 
     x=&amp;quot;50&amp;quot; 
     y=&amp;quot;7&amp;quot; 
     width=&amp;quot;150&amp;quot; 
     height=&amp;quot;162&amp;quot; 
     viewBox=&amp;quot;-0.6 -5 15 16.2&amp;quot;&amp;gt;&lt;/pre&gt;
  &lt;h3 id=&quot;TqlY&quot;&gt;Тег defs&lt;/h3&gt;
  &lt;p id=&quot;v72Y&quot;&gt;&amp;lt;&lt;strong&gt;defs&lt;/strong&gt;&amp;gt; — здесь описываем все примитивные повторяющиеся элементы. Замечена одна особенность при использовании SVG элемента &lt;code&gt;marker&lt;/code&gt; — на него не действуют параметры масштаба из тега &amp;lt;&lt;strong&gt;svg&lt;/strong&gt;&amp;gt;, что намного упрощает работу с масштабом. (При любом масштабе вида стрелки в размерах должны быть одинаковые). Но на линии в элемента &lt;code&gt;marker&lt;/code&gt; не действует параметр свойства &lt;em&gt;&lt;code&gt;vector-effect: non-scaling-stroke&lt;/code&gt;;&lt;/em&gt;&lt;/p&gt;
  &lt;h3 id=&quot;XVJq&quot;&gt;Тег line&lt;/h3&gt;
  &lt;p id=&quot;HBc4&quot;&gt;&amp;lt;&lt;strong&gt;line class=&lt;/strong&gt;«atr1» …/&amp;gt; — в файле CSS описываем стили линий для графических примитивов. Жаль что в Internet Explorer для каждого масштаба необходимо указывать свой стиль линии (толщина линии и интервал пунктиров). Как правило в чертеже одновременно не используются все возможные масштабы и достаточно задать только для используемых масштабов.&lt;br /&gt;Пример описания стилей линий для элементов &lt;code&gt;line&lt;/code&gt;, &lt;code&gt;circle&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, &lt;code&gt;rect&lt;/code&gt; и др.:&lt;/p&gt;
  &lt;pre id=&quot;3EpY&quot; data-lang=&quot;css&quot;&gt;line, rect, circle, ellipse, path, text {
  vector-effect: non-scaling-stroke;
}
/* main line */
.line-type-1 { fill: none; stroke: blue; stroke-width: 2;
}
/* hairline */
.line-type-2 { fill: none; stroke: black; stroke-width: .7;
}
/* axial */
.line-type-3 { fill: none; stroke: red; stroke-width: .7; stroke-dasharray: 25, 4, 3, 4;
}
/* dashed */
.line-type-4 { fill: none; stroke: black; stroke-width: .7; stroke-dasharray: 7, 4;
}
/* main line for the scale 0.25 */
.line-type-1_025 { fill: none; stroke: blue; stroke-width: 8;
}
/* hairline for the scale 0.25 */
.line-type-2_025 { stroke: black; stroke-width: 2.8;
}&lt;/pre&gt;
  &lt;h3 id=&quot;K4fN&quot;&gt;Тег g&lt;/h3&gt;
  &lt;p id=&quot;NQEE&quot;&gt;&amp;lt;&lt;strong&gt;g&lt;/strong&gt; class=&amp;quot;dimL&amp;quot;&amp;gt;…&amp;lt;/&lt;strong&gt;g&lt;/strong&gt;&amp;gt; — элементы описывающие объекты как размер группируем.&lt;br /&gt;Пример:&lt;/p&gt;
  &lt;pre id=&quot;EOKt&quot; data-lang=&quot;svg&quot;&gt;...
&amp;lt;defs id=&amp;quot;defsCAD&amp;quot;&amp;gt;
&amp;lt;!-- Draw arrows and tick marks --&amp;gt;
    &amp;lt;marker id=&amp;quot;dimArrow-1&amp;quot; viewBox=&amp;quot;-2 -12 29 24&amp;quot; markerWidth=&amp;quot;44&amp;quot; markerHeight=&amp;quot;36&amp;quot; orient=&amp;quot;auto&amp;quot;&amp;gt;
        &amp;lt;path class=&amp;quot;line-type-2_025&amp;quot; stroke=&amp;quot;black&amp;quot; d=&amp;quot;M0,0 L20,-4 16,0 20,4 z M0,-10 L0,10 M0,0 L27,0&amp;quot;/&amp;gt;
    &amp;lt;/marker&amp;gt;
    &amp;lt;marker id=&amp;quot;dimArrow-2&amp;quot; viewBox=&amp;quot;-27 -12 29 24&amp;quot; markerWidth=&amp;quot;44&amp;quot; markerHeight=&amp;quot;36&amp;quot; orient=&amp;quot;auto&amp;quot;&amp;gt;
        &amp;lt;path class=&amp;quot;line-type-2_025&amp;quot; stroke=&amp;quot;black&amp;quot; d=&amp;quot;M0,0 L-20,-4 -16,0 -20,4 z M0,-10 L0,10 M0,0 L-27,0&amp;quot;/&amp;gt;
    &amp;lt;/marker&amp;gt;
&amp;lt;/defs&amp;gt;
...
    &amp;lt;g class=&amp;quot;DimL&amp;quot;&amp;gt;
        &amp;lt;line class=&amp;quot;line-type-2&amp;quot; x1=&amp;quot;190&amp;quot; y1=&amp;quot;180&amp;quot; x2=&amp;quot;190&amp;quot; y2=&amp;quot;230&amp;quot;/&amp;gt;
        &amp;lt;line class=&amp;quot;line-type-2&amp;quot; x1=&amp;quot;310&amp;quot; y1=&amp;quot;180&amp;quot; x2=&amp;quot;310&amp;quot; y2=&amp;quot;230&amp;quot;/&amp;gt;
        &amp;lt;line id=&amp;quot;dim1&amp;quot; class=&amp;quot;line-type-2&amp;quot; x1=&amp;quot;190&amp;quot; y1=&amp;quot;230&amp;quot; x2=&amp;quot;310&amp;quot; y2=&amp;quot;230&amp;quot; marker-start=&amp;quot;url(#dimArrow-1)&amp;quot; marker-end=&amp;quot;url(#dimArrow-2)&amp;quot;/&amp;gt;
        &amp;lt;text x=&amp;quot;265&amp;quot; y=&amp;quot;222&amp;quot; font-size=&amp;quot;28&amp;quot; text-anchor=&amp;quot;middle&amp;quot;&amp;gt;120&amp;lt;/text&amp;gt; 
    &amp;lt;/g&amp;gt;
...&lt;/pre&gt;
  &lt;p id=&quot;guQ5&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;tEcV&quot;&gt;Есть одна особенность отображения графики в SVG. Если мы задали область и хотим по краю её нарисовать контур, то должны отступить на пол толщины линии иначе линии будут на половину тоньше. Например кода чёрной рамочки края чертежа.&lt;/p&gt;
  &lt;pre id=&quot;uZbt&quot; data-lang=&quot;svg&quot;&gt;&amp;lt;svg id=&amp;quot;Shtamp&amp;quot; type=&amp;quot;1&amp;quot; x=&amp;quot;0&amp;quot; y=&amp;quot;0&amp;quot; width=&amp;quot;420&amp;quot; height=&amp;quot;297&amp;quot; viewBox=&amp;quot;0 0 420 297&amp;quot;&amp;gt;
    ...
    &amp;lt;rect class=&amp;quot;line-type-2&amp;quot; x=&amp;quot;1&amp;quot; y=&amp;quot;1&amp;quot; width=&amp;quot;418&amp;quot; height=&amp;quot;295&amp;quot;/&amp;gt;
   ....
&amp;lt;/svg&amp;gt;&lt;/pre&gt;

</content></entry><entry><id>it_tale:rotate-svg-world</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_tale/rotate-svg-world?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_tale"></link><title>Дайте мне точку опоры...</title><published>2024-05-17T10:31:59.438Z</published><updated>2024-05-17T15:06:59.131Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img1.teletype.in/files/04/24/042455b5-67a8-4d0a-a540-5e12abebc77f.png"></media:thumbnail><category term="lisp" label="lisp"></category><summary type="html">&lt;img src=&quot;https://img3.teletype.in/files/a2/99/a29947b1-7cf5-4473-8903-35d183df61f8.jpeg&quot;&gt;...и я переверну землю. Заметка о том, как перевернуть мир в отдельно взятом SVG файле (на Lisp).</summary><content type="html">
  &lt;p id=&quot;uw2T&quot;&gt;...и я переверну землю.&lt;/p&gt;
  &lt;figure id=&quot;QYCy&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/a2/99/a29947b1-7cf5-4473-8903-35d183df61f8.jpeg&quot; width=&quot;1600&quot; /&gt;
    &lt;figcaption&gt; Котик переворачивает землю. &lt;a href=&quot;https://www.yaplakal.com/forum13/st/0/topic283452.html&quot; target=&quot;_blank&quot;&gt;ЯПлакал&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;18WZ&quot;&gt;Эту фразу приписывают Архимеду, так, во всяком случае, нам говорили в школе.&lt;/p&gt;
  &lt;p id=&quot;5sIT&quot;&gt;Иногда действительно требуется перевернуть мир, пусть и несколько в ином смысле.&lt;/p&gt;
  &lt;h3 id=&quot;JXqF&quot;&gt;Координатная система SVG&lt;/h3&gt;
  &lt;p id=&quot;bQLj&quot;&gt;Понадобилось мне сохранить векторный рисунок. Для этого SVG подходит как нельзя лучше — генерируется просто, размер небольшой, структура понятна и предсказуема.&lt;/p&gt;
  &lt;p id=&quot;MB1u&quot;&gt;Вот только незадача, координата Y в нем идет не так, как принято в математике — привычная нам ось идет вверх, тогда как в SVG — вниз. Пробуем рисовать. Будем использовать библиотеку &lt;a href=&quot;https://github.com/wmannis/cl-svg&quot; target=&quot;_blank&quot;&gt;cl-svg&lt;/a&gt;.&lt;/p&gt;
  &lt;blockquote id=&quot;wPqk&quot;&gt;В Quicklisp устаревшая версия, поэтому имеет смысл склонировать ее себе в ~/quicklisp/local-projects/ и выполнить &lt;code&gt;(ql:register-local-projects)&lt;/code&gt;.&lt;/blockquote&gt;
  &lt;p id=&quot;n3Yc&quot;&gt;Итак, делаем простейший файл, рисуем координатные оси, ну или их какое-то подобие.&lt;/p&gt;
  &lt;pre id=&quot;3ujX&quot;&gt;(in-package #:svg)

(with-svg-to-file
    (scene &amp;#x27;svg-1.1-toplevel :height 230 :width 230)
    (#p&amp;quot;~/devel/svg/coords.svg&amp;quot; :if-exists :supersede)
  
  (draw scene (:line :x1   0 :y1 -10 :x2   0 :y2 200))
  
  (draw scene (:line :x1 -10 :y1   0 :x2 200 :y2   0)))&lt;/pre&gt;
  &lt;p id=&quot;7Uyg&quot;&gt;Запускаем, получился файл &lt;code&gt;coord.svg&lt;/code&gt;. Запускаем его и... ничего.&lt;/p&gt;
  &lt;p id=&quot;jBPx&quot;&gt;Правильно, потому что по стандарту линия не рисуется, если у нее не задано значение цвета пера (&lt;code&gt;stroke&lt;/code&gt;). Кроме того, у нас не задано значение поля &lt;code&gt;viewPort&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;v0wF&quot;&gt;Корректный вариант такой (далее первую форму &lt;code&gt;(in-package #:svg)&lt;/code&gt; опускаю):&lt;/p&gt;
  &lt;pre id=&quot;yd8U&quot;&gt;(with-svg-to-file
    (scene &amp;#x27;svg-1.1-toplevel
	   :height 230
	   :width 230
	   :view-box &amp;quot;-20 -20 230 230&amp;quot;)
    (#p&amp;quot;~/devel/svg/coords.svg&amp;quot; :if-exists :supersede)

  (draw scene
      (:rect :x -20 :y -20 :height 230 :width 230)
      :fill &amp;quot;#EEEEEE&amp;quot; :stroke &amp;quot;none&amp;quot;)
  
  (draw scene
      (:line :x1 0 :y1 -10 :x2 0 :y2 200) :stroke &amp;quot;green&amp;quot;)
  
  (draw scene 
      (:line :x1 -10 :y1 0 :x2 200 :y2 0) :stroke &amp;quot;green&amp;quot;))&lt;/pre&gt;
  &lt;p id=&quot;eP1W&quot;&gt;Линии осей нарисованы зеленым цветом.&lt;/p&gt;
  &lt;p id=&quot;fRbx&quot;&gt;Для удобства восприятия добавлен прямоугольник под картинку.&lt;/p&gt;
  &lt;p id=&quot;KgjE&quot;&gt;Получилось что-то такое:&lt;/p&gt;
  &lt;figure id=&quot;bERQ&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/a8/8a/a88ac6f3-c468-4f28-a6da-c962d4e7e885.png&quot; width=&quot;230&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;ijkz&quot;&gt;Как мы видим, ось Y растет вниз.&lt;/p&gt;
  &lt;p id=&quot;v4Ya&quot;&gt;Для того, чтобы перевернуть ось, можно просто нарисовать ось X снизу, а остальные расчеты просто вести с поправкой на то, что координаты перевернуты. Решение вполне себе нормальное, но придется внести изменения в расчетную часть, а они могут быть нетривиальными и не всегда просто реализуемыми. Кроме того, наверняка появится множество краевых случаев, которые нужно будет обрабатывать.&lt;/p&gt;
  &lt;p id=&quot;5W7W&quot;&gt;Попробуем другой метод.&lt;/p&gt;
  &lt;h3 id=&quot;55xd&quot;&gt;Использование CSS&lt;/h3&gt;
  &lt;p id=&quot;wBiB&quot;&gt;SVG позволяет использовать стилевую информацию. Этим мы и воспользуемся.&lt;/p&gt;
  &lt;p id=&quot;aeFX&quot;&gt;Добавим стиль, применяющий масштаб &lt;code&gt;-1&lt;/code&gt; к тегу &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;AAv7&quot;&gt;(with-svg-to-file
    (scene &amp;#x27;svg-1.1-toplevel :height 230 :width 230 :view-box &amp;quot;-20 -20 230 230&amp;quot;)
    (#p&amp;quot;~/devel/svg/coords.svg&amp;quot; :if-exists :supersede)
  
  (style scene &amp;quot;svg#toplevel {display:flex; transform: scaleY(-1);}&amp;quot;)
  
  (draw scene
      (:rect :x -20 :y -20 :height 230 :width 230)
      :fill &amp;quot;#EEEEEE&amp;quot; :stroke &amp;quot;none&amp;quot;)
  
  (draw scene
      (:line :x1 0 :y1 -10 :x2 0 :y2 200) :stroke &amp;quot;green&amp;quot;)
  
  (draw scene
      (:line :x1 -10 :y1 0 :x2 200 :y2 0) :stroke &amp;quot;green&amp;quot;))&lt;/pre&gt;
  &lt;p id=&quot;tu3i&quot;&gt;Идентификатор &lt;code&gt;#toplevel&lt;/code&gt; создается внутри класса &lt;code&gt;svg-1.1-toplevel&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;OlzC&quot;&gt;Получилась замечательная картинка:&lt;/p&gt;
  &lt;figure id=&quot;MmMt&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d0/cb/d0cbafb4-16ee-492f-919a-c0ddebff8870.png&quot; width=&quot;230&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;lDQL&quot;&gt;Прекрасно, давайте развивать решение. Добавим текста:&lt;/p&gt;
  &lt;pre id=&quot;Wbho&quot;&gt;(with-svg-to-file
    (scene &amp;#x27;svg-1.1-toplevel :height 230 :width 230 :view-box &amp;quot;-20 -20 230 230&amp;quot;)
    (#p&amp;quot;~/devel/svg/coords.svg&amp;quot; :if-exists :supersede)

  (style scene &amp;quot;svg#toplevel {display:flex; transform: scaleY(-1);}&amp;quot;)

  (draw scene
      (:rect :x -20 :y -20 :height 230 :width 230)
      :fill &amp;quot;#EEEEEE&amp;quot; :stroke &amp;quot;none&amp;quot;)

  (draw scene
      (:line :x1 0 :y1 -10 :x2 0 :y2 200) :stroke &amp;quot;green&amp;quot;)

  (draw scene
      (:line :x1 -10 :y1 0 :x2 200 :y2 0) :stroke &amp;quot;green&amp;quot;)

  (text scene (:x 20 :y 20) &amp;quot;Привет!&amp;quot;))&lt;/pre&gt;
  &lt;figure id=&quot;cs0P&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/aa/ad/aaad5dc3-b8af-4215-9ef8-de0e546aad88.png&quot; width=&quot;230&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;KvkT&quot;&gt;Ооочень интересно. Перевернут не только мир, но и текст.&lt;/p&gt;
  &lt;p id=&quot;gQMz&quot;&gt;Это забавно, но логично.&lt;/p&gt;
  &lt;p id=&quot;OH3c&quot;&gt;Хорошо, давайте теперь применим масштабирование и к тексту:&lt;/p&gt;
  &lt;pre id=&quot;fZym&quot;&gt;(with-svg-to-file
    (scene &amp;#x27;svg-1.1-toplevel :height 230 :width 230 :view-box &amp;quot;-20 -20 230 230&amp;quot;)
    (#p&amp;quot;~/devel/svg/coords.svg&amp;quot; :if-exists :supersede)

  (style scene &amp;quot;svg#toplevel {display:flex; transform: scaleY(-1);}&amp;quot;)

  (style scene &amp;quot;svg#toplevel &amp;gt; text {transform: scaleY(-1);}&amp;quot;)

  (draw scene
      (:rect :x -20 :y -20 :height 230 :width 230)
      :fill &amp;quot;#EEEEEE&amp;quot; :stroke &amp;quot;none&amp;quot;)

  (draw scene
      (:line :x1 0 :y1 -10 :x2 0 :y2 200) :stroke &amp;quot;green&amp;quot;)

  (draw scene
      (:line :x1 -10 :y1 0 :x2 200 :y2 0) :stroke &amp;quot;green&amp;quot;)

  (text scene (:x 20 :y 20) &amp;quot;Привет!&amp;quot;))&lt;/pre&gt;
  &lt;p id=&quot;786d&quot;&gt;Текст перевернулся, но теперь он расположен непонятно где:&lt;/p&gt;
  &lt;figure id=&quot;XPnE&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/0d/b8/0db8f64c-0a20-4421-b623-c77493a240a9.png&quot; width=&quot;230&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;lmey&quot;&gt;Все дело в том, что преобразование &lt;code&gt;scaleY(-1)&lt;/code&gt; выполняется относительно начала координат, а не относительно координат элемента, к которому это преобразование применяется. Получается, что базовая линия шрифта находится на координате Y, равной 20, и после применения масштабирования расположено также на 20 пикселей от оси, но в другую сторону.&lt;/p&gt;
  &lt;p id=&quot;EL8k&quot;&gt;Можно применить дополнительные преобразования — сначала перенести текст к началу координат, затем масштабировать, затем отнести снова назад. Но это нужно рассчитывать, давайте сделаем по-другому.&lt;/p&gt;
  &lt;p id=&quot;UXWy&quot;&gt;Обернем текст в группу и потом применим преобразование к тексту.&lt;/p&gt;
  &lt;pre id=&quot;1SIY&quot;&gt;(with-svg-to-file
    (scene &amp;#x27;svg-1.1-toplevel :height 230 :width 230 :view-box &amp;quot;-20 -20 230 230&amp;quot;)
    (#p&amp;quot;~/devel/svg/coords.svg&amp;quot; :if-exists :supersede)

  (style scene &amp;quot;svg#toplevel {display:flex; transform: scaleY(-1);}&amp;quot;)

  (style scene &amp;quot;svg#toplevel g &amp;gt; text {transform: scaleY(-1);}&amp;quot;)

  (draw scene
      (:rect :x -20 :y -20 :height 230 :width 230)
      :fill &amp;quot;#EEEEEE&amp;quot; :stroke &amp;quot;none&amp;quot;)

  (draw scene
      (:line :x1 0 :y1 -10 :x2 0 :y2 200) :stroke &amp;quot;green&amp;quot;)

  (draw scene
      (:line :x1 -10 :y1 0 :x2 200 :y2 0) :stroke &amp;quot;green&amp;quot;)

  (make-group scene
      (:transform &amp;quot;translate(20, 20)&amp;quot;)
    (text*  (:x 0 :y 0) &amp;quot;Привет!&amp;quot;)))&lt;/pre&gt;
  &lt;figure id=&quot;Q3ov&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/32/9e/329e9272-51be-4791-8470-1bc528d03779.png&quot; width=&quot;230&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;RoxK&quot;&gt;Получилось то, что нужно!&lt;/p&gt;
  &lt;p id=&quot;YdLM&quot;&gt;Ключевым здесь являются вызовы &lt;/p&gt;
  &lt;pre id=&quot;eJyh&quot;&gt;(style scene &amp;quot;svg#toplevel g &amp;gt; text {transform: scaleY(-1);}&amp;quot;)

...

(make-group scene
      (:transform &amp;quot;translate(20, 20)&amp;quot;)
    (text*  (:x 0 :y 0) &amp;quot;Привет!&amp;quot;))&lt;/pre&gt;
  &lt;p id=&quot;e6cu&quot;&gt;У тега &lt;code&gt;&amp;lt;g&amp;gt;&lt;/code&gt; нет своих координат &lt;code&gt;x&lt;/code&gt; и &lt;code&gt;y&lt;/code&gt;, но к нему можно применить преобразование  &lt;code&gt;transform()&lt;/code&gt;, и в нем задать нужное смещение. Кроме того, координаты &lt;code&gt;x&lt;/code&gt; и &lt;code&gt;y&lt;/code&gt; для текста можно не указывать, но библиотека &lt;code&gt;cl-svg&lt;/code&gt; требует, чтобы они были указаны, поэтому вписываем.&lt;/p&gt;
  &lt;h3 id=&quot;0vqu&quot;&gt;Вложенные группы&lt;/h3&gt;
  &lt;p id=&quot;hcRR&quot;&gt;Как частное решение это вполне сойдет. Но для более сложного документа лучше будет сгруппировать элементы логически. Это сделает документ структурированным и более удобным в ряде случаев, например при работе в консоли браузера.&lt;/p&gt;
  &lt;pre id=&quot;Kd95&quot;&gt;(with-svg-to-file
    (scene &amp;#x27;svg-1.1-toplevel :height 230 :width 230 :view-box &amp;quot;-20 -20 230 230&amp;quot;)
    (#p&amp;quot;~/devel/svg/coords.svg&amp;quot; :if-exists :supersede)

  (style scene &amp;quot;svg#toplevel {display:flex; transform: scaleY(-1);}&amp;quot;)

  (style scene &amp;quot;svg#toplevel g &amp;gt; text {transform: scaleY(-1);}&amp;quot;)

  (make-group scene (:id &amp;quot;frame&amp;quot;)
    (draw*
     (:rect :x -20 :y -20 :height 230 :width 230)
     :fill &amp;quot;#EEEEEE&amp;quot; :stroke &amp;quot;none&amp;quot;))

  (make-group scene (:id &amp;quot;axes&amp;quot;)
    (draw*
     (:line :x1 0 :y1 -10 :x2 0 :y2 200) :stroke &amp;quot;green&amp;quot;)
    (draw*
     (:line :x1 -10 :y1 0 :x2 200 :y2 0) :stroke &amp;quot;green&amp;quot;))

  (let ((texts (make-group scene (:id &amp;quot;texts&amp;quot;))))
    (make-group texts (:transform &amp;quot;translate(20, 20)&amp;quot;)
      (text*  (:x 0 :y 0) &amp;quot;Привет&amp;quot;))
    (make-group texts (:transform &amp;quot;translate(30, 40)&amp;quot;)
      (text*  (:x 0 :y 0) &amp;quot;Мир&amp;quot;))
    (make-group texts (:transform &amp;quot;translate(30, 60) rotate(90)&amp;quot;)
      (text*  (:x 0 :y 0) &amp;quot;SVG&amp;quot;))))&lt;/pre&gt;
  &lt;p id=&quot;hRyV&quot;&gt;В последнем случае дополнительно текст повернут на 90 градусов, то есть можно производить больше одной трансформации или вообще задать матрицу преобразований.&lt;/p&gt;
  &lt;figure id=&quot;ZE1y&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/bd/46/bd4639b7-cbb6-4a21-8b41-3e7e1be5e634.png&quot; width=&quot;230&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;puUT&quot;&gt;Параметры viewPort&lt;/h3&gt;
  &lt;p id=&quot;Ii9b&quot;&gt;Последнее замечание сделаю относительно значений параметра &lt;code&gt;viewPort&lt;/code&gt; тега &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;BXgZ&quot;&gt;Я долго не мог понять, как им пользоваться, пока не придумал такое объяснение.&lt;/p&gt;
  &lt;p id=&quot;IDV6&quot;&gt;Окно просмотра характеризуется двумя парами значений. Первая — положение окна просмотра в мировых (или пользовательских) координатах. По сути это просто точка на пользовательском чертеже.&lt;/p&gt;
  &lt;p id=&quot;SDSf&quot;&gt;Вторая пара задает количество точек на пользовательском чертеже, которые мы видим в окно просмотра, соответственно по горизонтали и вертикали, но при этом само окно имеет размер, задаваемый параметрами ширины и высоты тега &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;. &lt;/p&gt;
  &lt;p id=&quot;yeCV&quot;&gt;То есть, &lt;code&gt;viewPort&lt;/code&gt; имеет фиксированный размер на экране или в браузере, а вот содержимое его подстраивается с учетом коэффициентов, получаемых соотношением размеров из окна просмотра и тега &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;. Кому как, а мне так стало понятнее.&lt;/p&gt;
  &lt;p id=&quot;IHYN&quot;&gt;UPD:&lt;/p&gt;
  &lt;p id=&quot;TCJf&quot;&gt;В библиотеке подумали о том, что иногда можно опустить проверку параметров:&lt;/p&gt;
  &lt;pre id=&quot;4GO8&quot;&gt;(with-svg-to-file
    (scene &amp;#x27;svg-1.1-toplevel :height 230 :width 230 :view-box &amp;quot;-20 -20 230 230&amp;quot;)
    (#p&amp;quot;~/devel/svg/coords.svg&amp;quot; :if-exists :supersede)

  (style scene &amp;quot;svg#toplevel {display:flex; transform: scaleY(-1);}&amp;quot;)

  (style scene &amp;quot;svg#toplevel g &amp;gt; text {transform: scaleY(-1);}&amp;quot;)

  (make-group scene (:id &amp;quot;frame&amp;quot;)
    (draw*
     (:rect :x -20 :y -20 :height 230 :width 230)
     :fill &amp;quot;#EEEEEE&amp;quot; :stroke &amp;quot;none&amp;quot;))

  (make-group scene (:id &amp;quot;axes&amp;quot;)
    (draw*
     (:line :x1 0 :y1 -10 :x2 0 :y2 200) :stroke &amp;quot;green&amp;quot;)
    (draw*
     (:line :x1 -10 :y1 0 :x2 200 :y2 0) :stroke &amp;quot;green&amp;quot;))

  (without-attribute-check 
    (let ((texts (make-group scene (:id &amp;quot;texts&amp;quot;))))
      (make-group texts (:transform &amp;quot;translate(20, 20)&amp;quot;)
	(text* () &amp;quot;Привет&amp;quot;))
      (make-group texts (:transform &amp;quot;translate(30, 40)&amp;quot;)
	(text*  () &amp;quot;Мир&amp;quot;))
      (make-group texts (:transform &amp;quot;translate(30, 60) rotate(90)&amp;quot;)
	(text* () &amp;quot;SVG&amp;quot;)))))&lt;/pre&gt;
  &lt;p id=&quot;c91C&quot;&gt;см. (&lt;code&gt;without-attribute-check ... )&lt;/code&gt;&lt;/p&gt;

</content></entry></feed>