Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

As I could swear this worked just yesterday, I am probably just doing something stupid here and I am sorry to bother you :)

I am trying to find the point where a curve crosses a line. In this case, the curve is a box, but it should work for other things, too, so I need to find the problem here, not better ways for a solution for boxes.

boxCurve[boundaries_List] := Module[{b = boundaries, bH, bW, cf},
  bH = b[[2, 2]] - b[[2, 1]];
  bW = b[[1, 2]] - b[[1, 1]];
  cf = 2 bH + 2 bW;
  Function[t, Piecewise[{
     {{b[[1, 1]] + cf t, b[[2, 1]]}, 0 <= cf t < bW},
     {{b[[1, 2]], b[[2, 1]] + (cf t - bW)}, bW <= cf t < bW + bH},
     {{b[[1, 2]] - (cf t - bW - bH), b[[2, 2]]}, 
      bW + bH <= cf t < 2 bW + bH},
     {{b[[1, 1]], b[[2, 2]] - (cf t - 2 bW - bH)}, 
      2 bW + bH <= cf t < 2 bW + 2 bH}
     }, {b[[1, 1]], b[[2, 1]] + bH/4}]]
  ]

The box does work ParametricPlot[boxCurve[{{-1, 1}, {-1, 1}}][s], {s, 0, 1}] But, when I am trying to calculate the crossing point

Solve[boxCurve[{{-1, 1}, {-1, 1}}][s] == {-1, 1} + {1, -1} t, {s, t}]
Solve[boxCurve[{{-1, 1}, {-1, 1}}][s] == {-0.9, 1} + {1, -1} t, {s, t}]
Solve[boxCurve[{{-1, 1}, {-1, 1}}][s] == {0, 0} + {1, 0} t, {s, t}]

I get no result, although two of the base points of the lines are already on the box. This is true for both NSolve and Solve.

As said I could swear that something like this worked yesterday, because I got conditional expressions depending on s, when I only solved for t.

share|improve this question
    
I would prefer a general solution, but if nothing else comes, I am willing to give the bounty to an answer that solves the problem for the case where one curve is a straight line and only the other curve is arbitrarily continuous. Also, the first crossing point in a given direction from a defined point on the line would be enough. –  mcandril Jun 15 '12 at 7:50
    
It seems like the problem in what you post really boils down to solve not handling the conditions of piece-wise unless you hit the default conditions. This fails: Solve[Piecewise[{{{0, t}, 0 < t <= 1}}] == {t2,1/2},{t, t2}] yet this succeeds: Solve[ConditionalExpression[{0, t}, 0 < t <= 1] == {t2, 0.5}, {t, t2}]. If you are having problems with general shapes defined without piecewise, I would suggest you update your question with simple examples. –  jVincent Jun 15 '12 at 10:26
    
No, I definitely need piecewise defined functions. Strictly speaking, this is exactly on of the cases (but not the only one), where I need this function. Want I am trying to do is writing a Module, where you input a "surface" and the starting point and direction of a light beam, and it calculates the light path upon potentially multiple reflection. All in 2D, so curves are enough. –  mcandril Jun 15 '12 at 11:24
    
Well then I'm unsure of your remaining problem is. It seems Heike's answer solves the only special case where your code doesn't work. –  jVincent Jun 15 '12 at 11:43
    
It does not consider the else case of Piecewise, does it? Then, I am not quite sure how to differentiate if the submitted function is defined piecewise. And finally, I would really like to know how I got conditional answers from Solve (or NSolve, not sure) that one time, because that Solution would be much cleaner. –  mcandril Jun 15 '12 at 11:51
show 2 more comments

3 Answers 3

up vote 3 down vote accepted
+50

For a function defined using Piecewise you could try something like this

SetAttributes[solve, HoldAll];

