Segment2
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RicardoSantos

//@version=5

// @description Structure representation of a directed straight line in two dimensions from origin to target vectors.
// .
// reference:
//		https://graphics.stanford.edu/courses/cs368/CGAL/ref-manual1/CGAL_Segment_2.html
// .
library(title='Segment2')



//#region ~~~ Imports:

import RicardoSantos/CommonTypesMath/1 as TMath
import RicardoSantos/Vector2/1 as Vector2

//#endregion



//#region 	-> Constructor:

// new () {

// @function Generate a new segment.
// @param origin Vector2 . Origin of the segment.
// @param target Vector2 . Target of the segment.
// @returns Segment2.
export new (TMath.Vector2 origin, TMath.Vector2 target) =>
	TMath.Segment2.new(origin, target)

// @function Generate a new segment.
// @param origin_x float . Origin of the segment x coordinate.
// @param origin_y float . Origin of the segment y coordinate.
// @param target_x float . Target of the segment x coordinate.
// @param target_y float . Target of the segment y coordinate.
// @returns Segment2.
export new (float origin_x, float origin_y, float target_x, float target_y) =>
	TMath.Segment2.new(TMath.Vector2.new(origin_x, origin_y), TMath.Vector2.new(target_x, target_y))

// }
// copy () {

// @function Copy a segment.
// @param this Vector2 . Segment to copy.
// @returns Segment2.
export method copy (TMath.Segment2 this) =>
	TMath.Segment2.copy(this)

// }

//#endregion



//#region	-> Methods:

//#region		-> Properties:

// length_squared () {

// @function Squared length of the normalized segment vector. For comparing vectors this is computationaly lighter.
// @param this Segment2 . Sorce segment.
// @returns float.
export method length_squared (TMath.Segment2 this) =>
	Vector2.length_squared(this.target.subtract(this.origin))

// }
// length () {

// @function Length of the normalized segment vector.
// @param this Segment2 . Sorce segment.
// @returns float.
export method length (TMath.Segment2 this) =>
	Vector2.length(this.target.subtract(this.origin))

// }
// opposite () {

// @function Reverse the direction of the segment. 
// @param this Segment2 . Source segment.
// @returns Segment2.
export method opposite (TMath.Segment2 this) =>
	new(this.target, this.origin)

// }
// is_degenerate () {

// @function Segment is degenerate when origin and target are equal. 
// @param this Segment2 . Source segment.
// @returns bool.
export method is_degenerate (TMath.Segment2 this) =>
	this.origin.x == this.target.x and this.origin.y == this.target.y

// }
// is_horizontal () {

// @function Segment is horizontal?. 
// @param this Segment2 . Source segment.
// @returns bool.
export method is_horizontal (TMath.Segment2 this) =>
	this.origin.y == this.target.y

// @function Segment is horizontal?. 
// @param this      Segment2 . Source segment.
// @param precision float    . Limit of precision.
// @returns bool.
export method is_horizontal (TMath.Segment2 this, float precision) =>
	math.abs(this.origin.y - this.target.y) <= precision

// }
// is_vertical () {

// @function Segment is vertical?. 
// @param this Segment2 . Source segment.
// @returns bool.
export method is_vertical (TMath.Segment2 this) =>
	this.origin.x == this.target.x

// @function Segment is vertical?. 
// @param this      Segment2 . Source segment.
// @param precision float    . Limit of precision.
// @returns bool.
export method is_vertical (TMath.Segment2 this, float precision) =>
	math.abs(this.origin.x - this.target.x) <= precision

// }

//#endregion

//#region		-> Functions:

// equals () {

// @function Tests two segments for equality (share same origin and target).
// @param this  Segment2 . Source segment.
// @param other Segment2 . Target segment.
// @returns bool.
export method equals (TMath.Segment2 this, TMath.Segment2 other) =>
	this.origin.equals(other.origin) and this.target.equals(other.target)

// }
// nearest_to_point () {
// reference:
//		https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/math/Intersector.java

// @function Find the nearest point in a segment to another point.
// @param this Segment2 . Source segment.
// @param point Vector2 . Point to aproximate.
// @returns Vector2.
export method nearest_to_point (TMath.Segment2 this, TMath.Vector2 point) =>
	float _dx = this.target.x - this.origin.x
	float _dy = this.target.y - this.origin.y
	float _length = _dx * _dx + _dy * _dy
	if _length == 0.0
		this.origin
	else
		float _t = ((point.x - this.origin.x) * _dx + (point.y - this.origin.y) * _dy) / _length
		switch
			_t <= 0.0 => this.origin
			_t >= 1.0 => this.target
			=> Vector2.lerp(this.origin, this.target, _t)

