Class: 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.



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

def im
  @im
end

#jmObject (readonly)

Returns the value of attribute jm.



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

def jm
  @jm
end

#kmObject (readonly)

Returns the value of attribute km.



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

def km
  @km
end

#reObject (readonly)

Returns the value of attribute re.



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

def re
  @re
end

Class Method Details

.generic?(other) ⇒ Boolean

Returns:

  • (Boolean)


249
250
251
# File 'lib/stick/quaternion.rb', line 249

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

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



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/stick/quaternion.rb', line 267

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



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

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



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

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



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

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

#*(other) ⇒ Object



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/stick/quaternion.rb', line 372

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



477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/stick/quaternion.rb', line 477

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



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

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



362
363
364
365
366
367
368
369
370
371
# File 'lib/stick/quaternion.rb', line 362

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



394
395
396
397
398
399
400
401
# File 'lib/stick/quaternion.rb', line 394

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



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

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

#==(other) ⇒ Object



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

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



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

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

#abs2Object



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

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

#acosObject



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

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



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

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

#arg1Object



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

def arg1; return amplitude; end

#arg2Object



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

def arg2; return latitude; end

#arg3Object



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

def arg3; return longitude; end

#asinObject

Inverse trigonometric functions



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

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



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

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



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

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



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

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

#cosObject



507
508
509
510
511
512
# File 'lib/stick/quaternion.rb', line 507

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



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

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

#cross_product(other) ⇒ Object



391
392
393
# File 'lib/stick/quaternion.rb', line 391

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

#divmod(other) ⇒ Object



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

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



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

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



388
389
390
# File 'lib/stick/quaternion.rb', line 388

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

#expObject

Exponential and logarithmic functions



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

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



446
447
448
449
450
451
452
453
454
# File 'lib/stick/quaternion.rb', line 446

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



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

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

#imageObject



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

def image; return @im; end

#inspectObject



536
537
538
539
# File 'lib/stick/quaternion.rb', line 536

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

#inverseObject



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

def inverse; conjugate/abs2; end

#is_complex?Boolean

Returns:

  • (Boolean)


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

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

#is_quaternion?Boolean

Returns:

  • (Boolean)


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

def is_quaternion?; not(is_complex?); end

#is_real?Boolean

Returns:

  • (Boolean)


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

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

#is_unit_vector?Boolean

Returns:

  • (Boolean)


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

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

#is_vector?Boolean

Returns:

  • (Boolean)


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

def is_vector?; @re==0; end

#latitudeObject



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

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

#ldiv(other) ⇒ Object



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

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

#ldivmod(other) ⇒ Object



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

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



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

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



438
439
440
441
# File 'lib/stick/quaternion.rb', line 438

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

#lmod_D4(other) ⇒ Object



442
443
444
445
# File 'lib/stick/quaternion.rb', line 442

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

#logObject



467
468
469
470
471
472
473
474
475
476
# File 'lib/stick/quaternion.rb', line 467

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



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

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

#magnitudeObject



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

def magnitude; return abs; end

#orthogonal_split(o) ⇒ Object



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

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



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

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

#rdiv(other) ⇒ Object



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

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

#realObject



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

def real; return @re; end

#real_partObject



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

def real_part; return @re; end

#rmod(other) ⇒ Object



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

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

#rmod_D4(other) ⇒ Object



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

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

#rotate(r) ⇒ Object



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

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

#rotate_angleObject



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

def rotate_angle; amplitude/2; end

#roundObject



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

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

#round_D4Object



292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/stick/quaternion.rb', line 292

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



501
502
503
504
505
506
# File 'lib/stick/quaternion.rb', line 501

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



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

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

#sqrtObject



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

def sqrt; self**(0.5); end

#tanObject



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

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



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

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

#to_aObject



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

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

#to_cObject



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

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

#to_c2Object



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

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

#to_sObject



540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
# File 'lib/stick/quaternion.rb', line 540

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



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

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

#unit_vectorObject



321
322
323
324
325
# File 'lib/stick/quaternion.rb', line 321

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



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

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