solve[(bc : boxCurve[a_][s_]) == b_, c__] := Solve[
  Or @@ Append[(#1 == b && #2) & @@@ bc[[1]],
    bc[[2]] == b && Not[Or @@ bc[[1, All, 2]]]], c]

solve[boxCurve[{{-1, 1}, {-1, 1}}][s] == {t, t/2}, {s, t}]    

(* {{t -> ConditionalExpression[-1, s >= 1 || s < 0]}, 
    {s -> 7/16, t -> 1}, {s -> 15/16, t -> -1}} *)

Edit

I've wrapped the whole thing in a function to make it more manageable. The default value works as well. This actually already worked in the previous version, but I've added the conditions for which the default value should be taken to the equations used in Solve.

share|improve this answer
1  
Hm, this does not consider the "else" case of the Piecewise and it makes the whole thing specific to Piecewise functions. I don't fully understand 1) Why my code does not work, especially numerically and 2) What I did successfully yesterday. Unfortunately I deleted the test code, integrated this into a module and now it stopped working. –  mcandril Jun 5 '12 at 12:16
    
Your skill with Plot is needed here. :-) –  Mr.Wizard Jun 5 '12 at 12:26
add comment

I got an answer from Wolfram Premier support. Their final solution boiled down to basically what Heike wrote. However, they provided an explanation that explains Mike's remark (and renders it really close to the solution) and also helped me to develop a Solution that suits me better (I'm not saying that it is generally better. I'll give the bounty to whoever has the most votes on Thursday).

The reason for the problem is that Solve (and Reduce) use Thread internally. If fun1 is not a list, but e.g. a Piecewise defined function, that fun1 == {t-1,t+2} is evaluated to fun1 == t-1 && fun1 == t+2

Knowing this, it is clear that Mike's example works, as it does not contain lists anymore.

My final solution now is to use

Solve[Evaluate[
  Simplify[
     boxCurve[{{-1, 1}, {-1, 1}}][s].{1, 0}] == {t/2, t}.{1, 0} &&
   Simplify[boxCurve[{{-1, 1}, {-1, 1}}][s].{0, 1}] == {t/2, t}.{0, 1}
  ], {s, t}]

as this still perfectly works for normal, non-Piecewise function.

share|improve this answer
    
Yup, list does not thread within Solve unless its at the top level... Nice solution! –  M.R. Jun 19 '12 at 18:05
add comment

My friend Sam noticed that if you split it up into two functions it works:

Second = Part[#, 2] &;
boxCurve1[boundaries_List] := 
     Module[{b = boundaries, bH, bW, cf}, bH = b[[2, 2]] - b[[2, 1]];
      bW = b[[1, 2]] - b[[1, 1]];
      cf = 2 bH + 2 bW;
      Function[t, Piecewise[{{First@{b[[1, 1]] + cf t, b[[2, 1]]}, 
          0 <= cf t < bW}, {First@{b[[1, 2]], b[[2, 1]] + (cf t - bW)}, 
          bW <= cf t < 
           bW + bH}, {First@{b[[1, 2]] - (cf t - bW - bH), b[[2, 2]]}, 
          bW + bH <= cf t < 
           2 bW + bH}, {First@{b[[1, 1]], b[[2, 2]] - (cf t - 2 bW - bH)},
           2 bW + bH <= cf t < 2 bW + 2 bH}}, 
        First @ {b[[1, 1]], b[[2, 1]]}]]];

boxCurve2[boundaries_List] := 
     Module[{b = boundaries, bH, bW, cf}, bH = b[[2, 2]] - b[[2, 1]];
      bW = b[[1, 2]] - b[[1, 1]];
      cf = 2 bH + 2 bW;
      Function[t, Piecewise[{{Second@{b[[1, 1]] + cf t, b[[2, 1]]}, 
          0 <= cf t < bW}, {Second@{b[[1, 2]], b[[2, 1]] + (cf t - bW)}, 
          bW <= cf t < bW + bH}, 
          {Second@{b[[1, 2]] - (cf t - bW - bH), b[[2, 2]]}, 
          bW + bH <= cf t < 2 bW + bH}, {Second@{b[[1, 1]], 
            b[[2, 2]] - (cf t - 2 bW - bH)}, 
          2 bW + bH <= cf t < 2 bW + 2 bH}}, 
        Second@{b[[1, 1]], b[[2, 1]]}]]];

Solve[boxCurve1[{{-1, 1}, {-1, 1}}][s] == t && 
      boxCurve2[{{-1, 1}, {-1, 1}}][s] == t - 1/2, {s, t}]
boxCurve[{{-1, 1}, {-1, 1}}][1/16]
boxCurve[{{-1, 1}, {-1, 1}}][7/16]
(* 
    {{s -> 1/16, t -> -(1/2)}, {s -> 7/16, t -> 1}}
    {-(1/2), -1}
    {1, 1/2}
*)
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.