Skip to content

Commit

Permalink
feat: PathString GPI (#549)
Browse files Browse the repository at this point in the history
* feat: PathString GPI

* fix: add rotation support back in

* chore: add comment details
  • Loading branch information
maxkrieger authored May 15, 2021
1 parent f90ee5c commit 2e9069d
Show file tree
Hide file tree
Showing 16 changed files with 255 additions and 25 deletions.
4 changes: 3 additions & 1 deletion examples/shape-spec/shape-spec.dsl
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ type Image

-- --
type Arrow
type Square
type Square

type PathString
12 changes: 11 additions & 1 deletion examples/shape-spec/shape-spec.sty
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ forall Arrow c {
forall Square d {
d.icon = Square {
rx: 0
rotation: 45
}
}

Expand All @@ -34,7 +35,8 @@ forall Line g {

forall Text h {
h.icon = Text {
string: "ahhhh"
string: "i am a label"
rotation: 45
}
}

Expand Down Expand Up @@ -75,3 +77,11 @@ forall FreeformPolygon u {
}
}

forall PathString p {
p.icon = PathString {
w: 300
h: 300
data: "M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80"
viewBox: "0 0 200 200"
}
}
6 changes: 4 additions & 2 deletions examples/shape-spec/shapes.sub
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ Rectangle F

-- -- Graphics Elements

-- Text H
Text H
-- Path A
-- Arrow C
-- Square D
Square D
-- Image I

PathString PS
18 changes: 13 additions & 5 deletions packages/browser-ui/src/inspector/views/Mod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface IState {
}

class Mod extends React.Component<IViewProps, IState> {
public readonly state = { selectedShape: -1 };
public readonly state = { selectedShape: 0 };
public setSelectedShape = (key: number) => {
this.setState({ selectedShape: key });
};
Expand All @@ -30,7 +30,7 @@ class Mod extends React.Component<IViewProps, IState> {
shape.properties[attrname] = attrval;
const newFrame = {
...frame!,
shapes: newShapes,
shapes: newShapes
} as PenroseState;
modShapes(newFrame);
} else throw new Error("Shape does not have property " + attrname + " .");
Expand All @@ -42,13 +42,21 @@ class Mod extends React.Component<IViewProps, IState> {
}

const { selectedShape } = this.state;
const def = defmap[frame.shapes[selectedShape].shapeType];
if (!def) {
return (
<div style={{ padding: "1em" }}>
{frame.shapes[selectedShape].shapeType} is not in defmap
</div>
);
}
return (
<div
style={{
display: "flex",
width: "100%",
height: "100%",
overflow: "hidden",
overflow: "hidden"
}}
>
{makeViewBoxes(frame.shapes, selectedShape, this.setSelectedShape)}
Expand All @@ -60,13 +68,13 @@ class Mod extends React.Component<IViewProps, IState> {
height: "100%",
flexBasis: 0,
flexGrow: 1,
boxSizing: "border-box",
boxSizing: "border-box"
}}
>
{frame.shapes[selectedShape] && (
<AttrPicker
shape={frame.shapes[selectedShape]}
sAttrs={defmap[frame.shapes[selectedShape].shapeType]}
sAttrs={def}
modAttr={this.modAttr}
/>
)}
Expand Down
3 changes: 2 additions & 1 deletion packages/browser-ui/src/inspector/views/mod/AttrPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ interface IShapeDef {
class AttrPicker extends React.Component<IProps> {
public render() {
const { sAttrs, shape, modAttr } = this.props;
if (!sAttrs.hasOwnProperty("properties"))
if (!sAttrs.hasOwnProperty("properties")) {
throw new Error("JSON missing the 'properties' attribute.");
}
return (
<div
id="attrPicker"
Expand Down
2 changes: 2 additions & 0 deletions packages/browser-ui/src/inspector/views/mod/defmap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ArrowDef from "./shapedefs/arrow.json";
import ImageDef from "./shapedefs/image.json";
import TextDef from "./shapedefs/text.json";
import CurveDef from "./shapedefs/curve.json";
import PathStrDef from "./shapedefs/pathStr.json";

const defMap = {
Circle: CircDef,
Expand All @@ -24,5 +25,6 @@ const defMap = {
Image: ImageDef,
Text: TextDef,
Path: CurveDef,
PathString: PathStrDef
};
export default defMap;
58 changes: 58 additions & 0 deletions packages/browser-ui/src/inspector/views/mod/shapedefs/pathStr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"shapeType": "PathString",
"properties": {
"center": {
"inputType": "ptrange",
"minX": "CANVASL",
"maxX": "CANVASR",
"minY": "CANVASB",
"maxY": "CANVAST",
"showValue": "true"
},
"color": {
"inputType": "color",
"showValue": "true"
},
"w": {
"inputType": "range",
"min": "0",
"max": "350",
"showValue": "true"
},
"rotation": {
"inputType": "range",
"min": "0",
"max": "360",
"showValue": "true"
},
"h": {
"inputType": "range",
"min": "0",
"max": "350",
"showValue": "true"
},
"strokeWidth": {
"inputType": "range",
"min": "0",
"max": "20",
"showValue": "true"
},
"strokeColor": {
"inputType": "color",
"showValue": "true"
},
"strokeStyle": {
"inputType": "select",
"options": ["solid", "dashed"],
"showValue": "false"
},
"data": {
"inputType": "text",
"showValue": "true"
},
"viewBox": {
"inputType": "text",
"showValue": "true"
}
}
}
3 changes: 1 addition & 2 deletions packages/browser-ui/src/inspector/views/viewMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import Settings from "./Settings";
const viewMap = {
frames: Frames,
errors: Errors,
// logs: LogView,
shapes: ShapeView,
mod: Mod,
opt: Opt,
settings: Settings,
settings: Settings
};
export default viewMap;
46 changes: 42 additions & 4 deletions packages/core/src/renderer/AttrHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
IStrV,
IPtListV,
ILListV,
Value,
} from "types/value";
import { Shape } from "types/shape";
import { toHex, toScreen } from "utils/Util";
Expand Down Expand Up @@ -106,6 +107,35 @@ export const attrXY = (
elem.setAttribute("y", (y - h.contents / 2).toString());
};

/**
* Rotates a GPI by n degrees about a center
* Note: elem must be `transform`able
* NOTE: must be called before transform translate coords (matrix rules)
* https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform
*/
export const attrRotation = (
{ properties }: Shape,
center: Value<number>,
w: Value<number>,
h: Value<number>,
canvasSize: [number, number],
elem: SVGElement
): void => {
const rotation = (properties.rotation as IFloatV<number>).contents;
const [x, y] = toScreen(center.contents as [number, number], canvasSize);
let transform = elem.getAttribute("transform");
transform =
transform == null
? `rotate(${rotation}, ${x - (w.contents as number) / 2}, ${
y - (h.contents as number) / 2
})`
: transform +
`rotate(${rotation}, ${x - (w.contents as number) / 2}, ${
y - (h.contents as number) / 2
})`;
elem.setAttribute("transform", transform);
};

export const attrSideCoords = (
{ properties }: Shape,
canvasSize: [number, number],
Expand All @@ -114,10 +144,13 @@ export const attrSideCoords = (
const center = properties.center as IVectorV<number>;
const [x, y] = toScreen(center.contents as [number, number], canvasSize);
const side = properties.side as IFloatV<number>;
elem.setAttribute(
"transform",
`translate(${x - side.contents / 2}, ${y - side.contents / 2})`
);
let transform = elem.getAttribute("transform");
transform =
transform == null
? `translate(${x - side.contents / 2}, ${y - side.contents / 2})`
: transform +
`translate(${x - side.contents / 2}, ${y - side.contents / 2})`;
elem.setAttribute("transform", transform);
};

export const attrRadius = ({ properties }: Shape, elem: SVGElement) => {
Expand Down Expand Up @@ -165,6 +198,11 @@ export const attrSide = ({ properties }: Shape, elem: SVGElement) => {
elem.setAttribute("height", side.contents.toString());
};

export const attrPathData = ({ properties }: Shape, elem: SVGElement) => {
const d = properties.data as IStrV<string>;
elem.setAttribute("d", d.contents.toString());
};

export const DASH_ARRAY = "7,5";

export const attrStroke = ({ properties }: Shape, elem: SVGElement) => {
Expand Down
16 changes: 15 additions & 1 deletion packages/core/src/renderer/Image.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { attrOpacity, attrTransformCoords, attrWH } from "./AttrHelper";
import {
attrOpacity,
attrRotation,
attrTransformCoords,
attrWH,
} from "./AttrHelper";
import { ShapeProps } from "./Renderer";
import images from "contrib/images.json";
import { IStrV } from "types/value";
Expand Down Expand Up @@ -42,6 +47,15 @@ const Image = ({ shape, canvasSize }: ShapeProps): SVGGElement => {
attrOpacity(shape, svg);
attrWH(shape, svg);
attrTransformCoords(shape, canvasSize, elem);
attrRotation(
shape,
shape.properties.center,
shape.properties.w,
shape.properties.h,
canvasSize,
elem
);

return elem;
};
export default Image;
16 changes: 15 additions & 1 deletion packages/core/src/renderer/Label.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { IStrV } from "types/value";
import { retrieveLabel } from "utils/CollectLabels";
import { attrFill, attrTitle, attrTransformCoords, attrWH } from "./AttrHelper";
import {
attrFill,
attrRotation,
attrTitle,
attrTransformCoords,
attrWH,
} from "./AttrHelper";
import { ShapeProps } from "./Renderer";

const Label = ({ shape, canvasSize, labels }: ShapeProps) => {
const elem = document.createElementNS("http://www.w3.org/2000/svg", "g");
attrRotation(
shape,
shape.properties.center,
shape.properties.w,
shape.properties.h,
canvasSize,
elem
);
attrTransformCoords(shape, canvasSize, elem);
attrTitle(shape, elem);
const name = shape.properties.name as IStrV<string>;
Expand Down
42 changes: 42 additions & 0 deletions packages/core/src/renderer/PathString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { IStrV } from "types/value";
import {
attrStroke,
attrTitle,
attrFill,
attrPathData,
attrWH,
attrXY,
attrRotation,
} from "./AttrHelper";
import { ShapeProps } from "./Renderer";

const PathString = ({ shape, canvasSize }: ShapeProps): SVGGElement => {
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
const elem = document.createElementNS("http://www.w3.org/2000/svg", "path");

attrRotation(
shape,
shape.properties.center,
shape.properties.w,
shape.properties.h,
canvasSize,
g
);

attrFill(shape, elem);
attrStroke(shape, elem);
attrTitle(shape, elem);
attrPathData(shape, elem);

attrWH(shape, svg);
attrXY(shape, canvasSize, svg);

const viewBox = shape.properties.viewBox as IStrV<string>;
svg.setAttribute("viewBox", viewBox.contents);

svg.appendChild(elem);
g.appendChild(svg);
return g;
};
export default PathString;
Loading

0 comments on commit 2e9069d

Please sign in to comment.