I have an image with few lines of japanese Kanji for which I am trying modifying the boxes boundes before to iterate over and get the font and its attributes.
I could check that Pyplot is displaying the boxes correctly. Also the text is correctly returned if I remove the loop which setRectangle.
My function is as below:
def font_attr(resized):
img = resized
image = Image.fromarray(img)
with PyTessBaseAPI(path='C:\\Users\\mdelal001\\fast_format_assist\\tessdata\\', lang='jpn+msp+hgp', oem=0, psm=3) as api:
api.SetImage(image)
boxes = api.GetComponentImages(RIL.TEXTLINE, True)
delta = 5
image_array = np.array(image)
for box in boxes:
print(box)
box = box[1]
x, y, w, h = box['x'] - delta, box['y'] - delta, box['w'] + 2 * delta, box['h'] + 2 * delta
cv2.line(image_array, (x, y), (x + w, y), (0, 0, 0), 2)
cv2.line(image_array, (x, y), (x, y + h), (0, 0, 0), 2)
cv2.line(image_array, (x + w, y), (x + w, y + h), (0, 0, 0), 2)
cv2.line(image_array, (x, y + h), (x + w, y + h), (0, 0, 0), 2)
plt.imshow(image_array)
plt.show()
for i, (im, box, _, _) in enumerate(boxes):
print(i, (im, box, _, _))
api.SetRectangle(box['x'] - delta, box['y'] - delta, box['w'] + 2 * delta, box['h'] + 2 * delta)
api.Recognize()
ri = api.GetIterator()
font = []
attributes = []
for r in iterate_level(ri, RIL.BLOCK):
symbol = r.GetUTF8Text(RIL.BLOCK)
conf = r.Confidence(RIL.BLOCK)
symbol = symbol.replace('\n',' ').replace(' ', '')
word_attributes = r.WordFontAttributes()
if not symbol:
continue
else:
font.append([symbol, 'confidence: ',conf])
attributes.append(word_attributes)
return font, attributes
The error:
Traceback (most recent call last):
File "c:\Users\m1\fast_format_assist\font_reader.py", line 338, in <module>
attr = font_attr(resized)
File "c:\Users\m1\fast_format_assist\font_reader.py", line 216, in font_attr
symbol = r.GetUTF8Text(RIL.TEXTLINE)
File "tesserocr.pyx", line 820, in tesserocr._tesserocr.PyLTRResultIterator.GetUTF8Text
RuntimeError: No text returned
Better you go with easyocr for this case.
import easyocr
reader = easyocr.Reader(['ch_sim'],gpu=False)
result = reader.readtext('5.png')
for detection in result:
print(detection)
the result is,
([[0, 0], [384, 0], [384, 70], [0, 70]], '二九办一番目', 0.04030529339493764)
([[0, 75], [381, 75], [381, 155], [0, 155]], '二九加二番目', 0.06939456423064959)
([[0, 164], [380, 164], [380, 242], [0, 242]], '二九|4别0去', 0.02179895938530989)
([[0, 249], [385, 249], [385, 335], [0, 335]], '二九|寸危害学', 0.01723975047661578)
([[0, 335], [385, 335], [385, 421], [0, 421]], '二九(丈著盥目', 0.004155675980245961)
([[0, 424], [382, 424], [382, 499], [0, 499]], '二札|击六番目', 0.022589517592635434)
You can install easyocr by pip install easyocr
Related
I was getting error as :
Traceback (most recent call last):
File "/Users/dilipreddy/Downloads/Lane-Segmentation-master/lane_segmentation.py", line 132, in
X, y = train_generator.getitem(0)
File "/Users/dilipreddy/Downloads/Lane-Segmentation-master/lane_segmentation.py", line 107, in getitem
_img, _mask = self.load(id_name)
File "/Users/dilipreddy/Downloads/Lane-Segmentation-master/lane_segmentation.py", line 80, in load
id_name_actual, text, _ = id_name.split('.')
ValueError: not enough values to unpack (expected 3, got 2)
(base) dilipreddy#Dilips-MacBook-Pro Lane-Segmentation-master %
import matplotlib.pyplot as plt
import numpy as np
import cv2
import os
import random
import sys
import tensorflow as tf
import keras
from keras.layers import *
from keras.models import *
# Part 1 - Data Preprocessing
def get_mask(img_path, label_path):
label_file = open(label_path, "r")
if label_file.mode == 'r':
contents = label_file.read()
lines_text = contents.split('\n')
x_coordinate, y_coordinate, lanes = [], [], []
for line_text in lines_text:
number_lines = line_text.split(" ")
number_lines.pop()
x = list([float(number_lines[i]) for i in range(len(number_lines)) if i % 2 == 0])
y = list([float(number_lines[i]) for i in range(len(number_lines)) if i % 2 != 0])
x_coordinate.append(x)
y_coordinate.append(y)
lanes.append(set(zip(x, y)))
lanes.pop()
img = cv2.imread(img_path)
mask = np.zeros_like(img)
# colors = [[255,0,0], [0,255,0], [0,0,255], [255,255,0]]
colors = [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255]]
for i in range(len(lanes)):
cv2.polylines(img, np.int32([list(lanes[i])]), isClosed=False, color=colors[i], thickness=10)
label = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return label
img = get_mask("data/CULane/driver_161_90frame/06030819_0755.MP4/00000.jpg",
"data/CULane/driver_161_90frame/06030819_0755.MP4/00000.lines.txt")
plt.imshow(img)
print(img.shape)
import os
from tensorflow.keras.utils import Sequence
import os
from tensorflow.keras.utils import Sequence
class DataGenerator2D(Sequence):
"""Generates data for Keras
Sequence based data generator. Suitable for building data generator for training and prediction.
"""
def __init__(self, base_path, img_size=256, batch_size=1, shuffle=True):
self.base_path = base_path
self.img_size = img_size
self.id = os.listdir(os.path.join(base_path, "CULane"))
self.batch_size = batch_size
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
"""Denotes the number of batches per epoch
:return: number of batches per epoch
"""
return int(np.ceil(len(self.id) / float(self.batch_size)))
def __load__(self, id_name):
id_name_actual, text, _ = id_name.split('.')
image_path = os.path.join(self.base_path, "images", (id_name_actual + '.' + text + '.jpg'))
label_path = os.path.join(self.base_path, "labels", (id_name_actual + '.' + text + '.lines.txt'))
image = cv2.imread(image_path, 1) # Reading Image in RGB format
image = cv2.resize(image, (self.img_size, self.img_size))
# image = cv2.resize(image, (int(img.shape[1]/2), int(img.shape[0]/2)))
mask = get_mask(image_path, label_path)
mask = cv2.resize(mask, (self.img_size, self.img_size))
# mask = cv2.resize(mask, (int(img.shape[1]/2), int(img.shape[0]/2)))
# Normalizing the image
image = image / 255.0
mask = mask / 255.0
return image, mask
def __getitem__(self, index):
if (index + 1) * self.batch_size > len(self.id):
file_batch = self.id[index * self.batch_size:]
else:
file_batch = self.id[index * self.batch_size:(index + 1) * self.batch_size]
images, masks = [], []
for id_name in file_batch:
_img, _mask = self.__load__(id_name)
images.append(_img)
masks.append(_mask)
images = np.array(images)
masks = np.array(masks)
return images, masks
def on_epoch_end(self):
"""Updates indexes after each epoch
"""
self.indexes = np.arange(len(self.id))
if self.shuffle == True:
np.random.shuffle(self.indexes)
def on_epoch_end(self):
"""Updates indexes after each epoch
"""
self.indexes = np.arange(len(self.id))
if self.shuffle == True:
np.random.shuffle(self.indexes)
train_generator = DataGenerator2D(base_path='data', img_size=256, batch_size=64, shuffle=False)
X, y = train_generator.__getitem__(0)
print(X.shape, y.shape)
fig = plt.figure(figsize=(17, 8))
columns = 4
rows = 3
for i in range(1, columns*rows + 1):
img = X[i-1]
fig.add_subplot(rows, columns, i)
plt.imshow(img)
plt.show()
fig = plt.figure(figsize=(17, 8))
columns = 4
rows = 3
for i in range(1, columns*rows + 1):
img = y[i-1]
fig.add_subplot(rows, columns, i)
plt.imshow(img)
plt.show()
# Part 2 - Model
def dice_coef(y_true, y_pred, smooth=1):
"""
Dice = (2*|X & Y|)/ (|X|+ |Y|)
= 2*sum(|A*B|)/(sum(A^2)+sum(B^2))
ref: https://arxiv.org/pdf/1606.04797v1.pdf
"""
intersection = K.sum(K.abs(y_true * y_pred), axis=-1)
return (2. * intersection + smooth) / (K.sum(K.square(y_true),-1) + K.sum(K.square(y_pred),-1) + smooth)
def dice_coef_loss(y_true, y_pred):
return 1-dice_coef(y_true, y_pred)
reconstructed_model = tf.keras.models.load_model("pretrained models/UNET-BN-20-0.081170.hdf5",
custom_objects = {'dice_coef_loss': dice_coef_loss})
# Part 3 - Visualization
val_generator = DataGenerator2D('content/data/', img_size=256, batch_size=128, shuffle=True)
X, y = val_generator.__getitem__(10)
print(X.shape, y.shape)
plt.imshow(X[2])
predict = reconstructed_model.predict(X)
print(predict.shape)
img = cv2.cvtColor(predict[2], cv2.COLOR_GRAY2BGR)
plt.imshow(img)
cocos2dx-3.10 covertToWorldSpace result is wrong when using OrbitCamera
devices test on:Windows
Steps to Reproduce:
local size = cc.size(400, 400)
local layer = cc.LayerColor:create(cc.c4b(200, 0, 0, 255), size.width, size.height)
layer:setPosition(display.cx - size.width, display.cy - size.height)
self:addChild(layer)
local label = cc.Label:create()
label:setSystemFontSize(30)
label:setSystemFontName("Arial")
label:setString("XXX")
label:setColor(cc.c3b(0, 255, 100))
label:setPosition(size.width * 0.5, 0)
layer:addChild(label)
local lsize = label:getContentSize()
local action = cc.OrbitCamera:create(1, 1, 0, 0, 70, -90, 0)
local callFunc = cc.CallFunc:create(function()
local worldPos = label:convertToWorldSpace(cc.p(lsize.width * 0.5, lsize.height * 0.5))
label = cc.Label:create()
label:setSystemFontSize(30)
label:setSystemFontName("Arial")
label:setString("AAA")
label:setColor(cc.c3b(100, 0, 100))
label:setPosition(worldPos)
self:addChild(label)
end)
layer:runAction(cc.Sequence:create(action, callFunc))
I found a solution:
local mat4 = label:getNodeToParentTransform(self)
local vec4 = {x = lsize.width * 0.5, y = lsize.height * 0.5, z = 0, w = 1}
-- convert to world space (we need to calculate its Z-value)
local pos = mat4_transformVector(mat4, vec4, cc.mat4.createIdentity())
-- convert GL-screen-space coordinates
local worldPos = cc.Camera:getDefaultCamera():projectGL(pos)
enter image description here
I am aware of how to detect collision with circle and rectangles but I am not sure how to find the minimum translation vector between the two. I know how to do it with the SAT collision detection algorithm, but that is too complicated for my simple implementation right now.
I am really not sure what to do except change the x-coordinate appropriately.
Here is the code. When you pressed the down button, what I would want is that the circle automatically is "shoved" to the left (since it is already positioned a bit left to the center) when constantly having the down button pressed i.e. it is moving down but sliding to the left.
import pygame
if __name__ == "__main__":
pygame.init()
display = pygame.display.set_mode((500, 500))
display.fill((255, 255, 255))
circle_x = 240
circle_y = 50
pygame.draw.circle(display, (0, 0, 255), (circle_x, circle_y), 50)
pygame.draw.rect(display, (0, 255, 255), (240, 250, 20, 250))
pygame.display.update()
vel = 1
is_down_held = False
clock = pygame.time.Clock()
while True:
pressed_keys = pygame.key.get_pressed()
if pressed_keys[pygame.K_DOWN]:
circle_y += vel
display.fill((255, 255, 255))
pygame.draw.circle(display, (0, 0, 255), (circle_x, circle_y), 50)
pygame.draw.rect(display, (0, 255, 255), (240, 250, 20, 250))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
dt = clock.tick(60)
dt /= 1000
I tried moving the x-coordinate with some constant, however, it looks unrealistic and is actually passing through the rectangle sometimes (since it actually doesn't detect the rectangle).
Okay, it seems like I had to implement SAT collision detection at the end anyways. To calculate the minimum translation vector between a rectangle and a circle, you go through the necessary axes for the shapes (see https://jcharry.com/blog/physengine10 and https://www.sevenson.com.au/actionscript/sat/ for an explanation on that) then you take the smallest overlap axis for when there is a collision. Below is the code using pygame GUI library:
import math
import pygame
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
self.magnitude = math.sqrt(x ** 2 + y ** 2)
if self.magnitude != 0:
self.direction_x = -x / self.magnitude
self.direction_y = -y / self.magnitude
else:
self.direction_x = 0
self.direction_y = 0
def normalize(self):
if self.magnitude != 0:
self.x /= self.magnitude
self.y /= self.magnitude
self.magnitude = 1
def project_vector(vector1, vector2):
return get_dot_product(vector1, get_unit_vector(vector2))
def get_dot_product(vector1, vector2):
return (vector1.x * vector2.x) + (vector1.y * vector2.y)
def get_normal(vector):
return Vector(vector.y, -vector.x)
def get_vector(point):
return Vector(point[0], point[1])
def scale_vector(vector, magnitude):
return Vector(vector.x*magnitude, vector.y*magnitude)
def get_unit_vector(vector):
if vector.magnitude != 0:
return scale_vector(vector, 1 / vector.magnitude)
else:
return scale_vector(vector, 0)
def get_closest_point(circle_centre, rectangle_points):
closest_distance = float('inf')
closest_point = None
for point in rectangle_points:
distance = (circle_centre[0] - point[0])**2 + (circle_centre[1] - point[1])**2
if distance <= closest_distance:
closest_distance = distance
closest_point = point
return closest_point
def is_collision(circle_centre, rectangle_points):
closest_point = get_closest_point(circle_centre, rectangle_points)
rectangle_edge_vectors = []
for point in rectangle_points:
rectangle_edge_vectors += [get_vector(point)]
rectangle_edge_normals = []
for i in range(len(rectangle_points) - 1):
rectangle_edge_normals += [get_normal(get_vector((rectangle_points[i + 1][0] - rectangle_points[i][0], rectangle_points[i + 1][1] - rectangle_points[i][1])))]
rectangle_edge_normals += [get_normal(get_vector((rectangle_points[0][0] - rectangle_points[len(rectangle_points) - 1][0], rectangle_points[0][1] - rectangle_points[len(rectangle_points) - 1][1])))]
rectangle_edge_normals += [get_vector((circle_centre[0] - closest_point[0], circle_centre[1] - closest_point[1]))]
axes = rectangle_edge_normals
vectors = rectangle_edge_vectors
for axis in axes:
current_rect_max_x = float('-inf')
current_rect_min_x = float('inf')
for vector in vectors:
current_rect_projection = project_vector(vector, axis)
if current_rect_projection >= current_rect_max_x:
current_rect_max_x = current_rect_projection
if current_rect_projection <= current_rect_min_x:
current_rect_min_x = current_rect_projection
current_circle_projection = project_vector(get_vector(circle_centre), axis)
current_circle_max_x = current_circle_projection + 25
current_circle_min_x = current_circle_projection - 25
if current_rect_min_x > current_circle_max_x or current_circle_min_x > current_rect_max_x:
return False
return True
def get_minimum_translation_vector(circle_centre, rectangle_points):
closest_point = get_closest_point(circle_centre, rectangle_points)
rectangle_edge_vectors = []
for point in rectangle_points:
rectangle_edge_vectors += [get_vector(point)]
rectangle_edge_normals = []
for i in range(len(rectangle_points) - 1):
rectangle_edge_normals += [get_normal(get_vector((rectangle_points[i + 1][0] - rectangle_points[i][0], rectangle_points[i + 1][1] - rectangle_points[i][1])))]
rectangle_edge_normals += [get_normal(get_vector((rectangle_points[0][0] - rectangle_points[len(rectangle_points) - 1][0], rectangle_points[0][1] - rectangle_points[len(rectangle_points) - 1][1])))]
rectangle_edge_normals += [get_vector((circle_centre[0] - closest_point[0], circle_centre[1] - closest_point[1]))]
axes = rectangle_edge_normals
for axis in axes:
axis.normalize()
vectors = rectangle_edge_vectors
minimum_translation_vector = Vector(axes[0].x, axes[0].y)
minimum_translation_vector.magnitude = float('inf')
current_minimum_translation_vector = Vector(axes[0].x, axes[0].y)
current_minimum_translation_vector.magnitude = float('inf')
for axis in axes:
current_rect_max_x = float('-inf')
current_rect_min_x = float('inf')
for vector in vectors:
current_rect_projection = project_vector(vector, axis)
if current_rect_projection >= current_rect_max_x:
current_rect_max_x = current_rect_projection
if current_rect_projection <= current_rect_min_x:
current_rect_min_x = current_rect_projection
current_circle_projection = project_vector(get_vector(circle_centre), axis)
current_circle_max_x = current_circle_projection + 25
current_circle_min_x = current_circle_projection - 25
current_minimum_translation_vector = axis
current_minimum_translation_vector.magnitude = abs(current_circle_min_x - current_rect_max_x)
if current_minimum_translation_vector.magnitude <= minimum_translation_vector.magnitude:
minimum_translation_vector = axis
minimum_translation_vector.magnitude = current_minimum_translation_vector.magnitude
return minimum_translation_vector
if __name__ == "__main__":
pygame.init()
display = pygame.display.set_mode((500, 500))
rectangle_points_main = [(250, 250), (300, 250), (300, 300), (250, 300)]
circle_centre_main = (0, 0)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
circle_centre_main = (pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1])
display.fill((255, 255, 255))
if is_collision(circle_centre_main, rectangle_points_main):
pygame.draw.circle(display, (255, 0, 0), circle_centre_main, 25)
minimum_translation_vector_main = get_minimum_translation_vector(circle_centre_main, rectangle_points_main)
dx = minimum_translation_vector_main.magnitude * minimum_translation_vector_main.direction_x
dy = minimum_translation_vector_main.magnitude * minimum_translation_vector_main.direction_y
rectangle_points_main = [(rectangle_points_main[0][0] + dx, rectangle_points_main[0][1] + dy),
(rectangle_points_main[1][0] + dx, rectangle_points_main[1][1] + dy),
(rectangle_points_main[2][0] + dx, rectangle_points_main[2][1] + dy),
(rectangle_points_main[3][0] + dx, rectangle_points_main[3][1] + dy)]
else:
pygame.draw.circle(display, (0, 0, 255), circle_centre_main, 25)
pygame.draw.rect(display, (0, 255, 0), (rectangle_points_main[0][0], rectangle_points_main[0][1], 50, 50))
dt = clock.tick(60)
dt /= 1000
pygame.display.update()
So I am aware the HTML5 canvas pixel methods are a little different then normal. I am porting a ray-tracer from a Scala Swing application where the old draw method looked like this:
for {
x <- pixels.s.indices
y <- pixels.s(x).indices
} {
g.setColor(pixels.s(x)(y))
g.fillRect(x, y, x, y)
}
Where pixels.s was a 2D array like so:
[[Color, Color, Color],
[Color, Color, Color],
[Color, Color, Color]]
I.e. it mapped exactly to a 2D cartesian plane.
So now I have changed tack slightly, and I have a FLATTENED array of objects with x, y and color:
[{ x: 0, y: 0, color: { red: 33, green: 220, blue: 181 },
{ x: 0, y: 1, color: { red: 33, green: 220, blue: 231 },
{ x: 0, y: 2, color: { red: 33, green: 220, blue: 221 },
...
{ x: 319, y: 238, color: { red: 23, green: 10, blue: 31 },
{ x: 319, y: 239, color: { red: 13, green: 120, blue: 11 }]
I'm trying to draw my flattened array of colors to the screen using this code:
var imageData = context.createImageData(width, height);
var numPixels = width*height;
for (var i = 0; i < numPixels; i++) {
imageData.data[(i*4)] = tracedScene[i].color.red;
imageData.data[(i*4) + 1] = tracedScene[i].color.green;
imageData.data[(i*4) + 2] = tracedScene[i].color.blue;
imageData.data[(i*4) + 3] = 255; // alpha color
};
context.putImageData(imageData, 0, 0);
But it's coming out a bit wrong. I've tripled checked my tests and the actual array is the correct screen data. I'm just rendering it incorrectly. I'm currently getting this:
Whereas I should see:
Now I've had the exact same graphical oddities in the Swing version before (years ago), and it was because of the way I was drawing the image. I can't remember how I fixed it however.
What I am looking to be able to do, is have some method:
drawPixel(x, y, color, canvas) { ... }
that is able to draw a particular pixel at x, y with a color. OR... a way to do it traditionally.
Your input "flattened" array in wrong order: it should be by rows, but yours is by columns.
[{ x: 0, y: 0, color: { red: 33, green: 220, blue: 181 },
{ x: 1, y: 0, color: { red: 33, green: 220, blue: 231 },
{ x: 2, y: 0, color: { red: 33, green: 220, blue: 221 },
...
(Assuming y is top to bottom, x - left to right)
Image data goes from (0, 0) to (1, 0) and your code goes from (0, 0) to (0, 1). In other words, there's a conflict of row/column major order.
Anyway, depending on your performance needs you might be overthinking this.
What I am looking to be able to do, is have some method:
drawPixel(x, y, color, canvas) { ... }
that is able to draw a particular pixel at x, y with a color.
Why not just fill single pixels?
ctx.fillStyle = 'rgba(red, green, blue, alpha)';
ctx.fillRect(x, y, 1, 1);
This will be inefficient at around 100 pixels filled, and you'll want to switch to image data.
If you want to keep your data structure the same you could use something like this, which should work:
var imageData = context.createImageData(width, height);
var numPixels = width*height;
var pixelCount = 0;
for (var i = 0; i < width; i++) {
for (var j = 0; j < height; j++) {
var wpixel = j * width * 4;
wpixel += i * 4;
console.log(wpixel);
imageData.data[(wpixel)] = tracedScene[pixelCount].red;
imageData.data[(wpixel) + 1] = tracedScene[pixelCount].green;
imageData.data[(wpixel) + 2] = tracedScene[pixelCount].blue;
imageData.data[(wpixel) + 3] = 255; // alpha color
pixelCount++; // independent of i and j the way I wrote it
}
}
context.putImageData(imageData, 0, 0);
Demo: http://jsfiddle.net/t9edv/
I'm implementing a color picker. There is problem with the rendering. When I call c.fillRect(0, 0, 100, 80); the size of that rectangle is 103x42 px instead of 100x80. What is wrong here?
Also, rectangles are antialiased. Do I need offset the position by (0.5, 0.5) to avoid AA? I didn't use any kind of the coordinate system transformations.
colorSlider = function($e, color) {
this._$canvas = $('<canvas></canvas>');
this._c = this._$canvas[0].getContext('2d');
this._color = color || { r: 0, g: 0, b: 0 };
this._$canvas.width('310px');
this._$canvas.height('80px');
$e.append(this._$canvas);
this._render();
var me = this;
this._$canvas.mousedown(function(e) { me._mouseDown.call(me, e) });
this._$canvas.mouseup(function(e) { me._mouseUp.call(me, e) });
this._$canvas.mousemove(function(e) { me._mouseMove.call(me, e) });
this._dragChannel = 0;
}
colorSlider.prototype._pointInRect = function(x, y, rect) {
return x >= rect.x && x <= rect.x + rect.w && y >= rect.y && y <= rect.y + rect.h;
}
colorSlider.prototype._findTarget = function(event) {
var x = event.offsetX;
var y = event.offsetY;
console.log(x, y, this._rectR);
if (this._pointInRect(x, y, this._rectRThumb)) {
return { target: 1, value: x - this._rectR.x };
}
if (this._pointInRect(x, y, this._rectGThumb)) {
return { target: 2, value: x - this._rectG.x };
}
if (this._pointInRect(x, y, this._rectBThumb)) {
return { target: 3, value: x - this._rectB.x };
}
if (this._pointInRect(x, y, this._rectR)) {
return { target: 4, value: x - this._rectR.x };
}
if (this._pointInRect(x, y, this._rectG)) {
return { target: 5, value: x - this._rectG.x };
}
if (this._pointInRect(x, y, this._rectB)) {
return { target: 6, value: x - this._rectB.x };
}
return null;
}
colorSlider.prototype._mouseDown = function(event) {
this._dragChannel = 0;
var target = this._findTarget(event);
if (target) {
switch (target.target) {
case 1:
this._dragChannel = 1;
break;
case 2:
this._dragChannel = 2;
break;
case 3:
this._dragChannel = 3;
break;
case 4:
this._color.r = target.value;
break;
case 5:
this._color.g = target.value;
break;
case 6:
this._color.b = target.value;
break;
}
this._render();
}
};
colorSlider.prototype._mouseUp = function(event) {
//console.log('mouseUp');
};
colorSlider.prototype._mouseMove = function(event) {
//console.log('mouseMove', event);
};
colorSlider.prototype.padding = 4;
colorSlider.prototype._render = function() {
var padding = this.padding;
var thickness = 16;
var c = this._c;
var w = 255;
var h = this._$canvas.height();
c.clearRect(0, 0, this._$canvas.width(), this._$canvas.height());
var gradient = c.createLinearGradient(padding, 0, w, 0);
c.fillStyle = gradient;
gradient.addColorStop(0, this.colorToHex({ r: 0, g: this._color.g, b: this._color.b }));
gradient.addColorStop(1, this.colorToHex({ r: 255, g: this._color.g, b: this._color.b }));
c.fillRect(padding, padding, w, thickness);
c.lineWidth = 0;
c.fillRect(0, 0, 100, 80);
this._rectR = { x: padding, y: padding, w: w, h: thickness };
gradient = c.createLinearGradient(padding, 0, w, 0);
c.fillStyle = gradient;
gradient.addColorStop(0, this.colorToHex({ r: this._color.r, g: 0, b: this._color.b }));
gradient.addColorStop(1, this.colorToHex({ r: this._color.r, g: 255, b: this._color.b }));
c.fillRect(padding, padding + thickness + 2 * padding, w, thickness);
this._rectG = { x: padding, y: padding + thickness + 2 * padding, w: w, h: thickness };
gradient = c.createLinearGradient(padding, 0, w, 0);
c.fillStyle = gradient;
gradient.addColorStop(0, this.colorToHex({ r: this._color.r, g: this._color.g, b: 0 }));
gradient.addColorStop(1, this.colorToHex({ r: this._color.r, g: this._color.g, b: 255 }));
c.fillRect(padding, padding + 2 * (thickness + 2 * padding), w, thickness);
this._rectB = { x: padding, y: padding + 2 * (thickness + 2 * padding), w: w, h: thickness };
c.lineWidth = 2;
c.fillStyle = "white";
c.strokeStyle = "#888888";
this._rectRThumb = { x: padding + this._color.r - 2, y: padding / 2, w: 8, h: 20, r: 2 };
this.drawRoundedRectangle(c, this._rectRThumb);
this._rectGThumb = { x: padding + this._color.g - 2, y: padding / 2 + 2 * padding + thickness, w: 8, h: 20, r: 2 };
this.drawRoundedRectangle(c, this._rectGThumb);
this._rectBThumb = { x: padding + this._color.b - 2, y: padding / 2 + 2 * (2 * padding + thickness), w: 8, h: 20, r: 2 };
this.drawRoundedRectangle(c, this._rectBThumb);
};
colorSlider.prototype.colorToHex = function(color) {
var c = '#'
+ (color.r + 256).toString(16).substr(1, 2)
+ (color.g + 256).toString(16).substr(1, 2)
+ (color.b + 256).toString(16).substr(1, 2);
console.log(c);
return c;
};
// http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
colorSlider.prototype.drawRoundedRectangle = function(c, rect) {
var x = rect.x;
var y = rect.y;
var width = rect.w;
var height = rect.h;
var radius = rect.r;
c.beginPath();
c.moveTo(x + radius, y);
c.lineTo(x + width - radius, y);
c.quadraticCurveTo(x + width, y, x + width, y + radius);
c.lineTo(x + width, y + height - radius);
c.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
c.lineTo(x + radius, y + height);
c.quadraticCurveTo(x, y + height, x, y + height - radius);
c.lineTo(x, y + radius);
c.quadraticCurveTo(x, y, x + radius, y);
c.closePath();
c.stroke();
c.fill();
};
index.html
<script>
$(function() {
$("#directionalLight,#ambientLight").each(function() {
new colorSlider($(this));
});
});
</script>
<body>
<div>Directional light</div>
<div id="directionalLight"></div>
<div>Ambient light</div>
<div id="ambientLight"></div>
</body>
The first thing to know is that a canvas element has intrinsic dimensions = number of pixels in the inside coordinate space (set by the width and height attributes and properties). It also has extrinsic dimensions (style.width and style.height) which is the number of pixels that the image takes within the webpage. The intrinsic pixels are scaled to fit the extrinsic space.
It's confusing because an img also has intrinsic and extrinsic dimensions, but the names of the properties are completely different from canvas. If you set width and height on an image, it's basically the same as setting style.width or style.height; they both set the extrinsic dimensions to scale the image within the page. Meanwhile, you can only get the intrinsic dimensions of an img using the new naturalWidth and naturalHeight (HTML5 browsers only) properties.
If the extrinsic dimensions are not set on both img and canvas, the image will be laid out at the same size as the intrinsic dimensions (i.e., scale factor would be 1).
Now, when you use jQuery, $(canvas).width('310px') is the same as $(canvas).css('310px'), which sets the extrinsic dimensions. You have to call $(canvas).prop('width', 310) or simply set canvas.width = 310 to set the intrinsic width.