Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transform() origin issue #1085

Closed
ionut-gheorghe opened this issue Feb 21, 2020 · 13 comments
Closed

Transform() origin issue #1085

ionut-gheorghe opened this issue Feb 21, 2020 · 13 comments
Labels

Comments

@ionut-gheorghe
Copy link

Bug report

Cannot set origin with the transform() method
image

Also in typescript I cannot use origin like in the documentation
image

I'm using the 3.0.16 version of the library

@Fuzzyma
Copy link
Member

Fuzzyma commented Feb 21, 2020

Alright, seems like an error in the d.ts file. Can you provide corrected typings?

@ionut-gheorghe
Copy link
Author

ionut-gheorghe commented Feb 21, 2020

Added the folowing to the svg.js.d.ts and works correctly.

image

Is there a way to get intellisense for origin options?

@Fuzzyma
Copy link
Member

Fuzzyma commented Feb 21, 2020

Nice! You mean autocompletion for supported strings?

@ionut-gheorghe
Copy link
Author

Yep, autocomplete for origin, originX and originY

@Fuzzyma
Copy link
Member

Fuzzyma commented Feb 22, 2020

Well since originX and Y is only a number you are not getting anything special here. And for the origin case: Any combination of 'topleft', 'top left', 'top-left', 'left_top' or whatever is allowed and therefore we cannot really type it properly. Beside that words, only numbers are possible so nothing else to show here

@luismarcelino
Copy link

I also have an issue with Transform() origin: if coordinates are specified as an array [, ] the transformation appears to be fine, however is origin is specified as { x: , y: } results in origin (0, 0).
In this example, the blue and gray boxes should overlap: https://jsfiddle.net/luismarcelino/g2ft9yqb

There is also a problem with the transformation when position is set to y=0 (in the above example, if you try to set the position.y to 0 for the blue rect it will place it as if the origin was not set (but y=0.0001 works fine)

@Fuzzyma
Copy link
Member

Fuzzyma commented Feb 26, 2020

There is also a problem with the transformation when position is set to y=0 (in the above example, if you try to set the position.y to 0 for the blue rect it will place it as if the origin was not set (but y=0.0001 works fine)

That is most likely because of an unresolved todo in the source lol.

if (isFinite(t.px) || isFinite(t.py)) {
const origin = new Point(ox, oy).transform(transformer)
// TODO: Replace t.px with isFinite(t.px)
const dx = t.px ? t.px - origin.x : 0
const dy = t.py ? t.py - origin.y : 0
transformer.translateO(dx, dy)
}

However, passing an origin of {x, y} should be fine. Have to debug this...

@luismarcelino
Copy link

Yes, I think that solves the "==0" issue.
I believe the other problem is in the getOrigin method

svg.js/src/utils/utils.js

Lines 98 to 101 in 0d50ae8

} else {
ox = origin[0]
oy = origin[1]
}

I was actually preparing a PR with the done TODO and that code changed, but I didn't have the change to try the code properly.

 } else if (origin.x && origin.y) {
    ox = origin.x
    oy = origin.y
  } else {
    ox = origin[0]
    oy = origin[1]
  }

@Fuzzyma
Copy link
Member

Fuzzyma commented Feb 26, 2020

Wow nice catch! Thats a huge bug. Origin can be passed as ox, oy, originX, originY, [ox, oy], {x: ox, y: oy} and as string. That means that basically only the array, string and ox notation works properly - outch!
However, the getOrigin method is only there to make the support of the string notation work. Every other notation is handled when the object is passed to Matrix.transform(). Therefore, we have to rewrite the code so that the method does nothing when not a string was passed.
But this comes with its own bug. Because 'center' should be assumed when no origin at all was passed. But ox, oy / originX, originY could be set. Only checking if origin is null is not enough. We also have to check if ox and originX have values

So the actual fix could be:

transform.js

  if (!Matrix.isMatrixLike(o)) {
    // Set the origin according to the defined transform
    o = { ...o, ...getOrigin(o, this) }
  }

Matrix.js

export function getOrigin (o, element) {
  const hasOrigin = ox != null || originX != null
  if (hasOrigin) return {}

  const origin = o.origin

  // Allow the user to pass a string to rotate around a given point
  if (typeof origin !== 'string' && origin != null) {
    return {}
  }

  // Get the bounding box of the element with no transformations applied
  const string = (origin || 'center').toLowerCase().trim()
  const { height, width, x, y } = element.bbox()

  // Calculate the transformed x and y coordinates
  const ox = string.includes('left') ? x
    : string.includes('right') ? x + width
    : x + width / 2
  const oy = string.includes('top') ? y
    : string.includes('bottom') ? y + height
    : y + height / 2

  return {origin: [ox, oy]}
}

Maybe its a bit awkward to return an empty object. As an alternative we can do the checks before calling getOrigin and calling it only when it is a string or null and ox and originX are not set

@luismarcelino
Copy link

It is nice to have someone controlling the "big picture"! :)
I assumed that getOrigin() would return the origin under all conditions (and I did not explore the Matrix.isMatrixLike()). Maybe getOrigin should be named getOriginFromString! :)

I believe that the first line in the getOrigin() should be (parenthesis just for readability):

  const hasOrigin = (o.ox != null || o.originX != null)

Thanks for looking into the issue and I look forward for your updates.

@Fuzzyma
Copy link
Member

Fuzzyma commented Feb 29, 2020

Yes ofc you are correct. Looks like I have to set beside some time for fixing all these good issues popping up. Hope I get it fixed soon enough!

@Fuzzyma Fuzzyma added the bug label Feb 29, 2020
@luismarcelino
Copy link

luismarcelino commented Feb 29, 2020 via email

@Fuzzyma
Copy link
Member

Fuzzyma commented Mar 3, 2020

So here is what I came up with:

/**
 * This function adds support for string origins.
 * It searches for an origin in o.origin o.ox and o.originX.
 * This way, origin: {x: 'center', y: 50} can be passed as well as ox: 'center', oy: 50
**/
export function getOrigin (o, element) {
  const origin = o.origin
  // first check if origin is in ox or originX
  let ox = o.ox != null ? o.ox
    : o.originX != null ? o.originX
    : 'center'
  let oy = o.oy != null ? o.oy
    : o.originY != null ? o.originY
    : 'center'

  // then check if origin was used and overwrite in that case
  if (origin != null) {
    [ o.x, o.y ] = Array.isArray(origin) ? origin
      : typeof origin === 'object' ? [ origin.x, origin.y ]
      : [ origin, origin ]
  }

  // make sure to only call bbox when actually needed
  const condX = typeof ox === 'string'
  const condY = typeof oy === 'string'
  if (condX || condY) {
    const { height, width, x, y } = element.bbox()

    // and only overwrite if string was passed for this specific axis
    if (condX) {
      ox = ox.includes('left') ? x
        : ox.includes('right') ? x + width
        : x + width / 2
    }

    if (condY) {
      oy = oy.includes('top') ? y
        : oy.includes('bottom') ? y + height
        : y + height / 2
    }
  }

  // Return the origin as it is if it wasn't a string
  return [ ox, oy ]
}

Fuzzyma added a commit that referenced this issue Mar 28, 2020
### Fixed
 - fixed `zoom()` method of runner which was passed a wrong parameter
 - fixed positioning methods of `TSpan` to position them by its bounding box
 - fixed `flip()` method which flips correctly by center by default now and accepts correct arguments
 - fixed a case in `rbox()` where not always all values of the box were updated
 - fixed `getOrigin()` function used by `transform()` so that all origin (#1085) popssibilities specified in the docs are working
 - fixed positioning of text by its baseline when using `amove()`
 - fixed tons of typings in the svg.d.ts file

### Added
 - added second Parameter to `SVG(el, isHTML)` which allows to explicitely create elements in the HTML namespace (#1058)
 - added `unlink()` and `linker()` to hyperlinked elements to remove or access the underling `<a>` element
 - added `wrap()` method to `Dom` which lets you wrap an element by another one
 - added `orient()` method to `Marker`
 - added `options` parameter to `dispatch()` and `fire()` to allow for more special needs
 - added `newLine()` constructor to `Text` to create a tspan marked as new line (#1088)
 - added lots of tests in es6 format
@Fuzzyma Fuzzyma closed this as completed Sep 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants