CreativeInquiry / PEmbroider

Embroidery Library for Processing
Other
442 stars 28 forks source link

Confusion regarding strokeSpacing, hatchSpacing, stitchLength scaling w/transforms? #30

Closed golanlevin closed 4 years ago

golanlevin commented 4 years ago

I may be overthinking this, but: Below are two circles, both drawn with hatchSpacing(10) and PARALLEL hatch, but in the second case, I have scaled the world by 0.5. (code is below). The stitch length is being scaled, as is the hatch spacing:

Screen Shot 2020-06-17 at 11 33 31 PM

Here's another case, where I'm using CONCENTRIC hatch, and scaling non-uniformly. In this case, the stitch length is NOT being scaled, but the hatch spacing is:

Screen Shot 2020-06-17 at 11 38 00 PM

A few thoughts:

Screen Shot 2020-06-17 at 11 43 40 PM

@LingDong- , @tatyanade , what is your opinion of this question?

import processing.embroider.*;
void setup() {
  noLoop(); 
  size (800, 600);
  PEmbroiderGraphics E = new PEmbroiderGraphics(this, width, height);

  E.beginDraw(); 
  E.clear();
  E.noStroke();
  E.fill(0, 0, 0);
  E.ellipseMode(CENTER); 
  E.hatchMode(PEmbroiderGraphics.PARALLEL); 
  E.hatchSpacing(10); 

  E.circle(200, 200, 200); 

  E.pushMatrix();
  E.translate(400, 200); 
  E.scale(0.5, 0.5); 
  E.circle(0, 0, 200); 
  E.popMatrix(); 

  E.visualize();//true, true, true
}
LingDong- commented 4 years ago

@golanlevin

For PARALLEL mode, it is a special case, because we're using the special greek algorithm to resample it. Therefore the resampling has to happen before the matrix is applied. Normally, the matrix is first applied, and then resample happens.

Unless we save a global variable every time user do scaling. (Not going to work if the user applies a transformation matrix themselves, which will then involve doing matrix decomposition to figure out the scale factor) Which is a ugly hack but as you know I'm happy to add ugly hacks.

golanlevin commented 4 years ago

This isn't an urgent issue, but we should discuss and think it through. Let's assume that the user doesn't do anything weird with customized matrices. Instead, we will only deal with the case in which the user applies E.push/scale/pop commands.

  1. At the very least, I prefer a system in which the resampling of stitches happens at the very end. So a user could draw a sequence of differently-scaled Ducks, but they will all have the same stitch length.

  2. I also think there should be consistent behavior for stitch length, across all of the different hatchers and strokers, regarding how they are affected by scaling.

  3. It's a good question as to whether hatches (hatchSpacing and strokeSpacing) should be affected by the scaling operation. I am inclined to think this should be optional according to a drawing "mode". I don't know what the default should be (true/false).

  4. So it may be necessary to instrument the renderer so that, at any point in time, it is possible to look up the current scale transformation. Making a note that it could be non-uniform (different X and Y scale factors).

LingDong- commented 4 years ago

@golanlevin

At the very least, I prefer a system in which the resampling of stitches happens at the very end. So a user could draw a sequence of differently-scaled Ducks, but they will all have the same stitch length.

This is currently exactly what happens, except for the PARALLEL hatches, because we're using the special greek algorithm to do the resample which needs to happen before applying matrices. So yes, we'll need to save current scale every time user changes it, applyMatrix will not work, and will need to take rotation into account. Which is still doable.

I also think there should be consistent behavior for stitch length, across all of the different hatchers and strokers, regarding how they are affected by scaling.

This is currently exactly what happens, except for PARALLEL hatches, because we're using the special greek algorithm to do the resample which needs to happen before applying matrices.

It's a good question as to whether hatches (hatchSpacing and strokeSpacing) should be affected by the scaling operation. I am inclined to think this should be optional according to a drawing "mode". I don't know what the default should be (true/false).

I think hatches should be affected by scaling. In Processing and p5 (and HTML canvas, and probably OF), strokeWeight is affected by scaling. Also I don't think I think we have to provide a mode: it is easy enough for user to compute the new spacing if they don't want it to be affected. It is not harder at all than learning about and remembering a function to call every time they want that behavior. And if the user changed the mode earlier but forgot to set it back, then they might get even more confused. If our user is already writing code to generate their designs, we should assume they also understand how multiplication works.

Anyways just my opinion :)

golanlevin commented 4 years ago

OK, you have convinced me that hatches should be affected by scale(), as they already are, and that we don't need to provide a special mode to enable/disable this.

That said, could you kindly make a special case to handle scale-insensitive stitch length in PARALLEL mode.

Thanks @LingDong- !

LingDong- commented 4 years ago

@golanlevin

Now fixed with an elegant solution: 17907c75bd958ff48e5c0c6e8b64b20f0e1989ea

Instead of tracking scale every time it changes, we can apply the current transformation matrix stack to a unit vector and see what happens.

The unit vector points toward the direction of scaling we're interested in. If we want to know horizontal scaling, it's (1,0), vertical, (0,1). In the case of PARALLEL resampling, it's (cos(θ+π/2),sin(θ+π/2)). We also apply the transformation to zero (0,0) (to account for translation). Then we compute the distance between transformed zero, and transformed unit vector. This is the scaling. I think this will work for all affine transformations.

golanlevin commented 4 years ago

Genius

golanlevin commented 4 years ago

Hi @LingDong- , in examples/tests/PEmbroider_scaling_test.pde, I made the test program below. Could you please verify that the scaling of hatches and stitches is doing the expected behavior?

Screen Shot 2020-06-26 at 3 54 26 PM
LingDong- commented 4 years ago

@golanlevin

Looks like it's working correctly.

In the first column, the stitches looks smaller in the first row because the second argument setStitch(), which is the desired stitch length, is pretty large, and in order to preserve corners (in this case curvature), the curve is sampled more frequently. The minimum stitch length is set with the first argument to setStitch(), which looks like it is being abided too.

In the second column, the stitch length looks exactly the same, that's correct.

In the third column, spiral is derived from concentric, so reasoning in the first paragraph applies.

For the fourth column, the logic of satin stitches has now changed (screenshot seems to be using an older version). In the older version, the length of each stitch affected by size of each "pixel", whose size is determined by the spacing. However, even in the older version, the stitches then are resampled before being finalized. So the top and bottom rows, though a bit different, are both within the range between minimum stitch length and desired stitch length specified by setStitch(). But that's the older version, since we're now using true width for width and scaled height for height, this difference is gone too in the current version.

The spacing is scaled which is the expected behaviour as we discussed.

golanlevin commented 4 years ago

Excellent, thanks for verifying.