Example #1
0
/*
 * We hit the end of a map (rect or a new output)
 *
 */
static int outputs_end_map_cb(void *params_) {
    struct outputs_json_params *params = (struct outputs_json_params *)params_;
    if (params->in_rect) {
        params->in_rect = false;
        /* Ignore the end of a rect */
        return 1;
    }

    /* See if we actually handle that output */
    if (config.num_outputs > 0) {
        bool handle_output = false;
        for (int c = 0; c < config.num_outputs; c++) {
            if (strcasecmp(params->outputs_walk->name, config.outputs[c]) == 0 ||
                (strcasecmp(config.outputs[c], "primary") == 0 &&
                 params->outputs_walk->primary)) {
                handle_output = true;
                break;
            }
        }
        if (!handle_output) {
            DLOG("Ignoring output \"%s\", not configured to handle it.\n",
                 params->outputs_walk->name);
            clear_output(params->outputs_walk);
            FREE(params->outputs_walk);
            FREE(params->cur_key);
            return 1;
        }
    }

    i3_output *target = get_output_by_name(params->outputs_walk->name);

    if (target == NULL) {
        SLIST_INSERT_HEAD(outputs, params->outputs_walk, slist);
    } else {
        target->active = params->outputs_walk->active;
        target->primary = params->outputs_walk->primary;
        target->ws = params->outputs_walk->ws;
        target->rect = params->outputs_walk->rect;

        clear_output(params->outputs_walk);
        FREE(params->outputs_walk);
    }
    return 1;
}
Example #2
0
int main() {
	//set pin to output
	p.p_addr = GPIO_BASE_ADDR;
	map_gpio(&p);
	set_as_input(&p, 4);
	//set_as_output(&p, 4);
	set_output(&p, 4, 0);
	clear_output(&p, 4);
	read_pin(&p, 4);	
	return 0;
}
Example #3
0
/*
 * free() all outputs data structures.
 *
 */
void free_outputs(void) {
    free_workspaces();

    i3_output *outputs_walk;
    if (outputs == NULL) {
        return;
    }
    SLIST_FOREACH(outputs_walk, outputs, slist) {
        destroy_window(outputs_walk);
        if (outputs_walk->trayclients != NULL && !TAILQ_EMPTY(outputs_walk->trayclients)) {
            FREE_TAILQ(outputs_walk->trayclients, trayclient);
        }
        clear_output(outputs_walk);
    }
Example #4
0
/*Begins threading the program to search.  After creating
 * threads, waits for all threads to finish.*/
void begin_threading()
{
	pthread_t threads[NT];
	clear_output();
	int i = 0, rc;
	for (i = 0; i < NT; i++)
	{
		if (rc = pthread_create(&threads[i], NULL, &search_slice, (void *)(intptr_t)i))
		{
			fprintf(stderr, "return from pthread_create() is %d\n", rc);
			exit(-1);
		}
	}
	for (i = 0; i < NT; i++)
	{
		pthread_join(threads[i], NULL);
	}
}
Example #5
0
void PICLANG_next()
{
  picos_size_t command;
  picos_size_t a,b,c;// parameters, can be shared
  picos_signed_t siga;// a, but signed.
  signed char sch1;// signed char parameters
  char ch1;// char parameter
  static bit flag1;

  extern char ARG_buffer[ARG_SIZE];

  if(curr_process.page_size == 0)
    return;
  
  if(picos_processes[picos_curr_process].expires == 0)
    {
      if((curr_process.bitmap & PICLANG_BLOCKING_CALL) == 0 || PICLANG_debug == true)// Check for blocking call
	{
	  PICLANG_save(PICLANG_SUSPENDED);
	  return;
	}
    }
  
  if(picos_processes[picos_curr_process].signal_sent != PICOS_NUM_SIGNALS)
    {
      if(signal_valid_id(picos_processes[picos_curr_process].signal_sent) == false)
	{
	  PICLANG_exception(THREAD_INVALID_SIGNAL);
	  return;
	}
      curr_process.pc = signals[picos_processes[picos_curr_process].signal_sent].sig_action_addr;
      signal_free(picos_processes[picos_curr_process].signal_sent);
      picos_processes[picos_curr_process].signal_sent = PICOS_NUM_SIGNALS;
    }

  command = PICLANG_get_next_word();
  if(curr_process.status != PICLANG_SUCCESS)
    return;

  if(PICLANG_debug == true)
    PICLANG_debug_out(command);


  switch(command)
    {
    case PICLANG_UMINUS:
      {
	picos_signed_t inv = (picos_signed_t)PICLANG_pop();
	PICLANG_pushl(-inv);
	break;
      }
    case PICLANG_ADD:
      PICLANG_pushl(PICLANG_pop() + PICLANG_pop());
      PICLANG_update_arith_status();
      break;
    case PICLANG_MOD:case PICLANG_DIV:
      {
	// a/b --> c rem a
	b = PICLANG_pop();
	a = PICLANG_pop();
	c = 0;
	while(a >= b)
	  {
	    a -= b;
	    c++;
	  }
	if(command == PICLANG_MOD)
	  PICLANG_pushl(a);
	else
	  PICLANG_pushl(c);
	PICLANG_update_arith_status();
	break;
      }
    case PICLANG_SUB:
      {
	b = PICLANG_pop();
	a = PICLANG_pop();
	PICLANG_pushl(a - b);
	PICLANG_update_arith_status();
	break;
      }
    case PICLANG_MULT:
      a = PICLANG_pop();
      b = PICLANG_pop();
      c = a*b;
      PICLANG_pushl(c);
      PICLANG_update_arith_status();
      break;
    case PICLANG_PUSHL:
      a = PICLANG_get_next_word();
      PICLANG_pushl(a);
      break;
    case PICLANG_PUSH:
      {
	a = PICLANG_get_next_word();
	b = PAGE_get(a,picos_curr_process);
	if(error_code != SUCCESS)
	  PICLANG_exception(error_code);
	else
	  PICLANG_pushl(b);
	break;
      }
    case PICLANG_DROP:
      PICLANG_pop();
      break;
    case PICLANG_SWAP:
      a = PICLANG_pop();
      b = PICLANG_pop();
      PICLANG_pushl(a);
      PICLANG_pushl(b);
      break;
    case PICLANG_POP:
      {
	a = PICLANG_get_next_word();
	b = PICLANG_pop();
	PAGE_set(a,b,picos_curr_process);
	break;
      }
    case PICLANG_ARGC:
      PICLANG_pushl(picos_processes[picos_curr_process].nargs);
      break;
    case PICLANG_ARGV:
      {
	a = PICLANG_pop();
	sch1 = ARG_get(a);
	if(sch1 < 0)
	  {
	    PICLANG_exception(PICLANG_INVALID_PARAMETER);
	    break;
	  }
	b = (picos_size_t)sch1;
	PICLANG_pushl(b);
	break;
      }
    case PICLANG_PRINT:
      {
	a = PICLANG_pop();
	putch(a);
	IO_flush();
	break;
      }
    case PICLANG_DEREF:
      {
	a = PICLANG_pop();// array index
	b = PICLANG_pop();// array starting address
	PICLANG_next_dereference(b+a,command);
	break;
      }
    case PICLANG_BSL: case PICLANG_BSR:// bit shifts
      {
	a /*shift_amount*/ = PICLANG_pop();
	b /*val*/ = PICLANG_pop();
	if(command == PICLANG_BSL)
	  b <<= a;
	else
	  b >>= a;
	PICLANG_pushl(b);
	PICLANG_update_arith_status();
	break;
      }
    case PICLANG_FPUTD:
      {
	char hex_val[PICOS_SIZE_T_DECIMAL_DIGITS];//ch1 = index
	dec_to_word(hex_val,PICLANG_pop());
	ch1 = 0;
	flag1 = false;
	for(;ch1 < 5;ch1++)
	  {
	    if(flag1 == true || hex_val[ch1] != 0x30)
	      {
		SRAM_write(SRAM_PICLANG_RAW_FILE_BUFFER+PICLANG_file_buffer_index++,&hex_val[ch1],sizeof(char));
		flag1 = true;
	      }
	    if(PICLANG_file_buffer_index >= FS_BUFFER_SIZE)
	      {
		picfs_dump(picos_processes[picos_curr_process].program_file);
		SRAM_write(SRAM_PICLANG_NEXT_SWAP_ADDR,(void*)picfs_buffer,FS_BUFFER_SIZE);
		memset((char*)picfs_buffer,0,FS_BUFFER_SIZE);
		SRAM_write(SRAM_PICLANG_RAW_FILE_BUFFER,(void*)picfs_buffer,FS_BUFFER_SIZE);
		SRAM_read(SRAM_PICLANG_NEXT_SWAP_ADDR,(void*)picfs_buffer,FS_BUFFER_SIZE);
		PICLANG_file_buffer_index = 0;
	      }
	  }
	if(flag1 == false)
	  {
	    ch1 = '0';
	    SRAM_write(SRAM_PICLANG_RAW_FILE_BUFFER+PICLANG_file_buffer_index++,&ch1,sizeof(char));
	    if(PICLANG_file_buffer_index >= FS_BUFFER_SIZE)
	      {
		picfs_dump(picos_processes[picos_curr_process].program_file);
		SRAM_write(SRAM_PICLANG_NEXT_SWAP_ADDR,(void*)picfs_buffer,FS_BUFFER_SIZE);
		memset((char*)picfs_buffer,0,FS_BUFFER_SIZE);
		SRAM_write(SRAM_PICLANG_RAW_FILE_BUFFER,(void*)picfs_buffer,FS_BUFFER_SIZE);
		SRAM_read(SRAM_PICLANG_NEXT_SWAP_ADDR,(void*)picfs_buffer,FS_BUFFER_SIZE);
		PICLANG_file_buffer_index = 0;
	      }
	  }
	break;
      }
    case PICLANG_PWDIR:
      PICLANG_pushl(curr_dir);
      break;
    case PICLANG_MOUNT:
      a = PICLANG_pop();// device
      b = PICLANG_pop();// address of first byte
      if(picfs_mount((picos_addr_t)b,(picos_dev_t)a) != SUCCESS)
	curr_process.status = error_code;
      break;
    case PICLANG_CHDIR:
      {
	a = PICLANG_pop();
	picfs_chdir(a);
	break;
      }
    case PICLANG_MOVE:
      a = PICLANG_pop();// y
      b = PICLANG_pop();// x
      IO_move(a,b);
      break;
    case PICLANG_GETY: case PICLANG_GETX:
      IO_getxy(&a,&b);
      if(command == PICLANG_GETX)
	PICLANG_pushl(a);
      else
	PICLANG_pushl(b);
      break;
    case PICLANG_FPUTCH:// KEEP FPUTCH before FFLUSH
      {
	ch1 = (char)PICLANG_pop();
	SRAM_write(SRAM_PICLANG_RAW_FILE_BUFFER+PICLANG_file_buffer_index++,&ch1,sizeof(char));
	if(PICLANG_file_buffer_index < FS_BUFFER_SIZE)
	  break;
      }
    case PICLANG_FFLUSH:// KEEP FPUTCH before FFLUSH  KEEP FFLUSH before FCLEAR
      picfs_dump(picos_processes[picos_curr_process].program_file);
    case PICLANG_FCLEAR:// KEEP FFLUSH before FCLEAR
      SRAM_write(SRAM_PICLANG_NEXT_SWAP_ADDR,(void*)picfs_buffer,FS_BUFFER_SIZE);
      memset((char*)picfs_buffer,0,FS_BUFFER_SIZE);
      SRAM_write(SRAM_PICLANG_RAW_FILE_BUFFER,(void*)picfs_buffer,FS_BUFFER_SIZE);
      SRAM_read(SRAM_PICLANG_NEXT_SWAP_ADDR,(void*)picfs_buffer,FS_BUFFER_SIZE);
      PICLANG_file_buffer_index = 0;
      break;
    case PICLANG_FSTAT:
      a = PICLANG_pop();
      sch1 = picfs_stat(a);
      if(sch1 < 0)
	{
	  PICLANG_set_errno();
	  sch1 = 0;
	}
      a = picfs_buffer[ST_SIZE] << 8;
      a += picfs_buffer[ST_SIZE+1];
      PICLANG_pushl(a);
      break;
    case PICLANG_READDIR:
      a = PICLANG_pop();
      sch1 = picfs_readdir(1,a);
      if(sch1 != SUCCESS)
	PICLANG_set_errno();
      PICLANG_pushl(sch1);
      break;
    case PICLANG_FOPEN:
      {
	/*	mount_t mount;
		SRAM_read(curr_dir*sizeof(mount_t)+SRAM_MTAB_ADDR,&mount,sizeof(mount_t));*/
	a = PICLANG_pop();
	if(a < ARG_SIZE)
	  {
	    sch1 = picfs_open(ARG_buffer+a,curr_dir);
	  }
	else if(a < ARG_SIZE + FS_BUFFER_SIZE)
	  {
	    a -= ARG_SIZE;
	    sch1 = picfs_open((const char*)picfs_buffer+a,curr_dir);
	  }
	else
	  {
	    c = 0;
	    a -= ARG_SIZE + picos_processes[picos_curr_process].block_size;
	    sch1 = PICLANG_next_dereference(a,command);
	  }
	if(sch1 == -1)
	  PICLANG_set_errno();
	PICLANG_pushl(sch1);
	break;
      }
    case PICLANG_FCLOSE:
      a = PICLANG_pop();
      picfs_close(a);
      break;
    case PICLANG_FREAD:
      a = PICLANG_pop();
      sch1 = picfs_load(a);
      b = sch1;
      if(sch1 < 0)
	{
	  b = -1;
	  error_code = SUCCESS;
	  PICLANG_exception(error_code);
	}
      PICLANG_pushl(b);
      break;
    case PICLANG_PRINTL:
      IO_putd(PICLANG_pop());
      IO_flush();
      break;
    case PICLANG_CLEAR:
      clear_output();
      break;
    case PICLANG_MUTEX_LOCK:
      PICLANG_block();
      break;
    case PICLANG_MUTEX_UNLOCK:
      PICLANG_unblock();
      break;
    case PICLANG_GETCH:
      {
	PICLANG_block();
	PICLANG_pushl(getch());
	PICLANG_unblock();
	break;
      }
    case PICLANG_GETD:
      {
	PICLANG_block();
	ch1 = getch();
	PICLANG_unblock();
	if(ch1 < '0' || ch1 > '9')
	  {
	    PICLANG_exception(ARG_INVALID);
	    break;
	  }
	ch1 -= 0x30;
	PICLANG_pushl(ch1);
	break;
      }
    case PICLANG_SPRINT:case PICLANG_SPRINTN:
      {
	// string addresses start with arguments then const strings in executable
	/*string_pointer*/a = PICLANG_pop();
	PICLANG_next_dereference(a,command);
	IO_flush();
	break;
      }
    case PICLANG_LSOF:
      IO_puts("Implement lsof");
      break;
    case PICLANG_LSMOUNT:
      lsmount();
      break;
    case PICLANG_ERRNO:
      PICLANG_pushl(PICLANG_get_errno());
      break;
    case PICLANG_KVERSION:
      a = PICLANG_pop();
      if(a > 3)
      {
          PICLANG_exception(PICFS_EINVAL);
          break;
      }
      if(a == 0)
          b = KERNEL_MAJOR_VERSION;
      else if(a == 1)
          b = KERNEL_MINOR_VERSION;
      else if(a == 2)
          b = KERNEL_REVISION;
      else
          b = KERNEL_ID_TAG;
      PICLANG_pushl(b);
      break;
    case PICLANG_SIGNAL:
      a = PICLANG_get_next_word();// signal id
      b = PICLANG_get_next_word();// signal action
      if(a == PICOS_NUM_SIGNALS)
	{
	  signal_free(b);
	  break;
	}
      sch1 = signal_assign(a,picos_curr_process,b);
      if(sch1 != 0)
	PICLANG_exception(error_code);
      break;
    case PICLANG_SLEEP:
      a = PICLANG_pop();
      ch1 = picos_curr_process;
      PICLANG_save(PICLANG_SUSPENDED);
      picos_processes[ch1].expires = (quantum_t)a;
      break;
    case PICLANG_MORSE:
      {
	char two[2];
	/*addr*/a = PICLANG_pop();
	two[1] = PICLANG_pop();//char or string?
	if(two[1] == PICLANG_MORSE_STRING)
	  SRAM_read(a++,two,1);
	else
	  {
	    two[0] = (char)a;
	    two[1] = 0;
	    morse_sound(two);
	    break;
	  }
	two[1] = 0;
	while(two[0] != 0)
	  {
	    morse_sound(two);
	    SRAM_read(a++,two,1);
	  }
	break;
      }
    case PICLANG_TIME:
      {
	const TIME_t *thetime = TIME_get();
	ch1 = PICLANG_pop();
	switch(ch1)
	  {
	  case 'Y':
	    PICLANG_pushl(thetime->year);
	    break;
	  case 'm':
	    PICLANG_pushl(thetime->month);
	    break;
	  case 'd':
	    PICLANG_pushl(thetime->day);
	    break;
	  case 'H':
	    PICLANG_pushl(thetime->hours);
	    break;
	  case 'M':
	    PICLANG_pushl(thetime->minutes);
	    break;
	  case 'S':
	    PICLANG_pushl(thetime->seconds);
	    break;
	  default:
	    PICLANG_exception(PICLANG_INVALID_PARAMETER);
	    break;
	  }
	break;
      }
    case PICLANG_SET_TIME:case PICLANG_SET_DATE:
      {
	TIME_t newtime = *(TIME_get());
	if(command == PICLANG_SET_TIME)
	  {
	    newtime.minutes = PICLANG_pop();
	    newtime.hours = PICLANG_pop();
	    if(newtime.minutes > 59 || newtime.hours > 23)
	      {
		newtime.minutes = newtime.hours = 0;
		PICLANG_exception(TIME_INVALID);
	      }
	  }
	else
	  {
	    newtime.year = PICLANG_pop();
	    newtime.day = PICLANG_pop();
	    newtime.month = PICLANG_pop();
	    if(newtime.month > 12 || newtime.day > 31)
	      {
		newtime.month = newtime.day = 0;
		PICLANG_exception(TIME_INVALID);
	      }
	  }
	TIME_set(&newtime);
	break;
      }
    case PICLANG_JZ:case PICLANG_JMP:case PICLANG_CALL:
      {
	b = curr_process.pc;
	a = PICLANG_get_next_word();
	if(curr_process.status != PICLANG_SUCCESS)
	  {
	    curr_process.pc = b;
	    break;
	  }
	if(command == PICLANG_CALL)
	  {
	    PICLANG_call_push(curr_process.pc);
	    curr_process.pc = a;
	  }
	else if(command == PICLANG_JMP)
	  curr_process.pc = a;
	else if((curr_process.bitmap & PICLANG_ZERO) == 0)
	  curr_process.pc = a;
	if(curr_process.status != PICLANG_SUCCESS)
	  {
	    curr_process.pc = b;
	    break;
	  }
	break;
      }
    case PICLANG_RETURN:
      {
	b = curr_process.pc;
	curr_process.pc = PICLANG_call_pop();
	if(curr_process.status != PICLANG_SUCCESS)
	  {
	    curr_process.pc = b;
	    break;
	  }
	break;
      }
    case PICLANG_EXIT:
      PICLANG_save(PICLANG_pop());
      break;
    case PICLANG_LABEL:
      break;
    case PICLANG_COMPLT:
      {
	b = PICLANG_pop();
	a = PICLANG_pop();
	if(a < b)
	  curr_process.bitmap |= PICLANG_ZERO;
	else
	  curr_process.bitmap &= ~PICLANG_ZERO;
	break;
      }
    case PICLANG_COMPGT:
      {
	b = PICLANG_pop();
	a = PICLANG_pop();
	if(a > b)
	  curr_process.bitmap |= PICLANG_ZERO;
	else
	  curr_process.bitmap &= ~PICLANG_ZERO;
	break;
      }
    case PICLANG_COMPEQ:case PICLANG_COMPNE:
      {
	b = PICLANG_pop();
	a = PICLANG_pop();
	if(a == b)
	  curr_process.bitmap |= PICLANG_ZERO;
	else
	  curr_process.bitmap &= ~PICLANG_ZERO;
	if(command == PICLANG_COMPNE)
	  curr_process.bitmap ^= PICLANG_ZERO;
	break;
      }
    case PICLANG_AND:
      a = PICLANG_pop();
      b = PICLANG_pop();
      PICLANG_pushl(a & b);
      break;
    case PICLANG_OR:
      a = PICLANG_pop();
      b = PICLANG_pop();
      PICLANG_pushl(a | b);
      break;
    case PICLANG_NOT:
      a = PICLANG_pop();
      PICLANG_pushl(~a);
      break;
    case PICLANG_RAWLOAD:
      SRAM_read(SRAM_PICLANG_RAW_FILE_BUFFER,(void*)picfs_buffer,FS_BUFFER_SIZE);
      break;
    case PICLANG_NUM_COMMANDS:default:
      PICLANG_exception(PICLANG_UNKNOWN_COMMAND);
      break;
    }

}
Example #6
0
void LineBuilder::build() {

	// Need at least 2 points to draw a line
	if (points.size() < 2) {
		clear_output();
		return;
	}

	const float hw = width / 2.f;
	const float hw_sq = hw * hw;
	const float sharp_limit_sq = sharp_limit * sharp_limit;
	const int len = points.size();

	// Initial values

	Vector2 pos0 = points[0];
	Vector2 pos1 = points[1];
	Vector2 f0 = (pos1 - pos0).normalized();
	Vector2 u0 = rotate90(f0);
	Vector2 pos_up0 = pos0 + u0 * hw;
	Vector2 pos_down0 = pos0 - u0 * hw;

	Color color0;
	Color color1;

	float current_distance0 = 0.f;
	float current_distance1 = 0.f;
	float total_distance;
	_interpolate_color = gradient != NULL;
	bool distance_required = _interpolate_color || texture_mode == LINE_TEXTURE_TILE;
	if (distance_required)
		total_distance = calculate_total_distance(points);
	if (_interpolate_color)
		color0 = gradient->get_color(0);
	else
		colors.push_back(default_color);

	float uvx0 = 0.f;
	float uvx1 = 0.f;

	// Begin cap
	if (begin_cap_mode == LINE_CAP_BOX) {
		// Push back first vertices a little bit
		pos_up0 -= f0 * hw;
		pos_down0 -= f0 * hw;
		// The line's outer length will be a little higher due to begin and end caps
		total_distance += width;
		current_distance0 += hw;
		current_distance1 = current_distance0;
	} else if (begin_cap_mode == LINE_CAP_ROUND) {
		if (texture_mode == LINE_TEXTURE_TILE) {
			uvx0 = 0.5f;
		}
		new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, 1.f, 1.f));
		total_distance += width;
		current_distance0 += hw;
		current_distance1 = current_distance0;
	}

	strip_begin(pos_up0, pos_down0, color0, uvx0);

	//  pos_up0 ------------- pos_up1 --------------------
	//     |                     |
	//   pos0 - - - - - - - - - pos1 - - - - - - - - - pos2
	//     |                     |
	// pos_down0 ------------ pos_down1 ------------------
	//
	//   i-1                     i                      i+1

	// http://labs.hyperandroid.com/tag/opengl-lines
	// (not the same implementation but visuals help a lot)

	// For each additional segment
	for (int i = 1; i < len - 1; ++i) {

		pos1 = points[i];
		Vector2 pos2 = points[i + 1];

		Vector2 f1 = (pos2 - pos1).normalized();
		Vector2 u1 = rotate90(f1);

		// Determine joint orientation
		const float dp = u0.dot(f1);
		const Orientation orientation = (dp > 0.f ? UP : DOWN);

		Vector2 inner_normal0, inner_normal1;
		if (orientation == UP) {
			inner_normal0 = u0 * hw;
			inner_normal1 = u1 * hw;
		} else {
			inner_normal0 = -u0 * hw;
			inner_normal1 = -u1 * hw;
		}

		// ---------------------------
		//                        /
		// 0                     /    1
		//                      /          /
		// --------------------x------    /
		//                    /          /    (here shown with orientation == DOWN)
		//                   /          /
		//                  /          /
		//                 /          /
		//                     2     /
		//                          /

		// Find inner intersection at the joint
		Vector2 corner_pos_in, corner_pos_out;
		SegmentIntersectionResult intersection_result = segment_intersection(
				pos0 + inner_normal0, pos1 + inner_normal0,
				pos1 + inner_normal1, pos2 + inner_normal1,
				&corner_pos_in);

		if (intersection_result == SEGMENT_INTERSECT)
			// Inner parts of the segments intersect
			corner_pos_out = 2.f * pos1 - corner_pos_in;
		else {
			// No intersection, segments are either parallel or too sharp
			corner_pos_in = pos1 + inner_normal0;
			corner_pos_out = pos1 - inner_normal0;
		}

		Vector2 corner_pos_up, corner_pos_down;
		if (orientation == UP) {
			corner_pos_up = corner_pos_in;
			corner_pos_down = corner_pos_out;
		} else {
			corner_pos_up = corner_pos_out;
			corner_pos_down = corner_pos_in;
		}

		LineJointMode current_joint_mode = joint_mode;

		Vector2 pos_up1, pos_down1;
		if (intersection_result == SEGMENT_INTERSECT) {
			// Fallback on bevel if sharp angle is too high (because it would produce very long miters)
			if (current_joint_mode == LINE_JOINT_SHARP && corner_pos_out.distance_squared_to(pos1) / hw_sq > sharp_limit_sq) {
				current_joint_mode = LINE_JOINT_BEVEL;
			}
			if (current_joint_mode == LINE_JOINT_SHARP) {
				// In this case, we won't create joint geometry,
				// The previous and next line quads will directly share an edge.
				pos_up1 = corner_pos_up;
				pos_down1 = corner_pos_down;
			} else {
				// Bevel or round
				if (orientation == UP) {
					pos_up1 = corner_pos_up;
					pos_down1 = pos1 - u0 * hw;
				} else {
					pos_up1 = pos1 + u0 * hw;
					pos_down1 = corner_pos_down;
				}
			}
		} else {
			// No intersection: fallback
			pos_up1 = corner_pos_up;
			pos_down1 = corner_pos_down;
		}

		// Add current line body quad
		// Triangles are clockwise
		if (distance_required) {
			current_distance1 += pos0.distance_to(pos1);
		}
		if (_interpolate_color) {
			color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
		}
		if (texture_mode == LINE_TEXTURE_TILE) {
			uvx0 = current_distance0 / width;
			uvx1 = current_distance1 / width;
		}

		strip_add_quad(pos_up1, pos_down1, color1, uvx1);

		// Swap vars for use in the next line
		color0 = color1;
		u0 = u1;
		f0 = f1;
		pos0 = pos1;
		current_distance0 = current_distance1;
		if (intersection_result == SEGMENT_INTERSECT) {
			if (current_joint_mode == LINE_JOINT_SHARP) {
				pos_up0 = pos_up1;
				pos_down0 = pos_down1;
			} else {
				if (orientation == UP) {
					pos_up0 = corner_pos_up;
					pos_down0 = pos1 - u1 * hw;
				} else {
					pos_up0 = pos1 + u1 * hw;
					pos_down0 = corner_pos_down;
				}
			}
		} else {
			pos_up0 = pos1 + u1 * hw;
			pos_down0 = pos1 - u1 * hw;
		}
		// From this point, bu0 and bd0 concern the next segment

		// Add joint geometry
		if (current_joint_mode != LINE_JOINT_SHARP) {

			// ________________ cbegin
			//               / \
			//              /   \
			// ____________/_ _ _\ cend
			//             |     |
			//             |     |
			//             |     |

			Vector2 cbegin, cend;
			if (orientation == UP) {
				cbegin = pos_down1;
				cend = pos_down0;
			} else {
				cbegin = pos_up1;
				cend = pos_up0;
			}

			if (current_joint_mode == LINE_JOINT_BEVEL) {
				strip_add_tri(cend, orientation);
			} else if (current_joint_mode == LINE_JOINT_ROUND) {
				Vector2 vbegin = cbegin - pos1;
				Vector2 vend = cend - pos1;
				strip_add_arc(pos1, vend.angle_to(vbegin), orientation);
			}

			if (intersection_result != SEGMENT_INTERSECT)
				// In this case the joint is too f****d up to be re-used,
				// start again the strip with fallback points
				strip_begin(pos_up0, pos_down0, color1, uvx1);
		}
	}

	// Last (or only) segment

	pos1 = points[points.size() - 1];

	Vector2 pos_up1 = pos1 + u0 * hw;
	Vector2 pos_down1 = pos1 - u0 * hw;

	// End cap (box)
	if (end_cap_mode == LINE_CAP_BOX) {
		pos_up1 += f0 * hw;
		pos_down1 += f0 * hw;
	}

	if (distance_required) {
		current_distance1 += pos0.distance_to(pos1);
	}
	if (_interpolate_color) {
		color1 = gradient->get_color(gradient->get_points_count() - 1);
	}
	if (texture_mode == LINE_TEXTURE_TILE) {
		uvx1 = current_distance1 / width;
	}

	strip_add_quad(pos_up1, pos_down1, color1, uvx1);

	// End cap (round)
	if (end_cap_mode == LINE_CAP_ROUND) {
		// Note: color is not used in case we don't interpolate...
		Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0);
		new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f, 0.f, 1.f, 1.f));
	}
}