-
Notifications
You must be signed in to change notification settings - Fork 348
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
feat: Add generic SVG Element
shape to Style
#1824
Comments
tag
shape in Styleelement
shape to Style
For an experimental implementation, I would probably try for proposal (3) since this requires the least implementation effort. We simply don't provide constaints/objectives for |
element
shape to StyleElement
shape to Style
@liangyiliang I wonder if the shape myGroup = Group {
shapes: [ shape1, shape2, … ]
} with Element myGroup = g {
elements: [ shape1, shape2, … ]
} (You would best know from the implementation side, given that you implemented groups.) I guess the main counterargument is that it may hurt readability ( |
Element
shape to Styleelement
shape to Style
element
shape to StyleElement
shape to Style
I also realize now that the proposed syntax sort of breaks with our existing syntax. For instance, we currently write things like shape C = Circle { ... } Would it make more sense to have something like shape S = Element<elementname> { ... } ? For instance, shape gradient = Element<linearGradient> {
...
} This way we can still catch errors like
where the user mistakenly tries to define a Penrose shape of type |
Arbitrary elements also won't have any pre-defined geometry (like bounding boxes), whereas |
I don't think that's problematic. We can parse But during compilation, we check whether or not |
We can still do that. The logic is,
|
Whoops of course—you still want to optimize Groups. |
So, an important decision when we sis SVG passthrough was to not fo any checking on the field names. The issue that there are many versions and variants of SVG (and even moreso with HTML), and this puts a burden on us to manage that somehow. Likewise with element names. So, I think overall it's gonna be more straightforward to not so explicit checking—but also make the declaration of an Element more explicit in Style. |
Just to make sure I understand correctly: is this not a subset of what Style would gain from having functions? #894 |
I think it's more expressive than that. As far as I know it is not currently possible to generate arbitrary SVG nodes in Style. |
Makes sense to me! |
@keenancrane Just want to make sure we are on the same page in terms of design - here are some design decisions. First, should
? I think the answer should be "no" to maintain the conceptual consistency (which I think makes sense) that all built-in shapes (and hence all their children, if any) have geometry. Recall that at least for now, Then, if I want to make a group that contains a non-geometric element, then the entire group must be declared as a non-built-in element, like
Second, should built-in shapes be able to be a children of a non-built-in element, like
? I think the answer to this question is yes. Finally, I think there is no harm for non-built-in elements to participate in layering, so we should allow that. |
This issue is a stub to discuss the design of a potential Style language feature.
Currently we have a fixed set of shape definitions that will get rendered as SVG code (
Circle
,Line
,Rectangle
, etc.). The proposal is to allow a generic definition of the formThese tag definitions would be rendered by a direct translation into the corresponding SVG code
In some sense this is just an extension of the existing SVG passthrough functionality (with a similar set of warts).
Motivation
One of the original design goals for Penrose is that "There should be no inherent limit to visual sophistication." At present, there is quite a lot that can be expressed in the output format (SVG) but is not exposed through Penrose. Adding an
Element
construct to Style provides an "escape hatch" that exposes essentially all of SVG to Penrose, much in the same way that the passthrough feature already exposes significant additional functionality. It would for instance enable us to specify filters, animations, etc., which are currently not available through the fixed set of shape definitions.In principle this design also enables Penrose to generate arbitrary XML code, which need not conform to the SVG schema. For instance, one could immediately use Penrose to construct 3D diagrams in COLLADA format.
Finally, the design may also lighten the developer burden, in the sense that we do not have to expose such features in a one-by-one fashion. Moreover, it lightens the burden on the Penrose team to fully design and spec out system capabilities (gradients, animation, interactivity, etc.), enabling our user community to drive the development of new features in a more organic way. For instance, rather than trying to imagine what kinds of animation features and use cases might be relevant to users, we provide a generic mechanism that lets them specify whatever they want—albeit in a way that is low-level and perhaps not completely ergonomic. Common usage patterns can then be developed into more thoughtful language/system features as they emerge.
Of course, this design is not without some potential pitfalls—discussed below.
Nested Elements
SVG (and XML more generally) relies on the ability to specify nested tags. We already have support for nesting through the
Group
shape:Likewise, it would make sense to allow the proposed
Element
definitions to be nested. For instance, the definitionswould get rendered as the SVG code
producing an animated gradient like this:
Interaction with Optimization
A big challenge when designing anything for Penrose is that we are not simply building a renderer—we are also building an optimizer/layout engine. Here, a very legitimate concern is that attributes of the elements may be ignored by the optimizer. There are at least a couple paths we could take here:
Circle
shape that plays well with optimization, we could forbid the definitionElement C = circle
(which would otherwise generate a raw<circle>
element in the rendered SVG). This way, our handling of shape optimization is unchanged. A downside here, perhaps, is that we may lose some functionality inCircle
(e.g., using an animated gradient) unless this shape definition is also augmented to allowed childElements
.tangentTo( circle1, circle2 )
would need to be written asensure norm( circle1.center - circle2.center ) == circle1.r + circle2.r
. We already moved somewhat in this direction with @liangyiliang's revision of the Style library to allow most functions to be invoked directly (rather than through shapes). However, in addition to making some common functions more verbose, this makes it a bit harder to do generic programming (e.g.,tangentTo( x.icons, y.icons )
, where the icons forx
andy
may range over a wide variety of shapes).Element
s. This is perhaps a nice middle ground between the two above: we simply provide no Style functions that define constraints/objectives forElement
s. So for instance, someone can writeElement C = circle
, but, similar to proposal (1) can't pass this element in to a function likedisjoint
. Similar to proposal (2), they can still write their own objectives/constraints in terms of the scalar attributes of the elements (e.g., the center and radius). This adds a bit of cruft to the language, since there are now two ways to specify a circle: one that can be optimized, and one that can't. But if this functionality is for "experts only," perhaps it's an ok middle ground.Element
s special treatment. For instance, if a Style program statesdisjoint( x.element, y.element )
we could do inference on theelementname
(e.g., is it a circle?), sending it down the appropriate path if so. If attributes are specified that we do not support (e.g., the rotation of a rectangle), we can issue a warning that these attributes will be ignored for the purposes of layout. This path is perhaps the most "complete," but also requires the most development effort.As much as this issue is important to resolve, it is a bit tangential to the main use case for
Element
: incorporating "pass through" SVG features that we don't yet support (such as gradients and animations). The goal is not really to replace our basic shape definitions, which already work fine. (Though perhaps if there's some elegant solution, it's worth rolling shape definitions into this design someday down the line, for simplicity's sake.)Implementation
Implementation of a basic prototype shouldn't actually be so hard. A first step is to copy/paste/modify the existing
Group
shape, which already handles nesting. The main change that would need to be made to the parser is recognizing lines of the formwhere
elementname
is an arbitrary string (perhaps within some pattern), rather than one of a list of predefined shapes. Fortunately, we already do this kind of parsing for SVG passthrough (i.e., a field can have an arbitrary, unquoted name, without needing to be listed a priori).Interactions and Other Issues
There does not seem to be much interaction between this feature and other parts of the system, nor would it seem to cause any ambiguity for the parser. At its core, we're essentially just defining one more shape. (This shape just happens to be particularly flexible.)
Similar to SVG passthrough, SVG code generated in this way may not validate. Rather than place the validation burden on the Penrose compiler, Style programmers can simply rely on existing tools that do XML validation. A "convenience feature," perhaps, would be to integrate such validators with the IDE—but this is a somewhat orthogonal issue.
The text was updated successfully, but these errors were encountered: