modeling/src/text/vectorText.js

  1. const vectorChar = require('./vectorChar')
  2. const vectorParams = require('./vectorParams')
  3. // translate text line
  4. const translateLine = (options, line) => {
  5. const { x, y } = Object.assign({ x: 0, y: 0 }, options || {})
  6. const segments = line.segments
  7. let segment = null
  8. let point = null
  9. for (let i = 0, il = segments.length; i < il; i++) {
  10. segment = segments[i]
  11. for (let j = 0, jl = segment.length; j < jl; j++) {
  12. point = segment[j]
  13. segment[j] = [point[0] + x, point[1] + y]
  14. }
  15. }
  16. return line
  17. }
  18. /**
  19. * Construct an array of character segments from a ascii string whose characters code is between 31 and 127,
  20. * if one character is not supported it is replaced by a question mark.
  21. * @param {Object|String} [options] - options for construction or ascii string
  22. * @param {Float} [options.xOffset=0] - x offset
  23. * @param {Float} [options.yOffset=0] - y offset
  24. * @param {Float} [options.height=21] - font size (uppercase height)
  25. * @param {Float} [options.lineSpacing=1.4] - line spacing expressed as a percentage of font size
  26. * @param {Float} [options.letterSpacing=1] - extra letter spacing expressed as a percentage of font size
  27. * @param {String} [options.align='left'] - multi-line text alignment: left, center, right
  28. * @param {Float} [options.extrudeOffset=0] - width of the extrusion that will be applied (manually) after the creation of the character
  29. * @param {String} [options.input='?'] - ascii string (ignored/overwrited if provided as seconds parameter)
  30. * @param {String} [text='?'] - ascii string
  31. * @returns {Array} characters segments [[[x, y], ...], ...]
  32. * @alias module:modeling/text.vectorText
  33. *
  34. * @example
  35. * let textSegments = vectorText()
  36. * or
  37. * let textSegments = vectorText('OpenJSCAD')
  38. * or
  39. * let textSegments = vectorText({ yOffset: -50 }, 'OpenJSCAD')
  40. * or
  41. * let textSegments = vectorText({ yOffset: -80, input: 'OpenJSCAD' })
  42. */
  43. const vectorText = (options, text) => {
  44. const {
  45. xOffset, yOffset, input, font, height, align, extrudeOffset, lineSpacing, letterSpacing
  46. } = vectorParams(options, text)
  47. let [x, y] = [xOffset, yOffset]
  48. let i, il, char, vect, width, diff
  49. let line = { width: 0, segments: [] }
  50. const lines = []
  51. let output = []
  52. let maxWidth = 0
  53. const lineStart = x
  54. const pushLine = () => {
  55. lines.push(line)
  56. maxWidth = Math.max(maxWidth, line.width)
  57. line = { width: 0, segments: [] }
  58. }
  59. for (i = 0, il = input.length; i < il; i++) {
  60. char = input[i]
  61. vect = vectorChar({ xOffset: x, yOffset: y, font, height, extrudeOffset }, char)
  62. if (char === '\n') {
  63. x = lineStart
  64. y -= vect.height * lineSpacing
  65. pushLine()
  66. continue
  67. }
  68. width = vect.width * letterSpacing
  69. line.width += width
  70. x += width
  71. if (char !== ' ') {
  72. line.segments = line.segments.concat(vect.segments)
  73. }
  74. }
  75. if (line.segments.length) {
  76. pushLine()
  77. }
  78. for (i = 0, il = lines.length; i < il; i++) {
  79. line = lines[i]
  80. if (maxWidth > line.width) {
  81. diff = maxWidth - line.width
  82. if (align === 'right') {
  83. line = translateLine({ x: diff }, line)
  84. } else if (align === 'center') {
  85. line = translateLine({ x: diff / 2 }, line)
  86. }
  87. }
  88. output = output.concat(line.segments)
  89. }
  90. return output
  91. }
  92. module.exports = vectorText