Class: Stick::Quaternion

Inherits:
Numeric show all
Defined in:
lib/stick/quaternion.rb

Constant Summary collapse

Zero =
Quaternion(0)
One =
Quaternion(1)
I =
Quaternion(0,1)
J =
Quaternion(0,0,1)
K =
Quaternion(0,0,0,1)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Numeric

#method_missing, #to_value

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Numeric

Instance Attribute Details

#imObject (readonly)

Returns the value of attribute im.



232
233
234
# File 'lib/stick/quaternion.rb', line 232

def im
  @im
end

#jmObject (readonly)

Returns the value of attribute jm.



233
234
235
# File 'lib/stick/quaternion.rb', line 233

def jm
  @jm
end

#kmObject (readonly)

Returns the value of attribute km.



234
235
236
# File 'lib/stick/quaternion.rb', line 234

def km
  @km
end

#reObject (readonly)

Returns the value of attribute re.



231
232
233
# File 'lib/stick/quaternion.rb', line 231

def re
  @re
end

Class Method Details

.generic?(other) ⇒ Boolean

Returns:

  • (Boolean)


241
242
243
# File 'lib/stick/quaternion.rb', line 241

def Quaternion::generic?(other)
  return (other.kind_of?(Complex) or Complex.generic?(other));
end

.polar(m, t1 = 0, t2 = 0, t3 = 0) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/stick/quaternion.rb', line 259

def Quaternion::polar(m,t1=0,t2=0,t3=0)
  # q=
  #   m*cos(t1)
  #   +m*sin(t1)cos(t2)i
  #   +m*sin(t1)sin(t2)cos(t3)j
  #   +m*sin(t1)sin(t2)sin(t3)k
  # m is known as the magnitude,
  # t1 is the amplitude(or angle) of the quaternion,
  # t2 and t3 are the latitude (or co-latitude) and longitude respectively.
  if m.kind_of?(Array) and (m.size==4); t1=m[1]; t2=m[2]; t3=m[3]; m=m[0]; end;
  s=m
  r_part=s*Math.cos(t1); s=s*Math.sin(t1)
  i_part=s*Math.cos(t2); s=s*Math.sin(t2)
  j_part=s*Math.cos(t3); k_part=s*Math.sin(t3)
  new(r_part, i_part, j_part, k_part)
end

.rotation(v, t) ⇒ Object



319
320
321
322
# File 'lib/stick/quaternion.rb', line 319

def Quaternion::rotation(v,t)
  # t-rotatin along the 3-D vector v
  (Quaternion::vector(v).unit_vector) * Math::sin(t/2) + Math::cos(t/2)
end

.vector(v) ⇒ Object



309
310
311
312
# File 'lib/stick/quaternion.rb', line 309

def Quaternion::vector(v)
  # 3-D vector v=[x,y,z]
  Quaternion(0,v[0],v[1],v[2])
end

Instance Method Details

#%(other) ⇒ Object



418
419
420
421
# File 'lib/stick/quaternion.rb', line 418

def % other
  # right mod
  d,m=divmod(other); return m
end

#*(other) ⇒ Object



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/stick/quaternion.rb', line 364

def * (other)
  if other.kind_of?(Quaternion)
    Quaternion(@re*other.re-@im*other.im-@jm*other.jm-@km*other.km,
         @re*other.im+@im*other.re+@jm*other.km-@km*other.jm,
         @re*other.jm-@im*other.km+@jm*other.re+@km*other.im,
         @re*other.km+@im*other.jm-@jm*other.im+@km*other.re)
  elsif other.kind_of?(Complex)
    Quaternion(@re*other.real - @im*other.image,
         @re*other.image + @im*other.real,
         @jm*other.real + @km*other.image,
         @km*other.real - @jm*other.image)
  elsif Complex.generic?(other)
    Quaternion(@re * other, @im * other, @jm * other, @km * other)
  else x , y = other.coerce(self); x * y
  end
end

#**(other) ⇒ Object



469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/stick/quaternion.rb', line 469

def ** other
  # q1^q2 = exp((log q1)*q2)
  if other.kind_of?(Quaternion); ((self.log)*other).exp
  elsif other.kind_of?(Complex); ((self.log)*other).exp
  elsif other.kind_of?(Integer);
    if other==0; return One;
    elsif other>0;
      x = self; q = x; n = other - 1
      while n != 0
        while (d, m = n.divmod(2); m == 0); x = x*x; n = d; end
        q *= x; n -= 1
      end
      return q
    else return self.inverse**(-other)
    end
  elsif Quaternion::generic?(other); ((self.log)*other).exp
  else x, y = other.coerce(self); x ** y
  end;
end

#+(other) ⇒ Object



344
345
346
347
348
349
350
351
352
353
# File 'lib/stick/quaternion.rb', line 344

def + (other)
  if other.kind_of?(Quaternion)
    Quaternion(@re+other.re,@im+other.im,@jm+other.jm,@km+other.km)
  elsif other.kind_of?(Complex)
    Quaternion(@re+other.real,@im+other.image, @jm, @km)
  elsif Complex.generic?(other)
    Quaternion(@re+other.real,@im, @jm, @km)
  else x , y = other.coerce(self); x + y
  end
end

#-(other) ⇒ Object



354
355
356
357
358
359
360
361
362
363
# File 'lib/stick/quaternion.rb', line 354

def - (other)
  if other.kind_of?(Quaternion)
    Quaternion(@re-other.re,@im-other.im,@jm-other.jm,@km-other.km)
  elsif other.kind_of?(Complex)
    Quaternion(@re-other.real,@im-other.image, @jm, @km)
  elsif Complex.generic?(other)
    Quaternion(@re-other.real,@im, @jm, @km)
  else x , y = other.coerce(self); x - y
  end
end

#/(other) ⇒ Object



386
387
388
389
390
391
392
393
# File 'lib/stick/quaternion.rb', line 386

def / other
  if other.kind_of?(Quaternion); self*other.conjugate/other.abs2
  elsif other.kind_of?(Complex); self*other.conjugate/other.abs2
  elsif Complex.generic?(other);
    Quaternion(@re/other, @im/other, @jm/other, @km/other )
  else x, y = other.coerce(self); x / y
  end
end

#<=>(other) ⇒ Object



333
# File 'lib/stick/quaternion.rb', line 333

def <=> (other); self.abs <=> other.abs; end

#==(other) ⇒ Object



334
335
336
337
338
339
340
341
342
343
# File 'lib/stick/quaternion.rb', line 334

def == (other)
  if other.kind_of?(Quaternion)
    return (@re==other.re and @im==other.im and @jm==other.jm and @km==other.km)
  elsif other.kind_of?(Complex)
    @re==other.real and @im==other.image and @jm==0 and @km==0
  elsif Complex.generic?(other)
    @re==other and @im==0 and @jm==0 and @km==0
  else x , y = other.coerce(self); x == y
  end
end

#absObject



297
# File 'lib/stick/quaternion.rb', line 297

def abs; Math.sqrt((@re*@re+@im*@im+@jm*@jm+@km*@km).to_f); end

#abs2Object



296
# File 'lib/stick/quaternion.rb', line 296

def abs2; return @re*@re+@im*@im+@jm*@jm+@km*@km; end

#acosObject



516
517
518
519
# File 'lib/stick/quaternion.rb', line 516

def acos
  # acos q = -u log(q+sqrt(q^2-1))
  q=self; u=unit_vector; -u*((q+(q*q-1).sqrt).log)
end

#amplitudeObject



275
# File 'lib/stick/quaternion.rb', line 275

def amplitude; Math.atan2(Math.sqrt((@im*@im+@jm*@jm+@km*@km).to_f),@re.to_f); end

#arg1Object



278
# File 'lib/stick/quaternion.rb', line 278

def arg1; return amplitude; end

#arg2Object



279
# File 'lib/stick/quaternion.rb', line 279

def arg2; return latitude; end

#arg3Object



280
# File 'lib/stick/quaternion.rb', line 280

def arg3; return longitude; end

#asinObject

Inverse trigonometric functions



512
513
514
515
# File 'lib/stick/quaternion.rb', line 512

def asin
  # asin q = -u log(uq+sqrt(1-q^2))
  q=self; u=unit_vector; -u*((u*q+(1-q*q).sqrt).log)
end

#atanObject



520
521
522
523
# File 'lib/stick/quaternion.rb', line 520

def atan
  # atan q = u/2 log( (u+q)/(u-q)  )
  q=self; u=q.unit_vector; u*((u+q)/(u-q)).log/2
end

#coerce(other) ⇒ Object

Arithmetic



327
328
329
330
331
332
# File 'lib/stick/quaternion.rb', line 327

def coerce(other)
  if other.kind_of?(Complex); return Quaternion(other), self
  elsif Complex::generic?(other); return Quaternion(other), self
  else super
  end
end

#conjugateObject



299
# File 'lib/stick/quaternion.rb', line 299

def conjugate; Quaternion(@re,-@im,-@jm,-@km); end

#cosObject



499
500
501
502
503
504
# File 'lib/stick/quaternion.rb', line 499

def cos
  # cos(r+uv)=cos r cosh v - u sin r sinh v
  vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::cos(@re)); end
  u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
  (Math::cos(@re)*c-u*Math::sin(@re)*s)/2
end

#coshObject



490
# File 'lib/stick/quaternion.rb', line 490

def cosh; e=exp; return (e+e.inverse)/2; end

#cross_product(other) ⇒ Object



383
384
385
# File 'lib/stick/quaternion.rb', line 383

def cross_product other
  -(self*other.conjugate).vector
end

#divmod(other) ⇒ Object



402
403
404
405
# File 'lib/stick/quaternion.rb', line 402

def divmod other
  # right divmod: q1=d*q2+m
  d=self.rdiv(other).round; m=self-d*other; return d,m
end

#divmod_D4(other) ⇒ Object



406
407
408
409
# File 'lib/stick/quaternion.rb', line 406

def divmod_D4 other
  # right divmod: q1=d*q2+m, d be D4
  d=self.rdiv(other).round_D4; m=self-d*other; return d,m
end

#dot_product(other) ⇒ Object



380
381
382
# File 'lib/stick/quaternion.rb', line 380

def dot_product other
  (self*other.conjugate).re
end

#expObject

Exponential and logarithmic functions



453
454
455
456
457
458
# File 'lib/stick/quaternion.rb', line 453

def exp
  # e^(r+uv)=exp(r)(cos(v)+u*sin(v))
  if is_real?; return Quaternion(Math::exp(@re)); end
  vec=self.vector; v=vec.abs; u = vec/v;
  Math::exp(@re)*(Math::cos(v)+u*Math::sin(v))
end

#gcd(other) ⇒ Object



438
439
440
441
442
443
444
445
446
# File 'lib/stick/quaternion.rb', line 438

def gcd other
  a=self; b=other
  while true
    if b==0 ; return a;end
    a=a.rmod_D4(b)
    if a==0 ; return b;end
    b=a.lmod_D4(b)
  end
end

#hashObject



526
# File 'lib/stick/quaternion.rb', line 526

def hash; @re^@im^@jm^@km; end

#imageObject



235
# File 'lib/stick/quaternion.rb', line 235

def image; return @im; end

#inspectObject



528
529
530
531
# File 'lib/stick/quaternion.rb', line 528

def inspect
  sprintf("Quaternion(%s,%s,%s,%s)",
      @re.inspect, @im.inspect, @jm.inspect, @km.inspect)
end

#inverseObject



300
# File 'lib/stick/quaternion.rb', line 300

def inverse; conjugate/abs2; end

#is_complex?Boolean

Returns:

  • (Boolean)


303
# File 'lib/stick/quaternion.rb', line 303

def is_complex?; @jm==0 and @km==0; end

#is_quaternion?Boolean

Returns:

  • (Boolean)


304
# File 'lib/stick/quaternion.rb', line 304

def is_quaternion?; not(is_complex?); end

#is_real?Boolean

Returns:

  • (Boolean)


302
# File 'lib/stick/quaternion.rb', line 302

def is_real?; @im==0 and @jm==0 and @km==0; end

#is_unit_vector?Boolean

Returns:

  • (Boolean)


318
# File 'lib/stick/quaternion.rb', line 318

def is_unit_vector?; @re==0 and abs2==1; end

#is_vector?Boolean

Returns:

  • (Boolean)


307
# File 'lib/stick/quaternion.rb', line 307

def is_vector?; @re==0; end

#latitudeObject



276
# File 'lib/stick/quaternion.rb', line 276

def latitude; Math.atan2(Math.sqrt((@jm*@jm+@km*@km).to_f),@im.to_f); end

#ldiv(other) ⇒ Object



398
399
400
401
# File 'lib/stick/quaternion.rb', line 398

def ldiv other
  # left division: 1/q1 * q2
  (self.conjugate)*other/self.abs2
end

#ldivmod(other) ⇒ Object



410
411
412
413
# File 'lib/stick/quaternion.rb', line 410

def ldivmod other
  # left divmod: q2=q1*d+m
  d=self.ldiv(other).round; m=other-self*d; return d,m
end

#ldivmod_D4(other) ⇒ Object



414
415
416
417
# File 'lib/stick/quaternion.rb', line 414

def ldivmod_D4 other
  # left divmod: q2=q1*d+m, d be D4
  d=self.ldiv(other).round_D4; m=other-self*d; return d,m
end

#lmod(other) ⇒ Object



430
431
432
433
# File 'lib/stick/quaternion.rb', line 430

def lmod other
  # left mod
  d,m=ldivmod(other); return m
end

#lmod_D4(other) ⇒ Object



434
435
436
437
# File 'lib/stick/quaternion.rb', line 434

def lmod_D4 other
  # left mod with D4
  d,m=ldivmod_D4(other); return m
end

#logObject



459
460
461
462
463
464
465
466
467
468
# File 'lib/stick/quaternion.rb', line 459

def log
  # log(r+uv)=1/2 log(r^2+v^2)+u atan(v/r)
  if is_real?;
    if @re>=0; return Quaternion(Math::log(@re));
    else return Quaternion(Math::log(-@re),Math::PI,0,0);
    end
  end
  vec=self.vector; v=vec.abs; u = vec/v;
  Math::log(self.abs2.to_f)/2+u*Math::atan2( v, @re)
end

#longitudeObject



277
# File 'lib/stick/quaternion.rb', line 277

def longitude; Math.atan2( @km.to_f, @jm.to_f); end

#magnitudeObject



298
# File 'lib/stick/quaternion.rb', line 298

def magnitude; return abs; end

#orthogonal_split(o) ⇒ Object



447
448
449
450
# File 'lib/stick/quaternion.rb', line 447

def orthogonal_split(o)
  # [q1,q2]. q = q1 + q2 such that q1 parallel to o, and q2 orthogonal to o.
  q1 = o * dot_product(o); q2=self-q1; return q1,q2
end

#polarObject



281
# File 'lib/stick/quaternion.rb', line 281

def polar; [magnitude, amplitude, latitude, longitude]; end

#rdiv(other) ⇒ Object



394
395
396
397
# File 'lib/stick/quaternion.rb', line 394

def rdiv other
  # right division: q1/q2
  self/other
end

#realObject



237
# File 'lib/stick/quaternion.rb', line 237

def real; return @re; end

#real_partObject



236
# File 'lib/stick/quaternion.rb', line 236

def real_part; return @re; end

#rmod(other) ⇒ Object



422
423
424
425
# File 'lib/stick/quaternion.rb', line 422

def rmod other
  # right mod(same as %)
  d,m=divmod(other); return m
end

#rmod_D4(other) ⇒ Object



426
427
428
429
# File 'lib/stick/quaternion.rb', line 426

def rmod_D4 other
  # right mod with D4
  d,m=divmod_D4(other); return m
end

#rotate(r) ⇒ Object



323
# File 'lib/stick/quaternion.rb', line 323

def rotate(r); r * self * r.conjugate / r.abs2; end

#rotate_angleObject



324
# File 'lib/stick/quaternion.rb', line 324

def rotate_angle; amplitude/2; end

#roundObject



283
# File 'lib/stick/quaternion.rb', line 283

def round; Quaternion(@re.round,@im.round,@jm.round,@km.round);end

#round_D4Object



284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/stick/quaternion.rb', line 284

def round_D4
  # round to D4 lattice
  r1=@re.round; a1=@im.round; b1=@jm.round; c1=@km.round;
  q1=Quaternion(r1,a1,b1,c1); d1=(q1-self).abs2
  if d1<=1/4; return q1; end
  if @re<r1; r2=r1-1/2; else r2=r1+1/2; end
  if @im<r1; a2=a1-1/2; else a2=a1+1/2; end
  if @jm<r1; b2=b1-1/2; else b2=b1+1/2; end
  if @km<r1; c2=c1-1/2; else c2=c1+1/2; end
  q2=Quaternion(r2,a2,b2,c2); d2=(q2-self).abs2
  if d1<=d2; return q1; else return q2; end
end

#sinObject

Trigonometric functions



493
494
495
496
497
498
# File 'lib/stick/quaternion.rb', line 493

def sin
  # sin(r+uv)=sin r cosh v + u cos r sinh v
  vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::sin(@re)); end
  u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
  (Math::sin(@re)*c+u*Math::cos(@re)*s)/2
end

#sinhObject



489
# File 'lib/stick/quaternion.rb', line 489

def sinh; e=exp; return (e-e.inverse)/2; end

#sqrtObject



488
# File 'lib/stick/quaternion.rb', line 488

def sqrt; self**(0.5); end

#tanObject



505
506
507
508
509
# File 'lib/stick/quaternion.rb', line 505

def tan
  vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::tan(@re)); end
  u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
  co=Math::cos(@re); si=Math::sin(@re);  (si*c+u*co*s)/(co*c-u*si*s)
end

#tanhObject



491
# File 'lib/stick/quaternion.rb', line 491

def tanh; e=exp; e=e*e; return (e-1)/(e+1); end

#to_aObject



240
# File 'lib/stick/quaternion.rb', line 240

def to_a; return [@re, @im, @jm, @km]; end

#to_cObject



238
# File 'lib/stick/quaternion.rb', line 238

def to_c; return Complex(@re,@im); end

#to_c2Object



239
# File 'lib/stick/quaternion.rb', line 239

def to_c2; return Complex(@jm,@km); end

#to_sObject



532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
# File 'lib/stick/quaternion.rb', line 532

def to_s
  s=""
  if @re!=0; s=@re.to_s; end
  if @im!=0;
    if s==""; s=sprintf("%si", @im);
    else if @im>0; s=sprintf("%s+%si",s,@im); else s=sprintf("%s-%si",s,-@im); end
    end
  end
  if @jm!=0;
    if s==""; s=sprintf("%sj", @jm);
    else if @jm>0; s=sprintf("%s+%sj",s,@jm); else s=sprintf("%s-%sj",s,-@jm); end
    end
  end
  if @km!=0;
    if s==""; s=sprintf("%sk", @km);
    else if @km>0; s=sprintf("%s+%sk",s,@km); else s=sprintf("%s-%sk",s,-@km); end
    end
  end
  if s=="" ; s="0"; end;
  return s
end

#to_vObject



308
# File 'lib/stick/quaternion.rb', line 308

def to_v; return [@im, @jm, @km]; end

#unit_vectorObject



313
314
315
316
317
# File 'lib/stick/quaternion.rb', line 313

def unit_vector
  if is_real?; return Quaternion(0,1); end
  m=Math::sqrt((@im*@im+@jm*@jm+@km*@km).to_f)
  Quaternion(0,@im/m,@jm/m,@km/m);
end

#vectorObject



306
# File 'lib/stick/quaternion.rb', line 306

def vector; Quaternion(0,@im,@jm,@km); end