Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Выбрать движок SVG для React: система координат #2

Closed
sadr0b0t opened this issue Feb 27, 2017 · 11 comments

Comments

@sadr0b0t
Copy link
Owner

sadr0b0t commented Feb 27, 2017

Выбрать движок SVG для React - рисовать рабочую область станка - обычная координатная плоскость.

Требования:

  • Рисование базовых примитивов (SVG - понятно)
  • Анимация(плавное перемещение и изменение размера точки - рабочий инструмент). Желательно, с поддержкой родных механизмов React. анимация здесь Анимация SVG в React #3
  • Уметь нормально работать с системой координат: обычно Y начинается в левом верхнем углу и смотрит вниз, а у наших рисунков Y будет начинаться в в левом нижнем углу и смотреть вверх. Должна быть возможность трансформировать координаты для всей рабочей области без заморочек с ручным конвертированием координат. Плюс работа с единицами измерения.

про анимацию отдельно здесь
#3

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Feb 27, 2017

По движкам копирую отсда
#1 (comment)

RaphaelJS+React - шаблон проекта
https://github.com/arnemahl/react-raphael-webpack-es6
https://github.com/arnemahl/react-raphael-webpack-es6/blob/master/src/frontend/scripts/raphael/RaphaelRootComponent.js

обертка получше:
https://github.com/liuhong1happy/react-raphael

сам RaphaelJS
http://dmitrybaranovskiy.github.io/raphael/

Пишут, что Рафаэль больше не развивается, вместо него теперь Snap.SVG от того же автора
https://toster.ru/q/308783

Snap.SVG
http://snapsvg.io/
https://github.com/adobe-webplatform/Snap.svg

Получится ли скрестить с Ректом пока не ясно
http://stackoverflow.com/questions/34635421/reactjs-component-interacting-with-snap-svg-element

Быстрый старт на русском
http://htmlbook.ru/blog/kak-manipulirovat-i-animirovat-svg-cherez-snapsvg

Обзор библиотек для работы с SVG в JavaScript
http://noeticforce.com/Javascript-libraries-for-svg-animation

Эта заглушка React+Raphael работает
https://github.com/liuhong1happy/react-raphael

npm install --save raphael react-raphael

и пример с гитхаба - ок - рисует SVG

Работа с SVG напрямую в React
https://www.smashingmagazine.com/2015/12/generating-svg-with-react/
facebook/react#1657 (comment)

svg и React.js без боли
http://fcode.ninja/2015/11/11/svg-i-react-js-bez-boli/

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Feb 27, 2017

По анимации - предварительно варианты

react-svgpathplayer - анимация путей SVG (использует Snap.svg)
http://saschwarz.github.io/react-svgpathplayer/
https://github.com/saschwarz/react-svgpathplayer

Анимация с React+SVG. Требуется, например, анимировать перемещение точки - это изменение координат X и Y у кружочка.

чел задаётся аналогичным вопросом
http://stackoverflow.com/questions/35647928/transition-animation-using-react-and-svg

и сам себе отвечает
http://bl.ocks.org/herrstucki/9205257

еще какая-то анимация в react-native (похоже, прям покадрово рисуют)
http://browniefed.com/blog/react-native-morphing-svg-paths-with-react-art/

Паттерны React
https://habrahabr.ru/post/309422/

интересный момент про Function as children+Render callback - проверить, имеет ли отношение к анимации

