├── .prettierignore ├── src ├── typings │ ├── jszip │ │ └── typings.d.ts │ ├── svgo │ │ └── typings.d.ts │ ├── bezier-js │ │ └── typings.d.ts │ ├── deep-freeze-strict │ │ └── typings.d.ts │ ├── element-resize-detector │ │ └── typings.d.ts │ ├── common.d.ts │ └── paper │ │ ├── HitOptions.d.ts │ │ ├── PlacedSymbol.d.ts │ │ ├── PointText.d.ts │ │ ├── Group.d.ts │ │ ├── TextItem.d.ts │ │ ├── Symbol.d.ts │ │ ├── Layer.d.ts │ │ ├── HitResult.d.ts │ │ ├── paper.d.ts │ │ └── CompoundPath.d.ts ├── app │ └── modules │ │ └── editor │ │ ├── components │ │ ├── splitter │ │ │ ├── splitter.component.html │ │ │ └── splitter.component.scss │ │ ├── project │ │ │ ├── index.ts │ │ │ └── project.service.ts │ │ ├── playback │ │ │ ├── index.ts │ │ │ ├── _playback-theme.scss │ │ │ ├── playback.component.html │ │ │ ├── playback.component.ts │ │ │ └── playback.component.scss │ │ ├── layertimeline │ │ │ ├── constants.ts │ │ │ ├── index.ts │ │ │ ├── _timelineanimationrow-theme.scss │ │ │ ├── timelineanimationrow.component.scss │ │ │ ├── _layerlisttree-theme.scss │ │ │ └── timelineanimationrow.component.html │ │ ├── propertyinput │ │ │ ├── index.ts │ │ │ ├── _propertyinput-theme.scss │ │ │ └── InspectedProperty.ts │ │ ├── dialogs │ │ │ ├── confirmdialog.component.scss │ │ │ ├── dropfilesdialog.component.scss │ │ │ ├── index.ts │ │ │ ├── _dialog-theme.scss │ │ │ ├── demodialog.component.scss │ │ │ ├── confirmdialog.component.ts │ │ │ ├── demodialog.component.ts │ │ │ ├── dialog.service.ts │ │ │ └── dropfilesdialog.component.ts │ │ ├── splashscreen │ │ │ ├── splashscreen.component.html │ │ │ ├── splashscreen.component.ts │ │ │ └── splashscreen.component.scss │ │ ├── canvas │ │ │ ├── _canvas-theme.scss │ │ │ ├── index.ts │ │ │ ├── canvascontainer.directive.ts │ │ │ ├── canvas.component.scss │ │ │ ├── canvas.component.html │ │ │ ├── canvaspaper.directive.ts │ │ │ ├── CanvasUtil.ts │ │ │ └── CanvasLayoutMixin.ts │ │ ├── toolpanel │ │ │ ├── toolpanel.component.scss │ │ │ └── toolpanel.component.ts │ │ ├── root │ │ │ ├── _root-theme.scss │ │ │ └── root.component.html │ │ ├── toolbar │ │ │ ├── _toolbar-theme.scss │ │ │ └── toolbar.component.scss │ │ └── scrollgroup │ │ │ └── scrollgroup.directive.ts │ │ ├── scripts │ │ ├── dragger │ │ │ └── index.ts │ │ ├── paper │ │ │ ├── gesture │ │ │ │ ├── index.ts │ │ │ │ ├── hover │ │ │ │ │ ├── index.ts │ │ │ │ │ └── HoverGesture.ts │ │ │ │ ├── scale │ │ │ │ │ └── index.ts │ │ │ │ ├── transform │ │ │ │ │ └── index.ts │ │ │ │ ├── rotate │ │ │ │ │ ├── index.ts │ │ │ │ │ └── RotateItemsDragPivotGesture.ts │ │ │ │ ├── create │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── EllipseGesture.ts │ │ │ │ │ └── RectangleGesture.ts │ │ │ │ ├── select │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── DeselectItemGesture.ts │ │ │ │ │ ├── EditPathGesture.ts │ │ │ │ │ └── BatchSelectItemsGesture.ts │ │ │ │ ├── edit │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ToggleSegmentHandlesGesture.ts │ │ │ │ └── Gesture.ts │ │ │ ├── detector │ │ │ │ ├── index.ts │ │ │ │ └── Handler.ts │ │ │ ├── tool │ │ │ │ ├── index.ts │ │ │ │ ├── Tool.ts │ │ │ │ └── MasterToolPicker.ts │ │ │ ├── util │ │ │ │ ├── snap │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── Constants.ts │ │ │ │ │ └── SnapBounds.ts │ │ │ │ ├── index.ts │ │ │ │ └── PivotType.ts │ │ │ ├── item │ │ │ │ ├── index.ts │ │ │ │ ├── EditPathRaster.ts │ │ │ │ ├── RotateItemsPivotRaster.ts │ │ │ │ └── SelectionBoundsRaster.ts │ │ │ └── index.ts │ │ ├── intervals │ │ │ ├── index.ts │ │ │ └── IntervalTree.ts │ │ ├── mixins │ │ │ ├── index.ts │ │ │ └── DestroyableMixin.ts │ │ ├── animator │ │ │ └── index.ts │ │ ├── algorithms │ │ │ ├── index.ts │ │ │ ├── AutoAwesome.spec.ts │ │ │ └── NeedlemanWunsch.ts │ │ ├── common │ │ │ ├── Point.ts │ │ │ ├── Rect.ts │ │ │ ├── index.ts │ │ │ ├── MathUtil.spec.ts │ │ │ ├── Matrix.spec.ts │ │ │ ├── ColorUtil.spec.ts │ │ │ └── MathUtil.ts │ │ ├── actionmode │ │ │ ├── index.ts │ │ │ └── ActionModeUtil.ts │ │ ├── import │ │ │ └── index.ts │ │ ├── export │ │ │ └── index.ts │ │ ├── demos │ │ │ └── index.ts │ │ └── bugsnag │ │ │ └── index.ts │ │ ├── model │ │ ├── interpolators │ │ │ └── index.ts │ │ ├── paper │ │ │ ├── index.ts │ │ │ ├── ToolMode.ts │ │ │ └── CursorType.ts │ │ ├── paths │ │ │ ├── calculators │ │ │ │ ├── index.ts │ │ │ │ ├── MoveCalculator.ts │ │ │ │ ├── PointCalculator.ts │ │ │ │ └── Calculator.ts │ │ │ ├── SvgChar.ts │ │ │ ├── index.ts │ │ │ └── PathUtil.ts │ │ ├── actionmode │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── timeline │ │ │ ├── index.ts │ │ │ └── Animation.ts │ │ ├── layers │ │ │ ├── index.ts │ │ │ └── layers.spec.ts │ │ └── properties │ │ │ ├── PropertyMaps.ts │ │ │ ├── index.ts │ │ │ ├── FractionProperty.ts │ │ │ ├── NameProperty.ts │ │ │ ├── EnumProperty.ts │ │ │ ├── ColorProperty.ts │ │ │ ├── PathProperty.ts │ │ │ └── NumberProperty.ts │ │ ├── styles │ │ ├── app.scss │ │ ├── root.scss │ │ └── material-icons.scss │ │ ├── store │ │ ├── index.ts │ │ ├── reset │ │ │ ├── selectors.ts │ │ │ ├── reducer.ts │ │ │ ├── actions.ts │ │ │ └── metareducer.ts │ │ ├── selectors.ts │ │ ├── theme │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── batch │ │ │ ├── actions.ts │ │ │ └── metareducer.ts │ │ ├── storefreeze │ │ │ └── metareducer.ts │ │ ├── layers │ │ │ ├── selectors.ts │ │ │ ├── reducer.ts │ │ │ └── actions.ts │ │ ├── timeline │ │ │ ├── reducer.ts │ │ │ ├── actions.ts │ │ │ └── selectors.ts │ │ ├── playback │ │ │ ├── reducer.ts │ │ │ ├── selectors.ts │ │ │ └── actions.ts │ │ ├── undoredo │ │ │ └── metareducer.ts │ │ ├── actionmode │ │ │ └── actions.ts │ │ ├── reducer.ts │ │ └── common │ │ │ └── selectors.ts │ │ └── services │ │ ├── snackbar.service.ts │ │ ├── StoreUtil.ts │ │ ├── index.ts │ │ └── theme.service.ts ├── favicon.ico ├── assets │ ├── cursor │ │ ├── pen.png │ │ ├── pencil.png │ │ ├── pen-add.png │ │ ├── crosshair.png │ │ ├── pen-close.png │ │ ├── rotate-left.png │ │ ├── rotate-top.png │ │ ├── point-select.png │ │ ├── rotate-bottom.png │ │ ├── rotate-right.png │ │ ├── rotate-top-left.png │ │ ├── rotate-top-right.png │ │ ├── rotate-bottom-left.png │ │ └── rotate-bottom-right.png │ ├── screencap.jpg │ ├── shapeshifter.png │ ├── paper │ │ ├── vector-handle.png │ │ ├── vector-segment.png │ │ ├── rotate-items-pivot.png │ │ ├── vector-handle-selected.png │ │ ├── selection-bounds-segment.png │ │ ├── transform-bounds-segment.png │ │ ├── vector-segment-selected.png │ │ └── transform-bounds-segment-selected.png │ ├── icons │ │ ├── pauseplay-white.png │ │ ├── playpause-white.png │ │ ├── reverse.svg │ │ ├── autofix.svg │ │ ├── addlayer.svg │ │ ├── grouplayer.svg │ │ ├── collection.svg │ │ ├── animationblock.svg │ │ ├── vectorlayer.svg │ │ ├── clippathlayer.svg │ │ ├── animation.svg │ │ ├── contribute.svg │ │ └── pathlayer.svg │ ├── tools │ │ ├── tool_select.svg │ │ ├── tool_rectangle.svg │ │ ├── tool_vector.svg │ │ ├── tool_pencil.svg │ │ ├── tool_zoompan.svg │ │ └── tool_ellipse.svg │ └── shapeshifter.svg ├── styles.scss ├── environments │ ├── environment.ts │ ├── environment.beta.dev.ts │ ├── environment.beta.prod.ts │ ├── environment.stable.prod.ts │ └── version.ts ├── tsconfig.spec.json ├── manifest.json ├── tsconfig.app.json ├── main.ts ├── test.ts ├── index.html └── demos │ └── playtopause.shapeshifter ├── art ├── cast.gif ├── animals.gif ├── digits.gif ├── moreback.gif ├── plusminus.gif ├── screencap.gif ├── drawerarrow.gif ├── expandcollapse.gif ├── playpausestop.gif └── droidcon-sf-video-embed.png ├── scripts ├── deploy-stable.sh ├── deploy-beta.sh └── check-node-version.js ├── .prettierrc ├── e2e ├── app.po.ts ├── app.e2e-spec.ts └── tsconfig.e2e.json ├── coverconfig.json ├── .editorconfig ├── ngsw-config.json ├── .vscode └── settings.json ├── .travis.yml ├── .gitignore ├── tsconfig.json ├── protractor.conf.js └── karma.conf.js /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | -------------------------------------------------------------------------------- /src/typings/jszip/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'jszip'; 2 | -------------------------------------------------------------------------------- /src/typings/svgo/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'svgo/*'; 2 | -------------------------------------------------------------------------------- /src/typings/bezier-js/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'bezier-js'; 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/splitter/splitter.component.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /art/cast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/cast.gif -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/dragger/index.ts: -------------------------------------------------------------------------------- 1 | export { Dragger } from './Dragger'; 2 | -------------------------------------------------------------------------------- /src/typings/deep-freeze-strict/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'deep-freeze-strict'; 2 | -------------------------------------------------------------------------------- /art/animals.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/animals.gif -------------------------------------------------------------------------------- /art/digits.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/digits.gif -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /art/moreback.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/moreback.gif -------------------------------------------------------------------------------- /art/plusminus.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/plusminus.gif -------------------------------------------------------------------------------- /art/screencap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/screencap.gif -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/index.ts: -------------------------------------------------------------------------------- 1 | export { Gesture } from './Gesture'; 2 | -------------------------------------------------------------------------------- /src/typings/element-resize-detector/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'element-resize-detector'; 2 | -------------------------------------------------------------------------------- /art/drawerarrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/drawerarrow.gif -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/intervals/index.ts: -------------------------------------------------------------------------------- 1 | export { IntervalTree } from './IntervalTree'; 2 | -------------------------------------------------------------------------------- /art/expandcollapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/expandcollapse.gif -------------------------------------------------------------------------------- /art/playpausestop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/playpausestop.gif -------------------------------------------------------------------------------- /src/app/modules/editor/components/project/index.ts: -------------------------------------------------------------------------------- 1 | export { ProjectService } from './project.service'; 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/mixins/index.ts: -------------------------------------------------------------------------------- 1 | export { DestroyableMixin } from './DestroyableMixin'; 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/detector/index.ts: -------------------------------------------------------------------------------- 1 | export { ClickDetector } from './ClickDetector'; 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/playback/index.ts: -------------------------------------------------------------------------------- 1 | export { PlaybackComponent } from './playback.component'; 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/animator/index.ts: -------------------------------------------------------------------------------- 1 | export { AnimationRenderer } from './AnimationRenderer'; 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/hover/index.ts: -------------------------------------------------------------------------------- 1 | export { HoverGesture } from './HoverGesture'; 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/tool/index.ts: -------------------------------------------------------------------------------- 1 | export { MasterToolPicker } from './MasterToolPicker'; 2 | -------------------------------------------------------------------------------- /src/assets/cursor/pen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/pen.png -------------------------------------------------------------------------------- /src/assets/screencap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/screencap.jpg -------------------------------------------------------------------------------- /src/app/modules/editor/components/layertimeline/constants.ts: -------------------------------------------------------------------------------- 1 | export const TIMELINE_ANIMATION_PADDING = 20; // 20px 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/interpolators/index.ts: -------------------------------------------------------------------------------- 1 | export { Interpolator, INTERPOLATORS } from './Interpolator'; 2 | -------------------------------------------------------------------------------- /src/assets/cursor/pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/pencil.png -------------------------------------------------------------------------------- /src/assets/shapeshifter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/shapeshifter.png -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/scale/index.ts: -------------------------------------------------------------------------------- 1 | export { ScaleItemsGesture } from './ScaleItemsGesture'; 2 | -------------------------------------------------------------------------------- /src/assets/cursor/pen-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/pen-add.png -------------------------------------------------------------------------------- /art/droidcon-sf-video-embed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/art/droidcon-sf-video-embed.png -------------------------------------------------------------------------------- /src/app/modules/editor/components/propertyinput/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyInputComponent } from './propertyinput.component'; 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/algorithms/index.ts: -------------------------------------------------------------------------------- 1 | import * as AutoAwesome from './AutoAwesome'; 2 | export { AutoAwesome }; 3 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/util/snap/index.ts: -------------------------------------------------------------------------------- 1 | import * as SnapUtil from './SnapUtil'; 2 | export { SnapUtil }; 3 | -------------------------------------------------------------------------------- /src/assets/cursor/crosshair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/crosshair.png -------------------------------------------------------------------------------- /src/assets/cursor/pen-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/pen-close.png -------------------------------------------------------------------------------- /src/assets/cursor/rotate-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/rotate-left.png -------------------------------------------------------------------------------- /src/assets/cursor/rotate-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/rotate-top.png -------------------------------------------------------------------------------- /src/app/modules/editor/model/paper/index.ts: -------------------------------------------------------------------------------- 1 | export { ToolMode } from './ToolMode'; 2 | export { CursorType } from './CursorType'; 3 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/common/Point.ts: -------------------------------------------------------------------------------- 1 | export interface Point { 2 | readonly x: number; 3 | readonly y: number; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/transform/index.ts: -------------------------------------------------------------------------------- 1 | export { TransformPathsGesture } from './TransformPathsGesture'; 2 | -------------------------------------------------------------------------------- /src/assets/cursor/point-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/point-select.png -------------------------------------------------------------------------------- /src/assets/cursor/rotate-bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/rotate-bottom.png -------------------------------------------------------------------------------- /src/assets/cursor/rotate-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/rotate-right.png -------------------------------------------------------------------------------- /src/assets/paper/vector-handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/paper/vector-handle.png -------------------------------------------------------------------------------- /src/assets/paper/vector-segment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/paper/vector-segment.png -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/actionmode/index.ts: -------------------------------------------------------------------------------- 1 | import * as ActionModeUtil from './ActionModeUtil'; 2 | export { ActionModeUtil }; 3 | -------------------------------------------------------------------------------- /src/assets/cursor/rotate-top-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/rotate-top-left.png -------------------------------------------------------------------------------- /src/assets/cursor/rotate-top-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/rotate-top-right.png -------------------------------------------------------------------------------- /src/assets/icons/pauseplay-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/icons/pauseplay-white.png -------------------------------------------------------------------------------- /src/assets/icons/playpause-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/icons/playpause-white.png -------------------------------------------------------------------------------- /src/app/modules/editor/model/paths/calculators/index.ts: -------------------------------------------------------------------------------- 1 | export { Calculator, newCalculator, Projection, BBox, Line } from './Calculator'; 2 | -------------------------------------------------------------------------------- /src/assets/cursor/rotate-bottom-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/rotate-bottom-left.png -------------------------------------------------------------------------------- /src/assets/paper/rotate-items-pivot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/paper/rotate-items-pivot.png -------------------------------------------------------------------------------- /src/app/modules/editor/model/actionmode/index.ts: -------------------------------------------------------------------------------- 1 | export { ActionMode, ActionSource, Hover, HoverType, Selection, SelectionType } from './types'; 2 | -------------------------------------------------------------------------------- /src/assets/cursor/rotate-bottom-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/cursor/rotate-bottom-right.png -------------------------------------------------------------------------------- /src/assets/paper/vector-handle-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/paper/vector-handle-selected.png -------------------------------------------------------------------------------- /src/app/modules/editor/styles/app.scss: -------------------------------------------------------------------------------- 1 | // Core page layouts and common stuff. 2 | @import 'root'; 3 | // Material design theme. 4 | @import 'theme'; 5 | -------------------------------------------------------------------------------- /src/assets/paper/selection-bounds-segment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/paper/selection-bounds-segment.png -------------------------------------------------------------------------------- /src/assets/paper/transform-bounds-segment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/paper/transform-bounds-segment.png -------------------------------------------------------------------------------- /src/assets/paper/vector-segment-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/paper/vector-segment-selected.png -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | @import './app/modules/editor/styles/app.scss'; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | beta: false, 4 | analyticsTrackingId: 'UA-92075411-1', 5 | }; 6 | -------------------------------------------------------------------------------- /src/environments/environment.beta.dev.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | beta: true, 4 | analyticsTrackingId: 'UA-92075411-2', 5 | }; 6 | -------------------------------------------------------------------------------- /src/environments/environment.beta.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | beta: true, 4 | analyticsTrackingId: 'UA-92075411-2', 5 | }; 6 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/paths/SvgChar.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The different types of supported SVG commands. 3 | */ 4 | export type SvgChar = 'M' | 'L' | 'Q' | 'C' | 'Z'; 5 | -------------------------------------------------------------------------------- /src/assets/paper/transform-bounds-segment-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexjlockwood/ShapeShifter/HEAD/src/assets/paper/transform-bounds-segment-selected.png -------------------------------------------------------------------------------- /src/environments/environment.stable.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | beta: false, 4 | analyticsTrackingId: 'UA-92075411-1', 5 | }; 6 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/index.ts: -------------------------------------------------------------------------------- 1 | export { StoreModule, Store, Action, ActionReducer } from '@ngrx/store'; 2 | export { State, reducers, metaReducers } from './reducer'; 3 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/common/Rect.ts: -------------------------------------------------------------------------------- 1 | export interface Rect { 2 | readonly l: number; 3 | readonly t: number; 4 | readonly r: number; 5 | readonly b: number; 6 | } 7 | -------------------------------------------------------------------------------- /scripts/deploy-stable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | npm i 4 | npm run build 5 | echo "shapeshifter.design" > dist/CNAME 6 | ngh --repo git@github.com:alexjlockwood/ShapeShifterStable.git 7 | -------------------------------------------------------------------------------- /scripts/deploy-beta.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | npm i 4 | npm run build-beta 5 | echo "beta.shapeshifter.design" > dist/CNAME 6 | ngh --repo git@github.com:alexjlockwood/ShapeShifterBeta.git 7 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/import/index.ts: -------------------------------------------------------------------------------- 1 | import * as SvgLoader from './SvgLoader'; 2 | import * as VectorDrawableLoader from './VectorDrawableLoader'; 3 | export { SvgLoader, VectorDrawableLoader }; 4 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/rotate/index.ts: -------------------------------------------------------------------------------- 1 | export { RotateItemsGesture } from './RotateItemsGesture'; 2 | export { RotateItemsDragPivotGesture } from './RotateItemsDragPivotGesture'; 3 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/util/index.ts: -------------------------------------------------------------------------------- 1 | import * as PaperUtil from './PaperUtil'; 2 | export { PaperUtil }; 3 | export { PivotType } from './PivotType'; 4 | export { SnapUtil } from './snap'; 5 | -------------------------------------------------------------------------------- /src/environments/version.ts: -------------------------------------------------------------------------------- 1 | import { environment } from './environment'; 2 | 3 | export const version = `${require('../../package.json').version}-${ 4 | environment.beta ? 'beta' : 'stable' 5 | }`; 6 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/paper/ToolMode.ts: -------------------------------------------------------------------------------- 1 | export enum ToolMode { 2 | Default = 'Default', 3 | Pencil = 'Pencil', 4 | Ellipse = 'Ellipse', 5 | Rectangle = 'Rectangle', 6 | ZoomPan = 'ZoomPan', 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/tools/tool_select.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/create/index.ts: -------------------------------------------------------------------------------- 1 | export { RectangleGesture } from './RectangleGesture'; 2 | export { EllipseGesture } from './EllipseGesture'; 3 | export { PencilGesture } from './PencilGesture'; 4 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/timeline/index.ts: -------------------------------------------------------------------------------- 1 | export { Animation } from './Animation'; 2 | export { 3 | AnimationBlock, 4 | PathAnimationBlock, 5 | ColorAnimationBlock, 6 | NumberAnimationBlock, 7 | } from './AnimationBlock'; 8 | -------------------------------------------------------------------------------- /src/assets/tools/tool_rectangle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/item/index.ts: -------------------------------------------------------------------------------- 1 | import * as HitTests from './HitTests'; 2 | export { HitTests }; 3 | 4 | export { PaperLayer, HitResult } from './PaperLayer'; 5 | export { SelectionBoundsRaster } from './SelectionBoundsRaster'; 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "overrides": [ 6 | { 7 | "files": "*.scss", 8 | "options": { 9 | "tabWidth": 4 10 | } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/util/PivotType.ts: -------------------------------------------------------------------------------- 1 | export type PivotType = 2 | | 'bottomLeft' 3 | | 'leftCenter' 4 | | 'topLeft' 5 | | 'topCenter' 6 | | 'topRight' 7 | | 'rightCenter' 8 | | 'bottomRight' 9 | | 'bottomCenter'; 10 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/dialogs/confirmdialog.component.scss: -------------------------------------------------------------------------------- 1 | button { 2 | text-transform: uppercase; 3 | &:mat-dialog-close { 4 | margin-right: 8px; 5 | } 6 | } 7 | 8 | mat-dialog-actions { 9 | min-width: 220px; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/dialogs/dropfilesdialog.component.scss: -------------------------------------------------------------------------------- 1 | button { 2 | text-transform: uppercase; 3 | &:mat-dialog-close { 4 | margin-right: 8px; 5 | } 6 | } 7 | 8 | mat-dialog-actions { 9 | min-width: 220px; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/export/index.ts: -------------------------------------------------------------------------------- 1 | import * as AvdSerializer from './AvdSerializer'; 2 | import * as SpriteSerializer from './SpriteSerializer'; 3 | import * as SvgSerializer from './SvgSerializer'; 4 | export { AvdSerializer, SpriteSerializer, SvgSerializer }; 5 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | export class ShapeShifterPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /coverconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": true, 3 | "relativeSourcePath": "../src", 4 | "relativeCoverageDir": "../../coverage", 5 | "ignorePatterns": ["**/node_modules/**"], 6 | "includePid": false, 7 | "reports": ["json", "html", "lcov"], 8 | "verbose": false 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = true 14 | -------------------------------------------------------------------------------- /src/app/modules/editor/styles/root.scss: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | width: 100%; 6 | height: 100%; 7 | overflow: hidden; 8 | } 9 | 10 | $mdFontFamily: Roboto, 11 | 'Helvetica Neue', 12 | sans-serif !default; 13 | html { 14 | font-family: $mdFontFamily; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/reset/selectors.ts: -------------------------------------------------------------------------------- 1 | import { getEditorState } from 'app/modules/editor/store/selectors'; 2 | import { createSelector } from 'reselect'; 3 | 4 | const getResetState = createSelector(getEditorState, s => s.reset); 5 | export const isBeingReset = createSelector(getResetState, r => r.isBeingReset); 6 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/dialogs/index.ts: -------------------------------------------------------------------------------- 1 | export { ConfirmDialogComponent } from './confirmdialog.component'; 2 | export { DemoDialogComponent } from './demodialog.component'; 3 | export { DropFilesDialogComponent, DropFilesAction } from './dropfilesdialog.component'; 4 | export { DialogService } from './dialog.service'; 5 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/select/index.ts: -------------------------------------------------------------------------------- 1 | export { BatchSelectItemsGesture } from './BatchSelectItemsGesture'; 2 | export { DeselectItemGesture } from './DeselectItemGesture'; 3 | export { EditPathGesture } from './EditPathGesture'; 4 | export { SelectDragCloneItemsGesture } from './SelectDragCloneItemsGesture'; 5 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/layers/index.ts: -------------------------------------------------------------------------------- 1 | import * as LayerUtil from './LayerUtil'; 2 | export { LayerUtil }; 3 | 4 | export { 5 | Layer, 6 | ClipPathLayer, 7 | VectorLayer, 8 | GroupLayer, 9 | PathLayer, 10 | StrokeLineCap, 11 | StrokeLineJoin, 12 | FillType, 13 | MorphableLayer, 14 | } from './Layer'; 15 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/layertimeline/index.ts: -------------------------------------------------------------------------------- 1 | export { LayerListTreeComponent } from './layerlisttree.component'; 2 | export { LayerTimelineComponent } from './layertimeline.component'; 3 | export { LayerTimelineGridDirective } from './layertimelinegrid.directive'; 4 | export { TimelineAnimationRowComponent } from './timelineanimationrow.component'; 5 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["jasmine", "node"] 9 | }, 10 | "files": ["test.ts", "polyfills.ts"], 11 | "include": ["**/*.spec.ts", "**/*.d.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/splashscreen/splashscreen.component.html: -------------------------------------------------------------------------------- 1 |
3 | 4 | 7 | 8 |
Shape Shifter is an icon animation tool designed for desktop browsers
9 | 10 |
11 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/paths/index.ts: -------------------------------------------------------------------------------- 1 | import * as PathUtil from './PathUtil'; 2 | 3 | export { PathUtil }; 4 | export { Projection, Line } from './calculators'; 5 | export { SvgChar } from './SvgChar'; 6 | export { Path, HitOptions, HitResult, ProjectionOntoPath, PathMutator } from './Path'; 7 | export { SubPath } from './SubPath'; 8 | export { Command } from './Command'; 9 | -------------------------------------------------------------------------------- /src/assets/icons/reverse.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { ShapeShifterPage } from './app.po'; 2 | 3 | describe('Shape Shifter App', function() { 4 | let page: ShapeShifterPage; 5 | 6 | beforeEach(() => { 7 | page = new ShapeShifterPage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(true).toEqual(true); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/typings/common.d.ts: -------------------------------------------------------------------------------- 1 | interface Dictionary { 2 | [index: string]: T; 3 | } 4 | 5 | // A readonly 2D array. 6 | type ReadonlyTable = ReadonlyArray>; 7 | 8 | // An object with values that can be null. 9 | type Nullable = { [P in keyof T]: T[P] | null }; 10 | 11 | // A generic constructor type. 12 | type Constructor = new (...args: any[]) => T; 13 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/common/index.ts: -------------------------------------------------------------------------------- 1 | import * as ColorUtil from './ColorUtil'; 2 | import * as MathUtil from './MathUtil'; 3 | import * as ModelUtil from './ModelUtil'; 4 | import * as TransformUtil from './TransformUtil'; 5 | export * from './Matrix'; 6 | export { ColorUtil, MathUtil, ModelUtil, TransformUtil }; 7 | export { Point } from './Point'; 8 | export { Rect } from './Rect'; 9 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/splashscreen/splashscreen.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-splashscreen', 5 | templateUrl: './splashscreen.component.html', 6 | styleUrls: ['./splashscreen.component.scss'], 7 | changeDetection: ChangeDetectionStrategy.OnPush, 8 | }) 9 | export class SplashScreenComponent {} 10 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/create/EllipseGesture.ts: -------------------------------------------------------------------------------- 1 | import * as paper from 'paper'; 2 | 3 | import { ShapeGesture } from './ShapeGesture'; 4 | 5 | /** A gesture that creates an elliptical path. */ 6 | export class EllipseGesture extends ShapeGesture { 7 | // @Override 8 | protected newPath(vpBounds: paper.Rectangle) { 9 | return new paper.Path.Ellipse(vpBounds); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/dialogs/_dialog-theme.scss: -------------------------------------------------------------------------------- 1 | @mixin ss-dialog-theme($theme) { 2 | $accent: map-get($theme, accent); 3 | $foreground: map-get($theme, ss-foreground); 4 | mat-dialog-actions { 5 | button { 6 | color: mat-color($accent); 7 | } 8 | } 9 | mat-dialog-content { 10 | color: mat-color($foreground, secondary-text); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/create/RectangleGesture.ts: -------------------------------------------------------------------------------- 1 | import * as paper from 'paper'; 2 | 3 | import { ShapeGesture } from './ShapeGesture'; 4 | 5 | /** A gesture that creates a rectangular path. */ 6 | export class RectangleGesture extends ShapeGesture { 7 | // @Override 8 | protected newPath(vpBounds: paper.Rectangle) { 9 | return new paper.Path.Rectangle(vpBounds); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/selectors.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { createSelector, createSelectorCreator, defaultMemoize } from 'reselect'; 3 | 4 | import { State } from './reducer'; 5 | 6 | const getState = (state: State) => state; 7 | export const getEditorState = createSelector(getState, s => s.present); 8 | export const createDeepEqualSelector = createSelectorCreator(defaultMemoize, _.isEqual); 9 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/canvas/_canvas-theme.scss: -------------------------------------------------------------------------------- 1 | @mixin ss-canvas-theme($theme) { 2 | $background: map-get($theme, ss-background); 3 | .app-canvas-container { 4 | .canvas-container { 5 | canvas.rendering-canvas { 6 | // background-color: mat-color($background, base); 7 | background-color: #fff; 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/properties/PropertyMaps.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '.'; 2 | 3 | // Maps property names to inspectable properties. 4 | export interface Inspectable { 5 | readonly inspectableProperties: Map>; 6 | } 7 | 8 | // Maps property names to animatable properties. 9 | export interface Animatable { 10 | readonly animatableProperties: Map>; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/edit/index.ts: -------------------------------------------------------------------------------- 1 | export { BatchSelectSegmentsGesture } from './BatchSelectSegmentsGesture'; 2 | export { MouldCurveGesture } from './MouldCurveGesture'; 3 | export { SelectDragDrawSegmentsGesture } from './SelectDragDrawSegmentsGesture'; 4 | export { SelectDragHandleGesture } from './SelectDragHandleGesture'; 5 | export { ToggleSegmentHandlesGesture } from './ToggleSegmentHandlesGesture'; 6 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/dialogs/demodialog.component.scss: -------------------------------------------------------------------------------- 1 | button { 2 | text-transform: uppercase; 3 | &:mat-dialog-close { 4 | margin-right: 8px; 5 | } 6 | } 7 | 8 | mat-dialog-actions { 9 | min-width: 220px; 10 | } 11 | 12 | .dialog-radio-group { 13 | display: inline-flex; 14 | flex-direction: column; 15 | } 16 | 17 | .dialog-radio-button { 18 | margin-bottom: 16px; 19 | } 20 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/layers/layers.spec.ts: -------------------------------------------------------------------------------- 1 | // import { VectorLayer, ClipPathLayer, GroupLayer, PathLayer } from '.'; 2 | // import { Path } from '../paths'; 3 | 4 | // describe('Layers', () => { 5 | // it('ClipPathLayer', () => { 6 | // const layer = new ClipPathLayer('myId', new Path('M 0 0 1 1 2 2 3 3')); 7 | // console.info(layer); 8 | // expect(layer.hasOwnProperty('id')).toBe(true); 9 | // }); 10 | // }); 11 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/toolpanel/toolpanel.component.scss: -------------------------------------------------------------------------------- 1 | .tool-panel { 2 | // TODO: move this into a themed CSS file 3 | background-color: #eeeeee; 4 | position: relative; 5 | 6 | .tool-button { 7 | margin: 4px; 8 | &:hover { 9 | cursor: pointer; 10 | } 11 | &.is-checked { 12 | // TODO: move this into a themed CSS file 13 | background-color: #d0d0d0; 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/canvas/index.ts: -------------------------------------------------------------------------------- 1 | export { CanvasComponent } from './canvas.component'; 2 | export { CanvasContainerDirective } from './canvascontainer.directive'; 3 | export { CanvasLayersDirective } from './canvaslayers.directive'; 4 | export { CanvasOverlayDirective } from './canvasoverlay.directive'; 5 | export { CanvasRulerDirective } from './canvasruler.directive'; 6 | export { CanvasPaperDirective } from './canvaspaper.directive'; 7 | -------------------------------------------------------------------------------- /scripts/check-node-version.js: -------------------------------------------------------------------------------- 1 | const semver = require('semver'); 2 | const engines = require('../package.json').engines; 3 | const version = engines.node; 4 | 5 | if (!semver.satisfies(process.version, version)) { 6 | console.error( 7 | 'ERROR! The version of node on your machine is out of date (current: ' + 8 | process.version + 9 | ', required: ' + 10 | version + 11 | ').\n', 12 | ); 13 | process.exit(1); 14 | } 15 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "declaration": false, 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "lib": [ 9 | "es2016" 10 | ], 11 | "outDir": "../dist/out-tsc-e2e", 12 | "module": "commonjs", 13 | "target": "es6", 14 | "types":[ 15 | "jasmine", 16 | "node" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/properties/index.ts: -------------------------------------------------------------------------------- 1 | export { ColorProperty } from './ColorProperty'; 2 | export { FractionProperty } from './FractionProperty'; 3 | export { NameProperty } from './NameProperty'; 4 | export { NumberProperty } from './NumberProperty'; 5 | export { PathProperty } from './PathProperty'; 6 | export { Property } from './Property'; 7 | export { Animatable, Inspectable } from './PropertyMaps'; 8 | export { EnumProperty, Option } from './EnumProperty'; 9 | -------------------------------------------------------------------------------- /src/assets/icons/autofix.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/theme/selectors.ts: -------------------------------------------------------------------------------- 1 | import { getEditorState } from 'app/modules/editor/store/selectors'; 2 | import { createSelector, createStructuredSelector } from 'reselect'; 3 | 4 | const getThemeState = createSelector(getEditorState, s => s.theme); 5 | export const getThemeType = createStructuredSelector({ 6 | themeType: createSelector(getThemeState, t => t.themeType), 7 | isInitialPageLoad: createSelector(getThemeState, t => t.isInitialPageLoad), 8 | }); 9 | -------------------------------------------------------------------------------- /src/assets/tools/tool_vector.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "/index.html", 3 | "assetGroups": [ 4 | { 5 | "name": "app", 6 | "installMode": "prefetch", 7 | "resources": { 8 | "files": ["/favicon.ico", "/index.html", "/*.css", "/*.js"] 9 | } 10 | }, 11 | { 12 | "name": "assets", 13 | "installMode": "lazy", 14 | "updateMode": "prefetch", 15 | "resources": { 16 | "files": ["/assets/**"] 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/batch/actions.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'app/modules/editor/store'; 2 | 3 | export enum BatchActionTypes { 4 | BatchAction = '__batch__BATCH', 5 | } 6 | 7 | export class BatchAction implements Action { 8 | readonly type = BatchActionTypes.BatchAction; 9 | readonly payload: ReadonlyArray; 10 | constructor(...actions: Action[]) { 11 | this.payload = actions; 12 | } 13 | } 14 | 15 | export type BatchActions = BatchAction; 16 | -------------------------------------------------------------------------------- /src/assets/tools/tool_pencil.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/tools/tool_zoompan.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/demos/index.ts: -------------------------------------------------------------------------------- 1 | export interface DemoInfo { 2 | readonly id: string; 3 | readonly title: string; 4 | } 5 | 6 | export const DEMO_INFOS: ReadonlyArray = [ 7 | { id: 'playtopause', title: 'Play-to-pause' }, 8 | { id: 'searchtoclose', title: 'Search-to-close' }, 9 | { id: 'morphinganimals', title: 'Morphing animals' }, 10 | { id: 'visibilitystrike', title: 'Visibility strike' }, 11 | { id: 'heartbreak', title: 'Heart break' }, 12 | ]; 13 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Shape Shifter", 3 | "short_name": "ShapeShifter", 4 | "theme_color": "#607d8b", 5 | "background_color": "#e0e0e0", 6 | "display": "standalone", 7 | "description": "Shape Shifter is a web-app that simplifies the process of creating SVG-based path morphing animations.", 8 | "icons": [{ 9 | "src": "assets/shapeshifter.png", 10 | "sizes": "600x600", 11 | "type": "image/png" 12 | }], 13 | "scope": "./", 14 | "start_url": "./" 15 | } 16 | -------------------------------------------------------------------------------- /src/typings/paper/HitOptions.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'paper' { 2 | export interface HitOptions { 3 | tolerance?: number; 4 | class?: Constructor; 5 | fill?: boolean; 6 | stroke?: boolean; 7 | segments?: boolean; 8 | curves?: boolean; 9 | handles?: boolean; 10 | ends?: boolean; 11 | bounds?: boolean; 12 | center?: boolean; 13 | guides?: boolean; 14 | selected?: boolean; 15 | match?: (hitResult: HitResult) => boolean; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/tools/tool_ellipse.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/root/_root-theme.scss: -------------------------------------------------------------------------------- 1 | @mixin ss-root-theme($theme) { 2 | $background: map-get($theme, ss-background); 3 | $accent: map-get($theme, accent); 4 | div.display-container { 5 | background-color: mat-color($background, base300); 6 | } 7 | .file-drop-target { 8 | &.is-dragging-over::after { 9 | background-color: mat-color($accent, darker, 0.5); 10 | box-shadow: 0 0 0 8px mat-color($accent, darker) inset; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/theme/actions.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'app/modules/editor/store'; 2 | 3 | import { ThemeType } from './reducer'; 4 | 5 | export enum ThemeActionTypes { 6 | SetTheme = '__theme__SET_THEME', 7 | } 8 | 9 | export class SetTheme implements Action { 10 | readonly type = ThemeActionTypes.SetTheme; 11 | readonly payload: { themeType: ThemeType }; 12 | constructor(themeType: ThemeType) { 13 | this.payload = { themeType }; 14 | } 15 | } 16 | 17 | export type ThemeActions = SetTheme; 18 | -------------------------------------------------------------------------------- /src/app/modules/editor/services/snackbar.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { MatSnackBar } from '@angular/material'; 3 | 4 | export enum Duration { 5 | Short = 2750, 6 | Long = 5000, 7 | } 8 | 9 | @Injectable({ providedIn: 'root' }) 10 | export class SnackBarService { 11 | constructor(private readonly snackBar: MatSnackBar) {} 12 | 13 | show(message: string, action = '', duration = Duration.Short) { 14 | this.snackBar.open(message, action.toUpperCase(), { duration }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "files.exclude": { 4 | "node_modules/**": true, 5 | "**/*~": true, 6 | "**/*.js": { 7 | "when": "$(basename).ts" 8 | }, 9 | "**/*.js.map": { 10 | "when": "$(basename)" 11 | } 12 | }, 13 | "files.trimTrailingWhitespace": true, 14 | "html.format.wrapAttributes": "force", 15 | "prettier.printWidth": 100, 16 | "prettier.singleQuote": true, 17 | "prettier.tabWidth": 2, 18 | "typescript.tsdk": "node_modules/typescript/lib" 19 | } 20 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/index.ts: -------------------------------------------------------------------------------- 1 | import * as paper from 'paper'; 2 | 3 | // By default paper.js bakes matrix transformations directly into its children. 4 | // This is usually not the behavior we want (especially for groups). 5 | paper.settings.applyMatrix = false; 6 | 7 | // By default paper.js automatically inserts newly created items into the active layer. 8 | // This behavior makes it harder to explicitly position things in the item hierarchy. 9 | paper.settings.insertItems = false; 10 | 11 | export { PaperProject } from './PaperProject'; 12 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/common/MathUtil.spec.ts: -------------------------------------------------------------------------------- 1 | import { MathUtil, Matrix } from '.'; 2 | 3 | describe('MathUtil', () => { 4 | it('#transformPoint', () => { 5 | const point = { x: 1, y: 1 }; 6 | const matrix = Matrix.identity(); 7 | const transformed = MathUtil.transformPoint(point, matrix); 8 | expect(transformed).toEqual({ x: 1, y: 1 }); 9 | }); 10 | 11 | it('#areCollinear', () => { 12 | expect(MathUtil.areCollinear({ x: 16, y: 6 }, { x: 14, y: 5 }, { x: 19, y: 20 })).toEqual( 13 | false, 14 | ); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "paths": { 6 | "src/*": ["../src/*"], 7 | "os": ["../node_modules/os-browserify/browser.js"], 8 | "stream": ["../node_modules/stream-browserify/index.js"], 9 | "jszip": ["../node_modules/jszip/dist/jszip.min.js"], 10 | "paper": ["../node_modules/paper/dist/paper-core.min.js"] 11 | }, 12 | "baseUrl": "./", 13 | "module": "es2015" 14 | }, 15 | "exclude": ["test.ts", "**/*.spec.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/mixins/DestroyableMixin.ts: -------------------------------------------------------------------------------- 1 | import { OnDestroy } from '@angular/core'; 2 | import { Subscription } from 'rxjs'; 3 | 4 | export function DestroyableMixin(Base = class {} as T) { 5 | return class extends Base implements OnDestroy { 6 | private readonly subscriptions: Subscription[] = []; 7 | 8 | protected registerSubscription(sub: Subscription) { 9 | this.subscriptions.push(sub); 10 | } 11 | 12 | ngOnDestroy() { 13 | this.subscriptions.forEach(x => x.unsubscribe()); 14 | } 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/assets/icons/addlayer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/batch/metareducer.ts: -------------------------------------------------------------------------------- 1 | import { ActionReducer } from 'app/modules/editor/store'; 2 | import { EditorState } from 'app/modules/editor/store/reducer'; 3 | 4 | import { BatchActionTypes, BatchActions } from './actions'; 5 | 6 | export function metaReducer(reducer: ActionReducer): ActionReducer { 7 | return (state: EditorState, action: BatchActions) => { 8 | const isBatchAction = action.type === BatchActionTypes.BatchAction; 9 | return (isBatchAction ? action.payload : [action]).reduce(reducer, state); 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/properties/FractionProperty.ts: -------------------------------------------------------------------------------- 1 | import { NumberConfig, NumberProperty } from './NumberProperty'; 2 | 3 | export class FractionProperty extends NumberProperty { 4 | constructor(name: string, config: NumberConfig = {}) { 5 | super(name, { 6 | isAnimatable: config.isAnimatable, 7 | min: 0, 8 | max: 1, 9 | isInteger: false, 10 | }); 11 | } 12 | 13 | // @Override 14 | getAnimatorValueType() { 15 | return 'floatType'; 16 | } 17 | 18 | // @Override 19 | getTypeName() { 20 | return 'FractionProperty'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/typings/paper/PlacedSymbol.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'paper' { 2 | /** 3 | * A PlacedSymbol represents an instance of a symbol which has been placed in a Paper.js project. 4 | */ 5 | export class PlacedSymbol extends Item { 6 | /** 7 | * Creates a new PlacedSymbol Item. 8 | * @param symbol - the symbol to place 9 | * @param point [optional] - the center point of the placed symbol 10 | */ 11 | constructor(symbol: Symbol, point?: Point); 12 | 13 | /** 14 | * The symbol that the placed symbol refers to. 15 | */ 16 | symbol: Symbol; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/bugsnag/index.ts: -------------------------------------------------------------------------------- 1 | import BugsnagErrorHandler from 'bugsnag-angular'; 2 | import bugsnag from 'bugsnag-js'; 3 | import { environment } from 'environments/environment'; 4 | import { version } from 'environments/version'; 5 | 6 | export const bugsnagClient = bugsnag({ 7 | apiKey: 'd662c2c8a7e13ac94f67e81e26bf3a4e', 8 | appVersion: version, 9 | releaseStage: environment.production ? 'production' : 'development', 10 | notifyReleaseStages: ['production'], 11 | }); 12 | 13 | export function errorHandlerFactory() { 14 | return new BugsnagErrorHandler(bugsnagClient); 15 | } 16 | -------------------------------------------------------------------------------- /src/app/modules/editor/services/StoreUtil.ts: -------------------------------------------------------------------------------- 1 | import { ToolMode } from 'app/modules/editor/model/paper'; 2 | import { 3 | SetEditPathInfo, 4 | SetRotateItemsInfo, 5 | SetToolMode, 6 | SetTransformPathsInfo, 7 | } from 'app/modules/editor/store/paper/actions'; 8 | 9 | // TODO: expand on this class... possibly a better redesigned version? 10 | 11 | export function getEnterDefaultModeActions() { 12 | return [ 13 | new SetToolMode(ToolMode.Default), 14 | new SetEditPathInfo(undefined), 15 | new SetRotateItemsInfo(undefined), 16 | new SetTransformPathsInfo(undefined), 17 | ]; 18 | } 19 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/properties/NameProperty.ts: -------------------------------------------------------------------------------- 1 | import { Property } from './Property'; 2 | 3 | export class NameProperty extends Property { 4 | static sanitize(value = '') { 5 | return value 6 | .toLowerCase() 7 | .replace(/^\s+|\s+$/g, '') 8 | .replace(/[\s-]+/g, '_') 9 | .replace(/[^\w_]+/g, ''); 10 | } 11 | 12 | // @Override 13 | setEditableValue(model: any, propertyName: string, value: string) { 14 | model[propertyName] = NameProperty.sanitize(value); 15 | } 16 | 17 | // @Override 18 | getTypeName() { 19 | return 'NameProperty'; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/modules/editor/services/index.ts: -------------------------------------------------------------------------------- 1 | export { ActionModeService } from './actionmode.service'; 2 | export { ClipboardService } from './clipboard.service'; 3 | export { FileExportService } from './fileexport.service'; 4 | export { FileImportService } from './fileimport.service'; 5 | export { LayerTimelineService } from './layertimeline.service'; 6 | export { PlaybackService } from './playback.service'; 7 | export { SnackBarService } from './snackbar.service'; 8 | export { ShortcutService } from './shortcut.service'; 9 | export { ThemeService } from './theme.service'; 10 | export { PaperService } from './paper.service'; 11 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/item/EditPathRaster.ts: -------------------------------------------------------------------------------- 1 | import * as paper from 'paper'; 2 | 3 | export class EditPathRaster extends paper.Raster { 4 | constructor( 5 | readonly type: 'segment' | 'handle-in' | 'handle-out', 6 | readonly segmentIndex: number, 7 | readonly isSelected: boolean, 8 | center: paper.Point, 9 | ) { 10 | super( 11 | type === 'segment' 12 | ? `/assets/paper/${isSelected ? 'vector-segment-selected' : 'vector-segment'}.png` 13 | : `/assets/paper/${isSelected ? 'vector-handle-selected' : 'vector-handle'}.png`, 14 | center, 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/storefreeze/metareducer.ts: -------------------------------------------------------------------------------- 1 | import { Action, ActionReducer } from 'app/modules/editor/store'; 2 | import * as deepFreeze from 'deep-freeze-strict'; 3 | 4 | /** 5 | * Meta reducer that prevents state from being mutated anywhere in the app. 6 | */ 7 | export function metaReducer(reducer: ActionReducer): ActionReducer { 8 | return (state: T, action: Action) => { 9 | if (state) { 10 | deepFreeze(state); 11 | } 12 | const nextState = reducer(state, action); 13 | if (nextState) { 14 | deepFreeze(nextState); 15 | } 16 | return nextState; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: node_js 4 | node_js: 5 | - "9.10.1" 6 | os: 7 | - linux 8 | env: 9 | global: 10 | - DBUS_SESSION_BUS_ADDRESS=/dev/null 11 | - DISPLAY=:99.0 12 | - CHROME_BIN=chromium-browser 13 | before_script: 14 | - sh -e /etc/init.d/xvfb start 15 | install: 16 | - npm install 17 | script: 18 | - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then precise-commits --check-only --head=$TRAVIS_PULL_REQUEST_SHA --base=$(git merge-base HEAD $TRAVIS_BRANCH); fi' 19 | - npm run lint 20 | - npm run test-once 21 | - npm run build 22 | after_success: 23 | - npm run coveralls 24 | -------------------------------------------------------------------------------- /src/app/modules/editor/components/splashscreen/splashscreen.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | :host { 3 | position: absolute; 4 | left: 0; 5 | top: 0; 6 | right: 0; 7 | bottom: 0; 8 | user-select: none; 9 | background-color: mat-color(mat-palette($mat-grey, 300)); 10 | } 11 | 12 | .splashscreen-logo { 13 | width: 144px; 14 | height: 144px; 15 | margin-bottom: 16px; 16 | } 17 | 18 | .splashscreen-text { 19 | font-size: 18px; 20 | line-height: 22px; 21 | padding-left: 16px; 22 | padding-right: 16px; 23 | padding-bottom: 16px; 24 | text-align: center; 25 | } 26 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/item/RotateItemsPivotRaster.ts: -------------------------------------------------------------------------------- 1 | import * as paper from 'paper'; 2 | 3 | export class RotateItemsPivotRaster extends paper.Raster { 4 | private static instance: RotateItemsPivotRaster; 5 | 6 | static of(position: paper.Point) { 7 | if (!RotateItemsPivotRaster.instance) { 8 | RotateItemsPivotRaster.instance = new RotateItemsPivotRaster(); 9 | } 10 | const raster = RotateItemsPivotRaster.instance.clone(false) as RotateItemsPivotRaster; 11 | raster.position = position; 12 | return raster; 13 | } 14 | 15 | constructor() { 16 | super('/assets/paper/rotate-items-pivot.png'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/icons/grouplayer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/layers/selectors.ts: -------------------------------------------------------------------------------- 1 | import { createDeepEqualSelector, getEditorState } from 'app/modules/editor/store/selectors'; 2 | import { createSelector } from 'reselect'; 3 | 4 | const getLayerState = createSelector(getEditorState, s => s.layers); 5 | export const getVectorLayer = createSelector(getLayerState, l => l.vectorLayer); 6 | export const getSelectedLayerIds = createDeepEqualSelector(getLayerState, l => l.selectedLayerIds); 7 | export const getCollapsedLayerIds = createDeepEqualSelector( 8 | getLayerState, 9 | l => l.collapsedLayerIds, 10 | ); 11 | export const getHiddenLayerIds = createDeepEqualSelector(getLayerState, l => l.hiddenLayerIds); 12 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/tool/Tool.ts: -------------------------------------------------------------------------------- 1 | import * as paper from 'paper'; 2 | 3 | /** Represents the base class for all tool types. */ 4 | export abstract class Tool { 5 | /** Called immediately after this tool has been activated. */ 6 | onActivate() {} 7 | 8 | /** 9 | * Called when this tool has received a tool event (i.e. mouse down, 10 | * mouse drag, mouse move, mouse up). 11 | */ 12 | onToolEvent(event: paper.ToolEvent) {} 13 | 14 | /** Called when this tool has received a key event (i.e. key down, key up). */ 15 | onKeyEvent(event: paper.KeyEvent) {} 16 | 17 | /** Called immediately after this tool has been deactivated. */ 18 | onDeactivate() {} 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | 18 | # IDE - VSCode 19 | .vscode/* 20 | !.vscode/settings.json 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | !.vscode/extensions.json 24 | 25 | # misc 26 | /.sass-cache 27 | /connect.lock 28 | /coverage/* 29 | /libpeerconnection.log 30 | npm-debug.log 31 | testem.log 32 | /typings 33 | 34 | # e2e 35 | /e2e/*.js 36 | /e2e/*.map 37 | 38 | #System Files 39 | .DS_Store 40 | Thumbs.db 41 | 42 | *~ 43 | *.sublime-workspace 44 | -------------------------------------------------------------------------------- /src/assets/icons/collection.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/app/modules/editor/store/reset/reducer.ts: -------------------------------------------------------------------------------- 1 | import { ResetActionTypes, ResetActions } from './actions'; 2 | 3 | // TODO: remove this 'isBeingReset' flag... see TODO in layer timeline component 4 | export interface State { 5 | readonly isBeingReset: boolean; 6 | } 7 | 8 | export function buildInitialState() { 9 | return { 10 | isBeingReset: false, 11 | } as State; 12 | } 13 | 14 | export function reducer(state = buildInitialState(), action: ResetActions) { 15 | if (action.type === ResetActionTypes.ResetWorkspace) { 16 | return { ...state, isBeingReset: true }; 17 | } 18 | const { isBeingReset } = state; 19 | if (isBeingReset) { 20 | return { ...state, isBeingReset: false }; 21 | } 22 | return state; 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "baseUrl": "src", 6 | "paths": { 7 | "src/*": ["./src/*"] 8 | }, 9 | "sourceMap": true, 10 | "declaration": false, 11 | "moduleResolution": "node", 12 | "emitDecoratorMetadata": true, 13 | "experimentalDecorators": true, 14 | "noImplicitReturns": true, 15 | "noImplicitAny": true, 16 | "noUnusedLocals": false, 17 | "noUnusedParameters": false, 18 | "importHelpers": true, 19 | "target": "es5", 20 | "typeRoots": ["node_modules/@types"], 21 | "lib": ["es2017", "dom"] 22 | }, 23 | "angularCompilerOptions": { 24 | "preserveWhitespaces": false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/Gesture.ts: -------------------------------------------------------------------------------- 1 | import * as paper from 'paper'; 2 | 3 | /** 4 | * A gesture represents a user interaction with the mouse or keyboard. Typically 5 | * a gesture is used in one of two ways: 6 | * 7 | * (1) To monitor the state of events that occurs between the initial 8 | * mouse down through the final mouse up. 9 | * 10 | * (2) To monitor mouse move events and react accordingly. 11 | */ 12 | export abstract class Gesture { 13 | onMouseDown(event: paper.ToolEvent) {} 14 | onMouseDrag(event: paper.ToolEvent) {} 15 | onMouseMove(event: paper.ToolEvent) {} 16 | onMouseUp(event: paper.ToolEvent) {} 17 | onKeyDown(event: paper.KeyEvent) {} 18 | onKeyUp(event: paper.KeyEvent) {} 19 | } 20 | -------------------------------------------------------------------------------- /src/assets/icons/animationblock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/vectorlayer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/app/modules/editor/scripts/paper/gesture/select/DeselectItemGesture.ts: -------------------------------------------------------------------------------- 1 | import { Gesture } from 'app/modules/editor/scripts/paper/gesture'; 2 | import { PaperService } from 'app/modules/editor/services'; 3 | import * as paper from 'paper'; 4 | 5 | /** 6 | * A gesture that deselects a single item. 7 | * 8 | * Preconditions: 9 | * - The user is in default mode. 10 | */ 11 | export class DeselectItemGesture extends Gesture { 12 | constructor(private readonly ps: PaperService, private readonly deselectedItemId: string) { 13 | super(); 14 | } 15 | 16 | // @Override 17 | onMouseDown(event: paper.ToolEvent) { 18 | const layerIds = new Set(this.ps.getSelectedLayerIds()); 19 | layerIds.delete(this.deselectedItemId); 20 | this.ps.setSelectedLayerIds(layerIds); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/modules/editor/model/properties/EnumProperty.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | 3 | import { Property } from './Property'; 4 | 5 | export class EnumProperty extends Property { 6 | constructor(name: string, readonly options: ReadonlyArray