// TEST: OK 20230216 RS
// position = input.int(10)
// p = Vector2.new(float(bar_index+position), 10.0)
// L1 = new(bar_index+10.0, 10.0, bar_index+100.0, 20.0)
// if barstate.islast
// 	_p1 = L1.nearest_to_point(p)
// 	line.new(int(L1.origin.x), L1.origin.y, int(L1.target.x), L1.target.y, xloc.bar_index, extend.none, #e3e3e3, line.style_arrow_right)
// 	label.new(int(p.x), p.y, 'P', color=#e3e3e3, style=label.style_label_center)
// 	label.new(int(_p1.x), _p1.y, 'X', color=#e3e3e3, style=label.style_label_center)


// }
// intersection () {

// @function Find the intersection vector of 2 lines.
// @param this  Segment2 . Segment A.
// @param other Segment2 . Segment B.
// @returns Vector2.Vector2 Object.
export method intersection (TMath.Segment2 this, TMath.Segment2 other) =>
    float _a1 = this.target.y - this.origin.y
    float _b1 = this.origin.x - this.target.x
    float _c1 = _a1 * this.origin.x + _b1 * this.origin.y
    float _a2 = other.target.y - other.origin.y
    float _b2 = other.origin.x - other.target.x
    float _c2 = _a2 * other.origin.x + _b2 * other.origin.y
    float _delta = _a1 * _b2 - _a2 * _b1
    Vector2.new((_b2 * _c1 - _b1 * _c2) / _delta, (_a1 * _c2 - _a2 * _c1) / _delta)

// TEST: OK 20230216 RS
// L1 = new(bar_index+10.0, 10.0, bar_index+20.0, 20.0)
// L2 = new(bar_index+10.0, 40.0, bar_index+20.0, 30.0)
// if barstate.islast
// 	_intersect = L1.intersection(L2)
// 	line.new(int(L1.origin.x), L1.origin.y, int(L1.target.x), L1.target.y, xloc.bar_index, extend.none, #e3e3e3, line.style_arrow_right)
// 	line.new(int(L2.origin.x), L2.origin.y, int(L2.target.x), L2.target.y, xloc.bar_index, extend.none, #e3e3e3, line.style_arrow_right)
// 	label.new(int(_intersect.x), _intersect.y, 'X', color=#e3e3e3, style=label.style_label_center)

// }
// extend () {

// @function Extend a segment by the percent ratio provided.
// @param this      Segment2 . Source segment.
// @param at_origin float    . Percent ratio to extend at origin vector.
// @param at_target float    . Percent ratio to extend at target vector.
// @returns Segment2.
export method extend (TMath.Segment2 this, float at_origin=0.0, float at_target=0.0) =>
	float _dx = this.target.x - this.origin.x
	float _dy = this.target.y - this.origin.y
	new(
	 this.origin.x - _dx * at_origin ,
	 this.origin.y - _dy * at_origin ,
	 this.target.x + _dx * at_target ,
	 this.target.y + _dy * at_target )

// TEST: 20230217 RS
// ratio_at_origin = input.float(0.0)
// ratio_at_target = input.float(0.0)
// if barstate.islast
// 	_s = new(bar_index+5.0, 10.0, bar_index+20.0, 30.0).extend(ratio_at_origin, ratio_at_target)
// 	line.new(int(_s.origin.x), _s.origin.y, int(_s.target.x), _s.target.y)

// }

//#endregion

//#region		-> Translation:

// to_string () {

// @function Translate segment to string format `( (x,y), (x,y) )`.
// @param this Segment2 . Source segment.
// @returns string.
export method to_string (TMath.Segment2 this) =>
	str.format('( {0} , {1})', this.origin.to_string(), this.target.to_string())

// @function Translate segment to string format `((x,y), (x,y))`.
// @param this Segment2 . Source segment.
// @param format string . Format string to apply.
// @returns string.
export method to_string (TMath.Segment2 this, string format) =>
	str.format('( {0}, {1} )', this.origin.to_string(format), this.target.to_string(format))

// }
// to_array () {

// @function Translate segment to array format.
// @param this Segment2 . Source segment.
// @returns array<float>.
export method to_array (TMath.Segment2 this) =>
	array.from(float(this.origin.x), float(this.origin.y), float(this.target.x), float(this.target.y))

// }

//#endregion

//#endregion




