Giter Site home page Giter Site logo

mewpurpur / godsvg Goto Github PK

View Code? Open in Web Editor NEW
884.0 11.0 38.0 29.18 MB

An application in early development for creating simple vector graphics. Built in Godot.

License: MIT License

GDScript 98.38% Python 0.08% HTML 1.44% Shell 0.09%
gdscript godot godot-engine godotengine open-source svg-editor svg-parser vector-graphics thorvg svg

godsvg's Introduction

GodSVG

GodSVG logo

GodSVG is an editor for Scalable Vector Graphics (SVG) files. Unlike other editors, it represents the SVG code directly, doesn't add any metadata, and even lets you edit the SVG code in real time. GodSVG is inspired by the need for an SVG editor without abstractions that produces clean and optimized SVG files.

Important

GodSVG is not officially released, it's currently in late alpha.

GodSVG is almost entirely made by my work in my free time. If you like this project and want to help secure it through its development, you can use GitHub Sponsorship and make it a less financially stupid endeavor for me.

Features

  • Interactive SVG editing: Modify individual elements of an SVG file using a user-friendly interface.
  • Real-time code: As you manipulate elements in the UI, code is instantly generated and can be edited.
  • Optimized SVGs: The generated SVG files are small and efficient, and there are many options to assist with optimization.
  • Accessible on mobile: GodSVG aims to be usable on mobile devices. (but we're not there yet)

image

How to get it

Download the version you want from the list of GodSVG releases.

Note that if you're on MacOS, you need to disable Gatekeeper if you haven't yet. I don't have the time or money to deal with Apple's gatekeeping.

Link to the web build: https://mewpurpur.github.io/GodSVG/web-build (Currently experimental)

To run the latest unreleased version, you can download Godot from https://godotengine.org (development is currently happening in v4.2.2). After getting the repository files on your machine, you must open Godot, click on the "Import" button, and import the project.godot folder. If there are a lot of errors as some people have reported, it's Godot's fault. Try closing and opening the project a few times, changing small things on the code that errors out, etc. until the errors hopefully clear.

How to use it

Documentation for GodSVG is likely eventually going to be built-in. In the meantime, the basics of using it will be outlined here. This documentation is for the current master, which is a little ahead of the alpha 2 release.

If you want to import an existing graphic from scratch, use the Import button on top of the code editor or drag-and-drop an SVG file into the app.

To add new shapes, press the "+ Add new tag" button, right-click inside the viewport, or right-click inside the tags container. You can then select your shape from the dropdown. After your shape is added, you can drag its handles in the viewport to change its geometry, or modify the attributes in the inspector to change its other properties, like fill and stroke. You can also always modify the SVG code directly.

In the inspector, you can hover each tag's fields to see which attribute they represent. You may select tags in the viewport on the right or the inspector on the left, and right-click to do operations on them such as deleting or moving them. Some of these actions shortcuts, you can find them in the settings menu.

Pathdata attributes have a very complex editor that allows for selecting individual path commands with a lot of similarities to tags. You can right-click the path command and click "Insert After", then pick the one you want. If you're used to SVG paths, you can also use the M, L, H, V, Z, A, Q, T, C, S keys to insert a new path command after a selected one; pressing Shift will also make the new command absolute instead of relative.

Multiple tags or path commands can be selected as usual with Ctrl+Click and Shift+Click. Additionally, double-clicking a path command will select the whole subpath it's in.

To export the graphic, use the Export button on top of the code editor.

Community and contributing

Contributions are very welcome! GodSVG is built in Godot. For code contributions, read Contributing Guidelines. Before starting work on features, first propose them by using the issue form and wait for approval.

To report bugs or propose features, use Github's issue form. For more casual discussion around the tool or contributing to it, find me on GodSVG's Discord.

License

GodSVG is licensed under the MIT License:

  • You are free to use GodSVG for any purpose. GodSVG's license terms and copyright do not apply to the content created with it.
  • You can study how GodSVG works and change it.
  • You may distribute modified versions of GodSVG. Derivative products may use a different license, but they must still document that they derive from the MIT-licensed GodSVG.

The above explanation reflects my understanding of my own license terms and does not constitute legal advice.

godsvg's People

Contributors

aaronfranke avatar ajreckof avatar aladvs avatar alex2782 avatar devpoodle avatar ilikefrogs101 avatar kiisu-master avatar mewpurpur avatar qainguin avatar redfurrydemon avatar seremtitus avatar swarkin avatar thiagola92 avatar vovkiv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

godsvg's Issues

Invalid get index 'window_mode' (on base: 'null instance') error

I just cloned, imported it from the Project List and opened the project. Then without making any changes, I get this error:

Invalid get index 'window_mode' (on base: 'null instance') and its stopping in the "_enter_tree()" at like 45.

I want to try to use this, but I'm stuck here.

Implement grouping of certain properties in the inspector

Would be nice if related properties had a way to be grouped together, just visually, to reduce horizontal space and signify that they are related. The best example would be coordinates with X and Y components, as well as colors and their opacity attribute.

Add extra handles and helpers for bezier curve path commands

Cubic Bezier curves should draw two lines from each point to its in/out control, while quadratic Bezier curves should draw from both points to their single control. The lines should be smaller than normal contours. There should be handles for:

  • (x1, y1) and (x2, y2) in C
  • (x2, y2) in S
  • (x1, y1) in Q

Handles should probably have a draw_mode enum, that would be used to discern between normal handles and Bezier controls.

Implement an Import Error window

GodSVG is going to stick to the SVG basics and not support most of the SVG elements and attributes out there. This means that imported SVGs are often going to have undetected tags. A basic Import Error Window would work the following way:

  • Right after you select the SVG to import, its text is parsed and checked for syntax errors, and then it's also checked for unrecognized tags and attributes. The functions in the SVG singleton can be used for this. In case of no errors, the SVG is dropped in directly.
  • If there are syntax errors or unrecognized elements or attributes, GodSVG prompts the user with a dialog that shows how the SVG would display after passing, and explaining what the trouble is.

In the future, ways to correct the SVG could be implemented (e.g. expanding style attributes), but for an initial implementation this is not necessary.

Support <linarGradient> natively

An initial implementation should have the following:

  • Implement IDs.
  • Add relevant attributes of <linearGradient> and <stop>.
  • Add gradients in the color popup, next to the "none" color.

Would be great to also implement some kind of visual for how the gradient looks.

Z path commands aren't properly accounted for in calculations

I haven't investigated why yet, but paths very often have misplaced handles. This is tied to the Close Path command (z) and I'm assuming the problem is that it's not accounted for by the handles after in locate_start_points() of PathCommandArray.

Fixing this is high-priority and should be relatively straightforward.

Implement tag selecting

The minimum requirement to call this implemented would be to:

  • Have the ability to select tags in the inspector (typical selecting) - DONE
  • Have the ability to select handles on the preview (typical selecting)
  • Tags and handles are synced as they are selected.

Selecting a path tag is equivalent to selecting all of its path commands.

This will help with finding where a tag's handles are.

Missing namespace

The svg is missing the namespace which is needed for any app to render the svg. This is the xlmns parameter in the svg tag.

Implement Undo and Redo

Use Godot's UndoRedo to make actions reversible. Add Ctrl+Z as a shortcut; for Redo, allow both Ctrl+Y and Ctrl+Shift+Z. For an initial implementation, the whole SVG's state can be saved after each action, rebuilding all global definitions and tag editors in the process.

Interestingly, the code editor's own UndoRedo already works quite decently. But it considers every adjustment as an action, even if you're just holding a spinner arrow or dragging a handle.

Implement a setting to switch zoom direction

People have different preferences between zooming up/down to zoom in/out. I think we should accommodate that with a setting in GlobalSettings that's checked against when using the mouse scrollwheel.

Give GodSVG a better icon

Self-explanatory, GodSVG needs a good app icon. I'm thinking of keeping the blue flame, maybe even having some subtle reminiscence of Godot in its overall shape. I don't think the handle is necessary, or the eyes. The eyes are cute though, I'll try to keep them when/if I wind up making the design!

enables low-processor usage mode

set application setting application/run/low_processor_mode to true. Hence the screen is not redrawn if nothing changes visually ,this Godot feature is meant for applications

Extreme editor slowdown with large SVGs.

Turns out, high zoom levels in Image.load_svg cause extreme performance penalties, to the point where even a 16x16 texture causes lag spikes at the highest zoom levels.

I have no idea how to fix this right now, although #103 did improve the situation slightly by turning the contours texture into draw commands which cause less slowdown in the worst case and can also be optimized without sacrificing quality.

For this one, something different would be needed. Maybe constantly redrawing the SVG as you scroll around in the viewport, so a smaller section is drawn? (I fear this will cause stutters so maybe I'll have to think harder.

Draw outlines using draw() commands, rather than a second SVG

SVGs are really taxing on performance, so this should reduce the overhead of changing the SVG and zooming in/out by at least a little bit (probably by something like 40%).

This will also fix how these outlines aren't drawn outside the texture even if they stretch a little out.

Care should be taken to not draw a lot of lines out of bounds (like if you put x=999999 y=999999 as a curve end point in a path) as this might freeze the app if it's not done with care.

Short-term development plans

This list will be kept up-to-date.

Better icon

Self-explanatory.

Moving tags

A "Move" button on each tag, which should use drag-and-drop to be moved around.

Helpers/controls over the SVG image in the editor

All shape-related SVG tags should draw their own controls in the editor, for example, the ellipse tag should have a control for the center and for its two radii. There could also be an option for hiding them to see the clean SVG image, as well as to drag them around and update the corresponding SVG tag itself, but this isn't needed for the initial implementation.

Better stroke and fill editing from the inspector

Stroke and fill-related properties should be hidden when stroke or fill is set to none.

Improve the path editor

  • Paths commands should be able to be converted, for example LineTo to HorizontalLine would remove the y component.
  • When adding or converting path commands, invalid ones should be disabled (e.g. shorthand cubic without a cubic beforehand, or Close without a subpath)

Undo and Redo

Use Godot's UndoRedo to make actions reversible. Add Ctrl+Z as a shortcut; for Redo, allow both Ctrl+Y and Ctrl+Shift+Z. For an initial implementation, the whole SVG's state can be saved after each action, rebuilding all global definitions and tag editors in the process.

Support for gradients.

No implementation details on my mind yet, but @ajreckof seems to have something on mind.

Zooming improvement

Using touchpad gestures should zoom in such a way that the cursor becomes the center.

UI improvements

No implementation details on my mind yet. Feedback welcome.


Enhancements and bugfixes are always welcome, even if not on the list.

Implement the ability to move tags

For a start, it can be two arrows to make the tag move one step up or down. This is not so hard to anymore since the CodeEdit is now editable, but it would still be nice to have this functionality. I think the most challenging part here is figuring out a universal UI/UX that doesn't take too much space.

Inconsistent behavior in path command parameter editors

Clicking on a path command parameter editors after just editing a path command in not selecting it.

Can probably be fixed by making it so the path command editors are only rebuilt fully when there is a change in the command layout.

A/C path commands can offset the whole editor

image

Arcs and Curves are just too wide, when they stretch wider than the inspector, they can mess up the UI.

Yet, I feel like what needs to be done here is come up with a smaller widget for editing XY pairs of values. (#54)

Selected/hovered contours can pass under normal ones.

The draw calls are executed in order of the path commands. But that's wrong; instead, each color (selected/hovered/default) should have its lines/polylines saved in arrays for normal/selected/hovered and drawn at the end, first all normal ones, then all selected ones, then all hovered ones.

For a start, we could draw these as separate polylines, but ultimately it'll be a good idea to try to connect together lines into polylines as much as possible.

Make path commands individually selectable

Path Commands should be individually selectable. The selection should be mutually exclusive with tag selections (i.e. if you select a path command, you can't Ctrl+Click to select tags too, and vice versa). The selected path tag would highlight the handle in the display viewport and . Hovering should also have the usual indication.

Add duplicate option in the tag editor

Allow to duplicate tags from the same menu where the Move Up / Move Down options are. The deletion button can also be moved there to free up space (and not have a destructive operation easily available)

Show size of text next to the TextEdit

This would be a pre-requisite for future work on minifying inputs and optimizing SVGs. But long before work on that begins, it's going to just be a nice quality of life thing.

Limit new path commands to the currently allowed ones

  • M must always be the first command (maybe it should even be added by default when making a new path)
  • S should only go only after C
  • T should only go only after Q
  • M and Z shouldn't go after M or Z.

Commands that aren't allowed should be grayed out rather than removed.

Improve the Import dialog

It's an abomination. I should look into whether it can just use the default OS file manager.

If not, it still needs better theming - but it's really hard to work with the Godot node, so I hope I don't have to fix everything myself.

Path handles can't be dragged properly

Editing path data causes everything to rebuild.

This is very challenging. Paths have several ways they can be edited.

  • LineEdit changed or outside changes -> Change value -> Generate new path commands -> Rebuild inspector without autogenerating new value
  • Commands added or removed via UI -> Change path commands -> Rebuild inspector + Autogenerate new value
  • Values in a command changed via UI -> Change a single path command -> Autogenerate new value without rebuilding the inspector

But...

  • value has a setter that propagates the change to other parts of the codebase. It's also the thing that can be changed by other parts of the codebase.
  • So changing value should always update the inspector.
  • Yet, updating a single command via UI should update the value, but not the inspector.

How do I get out of this conundrum? I don't know, but it's a very high priority bugfix.

Elliptical arcs in `path` usually crash GodSVG

Steps to reproduce:

  1. Make a path
  2. Add a "MoveTo" command (M)
  3. Tweak the MoveTo command to be something other than (0, 0)
  4. Add an EllipticalArc command (A)

This freezes GodSVG for me on Ubuntu 22, Godot v4.1 rc1, and Godot is not leaving any trace. I have tried to patch it, but couldn't.

Rework the path editor's infrastructure

The path editor is a complex widget that's kind of dysfunctional at the moment. I want to untangle the mess now and make the LineEdit with the value editable.

The path editor, like all attribute editors, will hold a value variable that would be tied to changes to the main SVGData resource, which will allow it to communicate its changes externally. get modified externally. But this one is peculiar.

image

  • value here is the quirky string, but to have a clean way to edit the individual commands this string represents, we need some way to represent it as an array of objects. This is already done.
  • So we also need a commands property that would need to be synchronized with the value. This array is then used to populate the path editor with command editors.

This means that the following infrastructure is required:

  • When path commands are changed, they are parsed and a value is generated.
  • When the string is changed, e.g. by editing the LineEdit, it is parsed and commands are generated.
  • When the value or path command layout changes, the container of path commands editor must be rebuilt
  • When an individual path command changes, there should not be rebuilding, but the value still needs to be synced.

But let's look at it closely... There are the following four ways this value could be edited, and they need kind of contradictory side effects.

  • LineEdit changed or outside changes -> Change value -> Generate new path commands -> Rebuild inspector without autogenerating new value
  • Commands added or removed via UI -> Change path commands -> Rebuild inspector + Autogenerate new value
  • Values in a command changed via UI -> Change a single path command -> Autogenerate new value without rebuilding the inspector
  • Dragged handles (not implemented) -> Change a single path command -> Autogenerate new value, rebuilding the inspector is acceptable

So the value should change after all of these operations, but sometimes it should rebuild the inspector, and sometimes not.

Currently, changes that happen externally change the value variable, whose setter adjusts the property editor if needed. Normally this is not a problem, but here, changed value might mean that the path editor needs to be completely rebuilt (e.g. if you paste a pathdata into the CodeEdit with completely different commands). But this would be in the setter, so the whole path editor would need to be rebuilt every time the value changes, even from internal changes. This is actually undesired - if you click off from one number field into another - which should be common - you'd not be able to select it, because just then the path editor would rebuild.

There are some hacks that could fix this, but what I'm thinking of right now is to, instead of a full rebuild, do a check on the existing command editors and whether they are consistent with the path data being used for the rebuild. If there is an inconsistency in the numbers or flags, simply adjust them; and if there's an inconsistency in the commands, strip out the remaining command editors and rebuild the rest. This would have less overhead and avoid the aforementioned problem.

Dragged handles are also a challenge, right now they hold two SVGAttributes to be forced as their X and Y. I'm planning to make it so the generic handle is a base class, and there are two types of handles that inherit it - a NonPathHandle and a PathHandle. The NonPathHandle will be the current one, while the PathHandle will take in a single SVGPathAttribute and the index of the command it is to represent.

Since the path logic will be essentially split in two places, I will have a ParsedPathData class with static functions for parsing from a pathdata string to an array of path command objects, and in the opposite direction. The path editor will use them, and also the handles to know how to actually set the value they are to edit.


This should be a solid path infrastructure, hopefully nothing goes wrong. Tomorrow is gonna be fun for sure.

Fixing GodSVG's messy infrastructure.

Just writing out what's on my mind right now, as I try to figure out a good infrastructure that would allow to make SVG elements and attributes configurable from multiple places.

This post might be edited a few times as I rethink things.

New approach

SVG singleton

The core idea is that the SVG singleton will hold data, which will be the defacto way to manipulate anything about the SVG. This data will be independently able to be manipulated by:

  • The CodeEdit, which displays the code of the SVG. In the future, I want to make it editable. Manipulations will only happen when the SVG inside it is valid.
  • The inspector, which shows all the editable properties. Manipulations will happen when tags are moved, deleted, added, or attributes are changed.
  • The draggable control handles. Manipulations will happen when they are being dragged.

Data structures

As before, there will be:

  • SVGData, holds width, height, and an array of SVGTags. (I know this is just a tag, but right now I think this makes more sense)
  • SVGTag, with a title and a Dictionary of Strings to SVGAttributes. There are various types of SVGTags, each having their own titles and attributes.
  • SVGAttribute, with a type (e.g. color, int, flag, enum) and a value.

This is a rough simplification. So, these RefCounteds should also emit specific signals when they are changed.

  • SVGAttribute would have a value_changed() attribute. SVGTag would be subscribed to this and emit attribute_changed() itself.
  • SVGTag will have just the attribute_changed() signal for now. SVGData will be subscribed to this signal and emit attribute_changed() itself.
  • SVGData will have an attribute_changed() signal as explained. It will also have resized(), tag_deleted(), tag_added(), tag_moved(), and tags_changed_unknown() signals.

TODO: What about individual path commands in a path definition?

TODO: How to signal information about which specific things updated?

Display

The "Display" TextureRect will be subscribed to all SVGData signals with its queue_svg_update() function, always updating the SVG when something changes.

CodeEdit

Talking: When I make it editable, I plan to make it so after every edit, the text is checked for being a valid svg string. If it is, then gather from it all the values needed to make up a width, a height (I'll address viewBox later), and an array of SVGTags, and assign all that to the existing SVGData. This will emit the tags_changed_unknown() and resized() signals.

Listening: It will listen to all SVGData signals and use tags_to_string() to translate the data. But if it's currently being edited, it won't change its text. Unlike other ways to edit the SVG, we don't want to change the text to a standard generated one while the user is editing it.

Draggable handles

They will be handled in a TextureRect overlaid on top of the SVG.

Talking: Each handle will hold a reference to its the relevant SVGAttributes in the svg data: one for its x-pos, and one for its y-pos. While a handle is being dragged, it will use its X and Y position to update these two attributes.

Listening: All SVGData signals except for tag_moved() will be listened to. They will trigger a full rebuild of the handles, except for attribute_changed() which will search for the appropriate handle and update its x or y position.

TODO: What about individual path commands in a path definition?

Inspector

Talking: Each tag editor will hold a reference to its relevant SVGTag in the SVGData, and likewise each of the fields will hold a reference to its relevant SVGAttribute. The signals that will be emitted on each possible action are pretty self-explanatory.

Listening: All SVGData signals except for resized() will be listened to. tags_changed_unknown() will trigger a full rebuild, while the others will only change the relevant parts.

TODO: How to find which tag editor or attribute editor corresponds to a certain tag? Iterating and checking?

TODO: How to handle selections? Eventually, I want to have it so selecting a tag or a handle or such is reflected in all three places (e.g. clicking on a tag highlights its handles, and also highlights it subtly in the CodeEdit)

Issues with decoupled viewboxes

Problem 1: Viewbox is automatically coupled at the beginning.

Problem 2: Changing values is only possible from the code editor.

Problem 3: Certain shape contours (elliptical arcs) are fudged.

Evaluate expressions in number fields

Make things like sqrt(2), 2 + 5.6, 2,5 neatly evaluated in number fields. Godot's EditorSpinSlider has a really good system for this, I wonder if I can replicate it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.