From ce4aa9880d9bc05f15285a87843cf6ccd7f1cb53 Mon Sep 17 00:00:00 2001 From: Bill Dwyer Date: Wed, 8 Feb 2017 16:54:42 -0800 Subject: [PATCH 1/7] Add textShear option for shearing text --- preview/index.html | 8 +++++ preview/preview.js | 53 ++++++++++++++++++++++++++------- src/writers/writer.ts | 69 ++++++++++++++++++++++++++++++++----------- 3 files changed, 103 insertions(+), 27 deletions(-) diff --git a/preview/index.html b/preview/index.html index d455969..3e9438a 100644 --- a/preview/index.html +++ b/preview/index.html @@ -16,6 +16,13 @@
Input text here
+ + + + + +
Shear Angle
+
SVG output here
@@ -24,6 +31,7 @@
SVG output here
+
diff --git a/preview/preview.js b/preview/preview.js index 2d7e99b..e0014d5 100644 --- a/preview/preview.js +++ b/preview/preview.js @@ -15,11 +15,17 @@ function createUpdater(selector, options) { const wrapper = new SVGTypewriter.Wrapper(); const writer = new SVGTypewriter.Writer(measurer, wrapper); - return function(text) { + const update = function() { const rect = element.getBoundingClientRect(); selection.selectAll("*").remove() - writer.write(text, rect.width, rect.height, writeOptions); - } + writer.write(this.text, rect.width, rect.height, this.options); + }; + + return { + update, + text: "", + options: writeOptions, + }; } const updatables = [ @@ -28,17 +34,44 @@ const updatables = [ createUpdater("#svg3", {textRotation: 90}), createUpdater("#svg4", {textRotation: -90}), createUpdater("#svg5", {xAlign: "right"}), + createUpdater("#svg6", { + textRotation: -90, + textShear: 0, + xAlign: "right" + }), ]; +// bind text area const textArea = document.querySelector("textarea"); - -function update() { +function updateText() { const text = textArea.value; - updatables.forEach(function (updatable) { - updatable(text); + updatables.forEach((u) => { + u.text = text; + u.update.apply(u); + }); +}; +textArea.addEventListener("change", updateText); +textArea.addEventListener("keyup", updateText); +updateText(); + +// bind shear slider +const slider = document.querySelector("input#shear"); +function updateShear() { + const value = parseInt(slider.value); + updatables.forEach((u) => { + if (u.options.textShear != null) { + u.options.textShear = value; + u.update.apply(u); + } }); }; +slider.addEventListener("change", updateShear); +updateShear(); -textArea.addEventListener("change", update); -textArea.addEventListener("keyup", update); -update(); \ No newline at end of file +const textSetters = document.querySelectorAll("input[data-text]"); +textSetters.forEach((textSetter) => { + textSetter.addEventListener("click", () => { + textArea.value = textSetter.getAttribute("data-text"); + updateText(); + }); +}); diff --git a/src/writers/writer.ts b/src/writers/writer.ts index 86fd684..47ded9a 100644 --- a/src/writers/writer.ts +++ b/src/writers/writer.ts @@ -16,6 +16,7 @@ export interface IWriteOptions { xAlign: string; yAlign: string; textRotation: number; + textShear?: number; animator?: Animators.BaseAnimator; } @@ -74,13 +75,33 @@ export class Writer { public write(text: string, width: number, height: number, options: IWriteOptions) { if (Writer.SupportedRotation.indexOf(options.textRotation) === -1) { - throw new Error("unsupported rotation - " + options.textRotation); + throw new Error("unsupported rotation - " + options.textRotation + + ". Supported rotations are " + Writer.SupportedRotation.join(", ")); + } + if (options.textShear != null && options.textShear < -80 || options.textShear > 80) { + throw new Error("unsupported shear angle - " + options.textShear + ". Must be between -80 and 80"); } const orientHorizontally = Math.abs(Math.abs(options.textRotation) - 90) > 45; const primaryDimension = orientHorizontally ? width : height; const secondaryDimension = orientHorizontally ? height : width; + // compute shear parameters + const shearDegrees = (options.textShear || 0); + const shearRadians = shearDegrees * Math.PI / 180; + const lineHeight = this._measurer.measure().height; + const shearShift = lineHeight * Math.tan(shearRadians); + + // When we apply text shear, the primary axis grows and the secondary axis + // shrinks, due to trigonometry. The text shear feature uses the normal + // wrapping logic with a subsituted bounding box of the corrected size + // (computed below). When rendering the wrapped lines, we rotate the text + // container by the text rotation angle AND the shear angle then carefully + // offset each one so that they are still aligned to the primary alignment + // option. + const shearCorrectedPrimaryDimension = primaryDimension / Math.cos(shearRadians) - Math.abs(shearShift); + const shearCorrectedSecondaryDimension = secondaryDimension * Math.cos(shearRadians); + const textContainer = options.selection.append("g").classed("text-container", true); if (this._addTitleElement) { textContainer.append("title").text(text); @@ -93,40 +114,53 @@ export class Writer { this._wrapper.wrap( normalizedText, this._measurer, - primaryDimension, - secondaryDimension, + shearCorrectedPrimaryDimension, + shearCorrectedSecondaryDimension, ).wrappedText : normalizedText; + this.writeText( wrappedText, textArea, - primaryDimension, - secondaryDimension, + shearCorrectedPrimaryDimension, + shearCorrectedSecondaryDimension, + shearShift, options.xAlign, options.yAlign, ); const xForm = d3.transform(""); const xForm2 = d3.transform(""); - xForm.rotate = options.textRotation; + + // apply text rotation and shear angles + xForm.rotate = options.textRotation + shearDegrees; + + // correct the intial X offset of the text container accounting for shear + // angle and alignment option. + const shearCorrectedOffset = Writer.XOffsetFactor[options.xAlign] * + shearCorrectedPrimaryDimension * Math.sin(shearRadians); + + // console.log(options.textRotation, shearDegrees, "->", { + // shearShift, primaryDimension, shearCorrectedPrimaryDimension, shearCorrectedOffset}); switch (options.textRotation) { case 90: - xForm.translate = [width, 0]; - xForm2.rotate = -90; + xForm.translate = [width + shearCorrectedOffset, 0]; + xForm2.rotate = -90 - shearDegrees; xForm2.translate = [0, 200]; break; case -90: - xForm.translate = [0, height]; - xForm2.rotate = 90; + xForm.translate = [-shearCorrectedOffset, height]; + xForm2.rotate = 90 + shearDegrees; xForm2.translate = [width, 0]; break; case 180: - xForm.translate = [width, height]; - xForm2.translate = [width, height]; - xForm2.rotate = 180; + xForm.translate = [width, height + shearCorrectedOffset]; + xForm2.translate = [width, height + shearCorrectedOffset]; + xForm2.rotate = 180 - shearDegrees; break; default: + xForm.translate = [0, -shearCorrectedOffset]; break; } @@ -137,10 +171,10 @@ export class Writer { } } - private writeLine(line: string, g: d3.Selection, width: number, xAlign: string, yOffset: number) { + private writeLine(line: string, g: d3.Selection, width: number, xAlign: string, xOffset: number, yOffset: number) { const textEl = g.append("text"); textEl.text(line); - const xOffset = width * Writer.XOffsetFactor[xAlign]; + xOffset += width * Writer.XOffsetFactor[xAlign]; const anchor: string = Writer.AnchorConverter[xAlign]; textEl.attr("text-anchor", anchor).classed("text-line", true); Utils.DOM.transform(textEl, xOffset, yOffset).attr("y", "-0.25em"); @@ -151,14 +185,15 @@ export class Writer { writingArea: d3.Selection, width: number, height: number, + shearShift: number, xAlign: string, yAlign: string) { - const lines = text.split("\n"); const lineHeight = this._measurer.measure().height; const yOffset = Writer.YOffsetFactor[yAlign] * (height - lines.length * lineHeight); lines.forEach((line: string, i: number) => { - this.writeLine(line, writingArea, width, xAlign, (i + 1) * lineHeight + yOffset); + const xOffset = (shearShift > 0) ? (i + 1) * shearShift : (i) * shearShift; + this.writeLine(line, writingArea, width, xAlign, xOffset, (i + 1) * lineHeight + yOffset); }); } From 4d0779c8f869097ae9549218e6a7f085ad446026 Mon Sep 17 00:00:00 2001 From: Bill Dwyer Date: Wed, 8 Feb 2017 17:08:22 -0800 Subject: [PATCH 2/7] lint --- src/writers/writer.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/writers/writer.ts b/src/writers/writer.ts index 47ded9a..8db4f98 100644 --- a/src/writers/writer.ts +++ b/src/writers/writer.ts @@ -118,7 +118,6 @@ export class Writer { shearCorrectedSecondaryDimension, ).wrappedText : normalizedText; - this.writeText( wrappedText, textArea, @@ -140,9 +139,6 @@ export class Writer { const shearCorrectedOffset = Writer.XOffsetFactor[options.xAlign] * shearCorrectedPrimaryDimension * Math.sin(shearRadians); - // console.log(options.textRotation, shearDegrees, "->", { - // shearShift, primaryDimension, shearCorrectedPrimaryDimension, shearCorrectedOffset}); - switch (options.textRotation) { case 90: xForm.translate = [width + shearCorrectedOffset, 0]; @@ -171,7 +167,9 @@ export class Writer { } } - private writeLine(line: string, g: d3.Selection, width: number, xAlign: string, xOffset: number, yOffset: number) { + private writeLine( + line: string, g: d3.Selection, width: number, + xAlign: string, xOffset: number, yOffset: number) { const textEl = g.append("text"); textEl.text(line); xOffset += width * Writer.XOffsetFactor[xAlign]; From 756d393c4bb92717a2f6ac699c4a33e127864a1a Mon Sep 17 00:00:00 2001 From: Bill Dwyer Date: Thu, 9 Feb 2017 12:56:45 -0800 Subject: [PATCH 3/7] Make preview more interactive. Remove unused transform --- preview/index.css | 7 +++++- preview/index.html | 30 +++++++++++++++++++---- preview/preview.js | 56 +++++++++++++++++++++++++++++-------------- src/writers/writer.ts | 17 +++++-------- 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/preview/index.css b/preview/index.css index 4d29681..235ec2b 100644 --- a/preview/index.css +++ b/preview/index.css @@ -1,5 +1,5 @@ body { - padding: 40px 20px 20px 20px; + padding: 20px 20px 20px 20px; background:#EEE; font-family: sans-serif; } @@ -22,6 +22,11 @@ svg.big { height: 200px; } +label { + display: inline-block; + width: 120px; +} + .panel { position: relative; height: 600px; diff --git a/preview/index.html b/preview/index.html index 3e9438a..f7abf11 100644 --- a/preview/index.html +++ b/preview/index.html @@ -8,8 +8,6 @@
-

SVGTypewriter

-
Input text here
@@ -21,8 +19,6 @@
Input text here
-
Shear Angle
-
SVG output here
@@ -31,7 +27,31 @@
SVG output here
- + +
Configurable
+ + + + +
+ + + +
+ + + + + + +
+ + + + + +
+
diff --git a/preview/preview.js b/preview/preview.js index e0014d5..840b323 100644 --- a/preview/preview.js +++ b/preview/preview.js @@ -16,7 +16,7 @@ function createUpdater(selector, options) { const writer = new SVGTypewriter.Writer(measurer, wrapper); const update = function() { - const rect = element.getBoundingClientRect(); + const rect = writeOptions.rect == null ? element.getBoundingClientRect() : writeOptions.rect; selection.selectAll("*").remove() writer.write(this.text, rect.width, rect.height, this.options); }; @@ -34,12 +34,17 @@ const updatables = [ createUpdater("#svg3", {textRotation: 90}), createUpdater("#svg4", {textRotation: -90}), createUpdater("#svg5", {xAlign: "right"}), - createUpdater("#svg6", { - textRotation: -90, - textShear: 0, - xAlign: "right" - }), ]; +const configurable = createUpdater("#shearPreview", { + textRotation: -90, + textShear: 0, + xAlign: "right", + rect: { + width: 100, + height: 100 + } +}); +updatables.push(configurable); // bind text area const textArea = document.querySelector("textarea"); @@ -54,24 +59,39 @@ textArea.addEventListener("change", updateText); textArea.addEventListener("keyup", updateText); updateText(); +// bind text setters +const textSetters = document.querySelectorAll("input[data-text]"); +Array.prototype.forEach.call(textSetters, (textSetter) => { + textSetter.addEventListener("click", () => { + textArea.value = textSetter.getAttribute("data-text"); + updateText(); + }); +}); + // bind shear slider const slider = document.querySelector("input#shear"); function updateShear() { const value = parseInt(slider.value); - updatables.forEach((u) => { - if (u.options.textShear != null) { - u.options.textShear = value; - u.update.apply(u); - } - }); + configurable.options.textShear = value; + configurable.update.apply(configurable); }; -slider.addEventListener("change", updateShear); +slider.addEventListener("input", updateShear); updateShear(); -const textSetters = document.querySelectorAll("input[data-text]"); -textSetters.forEach((textSetter) => { - textSetter.addEventListener("click", () => { - textArea.value = textSetter.getAttribute("data-text"); - updateText(); +// bind angles +const rotationSetters = document.querySelectorAll("input[data-rotation]"); +Array.prototype.forEach.call(rotationSetters, (button) => { + button.addEventListener("click", () => { + configurable.options.textRotation = parseInt(button.getAttribute("data-rotation")); + configurable.update.apply(configurable); }); }); + +// bind alignment +const alignmentSetters = document.querySelectorAll("input[data-alignment]"); +Array.prototype.forEach.call(alignmentSetters, (button) => { + button.addEventListener("click", () => { + configurable.options.xAlign = button.getAttribute("data-alignment"); + configurable.update.apply(configurable); + }); +}); \ No newline at end of file diff --git a/src/writers/writer.ts b/src/writers/writer.ts index 8db4f98..60937e7 100644 --- a/src/writers/writer.ts +++ b/src/writers/writer.ts @@ -128,10 +128,8 @@ export class Writer { options.yAlign, ); - const xForm = d3.transform(""); - const xForm2 = d3.transform(""); - // apply text rotation and shear angles + const xForm = d3.transform(""); xForm.rotate = options.textRotation + shearDegrees; // correct the intial X offset of the text container accounting for shear @@ -142,18 +140,12 @@ export class Writer { switch (options.textRotation) { case 90: xForm.translate = [width + shearCorrectedOffset, 0]; - xForm2.rotate = -90 - shearDegrees; - xForm2.translate = [0, 200]; break; case -90: xForm.translate = [-shearCorrectedOffset, height]; - xForm2.rotate = 90 + shearDegrees; - xForm2.translate = [width, 0]; break; case 180: xForm.translate = [width, height + shearCorrectedOffset]; - xForm2.translate = [width, height + shearCorrectedOffset]; - xForm2.rotate = 180 - shearDegrees; break; default: xForm.translate = [0, -shearCorrectedOffset]; @@ -161,7 +153,10 @@ export class Writer { } textArea.attr("transform", xForm.toString()); - this.addClipPath(textContainer, xForm2); + + // TODO This has never taken into account the transform at all, so it's + // certainly in the wrong place. Why do we need it? + this.addClipPath(textContainer); if (options.animator) { options.animator.animate(textContainer); } @@ -195,7 +190,7 @@ export class Writer { }); } - private addClipPath(selection: d3.Selection, _transform: any) { + private addClipPath(selection: d3.Selection) { const elementID = this._elementID++; let prefix = /MSIE [5-9]/.test(navigator.userAgent) ? "" : document.location.href; prefix = prefix.split("#")[0]; // To fix cases where an anchor tag was used From 8c4bcc052e59f3f602609328c65d60859117dc9a Mon Sep 17 00:00:00 2001 From: Bill Dwyer Date: Thu, 9 Feb 2017 13:50:09 -0800 Subject: [PATCH 4/7] Fix ellipses Previously svg-typewriter would add two sets of ellipses to a wrapped line in many cases. This change fixes that bug. Also updating the unit test that was wrong. Also switching to circle-comment-bot library instead of shell script. --- .gitignore | 1 + circle.yml | 2 +- package.json | 2 ++ preview.sh | 48 ----------------------------------------- preview/demo.js | 13 +++++++++++ preview/index.css | 2 +- src/wrappers/wrapper.ts | 5 ++--- test/wrapperTests.ts | 4 ++-- yarn.lock | 4 ++++ 9 files changed, 26 insertions(+), 55 deletions(-) delete mode 100755 preview.sh create mode 100755 preview/demo.js diff --git a/.gitignore b/.gitignore index ca99189..327140c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ build coverage preview/bundle.js svg-typewriter.js +*.log # IDEs .idea/ diff --git a/circle.yml b/circle.yml index fdda359..6fc2046 100644 --- a/circle.yml +++ b/circle.yml @@ -13,7 +13,7 @@ deployment: preview: branch: /.*/ commands: - - ./preview.sh + - ./preview/demo.js npm: tag: /release-.*/ owner: palantir diff --git a/package.json b/package.json index 050a7ed..9f9ae50 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "test": "npm-run-all build lint:ts test:mocha test:coverage", "test:coverage": "istanbul check-coverage", "test:mocha": "PATH=$PATH:$(npm bin) mochify --reporter spec --plugin [ mochify-istanbul --report text --report json --dir coverage --exclude '**/test/**/*' ] ${npm_package_testsGlob}", + "test:local": "PATH=$PATH:$(npm bin) mochify --reporter spec ${npm_package_testsGlob}", "test:sauce": "mochify --reporter spec --wd ${npm_package_testsGlob}", "echo": "echo" }, @@ -41,6 +42,7 @@ "license": "MIT", "dependencies": { "@types/d3": "^3.5", + "circle-github-bot": "^0.4.0", "d3": "^3.5" }, "devDependencies": { diff --git a/preview.sh b/preview.sh deleted file mode 100755 index 7507b4e..0000000 --- a/preview.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# Echos the specified package.json variable -function npmvar { - npm run -s echo -- '$npm_package_'$1 -} - -# Compute all the fancy artifact variables for preview scripts -BUILD_PATH="/home/ubuntu/$(npmvar 'name')" -ARTIFACTS_URL="https://circleci.com/api/v1/project/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/$CIRCLE_BUILD_NUM/artifacts/0/$BUILD_PATH" -GH_API_URL="x-oauth-basic@api.github.com" -PROJECT_API_BASE_URL="https://$GH_AUTH_TOKEN:$GH_API_URL/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME" -PR_NUMBER=$(basename $CI_PULL_REQUEST) - -# Submit the comment -function submitPreviewComment { - COMMENT_JSON="{\"body\": \"$1\"}" - - if $PR_NUMBER; then - # post comment to PR - curl --data "$COMMENT_JSON" $PROJECT_API_BASE_URL/issues/$PR_NUMBER/comments - else - # PR not created yet; CircleCI doesn't know about it. - # post comment to commi - curl --data "$COMMENT_JSON" $PROJECT_API_BASE_URL/commits/$CIRCLE_SHA1/comments - fi -} - -# Create a artifact link string -function artifactLink { - local HREF="${ARTIFACTS_URL}${1}" - local LINK="$2" - echo "$LINK" -} - -# Build previews -echo "Building dev preview..." -npm run preview -echo "Building docs preview..." -npm run docs - -# Submit comment -echo "Submitting comment..." -COMMIT_MESSAGE=$(git --no-pager log --pretty=format:"%s" -1) -# escape commit message, see http://stackoverflow.com/a/10053951/3124288 -COMMIT_MESSAGE=${COMMIT_MESSAGE//\"/\\\"} -PREVIEWS="$(artifactLink '/docs/index.html' 'docs') | $(artifactLink '/preview/index.html' 'dev')" -submitPreviewComment "

${COMMIT_MESSAGE}

\n\nPreview: ${PREVIEWS}" diff --git a/preview/demo.js b/preview/demo.js new file mode 100755 index 0000000..745a7b2 --- /dev/null +++ b/preview/demo.js @@ -0,0 +1,13 @@ +#!/usr/bin/env node + +const bot = require("circle-github-bot").create(); + +demos = [ + bot.artifactLink('docs/fiddle.html', 'docs'), + bot.artifactLink('preview/index.html', 'dev'), +]; + +bot.comment(` +

${bot.env.commitMessage}

+Preview: ${demos.join(' | ')} +`); diff --git a/preview/index.css b/preview/index.css index 235ec2b..e232ef7 100644 --- a/preview/index.css +++ b/preview/index.css @@ -13,7 +13,7 @@ textarea { svg { display: inline-block; background: #FFF; - width: 120px; + width: 100px; height: 100px; } diff --git a/src/wrappers/wrapper.ts b/src/wrappers/wrapper.ts index 6a1e4bb..54a3741 100644 --- a/src/wrappers/wrapper.ts +++ b/src/wrappers/wrapper.ts @@ -133,9 +133,7 @@ export class Wrapper { state.wrapping.noLines += +(wrappedText !== ""); if (state.wrapping.noLines === state.availableLines && this._textTrimming !== "none" && hasNextLine) { - const ellipsisResult = this.addEllipsis(wrappedText, state.availableWidth, measurer); - state.wrapping.wrappedText += ellipsisResult.wrappedToken; - state.wrapping.truncatedText += ellipsisResult.remainingToken; + // Note: no need to add more ellipses, they were added in `wrapNextToken` state.canFitText = false; } else { state.wrapping.wrappedText += wrappedText; @@ -162,6 +160,7 @@ export class Wrapper { } let truncatedLine = line.substring(0).trim(); let lineWidth = measurer.measure(truncatedLine).width; + const ellipsesWidth = measurer.measure("...").width; const prefix = (line.length > 0 && line[0] === "\n") ? "\n" : ""; diff --git a/test/wrapperTests.ts b/test/wrapperTests.ts index 294e48f..2f78ce3 100644 --- a/test/wrapperTests.ts +++ b/test/wrapperTests.ts @@ -399,14 +399,14 @@ describe("Wrapper Test Suite", () => { it("multiple lines", () => { text = "hello world!.\nhello world!."; - const availableWidth = measurer.measure(text).width; + const availableWidth = measurer.measure("hello worl-").width; const result = wrapper.wrap(text, measurer, availableWidth); assert.deepEqual(result.originalText, text, "original text has been set"); assert.operator(result.wrappedText.indexOf("..."), ">", 0, "ellipsis has been added"); assert.deepEqual(result.wrappedText.substring(0, result.wrappedText.length - 3) + result.truncatedText, text, "non of letters disappeard"); - assert.deepEqual(result.noBrokeWords, 0, "one breaks"); + assert.deepEqual(result.noBrokeWords, 1, "one breaks"); assert.deepEqual(result.noLines, 1, "wrapped text has one lines"); }); }); diff --git a/yarn.lock b/yarn.lock index b7753a9..5af24a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -542,6 +542,10 @@ cipher-base@^1.0.0, cipher-base@^1.0.1: dependencies: inherits "^2.0.1" +circle-github-bot@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/circle-github-bot/-/circle-github-bot-0.4.0.tgz#d1702cea19277db4333936647828cd433fbcafd9" + cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" From 307da298bc40f90bf5267cbac5c346aca8e754ba Mon Sep 17 00:00:00 2001 From: Bill Dwyer Date: Thu, 9 Feb 2017 13:59:11 -0800 Subject: [PATCH 5/7] build preview bundle --- circle.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 6fc2046..7725b21 100644 --- a/circle.yml +++ b/circle.yml @@ -13,7 +13,8 @@ deployment: preview: branch: /.*/ commands: - - ./preview/demo.js + - npm run preview # build the preview bundle + - ./preview/demo.js # comment back on github npm: tag: /release-.*/ owner: palantir From 6e8268c828df6486a97e046b4ca8529295f93e80 Mon Sep 17 00:00:00 2001 From: Bill Dwyer Date: Thu, 9 Feb 2017 14:53:41 -0800 Subject: [PATCH 6/7] adding yalign to preview --- preview/index.html | 14 ++++++++++---- preview/preview.js | 17 ++++++++++++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/preview/index.html b/preview/index.html index f7abf11..2243452 100644 --- a/preview/index.html +++ b/preview/index.html @@ -46,10 +46,16 @@
Configurable

- - - - + + + + +
+ + + + +
diff --git a/preview/preview.js b/preview/preview.js index 840b323..65c28f7 100644 --- a/preview/preview.js +++ b/preview/preview.js @@ -87,11 +87,18 @@ Array.prototype.forEach.call(rotationSetters, (button) => { }); }); -// bind alignment -const alignmentSetters = document.querySelectorAll("input[data-alignment]"); -Array.prototype.forEach.call(alignmentSetters, (button) => { +// bind x alignment +Array.prototype.forEach.call(document.querySelectorAll("input[data-x-alignment]"), (button) => { button.addEventListener("click", () => { - configurable.options.xAlign = button.getAttribute("data-alignment"); + configurable.options.xAlign = button.getAttribute("data-x-alignment"); configurable.update.apply(configurable); }); -}); \ No newline at end of file +}); + +// bind y alignment +Array.prototype.forEach.call(document.querySelectorAll("input[data-y-alignment]"), (button) => { + button.addEventListener("click", () => { + configurable.options.yAlign = button.getAttribute("data-y-alignment"); + configurable.update.apply(configurable); + }); +}); From 6a50d4adff80f1824731432e54a15666be672c3e Mon Sep 17 00:00:00 2001 From: Bill Dwyer Date: Thu, 9 Feb 2017 16:08:43 -0800 Subject: [PATCH 7/7] Fix x- and y-alignment correction --- preview/index.html | 9 +++--- preview/preview.js | 6 ++-- src/writers/writer.ts | 67 +++++++++++++++++++++++-------------------- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/preview/index.html b/preview/index.html index 2243452..eb1963e 100644 --- a/preview/index.html +++ b/preview/index.html @@ -23,10 +23,9 @@
Input text here
SVG output here
- - - - +
+ +
Configurable
@@ -42,8 +41,8 @@
Configurable
- +
diff --git a/preview/preview.js b/preview/preview.js index 65c28f7..e1fdff5 100644 --- a/preview/preview.js +++ b/preview/preview.js @@ -30,10 +30,8 @@ function createUpdater(selector, options) { const updatables = [ createUpdater("#svg1"), - createUpdater("#svg2"), - createUpdater("#svg3", {textRotation: 90}), - createUpdater("#svg4", {textRotation: -90}), - createUpdater("#svg5", {xAlign: "right"}), + createUpdater("#svg2", {textRotation: -90}), + createUpdater("#svg3", {xAlign: "right"}), ]; const configurable = createUpdater("#shearPreview", { textRotation: -90, diff --git a/src/writers/writer.ts b/src/writers/writer.ts index 60937e7..9def028 100644 --- a/src/writers/writer.ts +++ b/src/writers/writer.ts @@ -102,14 +102,8 @@ export class Writer { const shearCorrectedPrimaryDimension = primaryDimension / Math.cos(shearRadians) - Math.abs(shearShift); const shearCorrectedSecondaryDimension = secondaryDimension * Math.cos(shearRadians); - const textContainer = options.selection.append("g").classed("text-container", true); - if (this._addTitleElement) { - textContainer.append("title").text(text); - } - + // normalize and wrap text const normalizedText = Utils.StringMethods.combineWhitespace(text); - - const textArea = textContainer.append("g").classed("text-area", true); const wrappedText = this._wrapper ? this._wrapper.wrap( normalizedText, @@ -117,43 +111,58 @@ export class Writer { shearCorrectedPrimaryDimension, shearCorrectedSecondaryDimension, ).wrappedText : normalizedText; + const lines = wrappedText.split("\n"); - this.writeText( - wrappedText, + // prepare svg components + const textContainer = options.selection.append("g").classed("text-container", true); + if (this._addTitleElement) { + textContainer.append("title").text(text); + } + const textArea = textContainer.append("g").classed("text-area", true); + this.writeLines( + lines, textArea, shearCorrectedPrimaryDimension, - shearCorrectedSecondaryDimension, shearShift, options.xAlign, - options.yAlign, ); - // apply text rotation and shear angles - const xForm = d3.transform(""); - xForm.rotate = options.textRotation + shearDegrees; - - // correct the intial X offset of the text container accounting for shear - // angle and alignment option. - const shearCorrectedOffset = Writer.XOffsetFactor[options.xAlign] * + // correct the intial x/y offset of the text container accounting shear and alignment + const shearCorrectedXOffset = Writer.XOffsetFactor[options.xAlign] * shearCorrectedPrimaryDimension * Math.sin(shearRadians); + const shearCorrectedYOffset = Writer.YOffsetFactor[options.yAlign] * + (shearCorrectedSecondaryDimension - (lines.length) * lineHeight); + const shearCorrection = shearCorrectedXOffset - shearCorrectedYOffset; + // build and apply transform + const xForm = d3.transform(""); + xForm.rotate = options.textRotation + shearDegrees; switch (options.textRotation) { case 90: - xForm.translate = [width + shearCorrectedOffset, 0]; + xForm.translate = [width + shearCorrection, 0]; break; case -90: - xForm.translate = [-shearCorrectedOffset, height]; + xForm.translate = [-shearCorrection, height]; break; case 180: - xForm.translate = [width, height + shearCorrectedOffset]; + xForm.translate = [width, height + shearCorrection]; break; default: - xForm.translate = [0, -shearCorrectedOffset]; + xForm.translate = [0, -shearCorrection]; break; } - textArea.attr("transform", xForm.toString()); + // // DEBUG + // textArea.append("rect").attr({ + // x: Math.max(0, shearShift), + // y: 0, + // width: shearCorrectedPrimaryDimension, + // height: shearCorrectedSecondaryDimension, + // fill: "none", + // stroke: "blue" + // }); + // TODO This has never taken into account the transform at all, so it's // certainly in the wrong place. Why do we need it? this.addClipPath(textContainer); @@ -173,20 +182,16 @@ export class Writer { Utils.DOM.transform(textEl, xOffset, yOffset).attr("y", "-0.25em"); } - private writeText( - text: string, + private writeLines( + lines: string[], writingArea: d3.Selection, width: number, - height: number, shearShift: number, - xAlign: string, - yAlign: string) { - const lines = text.split("\n"); + xAlign: string) { const lineHeight = this._measurer.measure().height; - const yOffset = Writer.YOffsetFactor[yAlign] * (height - lines.length * lineHeight); lines.forEach((line: string, i: number) => { const xOffset = (shearShift > 0) ? (i + 1) * shearShift : (i) * shearShift; - this.writeLine(line, writingArea, width, xAlign, xOffset, (i + 1) * lineHeight + yOffset); + this.writeLine(line, writingArea, width, xAlign, xOffset, (i + 1) * lineHeight); }); }