diff --git a/index.ts b/index.ts index 421de55c03a..be40e107ca5 100644 --- a/index.ts +++ b/index.ts @@ -1,6 +1,7 @@ import { Pattern } from './lib/pattern' import { Point } from './lib/point' import { Path } from './lib/path' +import { Snippet } from './lib/snippet' import themes from './lib/themes' import * as utils from './lib/utils' import bezier from 'bezier-js' @@ -10,6 +11,7 @@ var Freesewing = { pattern: Pattern, point: Point, path: Path, + snippet: Snippet, utils, bezier } diff --git a/lib/part.ts b/lib/part.ts index 908b320fe97..e69a9c4b5af 100644 --- a/lib/part.ts +++ b/lib/part.ts @@ -1,5 +1,6 @@ import { Point } from './point' import { Path } from './path' +import { Snippet } from './snippet' import { Attributes } from './attributes' export class Part { @@ -9,6 +10,7 @@ export class Part { [index: string]: Point | boolean; } paths: { [index: string]: Path; } = {}; + snippets: { [index: string]: Snippet; } = {}; attributes = new Attributes(); [propName: string]: any; @@ -20,7 +22,6 @@ export class Part { return this; } - // purge = { // points = function(prefix: string): void {} // paths = function(prefix: string): void {} diff --git a/lib/point.ts b/lib/point.ts index 8d99fc4f9df..088e96112f9 100644 --- a/lib/point.ts +++ b/lib/point.ts @@ -1,9 +1,10 @@ import { round, rad2deg, deg2rad } from './utils'; - +import { Attributes } from './attributes' export class Point { x: number; y: number; + attributes: Attributes = new Attributes(); constructor(x: number, y: number) { this.x = round(x); diff --git a/lib/snippet.ts b/lib/snippet.ts new file mode 100644 index 00000000000..081f42f1e22 --- /dev/null +++ b/lib/snippet.ts @@ -0,0 +1,17 @@ +import { Point } from './point' +import { Attributes } from './attributes' + +export class Snippet { + anchor: Point; + def: string; + attributes: Attributes = new Attributes(); + description: string | false; + + constructor(anchor: Point, def: string, description: string | false = false) { + this.anchor = anchor; + this.def = def; + this.description = description; + + return this; + } +} diff --git a/lib/svg.ts b/lib/svg.ts index 3346ef73142..9595fb66c38 100644 --- a/lib/svg.ts +++ b/lib/svg.ts @@ -1,5 +1,6 @@ import { Part } from './part' import { Path } from './path' +import { Snippet } from './snippet' import { Pattern } from './pattern' import { Attributes } from './attributes' @@ -7,6 +8,7 @@ export class Svg { prefix: string; body: string = ''; style: string = ''; + script: string = ''; header: string = ''; footer: string = ''; defs: string = ''; @@ -33,6 +35,7 @@ export class Svg { svg += this.renderComments(this.header); svg += this.renderSvgTag(pattern); svg += this.renderStyle(); + svg += this.renderScript(); svg += this.renderDefs(); svg += this.openGroup('draftContainer'); for (let partId in pattern.parts) { @@ -72,6 +75,17 @@ export class Svg { return svg; } + /** Returns SVG code for the script block */ + renderScript() { + let svg = ''+this.nl(); + + return svg; + } + /** Returns SVG code for the defs block */ renderDefs() { let svg = ''; @@ -91,16 +105,19 @@ export class Svg { /** Returns SVG code for a Part object */ renderPart(part: Part): string { let svg = ''; - for (let pathId in part.paths) { - let path = part.paths[pathId]; + for (let key in part.paths) { + let path = part.paths[key]; if(path.render) svg += this.renderPath(path); } + for (let key in part.snippets) { + let snippet = part.snippets[key]; + svg += this.renderSnippet(snippet); + } // includes // text on path // notes // dimensions // texts - // snippets return svg; } @@ -111,6 +128,19 @@ export class Svg { return `${this.nl()}`; } + /** Returns SVG code for a snippet */ + renderSnippet(snippet: Snippet): string { + let svg = this.nl(); + svg += ``; + if(snippet.description) { + svg += `${snippet.description}`; + } + svg += ''; + + return svg; + } + /** Returns SVG code to open a group */ openGroup(id: string, attributes?: Attributes): string { diff --git a/lib/themes.ts b/lib/themes.ts index 9812b98c960..3bb76f2cbc9 100644 --- a/lib/themes.ts +++ b/lib/themes.ts @@ -2,10 +2,11 @@ import { Draft } from './themes/draft' import { Paperless } from './themes/paperless' import { Sample } from './themes/sample' import { Compare } from './themes/compare' +import { Designer } from './themes/designer' /** Standard themes that ship with freesewing */ var themes = { - draft: new Draft(), + draft: new Designer(), paperless: new Paperless(), sample: new Sample(), compare: new Compare() diff --git a/lib/themes/designer.ts b/lib/themes/designer.ts new file mode 100644 index 00000000000..642add790f0 --- /dev/null +++ b/lib/themes/designer.ts @@ -0,0 +1,149 @@ +import { Pattern } from '../pattern' +import { Svg } from '../svg' +import { Path } from '../path' +import { Snippet } from '../snippet' +import { Theme } from './theme'; + +export class Designer extends Theme { + style: string = ` + path.curve-control{stroke:#f0ad4e;stroke-width: 0.2;} + path.debug{stroke:#d9534f;stroke-opacity:0.4;stroke-width:2;} + .point{fill:none;stroke-width:0.6;stroke:#f0ad4e;} + text.tooltip{font-size:3px;}`; + defs: string = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; + script:string = ` + function pointHover(evt) { + var point = evt.target; + var id = point.id; + var cx = point.getAttribute('x'); + var cy = point.getAttribute('y'); + console.log('Point '+id+' ( '+cx+' , '+cy+' )'); + var scale = 2; + cx = cx-scale*cx; + cy = cy-scale*cy; + point.setAttribute("transform", 'matrix('+scale+', 0, 0, '+scale+', '+cx+', '+cy+')'); + setTimeout(function(){ + var point = document.getElementById(evt.target.id); + point.removeAttribute("transform", ''); + }, 1000); + }`; + + /** Pre-render method is called just prior to rendering */ + preRender(pattern: Pattern, svg: Svg): void { + super.preRender(pattern, svg); + svg.style += this.style; + svg.defs += this.defs; + svg.script += this.script; + svg.attributes.add('freesewing:theme', 'designer'); + svg.attributes.add('viewBox', '-10 -10 300 500'); + //this.decoratePoints(pattern, svg); + this.decoratePaths(pattern, svg); + } + + /** Decorares points with extra info */ + decoratePoints(pattern: Pattern, svg: Svg): void { + for (let partId in pattern.parts) { + let part = pattern.parts[partId]; + if (part.render) { + for (let pointId in part.points) { + this.decoratePoint(pointId, part, svg); + } + } + } + } + + /** Decorares a point with extra info */ + decoratePoint(pointId: string, part: Part, svg: Svg): void { + let point = part.points[pointId]; + point.attributes.add('id', svg.getUid()); + point.attributes.add('data-point', id); + } + + /** Decorares paths with extra info */ + decoratePaths(pattern: Pattern, svg: Svg): void { + for (let partId in pattern.parts) { + let part = pattern.parts[partId]; + if (part.render) { + for (let pathId in part.paths) { + this.decoratePath(pathId, part, svg); + } + } + } + } + + /** Decorares a path with extra info */ + decoratePath(pathId: string, part: Part, svg: Svg): void { + let path = part.paths[pathId]; + if (!path.render) return false; + for (let op of path.ops) { + switch(op.type) { + case 'move': + let id = svg.getUid(); + part.snippets[id] = new Snippet(op.to, 'path-start-point', `Startpoint of path ${pathId}`); + part.snippets[id].attributes.add('onmouseover', 'pointHover(evt)'); + part.snippets[id].attributes.add('id', svg.getUid()); + break; + case 'line': + let id = svg.getUid(); + part.snippets[id] = new Snippet(op.to, 'path-point', `Line endpoint of path ${pathId}`); + part.snippets[id].attributes.add('onmouseover', 'pointHover(evt)'); + part.snippets[id].attributes.add('id', svg.getUid()); + break; + case 'curve': + let id = svg.getUid(); + part.snippets[id] = new Snippet(op.to, 'path-point', `Curve endpoint of path ${pathId}`); + part.snippets[id].attributes.add('onmouseover', 'pointHover(evt)'); + part.snippets[id].attributes.add('id', svg.getUid()); + id = svg.getUid(); + part.snippets[id] = new Snippet(op.cp1, 'path-curvecontrol', `Curve cp1 of path ${pathId}`); + part.snippets[id].attributes.add('onmouseover', 'pointHover(evt)'); + part.snippets[id].attributes.add('id', svg.getUid()); + id = svg.getUid(); + part.snippets[id] = new Snippet(op.cp2, 'path-curvecontrol', `Curve cp2 of path ${pathId}`); + part.snippets[id].attributes.add('onmouseover', 'pointHover(evt)'); + let cp1 = new Path().move(current).line(op.cp1); + let cp2 = new Path().move(op.to).line(op.cp2); + cp1.attributes.add('class', 'curve-control'); + cp1.attributes.add('id', svg.getUid()); + cp2.attributes.add('class', 'curve-control'); + cp2.attributes.add('id', svg.getUid()); + part.paths[svg.getUid()] = cp1; + part.paths[svg.getUid()] = cp2; + break; + } + let current = op.to; + } + } +} diff --git a/lib/themes/draft.ts b/lib/themes/draft.ts index b88e72c9790..3d10768c605 100644 --- a/lib/themes/draft.ts +++ b/lib/themes/draft.ts @@ -1,3 +1,11 @@ +import { Pattern } from '../pattern' +import { Svg } from '../svg' import { Theme } from './theme'; -export class Draft extends Theme {} +export class Draft extends Theme { + /** Pre-render method is called just prior to rendering */ + preRender(pattern: Pattern, svg: Svg): void { + super.preRender(pattern, svg); + svg.attributes.add('freesewing:theme', 'draft'); + } +} diff --git a/lib/themes/theme.ts b/lib/themes/theme.ts index 1d81f9dc00a..24366c5064c 100644 --- a/lib/themes/theme.ts +++ b/lib/themes/theme.ts @@ -8,6 +8,7 @@ export abstract class Theme { svg.header += this.loadHeader(pattern); svg.footer += this.loadFooter(pattern); svg.style += this.loadStyle(pattern); + svg.script += this.loadScript(pattern); svg.defs += this.loadDefs(pattern); } @@ -116,6 +117,11 @@ export abstract class Theme { .fill-gray{fill:#999}`; } + /** Returns a string containing the SVG ECMA script */ + loadScript(pattern: Pattern) { + return ''; + } + /** Returns a string containing the SVG defs */ loadDefs(pattern: Pattern) { return `