Использование функций как потомков требует дополнительного внимания с вашей стороны, чтобы можно было извлечь из них пользу.
{() => { return "hello world!»}()}
Однако, они могут придать вашим компонентам супер силу, такая техника обычно называется рендер-коллбэк. Эта мощная техника используется в таких библиотеках как ReactMotion. Когда вы применяете ее, логика рендера может управляйся из родительского компонента, вместо того, чтобы полностью передать ее самому компоненту.

@sadr0b0t
Copy link
Owner Author

Трансформ координат

Coordinate Systems, Transformations and Units
https://www.w3.org/TR/SVG/coords

SVG Transformation
http://tutorials.jenkov.com/svg/svg-transformation.html

Understanding SVG Coordinate Systems and Transformations (Part 1) — The viewport, viewBox, and preserveAspectRatio
https://sarasoueidan.com/blog/svg-coordinate-systems/

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Feb 28, 2017

По координатам.

Inkscape Coordinates
http://tavmjong.free.fr/INKSCAPE/MANUAL/html/Coordinates.html

Inkscape: why do coordinates in GUI and XML differ?
http://graphicdesign.stackexchange.com/questions/61708/inkscape-why-do-coordinates-in-gui-and-xml-differ

В инкскейпе, похоже, с направлением координаты Y такая же шизофазия. В GUI ноль у Y находится в нижнем левом углу, координата идет вверх, а в SVG - ноль Y находится в верхнем левом углу, координата идет вниз. Конвертация происходит каким-то образом налету, в редакторе XML по X координаты совпадают, а по Y отличаются на высоту документа. Причем, если поменять размер документа в свойствах (0 внизу сохраняется, значит по логике верхний ноль должен улететь к новой верхней границе документа), конвертация (по крайне мере, в отрытом документе) сходу не происходит, т.е. реальный 0 SVG вообще не понятно, где повисает и как от него считать. После сохранения документа с новым размером (A4->US Legal) тоже ничего не поменялось - 0 SVG по Y висит по центру документа (там, где была верхняя граница A4), бу-га-га.

Итого, имеем:

  • Хотим иметь 0 по Y в нижней части картинки, чтобы это выглядело естественно (natural), привычно и, главное, чтобы координаты, нарисованные на экране приложения, совпадали с координатами, которые видно в GUI Inkscape (ну, и на станке)

Делать трансформ - зеркальное отражение по Y (со смещением)
плюсы:

  • "Рисуем" вручную тегами SVG, как мышкой в Инкскейпе - координаты в коде будут совпадать с координатами на экране и на станке

минусы:

  • Такой код будет не совсем стандартным по сравнению с другими примерами из инета, где "рисуют" тегами в стандартной системе координат.
  • Нельзя будет просто взять и загрузить картинку из обычного файла SVG (из файла или вставить напрямую код) - его придется сначала перевернуть по вертикали (придется пересчитывать координаты, как вариант - применить обратный трансформ).

Не делать трансформ - оставлять стандартную систему координат, рисовать элементы рабочего поля и маркеры по Y с предварительной конвертацией из визуальных координат в систему координат SVG
плюсы:

  • "Стандартный" код SVG, сторонние SVG можно загружать и вставлять как есть без предварительной конвертации.

минусы:

  • Головомойка с пересчетом реальных (визуальных) кооридинат в координаты SVG (с точками еще куда ни шло, а с четырехугольником вообще жоп@ - его базовая точка [x0,y0] в SVG левая верхняя, а для перевернух координат она будет как [x0,y0+rect_height], тк прямоугольник должен расти вправо и вниз без вариантов).
  • Диссонанс при наблюдении текста маркеров по оси Y и их реальных координат в коде

Еще вариант - вообще не переворачивать координаты, использовать 0 в левом верхнем углу
плюсы:

  • внешние файлы SVG загружаются как надо
  • не нужно заморачиваться со всей этой фигней

минусы

  • нестандартно для станка
  • в Inkscape и других рисовалках отображаемые координаты начинаются слева снизу
  • Наверняка все равно вылезут проблемы с другими форматами (тем же DXF), у которых отсчет координат может начинаться слева снизу

Третий вариант - не вариант. Второй уже попробовал в DrawingCanvas 6eb1203 : головомойка с пересчетом точек вылезла во всей красе (особенно, с прямоугольником) - абсолютная шизофрения. Стоит попробовать первый вариант - попробовать перевернуть глобальные координаты.

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Mar 1, 2017

Короче, вариант с перевернутыми координатами внутри группы g норм. Код получается можно сказать чистый (по сравнению с предыдущими ручными пересчетами). По поводу "нестандартного" кода я тоже перегнул - трансформы с переворотами вполне стандартны, люди позабористее вещи делают и ничего.

Всего имеем 3 прямоугольника:

  • SVG: область, занимаемая виджетом на экране, width/height можно указывать в пикселях экрана (width="800") или в процентах (width="100%")
  • viewBox - область просмотра. Вписывается автоматом в svg, сохраняет пропорции (по умолчанию, менять не будем). Единицы измерения - единицы измерения станка. Через нее можно делать зум (указать ширину в половину размера рабочей области станка) и перемещение в состоянии зума (менять). Для начала (и по умолчанию) размер viewBox будет охватывать все рабочее поле станка плюс небольшие отступы по краям, чтобы нарисовать систему координат, метки и линейку.
  • fold (в дальшейшем, возможно, переименую) - габариты рабочей области станка (dimX, dimY, dimZ и значения стартовых точек x0,y0,z0). Единица измерения - рабочая единица измерения станка (на экране прямоугольник автомтически отмасштабируется и впишется в прямоугольник SVG - хоть 100x200 хоть 10000x20000, главное, чтобы viewBox покрывал эту же область с теми же единицами).

В теге svg задаем параметры width/height для svg (размер виджета) и область просмотра viewBox.

<svg
                id="svg2"
                version="1.0"
                x="0.00000000"
                y="0.00000000"
                width={this.props.screen.width}
                height={this.props.screen.height}
                viewBox={"" + 
                    -30 + " " + 
                    -30 + " " + 
                    (this.props.fold.dimX + 60) + " " + 
                    (this.props.fold.dimY + 60)}>

Дальше группа - перевернутая и сдвинутая на место, как в Inkscape. Внутри группы координаты относительно нижнего левого угла прямогольника fold.dimX x fold.dimY

<g id="layer1"
                transform={"scale(1, -1)" + 
                    " translate(0, -" + this.props.fold.dimY + ")"
            }>
                <line
                    x1={0}
                    y1={0}
                    x2={this.props.fold.dimX}
                    y2={this.props.fold.dimY}
                    style={{fill:"none", stroke:"black", strokeWidth:1, strokeDasharray:"2 1"}}/>
                    
                <rect x="0" y="0" width={this.props.fold.dimX} height={this.props.fold.dimY}
                    style={{fill:"none", stroke:"black", strokeWidth:1, strokeDasharray:"2 1"}}/>
                    
                <text transform={"scale(1, 1)"}
                    x={-10} y={-10}
                    style={{fontSize: "12px"}}>0</text>
                <text transform={"scale(1, -1)"}
                    x={this.props.fold.dimX} y={-10}
                    style={{fontSize: "12px"}}>x</text>
                <text transform={"scale(1, -1)"}
                    x={-10} y={this.props.fold.dimY}
                    style={{fontSize: "12px"}}>y</text>
            </g>

Проблемы:

  • Текст рисуется на нужно месте, но перевернутым. Нужно переворачивать обратно через transform/scale. Но в этом случае еще одно неудобство - scale(1, -1) отрабатывает не от начала координат объекта, а от начала координат главного блока SVG текущего блока g, поэтому после поворота вокруг Y текст улетает, нужно смещать его обратно, при этом расстояние смещения будет в каждом случае свое (можно попробовать указывать x,y всегда нулями, потом делать scale, потом перемещать в нужную точку).
  • Размер текста и толщина линий отображаются пропорционально текущему масштабу. Если взять единицы измерения для рабочей области слишком маленькие (например, 10000x2000), линии прямогольника получатся слишком тонкими. Нужно как-то научиться рисовать линии одной толщины вне зависимости от текущего масштаба. Аналогично с разметом текста.
  • От идеи рисовать координаты, (в перспективе) линейку и другие вспомогательные элементы управления прямо на холсте рядом с фигурами рабочей области скорее всего все равно придется отказаться. При масштабе 1:1 все относительно ок, но при зуме и перемещении по рабочей области линейка будет улезать вместе с границами за пределы области видимости. Пожалуй, боковые элементы управления нужно выносить в отдельные виджеты.

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Mar 1, 2017

Немного про трансформации с SVG vs CSS (много несуразностей) - на тему переворачивания текста и масштабирования толщины линий

Transforms on SVG Elements
https://css-tricks.com/transforms-on-svg-elements/

SVG Animation and CSS Transforms: A Complicated Love Story
https://css-tricks.com/svg-animation-on-css-transforms/

из последнего ссылка на библиотеку для анимации с JS

GreenSock's GSAP
https://greensock.com/gsap

здесь тоже вычисляют расстояние для перемещения вручную
Technique: Scaling Around a Center Point
https://www.safaribooksonline.com/library/view/svg-essentials/0596002238/ch05s06.html

и еще про толщину линий и внешние размеры элемента (такой проблемы нет, но статья хорошая, пусть будет ссылка для коллекции)
Tight Fitting SVG Shapes, the Present and Future
https://css-tricks.com/tight-fitting-svg-shapes/

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Mar 1, 2017

Размер текста и толщина линий отображаются пропорционально текущему масштабу. Если взять единицы измерения для рабочей области слишком маленькие (например, 10000x2000), линии прямогольника получатся слишком тонкими. Нужно

С толщиной линии решается добавлением стиля vector-effect:"non-scaling-stroke"

<rect x="0" y="0" width={this.props.fold.dimX} height={this.props.fold.dimY}
                    style={{fill:"none", stroke:"black", strokeWidth:1, strokeDasharray:"3 3",
                    vectorEffect:"non-scaling-stroke"}}/>

обсуждают такую опцию в Инкскейпе
https://bugs.launchpad.net/inkscape/+bug/448286
http://javascript.ru/forum/misc/45703-masshtabirovanie-v-svg-bez-poteri-kachestva-2.html

с размером текста так не работает (очевидно)

на экране прямоугольник автомтически отмасштабируется и впишется в прямоугольник SVG - хоть 100x200 хоть 10000x20000, главное, чтобы viewBox покрывал эту же область с теми же единицами

Не совсем так. Если область viewBox больше, чем область SVG, то в меньшую сторону действительно отмасштабируется (прямоугольник сожмется и впишется в видимую область). Если viewBox меньше, чем область SVG, то прямоугольник останется маленьким (возможно, решается специальной опцией, была статья выше).

Еще. Если указать ширину viewBox порядка width=300000000 (300 млн - это 30см в нанометрах, высота аналогично), прямоугольник скукоживается в маленький в левый нижний угол (очевидно, баг), если width=30000000 (убрать один ноль), то ок (во всю область SVG). Очевидно, это где-то близко к пределу размерностей, с которыми работает движок отрисовки/масштабирования.

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Mar 1, 2017

По поводу поворота вокруг центра

можно попробовать указывать x,y всегда нулями, потом делать scale, потом перемещать в нужную точку

да, вот так выглядит норм:

  • нулем нужно указывать только Y,
  • нужное смещение вклеивать в строку translate с отрицательным знаком
                <text transform={"scale(1, -1)" + " translate(0, "+(10)+")"}
                    x={-10} y={0}
                    style={{fontSize: "12px"}}>0</text>
                <text transform={"scale(1, -1)" + " translate(0, "+(10)+")"}
                    x={this.props.fold.dimX} y={0}
                    style={{fontSize: "12px"}}>x</text>
                <text transform={"scale(1, -1)" + " translate(0, "+(-this.props.fold.dimY)+")"}
                    x={-10} y={0}
                    style={{fontSize: "12px"}}>y</text>

Но есть еще способ.

вот здесь

Understanding SVG Coordinate Systems and Transformations (Part 2) — The transform Attribute
https://sarasoueidan.com/blog/svg-transformations/

и здесь (здесь просто CSS)

2.20. CSS3 трансформации
https://html5book.ru/css3-transform/

пишут про какое-то новое свойство CSS transform-origin, которое можно задать как transform-origin: 50% 50%

вот так не работает (тект поворачивается вокруг начала координат)

<text transform={"rotate(45)"}
                    x={-10} y={50}
                    style={{fontSize: "12px", transformOrigin: "50% 50%"}}>AAA</text>

так тоже не работает (поворачивает вокруг начала координат)

<text
                    x={-10} y={50}
                    style={{fontSize: "12px", 
                    transformOrigin: "50% 50%", transform: "rotate(45deg)"}}>AAA</text>

для scale тем более.

В инспекторе ок, дело не в реакте

<text x="-10" y="50" style="font-size: 12px; transform: rotate(-45deg); transform-origin: 50% 50% 0px;">AAA</text>

ДОБАВЛЕНО
короче, вот так все работает

<text
                    x={0} y={-10}
                    style={{fontSize: "12px", 
                    transform: "scale(1, -1)", 
                    transformOrigin: "50% 50%"}}>AAA</text>
  • transform: scale внутри CSS,
  • значение y ставим отрицательное (т.к. координаты опять перевернулись)

вот так текст будет точно в начале координат повернут как нужно

<text
                    x={0} y={0}
                    style={{fontSize: "12px", 
                    transform: "scale(1, -1)", 
                    transformOrigin: "50% 50%"}}>AAA</text>

transformOrigin 50% 50%, кстати, в этом случае тоже не нужен, т.к. для CSS это значение, похоже, стоит по умолчанию.

Одна проблема, Inkscape это свойство не поддерживает (точнее, это пока не проблема, пока нет потребности экспортить-импортить такой код).

вот так координаты рисовать норм (значение по Y со знаком минус)

                <text
                    x={-10} y={10}
                    style={{fontSize: "12px", 
                        transform: "scale(1, -1)"}}>0</text>
                <text
                    x={this.props.fold.dimX} y={10}
                    style={{fontSize: "12px", 
                        transform: "scale(1, -1)"}}>x</text>
                <text
                    x={-10} y={-this.props.fold.dimY}
                    style={{fontSize: "12px", 
                        transform: "scale(1, -1)"}}>y</text>

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Mar 1, 2017

Еще про анимацию

https://sarasoueidan.com/blog/svg-transformations/

Animating transform
SVG transformations can be animated, just like CSS transforms can be. If you’re using the CSS transform property to transform the SVG, you can animate the transformation using CSS animations and transitions just like you would animate CSS transforms on HTML elements.

@sadr0b0t
Copy link
Owner Author

sadr0b0t commented Mar 3, 2017

Коммит с человеческим вариантом системы координат
d3697b3

  • рисует поле x,y
  • рисует точку - текущее положение рабочего инструмента
  • в связке с CncTaskControl точка перемещается за реальным положением из платы

Остались проблемы:

  • Плавная анимация перемещения точки
  • Проблема с масштабированием размеров шрифта и точки: если задать размер рабочей области 300000x200000, поле впишется в видимую область и даже будет видно границы рабочей области, но названия осей и точка исчезнут (слишком мелкие при таком масштабе). С полем 300x200 всё ок.

@sadr0b0t sadr0b0t changed the title Выбрать движок SVG для React, анимация, система координат Выбрать движок SVG для React: система координат Mar 7, 2017
@sadr0b0t
Copy link
Owner Author

Плавная анимация перемещения точки
ушло в отдельный тикет, готово
#3

Проблема с масштабированием размеров шрифта и точки: если задать размер рабочей области 300000x200000, поле впишется в видимую область и даже будет видно границы рабочей области, но названия осей и точка исчезнут (слишком мелкие при таком масштабе). С полем 300x200 всё ок.
Переносим сюда
Рисовать линейку (метки на осях координат) и рабочий инструмент одним размером вне зависимости от масштаба рабочей области
#8

Здесь основной вопрос порешали - рисуем ванильным SVG без левых прибамбасов, закрываю

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant